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:
@@ -0,0 +1,18 @@
|
||||
export var ArmorClassTypeEnum;
|
||||
(function (ArmorClassTypeEnum) {
|
||||
ArmorClassTypeEnum["ARMOR"] = "Armor";
|
||||
ArmorClassTypeEnum["SHIELD"] = "Shield";
|
||||
ArmorClassTypeEnum["DEX_BONUS"] = "Dexterity Bonus";
|
||||
ArmorClassTypeEnum["MAGIC_BONUS"] = "Magic Bonus";
|
||||
ArmorClassTypeEnum["MISC_BONUS"] = "Misc Bonus";
|
||||
ArmorClassTypeEnum["UNARMORED_BONUS"] = "Unarmored Bonus";
|
||||
ArmorClassTypeEnum["ARMORED_BONUS"] = "Armored Bonus";
|
||||
ArmorClassTypeEnum["OVERRIDE_BASE_ARMOR"] = "Base Armor + DEX (Override)";
|
||||
})(ArmorClassTypeEnum || (ArmorClassTypeEnum = {}));
|
||||
export var ArmorClassExtraTypeEnum;
|
||||
(function (ArmorClassExtraTypeEnum) {
|
||||
ArmorClassExtraTypeEnum["MODIFIER"] = "MODIFIER";
|
||||
ArmorClassExtraTypeEnum["ITEM"] = "ITEM";
|
||||
ArmorClassExtraTypeEnum["STRING"] = "STRING";
|
||||
ArmorClassExtraTypeEnum["NUMBER"] = "NUMBER";
|
||||
})(ArmorClassExtraTypeEnum || (ArmorClassExtraTypeEnum = {}));
|
||||
@@ -0,0 +1,7 @@
|
||||
export var AttackSourceTypeEnum;
|
||||
(function (AttackSourceTypeEnum) {
|
||||
AttackSourceTypeEnum["ACTION"] = "ACTION";
|
||||
AttackSourceTypeEnum["ITEM"] = "ITEM";
|
||||
AttackSourceTypeEnum["SPELL"] = "SPELL";
|
||||
AttackSourceTypeEnum["CUSTOM"] = "CUSTOM";
|
||||
})(AttackSourceTypeEnum || (AttackSourceTypeEnum = {}));
|
||||
@@ -0,0 +1,6 @@
|
||||
export var DeathCauseEnum;
|
||||
(function (DeathCauseEnum) {
|
||||
DeathCauseEnum["NONE"] = "NONE";
|
||||
DeathCauseEnum["DEATHSAVES"] = "DEATHSAVES";
|
||||
DeathCauseEnum["CONDITION"] = "CONDITION";
|
||||
})(DeathCauseEnum || (DeathCauseEnum = {}));
|
||||
@@ -0,0 +1,14 @@
|
||||
export var ProtectionSupplierTypeEnum;
|
||||
(function (ProtectionSupplierTypeEnum) {
|
||||
ProtectionSupplierTypeEnum["RACIAL_TRAIT"] = "RACIAL_TRAIT";
|
||||
ProtectionSupplierTypeEnum["CLASS_FEATURE"] = "CLASS_FEATURE";
|
||||
ProtectionSupplierTypeEnum["FEAT"] = "FEAT";
|
||||
ProtectionSupplierTypeEnum["ITEM"] = "ITEM";
|
||||
})(ProtectionSupplierTypeEnum || (ProtectionSupplierTypeEnum = {}));
|
||||
export var ProtectionAvailabilityStatusEnum;
|
||||
(function (ProtectionAvailabilityStatusEnum) {
|
||||
ProtectionAvailabilityStatusEnum["AVAILABLE"] = "AVAILABLE";
|
||||
ProtectionAvailabilityStatusEnum["NO_LIMITED_USE_REMAINING"] = "NO_LIMITED_USE_REMAINING";
|
||||
ProtectionAvailabilityStatusEnum["ITEM_NOT_EQUIPPED"] = "ITEM_NOT_EQUIPPED";
|
||||
ProtectionAvailabilityStatusEnum["ITEM_NOT_ATTUNED"] = "ITEM_NOT_ATTUNED";
|
||||
})(ProtectionAvailabilityStatusEnum || (ProtectionAvailabilityStatusEnum = {}));
|
||||
@@ -0,0 +1,42 @@
|
||||
import { ActionAccessors } from '../../Action';
|
||||
import { ActivationAccessors } from '../../Activation';
|
||||
import { ActivatableTypeEnum } from '../../Core';
|
||||
import { OptionAccessors } from '../../Option';
|
||||
/**
|
||||
*
|
||||
* @param options
|
||||
* @param parentEntity
|
||||
*/
|
||||
export function deriveActivatableOptions(options, parentEntity) {
|
||||
const activatables = [];
|
||||
options.forEach((option) => {
|
||||
const actions = OptionAccessors.getActions(option);
|
||||
if (actions.length) {
|
||||
activatables.push(...deriveActivatableActions(actions, parentEntity));
|
||||
}
|
||||
});
|
||||
return activatables;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param actions
|
||||
* @param parentEntity
|
||||
*/
|
||||
export function deriveActivatableActions(actions, parentEntity) {
|
||||
const activatables = [];
|
||||
actions.forEach((action) => {
|
||||
const activation = ActionAccessors.getActivation(action);
|
||||
if (activation !== null && ActivationAccessors.getType(activation) !== null) {
|
||||
activatables.push({
|
||||
type: ActivatableTypeEnum.ACTION,
|
||||
sortText: ActionAccessors.getName(action),
|
||||
activation,
|
||||
key: ActionAccessors.getUniqueKey(action),
|
||||
limitedUse: ActionAccessors.getLimitedUse(action),
|
||||
entity: action,
|
||||
parentEntity,
|
||||
});
|
||||
}
|
||||
});
|
||||
return activatables;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
*
|
||||
* @param armorClassSources
|
||||
* @param initialValue
|
||||
*/
|
||||
export function deriveArmorClassSupplierTotal(armorClassSources, initialValue = 0) {
|
||||
return armorClassSources.reduce((acc, armorClassSource) => {
|
||||
if (armorClassSource.amount) {
|
||||
acc += armorClassSource.amount;
|
||||
}
|
||||
return acc;
|
||||
}, initialValue);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import { AbilityAccessors } from '../../Ability';
|
||||
import { ModifierDerivers, ModifierValidators } from '../../Modifier';
|
||||
/**
|
||||
*
|
||||
* @param proficiencyBonus
|
||||
* @param abilityModifier
|
||||
*/
|
||||
export function deriveAttackSaveValue(proficiencyBonus, abilityModifier) {
|
||||
return 8 + proficiencyBonus + abilityModifier;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param proficiencyBonus
|
||||
* @param abilityModifier
|
||||
*/
|
||||
export function deriveAttackModifier(proficiencyBonus, abilityModifier) {
|
||||
return proficiencyBonus + abilityModifier;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param availableAbilities
|
||||
* @param modifiers
|
||||
* @param proficiencyBonus
|
||||
* @param abilityLookup
|
||||
*/
|
||||
export function deriveAttackAbilityPossibilities(availableAbilities, modifiers, proficiencyBonus, abilityLookup) {
|
||||
return availableAbilities.map((abilityId) => {
|
||||
const ability = abilityLookup[abilityId];
|
||||
// get bonus stat attack modifiers
|
||||
const bonusStatToHitModifiers = modifiers.filter((modifier) => ModifierValidators.isValidBonusStatAttackModifier(modifier, abilityId));
|
||||
const bonusStatToHitModifierTotal = ModifierDerivers.sumModifiers(bonusStatToHitModifiers, abilityLookup);
|
||||
// get damage stat attack modifiers
|
||||
const damageStatAttackModifiers = modifiers.filter((modifier) => ModifierValidators.isValidDamageStatAttackModifier(modifier, abilityId));
|
||||
const damageStatAttackModifierTotal = ModifierDerivers.sumModifiers(damageStatAttackModifiers, abilityLookup);
|
||||
const abilityModifier = ability ? AbilityAccessors.getModifier(ability) : null;
|
||||
const modifier = abilityModifier ? abilityModifier : 0;
|
||||
const toHit = modifier + proficiencyBonus + bonusStatToHitModifierTotal;
|
||||
return {
|
||||
abilityId,
|
||||
toHit,
|
||||
modifier,
|
||||
damageBonus: damageStatAttackModifierTotal + modifier,
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { DiceAdjustmentTypeEnum } from '../../Dice';
|
||||
import { ModifierDerivers, ModifierValidators } from '../../Modifier';
|
||||
/**
|
||||
*
|
||||
* @param modifiers
|
||||
*/
|
||||
export function deriveAdvantageDeathSavesAdjustments(modifiers) {
|
||||
return modifiers
|
||||
.filter((modifier) => ModifierValidators.isAdvantageDeathSavesModifier(modifier))
|
||||
.map((modifier) => ModifierDerivers.deriveDiceAdjustment(modifier, DiceAdjustmentTypeEnum.ADVANTAGE));
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param modifiers
|
||||
*/
|
||||
export function deriveDisadvantageDeathSavesAdjustments(modifiers) {
|
||||
return modifiers
|
||||
.filter((modifier) => ModifierValidators.isDisadvantageDeathSavesModifier(modifier))
|
||||
.map((modifier) => ModifierDerivers.deriveDiceAdjustment(modifier, DiceAdjustmentTypeEnum.DISADVANTAGE));
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { DataOriginGenerators, DataOriginTypeEnum } from '../../DataOrigin';
|
||||
/**
|
||||
*
|
||||
* @param adjustment
|
||||
*/
|
||||
export function deriveCustomDefenseAdjustmentDataOrigin(adjustment) {
|
||||
return DataOriginGenerators.generateDataOrigin(DataOriginTypeEnum.CUSTOM, adjustment.source ? adjustment.source : 'Custom');
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param adjustment
|
||||
*/
|
||||
export function deriveCustomConditionDefenseAdjustmentDataOrigin(adjustment) {
|
||||
return DataOriginGenerators.generateDataOrigin(DataOriginTypeEnum.CUSTOM, adjustment.source ? adjustment.source : 'Custom');
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import { ItemAccessors, ItemValidators } from '../../Item';
|
||||
import { LimitedUseDerivers } from '../../LimitedUse';
|
||||
import { ModifierAccessors } from '../../Modifier';
|
||||
import { ProtectionAvailabilityStatusEnum } from '../constants';
|
||||
/**
|
||||
*
|
||||
* @param limitedUse
|
||||
* @param abilityLookup
|
||||
* @param ruleData
|
||||
* @param proficiencyBonus
|
||||
*/
|
||||
export function deriveProtectionSupplierAvailabilityStatus(limitedUse, abilityLookup, ruleData, proficiencyBonus) {
|
||||
let availabilityStatus = ProtectionAvailabilityStatusEnum.AVAILABLE;
|
||||
if (!LimitedUseDerivers.deriveHasUsesAvailable(limitedUse, abilityLookup, ruleData, proficiencyBonus)) {
|
||||
availabilityStatus = ProtectionAvailabilityStatusEnum.NO_LIMITED_USE_REMAINING;
|
||||
}
|
||||
return availabilityStatus;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param item
|
||||
* @param modifier
|
||||
* @param limitedUse
|
||||
* @param abilityLookup
|
||||
* @param ruleData
|
||||
* @param proficiencyBonus
|
||||
* @param characterId
|
||||
*/
|
||||
export function deriveItemProtectionSupplierAvailabilityStatus(item, modifier, limitedUse, abilityLookup, ruleData, proficiencyBonus, characterId) {
|
||||
if (ItemAccessors.canEquip(item)) {
|
||||
const isEquippedToCurrentCharacter = ItemValidators.isEquippedToCurrentCharacter(item, characterId);
|
||||
if (!isEquippedToCurrentCharacter) {
|
||||
return ProtectionAvailabilityStatusEnum.ITEM_NOT_EQUIPPED;
|
||||
}
|
||||
if (ModifierAccessors.requiresAttunement(modifier) && !ItemAccessors.isAttuned(item)) {
|
||||
return ProtectionAvailabilityStatusEnum.ITEM_NOT_ATTUNED;
|
||||
}
|
||||
}
|
||||
if (!LimitedUseDerivers.deriveHasUsesAvailable(limitedUse, abilityLookup, ruleData, proficiencyBonus)) {
|
||||
return ProtectionAvailabilityStatusEnum.NO_LIMITED_USE_REMAINING;
|
||||
}
|
||||
return ProtectionAvailabilityStatusEnum.AVAILABLE;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import { ProficiencyLevelEnum, ProficiencyRoundingEnum } from '../../Core';
|
||||
import { ModifierDerivers, ModifierValidators } from '../../Modifier';
|
||||
import { RuleDataUtils } from '../../RuleData';
|
||||
/**
|
||||
*
|
||||
* @param level
|
||||
* @param modifiers
|
||||
* @param ruleData
|
||||
*/
|
||||
export function deriveProficiencyBonus(level, modifiers, ruleData) {
|
||||
const levelBonus = RuleDataUtils.getLevelProficiencyBonus(level, ruleData);
|
||||
const bonusProficiencyBonusModifiers = modifiers.filter((modifier) => ModifierValidators.isBonusProficiencyBonusModifier(modifier));
|
||||
const modifierBonusTotal = ModifierDerivers.sumModifiers(bonusProficiencyBonusModifiers);
|
||||
return levelBonus + modifierBonusTotal;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param proficiencyLevel
|
||||
* @param proficiencyBonus
|
||||
* @param proficiencyRounding
|
||||
*/
|
||||
export function deriveProficiencyBonusAmount(proficiencyLevel, proficiencyBonus, proficiencyRounding = ProficiencyRoundingEnum.DOWN) {
|
||||
let proficiencyBonusAmount = 0;
|
||||
switch (proficiencyLevel) {
|
||||
case ProficiencyLevelEnum.EXPERT:
|
||||
proficiencyBonusAmount = proficiencyBonus * 2;
|
||||
break;
|
||||
case ProficiencyLevelEnum.FULL:
|
||||
proficiencyBonusAmount = proficiencyBonus;
|
||||
break;
|
||||
case ProficiencyLevelEnum.HALF:
|
||||
if (proficiencyRounding === ProficiencyRoundingEnum.UP) {
|
||||
proficiencyBonusAmount = Math.ceil(proficiencyBonus / 2);
|
||||
}
|
||||
else {
|
||||
proficiencyBonusAmount = Math.floor(proficiencyBonus / 2);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// not implemented
|
||||
}
|
||||
return proficiencyBonusAmount;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import { RuleDataAccessors } from '../../RuleData';
|
||||
/**
|
||||
*
|
||||
* @param xp
|
||||
* @param ruleData
|
||||
*/
|
||||
export function deriveXpLevel(xp, ruleData) {
|
||||
let level = 0;
|
||||
if (xp === 0) {
|
||||
return 1;
|
||||
}
|
||||
if (xp >= deriveMaxXp(ruleData)) {
|
||||
return RuleDataAccessors.getMaxCharacterLevel(ruleData);
|
||||
}
|
||||
const levelExperiencePoints = RuleDataAccessors.getLevelExperiencePoints(ruleData);
|
||||
levelExperiencePoints.forEach((levelXpAmount, levelIdx) => {
|
||||
if (level === 0 && xp < levelXpAmount) {
|
||||
level = levelIdx;
|
||||
}
|
||||
});
|
||||
return level;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param level
|
||||
* @param ruleData
|
||||
*/
|
||||
export function deriveCurrentLevelXp(level, ruleData) {
|
||||
const levelExperiencePoints = RuleDataAccessors.getLevelExperiencePoints(ruleData);
|
||||
return levelExperiencePoints[Math.max(0, level - 1)];
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param level
|
||||
* @param ruleData
|
||||
*/
|
||||
export function deriveNextLevelXp(level, ruleData) {
|
||||
const levelExperiencePoints = RuleDataAccessors.getLevelExperiencePoints(ruleData);
|
||||
return levelExperiencePoints[Math.min(levelExperiencePoints.length - 1, level)];
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param ruleData
|
||||
*/
|
||||
export function deriveMaxXp(ruleData) {
|
||||
const levelExperiencePoints = RuleDataAccessors.getLevelExperiencePoints(ruleData);
|
||||
return levelExperiencePoints[levelExperiencePoints.length - 1];
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { SaveTypeEnum } from '../../Core';
|
||||
/**
|
||||
*
|
||||
* @param type
|
||||
*/
|
||||
export function deriveSaveTypeName(type) {
|
||||
let name = '';
|
||||
switch (type) {
|
||||
case SaveTypeEnum.ADVANTAGE:
|
||||
name = 'Advantage';
|
||||
break;
|
||||
case SaveTypeEnum.DISADVANTAGE:
|
||||
name = 'Disadvantage';
|
||||
break;
|
||||
case SaveTypeEnum.BONUS:
|
||||
name = 'Bonus';
|
||||
break;
|
||||
default:
|
||||
//not implemented
|
||||
}
|
||||
return name;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import { AbilitySkillEnum } from '../../Core';
|
||||
import { ModifierDerivers, ModifierValidators } from '../../Modifier';
|
||||
import { SkillAccessors } from '../../Skill';
|
||||
/**
|
||||
*
|
||||
* @param skillModifier
|
||||
* @param bonuses
|
||||
*/
|
||||
export function derivePassiveSkill(skillModifier, bonuses = 0) {
|
||||
return 10 + skillModifier + bonuses;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param skills
|
||||
* @param modifiers
|
||||
* @param abilityLookup
|
||||
*/
|
||||
export function derivePassivePerception(skills, modifiers, abilityLookup) {
|
||||
const skill = skills.find((skill) => SkillAccessors.getId(skill) === AbilitySkillEnum.PERCEPTION);
|
||||
const skillModifier = skill ? SkillAccessors.getModifier(skill) : null;
|
||||
if (!skill || skillModifier === null) {
|
||||
return 0;
|
||||
}
|
||||
const bonusModifiers = modifiers.filter((modifier) => ModifierValidators.isBonusPassivePerceptionModifier(modifier));
|
||||
const bonusModifierTotal = ModifierDerivers.sumModifiers(bonusModifiers, abilityLookup);
|
||||
return derivePassiveSkill(skillModifier, bonusModifierTotal);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param skills
|
||||
* @param modifiers
|
||||
* @param abilityLookup
|
||||
*/
|
||||
export function derivePassiveInvestigation(skills, modifiers, abilityLookup) {
|
||||
const skill = skills.find((skill) => SkillAccessors.getId(skill) === AbilitySkillEnum.INVESTIGATION);
|
||||
const skillModifier = skill ? SkillAccessors.getModifier(skill) : null;
|
||||
if (!skill || skillModifier === null) {
|
||||
return 0;
|
||||
}
|
||||
const bonusModifiers = modifiers.filter((modifier) => ModifierValidators.isBonusPassiveInvestigationModifier(modifier));
|
||||
const bonusModifierTotal = ModifierDerivers.sumModifiers(bonusModifiers, abilityLookup);
|
||||
return derivePassiveSkill(skillModifier, bonusModifierTotal);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param skills
|
||||
* @param modifiers
|
||||
* @param abilityLookup
|
||||
*/
|
||||
export function derivePassiveInsight(skills, modifiers, abilityLookup) {
|
||||
const skill = skills.find((skill) => SkillAccessors.getId(skill) === AbilitySkillEnum.INSIGHT);
|
||||
const skillModifier = skill ? SkillAccessors.getModifier(skill) : null;
|
||||
if (!skill || skillModifier === null) {
|
||||
return 0;
|
||||
}
|
||||
const bonusModifiers = modifiers.filter((modifier) => ModifierValidators.isBonusPassiveInsightModifier(modifier));
|
||||
const bonusModifierTotal = ModifierDerivers.sumModifiers(bonusModifiers, abilityLookup);
|
||||
return derivePassiveSkill(skillModifier, bonusModifierTotal);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { PreferenceEncumbranceTypeEnum, WeightSpeedTypeEnum } from '../../Core';
|
||||
import { getCarryCapacity, getEncumberedWeight, getHeavilyEncumberedWeight } from '../utils';
|
||||
/**
|
||||
*
|
||||
* @param currentWeight
|
||||
* @param strScore
|
||||
* @param size
|
||||
* @param preferences
|
||||
* @param modifiers
|
||||
*/
|
||||
export function deriveCurrentWeightSpeedType(currentWeight, strScore, size, preferences, modifiers) {
|
||||
if (preferences !== null) {
|
||||
if (preferences.encumbranceType === PreferenceEncumbranceTypeEnum.VARIANT) {
|
||||
if (currentWeight > getHeavilyEncumberedWeight(strScore, size, modifiers)) {
|
||||
return WeightSpeedTypeEnum.HEAVILY_ENCUMBERED;
|
||||
}
|
||||
else if (currentWeight > getEncumberedWeight(strScore, size, modifiers)) {
|
||||
return WeightSpeedTypeEnum.ENCUMBERED;
|
||||
}
|
||||
}
|
||||
else if (preferences.encumbranceType === PreferenceEncumbranceTypeEnum.ENCUMBRANCE) {
|
||||
if (currentWeight > getCarryCapacity(strScore, size, modifiers)) {
|
||||
return WeightSpeedTypeEnum.OVER_CARRYING_CAPACITY;
|
||||
}
|
||||
}
|
||||
}
|
||||
return WeightSpeedTypeEnum.NORMAL;
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
import { orderBy } from 'lodash';
|
||||
import { ActionAccessors } from '../../Action';
|
||||
import { ActivationAccessors } from '../../Activation';
|
||||
import { ClassAccessors } from '../../Class';
|
||||
import { ClassFeatureAccessors } from '../../ClassFeature';
|
||||
import { ActivatableTypeEnum } from '../../Core';
|
||||
import { FeatAccessors } from '../../Feat';
|
||||
import { ItemAccessors, ItemValidators } from '../../Item';
|
||||
import { RaceAccessors } from '../../Race';
|
||||
import { RacialTraitAccessors } from '../../RacialTrait';
|
||||
import { SpellAccessors } from '../../Spell';
|
||||
import { deriveActivatableActions, deriveActivatableOptions } from '../derivers';
|
||||
/**
|
||||
*
|
||||
* @param characterSpells
|
||||
* @param classSpells
|
||||
* @param classes
|
||||
* @param race
|
||||
* @param feats
|
||||
* @param innateActions
|
||||
* @param customActions
|
||||
* @param equippedItems
|
||||
*/
|
||||
export function generateActivatables(characterSpells, classSpells, classes, race, feats, innateActions, // TODO might need to change to InnateAction
|
||||
customActions, // TODO might need to change to CustomAction
|
||||
equippedItems) {
|
||||
const activatables = [];
|
||||
characterSpells.forEach((spell) => {
|
||||
const activation = SpellAccessors.getActivation(spell);
|
||||
if (activation !== null && ActivationAccessors.getType(activation) !== null) {
|
||||
activatables.push({
|
||||
type: ActivatableTypeEnum.CHARACTER_SPELL,
|
||||
sortText: SpellAccessors.getName(spell),
|
||||
key: SpellAccessors.getUniqueKey(spell),
|
||||
activation,
|
||||
limitedUse: SpellAccessors.getLimitedUse(spell),
|
||||
entity: spell,
|
||||
parentEntity: null,
|
||||
});
|
||||
}
|
||||
});
|
||||
classSpells.forEach((spell) => {
|
||||
const activation = SpellAccessors.getActivation(spell);
|
||||
if (activation !== null && ActivationAccessors.getType(activation) !== null) {
|
||||
activatables.push({
|
||||
type: ActivatableTypeEnum.CLASS_SPELL,
|
||||
sortText: SpellAccessors.getName(spell),
|
||||
key: SpellAccessors.getUniqueKey(spell),
|
||||
activation,
|
||||
limitedUse: null,
|
||||
entity: spell,
|
||||
parentEntity: null,
|
||||
});
|
||||
}
|
||||
});
|
||||
classes.forEach((charClass) => {
|
||||
ClassAccessors.getUniqueClassFeatures(charClass).forEach((feature) => {
|
||||
const actions = ClassFeatureAccessors.getActions(feature);
|
||||
const options = ClassFeatureAccessors.getOptions(feature);
|
||||
if (actions.length) {
|
||||
activatables.push(...deriveActivatableActions(actions, charClass));
|
||||
}
|
||||
if (options.length) {
|
||||
activatables.push(...deriveActivatableOptions(options, charClass));
|
||||
}
|
||||
});
|
||||
});
|
||||
if (race) {
|
||||
RaceAccessors.getRacialTraits(race).forEach((racialTrait) => {
|
||||
const actions = RacialTraitAccessors.getActions(racialTrait);
|
||||
const options = RacialTraitAccessors.getOptions(racialTrait);
|
||||
if (actions.length) {
|
||||
activatables.push(...deriveActivatableActions(actions, race));
|
||||
}
|
||||
if (options.length) {
|
||||
activatables.push(...deriveActivatableOptions(options, race));
|
||||
}
|
||||
});
|
||||
}
|
||||
feats.forEach((feat) => {
|
||||
const actions = FeatAccessors.getActions(feat);
|
||||
const options = FeatAccessors.getOptions(feat);
|
||||
if (actions.length) {
|
||||
activatables.push(...deriveActivatableActions(actions, race));
|
||||
}
|
||||
if (options.length) {
|
||||
activatables.push(...deriveActivatableOptions(options, race));
|
||||
}
|
||||
});
|
||||
innateActions.forEach((action) => {
|
||||
const activation = ActionAccessors.getActivation(action);
|
||||
if (activation !== null) {
|
||||
activatables.push({
|
||||
type: ActivatableTypeEnum.ACTION,
|
||||
sortText: ActionAccessors.getName(action),
|
||||
activation,
|
||||
key: ActionAccessors.getUniqueKey(action),
|
||||
limitedUse: null,
|
||||
entity: action,
|
||||
parentEntity: null,
|
||||
});
|
||||
}
|
||||
});
|
||||
customActions.forEach((action) => {
|
||||
const activation = ActionAccessors.getActivation(action);
|
||||
if (activation !== null && ActivationAccessors.getType(activation) !== null) {
|
||||
activatables.push({
|
||||
type: ActivatableTypeEnum.ACTION,
|
||||
sortText: ActionAccessors.getName(action),
|
||||
activation,
|
||||
key: ActionAccessors.getUniqueKey(action),
|
||||
limitedUse: null,
|
||||
entity: action,
|
||||
parentEntity: null,
|
||||
});
|
||||
}
|
||||
});
|
||||
equippedItems.filter(ItemValidators.validateCanContributeActions).forEach((item) => {
|
||||
activatables.push(...deriveActivatableActions(ItemAccessors.getInfusionActions(item), item));
|
||||
});
|
||||
return orderBy(activatables, ['sortText']);
|
||||
}
|
||||
@@ -0,0 +1,294 @@
|
||||
import { forOwn, groupBy } from 'lodash';
|
||||
import { AbilityAccessors } from '../../Ability';
|
||||
import { AbilityStatEnum } from '../../Core';
|
||||
import { DataOriginTypeEnum } from '../../DataOrigin';
|
||||
import { HelperUtils } from '../../Helper';
|
||||
import { ArmorTypeEnum, ItemAccessors, ItemDerivers, ItemHacks, ItemValidators, } from '../../Item';
|
||||
import { ModifierAccessors, ModifierDerivers, ModifierGenerators, ModifierHacks, ModifierValidators, } from '../../Modifier';
|
||||
import { AdjustmentTypeEnum, ValueAccessors, ValueUtils } from '../../Value';
|
||||
import { ArmorClassExtraTypeEnum, ArmorClassTypeEnum } from '../constants';
|
||||
import { deriveArmorClassSupplierTotal } from '../derivers';
|
||||
/**
|
||||
*
|
||||
* @param type
|
||||
* @param amount
|
||||
* @param extra
|
||||
* @param extraType
|
||||
*/
|
||||
export function generateArmorClassSupplier(type, amount, extra = null, extraType = null) {
|
||||
return {
|
||||
type,
|
||||
amount,
|
||||
extra,
|
||||
extraType,
|
||||
};
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param armorItem
|
||||
* @param localModifiers
|
||||
* @param globalModifiers
|
||||
* @param abilityLookup
|
||||
* @param valueLookup
|
||||
* @param modifierData
|
||||
*/
|
||||
export function generateArmoredBaseArmorSuppliers(armorItem, localModifiers, globalModifiers, abilityLookup, valueLookup, modifierData) {
|
||||
const dexModifier = AbilityAccessors.getModifier(abilityLookup[AbilityStatEnum.DEXTERITY]);
|
||||
const baseArmorOverrideValue = ValueUtils.getKeyValue(valueLookup, AdjustmentTypeEnum.OVERRIDE_BASE_ARMOR);
|
||||
const suppliers = [];
|
||||
if (baseArmorOverrideValue === null) {
|
||||
suppliers.push(generateArmorClassSupplier(ArmorClassTypeEnum.ARMOR, armorItem.armorClass, armorItem.item, ArmorClassExtraTypeEnum.ITEM));
|
||||
suppliers.push(generateArmorClassSupplier(ArmorClassTypeEnum.DEX_BONUS, ItemDerivers.deriveArmorDexBonus(armorItem.item, dexModifier ? dexModifier : 0, globalModifiers, abilityLookup), ItemDerivers.deriveArmorDexBonusMax(armorItem.item, globalModifiers, abilityLookup), ArmorClassExtraTypeEnum.NUMBER));
|
||||
}
|
||||
const setArmoredModifiers = localModifiers.filter((modifier) => ModifierValidators.isSetArmoredArmorClassModifier(modifier));
|
||||
const setArmoredHighestModifier = ModifierDerivers.deriveHighestValueModifier(setArmoredModifiers, modifierData);
|
||||
if (setArmoredHighestModifier) {
|
||||
suppliers.push(generateArmorClassSupplier(ArmorClassTypeEnum.ARMORED_BONUS, ModifierDerivers.deriveValue(setArmoredHighestModifier, modifierData), setArmoredHighestModifier, ArmorClassExtraTypeEnum.MODIFIER));
|
||||
}
|
||||
const bonusArmoredModifiers = globalModifiers.filter((modifier) => ModifierValidators.isBonusArmoredArmorClassModifier(modifier));
|
||||
bonusArmoredModifiers.forEach((modifier) => {
|
||||
suppliers.push(generateArmorClassSupplier(ArmorClassTypeEnum.ARMORED_BONUS, ModifierDerivers.deriveValue(modifier, modifierData), modifier, ArmorClassExtraTypeEnum.MODIFIER));
|
||||
});
|
||||
return suppliers;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param localModifiers
|
||||
* @param globalModifiers
|
||||
* @param abilityLookup
|
||||
* @param valueLookup
|
||||
* @param ruleData
|
||||
* @param modifierData
|
||||
*/
|
||||
export function generateUnarmoredBaseArmorSuppliers(localModifiers, globalModifiers, abilityLookup, valueLookup, ruleData, modifierData) {
|
||||
const baseArmorOverrideValue = ValueUtils.getKeyValue(valueLookup, AdjustmentTypeEnum.OVERRIDE_BASE_ARMOR);
|
||||
const suppliers = [];
|
||||
if (baseArmorOverrideValue === null) {
|
||||
suppliers.push(generateArmorClassSupplier(ArmorClassTypeEnum.ARMOR, ruleData.noArmorAcAmount, 'None', ArmorClassExtraTypeEnum.STRING));
|
||||
const unarmoredDexModifier = ItemDerivers.deriveUnarmoredDexBonus(localModifiers, globalModifiers, abilityLookup);
|
||||
if (unarmoredDexModifier !== 0) {
|
||||
suppliers.push(generateArmorClassSupplier(ArmorClassTypeEnum.DEX_BONUS, unarmoredDexModifier, ItemDerivers.deriveUnarmoredDexBonusMax(globalModifiers), ArmorClassExtraTypeEnum.NUMBER));
|
||||
}
|
||||
}
|
||||
const setUnarmoredModifiers = localModifiers.filter((modifier) => ModifierValidators.isSetUnarmoredArmorClassModifier(modifier));
|
||||
// TODO: Fix Dirty Hack for Integrated Protection
|
||||
const hasLightArmorProficiency = globalModifiers.filter((modifier) => ModifierHacks.hack__isProficiencyLightArmor(modifier)).length > 0;
|
||||
if (setUnarmoredModifiers.length) {
|
||||
// let setUnarmoredHighestModifier = ModifierUtils.getHighestValueModifier(setUnarmoredModifiers, modifierData);
|
||||
const setUnarmoredHighestModifier = HelperUtils.getLast(setUnarmoredModifiers, (modifier) => ItemHacks.hack__getSetUnarmoredModifierValue(modifier, modifierData, hasLightArmorProficiency));
|
||||
if (setUnarmoredHighestModifier) {
|
||||
suppliers.push(generateArmorClassSupplier(ArmorClassTypeEnum.UNARMORED_BONUS, ItemHacks.hack__getSetUnarmoredModifierValue(setUnarmoredHighestModifier, modifierData, hasLightArmorProficiency), setUnarmoredHighestModifier, ArmorClassExtraTypeEnum.MODIFIER));
|
||||
}
|
||||
}
|
||||
const bonusUnarmoredModifiers = globalModifiers.filter((modifier) => ModifierValidators.isBonusUnarmoredArmorClassModifier(modifier));
|
||||
bonusUnarmoredModifiers.forEach((modifier) => {
|
||||
suppliers.push(generateArmorClassSupplier(ArmorClassTypeEnum.UNARMORED_BONUS, ModifierDerivers.deriveValue(modifier, modifierData), modifier, ArmorClassExtraTypeEnum.MODIFIER));
|
||||
});
|
||||
return suppliers;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param armorItem
|
||||
* @param modifiers
|
||||
* @param abilityLookup
|
||||
* @param valueLookup
|
||||
* @param ruleData
|
||||
* @param modifierData
|
||||
*/
|
||||
export function generateBaseArmorSuppliers(armorItem, modifiers, abilityLookup, valueLookup, ruleData, modifierData) {
|
||||
const branchPossibilities = [];
|
||||
const branchingModifiers = modifiers.filter((modifier) => ModifierValidators.isArmorBranchingModifier(modifier));
|
||||
const groupedBranchModifiers = groupBy(branchingModifiers, (modifier) => ModifierGenerators.generateDataOriginKey(modifier));
|
||||
forOwn(groupedBranchModifiers, (branchGroupModifiers) => {
|
||||
const ignoreUnarmoredWhileArmoredModifiers = branchGroupModifiers.filter((modifier) => ModifierValidators.isIgnoreUnarmoredWhileArmoredModifier(modifier));
|
||||
const ignoreUnarmoredWhileArmored = !!ignoreUnarmoredWhileArmoredModifiers.length;
|
||||
if (armorItem) {
|
||||
branchPossibilities.push(generateBaseArmorBranchPossibility(armorItem, branchGroupModifiers, modifiers, abilityLookup, valueLookup, ruleData, modifierData));
|
||||
if (!ignoreUnarmoredWhileArmored) {
|
||||
branchPossibilities.push(generateBaseArmorBranchPossibility(null, branchGroupModifiers, modifiers, abilityLookup, valueLookup, ruleData, modifierData));
|
||||
}
|
||||
}
|
||||
else {
|
||||
branchPossibilities.push(generateBaseArmorBranchPossibility(null, branchGroupModifiers, modifiers, abilityLookup, valueLookup, ruleData, modifierData));
|
||||
}
|
||||
});
|
||||
// innate armor calc
|
||||
branchPossibilities.push(generateBaseArmorBranchPossibility(armorItem, [], modifiers, abilityLookup, valueLookup, ruleData, modifierData));
|
||||
const bestBaseArmorBranchPossibility = HelperUtils.getLast(branchPossibilities, (possibility) => possibility.total);
|
||||
if (bestBaseArmorBranchPossibility === null) {
|
||||
return [];
|
||||
}
|
||||
return bestBaseArmorBranchPossibility.suppliers;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param mainhandWeapons
|
||||
* @param offhandWeapons
|
||||
* @param modifiers
|
||||
* @param modifierData
|
||||
*/
|
||||
export function generateDualWieldArmorClassSuppliers(mainhandWeapons, offhandWeapons, modifiers, modifierData) {
|
||||
const armorClassSuppliers = [];
|
||||
const bonusDualWieldAcModifiers = modifiers.filter((modifier) => ModifierValidators.isBonusDualWieldAcModifier(modifier));
|
||||
if (bonusDualWieldAcModifiers.length && mainhandWeapons.length && offhandWeapons.length) {
|
||||
bonusDualWieldAcModifiers.forEach((modifier) => {
|
||||
armorClassSuppliers.push(generateArmorClassSupplier(ArmorClassTypeEnum.MISC_BONUS, ModifierDerivers.deriveValue(modifier, modifierData), modifier, ArmorClassExtraTypeEnum.MODIFIER));
|
||||
});
|
||||
}
|
||||
return armorClassSuppliers;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param armorItem
|
||||
* @param shieldItem
|
||||
* @param modifiers
|
||||
* @param abilityLookup
|
||||
* @param equippedItems
|
||||
* @param valueLookup
|
||||
* @param ruleData
|
||||
* @param modifierData
|
||||
*/
|
||||
export function generateArmorClassSuppliers(armorItem, shieldItem, modifiers, abilityLookup, equippedItems, valueLookup, ruleData, modifierData) {
|
||||
const armorClassSuppliers = [];
|
||||
// base armor AC sources based on potential branching paths
|
||||
const baseArmorSuppliers = generateBaseArmorSuppliers(armorItem, modifiers, abilityLookup, valueLookup, ruleData, modifierData);
|
||||
armorClassSuppliers.push(...baseArmorSuppliers);
|
||||
// TODO part of dirty hack
|
||||
const isWearingHeavyArmor = armorItem
|
||||
? ItemAccessors.getArmorTypeId(armorItem.item) === ArmorTypeEnum.HEAVY_ARMOR
|
||||
: false;
|
||||
// general AC bonuses (for both unarmored and armored)
|
||||
const armorModifiers = modifiers.filter((modifier) => ModifierValidators.isBonusArmorModifier(modifier));
|
||||
armorModifiers.forEach((modifier) => {
|
||||
const dataOrigin = ModifierAccessors.getDataOrigin(modifier);
|
||||
const dataOriginType = ModifierAccessors.getDataOriginType(modifier);
|
||||
let type = ArmorClassTypeEnum.MISC_BONUS;
|
||||
if (dataOriginType === DataOriginTypeEnum.ITEM) {
|
||||
// TODO This may cause issues with magic infusions, but should be minor and can be fixed when we do overhaul of accessing DataOrigin entities
|
||||
type = ItemAccessors.getDefinitionMagic(dataOrigin.primary)
|
||||
? ArmorClassTypeEnum.MAGIC_BONUS
|
||||
: ArmorClassTypeEnum.MISC_BONUS;
|
||||
}
|
||||
// TODO part of dirty hack
|
||||
const value = ItemHacks.hack__deriveBonusArmorModifierValue(modifier, modifierData, isWearingHeavyArmor);
|
||||
armorClassSuppliers.push(generateArmorClassSupplier(type, value,
|
||||
// ModifierDerivers.deriveValue(modifier, modifierData),
|
||||
modifier, ArmorClassExtraTypeEnum.MODIFIER));
|
||||
});
|
||||
// misc overall bonuses
|
||||
const shieldArmorClass = shieldItem ? shieldItem.armorClass : 0;
|
||||
const mainhandWeapons = equippedItems.filter(ItemValidators.isEquippedMainhandWeapon);
|
||||
const offhandWeapons = equippedItems.filter(ItemValidators.isEquippedOffhandWeapon);
|
||||
const dualWieldArmorClassSuppliers = generateDualWieldArmorClassSuppliers(mainhandWeapons, offhandWeapons, modifiers, modifierData);
|
||||
const dualWieldArmorClassBonus = deriveArmorClassSupplierTotal(dualWieldArmorClassSuppliers);
|
||||
if (dualWieldArmorClassBonus > 0 && dualWieldArmorClassBonus > shieldArmorClass) {
|
||||
armorClassSuppliers.push(...dualWieldArmorClassSuppliers);
|
||||
}
|
||||
else {
|
||||
// best shield AC
|
||||
if (shieldItem) {
|
||||
armorClassSuppliers.push(generateArmorClassSupplier(ArmorClassTypeEnum.SHIELD, shieldItem.armorClass, shieldItem.item, ArmorClassExtraTypeEnum.ITEM));
|
||||
}
|
||||
}
|
||||
// overall magic bonus adjustment value
|
||||
const magicBonusAdjustment = ValueUtils.getData(valueLookup, AdjustmentTypeEnum.MAGIC_BONUS_AC);
|
||||
if (magicBonusAdjustment !== null) {
|
||||
armorClassSuppliers.push(generateArmorClassSupplier(ArmorClassTypeEnum.MAGIC_BONUS, ValueAccessors.getValue(magicBonusAdjustment), ValueAccessors.getNotes(magicBonusAdjustment), ArmorClassExtraTypeEnum.STRING));
|
||||
}
|
||||
// overall misc bonus adjustment value
|
||||
const miscBonusAdjustment = ValueUtils.getData(valueLookup, AdjustmentTypeEnum.MISC_BONUS_AC);
|
||||
if (miscBonusAdjustment !== null) {
|
||||
armorClassSuppliers.push(generateArmorClassSupplier(ArmorClassTypeEnum.MISC_BONUS, ValueAccessors.getValue(miscBonusAdjustment), ValueAccessors.getNotes(miscBonusAdjustment), ArmorClassExtraTypeEnum.STRING));
|
||||
}
|
||||
return armorClassSuppliers;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param armorItem
|
||||
* @param localModifiers
|
||||
* @param globalModifiers
|
||||
* @param abilityLookup
|
||||
* @param valueLookup
|
||||
* @param ruleData
|
||||
* @param modifierData
|
||||
*/
|
||||
export function generateBaseArmorBranchPossibility(armorItem, localModifiers, globalModifiers, abilityLookup, valueLookup, ruleData, modifierData) {
|
||||
let suppliers = [];
|
||||
const baseArmorOverride = ValueUtils.getData(valueLookup, AdjustmentTypeEnum.OVERRIDE_BASE_ARMOR);
|
||||
if (baseArmorOverride !== null) {
|
||||
suppliers.push(generateArmorClassSupplier(ArmorClassTypeEnum.OVERRIDE_BASE_ARMOR, ValueAccessors.getValue(baseArmorOverride), ValueAccessors.getNotes(baseArmorOverride), ArmorClassExtraTypeEnum.STRING));
|
||||
}
|
||||
// basearmor + set highest + status bonuses
|
||||
// - set highest is all of the "set > (un)armored armor class" modifiers
|
||||
// - status bonuses are bonuses based on whether you are armored or unarmored
|
||||
if (armorItem) {
|
||||
suppliers.push(...generateArmoredBaseArmorSuppliers(armorItem, localModifiers, globalModifiers, abilityLookup, valueLookup, modifierData));
|
||||
}
|
||||
else {
|
||||
suppliers.push(...generateUnarmoredBaseArmorSuppliers(localModifiers, globalModifiers, abilityLookup, valueLookup, ruleData, modifierData));
|
||||
}
|
||||
const minimumBaseArmorModifiers = localModifiers.filter((modifier) => ModifierValidators.isSetMinimumBaseArmorModifier(modifier));
|
||||
if (minimumBaseArmorModifiers.length) {
|
||||
const minBaseArmorModifier = minimumBaseArmorModifiers[0];
|
||||
const setMinBaseArmorValue = ModifierDerivers.deriveValue(minBaseArmorModifier, modifierData);
|
||||
if (setMinBaseArmorValue > deriveArmorClassSupplierTotal(suppliers)) {
|
||||
suppliers = [
|
||||
generateArmorClassSupplier(ArmorClassTypeEnum.ARMOR, setMinBaseArmorValue, minBaseArmorModifier, ArmorClassExtraTypeEnum.MODIFIER),
|
||||
];
|
||||
}
|
||||
}
|
||||
return {
|
||||
total: deriveArmorClassSupplierTotal(suppliers),
|
||||
suppliers,
|
||||
};
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param equippedItems
|
||||
* @param dexModifier
|
||||
* @param modifiers
|
||||
* @param abilityLookup
|
||||
* @param ruleData
|
||||
*/
|
||||
export function generateHighestAcEquippedArmor(equippedItems, dexModifier, modifiers, abilityLookup, ruleData) {
|
||||
const equippedArmor = equippedItems.filter(ItemValidators.isEquippedNonShieldArmor);
|
||||
return ItemDerivers.deriveHighestArmorAcItem(equippedArmor, dexModifier, modifiers, abilityLookup, ruleData);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param equippedItems
|
||||
* @param dexModifier
|
||||
* @param modifiers
|
||||
* @param abilityLookup
|
||||
* @param ruleData
|
||||
*/
|
||||
export function generateHighestAcEquippedShield(equippedItems, dexModifier, modifiers, abilityLookup, ruleData) {
|
||||
const equippedShields = equippedItems.filter(ItemValidators.isEquippedShield);
|
||||
return ItemDerivers.deriveHighestArmorAcItem(equippedShields, dexModifier, modifiers, abilityLookup, ruleData);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param valueLookup
|
||||
*/
|
||||
export function generateArmorClassAdjustments(valueLookup) {
|
||||
return {
|
||||
overrideAc: ValueUtils.getData(valueLookup, AdjustmentTypeEnum.OVERRIDE_AC),
|
||||
overrideBaseArmor: ValueUtils.getData(valueLookup, AdjustmentTypeEnum.OVERRIDE_BASE_ARMOR),
|
||||
miscBonus: ValueUtils.getData(valueLookup, AdjustmentTypeEnum.MISC_BONUS_AC),
|
||||
magicBonus: ValueUtils.getData(valueLookup, AdjustmentTypeEnum.MAGIC_BONUS_AC),
|
||||
};
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param armorClassSuppliers
|
||||
* @param armorClassAdjustments
|
||||
*/
|
||||
export function generateArmorClassTotal(armorClassSuppliers, armorClassAdjustments) {
|
||||
if (armorClassAdjustments.overrideAc !== null) {
|
||||
const overrideAcValue = ValueUtils.getTypedCharacterValueValue(armorClassAdjustments.overrideAc);
|
||||
if (overrideAcValue !== null) {
|
||||
return overrideAcValue;
|
||||
}
|
||||
}
|
||||
return deriveArmorClassSupplierTotal(armorClassSuppliers);
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
import { orderBy } from 'lodash';
|
||||
import { ActionAccessors } from '../../Action';
|
||||
import { ActivationAccessors, ActivationTypeEnum } from '../../Activation';
|
||||
import { ItemAccessors } from '../../Item';
|
||||
import { ModifierAccessors, ModifierValidators } from '../../Modifier';
|
||||
import { SpellAccessors } from '../../Spell';
|
||||
import { AttackSourceTypeEnum } from '../constants';
|
||||
/**
|
||||
*
|
||||
* @param actions
|
||||
* @param itemAttacks
|
||||
* @param spellAttackList
|
||||
* @param customAttacks
|
||||
*/
|
||||
export function generateAttacks(actions, itemAttacks, spellAttackList, customAttacks) {
|
||||
const attacks = [];
|
||||
itemAttacks.forEach((attack) => {
|
||||
let activation = ActivationTypeEnum.ACTION;
|
||||
if (ItemAccessors.isOffhand(attack)) {
|
||||
activation = ActivationTypeEnum.BONUS_ACTION;
|
||||
}
|
||||
attacks.push({
|
||||
type: AttackSourceTypeEnum.ITEM,
|
||||
key: `${AttackSourceTypeEnum.ITEM}-${attack.id}`,
|
||||
activation,
|
||||
data: attack,
|
||||
});
|
||||
});
|
||||
spellAttackList.forEach((attack) => {
|
||||
attacks.push({
|
||||
type: AttackSourceTypeEnum.SPELL,
|
||||
key: `${AttackSourceTypeEnum.SPELL}-${SpellAccessors.getUniqueKey(attack)}`,
|
||||
activation: SpellAccessors.getActivationType(attack),
|
||||
data: attack,
|
||||
});
|
||||
});
|
||||
actions.forEach((action, idx) => {
|
||||
if (!ActionAccessors.displayAsAttack(action)) {
|
||||
return;
|
||||
}
|
||||
attacks.push({
|
||||
type: AttackSourceTypeEnum.ACTION,
|
||||
key: `${AttackSourceTypeEnum.ACTION}-${idx}-${ActionAccessors.getUniqueKey(action)}`,
|
||||
activation: ActivationAccessors.getType(ActionAccessors.getActivation(action)),
|
||||
data: action,
|
||||
});
|
||||
});
|
||||
customAttacks.forEach((attack) => {
|
||||
if (!ActionAccessors.displayAsAttack(attack)) {
|
||||
return;
|
||||
}
|
||||
attacks.push({
|
||||
type: AttackSourceTypeEnum.CUSTOM,
|
||||
key: `${AttackSourceTypeEnum.CUSTOM}-${ActionAccessors.getUniqueKey(attack)}`,
|
||||
activation: ActivationAccessors.getType(ActionAccessors.getActivation(attack)),
|
||||
data: attack,
|
||||
});
|
||||
});
|
||||
return attacks;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param modifiers
|
||||
*/
|
||||
export function generateAttacksPerActionInfo(modifiers) {
|
||||
const extraAttackModifiers = modifiers.filter((modifier) => ModifierValidators.isSetExtraAttacksModifier(modifier));
|
||||
const sortedExtraAttackModifiers = orderBy(extraAttackModifiers, [
|
||||
(modifier) => ModifierAccessors.getValue(modifier),
|
||||
(modifier) => {
|
||||
const restriction = ModifierAccessors.getRestriction(modifier);
|
||||
return restriction !== null && restriction.length;
|
||||
},
|
||||
], ['asc', 'desc']);
|
||||
const attacksPerActionBestModifier = sortedExtraAttackModifiers.pop();
|
||||
const defaultValue = 0;
|
||||
const defaultRestriction = '';
|
||||
let attackInfoValue = defaultValue;
|
||||
let attackInfoRestriction = defaultRestriction;
|
||||
if (attacksPerActionBestModifier) {
|
||||
const bestModiferValue = ModifierAccessors.getValue(attacksPerActionBestModifier);
|
||||
attackInfoValue = bestModiferValue ? bestModiferValue : defaultValue;
|
||||
const bestModifierRestriction = ModifierAccessors.getRestriction(attacksPerActionBestModifier);
|
||||
attackInfoRestriction = bestModifierRestriction ? bestModifierRestriction : defaultRestriction;
|
||||
}
|
||||
return {
|
||||
value: 1 + attackInfoValue,
|
||||
restriction: attackInfoRestriction,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import { HelperUtils } from '../../Helper';
|
||||
import { RaceAccessors } from '../../Race';
|
||||
import { RuleDataAccessors } from '../../RuleData';
|
||||
/**
|
||||
*
|
||||
* @param alignmentId
|
||||
* @param ruleData
|
||||
*/
|
||||
export function generateAlignment(alignmentId, ruleData) {
|
||||
if (alignmentId === null) {
|
||||
return null;
|
||||
}
|
||||
return HelperUtils.lookupDataOrFallback(RuleDataAccessors.getAlignmentLookup(ruleData), alignmentId);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param lifeStyleId
|
||||
* @param ruleData
|
||||
*/
|
||||
export function generateLifestyle(lifeStyleId, ruleData) {
|
||||
if (lifeStyleId === null) {
|
||||
return null;
|
||||
}
|
||||
return HelperUtils.lookupDataOrFallback(RuleDataAccessors.getLifestyleLookup(ruleData), lifeStyleId);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param race
|
||||
*/
|
||||
export function generateSize(race) {
|
||||
if (race === null) {
|
||||
return null;
|
||||
}
|
||||
return RaceAccessors.getSizeInfo(race);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param traitsContract
|
||||
*/
|
||||
export function generateCharacterTraits(traitsContract) {
|
||||
if (traitsContract !== null) {
|
||||
return traitsContract;
|
||||
}
|
||||
return {
|
||||
appearance: null,
|
||||
bonds: null,
|
||||
flaws: null,
|
||||
ideals: null,
|
||||
personalityTraits: null,
|
||||
};
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param notesContract
|
||||
*/
|
||||
export function generateCharacterNotes(notesContract) {
|
||||
if (notesContract !== null) {
|
||||
return notesContract;
|
||||
}
|
||||
return {
|
||||
allies: null,
|
||||
backstory: null,
|
||||
enemies: null,
|
||||
organizations: null,
|
||||
otherHoldings: null,
|
||||
otherNotes: null,
|
||||
personalPossessions: null,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import { MovementTypeEnum, PreferenceAbilityScoreDisplayTypeEnum, PreferenceEncumbranceTypeEnum, PreferenceHitPointTypeEnum, PreferencePrivacyTypeEnum, PreferenceProgressionTypeEnum, PreferenceSharingTypeEnum, SenseTypeEnum, } from '../../Core';
|
||||
/**
|
||||
*
|
||||
* @param race
|
||||
* @param classes
|
||||
* @param stats
|
||||
* @param configuration
|
||||
*/
|
||||
export function generateIsCharacterSheetReady(race, classes, stats, configuration) {
|
||||
if (!race) {
|
||||
return false;
|
||||
}
|
||||
if (!classes.length) {
|
||||
return false;
|
||||
}
|
||||
let hasAllStats = true;
|
||||
if (configuration.abilityScoreType === null) {
|
||||
hasAllStats = false;
|
||||
}
|
||||
stats.forEach((stat) => {
|
||||
if (stat.value === null) {
|
||||
hasAllStats = false;
|
||||
}
|
||||
});
|
||||
if (!hasAllStats) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param preferencesContract
|
||||
*/
|
||||
export function generateCharacterPreferences(preferencesContract, featureFlagInfo) {
|
||||
if (preferencesContract !== null) {
|
||||
return preferencesContract;
|
||||
}
|
||||
return {
|
||||
abilityScoreDisplayType: PreferenceAbilityScoreDisplayTypeEnum.MODIFIERS_TOP,
|
||||
enableOptionalClassFeatures: false,
|
||||
enableOptionalOrigins: false,
|
||||
encumbranceType: PreferenceEncumbranceTypeEnum.ENCUMBRANCE,
|
||||
enforceFeatRules: true,
|
||||
enforceMulticlassRules: true,
|
||||
hitPointType: PreferenceHitPointTypeEnum.FIXED,
|
||||
ignoreCoinWeight: true,
|
||||
primaryMovement: MovementTypeEnum.WALK,
|
||||
primarySense: SenseTypeEnum.PASSIVE_PERCEPTION,
|
||||
privacyType: PreferencePrivacyTypeEnum.CAMPAIGN_ONLY,
|
||||
progressionType: PreferenceProgressionTypeEnum.MILESTONE,
|
||||
sharingType: PreferenceSharingTypeEnum.LIMITED,
|
||||
showCompanions: false,
|
||||
showScaledSpells: true,
|
||||
showUnarmedStrike: true,
|
||||
showWildShape: false,
|
||||
enableDarkMode: false,
|
||||
useHomebrewContent: true,
|
||||
enableContainerCurrency: false,
|
||||
};
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param configurationContract
|
||||
*/
|
||||
export function generateCharacterConfiguration(configurationContract) {
|
||||
if (configurationContract !== null) {
|
||||
return configurationContract;
|
||||
}
|
||||
return {
|
||||
abilityScoreType: null,
|
||||
showHelpText: false,
|
||||
startingEquipmentType: null,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import { ModifierValidators } from '../../Modifier';
|
||||
import { RuleDataAccessors } from '../../RuleData';
|
||||
import { DeathCauseEnum } from '../constants';
|
||||
import { deriveAdvantageDeathSavesAdjustments, deriveDisadvantageDeathSavesAdjustments } from '../derivers';
|
||||
/**
|
||||
*
|
||||
* @param deathSaves
|
||||
* @param modifiers
|
||||
* @param ruleData
|
||||
*/
|
||||
export function generateDeathCause(deathSaves, modifiers, ruleData) {
|
||||
const maxDeathsavesFail = RuleDataAccessors.getMaxDeathsavesFail(ruleData);
|
||||
if (deathSaves !== null && deathSaves.failCount !== null && deathSaves.failCount >= maxDeathsavesFail) {
|
||||
return DeathCauseEnum.DEATHSAVES;
|
||||
}
|
||||
if (modifiers.find((modifier) => ModifierValidators.isSetDeathModifier(modifier))) {
|
||||
return DeathCauseEnum.CONDITION;
|
||||
}
|
||||
return DeathCauseEnum.NONE;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param deathCause
|
||||
*/
|
||||
export function generateIsDead(deathCause) {
|
||||
return deathCause !== DeathCauseEnum.NONE;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param deathSaveContract
|
||||
* @param modifiers
|
||||
*/
|
||||
export function generateDeathSaveInfo(deathSaveContract, modifiers) {
|
||||
return {
|
||||
failCount: deathSaveContract !== null && deathSaveContract.failCount !== null ? deathSaveContract.failCount : 0,
|
||||
successCount: deathSaveContract !== null && deathSaveContract.successCount !== null ? deathSaveContract.successCount : 0,
|
||||
isStabilized: deathSaveContract === null ? false : deathSaveContract.isStabilized,
|
||||
advantageAdjustments: deriveAdvantageDeathSavesAdjustments(modifiers),
|
||||
disadvantageAdjustments: deriveDisadvantageDeathSavesAdjustments(modifiers),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
import { TypeScriptUtils } from '../../../utils';
|
||||
import { ConditionAccessors } from '../../Condition';
|
||||
import { DamageAdjustmentTypeEnum, DefenseAdjustmentTypeEnum } from '../../Core';
|
||||
import { FormatUtils } from '../../Format';
|
||||
import { HelperUtils } from '../../Helper';
|
||||
import { ModifierAccessors } from '../../Modifier';
|
||||
import { RuleDataAccessors, RuleDataUtils } from '../../RuleData';
|
||||
import { deriveCustomConditionDefenseAdjustmentDataOrigin, deriveCustomDefenseAdjustmentDataOrigin } from '../derivers';
|
||||
import { isImmunityDamageAdjustment, isResistanceDamageAdjustment, isVulnerabilityDamageAdjustment, } from '../validators';
|
||||
/**
|
||||
*
|
||||
* @param customDamageAdjustments
|
||||
* @param modifiers
|
||||
* @param ruleData
|
||||
*/
|
||||
export function generateActiveResistances(customDamageAdjustments, modifiers, ruleData) {
|
||||
const damageAdjustments = [];
|
||||
customDamageAdjustments.forEach((adjustment) => {
|
||||
damageAdjustments.push({
|
||||
name: adjustment.name === null ? '' : adjustment.name,
|
||||
dataOrigin: deriveCustomDefenseAdjustmentDataOrigin(adjustment),
|
||||
isCustom: true,
|
||||
type: DefenseAdjustmentTypeEnum.DAMAGE_ADJUSTMENT,
|
||||
slug: FormatUtils.slugify(adjustment.name),
|
||||
});
|
||||
});
|
||||
modifiers.forEach((modifier) => {
|
||||
const damageAdjustment = RuleDataUtils.getDamageAdjustmentBySlug(ModifierAccessors.getSubType(modifier), DamageAdjustmentTypeEnum.RESISTANCE, ruleData);
|
||||
let name = '';
|
||||
if (damageAdjustment) {
|
||||
name = damageAdjustment.name === null ? '' : damageAdjustment.name;
|
||||
}
|
||||
else {
|
||||
const subtypeName = ModifierAccessors.getFriendlySubtypeName(modifier);
|
||||
name = subtypeName === null ? '' : subtypeName;
|
||||
}
|
||||
damageAdjustments.push({
|
||||
name,
|
||||
dataOrigin: ModifierAccessors.getDataOrigin(modifier),
|
||||
isCustom: false,
|
||||
type: DefenseAdjustmentTypeEnum.DAMAGE_ADJUSTMENT,
|
||||
slug: FormatUtils.slugify(name),
|
||||
});
|
||||
});
|
||||
return damageAdjustments;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param customDamageAdjustments
|
||||
* @param customConditionAdjustments
|
||||
* @param modifiers
|
||||
* @param ruleData
|
||||
*/
|
||||
export function generateActiveImmunities(customDamageAdjustments, customConditionAdjustments, modifiers, ruleData) {
|
||||
const defenseAdjustments = [];
|
||||
customDamageAdjustments.forEach((adjustment) => {
|
||||
defenseAdjustments.push({
|
||||
name: adjustment.name === null ? '' : adjustment.name,
|
||||
dataOrigin: deriveCustomDefenseAdjustmentDataOrigin(adjustment),
|
||||
isCustom: true,
|
||||
type: DefenseAdjustmentTypeEnum.DAMAGE_ADJUSTMENT,
|
||||
slug: FormatUtils.slugify(adjustment.name),
|
||||
});
|
||||
});
|
||||
customConditionAdjustments.forEach((adjustment) => {
|
||||
defenseAdjustments.push({
|
||||
name: ConditionAccessors.getName(adjustment),
|
||||
dataOrigin: deriveCustomConditionDefenseAdjustmentDataOrigin(adjustment),
|
||||
isCustom: true,
|
||||
type: DefenseAdjustmentTypeEnum.CONDITION_ADJUSTMENT,
|
||||
slug: FormatUtils.slugify(ConditionAccessors.getName(adjustment)),
|
||||
});
|
||||
});
|
||||
modifiers.forEach((modifier) => {
|
||||
const damageAdjustment = RuleDataUtils.getDamageAdjustmentBySlug(ModifierAccessors.getSubType(modifier), DamageAdjustmentTypeEnum.IMMUNITY, ruleData);
|
||||
if (damageAdjustment) {
|
||||
defenseAdjustments.push({
|
||||
name: damageAdjustment.name === null ? '' : damageAdjustment.name,
|
||||
dataOrigin: ModifierAccessors.getDataOrigin(modifier),
|
||||
isCustom: false,
|
||||
type: DefenseAdjustmentTypeEnum.DAMAGE_ADJUSTMENT,
|
||||
slug: FormatUtils.slugify(damageAdjustment.name),
|
||||
});
|
||||
return;
|
||||
}
|
||||
const condition = RuleDataUtils.getConditionBySlug(ModifierAccessors.getSubType(modifier), ruleData);
|
||||
if (condition) {
|
||||
defenseAdjustments.push({
|
||||
name: ConditionAccessors.getName(condition),
|
||||
dataOrigin: ModifierAccessors.getDataOrigin(modifier),
|
||||
isCustom: false,
|
||||
type: DefenseAdjustmentTypeEnum.CONDITION_ADJUSTMENT,
|
||||
slug: ConditionAccessors.getSlug(condition),
|
||||
});
|
||||
return;
|
||||
}
|
||||
const subtypeName = ModifierAccessors.getFriendlySubtypeName(modifier);
|
||||
defenseAdjustments.push({
|
||||
name: subtypeName === null ? '' : subtypeName,
|
||||
dataOrigin: ModifierAccessors.getDataOrigin(modifier),
|
||||
isCustom: false,
|
||||
type: DefenseAdjustmentTypeEnum.DAMAGE_ADJUSTMENT,
|
||||
slug: FormatUtils.slugify(ModifierAccessors.getFriendlySubtypeName(modifier)),
|
||||
});
|
||||
});
|
||||
return defenseAdjustments;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param customDamageAdjustments
|
||||
* @param modifiers
|
||||
* @param ruleData
|
||||
*/
|
||||
export function generateActiveVulnerabilities(customDamageAdjustments, modifiers, ruleData) {
|
||||
const damageAdjustments = [];
|
||||
customDamageAdjustments.forEach((adjustment) => {
|
||||
damageAdjustments.push({
|
||||
name: adjustment.name === null ? '' : adjustment.name,
|
||||
dataOrigin: deriveCustomDefenseAdjustmentDataOrigin(adjustment),
|
||||
isCustom: true,
|
||||
type: DefenseAdjustmentTypeEnum.DAMAGE_ADJUSTMENT,
|
||||
slug: FormatUtils.slugify(adjustment.name),
|
||||
});
|
||||
});
|
||||
modifiers.forEach((modifier) => {
|
||||
let name;
|
||||
const damageAdjustment = RuleDataUtils.getDamageAdjustmentBySlug(ModifierAccessors.getSubType(modifier), DamageAdjustmentTypeEnum.VULNERABILITY, ruleData);
|
||||
if (damageAdjustment) {
|
||||
name = damageAdjustment.name;
|
||||
}
|
||||
else {
|
||||
name = ModifierAccessors.getFriendlySubtypeName(modifier);
|
||||
}
|
||||
damageAdjustments.push({
|
||||
name,
|
||||
dataOrigin: ModifierAccessors.getDataOrigin(modifier),
|
||||
isCustom: false,
|
||||
type: DefenseAdjustmentTypeEnum.DAMAGE_ADJUSTMENT,
|
||||
slug: FormatUtils.slugify(name),
|
||||
});
|
||||
});
|
||||
return damageAdjustments;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param ruleData
|
||||
*/
|
||||
export function generateResistanceData(ruleData) {
|
||||
return RuleDataAccessors.getDamageAdjustments(ruleData).filter(isResistanceDamageAdjustment);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param ruleData
|
||||
*/
|
||||
export function generateVulnerabilityData(ruleData) {
|
||||
return RuleDataAccessors.getDamageAdjustments(ruleData).filter(isVulnerabilityDamageAdjustment);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param ruleData
|
||||
*/
|
||||
export function generateImmunityData(ruleData) {
|
||||
return RuleDataAccessors.getDamageAdjustments(ruleData).filter(isImmunityDamageAdjustment);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param customDamageAdjustments
|
||||
*/
|
||||
export function generateCustomResistanceDamageAdjustments(customDamageAdjustments) {
|
||||
return customDamageAdjustments.filter(isResistanceDamageAdjustment);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param customDamageAdjustments
|
||||
*/
|
||||
export function generateCustomImmunityDamageAdjustments(customDamageAdjustments) {
|
||||
return customDamageAdjustments.filter(isImmunityDamageAdjustment);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param customDamageAdjustments
|
||||
*/
|
||||
export function generateCustomVulnerabilityDamageAdjustments(customDamageAdjustments) {
|
||||
return customDamageAdjustments.filter(isVulnerabilityDamageAdjustment);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param customDefenseAdjustments
|
||||
* @param ruleData
|
||||
*/
|
||||
export function generateCustomDamageAdjustments(customDefenseAdjustments, ruleData) {
|
||||
const damageAdjustmentLookup = RuleDataAccessors.getDamageAdjustmentsLookup(ruleData);
|
||||
return customDefenseAdjustments
|
||||
.filter((adjustment) => adjustment.type === DefenseAdjustmentTypeEnum.DAMAGE_ADJUSTMENT)
|
||||
.map((adjustment) => {
|
||||
const damageAdjustmentData = HelperUtils.lookupDataOrFallback(damageAdjustmentLookup, adjustment.adjustmentId);
|
||||
if (damageAdjustmentData !== null) {
|
||||
return Object.assign(Object.assign({}, damageAdjustmentData), { source: adjustment.source === null ? '' : adjustment.source });
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.filter(TypeScriptUtils.isNotNullOrUndefined);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param customDefenseAdjustments
|
||||
* @param ruleData
|
||||
*/
|
||||
export function generateCustomConditionAdjustments(customDefenseAdjustments, ruleData) {
|
||||
const conditionDataLookup = RuleDataAccessors.getConditionLookup(ruleData);
|
||||
return customDefenseAdjustments
|
||||
.filter((adjustment) => adjustment.type === DefenseAdjustmentTypeEnum.CONDITION_ADJUSTMENT)
|
||||
.map((adjustment) => {
|
||||
const conditionData = HelperUtils.lookupDataOrFallback(conditionDataLookup, adjustment.adjustmentId);
|
||||
if (conditionData !== null) {
|
||||
return Object.assign(Object.assign({}, conditionData), { source: adjustment.source === null ? '' : adjustment.source });
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.filter(TypeScriptUtils.isNotNullOrUndefined);
|
||||
}
|
||||
@@ -0,0 +1,314 @@
|
||||
import { AbilityAccessors } from '../../Ability';
|
||||
import { ActionAccessors } from '../../Action';
|
||||
import { ClassAccessors } from '../../Class';
|
||||
import { ClassFeatureAccessors } from '../../ClassFeature';
|
||||
import { AbilityStatEnum, PreferenceHitPointTypeEnum } from '../../Core';
|
||||
import { DataOriginTypeEnum } from '../../DataOrigin';
|
||||
import { DiceUtils } from '../../Dice';
|
||||
import { EntityUtils } from '../../Entity';
|
||||
import { FeatAccessors } from '../../Feat';
|
||||
import { HelperUtils } from '../../Helper';
|
||||
import { ItemAccessors } from '../../Item';
|
||||
import { ModifierAccessors, ModifierDerivers, ModifierValidators } from '../../Modifier';
|
||||
import { RaceAccessors } from '../../Race';
|
||||
import { RacialTraitAccessors } from '../../RacialTrait';
|
||||
import { RuleDataAccessors } from '../../RuleData';
|
||||
import { ProtectionSupplierTypeEnum } from '../constants';
|
||||
import { deriveItemProtectionSupplierAvailabilityStatus, deriveProtectionSupplierAvailabilityStatus, } from '../derivers';
|
||||
/**
|
||||
*
|
||||
* @param hitPointParts
|
||||
* @param currentLevel
|
||||
* @param classes
|
||||
* @param abilityLookup
|
||||
* @param raceModifiers
|
||||
* @param classesModifiers
|
||||
* @param miscModifiers
|
||||
* @param globalModifiers
|
||||
* @param preferences
|
||||
* @param ruleData
|
||||
*/
|
||||
export function generateHitPointInfo(hitPointParts, currentLevel, classes, abilityLookup, raceModifiers, classesModifiers, miscModifiers, globalModifiers, preferences, ruleData) {
|
||||
return getHitPointInfo(hitPointParts.baseHp, hitPointParts.overrideHp, hitPointParts.bonusHp, hitPointParts.tempHp, hitPointParts.removedHp, currentLevel, classes, abilityLookup, raceModifiers, classesModifiers, miscModifiers, globalModifiers, preferences, ruleData);
|
||||
}
|
||||
/**
|
||||
* //TODO better naming that fits generator convention
|
||||
* @param baseHp
|
||||
* @param overrideHp
|
||||
* @param bonusHp
|
||||
* @param tempHp
|
||||
* @param removedHp
|
||||
* @param currentLevel
|
||||
* @param classes
|
||||
* @param abilityLookup
|
||||
* @param raceModifiers
|
||||
* @param classesModifiers
|
||||
* @param miscModifiers
|
||||
* @param globalModifiers
|
||||
* @param preferences
|
||||
* @param ruleData
|
||||
*/
|
||||
export function getHitPointInfo(baseHp, overrideHp, bonusHp, tempHp, removedHp, currentLevel, classes, abilityLookup, raceModifiers, classesModifiers, miscModifiers, globalModifiers, preferences, ruleData) {
|
||||
const classesHitDice = classes.map((charClass) => ({
|
||||
dice: {
|
||||
diceCount: ClassAccessors.getLevel(charClass),
|
||||
diceValue: ClassAccessors.getHitDiceType(charClass),
|
||||
diceMultiplier: null,
|
||||
diceString: null,
|
||||
fixedValue: null,
|
||||
},
|
||||
charClass,
|
||||
}));
|
||||
const hitPointSources = [];
|
||||
let conStat;
|
||||
let conStatModifier;
|
||||
if (Object.keys(abilityLookup).length) {
|
||||
conStat = abilityLookup[AbilityStatEnum.CONSTITUTION] || 0;
|
||||
conStatModifier = AbilityAccessors.getModifier(conStat);
|
||||
}
|
||||
if (conStatModifier) {
|
||||
const source = AbilityAccessors.getLabel(conStat);
|
||||
hitPointSources.push({
|
||||
amount: currentLevel * conStatModifier,
|
||||
source: source === null ? 'UNK' : source,
|
||||
});
|
||||
}
|
||||
const racialHpPerLevelModifiers = raceModifiers.filter((modifier) => ModifierValidators.isBonusHitPointsPerLevelModifier(modifier));
|
||||
const classesHpPerLevelModifiers = classesModifiers.filter((modifier) => ModifierValidators.isBonusHitPointsPerLevelModifier(modifier));
|
||||
const miscHpPerLevelModifiers = miscModifiers.filter((modifier) => ModifierValidators.isBonusHitPointsPerLevelModifier(modifier));
|
||||
if (racialHpPerLevelModifiers.length) {
|
||||
racialHpPerLevelModifiers.reduce((acc, modifier) => {
|
||||
const value = ModifierAccessors.getValue(modifier);
|
||||
if (value !== null) {
|
||||
acc.push({
|
||||
amount: currentLevel * value,
|
||||
source: EntityUtils.getDataOriginName(ModifierAccessors.getDataOrigin(modifier)),
|
||||
});
|
||||
}
|
||||
return acc;
|
||||
}, hitPointSources);
|
||||
}
|
||||
if (classesHpPerLevelModifiers.length) {
|
||||
classesHpPerLevelModifiers.reduce((acc, modifier) => {
|
||||
const value = ModifierAccessors.getValue(modifier);
|
||||
const dataOrigin = ModifierAccessors.getDataOrigin(modifier);
|
||||
const dataOriginType = ModifierAccessors.getDataOriginType(modifier);
|
||||
if (dataOriginType === DataOriginTypeEnum.CLASS_FEATURE && value !== null) {
|
||||
acc.push({
|
||||
amount: ClassAccessors.getLevel(dataOrigin.parent) * value,
|
||||
source: EntityUtils.getDataOriginName(ModifierAccessors.getDataOrigin(modifier)),
|
||||
});
|
||||
}
|
||||
return acc;
|
||||
}, hitPointSources);
|
||||
}
|
||||
if (miscHpPerLevelModifiers.length) {
|
||||
miscHpPerLevelModifiers.reduce((acc, modifier) => {
|
||||
const value = ModifierAccessors.getValue(modifier);
|
||||
if (value !== null) {
|
||||
acc.push({
|
||||
amount: currentLevel * value,
|
||||
source: EntityUtils.getDataOriginName(ModifierAccessors.getDataOrigin(modifier)),
|
||||
});
|
||||
}
|
||||
return acc;
|
||||
}, hitPointSources);
|
||||
}
|
||||
const globalBonusHpModifiers = globalModifiers.filter((modifier) => ModifierValidators.isBonusHitPointsModifier(modifier));
|
||||
if (globalBonusHpModifiers.length) {
|
||||
globalBonusHpModifiers.reduce((acc, modifier) => {
|
||||
const statId = ModifierAccessors.getStatId(modifier);
|
||||
if (statId !== null) {
|
||||
const stat = HelperUtils.lookupDataOrFallback(abilityLookup, statId);
|
||||
if (stat !== null) {
|
||||
const statModifier = AbilityAccessors.getModifier(stat);
|
||||
acc.push({
|
||||
amount: statModifier === null ? 0 : statModifier,
|
||||
source: EntityUtils.getDataOriginName(ModifierAccessors.getDataOrigin(modifier)),
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
const hpIncrease = ModifierAccessors.getValue(modifier);
|
||||
if (!!hpIncrease) {
|
||||
acc.push({
|
||||
amount: hpIncrease,
|
||||
source: EntityUtils.getDataOriginName(ModifierAccessors.getDataOrigin(modifier)),
|
||||
});
|
||||
}
|
||||
}
|
||||
return acc;
|
||||
}, hitPointSources);
|
||||
}
|
||||
const totalHitPointSources = hitPointSources.reduce((acc, hitPointSource) => (acc += hitPointSource.amount), 0);
|
||||
const totalAvgClassHitDie = classes.reduce((acc, charClass) => {
|
||||
const hitDiceType = ClassAccessors.getHitDiceType(charClass);
|
||||
const level = ClassAccessors.getLevel(charClass);
|
||||
if (ClassAccessors.isStartingClass(charClass)) {
|
||||
acc += hitDiceType;
|
||||
acc += Math.floor((level - 1) * DiceUtils.getAverageDieValue(hitDiceType));
|
||||
}
|
||||
else {
|
||||
acc += Math.floor(level * DiceUtils.getAverageDieValue(hitDiceType));
|
||||
}
|
||||
return acc;
|
||||
}, 0);
|
||||
const totalFixedClassHitDie = classes.reduce((acc, charClass) => {
|
||||
const hitDiceType = ClassAccessors.getHitDiceType(charClass);
|
||||
const level = ClassAccessors.getLevel(charClass);
|
||||
if (ClassAccessors.isStartingClass(charClass)) {
|
||||
acc += hitDiceType;
|
||||
acc += (level - 1) * DiceUtils.getFixedDieValue(hitDiceType);
|
||||
}
|
||||
else {
|
||||
acc += level * DiceUtils.getFixedDieValue(hitDiceType);
|
||||
}
|
||||
return acc;
|
||||
}, 0);
|
||||
const totalClassHitDieMax = classes.reduce((acc, charClass) => (acc += ClassAccessors.getLevel(charClass) * ClassAccessors.getHitDiceType(charClass)), 0);
|
||||
const totalFixedValueHp = totalFixedClassHitDie + totalHitPointSources;
|
||||
const totalAverageHp = totalAvgClassHitDie + totalHitPointSources;
|
||||
const possibleMaxHitPoints = totalClassHitDieMax + totalHitPointSources;
|
||||
let finalBaseHp = baseHp;
|
||||
if (preferences && preferences.hitPointType === PreferenceHitPointTypeEnum.FIXED) {
|
||||
finalBaseHp = totalFixedClassHitDie;
|
||||
}
|
||||
const baseTotalHp = finalBaseHp + totalHitPointSources;
|
||||
let totalHp = baseTotalHp + (bonusHp ? bonusHp : 0);
|
||||
if (overrideHp !== null) {
|
||||
totalHp = overrideHp;
|
||||
}
|
||||
const finalTotalHp = Math.max(totalHp, RuleDataAccessors.getMinimumHpTotal(ruleData));
|
||||
const finalRemovedHp = Math.min(finalTotalHp, removedHp);
|
||||
return {
|
||||
baseHp: finalBaseHp,
|
||||
baseTotalHp,
|
||||
bonusHp,
|
||||
classesHitDice,
|
||||
hitPointSources,
|
||||
overrideHp,
|
||||
possibleMaxHitPoints,
|
||||
remainingHp: finalTotalHp - finalRemovedHp,
|
||||
removedHp: finalRemovedHp,
|
||||
tempHp,
|
||||
totalAverageHp,
|
||||
totalFixedValueHp,
|
||||
totalHitPointSources,
|
||||
totalHp: finalTotalHp,
|
||||
};
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param classes
|
||||
* @param race
|
||||
* @param feats
|
||||
* @param items
|
||||
* @param modifierData
|
||||
* @param abilityLookup
|
||||
* @param ruleData
|
||||
* @param proficiencyBonus
|
||||
* @param characterId
|
||||
*/
|
||||
export function generateProtectionSuppliers(classes, race, feats, items, modifierData, abilityLookup, ruleData, proficiencyBonus, characterId) {
|
||||
let protectionSuppliers = [];
|
||||
if (race !== null) {
|
||||
RaceAccessors.getRacialTraits(race).forEach((racialTrait) => {
|
||||
const modifiers = RacialTraitAccessors.getModifiers(racialTrait);
|
||||
const foundProtectionModifier = modifiers.find(ModifierValidators.isProtectionZeroHpModifier);
|
||||
if (!foundProtectionModifier) {
|
||||
return;
|
||||
}
|
||||
const actions = RacialTraitAccessors.getActions(racialTrait);
|
||||
if (actions.length !== 1) {
|
||||
return;
|
||||
}
|
||||
const action = actions[0];
|
||||
const limitedUse = ActionAccessors.getLimitedUse(action);
|
||||
if (!limitedUse) {
|
||||
return;
|
||||
}
|
||||
protectionSuppliers.push({
|
||||
type: ProtectionSupplierTypeEnum.RACIAL_TRAIT,
|
||||
data: racialTrait,
|
||||
modifier: foundProtectionModifier,
|
||||
setHpValue: ModifierDerivers.deriveValue(foundProtectionModifier, modifierData),
|
||||
key: [ProtectionSupplierTypeEnum.RACIAL_TRAIT, RacialTraitAccessors.getUniqueKey(racialTrait)].join('-'),
|
||||
availabilityStatus: deriveProtectionSupplierAvailabilityStatus(limitedUse, abilityLookup, ruleData, proficiencyBonus),
|
||||
action,
|
||||
});
|
||||
});
|
||||
}
|
||||
classes.forEach((charClass) => {
|
||||
ClassAccessors.getClassFeatures(charClass).forEach((classFeature) => {
|
||||
const modifiers = ClassFeatureAccessors.getModifiers(classFeature);
|
||||
const foundProtectionModifier = modifiers.find(ModifierValidators.isProtectionZeroHpModifier);
|
||||
if (!foundProtectionModifier) {
|
||||
return;
|
||||
}
|
||||
const actions = ClassFeatureAccessors.getActions(classFeature);
|
||||
if (actions.length !== 1) {
|
||||
return;
|
||||
}
|
||||
const action = actions[0];
|
||||
const limitedUse = ActionAccessors.getLimitedUse(action);
|
||||
if (!limitedUse) {
|
||||
return;
|
||||
}
|
||||
protectionSuppliers.push({
|
||||
type: ProtectionSupplierTypeEnum.CLASS_FEATURE,
|
||||
data: classFeature,
|
||||
modifier: foundProtectionModifier,
|
||||
setHpValue: ModifierDerivers.deriveValue(foundProtectionModifier, modifierData),
|
||||
key: [ProtectionSupplierTypeEnum.CLASS_FEATURE, ClassFeatureAccessors.getUniqueKey(classFeature)].join('-'),
|
||||
availabilityStatus: deriveProtectionSupplierAvailabilityStatus(limitedUse, abilityLookup, ruleData, proficiencyBonus),
|
||||
action,
|
||||
});
|
||||
});
|
||||
});
|
||||
feats.forEach((feat) => {
|
||||
const modifiers = FeatAccessors.getModifiers(feat);
|
||||
const foundProtectionModifier = modifiers.find(ModifierValidators.isProtectionZeroHpModifier);
|
||||
if (!foundProtectionModifier) {
|
||||
return;
|
||||
}
|
||||
const actions = FeatAccessors.getActions(feat);
|
||||
if (actions.length !== 1) {
|
||||
return;
|
||||
}
|
||||
const action = actions[0];
|
||||
const limitedUse = ActionAccessors.getLimitedUse(action);
|
||||
if (!limitedUse) {
|
||||
return;
|
||||
}
|
||||
protectionSuppliers.push({
|
||||
type: ProtectionSupplierTypeEnum.FEAT,
|
||||
data: feat,
|
||||
modifier: foundProtectionModifier,
|
||||
setHpValue: ModifierDerivers.deriveValue(foundProtectionModifier, modifierData),
|
||||
key: [ProtectionSupplierTypeEnum.FEAT, FeatAccessors.getUniqueKey(feat)].join('-'),
|
||||
availabilityStatus: deriveProtectionSupplierAvailabilityStatus(limitedUse, abilityLookup, ruleData, proficiencyBonus),
|
||||
action,
|
||||
});
|
||||
});
|
||||
items.forEach((item) => {
|
||||
const modifiers = ItemAccessors.getModifiers(item);
|
||||
const foundProtectionModifier = modifiers.find(ModifierValidators.isProtectionZeroHpModifier);
|
||||
if (!foundProtectionModifier) {
|
||||
return;
|
||||
}
|
||||
const limitedUse = ItemAccessors.getLimitedUse(item);
|
||||
if (limitedUse === null) {
|
||||
return;
|
||||
}
|
||||
protectionSuppliers.push({
|
||||
type: ProtectionSupplierTypeEnum.ITEM,
|
||||
data: item,
|
||||
modifier: foundProtectionModifier,
|
||||
setHpValue: ModifierDerivers.deriveValue(foundProtectionModifier, modifierData),
|
||||
key: [ProtectionSupplierTypeEnum.ITEM, ItemAccessors.getUniqueKey(item)].join('-'),
|
||||
availabilityStatus: deriveItemProtectionSupplierAvailabilityStatus(item, foundProtectionModifier, limitedUse, abilityLookup, ruleData, proficiencyBonus, characterId),
|
||||
action: null,
|
||||
});
|
||||
});
|
||||
return protectionSuppliers;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import { AbilityAccessors } from '../../Ability';
|
||||
import { AbilityStatEnum } from '../../Core';
|
||||
import { ModifierBonusTypeEnum, ModifierDerivers, ModifierUtils, ModifierValidators } from '../../Modifier';
|
||||
/**
|
||||
*
|
||||
* @param modifiers
|
||||
* @param abilityLookup
|
||||
* @param profBonus
|
||||
*/
|
||||
export function getInitiative(modifiers, abilityLookup, profBonus) {
|
||||
const bonusInitiativeModifiers = modifiers.filter((modifier) => ModifierValidators.isBonusInitiativeModifier(modifier));
|
||||
const bonusInitiativeModifierTotal = abilityLookup
|
||||
? ModifierDerivers.sumModifiers(bonusInitiativeModifiers, abilityLookup)
|
||||
: 0;
|
||||
const includeProficiencyBonus = ModifierUtils.countModifierBonusTypes(bonusInitiativeModifiers, ModifierBonusTypeEnum.PROFICIENCY_BONUS) > 0;
|
||||
let halfProficiency = false;
|
||||
if (modifiers.filter((modifier) => ModifierValidators.isHalfProficiencyInitiativeModifier(modifier)).length) {
|
||||
halfProficiency = true;
|
||||
}
|
||||
let halfProficiencyRoundUp = false;
|
||||
if (modifiers.filter((modifier) => ModifierValidators.isHalfProficiencyRoundUpInitiativeModifier(modifier)).length) {
|
||||
halfProficiencyRoundUp = true;
|
||||
}
|
||||
let modifierProficiencyBonus = 0;
|
||||
if (includeProficiencyBonus) {
|
||||
modifierProficiencyBonus = profBonus;
|
||||
}
|
||||
else if (halfProficiencyRoundUp) {
|
||||
modifierProficiencyBonus = Math.ceil(profBonus / 2);
|
||||
}
|
||||
else if (halfProficiency) {
|
||||
modifierProficiencyBonus = Math.floor(profBonus / 2);
|
||||
}
|
||||
let dexAbility;
|
||||
let dexAbilityModifier;
|
||||
if (Object.keys(abilityLookup).length) {
|
||||
dexAbility = abilityLookup[AbilityStatEnum.DEXTERITY];
|
||||
dexAbilityModifier = AbilityAccessors.getModifier(dexAbility) || 0;
|
||||
}
|
||||
return dexAbilityModifier + bonusInitiativeModifierTotal + modifierProficiencyBonus;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param processedInitiative
|
||||
* @param initiativeScore
|
||||
*
|
||||
* @returns number
|
||||
*/
|
||||
export function getStaticInitiative(processedInitiative, initiativeScore) {
|
||||
return processedInitiative + initiativeScore.amount;
|
||||
}
|
||||
/**
|
||||
* //TODO use array.some prototype
|
||||
* @param modifiers
|
||||
*/
|
||||
export function generateHasInitiativeAdvantage(modifiers) {
|
||||
return modifiers.filter((modifier) => ModifierValidators.isAdvantageInitiativeModifier(modifier)).length > 0;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { TypeScriptUtils } from '../../../utils';
|
||||
import { ModifierAccessors, ModifierValidators } from '../../Modifier';
|
||||
/**
|
||||
*
|
||||
* @param modifiers
|
||||
*/
|
||||
export function generateLanguages(modifiers) {
|
||||
return modifiers
|
||||
.filter((modifier) => ModifierValidators.isLanguageModifier(modifier))
|
||||
.map((modifier) => ModifierAccessors.getFriendlySubtypeName(modifier))
|
||||
.filter(TypeScriptUtils.isNotNullOrUndefined);
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
import { orderBy } from 'lodash';
|
||||
import { EntityUtils } from '../../Entity';
|
||||
import { HelperUtils } from '../../Helper';
|
||||
import { ModifierAccessors, ModifierValidators } from '../../Modifier';
|
||||
import { RuleDataAccessors, RuleDataUtils } from '../../RuleData';
|
||||
import { AdjustmentTypeEnum, ProficiencyAdjustmentTypeEnum, ValueAccessors, ValueUtils, } from '../../Value';
|
||||
import { deriveProficiencyBonus } from '../derivers';
|
||||
import { isCustomSkillProficiency } from '../validators';
|
||||
/**
|
||||
*
|
||||
* @param typeValueLookup
|
||||
* @param modifiers
|
||||
*/
|
||||
export function generateProficiencyLookup(typeValueLookup, modifiers) {
|
||||
let proficiencyLookup = {};
|
||||
ProficiencyAdjustmentTypeEnum.forEach((typeId) => {
|
||||
const values = ValueUtils.getTypeData(typeValueLookup, typeId);
|
||||
values.forEach((value) => {
|
||||
proficiencyLookup = Object.assign(Object.assign({}, proficiencyLookup), { [ValueAccessors.getEntityKey(value)]: 1 });
|
||||
});
|
||||
});
|
||||
modifiers
|
||||
.filter((modifier) => ModifierValidators.isProficiencyModifier(modifier) && ModifierAccessors.getEntityId(modifier))
|
||||
.forEach((modifier) => {
|
||||
proficiencyLookup = Object.assign(Object.assign({}, proficiencyLookup), { [ModifierAccessors.getEntityKey(modifier)]: 1 });
|
||||
});
|
||||
return proficiencyLookup;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param currentLevel
|
||||
* @param modifiers
|
||||
* @param ruleData
|
||||
*/
|
||||
export function generateProficiencyBonus(currentLevel, modifiers, ruleData) {
|
||||
return deriveProficiencyBonus(currentLevel, modifiers, ruleData);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param customProficiencies
|
||||
* @param valueLookupByType
|
||||
* @param modifiers
|
||||
* @param ruleData
|
||||
*/
|
||||
export function generateProficiencyGroups(customProficiencies, valueLookupByType, modifiers, ruleData) {
|
||||
const decoratedProficiencyGroups = RuleDataAccessors.getProficiencyGroups(ruleData).map((group) => (Object.assign(Object.assign({}, group), { modifiers: [], custom: [], values: [] })));
|
||||
const proficiencyGroupLookup = {};
|
||||
const customProficiencyGroupLookup = {};
|
||||
const customAdjustmentGroupLookup = {};
|
||||
decoratedProficiencyGroups.forEach((group, groupIdx) => {
|
||||
if (group.entityTypeIds !== null) {
|
||||
group.entityTypeIds.forEach((entityTypeId) => {
|
||||
proficiencyGroupLookup[entityTypeId] = groupIdx;
|
||||
});
|
||||
}
|
||||
if (group.customAdjustments !== null) {
|
||||
group.customAdjustments.forEach((adjustmentTypeId) => {
|
||||
customAdjustmentGroupLookup[adjustmentTypeId] = groupIdx;
|
||||
});
|
||||
}
|
||||
customProficiencyGroupLookup[group.customProficiencyGroup] = groupIdx;
|
||||
});
|
||||
modifiers.forEach((modifier) => {
|
||||
const entityTypeId = ModifierAccessors.getEntityTypeId(modifier);
|
||||
const isMasteryModifier = ModifierValidators.isValidWeaponMasteryModifier(modifier);
|
||||
if (entityTypeId !== null && !isMasteryModifier) {
|
||||
const groupLookupIdx = proficiencyGroupLookup[entityTypeId];
|
||||
if (typeof groupLookupIdx !== 'undefined') {
|
||||
decoratedProficiencyGroups[groupLookupIdx].modifiers.push(modifier);
|
||||
}
|
||||
}
|
||||
});
|
||||
customProficiencies.forEach((proficiency) => {
|
||||
const groupLookupIdx = customProficiencyGroupLookup[proficiency.type];
|
||||
if (typeof groupLookupIdx !== 'undefined') {
|
||||
decoratedProficiencyGroups[groupLookupIdx].custom.push(proficiency);
|
||||
}
|
||||
});
|
||||
Object.keys(customAdjustmentGroupLookup).forEach((adjustmentTypeId) => {
|
||||
if (valueLookupByType.hasOwnProperty(adjustmentTypeId)) {
|
||||
const groupLookupIdx = customAdjustmentGroupLookup[adjustmentTypeId];
|
||||
decoratedProficiencyGroups[groupLookupIdx].values.push(...valueLookupByType[adjustmentTypeId]);
|
||||
}
|
||||
});
|
||||
const proficiencyGroups = decoratedProficiencyGroups.map((group) => {
|
||||
if (group.label === 'Weapons') {
|
||||
RuleDataAccessors.getWeaponCategories(ruleData).forEach((category) => {
|
||||
if (group.modifiers.find((modifier) => ModifierValidators.isValidProficiencyWeaponCategoryModifier(modifier, category))) {
|
||||
group.modifiers = group.modifiers.filter((modifier) => {
|
||||
const weapon = RuleDataUtils.getWeaponByEntityId(ModifierAccessors.getEntityId(modifier), ModifierAccessors.getEntityTypeId(modifier), ruleData);
|
||||
return !weapon || (weapon && weapon.categoryId !== category.id);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
const uniqueProficiencyGroups = {};
|
||||
group.modifiers.forEach((modifier) => {
|
||||
const friendlySubTypeName = ModifierAccessors.getFriendlySubtypeName(modifier);
|
||||
if (friendlySubTypeName !== null) {
|
||||
if (!uniqueProficiencyGroups.hasOwnProperty(friendlySubTypeName)) {
|
||||
uniqueProficiencyGroups[friendlySubTypeName] = {
|
||||
label: friendlySubTypeName,
|
||||
sources: [],
|
||||
restriction: ModifierAccessors.getRestriction(modifier),
|
||||
};
|
||||
}
|
||||
uniqueProficiencyGroups[friendlySubTypeName].sources.push(EntityUtils.getDataOriginName(ModifierAccessors.getDataOrigin(modifier), undefined, true));
|
||||
}
|
||||
});
|
||||
group.custom.forEach((proficiency) => {
|
||||
const proficiencyName = proficiency.name;
|
||||
if (proficiencyName !== null) {
|
||||
if (!uniqueProficiencyGroups.hasOwnProperty(proficiencyName)) {
|
||||
uniqueProficiencyGroups[proficiencyName] = {
|
||||
label: proficiencyName,
|
||||
sources: [],
|
||||
};
|
||||
}
|
||||
if (proficiency.notes !== null) {
|
||||
uniqueProficiencyGroups[proficiencyName].sources.push(proficiency.notes);
|
||||
}
|
||||
}
|
||||
});
|
||||
const armorNameLookup = RuleDataUtils.getArmorNameLookup(ruleData);
|
||||
const weaponNameLookup = RuleDataUtils.getWeaponNameLookup(ruleData);
|
||||
const toolNameLookup = RuleDataUtils.getToolNameLookup(ruleData);
|
||||
group.values.forEach((charValue) => {
|
||||
let name = '';
|
||||
const valueId = ValueAccessors.getValueIntId(charValue);
|
||||
const notes = ValueAccessors.getNotes(charValue);
|
||||
if (valueId !== null) {
|
||||
switch (ValueAccessors.getTypeId(charValue)) {
|
||||
case AdjustmentTypeEnum.ARMOR_PROFICIENCY_LEVEL:
|
||||
name = armorNameLookup[valueId];
|
||||
break;
|
||||
case AdjustmentTypeEnum.WEAPON_PROFICIENCY_LEVEL:
|
||||
name = weaponNameLookup[valueId];
|
||||
break;
|
||||
case AdjustmentTypeEnum.TOOL_PROFICIENCY_LEVEL:
|
||||
name = toolNameLookup[valueId];
|
||||
break;
|
||||
case AdjustmentTypeEnum.LANGUAGE_PROFICIENCY_LEVEL:
|
||||
name = RuleDataUtils.getLanguageName(valueId, ruleData);
|
||||
break;
|
||||
default:
|
||||
// not implemented
|
||||
}
|
||||
}
|
||||
if (!uniqueProficiencyGroups.hasOwnProperty(name)) {
|
||||
uniqueProficiencyGroups[name] = {
|
||||
label: name,
|
||||
sources: [],
|
||||
};
|
||||
}
|
||||
uniqueProficiencyGroups[name].sources.push(notes ? notes : 'Custom');
|
||||
});
|
||||
return Object.assign(Object.assign({}, group), { modifierGroups: orderBy(HelperUtils.convertObjectToArray(uniqueProficiencyGroups), [
|
||||
(modifierGroup) => modifierGroup.label,
|
||||
]) });
|
||||
});
|
||||
return proficiencyGroups;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param proficiencies
|
||||
*/
|
||||
export function generateCustomSkillProficiencies(proficiencies) {
|
||||
return proficiencies.filter(isCustomSkillProficiency);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import { ClassAccessors } from '../../Class';
|
||||
import { ClassFeatureAccessors, ClassFeatureUtils } from '../../ClassFeature';
|
||||
import { PreferenceProgressionTypeEnum } from '../../Core';
|
||||
import { ItemUtils } from '../../Item';
|
||||
import { deriveCurrentLevelXp, deriveNextLevelXp, deriveXpLevel } from '../derivers';
|
||||
/**
|
||||
*
|
||||
* @param xp
|
||||
* @param preferences
|
||||
* @param totalClassLevel
|
||||
* @param ruleData
|
||||
*/
|
||||
export function generateCurrentLevel(xp, preferences, totalClassLevel, ruleData) {
|
||||
if (preferences !== null && preferences.progressionType === PreferenceProgressionTypeEnum.XP) {
|
||||
return deriveXpLevel(xp, ruleData);
|
||||
}
|
||||
return totalClassLevel;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param classLevel
|
||||
* @param currentLevel
|
||||
* @param currentLevelXp
|
||||
* @param ruleData
|
||||
*/
|
||||
export function generateExperienceInfo(classLevel, currentLevel, currentLevelXp, ruleData) {
|
||||
return {
|
||||
classLevel,
|
||||
currentLevel,
|
||||
currentLevelXp,
|
||||
currentLevelStartingXp: deriveCurrentLevelXp(currentLevel, ruleData),
|
||||
nextLevelXp: deriveNextLevelXp(currentLevel, ruleData),
|
||||
};
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param classes
|
||||
* @param equippedItems
|
||||
*/
|
||||
export function generateMartialArtsLevelScale(classes, equippedItems) {
|
||||
if (equippedItems.filter(ItemUtils.isArmorContract).length) {
|
||||
return null;
|
||||
}
|
||||
const martialArtsClass = classes.find((charClass) => ClassAccessors.getClassFeatures(charClass).filter((feature) => ClassFeatureUtils.doesEnableMartialArts(feature)).length > 0);
|
||||
if (martialArtsClass) {
|
||||
const classFeature = ClassAccessors.getClassFeatures(martialArtsClass).find((feature) => ClassFeatureUtils.doesEnableMartialArts(feature));
|
||||
return classFeature ? ClassFeatureAccessors.getLevelScale(classFeature) : null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
import { groupBy } from 'lodash';
|
||||
import { AbilityStatEnum, SaveTypeEnum, SituationalBonusSavingThrowTypeEnum } from '../../Core';
|
||||
import { DiceAdjustmentRollTypeEnum, DiceAdjustmentTypeEnum, DiceDerivers } from '../../Dice';
|
||||
import { FormatUtils } from '../../Format';
|
||||
import { ModifierAccessors, ModifierDerivers, ModifierValidators } from '../../Modifier';
|
||||
import { RuleDataAccessors, RuleDataUtils } from '../../RuleData';
|
||||
import { deriveSaveTypeName } from '../derivers';
|
||||
/**
|
||||
*
|
||||
* @param highestShield
|
||||
* @param modifiers
|
||||
* @param ruleData
|
||||
*/
|
||||
export function generateSituationalBonusSavingThrows(highestShield, modifiers, ruleData) {
|
||||
const saves = [];
|
||||
RuleDataAccessors.getStats(ruleData).forEach((statData) => {
|
||||
if (statData.id === AbilityStatEnum.DEXTERITY) {
|
||||
const bonusShieldAcModifiers = modifiers.filter((modifier) => ModifierValidators.isBonusShieldAcOnDexSavesModifier(modifier));
|
||||
if (highestShield) {
|
||||
bonusShieldAcModifiers.forEach((modifier) => {
|
||||
saves.push({
|
||||
key: ModifierAccessors.getEntityKey(modifier),
|
||||
statId: statData.id,
|
||||
value: highestShield.armorClass,
|
||||
extra: modifier,
|
||||
type: SituationalBonusSavingThrowTypeEnum.MODIFIER,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
return saves;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param situationalBonusSavingThrows
|
||||
*/
|
||||
export function generateSituationalBonusSavingThrowsLookup(situationalBonusSavingThrows) {
|
||||
return groupBy(situationalBonusSavingThrows, 'statId');
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param type
|
||||
* @param modifier
|
||||
*/
|
||||
export function generateSavingThrowModifierSummary(type, modifier) {
|
||||
let tooltipParts = [];
|
||||
if (type === SaveTypeEnum.BONUS) {
|
||||
let valueTotal = ModifierAccessors.getValueTotal(modifier);
|
||||
if (valueTotal) {
|
||||
tooltipParts.push(FormatUtils.renderSignedNumber(valueTotal));
|
||||
}
|
||||
}
|
||||
tooltipParts.push(deriveSaveTypeName(type));
|
||||
let abilityName = ModifierDerivers.deriveSavingThrowModifierAbilityName(modifier);
|
||||
if (abilityName) {
|
||||
tooltipParts.push(`on ${abilityName}`);
|
||||
}
|
||||
else {
|
||||
if (!modifier.restriction) {
|
||||
tooltipParts.push('on saves');
|
||||
}
|
||||
}
|
||||
let restrictionText = ModifierAccessors.getRestriction(modifier);
|
||||
if (restrictionText) {
|
||||
tooltipParts.push(restrictionText);
|
||||
}
|
||||
return tooltipParts.join(' ');
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param adjustment
|
||||
* @param ruleData
|
||||
*/
|
||||
export function generateSavingThrowAdjustmentSummary(adjustment, ruleData) {
|
||||
let tooltipParts = [];
|
||||
if (adjustment.type === DiceAdjustmentTypeEnum.BONUS) {
|
||||
if (adjustment.amount) {
|
||||
tooltipParts.push(FormatUtils.renderSignedNumber(adjustment.amount));
|
||||
}
|
||||
}
|
||||
tooltipParts.push(DiceDerivers.deriveDiceAdjustmentTypeName(adjustment.type));
|
||||
let abilityName = null;
|
||||
if (adjustment.statId) {
|
||||
abilityName = RuleDataUtils.getStatNameById(adjustment.statId, ruleData);
|
||||
}
|
||||
if (abilityName) {
|
||||
tooltipParts.push(`on ${abilityName}`);
|
||||
}
|
||||
else if (adjustment.rollType === DiceAdjustmentRollTypeEnum.DEATH_SAVE) {
|
||||
tooltipParts.push('on death saves');
|
||||
}
|
||||
if (adjustment.restriction) {
|
||||
tooltipParts.push(adjustment.restriction);
|
||||
}
|
||||
else {
|
||||
if (adjustment.rollType === DiceAdjustmentRollTypeEnum.SAVE) {
|
||||
tooltipParts.push('on saves');
|
||||
}
|
||||
}
|
||||
return tooltipParts.join(' ');
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
import { keyBy } from 'lodash';
|
||||
import { SenseTypeEnum } from '../../Core';
|
||||
import { ModifierDerivers, ModifierValidators } from '../../Modifier';
|
||||
import { RuleDataUtils } from '../../RuleData';
|
||||
import { derivePassiveInsight, derivePassiveInvestigation, derivePassivePerception } from '../derivers';
|
||||
/**
|
||||
*
|
||||
* @param senseKey
|
||||
* @param modifiers
|
||||
* @param abilityLookup
|
||||
* @param customSenseLookup
|
||||
*/
|
||||
export function getSenseDistanceByKeyBonusTakeHighest(senseKey, modifiers, abilityLookup, customSenseLookup) {
|
||||
const customSenseData = customSenseLookup[senseKey];
|
||||
if (customSenseData) {
|
||||
const customDistance = customSenseData.distance;
|
||||
if (customDistance !== null) {
|
||||
return customDistance;
|
||||
}
|
||||
}
|
||||
const typeModifierKey = RuleDataUtils.getSenseTypeModifierKey(senseKey);
|
||||
const setSenseModifiers = modifiers.filter((modifier) => ModifierValidators.isValidSetSenseModifier(modifier, typeModifierKey));
|
||||
if (setSenseModifiers.length) {
|
||||
return ModifierDerivers.deriveHighestModifierValue(setSenseModifiers, abilityLookup);
|
||||
}
|
||||
const baseSenseModifiers = modifiers.filter((modifier) => ModifierValidators.isValidSetBaseSenseModifier(modifier, typeModifierKey));
|
||||
const highestBaseSenseModifierValue = ModifierDerivers.deriveHighestModifierValue(baseSenseModifiers, abilityLookup);
|
||||
const bonusSenseModifiers = modifiers.filter((modifier) => ModifierValidators.isValidSenseModifier(modifier, typeModifierKey) ||
|
||||
ModifierValidators.isValidBonusSenseModifier(modifier, typeModifierKey));
|
||||
const highestBonusSenseModifierValue = ModifierDerivers.deriveHighestModifierValue(bonusSenseModifiers, abilityLookup);
|
||||
return highestBaseSenseModifierValue + highestBonusSenseModifierValue;
|
||||
}
|
||||
/**
|
||||
* @deprecated use getSenseDistanceByKeyBonusTakeHighest
|
||||
*/
|
||||
export const getSenseDistanceByKey = getSenseDistanceByKeyBonusTakeHighest;
|
||||
/**
|
||||
*
|
||||
* @param senseKey
|
||||
* @param modifiers
|
||||
* @param abilityLookup
|
||||
* @param customSenseLookup
|
||||
*/
|
||||
export function getSenseDistanceByKeyBonusTakeAll(senseKey, modifiers, abilityLookup, customSenseLookup) {
|
||||
const customSenseData = customSenseLookup[senseKey];
|
||||
if (customSenseData) {
|
||||
const customDistance = customSenseData.distance;
|
||||
if (customDistance !== null) {
|
||||
return customDistance;
|
||||
}
|
||||
}
|
||||
const typeModifierKey = RuleDataUtils.getSenseTypeModifierKey(senseKey);
|
||||
const setSenseModifiers = modifiers.filter((modifier) => ModifierValidators.isValidSetSenseModifier(modifier, typeModifierKey));
|
||||
if (setSenseModifiers.length) {
|
||||
return ModifierDerivers.deriveHighestModifierValue(setSenseModifiers, abilityLookup);
|
||||
}
|
||||
const baseSenseModifiers = modifiers.filter((modifier) => ModifierValidators.isValidSetBaseSenseModifier(modifier, typeModifierKey));
|
||||
const highestBaseSenseModifierValue = ModifierDerivers.deriveHighestModifierValue(baseSenseModifiers, abilityLookup);
|
||||
const bonusSenseModifiers = modifiers.filter((modifier) => ModifierValidators.isValidSenseModifier(modifier, typeModifierKey) ||
|
||||
ModifierValidators.isValidBonusSenseModifier(modifier, typeModifierKey));
|
||||
const totalBonusSenseModifierValue = bonusSenseModifiers.reduce((total, modifier) => total + ModifierDerivers.getModifierValue(modifier, abilityLookup), 0);
|
||||
return highestBaseSenseModifierValue + totalBonusSenseModifierValue;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param modifiers
|
||||
* @param abilityLookup
|
||||
* @param customSenseLookup
|
||||
*/
|
||||
export function generateSenseInfo(modifiers, abilityLookup, customSenseLookup) {
|
||||
return {
|
||||
[SenseTypeEnum.BLINDSIGHT]: getSenseDistanceByKeyBonusTakeHighest(SenseTypeEnum.BLINDSIGHT, modifiers, abilityLookup, customSenseLookup),
|
||||
[SenseTypeEnum.DARKVISION]: getSenseDistanceByKeyBonusTakeAll(SenseTypeEnum.DARKVISION, modifiers, abilityLookup, customSenseLookup),
|
||||
[SenseTypeEnum.PASSIVE_PERCEPTION]: getSenseDistanceByKeyBonusTakeHighest(SenseTypeEnum.PASSIVE_PERCEPTION, modifiers, abilityLookup, customSenseLookup),
|
||||
[SenseTypeEnum.TREMORSENSE]: getSenseDistanceByKeyBonusTakeHighest(SenseTypeEnum.TREMORSENSE, modifiers, abilityLookup, customSenseLookup),
|
||||
[SenseTypeEnum.TRUESIGHT]: getSenseDistanceByKeyBonusTakeHighest(SenseTypeEnum.TRUESIGHT, modifiers, abilityLookup, customSenseLookup),
|
||||
};
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param skills
|
||||
* @param modifiers
|
||||
* @param abilityLookup
|
||||
* @param overrideValue
|
||||
*/
|
||||
export function generatePassivePerception(skills, modifiers, abilityLookup, overrideValue) {
|
||||
if (overrideValue !== null) {
|
||||
return overrideValue;
|
||||
}
|
||||
return derivePassivePerception(skills, modifiers, abilityLookup);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param skills
|
||||
* @param modifiers
|
||||
* @param abilityLookup
|
||||
* @param overrideValue
|
||||
*/
|
||||
export function generatePassiveInvestigation(skills, modifiers, abilityLookup, overrideValue) {
|
||||
if (overrideValue !== null) {
|
||||
return overrideValue;
|
||||
}
|
||||
return derivePassiveInvestigation(skills, modifiers, abilityLookup);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param skills
|
||||
* @param modifiers
|
||||
* @param abilityLookup
|
||||
* @param overrideValue
|
||||
*/
|
||||
export function generatePassiveInsight(skills, modifiers, abilityLookup, overrideValue) {
|
||||
if (overrideValue !== null) {
|
||||
return overrideValue;
|
||||
}
|
||||
return derivePassiveInsight(skills, modifiers, abilityLookup);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param senses
|
||||
*/
|
||||
export function generateCustomSenseLookup(senses) {
|
||||
return keyBy(senses, 'senseId');
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { CampaignSettingAccessors } from '../../CampaignSetting';
|
||||
import { FeatureFlagEnum, FeatureFlagInfoUtils } from '../../FeatureFlagInfo';
|
||||
import { RuleDataAccessors } from '../../RuleData';
|
||||
/**
|
||||
*
|
||||
* @param activeSourceCategories
|
||||
* @param campaignSetting
|
||||
* @param ruleData
|
||||
*/
|
||||
export function generateActiveSources(activeSourceCategories, customCampaignSetting, campaignSetting, ruleData, featureFlagInfo) {
|
||||
// console.log('customCampaignSetting', customCampaignSetting);
|
||||
const isCampaignSettingsActive = FeatureFlagInfoUtils.getFeatureFlagInfoValue(FeatureFlagEnum.RELEASE_GATE_CAMPAIGN_SETTINGS, featureFlagInfo);
|
||||
const sourceData = RuleDataAccessors.getSourceData(ruleData);
|
||||
let campaignSettingSources = [];
|
||||
if (customCampaignSetting) {
|
||||
if (customCampaignSetting.campaignSettingId) {
|
||||
campaignSettingSources = CampaignSettingAccessors.getAvailableSourceIds(campaignSetting[0]);
|
||||
}
|
||||
else {
|
||||
campaignSettingSources = CampaignSettingAccessors.getEnabledSourceIds(customCampaignSetting);
|
||||
}
|
||||
}
|
||||
// Return the Campaign Setting sources if the feature is enabled and they have been set
|
||||
if (isCampaignSettingsActive && (campaignSettingSources === null || campaignSettingSources === void 0 ? void 0 : campaignSettingSources.length)) {
|
||||
return sourceData.filter((source) => campaignSettingSources === null || campaignSettingSources === void 0 ? void 0 : campaignSettingSources.includes(source.id));
|
||||
}
|
||||
// Otherwise return the sources mapped to the specified Source Category
|
||||
return sourceData.filter((source) => activeSourceCategories.includes(source.sourceCategoryId));
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
import { keyBy } from 'lodash';
|
||||
import { SpeedMovementKeyEnum, WeightSpeedTypeEnum } from '../../Core';
|
||||
import { ModifierDerivers, ModifierValidators } from '../../Modifier';
|
||||
import { RaceAccessors } from '../../Race';
|
||||
import { RuleDataUtils } from '../../RuleData';
|
||||
/**
|
||||
*
|
||||
* @param speedMovementKey
|
||||
* @param baseWeightSpeedLookup
|
||||
* @param weightSpeedType
|
||||
* @param mirrorSpeed
|
||||
* @param armorSpeedAdjustment
|
||||
* @param modifiers
|
||||
* @param customSpeedLookup
|
||||
* @param equippedArmor
|
||||
*/
|
||||
export function generateSpeedInfoByKey(speedMovementKey, baseWeightSpeedLookup, weightSpeedType, mirrorSpeed, armorSpeedAdjustment, modifiers, customSpeedLookup, equippedArmor) {
|
||||
if (baseWeightSpeedLookup === null) {
|
||||
return null;
|
||||
}
|
||||
const speedReductionRemoveModifiers = modifiers.filter((modifier) => ModifierValidators.isSpeedReductionRemoveModifier(modifier));
|
||||
const hasSpeedReductionRemove = speedReductionRemoveModifiers.length > 0;
|
||||
// set up initial speed
|
||||
let finalSpeed = baseWeightSpeedLookup[speedMovementKey];
|
||||
// calc based on standard modifiers
|
||||
const setModifiers = modifiers.filter((modifier) => ModifierValidators.isValidSetSpeedModifier(modifier, speedMovementKey));
|
||||
if (setModifiers.length) {
|
||||
finalSpeed = ModifierDerivers.deriveHighestModifierValue(setModifiers);
|
||||
}
|
||||
else {
|
||||
const innateSetModifiers = modifiers.filter((modifier) => ModifierValidators.isValidSetInnateSpeedModifier(modifier, speedMovementKey));
|
||||
let baseMovementSpeed = baseWeightSpeedLookup[speedMovementKey];
|
||||
if (baseMovementSpeed === null) {
|
||||
baseMovementSpeed = 0;
|
||||
}
|
||||
const baseSpeed = Math.max(baseMovementSpeed, ModifierDerivers.deriveHighestModifierValue(innateSetModifiers));
|
||||
if (baseSpeed === 0) {
|
||||
finalSpeed = baseSpeed;
|
||||
}
|
||||
else {
|
||||
const speedAdjustmentModifiers = modifiers.filter((modifier) => ModifierValidators.isValidBonusSpeedModifier(modifier, speedMovementKey));
|
||||
const speedAdjustmentModifierTotal = ModifierDerivers.sumModifiers(speedAdjustmentModifiers);
|
||||
let unarmoredBonus = 0;
|
||||
if (!equippedArmor) {
|
||||
const unarmoredBonusModifiers = modifiers.filter((modifier) => ModifierValidators.isBonusUnarmoredMovementModifier(modifier));
|
||||
unarmoredBonus = ModifierDerivers.sumModifiers(unarmoredBonusModifiers);
|
||||
}
|
||||
finalSpeed = baseSpeed + speedAdjustmentModifierTotal + armorSpeedAdjustment + unarmoredBonus;
|
||||
}
|
||||
}
|
||||
// check if there are any speed reductions
|
||||
if (!hasSpeedReductionRemove && finalSpeed !== 0) {
|
||||
let speed = finalSpeed;
|
||||
if (weightSpeedType === WeightSpeedTypeEnum.ENCUMBERED) {
|
||||
speed -= 10;
|
||||
}
|
||||
else if (weightSpeedType === WeightSpeedTypeEnum.HEAVILY_ENCUMBERED) {
|
||||
speed -= 20;
|
||||
}
|
||||
finalSpeed = Math.max(0, speed);
|
||||
}
|
||||
// check for custom speed overrides
|
||||
const movementId = RuleDataUtils.getMovementTypeBySpeedMovementKey(speedMovementKey);
|
||||
if (movementId !== null) {
|
||||
const speed = customSpeedLookup[movementId];
|
||||
if (speed && speed.distance !== null) {
|
||||
finalSpeed = speed.distance;
|
||||
}
|
||||
}
|
||||
// Have to do another pass to check if any of the speed set modifiers are null, if so use the mirror speed (most likely walking)
|
||||
if (mirrorSpeed !== null) {
|
||||
const speedSetModifiers = modifiers.filter((modifier) => ModifierValidators.isValidSetInnateSpeedModifier(modifier, speedMovementKey));
|
||||
const nullSpeedSetModifiers = speedSetModifiers.filter((modifier) => ModifierDerivers.deriveFixedModifierValue(modifier, null) === null);
|
||||
if (nullSpeedSetModifiers.length) {
|
||||
finalSpeed = mirrorSpeed;
|
||||
}
|
||||
}
|
||||
return finalSpeed;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param weightSpeeds
|
||||
* @param weightSpeedType
|
||||
* @param armorSpeedAdjustment
|
||||
* @param modifiers
|
||||
* @param customSpeedLookup
|
||||
* @param equippedArmor
|
||||
*/
|
||||
export function generateSpeedInfo(weightSpeeds, weightSpeedType, armorSpeedAdjustment, modifiers, customSpeedLookup, equippedArmor) {
|
||||
const baseWeightSpeedLookup = weightSpeeds ? weightSpeeds[WeightSpeedTypeEnum.NORMAL] : null;
|
||||
const walkingSpeed = generateSpeedInfoByKey(SpeedMovementKeyEnum.WALK, baseWeightSpeedLookup, weightSpeedType, null, armorSpeedAdjustment, modifiers, customSpeedLookup, equippedArmor);
|
||||
return {
|
||||
[SpeedMovementKeyEnum.WALK]: walkingSpeed,
|
||||
[SpeedMovementKeyEnum.FLY]: generateSpeedInfoByKey(SpeedMovementKeyEnum.FLY, baseWeightSpeedLookup, weightSpeedType, walkingSpeed, armorSpeedAdjustment, modifiers, customSpeedLookup, equippedArmor),
|
||||
[SpeedMovementKeyEnum.CLIMB]: generateSpeedInfoByKey(SpeedMovementKeyEnum.CLIMB, baseWeightSpeedLookup, weightSpeedType, walkingSpeed, armorSpeedAdjustment, modifiers, customSpeedLookup, equippedArmor),
|
||||
[SpeedMovementKeyEnum.BURROW]: generateSpeedInfoByKey(SpeedMovementKeyEnum.BURROW, baseWeightSpeedLookup, weightSpeedType, walkingSpeed, armorSpeedAdjustment, modifiers, customSpeedLookup, equippedArmor),
|
||||
[SpeedMovementKeyEnum.SWIM]: generateSpeedInfoByKey(SpeedMovementKeyEnum.SWIM, baseWeightSpeedLookup, weightSpeedType, walkingSpeed, armorSpeedAdjustment, modifiers, customSpeedLookup, equippedArmor),
|
||||
};
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param speedInfo
|
||||
* @param customSpeedLookup
|
||||
* @param ruleData
|
||||
*/
|
||||
export function generateSpeeds(speedInfo, customSpeedLookup, ruleData) {
|
||||
return Object.keys(speedInfo).map((movementKey, idx) => {
|
||||
const movementAmount = speedInfo[movementKey];
|
||||
let source = '';
|
||||
const movementType = RuleDataUtils.getMovementTypeBySpeedMovementKey(movementKey);
|
||||
if (movementType && customSpeedLookup[movementType]) {
|
||||
source = customSpeedLookup[movementType].source;
|
||||
}
|
||||
return {
|
||||
distance: movementAmount,
|
||||
type: movementType,
|
||||
source,
|
||||
name: RuleDataUtils.getMovementDescription(movementType, ruleData),
|
||||
};
|
||||
});
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param race
|
||||
*/
|
||||
export function generateWeightSpeeds(race) {
|
||||
if (race === null) {
|
||||
return null;
|
||||
}
|
||||
return RaceAccessors.getWeightSpeeds(race);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param speeds
|
||||
*/
|
||||
export function generateCustomSpeedLookup(speeds) {
|
||||
return keyBy(speeds, 'movementId');
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
import { AbilityAccessors } from '../../Ability';
|
||||
import { ClassAccessors } from '../../Class';
|
||||
import { ModifierDerivers, ModifierValidators } from '../../Modifier';
|
||||
/**
|
||||
*
|
||||
* @param modifiers
|
||||
* @param xpInfo
|
||||
* @param proficiencyBonus
|
||||
* @param abilityLookup
|
||||
* @param spellListDataOriginLookup
|
||||
*/
|
||||
export function generateOverallSpellInfo(modifiers, xpInfo, proficiencyBonus, abilityLookup, spellListDataOriginLookup) {
|
||||
const bonusCantripDamageModifiers = modifiers.filter((modifier) => ModifierValidators.isBonusCantripDamage(modifier));
|
||||
const bonusCantripDamageModifierTotal = ModifierDerivers.sumModifiers(bonusCantripDamageModifiers, abilityLookup);
|
||||
return {
|
||||
bonusCantripDamage: bonusCantripDamageModifierTotal,
|
||||
characterLevel: xpInfo.currentLevel,
|
||||
proficiencyBonus,
|
||||
abilityLookup,
|
||||
modifiers,
|
||||
spellListDataOriginLookup,
|
||||
};
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param classes
|
||||
* @param characterLevel
|
||||
* @param spellSlots
|
||||
* @param pactMagicSlots
|
||||
* @param castableSpellLevels
|
||||
* @param castablePactMagicLevels
|
||||
* @param availableSpellLevels
|
||||
* @param availablePactMagicLevels
|
||||
* @param maxLevel
|
||||
* @param abilityLookup
|
||||
* @param classSpellInfoLookup
|
||||
*/
|
||||
export function generateSpellCasterInfo(classes, characterLevel, spellSlots, pactMagicSlots, castableSpellLevels, castablePactMagicLevels, availableSpellLevels, availablePactMagicLevels, maxLevel, abilityLookup, classSpellInfoLookup) {
|
||||
var _a, _b, _c, _d;
|
||||
const castingInfo = {
|
||||
modifiers: [],
|
||||
spellAttacks: [],
|
||||
saveDcs: [],
|
||||
};
|
||||
for (let i = 0; i < classes.length; i++) {
|
||||
const charClass = classes[i];
|
||||
const spellRules = ClassAccessors.getSpellRules(charClass);
|
||||
if (spellRules === null) {
|
||||
continue;
|
||||
}
|
||||
let modifier = 0;
|
||||
if (spellRules.spellcastingAbilityStatId) {
|
||||
modifier = AbilityAccessors.getModifier(abilityLookup[spellRules.spellcastingAbilityStatId]);
|
||||
}
|
||||
const classSpellInfo = classSpellInfoLookup[ClassAccessors.getId(charClass)];
|
||||
const spellAttack = classSpellInfo.toHit;
|
||||
const saveDc = classSpellInfo.spellSaveDc;
|
||||
if (ClassAccessors.isSpellcastingActive(charClass) || ClassAccessors.isPactMagicActive(charClass)) {
|
||||
const existingModifierIndex = castingInfo.modifiers.findIndex((existingModifier) => existingModifier.value === modifier);
|
||||
if (existingModifierIndex > -1) {
|
||||
castingInfo.modifiers[existingModifierIndex].sources.push(charClass);
|
||||
}
|
||||
else {
|
||||
castingInfo.modifiers.push({
|
||||
value: modifier,
|
||||
sources: [charClass],
|
||||
});
|
||||
}
|
||||
const existingSpellAttackIndex = castingInfo.spellAttacks.findIndex((existingSpellAttack) => existingSpellAttack.value === spellAttack);
|
||||
if (existingSpellAttackIndex > -1) {
|
||||
castingInfo.spellAttacks[existingSpellAttackIndex].sources.push(charClass);
|
||||
}
|
||||
else {
|
||||
castingInfo.spellAttacks.push({
|
||||
value: spellAttack,
|
||||
sources: [charClass],
|
||||
});
|
||||
}
|
||||
const existingSaveDcIndex = castingInfo.saveDcs.findIndex((existingSaveDc) => existingSaveDc.value === saveDc);
|
||||
if (existingSaveDcIndex > -1) {
|
||||
castingInfo.saveDcs[existingSaveDcIndex].sources.push(charClass);
|
||||
}
|
||||
else {
|
||||
castingInfo.saveDcs.push({
|
||||
value: saveDc,
|
||||
sources: [charClass],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
const firstAvailableSpellSlotLevel = (_a = availableSpellLevels.sort()[0]) !== null && _a !== void 0 ? _a : null;
|
||||
const firstAvailablePactMagicSlotLevel = (_b = availablePactMagicLevels.sort()[0]) !== null && _b !== void 0 ? _b : null;
|
||||
const lastAvailableSpellSlotLevel = (_c = availableSpellLevels.sort().reverse()[0]) !== null && _c !== void 0 ? _c : null;
|
||||
const lastAvailablePactMagicSlotLevel = (_d = availablePactMagicLevels.sort().reverse()[0]) !== null && _d !== void 0 ? _d : null;
|
||||
return {
|
||||
characterLevel,
|
||||
spellSlots,
|
||||
pactMagicSlots,
|
||||
castableSpellLevels,
|
||||
castablePactMagicLevels,
|
||||
availableSpellLevels,
|
||||
availablePactMagicLevels,
|
||||
firstAvailableSpellSlotLevel,
|
||||
firstAvailablePactMagicSlotLevel,
|
||||
lastAvailableSpellSlotLevel,
|
||||
lastAvailablePactMagicSlotLevel,
|
||||
maxLevel,
|
||||
castingInfo,
|
||||
};
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param classSpellLists
|
||||
* @param characterSpells
|
||||
*/
|
||||
export function generateHasSpells(classSpellLists, characterSpells) {
|
||||
return !!(classSpellLists.length || characterSpells.length);
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
import { ContainerAccessors, ContainerTypeEnum } from '../../Container';
|
||||
import { ItemDerivers, ItemValidators } from '../../Item';
|
||||
import { RuleDataAccessors } from '../../RuleData';
|
||||
/**
|
||||
*
|
||||
* @param currencies
|
||||
* @param preferences
|
||||
* @param ruleData
|
||||
*/
|
||||
export function generateCoinWeight(currencies, preferences, ruleData) {
|
||||
let weight = 0;
|
||||
if (currencies === null) {
|
||||
return weight;
|
||||
}
|
||||
if (preferences !== null && !preferences.ignoreCoinWeight) {
|
||||
RuleDataAccessors.getCurrencyData(ruleData).forEach((currencyInfo) => {
|
||||
if (currencyInfo.name !== null) {
|
||||
const heldCurrencyAmount = currencies[currencyInfo.name.toLowerCase()];
|
||||
if (heldCurrencyAmount) {
|
||||
weight += currencyInfo.weight * heldCurrencyAmount;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return weight;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param allItems
|
||||
* @param customItems
|
||||
* @returns {number}
|
||||
*/
|
||||
export function generateItemWeight(allItems, customItems) {
|
||||
return ItemDerivers.deriveItemsWeightTotal(allItems) + ItemDerivers.deriveItemsWeightTotal(customItems);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param characterContainers
|
||||
* @param partyContainers
|
||||
* @param characterId
|
||||
* @returns {number}
|
||||
*/
|
||||
export function generateContainerItemWeight(characterContainers, partyContainers, characterId) {
|
||||
return [...characterContainers, ...partyContainers].reduce((acc, container) => {
|
||||
const type = ContainerAccessors.getContainerType(container);
|
||||
const isEquipped = ContainerAccessors.isEquipped(container);
|
||||
if (type === ContainerTypeEnum.ITEM && isEquipped) {
|
||||
// A container item
|
||||
const containerItem = ContainerAccessors.getItem(container);
|
||||
// Ignore containers equipped to someone else in the party inventory
|
||||
if (containerItem && ItemValidators.isEquippedToCurrentCharacter(containerItem, characterId)) {
|
||||
const appliedWeight = ContainerAccessors.getWeightInfo(container).itemApplied;
|
||||
acc += appliedWeight;
|
||||
}
|
||||
}
|
||||
if (type === ContainerTypeEnum.CHARACTER || type === ContainerTypeEnum.CAMPAIGN) {
|
||||
// Based on the generateContainerWeightInfo generator,
|
||||
// when looking at the top level party inventory,
|
||||
// applied weight only includes items that are equipped to you.
|
||||
const appliedWeight = ContainerAccessors.getWeightInfo(container).itemApplied;
|
||||
acc += appliedWeight;
|
||||
}
|
||||
return acc;
|
||||
}, 0);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param characterContainers
|
||||
* @param partyContainers
|
||||
* @param characterId
|
||||
* @param preferences
|
||||
* @returns {number}
|
||||
*/
|
||||
export function generateCointainerCoinWeight(characterContainers, partyContainers, characterId, preferences) {
|
||||
// 0 when ignoring coin weight
|
||||
if (!preferences || preferences.ignoreCoinWeight) {
|
||||
return 0;
|
||||
}
|
||||
// Whether cointainers are enabled or not, the process is the same.
|
||||
// All the coins will be in the top level character container if cointainers are off.
|
||||
return [...characterContainers, ...partyContainers].reduce((acc, container) => {
|
||||
const type = ContainerAccessors.getContainerType(container);
|
||||
const isEquipped = ContainerAccessors.isEquipped(container);
|
||||
if (type === ContainerTypeEnum.ITEM && isEquipped) {
|
||||
const containerItem = ContainerAccessors.getItem(container);
|
||||
if (containerItem && ItemValidators.isEquippedToCurrentCharacter(containerItem, characterId)) {
|
||||
const appliedWeight = ContainerAccessors.getWeightInfo(container).coinApplied;
|
||||
acc += appliedWeight;
|
||||
}
|
||||
}
|
||||
if (type === ContainerTypeEnum.CHARACTER || type === ContainerTypeEnum.CAMPAIGN) {
|
||||
const appliedWeight = ContainerAccessors.getWeightInfo(container).coinApplied;
|
||||
acc += appliedWeight;
|
||||
}
|
||||
return acc;
|
||||
}, 0);
|
||||
}
|
||||
/**
|
||||
* //TODO could include suppliers data
|
||||
* @param itemWeight
|
||||
* @param coinWeight
|
||||
*/
|
||||
export function generateTotalWeight(itemWeight, coinWeight) {
|
||||
// When coin weight is ignored, coinWeight will be 0.
|
||||
return itemWeight + coinWeight;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
*
|
||||
* @param baseHp
|
||||
* @param overrideHp
|
||||
* @param bonusHp
|
||||
* @param tempHp
|
||||
* @param removedHp
|
||||
* https://github.com/reduxjs/reselect/issues/378
|
||||
*/
|
||||
export function hack__generateHitPointParts(baseHp, overrideHp, bonusHp, tempHp, removedHp) {
|
||||
return {
|
||||
baseHp,
|
||||
overrideHp,
|
||||
bonusHp,
|
||||
tempHp,
|
||||
removedHp,
|
||||
};
|
||||
}
|
||||
export function hack__generateSpecialWeaponPropertiesEnabled(hexWeaponEnabled, pactWeaponEnabled, improvedPactWeaponEnabled, dedicatedWeaponEnabled) {
|
||||
return {
|
||||
hexWeaponEnabled,
|
||||
pactWeaponEnabled,
|
||||
improvedPactWeaponEnabled,
|
||||
dedicatedWeaponEnabled,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import * as CharacterConstants from './constants';
|
||||
import * as CharacterDerivers from './derivers';
|
||||
import * as CharacterGenerators from './generators';
|
||||
import * as CharacterHacks from './hacks';
|
||||
import * as CharacterTypings from './typings';
|
||||
import * as CharacterUtils from './utils';
|
||||
import * as CharacterValidators from './validators';
|
||||
export * from './typings';
|
||||
export * from './constants';
|
||||
export { CharacterDerivers, CharacterValidators, CharacterGenerators, CharacterUtils, CharacterHacks };
|
||||
export default Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, CharacterConstants), CharacterDerivers), CharacterValidators), CharacterGenerators), CharacterTypings), CharacterUtils), CharacterHacks);
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
*
|
||||
* @param preKey
|
||||
*/
|
||||
export function getPreferenceKey(preKey) {
|
||||
switch (preKey) {
|
||||
case 'abilityScoreDisplayType':
|
||||
return preKey;
|
||||
case 'enableDarkMode':
|
||||
return preKey;
|
||||
case 'enableContainerCurrency':
|
||||
return preKey;
|
||||
case 'enableOptionalClassFeatures':
|
||||
return preKey;
|
||||
case 'enableOptionalOrigins':
|
||||
return preKey;
|
||||
case 'encumbranceType':
|
||||
return preKey;
|
||||
case 'enforceFeatRules':
|
||||
return preKey;
|
||||
case 'enforceMulticlassRules':
|
||||
return preKey;
|
||||
case 'hitPointType':
|
||||
return preKey;
|
||||
case 'ignoreCoinWeight':
|
||||
return preKey;
|
||||
case 'primaryMovement':
|
||||
return preKey;
|
||||
case 'primarySense':
|
||||
return preKey;
|
||||
case 'privacyType':
|
||||
return preKey;
|
||||
case 'progressionType':
|
||||
return preKey;
|
||||
case 'sharingType':
|
||||
return preKey;
|
||||
case 'showCompanions':
|
||||
return preKey;
|
||||
case 'showScaledSpells':
|
||||
return preKey;
|
||||
case 'showUnarmedStrike':
|
||||
return preKey;
|
||||
case 'showWildShape':
|
||||
return preKey;
|
||||
case 'useHomebrewContent':
|
||||
return preKey;
|
||||
default:
|
||||
//not implemented
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { groupBy } from 'lodash';
|
||||
import { EntityUtils } from '../../Entity';
|
||||
/**
|
||||
*
|
||||
* @param defenseAdjustments
|
||||
*/
|
||||
export function groupDefenseAdjustments(defenseAdjustments) {
|
||||
const groupedDefenseAdjustments = [];
|
||||
const keyedGroupDefenseAdjustments = groupBy(defenseAdjustments, 'name');
|
||||
Object.keys(keyedGroupDefenseAdjustments).forEach((groupKey) => {
|
||||
const damageAdjustments = keyedGroupDefenseAdjustments[groupKey];
|
||||
groupedDefenseAdjustments.push({
|
||||
name: groupKey,
|
||||
sources: damageAdjustments
|
||||
.map((adjustment) => {
|
||||
return EntityUtils.getDataOriginName(adjustment.dataOrigin, undefined, true);
|
||||
})
|
||||
.sort(),
|
||||
hasCustom: !!damageAdjustments.find((adjustment) => adjustment.isCustom),
|
||||
damageAdjustments,
|
||||
});
|
||||
});
|
||||
return groupedDefenseAdjustments;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
*
|
||||
* @param hitPointInfo
|
||||
* @param hpDiff
|
||||
*/
|
||||
export function calculateHitPoints(hitPointInfo, hpDiff) {
|
||||
let newTemp;
|
||||
let newHp;
|
||||
const hpTemp = hitPointInfo.tempHp ? hitPointInfo.tempHp : 0;
|
||||
if (hpDiff < 0) {
|
||||
newTemp = Math.max(hpTemp + hpDiff, 0);
|
||||
if (Math.abs(hpDiff) > hpTemp) {
|
||||
newHp = hitPointInfo.remainingHp + (hpTemp + hpDiff);
|
||||
}
|
||||
else {
|
||||
newHp = hitPointInfo.remainingHp;
|
||||
}
|
||||
}
|
||||
else {
|
||||
newTemp = hitPointInfo.tempHp;
|
||||
newHp = Math.min(hitPointInfo.remainingHp + hpDiff, hitPointInfo.totalHp);
|
||||
}
|
||||
newHp = Math.max(0, newHp);
|
||||
const newRemovedHp = hitPointInfo.totalHp - newHp;
|
||||
return {
|
||||
newTemp,
|
||||
newHp,
|
||||
newRemovedHp,
|
||||
startHp: hitPointInfo.remainingHp,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
import { CreatureSizeEnum } from '../../Core';
|
||||
import { ModifierAccessors, ModifierDerivers, ModifierSubTypeEnum, ModifierValidators } from '../../Modifier';
|
||||
/**
|
||||
*
|
||||
* @param sizeId
|
||||
* @param modifiers
|
||||
*/
|
||||
export function getEncumbranceSize(sizeId, modifiers) {
|
||||
let encumbranceSize = sizeId;
|
||||
let isSmallerOverride = false;
|
||||
let isLargerOverride = false;
|
||||
const carryingCapacitySizeModifiers = modifiers.filter((modifier) => ModifierValidators.isCarryingCapacitySizeModifier(modifier));
|
||||
carryingCapacitySizeModifiers.forEach((modifier) => {
|
||||
switch (ModifierAccessors.getSubType(modifier)) {
|
||||
case ModifierSubTypeEnum.TINY:
|
||||
isSmallerOverride = true;
|
||||
break;
|
||||
case ModifierSubTypeEnum.LARGE:
|
||||
case ModifierSubTypeEnum.HUGE:
|
||||
case ModifierSubTypeEnum.GARGANTUAN:
|
||||
isLargerOverride = true;
|
||||
break;
|
||||
default:
|
||||
// not implemented
|
||||
}
|
||||
});
|
||||
if (isSmallerOverride) {
|
||||
encumbranceSize = CreatureSizeEnum.TINY;
|
||||
}
|
||||
if (isLargerOverride) {
|
||||
encumbranceSize = CreatureSizeEnum.LARGE;
|
||||
}
|
||||
return encumbranceSize;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param value
|
||||
* @param encumbranceSizeId
|
||||
*/
|
||||
export function getEncumbranceSizeScaledValue(value, encumbranceSizeId) {
|
||||
switch (encumbranceSizeId) {
|
||||
case CreatureSizeEnum.TINY:
|
||||
return value / 2;
|
||||
case CreatureSizeEnum.LARGE:
|
||||
case CreatureSizeEnum.HUGE:
|
||||
case CreatureSizeEnum.GARGANTUAN:
|
||||
return value * 2;
|
||||
default:
|
||||
// not implemented
|
||||
}
|
||||
return value;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param value
|
||||
* @param modifiers
|
||||
*/
|
||||
export function getCarryCapacityModifierScaledValue(value, modifiers) {
|
||||
// get weight multiplied by any multipliers, positive is times, negative is division (ex: 2 is times two, -3 is divide by 3)
|
||||
const carryingCapacityMultiplierModifiers = modifiers.filter((modifier) => ModifierValidators.isCarringCapacityMultiplierModifier(modifier));
|
||||
const highestCarryingCapacityMultiplier = ModifierDerivers.deriveHighestModifierValue(carryingCapacityMultiplierModifiers);
|
||||
if (highestCarryingCapacityMultiplier) {
|
||||
if (highestCarryingCapacityMultiplier < 0) {
|
||||
return (value /= Math.abs(highestCarryingCapacityMultiplier));
|
||||
}
|
||||
return (value *= highestCarryingCapacityMultiplier);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param strengthScale
|
||||
* @param strScore
|
||||
* @param size
|
||||
* @param modifiers
|
||||
*/
|
||||
export function getStrengthScaledCarryCapacity(strengthScale, strScore, size, modifiers) {
|
||||
const sizeId = size ? size.id : null;
|
||||
const baseWeight = strScore * strengthScale;
|
||||
const encumbranceSize = getEncumbranceSize(sizeId, modifiers);
|
||||
let weight = getEncumbranceSizeScaledValue(baseWeight, encumbranceSize);
|
||||
weight = getCarryCapacityModifierScaledValue(weight, modifiers);
|
||||
return weight;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param strScore
|
||||
* @param size
|
||||
* @param modifiers
|
||||
*/
|
||||
export function getCarryCapacity(strScore, size, modifiers) {
|
||||
return getStrengthScaledCarryCapacity(15, strScore, size, modifiers);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param carryCapacity
|
||||
*/
|
||||
export function getPushDragLiftWeight(carryCapacity) {
|
||||
return carryCapacity * 2;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param strScore
|
||||
* @param size
|
||||
* @param modifiers
|
||||
*/
|
||||
export function getEncumberedWeight(strScore, size, modifiers) {
|
||||
return getStrengthScaledCarryCapacity(5, strScore, size, modifiers);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param strScore
|
||||
* @param size
|
||||
* @param modifiers
|
||||
*/
|
||||
export function getHeavilyEncumberedWeight(strScore, size, modifiers) {
|
||||
return getStrengthScaledCarryCapacity(10, strScore, size, modifiers);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { DamageAdjustmentTypeEnum } from '../../Core';
|
||||
/**
|
||||
*
|
||||
* @param damageAdjustment
|
||||
*/
|
||||
export function isResistanceDamageAdjustment(damageAdjustment) {
|
||||
return damageAdjustment.type === DamageAdjustmentTypeEnum.RESISTANCE;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param damageAdjustment
|
||||
*/
|
||||
export function isVulnerabilityDamageAdjustment(damageAdjustment) {
|
||||
return damageAdjustment.type === DamageAdjustmentTypeEnum.VULNERABILITY;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param damageAdjustment
|
||||
*/
|
||||
export function isImmunityDamageAdjustment(damageAdjustment) {
|
||||
return damageAdjustment.type === DamageAdjustmentTypeEnum.IMMUNITY;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { CustomProficiencyTypeEnum } from '../../Core';
|
||||
/**
|
||||
*
|
||||
* @param proficiency
|
||||
*/
|
||||
export function isCustomSkillProficiency(proficiency) {
|
||||
return proficiency.type === CustomProficiencyTypeEnum.SKILL;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { SourceTypeEnum } from '../../Core';
|
||||
/**
|
||||
*
|
||||
* @param source
|
||||
*/
|
||||
export function isPrimarySource(source) {
|
||||
return source.sourceType === SourceTypeEnum.PRIMARY;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param source
|
||||
*/
|
||||
export function isSecondarySource(source) {
|
||||
return source.sourceType === SourceTypeEnum.SECONDARY;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param source
|
||||
*/
|
||||
export function isHiddenSource(source) {
|
||||
return source.sourceType === SourceTypeEnum.HIDDEN;
|
||||
}
|
||||
Reference in New Issue
Block a user