);
}
const canPlaceAbilityAtSlot = (
rowIndex: number,
abilityIndex: number,
ability?: (typeof abilities)[number]
) => {
if (!ability) {
return false;
}
const legalGearTypeForMain =
rowIndex === 0
? "HEAD_MAIN_ONLY"
: rowIndex === 1
? "CLOTHES_MAIN_ONLY"
: "SHOES_MAIN_ONLY";
const isMainSlot = abilityIndex === 0;
if (
!["STACKABLE", legalGearTypeForMain].includes(ability.type) &&
isMainSlot
) {
// Can't put this type of gear in main slot
return false;
}
if (!isMainSlot && ability.type !== "STACKABLE") {
// Can't put main slot only gear to sub slots
return false;
}
return true;
};
function addAbility({
oldAbilities,
ability,
atRowIndex,
atAbilityIndex,
}: {
oldAbilities: BuildAbilitiesTupleWithUnknown;
ability: (typeof abilities)[number];
atRowIndex?: number;
atAbilityIndex?: number;
}): BuildAbilitiesTupleWithUnknown {
const abilitiesClone = JSON.parse(
JSON.stringify(oldAbilities)
) as BuildAbilitiesTupleWithUnknown;
if (atRowIndex !== undefined && atAbilityIndex !== undefined) {
// Attempt to place the ability at a specific slot since we
// were given an atRowIndex and atAbilityIndex
if (canPlaceAbilityAtSlot(atRowIndex, atAbilityIndex, ability)) {
// Assign this ability to the slot
abilitiesClone[atRowIndex]![atAbilityIndex] = ability.name;
}
} else {
// Loop through all slots and attempt to place this ability
// in the first empty one
for (const [rowIndex, row] of abilitiesClone.entries()) {
for (const [abilityIndex, oldAbility] of row.entries()) {
if (oldAbility !== "UNKNOWN") {
// Skip any filled slots in this loop until we arrive at an empty one.
continue;
}
if (!canPlaceAbilityAtSlot(rowIndex, abilityIndex, ability)) {
// This ability isn't valid for this slot
continue;
}
// Assign this ability to the slot
abilitiesClone[rowIndex]![abilityIndex] = ability.name;
return abilitiesClone;
}
}
}
// no-op if no available slots
return abilitiesClone;
}