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:
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;
|
||||
}
|
||||
Reference in New Issue
Block a user