2025-05-28 15:36:51 -07:00

161 lines
7.9 KiB
JavaScript

import { TypeScriptUtils } from '../../utils';
import { DataOriginTypeEnum } from '../DataOrigin';
import { DurationAccessors, DurationRenderers } from '../Duration';
import { FormatUtils } from '../Format';
import { LimitedUseAccessors, LimitedUseDerivers, LimitedUseRenderers } from '../LimitedUse';
import { NoteGenerators } from '../Note';
import { DisplayIntentionEnum } from '../Note';
import { RuleDataUtils } from '../RuleData';
import { getAtHigherLevels, getComponents, getConcentration, getDuration, getLimitedUse, getNotes, getRange, getDataOriginType, isCantrip, getScaleType, } from './accessors';
import { SpellDurationTypeEnum } from './constants';
import { getConsumedLimitedUse, getSpellScaledAtHigher, isSpellScaledByCastLevel } from './derivers';
/**
*
* @param spell
* @param castLevel
* @param characterLevel
*/
export function getScaledNoteComponent(spell, castLevel, characterLevel) {
const atHigherLevels = getAtHigherLevels(spell);
if (!atHigherLevels) {
return null;
}
const scaleType = getScaleType(spell);
const additionalAttacks = atHigherLevels.additionalAttacks === null ? [] : atHigherLevels.additionalAttacks;
const additionalTargets = atHigherLevels.additionalTargets === null ? [] : atHigherLevels.additionalTargets;
const areaOfEffect = atHigherLevels.areaOfEffect === null ? [] : atHigherLevels.areaOfEffect;
const duration = atHigherLevels.duration === null ? [] : atHigherLevels.duration;
const creatures = atHigherLevels.creatures === null ? [] : atHigherLevels.creatures;
const special = atHigherLevels.special === null ? [] : atHigherLevels.special;
if (!additionalAttacks.length &&
!additionalTargets.length &&
!areaOfEffect.length &&
!duration.length &&
!creatures.length &&
!special.length) {
return null;
}
let isScaled = false;
isScaled = isScaled || isSpellScaledByCastLevel(spell, getScaleType(spell), additionalAttacks, castLevel);
isScaled = isScaled || isSpellScaledByCastLevel(spell, getScaleType(spell), additionalTargets, castLevel);
isScaled = isScaled || isSpellScaledByCastLevel(spell, getScaleType(spell), areaOfEffect, castLevel);
isScaled = isScaled || isSpellScaledByCastLevel(spell, getScaleType(spell), duration, castLevel);
isScaled = isScaled || isSpellScaledByCastLevel(spell, getScaleType(spell), creatures, castLevel);
isScaled = isScaled || isSpellScaledByCastLevel(spell, getScaleType(spell), special, castLevel);
let higherDisplay;
const additionalAttackInfo = getSpellScaledAtHigher(spell, scaleType, additionalAttacks, characterLevel, castLevel);
if (additionalAttackInfo) {
const totalCount = additionalAttackInfo.totalCount;
const additionalAttackCount = isCantrip(spell) ? totalCount + 1 : totalCount;
const additionalAttackLabel = isCantrip(spell) ? '' : '+';
higherDisplay = `Count: ${additionalAttackLabel}${additionalAttackCount}`;
}
const additionalTargetInfo = getSpellScaledAtHigher(spell, scaleType, additionalTargets, characterLevel, castLevel);
if (!higherDisplay && additionalTargetInfo) {
higherDisplay = `Targets: +${additionalTargetInfo.targets}`;
}
const aoeInfo = getSpellScaledAtHigher(spell, scaleType, areaOfEffect, characterLevel, castLevel);
if (!higherDisplay && aoeInfo) {
const extArea = aoeInfo.extendedAoe;
if (extArea) {
higherDisplay = `Ext. Area: ${FormatUtils.renderDistance(extArea)}`;
}
}
const durationInfo = getSpellScaledAtHigher(spell, scaleType, duration, characterLevel, castLevel);
if (!higherDisplay && durationInfo) {
higherDisplay = `Ext. D: ${durationInfo.description}`;
}
const creatureInfo = getSpellScaledAtHigher(spell, scaleType, creatures, characterLevel, castLevel);
if (!higherDisplay && creatureInfo) {
higherDisplay = `Creatures: ${creatureInfo.description}`;
}
const specialInfo = getSpellScaledAtHigher(spell, scaleType, special, characterLevel, castLevel);
if (!higherDisplay && specialInfo) {
higherDisplay = `Special: ${specialInfo.description}`;
}
if (!higherDisplay) {
return null;
}
const displayIntention = isScaled ? DisplayIntentionEnum.SCALED : null;
return NoteGenerators.createPlainText(higherDisplay, displayIntention);
}
/**
*
* @param spell
*/
function getUserNoteComponent(spell) {
const notes = getNotes(spell);
if (notes) {
return NoteGenerators.createPlainText(notes);
}
return null;
}
/**
*
* @param spell
* @param castLevel
* @param characterLevel
* @param abilityLookup
* @param ruleData
* @param scaledAmount
* @param proficiencyBonus
*/
export function getNoteComponents(spell, castLevel, characterLevel, abilityLookup, ruleData, scaledAmount = 0, proficiencyBonus) {
const notes = [];
const range = getRange(spell);
const components = getComponents(spell);
const duration = getDuration(spell);
const limitedUse = getLimitedUse(spell);
if (limitedUse) {
const consumedAmount = getConsumedLimitedUse(spell, scaledAmount);
const maxUses = LimitedUseDerivers.deriveMaxUses(limitedUse, abilityLookup, ruleData, proficiencyBonus);
const numberUsed = LimitedUseAccessors.getNumberUsed(limitedUse);
const numberRemaining = maxUses - numberUsed;
if (getDataOriginType(spell) === DataOriginTypeEnum.ITEM) {
if (consumedAmount !== null) {
if (maxUses === 1) {
const isUsed = numberUsed === 1;
notes.push(NoteGenerators.createPlainText(`1 Charge${isUsed ? ' (Used)' : ''}`));
}
else {
notes.push(NoteGenerators.createPlainText(`${consumedAmount} Charge${consumedAmount !== 1 ? 's' : ''} (${numberRemaining}/${maxUses})`));
}
}
}
else if (maxUses === 1) {
const resetType = LimitedUseAccessors.getResetType(limitedUse);
const resetTypeAbbr = LimitedUseRenderers.renderLimitedUseResetAbbreviation(resetType);
const isUsed = numberUsed === 1;
const limitedUseResetTypeName = resetType === null ? '' : RuleDataUtils.getLimitedUseResetTypeName(resetType, ruleData);
notes.push(NoteGenerators.createTooltip(`1/${resetTypeAbbr}${isUsed ? ' (Used)' : ''}`, `Once per ${limitedUseResetTypeName}${isUsed ? ' (Used)' : ''}`, {
dynamicTitle: true,
}));
}
else {
notes.push(NoteGenerators.createPlainText(`${consumedAmount} Use${consumedAmount !== 1 ? 's' : ''} (${numberRemaining}/${maxUses})`));
}
}
const scaledNotesNode = getScaledNoteComponent(spell, castLevel, characterLevel);
if (scaledNotesNode) {
notes.push(scaledNotesNode);
}
if (duration && DurationAccessors.getType(duration) !== SpellDurationTypeEnum.INSTANTANEOUS) {
notes.push(NoteGenerators.createTooltip(`D: ${DurationRenderers.renderDurationAbbreviation(duration)}`, `${DurationRenderers.renderDuration(duration, getConcentration(spell))} Duration`));
}
if (range && range.aoeValue !== null && range.aoeType !== null) {
notes.push(NoteGenerators.createGroup([NoteGenerators.createDistance(range.aoeValue), NoteGenerators.createAoeIcon(range.aoeType)], ''));
}
if (components) {
const componentNoteParts = [];
components.forEach((componentId, idx) => {
const componentInfo = RuleDataUtils.getSpellComponentInfo(componentId, ruleData);
if (componentInfo && componentInfo.shortName !== null) {
componentNoteParts.push(NoteGenerators.createTooltip(componentInfo.shortName, componentInfo.name));
}
});
notes.push(NoteGenerators.createGroup(componentNoteParts, '/'));
}
notes.push(getUserNoteComponent(spell));
return notes.filter(TypeScriptUtils.isNotNullOrUndefined);
}