import { visuallyHidden } from "@mui/utils"; import { orderBy } from "lodash"; import React from "react"; import { AdvantageIcon, AdvantageDisadvantageIcon, DisadvantageIcon, ProficiencyLevelIcon, DigitalDiceWrapper, } from "@dndbeyond/character-components/es"; import { CharacterTheme, Constants, RuleData, RuleDataUtils, Skill, SkillUtils, ValueLookup, ValueUtils, } from "@dndbeyond/character-rules-engine/es"; import { RollType, DiceTools, IRollContext } from "@dndbeyond/dice"; import { GameLogContext } from "@dndbeyond/game-log-components"; import { NumberDisplay } from "~/components/NumberDisplay"; interface Props { skills: Array; customSkills: Array; onCustomSkillClick?: (skill: Skill) => void; onSkillClick?: (skill: Skill) => void; onEmptyClick?: () => void; valueLookup: ValueLookup; diceEnabled: boolean; theme: CharacterTheme; ruleData: RuleData; rollContext: IRollContext; } class Skills extends React.PureComponent { static defaultProps = { diceEnabled: false, }; handleCustomSkillClick = (skill: Skill, evt: React.MouseEvent): void => { const { onCustomSkillClick } = this.props; if (onCustomSkillClick) { evt.stopPropagation(); evt.nativeEvent.stopImmediatePropagation(); onCustomSkillClick(skill); } }; handleEmptyClick = (evt: React.MouseEvent): void => { const { onEmptyClick } = this.props; if (onEmptyClick) { evt.stopPropagation(); evt.nativeEvent.stopImmediatePropagation(); onEmptyClick(); } }; handleSkillClick = (skill: Skill, evt: React.MouseEvent): void => { const { onSkillClick } = this.props; if (onSkillClick) { evt.stopPropagation(); evt.nativeEvent.stopImmediatePropagation(); onSkillClick(skill); } }; renderEmptyCustomSkills = (): React.ReactNode => { return (
Additional Skills
); }; renderCustomSkills = (): React.ReactNode => { const { customSkills, ruleData, diceEnabled, theme, rollContext } = this.props; const [{ messageTargetOptions, defaultMessageTargetOption, userId }] = this.context; return customSkills.map((skill) => { const modifier = SkillUtils.getModifier(skill); const statName = RuleDataUtils.getAbilityShortName( SkillUtils.getStat(skill), ruleData ); const proficiencyLevel = SkillUtils.getProficiencyLevel(skill); const name = SkillUtils.getName(skill); return (
{statName === null ? "--" : statName}
{name}
{modifier === null ? ( "--" ) : ( )}
); }); }; render() { const { skills, valueLookup, customSkills, diceEnabled, ruleData, theme, rollContext, } = this.props; const [{ messageTargetOptions, defaultMessageTargetOption, userId }] = this.context; let orderedSkills = orderBy(skills, (skill) => SkillUtils.getName(skill)); return (
Proficiency
Modifier
Skill
Bonus
{orderedSkills.map((skill) => { let valueTypes: Array = [ Constants.AdjustmentTypeEnum.SKILL_STAT_OVERRIDE, Constants.AdjustmentTypeEnum.SKILL_PROFICIENCY_LEVEL, Constants.AdjustmentTypeEnum.SKILL_MISC_BONUS, Constants.AdjustmentTypeEnum.SKILL_MAGIC_BONUS, Constants.AdjustmentTypeEnum.SKILL_OVERRIDE, ]; let valueDataLookup = ValueUtils.getDataLookup( valueLookup, valueTypes, ValueUtils.hack__toString(SkillUtils.getId(skill)), ValueUtils.hack__toString(SkillUtils.getEntityTypeId(skill)) ); let hasModifiedLevel = valueDataLookup.hasOwnProperty( Constants.AdjustmentTypeEnum.SKILL_PROFICIENCY_LEVEL ); let hasModifiedValue = valueDataLookup.hasOwnProperty( Constants.AdjustmentTypeEnum.SKILL_OVERRIDE ) || valueDataLookup.hasOwnProperty( Constants.AdjustmentTypeEnum.SKILL_MAGIC_BONUS ) || valueDataLookup.hasOwnProperty( Constants.AdjustmentTypeEnum.SKILL_MISC_BONUS ); let statClassNames: Array = ["ct-skills__col--stat"]; if ( valueDataLookup.hasOwnProperty( Constants.AdjustmentTypeEnum.SKILL_STAT_OVERRIDE ) ) { statClassNames.push("ct-skills__col--stat-modified"); } let modifierClassNames: Array = [ "ct-skills__col--modifier", ]; if (hasModifiedValue) { modifierClassNames.push("ct-skills__col--modifier-modified"); } if (theme.isDarkMode) { statClassNames.push("ct-skills__col--stat--dark-mode"); modifierClassNames.push("ct-skills__col--modifier--dark-mode"); } let statName = RuleDataUtils.getAbilityShortName( SkillUtils.getStat(skill), ruleData ); let modifier = SkillUtils.getModifier(skill); let name = SkillUtils.getName(skill); let usableDiceAdjustmentType = SkillUtils.getUsableDiceAdjustmentType(skill); let adjustmentsNode: React.ReactNode; if ( usableDiceAdjustmentType !== Constants.UsableDiceAdjustmentTypeEnum.NONE ) { let iconNode: React.ReactNode; let iconClassName: string = "ct-skills__adjustment"; switch (usableDiceAdjustmentType) { case Constants.UsableDiceAdjustmentTypeEnum.ADVANTAGE: iconNode = ( ); break; case Constants.UsableDiceAdjustmentTypeEnum.DISADVANTAGE: iconNode = ( ); break; case Constants.UsableDiceAdjustmentTypeEnum .ADVANTAGE_DISADVANTAGE: iconNode = ( ); break; default: // not implemented } if (iconNode) { adjustmentsNode = (
{iconNode}
); } } return (
{statName === null ? "--" : statName}
{name}
{adjustmentsNode}
{modifier === null ? ( "--" ) : ( )}
); })} {customSkills.length ? this.renderCustomSkills() : this.renderEmptyCustomSkills()}
); } } Skills.contextType = GameLogContext; export default Skills;