New source found from dndbeyond.com

This commit is contained in:
2026-07-08 01:00:09 -07:00
parent 9a983a6d7b
commit dcefa0d2f2
2865 changed files with 222467 additions and 49053 deletions
@@ -7,11 +7,11 @@ import {
Damage,
DamageTypeIcon,
DataOriginName,
InfusionPreview,
} from "@dndbeyond/character-components/es";
import {
Action,
ActionUtils,
CampaignUtils,
CharacterTheme,
CharacterUtils,
Constants,
@@ -23,7 +23,7 @@ import {
EntityValueLookup,
FormatUtils,
Infusion,
InfusionChoiceLookup,
InfusionUtils,
InventoryManager,
Item,
ItemUtils,
@@ -41,6 +41,7 @@ import StarIcon from "@dndbeyond/fontawesome-cache/svgs/solid/star.svg";
import { HtmlContent } from "~/components/HtmlContent";
import { InfoItem } from "~/components/InfoItem";
import { InfusionPreview } from "~/components/InfusionPreview";
import { NumberDisplay } from "~/components/NumberDisplay";
import { Reference } from "~/components/Reference";
import { TagGroup } from "~/components/TagGroup";
@@ -56,8 +57,6 @@ import ValueEditor from "../ValueEditor";
import { RemoveButton } from "../common/Button";
import styles from "./styles.module.css";
const infoItemProps = { role: "listitem", inline: true };
interface Props {
item: Item;
weaponSpellDamageGroups: Array<WeaponSpellDamageGroup>;
@@ -80,9 +79,7 @@ interface Props {
onInfusionClick?: (infusion: Infusion) => void;
onMasteryActionClick?: (action: Action) => void;
entityValueLookup: EntityValueLookup;
infusionChoiceLookup: InfusionChoiceLookup;
isReadonly: boolean;
proficiencyBonus: number;
theme: CharacterTheme;
container: Container | null;
onPostRemoveNavigation?: PaneComponentEnum;
@@ -91,6 +88,8 @@ interface Props {
isCustomizeClosed?: boolean;
onCustomizeClick?: (isCollapsed: boolean) => void;
actions: Array<Action>;
onRemoveItem?: () => void;
characterId: number;
}
export class ItemDetail extends React.PureComponent<Props> {
static defaultProps = {
@@ -103,7 +102,6 @@ export class ItemDetail extends React.PureComponent<Props> {
showImage: true,
snippetData: null,
entityValueLookup: {},
infusionChoiceLookup: {},
isReadonly: false,
container: null,
};
@@ -245,7 +243,7 @@ export class ItemDetail extends React.PureComponent<Props> {
}
return (
<InfoItem label="Source:" {...infoItemProps}>
<InfoItem label="Source:">
<Reference
name={
RuleDataUtils.getSourceDataInfo(sourceId, ruleData)?.description ||
@@ -288,7 +286,7 @@ export class ItemDetail extends React.PureComponent<Props> {
} else if (isCustom) {
type = "Custom";
}
//TODO - itemManager exposes item.getIntroText() which should be used in the future to render the intro text and share logic with mobile
return (
<>
<div className="ct-item-detail__intro">
@@ -301,27 +299,26 @@ export class ItemDetail extends React.PureComponent<Props> {
})`}
{ItemUtils.isContainer(item) && ", container"}
</div>
{container && (
<div className="ct-item-detail__intro-subtitle">
{" "}
{!ItemUtils.isContainer(item)
<div className="ct-item-detail__intro-subtitle">
{container &&
(!ItemUtils.isContainer(item)
? ` In ${ContainerUtils.getName(container)}`
: ` In ${
ContainerUtils.isShared(container) ? "Party " : ""
}Inventory`}
<div>
{partyInfo &&
equippedEntityId &&
`${
inventoryManager.isEquippedToCurrentCharacter(item)
? "Equipped"
: `Equipped by ${inventoryManager.getEquippedCharacterName(
item
)}`
}`}
</div>
}Inventory`)}
<div>
{partyInfo &&
equippedEntityId &&
`${
inventoryManager.isEquippedToCurrentCharacter(item)
? "Equipped"
: `Equipped by ${inventoryManager.getEquippedCharacterName(
item
)}`
}`}
</div>
)}
{ItemUtils.isReplicated(item) && <span>Replicated Item</span>}
</div>
</>
);
};
@@ -552,24 +549,20 @@ export class ItemDetail extends React.PureComponent<Props> {
return (
<div className="ct-item-detail__customize" role="list">
<InfoItem label="Weight:" {...infoItemProps}>
<InfoItem label="Weight:">
{weight ? <NumberDisplay type="weightInLb" number={weight} /> : "--"}
</InfoItem>
<InfoItem label="Cost:" {...infoItemProps}>
<InfoItem label="Cost:">
{cost ? `${FormatUtils.renderLocaleNumber(cost)} gp` : "--"}
</InfoItem>
{notes && (
<InfoItem label="Notes:" {...infoItemProps}>
{notes}
</InfoItem>
)}
{notes && <InfoItem label="Notes:">{notes}</InfoItem>}
</div>
);
//`
};
renderGearProperties = (): React.ReactNode => {
const { item, theme } = this.props;
const { item } = this.props;
const bundleSize = ItemUtils.getBundleSize(item);
const isStackable = ItemUtils.isStackable(item);
@@ -581,11 +574,11 @@ export class ItemDetail extends React.PureComponent<Props> {
return (
<div className="ct-item-detail__customize" role="list">
<InfoItem label="Weight:" {...infoItemProps}>
<InfoItem label="Weight:">
{weight ? <NumberDisplay type="weightInLb" number={weight} /> : "--"}
</InfoItem>
{ItemUtils.isContainer(item) && (
<InfoItem label="Capacity:" {...infoItemProps}>
<InfoItem label="Capacity:">
{capacityWeight ? (
<NumberDisplay type="weightInLb" number={capacityWeight} />
) : (
@@ -593,24 +586,14 @@ export class ItemDetail extends React.PureComponent<Props> {
)}
</InfoItem>
)}
<InfoItem label="Cost:" {...infoItemProps}>
<InfoItem label="Cost:">
{cost ? `${FormatUtils.renderLocaleNumber(cost)} gp` : "--"}
</InfoItem>
{isStackable && bundleSize > 1 && (
<InfoItem label="Bundle Size:" {...infoItemProps}>
{bundleSize}
</InfoItem>
)}
{version !== null && (
<InfoItem label="Version:" {...infoItemProps}>
{version}
</InfoItem>
)}
{notes && (
<InfoItem label="Notes:" {...infoItemProps}>
{notes}
</InfoItem>
<InfoItem label="Bundle Size:">{bundleSize}</InfoItem>
)}
{version !== null && <InfoItem label="Version:">{version}</InfoItem>}
{notes && <InfoItem label="Notes:">{notes}</InfoItem>}
{this.getSourceReference(item)}
</div>
);
@@ -662,27 +645,23 @@ export class ItemDetail extends React.PureComponent<Props> {
return (
<div className="ct-item-detail__customize" role="list">
{proficiency && (
<InfoItem label="Proficient: " {...infoItemProps}>
Yes
</InfoItem>
)}
<InfoItem label="Attack Type:" {...infoItemProps}>
{proficiency && <InfoItem label="Proficient: ">Yes</InfoItem>}
<InfoItem label="Attack Type:">
{attackType === null ? "--" : attackTypeName}
</InfoItem>
{reach && (
<InfoItem label="Reach:" {...infoItemProps}>
<InfoItem label="Reach:">
<NumberDisplay type="distanceInFt" number={reach} />
</InfoItem>
)}
{showRange && (
<InfoItem label="Range:" {...infoItemProps}>
<InfoItem label="Range:">
<NumberDisplay type="distanceInFt" number={range} />
/
<NumberDisplay type="distanceInFt" number={longRange} />
</InfoItem>
)}
<InfoItem label="Damage:" {...infoItemProps}>
<InfoItem label="Damage:">
<div>
{damageDisplay}
{versatileDamageNode}
@@ -710,11 +689,11 @@ export class ItemDetail extends React.PureComponent<Props> {
</div>
))}
</InfoItem>
<InfoItem label="Damage Type:" {...infoItemProps}>
<InfoItem label="Damage Type:">
{damageType === null ? (
"--"
) : (
<React.Fragment>
<>
<DamageTypeIcon
theme={theme}
type={
@@ -724,37 +703,27 @@ export class ItemDetail extends React.PureComponent<Props> {
}
/>{" "}
{damageType}
</React.Fragment>
</>
)}
</InfoItem>
<InfoItem label="Weight:" {...infoItemProps}>
<InfoItem label="Weight:">
{weight ? <NumberDisplay type="weightInLb" number={weight} /> : "--"}
</InfoItem>
<InfoItem label="Cost:" {...infoItemProps}>
<InfoItem label="Cost:">
{cost ? `${FormatUtils.renderLocaleNumber(cost)} gp` : "--"}
</InfoItem>
{isStackable && bundleSize > 1 && (
<InfoItem label="Bundle Size:" {...infoItemProps}>
{bundleSize}
</InfoItem>
<InfoItem label="Bundle Size:">{bundleSize}</InfoItem>
)}
{properties && properties.length > 0 && (
<InfoItem label="Properties:" {...infoItemProps}>
<InfoItem label="Properties:">
{properties
.map((prop) => prop.name + (prop.notes ? ` (${prop.notes})` : ""))
.join(", ")}
</InfoItem>
)}
{version !== null && (
<InfoItem label="Version:" {...infoItemProps}>
{version}
</InfoItem>
)}
{notes && (
<InfoItem label="Notes:" {...infoItemProps}>
{notes}
</InfoItem>
)}
{version !== null && <InfoItem label="Version:">{version}</InfoItem>}
{notes && <InfoItem label="Notes:">{notes}</InfoItem>}
{this.getSourceReference(item)}
</div>
);
@@ -762,7 +731,7 @@ export class ItemDetail extends React.PureComponent<Props> {
};
renderArmorProperties = (): React.ReactNode => {
const { item, theme } = this.props;
const { item } = this.props;
const bundleSize = ItemUtils.getBundleSize(item);
const isStackable = ItemUtils.isStackable(item);
@@ -771,6 +740,7 @@ export class ItemDetail extends React.PureComponent<Props> {
const weight = ItemUtils.getWeight(item);
const cost = ItemUtils.getCost(item);
const notes = ItemUtils.getNotes(item);
const proficiency = ItemUtils.hasProficiency(item);
let armorClassDisplay: React.ReactNode = armorClass;
if (ItemUtils.isShield(item)) {
@@ -784,30 +754,19 @@ export class ItemDetail extends React.PureComponent<Props> {
return (
<div className="ct-item-detail__customize" role="list">
<InfoItem label="Armor Class:" {...infoItemProps}>
{armorClassDisplay}
</InfoItem>
<InfoItem label="Weight:" {...infoItemProps}>
{proficiency && <InfoItem label="Proficient: ">Yes</InfoItem>}
<InfoItem label="Armor Class:">{armorClassDisplay}</InfoItem>
<InfoItem label="Weight:">
{weight ? <NumberDisplay type="weightInLb" number={weight} /> : "--"}
</InfoItem>
<InfoItem label="Cost:" {...infoItemProps}>
<InfoItem label="Cost:">
{cost ? `${FormatUtils.renderLocaleNumber(cost)} gp` : "--"}
</InfoItem>
{isStackable && bundleSize > 1 && (
<InfoItem label="Bundle Size:" {...infoItemProps}>
{bundleSize}
</InfoItem>
)}
{version !== null && (
<InfoItem label="Version:" {...infoItemProps}>
{version}
</InfoItem>
)}
{notes && (
<InfoItem label="Notes:" {...infoItemProps}>
{notes}
</InfoItem>
<InfoItem label="Bundle Size:">{bundleSize}</InfoItem>
)}
{version !== null && <InfoItem label="Version:">{version}</InfoItem>}
{notes && <InfoItem label="Notes:">{notes}</InfoItem>}
{this.getSourceReference(item)}
</div>
);
@@ -958,25 +917,27 @@ export class ItemDetail extends React.PureComponent<Props> {
};
renderInfusionPreview = (): React.ReactNode => {
const {
item,
snippetData,
ruleData,
infusionChoiceLookup,
proficiencyBonus,
} = this.props;
const { item, partyInfo, characterId } = this.props;
const infusion = ItemUtils.getInfusion(item);
if (infusion && snippetData && infusionChoiceLookup) {
if (infusion) {
const itemMappingId = InfusionUtils.getInventoryMappingId(infusion);
const isInfusionOwner = partyInfo
? CampaignUtils.getPartyInventoryRestrictions(partyInfo).some(
(restriction) =>
restriction.type ===
Constants.PartyRestrictionTypeEnum.INFUSION &&
restriction.inventoryMappingId === itemMappingId &&
restriction.characterId === characterId
)
: true;
return (
<div className="ct-item-detail__infusion">
<InfusionPreview
infusion={infusion}
snippetData={snippetData}
ruleData={ruleData}
infusionChoiceLookup={infusionChoiceLookup}
onClick={this.handleInfusionClick}
proficiencyBonus={proficiencyBonus}
onClick={isInfusionOwner ? this.handleInfusionClick : undefined}
/>
</div>
);
@@ -999,6 +960,7 @@ export class ItemDetail extends React.PureComponent<Props> {
onCustomizeClick,
actions,
theme,
onRemoveItem,
} = this.props;
const isCustomized =
@@ -1050,6 +1012,7 @@ export class ItemDetail extends React.PureComponent<Props> {
<ItemDetailActions
onPostRemoveNavigation={onPostRemoveNavigation}
item={item}
onRemoveItem={onRemoveItem}
/>
)}
</div>
@@ -1059,11 +1022,12 @@ export class ItemDetail extends React.PureComponent<Props> {
const ItemDetailContainer = (props) => {
const { inventoryManager } = useContext(InventoryManagerContext);
const { actions } = useCharacterEngine();
const { actions, characterId } = useCharacterEngine();
return (
<ItemDetail
inventoryManager={inventoryManager}
actions={actions}
characterId={characterId}
{...props}
/>
);