From ce79166f9670f59e3e56231bf1e7ed2b47ec7851 Mon Sep 17 00:00:00 2001 From: Wickedness Date: Sun, 5 Jul 2026 14:41:47 +0900 Subject: [PATCH] Normalize campaign reward labels --- scripts/verify-campaign-save-normalization.mjs | 4 ++-- src/game/state/campaignState.ts | 16 ++++++++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/scripts/verify-campaign-save-normalization.mjs b/scripts/verify-campaign-save-normalization.mjs index e2689c4..41e90fd 100644 --- a/scripts/verify-campaign-save-normalization.mjs +++ b/scripts/verify-campaign-save-normalization.mjs @@ -642,7 +642,7 @@ try { })) ], mvp: { unitId: 'liu-bei', name: 'Liu Bei', damageDealt: '1000000', defeats: '2' }, - itemRewards: ['Bean', 'Bean', 10, 'x'.repeat(97)], + itemRewards: ['Bean', 'Bean', 'Bean -2', 'Bean +0', 10, 'x'.repeat(97)], campaignRewards: { supplies: [' Bean ', 'Bean', 20, 'x'.repeat(97)], equipment: [' Iron Sword ', 'Iron Sword'], @@ -753,7 +753,7 @@ try { battleTitle: ' ', outcome: 'victory', rewardGold: '120', - itemRewards: ['Bean', 12, 'Bean'], + itemRewards: ['Bean', 12, 'Bean', 'Bean -2', 'Bean +0'], campaignRewards: { supplies: { corrupted: true }, equipment: ['Iron Sword'], diff --git a/src/game/state/campaignState.ts b/src/game/state/campaignState.ts index 9e94c86..9ad31f5 100644 --- a/src/game/state/campaignState.ts +++ b/src/game/state/campaignState.ts @@ -906,6 +906,17 @@ function uniqueStrings(value: unknown): string[] { ].slice(0, maxCampaignStringListEntries); } +function normalizeRewardStrings(value: unknown): string[] { + return [ + ...new Set( + arrayOrEmpty(value) + .filter((entry): entry is string => typeof entry === 'string') + .map((entry) => entry.replace(/\s+/g, ' ').trim()) + .filter((entry) => entry.length > 0 && entry.length <= maxCampaignStringListLength && Boolean(parseRewardLabel(entry))) + ) + ].slice(0, maxCampaignStringListEntries); +} + function uniqueByKey(entries: T[], keyForEntry: (entry: T) => string) { const seen = new Set(); return entries.filter((entry) => { @@ -1083,7 +1094,7 @@ function normalizeCampaignBattleSettlement(value: unknown): CampaignBattleSettle battleTitle, outcome: settlement.outcome, rewardGold: normalizeNonNegativeInteger(settlement.rewardGold), - itemRewards: uniqueStrings(settlement.itemRewards), + itemRewards: normalizeRewardStrings(settlement.itemRewards), campaignRewards: cloneCampaignRewardSnapshot(settlement.campaignRewards, battleId), objectives: normalizeLimitedArray(settlement.objectives, normalizeBattleObjectiveSnapshot, maxCampaignBattleObjectiveEntries), units: normalizeLimitedArray(settlement.units, normalizeCampaignUnitProgressSnapshot, maxCampaignBattleUnitEntries), @@ -1289,7 +1300,7 @@ function normalizeFirstBattleReport(report: unknown): FirstBattleReport | undefi units: normalizeLimitedArray(report.units, normalizeUnitDataSnapshot, maxCampaignBattleUnitEntries), bonds: normalizeLimitedArray(report.bonds, normalizeCampBondSnapshot, maxCampaignBattleBondEntries), mvp: normalizeCampMvpSnapshot(report.mvp), - itemRewards: uniqueStrings(report.itemRewards), + itemRewards: normalizeRewardStrings(report.itemRewards), campaignRewards: cloneCampaignRewardSnapshot( isPlainObject(report.campaignRewards) ? report.campaignRewards as CampaignRewardSnapshot : undefined, battleId @@ -1653,6 +1664,7 @@ function cloneCampaignState(state: CampaignState): CampaignState { function cloneReport(report: FirstBattleReport): FirstBattleReport { const cloned = JSON.parse(JSON.stringify(report)) as FirstBattleReport; + cloned.itemRewards = normalizeRewardStrings(cloned.itemRewards); cloned.completedCampDialogues = uniqueStrings(cloned.completedCampDialogues); cloned.completedCampVisits = uniqueStrings(cloned.completedCampVisits); if (cloned.campaignRewards) {