2022-02-13 18:35:15 +01:00
|
|
|
|
// SPDX-FileCopyrightText: 2022 Johannes Loher
|
|
|
|
|
//
|
|
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
|
|
2022-08-22 22:05:14 +02:00
|
|
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
|
|
2022-11-04 21:47:18 +01:00
|
|
|
|
import { calculateSpellPrice } from "../../../../src/documents/item/spell/calculate-spell-price";
|
2022-02-13 18:35:15 +01:00
|
|
|
|
|
2022-11-04 21:47:18 +01:00
|
|
|
|
import type { CooldownDuration, DS4SpellDataSourceData } from "../../../../src/documents/item/spell/spell-data-source";
|
2022-02-17 00:55:22 +01:00
|
|
|
|
|
2022-02-13 18:35:15 +01:00
|
|
|
|
const defaultData: DS4SpellDataSourceData = {
|
|
|
|
|
description: "",
|
|
|
|
|
equipped: false,
|
|
|
|
|
spellType: "spellcasting",
|
2022-11-04 21:08:23 +01:00
|
|
|
|
spellModifier: {
|
|
|
|
|
numerical: 0,
|
|
|
|
|
complex: "",
|
|
|
|
|
},
|
2022-11-09 03:02:27 +01:00
|
|
|
|
allowsDefense: false,
|
2022-11-04 21:08:23 +01:00
|
|
|
|
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,
|
|
|
|
|
},
|
2022-02-13 18:35:15 +01:00
|
|
|
|
maxDistance: {
|
|
|
|
|
value: "",
|
|
|
|
|
unit: "meter",
|
|
|
|
|
},
|
|
|
|
|
effectRadius: {
|
|
|
|
|
value: "",
|
|
|
|
|
unit: "meter",
|
|
|
|
|
},
|
|
|
|
|
duration: {
|
|
|
|
|
value: "",
|
|
|
|
|
unit: "custom",
|
|
|
|
|
},
|
2022-02-14 00:58:23 +01:00
|
|
|
|
cooldownDuration: "0r",
|
2022-02-13 18:35:15 +01:00
|
|
|
|
minimumLevels: {
|
|
|
|
|
healer: null,
|
|
|
|
|
wizard: null,
|
|
|
|
|
sorcerer: null,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
type TestCase = {
|
|
|
|
|
minimumLevel: number | null;
|
|
|
|
|
expected: number | null;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
type CombinedTestCase = {
|
|
|
|
|
minimumLevels: DS4SpellDataSourceData["minimumLevels"];
|
|
|
|
|
expected: number | null;
|
2022-08-22 22:05:14 +02:00
|
|
|
|
description: string;
|
2022-02-13 18:35:15 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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;
|
2022-08-22 22:05:14 +02:00
|
|
|
|
const minimumLevels = {
|
|
|
|
|
healer: healerTestCase.minimumLevel,
|
|
|
|
|
sorcerer: sorcererTestCase.minimumLevel,
|
|
|
|
|
wizard: wizardTestCase.minimumLevel,
|
|
|
|
|
};
|
|
|
|
|
const description = JSON.stringify(minimumLevels);
|
2022-02-13 18:35:15 +01:00
|
|
|
|
combinedTestCases.push({
|
2022-08-22 22:05:14 +02:00
|
|
|
|
minimumLevels,
|
2022-02-13 18:35:15 +01:00
|
|
|
|
expected,
|
2022-08-22 22:05:14 +02:00
|
|
|
|
description,
|
2022-02-13 18:35:15 +01:00
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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)) {
|
2022-08-22 22:05:14 +02:00
|
|
|
|
const minimumLevels = {
|
|
|
|
|
...defaultData.minimumLevels,
|
|
|
|
|
[spellCasterClass]: testCase.minimumLevel,
|
|
|
|
|
};
|
|
|
|
|
const description = JSON.stringify(minimumLevels);
|
2022-02-13 18:35:15 +01:00
|
|
|
|
combinedTestCases.push({
|
2022-08-22 22:05:14 +02:00
|
|
|
|
minimumLevels,
|
2022-02-13 18:35:15 +01:00
|
|
|
|
expected: testCase.expected,
|
2022-08-22 22:05:14 +02:00
|
|
|
|
description,
|
2022-02-13 18:35:15 +01:00
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return combinedTestCases;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
describe("calculateSpellPrice", () => {
|
2022-02-14 00:58:23 +01:00
|
|
|
|
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 },
|
2022-02-13 18:35:15 +01:00
|
|
|
|
];
|
|
|
|
|
|
2022-02-14 00:58:23 +01:00
|
|
|
|
describe.each(cooldownDurations)(
|
|
|
|
|
"with cooldown duration set to $cooldownDuration",
|
|
|
|
|
({ cooldownDuration, factor }) => {
|
|
|
|
|
const dataWithCooldownDuration = {
|
|
|
|
|
...defaultData,
|
|
|
|
|
cooldownDuration,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
it.each(buildCombinedTestCases())(
|
2022-08-22 22:05:14 +02:00
|
|
|
|
`returns ${factor} × $expected if the minimum leves are $description`,
|
2022-02-14 00:58:23 +01:00
|
|
|
|
({ minimumLevels, expected }) => {
|
|
|
|
|
// given
|
|
|
|
|
const data: DS4SpellDataSourceData = {
|
|
|
|
|
...dataWithCooldownDuration,
|
|
|
|
|
minimumLevels,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// when
|
|
|
|
|
const spellPrice = calculateSpellPrice(data);
|
|
|
|
|
|
|
|
|
|
// then
|
|
|
|
|
expect(spellPrice).toBe(expected !== null ? expected * factor : expected);
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
);
|
2022-02-13 18:35:15 +01:00
|
|
|
|
});
|