import sortBy from 'lodash/sortBy'; import { AbilityAccessors } from '../Ability'; import { FormatUtils } from '../Format'; import { getEntityId, getFriendlySubtypeName, getPrerequisites, getSubType, getType, getValue, getShouldExclude, } from './accessors'; import { PrerequisiteSubTypeEnum, PrerequisiteTypeEnum } from './constants'; import { validatePrerequisite } from './validators'; /** * * @param prerequisite * @param prerequisiteData */ export function getPrerequisiteFailure(prerequisite, prerequisiteData) { switch (getType(prerequisite)) { case PrerequisiteTypeEnum.ABILITY_SCORE: return getPrerequisiteFailureAbilityScore(prerequisite, prerequisiteData); case PrerequisiteTypeEnum.PROFICIENCY: return getPrerequisiteFailureProficiency(prerequisite); case PrerequisiteTypeEnum.SPECIES: return getPrerequisiteFailureRace(prerequisite); case PrerequisiteTypeEnum.SIZE: return getPrerequisiteFailureSize(prerequisite); case PrerequisiteTypeEnum.SPECIES_OPTION: return getPrerequisiteFailureSubrace(prerequisite); case PrerequisiteTypeEnum.LEVEL: return getPrerequisiteFailureLevel(prerequisite); case PrerequisiteTypeEnum.CLASS: return getPrerequisiteFailureClass(prerequisite); case PrerequisiteTypeEnum.FEAT: return getPrerequisiteFailureFeat(prerequisite); case PrerequisiteTypeEnum.CLASS_FEATURE: return getPrerequisiteFailureClassFeature(prerequisite); case PrerequisiteTypeEnum.CUSTOM_VALUE: default: // not defined } return null; } /** * * @param prerequisite * @param prerequisiteData */ export function getPrerequisiteFailureLevel(prerequisite) { let requiredDescription = ''; switch (getSubType(prerequisite)) { case PrerequisiteSubTypeEnum.CHARACTER_LEVEL: let value = getValue(prerequisite); value = value ? value : 1; requiredDescription = `${FormatUtils.ordinalize(value)} Level`; break; default: // not implemented } return { type: PrerequisiteTypeEnum.LEVEL, data: { requiredChoice: getFriendlySubtypeName(prerequisite), requiredDescription, }, shouldExclude: !!getShouldExclude(prerequisite), }; } /** * * @param prerequisite * @param prerequisiteData */ export function getPrerequisiteFailureAbilityScore(prerequisite, prerequisiteData) { let statKey = ''; let currentAmount = null; const entityId = getEntityId(prerequisite); if (entityId !== null) { const ability = prerequisiteData.abilityLookup[entityId]; statKey = AbilityAccessors.getStatKey(ability); currentAmount = AbilityAccessors.getScore(ability); } return { type: PrerequisiteTypeEnum.ABILITY_SCORE, data: { statKey, currentAmount, requiredValue: getValue(prerequisite), requiredChoice: getFriendlySubtypeName(prerequisite), requiredDescription: `${getFriendlySubtypeName(prerequisite)} ${getValue(prerequisite)}+`, }, shouldExclude: !!getShouldExclude(prerequisite), }; } /** * * @param prerequisite * @param prerequisiteData */ export function getPrerequisiteFailureProficiency(prerequisite) { return { type: PrerequisiteTypeEnum.PROFICIENCY, data: { requiredChoice: getFriendlySubtypeName(prerequisite), requiredDescription: `${getFriendlySubtypeName(prerequisite)} Proficiency`, }, shouldExclude: !!getShouldExclude(prerequisite), }; } /** * * @param prerequisite * @param prerequisiteData */ export function getPrerequisiteFailureRace(prerequisite) { return { type: PrerequisiteTypeEnum.SPECIES, data: { requiredChoice: getFriendlySubtypeName(prerequisite), requiredDescription: getFriendlySubtypeName(prerequisite), }, shouldExclude: !!getShouldExclude(prerequisite), }; } /** * * @param prerequisite * @param prerequisiteData */ export function getPrerequisiteFailureSubrace(prerequisite) { return { type: PrerequisiteTypeEnum.SPECIES_OPTION, data: { requiredChoice: getFriendlySubtypeName(prerequisite), requiredDescription: getFriendlySubtypeName(prerequisite), }, shouldExclude: !!getShouldExclude(prerequisite), }; } /** * * @param prerequisite * @param prerequisiteData */ export function getPrerequisiteFailureSize(prerequisite) { return { type: PrerequisiteTypeEnum.SIZE, data: { requiredChoice: getFriendlySubtypeName(prerequisite), requiredDescription: `${getFriendlySubtypeName(prerequisite)} Size`, }, shouldExclude: !!getShouldExclude(prerequisite), }; } /** * * @param prerequisite * @param prerequisiteData */ export function getPrerequisiteFailureClass(prerequisite) { return { type: PrerequisiteTypeEnum.CLASS, data: { requiredChoice: getFriendlySubtypeName(prerequisite), requiredDescription: `${getFriendlySubtypeName(prerequisite)}`, }, shouldExclude: !!getShouldExclude(prerequisite), }; } export const getPrerequisiteFailureClassFeature = (prerequisite) => ({ type: PrerequisiteTypeEnum.CLASS_FEATURE, data: { requiredChoice: prerequisite.friendlySubTypeName, requiredDescription: prerequisite.friendlySubTypeName, }, shouldExclude: !!getShouldExclude(prerequisite), }); /** * * @param prerequisite * @param prerequisiteData */ export function getPrerequisiteFailureFeat(prerequisite) { return { type: PrerequisiteTypeEnum.FEAT, data: { requiredChoice: getFriendlySubtypeName(prerequisite), requiredDescription: `${getFriendlySubtypeName(prerequisite)}`, }, shouldExclude: !!getShouldExclude(prerequisite), }; } /** * * @param prerequisiteGrouping * @param prerequisiteData */ export function getPrerequisiteGroupingFailures(prerequisiteGrouping, prerequisiteData) { const groupingFailures = []; if (prerequisiteGrouping !== null) { prerequisiteGrouping.forEach((prerequisiteGroup) => { const groupFailures = []; const prerequisites = getPrerequisites(prerequisiteGroup); prerequisites.forEach((prerequisite) => { if (!validatePrerequisite(prerequisite, prerequisiteData)) { const prereqFailure = getPrerequisiteFailure(prerequisite, prerequisiteData); if (prereqFailure) { groupFailures.push(prereqFailure); } } }); groupingFailures.push(groupFailures); }); } return groupingFailures.filter((group) => group.length); } // Creates a string that summarizes the prerequisite failures for feats and multiclassing export function getPrerequisiteFailuresText(prereqFailures) { //get all the prerequisite failures, sorted so that if every group failure is a missing prereq, they come first const failures = sortBy(prereqFailures, (failureGroup) => (failureGroup.every((f) => !f.shouldExclude) ? 0 : 1)); // Create an intro string for the summary let intro = ''; // Check if all failures are either missing or prohibited const hasOnlyMissingFailures = failures.every((failureGroup) => failureGroup.every((f) => !f.shouldExclude)); const hasOnlyProhibitedFailures = failures.every((failureGroup) => failureGroup.every((f) => f.shouldExclude)); // Determine the intro text based on the type of failures if (hasOnlyMissingFailures) { intro = 'Missing: '; } else if (hasOnlyProhibitedFailures) { intro = 'Prohibited by: '; } else { intro = 'Requires: '; } // Each failure is sorted so that prohibited failures come last, and then combined into a single string const combinedStrings = failures .map((failure) => sortBy(failure, (f) => (f.shouldExclude ? 1 : 0)) .map((failureAnd) => { if (failureAnd.shouldExclude && !hasOnlyProhibitedFailures && !hasOnlyMissingFailures) { return `Prohibited by: ${failureAnd.data.requiredDescription}`; } return `${failureAnd.data.requiredDescription}`; }) .reduce((acc, desc, idx) => { let connector = ''; if (idx < failure.length - 2) { connector = ', '; } else if (idx < failure.length - 1) { connector = ' and '; } acc += `${desc}${connector}`; return acc; }, '')) .join(' or '); // return the intro text followed by the combined strings return `${intro}${combinedStrings}`; }