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
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,81 @@
import { AdjustmentTypeEnum } from '../Value';
export var ItemBaseTypeEnum;
(function (ItemBaseTypeEnum) {
ItemBaseTypeEnum["ARMOR"] = "ARMOR";
ItemBaseTypeEnum["GEAR"] = "GEAR";
ItemBaseTypeEnum["WEAPON"] = "WEAPON";
})(ItemBaseTypeEnum || (ItemBaseTypeEnum = {}));
export var ItemBaseTypeIdEnum;
(function (ItemBaseTypeIdEnum) {
ItemBaseTypeIdEnum[ItemBaseTypeIdEnum["ARMOR"] = 701257905] = "ARMOR";
ItemBaseTypeIdEnum[ItemBaseTypeIdEnum["GEAR"] = 2103445194] = "GEAR";
ItemBaseTypeIdEnum[ItemBaseTypeIdEnum["WEAPON"] = 1782728300] = "WEAPON";
})(ItemBaseTypeIdEnum || (ItemBaseTypeIdEnum = {}));
export var ItemTypeEnum;
(function (ItemTypeEnum) {
ItemTypeEnum[ItemTypeEnum["ARMOR"] = 1] = "ARMOR";
ItemTypeEnum[ItemTypeEnum["ARTIFACT"] = 2] = "ARTIFACT";
ItemTypeEnum[ItemTypeEnum["POTION"] = 3] = "POTION";
ItemTypeEnum[ItemTypeEnum["RING"] = 4] = "RING";
ItemTypeEnum[ItemTypeEnum["ROD"] = 5] = "ROD";
ItemTypeEnum[ItemTypeEnum["SCROLL"] = 6] = "SCROLL";
ItemTypeEnum[ItemTypeEnum["STAFF"] = 7] = "STAFF";
ItemTypeEnum[ItemTypeEnum["WAND"] = 8] = "WAND";
ItemTypeEnum[ItemTypeEnum["WEAPON"] = 9] = "WEAPON";
ItemTypeEnum[ItemTypeEnum["WONDROUS_ITEM"] = 10] = "WONDROUS_ITEM";
})(ItemTypeEnum || (ItemTypeEnum = {}));
export var ArmorTypeEnum;
(function (ArmorTypeEnum) {
ArmorTypeEnum[ArmorTypeEnum["LIGHT_ARMOR"] = 1] = "LIGHT_ARMOR";
ArmorTypeEnum[ArmorTypeEnum["MEDIUM_ARMOR"] = 2] = "MEDIUM_ARMOR";
ArmorTypeEnum[ArmorTypeEnum["HEAVY_ARMOR"] = 3] = "HEAVY_ARMOR";
ArmorTypeEnum[ArmorTypeEnum["SHIELD"] = 4] = "SHIELD";
})(ArmorTypeEnum || (ArmorTypeEnum = {}));
export var WeaponTypeEnum;
(function (WeaponTypeEnum) {
WeaponTypeEnum["AMMUNITION"] = "Ammunition";
})(WeaponTypeEnum || (WeaponTypeEnum = {}));
export var WeaponPropertyEnum;
(function (WeaponPropertyEnum) {
WeaponPropertyEnum["VERSATILE"] = "Versatile";
WeaponPropertyEnum["AMMUNITION"] = "Ammunition";
WeaponPropertyEnum["LOADING"] = "Loading";
WeaponPropertyEnum["TWO_HANDED"] = "Two-Handed";
WeaponPropertyEnum["FINESSE"] = "Finesse";
WeaponPropertyEnum["LIGHT"] = "Light";
WeaponPropertyEnum["THROWN"] = "Thrown";
WeaponPropertyEnum["HEAVY"] = "Heavy";
WeaponPropertyEnum["RANGE"] = "Range";
WeaponPropertyEnum["REACH"] = "Reach";
WeaponPropertyEnum["SPECIAL"] = "Special";
})(WeaponPropertyEnum || (WeaponPropertyEnum = {}));
export var ItemRarityNameEnum;
(function (ItemRarityNameEnum) {
ItemRarityNameEnum["COMMON"] = "Common";
ItemRarityNameEnum["UNCOMMON"] = "Uncommon";
ItemRarityNameEnum["RARE"] = "Rare";
ItemRarityNameEnum["VERY_RARE"] = "Very Rare";
ItemRarityNameEnum["LEGENDARY"] = "Legendary";
ItemRarityNameEnum["ARTIFACT"] = "Artifact";
})(ItemRarityNameEnum || (ItemRarityNameEnum = {}));
//ONLY USED FOR INFUSIONS CURRENTLY
export const FUTURE_ITEM_DEFINITION_TYPE = 'magic-item';
export const MAGIC_ITEM_ENTITY_TYPE_ID = 112130694;
export const CUSTOM_ITEM_DEFINITION_ENTITY_TYPE_ID = 988229960;
export const ITEM_CUSTOMIZATION_ADJUSTMENT_TYPES = [
AdjustmentTypeEnum.COST_OVERRIDE,
AdjustmentTypeEnum.FIXED_VALUE_BONUS,
AdjustmentTypeEnum.DISPLAY_AS_ATTACK,
AdjustmentTypeEnum.IS_ADAMANTINE,
AdjustmentTypeEnum.IS_OFFHAND,
AdjustmentTypeEnum.IS_PROFICIENT,
AdjustmentTypeEnum.IS_SILVER,
AdjustmentTypeEnum.NAME_OVERRIDE,
AdjustmentTypeEnum.NOTES,
AdjustmentTypeEnum.TO_HIT_OVERRIDE,
AdjustmentTypeEnum.TO_HIT_BONUS,
AdjustmentTypeEnum.SAVE_DC_OVERRIDE,
AdjustmentTypeEnum.SAVE_DC_BONUS,
AdjustmentTypeEnum.WEIGHT_OVERRIDE,
AdjustmentTypeEnum.CAPACITY_WEIGHT_OVERRIDE,
];
@@ -0,0 +1,973 @@
import { flatten, round, uniqBy } from 'lodash';
import { TypeScriptUtils } from '../../utils';
import { AbilityAccessors } from '../Ability';
import { ActionAccessors, ActionGenerators } from '../Action';
import { CharacterValidators } from '../Character';
import { AbilityStatEnum, AttackTypeRangeEnum, PreferenceEncumbranceTypeEnum, StealthCheckTypeEnum } from '../Core';
import { DataOriginTypeEnum } from '../DataOrigin';
import { DefinitionHacks } from '../Definition';
import { HelperUtils } from '../Helper';
import { InfusionAccessors, InfusionTypeEnum } from '../Infusion';
import { LimitedUseAccessors } from '../LimitedUse';
import { ModifierAccessors, ModifierDerivers, ModifierValidators } from '../Modifier';
import { isValidWeaponMasteryModifier } from '../Modifier/validators';
import { RuleDataAccessors, RuleDataUtils } from '../RuleData';
import { AdjustmentTypeEnum, ValueAccessors, ValueHacks, ValueUtils, ValueValidators, } from '../Value';
import { getArmorDefinitionAc, getArmorTypeId, getAttackType, getBaseItemId, getBaseTypeId, getBundleSize, getCategoryId, getDefinitionAvatarUrl, getDefinitionCanAttune, getDefinitionCanEquip, getDefinitionCapacityWeight, getDefinitionCost, getDefinitionDamageType, getDefinitionId, getDefinitionMagic, getDefinitionName, getDefinitionProperties, getDefinitionStealthCheck, getDescription, getGearTypeId, getItemRequiredStr, getItemWeaponBehaviors, getLimitedUse, getMappingEntityTypeId, getMappingId, getModifiers, getQuantity, getSources, getWeight, getWeightDefinition, isEquipped, isMagic, isMonkWeapon, isStackable, } from './accessors';
import { ArmorTypeEnum, FUTURE_ITEM_DEFINITION_TYPE, ITEM_CUSTOMIZATION_ADJUSTMENT_TYPES, ItemBaseTypeEnum, ItemBaseTypeIdEnum, ItemTypeEnum, WeaponPropertyEnum, } from './constants';
import { hack__deriveStealthMediumArmorMasterShouldReturnStealthDefinition } from './hacks';
import { hasWeaponProperty, isArmorContract, isBaseArmor, isBaseGear, isBaseWeapon, hasItemWeaponBehaviors, isWeaponContract, isAmmunition, isGearContract, } from './utils';
import { isCustomItem, validateIsWeaponLike } from './validators';
/**
*
* @param item
*/
export function deriveArmorClass(item) {
if (!isArmorContract(item)) {
return null;
}
const definitionArmorClass = getArmorDefinitionAc(item);
return deriveArmorModifierAcTotal(item) + (definitionArmorClass === null ? 0 : definitionArmorClass);
}
/**
*
* @param weapon
* @param valueLookup
*/
export function deriveIsOffhand(weapon, valueLookup) {
return !!ValueUtils.getKeyValue(valueLookup, AdjustmentTypeEnum.IS_OFFHAND, ValueHacks.hack__toString(getMappingId(weapon)), ValueHacks.hack__toString(getMappingEntityTypeId(weapon)));
}
/**
*
* @param weapon
* @param valueLookup
*/
export function deriveIsSilvered(weapon, valueLookup) {
return !!ValueUtils.getKeyValue(valueLookup, AdjustmentTypeEnum.IS_SILVER, ValueHacks.hack__toString(getMappingId(weapon)), ValueHacks.hack__toString(getMappingEntityTypeId(weapon)));
}
/**
*
* @param weapon
* @param valueLookup
*/
export function deriveIsAdamantine(weapon, valueLookup) {
return !!ValueUtils.getKeyValue(valueLookup, AdjustmentTypeEnum.IS_ADAMANTINE, ValueHacks.hack__toString(getMappingId(weapon)), ValueHacks.hack__toString(getMappingEntityTypeId(weapon)));
}
/**
*
* @param item
* @param infusion
*/
export function deriveCanAttune(item, infusion) {
if (infusion && InfusionAccessors.getType(infusion) !== InfusionTypeEnum.REPLICATE) {
return InfusionAccessors.requiresAttunement(infusion);
}
return getDefinitionCanAttune(item);
}
/**
*
* @param item
* @param infusion
*/
export function deriveCanEquip(item, infusion) {
if (infusion !== null) {
return true;
}
return getDefinitionCanEquip(item);
}
/**
*
* @param item
* @param ruleData
*/
export function deriveCategoryInfo(item, ruleData) {
if (!isWeaponContract(item)) {
return null;
}
return RuleDataUtils.getWeaponCategoryInfo(getCategoryId(item), ruleData);
}
/**
*
* @param item
* @param valueLookup
*/
export function deriveIsDisplayAsAttack(item, valueLookup) {
const override = ValueUtils.getKeyValue(valueLookup, AdjustmentTypeEnum.DISPLAY_AS_ATTACK, ValueHacks.hack__toString(getMappingId(item)), ValueHacks.hack__toString(getMappingEntityTypeId(item)));
const defaultIsDisplayAsAttack = deriveDefaultIsDisplayAsAttack(item);
return (defaultIsDisplayAsAttack && (override === null || override)) || !!override;
}
/**
*
* @param baseTypeId
*/
export function deriveBaseTypeNameFromId(baseTypeId) {
switch (baseTypeId) {
case ItemBaseTypeIdEnum.WEAPON:
return 'Weapon';
case ItemBaseTypeIdEnum.ARMOR:
return 'Armor';
case ItemBaseTypeIdEnum.GEAR:
return 'Gear';
default:
// not implemented
}
return '';
}
/**
*
* @param item
* @param valueLookup
*/
export function deriveCost(item, valueLookup) {
const override = ValueUtils.getKeyValue(valueLookup, AdjustmentTypeEnum.COST_OVERRIDE, ValueHacks.hack__toString(getMappingId(item)), ValueHacks.hack__toString(getMappingEntityTypeId(item)));
const quantity = getQuantity(item);
const bundleSize = getBundleSize(item);
let costPerItem = getDefinitionCost(item);
if (costPerItem === null) {
costPerItem = 0;
}
if (override !== null) {
costPerItem = override;
}
if (isStackable(item)) {
costPerItem /= bundleSize === 0 ? 1 : bundleSize;
}
return round(costPerItem * quantity, 2);
}
/**
*
* @param item
* @param globalModifiers
*/
export function deriveStealthCheck(item, globalModifiers) {
if (!isArmorContract(item)) {
return StealthCheckTypeEnum.NONE;
}
const itemModifiers = getModifiers(item);
const definitionCheck = getDefinitionStealthCheck(item);
if (definitionCheck === StealthCheckTypeEnum.DISADVANTAGE) {
const filteredGlobalModifiers = globalModifiers.filter((modifier) => ModifierValidators.isStealthDisadvantageRemoveModifier(modifier));
if (itemModifiers.find((modifier) => ModifierValidators.isStealthDisadvantageRemoveModifier(modifier)) ||
filteredGlobalModifiers.length) {
if (hack__deriveStealthMediumArmorMasterShouldReturnStealthDefinition(item, filteredGlobalModifiers)) {
return definitionCheck;
}
return StealthCheckTypeEnum.NONE;
}
}
return definitionCheck;
}
/**
*
* @param item
* @param ruleData
*/
export function deriveAvatarUrl(item, ruleData) {
let previewImageUrl = getDefinitionAvatarUrl(item);
switch (getBaseTypeId(item)) {
case ItemBaseTypeIdEnum.WEAPON:
if (!previewImageUrl) {
previewImageUrl = ruleData.defaultWeaponImageUrl;
}
break;
case ItemBaseTypeIdEnum.ARMOR:
if (!previewImageUrl) {
previewImageUrl = ruleData.defaultArmorImageUrl;
}
break;
case ItemBaseTypeIdEnum.GEAR:
if (!previewImageUrl) {
previewImageUrl = ruleData.defaultGearImageUrl;
}
break;
}
if (!previewImageUrl && isCustomItem(item)) {
previewImageUrl = ruleData.defaultGearImageUrl;
}
if (previewImageUrl) {
return previewImageUrl;
}
return '';
}
/**
*
* @param item
*/
export function deriveBaseType(item) {
if (isArmorContract(item)) {
return ItemBaseTypeEnum.ARMOR;
}
else if (isWeaponContract(item)) {
return ItemBaseTypeEnum.WEAPON;
}
return ItemBaseTypeEnum.GEAR;
}
/**
*
* @param item
* @param valueLookup
*/
export function deriveWeight(item, valueLookup) {
const quantity = getQuantity(item);
const bundleSize = getBundleSize(item);
const isItemStackable = isStackable(item);
const weight = getWeightDefinition(item);
const weightOverride = ValueUtils.getKeyValue(valueLookup, AdjustmentTypeEnum.WEIGHT_OVERRIDE, ValueHacks.hack__toString(getMappingId(item)), ValueHacks.hack__toString(getMappingEntityTypeId(item)));
let baseWeight = weight;
if (weightOverride !== null) {
baseWeight = weightOverride;
}
if (!baseWeight) {
return 0;
}
if (isItemStackable) {
if (quantity) {
return round(baseWeight * (quantity / (bundleSize === 0 ? 1 : bundleSize)), 2);
}
return 0;
}
return baseWeight;
}
/**
*
* @param item
* @param valueLookup
*/
export function deriveCapacityWeight(item, valueLookup) {
const capacityWeight = getDefinitionCapacityWeight(item);
const capacityOverride = ValueUtils.getKeyValue(valueLookup, AdjustmentTypeEnum.CAPACITY_WEIGHT_OVERRIDE, ValueHacks.hack__toString(getMappingId(item)), ValueHacks.hack__toString(getMappingEntityTypeId(item)));
return capacityOverride !== null && capacityOverride !== void 0 ? capacityOverride : capacityWeight;
}
/**
*
* @param items
* @param initialValue
*/
export function deriveItemsWeightTotal(items, initialValue = 0) {
return items.reduce((acc, item) => (acc += getWeight(item)), initialValue);
}
/**
*
* @param armor
* @param modifiers
* @param abilityLookup
*/
export function deriveArmorDexBonusMax(armor, modifiers, abilityLookup) {
const armorTypeId = getArmorTypeId(armor);
switch (armorTypeId) {
case ArmorTypeEnum.MEDIUM_ARMOR:
const dexAbility = abilityLookup[AbilityStatEnum.DEXTERITY];
const dexAbilityScore = AbilityAccessors.getScore(dexAbility);
if (dexAbility && dexAbilityScore !== null && dexAbilityScore >= 16) {
const maxModifiers = modifiers.filter((modifier) => ModifierValidators.isSetAcMaxDexModifierModifier(modifier));
const maxArmoredModifiers = modifiers.filter((modifier) => ModifierValidators.isSetAcMaxDexArmoredModifier(modifier));
const maxModifierPossibilities = [];
if (maxModifiers.length) {
maxModifierPossibilities.push(ModifierDerivers.deriveHighestModifierValue(maxModifiers));
}
if (maxArmoredModifiers.length) {
maxModifierPossibilities.push(ModifierDerivers.deriveHighestModifierValue(maxArmoredModifiers));
}
if (maxModifierPossibilities.length) {
return Math.max(...maxModifierPossibilities);
}
}
return 2;
case ArmorTypeEnum.HEAVY_ARMOR:
return 0;
default:
// not implemented
}
return null;
}
/**
*
* @param modifiers
*/
export function deriveUnarmoredDexBonusMax(modifiers) {
const maxDexModifiers = modifiers.filter((modifier) => ModifierValidators.isSetAcMaxDexModifierModifier(modifier));
const maxDexUnarmoredModifiers = modifiers.filter((modifier) => ModifierValidators.isSetAcMaxDexUnarmoredModifier(modifier));
const maxDexValuePossibilities = [];
if (maxDexModifiers.length) {
maxDexValuePossibilities.push(ModifierDerivers.deriveHighestModifierValue(maxDexModifiers));
}
if (maxDexUnarmoredModifiers.length) {
maxDexValuePossibilities.push(ModifierDerivers.deriveHighestModifierValue(maxDexUnarmoredModifiers));
}
if (maxDexValuePossibilities.length) {
return Math.max(...maxDexValuePossibilities);
}
return null;
}
/**
*
* @param localModifiers
* @param globalModifiers
* @param abilityLookup
*/
export function deriveUnarmoredDexBonus(localModifiers, globalModifiers, abilityLookup) {
let bonus = 0;
if (Object.keys(abilityLookup).length) {
const dexMod = abilityLookup[AbilityStatEnum.DEXTERITY];
bonus = AbilityAccessors.getModifier(dexMod) || 0;
}
const ignoreUnarmoredDexBonusModifiers = localModifiers.filter((modifier) => ModifierValidators.isIgnoreUnarmoredDexAcBonusModifier(modifier));
if (ignoreUnarmoredDexBonusModifiers.length) {
return 0;
}
const max = deriveUnarmoredDexBonusMax(globalModifiers);
return Math.min(bonus, max === null ? Number.MAX_VALUE : max);
}
/**
*
* @param armor
* @param dexModifier
* @param modifiers
* @param abilityLookup
*/
export function deriveArmorDexBonus(armor, dexModifier, modifiers, abilityLookup) {
switch (getArmorTypeId(armor)) {
case ArmorTypeEnum.HEAVY_ARMOR:
case ArmorTypeEnum.SHIELD:
return 0;
}
const armorDexBonusMax = deriveArmorDexBonusMax(armor, modifiers, abilityLookup);
return armorDexBonusMax === null ? dexModifier : Math.min(armorDexBonusMax, dexModifier);
}
/**
*
* @param armor
* @param dexModifier
* @param modifiers
* @param abilityLookup
* @param ruleData
*/
export function deriveArmorAc(armor, dexModifier, modifiers, abilityLookup, ruleData) {
let armorAc = ruleData.noArmorAcAmount;
if (armor) {
const definitionArmorClass = getArmorDefinitionAc(armor);
if (definitionArmorClass !== null) {
armorAc = definitionArmorClass;
}
}
return armorAc + deriveArmorDexBonus(armor, dexModifier, modifiers, abilityLookup);
}
/**
*
* @param armorItems
* @param dexModifier
* @param modifiers
* @param abilityLookup
* @param ruleData
*/
export function deriveHighestArmorAcItem(armorItems, dexModifier, modifiers, abilityLookup, ruleData) {
const armorAc = armorItems.map((item) => {
const armorAc = deriveArmorAc(item, dexModifier, modifiers, abilityLookup, ruleData);
const modifierAc = deriveArmorModifierAcTotal(item);
const definitionArmorClass = getArmorDefinitionAc(item);
return {
item,
armorClassWithDex: armorAc + modifierAc,
armorClass: (definitionArmorClass === null ? 0 : definitionArmorClass) + modifierAc,
};
});
return HelperUtils.getLast(armorAc, ['armorClassWithDex']);
}
/**
*
* @param equippedItems
* @param strScore
* @param preferences
* @param modifiers
*/
export function deriveArmorSpeedAdjustmentAmount(equippedItems, strScore, preferences, modifiers) {
if (preferences.encumbranceType === PreferenceEncumbranceTypeEnum.VARIANT ||
modifiers.some(ModifierValidators.isIgnoreHeavyArmorSpeedReductionModifier)) {
return 0;
}
const armorItems = equippedItems.filter(isArmorContract);
const encumberingArmorItem = armorItems.find((item) => {
const reqStr = getItemRequiredStr(item);
return reqStr && reqStr > strScore;
});
if (encumberingArmorItem) {
return -10;
}
return 0;
}
/**
*
* @param item
* @param globalModifiers
* @param isPactWeapon
* @param valueLookupByType
* @param ruleData
*/
export function deriveIsProficient(item, globalModifiers, isPactWeapon, valueLookupByType, ruleData) {
if (isBaseWeapon(item)) {
if (globalModifiers.filter((modifier) => ModifierValidators.isValidWeaponProficiencyModifier(modifier, item))
.length) {
return true;
}
if (valueLookupByType.hasOwnProperty(AdjustmentTypeEnum.WEAPON_PROFICIENCY_LEVEL)) {
const charValues = valueLookupByType[AdjustmentTypeEnum.WEAPON_PROFICIENCY_LEVEL];
if (charValues.find((charValue) => ValueAccessors.getValueIntId(charValue) === getBaseItemId(item))) {
return true;
}
}
return isPactWeapon;
}
if (isBaseArmor(item)) {
if (globalModifiers.filter((modifier) => ModifierValidators.isValidArmorProficiencyModifier(modifier, item))
.length) {
return true;
}
if (valueLookupByType.hasOwnProperty(AdjustmentTypeEnum.ARMOR_PROFICIENCY_LEVEL)) {
const charValues = valueLookupByType[AdjustmentTypeEnum.ARMOR_PROFICIENCY_LEVEL];
if (charValues.find((charValue) => ValueAccessors.getValueIntId(charValue) === getBaseItemId(item))) {
return true;
}
}
}
if (!isBaseGear(item)) {
if (getModifiers(item).filter((modifier) => ModifierValidators.isProficiencySelfModifier(modifier)).length) {
return true;
}
}
return false;
}
/**
*
* @param item
* @param modifiers
*/
export function deriveIsMonkWeapon(item, modifiers) {
if (!isWeaponContract(item)) {
return false;
}
const monkWeaponModifiers = modifiers.filter((modifier) => ModifierValidators.isMonkWeaponModifier(modifier));
return (isMonkWeapon(item) ||
!!monkWeaponModifiers.find((modifier) => ModifierValidators.isValidWeaponSubTypeModifier(modifier, item)));
}
/**
*
* @param item
* @param valueLookup
*/
export function deriveIsCustomized(item, valueLookup) {
return isCustomItem(item)
? false
: ValueValidators.validateHasCustomization(ITEM_CUSTOMIZATION_ADJUSTMENT_TYPES, valueLookup, ValueHacks.hack__toString(getMappingId(item)), ValueHacks.hack__toString(getMappingEntityTypeId(item)));
}
/**
*
* @param item
*/
export function deriveWeaponAdditionalDamage(item) {
let additionalDamages = [];
if (!isWeaponContract(item)) {
return additionalDamages;
}
const itemModifiers = getModifiers(item);
const additionalDamageModifiers = itemModifiers.filter((modifier) => ModifierValidators.isWeaponAdditionalDamageModifier(modifier));
if (additionalDamageModifiers.length) {
additionalDamages = additionalDamageModifiers.map((modifier) => {
const damageValue = ModifierAccessors.getValue(modifier);
return {
damageType: ModifierAccessors.getFriendlySubtypeName(modifier),
damage: damageValue ? damageValue : ModifierAccessors.getDice(modifier),
info: ModifierAccessors.getRestriction(modifier),
id: ModifierAccessors.getId(modifier),
};
});
}
return additionalDamages;
}
/**
*
* @param item
* @param modifiers
* @param ruleData
*/
export function deriveWeaponReach(item, modifiers, ruleData) {
let reach = null;
if (!isBaseWeapon(item)) {
return reach;
}
if (getAttackType(item) === AttackTypeRangeEnum.MELEE) {
const baseReach = ruleData.baseWeaponReach;
const bonusReachModifiers = modifiers.filter((modifier) => ModifierValidators.isBonusMeleeReachModifier(modifier));
const bonusReachModifierTotal = ModifierDerivers.sumModifiers(bonusReachModifiers);
let reachPropertyTotal = 0;
if (hasWeaponProperty(item, WeaponPropertyEnum.REACH)) {
reachPropertyTotal = ruleData.weaponPropertyReachDistance;
}
reach = baseReach + reachPropertyTotal + bonusReachModifierTotal;
}
return reach;
}
/**
*
* @param item
* @param modifiers
* @param martialArtsLevel
*/
export function deriveAvailableWeaponAbilities(item, modifiers, martialArtsLevelScale, isDedicatedWeapon, isPactWeapon, isHexWeapon) {
const availableAbilities = [];
const attackType = getAttackType(item);
if (attackType === AttackTypeRangeEnum.MELEE || hasWeaponProperty(item, WeaponPropertyEnum.FINESSE)) {
availableAbilities.push(AbilityStatEnum.STRENGTH);
}
if (attackType === AttackTypeRangeEnum.RANGED ||
(martialArtsLevelScale !== null && (deriveIsMonkWeapon(item, modifiers) || isDedicatedWeapon)) ||
hasWeaponProperty(item, WeaponPropertyEnum.FINESSE)) {
availableAbilities.push(AbilityStatEnum.DEXTERITY);
}
if (isMagic(item)) {
const magicItemBonusModifiers = modifiers.filter((modifier) => ModifierValidators.isBonusMagicItemAttackWithStatModifier(modifier));
magicItemBonusModifiers.forEach((modifier) => {
const entityId = ModifierAccessors.getEntityId(modifier);
if (entityId !== null && !availableAbilities.includes(entityId)) {
availableAbilities.push(entityId);
}
});
}
//handle PactWeapon modifiers
if (isPactWeapon) {
//Get the enable Pact modifier
const enablePactModifier = modifiers.find((modifier) => ModifierValidators.isEnablePactWeaponModifier(modifier));
//Get the origin of the enable Pact Modifier
const pactDataOrigin = enablePactModifier && ModifierAccessors.getDataOrigin(enablePactModifier);
if (pactDataOrigin) {
const replacementAbilities = deriveReplacementWeaponAbilities(modifiers, pactDataOrigin);
replacementAbilities.forEach((ability) => {
if (!availableAbilities.includes(ability)) {
availableAbilities.push(ability);
}
});
}
}
//handle HexWeapon modifiers
if (isHexWeapon) {
//Get the enable Hex modifier
const enableHexModifier = modifiers.find((modifier) => ModifierValidators.isEnableHexWeaponModifier(modifier));
//Get the origin of the enable Hex Modifier
const hexDataOrigin = enableHexModifier && ModifierAccessors.getDataOrigin(enableHexModifier);
if (hexDataOrigin) {
const replacementAbilities = deriveReplacementWeaponAbilities(modifiers, hexDataOrigin);
replacementAbilities.forEach((ability) => {
if (!availableAbilities.includes(ability)) {
availableAbilities.push(ability);
}
});
}
}
return availableAbilities;
}
export function deriveReplacementWeaponAbilities(modifiers, enableModifierDataOrigin) {
let replacementAbilities = [];
const replaceAbilityModifiers = modifiers.filter((modifier) => ModifierValidators.isReplaceWeaponAbilityModifier(modifier));
if (replaceAbilityModifiers.length > 0) {
replaceAbilityModifiers.forEach((replaceAbilityModifier) => {
var _a, _b;
//get the id of the Ability Stat on the replace weapon ability modifier
const statId = ModifierAccessors.getEntityId(replaceAbilityModifier);
//get the data origin of the replace weapon ability modifier
const dataOrigin = ModifierAccessors.getDataOrigin(replaceAbilityModifier);
if (statId &&
dataOrigin.type === enableModifierDataOrigin.type &&
((_a = dataOrigin.primary.definition) === null || _a === void 0 ? void 0 : _a.id) === ((_b = enableModifierDataOrigin.primary.definition) === null || _b === void 0 ? void 0 : _b.id) &&
!replacementAbilities.includes(statId)) {
replacementAbilities.push(statId);
}
});
}
return replacementAbilities;
}
/**
*
* @param item
* @param modifiers
*/
export function deriveCanWeaponOffhand(item, modifiers) {
if (!isBaseWeapon(item)) {
return false;
}
const ignoreOffhandLightRestrictionsModifiers = modifiers.filter((modifier) => ModifierValidators.isIgnoreOffhandLightRestrictionsModifier(modifier));
let canOffhand = false;
if (hasWeaponProperty(item, WeaponPropertyEnum.LIGHT) || ignoreOffhandLightRestrictionsModifiers.length > 0) {
canOffhand = true;
}
return canOffhand;
}
/**
*
* @param weapon
*/
export function deriveCanPactWeapon(weapon) {
return getAttackType(weapon) === AttackTypeRangeEnum.MELEE;
}
/**
*
* @param item
* @param proficiency
*/
export function deriveCanHexWeapon(item, proficiency) {
if (!isBaseWeapon(item) && !validateIsWeaponLike(item)) {
return false;
}
return proficiency && !hasWeaponProperty(item, WeaponPropertyEnum.TWO_HANDED);
}
/**
*
* @param item
* @param proficiency
*/
export function deriveCanBeDedicatedWeapon(item, proficiency) {
if (!isBaseWeapon(item) && !validateIsWeaponLike(item)) {
return false;
}
return (proficiency &&
!hasWeaponProperty(item, WeaponPropertyEnum.HEAVY) &&
!hasWeaponProperty(item, WeaponPropertyEnum.SPECIAL));
}
/**
*
* @param item
* @param modifiers
*/
export function deriveMasteryName(item, modifiers) {
if (getBaseTypeId(item) !== ItemBaseTypeIdEnum.WEAPON) {
return null;
}
//get modifiers that are weaponmastery
const masteryModifiers = modifiers.filter((modifier) => isValidWeaponMasteryModifier(modifier));
//get entity id from modifier and match to item baseWeaponid
const masteryModifier = masteryModifiers.find((modifier) => ModifierAccessors.getEntityId(modifier) === getBaseItemId(item));
return masteryModifier ? ModifierAccessors.getFriendlySubtypeName(masteryModifier) : null;
}
/**
*
* @param item
* @param valueLookup
*/
export function deriveName(item, valueLookup) {
const override = ValueUtils.getKeyValue(valueLookup, AdjustmentTypeEnum.NAME_OVERRIDE, ValueHacks.hack__toString(getMappingId(item)), ValueHacks.hack__toString(getMappingEntityTypeId(item)));
if (override !== null && override !== '') {
return override;
}
const definitionName = getDefinitionName(item);
if (definitionName === null) {
return '';
}
return definitionName;
}
/**
*
* @param item
* @param valueLookup
*/
export function deriveNotes(item, valueLookup) {
const notes = ValueUtils.getKeyValue(valueLookup, AdjustmentTypeEnum.NOTES, ValueHacks.hack__toString(getMappingId(item)), ValueHacks.hack__toString(getMappingEntityTypeId(item)));
if (notes !== null && notes !== '') {
return notes;
}
return null;
}
/**
*
* @param item
* @param modifiers
* @param ruleData
*/
export function deriveProperties(item, modifiers, ruleData) {
let properties = [];
if (isArmorContract(item)) {
return properties;
}
if (isWeaponContract(item)) {
properties = getDefinitionProperties(item);
}
else if (hasItemWeaponBehaviors(item)) {
const behavior = getItemWeaponBehaviors(item);
const firstBehavior = behavior[0];
properties = firstBehavior.properties === null ? [] : firstBehavior.properties;
}
const weaponPropertyModifiers = modifiers.filter((modifier) => ModifierValidators.isWeaponPropertyModifier(modifier));
const weaponPropertyModifierInfos = weaponPropertyModifiers
.map((modifier) => {
const entityId = ModifierAccessors.getEntityId(modifier);
if (entityId === null) {
return null;
}
const configWeaponProperty = RuleDataUtils.getWeaponPropertyInfo(entityId, ruleData);
if (configWeaponProperty === null) {
return null;
}
return {
id: configWeaponProperty.id,
description: configWeaponProperty.description,
name: configWeaponProperty.name,
notes: '',
};
})
.filter(TypeScriptUtils.isNotNullOrUndefined);
properties = uniqBy([...properties, ...weaponPropertyModifierInfos], (property) => property.name);
const ignoreWeaponPropertyModifiers = modifiers.filter((modifier) => ModifierValidators.isIgnoreWeaponPropertyModifier(modifier));
if (ignoreWeaponPropertyModifiers.length) {
const ignoreWeaponPropertyModifiersNames = ignoreWeaponPropertyModifiers
.map((modifier) => ModifierAccessors.getFriendlySubtypeName(modifier))
.filter(TypeScriptUtils.isNotNullOrUndefined);
properties = properties.filter((property) => property.name !== null && !ignoreWeaponPropertyModifiersNames.includes(property.name));
}
return properties;
}
/**
*
* @param armorItems
*/
export function deriveValidGlobalModifiersFromArmor(armorItems) {
if (!armorItems.length) {
return [];
}
// get all armor modifiers that aren't AC bonuses
return flatten(armorItems.map((item) => getModifiers(item).filter((modifier) => !ModifierValidators.isBonusArmorModifier(modifier) &&
!ModifierValidators.isProficiencySelfModifier(modifier) &&
isEquipped(item) &&
ModifierValidators.isValidAttunedItemModifier(modifier, item))));
}
/**
*
* @param weaponItems
*/
export function deriveValidGlobalModifiersFromWeapons(weaponItems) {
if (!weaponItems.length) {
return [];
}
// get all weapon modifiers that aren't bonus magic
return flatten(weaponItems.map((item) => getModifiers(item).filter((modifier) => !ModifierValidators.isBonusMagicModifier(modifier) &&
!ModifierValidators.isProficiencySelfModifier(modifier) &&
!ModifierValidators.isReplaceDamageTypeModifier(modifier) &&
!ModifierValidators.isWeaponPropertyModifier(modifier) &&
!ModifierValidators.isWeaponAdditionalDamageModifier(modifier) &&
isEquipped(item) &&
ModifierValidators.isValidAttunedItemModifier(modifier, item))));
}
/**
*
* @param gearItems
*/
export function deriveValidGlobalModifiersFromGear(gearItems) {
if (!gearItems.length) {
return [];
}
return flatten(gearItems.map((item) => getModifiers(item).filter((modifier) => isEquipped(item) &&
(!hasItemWeaponBehaviors(item) ||
(hasItemWeaponBehaviors(item) && !ModifierValidators.isBonusMagicModifier(modifier))) &&
!ModifierValidators.isProficiencySelfModifier(modifier) &&
ModifierValidators.isValidAttunedItemModifier(modifier, item))));
}
/**
*
* @param item
*/
export function deriveValidArmorStatModifiers(item) {
return getModifiers(item).filter((modifier) => ModifierValidators.isBonusArmorModifier(modifier) &&
ModifierValidators.isValidAttunedItemModifier(modifier, item));
}
/**
*
* @param item
*/
export function deriveArmorModifierAcTotal(item) {
const validModifiers = deriveValidArmorStatModifiers(item);
return ModifierDerivers.sumModifiers(validModifiers);
}
/**
*
* @param item
* @param modifiers
*/
export function deriveDamageType(item, modifiers) {
let damageType = getDefinitionDamageType(item);
const replaceDamageTypeModifiers = modifiers.filter((modifier) => ModifierValidators.isReplaceDamageTypeModifier(modifier));
if (replaceDamageTypeModifiers.length) {
damageType = ModifierAccessors.getFriendlySubtypeName(replaceDamageTypeModifiers[0]);
}
return damageType;
}
/**
*
* @param item
*/
export function deriveDefinitionKey(item) {
return DefinitionHacks.hack__generateDefinitionKey(FUTURE_ITEM_DEFINITION_TYPE, getDefinitionId(item));
}
/**
*
* @param infusion
* @param valueLookup
* @param item
*/
export function deriveInfusionActions(infusion, valueLookup, item) {
if (infusion === null) {
return [];
}
const itemLimitedUse = getLimitedUse(item);
const baseActions = ActionGenerators.generateDataOriginBaseActions(InfusionAccessors.getDefinitionActions(infusion), valueLookup, DataOriginTypeEnum.ITEM, item);
return baseActions.map((action) => {
const actionLimitedUse = ActionAccessors.getLimitedUse(action);
// TODO I don't like this but im not sure how to fix it. and moving on because of time
// possibly calculate in ActionDerivers.deriveLimitedUse
if (actionLimitedUse !== null) {
return Object.assign(Object.assign({}, action), { limitedUse: Object.assign(Object.assign({}, actionLimitedUse), { numberUsed: itemLimitedUse === null ? 0 : LimitedUseAccessors.getNumberUsed(itemLimitedUse) }) });
}
return action;
});
}
/**
*
* @param infusionActions
* @param itemLimitedUse
*/
export function deriveLimitedUse(infusionActions, itemLimitedUse) {
// if we have no infusion actions, we will just keep what we have
if (infusionActions.length === 0) {
return itemLimitedUse;
}
// This is for when we updated charges used to null, but still have limited use object
if (itemLimitedUse !== null && LimitedUseAccessors.getNumberUsed(itemLimitedUse) === null) {
return null;
}
const limitedUseInfusionAction = infusionActions.find((action) => ActionAccessors.getLimitedUse(action) !== null);
if (!limitedUseInfusionAction) {
return itemLimitedUse;
}
// We made assumption that all of the limited use must be the same for all actions on items
const actionLimitedUse = ActionAccessors.getLimitedUse(limitedUseInfusionAction);
if (actionLimitedUse) {
return {
resetType: actionLimitedUse.resetType === null ? null : actionLimitedUse.resetType.toString(),
resetTypeDescription: null,
maxUses: actionLimitedUse.maxUses,
numberUsed: itemLimitedUse === null ? 0 : itemLimitedUse.numberUsed,
};
}
return itemLimitedUse;
}
/**
*
* @param item
* @param infusion
*/
export function deriveIsMagic(item, infusion) {
if (infusion) {
return true;
}
return getDefinitionMagic(item);
}
/**
*
* @param item
*/
export function deriveDefaultIsDisplayAsAttack(item) {
if (!isEquipped(item)) {
return false;
}
return validateIsWeaponLike(item);
}
/**
*
* @param item
* @param ruleData
*/
export function deriveType(item, ruleData) {
if (isArmorContract(item)) {
const armorTypeId = getArmorTypeId(item);
if (armorTypeId !== null) {
const armorType = HelperUtils.lookupDataOrFallback(RuleDataAccessors.getArmorTypeLookup(ruleData), armorTypeId);
if (armorType !== null) {
return armorType.name;
}
}
}
else if (isWeaponContract(item)) {
if (isAmmunition(item)) {
return 'Ammunition';
}
const weapon = RuleDataUtils.getWeaponByEntityId(getBaseItemId(item), getBaseTypeId(item), ruleData);
if (weapon !== null) {
return weapon.name;
}
}
else if (isGearContract(item)) {
if (!getDefinitionMagic(item)) {
return 'Gear';
}
const gearTypeId = getGearTypeId(item);
switch (gearTypeId) {
case ItemTypeEnum.ARMOR:
return 'Armor';
case ItemTypeEnum.ARTIFACT:
return 'Artifact';
case ItemTypeEnum.POTION:
return 'Potion';
case ItemTypeEnum.RING:
return 'Ring';
case ItemTypeEnum.ROD:
return 'Rod';
case ItemTypeEnum.SCROLL:
return 'Scroll';
case ItemTypeEnum.STAFF:
return 'Staff';
case ItemTypeEnum.WAND:
return 'Wand';
case ItemTypeEnum.WEAPON:
return 'Weapon';
case ItemTypeEnum.WONDROUS_ITEM:
return 'Wondrous item';
default:
// not implemented
}
}
else if (isCustomItem(item)) {
return 'Custom';
}
return null;
}
/**
*
* @param item
* @param notes
*/
export function deriveOriginalCustomContract(item, notes) {
if (isCustomItem(item)) {
return {
id: getDefinitionId(item),
name: getDefinitionName(item),
weight: getWeightDefinition(item),
cost: getDefinitionCost(item),
quantity: getQuantity(item),
description: getDescription(item),
notes,
};
}
return null;
}
export function derivePrimarySourceCategoryId(item, ruleData) {
var _a;
const primarySources = getSources(item).filter(CharacterValidators.isPrimarySource);
if (!primarySources.length) {
return 0;
}
const source = HelperUtils.lookupDataOrFallback(RuleDataAccessors.getSourceDataLookup(ruleData), primarySources[0].sourceId);
return ((_a = source === null || source === void 0 ? void 0 : source.sourceCategory) === null || _a === void 0 ? void 0 : _a.id) || 0;
}
export function deriveSecondarySourceCategoryIds(item, ruleData) {
const secondarySources = getSources(item).filter(CharacterValidators.isSecondarySource);
return secondarySources.map((source) => {
var _a;
const sourceData = HelperUtils.lookupDataOrFallback(RuleDataAccessors.getSourceDataLookup(ruleData), source.sourceId);
return ((_a = sourceData === null || sourceData === void 0 ? void 0 : sourceData.sourceCategory) === null || _a === void 0 ? void 0 : _a.id) || 0;
});
}
@@ -0,0 +1,439 @@
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
import { keyBy, orderBy } from 'lodash';
import { TypeScriptUtils } from '../../utils';
import { CharacterDerivers } from '../Character';
import { ContainerTypeEnum } from '../Container';
import { StealthCheckTypeEnum } from '../Core';
import { DataOriginDataInfoKeyEnum, DataOriginTypeEnum } from '../DataOrigin';
import { DefinitionHacks } from '../Definition';
import { DiceAccessors, DiceDerivers, DiceUtils } from '../Dice';
import { HelperUtils } from '../Helper';
import { InfusionAccessors } from '../Infusion';
import { LimitedUseAccessors, LimitedUseUtils } from '../LimitedUse';
import { ModifierDerivers, ModifierGenerators, ModifierValidators, } from '../Modifier';
import { RuleDataAccessors } from '../RuleData';
import { SpellGenerators } from '../Spell';
import { AdjustmentTypeEnum, ValueAccessors, ValueHacks, ValueUtils, } from '../Value';
import { getDefinitionDamage, getEntityTypeId, getFixedDamage, getId, getItemWeaponBehaviors, getLimitedUse, getMappingEntityTypeId, getMappingId, getModifiers, getName, getUniqueKey, isAttuned, isDisplayAsAttack, isEquipped, } from './accessors';
import { ItemBaseTypeEnum, ItemRarityNameEnum, WeaponPropertyEnum } from './constants';
import { deriveArmorClass, deriveAvailableWeaponAbilities, deriveAvatarUrl, deriveBaseType, deriveCanAttune, deriveCanBeDedicatedWeapon, deriveCanEquip, deriveCanHexWeapon, deriveCanWeaponOffhand, deriveCapacityWeight, deriveCategoryInfo, deriveCost, deriveDamageType, deriveDefinitionKey, deriveInfusionActions, deriveIsAdamantine, deriveIsCustomized, deriveIsDisplayAsAttack, deriveIsMagic, deriveIsMonkWeapon, deriveIsOffhand, deriveIsProficient, deriveIsSilvered, deriveLimitedUse, deriveMasteryName, deriveName, deriveNotes, deriveOriginalCustomContract, derivePrimarySourceCategoryId, deriveProperties, deriveSecondarySourceCategoryIds, deriveStealthCheck, deriveType, deriveValidGlobalModifiersFromArmor, deriveValidGlobalModifiersFromGear, deriveValidGlobalModifiersFromWeapons, deriveWeaponAdditionalDamage, deriveWeaponReach, deriveWeight, } from './derivers';
import { hasItemWeaponBehaviors, hasWeaponProperty, isAmmunition, isArmorContract, isBaseWeapon, isGearContract, isWeaponContract, } from './utils';
import { isCustomItem, isEquippedToCurrentCharacter, validateIsWeaponLike } from './validators';
/**
*
* @param customItems
* @param ruleData
* @param characterId
*/
export function generateCustomItems(customItems, ruleData, characterId) {
return customItems.map((item) => generateCustomItem(item, ruleData, characterId));
}
/**
* TODO v5.1: should be able to remove this generator and all usages after mobile can support customItems as Items
* @param customItem
* @param ruleData
* @param characterId
*/
export function generateCustomItem(customItem, ruleData, characterId) {
var _a, _b;
const { id, quantity, notes, weight } = customItem, customItemData = __rest(customItem, ["id", "quantity", "notes", "weight"]);
const definition = Object.assign(Object.assign({}, customItemData), { armorTypeId: null, armorClass: null, attackType: null, attunementDescription: null, avatarUrl: RuleDataAccessors.getDefaultGearImageUrl(ruleData), baseArmorName: null, baseItemId: null, baseTypeId: -1, bundleSize: 1, canAttune: false, canBeAddedToInventory: false, canEquip: false, capacity: null, capacityWeight: 0, categoryId: null, damage: null, damageType: null, entityTypeId: -1, filterType: null, fixedDamage: null, gearTypeId: null, grantedModifiers: [], groupedId: null, id: -1, isConsumable: false, isContainer: false, isHomebrew: false, isLegacy: false, isMonkWeapon: false, isPack: false, largeAvatarUrl: null, levelInfusionGranted: null, longRange: null, magic: false, properties: [], range: null, rarity: ItemRarityNameEnum.COMMON, snippet: null, sourcePageNumber: null, sources: [], stackable: true, stealthCheck: StealthCheckTypeEnum.NONE, strengthRequirement: null, subType: null, tags: [], type: 'Custom', version: null, weaponBehaviors: [], weight: weight !== null && weight !== void 0 ? weight : 0, weightMultiplier: 1 });
const simulatedContract = {
chargesUsed: 0,
containerEntityId: characterId,
containerEntityTypeId: ContainerTypeEnum.CHARACTER,
containerDefinitionKey: DefinitionHacks.hack__generateDefinitionKey(ContainerTypeEnum.CHARACTER, characterId),
definition,
currency: null,
definitionId: -1,
definitionTypeId: -1,
displayAsAttack: false,
entityTypeId: -1,
equipped: false,
equippedEntityId: null,
equippedEntityTypeId: null,
id,
isAttuned: false,
limitedUse: null,
quantity: quantity !== null && quantity !== void 0 ? quantity : 1,
};
return Object.assign(Object.assign({}, simulatedContract), { avatarUrl: (_a = definition.avatarUrl) !== null && _a !== void 0 ? _a : '', armorClass: null, baseItemType: ItemBaseTypeEnum.GEAR, additionalDamages: [], versatileDamage: null, reach: null, canAttune: false, canBeDedicatedWeapon: false, canEquip: false, canHexWeapon: false, canOffhand: false, canPactWeapon: false, categoryInfo: null, definitionKey: '', hexWeaponEnabled: false, isDedicatedWeapon: false, isHexWeapon: false, isPactWeapon: false, metaItems: [], pactWeaponEnabled: false, toHit: null, damage: null, infusion: null, infusionActions: [], isOffhand: false, isAdamantine: false, isMagic: false, isSilvered: false, damageType: definition.damageType, displayAsAttack: false, isCustom: true, isCustomized: false, isDisplayAsAttack: false, proficiency: false, modifiers: [], originalContract: customItem, spells: [], type: null, name: (_b = definition.name) !== null && _b !== void 0 ? _b : 'Unidentified Item', cost: deriveCost(simulatedContract, {}), weight: deriveWeight(simulatedContract, {}), capacityWeight: deriveCapacityWeight(simulatedContract, {}), notes, properties: [], stealthCheck: StealthCheckTypeEnum.NONE, masteryName: null, primarySourceCategoryId: -1, secondarySourceCategoryIds: [] });
}
/**
*
* @param baseWeapon
* @param parentItem
* @param valueLookup
* @param ruleData
*/
export function generateWeaponBehaviorBaseItem(baseWeapon, parentItem, valueLookup, ruleData) {
if (parentItem.definition === null) {
return null;
}
const item = Object.assign(Object.assign({}, parentItem), { definition: Object.assign(Object.assign(Object.assign({}, parentItem.definition), baseWeapon), { fixedDamage: null }) });
return Object.assign(Object.assign({}, item), { avatarUrl: deriveAvatarUrl(item, ruleData), baseItemType: ItemBaseTypeEnum.WEAPON, canAttune: deriveCanAttune(item, null), canEquip: deriveCanEquip(item, null), categoryInfo: deriveCategoryInfo(item, ruleData), cost: deriveCost(item, valueLookup), damageType: deriveDamageType(item, []), definitionKey: deriveDefinitionKey(item), isAdamantine: deriveIsAdamantine(item, valueLookup), isCustom: isCustomItem(item), isCustomized: deriveIsCustomized(item, valueLookup), isDisplayAsAttack: deriveIsDisplayAsAttack(item, valueLookup), isOffhand: deriveIsOffhand(item, valueLookup), isSilvered: deriveIsSilvered(item, valueLookup), isMagic: deriveIsMagic(item, null), infusion: null, infusionActions: [], modifiers: getModifiers(parentItem), name: deriveName(item, valueLookup), notes: deriveNotes(item, valueLookup), originalContract: null, properties: deriveProperties(item, [], ruleData), spells: [], type: deriveType(item, ruleData), weight: deriveWeight(item, valueLookup), capacityWeight: deriveCapacityWeight(item, valueLookup) });
}
/**
*
* @param items
* @param abilityLookup
* @param proficiencyBonus
* @param modifiers
* @param entityValueLookup
* @param typeValueLookup
* @param valueLookup
* @param martialArtsLevelScale
* @param ruleData
* @param hack__characterSpecialWeaponPropertiesEnabled
*/
export function generateItems(items, abilityLookup, proficiencyBonus, modifiers, entityValueLookup, typeValueLookup, valueLookup, martialArtsLevelScale, ruleData, hack__characterSpecialWeaponPropertiesEnabled) {
const { hexWeaponEnabled, pactWeaponEnabled, improvedPactWeaponEnabled, dedicatedWeaponEnabled } = hack__characterSpecialWeaponPropertiesEnabled;
return items.map((item) => generateItem(item, abilityLookup, proficiencyBonus, modifiers, entityValueLookup, typeValueLookup, valueLookup, martialArtsLevelScale, hexWeaponEnabled, pactWeaponEnabled, improvedPactWeaponEnabled, ruleData, dedicatedWeaponEnabled));
}
/**
*
* @param item
* @param abilityLookup
* @param proficiencyBonus
* @param modifiers
* @param entityValueLookup
* @param typeValueLookup
* @param valueLookup
* @param martialArtsLevel
* @param hexWeaponEnabled
* @param pactWeaponEnabled
* @param improvedPactWeaponEnabled
* @param ruleData
* @param dedicatedWeaponEnabled
*/
export function generateItem(item, abilityLookup, proficiencyBonus, modifiers, entityValueLookup, typeValueLookup, valueLookup, martialArtsLevelScale, hexWeaponEnabled, pactWeaponEnabled, improvedPactWeaponEnabled, ruleData, dedicatedWeaponEnabled) {
const mappingId = getMappingId(item);
const mappingIdString = ValueHacks.hack__toString(mappingId);
const mappingEntityTypeId = getMappingEntityTypeId(item);
const mappingEntityTypeIdString = ValueHacks.hack__toString(mappingEntityTypeId);
const dataLookup = ValueUtils.getEntityData(entityValueLookup, mappingIdString, mappingEntityTypeIdString);
const isOffhand = ValueUtils.getIsOffhand(valueLookup, mappingIdString, mappingEntityTypeIdString);
const damageBonus = ValueUtils.getFixedValueBonus(valueLookup, mappingIdString, mappingEntityTypeIdString);
const toHitBonus = ValueUtils.getToHitBonus(valueLookup, mappingIdString, mappingEntityTypeIdString);
const toHitOverride = ValueUtils.getToHitOverride(valueLookup, mappingIdString, mappingEntityTypeIdString);
const itemModifiers = getModifiers(item);
const isMonkWeapon = deriveIsMonkWeapon(item, modifiers);
const isOffhandWeapon = !!isOffhand;
const canOffhand = deriveCanWeaponOffhand(item, modifiers);
let canPactWeapon = false;
let isPactWeapon = false;
if (pactWeaponEnabled && (isBaseWeapon(item) || validateIsWeaponLike(item)) && !isAmmunition(item)) {
canPactWeapon = pactWeaponEnabled;
isPactWeapon =
dataLookup.hasOwnProperty(AdjustmentTypeEnum.IS_PACT_WEAPON) &&
!!ValueAccessors.getValue(dataLookup[AdjustmentTypeEnum.IS_PACT_WEAPON]);
}
const proficiency = deriveIsProficient(item, modifiers, isPactWeapon, typeValueLookup, ruleData);
let canHexWeapon = false;
let isHexWeapon = false;
if (hexWeaponEnabled && (isBaseWeapon(item) || validateIsWeaponLike(item)) && !isAmmunition(item)) {
canHexWeapon = deriveCanHexWeapon(item, proficiency);
isHexWeapon =
dataLookup.hasOwnProperty(AdjustmentTypeEnum.IS_HEXBLADE) &&
!!ValueAccessors.getValue(dataLookup[AdjustmentTypeEnum.IS_HEXBLADE]);
}
let canBeDedicatedWeapon = false;
let isDedicatedWeapon = false;
if (dedicatedWeaponEnabled && (isBaseWeapon(item) || validateIsWeaponLike(item)) && !isAmmunition(item)) {
canBeDedicatedWeapon = deriveCanBeDedicatedWeapon(item, proficiency);
isDedicatedWeapon =
dataLookup.hasOwnProperty(AdjustmentTypeEnum.IS_DEDICATED_WEAPON) &&
!!ValueAccessors.getValue(dataLookup[AdjustmentTypeEnum.IS_DEDICATED_WEAPON]);
}
const reach = deriveWeaponReach(item, modifiers, ruleData);
const additionalDamages = deriveWeaponAdditionalDamage(item);
let newDamage = null;
let versatileDamage = null;
let bestAbilityToHit = null;
const metaItems = [];
if (isBaseWeapon(item)) {
const adjustmentDamageBonus = damageBonus === null ? 0 : damageBonus;
const adjustmentToHitBonus = toHitBonus === null ? 0 : toHitBonus;
// get the current item bonus magic modifiers
const bonusMagicModifiers = itemModifiers.filter((modifier) => ModifierValidators.isBonusMagicModifier(modifier) &&
ModifierValidators.isValidAttunedItemModifier(modifier, item));
let bonusMagicModifierTotal = ModifierDerivers.sumModifiers(bonusMagicModifiers);
if (bonusMagicModifierTotal <= 0 && improvedPactWeaponEnabled && isPactWeapon) {
bonusMagicModifierTotal = 1;
// TODO hard coded for now, should be supplied from server data
}
// get anything else that may increase bonus magic
const globalBonusMagicModifiers = modifiers.filter((modifier) => ModifierValidators.isBonusMagicModifier(modifier));
const globalBonusMagicModifierTotal = ModifierDerivers.sumModifiers(globalBonusMagicModifiers);
const totalModifierBonusMagic = globalBonusMagicModifierTotal + bonusMagicModifierTotal;
// test for proficiency
const weaponProficiencyBonus = proficiency ? proficiencyBonus : 0;
// figure out the best stat to use with weapon
let bestAbilityDamageBonus = 0;
if (isBaseWeapon(item) && !isAmmunition(item)) {
// find out about bonus toHit for current weapon
const bonusToHitModifiers = modifiers.filter((modifier) => ModifierValidators.isValidBonusWeaponToHitModifier(modifier, item, ruleData));
const bonusToHitModifierTotal = ModifierDerivers.sumModifiers(bonusToHitModifiers, abilityLookup);
//get all the available ability stats for the weapon
const availableAbilities = deriveAvailableWeaponAbilities(item, modifiers, martialArtsLevelScale, isDedicatedWeapon, isPactWeapon, isHexWeapon);
// transform the available ability stats to have toHit and damage bonuses
const abilityPossibilities = CharacterDerivers.deriveAttackAbilityPossibilities(availableAbilities, modifiers, weaponProficiencyBonus, abilityLookup);
const bestAbility = HelperUtils.getLast(abilityPossibilities, ['toHit', 'modifier']);
if (bestAbility !== null) {
bestAbilityToHit =
bestAbility.toHit + adjustmentToHitBonus + totalModifierBonusMagic + bonusToHitModifierTotal;
if (toHitOverride !== null) {
bestAbilityToHit = toHitOverride;
}
const ignoreOffhandModifierRestrictionsModifiers = modifiers.filter((modifier) => ModifierValidators.isIgnoreOffhandModifierRestrictionsModifier(modifier));
if (isOffhandWeapon && ignoreOffhandModifierRestrictionsModifiers.length === 0) {
bestAbilityDamageBonus = Math.min(0, bestAbility.damageBonus);
}
else {
bestAbilityDamageBonus = bestAbility.damageBonus;
}
}
}
// generate new damage based on all the above info
const damage = getDefinitionDamage(item);
if (damage) {
const allDamageDice = [damage];
let martialArtsDie = null;
if (martialArtsLevelScale !== null && (isMonkWeapon || isDedicatedWeapon)) {
martialArtsDie = DiceDerivers.deriveMartialArtsDamageDie(martialArtsLevelScale);
allDamageDice.push(martialArtsDie);
}
const highestDamageDie = DiceUtils.getHighestDie(allDamageDice);
if (highestDamageDie !== null) {
const damageModifiers = modifiers.filter((modifier) => ModifierValidators.isValidWeaponDamageModifier(modifier, item, ruleData));
const damageModifierTotal = ModifierDerivers.sumModifiers(damageModifiers, abilityLookup);
const fixedDieValue = DiceAccessors.getFixedValue(highestDamageDie);
const damageFixedValue = fixedDieValue === null ? 0 : fixedDieValue;
newDamage = Object.assign(Object.assign({}, highestDamageDie), { fixedValue: damageFixedValue +
totalModifierBonusMagic +
damageModifierTotal +
bestAbilityDamageBonus +
adjustmentDamageBonus });
if (!isOffhandWeapon && hasWeaponProperty(item, WeaponPropertyEnum.VERSATILE)) {
const versatileDamageModifiers = modifiers.filter((modifier) => ModifierValidators.isValidWeaponDamageModifier(modifier, item, ruleData, true));
const versatileDamageModifierTotal = ModifierDerivers.sumModifiers(versatileDamageModifiers, abilityLookup);
const versatileDieValuePossibilities = [];
const diceValue = DiceAccessors.getValue(damage);
const baseDamageVersatileDie = diceValue === null ? 0 : ruleData.versatileDieLookup[diceValue];
versatileDieValuePossibilities.push(baseDamageVersatileDie);
if (martialArtsLevelScale !== null && (isMonkWeapon || isDedicatedWeapon) && martialArtsDie) {
const martialArtsDamageVersatileDie = DiceAccessors.getValue(martialArtsDie);
if (martialArtsDamageVersatileDie !== null) {
versatileDieValuePossibilities.push(martialArtsDamageVersatileDie);
}
}
versatileDamage = Object.assign(Object.assign({}, newDamage), { diceValue: Math.max(...versatileDieValuePossibilities), fixedValue: damageFixedValue +
totalModifierBonusMagic +
versatileDamageModifierTotal +
bestAbilityDamageBonus +
adjustmentDamageBonus });
}
}
}
else {
const fixedDamage = getFixedDamage(item);
if (fixedDamage) {
newDamage = fixedDamage + totalModifierBonusMagic + bestAbilityDamageBonus + adjustmentDamageBonus;
}
}
if (martialArtsLevelScale !== null && (isMonkWeapon || isDedicatedWeapon)) {
metaItems.push('Martial Arts');
}
}
return Object.assign(Object.assign({}, item), { armorClass: deriveArmorClass(item), stealthCheck: deriveStealthCheck(item, modifiers), toHit: bestAbilityToHit, proficiency,
metaItems,
canOffhand,
isHexWeapon,
canHexWeapon,
isPactWeapon,
canPactWeapon,
isDedicatedWeapon,
canBeDedicatedWeapon,
hexWeaponEnabled,
pactWeaponEnabled, damage: newDamage, versatileDamage,
additionalDamages,
reach, masteryName: deriveMasteryName(item, modifiers), primarySourceCategoryId: derivePrimarySourceCategoryId(item, ruleData), secondarySourceCategoryIds: deriveSecondarySourceCategoryIds(item, ruleData) });
}
/**
*
* @param items
* @param modifierLookup
* @param spellLookup
* @param valueLookup
* @param inventoryInfusionLookup
* @param ruleData
*/
export function generateBaseItems(items, modifierLookup, spellLookup, valueLookup, inventoryInfusionLookup, ruleData) {
return items.map((item) => generateBaseItem(item, modifierLookup, spellLookup, valueLookup, inventoryInfusionLookup, ruleData));
}
/**
*
* @param item
* @param modifierLookup
* @param spellLookup
* @param valueLookup
* @param inventoryInfusionLookup
* @param ruleData
*/
export function generateBaseItem(item, modifierLookup, spellLookup, valueLookup, inventoryInfusionLookup, ruleData) {
const id = getId(item);
const entityTypeId = getEntityTypeId(item);
const infusion = HelperUtils.lookupDataOrFallback(inventoryInfusionLookup, getMappingId(item));
let infusionModifiers = [];
if (infusion) {
const selectedModifierData = InfusionAccessors.getSelectedModifierData(infusion);
if (selectedModifierData && selectedModifierData.modifiers) {
infusionModifiers = selectedModifierData.modifiers;
}
}
const modifiers = ModifierGenerators.generateModifiers(id, entityTypeId, modifierLookup, DataOriginTypeEnum.ITEM, item, null, infusionModifiers);
const baseSpells = SpellGenerators.generateBaseSpells(id, entityTypeId, spellLookup, DataOriginTypeEnum.ITEM, item, null, ruleData, {}, {});
const infusionActions = deriveInfusionActions(infusion, valueLookup, item);
const notes = deriveNotes(item, valueLookup);
return Object.assign(Object.assign({}, item), { avatarUrl: deriveAvatarUrl(item, ruleData), baseItemType: deriveBaseType(item), canAttune: deriveCanAttune(item, infusion), canEquip: deriveCanEquip(item, infusion), categoryInfo: deriveCategoryInfo(item, ruleData), cost: deriveCost(item, valueLookup), definitionKey: deriveDefinitionKey(item), damageType: deriveDamageType(item, modifiers), infusion,
infusionActions, isAdamantine: deriveIsAdamantine(item, valueLookup), isCustom: isCustomItem(item), isCustomized: deriveIsCustomized(item, valueLookup), isDisplayAsAttack: deriveIsDisplayAsAttack(item, valueLookup), isMagic: deriveIsMagic(item, infusion), isOffhand: deriveIsOffhand(item, valueLookup), isSilvered: deriveIsSilvered(item, valueLookup), limitedUse: deriveLimitedUse(infusionActions, getLimitedUse(item)), modifiers, name: deriveName(item, valueLookup), notes, originalContract: deriveOriginalCustomContract(item, notes), properties: deriveProperties(item, modifiers, ruleData), spells: baseSpells.map((spell) => updateBaseItemSpells(item, spell)), type: deriveType(item, ruleData), weight: deriveWeight(item, valueLookup), capacityWeight: deriveCapacityWeight(item, valueLookup) });
}
/**
*
* @param item
* @param spell
*/
function updateBaseItemSpells(item, spell) {
let spellLimitedUse = null;
if (spell.limitedUse) {
spellLimitedUse = spell.limitedUse;
const itemLimitedUse = getLimitedUse(item);
if (itemLimitedUse !== null) {
spellLimitedUse = Object.assign(Object.assign(Object.assign({}, spellLimitedUse), item.limitedUse), {
// TODO fix when resetType on ItemLimitedUse is no longer a string
resetType: LimitedUseUtils.getResetTypeIdByName(LimitedUseAccessors.getResetTypeName(itemLimitedUse)) });
}
}
return Object.assign(Object.assign({}, spell), { limitedUse: spellLimitedUse });
}
/**
*
* @param items
* @param abilityLookup
* @param proficiencyBonus
* @param modifiers
* @param entityValueLookup
* @param typeValueLookup
* @param valueLookup
* @param martialArtsLevel
* @param ruleData
* @param hack__characterSpecialWeaponPropertiesEnabled
* @param characterId
*/
export function generateGearWeaponItems(items, abilityLookup, proficiencyBonus, modifiers, entityValueLookup, typeValueLookup, valueLookup, martialArtsLevelScale, ruleData, hack__characterSpecialWeaponPropertiesEnabled, characterId) {
const { hexWeaponEnabled, pactWeaponEnabled, improvedPactWeaponEnabled, dedicatedWeaponEnabled } = hack__characterSpecialWeaponPropertiesEnabled;
return items
.filter(isGearContract)
.filter((item) => hasItemWeaponBehaviors(item))
.filter((item) => isEquippedToCurrentCharacter(item, characterId))
.map((gearItem) => getItemWeaponBehaviors(gearItem)
.map((itemDefinition) => {
const simulatedBaseItem = generateWeaponBehaviorBaseItem(itemDefinition, gearItem, valueLookup, ruleData);
if (simulatedBaseItem === null) {
return null;
}
return generateItem(simulatedBaseItem, abilityLookup, proficiencyBonus, modifiers, entityValueLookup, typeValueLookup, valueLookup, martialArtsLevelScale, hexWeaponEnabled, pactWeaponEnabled, improvedPactWeaponEnabled, ruleData, dedicatedWeaponEnabled);
})
.filter(TypeScriptUtils.isNotNullOrUndefined))
.reduce((acc, itemBehaviors) => {
acc.push(...itemBehaviors);
return acc;
}, []);
}
/**
*
* @param ruleData
* @param modifiers
*/
export function generateAttunedItemCountMax(ruleData, modifiers) {
const setAttunementModifiers = modifiers.filter((modifier) => ModifierValidators.isSetAttunementSlotsModifier(modifier));
if (setAttunementModifiers.length) {
const highestModifierValue = ModifierDerivers.deriveHighestModifierValue(setAttunementModifiers, null);
return HelperUtils.clampInt(highestModifierValue, RuleDataAccessors.getMinAttunedItemCountMax(ruleData), RuleDataAccessors.getMaxAttunedItemCountMax(ruleData));
}
return RuleDataAccessors.getDefaultAttunedItemCountMax(ruleData);
}
/**
*
* @param attunedItems
* @param attunedItemCountMax
*/
export function generateHasMaxAttunedItems(attunedItems, attunedItemCountMax) {
return attunedItems.length >= attunedItemCountMax;
}
/**
*
* @param items
*/
export function generateInventoryLookup(items) {
return keyBy(items, (item) => getMappingId(item));
}
/**
*
* @param items
* @param characterId
*/
export function generateEquippedItems(items, characterId) {
return items.filter((item) => isEquipped(item) && isEquippedToCurrentCharacter(item, characterId));
}
/**
*
* @param equippedItems
* @param attunedItemCountMax
*/
export function generateEquippedAttunedItems(equippedItems, attunedItemCountMax) {
return equippedItems.filter(isAttuned).slice(0, attunedItemCountMax);
}
/**
*
* @param equippedItems
* @param gearWeaponItems
*/
export function generateOrderedItemAttacks(equippedItems, gearWeaponItems) {
const equippedWeaponItems = equippedItems.filter(isWeaponContract);
return orderBy([...equippedWeaponItems, ...gearWeaponItems].filter(isDisplayAsAttack), [(item) => getName(item)]);
}
/**
*
* @param equippedItems
*/
export function generateValidEquipmentModifiers(equippedItems) {
const armorModifiers = deriveValidGlobalModifiersFromArmor(equippedItems.filter(isArmorContract));
const weaponModifiers = deriveValidGlobalModifiersFromWeapons(equippedItems.filter(isWeaponContract));
const gearModifiers = deriveValidGlobalModifiersFromGear(equippedItems.filter(isGearContract));
return [...armorModifiers, ...weaponModifiers, ...gearModifiers];
}
/**
*
* @param items
*/
export function generateRefItemData(items) {
let data = {};
items.forEach((item) => {
data[getUniqueKey(item)] = {
[DataOriginDataInfoKeyEnum.PRIMARY]: item,
[DataOriginDataInfoKeyEnum.PARENT]: null,
};
});
return data;
}
@@ -0,0 +1,61 @@
import { DB_STRING_CARAPACE, DB_STRING_INTEGRATED_PROTECTION } from '../Core';
import { DataOriginTypeEnum } from '../DataOrigin';
import { ModifierAccessors, ModifierDerivers, ModifierValidators } from '../Modifier';
import { RacialTraitAccessors } from '../RacialTrait';
import { getArmorTypeId } from './accessors';
import { ArmorTypeEnum } from './constants';
/**
* TODO fix this dirty dirty hack for Integrated Protection
* @param modifier
* @param modifierData
* @param hasLightArmorProficiency
*/
export function hack__getSetUnarmoredModifierValue(modifier, modifierData, hasLightArmorProficiency) {
const dataOrigin = ModifierAccessors.getDataOrigin(modifier);
const dataOriginType = ModifierAccessors.getDataOriginType(modifier);
if (dataOriginType === DataOriginTypeEnum.RACE &&
RacialTraitAccessors.getName(dataOrigin.primary) === DB_STRING_INTEGRATED_PROTECTION &&
ModifierAccessors.getValue(modifier) === 1 &&
!hasLightArmorProficiency) {
return ModifierDerivers.deriveValue(modifier, modifierData, undefined, false);
}
return ModifierDerivers.deriveValue(modifier, modifierData);
}
/**
* TODO fix this dirty dirty hack for Carapace
* @param modifier
* @param modifierData
* @param isWearingHeavyArmor
*/
export function hack__deriveBonusArmorModifierValue(modifier, modifierData, isWearingHeavyArmor) {
const dataOrigin = ModifierAccessors.getDataOrigin(modifier);
const dataOriginType = ModifierAccessors.getDataOriginType(modifier);
if (dataOriginType === DataOriginTypeEnum.RACE &&
RacialTraitAccessors.getName(dataOrigin.primary) === DB_STRING_CARAPACE &&
ModifierAccessors.getValue(modifier) === 1 &&
isWearingHeavyArmor) {
return 0;
}
return ModifierDerivers.deriveValue(modifier, modifierData);
}
// This is a very CLEAN hack, but should probably not be here
// If we have a global modifier turning off disadvantage on stealth, and it is
// medium armor master, we need to check if this is medium armor
// since we only remove disadvantage in that case for that modifier
/**
*
* @param item
* @param modifiers
* @returns {boolean}
*/
export function hack__deriveStealthMediumArmorMasterShouldReturnStealthDefinition(item, modifiers) {
if (modifiers.length === 1) {
if (ModifierValidators.isStealthDisadvantageRemoveModifierForMediumArmor(modifiers[0])) {
const armorType = getArmorTypeId(item);
if (armorType && armorType !== ArmorTypeEnum.MEDIUM_ARMOR) {
return true;
}
}
}
return false;
}
@@ -0,0 +1,14 @@
import * as ItemAccessors from './accessors';
import * as ItemConstants from './constants';
import * as ItemDerivers from './derivers';
import * as ItemGenerators from './generators';
import * as ItemHacks from './hacks';
import * as ItemNotes from './notes';
import * as ItemSimulators from './simulators';
import * as ItemTypings from './typings';
import * as ItemUtils from './utils';
import * as ItemValidators from './validators';
export * from './typings';
export * from './constants';
export { ItemAccessors, ItemDerivers, ItemGenerators, ItemHacks, ItemNotes, ItemSimulators, ItemUtils, ItemValidators };
export default Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, ItemAccessors), ItemConstants), ItemDerivers), ItemGenerators), ItemHacks), ItemNotes), ItemTypings), ItemSimulators), ItemUtils), ItemValidators);
@@ -0,0 +1,217 @@
import { TypeScriptUtils } from '../../utils';
import { AttackTypeRangeEnum, StealthCheckTypeEnum } from '../Core';
import { InfusionAccessors } from '../Infusion';
import { LimitedUseAccessors, LimitedUseDerivers } from '../LimitedUse';
import { NoteGenerators } from '../Note';
import { SpellAccessors } from '../Spell';
import { getAdditionalDamages, getAttackType, getLimitedUse, getLongRange, getNotes, getProperties, getRange, getStealthCheck, getStrengthRequirement, getTags, isAdamantine, isOffhand, isSilvered, getArmorClass, getInfusion, isCustom, } from './accessors';
import { WeaponPropertyEnum } from './constants';
import { deriveCategoryInfo } from './derivers';
import { getApplicableWeaponSpellDamageGroups, hasWeaponProperty, isBaseArmor, isBaseGear, isBaseWeapon, isShield, } from './utils';
import { validateIsWeaponLike } from './validators';
/**
*
* @param item
* @param ruleData
* @param abilityLookup
* @param proficiencyBonus
*/
function getLimitedUseNoteComponent(item, ruleData, abilityLookup, proficiencyBonus) {
const limitedUse = getLimitedUse(item);
if (!limitedUse) {
return null;
}
const maxUses = LimitedUseDerivers.deriveMaxUses(limitedUse, abilityLookup, ruleData, proficiencyBonus);
const numberUsed = LimitedUseAccessors.getNumberUsed(limitedUse);
const numberRemaining = maxUses - numberUsed;
if (maxUses === 1) {
return NoteGenerators.createPlainText(`1 Charge${numberUsed === 1 ? ' (Used)' : ''}`);
}
return NoteGenerators.createPlainText(`${numberRemaining}/${maxUses} Charges`);
}
function getPropertyNoteComponents(item) {
const properties = getProperties(item);
if (!properties.length) {
return null;
}
const notes = [];
properties.forEach((property, idx) => {
if (property.name !== null) {
notes.push(NoteGenerators.createPlainText(property.name));
}
});
return NoteGenerators.createGroup(notes, ', ');
}
function getRangeNoteComponent(item) {
if (getAttackType(item) === AttackTypeRangeEnum.RANGED ||
hasWeaponProperty(item, WeaponPropertyEnum.THROWN) ||
hasWeaponProperty(item, WeaponPropertyEnum.RANGE)) {
const range = getRange(item);
const longRange = getLongRange(item);
return NoteGenerators.createPlainText(`Range (${range}/${longRange})`);
}
return null;
}
function getUserNoteComponent(item) {
const notes = getNotes(item);
if (notes) {
return NoteGenerators.createPlainText(notes);
}
return null;
}
function getWeaponSpellDamageGroupsNoteComponents(item, weaponSpellDamageGroups, ruleData) {
const filteredSpellDamage = getApplicableWeaponSpellDamageGroups(item, weaponSpellDamageGroups);
if (!filteredSpellDamage.length) {
return null;
}
const notes = [];
filteredSpellDamage.forEach((spellDamageGroup) => {
const damageNotes = spellDamageGroup.damageDice.map((damage) => NoteGenerators.createDamage(damage.type, damage.dice, damage.restriction ? damage.restriction : ''));
const groupNotes = [
NoteGenerators.createPlainText(SpellAccessors.getName(spellDamageGroup.spell)),
NoteGenerators.createGroup(damageNotes, ', '),
].filter(TypeScriptUtils.isNotNullOrUndefined);
notes.push(NoteGenerators.createGroup(groupNotes, ': '));
});
return NoteGenerators.createGroup(notes.filter(TypeScriptUtils.isNotNullOrUndefined), ', ');
}
/**
*
* @param item
* @param weaponSpellDamageGroups
* @param ruleData
* @param abilityLookup
* @param proficiencyBonus
*/
function getWeaponNoteComponents(item, weaponSpellDamageGroups, ruleData, abilityLookup, proficiencyBonus) {
const notes = [];
if (isBaseWeapon(item)) {
const additionalDamages = getAdditionalDamages(item);
if (additionalDamages) {
additionalDamages.forEach((additionalDamage, idx) => {
if (additionalDamage.damage !== null && additionalDamage.damageType !== null) {
notes.push(NoteGenerators.createGroup([
NoteGenerators.createPlainText('+'),
NoteGenerators.createDamage(additionalDamage.damageType, additionalDamage.damage, additionalDamage.info),
], ''));
}
});
}
}
notes.push(getLimitedUseNoteComponent(item, ruleData, abilityLookup, proficiencyBonus));
if (isBaseWeapon(item)) {
const category = deriveCategoryInfo(item, ruleData);
if (category && category.name) {
notes.push(NoteGenerators.createPlainText(category.name));
}
if (isOffhand(item)) {
notes.push(NoteGenerators.createPlainText('Dual Wield'));
}
if (isSilvered(item)) {
notes.push(NoteGenerators.createPlainText('Silvered'));
}
if (isAdamantine(item)) {
notes.push(NoteGenerators.createPlainText('Adamantine'));
}
}
if (isBaseWeapon(item) || isBaseGear(item)) {
notes.push(getPropertyNoteComponents(item));
}
if (isBaseWeapon(item)) {
notes.push(getRangeNoteComponent(item));
}
notes.push(getWeaponSpellDamageGroupsNoteComponents(item, weaponSpellDamageGroups, ruleData));
notes.push(getUserNoteComponent(item));
return notes.filter(TypeScriptUtils.isNotNullOrUndefined);
}
/**
*
* @param item
* @param ruleData
* @param abilityLookup
* @param proficiencyBonus
*/
function getArmorNoteComponents(item, ruleData, abilityLookup, proficiencyBonus) {
const notes = [];
notes.push(getLimitedUseNoteComponent(item, ruleData, abilityLookup, proficiencyBonus));
const armorClass = getArmorClass(item);
const strengthRequirement = getStrengthRequirement(item);
const stealthCheck = getStealthCheck(item);
if (isShield(item)) {
notes.push(NoteGenerators.createPlainText(`+${armorClass} AC`));
}
else {
notes.push(NoteGenerators.createPlainText(`AC ${armorClass}`));
}
if (strengthRequirement) {
notes.push(NoteGenerators.createPlainText(`Str ${strengthRequirement}`));
}
if (stealthCheck === StealthCheckTypeEnum.DISADVANTAGE) {
notes.push(NoteGenerators.createGroup([
NoteGenerators.createPlainText('Stealth'),
NoteGenerators.createDisadvantageIcon(),
]));
}
notes.push(getUserNoteComponent(item));
return notes.filter(TypeScriptUtils.isNotNullOrUndefined);
}
/**
*
* @param item
* @param ruleData
* @param abilityLookup
* @param proficiencyBonus
*/
function getGearNoteComponents(item, ruleData, abilityLookup, proficiencyBonus) {
const notes = [];
notes.push(getLimitedUseNoteComponent(item, ruleData, abilityLookup, proficiencyBonus));
const tags = getTags(item);
tags.forEach((tag) => {
notes.push(NoteGenerators.createPlainText(tag));
});
notes.push(getUserNoteComponent(item));
return notes.filter(TypeScriptUtils.isNotNullOrUndefined);
}
function getCustomNoteComponents(item) {
const notes = [];
notes.push(getUserNoteComponent(item));
return notes.filter(TypeScriptUtils.isNotNullOrUndefined);
}
function getInfusionNoteComponents(item, infusion) {
const notes = [];
notes.push(NoteGenerators.createPlainText(`Infusion: ${InfusionAccessors.getName(infusion)}`));
return notes;
}
/**
*
* @param item
* @param weaponSpellDamageGroups
* @param ruleData
* @param abilityLookup
* @param proficiencyBonus
*/
export function getNoteComponents(item, weaponSpellDamageGroups, ruleData, abilityLookup, proficiencyBonus) {
const notes = [];
const infusion = getInfusion(item);
if (infusion) {
notes.push(...getInfusionNoteComponents(item, infusion));
}
if (isBaseWeapon(item)) {
notes.push(...getWeaponNoteComponents(item, weaponSpellDamageGroups, ruleData, abilityLookup, proficiencyBonus));
}
else if (isBaseArmor(item)) {
notes.push(...getArmorNoteComponents(item, ruleData, abilityLookup, proficiencyBonus));
}
else if (isCustom(item)) {
notes.push(...getCustomNoteComponents(item));
}
else if (isBaseGear(item)) {
if (validateIsWeaponLike(item)) {
notes.push(...getWeaponNoteComponents(item, weaponSpellDamageGroups, ruleData, abilityLookup, proficiencyBonus));
}
else {
notes.push(...getGearNoteComponents(item, ruleData, abilityLookup, proficiencyBonus));
}
}
return notes.filter(TypeScriptUtils.isNotNullOrUndefined);
}
@@ -0,0 +1,24 @@
import { StealthCheckTypeEnum } from '../Core';
import { DataOriginTypeEnum } from '../DataOrigin';
import { ModifierGenerators } from '../Modifier';
import { getBundleSize, getDefinitionDamage, getDefinitionGrantedModifiers } from './accessors';
import { deriveArmorClass, deriveAvatarUrl, deriveBaseType, deriveCanAttune, deriveCanEquip, deriveCapacityWeight, deriveCategoryInfo, deriveCost, deriveDamageType, deriveDefinitionKey, deriveIsDisplayAsAttack, deriveIsMagic, deriveIsProficient, deriveName, derivePrimarySourceCategoryId, deriveProperties, deriveSecondarySourceCategoryIds, deriveType, deriveWeaponAdditionalDamage, deriveWeaponReach, deriveWeight, } from './derivers';
/**
*
* @param itemDefinition
* @param globalModifiers
* @param valueLookupByType
* @param ruleData
* @param overrides
*/
export function simulateItem(itemDefinition, globalModifiers, valueLookupByType, ruleData, overrides) {
var _a, _b;
const simulatedItemContract = Object.assign({ chargesUsed: 0, containerEntityId: -1, containerEntityTypeId: -1, containerDefinitionKey: '-1:-1', currency: null, definition: itemDefinition, definitionId: itemDefinition.id, definitionTypeId: -1, displayAsAttack: null, entityTypeId: -1, equipped: false, equippedEntityId: null, equippedEntityTypeId: null, id: -1, isAttuned: false, limitedUse: null, quantity: 1 }, ((_a = overrides === null || overrides === void 0 ? void 0 : overrides.simulatedItemContract) !== null && _a !== void 0 ? _a : {}));
const modifiers = (_b = getDefinitionGrantedModifiers(simulatedItemContract)) !== null && _b !== void 0 ? _b : [];
const simulatedItem = Object.assign(Object.assign({}, simulatedItemContract), { avatarUrl: deriveAvatarUrl(simulatedItemContract, ruleData), baseItemType: deriveBaseType(simulatedItemContract), canAttune: deriveCanAttune(simulatedItemContract, null), canEquip: deriveCanEquip(simulatedItemContract, null), categoryInfo: deriveCategoryInfo(simulatedItemContract, ruleData), cost: deriveCost(simulatedItemContract, {}), damageType: deriveDamageType(simulatedItemContract, []), definitionKey: deriveDefinitionKey(simulatedItemContract), isAdamantine: false, isCustom: false, isCustomized: false, isDisplayAsAttack: deriveIsDisplayAsAttack(simulatedItemContract, {}), isOffhand: false, isSilvered: false, isMagic: deriveIsMagic(simulatedItemContract, null), infusion: null, infusionActions: [], modifiers: modifiers.map((modifier) => ModifierGenerators.generateModifier(modifier, DataOriginTypeEnum.SIMULATED, null)), name: deriveName(simulatedItemContract, {}), notes: null, originalContract: null, properties: deriveProperties(simulatedItemContract, [], ruleData), spells: [], type: deriveType(simulatedItemContract, ruleData), weight: deriveWeight(simulatedItemContract, {}), capacityWeight: deriveCapacityWeight(simulatedItemContract, {}) });
const additionalDamages = deriveWeaponAdditionalDamage(simulatedItem);
const reach = deriveWeaponReach(simulatedItem, globalModifiers, ruleData);
const proficiency = deriveIsProficient(simulatedItem, globalModifiers, false, valueLookupByType, ruleData);
return Object.assign(Object.assign({}, simulatedItem), { armorClass: deriveArmorClass(simulatedItem), proficiency, quantity: getBundleSize(simulatedItem), canBeDedicatedWeapon: false, canHexWeapon: false, canOffhand: false, canPactWeapon: false, hexWeaponEnabled: false, isDedicatedWeapon: false, isHexWeapon: false, isPactWeapon: false, masteryName: null, metaItems: [], pactWeaponEnabled: false, toHit: 0, damage: getDefinitionDamage(simulatedItem), categoryInfo: deriveCategoryInfo(simulatedItem, ruleData), stealthCheck: StealthCheckTypeEnum.NONE, additionalDamages,
reach, versatileDamage: null, primarySourceCategoryId: derivePrimarySourceCategoryId(simulatedItem, ruleData), secondarySourceCategoryIds: deriveSecondarySourceCategoryIds(simulatedItem, ruleData) });
}
@@ -0,0 +1,179 @@
import { orderBy } from 'lodash';
import { TypeScriptUtils } from '../../utils';
import { ActionAccessors } from '../Action';
import { ContainerValidators } from '../Container';
import { ValueHacks, ValueUtils } from '../Value';
import { canAttune, getArmorTypeId, getAttackType, getBaseItemId, getBaseType, getBaseTypeId, getContainerDefinitionKey, getGearTypeId, getItemWeaponBehaviors, getMappingEntityTypeId, getMappingId, getMasteryName, getName, getPrimarySourceCategoryId, getProperties, getRarity, getSecondarySourceCategoryIds, isAttuned, isContainer, } from './accessors';
import { ArmorTypeEnum, ItemBaseTypeEnum, ItemBaseTypeIdEnum, ItemRarityNameEnum, ItemTypeEnum, } from './constants';
//TODO probably move alot of these to validators and validate naming :)
export function isShield(armor) {
return getArmorTypeId(armor) === ArmorTypeEnum.SHIELD;
}
export function isAmmunition(item) {
return isWeaponContract(item) && getBaseItemId(item) === null;
}
export function isStaff(item) {
return getGearTypeId(item) === ItemTypeEnum.STAFF;
}
export function isRod(item) {
return getGearTypeId(item) === ItemTypeEnum.ROD;
}
export function isWand(item) {
return getGearTypeId(item) === ItemTypeEnum.WAND;
}
export function isAvailableToAttune(item) {
return canAttune(item) && !isAttuned(item);
}
export function hasItemWeaponBehaviors(item) {
return getItemWeaponBehaviors(item) && getItemWeaponBehaviors(item).length > 0;
}
/**
* @param item
*/
export function isWeaponContract(item) {
return getBaseTypeId(item) === ItemBaseTypeIdEnum.WEAPON;
}
/**
* @param item
*/
export function isArmorContract(item) {
return getBaseTypeId(item) === ItemBaseTypeIdEnum.ARMOR;
}
/**
* @param item
*/
export function isGearContract(item) {
return getBaseTypeId(item) === ItemBaseTypeIdEnum.GEAR;
}
/**
* @param item
*/
export function isBaseWeapon(item) {
return getBaseType(item) === ItemBaseTypeEnum.WEAPON;
}
/**
* @param item
*/
export function isBaseArmor(item) {
return getBaseType(item) === ItemBaseTypeEnum.ARMOR;
}
/**
* @param item
*/
export function isBaseGear(item) {
return getBaseType(item) === ItemBaseTypeEnum.GEAR;
}
/**
*
* @param item
*/
export function getWeaponBehaviorAttackTypes(item) {
let attackTypes = [];
if (isWeaponContract(item)) {
return [];
}
const weaponBehaviors = getItemWeaponBehaviors(item);
if (weaponBehaviors && weaponBehaviors.length) {
attackTypes = weaponBehaviors.map((behavior) => behavior.attackType);
}
return attackTypes.filter(TypeScriptUtils.isNotNullOrUndefined);
}
/**
*
* @param item
* @param propertyName
*/
export function hasWeaponProperty(item, propertyName) {
return !!getProperties(item).find((property) => property.name === propertyName);
}
/**
*
* @param item
* @param weaponSpellDamageGroups
*/
export function getApplicableWeaponSpellDamageGroups(item, weaponSpellDamageGroups) {
const behaviorAttackTypes = getWeaponBehaviorAttackTypes(item);
return weaponSpellDamageGroups.filter((spellDamage) => (isWeaponContract(item) && spellDamage.attackTypeRange === getAttackType(item)) ||
(spellDamage.attackTypeRange && behaviorAttackTypes.includes(spellDamage.attackTypeRange)));
}
/**
*
* @param item
*/
export function getRarityLevel(item) {
switch (getRarity(item)) {
case ItemRarityNameEnum.ARTIFACT:
return 5;
case ItemRarityNameEnum.LEGENDARY:
return 4;
case ItemRarityNameEnum.VERY_RARE:
return 3;
case ItemRarityNameEnum.RARE:
return 2;
case ItemRarityNameEnum.UNCOMMON:
return 1;
case ItemRarityNameEnum.COMMON:
default:
return 0;
}
}
/**
*
* @param items
* @param attunedItemCountMax
*/
export function getAttunedSlots(items, attunedItemCountMax) {
const attunedSlots = [];
for (let i = 0; i < attunedItemCountMax; i++) {
attunedSlots.push(items[i] ? items[i] : null);
}
return attunedSlots;
}
export function sortInventoryItems(items, sortContainers) {
const sortCriteria = [
sortContainers ? (item) => isContainer(item) : () => { } /* no-op */,
(item) => getRarityLevel(item),
(item) => getName(item),
(item) => getMappingId(item),
];
const sortOrder = ['desc', 'desc', 'asc', 'asc'];
return orderBy(items, sortCriteria, sortOrder);
}
export function isShared(item, containerLookup) {
const containerDefinitionKey = getContainerDefinitionKey(item);
return ContainerValidators.validateIsShared(containerDefinitionKey, containerLookup);
}
/**
*
* @param entityValueLookup
* @param items
*/
export function getInventoryValuesMappings(entityValueLookup, items) {
const itemValueLookups = items
.map((item) => ValueUtils.getEntityData(entityValueLookup, ValueHacks.hack__toString(getMappingId(item)), ValueHacks.hack__toString(getMappingEntityTypeId(item))))
.filter((lookup) => Object.keys(lookup).length > 0);
const itemValuesContracts = [];
for (let i = 0; i < itemValueLookups.length; i++) {
itemValuesContracts.push(...Object.values(itemValueLookups[i]));
}
return itemValuesContracts;
}
/**
*
* @param item
* @param actions
*/
export function getMasteryAction(item, actions) {
var _a;
const masteryName = getMasteryName(item);
if (masteryName) {
//Find the action where the actionName matches the masteryName (meaning the name is equal to the modifier friendly subtype name of the weapon mastery modifier type group)
return ((_a = actions.find((action) => {
return masteryName === ActionAccessors.getDefinitionName(action);
})) !== null && _a !== void 0 ? _a : null);
}
return null;
}
export function getAllSourceCategoryIds(item) {
return [getPrimarySourceCategoryId(item), ...getSecondarySourceCategoryIds(item)];
}
@@ -0,0 +1,76 @@
import { canAttune, isAttuned, getEquippedEntityId, isEquipped, hasProficiency, isOffhand, getDefinitionEntityTypeId, } from './accessors';
import { CUSTOM_ITEM_DEFINITION_ENTITY_TYPE_ID } from './constants';
import { isGearContract, hasItemWeaponBehaviors, isWeaponContract, isAmmunition, isArmorContract, isShield, } from './utils';
/**
*
* @param item
*/
export function validateIsWeaponLike(item) {
if (isGearContract(item) && hasItemWeaponBehaviors(item)) {
return true;
}
if (isWeaponContract(item) && !isAmmunition(item)) {
return true;
}
return false;
}
/**
*
* @param item
*/
export function isNonProficientEquippedArmorItem(item) {
return isArmorContract(item) && !!isEquipped(item) && !hasProficiency(item);
}
/**
*
* @param item
*/
export function isEquippedMainhandWeapon(item) {
return isWeaponContract(item) && !isOffhand(item) && !!isEquipped(item);
}
/**
*
* @param item
*/
export function isEquippedOffhandWeapon(item) {
return isWeaponContract(item) && isOffhand(item) && !!isEquipped(item);
}
/**
*
* @param item
*/
export function isEquippedShield(item) {
return isArmorContract(item) && isShield(item) && !!isEquipped(item);
}
/**
*
* @param item
*/
export function isEquippedNonShieldArmor(item) {
return isArmorContract(item) && !isShield(item) && !!isEquipped(item);
}
/**
*
* @param item
*/
export function validateCanContributeActions(item) {
if (canAttune(item)) {
return !!isAttuned(item);
}
return true;
}
/**
*
* @param item
* @param characterId
*/
export function isEquippedToCurrentCharacter(item, characterId) {
return getEquippedEntityId(item) === characterId;
}
/**
*
* @param item
*/
export function isCustomItem(item) {
return getDefinitionEntityTypeId(item) === CUSTOM_ITEM_DEFINITION_ENTITY_TYPE_ID;
}