From e4f494b7b188c0a31deb8d4f181e713b0fdb8264 Mon Sep 17 00:00:00 2001 From: Wickedness Date: Thu, 9 Jul 2026 13:40:54 +0900 Subject: [PATCH] Apply per-officer reserve drill focus --- .../verify-campaign-save-normalization.mjs | 27 ++++++++++++++++ src/game/state/campaignState.ts | 31 ++++++++++++------- 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/scripts/verify-campaign-save-normalization.mjs b/scripts/verify-campaign-save-normalization.mjs index bcad6ae..7a09f77 100644 --- a/scripts/verify-campaign-save-normalization.mjs +++ b/scripts/verify-campaign-save-normalization.mjs @@ -184,6 +184,33 @@ try { completedCampVisits: [], createdAt: '2026-07-03T09:00:00.000Z' }; + const reserveDrillTemplate = campaignRecruitUnits.find((unit) => unit.id === 'jian-yong'); + assert(reserveDrillTemplate, 'Expected Jian Yong recruit template to exist for reserve drill assignment checks'); + storage.clear(); + storage.set( + campaignStorageKey, + JSON.stringify({ + version: 1, + updatedAt: '2026-07-03T12:10:00.000Z', + step: 'first-camp', + roster: [reserveDrillTemplate], + reserveTrainingFocus: 'balanced', + reserveTrainingAssignments: { [reserveDrillTemplate.id]: 'class-practice' } + }) + ); + loadCampaignState(); + setFirstBattleReport(firstBattleReport); + const individualizedReserveTraining = getCampaignState(); + const individualizedReserveAward = individualizedReserveTraining.battleHistory[firstScenario.id]?.reserveTraining?.find( + (entry) => entry.unitId === reserveDrillTemplate.id + ); + assert( + individualizedReserveAward?.focusId === 'class-practice' && + individualizedReserveAward.expGained === 4 && + individualizedReserveAward.equipmentExpGained === 5, + `Expected per-officer reserve drill assignments to override the global focus during battle settlement: ${JSON.stringify(individualizedReserveTraining)}` + ); + resetCampaignState(); setFirstBattleReport(firstBattleReport); setFirstBattleReport(firstBattleReport); const repeatedSettlement = getCampaignState(); diff --git a/src/game/state/campaignState.ts b/src/game/state/campaignState.ts index 3f03591..8926d91 100644 --- a/src/game/state/campaignState.ts +++ b/src/game/state/campaignState.ts @@ -710,7 +710,8 @@ export function setFirstBattleReport(report: FirstBattleReport) { state.roster, state.bonds, reportClone.units.filter((unit) => unit.faction === 'ally'), - state.reserveTrainingFocus + state.reserveTrainingFocus, + state.reserveTrainingAssignments ); if (!previousSettlement && reportClone.outcome === 'victory') { reportClone.bonds = state.bonds.map(cloneBondSnapshot); @@ -1624,31 +1625,39 @@ function applyReserveTrainingProgress( roster: UnitData[], bonds: CampBondSnapshot[], deployedUnits: UnitData[], - focusId: CampaignReserveTrainingFocusId + focusId: CampaignReserveTrainingFocusId, + reserveTrainingAssignments: CampaignReserveTrainingAssignments = {} ): CampaignReserveTrainingSnapshot[] { const focus = reserveTrainingFocusDefinition(focusId); const deployedIds = new Set(deployedUnits.map((unit) => unit.id)); const reserveUnits = roster.filter((unit) => unit.faction === 'ally' && !deployedIds.has(unit.id)); const reserveIds = new Set(reserveUnits.map((unit) => unit.id)); + const focusByUnitId = new Map( + reserveUnits.map((unit) => [unit.id, reserveTrainingFocusDefinition(reserveTrainingAssignments[unit.id] ?? focus.id)]) + ); const bondExpByUnit = new Map(); - if (focus.bondExpGained > 0 && reserveIds.size > 0) { + if (reserveIds.size > 0) { bonds.forEach((bond) => { - const reservePartners = bond.unitIds.filter((unitId) => reserveIds.has(unitId)); + const reservePartners = bond.unitIds + .filter((unitId) => reserveIds.has(unitId)) + .map((unitId) => ({ unitId, bondExpGained: focusByUnitId.get(unitId)?.bondExpGained ?? 0 })) + .filter((partner) => partner.bondExpGained > 0); if (reservePartners.length === 0) { return; } - advanceBond(bond, focus.bondExpGained); - reservePartners.forEach((unitId) => { - bondExpByUnit.set(unitId, (bondExpByUnit.get(unitId) ?? 0) + focus.bondExpGained); + advanceBond(bond, Math.max(...reservePartners.map((partner) => partner.bondExpGained))); + reservePartners.forEach((partner) => { + bondExpByUnit.set(partner.unitId, (bondExpByUnit.get(partner.unitId) ?? 0) + partner.bondExpGained); }); }); } return reserveUnits .map((unit) => { - const expGained = focus.expGained; - const equipmentExpGained = focus.equipmentExpGained; + const unitFocus = focusByUnitId.get(unit.id) ?? focus; + const expGained = unitFocus.expGained; + const equipmentExpGained = unitFocus.equipmentExpGained; advanceUnitExp(unit, expGained); advanceReserveEquipment(unit, equipmentExpGained); return { @@ -1657,8 +1666,8 @@ function applyReserveTrainingProgress( expGained, equipmentExpGained, bondExpGained: bondExpByUnit.get(unit.id) ?? 0, - focusId: focus.id, - focusLabel: focus.label, + focusId: unitFocus.id, + focusLabel: unitFocus.label, level: unit.level, exp: unit.exp };