diff --git a/src/damage.ts b/src/damage.ts new file mode 100644 index 0000000000..66439c5847 --- /dev/null +++ b/src/damage.ts @@ -0,0 +1,67 @@ +import { Element } from "kolmafia"; +import { $elements } from "./template-string.js"; +import { get as getModifier } from "./modifier.js"; +import { capitalize, sum } from "./utils.js"; + +export const SUPER_EFFECTIVE_CHART = Object.freeze({ + hot: $elements`Spooky, Cold`, + spooky: $elements`Cold, Sleaze`, + cold: $elements`Sleaze, Stench`, + sleaze: $elements`Stench, Hot`, + stench: $elements`Hot, Spooky`, +} as const); + +function isConventionalElement( + name: string, +): name is keyof typeof SUPER_EFFECTIVE_CHART { + return name in SUPER_EFFECTIVE_CHART; +} + +/** + * Determines if one type deals super effective damage against the other + * @param element The type of the damage + * @param against The type of the monster + * @returns Whether `element` is super effective against `against` + */ +export function isSuperEffective(element: Element, against: Element): boolean { + const elementName = element.toString(); + + return ( + isConventionalElement(elementName) && + SUPER_EFFECTIVE_CHART[elementName].includes(against) + ); +} + +/** + * Determines the amount of expected elemental damage, taking into account immunity and super effectiveness. + * @param damage The base amount of damage + * @param damageType The elemental type of the damage + * @param monsterType The elemental type of the target + * @returns The expected amount of damage the target would take + */ +export function elementalDamage( + damage: number, + damageType: Element, + monsterType: Element, +): number { + if (damage === 0) return 0; + if (damageType === monsterType) return 1; + if (isSuperEffective(damageType, monsterType)) return 2 * damage; + return damage; +} + +export const TRADITIONAL_ELEMENTS = Object.freeze( + $elements`cold, hot, sleaze, spooky, stench`, +); + +/** + * @returns The total amount of elemental damage you have from all sources + */ +export function totalElementalDamage(): number { + return sum( + TRADITIONAL_ELEMENTS.map((el) => el.toString()).filter( + isConventionalElement, + ), + (el) => getModifier(`${capitalize(el)} Damage`), + ); +} diff --git a/src/utils.ts b/src/utils.ts index 6d71e56d1d..2ba5c591f2 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -410,6 +410,9 @@ export type Range = Exclude< Enumerate >; +export const capitalize = (str: T): Capitalize => + (str.length ? `${str[0].toUpperCase()}${str.slice(1)}` : "") as Capitalize; + /** * Translate mafia's multi-dimensional array prefs into a multi-dimensional array * @param prop The property (or whatever) to process; not the property NAME, the value itself @@ -436,3 +439,4 @@ export function multiSplit( mappers.map((func, index) => func(tup[index])), ) as T[]; } +