feat: unlock tactical commands from sortie orders
This commit is contained in:
@@ -360,6 +360,62 @@ try {
|
||||
normalizedStringBooleans.progress.every((entry) => entry.achieved === false),
|
||||
`Expected malformed string booleans to stay false instead of restoring an unearned reward claim: ${JSON.stringify(normalizedStringBooleans)}`
|
||||
);
|
||||
const normalizedOrderCommand = normalizeCampaignSortieOrderResultSnapshot({
|
||||
...normalizedEliteOrder,
|
||||
command: {
|
||||
source: 'sortie-order',
|
||||
role: 'support',
|
||||
actorId: ' liu-bei ',
|
||||
usedTurn: '3.9',
|
||||
effectPending: 'true',
|
||||
effectValue: '1000001',
|
||||
statusesCleared: '2.9',
|
||||
effectLost: true,
|
||||
extra: 'discard'
|
||||
}
|
||||
});
|
||||
assert(
|
||||
normalizedOrderCommand?.command?.source === 'sortie-order' &&
|
||||
normalizedOrderCommand.command.role === 'support' &&
|
||||
normalizedOrderCommand.command.actorId === 'liu-bei' &&
|
||||
normalizedOrderCommand.command.usedTurn === 3 &&
|
||||
normalizedOrderCommand.command.effectPending === false &&
|
||||
normalizedOrderCommand.command.effectValue === 999999 &&
|
||||
normalizedOrderCommand.command.statusesCleared === 2 &&
|
||||
normalizedOrderCommand.command.effectLost === true &&
|
||||
!Object.prototype.hasOwnProperty.call(normalizedOrderCommand.command, 'extra') &&
|
||||
!Object.prototype.hasOwnProperty.call(normalizedEliteOrder, 'command'),
|
||||
`Expected sortie-order commands to sanitize ids, numbers, booleans, and preserve command-less legacy snapshots: ${JSON.stringify(normalizedOrderCommand)}`
|
||||
);
|
||||
const invalidOrderCommands = [
|
||||
{ ...normalizedEliteOrder, command: { ...normalizedOrderCommand.command, source: 'morale' } },
|
||||
{ ...normalizedEliteOrder, command: { ...normalizedOrderCommand.command, role: 'reserve' } },
|
||||
{ ...normalizedEliteOrder, command: { ...normalizedOrderCommand.command, actorId: 'x'.repeat(97) } },
|
||||
{ ...normalizedEliteOrder, command: { ...normalizedOrderCommand.command, usedTurn: 0 } }
|
||||
];
|
||||
assert(
|
||||
invalidOrderCommands.every((snapshot) => (
|
||||
!Object.prototype.hasOwnProperty.call(normalizeCampaignSortieOrderResultSnapshot(snapshot) ?? {}, 'command')
|
||||
)),
|
||||
'Expected malformed optional command records to be discarded without losing the sortie-order result.'
|
||||
);
|
||||
const normalizedInitiativeCommand = normalizeCampaignSortieOrderResultSnapshot({
|
||||
...normalizedEliteOrder,
|
||||
command: {
|
||||
...normalizedOrderCommand.command,
|
||||
source: 'initiative',
|
||||
role: 'flank',
|
||||
effectPending: true,
|
||||
effectLost: 'false'
|
||||
}
|
||||
})?.command;
|
||||
assert(
|
||||
normalizedInitiativeCommand?.source === 'initiative' &&
|
||||
normalizedInitiativeCommand.role === 'flank' &&
|
||||
normalizedInitiativeCommand.effectPending === true &&
|
||||
normalizedInitiativeCommand.effectLost === false,
|
||||
`Expected explicit initiative command sources and strict command booleans to normalize: ${JSON.stringify(normalizedInitiativeCommand)}`
|
||||
);
|
||||
|
||||
storage.clear();
|
||||
storage.set(
|
||||
@@ -900,7 +956,17 @@ try {
|
||||
{ id: 'victory', achieved: true, value: 1, target: 1 },
|
||||
{ id: 'elite-grade', achieved: true, value: 88, target: 75 },
|
||||
{ id: 'elite-survival', achieved: true, value: 1, target: 1 }
|
||||
]
|
||||
],
|
||||
command: {
|
||||
source: 'sortie-order',
|
||||
role: 'front',
|
||||
actorId: performanceUnit.id,
|
||||
usedTurn: 3,
|
||||
effectPending: false,
|
||||
effectValue: 8,
|
||||
statusesCleared: 0,
|
||||
effectLost: false
|
||||
}
|
||||
};
|
||||
const successfulMobileOrder = {
|
||||
version: 1,
|
||||
@@ -964,7 +1030,9 @@ try {
|
||||
firstOrderSettlement.claimedSortieOrderRewardIds[0] === `${firstScenario.id}:elite` &&
|
||||
firstOrderSettlement.sortieOrderSelection === undefined &&
|
||||
firstOrderSettlement.firstBattleReport?.sortieOrder?.rewardGranted === true &&
|
||||
firstOrderSettlement.firstBattleReport.sortieOrder.command?.source === 'sortie-order' &&
|
||||
firstOrderSettlement.battleHistory[firstScenario.id]?.sortieOrder?.rewardGranted === true &&
|
||||
firstOrderSettlement.battleHistory[firstScenario.id].sortieOrder.command?.effectValue === 8 &&
|
||||
firstOrderSettlement.sortieOrderHistory[firstScenario.id]?.elite?.rewardGranted === true,
|
||||
`Expected the first battle/order success to grant its independent reward once, persist every snapshot, and clear the matching selection: ${JSON.stringify(firstOrderSettlement)}`
|
||||
);
|
||||
@@ -972,23 +1040,55 @@ try {
|
||||
assert(
|
||||
reloadedFirstOrderSettlement.firstBattleReport?.sortieOrder?.achieved === true &&
|
||||
reloadedFirstOrderSettlement.firstBattleReport.sortieOrder.rewardGranted === true &&
|
||||
reloadedFirstOrderSettlement.firstBattleReport.sortieOrder.command?.actorId === performanceUnit.id &&
|
||||
reloadedFirstOrderSettlement.battleHistory[firstScenario.id]?.sortieOrder?.achieved === true &&
|
||||
reloadedFirstOrderSettlement.battleHistory[firstScenario.id].turnNumber === orderBattleReport.turnNumber &&
|
||||
reloadedFirstOrderSettlement.battleHistory[firstScenario.id].sortieOrder.rewardGranted === true &&
|
||||
reloadedFirstOrderSettlement.battleHistory[firstScenario.id].sortieOrder.command?.effectValue === 8 &&
|
||||
reloadedFirstOrderSettlement.sortieOrderHistory[firstScenario.id]?.elite?.achieved === true &&
|
||||
reloadedFirstOrderSettlement.sortieOrderHistory[firstScenario.id].elite.command?.source === 'sortie-order' &&
|
||||
reloadedFirstOrderSettlement.claimedSortieOrderRewardIds.includes(`${firstScenario.id}:elite`),
|
||||
`Expected report, settlement, independent history, and reward claim to survive save normalization together: ${JSON.stringify(reloadedFirstOrderSettlement)}`
|
||||
);
|
||||
|
||||
const validContextualCommandStateRaw = storage.get(campaignStorageKey);
|
||||
assert(validContextualCommandStateRaw, 'Expected a persisted command state for contextual corruption checks.');
|
||||
const corruptedContextualCommandState = JSON.parse(validContextualCommandStateRaw);
|
||||
corruptedContextualCommandState.firstBattleReport.sortieOrder.command.actorId = 'ghost-unit';
|
||||
corruptedContextualCommandState.battleHistory[firstScenario.id].sortieOrder.command.role = 'support';
|
||||
corruptedContextualCommandState.sortieOrderHistory[firstScenario.id].elite.command.usedTurn = orderBattleReport.turnNumber + 1;
|
||||
storage.set(campaignStorageKey, JSON.stringify(corruptedContextualCommandState));
|
||||
const normalizedContextualCommandState = loadCampaignState();
|
||||
assert(
|
||||
normalizedContextualCommandState.firstBattleReport?.sortieOrder?.command === undefined &&
|
||||
normalizedContextualCommandState.battleHistory[firstScenario.id]?.sortieOrder?.command === undefined &&
|
||||
normalizedContextualCommandState.sortieOrderHistory[firstScenario.id]?.elite?.command === undefined &&
|
||||
normalizedContextualCommandState.firstBattleReport?.sortieOrder?.achieved === true &&
|
||||
normalizedContextualCommandState.battleHistory[firstScenario.id]?.sortieOrder?.achieved === true &&
|
||||
normalizedContextualCommandState.sortieOrderHistory[firstScenario.id]?.elite?.achieved === true,
|
||||
`Expected contextual normalization to remove ghost actors, role mismatches, and future command turns without dropping their order results: ${JSON.stringify(normalizedContextualCommandState)}`
|
||||
);
|
||||
storage.set(campaignStorageKey, validContextualCommandStateRaw);
|
||||
loadCampaignState();
|
||||
|
||||
orderBattleReport.sortieOrder.progress[0].achieved = false;
|
||||
orderBattleReport.sortieOrder.command.effectValue = 999;
|
||||
firstOrderCanonicalReport.sortieOrder.progress[1].achieved = false;
|
||||
firstOrderCanonicalReport.sortieOrder.command.actorId = 'ghost-unit';
|
||||
const deepCopyOrderState = getCampaignState();
|
||||
const detachedOrderState = getCampaignState();
|
||||
detachedOrderState.firstBattleReport.sortieOrder.progress[2].achieved = false;
|
||||
detachedOrderState.firstBattleReport.sortieOrder.command.effectLost = true;
|
||||
assert(
|
||||
deepCopyOrderState.firstBattleReport?.sortieOrder?.progress.every((entry) => entry.achieved) &&
|
||||
deepCopyOrderState.firstBattleReport.sortieOrder.command?.effectValue === 8 &&
|
||||
deepCopyOrderState.firstBattleReport.sortieOrder.command.actorId === performanceUnit.id &&
|
||||
deepCopyOrderState.battleHistory[firstScenario.id]?.sortieOrder?.progress.every((entry) => entry.achieved) &&
|
||||
deepCopyOrderState.battleHistory[firstScenario.id].sortieOrder.command?.effectLost === false &&
|
||||
deepCopyOrderState.sortieOrderHistory[firstScenario.id]?.elite?.progress.every((entry) => entry.achieved) &&
|
||||
deepCopyOrderState.sortieOrderHistory[firstScenario.id].elite.command?.effectValue === 8 &&
|
||||
detachedOrderState.battleHistory[firstScenario.id]?.sortieOrder?.progress[2].achieved === true &&
|
||||
detachedOrderState.battleHistory[firstScenario.id].sortieOrder.command?.effectLost === false &&
|
||||
getCampaignState().firstBattleReport?.sortieOrder?.progress[2].achieved === true,
|
||||
`Expected input, returned report, report/history, and returned campaign sortie-order progress to be deeply detached: ${JSON.stringify(deepCopyOrderState)}`
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user