Grabbed dndbeyond's source code

```
~/go/bin/sourcemapper -output ddb -jsurl https://media.dndbeyond.com/character-app/static/js/main.90aa78c5.js
```
This commit is contained in:
2025-05-28 11:50:03 -07:00
commit 8df9031d27
3592 changed files with 319051 additions and 0 deletions
@@ -0,0 +1,832 @@
import { ActivationAccessors } from '../Activation';
/**
*
* @param spell
*/
export function getDefinition(spell) {
return spell.definition;
}
/**
*
* @param spell
*/
export function getUniqueKey(spell) {
return spell.uniqueKey;
}
/**
*
* @param spell
*/
export function getDefinitionName(spell) {
var _a, _b;
return (_b = (_a = spell.definition) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : null;
}
/**
*
* @param spell
*/
export function getAttackType(spell) {
return getDefinitionAttackType(spell);
}
/**
*
* @param spell
*/
export function getDefinitionAttackType(spell) {
var _a, _b;
return (_b = (_a = spell.definition) === null || _a === void 0 ? void 0 : _a.attackType) !== null && _b !== void 0 ? _b : null;
}
/**
*
* @param spell
*/
export function getId(spell) {
var _a, _b;
return (_b = (_a = spell.definition) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : -1;
}
/**
*
* @param spell - The spell in question
*/
export function getSpellGroups(spell) {
var _a;
return ((_a = getDefinition(spell)) === null || _a === void 0 ? void 0 : _a.spellGroups) || [];
}
/**
*
* @param spell
*/
export function getDefinitionRange(spell) {
var _a, _b;
return (_b = (_a = spell.definition) === null || _a === void 0 ? void 0 : _a.range) !== null && _b !== void 0 ? _b : null;
}
/**
*
* @param spell
*/
export function getDefinitionRangeArea(spell) {
var _a, _b;
return (_b = (_a = spell.definition) === null || _a === void 0 ? void 0 : _a.rangeArea) !== null && _b !== void 0 ? _b : null;
}
/**
*
* @param spell
*/
export function getMappingId(spell) {
return spell.id;
}
/**
*
* @param spell
*/
export function getMappingEntityTypeId(spell) {
return spell.entityTypeId;
}
/**
*
* @param spell
*/
export function getLimitedUse(spell) {
return spell.limitedUse;
}
/**
*
* @param spell
*/
export function getActivation(spell) {
return spell.activation;
}
/**
*
* @param spell
*/
export function getDefinitionActivation(spell) {
var _a, _b;
return (_b = (_a = spell.definition) === null || _a === void 0 ? void 0 : _a.activation) !== null && _b !== void 0 ? _b : null;
}
/**
*
* @param spell
*/
export function getActivationTime(spell) {
return ActivationAccessors.getTime(getActivation(spell));
}
/**
*
* @param spell
*/
export function getActivationType(spell) {
return ActivationAccessors.getType(getActivation(spell));
}
/**
*
* @param spell
*/
export function getCastingTimeDescription(spell) {
return getDefinitionCastingTimeDescription(spell);
}
/**
*
* @param spell
*/
export function getDefinitionCastingTimeDescription(spell) {
var _a, _b;
return (_b = (_a = spell.definition) === null || _a === void 0 ? void 0 : _a.castingTimeDescription) !== null && _b !== void 0 ? _b : null;
}
/**
*
* @param spell
*/
export function getTags(spell) {
return getDefinitionTags(spell);
}
/**
*
* @param spell
*/
export function getDefinitionTags(spell) {
var _a, _b;
return (_b = (_a = spell.definition) === null || _a === void 0 ? void 0 : _a.tags) !== null && _b !== void 0 ? _b : [];
}
/**
*
* @param spell
*/
export function getDefinitionTempHpDice(spell) {
var _a, _b;
return (_b = (_a = spell.definition) === null || _a === void 0 ? void 0 : _a.tempHpDice) !== null && _b !== void 0 ? _b : [];
}
/**
*
* @param spell
*/
export function getConditions(spell) {
return getDefinitionConditions(spell);
}
/**
*
* @param spell
*/
export function getDefinitionConditions(spell) {
var _a, _b;
return (_b = (_a = spell.definition) === null || _a === void 0 ? void 0 : _a.conditions) !== null && _b !== void 0 ? _b : [];
}
/**
*
* @param spell
*/
export function getDefinitionDamageEffect(spell) {
var _a, _b;
return (_b = (_a = spell.definition) === null || _a === void 0 ? void 0 : _a.damageEffect) !== null && _b !== void 0 ? _b : null;
}
/**
*
* @param spell
*/
export function getLevel(spell) {
return getDefinitionLevel(spell);
}
/**
*
* @param spell
*/
export function getDefinitionLevel(spell) {
var _a, _b;
return (_b = (_a = spell.definition) === null || _a === void 0 ? void 0 : _a.level) !== null && _b !== void 0 ? _b : 0;
}
/**
*
* @param spell
*/
export function getSchool(spell) {
return getDefinitionSchool(spell);
}
/**
*
* @param spell
*/
export function getDefinitionSchool(spell) {
var _a, _b;
return (_b = (_a = spell.definition) === null || _a === void 0 ? void 0 : _a.school) !== null && _b !== void 0 ? _b : null;
}
/**
*
* @param spell
*/
export function getDefinitionSnippet(spell) {
var _a, _b;
return (_b = (_a = spell.definition) === null || _a === void 0 ? void 0 : _a.snippet) !== null && _b !== void 0 ? _b : null;
}
/**
*
* @param spell
*/
export function getDescription(spell) {
return getDefinitionDescription(spell);
}
/**
*
* @param spell
*/
export function getDefinitionDescription(spell) {
var _a, _b;
return (_b = (_a = spell.definition) === null || _a === void 0 ? void 0 : _a.description) !== null && _b !== void 0 ? _b : null;
}
/**
*
* @param spell
*/
export function getDefinitionIsHomebrew(spell) {
var _a, _b;
return (_b = (_a = spell.definition) === null || _a === void 0 ? void 0 : _a.isHomebrew) !== null && _b !== void 0 ? _b : false;
}
/**
*
* @param spell
*/
export function getVersion(spell) {
return getDefinitionVersion(spell);
}
/**
*
* @param spell
*/
export function getDefinitionVersion(spell) {
var _a, _b;
return (_b = (_a = spell.definition) === null || _a === void 0 ? void 0 : _a.version) !== null && _b !== void 0 ? _b : null;
}
/**
*
* @param spell
*/
export function getUsesSpellSlot(spell) {
if (isCantrip(spell)) {
return false;
}
return spell.usesSpellSlot;
}
/**
*
* @param spell
*/
export function isDisplayAsAttack(spell) {
return spell.displayAsAttack;
}
/**
*
* @param spell
*/
export function isAlwaysPrepared(spell) {
return spell.alwaysPrepared;
}
/**
*
* @param spell
*/
export function isCantrip(spell) {
return getLevel(spell) === 0;
}
/**
*
* @param spell
*/
export function isAttack(spell) {
return getDefinitionRequiresAttackRoll(spell);
}
/**
*
* @param spell
*/
export function getDefinitionRequiresAttackRoll(spell) {
var _a, _b;
return (_b = (_a = spell.definition) === null || _a === void 0 ? void 0 : _a.requiresAttackRoll) !== null && _b !== void 0 ? _b : false;
}
/**
*
* @param spell
*/
export function asPartOfWeaponAttack(spell) {
return getDefinitionAsPartOfWeaponAttack(spell);
}
/**
*
* @param spell
*/
export function getDefinitionAsPartOfWeaponAttack(spell) {
var _a, _b;
return (_b = (_a = spell.definition) === null || _a === void 0 ? void 0 : _a.asPartOfWeaponAttack) !== null && _b !== void 0 ? _b : false;
}
/**
*
* @param spell
*/
export function getToHit(spell) {
return spell.toHit;
}
/**
*
* @param spell
*/
export function canRemove(spell) {
return spell.canRemove;
}
/**
*
* @param spell
*/
export function canPrepare(spell) {
return spell.canPrepare;
}
/**
*
* @param spell
*/
export function canAdd(spell) {
return spell.canAdd;
}
/**
*
* @param spell
*/
export function getModifiers(spell) {
return spell.modifiers;
}
/**
*
* @param spell
*/
export function getDefinitionModifiers(spell) {
var _a, _b;
return (_b = (_a = spell.definition) === null || _a === void 0 ? void 0 : _a.modifiers) !== null && _b !== void 0 ? _b : [];
}
/**
*
* @param spell
*/
export function getCastAtLevel(spell) {
return spell.castAtLevel;
}
/**
*
* @param spell
*/
export function getCastOnlyAsRitual(spell) {
return spell.castOnlyAsRitual;
}
/**
*
* @param spell
*/
export function getRitualCastingType(spell) {
return spell.ritualCastingType;
}
/**
*
* @param spell
*/
export function getBaseLevelAtWill(spell) {
return spell.baseLevelAtWill;
}
/**
*
* @param spell
*/
export function getConcentration(spell) {
return getDefinitionConcentration(spell);
}
/**
*
* @param spell
*/
export function getDefinitionConcentration(spell) {
var _a, _b;
return (_b = (_a = spell.definition) === null || _a === void 0 ? void 0 : _a.concentration) !== null && _b !== void 0 ? _b : false;
}
/**
*
* @param spell
*/
export function getRitual(spell) {
return getDefinitionRitual(spell);
}
/**
*
* @param spell
*/
export function getDefinitionRitual(spell) {
var _a, _b;
return (_b = (_a = spell.definition) === null || _a === void 0 ? void 0 : _a.ritual) !== null && _b !== void 0 ? _b : false;
}
/**
*
* @param spell
*/
export function isCustomized(spell) {
return spell.isCustomized;
}
/**
*
* @param spell
*/
export function getDuration(spell) {
return getDefinitionDuration(spell);
}
/**
*
* @param spell
*/
export function getDefinitionDuration(spell) {
var _a, _b;
return (_b = (_a = spell.definition) === null || _a === void 0 ? void 0 : _a.duration) !== null && _b !== void 0 ? _b : null;
}
/**
*
* @param spell
*/
export function getDefinitionHealing(spell) {
var _a, _b;
return (_b = (_a = spell.definition) === null || _a === void 0 ? void 0 : _a.healing) !== null && _b !== void 0 ? _b : null;
}
/**
*
* @param spell
*/
export function getDefinitionHealingDice(spell) {
var _a, _b;
return (_b = (_a = spell.definition) === null || _a === void 0 ? void 0 : _a.healingDice) !== null && _b !== void 0 ? _b : [];
}
/**
*
* @param spell
*/
export function getComponents(spell) {
return getDefinitionComponents(spell);
}
/**
*
* @param spell
*/
export function getDefinitionComponents(spell) {
var _a, _b;
return (_b = (_a = spell.definition) === null || _a === void 0 ? void 0 : _a.components) !== null && _b !== void 0 ? _b : [];
}
/**
*
* @param spell
*/
export function getComponentsDescription(spell) {
return getDefinitionComponentsDescription(spell);
}
/**
*
* @param spell
*/
export function getDefinitionComponentsDescription(spell) {
var _a, _b;
return (_b = (_a = spell.definition) === null || _a === void 0 ? void 0 : _a.componentsDescription) !== null && _b !== void 0 ? _b : null;
}
/**
*
* @param spell
*/
export function getRange(spell) {
return spell.range;
}
/**
*
* @param spell
*/
export function getRescriction(spell) {
return spell.restriction;
}
/**
*
* @param spell
*/
export function getAdditionalDescription(spell) {
return spell.additionalDescription;
}
/**
*
* @param spell
*/
export function getRequiresAttackRoll(spell) {
return getDefinitionRequiresAttackRoll(spell);
}
/**
*
* @param spell
*/
export function getRequiresSavingThrow(spell) {
return getDefinitionRequiresSavingThrow(spell);
}
/**
*
* @param spell
*/
export function getDefinitionRequiresSavingThrow(spell) {
var _a, _b;
return (_b = (_a = spell.definition) === null || _a === void 0 ? void 0 : _a.requiresSavingThrow) !== null && _b !== void 0 ? _b : false;
}
/**
*
* @param spell
*/
export function getAtHigherLevels(spell) {
return spell.atHigherLevels;
}
/**
*
* @param spell
*/
export function getDefinitionAtHigherLevels(spell) {
var _a, _b;
return (_b = (_a = spell.definition) === null || _a === void 0 ? void 0 : _a.atHigherLevels) !== null && _b !== void 0 ? _b : null;
}
/**
*
* @param spell
*/
export function getCanCastAtHigherLevel(spell) {
return getDefinitionCanCastAtHigherLevel(spell);
}
/**
*
* @param spell
*/
export function getDefinitionCanCastAtHigherLevel(spell) {
var _a, _b;
return (_b = (_a = spell.definition) === null || _a === void 0 ? void 0 : _a.canCastAtHigherLevel) !== null && _b !== void 0 ? _b : false;
}
/**
*
* @param spell
*/
export function getAttackSaveValue(spell) {
return spell.attackSaveValue;
}
/**
*
* @param spell
*/
export function getBonusFixedDamage(spell) {
return spell.bonusFixedDamage;
}
/**
*
* @param spell
*/
export function getSaveDcAbility(spell) {
return getDefinitionSaveDcAbility(spell);
}
/**
*
* @param spell
*/
export function getDefinitionSaveDcAbility(spell) {
var _a, _b;
return (_b = (_a = spell.definition) === null || _a === void 0 ? void 0 : _a.saveDcAbilityId) !== null && _b !== void 0 ? _b : null;
}
/**
*
* @param spell
*/
export function getDefinitionScaleType(spell) {
var _a, _b;
return (_b = (_a = spell.definition) === null || _a === void 0 ? void 0 : _a.scaleType) !== null && _b !== void 0 ? _b : null;
}
/**
*
* @param spell
*/
export function getScaleType(spell) {
return getDefinitionScaleType(spell);
}
/**
*
* @param spell
*/
export function getNotes(spell) {
return spell.notes;
}
/**
*
* @param spell
*/
export function getDataOrigin(spell) {
return spell.dataOrigin;
}
/**
*
* @param spell
*/
export function getDataOriginType(spell) {
const dataOrigin = getDataOrigin(spell);
return dataOrigin.type;
}
/**
*
* @param spell
*/
export function getExpandedDataOriginRef(spell) {
return spell.expandedDataOriginRef;
}
/**
*
* @param spell
*/
export function getAtWillLimitedUseLevel(spell) {
return spell.atWillLimitedUseLevel;
}
/**
*
* @param spell
*/
export function getComponentId(spell) {
return spell.componentId;
}
/**
*
* @param spell
*/
export function getComponentTypeId(spell) {
return spell.componentTypeId;
}
/**
*
* @param spell
*/
export function countsAsKnownSpell(spell) {
return spell.countsAsKnownSpell;
}
/**
*
* @param spell
*/
export function getSpellCastingAbilityId(spell) {
return spell.spellCastingAbilityId;
}
/**
*
* @param spell
*/
export function getOverrideSaveDc(spell) {
return spell.overrideSaveDc;
}
/**
*
* @param spell
*/
export function getSpellListId(spell) {
return spell.spellListId;
}
/**
*
* @param spell
* @param useOverride
*/
export function getName(spell, useOverride = true) {
var _a;
if (useOverride && spell.name) {
return spell.name;
}
return (_a = getDefinitionName(spell)) !== null && _a !== void 0 ? _a : '';
}
/**
*
* @param spell
*/
export function getCharacterLevel(spell) {
return spell.characterLevel;
}
/**
*
* @param spell
*/
export function getEntity(spell) {
return spell.entity;
}
/**
*
* @param spell
*/
export function getSpellcastingModifier(spell) {
return spell.spellcastingModifier;
}
/**
*
* @param spell
*/
export function getDefinitionId(spell) {
return spell.definitionId;
}
/**
*
* @param spell
*/
export function getIsSignatureSpell(spell) {
return spell.isSignatureSpell;
}
/**
* This is the mapping value of whether it is prepared, but may be false and the spell has
* alwaysPrepared, so if you are checking for if the spell really is prepared use isPrepared()
* @param spell
*/
export function getPrepared(spell) {
return spell.prepared;
}
/**
*
* @param spell
*/
export function isPrepared(spell) {
return spell.isPrepared;
}
/**
*
* @param spell
*/
export function isRitual(spell) {
return spell.isRitual;
}
/**
* @param spell
*/
export function isActive(spell) {
return spell.isActive;
}
/**
*
* @param spell
*/
export function isCastAsRitual(spell) {
return spell.isCastAsRitual;
}
/**
*
* @param spell
*/
export function getDefinitionSources(spell) {
var _a, _b;
return (_b = (_a = spell.definition) === null || _a === void 0 ? void 0 : _a.sources) !== null && _b !== void 0 ? _b : [];
}
/**
*
* @param spell
*/
export function getSources(spell) {
return getDefinitionSources(spell);
}
/**
*
* @param spell - The spell in question
*/
export function getSpellGroupInfoLookup(spell) {
return spell.spellGroupInfoLookup;
}
/**
*
* @param spell
*/
export function isAtWill(spell) {
return spell.isAtWill;
}
/**
*
* @param spell
*/
export function isLimitedUseAvailableAtScaledAmount(spell) {
return spell.isLimitedUseAvailableAtScaledAmount;
}
/**
*
* @param spell
*/
export function getScaledAmount(spell) {
return spell.scaledAmount;
}
/**
*
* @param spell
*/
export function getCastLevel(spell) {
return spell.castLevel;
}
/**
*
* @param spell
*/
export function getMaxUses(spell) {
return spell.maxUses;
}
/**
*
* @param spell
*/
export function getConsumedUses(spell) {
return spell.consumedUses;
}
export function isLegacy(spell) {
var _a, _b;
return (_b = (_a = getDefinition(spell)) === null || _a === void 0 ? void 0 : _a.isLegacy) !== null && _b !== void 0 ? _b : false;
}
@@ -0,0 +1,78 @@
import { AdjustmentTypeEnum } from '../Value';
export const DB_STRING_SPELL_ELDRITCH_BLAST = 'Eldritch Blast';
export var SpellRangeTypeEnum;
(function (SpellRangeTypeEnum) {
SpellRangeTypeEnum[SpellRangeTypeEnum["SELF"] = 1] = "SELF";
SpellRangeTypeEnum[SpellRangeTypeEnum["TOUCH"] = 2] = "TOUCH";
SpellRangeTypeEnum[SpellRangeTypeEnum["RANGED"] = 3] = "RANGED";
SpellRangeTypeEnum[SpellRangeTypeEnum["SIGHT"] = 4] = "SIGHT";
SpellRangeTypeEnum[SpellRangeTypeEnum["UNLIMITED"] = 9] = "UNLIMITED";
})(SpellRangeTypeEnum || (SpellRangeTypeEnum = {}));
export var SpellRangeTypeNameEnum;
(function (SpellRangeTypeNameEnum) {
SpellRangeTypeNameEnum["SELF"] = "Self";
SpellRangeTypeNameEnum["TOUCH"] = "Touch";
SpellRangeTypeNameEnum["RANGED"] = "Ranged";
SpellRangeTypeNameEnum["SIGHT"] = "Sight";
SpellRangeTypeNameEnum["UNLIMITED"] = "Unlimited";
})(SpellRangeTypeNameEnum || (SpellRangeTypeNameEnum = {}));
export var SpellConditionTypeEnum;
(function (SpellConditionTypeEnum) {
SpellConditionTypeEnum[SpellConditionTypeEnum["APPLY"] = 1] = "APPLY";
SpellConditionTypeEnum[SpellConditionTypeEnum["REMOVE"] = 2] = "REMOVE";
SpellConditionTypeEnum[SpellConditionTypeEnum["SUPRESS"] = 3] = "SUPRESS";
})(SpellConditionTypeEnum || (SpellConditionTypeEnum = {}));
export var SpellDurationTypeEnum;
(function (SpellDurationTypeEnum) {
SpellDurationTypeEnum["INSTANTANEOUS"] = "Instantaneous";
SpellDurationTypeEnum["CONCENTRATION"] = "Concentration";
SpellDurationTypeEnum["TIME"] = "Time";
SpellDurationTypeEnum["SPECIAL"] = "Special";
SpellDurationTypeEnum["UNIT_DISPELLED"] = "Unit Dispelled";
SpellDurationTypeEnum["UNIT_DISPELLED_TRIGGERED"] = "Unit Dispelled or Triggered";
})(SpellDurationTypeEnum || (SpellDurationTypeEnum = {}));
export var SpellPrepareTypeEnum;
(function (SpellPrepareTypeEnum) {
SpellPrepareTypeEnum[SpellPrepareTypeEnum["LEVEL"] = 1] = "LEVEL";
SpellPrepareTypeEnum[SpellPrepareTypeEnum["HALF_LEVEL"] = 2] = "HALF_LEVEL";
})(SpellPrepareTypeEnum || (SpellPrepareTypeEnum = {}));
export var SpellScaleTypeNameEnum;
(function (SpellScaleTypeNameEnum) {
SpellScaleTypeNameEnum["CHARACTER_LEVEL"] = "characterlevel";
SpellScaleTypeNameEnum["SPELL_SCALE"] = "spellscale";
SpellScaleTypeNameEnum["SPELL_LEVEL"] = "spelllevel";
})(SpellScaleTypeNameEnum || (SpellScaleTypeNameEnum = {}));
export var RitualCastingTypeEnum;
(function (RitualCastingTypeEnum) {
RitualCastingTypeEnum[RitualCastingTypeEnum["CAN_CAST_AS_RITUAL"] = 1] = "CAN_CAST_AS_RITUAL";
RitualCastingTypeEnum[RitualCastingTypeEnum["MUST_CAST_AS_RITUAL"] = 2] = "MUST_CAST_AS_RITUAL";
})(RitualCastingTypeEnum || (RitualCastingTypeEnum = {}));
export var SpellGroupEnum;
(function (SpellGroupEnum) {
SpellGroupEnum[SpellGroupEnum["HEALING"] = 1] = "HEALING";
})(SpellGroupEnum || (SpellGroupEnum = {}));
export const SPELL_CUSTOMIZATION_ADJUSTMENT_TYPES = [
AdjustmentTypeEnum.SAVE_DC_BONUS,
AdjustmentTypeEnum.SAVE_DC_OVERRIDE,
AdjustmentTypeEnum.FIXED_VALUE_BONUS,
AdjustmentTypeEnum.NAME_OVERRIDE,
AdjustmentTypeEnum.TO_HIT_BONUS,
AdjustmentTypeEnum.TO_HIT_OVERRIDE,
AdjustmentTypeEnum.NOTES,
AdjustmentTypeEnum.DISPLAY_AS_ATTACK,
];
export var SpellCastingLearningStyle;
(function (SpellCastingLearningStyle) {
SpellCastingLearningStyle[SpellCastingLearningStyle["Prepared"] = 1] = "Prepared";
SpellCastingLearningStyle[SpellCastingLearningStyle["Learned"] = 2] = "Learned";
})(SpellCastingLearningStyle || (SpellCastingLearningStyle = {}));
;
export const SpellCastingLearningStyleAddText = {
[SpellCastingLearningStyle.Learned]: "Learn",
[SpellCastingLearningStyle.Prepared]: "Prepare",
};
export const SpellCastingLearningStyleRemoveText = {
[SpellCastingLearningStyle.Learned]: "Delete",
[SpellCastingLearningStyle.Prepared]: "Unprepare",
};
export const DefaultSpellCastingLearningStyle = SpellCastingLearningStyle.Learned;
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,702 @@
import { flatten, groupBy, orderBy, uniqBy } from 'lodash';
import { TypeScriptUtils } from '../../utils';
import { AbilityDerivers } from '../Ability';
import { BackgroundAccessors, BackgroundDerivers } from '../Background';
import { CampaignAccessors } from '../Campaign';
import { CharacterDerivers, } from '../Character';
import { ClassAccessors } from '../Class';
import { ClassFeatureAccessors, ClassFeatureSimulators } from '../ClassFeature';
import { DB_STRING_RITUAL_CASTER_LIST } from '../Core';
import { DataOriginGenerators, DataOriginTypeEnum } from '../DataOrigin';
import { FeatAccessors } from '../Feat';
import { HelperUtils } from '../Helper';
import { ItemAccessors, ItemDerivers, ItemValidators } from '../Item';
import { LimitedUseDerivers } from '../LimitedUse';
import { ModifierAccessors, ModifierBonusTypeEnum, ModifierDerivers, ModifierValidators, } from '../Modifier';
import { OptionalClassFeatureAccessors } from '../OptionalClassFeature';
import { OptionalOriginAccessors } from '../OptionalOrigin';
import { RaceAccessors } from '../Race';
import { RacialTraitAccessors, RacialTraitSimulators } from '../RacialTrait';
import { RuleDataAccessors } from '../RuleData';
import { asPartOfWeaponAttack, countsAsKnownSpell, getAttackType, getAtWillLimitedUseLevel, getBaseLevelAtWill, getComponentId, getComponentTypeId, getDataOrigin, getDataOriginType, getId, getLevel, getLimitedUse, getMappingId, getName, getSpellGroups, isActive, isAlwaysPrepared, isAttack, isCantrip, isDisplayAsAttack, } from './accessors';
import { deriveAtHigherLevels, deriveCanPrepare, deriveCanRemove, deriveCharacterLevel, deriveExpandedDataOriginRef, deriveIsActive, deriveIsCantripsKnownMaxed, deriveIsCastAsRitual, deriveIsCastedByRitualSpellcaster, deriveIsCastedBySpellbookSpellcaster, deriveIsCustomized, deriveIsDisplayAsAttack, deriveIsPrepared, deriveIsRitual, deriveIsSpellsKnownMaxed, deriveKnownKey, deriveModifiers, deriveName, deriveNotes, deriveRange, deriveSpellAttackModifier, deriveSpellAttackSaveValue, deriveSpellBonusFixedDamage, deriveSpellPrepareMax, deriveSpellSpellcastingModifier, deriveSpellToHit, deriveStartingCastLevel, deriveUniqueKey, getCastLevelRange, getConsumedLimitedUse, getScaledDamage, } from './derivers';
import { getScaledRange } from './utils';
import { validateIsAtWill, validateIsLimitedUseAvailableAtScaledAmount, validateShouldShowCharacterSpellAtLevel, validateShouldShowClassSpellAtLevel, } from './validators';
/**
*
* @param dataOriginSpell
* @param valueLookup
* @param ruleData
*/
export function generateBaseSpell(dataOriginSpell, valueLookup, ruleData) {
return Object.assign(Object.assign({}, dataOriginSpell), { modifiers: deriveModifiers(dataOriginSpell, ruleData), isPrepared: deriveIsPrepared(dataOriginSpell), isActive: deriveIsActive(dataOriginSpell), isRitual: deriveIsRitual(dataOriginSpell), name: deriveName(dataOriginSpell, valueLookup), uniqueKey: deriveUniqueKey(dataOriginSpell), isCustomized: deriveIsCustomized(dataOriginSpell, valueLookup), atHigherLevels: deriveAtHigherLevels(dataOriginSpell, ruleData) });
}
/**
*
* @param spell
* @param classInfo
* @param overallInfo
* @param valueLookup
* @param ruleData
* @param modifierData
*/
export function generateClassSpell(spell, classInfo, overallInfo, valueLookup, ruleData, modifierData) {
const { proficiencyBonus, modifiers, abilityLookup, characterLevel, bonusCantripDamage } = overallInfo;
const baseSpell = generateBaseSpell(spell, valueLookup, ruleData);
let classFixedDamageTotal = classInfo.spellDamage;
if (isCantrip(baseSpell)) {
classFixedDamageTotal += bonusCantripDamage + classInfo.bonusCantripDamage;
}
return Object.assign(Object.assign({}, baseSpell), { range: deriveRange(baseSpell, modifiers, abilityLookup), name: deriveName(baseSpell, valueLookup), displayAsAttack: deriveIsDisplayAsAttack(baseSpell, valueLookup), isCastAsRitual: deriveIsCastAsRitual(baseSpell), notes: deriveNotes(baseSpell, valueLookup), toHit: deriveSpellToHit(baseSpell, modifiers, abilityLookup, proficiencyBonus, classInfo.toHit, valueLookup), attackSaveValue: deriveSpellAttackSaveValue(baseSpell, modifiers, proficiencyBonus, abilityLookup, classInfo.spellSaveDc, valueLookup), bonusFixedDamage: deriveSpellBonusFixedDamage(baseSpell, modifiers, abilityLookup, classFixedDamageTotal, valueLookup), spellcastingModifier: classInfo.spellcastingModifier, characterLevel: deriveCharacterLevel(baseSpell, characterLevel), canRemove: deriveCanRemove(baseSpell, classInfo), canPrepare: deriveCanPrepare(baseSpell, classInfo), canAdd: false, entity: null, spellGroupInfoLookup: generateSpellGroupInfoLookup(baseSpell, modifiers, modifierData) });
}
/**
*
* @param spell
* @param modifiers
* @param modifierData
*/
export function generateSpellGroupInfo(spell, spellGroupId, spellGroupModifiers, modifierData) {
const highestGroupModifier = ModifierDerivers.deriveHighestValueModifier(spellGroupModifiers, modifierData);
if (!highestGroupModifier) {
return null;
}
return {
spellGroupId,
bonusFixedValue: ModifierDerivers.deriveValue(highestGroupModifier, modifierData, 0, true, {
[ModifierBonusTypeEnum.SPELL_LEVEL]: getLevel(spell),
}),
useCastLevel: ModifierAccessors.getBonusTypes(highestGroupModifier).includes(ModifierBonusTypeEnum.SPELL_LEVEL),
};
}
/**
*
* @param spell
* @param modifiers
* @param modifierData
*/
export function generateSpellGroupInfoLookup(spell, modifiers, modifierData) {
const spellGroupInfos = getSpellGroups(spell)
.map((spellGroupId) => {
const spellGroupModifiers = modifiers.filter((modifier) => ModifierValidators.isValidBonusSpellGroupModifier(modifier, spellGroupId));
return generateSpellGroupInfo(spell, spellGroupId, spellGroupModifiers, modifierData);
})
.filter(TypeScriptUtils.isNotNullOrUndefined);
return HelperUtils.generateNonNullLookup(spellGroupInfos, (spellGroupInfo) => spellGroupInfo.spellGroupId);
}
/**
*
* @param toHitFallback
* @param attackSaveFallback
* @param bonusFixedDamageFallback
* @param spellcastingModifier
*/
export function generateCharacterSpell_contextInfo(toHitFallback, attackSaveFallback, bonusFixedDamageFallback, spellcastingModifier) {
return {
toHitFallback,
attackSaveFallback,
bonusFixedDamageFallback,
spellcastingModifier,
};
}
/**
*
* @param spell
* @param overallInfo
* @param contextInfo
* @param valueLookup
* @param ruleData
*/
export function generateCharacterSpell(spell, overallInfo, contextInfo, valueLookup, ruleData, modifierData) {
const { proficiencyBonus, modifiers, abilityLookup, characterLevel } = overallInfo;
const baseSpell = generateBaseSpell(spell, valueLookup, ruleData);
return Object.assign(Object.assign({}, baseSpell), { range: deriveRange(baseSpell, modifiers, abilityLookup), name: deriveName(baseSpell, valueLookup), displayAsAttack: deriveIsDisplayAsAttack(baseSpell, valueLookup), isCastAsRitual: deriveIsCastAsRitual(baseSpell), notes: deriveNotes(baseSpell, valueLookup), toHit: deriveSpellToHit(baseSpell, modifiers, abilityLookup, proficiencyBonus, contextInfo.toHitFallback, valueLookup), attackSaveValue: deriveSpellAttackSaveValue(baseSpell, modifiers, proficiencyBonus, abilityLookup, contextInfo.attackSaveFallback, valueLookup), bonusFixedDamage: deriveSpellBonusFixedDamage(baseSpell, modifiers, abilityLookup, contextInfo.bonusFixedDamageFallback, valueLookup), spellcastingModifier: deriveSpellSpellcastingModifier(baseSpell, abilityLookup, contextInfo.spellcastingModifier), canPrepare: false, canRemove: false, characterLevel: deriveCharacterLevel(baseSpell, characterLevel), canAdd: false, entity: null, spellGroupInfoLookup: generateSpellGroupInfoLookup(baseSpell, modifiers, modifierData) });
}
/**
*
* @param featureSpells
* @param classSpells
*/
export function updateClassSpells(featureSpells, classSpells) {
featureSpells.forEach((spell, spellIdx) => {
if (getAtWillLimitedUseLevel(spell) !== null) {
const classSpellIdx = classSpells.findIndex((decoratedSpell) => getId(decoratedSpell) === getId(spell));
if (classSpellIdx > -1) {
const classSpell = classSpells[classSpellIdx];
const spellAlwaysPrepared = isAlwaysPrepared(spell);
classSpells[classSpellIdx] = Object.assign(Object.assign({}, classSpell), { alwaysPrepared: spellAlwaysPrepared ? spellAlwaysPrepared : isAlwaysPrepared(classSpell) });
}
}
});
const alwaysPreparedSpellIds = classSpells.filter((spell) => isAlwaysPrepared(spell)).map((spell) => getId(spell));
let finalSpells = classSpells;
if (alwaysPreparedSpellIds.length) {
finalSpells = classSpells.filter((spell) => {
if (!isAlwaysPrepared(spell) && alwaysPreparedSpellIds.includes(getId(spell))) {
return false;
}
return true;
});
}
return finalSpells;
}
/**
*
* @param featureSpells
* @param classSpells
*/
export function updateClassFeatureSpells(featureSpells, classSpells) {
return featureSpells.map((spell, spellIdx) => {
if (getBaseLevelAtWill(spell)) {
const decoratedSpellIdx = classSpells.findIndex((decoratedSpell) => getId(decoratedSpell) === getId(spell));
if (decoratedSpellIdx > -1) {
const decoratedSpell = classSpells[decoratedSpellIdx];
return Object.assign(Object.assign({}, spell), { prepared: deriveIsPrepared(decoratedSpell) });
}
}
return spell;
});
}
/**
*
* @param characterLevel
* @param charClasses
* @param abilityLookup
* @param profBonus
* @param modifiers
*/
export function generateClassSpellLists(characterLevel, charClasses, abilityLookup, profBonus, modifiers) {
return charClasses.map((charClass) => generateClassSpellsInfo(characterLevel, charClass, abilityLookup, profBonus, modifiers));
}
/**
*
* @param characterLevel
* @param charClass
* @param abilityLookup
* @param profBonus
* @param modifiers
*/
export function generateClassSpellsInfo(characterLevel, charClass, abilityLookup, profBonus, modifiers) {
var _a;
let decoratedSpells = ClassAccessors.getSpells(charClass);
const classSlug = ClassAccessors.getSlug(charClass);
const classLevel = ClassAccessors.getLevel(charClass);
const classSpellRules = ClassAccessors.getSpellRules(charClass);
const spellPrepareType = ClassAccessors.getSpellPrepareType(charClass);
const spellcastingModifier = AbilityDerivers.deriveStatModifier(classSpellRules.spellcastingAbilityStatId, abilityLookup);
const knownCantripsMax = classSpellRules.levelCantripsKnownMaxes
? classSpellRules.levelCantripsKnownMaxes[classLevel]
: null;
const knownSpellsMax = classSpellRules.levelSpellKnownMaxes
? classSpellRules.levelSpellKnownMaxes[classLevel]
: null;
const preparedSpellsMax = classSpellRules.levelPreparedSpellMaxes
? (_a = classSpellRules.levelPreparedSpellMaxes[classLevel]) !== null && _a !== void 0 ? _a : null // coallesce if the array is shorter than the class level, happens for prepared classes not using OSIRIS rules
: null;
const prepareMax = preparedSpellsMax !== null && preparedSpellsMax !== void 0 ? preparedSpellsMax : deriveSpellPrepareMax(spellPrepareType, classLevel, spellcastingModifier);
const canAddSpells = !classSpellRules.knowsAllSpells || knownCantripsMax !== null || knownSpellsMax === null;
const baseToHitModifier = deriveSpellAttackModifier(profBonus, spellcastingModifier);
const bonusSpellSaveDcModifiers = modifiers.filter((modifier) => ModifierValidators.isBonusSpellSaveDc(modifier));
const bonusSpellSaveDcModifierTotal = ModifierDerivers.sumModifiers(bonusSpellSaveDcModifiers, abilityLookup);
const classSaveDcModifiers = modifiers.filter((modifier) => ModifierValidators.isValidBonusClassSpellSaveDcModifier(modifier, classSlug));
const classSaveDcModifierTotal = ModifierDerivers.sumModifiers(classSaveDcModifiers, abilityLookup);
const spellSaveDc = CharacterDerivers.deriveAttackSaveValue(profBonus, spellcastingModifier) +
bonusSpellSaveDcModifierTotal +
classSaveDcModifierTotal;
decoratedSpells = orderBy(decoratedSpells, [(spell) => getLevel(spell), (spell) => getName(spell)]);
const activeSpells = decoratedSpells.filter((spell) => deriveIsActive(spell));
let activeCantripsCount = decoratedSpells.filter((s) => countsAsKnownSpell(s) && isCantrip(s)).length;
const activeFeatureCantripCount = ClassAccessors.getFeatureSpells(charClass).filter((s) => countsAsKnownSpell(s) && isCantrip(s)).length;
activeCantripsCount += activeFeatureCantripCount;
let knownSpellCount = decoratedSpells.filter((s) => countsAsKnownSpell(s) && !isCantrip(s)).length;
const knownFeatureSpellCount = ClassAccessors.getFeatureSpells(charClass).filter((s) => countsAsKnownSpell(s) && !isCantrip(s)).length;
knownSpellCount += knownFeatureSpellCount;
const preparedSpellCount = decoratedSpells.filter((s) => !isCantrip(s) && deriveIsPrepared(s, false) && countsAsKnownSpell(s)).length;
const knownSpellNames = decoratedSpells.map((spell) => getName(spell, false));
const knownSpellIds = decoratedSpells.map((spell) => deriveKnownKey(spell));
const isSpellsKnownMaxed = deriveIsSpellsKnownMaxed(knownSpellsMax, knownSpellCount, classSpellRules.knowsAllSpells);
const isCantripsKnownMaxed = deriveIsCantripsKnownMaxed(knownCantripsMax, activeCantripsCount);
const isPrepareMaxed = !!prepareMax && preparedSpellCount >= prepareMax;
let ritualSpells = [];
if (classSpellRules.isRitualSpellCaster) {
ritualSpells = decoratedSpells.filter((spell) => deriveIsRitual(spell) &&
(isCantrip(spell) ||
(classSpellRules.isPreparedSpellcaster && deriveIsPrepared(spell)) ||
classSpellRules.isKnownSpellcaster ||
classSpellRules.isSpellbookSpellcaster));
}
return {
charClass,
knownSpellcaster: classSpellRules.isKnownSpellcaster,
preparedSpellcaster: classSpellRules.isPreparedSpellcaster,
spellbookSpellcaster: classSpellRules.isSpellbookSpellcaster,
spellcastingModifier,
spellcastingAbility: classSpellRules.spellcastingAbilityStat,
attackSaveValue: spellSaveDc,
toHitValue: baseToHitModifier,
prepareMax,
knownCantripsMax,
knownSpellsMax,
canAddSpells,
isSpellsKnownMaxed,
isCantripsKnownMaxed,
isPrepareMaxed,
spells: decoratedSpells,
activeSpells,
ritualSpells,
knownSpellNames,
knownSpellIds,
activeCantripsCount,
activeFeatureCantripCount,
knownSpellCount,
knownFeatureSpellCount,
preparedSpellCount,
};
}
/**
*
* @param spell
*/
export function generateDataOriginKey(spell) {
return DataOriginGenerators.generateDataOriginKey(getComponentId(spell), getComponentTypeId(spell));
}
/**
*
* @param spells
*/
export function generateSpellComponentLookup(spells) {
return groupBy(spells, (spell) => generateDataOriginKey(spell));
}
/**
*
* @param spells
* @param partyInfo
*/
export function generateItemSpellLookup(spells, partyInfo) {
const partyItemSpells = partyInfo ? CampaignAccessors.getItemSpells(partyInfo) : [];
return generateSpellComponentLookup(uniqBy([...partyItemSpells, ...spells], (spell) => getMappingId(spell)));
}
/**
*
* @param modifiers
* @param items
* @param feats
* @param race
* @param classes
* @param proficiencyBonus
* @param abilityLookup
* @param classSpellInfoLookup
* @param overallSpellInfo
* @param valueLookup
* @param ruleData
* @param modifierData
*/
export function generateCharacterSpells(modifiers, items, feats, race, classes, proficiencyBonus, abilityLookup, classSpellInfoLookup, overallSpellInfo, valueLookup, ruleData, modifierData) {
/**
* NOTE: Talked through, and we decided that any character spell cannot be forced
* to go lower than a 0 value for each of these highest values. You shouldn't be forced
* to take a -1 into your calculation if you are very unoptimized
*/
let highestMiscAttackSave = CharacterDerivers.deriveAttackSaveValue(proficiencyBonus, 0);
let highestMiscToHit = proficiencyBonus;
let highestSpellcastingModifier = 0;
let highestFixedDamage = 0;
classes.forEach((charClass) => {
if (ClassAccessors.isSpellcastingActive(charClass) || ClassAccessors.isPactMagicActive(charClass)) {
// TODO fix lookup
const classSpellInfo = classSpellInfoLookup[ClassAccessors.getId(charClass)];
if (classSpellInfo.spellSaveDc > highestMiscAttackSave) {
highestMiscAttackSave = classSpellInfo.spellSaveDc;
}
if (classSpellInfo.baseToHitModifier > highestMiscToHit) {
highestMiscToHit = classSpellInfo.baseToHitModifier;
}
if (classSpellInfo.spellcastingModifier > highestSpellcastingModifier) {
highestSpellcastingModifier = classSpellInfo.spellcastingModifier;
}
if (classSpellInfo.spellDamage > highestFixedDamage) {
highestFixedDamage = classSpellInfo.spellDamage;
}
}
});
const spells = [];
items.forEach((item) => {
const itemSpells = ItemAccessors.getSpells(item);
const updatedSpells = itemSpells.map((spell) => {
const contextInfo = generateCharacterSpell_contextInfo(highestMiscToHit, highestMiscAttackSave, isCantrip(spell) ? overallSpellInfo.bonusCantripDamage : highestFixedDamage, highestSpellcastingModifier);
return generateCharacterSpell(spell, overallSpellInfo, contextInfo, valueLookup, ruleData, modifierData);
});
spells.push(...updatedSpells);
});
const featureFallbackToHit = CharacterDerivers.deriveAttackModifier(proficiencyBonus, 0);
const featureFallbackSaveDc = CharacterDerivers.deriveAttackSaveValue(proficiencyBonus, 0);
const featureFallbackSpellcastingModifier = 0;
const featureFixedDamage = 0;
feats.forEach((feat) => {
const featSpells = FeatAccessors.getSpells(feat);
const updatedSpells = featSpells.map((spell) => {
const contextInfo = generateCharacterSpell_contextInfo(featureFallbackToHit, featureFallbackSaveDc, isCantrip(spell) ? overallSpellInfo.bonusCantripDamage : featureFixedDamage, featureFallbackSpellcastingModifier);
return generateCharacterSpell(spell, overallSpellInfo, contextInfo, valueLookup, ruleData, modifierData);
});
spells.push(...updatedSpells);
});
if (race) {
const raceSpells = RaceAccessors.getSpells(race);
const updatedRaceSpells = raceSpells.map((spell) => {
const contextInfo = generateCharacterSpell_contextInfo(featureFallbackToHit, featureFallbackSaveDc, isCantrip(spell) ? overallSpellInfo.bonusCantripDamage : featureFixedDamage, featureFallbackSpellcastingModifier);
return generateCharacterSpell(spell, overallSpellInfo, contextInfo, valueLookup, ruleData, modifierData);
});
spells.push(...updatedRaceSpells);
}
classes.forEach((charClass) => {
const classSpellInfo = classSpellInfoLookup[ClassAccessors.getId(charClass)];
ClassAccessors.getFeatureSpells(charClass).forEach((spell) => {
let fixedDamageTotal = classSpellInfo.spellDamage;
if (isCantrip(spell)) {
fixedDamageTotal += overallSpellInfo.bonusCantripDamage + classSpellInfo.bonusCantripDamage;
}
const contextInfo = generateCharacterSpell_contextInfo(classSpellInfo.toHit, classSpellInfo.spellSaveDc, fixedDamageTotal, classSpellInfo.spellcastingModifier);
spells.push(generateCharacterSpell(spell, overallSpellInfo, contextInfo, valueLookup, ruleData, modifierData));
});
});
return spells;
}
/**
*
* @param spellSlots
*/
export function generateMaxSpellSlotLevel(spellSlots) {
if (spellSlots.length) {
const sortedSlot = HelperUtils.getLast(spellSlots, ['level']);
if (sortedSlot) {
return sortedSlot.level;
}
return 0;
}
return 0;
}
/**
*
* @param maxSpellSlotLevel
* @param maxPactMagicSlotLevel
*/
export function generateCombinedMaxSpellSlotLevel(maxSpellSlotLevel, maxPactMagicSlotLevel) {
return Math.max(maxSpellSlotLevel, maxPactMagicSlotLevel);
}
/**
*
* @param spellSlots
*/
export function generateAvailableSpellSlotLevels(spellSlots) {
return spellSlots.filter((slot) => slot.available > 0).map((slot) => slot.level);
}
/**
*
* @param spellSlots
*/
export function generateCastableSpellSlotLevels(spellSlots) {
return spellSlots.filter((slot) => slot.available - slot.used > 0).map((slot) => slot.level);
}
/**
*
* @param classes
*/
export function generateClassSpells(classes) {
const spells = [];
for (let i = 0; i < classes.length; i++) {
const charClass = classes[i];
spells.push(...ClassAccessors.getSpells(charClass));
}
return spells;
}
/**
*
* @param classSpells
*/
export function generateActiveClassSpells(classSpells) {
return classSpells.filter(isActive);
}
/**
*
* @param attackClassSpellLists
* @param characterSpells
*/
export function generateActiveSpellAttackList(attackClassSpellLists, characterSpells) {
return [
...flatten(attackClassSpellLists.map((ClassSpellInfo) => ClassSpellInfo.activeSpells)),
...characterSpells,
].filter((spell) => !asPartOfWeaponAttack(spell) &&
((isAttack(spell) && (isDisplayAsAttack(spell) || isDisplayAsAttack(spell) === null)) ||
isDisplayAsAttack(spell)));
}
/**
*
* @param classSpells
* @param characterSpells
* @param xpInfo
*/
export function generateWeaponSpellDamageGroups(classSpells, characterSpells, xpInfo) {
const damageSources = [];
classSpells.forEach((spell) => {
if (!asPartOfWeaponAttack(spell)) {
return;
}
const damageDice = getScaledDamage(spell);
if (damageDice.length) {
damageSources.push({
spell,
damageDice,
attackTypeRange: getAttackType(spell),
});
}
});
characterSpells.forEach((spell) => {
if (!asPartOfWeaponAttack(spell)) {
return;
}
const damageDice = getScaledDamage(spell);
if (damageDice.length) {
damageSources.push({
spell,
damageDice,
attackTypeRange: getAttackType(spell),
});
}
});
return damageSources;
}
/**
*
* @param activeCharacterSpells
* @param classSpells
*/
export function generateRitualSpells(activeCharacterSpells, classSpells) {
const ritualCharacterSpells = activeCharacterSpells.filter((spell) => {
const dataOrigin = getDataOrigin(spell);
const dataOriginType = getDataOriginType(spell);
if (dataOriginType === DataOriginTypeEnum.FEAT &&
DB_STRING_RITUAL_CASTER_LIST.includes(FeatAccessors.getName(dataOrigin.primary)) &&
deriveIsRitual(spell)) {
return true;
}
return false;
});
const ritualClassSpells = classSpells.filter((spell) => {
if (deriveIsRitual(spell) && deriveIsCastedByRitualSpellcaster(spell)) {
if (deriveIsCastedBySpellbookSpellcaster(spell)) {
return true;
}
return deriveIsActive(spell);
}
return false;
});
return [...ritualCharacterSpells, ...ritualClassSpells];
}
/**
*
* @param spell
* @param castLevel
* @param spellCasterInfo
* @param ruleData
* @param abilityLookup
* @param proficiencyBonus
*/
function generateScaledSpell(spell, castLevel, spellCasterInfo, ruleData, abilityLookup, proficiencyBonus) {
const startingCastLevel = deriveStartingCastLevel(spell);
const scaledAmount = Math.max(0, castLevel - startingCastLevel);
const limitedUse = getLimitedUse(spell);
let maxUses = null;
if (limitedUse != null) {
maxUses = LimitedUseDerivers.deriveMaxUses(limitedUse, abilityLookup, ruleData, proficiencyBonus);
}
const isLimitedUseAvailableAtScaledAmount = validateIsLimitedUseAvailableAtScaledAmount(spell, castLevel, scaledAmount, abilityLookup, ruleData, spellCasterInfo, proficiencyBonus);
return Object.assign(Object.assign({}, spell), { range: getScaledRange(spell, castLevel), consumedUses: getConsumedLimitedUse(spell, scaledAmount), isAtWill: validateIsAtWill(spell, castLevel), isLimitedUseAvailableAtScaledAmount,
castLevel,
maxUses,
scaledAmount });
}
/**
*
* @param xpInfo
* @param classSpells
* @param characterSpells
* @param proficiencyBonus
* @param spellCasterInfo
* @param ruleData
* @param preferences
* @param abilityLookup
*/
export function generateLevelSpells(xpInfo, classSpells, characterSpells, proficiencyBonus, spellCasterInfo, ruleData, preferences, abilityLookup) {
const maxSpellLevel = RuleDataAccessors.getMaxSpellLevel(ruleData);
const levels = [];
// Setup array to exist for all possible spell levels cantrip => maxSpellLevel
for (let i = 0; i <= maxSpellLevel; i++) {
levels.push([]);
}
classSpells.forEach((spell) => {
const startLevel = getLevel(spell);
for (let i = startLevel; i <= maxSpellLevel; i++) {
if (validateShouldShowClassSpellAtLevel(spell, i, spellCasterInfo, preferences)) {
levels[i].push(generateScaledSpell(spell, i, spellCasterInfo, ruleData, abilityLookup, proficiencyBonus));
}
}
});
characterSpells.forEach((spell) => {
const { startLevel, endLevel } = getCastLevelRange(spell, spellCasterInfo, ruleData);
for (let i = startLevel; i <= endLevel; i++) {
if (validateShouldShowCharacterSpellAtLevel(spell, i, spellCasterInfo, preferences)) {
levels[i].push(generateScaledSpell(spell, i, spellCasterInfo, ruleData, abilityLookup, proficiencyBonus));
}
}
});
const sortOrderLookup = {
[DataOriginTypeEnum.RACE]: 1,
[DataOriginTypeEnum.CLASS]: 2,
[DataOriginTypeEnum.FEAT]: 3,
[DataOriginTypeEnum.BACKGROUND]: 4,
[DataOriginTypeEnum.ITEM]: 5,
};
const sortedLevels = [];
for (let i = 0; i <= maxSpellLevel; i++) {
sortedLevels.push(orderBy(levels[i], [
(spell) => getName(spell),
(spell) => {
const type = getDataOriginType(spell);
let orderNum = 100;
if (type !== null) {
const lookupOrderNum = sortOrderLookup[type];
if (lookupOrderNum) {
orderNum = lookupOrderNum;
}
}
return orderNum;
},
]));
}
return sortedLevels;
}
/**
*
* @param spell
* @param dataOriginType
* @param primary
* @param parent
* @param spellListDataOriginLookup
*/
export function generateDataOriginSpell(spell, dataOriginType, primary, parent, spellListDataOriginLookup) {
return Object.assign(Object.assign({}, spell), { dataOrigin: DataOriginGenerators.generateDataOrigin(dataOriginType, primary, parent), expandedDataOriginRef: deriveExpandedDataOriginRef(spell, spellListDataOriginLookup) });
}
/**
*
* @param id
* @param entityTypeId
* @param spellLookup
* @param dataOriginType
* @param primary
* @param parent
* @param ruleData
* @param spellListDataOriginLookup
* @param valueLookup
*/
export function generateBaseSpells(id, entityTypeId, spellLookup, dataOriginType, primary, parent, ruleData, spellListDataOriginLookup, valueLookup) {
const spells = HelperUtils.lookupDataOrFallback(spellLookup, DataOriginGenerators.generateDataOriginKey(id, entityTypeId));
if (!spells) {
return [];
}
return spells.map((spell) => {
const dataOriginSpell = generateDataOriginSpell(spell, dataOriginType, primary, parent, spellListDataOriginLookup);
return generateBaseSpell(dataOriginSpell, valueLookup, ruleData);
});
}
/**
*
* @param spells
* @param inventoryInfusionLookup
* @param characterId
*/
export function generateActiveCharacterSpells(spells, inventoryInfusionLookup, characterId) {
return spells.filter((spell) => {
const dataOrigin = getDataOrigin(spell);
const dataOriginType = getDataOriginType(spell);
if (dataOriginType === DataOriginTypeEnum.ITEM) {
const item = dataOrigin.primary;
const mappingId = ItemAccessors.getMappingId(item);
const infusion = HelperUtils.lookupDataOrFallback(inventoryInfusionLookup, mappingId);
if (ItemDerivers.deriveCanAttune(item, infusion)) {
return ItemValidators.isEquippedToCurrentCharacter(item, characterId) && ItemAccessors.isAttuned(item);
}
return ItemValidators.isEquippedToCurrentCharacter(item, characterId);
}
return true;
});
}
/**
*
* @param race
* @param classes
* @param background
* @param optionalOrigins
* @param optionalClassFeatures
* @param definitionPool
*/
export function generateSpellListDataOriginLookup(race, classes, background, optionalOrigins, optionalClassFeatures, definitionPool) {
let lookup = {};
if (race !== null) {
RaceAccessors.getDefinitionRacialTraits(race).forEach((feature) => {
const uniqueKey = RacialTraitAccessors.getUniqueKey(feature);
RacialTraitAccessors.getSpellListIds(feature).forEach((spellListId) => {
lookup[spellListId] = DataOriginGenerators.generateDataOriginRef(DataOriginTypeEnum.RACE, uniqueKey);
});
});
}
if (background !== null) {
const uniqueKey = BackgroundDerivers.deriveUniqueKey(background);
BackgroundAccessors.getSpellListIds(background).forEach((spellListId) => {
lookup[spellListId] = DataOriginGenerators.generateDataOriginRef(DataOriginTypeEnum.BACKGROUND, uniqueKey);
});
}
classes.forEach((charClass) => {
ClassAccessors.getDefinitionClassFeatures(charClass).forEach((feature) => {
const uniqueKey = ClassFeatureAccessors.getUniqueKey(feature);
ClassFeatureAccessors.getSpellListIds(feature).forEach((spellListId) => {
lookup[spellListId] = DataOriginGenerators.generateDataOriginRef(DataOriginTypeEnum.CLASS_FEATURE, uniqueKey);
});
});
});
optionalOrigins.forEach((optionalOrigin) => {
const definitionKey = OptionalOriginAccessors.getDefinitionKey(optionalOrigin);
if (!definitionKey) {
return;
}
const racialTrait = RacialTraitSimulators.simulateRacialTrait(definitionKey, definitionPool);
if (!racialTrait) {
return;
}
const uniqueKey = RacialTraitAccessors.getUniqueKey(racialTrait);
RacialTraitAccessors.getSpellListIds(racialTrait).forEach((spellListId) => {
lookup[spellListId] = DataOriginGenerators.generateDataOriginRef(DataOriginTypeEnum.RACE, uniqueKey);
});
});
optionalClassFeatures.forEach((optionalClassFeature) => {
const definitionKey = OptionalClassFeatureAccessors.getDefinitionKey(optionalClassFeature);
if (!definitionKey) {
return;
}
const feature = ClassFeatureSimulators.simulateClassFeature(definitionKey, definitionPool, classes);
if (!feature) {
return;
}
const uniqueKey = ClassFeatureAccessors.getUniqueKey(feature);
ClassFeatureAccessors.getSpellListIds(feature).forEach((spellListId) => {
lookup[spellListId] = DataOriginGenerators.generateDataOriginRef(DataOriginTypeEnum.CLASS_FEATURE, uniqueKey);
});
});
return lookup;
}
/**
*
* @param raceSpellListIds
* @param backgroundSpellListIds
*/
export function generateGlobalSpellListIds(raceSpellListIds, backgroundSpellListIds) {
return [...raceSpellListIds, ...backgroundSpellListIds];
}
@@ -0,0 +1,25 @@
import { HelperUtils } from '../Helper';
import { ModifierValidators } from '../Modifier';
import { getSpellGroupInfoLookup } from './accessors';
import { SpellGroupEnum } from './constants';
/**
* We needed to display very contextual verbiage for
* spells that did damage and the healed based on that
* damage on SpellDetail.tsx via SpellCaster.tsx on
* the healing node.
* - Ask Julie or Brian Life Domain Cleric
* @param modifiers
* @param spell
* @param scaledHealingDie
*/
export function hack__isHealingDieAdditionalBonusFixedValue(modifiers, spell, scaledHealingDie) {
if (!scaledHealingDie) {
return false;
}
const damageModifiers = modifiers.filter((modifier) => ModifierValidators.isSpellDamageModifier(modifier));
return (damageModifiers.length > 0 &&
HelperUtils.lookupDataOrFallback(getSpellGroupInfoLookup(spell), SpellGroupEnum.HEALING, false) &&
!scaledHealingDie.diceValue &&
!scaledHealingDie.diceCount &&
scaledHealingDie.fixedValue !== null);
}
@@ -0,0 +1,14 @@
import * as SpellAccessors from './accessors';
import * as SpellConstants from './constants';
import * as SpellDerivers from './derivers';
import * as SpellGenerators from './generators';
import * as SpellHacks from './hacks';
import * as SpellNotes from './notes';
import * as SpellSimulators from './simulators';
import * as SpellTypings from './typings';
import * as SpellUtils from './utils';
import * as SpellValidators from './validators';
export * from './constants';
export * from './typings';
export { SpellAccessors, SpellDerivers, SpellGenerators, SpellNotes, SpellSimulators, SpellUtils, SpellValidators, SpellHacks, };
export default Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, SpellAccessors), SpellConstants), SpellDerivers), SpellGenerators), SpellNotes), SpellSimulators), SpellTypings), SpellUtils), SpellValidators), SpellHacks);
@@ -0,0 +1,160 @@
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);
}
@@ -0,0 +1,16 @@
import { DataOriginTypeEnum } from '../DataOrigin';
import { deriveCharacterLevel, deriveIsCastAsRitual, deriveName } from './derivers';
import { generateBaseSpell, generateDataOriginSpell } from './generators';
/**
*
* @param spell
* @param overallInfo
* @param ruleData
* @param primary
* @param parent
*/
export function simulateSpell(spell, overallInfo, ruleData, primary, parent) {
const dataOriginSpell = generateDataOriginSpell(spell, DataOriginTypeEnum.SIMULATED, primary, parent, overallInfo.spellListDataOriginLookup);
const baseSpell = generateBaseSpell(dataOriginSpell, {}, ruleData);
return Object.assign(Object.assign({}, baseSpell), { attackSaveValue: null, bonusFixedDamage: 0, canPrepare: false, canRemove: false, characterLevel: deriveCharacterLevel(baseSpell, overallInfo.characterLevel), isCustomized: false, isCastAsRitual: deriveIsCastAsRitual(baseSpell), name: deriveName(baseSpell, {}), notes: '', spellcastingModifier: 0, toHit: null, canAdd: true, entity: spell, spellGroupInfoLookup: {} });
}
@@ -0,0 +1,86 @@
import { DiceAccessors } from '../Dice';
import { HelperUtils } from '../Helper';
import { ModifierAccessors, ModifierValidators } from '../Modifier';
import { RuleDataUtils } from '../RuleData';
import { SpellGroupEnum } from '../Spell';
import { getAtHigherLevels, getBonusFixedDamage, getLevel, getRange, getSaveDcAbility, getScaleType, getSpellcastingModifier, getSpellGroupInfoLookup, } from './accessors';
import { getSpellScaledAtHigher } from './derivers';
/**
* @deprecated
*/
export function getSaveDcAbilityKey(spell, ruleData) {
return RuleDataUtils.getAbilityKey(getSaveDcAbility(spell), ruleData);
}
export function getSaveDcAbilityShortName(spell, ruleData) {
return RuleDataUtils.getAbilityShortName(getSaveDcAbility(spell), ruleData);
}
/**
* @param spell
* @param modifier
* @param atHigherLevelInfo
* @param castLevel
*/
export function getSpellFinalScaledDie(spell, modifier, atHigherLevelInfo, castLevel = null) {
let scaledDie = atHigherLevelInfo === null ? ModifierAccessors.getDie(modifier) : atHigherLevelInfo.die;
if (scaledDie === null) {
return null;
}
const fixedDiceValue = DiceAccessors.getFixedValue(scaledDie);
let fixedValue = (fixedDiceValue === null ? 0 : fixedDiceValue) + getBonusFixedDamage(spell);
if (ModifierAccessors.getUsePrimaryStat(modifier)) {
fixedValue += getSpellcastingModifier(spell);
}
const isHealingHitModifier = ModifierValidators.isSpellHealingHitPointsModifier(modifier);
const spellGroupInfoLookup = getSpellGroupInfoLookup(spell);
const healingGroupInfo = HelperUtils.lookupDataOrFallback(spellGroupInfoLookup, SpellGroupEnum.HEALING);
if (isHealingHitModifier && healingGroupInfo) {
fixedValue += healingGroupInfo.bonusFixedValue;
if (healingGroupInfo.useCastLevel && castLevel) {
fixedValue += castLevel - getLevel(spell);
}
}
return Object.assign(Object.assign({}, scaledDie), { fixedValue });
}
export function getScaledRange(spell, castLevel) {
var _a, _b, _c;
const spellRange = getRange(spell);
if (spellRange === null) {
return null;
}
const higherLevel = getSpellScaledAtHigher(spell, getScaleType(spell), (_b = (_a = getAtHigherLevels(spell)) === null || _a === void 0 ? void 0 : _a.range) !== null && _b !== void 0 ? _b : [], null, castLevel);
return Object.assign(Object.assign({}, spellRange), { rangeValue: (_c = higherLevel === null || higherLevel === void 0 ? void 0 : higherLevel.range) !== null && _c !== void 0 ? _c : spellRange.rangeValue });
}
/**
*
* @param spell
*/
export function makeKnownKey(mappingId, mappingEntityTypeId) {
return `${mappingId}-${mappingEntityTypeId}`;
}
/**
*
* @param spell
*/
export function makeLeveledKnownKey(mappingId, mappingEntityTypeId, castLevel) {
return `${makeKnownKey(mappingId, mappingEntityTypeId)}-${castLevel}`;
}
/**
* Given a ClassSpellInfo object and a SpellID, determine if the spell is known by the character
* using RPGSpell ID as key
* @param spellList
* @param spellId
* @returns
*/
export const isSpellKnown = (spellList, spellId) => spellList.spells.some(
// Compare the given SpellID with the Spell Definition ID
// SpellID is the ID of the record from RPGSpell (SQL Server: Waterdeep)
(spell) => {
var _a;
return ((_a = spell.definition) === null || _a === void 0 ? void 0 : _a.id) === spellId &&
spellList.knownSpellIds.some(
// Compare the resulting Spell's ID with the KnownSpellID
// KnownSpellID is a combination of the ClassMappingID of the record from CharacterSpellMapping (MySQL: YawningPortal)
// and the EntityTypeID of its granting feature (i.e.- a Wizard's class spells will be granted as RPGClassSpellMapping) in
// the format of "{ClassMappingID}-{EntityTypeID}"
(knownSpellId) => spell.id === Number(knownSpellId.split('-')[0]));
});
@@ -0,0 +1,184 @@
import { ClassAccessors } from '../Class';
import { DataOriginTypeEnum } from '../DataOrigin';
import { FeatUtils } from '../Feat';
import { LimitedUseAccessors, LimitedUseDerivers } from '../LimitedUse';
import { getBaseLevelAtWill, getCanCastAtHigherLevel, getCastAtLevel, getDataOrigin, getDataOriginType, getLevel, getLimitedUse, getUsesSpellSlot, isActive, isCantrip, isRitual, } from './accessors';
import { deriveIsActive, deriveIsCastedByRitualSpellcaster, deriveIsCastedBySpellbookSpellcaster, deriveIsPrepared, deriveIsRitual, deriveMustCastAsRitual, getCastLevelAvailableCount, getConsumedLimitedUse, isSpellAvailableAtLevel, } from './derivers';
/**
*
* @param spell
* @param level
* @param spellCasterInfo
*/
export function validateHasShownSpellSlotsBeforeLevel(spell, level, spellCasterInfo) {
if ((spellCasterInfo.firstAvailableSpellSlotLevel !== null &&
spellCasterInfo.lastAvailableSpellSlotLevel !== null &&
spellCasterInfo.firstAvailableSpellSlotLevel < level &&
spellCasterInfo.lastAvailableSpellSlotLevel >= getLevel(spell)) ||
(spellCasterInfo.firstAvailablePactMagicSlotLevel !== null &&
spellCasterInfo.lastAvailablePactMagicSlotLevel !== null &&
spellCasterInfo.firstAvailablePactMagicSlotLevel < level &&
spellCasterInfo.lastAvailablePactMagicSlotLevel >= getLevel(spell))) {
return true;
}
return false;
}
/**
*
* @param spell
* @param level
* @param spellCasterInfo
* @param preferences
*/
export function validateShouldShowClassSpellAtLevel(spell, level, spellCasterInfo, preferences) {
if (!deriveIsActive(spell)) {
if (deriveIsRitual(spell) &&
getLevel(spell) === level &&
deriveIsCastedByRitualSpellcaster(spell) &&
deriveIsCastedBySpellbookSpellcaster(spell)) {
return true;
}
else {
return false;
}
}
if (isCantrip(spell)) {
return getLevel(spell) === level;
}
// show spells that have to show at a specific level
if (getCastAtLevel(spell) === level) {
return true;
}
if (isSpellAvailableAtLevel(spell, level, spellCasterInfo)) {
if (getLevel(spell) === level) {
// Check for normal spellcasting rules if a spell is just supposed to show at current level
return true;
}
else {
// show scalable spells
if (preferences.showScaledSpells && getCanCastAtHigherLevel(spell)) {
return true;
}
// Force scale spells that don't normally scale, but this is first time slots are available (warlocks)
if (!validateHasShownSpellSlotsBeforeLevel(spell, level, spellCasterInfo)) {
return true;
}
}
}
return false;
}
/**
*
* @param spell
* @param level
* @param spellCasterInfo
* @param preferences
*/
export function validateShouldShowCharacterSpellAtLevel(spell, level, spellCasterInfo, preferences) {
if (isCantrip(spell)) {
return getLevel(spell) === level;
}
if (getUsesSpellSlot(spell)) {
if (isSpellAvailableAtLevel(spell, level, spellCasterInfo)) {
if (preferences.showScaledSpells) {
return true;
}
else {
return getLevel(spell) === level;
}
}
else {
return false;
}
}
if (getBaseLevelAtWill(spell)) {
return deriveIsPrepared(spell);
}
if (preferences.showScaledSpells) {
return true;
}
else {
if (getCastAtLevel(spell) === level) {
return true;
}
return getLevel(spell) === level;
}
}
/**
*
* @param spell
* @param castLevel
*/
export function validateIsAtWill(spell, castLevel) {
const limitedUse = getLimitedUse(spell);
const usesSpellSlot = getUsesSpellSlot(spell);
const baseLevelAtWill = getBaseLevelAtWill(spell);
if (isCantrip(spell)) {
return limitedUse === null;
}
if (baseLevelAtWill && castLevel === getLevel(spell)) {
return true;
}
if (!limitedUse && !usesSpellSlot) {
return true;
}
if (isRitual(spell) && deriveMustCastAsRitual(spell)) {
return true;
}
if (isRitual(spell) &&
!isActive(spell) &&
deriveIsCastedByRitualSpellcaster(spell) &&
deriveIsCastedBySpellbookSpellcaster(spell)) {
return true;
}
return false;
}
/**
*
* @param spell
* @param castLevel
* @param scaledAmount
* @param abilityLookup
* @param ruleData
* @param spellCasterInfo
* @param proficiencyBonus
*/
export function validateIsLimitedUseAvailableAtScaledAmount(spell, castLevel, scaledAmount, abilityLookup, ruleData, spellCasterInfo, proficiencyBonus) {
const limitedUse = getLimitedUse(spell);
if (limitedUse === null) {
return false;
}
const maxUses = LimitedUseDerivers.deriveMaxUses(limitedUse, abilityLookup, ruleData, proficiencyBonus);
const numberUsed = LimitedUseAccessors.getNumberUsed(limitedUse);
const usesSpellSlots = getUsesSpellSlot(spell);
const consumedAmount = getConsumedLimitedUse(spell, scaledAmount);
let isAvailable = numberUsed + consumedAmount <= maxUses;
let areSpellSlotsAvailable;
if (isAvailable && usesSpellSlots) {
const dataOrigin = getDataOrigin(spell);
const dataOriginType = getDataOriginType(spell);
if (dataOriginType === DataOriginTypeEnum.CLASS_FEATURE &&
ClassAccessors.isPactMagicActive(dataOrigin.parent)) {
areSpellSlotsAvailable = getCastLevelAvailableCount(castLevel, spellCasterInfo.pactMagicSlots);
}
else {
areSpellSlotsAvailable = getCastLevelAvailableCount(castLevel, spellCasterInfo.spellSlots);
}
if (areSpellSlotsAvailable !== null) {
isAvailable = areSpellSlotsAvailable > 0;
}
}
return isAvailable;
}
/**
*
* @param spell
*/
export function isValidToUsePactSlots(spell) {
const dataOrigin = getDataOrigin(spell);
const dataOriginType = getDataOriginType(spell);
return ((dataOriginType === DataOriginTypeEnum.CLASS_FEATURE &&
ClassAccessors.isPactMagicActive(dataOrigin.parent)) ||
(dataOriginType === DataOriginTypeEnum.FEAT &&
FeatUtils.isEldritchAdept(dataOrigin.parent)));
}