diff --git a/app/modules/analyzer/utils.test.ts b/app/modules/analyzer/utils.test.ts new file mode 100644 index 000000000..99dac92c4 --- /dev/null +++ b/app/modules/analyzer/utils.test.ts @@ -0,0 +1,52 @@ +import { suite } from "uvu"; +import * as assert from "uvu/assert"; +import type { AbilityWithUnknown } from "../in-game-lists/types"; +import { buildToAbilityPoints } from "./utils"; + +const BuildToAbilityPoints = suite("buildToAbilityPoints()"); + +const EMPTY_ROW: [ + AbilityWithUnknown, + AbilityWithUnknown, + AbilityWithUnknown, + AbilityWithUnknown +] = ["UNKNOWN", "UNKNOWN", "UNKNOWN", "UNKNOWN"]; + +BuildToAbilityPoints("Empty build leads to empty AP map", () => { + const aps = buildToAbilityPoints([EMPTY_ROW, EMPTY_ROW, EMPTY_ROW]); + + assert.equal(aps.size, 0); +}); + +BuildToAbilityPoints("Calculates ability points", () => { + const aps = buildToAbilityPoints([ + ["SS", "SS", "RSU", "RSU"], + EMPTY_ROW, + EMPTY_ROW, + ]); + + assert.equal(aps.get("SS"), 13); + assert.equal(aps.get("RSU"), 6); +}); + +BuildToAbilityPoints("Handles ability doubler", () => { + const aps = buildToAbilityPoints([ + EMPTY_ROW, + ["AD", "SS", "UNKNOWN", "UNKNOWN"], + EMPTY_ROW, + ]); + + assert.equal(aps.get("SS"), 6); +}); + +BuildToAbilityPoints("Does not calculate AP for main only abilities", () => { + const aps = buildToAbilityPoints([ + ["LDE", "SS", "RSU", "RSU"], + EMPTY_ROW, + EMPTY_ROW, + ]); + + assert.not.ok(aps.has("LDE")); +}); + +BuildToAbilityPoints.run(); diff --git a/app/modules/analyzer/utils.ts b/app/modules/analyzer/utils.ts index 4dab84476..8a53d9d16 100644 --- a/app/modules/analyzer/utils.ts +++ b/app/modules/analyzer/utils.ts @@ -1,8 +1,10 @@ import type { Ability, BuildAbilitiesTupleWithUnknown } from "../in-game-lists"; +import { abilities } from "../in-game-lists"; import weaponParamsJson from "./weapon-params.json"; import abilityValuesJson from "./ability-values.json"; import type { AbilityPoints, MainWeaponParams, ParamsJson } from "./types"; import invariant from "tiny-invariant"; +import type { AbilityWithUnknown } from "../in-game-lists/types"; export function weaponParams(): ParamsJson { return weaponParamsJson as ParamsJson; @@ -12,18 +14,33 @@ export function buildToAbilityPoints(build: BuildAbilitiesTupleWithUnknown) { const result: AbilityPoints = new Map(); for (const abilityRow of build) { + let abilityDoublerActive = false; for (const [i, ability] of abilityRow.entries()) { - if (ability === "UNKNOWN") continue; + if (ability === "AD") { + abilityDoublerActive = true; + } + if (!isStackableAbility(ability)) { + continue; + } const aps = i === 0 ? 10 : 3; + const apsDoubled = aps * (abilityDoublerActive ? 2 : 1); - result.set(ability, (result.get(ability) ?? 0) + aps); + result.set(ability, (result.get(ability) ?? 0) + apsDoubled); } } return result; } +function isStackableAbility(ability: AbilityWithUnknown): ability is Ability { + if (ability === "UNKNOWN") return false; + const abilityObj = abilities.find((a) => a.name === ability); + invariant(abilityObj); + + return abilityObj.type === "STACKABLE"; +} + export function apFromMap({ abilityPoints, ability,