Grabbed dndbeyond's source code
``` ~/go/bin/sourcemapper -output ddb -jsurl https://media.dndbeyond.com/character-app/static/js/main.90aa78c5.js ```
This commit is contained in:
@@ -0,0 +1,250 @@
|
||||
import * as React from "react";
|
||||
import {
|
||||
Action,
|
||||
ActionUtils,
|
||||
Attack,
|
||||
CharacterTheme,
|
||||
Item,
|
||||
ItemUtils,
|
||||
Spell,
|
||||
SpellUtils,
|
||||
} from "@dndbeyond/character-rules-engine/es";
|
||||
import { AttackSourceTypeEnum } from "@dndbeyond/character-rules-engine/es/engine/Character";
|
||||
import {
|
||||
DiceTools,
|
||||
RollType,
|
||||
RollRequest,
|
||||
IRollContext,
|
||||
} from "@dndbeyond/dice";
|
||||
import { GameLogContext } from "@dndbeyond/game-log-components";
|
||||
import { DiceComponentUtils } from "../utils";
|
||||
import { NumberDisplay } from "~/components/NumberDisplay";
|
||||
import { DigitalDiceWrapper } from "../Dice";
|
||||
|
||||
interface Props {
|
||||
attack: Attack;
|
||||
className: string;
|
||||
icon: React.ReactNode;
|
||||
name: React.ReactNode;
|
||||
metaItems: Array<string>;
|
||||
rangeValue: React.ReactNode;
|
||||
rangeLabel: React.ReactNode;
|
||||
isProficient: boolean;
|
||||
toHit: number | null;
|
||||
attackSaveValue?: number | null;
|
||||
attackSaveLabel?: React.ReactNode;
|
||||
damage: React.ReactNode;
|
||||
notes?: React.ReactNode;
|
||||
theme: CharacterTheme;
|
||||
rollContext: IRollContext;
|
||||
onClick?: (attack: Attack) => void;
|
||||
|
||||
showNotes: boolean;
|
||||
diceEnabled: boolean;
|
||||
onRoll?: (wasCrit: boolean) => void;
|
||||
}
|
||||
|
||||
class CombatAttack extends React.PureComponent<Props, {}> {
|
||||
static defaultProps = {
|
||||
rangeLabel: "Reach",
|
||||
damage: "--",
|
||||
className: "",
|
||||
showNotes: true,
|
||||
isProficient: false,
|
||||
diceEnabled: false,
|
||||
};
|
||||
|
||||
handleClick = (evt: React.MouseEvent): void => {
|
||||
const { onClick, attack } = this.props;
|
||||
|
||||
if (onClick) {
|
||||
evt.nativeEvent.stopImmediatePropagation();
|
||||
evt.stopPropagation();
|
||||
|
||||
onClick(attack);
|
||||
}
|
||||
};
|
||||
|
||||
handleRollResults = (result: RollRequest) => {
|
||||
const { onRoll } = this.props;
|
||||
|
||||
let wasCrit = DiceComponentUtils.isCriticalRoll(result);
|
||||
if (onRoll) {
|
||||
onRoll(wasCrit);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
icon,
|
||||
name,
|
||||
metaItems,
|
||||
rangeLabel,
|
||||
rangeValue,
|
||||
toHit,
|
||||
attackSaveValue,
|
||||
attackSaveLabel,
|
||||
damage,
|
||||
notes,
|
||||
className,
|
||||
showNotes,
|
||||
diceEnabled,
|
||||
attack,
|
||||
theme,
|
||||
rollContext,
|
||||
} = this.props;
|
||||
|
||||
const [{ messageTargetOptions, defaultMessageTargetOption, userId }] =
|
||||
this.context;
|
||||
|
||||
let actionNode: React.ReactNode = null;
|
||||
if (toHit !== null) {
|
||||
let rollAction: string;
|
||||
switch (attack.type) {
|
||||
case AttackSourceTypeEnum.ACTION:
|
||||
case AttackSourceTypeEnum.CUSTOM:
|
||||
rollAction = ActionUtils.getName(attack.data as Action);
|
||||
break;
|
||||
case AttackSourceTypeEnum.ITEM:
|
||||
rollAction = ItemUtils.getName(attack.data as Item);
|
||||
break;
|
||||
case AttackSourceTypeEnum.SPELL:
|
||||
rollAction = SpellUtils.getName(attack.data as Spell);
|
||||
break;
|
||||
}
|
||||
|
||||
actionNode = (
|
||||
<div className="ddbc-combat-attack__tohit">
|
||||
<DigitalDiceWrapper
|
||||
diceNotation={DiceTools.CustomD20(toHit)}
|
||||
rollType={RollType.ToHit}
|
||||
rollAction={rollAction}
|
||||
diceEnabled={diceEnabled}
|
||||
onRollResults={this.handleRollResults}
|
||||
themeColor={theme.themeColor}
|
||||
rollContext={rollContext}
|
||||
rollTargetOptions={
|
||||
messageTargetOptions
|
||||
? Object.values(messageTargetOptions?.entities)
|
||||
: undefined
|
||||
}
|
||||
rollTargetDefault={defaultMessageTargetOption}
|
||||
userId={userId}
|
||||
>
|
||||
<NumberDisplay
|
||||
number={toHit}
|
||||
type="signed"
|
||||
isModified={false}
|
||||
/>
|
||||
</DigitalDiceWrapper>
|
||||
</div>
|
||||
);
|
||||
} else if (attackSaveValue !== null) {
|
||||
actionNode = (
|
||||
<div className="ddbc-combat-attack__save">
|
||||
<span
|
||||
className={`ddbc-combat-attack__save-value ${
|
||||
theme.isDarkMode
|
||||
? "ddbc-combat-attack__save-value--dark-mode"
|
||||
: ""
|
||||
}`}
|
||||
>
|
||||
{attackSaveValue}
|
||||
</span>
|
||||
<span
|
||||
className={`ddbc-combat-attack__save-label ${
|
||||
theme.isDarkMode
|
||||
? "ddbc-combat-attack__save-label--dark-mode"
|
||||
: ""
|
||||
}`}
|
||||
>
|
||||
{attackSaveLabel}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let classNames: Array<string> = [className, "ddbc-combat-attack"];
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames.join(" ")}
|
||||
style={
|
||||
theme?.isDarkMode
|
||||
? { borderColor: `${theme.themeColor}66` }
|
||||
: undefined
|
||||
}
|
||||
onClick={this.handleClick}
|
||||
>
|
||||
<div className="ddbc-combat-attack__icon">{icon}</div>
|
||||
<div className="ddbc-combat-attack__name">
|
||||
<div
|
||||
className={`ddbc-combat-attack__label ${
|
||||
theme.isDarkMode ? "ddbc-combat-attack__label--dark-mode" : ""
|
||||
}`}
|
||||
>
|
||||
{name}
|
||||
</div>
|
||||
{metaItems.length > 0 && (
|
||||
<div
|
||||
className={`ddbc-combat-attack__meta ${
|
||||
theme.isDarkMode ? "ddbc-combat-attack__meta--dark-mode" : ""
|
||||
}`}
|
||||
>
|
||||
{metaItems.map((metaItem, idx) => (
|
||||
<span className="ddbc-combat-attack__meta-item" key={idx}>
|
||||
{metaItem}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="ddbc-combat-attack__range">
|
||||
<div
|
||||
className={`ddbc-combat-attack__range-value ${
|
||||
theme.isDarkMode
|
||||
? "ddbc-combat-attack__range-value--dark-mode"
|
||||
: ""
|
||||
}`}
|
||||
>
|
||||
{rangeValue ? (
|
||||
rangeValue
|
||||
) : (
|
||||
<span className="ddbc-combat-attack__empty">--</span>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className={`ddbc-combat-attack__range-label ${
|
||||
theme.isDarkMode
|
||||
? "ddbc-combat-attack__range-label--dark-mode"
|
||||
: ""
|
||||
}`}
|
||||
>
|
||||
{rangeLabel}
|
||||
</div>
|
||||
</div>
|
||||
<div className="ddbc-combat-attack__action">
|
||||
{actionNode ? (
|
||||
actionNode
|
||||
) : (
|
||||
<span className="ddbc-combat-attack__empty">--</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="ddbc-combat-attack__damage">{damage}</div>
|
||||
{showNotes && (
|
||||
<div
|
||||
className={`ddbc-combat-attack__notes ${
|
||||
theme.isDarkMode ? "ddbc-combat-attack__notes--dark-mode" : ""
|
||||
}`}
|
||||
>
|
||||
{notes}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
CombatAttack.contextType = GameLogContext;
|
||||
|
||||
export default CombatAttack;
|
||||
Reference in New Issue
Block a user