Johannes Loher
1e094691ff
This makes it so that the Talents “Verletzen” and “Verheerer” can sort of be automated. Compendium packs have been updated accordingly.
236 lines
8.1 KiB
TypeScript
236 lines
8.1 KiB
TypeScript
// SPDX-FileCopyrightText: 2022 Johannes Loher
|
||
//
|
||
// SPDX-License-Identifier: MIT
|
||
|
||
import { describe, expect, it } from "vitest";
|
||
|
||
import { calculateSpellPrice } from "../../../../src/documents/item/spell/calculate-spell-price";
|
||
|
||
import type { CooldownDuration, DS4SpellDataSourceData } from "../../../../src/documents/item/spell/spell-data-source";
|
||
|
||
const defaultData: DS4SpellDataSourceData = {
|
||
description: "",
|
||
equipped: false,
|
||
spellType: "spellcasting",
|
||
spellModifier: {
|
||
numerical: 0,
|
||
complex: "",
|
||
},
|
||
allowsDefense: false,
|
||
spellGroups: {
|
||
lightning: false,
|
||
earth: false,
|
||
water: false,
|
||
ice: false,
|
||
fire: false,
|
||
healing: false,
|
||
light: false,
|
||
air: false,
|
||
transport: false,
|
||
damage: false,
|
||
shadow: false,
|
||
protection: false,
|
||
mindAffecting: false,
|
||
demonology: false,
|
||
necromancy: false,
|
||
transmutation: false,
|
||
area: false,
|
||
},
|
||
maxDistance: {
|
||
value: "",
|
||
unit: "meter",
|
||
},
|
||
effectRadius: {
|
||
value: "",
|
||
unit: "meter",
|
||
},
|
||
duration: {
|
||
value: "",
|
||
unit: "custom",
|
||
},
|
||
cooldownDuration: "0r",
|
||
minimumLevels: {
|
||
healer: null,
|
||
wizard: null,
|
||
sorcerer: null,
|
||
},
|
||
};
|
||
|
||
type TestCase = {
|
||
minimumLevel: number | null;
|
||
expected: number | null;
|
||
};
|
||
|
||
type CombinedTestCase = {
|
||
minimumLevels: DS4SpellDataSourceData["minimumLevels"];
|
||
expected: number | null;
|
||
description: string;
|
||
};
|
||
|
||
const testCases: Record<keyof DS4SpellDataSourceData["minimumLevels"], TestCase[]> = {
|
||
healer: [
|
||
{ minimumLevel: null, expected: null },
|
||
{ minimumLevel: 1, expected: 10 },
|
||
{ minimumLevel: 2, expected: 45 },
|
||
{ minimumLevel: 3, expected: 80 },
|
||
{ minimumLevel: 4, expected: 115 },
|
||
{ minimumLevel: 5, expected: 150 },
|
||
{ minimumLevel: 6, expected: 185 },
|
||
{ minimumLevel: 7, expected: 220 },
|
||
{ minimumLevel: 8, expected: 255 },
|
||
{ minimumLevel: 9, expected: 290 },
|
||
{ minimumLevel: 10, expected: 325 },
|
||
{ minimumLevel: 11, expected: 360 },
|
||
{ minimumLevel: 12, expected: 395 },
|
||
{ minimumLevel: 13, expected: 430 },
|
||
{ minimumLevel: 14, expected: 465 },
|
||
{ minimumLevel: 15, expected: 500 },
|
||
{ minimumLevel: 16, expected: 535 },
|
||
{ minimumLevel: 17, expected: 570 },
|
||
{ minimumLevel: 18, expected: 605 },
|
||
{ minimumLevel: 19, expected: 640 },
|
||
{ minimumLevel: 20, expected: 675 },
|
||
],
|
||
sorcerer: [
|
||
{ minimumLevel: null, expected: null },
|
||
{ minimumLevel: 1, expected: 10 },
|
||
{ minimumLevel: 2, expected: 75 },
|
||
{ minimumLevel: 3, expected: 140 },
|
||
{ minimumLevel: 4, expected: 205 },
|
||
{ minimumLevel: 5, expected: 270 },
|
||
{ minimumLevel: 6, expected: 335 },
|
||
{ minimumLevel: 7, expected: 400 },
|
||
{ minimumLevel: 8, expected: 465 },
|
||
{ minimumLevel: 9, expected: 530 },
|
||
{ minimumLevel: 10, expected: 595 },
|
||
{ minimumLevel: 11, expected: 660 },
|
||
{ minimumLevel: 12, expected: 725 },
|
||
{ minimumLevel: 13, expected: 790 },
|
||
{ minimumLevel: 14, expected: 855 },
|
||
{ minimumLevel: 15, expected: 920 },
|
||
{ minimumLevel: 16, expected: 985 },
|
||
{ minimumLevel: 17, expected: 1050 },
|
||
{ minimumLevel: 18, expected: 1115 },
|
||
{ minimumLevel: 19, expected: 1180 },
|
||
{ minimumLevel: 20, expected: 1245 },
|
||
],
|
||
wizard: [
|
||
{ minimumLevel: null, expected: null },
|
||
{ minimumLevel: 1, expected: 10 },
|
||
{ minimumLevel: 2, expected: 60 },
|
||
{ minimumLevel: 3, expected: 110 },
|
||
{ minimumLevel: 4, expected: 160 },
|
||
{ minimumLevel: 5, expected: 210 },
|
||
{ minimumLevel: 6, expected: 260 },
|
||
{ minimumLevel: 7, expected: 310 },
|
||
{ minimumLevel: 8, expected: 360 },
|
||
{ minimumLevel: 9, expected: 410 },
|
||
{ minimumLevel: 10, expected: 460 },
|
||
{ minimumLevel: 11, expected: 510 },
|
||
{ minimumLevel: 12, expected: 560 },
|
||
{ minimumLevel: 13, expected: 610 },
|
||
{ minimumLevel: 14, expected: 660 },
|
||
{ minimumLevel: 15, expected: 710 },
|
||
{ minimumLevel: 16, expected: 760 },
|
||
{ minimumLevel: 17, expected: 810 },
|
||
{ minimumLevel: 18, expected: 860 },
|
||
{ minimumLevel: 19, expected: 910 },
|
||
{ minimumLevel: 20, expected: 960 },
|
||
],
|
||
};
|
||
|
||
function buildCombinedTestCases(): CombinedTestCase[] {
|
||
const combinedTestCases: CombinedTestCase[] = [];
|
||
|
||
// permutation test cases
|
||
const isRelevantPermutationTestCase = (t: TestCase) =>
|
||
([null, 1, 10, 20] as (number | null)[]).includes(t.minimumLevel);
|
||
|
||
for (const healerTestCase of testCases.healer.filter(isRelevantPermutationTestCase)) {
|
||
for (const sorcererTestCase of testCases.sorcerer.filter(isRelevantPermutationTestCase)) {
|
||
for (const wizardTestCase of testCases.wizard.filter(isRelevantPermutationTestCase)) {
|
||
const expected =
|
||
healerTestCase.expected !== null ||
|
||
sorcererTestCase.expected !== null ||
|
||
wizardTestCase.expected !== null
|
||
? Math.min(
|
||
healerTestCase.expected ?? Infinity,
|
||
sorcererTestCase.expected ?? Infinity,
|
||
wizardTestCase.expected ?? Infinity,
|
||
)
|
||
: null;
|
||
const minimumLevels = {
|
||
healer: healerTestCase.minimumLevel,
|
||
sorcerer: sorcererTestCase.minimumLevel,
|
||
wizard: wizardTestCase.minimumLevel,
|
||
};
|
||
const description = JSON.stringify(minimumLevels);
|
||
combinedTestCases.push({
|
||
minimumLevels,
|
||
expected,
|
||
description,
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
// single test cases
|
||
const isRelevantSingleTestCase = (t: TestCase) => t.minimumLevel !== null;
|
||
|
||
for (const spellCasterClass of ["healer", "sorcerer", "wizard"] as const) {
|
||
for (const testCase of testCases[spellCasterClass].filter(isRelevantSingleTestCase)) {
|
||
const minimumLevels = {
|
||
...defaultData.minimumLevels,
|
||
[spellCasterClass]: testCase.minimumLevel,
|
||
};
|
||
const description = JSON.stringify(minimumLevels);
|
||
combinedTestCases.push({
|
||
minimumLevels,
|
||
expected: testCase.expected,
|
||
description,
|
||
});
|
||
}
|
||
}
|
||
|
||
return combinedTestCases;
|
||
}
|
||
|
||
describe("calculateSpellPrice", () => {
|
||
const cooldownDurations: { cooldownDuration: CooldownDuration; factor: number }[] = [
|
||
{ cooldownDuration: "0r", factor: 1 },
|
||
{ cooldownDuration: "1r", factor: 1 },
|
||
{ cooldownDuration: "2r", factor: 1 },
|
||
{ cooldownDuration: "5r", factor: 1 },
|
||
{ cooldownDuration: "10r", factor: 1 },
|
||
{ cooldownDuration: "100r", factor: 1 },
|
||
{ cooldownDuration: "1d", factor: 2 },
|
||
{ cooldownDuration: "d20d", factor: 3 },
|
||
];
|
||
|
||
describe.each(cooldownDurations)(
|
||
"with cooldown duration set to $cooldownDuration",
|
||
({ cooldownDuration, factor }) => {
|
||
const dataWithCooldownDuration = {
|
||
...defaultData,
|
||
cooldownDuration,
|
||
};
|
||
|
||
it.each(buildCombinedTestCases())(
|
||
`returns ${factor} × $expected if the minimum leves are $description`,
|
||
({ minimumLevels, expected }) => {
|
||
// given
|
||
const data: DS4SpellDataSourceData = {
|
||
...dataWithCooldownDuration,
|
||
minimumLevels,
|
||
};
|
||
|
||
// when
|
||
const spellPrice = calculateSpellPrice(data);
|
||
|
||
// then
|
||
expect(spellPrice).toBe(expected !== null ? expected * factor : expected);
|
||
},
|
||
);
|
||
},
|
||
);
|
||
});
|