Apply per-officer reserve drill focus

This commit is contained in:
2026-07-09 13:40:54 +09:00
parent 02578e2f28
commit e4f494b7b1
2 changed files with 47 additions and 11 deletions

View File

@@ -184,6 +184,33 @@ try {
completedCampVisits: [], completedCampVisits: [],
createdAt: '2026-07-03T09:00:00.000Z' 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);
setFirstBattleReport(firstBattleReport); setFirstBattleReport(firstBattleReport);
const repeatedSettlement = getCampaignState(); const repeatedSettlement = getCampaignState();

View File

@@ -710,7 +710,8 @@ export function setFirstBattleReport(report: FirstBattleReport) {
state.roster, state.roster,
state.bonds, state.bonds,
reportClone.units.filter((unit) => unit.faction === 'ally'), reportClone.units.filter((unit) => unit.faction === 'ally'),
state.reserveTrainingFocus state.reserveTrainingFocus,
state.reserveTrainingAssignments
); );
if (!previousSettlement && reportClone.outcome === 'victory') { if (!previousSettlement && reportClone.outcome === 'victory') {
reportClone.bonds = state.bonds.map(cloneBondSnapshot); reportClone.bonds = state.bonds.map(cloneBondSnapshot);
@@ -1624,31 +1625,39 @@ function applyReserveTrainingProgress(
roster: UnitData[], roster: UnitData[],
bonds: CampBondSnapshot[], bonds: CampBondSnapshot[],
deployedUnits: UnitData[], deployedUnits: UnitData[],
focusId: CampaignReserveTrainingFocusId focusId: CampaignReserveTrainingFocusId,
reserveTrainingAssignments: CampaignReserveTrainingAssignments = {}
): CampaignReserveTrainingSnapshot[] { ): CampaignReserveTrainingSnapshot[] {
const focus = reserveTrainingFocusDefinition(focusId); const focus = reserveTrainingFocusDefinition(focusId);
const deployedIds = new Set(deployedUnits.map((unit) => unit.id)); const deployedIds = new Set(deployedUnits.map((unit) => unit.id));
const reserveUnits = roster.filter((unit) => unit.faction === 'ally' && !deployedIds.has(unit.id)); const reserveUnits = roster.filter((unit) => unit.faction === 'ally' && !deployedIds.has(unit.id));
const reserveIds = new Set(reserveUnits.map((unit) => 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<string, number>(); const bondExpByUnit = new Map<string, number>();
if (focus.bondExpGained > 0 && reserveIds.size > 0) { if (reserveIds.size > 0) {
bonds.forEach((bond) => { 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) { if (reservePartners.length === 0) {
return; return;
} }
advanceBond(bond, focus.bondExpGained); advanceBond(bond, Math.max(...reservePartners.map((partner) => partner.bondExpGained)));
reservePartners.forEach((unitId) => { reservePartners.forEach((partner) => {
bondExpByUnit.set(unitId, (bondExpByUnit.get(unitId) ?? 0) + focus.bondExpGained); bondExpByUnit.set(partner.unitId, (bondExpByUnit.get(partner.unitId) ?? 0) + partner.bondExpGained);
}); });
}); });
} }
return reserveUnits return reserveUnits
.map((unit) => { .map((unit) => {
const expGained = focus.expGained; const unitFocus = focusByUnitId.get(unit.id) ?? focus;
const equipmentExpGained = focus.equipmentExpGained; const expGained = unitFocus.expGained;
const equipmentExpGained = unitFocus.equipmentExpGained;
advanceUnitExp(unit, expGained); advanceUnitExp(unit, expGained);
advanceReserveEquipment(unit, equipmentExpGained); advanceReserveEquipment(unit, equipmentExpGained);
return { return {
@@ -1657,8 +1666,8 @@ function applyReserveTrainingProgress(
expGained, expGained,
equipmentExpGained, equipmentExpGained,
bondExpGained: bondExpByUnit.get(unit.id) ?? 0, bondExpGained: bondExpByUnit.get(unit.id) ?? 0,
focusId: focus.id, focusId: unitFocus.id,
focusLabel: focus.label, focusLabel: unitFocus.label,
level: unit.level, level: unit.level,
exp: unit.exp exp: unit.exp
}; };