feat: add post-battle formation review
This commit is contained in:
@@ -44,6 +44,30 @@ export type CampaignRewardSnapshot = {
|
||||
note?: string;
|
||||
};
|
||||
|
||||
export type CampaignSortieUnitPerformanceSnapshot = {
|
||||
unitId: string;
|
||||
hpBefore: number;
|
||||
maxHpBefore: number;
|
||||
damageDealt: number;
|
||||
damageTaken: number;
|
||||
defeats: number;
|
||||
actions: number;
|
||||
support: number;
|
||||
sortieBonusDamage: number;
|
||||
sortiePreventedDamage: number;
|
||||
sortieBonusHealing: number;
|
||||
sortieExtendedBondTriggers: number;
|
||||
intentDefeats: number;
|
||||
intentLineBreaks: number;
|
||||
intentGuardSuccesses: number;
|
||||
};
|
||||
|
||||
export type CampaignSortiePerformanceSnapshot = {
|
||||
selectedUnitIds: string[];
|
||||
formationAssignments: SortieFormationAssignments;
|
||||
unitStats: CampaignSortieUnitPerformanceSnapshot[];
|
||||
};
|
||||
|
||||
export type FirstBattleReport = {
|
||||
battleId: string;
|
||||
battleTitle: string;
|
||||
@@ -58,6 +82,7 @@ export type FirstBattleReport = {
|
||||
mvp?: CampMvpSnapshot;
|
||||
itemRewards: string[];
|
||||
campaignRewards?: CampaignRewardSnapshot;
|
||||
sortiePerformance?: CampaignSortiePerformanceSnapshot;
|
||||
completedCampDialogues: string[];
|
||||
completedCampVisits: string[];
|
||||
createdAt: string;
|
||||
@@ -320,6 +345,7 @@ export type CampaignBattleSettlement = {
|
||||
units: CampaignUnitProgressSnapshot[];
|
||||
bonds: CampaignBondProgressSnapshot[];
|
||||
reserveTraining?: CampaignReserveTrainingSnapshot[];
|
||||
sortiePerformance?: CampaignSortiePerformanceSnapshot;
|
||||
completedAt: string;
|
||||
};
|
||||
|
||||
@@ -1117,11 +1143,16 @@ function normalizeLatestBattleId(value: unknown, battleHistory: Record<string, C
|
||||
}
|
||||
|
||||
function filterFirstBattleReportRosterReferences(report: FirstBattleReport, rosterUnitIds: Set<string>): FirstBattleReport {
|
||||
const sortiePerformance = normalizeCampaignSortiePerformanceSnapshot(report.sortiePerformance, rosterUnitIds);
|
||||
const filtered = {
|
||||
...report,
|
||||
units: report.units.filter((unit) => unit.faction !== 'ally' || rosterUnitIds.has(unit.id)),
|
||||
bonds: report.bonds.filter((bond) => bond.unitIds.every((unitId) => rosterUnitIds.has(unitId)))
|
||||
bonds: report.bonds.filter((bond) => bond.unitIds.every((unitId) => rosterUnitIds.has(unitId))),
|
||||
...(sortiePerformance ? { sortiePerformance } : {})
|
||||
};
|
||||
if (!sortiePerformance) {
|
||||
delete filtered.sortiePerformance;
|
||||
}
|
||||
if (filtered.mvp && !rosterUnitIds.has(filtered.mvp.unitId)) {
|
||||
delete filtered.mvp;
|
||||
}
|
||||
@@ -1134,12 +1165,17 @@ function filterBattleHistoryRosterReferences(
|
||||
bondIds: Set<string>
|
||||
): Record<string, CampaignBattleSettlement> {
|
||||
return Object.entries(history).reduce<Record<string, CampaignBattleSettlement>>((filteredHistory, [battleId, settlement]) => {
|
||||
const sortiePerformance = normalizeCampaignSortiePerformanceSnapshot(settlement.sortiePerformance, rosterUnitIds);
|
||||
filteredHistory[battleId] = {
|
||||
...settlement,
|
||||
units: settlement.units.filter((unit) => rosterUnitIds.has(unit.unitId)),
|
||||
bonds: bondIds.size > 0 ? settlement.bonds.filter((bond) => bondIds.has(bond.id)) : settlement.bonds,
|
||||
reserveTraining: settlement.reserveTraining?.filter((entry) => rosterUnitIds.has(entry.unitId))
|
||||
reserveTraining: settlement.reserveTraining?.filter((entry) => rosterUnitIds.has(entry.unitId)),
|
||||
...(sortiePerformance ? { sortiePerformance } : {})
|
||||
};
|
||||
if (!sortiePerformance) {
|
||||
delete filteredHistory[battleId].sortiePerformance;
|
||||
}
|
||||
return filteredHistory;
|
||||
}, {});
|
||||
}
|
||||
@@ -1163,6 +1199,8 @@ function normalizeCampaignBattleSettlement(value: unknown): CampaignBattleSettle
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const sortiePerformance = normalizeCampaignSortiePerformanceSnapshot(settlement.sortiePerformance);
|
||||
|
||||
return {
|
||||
battleId,
|
||||
battleTitle,
|
||||
@@ -1174,6 +1212,7 @@ function normalizeCampaignBattleSettlement(value: unknown): CampaignBattleSettle
|
||||
units: normalizeLimitedArray(settlement.units, normalizeCampaignUnitProgressSnapshot, maxCampaignBattleUnitEntries),
|
||||
bonds: normalizeLimitedArray(settlement.bonds, normalizeCampaignBondProgressSnapshot, maxCampaignBattleBondEntries),
|
||||
reserveTraining: normalizeLimitedArray(settlement.reserveTraining, normalizeReserveTrainingSnapshot, maxCampaignReserveTrainingEntries),
|
||||
...(sortiePerformance ? { sortiePerformance } : {}),
|
||||
completedAt
|
||||
};
|
||||
}
|
||||
@@ -1355,6 +1394,60 @@ export function normalizeCampaignSortieFormationPresets(value: unknown, allowedU
|
||||
}, {});
|
||||
}
|
||||
|
||||
export function normalizeCampaignSortiePerformanceSnapshot(
|
||||
value: unknown,
|
||||
allowedUnitIds?: Set<string>
|
||||
): CampaignSortiePerformanceSnapshot | undefined {
|
||||
if (!isPlainObject(value)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const selectedUnitIds = uniqueStrings(value.selectedUnitIds)
|
||||
.filter((unitId) => !allowedUnitIds || allowedUnitIds.has(unitId))
|
||||
.slice(0, maxCampaignSortieAssignmentUnits);
|
||||
if (selectedUnitIds.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const selectedIdSet = new Set(selectedUnitIds);
|
||||
const unitStats = uniqueByKey(
|
||||
normalizeLimitedArray(value.unitStats, (entry): CampaignSortieUnitPerformanceSnapshot | undefined => {
|
||||
if (!isPlainObject(entry)) {
|
||||
return undefined;
|
||||
}
|
||||
const unitId = normalizeKeyString(entry.unitId);
|
||||
if (!unitId || !selectedIdSet.has(unitId)) {
|
||||
return undefined;
|
||||
}
|
||||
const hpBefore = normalizeNonNegativeInteger(entry.hpBefore);
|
||||
return {
|
||||
unitId,
|
||||
hpBefore,
|
||||
maxHpBefore: Math.max(hpBefore, normalizeNonNegativeInteger(entry.maxHpBefore)),
|
||||
damageDealt: normalizeNonNegativeInteger(entry.damageDealt),
|
||||
damageTaken: normalizeNonNegativeInteger(entry.damageTaken),
|
||||
defeats: normalizeNonNegativeInteger(entry.defeats),
|
||||
actions: normalizeNonNegativeInteger(entry.actions),
|
||||
support: normalizeNonNegativeInteger(entry.support),
|
||||
sortieBonusDamage: normalizeNonNegativeInteger(entry.sortieBonusDamage),
|
||||
sortiePreventedDamage: normalizeNonNegativeInteger(entry.sortiePreventedDamage),
|
||||
sortieBonusHealing: normalizeNonNegativeInteger(entry.sortieBonusHealing),
|
||||
sortieExtendedBondTriggers: normalizeNonNegativeInteger(entry.sortieExtendedBondTriggers),
|
||||
intentDefeats: normalizeNonNegativeInteger(entry.intentDefeats),
|
||||
intentLineBreaks: normalizeNonNegativeInteger(entry.intentLineBreaks),
|
||||
intentGuardSuccesses: normalizeNonNegativeInteger(entry.intentGuardSuccesses)
|
||||
};
|
||||
}, maxCampaignSortieAssignmentUnits),
|
||||
(entry) => entry.unitId
|
||||
);
|
||||
|
||||
return {
|
||||
selectedUnitIds,
|
||||
formationAssignments: normalizeSortieFormationAssignments(recordOrEmpty(value.formationAssignments), selectedIdSet),
|
||||
unitStats
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeReserveTrainingFocusId(focusId?: string): CampaignReserveTrainingFocusId {
|
||||
return isReserveTrainingFocusId(focusId)
|
||||
? focusId as CampaignReserveTrainingFocusId
|
||||
@@ -1392,6 +1485,8 @@ function normalizeFirstBattleReport(report: unknown): FirstBattleReport | undefi
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const sortiePerformance = normalizeCampaignSortiePerformanceSnapshot(report.sortiePerformance);
|
||||
|
||||
return {
|
||||
battleId,
|
||||
battleTitle,
|
||||
@@ -1409,6 +1504,7 @@ function normalizeFirstBattleReport(report: unknown): FirstBattleReport | undefi
|
||||
isPlainObject(report.campaignRewards) ? report.campaignRewards as CampaignRewardSnapshot : undefined,
|
||||
battleId
|
||||
),
|
||||
...(sortiePerformance ? { sortiePerformance } : {}),
|
||||
completedCampDialogues: uniqueStrings(report.completedCampDialogues),
|
||||
completedCampVisits: uniqueStrings(report.completedCampVisits),
|
||||
createdAt: normalizeCampaignTimestamp(report.createdAt) ?? new Date().toISOString()
|
||||
@@ -1643,6 +1739,15 @@ function createBattleSettlement(report: FirstBattleReport, reserveTraining: Camp
|
||||
battleExp: bond.battleExp
|
||||
})),
|
||||
reserveTraining: reserveTraining.map((entry) => ({ ...entry })),
|
||||
...(report.sortiePerformance
|
||||
? {
|
||||
sortiePerformance: {
|
||||
selectedUnitIds: [...report.sortiePerformance.selectedUnitIds],
|
||||
formationAssignments: { ...report.sortiePerformance.formationAssignments },
|
||||
unitStats: report.sortiePerformance.unitStats.map((stats) => ({ ...stats }))
|
||||
}
|
||||
}
|
||||
: {}),
|
||||
completedAt: report.createdAt
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user