New source found from dndbeyond.com
This commit is contained in:
parent
2c657770de
commit
b7f7f4a655
@ -29,7 +29,7 @@ export const Layout: FC<LayoutProps> = ({ children, ...props }) => {
|
|||||||
<div className={styles.siteStyles}>
|
<div className={styles.siteStyles}>
|
||||||
<Sitebar user={user as any} navItems={[]} />
|
<Sitebar user={user as any} navItems={[]} />
|
||||||
{/* TODO: fetch sources */}
|
{/* TODO: fetch sources */}
|
||||||
<MegaMenu sources={[]} />
|
<MegaMenu enablePartyWizard={true} sources={[]} />
|
||||||
</div>
|
</div>
|
||||||
<div className={clsx(["container", styles.devContainer])}>{children}</div>
|
<div className={clsx(["container", styles.devContainer])}>{children}</div>
|
||||||
{!matchSheet && (
|
{!matchSheet && (
|
||||||
|
17
ddb_main/node_modules/@dndbeyond/ttui/components/Tooltip/Tooltip.tsx
generated
vendored
17
ddb_main/node_modules/@dndbeyond/ttui/components/Tooltip/Tooltip.tsx
generated
vendored
@ -5,6 +5,21 @@ import type { FC } from "react";
|
|||||||
import { Tooltip as ReactTooltip, type ITooltip } from "react-tooltip";
|
import { Tooltip as ReactTooltip, type ITooltip } from "react-tooltip";
|
||||||
import styles from "./Tooltip.module.css";
|
import styles from "./Tooltip.module.css";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BEST PRACTICES:
|
||||||
|
* - Tooltips are intended for interactive elements, and should not be used on non-interactive elements, such as divs, spans, p, etc.
|
||||||
|
* - Tooltips should not be focusable, so you should not put interactive elements inside of them, such as links.
|
||||||
|
* - https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/tooltip_role
|
||||||
|
*
|
||||||
|
* USAGE REQUIREMENTS:
|
||||||
|
* - The anchor element must have an `aria-describedby` attribute that is equivalent to the tooltip's ID.
|
||||||
|
* - If you must put a tooltip on a non-interactive element, it must have a `tabIndex` attribute set to 0 so that it can be reached by
|
||||||
|
* keyboard interaction. However, this still will not ensure that the tooltip will be read by screen readers. It is recommended to have a
|
||||||
|
* secondary means for screen readers to access the tooltip information, such as a span with a "screen-reader-only" class and the tooltip
|
||||||
|
* information inside of it.
|
||||||
|
* - If you must put content that is clickable inside of the tooltip, the `clickable` prop must provided and set to true.
|
||||||
|
**/
|
||||||
|
|
||||||
export interface TooltipProps extends ITooltip {
|
export interface TooltipProps extends ITooltip {
|
||||||
id: string;
|
id: string;
|
||||||
"data-testid"?: string;
|
"data-testid"?: string;
|
||||||
@ -24,6 +39,8 @@ export const Tooltip: FC<TooltipProps> = ({
|
|||||||
{...props}
|
{...props}
|
||||||
className={clsx([styles.tooltip, props.className])}
|
className={clsx([styles.tooltip, props.className])}
|
||||||
disableStyleInjection={disableStyleInjection}
|
disableStyleInjection={disableStyleInjection}
|
||||||
|
globalCloseEvents={{ escape: true, clickOutsideAnchor: true }}
|
||||||
|
closeEvents={{ mouseleave: true, blur: !props.clickable }}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</ReactTooltip>
|
</ReactTooltip>
|
||||||
|
@ -16,11 +16,12 @@ export function hack__generateHitPointParts(baseHp, overrideHp, bonusHp, tempHp,
|
|||||||
removedHp,
|
removedHp,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
export function hack__generateSpecialWeaponPropertiesEnabled(hexWeaponEnabled, pactWeaponEnabled, improvedPactWeaponEnabled, dedicatedWeaponEnabled) {
|
export function hack__generateSpecialWeaponPropertiesEnabled(hexWeaponEnabled, pactWeaponEnabled, improvedPactWeaponEnabled, dedicatedWeaponEnabled, replacementWeaponStats) {
|
||||||
return {
|
return {
|
||||||
hexWeaponEnabled,
|
hexWeaponEnabled,
|
||||||
pactWeaponEnabled,
|
pactWeaponEnabled,
|
||||||
improvedPactWeaponEnabled,
|
improvedPactWeaponEnabled,
|
||||||
dedicatedWeaponEnabled,
|
dedicatedWeaponEnabled,
|
||||||
|
replacementWeaponStats,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -540,3 +540,6 @@ export function getSpellListIds(charClass) {
|
|||||||
export function getOptionalClassFeatures(charClass) {
|
export function getOptionalClassFeatures(charClass) {
|
||||||
return charClass.optionalClassFeatures;
|
return charClass.optionalClassFeatures;
|
||||||
}
|
}
|
||||||
|
export function getReplacementWeaponAbilityStats(charClass) {
|
||||||
|
return charClass.replacementWeaponAbilityStats;
|
||||||
|
}
|
||||||
|
@ -4,7 +4,7 @@ import { ClassFeatureAccessors, ClassFeatureUtils, ClassFeatureValidators } from
|
|||||||
import { DB_STRING_BOOK_OF_ANCIENT_SECRETS, DB_STRING_INFUSE_ITEM, FeatureTypeEnum, } from '../Core';
|
import { DB_STRING_BOOK_OF_ANCIENT_SECRETS, DB_STRING_INFUSE_ITEM, FeatureTypeEnum, } from '../Core';
|
||||||
import { DataOriginGenerators } from '../DataOrigin';
|
import { DataOriginGenerators } from '../DataOrigin';
|
||||||
import { HelperUtils } from '../Helper';
|
import { HelperUtils } from '../Helper';
|
||||||
import { ModifierValidators } from '../Modifier';
|
import { ModifierAccessors, ModifierValidators } from '../Modifier';
|
||||||
import { OptionalClassFeatureAccessors } from '../OptionalClassFeature';
|
import { OptionalClassFeatureAccessors } from '../OptionalClassFeature';
|
||||||
import { DefaultSpellCastingLearningStyle, SpellDerivers } from '../Spell';
|
import { DefaultSpellCastingLearningStyle, SpellDerivers } from '../Spell';
|
||||||
import { getActiveId, getDefinitionClassFeatures, getDefinitionSpellcastingStatId, getId, getLevel, getMappingId, getSubclass, getClassFeatures, getDefinitionSpellCastingLearningStyle, } from './accessors';
|
import { getActiveId, getDefinitionClassFeatures, getDefinitionSpellcastingStatId, getId, getLevel, getMappingId, getSubclass, getClassFeatures, getDefinitionSpellCastingLearningStyle, } from './accessors';
|
||||||
@ -166,6 +166,18 @@ export function deriveOrderedClassFeatures(classFeatures) {
|
|||||||
(classFeature) => ClassFeatureAccessors.getName(classFeature),
|
(classFeature) => ClassFeatureAccessors.getName(classFeature),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
export function deriveReplacementWeaponAbilityStats(classModifiers) {
|
||||||
|
const replacementWeaponAbilityStats = [];
|
||||||
|
classModifiers.forEach((modifier) => {
|
||||||
|
if (ModifierValidators.isEnablesAbilityStatModifier(modifier)) {
|
||||||
|
const statId = ModifierAccessors.getEntityId(modifier) || 0;
|
||||||
|
if (statId && !replacementWeaponAbilityStats.includes(statId)) {
|
||||||
|
replacementWeaponAbilityStats.push(statId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return replacementWeaponAbilityStats;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param classFeatures
|
* @param classFeatures
|
||||||
|
@ -8,8 +8,8 @@ import { FormatUtils } from '../Format';
|
|||||||
import { ModifierDerivers, ModifierValidators } from '../Modifier';
|
import { ModifierDerivers, ModifierValidators } from '../Modifier';
|
||||||
import { OptionAccessors } from '../Option';
|
import { OptionAccessors } from '../Option';
|
||||||
import { SpellAccessors, SpellDerivers, SpellGenerators, } from '../Spell';
|
import { SpellAccessors, SpellDerivers, SpellGenerators, } from '../Spell';
|
||||||
import { getActiveId, getBaseClassSpells, getClassFeatures, getDefinitionClassFeatures, getEnablesDedicatedWeapon, getEnablesHexWeapon, getEnablesPactWeapon, getId, getLevel, getMappingId, getName, getSlug, getSpellPrepareType, getSpellRules, getSpells, getSubclass, isPactMagicActive, isSpellcastingActive, isStartingClass, } from './accessors';
|
import { getActiveId, getBaseClassSpells, getClassFeatures, getDefinitionClassFeatures, getEnablesDedicatedWeapon, getEnablesHexWeapon, getEnablesPactWeapon, getId, getLevel, getMappingId, getName, getReplacementWeaponAbilityStats, getSlug, getSpellPrepareType, getSpellRules, getSpells, getSubclass, isPactMagicActive, isSpellcastingActive, isStartingClass, } from './accessors';
|
||||||
import { deriveActiveId, deriveClassFeatureGroups, deriveConsolidatedClassFeatures, deriveDedicatedHexAndPactWeaponEnabled, deriveIsPactMagicActive, deriveIsSpellcastingActive, deriveSpellCastingLearningStyle, deriveSpellListIds, deriveSpellRules, deriveSpells, deriveUniqueKey, } from './derivers';
|
import { deriveActiveId, deriveClassFeatureGroups, deriveConsolidatedClassFeatures, deriveDedicatedHexAndPactWeaponEnabled, deriveIsPactMagicActive, deriveIsSpellcastingActive, deriveReplacementWeaponAbilityStats, deriveSpellCastingLearningStyle, deriveSpellListIds, deriveSpellRules, deriveSpells, deriveUniqueKey, } from './derivers';
|
||||||
export function generateBaseCharClasses(charClasses, optionalClassFeatures, allClassSpells, allClassAlwaysPreparedSpells, allClassAlwaysKnownSpells, appContext, ruleData, classesLookupData, definitionPool, characterPreferences, characterId, choiceComponents, baseFeats) {
|
export function generateBaseCharClasses(charClasses, optionalClassFeatures, allClassSpells, allClassAlwaysPreparedSpells, allClassAlwaysKnownSpells, appContext, ruleData, classesLookupData, definitionPool, characterPreferences, characterId, choiceComponents, baseFeats) {
|
||||||
const orderedClasses = orderBy(charClasses, [(charClass) => isStartingClass(charClass), (charClass) => getName(charClass)], ['desc', 'asc']);
|
const orderedClasses = orderBy(charClasses, [(charClass) => isStartingClass(charClass), (charClass) => getName(charClass)], ['desc', 'asc']);
|
||||||
const hack__baseCharClasses = orderedClasses.map((charClass) => hack__generateHackBaseCharClass(charClass, optionalClassFeatures, ruleData, classesLookupData, characterPreferences.enableOptionalClassFeatures));
|
const hack__baseCharClasses = orderedClasses.map((charClass) => hack__generateHackBaseCharClass(charClass, optionalClassFeatures, ruleData, classesLookupData, characterPreferences.enableOptionalClassFeatures));
|
||||||
@ -70,7 +70,7 @@ export function generateBaseCharClass(charClass, optionalClassFeatures, allClass
|
|||||||
actions,
|
actions,
|
||||||
enablesDedicatedWeapon,
|
enablesDedicatedWeapon,
|
||||||
enablesHexWeapon,
|
enablesHexWeapon,
|
||||||
enablesPactWeapon, slug: FormatUtils.cobaltSlugify(getName(charClass)), spellListIds: deriveSpellListIds(updatedFeatures, getLevel(charClass)), spellCastingLearningStyle: deriveSpellCastingLearningStyle(charClass) });
|
enablesPactWeapon, slug: FormatUtils.cobaltSlugify(getName(charClass)), spellListIds: deriveSpellListIds(updatedFeatures, getLevel(charClass)), spellCastingLearningStyle: deriveSpellCastingLearningStyle(charClass), replacementWeaponAbilityStats: deriveReplacementWeaponAbilityStats(classModifiers) });
|
||||||
}
|
}
|
||||||
export function generateClassFeatureLookup(baseCharClasses) {
|
export function generateClassFeatureLookup(baseCharClasses) {
|
||||||
const lookup = {};
|
const lookup = {};
|
||||||
@ -209,6 +209,14 @@ export function generatePactWeaponEnabled(classes) {
|
|||||||
});
|
});
|
||||||
return pactWeaponEnabled;
|
return pactWeaponEnabled;
|
||||||
}
|
}
|
||||||
|
export function generateReplacementWeaponAbilityStats(classes) {
|
||||||
|
const replacementWeaponStats = [];
|
||||||
|
classes.forEach((charClass) => {
|
||||||
|
const classReplacementWeaponStats = getReplacementWeaponAbilityStats(charClass);
|
||||||
|
replacementWeaponStats.push(...classReplacementWeaponStats);
|
||||||
|
});
|
||||||
|
return replacementWeaponStats;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param classes
|
* @param classes
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { AdjustmentTypeEnum } from '../Value';
|
||||||
export const FEET_IN_MILES = 5280;
|
export const FEET_IN_MILES = 5280;
|
||||||
export const POUNDS_IN_TON = 2000;
|
export const POUNDS_IN_TON = 2000;
|
||||||
export var AppContextTypeEnum;
|
export var AppContextTypeEnum;
|
||||||
@ -400,3 +401,11 @@ export const ABILITY_STAT_PHYSICAL_LIST = [
|
|||||||
AbilityStatEnum.DEXTERITY,
|
AbilityStatEnum.DEXTERITY,
|
||||||
AbilityStatEnum.CONSTITUTION,
|
AbilityStatEnum.CONSTITUTION,
|
||||||
];
|
];
|
||||||
|
export const STAT_REPLACEMENT_TYPE_LOOKUP = {
|
||||||
|
[AdjustmentTypeEnum.USE_STRENGTH]: AbilityStatEnum.STRENGTH,
|
||||||
|
[AdjustmentTypeEnum.USE_DEXTERITY]: AbilityStatEnum.DEXTERITY,
|
||||||
|
[AdjustmentTypeEnum.USE_CONSTITUTION]: AbilityStatEnum.CONSTITUTION,
|
||||||
|
[AdjustmentTypeEnum.USE_INTELLIGENCE]: AbilityStatEnum.INTELLIGENCE,
|
||||||
|
[AdjustmentTypeEnum.USE_WISDOM]: AbilityStatEnum.WISDOM,
|
||||||
|
[AdjustmentTypeEnum.USE_CHARISMA]: AbilityStatEnum.CHARISMA,
|
||||||
|
};
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { round } from 'lodash';
|
import { round } from 'lodash';
|
||||||
import { RuleDataAccessors } from '../RuleData';
|
import { RuleDataAccessors } from '../RuleData';
|
||||||
import { CURRENCY_VALUE, FEET_IN_MILES } from './constants';
|
import { AdjustmentTypeEnum } from '../Value';
|
||||||
|
import { AbilityStatEnum, CURRENCY_VALUE, FEET_IN_MILES } from './constants';
|
||||||
export function convertFeetToMiles(number, precision = 2) {
|
export function convertFeetToMiles(number, precision = 2) {
|
||||||
return round(number / FEET_IN_MILES, precision);
|
return round(number / FEET_IN_MILES, precision);
|
||||||
}
|
}
|
||||||
@ -41,3 +42,23 @@ export function getCurrencyTransactionAdjustment(multiplier, adjustments) {
|
|||||||
});
|
});
|
||||||
return adjustedCurrency;
|
return adjustedCurrency;
|
||||||
}
|
}
|
||||||
|
// returns the adjusment type that corresponds to a given stat id
|
||||||
|
// Only returns types that are defined in the STAT_REPLACEMENT_TYPE_LOOKUP
|
||||||
|
export function getAdjustmentTypeByStatId(statId) {
|
||||||
|
switch (statId) {
|
||||||
|
case AbilityStatEnum.STRENGTH:
|
||||||
|
return AdjustmentTypeEnum.USE_STRENGTH;
|
||||||
|
case AbilityStatEnum.DEXTERITY:
|
||||||
|
return AdjustmentTypeEnum.USE_DEXTERITY;
|
||||||
|
case AbilityStatEnum.CONSTITUTION:
|
||||||
|
return AdjustmentTypeEnum.USE_CONSTITUTION;
|
||||||
|
case AbilityStatEnum.INTELLIGENCE:
|
||||||
|
return AdjustmentTypeEnum.USE_INTELLIGENCE;
|
||||||
|
case AbilityStatEnum.WISDOM:
|
||||||
|
return AdjustmentTypeEnum.USE_WISDOM;
|
||||||
|
case AbilityStatEnum.CHARISMA:
|
||||||
|
return AdjustmentTypeEnum.USE_CHARISMA;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1040,3 +1040,9 @@ export function getPrimarySourceCategoryId(item) {
|
|||||||
export function getSecondarySourceCategoryIds(item) {
|
export function getSecondarySourceCategoryIds(item) {
|
||||||
return item.secondarySourceCategoryIds;
|
return item.secondarySourceCategoryIds;
|
||||||
}
|
}
|
||||||
|
export function getReplacementWeaponStats(item) {
|
||||||
|
return item.replacementWeaponStats;
|
||||||
|
}
|
||||||
|
export function getAppliedWeaponReplacementStats(item) {
|
||||||
|
return item.appliedWeaponReplacementStats;
|
||||||
|
}
|
||||||
|
@ -512,7 +512,7 @@ export function deriveWeaponReach(item, modifiers, ruleData) {
|
|||||||
* @param modifiers
|
* @param modifiers
|
||||||
* @param martialArtsLevel
|
* @param martialArtsLevel
|
||||||
*/
|
*/
|
||||||
export function deriveAvailableWeaponAbilities(item, modifiers, martialArtsLevelScale, isDedicatedWeapon, isPactWeapon, isHexWeapon) {
|
export function deriveAvailableWeaponAbilities(item, modifiers, martialArtsLevelScale, isDedicatedWeapon, isPactWeapon, isHexWeapon, appliedWeaponReplacementStats) {
|
||||||
const availableAbilities = [];
|
const availableAbilities = [];
|
||||||
const attackType = getAttackType(item);
|
const attackType = getAttackType(item);
|
||||||
if (attackType === AttackTypeRangeEnum.MELEE || hasWeaponProperty(item, WeaponPropertyEnum.FINESSE)) {
|
if (attackType === AttackTypeRangeEnum.MELEE || hasWeaponProperty(item, WeaponPropertyEnum.FINESSE)) {
|
||||||
@ -562,6 +562,22 @@ export function deriveAvailableWeaponAbilities(item, modifiers, martialArtsLevel
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//handle Weapon Replacement Stats
|
||||||
|
if (appliedWeaponReplacementStats.length > 0) {
|
||||||
|
const enableAbilityStatReplacementModifiers = modifiers.filter((modifier) => ModifierValidators.isReplaceWeaponAbilityModifier(modifier));
|
||||||
|
enableAbilityStatReplacementModifiers.forEach((modifier) => {
|
||||||
|
//get the origin of the modifier
|
||||||
|
const dataOrigin = ModifierAccessors.getDataOrigin(modifier);
|
||||||
|
if (dataOrigin) {
|
||||||
|
const replacementAbilities = deriveReplacementWeaponAbilities(modifiers, dataOrigin);
|
||||||
|
replacementAbilities.forEach((ability) => {
|
||||||
|
if (!availableAbilities.includes(ability)) {
|
||||||
|
availableAbilities.push(ability);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
return availableAbilities;
|
return availableAbilities;
|
||||||
}
|
}
|
||||||
export function deriveReplacementWeaponAbilities(modifiers, enableModifierDataOrigin) {
|
export function deriveReplacementWeaponAbilities(modifiers, enableModifierDataOrigin) {
|
||||||
|
@ -13,7 +13,7 @@ import { keyBy, orderBy } from 'lodash';
|
|||||||
import { TypeScriptUtils } from '../../utils';
|
import { TypeScriptUtils } from '../../utils';
|
||||||
import { CharacterDerivers } from '../Character';
|
import { CharacterDerivers } from '../Character';
|
||||||
import { ContainerTypeEnum } from '../Container';
|
import { ContainerTypeEnum } from '../Container';
|
||||||
import { StealthCheckTypeEnum } from '../Core';
|
import { STAT_REPLACEMENT_TYPE_LOOKUP, StealthCheckTypeEnum } from '../Core';
|
||||||
import { DataOriginDataInfoKeyEnum, DataOriginTypeEnum } from '../DataOrigin';
|
import { DataOriginDataInfoKeyEnum, DataOriginTypeEnum } from '../DataOrigin';
|
||||||
import { DefinitionHacks } from '../Definition';
|
import { DefinitionHacks } from '../Definition';
|
||||||
import { DiceAccessors, DiceDerivers, DiceUtils } from '../Dice';
|
import { DiceAccessors, DiceDerivers, DiceUtils } from '../Dice';
|
||||||
@ -67,7 +67,7 @@ export function generateCustomItem(customItem, ruleData, characterId) {
|
|||||||
limitedUse: null,
|
limitedUse: null,
|
||||||
quantity: quantity !== null && quantity !== void 0 ? quantity : 1,
|
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: [] });
|
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: [], replacementWeaponStats: [], appliedWeaponReplacementStats: [] });
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -97,8 +97,8 @@ export function generateWeaponBehaviorBaseItem(baseWeapon, parentItem, valueLook
|
|||||||
* @param hack__characterSpecialWeaponPropertiesEnabled
|
* @param hack__characterSpecialWeaponPropertiesEnabled
|
||||||
*/
|
*/
|
||||||
export function generateItems(items, abilityLookup, proficiencyBonus, modifiers, entityValueLookup, typeValueLookup, valueLookup, martialArtsLevelScale, ruleData, hack__characterSpecialWeaponPropertiesEnabled) {
|
export function generateItems(items, abilityLookup, proficiencyBonus, modifiers, entityValueLookup, typeValueLookup, valueLookup, martialArtsLevelScale, ruleData, hack__characterSpecialWeaponPropertiesEnabled) {
|
||||||
const { hexWeaponEnabled, pactWeaponEnabled, improvedPactWeaponEnabled, dedicatedWeaponEnabled } = hack__characterSpecialWeaponPropertiesEnabled;
|
const { hexWeaponEnabled, pactWeaponEnabled, improvedPactWeaponEnabled, dedicatedWeaponEnabled, replacementWeaponStats, } = hack__characterSpecialWeaponPropertiesEnabled;
|
||||||
return items.map((item) => generateItem(item, abilityLookup, proficiencyBonus, modifiers, entityValueLookup, typeValueLookup, valueLookup, martialArtsLevelScale, hexWeaponEnabled, pactWeaponEnabled, improvedPactWeaponEnabled, ruleData, dedicatedWeaponEnabled));
|
return items.map((item) => generateItem(item, abilityLookup, proficiencyBonus, modifiers, entityValueLookup, typeValueLookup, valueLookup, martialArtsLevelScale, hexWeaponEnabled, pactWeaponEnabled, improvedPactWeaponEnabled, ruleData, dedicatedWeaponEnabled, replacementWeaponStats));
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -116,7 +116,7 @@ export function generateItems(items, abilityLookup, proficiencyBonus, modifiers,
|
|||||||
* @param ruleData
|
* @param ruleData
|
||||||
* @param dedicatedWeaponEnabled
|
* @param dedicatedWeaponEnabled
|
||||||
*/
|
*/
|
||||||
export function generateItem(item, abilityLookup, proficiencyBonus, modifiers, entityValueLookup, typeValueLookup, valueLookup, martialArtsLevelScale, hexWeaponEnabled, pactWeaponEnabled, improvedPactWeaponEnabled, ruleData, dedicatedWeaponEnabled) {
|
export function generateItem(item, abilityLookup, proficiencyBonus, modifiers, entityValueLookup, typeValueLookup, valueLookup, martialArtsLevelScale, hexWeaponEnabled, pactWeaponEnabled, improvedPactWeaponEnabled, ruleData, dedicatedWeaponEnabled, replacementWeaponStats) {
|
||||||
const mappingId = getMappingId(item);
|
const mappingId = getMappingId(item);
|
||||||
const mappingIdString = ValueHacks.hack__toString(mappingId);
|
const mappingIdString = ValueHacks.hack__toString(mappingId);
|
||||||
const mappingEntityTypeId = getMappingEntityTypeId(item);
|
const mappingEntityTypeId = getMappingEntityTypeId(item);
|
||||||
@ -139,6 +139,22 @@ export function generateItem(item, abilityLookup, proficiencyBonus, modifiers, e
|
|||||||
!!ValueAccessors.getValue(dataLookup[AdjustmentTypeEnum.IS_PACT_WEAPON]);
|
!!ValueAccessors.getValue(dataLookup[AdjustmentTypeEnum.IS_PACT_WEAPON]);
|
||||||
}
|
}
|
||||||
const proficiency = deriveIsProficient(item, modifiers, isPactWeapon, typeValueLookup, ruleData);
|
const proficiency = deriveIsProficient(item, modifiers, isPactWeapon, typeValueLookup, ruleData);
|
||||||
|
//handle any replacement stats granted by modifiers from class features
|
||||||
|
const appliedWeaponReplacementStats = [];
|
||||||
|
let canUseWeaponReplacementStats = false;
|
||||||
|
if (replacementWeaponStats.length > 0 &&
|
||||||
|
(isBaseWeapon(item) || validateIsWeaponLike(item)) &&
|
||||||
|
!isAmmunition(item)) {
|
||||||
|
canUseWeaponReplacementStats = true;
|
||||||
|
// check if any of the replacement stats are being used
|
||||||
|
Object.keys(STAT_REPLACEMENT_TYPE_LOOKUP).forEach((adjustmentType) => {
|
||||||
|
const isUsingAdjustmentType = dataLookup.hasOwnProperty(adjustmentType) && !!ValueAccessors.getValue(dataLookup[adjustmentType]);
|
||||||
|
// if the adjustment type is being used, add it to the applied weapon replacement stats
|
||||||
|
if (isUsingAdjustmentType) {
|
||||||
|
appliedWeaponReplacementStats.push(STAT_REPLACEMENT_TYPE_LOOKUP[adjustmentType]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
let canHexWeapon = false;
|
let canHexWeapon = false;
|
||||||
let isHexWeapon = false;
|
let isHexWeapon = false;
|
||||||
if (hexWeaponEnabled && (isBaseWeapon(item) || validateIsWeaponLike(item)) && !isAmmunition(item)) {
|
if (hexWeaponEnabled && (isBaseWeapon(item) || validateIsWeaponLike(item)) && !isAmmunition(item)) {
|
||||||
@ -185,7 +201,7 @@ export function generateItem(item, abilityLookup, proficiencyBonus, modifiers, e
|
|||||||
const bonusToHitModifiers = modifiers.filter((modifier) => ModifierValidators.isValidBonusWeaponToHitModifier(modifier, item, ruleData));
|
const bonusToHitModifiers = modifiers.filter((modifier) => ModifierValidators.isValidBonusWeaponToHitModifier(modifier, item, ruleData));
|
||||||
const bonusToHitModifierTotal = ModifierDerivers.sumModifiers(bonusToHitModifiers, abilityLookup);
|
const bonusToHitModifierTotal = ModifierDerivers.sumModifiers(bonusToHitModifiers, abilityLookup);
|
||||||
//get all the available ability stats for the weapon
|
//get all the available ability stats for the weapon
|
||||||
const availableAbilities = deriveAvailableWeaponAbilities(item, modifiers, martialArtsLevelScale, isDedicatedWeapon, isPactWeapon, isHexWeapon);
|
const availableAbilities = deriveAvailableWeaponAbilities(item, modifiers, martialArtsLevelScale, isDedicatedWeapon, isPactWeapon, isHexWeapon, appliedWeaponReplacementStats);
|
||||||
// transform the available ability stats to have toHit and damage bonuses
|
// transform the available ability stats to have toHit and damage bonuses
|
||||||
const abilityPossibilities = CharacterDerivers.deriveAttackAbilityPossibilities(availableAbilities, modifiers, weaponProficiencyBonus, abilityLookup);
|
const abilityPossibilities = CharacterDerivers.deriveAttackAbilityPossibilities(availableAbilities, modifiers, weaponProficiencyBonus, abilityLookup);
|
||||||
const bestAbility = HelperUtils.getLast(abilityPossibilities, ['toHit', 'modifier']);
|
const bestAbility = HelperUtils.getLast(abilityPossibilities, ['toHit', 'modifier']);
|
||||||
@ -267,7 +283,7 @@ export function generateItem(item, abilityLookup, proficiencyBonus, modifiers, e
|
|||||||
hexWeaponEnabled,
|
hexWeaponEnabled,
|
||||||
pactWeaponEnabled, damage: newDamage, versatileDamage,
|
pactWeaponEnabled, damage: newDamage, versatileDamage,
|
||||||
additionalDamages,
|
additionalDamages,
|
||||||
reach, masteryName: deriveMasteryName(item, modifiers), primarySourceCategoryId: derivePrimarySourceCategoryId(item, ruleData), secondarySourceCategoryIds: deriveSecondarySourceCategoryIds(item, ruleData) });
|
reach, masteryName: deriveMasteryName(item, modifiers), primarySourceCategoryId: derivePrimarySourceCategoryId(item, ruleData), secondarySourceCategoryIds: deriveSecondarySourceCategoryIds(item, ruleData), replacementWeaponStats: canUseWeaponReplacementStats ? replacementWeaponStats : [], appliedWeaponReplacementStats: canUseWeaponReplacementStats ? appliedWeaponReplacementStats : [] });
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -341,7 +357,7 @@ function updateBaseItemSpells(item, spell) {
|
|||||||
* @param characterId
|
* @param characterId
|
||||||
*/
|
*/
|
||||||
export function generateGearWeaponItems(items, abilityLookup, proficiencyBonus, modifiers, entityValueLookup, typeValueLookup, valueLookup, martialArtsLevelScale, ruleData, hack__characterSpecialWeaponPropertiesEnabled, characterId) {
|
export function generateGearWeaponItems(items, abilityLookup, proficiencyBonus, modifiers, entityValueLookup, typeValueLookup, valueLookup, martialArtsLevelScale, ruleData, hack__characterSpecialWeaponPropertiesEnabled, characterId) {
|
||||||
const { hexWeaponEnabled, pactWeaponEnabled, improvedPactWeaponEnabled, dedicatedWeaponEnabled } = hack__characterSpecialWeaponPropertiesEnabled;
|
const { hexWeaponEnabled, pactWeaponEnabled, improvedPactWeaponEnabled, dedicatedWeaponEnabled, replacementWeaponStats, } = hack__characterSpecialWeaponPropertiesEnabled;
|
||||||
return items
|
return items
|
||||||
.filter(isGearContract)
|
.filter(isGearContract)
|
||||||
.filter((item) => hasItemWeaponBehaviors(item))
|
.filter((item) => hasItemWeaponBehaviors(item))
|
||||||
@ -352,7 +368,7 @@ export function generateGearWeaponItems(items, abilityLookup, proficiencyBonus,
|
|||||||
if (simulatedBaseItem === null) {
|
if (simulatedBaseItem === null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return generateItem(simulatedBaseItem, abilityLookup, proficiencyBonus, modifiers, entityValueLookup, typeValueLookup, valueLookup, martialArtsLevelScale, hexWeaponEnabled, pactWeaponEnabled, improvedPactWeaponEnabled, ruleData, dedicatedWeaponEnabled);
|
return generateItem(simulatedBaseItem, abilityLookup, proficiencyBonus, modifiers, entityValueLookup, typeValueLookup, valueLookup, martialArtsLevelScale, hexWeaponEnabled, pactWeaponEnabled, improvedPactWeaponEnabled, ruleData, dedicatedWeaponEnabled, replacementWeaponStats);
|
||||||
})
|
})
|
||||||
.filter(TypeScriptUtils.isNotNullOrUndefined))
|
.filter(TypeScriptUtils.isNotNullOrUndefined))
|
||||||
.reduce((acc, itemBehaviors) => {
|
.reduce((acc, itemBehaviors) => {
|
||||||
|
@ -20,5 +20,5 @@ export function simulateItem(itemDefinition, globalModifiers, valueLookupByType,
|
|||||||
const reach = deriveWeaponReach(simulatedItem, globalModifiers, ruleData);
|
const reach = deriveWeaponReach(simulatedItem, globalModifiers, ruleData);
|
||||||
const proficiency = deriveIsProficient(simulatedItem, globalModifiers, false, valueLookupByType, 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,
|
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) });
|
reach, versatileDamage: null, primarySourceCategoryId: derivePrimarySourceCategoryId(simulatedItem, ruleData), secondarySourceCategoryIds: deriveSecondarySourceCategoryIds(simulatedItem, ruleData), appliedWeaponReplacementStats: [], replacementWeaponStats: [] });
|
||||||
}
|
}
|
||||||
|
@ -220,6 +220,13 @@ export var ModifierSubTypeEnum;
|
|||||||
// Warlock Feature
|
// Warlock Feature
|
||||||
ModifierSubTypeEnum["ENABLE_PACT_WEAPON"] = "enable-pact-weapon";
|
ModifierSubTypeEnum["ENABLE_PACT_WEAPON"] = "enable-pact-weapon";
|
||||||
ModifierSubTypeEnum["ENABLE_HEX_WEAPON"] = "enable-hex-weapon";
|
ModifierSubTypeEnum["ENABLE_HEX_WEAPON"] = "enable-hex-weapon";
|
||||||
|
// Enable Feature (used in tandem with REPLACE_WEAPON_ABILITY modifier)
|
||||||
|
ModifierSubTypeEnum["ENABLE_STRENGTH"] = "enable-strength";
|
||||||
|
ModifierSubTypeEnum["ENABLE_DEXTERITY"] = "enable-dexterity";
|
||||||
|
ModifierSubTypeEnum["ENABLE_CONSTITUTION"] = "enable-constitution";
|
||||||
|
ModifierSubTypeEnum["ENABLE_INTELLIGENCE"] = "enable-intelligence";
|
||||||
|
ModifierSubTypeEnum["ENABLE_WISDOM"] = "enable-wisdom";
|
||||||
|
ModifierSubTypeEnum["ENABLE_CHARISMA"] = "enable-charisma";
|
||||||
})(ModifierSubTypeEnum || (ModifierSubTypeEnum = {}));
|
})(ModifierSubTypeEnum || (ModifierSubTypeEnum = {}));
|
||||||
export const SIZE_LIST = [
|
export const SIZE_LIST = [
|
||||||
ModifierSubTypeEnum.GARGANTUAN,
|
ModifierSubTypeEnum.GARGANTUAN,
|
||||||
@ -269,10 +276,7 @@ export const HEAVY_ARMOR_LIST = [
|
|||||||
ModifierSubTypeEnum.POWERED_ARMOR,
|
ModifierSubTypeEnum.POWERED_ARMOR,
|
||||||
ModifierSubTypeEnum.DIVERS_ARMOR,
|
ModifierSubTypeEnum.DIVERS_ARMOR,
|
||||||
];
|
];
|
||||||
export const SHIELDS_LIST = [
|
export const SHIELDS_LIST = [ModifierSubTypeEnum.SHIELD, ModifierSubTypeEnum.POT_LID];
|
||||||
ModifierSubTypeEnum.SHIELD,
|
|
||||||
ModifierSubTypeEnum.POT_LID,
|
|
||||||
];
|
|
||||||
export const ALL_ARMOR_LIST = [
|
export const ALL_ARMOR_LIST = [
|
||||||
...LIGHT_ARMOR_LIST,
|
...LIGHT_ARMOR_LIST,
|
||||||
...MEDIUM_ARMOR_LIST,
|
...MEDIUM_ARMOR_LIST,
|
||||||
|
@ -306,6 +306,23 @@ export function isEnableHexWeaponModifier(modifier) {
|
|||||||
return (getType(modifier) === ModifierTypeEnum.ENABLE_FEATURE &&
|
return (getType(modifier) === ModifierTypeEnum.ENABLE_FEATURE &&
|
||||||
getSubType(modifier) === ModifierSubTypeEnum.ENABLE_HEX_WEAPON);
|
getSubType(modifier) === ModifierSubTypeEnum.ENABLE_HEX_WEAPON);
|
||||||
}
|
}
|
||||||
|
export function isEnablesAbilityStatModifier(modifier) {
|
||||||
|
if (getType(modifier) !== ModifierTypeEnum.ENABLE_FEATURE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const subType = getSubType(modifier);
|
||||||
|
switch (subType) {
|
||||||
|
case ModifierSubTypeEnum.ENABLE_STRENGTH:
|
||||||
|
case ModifierSubTypeEnum.ENABLE_DEXTERITY:
|
||||||
|
case ModifierSubTypeEnum.ENABLE_CONSTITUTION:
|
||||||
|
case ModifierSubTypeEnum.ENABLE_INTELLIGENCE:
|
||||||
|
case ModifierSubTypeEnum.ENABLE_WISDOM:
|
||||||
|
case ModifierSubTypeEnum.ENABLE_CHARISMA:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param modifier
|
* @param modifier
|
||||||
|
@ -50,6 +50,12 @@ export var AdjustmentTypeEnum;
|
|||||||
AdjustmentTypeEnum[AdjustmentTypeEnum["IS_DEDICATED_WEAPON"] = 48] = "IS_DEDICATED_WEAPON";
|
AdjustmentTypeEnum[AdjustmentTypeEnum["IS_DEDICATED_WEAPON"] = 48] = "IS_DEDICATED_WEAPON";
|
||||||
AdjustmentTypeEnum[AdjustmentTypeEnum["CAPACITY_OVERRIDE"] = 49] = "CAPACITY_OVERRIDE";
|
AdjustmentTypeEnum[AdjustmentTypeEnum["CAPACITY_OVERRIDE"] = 49] = "CAPACITY_OVERRIDE";
|
||||||
AdjustmentTypeEnum[AdjustmentTypeEnum["CAPACITY_WEIGHT_OVERRIDE"] = 50] = "CAPACITY_WEIGHT_OVERRIDE";
|
AdjustmentTypeEnum[AdjustmentTypeEnum["CAPACITY_WEIGHT_OVERRIDE"] = 50] = "CAPACITY_WEIGHT_OVERRIDE";
|
||||||
|
AdjustmentTypeEnum[AdjustmentTypeEnum["USE_STRENGTH"] = 51] = "USE_STRENGTH";
|
||||||
|
AdjustmentTypeEnum[AdjustmentTypeEnum["USE_DEXTERITY"] = 52] = "USE_DEXTERITY";
|
||||||
|
AdjustmentTypeEnum[AdjustmentTypeEnum["USE_CONSTITUTION"] = 53] = "USE_CONSTITUTION";
|
||||||
|
AdjustmentTypeEnum[AdjustmentTypeEnum["USE_INTELLIGENCE"] = 54] = "USE_INTELLIGENCE";
|
||||||
|
AdjustmentTypeEnum[AdjustmentTypeEnum["USE_WISDOM"] = 55] = "USE_WISDOM";
|
||||||
|
AdjustmentTypeEnum[AdjustmentTypeEnum["USE_CHARISMA"] = 56] = "USE_CHARISMA";
|
||||||
})(AdjustmentTypeEnum || (AdjustmentTypeEnum = {}));
|
})(AdjustmentTypeEnum || (AdjustmentTypeEnum = {}));
|
||||||
export const ProficiencyAdjustmentTypeEnum = [
|
export const ProficiencyAdjustmentTypeEnum = [
|
||||||
AdjustmentTypeEnum.IS_PROFICIENT,
|
AdjustmentTypeEnum.IS_PROFICIENT,
|
||||||
|
@ -88,6 +88,8 @@ export class ItemManager extends PartyManager {
|
|||||||
this.isPactWeapon = () => ItemAccessors.isPactWeapon(this.item);
|
this.isPactWeapon = () => ItemAccessors.isPactWeapon(this.item);
|
||||||
this.isDedicatedWeapon = () => ItemAccessors.isDedicatedWeapon(this.item);
|
this.isDedicatedWeapon = () => ItemAccessors.isDedicatedWeapon(this.item);
|
||||||
this.isOffhand = () => ItemAccessors.isOffhand(this.item);
|
this.isOffhand = () => ItemAccessors.isOffhand(this.item);
|
||||||
|
this.getReplacementWeaponStats = () => ItemAccessors.getReplacementWeaponStats(this.item);
|
||||||
|
this.getAppliedWeaponReplacementStats = () => ItemAccessors.getAppliedWeaponReplacementStats(this.item);
|
||||||
this.getBaseArmorName = () => ItemAccessors.getBaseArmorName(this.item);
|
this.getBaseArmorName = () => ItemAccessors.getBaseArmorName(this.item);
|
||||||
this.getSubType = () => ItemAccessors.getSubType(this.item);
|
this.getSubType = () => ItemAccessors.getSubType(this.item);
|
||||||
this.getQuantity = () => ItemAccessors.getQuantity(this.item);
|
this.getQuantity = () => ItemAccessors.getQuantity(this.item);
|
||||||
@ -167,6 +169,8 @@ export class ItemManager extends PartyManager {
|
|||||||
return ItemNotes.getNoteComponents(this.item, weaponSpellDamageGroups, ruleData, abilityLookup, proficiencyBonus);
|
return ItemNotes.getNoteComponents(this.item, weaponSpellDamageGroups, ruleData, abilityLookup, proficiencyBonus);
|
||||||
};
|
};
|
||||||
this.getMetaText = () => {
|
this.getMetaText = () => {
|
||||||
|
const appliedWeaponReplacementStats = this.getAppliedWeaponReplacementStats();
|
||||||
|
const ruleData = rulesEngineSelectors.getRuleData(this.state);
|
||||||
let metaItems = [];
|
let metaItems = [];
|
||||||
if (this.isLegacy()) {
|
if (this.isLegacy()) {
|
||||||
metaItems.push('Legacy');
|
metaItems.push('Legacy');
|
||||||
@ -184,6 +188,11 @@ export class ItemManager extends PartyManager {
|
|||||||
if (this.isDedicatedWeapon()) {
|
if (this.isDedicatedWeapon()) {
|
||||||
metaItems.push('Dedicated Weapon');
|
metaItems.push('Dedicated Weapon');
|
||||||
}
|
}
|
||||||
|
if (appliedWeaponReplacementStats.length > 0) {
|
||||||
|
appliedWeaponReplacementStats.forEach((statId) => {
|
||||||
|
metaItems.push(`Using ${RuleDataUtils.getStatNameById(statId, ruleData, true)}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
if (this.isOffhand()) {
|
if (this.isOffhand()) {
|
||||||
metaItems.push('Dual Wield');
|
metaItems.push('Dual Wield');
|
||||||
}
|
}
|
||||||
|
@ -695,10 +695,20 @@ export const getPactWeaponEnabled = createSelector([getClasses], ClassGenerators
|
|||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
export const getImprovedPactWeaponEnabled = createSelector([getClasses], ClassGenerators.generateImprovedPactWeaponEnabled);
|
export const getImprovedPactWeaponEnabled = createSelector([getClasses], ClassGenerators.generateImprovedPactWeaponEnabled);
|
||||||
|
/**
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
export const getReplacementWeaponAbilityStats = createSelector([getClasses], ClassGenerators.generateReplacementWeaponAbilityStats);
|
||||||
/**
|
/**
|
||||||
* TODO: add return here
|
* TODO: add return here
|
||||||
*/
|
*/
|
||||||
export const hack__getSpecialWeaponPropertiesEnabled = createSelector([getHexWeaponEnabled, getPactWeaponEnabled, getImprovedPactWeaponEnabled, getDedicatedWeaponEnabled], CharacterHacks.hack__generateSpecialWeaponPropertiesEnabled);
|
export const hack__getSpecialWeaponPropertiesEnabled = createSelector([
|
||||||
|
getHexWeaponEnabled,
|
||||||
|
getPactWeaponEnabled,
|
||||||
|
getImprovedPactWeaponEnabled,
|
||||||
|
getDedicatedWeaponEnabled,
|
||||||
|
getReplacementWeaponAbilityStats,
|
||||||
|
], CharacterHacks.hack__generateSpecialWeaponPropertiesEnabled);
|
||||||
/**
|
/**
|
||||||
* TODO v5.1: should be able to remove this selector and all usages after mobile can support customItems as Items
|
* TODO v5.1: should be able to remove this selector and all usages after mobile can support customItems as Items
|
||||||
* @returns {Array<Item>}
|
* @returns {Array<Item>}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import React, { useCallback, useEffect, useState } from "react";
|
import React, { useCallback, useEffect, useState } from "react";
|
||||||
|
|
||||||
import ArrowDown from "@dndbeyond/fontawesome-cache/svgs/regular/arrow-down-to-line.svg";
|
import ArrowDown from "@dndbeyond/fontawesome-cache/svgs/regular/arrow-down-to-line.svg";
|
||||||
import { Button } from "@dndbeyond/ttui/components/Button";
|
|
||||||
|
|
||||||
|
import { Button } from "~/components/Button";
|
||||||
import { MaxCharactersDialog } from "~/components/MaxCharactersDialog";
|
import { MaxCharactersDialog } from "~/components/MaxCharactersDialog";
|
||||||
import config from "~/config";
|
import config from "~/config";
|
||||||
import { UserPreferenceProvider } from "~/tools/js/smartComponents/UserPreference";
|
import { UserPreferenceProvider } from "~/tools/js/smartComponents/UserPreference";
|
||||||
@ -263,6 +263,7 @@ export const CharacterGrid: React.FC<CharacterGridProps> = ({
|
|||||||
href="https://media.dndbeyond.com/compendium-images/free-rules/ph/character-sheet.pdf"
|
href="https://media.dndbeyond.com/compendium-images/free-rules/ph/character-sheet.pdf"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
className={styles.pdfLink}
|
className={styles.pdfLink}
|
||||||
|
rel="noreferrer"
|
||||||
>
|
>
|
||||||
<ArrowDown className={styles.pdfLinkSvg}></ArrowDown>
|
<ArrowDown className={styles.pdfLinkSvg}></ArrowDown>
|
||||||
Download a blank character sheet
|
Download a blank character sheet
|
||||||
|
@ -131,7 +131,6 @@ export const SearchSort: FC<SearchSortProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
<Select
|
<Select
|
||||||
className={styles.sort}
|
className={styles.sort}
|
||||||
placeholder="Select a movement"
|
|
||||||
name="sort"
|
name="sort"
|
||||||
label="Sort By"
|
label="Sort By"
|
||||||
value={getSortValue.label}
|
value={getSortValue.label}
|
||||||
|
@ -20,6 +20,7 @@ import {
|
|||||||
DefinitionPool,
|
DefinitionPool,
|
||||||
DefinitionUtils,
|
DefinitionUtils,
|
||||||
EntitledEntity,
|
EntitledEntity,
|
||||||
|
EntityRestrictionData,
|
||||||
FeatDefinitionContract,
|
FeatDefinitionContract,
|
||||||
FeatLookup,
|
FeatLookup,
|
||||||
InfusionChoice,
|
InfusionChoice,
|
||||||
@ -156,6 +157,7 @@ interface Props {
|
|||||||
optionalClassFeatureLookup: OptionalClassFeatureLookup;
|
optionalClassFeatureLookup: OptionalClassFeatureLookup;
|
||||||
theme: CharacterTheme;
|
theme: CharacterTheme;
|
||||||
inventoryManager: InventoryManager;
|
inventoryManager: InventoryManager;
|
||||||
|
entityRestrictionData: EntityRestrictionData;
|
||||||
}
|
}
|
||||||
interface State {
|
interface State {
|
||||||
showClassFeatures: boolean;
|
showClassFeatures: boolean;
|
||||||
@ -407,6 +409,7 @@ export default class ClassManager extends React.PureComponent<Props, State> {
|
|||||||
optionalClassFeatureLookup,
|
optionalClassFeatureLookup,
|
||||||
theme,
|
theme,
|
||||||
activeSourceCategories,
|
activeSourceCategories,
|
||||||
|
entityRestrictionData,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -441,6 +444,7 @@ export default class ClassManager extends React.PureComponent<Props, State> {
|
|||||||
onChangeReplacementPromise={onChangeReplacementPromise}
|
onChangeReplacementPromise={onChangeReplacementPromise}
|
||||||
onRemoveSelectionPromise={onRemoveSelectionPromise}
|
onRemoveSelectionPromise={onRemoveSelectionPromise}
|
||||||
ruleData={ruleData}
|
ruleData={ruleData}
|
||||||
|
entityRestrictionData={entityRestrictionData}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
id: "optional-features",
|
id: "optional-features",
|
||||||
|
@ -49,6 +49,7 @@ import {
|
|||||||
InventoryLookup,
|
InventoryLookup,
|
||||||
ItemUtils,
|
ItemUtils,
|
||||||
InventoryManager,
|
InventoryManager,
|
||||||
|
EntityRestrictionData,
|
||||||
} from "@dndbeyond/character-rules-engine/es";
|
} from "@dndbeyond/character-rules-engine/es";
|
||||||
|
|
||||||
import { HpSummary } from "~/subApps/builder/components/HpSummary";
|
import { HpSummary } from "~/subApps/builder/components/HpSummary";
|
||||||
@ -125,6 +126,7 @@ interface Props extends DispatchProp {
|
|||||||
characterId: number;
|
characterId: number;
|
||||||
activeSourceCategories: Array<number>;
|
activeSourceCategories: Array<number>;
|
||||||
createModal: (modalData: ModalData) => void;
|
createModal: (modalData: ModalData) => void;
|
||||||
|
entityRestrictionData: EntityRestrictionData;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ClassesManage extends React.PureComponent<Props> {
|
class ClassesManage extends React.PureComponent<Props> {
|
||||||
@ -931,6 +933,7 @@ class ClassesManage extends React.PureComponent<Props> {
|
|||||||
theme,
|
theme,
|
||||||
activeSourceCategories,
|
activeSourceCategories,
|
||||||
inventoryManager,
|
inventoryManager,
|
||||||
|
entityRestrictionData,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const levelsRemaining: number = Math.max(
|
const levelsRemaining: number = Math.max(
|
||||||
@ -1010,6 +1013,7 @@ class ClassesManage extends React.PureComponent<Props> {
|
|||||||
classSpellListSpellsLookup={classSpellListSpellsLookup}
|
classSpellListSpellsLookup={classSpellListSpellsLookup}
|
||||||
activeSourceCategories={activeSourceCategories}
|
activeSourceCategories={activeSourceCategories}
|
||||||
inventoryManager={inventoryManager}
|
inventoryManager={inventoryManager}
|
||||||
|
entityRestrictionData={entityRestrictionData}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{levelsRemaining !== 0 && (
|
{levelsRemaining !== 0 && (
|
||||||
@ -1086,6 +1090,8 @@ export default ConnectedBuilderPage(
|
|||||||
characterId: rulesEngineSelectors.getId(state),
|
characterId: rulesEngineSelectors.getId(state),
|
||||||
activeSourceCategories:
|
activeSourceCategories:
|
||||||
rulesEngineSelectors.getActiveSourceCategories(state),
|
rulesEngineSelectors.getActiveSourceCategories(state),
|
||||||
|
entityRestrictionData:
|
||||||
|
rulesEngineSelectors.getEntityRestrictionData(state),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
import axios, { Canceler } from "axios";
|
import axios, { Canceler } from "axios";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import {
|
import { LoadingPlaceholder } from "@dndbeyond/character-components/es";
|
||||||
MarketplaceCta,
|
|
||||||
LoadingPlaceholder,
|
|
||||||
} from "@dndbeyond/character-components/es";
|
|
||||||
import {
|
import {
|
||||||
ApiAdapterUtils,
|
ApiAdapterUtils,
|
||||||
CharClass,
|
CharClass,
|
||||||
@ -20,6 +17,7 @@ import {
|
|||||||
ClassUtils,
|
ClassUtils,
|
||||||
HelperUtils,
|
HelperUtils,
|
||||||
RuleData,
|
RuleData,
|
||||||
|
EntityRestrictionData,
|
||||||
} from "@dndbeyond/character-rules-engine/es";
|
} from "@dndbeyond/character-rules-engine/es";
|
||||||
|
|
||||||
import { Link } from "~/components/Link";
|
import { Link } from "~/components/Link";
|
||||||
@ -58,6 +56,7 @@ interface OptionalFeatureManagerProps {
|
|||||||
reject: () => void
|
reject: () => void
|
||||||
) => void;
|
) => void;
|
||||||
ruleData: RuleData;
|
ruleData: RuleData;
|
||||||
|
entityRestrictionData: EntityRestrictionData;
|
||||||
}
|
}
|
||||||
interface OptionalFeatureManagerState {
|
interface OptionalFeatureManagerState {
|
||||||
loadingStatus: DataLoadingStatusEnum;
|
loadingStatus: DataLoadingStatusEnum;
|
||||||
@ -180,6 +179,7 @@ export class OptionalFeatureManager extends React.PureComponent<
|
|||||||
onChangeReplacementPromise,
|
onChangeReplacementPromise,
|
||||||
onRemoveSelectionPromise,
|
onRemoveSelectionPromise,
|
||||||
onSelection,
|
onSelection,
|
||||||
|
entityRestrictionData,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
let contentNode: React.ReactNode;
|
let contentNode: React.ReactNode;
|
||||||
@ -234,13 +234,41 @@ export class OptionalFeatureManager extends React.PureComponent<
|
|||||||
const consolidatedOptionalOrigins = [
|
const consolidatedOptionalOrigins = [
|
||||||
...availableOptionalFeatures,
|
...availableOptionalFeatures,
|
||||||
...unEntitledOptionalClassFeatures,
|
...unEntitledOptionalClassFeatures,
|
||||||
].filter(
|
]
|
||||||
(feature) =>
|
.filter(
|
||||||
!ClassFeatureUtils.getHideInContext(
|
(feature) =>
|
||||||
feature,
|
!ClassFeatureUtils.getHideInContext(
|
||||||
Constants.AppContextTypeEnum.BUILDER
|
feature,
|
||||||
)
|
Constants.AppContextTypeEnum.BUILDER
|
||||||
);
|
)
|
||||||
|
)
|
||||||
|
.filter((feature) => {
|
||||||
|
//check if the feature is already selectec
|
||||||
|
const optionalFeatureMapping = HelperUtils.lookupDataOrFallback(
|
||||||
|
optionalClassFeatureLookup,
|
||||||
|
ClassFeatureUtils.getDefinitionKey(feature)
|
||||||
|
);
|
||||||
|
|
||||||
|
//always return the feature if it is already selected
|
||||||
|
if (optionalFeatureMapping) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sourceIds = ClassFeatureUtils.getSources(feature).map(
|
||||||
|
(source) => source.sourceId
|
||||||
|
);
|
||||||
|
|
||||||
|
// if there are no sources, we assume it is homebrew
|
||||||
|
if (sourceIds.length === 0) {
|
||||||
|
//return true if the user has homebrew content enabled
|
||||||
|
return entityRestrictionData.preferences.useHomebrewContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// filter out features that do not have any sources from the active sources lookup
|
||||||
|
return sourceIds.some((sourceId) =>
|
||||||
|
entityRestrictionData.activeSourceLookup.hasOwnProperty(sourceId)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
if (!consolidatedOptionalOrigins.length) {
|
if (!consolidatedOptionalOrigins.length) {
|
||||||
contentNode = this.renderOptionalFeatureCta();
|
contentNode = this.renderOptionalFeatureCta();
|
||||||
@ -294,7 +322,7 @@ export class OptionalFeatureManager extends React.PureComponent<
|
|||||||
affectedFeatureDefinitionKey={
|
affectedFeatureDefinitionKey={
|
||||||
affectedFeatureDefinitionKey
|
affectedFeatureDefinitionKey
|
||||||
}
|
}
|
||||||
isSelected={!!optionalFeatureMapping ?? false}
|
isSelected={!!optionalFeatureMapping}
|
||||||
onSelection={onSelection}
|
onSelection={onSelection}
|
||||||
onChangeReplacementPromise={onChangeReplacementPromise}
|
onChangeReplacementPromise={onChangeReplacementPromise}
|
||||||
onRemoveSelectionPromise={onRemoveSelectionPromise}
|
onRemoveSelectionPromise={onRemoveSelectionPromise}
|
||||||
@ -338,7 +366,7 @@ export class OptionalFeatureManager extends React.PureComponent<
|
|||||||
affectedFeatureDefinitionKey={
|
affectedFeatureDefinitionKey={
|
||||||
affectedFeatureDefinitionKey
|
affectedFeatureDefinitionKey
|
||||||
}
|
}
|
||||||
isSelected={!!optionalFeatureMapping ?? false}
|
isSelected={!!optionalFeatureMapping}
|
||||||
onSelection={onSelection}
|
onSelection={onSelection}
|
||||||
onChangeReplacementPromise={onChangeReplacementPromise}
|
onChangeReplacementPromise={onChangeReplacementPromise}
|
||||||
onRemoveSelectionPromise={onRemoveSelectionPromise}
|
onRemoveSelectionPromise={onRemoveSelectionPromise}
|
||||||
|
@ -17,6 +17,7 @@ import {
|
|||||||
Constants,
|
Constants,
|
||||||
Container,
|
Container,
|
||||||
ContainerUtils,
|
ContainerUtils,
|
||||||
|
CoreUtils,
|
||||||
DataOrigin,
|
DataOrigin,
|
||||||
DiceUtils,
|
DiceUtils,
|
||||||
EntityValueLookup,
|
EntityValueLookup,
|
||||||
@ -135,6 +136,19 @@ export class ItemDetail extends React.PureComponent<Props> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
handleReplaceAbilityStatChange = (
|
||||||
|
enabled: boolean,
|
||||||
|
statId: Constants.AbilityStatEnum
|
||||||
|
) => {
|
||||||
|
const { onCustomDataUpdate } = this.props;
|
||||||
|
|
||||||
|
const adjustmentType = CoreUtils.getAdjustmentTypeByStatId(statId);
|
||||||
|
|
||||||
|
if (onCustomDataUpdate && adjustmentType) {
|
||||||
|
onCustomDataUpdate(adjustmentType, enabled);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
handleHexWeaponChange = (enabled) => {
|
handleHexWeaponChange = (enabled) => {
|
||||||
const { onCustomDataUpdate } = this.props;
|
const { onCustomDataUpdate } = this.props;
|
||||||
|
|
||||||
@ -319,7 +333,7 @@ export class ItemDetail extends React.PureComponent<Props> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
renderClassCustomize = () => {
|
renderClassCustomize = () => {
|
||||||
const { item } = this.props;
|
const { item, ruleData } = this.props;
|
||||||
|
|
||||||
const isHexWeapon = ItemUtils.isHexWeapon(item);
|
const isHexWeapon = ItemUtils.isHexWeapon(item);
|
||||||
const canHexWeapon = ItemUtils.canHexWeapon(item);
|
const canHexWeapon = ItemUtils.canHexWeapon(item);
|
||||||
@ -327,8 +341,16 @@ export class ItemDetail extends React.PureComponent<Props> {
|
|||||||
const canPactWeapon = ItemUtils.canPactWeapon(item);
|
const canPactWeapon = ItemUtils.canPactWeapon(item);
|
||||||
const canBeDedicatedWeapon = ItemUtils.canBeDedicatedWeapon(item);
|
const canBeDedicatedWeapon = ItemUtils.canBeDedicatedWeapon(item);
|
||||||
const isDedicatedWeapon = ItemUtils.isDedicatedWeapon(item);
|
const isDedicatedWeapon = ItemUtils.isDedicatedWeapon(item);
|
||||||
|
const replacementWeaponStats = ItemUtils.getReplacementWeaponStats(item);
|
||||||
|
const appliedWeaponReplacementStats =
|
||||||
|
ItemUtils.getAppliedWeaponReplacementStats(item);
|
||||||
|
|
||||||
if (!canHexWeapon && !canPactWeapon && !canBeDedicatedWeapon) {
|
if (
|
||||||
|
!canHexWeapon &&
|
||||||
|
!canPactWeapon &&
|
||||||
|
!canBeDedicatedWeapon &&
|
||||||
|
replacementWeaponStats.length === 0
|
||||||
|
) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -361,6 +383,21 @@ export class ItemDetail extends React.PureComponent<Props> {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{replacementWeaponStats.map((statId) => (
|
||||||
|
<div className="ct-item-detail__class-customize-item" key={statId}>
|
||||||
|
<Checkbox
|
||||||
|
enabled={appliedWeaponReplacementStats.includes(statId)}
|
||||||
|
onChange={(enabled) =>
|
||||||
|
this.handleReplaceAbilityStatChange(enabled, statId)
|
||||||
|
}
|
||||||
|
label={`Use ${RuleDataUtils.getStatNameById(
|
||||||
|
statId,
|
||||||
|
ruleData,
|
||||||
|
true
|
||||||
|
)}`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AbilityLookup,
|
AbilityLookup,
|
||||||
Attack,
|
Attack,
|
||||||
@ -21,15 +22,17 @@ import {
|
|||||||
} from "@dndbeyond/dice";
|
} from "@dndbeyond/dice";
|
||||||
import StarIcon from "@dndbeyond/fontawesome-cache/svgs/solid/star.svg";
|
import StarIcon from "@dndbeyond/fontawesome-cache/svgs/solid/star.svg";
|
||||||
import { GameLogContext } from "@dndbeyond/game-log-components";
|
import { GameLogContext } from "@dndbeyond/game-log-components";
|
||||||
|
|
||||||
import { ItemName } from "~/components/ItemName";
|
import { ItemName } from "~/components/ItemName";
|
||||||
import { NumberDisplay } from "~/components/NumberDisplay";
|
import { NumberDisplay } from "~/components/NumberDisplay";
|
||||||
import Tooltip from "~/tools/js/commonComponents/Tooltip";
|
import Tooltip from "~/tools/js/commonComponents/Tooltip";
|
||||||
|
|
||||||
|
import Damage from "../../Damage";
|
||||||
|
import { DigitalDiceWrapper } from "../../Dice";
|
||||||
import { AttackTypeIcon } from "../../Icons";
|
import { AttackTypeIcon } from "../../Icons";
|
||||||
import NoteComponents from "../../NoteComponents";
|
import NoteComponents from "../../NoteComponents";
|
||||||
import { DiceComponentUtils } from "../../utils";
|
import { DiceComponentUtils } from "../../utils";
|
||||||
import CombatAttack from "../CombatAttack";
|
import CombatAttack from "../CombatAttack";
|
||||||
import { DigitalDiceWrapper } from "../../Dice";
|
|
||||||
import Damage from "../../Damage";
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
attack: Attack;
|
attack: Attack;
|
||||||
@ -130,6 +133,7 @@ class CombatItemAttack extends React.PureComponent<Props, State> {
|
|||||||
theme,
|
theme,
|
||||||
rollContext,
|
rollContext,
|
||||||
className,
|
className,
|
||||||
|
ruleData,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const [{ messageTargetOptions, defaultMessageTargetOption, userId }] =
|
const [{ messageTargetOptions, defaultMessageTargetOption, userId }] =
|
||||||
@ -156,6 +160,8 @@ class CombatItemAttack extends React.PureComponent<Props, State> {
|
|||||||
const isPactWeapon = ItemUtils.isPactWeapon(item);
|
const isPactWeapon = ItemUtils.isPactWeapon(item);
|
||||||
const isDedicatedWeapon = ItemUtils.isDedicatedWeapon(item);
|
const isDedicatedWeapon = ItemUtils.isDedicatedWeapon(item);
|
||||||
const isLegacy = ItemUtils.isLegacy(item);
|
const isLegacy = ItemUtils.isLegacy(item);
|
||||||
|
const appliedWeaponReplacementStats =
|
||||||
|
ItemUtils.getAppliedWeaponReplacementStats(item);
|
||||||
|
|
||||||
let combinedMetaItems: Array<string> = [];
|
let combinedMetaItems: Array<string> = [];
|
||||||
|
|
||||||
@ -165,7 +171,12 @@ class CombatItemAttack extends React.PureComponent<Props, State> {
|
|||||||
if (type === Constants.WeaponTypeEnum.AMMUNITION) {
|
if (type === Constants.WeaponTypeEnum.AMMUNITION) {
|
||||||
combinedMetaItems.push("Ammunition");
|
combinedMetaItems.push("Ammunition");
|
||||||
} else {
|
} else {
|
||||||
if (isHexWeapon || isPactWeapon || isDedicatedWeapon) {
|
if (
|
||||||
|
isHexWeapon ||
|
||||||
|
isPactWeapon ||
|
||||||
|
isDedicatedWeapon ||
|
||||||
|
appliedWeaponReplacementStats.length > 0
|
||||||
|
) {
|
||||||
if (isHexWeapon) {
|
if (isHexWeapon) {
|
||||||
combinedMetaItems.push("Hex Weapon");
|
combinedMetaItems.push("Hex Weapon");
|
||||||
}
|
}
|
||||||
@ -175,6 +186,13 @@ class CombatItemAttack extends React.PureComponent<Props, State> {
|
|||||||
if (isDedicatedWeapon) {
|
if (isDedicatedWeapon) {
|
||||||
combinedMetaItems.push("Dedicated Weapon");
|
combinedMetaItems.push("Dedicated Weapon");
|
||||||
}
|
}
|
||||||
|
if (appliedWeaponReplacementStats.length > 0) {
|
||||||
|
appliedWeaponReplacementStats.forEach((statId) => {
|
||||||
|
combinedMetaItems.push(
|
||||||
|
`Using ${RuleDataUtils.getStatNameById(statId, ruleData, true)}`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
combinedMetaItems.push(`${attackTypeName} Weapon`);
|
combinedMetaItems.push(`${attackTypeName} Weapon`);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user