import { keyBy } from "lodash"; import * as React from "react"; import { ItemPreview, Snippet, CreaturePreview, } from "@dndbeyond/character-components/es"; import { AccessUtils, ClassUtils, Constants, Creature, CreatureLookup, CreatureUtils, HelperUtils, Infusion, InfusionChoice, InfusionChoiceUtils, InfusionModifierDataContract, InfusionUtils, InventoryLookup, Item, ItemUtils, KnownInfusionUtils, RuleData, SnippetData, Hack__BaseCharClass, CharacterTheme, } from "@dndbeyond/character-rules-engine/es"; interface Props { inventory: Array; creatures: Array; infusionChoices: Array; proficiencyBonus: number; snippetData: SnippetData; ruleData: RuleData; isReadonly: boolean; isMobile: boolean; theme: CharacterTheme; onInfusionChoiceShow?: (infusionChoice: InfusionChoice) => void; onItemShow?: (item: Item) => void; onCreatureShow?: (creature: Creature) => void; } interface State { inventoryLookup: InventoryLookup; creatureLookup: CreatureLookup; } export class Infusions extends React.PureComponent { static defaultProps = { isReadonly: false, isMobile: false, }; constructor(props) { super(props); this.state = { inventoryLookup: keyBy(props.inventory, (item) => ItemUtils.getMappingId(item) ), creatureLookup: keyBy(props.creatures, (creature) => CreatureUtils.getMappingId(creature) ), }; } componentDidUpdate(prevProps, prevState, snapshot) { if ( prevProps.inventory !== this.props.inventory || prevProps.creatures !== this.props.creatures ) { this.setState({ inventoryLookup: keyBy(this.props.inventory, (item) => ItemUtils.getMappingId(item) ), creatureLookup: keyBy(this.props.creatures, (creature) => CreatureUtils.getMappingId(creature) ), }); } } handleItemClick = (item: Item): void => { const { onItemShow } = this.props; if (onItemShow) { onItemShow(item); } }; handleCreatureClick = (creature: Creature): void => { const { onCreatureShow } = this.props; if (onCreatureShow) { onCreatureShow(creature); } }; handleInfusionClick = ( infusionChoice: InfusionChoice, evt: React.MouseEvent ): void => { const { onInfusionChoiceShow } = this.props; evt.stopPropagation(); evt.nativeEvent.stopImmediatePropagation(); if (onInfusionChoiceShow) { onInfusionChoiceShow(infusionChoice); } }; renderDescription = ( simulatedInfusion: Infusion, infusionChoice: InfusionChoice, selectedModifierData: InfusionModifierDataContract | null ): React.ReactNode => { const { snippetData, proficiencyBonus } = this.props; if ( AccessUtils.isAccessible(InfusionUtils.getAccessType(simulatedInfusion)) ) { let classLevel: number | null = selectedModifierData?.value ?? null; const dataOriginType = InfusionChoiceUtils.getDataOriginType(infusionChoice); if ( dataOriginType === Constants.DataOriginTypeEnum.CLASS_FEATURE && !classLevel ) { const dataOrigin = InfusionChoiceUtils.getDataOrigin(infusionChoice); classLevel = ClassUtils.getLevel( dataOrigin.parent as Hack__BaseCharClass ); } return ( {InfusionUtils.getSnippet(simulatedInfusion)} ); } return (
Check out the Marketplace to unlock this infusion.
); }; renderInfusions = (): React.ReactNode => { const { inventoryLookup, creatureLookup } = this.state; const { infusionChoices, ruleData, theme } = this.props; return ( {infusionChoices.map((infusionChoice) => { const knownInfusion = InfusionChoiceUtils.getKnownInfusion(infusionChoice); if (knownInfusion === null) { return null; } const simulatedInfusion = KnownInfusionUtils.getSimulatedInfusion(knownInfusion); if (simulatedInfusion === null) { return null; } const infusion = InfusionChoiceUtils.getInfusion(infusionChoice); const infusionType = InfusionUtils.getType(simulatedInfusion); let item: Item | null = null; let creature: Creature | null = null; let selectedModifierData: InfusionModifierDataContract | null = null; if (infusion) { let inventoryMappingId = InfusionUtils.getInventoryMappingId(infusion); item = HelperUtils.lookupDataOrFallback( inventoryLookup, inventoryMappingId ); let creatureMappingId = InfusionUtils.getCreatureMappingId(infusion); if (creatureMappingId !== null) { creature = HelperUtils.lookupDataOrFallback( creatureLookup, creatureMappingId ); } selectedModifierData = InfusionUtils.getSelectedModifierData(infusion); } let itemNode: React.ReactNode; if (item) { itemNode = (
); } else { itemNode = (
No item infused
); } let creatureNode: React.ReactNode; if (creature) { creatureNode = (
); } let selectedModifierDataNode: React.ReactNode; if (selectedModifierData && selectedModifierData.name) { selectedModifierDataNode = (
{selectedModifierData.name}
); } let infusionName = InfusionUtils.getName(simulatedInfusion); let infusionNameExtraNode: React.ReactNode; if (infusionType === Constants.InfusionTypeEnum.REPLICATE) { let itemName = KnownInfusionUtils.getItemName(knownInfusion); if (itemName) { infusionNameExtraNode = `: ${itemName}`; } } const choiceKey = InfusionChoiceUtils.getKey(infusionChoice); return (
{infusionName} {infusionNameExtraNode}
{this.renderDescription( simulatedInfusion, infusionChoice, selectedModifierData )}
{itemNode} {creatureNode} {selectedModifierDataNode}
); })}
); }; render() { const { infusionChoices } = this.props; let contentNode: React.ReactNode = (
No infusions choices have been made for this character
); if (infusionChoices.length > 0) { contentNode = this.renderInfusions(); } return
{contentNode}
; } } export default Infusions;