2025-05-28 15:36:51 -07:00

51 lines
1.8 KiB
JavaScript

import { FeatAccessors } from '.';
import { DB_STRING_ELDRITCH_ADEPT, DB_STRING_MEDIUM_ARMOR_MASTER, DISGUISE_FEAT_TAG_NAME, INITIAL_ASI_TAG_NAME, } from '../Core';
import { getId, getName, getRepeatableParentId, isRepeatable } from './accessors';
/**
*
* @param feat
*/
export function isEldritchAdept(feat) {
return getName(feat) === DB_STRING_ELDRITCH_ADEPT;
}
export function isMediumArmorMaster(feat) {
return getName(feat) === DB_STRING_MEDIUM_ARMOR_MASTER;
}
export function doesTagSatisfyConstraint(tag, constraint) {
return tag.entityTagId === constraint.entityTagId;
}
export function doesSatisfyTagConstraints(tags, constraints) {
// For every constraint C,
return constraints.every((constraint) => {
// There exists at least one tag T such that
return tags.some((tag) => {
// T satisfies/matches C (i.e. T.entityTagId === C.entityTagId)
return this.doesTagSatisfyConstraint(tag, constraint);
});
});
}
/*
* Some feats, such as the ASI feats granted to new backgrounds
* aren't Feats in the official sense. We're just using the feat
* data structure to support other rules.
* Another example is the Weapon Mastery class feature, which
* actually just grants a special Weapon Mastery feat.
*
* Returns true when it is representing something other than a feat.
* Returns false when the feat is a real feat from the rules.
*/
export function isHiddenFeat(feat) {
return FeatAccessors.getCategories(feat).some(
// TODO: Consider simplifying the logic by adding the __DISGUISE_FEAT tag to initial ASI feats.
(cat) => cat.tagName === INITIAL_ASI_TAG_NAME || cat.tagName === DISGUISE_FEAT_TAG_NAME);
}
/**
* @param feat
*/
export const getRepeatableGroupId = (feat) => {
if (!isRepeatable(feat)) {
return null;
}
return getRepeatableParentId(feat) || getId(feat);
};