Prepare reserve drill assignment state
This commit is contained in:
@@ -32,6 +32,7 @@ try {
|
|||||||
getCampaignState,
|
getCampaignState,
|
||||||
hasCampaignSave,
|
hasCampaignSave,
|
||||||
loadCampaignState,
|
loadCampaignState,
|
||||||
|
normalizeCampaignReserveTrainingAssignments,
|
||||||
normalizeCampaignSortieItemAssignments,
|
normalizeCampaignSortieItemAssignments,
|
||||||
resetCampaignState,
|
resetCampaignState,
|
||||||
setCampaignState,
|
setCampaignState,
|
||||||
@@ -104,6 +105,35 @@ try {
|
|||||||
cappedLaunchFormation['unit-96'] === undefined,
|
cappedLaunchFormation['unit-96'] === undefined,
|
||||||
`Expected launch sortie formation assignments without a roster filter to stay capped: ${JSON.stringify(cappedLaunchFormation)}`
|
`Expected launch sortie formation assignments without a roster filter to stay capped: ${JSON.stringify(cappedLaunchFormation)}`
|
||||||
);
|
);
|
||||||
|
const normalizedReserveDrills = normalizeCampaignReserveTrainingAssignments(
|
||||||
|
{
|
||||||
|
'liu-bei': 'bond-practice',
|
||||||
|
' liu-bei ': 'class-practice',
|
||||||
|
'guan-yu': 'class-practice',
|
||||||
|
'ghost-unit': 'balanced',
|
||||||
|
'zhang-fei': 'broken-focus',
|
||||||
|
['x'.repeat(97)]: 'balanced',
|
||||||
|
'': 'balanced'
|
||||||
|
},
|
||||||
|
new Set(['liu-bei', 'guan-yu'])
|
||||||
|
);
|
||||||
|
assert(
|
||||||
|
normalizedReserveDrills['liu-bei'] === 'bond-practice' &&
|
||||||
|
normalizedReserveDrills['guan-yu'] === 'class-practice' &&
|
||||||
|
normalizedReserveDrills['ghost-unit'] === undefined &&
|
||||||
|
normalizedReserveDrills['zhang-fei'] === undefined &&
|
||||||
|
normalizedReserveDrills['x'.repeat(97)] === undefined,
|
||||||
|
`Expected reserve drill assignments to trim, filter, and preserve first valid focus ids: ${JSON.stringify(normalizedReserveDrills)}`
|
||||||
|
);
|
||||||
|
const cappedReserveDrills = normalizeCampaignReserveTrainingAssignments(
|
||||||
|
Object.fromEntries(Array.from({ length: 120 }, (_, index) => [`unit-${index}`, 'balanced']))
|
||||||
|
);
|
||||||
|
assert(
|
||||||
|
Object.keys(cappedReserveDrills).length === 96 &&
|
||||||
|
cappedReserveDrills['unit-95'] === 'balanced' &&
|
||||||
|
cappedReserveDrills['unit-96'] === undefined,
|
||||||
|
`Expected reserve drill assignments without a roster filter to stay capped: ${JSON.stringify(cappedReserveDrills)}`
|
||||||
|
);
|
||||||
|
|
||||||
resetCampaignState();
|
resetCampaignState();
|
||||||
const firstScenario = battleScenarios['first-battle-zhuo-commandery'];
|
const firstScenario = battleScenarios['first-battle-zhuo-commandery'];
|
||||||
@@ -325,7 +355,8 @@ try {
|
|||||||
assert(
|
assert(
|
||||||
Array.isArray(legacy.selectedSortieUnitIds) &&
|
Array.isArray(legacy.selectedSortieUnitIds) &&
|
||||||
typeof legacy.sortieFormationAssignments === 'object' &&
|
typeof legacy.sortieFormationAssignments === 'object' &&
|
||||||
typeof legacy.sortieItemAssignments === 'object',
|
typeof legacy.sortieItemAssignments === 'object' &&
|
||||||
|
typeof legacy.reserveTrainingAssignments === 'object',
|
||||||
`Expected modern sortie fields to be restored: ${JSON.stringify(legacy)}`
|
`Expected modern sortie fields to be restored: ${JSON.stringify(legacy)}`
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -360,6 +391,13 @@ try {
|
|||||||
},
|
},
|
||||||
selectedSortieUnitIds: { corrupted: true },
|
selectedSortieUnitIds: { corrupted: true },
|
||||||
sortieFormationAssignments: 'front',
|
sortieFormationAssignments: 'front',
|
||||||
|
reserveTrainingFocus: 'ghost-focus',
|
||||||
|
reserveTrainingAssignments: {
|
||||||
|
'liu-bei': 'bond-practice',
|
||||||
|
'guan-yu': 'unknown-focus',
|
||||||
|
['x'.repeat(97)]: 'balanced',
|
||||||
|
'': 'balanced'
|
||||||
|
},
|
||||||
sortieItemAssignments: {
|
sortieItemAssignments: {
|
||||||
'liu-bei': { ' bean ': '5', wine: -1, salve: '1', stone: '9', ['x'.repeat(97)]: '1' },
|
'liu-bei': { ' bean ': '5', wine: -1, salve: '1', stone: '9', ['x'.repeat(97)]: '1' },
|
||||||
'guan-yu': '2',
|
'guan-yu': '2',
|
||||||
@@ -407,6 +445,13 @@ try {
|
|||||||
Object.keys(malformed.sortieFormationAssignments).length === 0,
|
Object.keys(malformed.sortieFormationAssignments).length === 0,
|
||||||
`Expected malformed inventory and sortie assignments to normalize: ${JSON.stringify(malformed)}`
|
`Expected malformed inventory and sortie assignments to normalize: ${JSON.stringify(malformed)}`
|
||||||
);
|
);
|
||||||
|
assert(
|
||||||
|
malformed.reserveTrainingFocus === 'balanced' &&
|
||||||
|
malformed.reserveTrainingAssignments['liu-bei'] === 'bond-practice' &&
|
||||||
|
malformed.reserveTrainingAssignments['guan-yu'] === undefined &&
|
||||||
|
malformed.reserveTrainingAssignments['x'.repeat(97)] === undefined,
|
||||||
|
`Expected malformed reserve drill focus fields to normalize: ${JSON.stringify(malformed)}`
|
||||||
|
);
|
||||||
|
|
||||||
storage.clear();
|
storage.clear();
|
||||||
storage.set(
|
storage.set(
|
||||||
@@ -445,6 +490,11 @@ try {
|
|||||||
'guan-yu': 'support',
|
'guan-yu': 'support',
|
||||||
broken: 'center'
|
broken: 'center'
|
||||||
},
|
},
|
||||||
|
reserveTrainingAssignments: {
|
||||||
|
'liu-bei': 'bond-practice',
|
||||||
|
'ghost-unit': 'balanced',
|
||||||
|
'guan-yu': 'class-practice'
|
||||||
|
},
|
||||||
sortieItemAssignments: {
|
sortieItemAssignments: {
|
||||||
'liu-bei': { bean: '2' },
|
'liu-bei': { bean: '2' },
|
||||||
'ghost-unit': { bean: '1' },
|
'ghost-unit': { bean: '1' },
|
||||||
@@ -458,6 +508,9 @@ try {
|
|||||||
staleSortie.selectedSortieUnitIds[0] === 'liu-bei' &&
|
staleSortie.selectedSortieUnitIds[0] === 'liu-bei' &&
|
||||||
staleSortie.sortieFormationAssignments['liu-bei'] === 'front' &&
|
staleSortie.sortieFormationAssignments['liu-bei'] === 'front' &&
|
||||||
staleSortie.sortieFormationAssignments['ghost-unit'] === undefined &&
|
staleSortie.sortieFormationAssignments['ghost-unit'] === undefined &&
|
||||||
|
staleSortie.reserveTrainingAssignments['liu-bei'] === 'bond-practice' &&
|
||||||
|
staleSortie.reserveTrainingAssignments['ghost-unit'] === undefined &&
|
||||||
|
staleSortie.reserveTrainingAssignments['guan-yu'] === undefined &&
|
||||||
staleSortie.sortieItemAssignments['liu-bei']?.bean === 2 &&
|
staleSortie.sortieItemAssignments['liu-bei']?.bean === 2 &&
|
||||||
staleSortie.sortieItemAssignments['ghost-unit'] === undefined &&
|
staleSortie.sortieItemAssignments['ghost-unit'] === undefined &&
|
||||||
staleSortie.sortieItemAssignments['guan-yu'] === undefined,
|
staleSortie.sortieItemAssignments['guan-yu'] === undefined,
|
||||||
|
|||||||
@@ -246,6 +246,8 @@ export type CampaignReserveTrainingFocusDefinition = {
|
|||||||
bondExpGained: number;
|
bondExpGained: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type CampaignReserveTrainingAssignments = Partial<Record<string, CampaignReserveTrainingFocusId>>;
|
||||||
|
|
||||||
export const defaultCampaignReserveTrainingFocusId: CampaignReserveTrainingFocusId = 'balanced';
|
export const defaultCampaignReserveTrainingFocusId: CampaignReserveTrainingFocusId = 'balanced';
|
||||||
|
|
||||||
const defaultEquipmentBySlot: Record<EquipmentSlot, string> = {
|
const defaultEquipmentBySlot: Record<EquipmentSlot, string> = {
|
||||||
@@ -274,6 +276,7 @@ const maxCampaignBattleObjectiveEntries = 24;
|
|||||||
const maxCampaignBattleUnitEntries = 96;
|
const maxCampaignBattleUnitEntries = 96;
|
||||||
const maxCampaignBattleBondEntries = 96;
|
const maxCampaignBattleBondEntries = 96;
|
||||||
const maxCampaignReserveTrainingEntries = 96;
|
const maxCampaignReserveTrainingEntries = 96;
|
||||||
|
const maxCampaignReserveTrainingAssignmentUnits = 96;
|
||||||
const maxCampaignSortieAssignmentUnits = 96;
|
const maxCampaignSortieAssignmentUnits = 96;
|
||||||
const maxCampaignDisplayTextLength = 180;
|
const maxCampaignDisplayTextLength = 180;
|
||||||
const maxCampaignNumericValue = 999999;
|
const maxCampaignNumericValue = 999999;
|
||||||
@@ -335,6 +338,7 @@ export type CampaignState = {
|
|||||||
sortieFormationAssignments: SortieFormationAssignments;
|
sortieFormationAssignments: SortieFormationAssignments;
|
||||||
sortieItemAssignments: CampaignSortieItemAssignments;
|
sortieItemAssignments: CampaignSortieItemAssignments;
|
||||||
reserveTrainingFocus: CampaignReserveTrainingFocusId;
|
reserveTrainingFocus: CampaignReserveTrainingFocusId;
|
||||||
|
reserveTrainingAssignments: CampaignReserveTrainingAssignments;
|
||||||
completedCampDialogues: string[];
|
completedCampDialogues: string[];
|
||||||
completedCampVisits: string[];
|
completedCampVisits: string[];
|
||||||
battleHistory: Record<string, CampaignBattleSettlement>;
|
battleHistory: Record<string, CampaignBattleSettlement>;
|
||||||
@@ -535,6 +539,7 @@ export function createInitialCampaignState(): CampaignState {
|
|||||||
sortieFormationAssignments: {},
|
sortieFormationAssignments: {},
|
||||||
sortieItemAssignments: {},
|
sortieItemAssignments: {},
|
||||||
reserveTrainingFocus: defaultCampaignReserveTrainingFocusId,
|
reserveTrainingFocus: defaultCampaignReserveTrainingFocusId,
|
||||||
|
reserveTrainingAssignments: {},
|
||||||
completedCampDialogues: [],
|
completedCampDialogues: [],
|
||||||
completedCampVisits: [],
|
completedCampVisits: [],
|
||||||
battleHistory: {}
|
battleHistory: {}
|
||||||
@@ -623,6 +628,27 @@ export function setCampaignReserveTrainingFocus(focusId: CampaignReserveTraining
|
|||||||
return cloneCampaignState(state);
|
return cloneCampaignState(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function normalizeCampaignReserveTrainingAssignments(
|
||||||
|
assignments?: Record<string, unknown>,
|
||||||
|
allowedUnitIds?: Set<string>
|
||||||
|
): CampaignReserveTrainingAssignments {
|
||||||
|
return Object.entries(assignments ?? {}).reduce<CampaignReserveTrainingAssignments>((next, [unitId, focusId]) => {
|
||||||
|
const normalizedUnitId = normalizeKeyString(unitId);
|
||||||
|
if (
|
||||||
|
!normalizedUnitId ||
|
||||||
|
next[normalizedUnitId] ||
|
||||||
|
(allowedUnitIds && !allowedUnitIds.has(normalizedUnitId)) ||
|
||||||
|
(!allowedUnitIds && Object.keys(next).length >= maxCampaignReserveTrainingAssignmentUnits) ||
|
||||||
|
!isReserveTrainingFocusId(focusId)
|
||||||
|
) {
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
next[normalizedUnitId] = focusId;
|
||||||
|
return next;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
export function setFirstBattleReport(report: FirstBattleReport) {
|
export function setFirstBattleReport(report: FirstBattleReport) {
|
||||||
const state = ensureCampaignState();
|
const state = ensureCampaignState();
|
||||||
const reportClone = cloneReport(report);
|
const reportClone = cloneReport(report);
|
||||||
@@ -867,6 +893,7 @@ function normalizeCampaignState(state: Partial<CampaignState>): CampaignState {
|
|||||||
normalized.sortieFormationAssignments = normalizeSortieFormationAssignments(recordOrEmpty(normalized.sortieFormationAssignments), sortieUnitFilter);
|
normalized.sortieFormationAssignments = normalizeSortieFormationAssignments(recordOrEmpty(normalized.sortieFormationAssignments), sortieUnitFilter);
|
||||||
normalized.sortieItemAssignments = normalizeCampaignSortieItemAssignments(recordOrEmpty(normalized.sortieItemAssignments), sortieUnitFilter);
|
normalized.sortieItemAssignments = normalizeCampaignSortieItemAssignments(recordOrEmpty(normalized.sortieItemAssignments), sortieUnitFilter);
|
||||||
normalized.reserveTrainingFocus = normalizeReserveTrainingFocusId(normalized.reserveTrainingFocus);
|
normalized.reserveTrainingFocus = normalizeReserveTrainingFocusId(normalized.reserveTrainingFocus);
|
||||||
|
normalized.reserveTrainingAssignments = normalizeCampaignReserveTrainingAssignments(recordOrEmpty(normalized.reserveTrainingAssignments), sortieUnitFilter);
|
||||||
normalized.battleHistory = normalizeBattleHistory(normalized.battleHistory);
|
normalized.battleHistory = normalizeBattleHistory(normalized.battleHistory);
|
||||||
if (sortieUnitFilter) {
|
if (sortieUnitFilter) {
|
||||||
normalized.battleHistory = filterBattleHistoryRosterReferences(
|
normalized.battleHistory = filterBattleHistoryRosterReferences(
|
||||||
@@ -1256,11 +1283,15 @@ export function normalizeCampaignSortieItemAssignments(assignments?: Record<stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
function normalizeReserveTrainingFocusId(focusId?: string): CampaignReserveTrainingFocusId {
|
function normalizeReserveTrainingFocusId(focusId?: string): CampaignReserveTrainingFocusId {
|
||||||
return campaignReserveTrainingFocusDefinitions.some((focus) => focus.id === focusId)
|
return isReserveTrainingFocusId(focusId)
|
||||||
? focusId as CampaignReserveTrainingFocusId
|
? focusId as CampaignReserveTrainingFocusId
|
||||||
: defaultCampaignReserveTrainingFocusId;
|
: defaultCampaignReserveTrainingFocusId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isReserveTrainingFocusId(value: unknown): value is CampaignReserveTrainingFocusId {
|
||||||
|
return typeof value === 'string' && campaignReserveTrainingFocusDefinitions.some((focus) => focus.id === value);
|
||||||
|
}
|
||||||
|
|
||||||
export function isCampaignStep(value: unknown): value is CampaignStep {
|
export function isCampaignStep(value: unknown): value is CampaignStep {
|
||||||
return typeof value === 'string' && campaignStepIds.has(value as CampaignStep);
|
return typeof value === 'string' && campaignStepIds.has(value as CampaignStep);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user