mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-05-11 05:05:07 -05:00
* Added Ability Chunks Required section in Build Analyzer * Renamed a variable to be more precisely correct * Added reference * Removed some extra Javadoc comments * Prettier fix * We now only render the AbilityChunksRequired section only if the main abilities array contains a value other than "UNKNOWN" * Improved React keys naming for performance reasons * Ability Chunks map is now converted to an array & sorted by value (descending) before it gets rendered as visual components * Fixed typing error * Moved logical function to a new file in the analyzer module called abilityChunksCalc.ts - Refactored for loop content to be cleaner - Removed & changed some comments * More for loop refactoring * We now pass the entire build into abilityChunksCalc.ts * Refactored map() to flatMap() so we avoid unknowns/null/undefined * Refactored code to process mainAbilities and subAbilities * Fixed subability list construction logic & typing in updateAbilityChunksMap() * Got my first unit test working * Added working unit tests, also changed sort order slightly * Added a "real" build for testing * Removed residual console.warn() call * Moved constants to abilityChunksCalc.ts * Ability chunk calculation is now correct for sub abilities * Uncommented tests & improved their descriptions * Rearranged expected output to match sorted order for clarity (even though it doesn't have to be) * Fixed Prettier error * Spacing * Moved comments around * More spacing * Prettier error on test file * Improved check in the tests * Added a second "real" build to tests for good measure * Added error message to empty array test * Updated comments again * More comments updated * Update test name * Ability Chunks section is now shown if we have at least one selected ability (handles edge case for primary slot-only abilities being the only chosen ability)
86 lines
3.1 KiB
TypeScript
86 lines
3.1 KiB
TypeScript
import { abilities } from "../in-game-lists";
|
|
import type {
|
|
AbilityWithUnknown,
|
|
BuildAbilitiesTupleWithUnknown,
|
|
} from "../in-game-lists/types";
|
|
import type { AbilityChunks } from "./types";
|
|
|
|
// Reference for Ability Chunks numbers: https://splatoonwiki.org/wiki/Ability_chunk#Splatoon_3
|
|
const MAIN_REQUIRED_ABILITY_CHUNKS_COUNT = 45;
|
|
const PRIMARY_SLOT_ONLY_REQUIRED_ABILITY_CHUNKS_COUNT = 15;
|
|
const SUB_REQUIRED_ABILITY_CHUNKS_COUNT = 10;
|
|
|
|
// From a given build, create a map of <Ability, number>, then return it as an Array after sorting by value, descending.
|
|
// The data structure describes the number of Ability chunks required for any given build.
|
|
export function getAbilityChunksMapAsArray(
|
|
build: BuildAbilitiesTupleWithUnknown
|
|
) {
|
|
const abilityChunksMap: AbilityChunks = new Map<AbilityWithUnknown, number>();
|
|
updateAbilityChunksMap(abilityChunksMap, build);
|
|
|
|
// Sort by value (number, descending) first, then sort by name (string, ascending)
|
|
return Array.from(abilityChunksMap).sort(
|
|
(a, b) => b[1] - a[1] || a[0].localeCompare(b[0])
|
|
);
|
|
}
|
|
|
|
function updateAbilityChunksMap(
|
|
abilityChunksMap: AbilityChunks,
|
|
build: BuildAbilitiesTupleWithUnknown
|
|
) {
|
|
for (const gear of build) {
|
|
// Handles the incremental amount of ability chunks required for the same ability for 1 piece of gear
|
|
const abilityChunksMapForGear = new Map<AbilityWithUnknown, number>();
|
|
|
|
for (const [index, selectedAbility] of gear.entries()) {
|
|
if (!selectedAbility) continue;
|
|
if (selectedAbility === "UNKNOWN") continue;
|
|
|
|
// Ability is in main slot
|
|
if (index === 0) {
|
|
const primarySlotOnlyAbilityRef = abilities.filter(
|
|
(a) =>
|
|
a.name === selectedAbility && a.abilityChunkTypesRequired.length > 0
|
|
);
|
|
|
|
// Extra processing is required for Main abilities that are primary slot-only abilities,
|
|
// as they are comprised of 3 stackable ability chunks at a lower ability chunk count than usual.
|
|
if (primarySlotOnlyAbilityRef.length === 1) {
|
|
const primaryAbility = primarySlotOnlyAbilityRef[0];
|
|
if (!primaryAbility) continue;
|
|
|
|
for (const ability of primaryAbility.abilityChunkTypesRequired) {
|
|
abilityChunksMap.set(
|
|
ability,
|
|
(abilityChunksMap.get(ability) ?? 0) +
|
|
PRIMARY_SLOT_ONLY_REQUIRED_ABILITY_CHUNKS_COUNT
|
|
);
|
|
}
|
|
} else {
|
|
abilityChunksMap.set(
|
|
selectedAbility,
|
|
(abilityChunksMap.get(selectedAbility) ?? 0) +
|
|
MAIN_REQUIRED_ABILITY_CHUNKS_COUNT
|
|
);
|
|
}
|
|
}
|
|
|
|
// Ability is in a sub slot
|
|
else {
|
|
// 1 Ability in sub slot = 10 chunks, 2 abilities = 20 chunks, 3 abilities = 30 chunks
|
|
abilityChunksMapForGear.set(
|
|
selectedAbility,
|
|
(abilityChunksMapForGear.get(selectedAbility) ?? 0) +
|
|
SUB_REQUIRED_ABILITY_CHUNKS_COUNT
|
|
);
|
|
|
|
abilityChunksMap.set(
|
|
selectedAbility,
|
|
(abilityChunksMap.get(selectedAbility) ?? 0) +
|
|
(abilityChunksMapForGear.get(selectedAbility) ?? 0)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|