feat: add source-owned combat mechanic programs

This commit is contained in:
JB
2026-07-27 13:35:00 -04:00
parent 5a67b252aa
commit 4eb754e7c8
62 changed files with 14815 additions and 905 deletions
+31 -4
View File
@@ -9,6 +9,7 @@ describe('parseLoadoutDraft', () => {
const parsed = parseLoadoutDraft(draft);
expect(parsed).toEqual(draft);
expect(parsed?.calculationMode).toBe('maximum');
expect(parsed).not.toBe(draft);
parsed!.gear.Holster.level = 1;
expect(draft.gear.Holster.level).toBe(50);
@@ -22,20 +23,46 @@ describe('parseLoadoutDraft', () => {
expect(parseLoadoutDraft(retired)).toBeNull();
});
it('rejects a version 2 draft that does not contain exactly nine chips', () => {
it('rejects a version 4 draft that does not contain exactly nine chips', () => {
const draft = copyDraft();
draft.chips.pop();
expect(parseLoadoutDraft(draft)).toBeNull();
});
it('rejects a specialization missing a required current field', () => {
const draft = copyDraft() as unknown as { specialization: Record<string, unknown> };
delete draft.specialization.combatProfile;
it('rejects a draft missing the unified combat profile', () => {
const draft = copyDraft() as unknown as Record<string, unknown>;
delete draft.combatProfile;
expect(parseLoadoutDraft(draft)).toBeNull();
});
it('accepts a custom scenario and preserves its assumptions', () => {
const draft = copyDraft();
draft.calculationMode = 'custom';
draft.combatProfile.hitRate = 63;
draft.combatProfile.targetDistanceMeters = 28;
draft.conditionalEffects.relentless = {
mode: 'encounter',
inputs: {
alliesWithinRange: 3,
targetHasDebuff: true
}
};
expect(parseLoadoutDraft(draft)).toEqual(draft);
});
it('rejects drafts without the required calculation model', () => {
const missing = copyDraft() as unknown as Record<string, unknown>;
delete missing.calculationMode;
expect(parseLoadoutDraft(missing)).toBeNull();
const invalid = copyDraft() as unknown as Record<string, unknown>;
invalid.calculationMode = 'legacy';
expect(parseLoadoutDraft(invalid)).toBeNull();
});
it('rejects a specialization that carries retired unassigned chips', () => {
const draft = copyDraft() as unknown as { specialization: Record<string, unknown> };
draft.specialization.unassignedChips = [];