import React from "react"; import { Tooltip } from "@dndbeyond/character-common-components/es"; import { ClassUtils, SpellCasterCastingEntry, SpellCasterInfo, SpellSlotContract, } from "@dndbeyond/character-rules-engine/es"; import { NumberDisplay } from "~/components/NumberDisplay"; import SlotManager from "../SlotManager"; interface Props { level: number; spellCasterInfo: SpellCasterInfo; onSpellSlotSet?: (level: number, uses: number) => void; onPactSlotSet?: (level: number, uses: number) => void; showSlots: boolean; showCastingInfo: boolean; isInteractive: boolean; isDarkMode?: boolean; } export default class SpellsLevelCasting extends React.PureComponent { static defaultProps = { level: 1, showSlots: true, showCastingInfo: true, isInteractive: true, }; handleSpellSlotSet = (uses: number): void => { const { onSpellSlotSet, level } = this.props; if (onSpellSlotSet) { onSpellSlotSet(level, uses); } }; handlePactSlotSet = (uses: number): void => { const { onPactSlotSet, level } = this.props; if (onPactSlotSet) { onPactSlotSet(level, uses); } }; getSpellSlotInfo = (): SpellSlotContract | null => { const { spellCasterInfo, level } = this.props; const { spellSlots } = spellCasterInfo; let spellSlotInfo = spellSlots.find( (spellSlotGroup) => spellSlotGroup.level === level ); return spellSlotInfo ? spellSlotInfo : null; }; getPactSlotInfo = (): SpellSlotContract | null => { const { spellCasterInfo, level } = this.props; const { pactMagicSlots } = spellCasterInfo; let spellSlotInfo = pactMagicSlots.find( (pactSlotGroup) => pactSlotGroup.level === level ); return spellSlotInfo ? spellSlotInfo : null; }; hasLevelSlots = (): boolean => { let spellSlotLevel = this.getSpellSlotInfo(); let pactMagicLevel = this.getPactSlotInfo(); return !!(spellSlotLevel || pactMagicLevel); }; hasCastingInfo = (): boolean => { const { spellCasterInfo } = this.props; const { castingInfo } = spellCasterInfo; return !!( castingInfo.modifiers.length || castingInfo.spellAttacks.length || castingInfo.saveDcs.length ); }; renderLevelSlots = (): React.ReactNode => { const { isInteractive, isDarkMode } = this.props; let spellSlotLevel = this.getSpellSlotInfo(); let pactMagicLevel = this.getPactSlotInfo(); if (!this.hasLevelSlots()) { return null; } return (
{pactMagicLevel && (
Pact
)} {spellSlotLevel && (
Slots
)}
); }; renderCastingInfoGroup = ( label: React.ReactNode, entries: Array, isSignedNumber: boolean = true ): React.ReactNode => { const { isDarkMode } = this.props; return (
{entries.map((entry) => { if (entry.value === null) { return null; } let tooltip = entry.sources .map((charClass) => ClassUtils.getName(charClass)) .join(", "); return ( {isSignedNumber ? ( ) : ( entry.value )} ); })}
{label}
); }; renderCastingInfo = (): React.ReactNode => { const { spellCasterInfo } = this.props; const { castingInfo } = spellCasterInfo; if (!this.hasCastingInfo()) { return null; } return (
{this.renderCastingInfoGroup("Modifier", castingInfo.modifiers)} {this.renderCastingInfoGroup("Spell Attack", castingInfo.spellAttacks)} {this.renderCastingInfoGroup("Save DC", castingInfo.saveDcs, false)}
); }; render() { const { showSlots, showCastingInfo } = this.props; if (!this.hasCastingInfo() && !this.hasLevelSlots()) { return null; } return (
{showSlots && this.renderLevelSlots()} {showCastingInfo && this.renderCastingInfo()}
); } }