Deduplicate campaign reward references

This commit is contained in:
2026-07-05 14:13:27 +09:00
parent 7196ab3dbd
commit d0085eeb18
2 changed files with 38 additions and 14 deletions

View File

@@ -649,11 +649,13 @@ try {
reputation: ['Village Thanks', ' Village Thanks '],
recruits: [
{ unitId: ' guan-yu ', name: ' Guan Yu ' },
{ unitId: 'guan-yu', name: 'Duplicate Guan Yu' },
{ unitId: '', name: 'Broken' },
...Array.from({ length: 140 }, (_, index) => ({ unitId: `recruit-${index}`, name: `Recruit ${index}` }))
],
unlocks: [
{ battleId: ' second-battle-yellow-turban-pursuit ', title: ' Next ' },
{ battleId: 'second-battle-yellow-turban-pursuit', title: 'Duplicate Next' },
{ battleId: '', title: 'Broken' },
...Array.from({ length: 140 }, (_, index) => ({ battleId: `battle-${index}`, title: `Battle ${index}` }))
],
@@ -690,8 +692,12 @@ try {
malformedReport.firstBattleReport.campaignRewards?.reputation.length === 1 &&
malformedReport.firstBattleReport.campaignRewards?.recruits.length === 128 &&
malformedReport.firstBattleReport.campaignRewards?.recruits[0].unitId === 'guan-yu' &&
malformedReport.firstBattleReport.campaignRewards?.recruits[0].name === 'Guan Yu' &&
malformedReport.firstBattleReport.campaignRewards?.recruits.filter((recruit) => recruit.unitId === 'guan-yu').length === 1 &&
malformedReport.firstBattleReport.campaignRewards?.unlocks.length === 128 &&
malformedReport.firstBattleReport.campaignRewards?.unlocks[0].battleId === 'second-battle-yellow-turban-pursuit' &&
malformedReport.firstBattleReport.campaignRewards?.unlocks[0].title === 'Next' &&
malformedReport.firstBattleReport.campaignRewards?.unlocks.filter((unlock) => unlock.battleId === 'second-battle-yellow-turban-pursuit').length === 1 &&
malformedReport.firstBattleReport.campaignRewards?.note === 'Reward note' &&
malformedReport.firstBattleReport.completedCampDialogues.length === 1 &&
malformedReport.firstBattleReport.completedCampVisits.length === 0,

View File

@@ -905,6 +905,18 @@ function uniqueStrings(value: unknown): string[] {
].slice(0, maxCampaignStringListEntries);
}
function uniqueByKey<T>(entries: T[], keyForEntry: (entry: T) => string) {
const seen = new Set<string>();
return entries.filter((entry) => {
const key = keyForEntry(entry);
if (!key || seen.has(key)) {
return false;
}
seen.add(key);
return true;
});
}
function normalizeKeyString(value: unknown): string {
if (typeof value !== 'string') {
return '';
@@ -1658,21 +1670,27 @@ function cloneCampaignRewardSnapshot(rewards?: CampaignRewardSnapshot): Campaign
supplies: uniqueStrings(rewardRecord.supplies),
equipment: uniqueStrings(rewardRecord.equipment),
reputation: uniqueStrings(rewardRecord.reputation),
recruits: arrayOrEmpty<unknown>(rewardRecord.recruits)
.filter(isPlainObject)
.map((recruit) => ({
unitId: normalizeKeyString(recruit.unitId),
name: normalizeDisplayString(recruit.name)
}))
.filter((recruit) => recruit.unitId && recruit.name)
recruits: uniqueByKey(
arrayOrEmpty<unknown>(rewardRecord.recruits)
.filter(isPlainObject)
.map((recruit) => ({
unitId: normalizeKeyString(recruit.unitId),
name: normalizeDisplayString(recruit.name)
}))
.filter((recruit) => recruit.unitId && recruit.name),
(recruit) => recruit.unitId
)
.slice(0, maxCampaignStringListEntries),
unlocks: arrayOrEmpty<unknown>(rewardRecord.unlocks)
.filter(isPlainObject)
.map((unlock) => ({
battleId: normalizeKeyString(unlock.battleId),
title: normalizeDisplayString(unlock.title)
}))
.filter((unlock) => unlock.battleId && unlock.title)
unlocks: uniqueByKey(
arrayOrEmpty<unknown>(rewardRecord.unlocks)
.filter(isPlainObject)
.map((unlock) => ({
battleId: normalizeKeyString(unlock.battleId),
title: normalizeDisplayString(unlock.title)
}))
.filter((unlock) => unlock.battleId && unlock.title),
(unlock) => unlock.battleId
)
.slice(0, maxCampaignStringListEntries),
note: note.length > 0 && note.length <= maxCampaignRewardNoteLength ? note : undefined
};