From 7185566b5dcf7224efa0f28e12af58e73fd8afc2 Mon Sep 17 00:00:00 2001 From: Wickedness Date: Sun, 5 Jul 2026 09:36:25 +0900 Subject: [PATCH] Harden sortie item save recovery --- scripts/verify-campaign-save-normalization.mjs | 9 +++++++-- src/game/state/campaignState.ts | 8 ++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/scripts/verify-campaign-save-normalization.mjs b/scripts/verify-campaign-save-normalization.mjs index cf48ff7..7a7c740 100644 --- a/scripts/verify-campaign-save-normalization.mjs +++ b/scripts/verify-campaign-save-normalization.mjs @@ -81,7 +81,9 @@ try { sortieFormationAssignments: 'front', sortieItemAssignments: { 'liu-bei': { bean: '2', wine: -1 }, - 'guan-yu': 'broken' + 'guan-yu': '2', + 'zhang-fei': ['bean', '2'], + '': { bean: 4 } }, battleHistory: [] }) @@ -109,6 +111,9 @@ try { malformed.inventory.wine === undefined && malformed.sortieItemAssignments['liu-bei']?.bean === 2 && malformed.sortieItemAssignments['liu-bei']?.wine === undefined && + malformed.sortieItemAssignments['guan-yu'] === undefined && + malformed.sortieItemAssignments['zhang-fei'] === undefined && + Object.keys(malformed.sortieItemAssignments).length === 1 && Object.keys(malformed.sortieFormationAssignments).length === 0, `Expected malformed inventory and sortie assignments to normalize: ${JSON.stringify(malformed)}` ); @@ -258,7 +263,7 @@ try { const fallback = loadCampaignState(); assert(fallback.step === 'second-camp' && fallback.activeSaveSlot === 2, `Expected valid slotted save fallback: ${JSON.stringify(fallback)}`); - console.log('Verified campaign save normalization, malformed-field/bond/history-detail recovery, unknown-step/report/history recovery, unsupported-version rejection, and slotted fallback.'); + console.log('Verified campaign save normalization, malformed-field/bond/sortie-item/history-detail recovery, unknown-step/report/history recovery, unsupported-version rejection, and slotted fallback.'); } finally { await server.close(); } diff --git a/src/game/state/campaignState.ts b/src/game/state/campaignState.ts index 1be7142..c9c5361 100644 --- a/src/game/state/campaignState.ts +++ b/src/game/state/campaignState.ts @@ -991,17 +991,17 @@ function normalizeReserveTrainingSnapshot(value: unknown): CampaignReserveTraini }; } -function normalizeSortieItemAssignments(assignments?: CampaignSortieItemAssignments) { +function normalizeSortieItemAssignments(assignments?: Record) { return Object.entries(assignments ?? {}).reduce((next, [unitId, stocks]) => { - const unitStocks = Object.entries(stocks ?? {}).reduce>((nextStocks, [itemId, amount]) => { - const normalizedAmount = Math.floor(Number(amount)); + const unitStocks = Object.entries(recordOrEmpty(stocks)).reduce>((nextStocks, [itemId, amount]) => { + const normalizedAmount = normalizeNonNegativeInteger(amount); if (itemId && normalizedAmount > 0) { nextStocks[itemId] = normalizedAmount; } return nextStocks; }, {}); - if (Object.keys(unitStocks).length > 0) { + if (unitId && Object.keys(unitStocks).length > 0) { next[unitId] = unitStocks; } return next;