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:
2025-05-28 11:50:03 -07:00
commit 8df9031d27
3592 changed files with 319051 additions and 0 deletions
@@ -0,0 +1,86 @@
import React from "react";
import { CharacterTheme } from "@dndbeyond/character-rules-engine/es";
import { GD_VehicleBlockProps } from "../../utils/Component";
import VehicleBlockActionStations from "./VehicleBlockActionStations";
import VehicleBlockActionSummaries from "./VehicleBlockActionSummaries";
import VehicleBlockActions from "./VehicleBlockActions";
import VehicleBlockComponents from "./VehicleBlockComponents";
import VehicleBlockFeatures from "./VehicleBlockFeatures";
import VehicleBlockHeader from "./VehicleBlockHeader";
import VehicleBlockPrimary from "./VehicleBlockPrimary";
import VehicleBlockShell from "./VehicleBlockShell";
interface Props extends GD_VehicleBlockProps {
isInteractive: boolean;
onComponentClick?: (componentId: number, vehicleId: number) => void;
onActionStationClick?: (stationId: number, vehicleId: number) => void;
shouldCoalesce: boolean;
theme: CharacterTheme;
}
export default class VehicleBlock extends React.PureComponent<Props, {}> {
static defaultProps = {
isInteractive: false,
shouldCoalesce: true,
featuresProps: null,
actionsProps: null,
actionSummariesProps: null,
actionStationsProps: null,
componentsProps: null,
};
render() {
const {
headerProps,
primaryProps,
actionSummariesProps,
featuresProps,
actionsProps,
actionStationsProps,
componentsProps,
shellProps,
isInteractive,
onComponentClick,
onActionStationClick,
shouldCoalesce,
theme,
} = this.props;
return (
<VehicleBlockShell {...shellProps}>
<VehicleBlockHeader {...headerProps} />
<VehicleBlockPrimary
{...primaryProps}
shouldCoalesce={shouldCoalesce}
/>
{featuresProps !== null && <VehicleBlockFeatures {...featuresProps} />}
{actionSummariesProps !== null && (
<VehicleBlockActionSummaries
{...actionSummariesProps}
theme={theme}
/>
)}
{actionStationsProps !== null && (
<VehicleBlockActionStations
{...actionStationsProps}
theme={theme}
isInteractive={isInteractive}
onActionStationClick={onActionStationClick}
shouldCoalesce={shouldCoalesce}
/>
)}
{componentsProps !== null && (
<VehicleBlockComponents
{...componentsProps}
theme={theme}
isInteractive={isInteractive}
onComponentClick={onComponentClick}
shouldCoalesce={shouldCoalesce}
/>
)}
{actionsProps !== null && <VehicleBlockActions {...actionsProps} />}
</VehicleBlockShell>
);
}
}
@@ -0,0 +1,67 @@
import React from "react";
import { HtmlContent } from "~/components/HtmlContent";
import { VehicleBlockActionProp } from "../../../utils/Component";
import InlineSeparatedNodes from "../../common/InlineSeparatedNodes";
interface Props {
action: VehicleBlockActionProp;
}
export default class VehicleBlockAction extends React.PureComponent<Props> {
render() {
const { action } = this.props;
const { key, name, description, ammo } = action;
const actionText: Array<React.ReactNode> = [];
if (name) {
actionText.push(
<span className="ct-vehicle-block__action-name">{name}.</span>
);
}
if (ammo !== null && ammo.length > 0) {
let ammunitionText: Array<string> = [];
ammo.forEach((ammoContract) => {
let text: string = `${ammoContract.quantity}`;
if (ammoContract.custom !== null) {
text = `${text} ${ammoContract.custom}`;
}
ammunitionText.push(text);
});
actionText.push(
<React.Fragment>
<span className="ct-vehicle-block__action-ammo-label">
Ammunition:{" "}
</span>
<span className="ct-vehicle-block__action-ammo-description">
{ammunitionText.join(", ")}.
</span>
</React.Fragment>
);
}
if (description) {
//All vehicle && component actions descriptions are currently formatted html,
//should use action props derivers to generate the content displayed in this description in the future
actionText.push(
<React.Fragment>
<HtmlContent
className="ct-vehicle-block__action-description"
html={description}
withoutTooltips
/>
</React.Fragment>
);
}
return (
<div className="ct-vehicle-block__action" key={key}>
<InlineSeparatedNodes nodes={actionText} sep={" "} />
</div>
);
}
}
@@ -0,0 +1,4 @@
import VehicleBlockAction from "./VehicleBlockAction";
export default VehicleBlockAction;
export { VehicleBlockAction };
@@ -0,0 +1,191 @@
import React from "react";
import { ManageIcon } from "@dndbeyond/character-components/es";
import {
CharacterTheme,
FormatUtils,
} from "@dndbeyond/character-rules-engine/es";
import { GD_VehicleBlockActionStationProps } from "../../../utils/Component";
import InlineSeparatedNodes from "../../common/InlineSeparatedNodes";
import VehicleBlockAction from "../VehicleBlockAction";
import VehicleBlockPrimaryAttributes from "../VehicleBlockPrimaryAttributes";
interface Props extends GD_VehicleBlockActionStationProps {
className: string;
shouldCoalesce: boolean;
onActionStationClick?: (id: number, vehicleId: number) => void;
isInteractive: boolean;
theme: CharacterTheme;
}
export default class VehicleBlockActionStation extends React.PureComponent<Props> {
static defaultProps = {
className: "",
isInteractive: false,
shouldCoalesce: true,
};
renderHeading = (): React.ReactNode => {
const { name, requiredCrew, coverType, count } = this.props;
let displayText: Array<string> = [];
if (count > 1) {
displayText.push(`${count}`);
}
if (name !== null) {
displayText.push(name);
}
let extraText: Array<string> = [];
if (requiredCrew !== null) {
let requiredCrewText: string = `Requires ${requiredCrew} Crew`;
if (count > 1) {
requiredCrewText = `Each Station ${requiredCrewText}`;
}
extraText.push(requiredCrewText);
}
if (coverType !== null) {
extraText.push(`Grants ${coverType} Cover`);
}
if (extraText.length > 0) {
displayText.push(`(${FormatUtils.renderNonOxfordCommaList(extraText)})`);
}
if (displayText.length === 0) {
return null;
}
return (
<span className="ct-vehicle-block-action-station__heading">
{displayText.join(" ")}.
</span>
);
};
renderActions = (): React.ReactNode => {
const { actions } = this.props;
if (actions.length === 0) {
return null;
}
return (
<span className="ct-vehicle-block-action-station__actions">
{actions.map((action) => (
<VehicleBlockAction action={action} key={action.key} />
))}
</span>
);
};
renderDescription = (): React.ReactNode => {
const { description } = this.props;
if (description === null) {
return null;
}
return (
<span className="ct-vehicle-block-action-station__description">
{description}
</span>
);
};
handleClick = (): void => {
const { id, vehicleId, isInteractive, onActionStationClick } = this.props;
if (isInteractive && onActionStationClick) {
onActionStationClick(id, vehicleId);
}
};
renderAttributes = (): React.ReactNode => {
const {
count,
shouldCoalesce,
armorClassInfo,
hitPointInfo,
speedInfos,
isInteractive,
theme,
costInfos,
displayType,
width,
length,
isPrimaryComponent,
} = this.props;
if (
armorClassInfo === null &&
hitPointInfo === null &&
speedInfos.length === 0
) {
return null;
}
return (
<div className="ct-vehicle-block-action-station__attributes">
{isInteractive && (
<div className="ct-vehicle-block-action-station__attributes-callout">
<ManageIcon
tooltip="Manage HP"
showIconOnEdge={false}
theme={theme}
/>
</div>
)}
<VehicleBlockPrimaryAttributes
armorClassInfo={armorClassInfo}
hitPointInfo={hitPointInfo}
speedInfos={speedInfos}
count={count}
shouldCoalesce={shouldCoalesce}
costInfos={costInfos}
displayType={displayType}
width={width}
length={length}
isPrimaryComponent={isPrimaryComponent}
/>
</div>
);
};
render() {
const { isInteractive } = this.props;
let classNames: Array<string> = ["ct-vehicle-block-action-station"];
if (isInteractive) {
classNames.push("ct-vehicle-block-action-station--interactive");
}
let nodes: Array<React.ReactNode> = [];
const headingNode = this.renderHeading();
if (headingNode !== null) {
nodes.push(headingNode);
}
const descriptionNode = this.renderDescription();
if (descriptionNode !== null) {
nodes.push(descriptionNode);
}
const actionsNode = this.renderActions();
if (actionsNode !== null) {
nodes.push(actionsNode);
}
return (
<div className={classNames.join(" ")} onClick={this.handleClick}>
<div className="ct-vehicle-block-action-station__content">
<InlineSeparatedNodes nodes={nodes} sep={" "} />
{this.renderAttributes()}
</div>
</div>
);
}
}
@@ -0,0 +1,4 @@
import VehicleBlockActionStation from "./VehicleBlockActionStation";
export default VehicleBlockActionStation;
export { VehicleBlockActionStation };
@@ -0,0 +1,90 @@
import { values, sortBy } from "lodash";
import React from "react";
import { CharacterTheme } from "@dndbeyond/character-rules-engine/es";
import {
GD_VehicleBlockActionStationListProps,
GD_VehicleBlockActionStationProps,
} from "../../../utils/Component";
import VehicleBlockActionStation from "../VehicleBlockActionStation";
import VehicleBlockActionStationsShell from "../VehicleBlockActionStationsShell";
interface Props extends GD_VehicleBlockActionStationListProps {
isInteractive: boolean;
shouldCoalesce: boolean;
theme: CharacterTheme;
onActionStationClick?: (stationId: number, vehicleId: number) => void;
}
export default class VehicleBlockActionStations extends React.PureComponent<Props> {
static defaultProps = {
shouldCoalesce: true,
isInteractive: false,
};
getActionStationProps = (): Array<GD_VehicleBlockActionStationProps> => {
const { actionStations, shouldCoalesce } = this.props;
let displayActionStations: Array<GD_VehicleBlockActionStationProps> =
actionStations;
if (shouldCoalesce) {
let uniquenessFactorLookup: Record<
string,
GD_VehicleBlockActionStationProps
> = displayActionStations.reduce(
(acc: Record<string, GD_VehicleBlockActionStationProps>, station) => {
let uniqueness: string = station.uniquenessFactor;
if (!acc[uniqueness]) {
acc[uniqueness] = station;
} else {
acc[uniqueness].count += 1;
}
return acc;
},
{}
);
displayActionStations = values(uniquenessFactorLookup);
}
return sortBy(
displayActionStations,
(actionStation) => actionStation.displayOrder
);
};
render() {
const {
actionStations,
shouldCoalesce,
isInteractive,
onActionStationClick,
theme,
} = this.props;
if (actionStations.length === 0) {
return null;
}
const actionStationsProps = this.getActionStationProps();
return (
<VehicleBlockActionStationsShell>
{actionStationsProps.map((stationProps) => (
<VehicleBlockActionStation
{...stationProps}
theme={theme}
isInteractive={
stationProps.enableComponentManagement && isInteractive
}
onActionStationClick={onActionStationClick}
shouldCoalesce={shouldCoalesce}
/>
))}
</VehicleBlockActionStationsShell>
);
}
}
@@ -0,0 +1,4 @@
import VehicleBlockActionStations from "./VehicleBlockActionStations";
export default VehicleBlockActionStations;
export { VehicleBlockActionStations };
@@ -0,0 +1,14 @@
import React from "react";
import VehicleBlockSectionHeader from "../VehicleBlockSectionHeader";
export default class VehicleBlockActionStationsShell extends React.PureComponent<{}> {
render() {
return (
<div className="ct-vehicle-block__action-stations">
<VehicleBlockSectionHeader label="Action Stations" />
{this.props.children}
</div>
);
}
}
@@ -0,0 +1,4 @@
import VehicleBlockActionStationsShell from "./VehicleBlockActionStationsShell";
export default VehicleBlockActionStationsShell;
export { VehicleBlockActionStationsShell };
@@ -0,0 +1,45 @@
import React from "react";
import { CharacterTheme } from "@dndbeyond/character-rules-engine/es";
import { GD_VehicleBlockActionSummariesProps } from "../../../utils/Component";
import VehicleBlockActionSummary from "../VehicleBlockActionSummary";
import VehicleBlockSectionHeader from "../VehicleBlockSectionHeader";
interface Props extends GD_VehicleBlockActionSummariesProps {
theme: CharacterTheme;
}
export default class VehicleBlockActionSummaries extends React.PureComponent<Props> {
render() {
const { actionsText, actionsSummaries, theme } = this.props;
if (actionsText === null && actionsSummaries.length === 0) {
return null;
}
return (
<div className="ct-vehicle-block__action-summaries">
<VehicleBlockSectionHeader label="Actions" />
{actionsText !== null && (
<div className="ct-vehicle-block__action-summaries-content">
{actionsText}
</div>
)}
{actionsSummaries.length > 0 && (
<div className="ct-vehicle-block__action-summaries-content">
{actionsSummaries.map((summary, idx) => {
const key = `${summary.name !== null ? summary.name : ""}-${idx}`;
return (
<VehicleBlockActionSummary
theme={theme}
actionSummary={summary}
key={key}
/>
);
})}
</div>
)}
</div>
);
}
}
@@ -0,0 +1,4 @@
import VehicleBlockActionSummaries from "./VehicleBlockActionSummaries";
export default VehicleBlockActionSummaries;
export { VehicleBlockActionSummaries };
@@ -0,0 +1,63 @@
import React from "react";
import { CharacterTheme } from "@dndbeyond/character-rules-engine/es";
import { Reference } from "~/components/Reference";
import { VehicleActionSummaryProps } from "../../../utils/Component";
import InlineSeparatedNodes from "../../common/InlineSeparatedNodes";
interface Props {
actionSummary: VehicleActionSummaryProps;
theme: CharacterTheme;
}
export default class VehicleBlockActionSummary extends React.PureComponent<Props> {
render() {
const {
name,
description,
sourceId,
sourceName,
sourceFullName,
sourceChapterNumber,
} = this.props.actionSummary;
const { theme } = this.props;
let nodes: Array<React.ReactNode> = [];
if (name !== null) {
nodes.push(
<span className="ct-vehicle-block__action-summary-label">{name}.</span>
);
}
if (description !== null) {
nodes.push(
<span className="ct-vehicle-block__action-summary-text">
{description}
</span>
);
}
if (sourceId !== null) {
nodes.push(
<React.Fragment>
{"("}
<Reference
isDarkMode={theme?.isDarkMode}
name={sourceName}
tooltip={sourceFullName}
chapter={sourceChapterNumber}
/>
{")."}
</React.Fragment>
);
}
return (
<div className="ct-vehicle-block__action-summary">
<InlineSeparatedNodes nodes={nodes} sep={" "} />
</div>
);
}
}
@@ -0,0 +1,4 @@
import VehicleBlockActionSummary from "./VehicleBlockActionSummary";
export default VehicleBlockActionSummary;
export { VehicleBlockActionSummary };
@@ -0,0 +1,55 @@
import React from "react";
import { GD_VehicleBlockActionsProps } from "../../../utils/Component";
import VehicleBlockAction from "../VehicleBlockAction";
import VehicleBlockSectionHeader from "../VehicleBlockSectionHeader";
interface Props extends GD_VehicleBlockActionsProps {}
export default class VehicleBlockActions extends React.PureComponent<Props> {
static defaultProps = {
reactions: [],
bonusActions: [],
special: [],
};
render() {
const { reactions, bonusActions, special } = this.props;
if (
reactions.length === 0 &&
bonusActions.length === 0 &&
special.length === 0
) {
return null;
}
return (
<React.Fragment>
{reactions.length > 0 && (
<React.Fragment>
<VehicleBlockSectionHeader label="Reactions" />
{reactions.map((action) => (
<VehicleBlockAction action={action} key={action.key} />
))}
</React.Fragment>
)}
{bonusActions.length > 0 && (
<React.Fragment>
<VehicleBlockSectionHeader label="Bonus Actions" />
{bonusActions.map((action) => (
<VehicleBlockAction action={action} key={action.key} />
))}
</React.Fragment>
)}
{special.length > 0 && (
<React.Fragment>
<VehicleBlockSectionHeader label="Special Actions" />
{special.map((action) => (
<VehicleBlockAction action={action} key={action.key} />
))}
</React.Fragment>
)}
</React.Fragment>
);
}
}
@@ -0,0 +1,4 @@
import VehicleBlockActions from "./VehicleBlockActions";
export default VehicleBlockActions;
export { VehicleBlockActions };
@@ -0,0 +1,32 @@
import React from "react";
interface Props {
label: string;
value: React.ReactNode;
extraValue: React.ReactNode;
}
export default class VehicleBlockAttribute extends React.PureComponent<Props> {
static defaultProps = {
extraValue: null,
};
render() {
const { label, value, extraValue } = this.props;
return (
<div className="ct-vehicle-block__attribute">
<span className="ct-vehicle-block__attribute-label">{label}</span>
<span className="ct-vehicle-block__attribute-data">
<span className="ct-vehicle-block__attribute-data-value">
{value}
</span>
{extraValue !== null && (
<span className="ct-vehicle-block__attribute-data-extra">
{extraValue}
</span>
)}
</span>
</div>
);
}
}
@@ -0,0 +1,188 @@
import React from "react";
import { ManageIcon } from "@dndbeyond/character-components/es";
import {
CharacterTheme,
Constants,
FormatUtils,
} from "@dndbeyond/character-rules-engine/es";
import { HtmlContent } from "~/components/HtmlContent";
import { GD_VehicleBlockComponentProps } from "../../../utils/Component";
import VehicleBlockAction from "../VehicleBlockAction";
import VehicleBlockPrimaryAttributes from "../VehicleBlockPrimaryAttributes";
import VehicleBlockSectionHeader from "../VehicleBlockSectionHeader";
interface Props extends GD_VehicleBlockComponentProps {
className: string;
shouldCoalesce: boolean;
onComponentClick?: (id: number, vehicleId: number) => void;
isInteractive: boolean;
theme: CharacterTheme;
}
export default class VehicleBlockComponent extends React.PureComponent<Props> {
static defaultProps = {
className: "",
isInteractive: false,
shouldCoalesce: true,
};
renderHeading = (): React.ReactNode => {
const {
typeNames,
name,
count,
isInteractive,
theme,
displayType,
requiredCrew,
} = this.props;
const nameLabel: string = name !== null ? name : "";
const isSpelljammer =
displayType === Constants.VehicleConfigurationDisplayTypeEnum.SPELLJAMMER;
let componentCount: string = "";
if (count > 1) {
componentCount = `(${count})`;
}
let primaryText: string = "";
if (name !== null && typeNames.includes(name)) {
primaryText = nameLabel;
} else if (isSpelljammer) {
primaryText = `${count > 1 ? `${count} ` : ""}${nameLabel}${
requiredCrew ? ` (${requiredCrew} Crew${count > 1 ? " Each" : ""})` : ""
}`;
} else {
let typesText = FormatUtils.renderNonOxfordCommaList(typeNames);
primaryText = `${typesText}: ${nameLabel} ${componentCount}`;
}
let callout: React.ReactNode = null;
if (isInteractive) {
callout = (
<ManageIcon tooltip="Manage HP" showIconOnEdge={false} theme={theme} />
);
}
return <VehicleBlockSectionHeader label={primaryText} callout={callout} />;
};
renderActions = (): React.ReactNode => {
const { actions } = this.props;
if (actions.length === 0) {
return null;
}
return (
<div className="ct-vehicle-block-component__actions">
{actions.map((action) => (
<VehicleBlockAction action={action} key={action.key} />
))}
</div>
);
};
renderDescription = (): React.ReactNode => {
const { description } = this.props;
if (description === null) {
return null;
}
return (
<HtmlContent
className="ct-vehicle-block-component__description"
html={description}
withoutTooltips
/>
);
};
handleClick = (): void => {
const { id, vehicleId, isInteractive, onComponentClick } = this.props;
if (isInteractive && onComponentClick) {
onComponentClick(id, vehicleId);
}
};
renderAttributes = (): React.ReactNode => {
const {
count,
costInfos,
shouldCoalesce,
armorClassInfo,
hitPointInfo,
speedInfos,
coverType,
requiredCrew,
displayType,
width,
length,
isPrimaryComponent,
} = this.props;
return (
<div className="ct-vehicle-block-component__attributes">
<VehicleBlockPrimaryAttributes
armorClassInfo={armorClassInfo}
hitPointInfo={hitPointInfo}
speedInfos={speedInfos}
coverType={coverType}
requiredCrew={requiredCrew}
count={count}
shouldCoalesce={shouldCoalesce}
costInfos={costInfos}
displayType={displayType}
width={width}
length={length}
isPrimaryComponent={isPrimaryComponent}
/>
</div>
);
};
renderActionsAndDescription() {
const { displayType } = this.props;
const isSpelljammer =
displayType === Constants.VehicleConfigurationDisplayTypeEnum.SPELLJAMMER;
return isSpelljammer ? (
<>
{this.renderDescription()}
{this.renderActions()}
</>
) : (
<>
{this.renderActions()}
{this.renderDescription()}
</>
);
}
render() {
const { isInteractive, uniqueKey } = this.props;
let classNames: Array<string> = ["ct-vehicle-block-component"];
if (isInteractive) {
classNames.push("ct-vehicle-block-component--interactive");
}
return (
<div
key={uniqueKey}
className={classNames.join(" ")}
onClick={this.handleClick}
>
{this.renderHeading()}
<div className="ct-vehicle-block-component__content">
{this.renderAttributes()}
{this.renderActionsAndDescription()}
</div>
</div>
);
}
}
@@ -0,0 +1,4 @@
import VehicleBlockComponent from "./VehicleBlockComponent";
export default VehicleBlockComponent;
export { VehicleBlockComponent };
@@ -0,0 +1,94 @@
import { values, sortBy, has } from "lodash";
import React from "react";
import {
CharacterTheme,
Constants,
} from "@dndbeyond/character-rules-engine/es";
import {
GD_VehicleBlockComponentListProps,
GD_VehicleBlockComponentProps,
} from "../../../utils/Component";
import VehicleBlockComponent from "../VehicleBlockComponent";
import VehicleBlockComponentsShell from "../VehicleBlockComponentsShell";
interface Props extends GD_VehicleBlockComponentListProps {
shouldCoalesce: boolean;
isInteractive: boolean;
theme: CharacterTheme;
onComponentClick?: (componentId: number, vehicleId: number) => void;
}
export default class VehicleBlockComponents extends React.PureComponent<Props> {
static defaultProps = {
shouldCoalesce: true,
};
getComponentInfos = (): Array<GD_VehicleBlockComponentProps> => {
const { components, shouldCoalesce } = this.props;
let displayComponents: Array<GD_VehicleBlockComponentProps> = components;
if (shouldCoalesce) {
const uniquenessFactorLookup: Record<
string,
GD_VehicleBlockComponentProps
> = components.reduce(
(acc: Record<string, GD_VehicleBlockComponentProps>, component) => {
let uniqueness = component.uniquenessFactor;
if (!acc[uniqueness]) {
acc[uniqueness] = component;
} else {
acc[uniqueness].count += 1;
}
return acc;
},
{}
);
displayComponents = values(uniquenessFactorLookup);
}
return sortBy(displayComponents, (component) => component.displayOrder);
};
render() {
const { shouldCoalesce, isInteractive, onComponentClick, theme } =
this.props;
let components = this.getComponentInfos();
if (components.length === 0) {
return null;
}
// Components only not action stations (Currently due to Spelljammer)
// Filters out helm (primary component) from Spelljammer
components = components.filter(
(component) =>
!(
component.displayType ===
Constants.VehicleConfigurationDisplayTypeEnum.SPELLJAMMER &&
component.isPrimaryComponent
)
);
return (
<VehicleBlockComponentsShell>
{components.map((componentProps) => (
<VehicleBlockComponent
{...componentProps}
theme={theme}
isInteractive={
componentProps.enableComponentManagement && isInteractive
}
onComponentClick={onComponentClick}
shouldCoalesce={shouldCoalesce}
/>
))}
</VehicleBlockComponentsShell>
);
}
}
@@ -0,0 +1,4 @@
import VehicleBlockComponents from "./VehicleBlockComponents";
export default VehicleBlockComponents;
export { VehicleBlockComponents };
@@ -0,0 +1,11 @@
import React from "react";
export default class VehicleBlockComponentsShell extends React.PureComponent {
render() {
return (
<div className="ct-vehicle-block__components-block">
{this.props.children}
</div>
);
}
}
@@ -0,0 +1,4 @@
import VehicleBlockComponentsShell from "./VehicleBlockComponentsShell";
export default VehicleBlockComponentsShell;
export { VehicleBlockComponentsShell };
@@ -0,0 +1,38 @@
import React from "react";
import { GD_VehicleBlockFeaturesProps } from "../../../utils/Component";
interface Props extends GD_VehicleBlockFeaturesProps {}
export default class VehicleBlockFeatures extends React.PureComponent<Props> {
static defaultProps = {
features: [],
};
render() {
const { features } = this.props;
if (features.length === 0) {
return null;
}
return (
<div className="ct-vehicle-block__features">
{features.map((feature, idx) => {
return (
<div
className="ct-vehicle-block__features-feature"
key={`${feature.name}-${idx}`}
>
<span className="ct-vehicle-block__features-feature-name">
{feature.name}.
</span>{" "}
<span className="ct-vehicle-block__features-feature-description">
{feature.description}
</span>
</div>
);
})}
</div>
);
}
}
@@ -0,0 +1,4 @@
import VehicleBlockFeatures from "./VehicleBlockFeatures";
export default VehicleBlockFeatures;
export { VehicleBlockFeatures };
@@ -0,0 +1,83 @@
import React from "react";
import { Constants, FormatUtils } from "@dndbeyond/character-rules-engine/es";
import { GD_VehicleBlockHeaderProps } from "../../../utils/Component";
import InlineSeparatedNodes from "../../common/InlineSeparatedNodes";
interface Props extends GD_VehicleBlockHeaderProps {}
export default class VehicleBlockHeader extends React.PureComponent<Props> {
renderTypeInfo = (): React.ReactNode => {
const { sizeName, typeName } = this.props;
if (sizeName === null && typeName === null) {
return null;
}
let size: string = sizeName !== null ? sizeName : "";
let type: string = typeName !== null ? typeName.toLowerCase() : "";
return [size, type].join(" ").trim();
};
renderDimensions = (): React.ReactNode => {
const { weight, width, length } = this.props;
let dimensions: Array<string> = [];
let lengthDisplay: string | null =
length !== null ? FormatUtils.renderDistance(length) : null;
if (lengthDisplay !== null) {
dimensions.push(lengthDisplay);
}
let widthDisplay: string | null =
width !== null ? FormatUtils.renderDistance(width) : null;
if (widthDisplay !== null) {
dimensions.push(widthDisplay);
}
let metaTextSizeNodes: Array<React.ReactNode> = [];
if (dimensions.length > 0) {
metaTextSizeNodes.push(dimensions.join(" by "));
}
let weightDisplay: string | null =
weight !== null ? `${FormatUtils.renderWeight(weight)}.` : null;
if (weightDisplay !== null) {
metaTextSizeNodes.push(weightDisplay);
}
if (metaTextSizeNodes.length === 0) {
return null;
}
return (
<React.Fragment>
(<InlineSeparatedNodes nodes={metaTextSizeNodes} />)
</React.Fragment>
);
};
render() {
const { name, displayType } = this.props;
const typeInfo = this.renderTypeInfo();
const dimensions = this.renderDimensions();
return (
<div className="ct-vehicle-block__header">
<div className="ct-vehicle-block__name">
{name !== null ? name : ""}
</div>
{displayType !==
Constants.VehicleConfigurationDisplayTypeEnum.SPELLJAMMER && (
<div className="ct-vehicle-block__meta">
{typeInfo} {dimensions}
</div>
)}
</div>
);
}
}
@@ -0,0 +1,4 @@
import VehicleBlockHeader from "./VehicleBlockHeader";
export default VehicleBlockHeader;
export { VehicleBlockHeader };
@@ -0,0 +1,210 @@
import React from "react";
import {
Constants,
CoreUtils,
FormatUtils,
} from "@dndbeyond/character-rules-engine/es";
import { GD_VehicleBlockPrimaryProps } from "../../../utils/Component";
import { VehicleBlockAttribute } from "../VehicleBlockAttribute";
import VehicleBlockPrimaryAttributes from "../VehicleBlockPrimaryAttributes";
import VehicleBlockSeparator from "../VehicleBlockSeparator";
interface Props extends GD_VehicleBlockPrimaryProps {
shouldCoalesce: boolean;
}
export default class VehicleBlockPrimary extends React.PureComponent<Props> {
renderDamageImmunities = (): React.ReactNode => {
const { damageImmunities } = this.props;
if (damageImmunities.length === 0) {
return null;
}
return (
<div className="ct-vehicle-block__tidbit">
<span className="ct-vehicle-block__tidbit-label">
Damage Immunities
</span>
<span className="ct-vehicle-block__tidbit-data">
{damageImmunities.join(", ")}
</span>
</div>
);
};
renderConditionImmunities = (): React.ReactNode => {
const { conditionImmunities } = this.props;
if (conditionImmunities.length === 0) {
return null;
}
return (
<div className="ct-vehicle-block__tidbit">
<span className="ct-vehicle-block__tidbit-label">
Condition Immunities
</span>
<span className="ct-vehicle-block__tidbit-data">
{conditionImmunities.join(", ")}
</span>
</div>
);
};
renderStats = (): React.ReactNode => {
const { stats, displayType } = this.props;
if (stats.length === 0) {
return null;
}
return (
<>
<div className="ct-vehicle-block__abilities">
{stats.map((stat) => {
let modifier = stat.modifier !== null ? stat.modifier : 0;
let statKey = stat.statKey !== null ? stat.statKey : "";
let score = stat.score !== null ? stat.score : 0;
return (
<div
className={`ct-vehicle-block__ability-stat ct-vehicle-block__ability-stat--${statKey.toLowerCase()}`}
key={statKey}
>
<div className="ct-vehicle-block__ability-heading">
{statKey}
</div>
<div className="ct-vehicle-block__ability-data">
<span className="ct-vehicle-block__ability-score">
{score}
</span>
{score !== 0 && (
<span className="ct-vehicle-block__ability-modifier">
({FormatUtils.renderSignedNumber(modifier)})
</span>
)}
</div>
</div>
);
})}
</div>
<VehicleBlockSeparator displayType={displayType} />
</>
);
};
renderEffectiveTravelDistance = (): React.ReactNode => {
const { travelPaceInfo } = this.props;
if (travelPaceInfo === null || travelPaceInfo.effectiveHours === null) {
return null;
}
const effectiveDistance: number =
CoreUtils.convertFeetToMiles(travelPaceInfo.pace) *
travelPaceInfo.effectiveHours;
return `(${effectiveDistance} ${
effectiveDistance === 1 ? "mile" : "miles"
} per day)`;
};
renderAttributes = (): React.ReactNode => {
const {
creatureCapacityDescriptions,
cargoCapacityInfo,
travelPaceInfo,
primaryProperties,
shouldCoalesce,
displayType,
} = this.props;
let cargoCapacity: Array<string> = [];
if (cargoCapacityInfo.weight !== null) {
let weight: string =
cargoCapacityInfo.weight >= Constants.POUNDS_IN_TON
? FormatUtils.renderWeightTons(cargoCapacityInfo.weight)
: `${FormatUtils.renderWeight(cargoCapacityInfo.weight)}.`;
cargoCapacity.push(weight);
}
if (cargoCapacityInfo.description) {
cargoCapacity.push(cargoCapacityInfo.description);
}
const isSpelljammer =
displayType === Constants.VehicleConfigurationDisplayTypeEnum.SPELLJAMMER;
return (
<>
<div className="ct-vehicle-block__attributes">
{primaryProperties !== null && (
<VehicleBlockPrimaryAttributes
armorClassInfo={primaryProperties.armorClassInfo}
hitPointInfo={primaryProperties.hitPointInfo}
speedInfos={primaryProperties.speedInfos}
costInfos={primaryProperties.costInfos}
shouldCoalesce={shouldCoalesce}
displayType={primaryProperties.displayType}
width={primaryProperties.width}
length={primaryProperties.length}
isPrimaryComponent={primaryProperties.isPrimaryComponent}
/>
)}
{travelPaceInfo !== null && (
<VehicleBlockAttribute
label="Travel Pace"
value={`${FormatUtils.renderDistance(
travelPaceInfo.pace
)} per hour`}
extraValue={this.renderEffectiveTravelDistance()}
/>
)}
<VehicleBlockAttribute
label={isSpelljammer ? "Crew" : "Creature Capacity"}
value={creatureCapacityDescriptions.join(", ")}
/>
{cargoCapacity.length > 0 && (
<VehicleBlockAttribute
label={isSpelljammer ? "Cargo" : "Cargo Capacity"}
value={cargoCapacity.join(", ")}
/>
)}
</div>
{!isSpelljammer && <VehicleBlockSeparator displayType={displayType} />}
</>
);
};
renderConditionsAndImmunities() {
const { displayType, conditionImmunities, damageImmunities } = this.props;
return conditionImmunities.length === 0 &&
damageImmunities.length === 0 ? null : (
<>
<div className="ct-vehicle-block__tidbits">
{this.renderDamageImmunities()}
{this.renderConditionImmunities()}
</div>
<VehicleBlockSeparator displayType={displayType} />
</>
);
}
render() {
const { displayType } = this.props;
return (
<div className="ct-vehicle-block__primary">
<VehicleBlockSeparator displayType={displayType} />
{this.renderAttributes()}
{this.renderStats()}
{this.renderConditionsAndImmunities()}
</div>
);
}
}
@@ -0,0 +1,4 @@
import VehicleBlockPrimary from "./VehicleBlockPrimary";
export default VehicleBlockPrimary;
export { VehicleBlockPrimary };
@@ -0,0 +1,281 @@
import { orderBy } from "lodash";
import React from "react";
import {
Constants,
FormatUtils,
VehicleComponentSpeedModeInfo,
} from "@dndbeyond/character-rules-engine/es";
import { GD_VehicleBlockPrimaryComponentProps } from "../../../utils/Component";
import InlineSeparatedNodes from "../../common/InlineSeparatedNodes";
import { VehicleBlockAttribute } from "../VehicleBlockAttribute";
interface Props extends GD_VehicleBlockPrimaryComponentProps {
shouldCoalesce: boolean;
coverType: string | null;
requiredCrew?: number | null;
count: number;
}
export default class VehicleBlockPrimaryAttributes extends React.PureComponent<Props> {
//this class renders on VehicleBlock and VehicleComponent and VehicleActionStation
static defaultProps = {
shouldCoalesce: true,
coverType: null,
requiredCrew: null,
count: 1,
};
renderHitPointInfo = (): React.ReactNode => {
const { hitPointInfo, count, shouldCoalesce } = this.props;
if (hitPointInfo === null) {
return null;
}
if (hitPointInfo.totalHp === null) {
return "--";
}
let currentHpNode: React.ReactNode = null;
if (shouldCoalesce) {
currentHpNode = hitPointInfo.totalHp;
if (count > 1) {
currentHpNode = `${hitPointInfo.totalHp} each`;
}
} else {
let classNames: Array<string> = ["ct-vehicle-block-component__hp-value"];
const remainingHp: number =
hitPointInfo.remainingHp !== null
? hitPointInfo.remainingHp
: hitPointInfo.totalHp;
if (remainingHp < hitPointInfo.totalHp) {
classNames.push("ct-vehicle-block-component__hp-value--is-damaged");
}
let showFullHitPointInfo: boolean = !shouldCoalesce;
currentHpNode = (
<React.Fragment>
{showFullHitPointInfo && (
<React.Fragment>
<span className={classNames.join(" ")}>{remainingHp}</span>
<span className="ct-vehicle-block-component__hp-sep">/</span>
</React.Fragment>
)}
<span className={classNames.join(" ")}>{hitPointInfo.totalHp}</span>
</React.Fragment>
);
}
let displayNodes: Array<React.ReactNode> = [currentHpNode];
if (hitPointInfo.hitPointSpeedAdjustments.length > 0) {
hitPointInfo.hitPointSpeedAdjustments.forEach((adjustment) => {
let speed: string = FormatUtils.renderDistance(
adjustment.perDamageValue
);
displayNodes.push(
`${speed} speed per ${adjustment.perDamageTaken} damage taken`
);
});
}
return <InlineSeparatedNodes nodes={displayNodes} sep={"; "} />;
};
renderHitPointThresholdInfo = (): React.ReactNode => {
const { hitPointInfo } = this.props;
if (hitPointInfo === null) {
return null;
}
let displayStrings: Array<string> = [];
if (hitPointInfo.damageThreshold) {
displayStrings.push(`damage threshold ${hitPointInfo.damageThreshold}`);
}
if (hitPointInfo.mishapThreshold) {
displayStrings.push(`mishap threshold ${hitPointInfo.mishapThreshold}`);
}
if (displayStrings.length === 0) {
return null;
}
return `(${displayStrings.join(", ")})`;
};
renderSpeedInfo = (
modes: Array<VehicleComponentSpeedModeInfo>
): React.ReactNode => {
let speedDisplayStrings: Array<string> = modes.map((mode) => {
const { value, description, restrictionsText, movementInfo } = mode;
let stringParts: Array<string> = [];
if (movementInfo) {
stringParts.push(`${movementInfo.description} speed`);
}
stringParts.push(FormatUtils.renderDistance(value));
if (description) {
stringParts.push(description);
}
if (restrictionsText) {
stringParts.push(`(${restrictionsText})`);
}
return stringParts.join(" ");
});
return <InlineSeparatedNodes nodes={speedDisplayStrings} sep={"; "} />;
};
renderArmorClassAttribute = (): React.ReactNode => {
const { armorClassInfo } = this.props;
if (armorClassInfo === null) {
return null;
}
const { moving, base, description } = armorClassInfo;
let armorClassValue: number | null = base;
let motionlessArmorClassValue: string | null = null;
if (moving !== null) {
armorClassValue = moving;
if (moving !== base) {
motionlessArmorClassValue = `(${base} while motionless)`;
}
}
return (
<VehicleBlockAttribute
label="Armor Class"
value={
description
? `${armorClassValue} (${description})`
: armorClassValue === 0
? "--"
: armorClassValue
}
extraValue={motionlessArmorClassValue}
/>
);
};
render() {
const {
armorClassInfo,
hitPointInfo,
speedInfos,
coverType,
requiredCrew,
costInfos,
displayType,
isPrimaryComponent,
width,
length,
} = this.props;
//sort cost infos by type component then ammunition
// const costInfos: Array<VehicleComponentCostContract> = [
// {
// description: 'cannon',
// type: 'component',
// value: null,
// },
// {
// description: 'giant iron ball',
// type: 'ammunition',
// value: 1000,
// },
// ];
const sortedCosts = orderBy(
costInfos,
[
(cost) => cost.type === Constants.ComponentCostTypeEnum.COMPONENT,
(cost) => cost.type === Constants.ComponentCostTypeEnum.AMMUNITION,
],
"desc"
);
const costValues: Array<string> = sortedCosts.map((cost) => {
const value: string = cost.value ? `${cost.value} gp` : "--";
const description: string = cost.description
? `(${cost.description})`
: "";
return [value, description].join(" ");
});
const isSpelljammer =
displayType === Constants.VehicleConfigurationDisplayTypeEnum.SPELLJAMMER;
return (
<React.Fragment>
{armorClassInfo !== null && this.renderArmorClassAttribute()}
{hitPointInfo !== null && (
<VehicleBlockAttribute
label="Hit Points"
value={this.renderHitPointInfo()}
extraValue={
!isSpelljammer ? this.renderHitPointThresholdInfo() : undefined
}
/>
)}
{isSpelljammer && !!hitPointInfo?.damageThreshold && (
<VehicleBlockAttribute
label="Damage Threshold"
value={hitPointInfo?.damageThreshold}
/>
)}
{speedInfos.map((speedInfo, idx) => {
let speedAttributeLabel: string = "Speed";
if (speedInfo.type !== null && !isSpelljammer) {
speedAttributeLabel = `Speed (${speedInfo.type})`;
}
let modes: Array<VehicleComponentSpeedModeInfo> =
speedInfo.modes !== null ? speedInfo.modes : [];
return (
<VehicleBlockAttribute
key={`${speedInfo.type}-${idx}`}
label={speedAttributeLabel}
value={this.renderSpeedInfo(modes)}
/>
);
})}
{coverType !== null && (
<VehicleBlockAttribute
label="Cover"
value={`Grants ${coverType} Cover`}
/>
)}
{requiredCrew !== null && !isSpelljammer && (
<VehicleBlockAttribute label="Required Crew" value={requiredCrew} />
)}
{isPrimaryComponent &&
isSpelljammer &&
length !== null &&
width !== null && (
<VehicleBlockAttribute
label="Keel/Beam"
value={`${FormatUtils.renderDistance(
length
)} / ${FormatUtils.renderDistance(width)}`}
/>
)}
{costValues.length > 0 && (
<VehicleBlockAttribute label="Cost" value={costValues.join(", ")} />
)}
</React.Fragment>
);
}
}
@@ -0,0 +1,4 @@
import VehicleBlockPrimaryAttributes from "./VehicleBlockPrimaryAttributes";
export default VehicleBlockPrimaryAttributes;
export { VehicleBlockPrimaryAttributes };
@@ -0,0 +1,30 @@
import React from "react";
interface Props {
label: React.ReactNode;
callout: React.ReactNode;
}
export default class VehicleBlockSectionHeader extends React.PureComponent<Props> {
static defaultProps = {
callout: null,
};
render() {
const { label, callout } = this.props;
return (
<div className="ct-vehicle-block__section-header">
<div className="ct-vehicle-block__section-header-content">
<div className="ct-vehicle-block__section-header-content-primary">
{label}
</div>
{callout !== null && (
<div className="ct-vehicle-block__section-header-content-callout">
{callout}
</div>
)}
</div>
</div>
);
}
}
@@ -0,0 +1,4 @@
import VehicleBlockSectionHeader from "./VehicleBlockSectionHeader";
export default VehicleBlockSectionHeader;
export { VehicleBlockSectionHeader };
@@ -0,0 +1,35 @@
import React from "react";
import { Constants } from "@dndbeyond/character-rules-engine/es";
import { VehicleDisplayTypeComponentLookup } from "../../../utils/Component";
import VehicleBlockSeparatorInfernal from "../VehicleBlockSeparatorInfernal";
import VehicleBlockSeparatorShip from "../VehicleBlockSeparatorShip";
interface Props {
displayType: Constants.VehicleConfigurationDisplayTypeEnum;
}
export default class VehicleBlockSeparator extends React.PureComponent<Props> {
render() {
const { displayType } = this.props;
let lookup: VehicleDisplayTypeComponentLookup = {
[Constants.VehicleConfigurationDisplayTypeEnum.SHIP]:
VehicleBlockSeparatorShip,
[Constants.VehicleConfigurationDisplayTypeEnum.INFERNAL_WAR_MACHINE]:
VehicleBlockSeparatorInfernal,
[Constants.VehicleConfigurationDisplayTypeEnum.SPELLJAMMER]:
VehicleBlockSeparatorShip,
};
//sets Ship style as default
let SeparatorComponent: React.ComponentType<any> =
VehicleBlockSeparatorShip;
if (lookup.hasOwnProperty(displayType)) {
SeparatorComponent = lookup[displayType];
}
return <SeparatorComponent className="ct-vehicle-block__separator" />;
}
}
@@ -0,0 +1,4 @@
import VehicleBlockSeparator from "./VehicleBlockSeparator";
export default VehicleBlockSeparator;
export { VehicleBlockSeparator };
@@ -0,0 +1,32 @@
import React from "react";
import { BaseSvg } from "@dndbeyond/character-components/es";
interface Props {
className: string;
fillColor: string;
}
export default class VehicleBlockSeparatorInfernal extends React.PureComponent<Props> {
static defaultProps = {
className: "",
fillColor: "#9D3856",
};
render() {
const { className, fillColor } = this.props;
let classNames: Array<string> = [
"ct-vehicle-block__separator-infernal",
className,
];
return (
<BaseSvg viewBox="0 0 1619.42 32.05" className={classNames.join(" ")}>
<polygon
fill={fillColor}
points="998.33,16.03 1619.42,25.35 809.71,32.05 0,25.35 621.09,16.03 0,12.45 809.71,0 1619.42,12.45 "
/>
</BaseSvg>
);
}
}
@@ -0,0 +1,4 @@
import VehicleBlockSeparatorInfernal from "./VehicleBlockSeparatorInfernal";
export default VehicleBlockSeparatorInfernal;
export { VehicleBlockSeparatorInfernal };
@@ -0,0 +1,25 @@
import React from "react";
interface Props {
className: string;
}
export default class VehicleBlockSeparatorShip extends React.PureComponent<Props> {
static defaultProps = {
className: "",
};
render() {
const { className } = this.props;
let classNames: Array<string> = [
"ct-vehicle-block__separator-ship",
className,
];
return (
<div className={classNames.join(" ")}>
<div className="ct-vehicle-block__separator-ship-border" />
</div>
);
}
}
@@ -0,0 +1,4 @@
import VehicleBlockSeparatorShip from "./VehicleBlockSeparatorShip";
export default VehicleBlockSeparatorShip;
export { VehicleBlockSeparatorShip };
@@ -0,0 +1,38 @@
import React from "react";
import { Constants } from "@dndbeyond/character-rules-engine/es";
import { GD_VehicleBlockShellProps } from "../../../utils/Component";
import VehicleBlockShellCap from "../VehicleBlockShellCap";
interface Props extends GD_VehicleBlockShellProps {}
export default class VehicleBlockShell extends React.PureComponent<Props> {
render() {
const { children, displayType } = this.props;
let type: string | null = null;
switch (displayType) {
case Constants.VehicleConfigurationDisplayTypeEnum.INFERNAL_WAR_MACHINE:
type = "infernal";
break;
case Constants.VehicleConfigurationDisplayTypeEnum.SHIP:
default:
type = "ship";
break;
}
return (
<div className="ct-vehicle-block">
<VehicleBlockShellCap displayType={displayType} />
<div
className={`ct-vehicle-block__block ct-vehicle-block__block--${type}`}
>
{children}
</div>
<VehicleBlockShellCap displayType={displayType} invertY={true} />
</div>
);
}
}
@@ -0,0 +1,4 @@
import VehicleBlockShell from "./VehicleBlockShell";
export default VehicleBlockShell;
export { VehicleBlockShell };
@@ -0,0 +1,43 @@
import React from "react";
import { Constants } from "@dndbeyond/character-rules-engine/es";
import { VehicleDisplayTypeComponentLookup } from "../../../utils/Component";
import VehicleBlockShellCapInfernal from "../VehicleBlockShellCapInfernal";
import VehicleBlockShellCapShip from "../VehicleBlockShellCapShip";
interface Props {
displayType: Constants.VehicleConfigurationDisplayTypeEnum;
invertY: boolean;
invertX: boolean;
}
export default class VehicleBlockShellCap extends React.PureComponent<Props> {
static defaultProps = {
invertX: false,
invertY: false,
};
render() {
const { displayType, ...restProps } = this.props;
let lookup: VehicleDisplayTypeComponentLookup = {
[Constants.VehicleConfigurationDisplayTypeEnum.SHIP]:
VehicleBlockShellCapShip,
[Constants.VehicleConfigurationDisplayTypeEnum.INFERNAL_WAR_MACHINE]:
VehicleBlockShellCapInfernal,
[Constants.VehicleConfigurationDisplayTypeEnum.SPELLJAMMER]:
VehicleBlockShellCapShip,
};
//sets Ship style as default
let CapComponent: React.ComponentType<any> = VehicleBlockShellCapShip;
if (lookup.hasOwnProperty(displayType)) {
CapComponent = lookup[displayType];
}
return (
<CapComponent className="ct-vehicle-block__shell-cap" {...restProps} />
);
}
}
@@ -0,0 +1,4 @@
import VehicleBlockShellCap from "./VehicleBlockShellCap";
export default VehicleBlockShellCap;
export { VehicleBlockShellCap };
@@ -0,0 +1,58 @@
import React from "react";
import { BaseSvg } from "@dndbeyond/character-components/es";
interface Props {
className: string;
invertY: boolean;
invertX: boolean;
}
export default class VehicleBlockShellCapInfernal extends React.PureComponent<Props> {
static defaultProps = {
className: "",
invertY: false,
invertX: false,
};
render() {
const { className, invertY, invertX } = this.props;
let classNames: Array<string> = [
"ct-vehicle-block__shell-cap-infernal",
className,
];
if (invertY) {
classNames.push("ct-vehicle-block__shell-cap--invert-y");
}
if (invertX) {
classNames.push("ct-vehicle-block__shell-cap--invert-x");
}
return (
<BaseSvg viewBox="0 0 2711.9 133.1" className={classNames.join(" ")}>
<defs>
<linearGradient
id="linear-gradient"
x1="1355.95"
y1="37.16"
x2="1355.95"
y2="138.66"
gradientUnits="userSpaceOnUse"
>
<stop offset="0" stopColor="#562c32" />
<stop offset="1" stopColor="#88817b" />
</linearGradient>
</defs>
<polygon
fill="#3c203c"
points="2711.9 46.18 2711.9 133.1 0 133.1 0 46.18 37.9 76.19 308.9 76.19 1074.9 25.19 782.9 76.19 883.9 76.19 1260.38 0 1032.9 76.19 1169.9 76.19 1336.9 4.19 1253.9 76.19 1322.9 76.19 1355.9 8.19 1355.9 8.6 1356 8.19 1389 76.19 1458 76.19 1375 4.19 1542 76.19 1679 76.19 1451.52 0 1828 76.19 1929 76.19 1637 25.19 2403 76.19 2674 76.19 2711.9 46.18"
/>
<polygon
fill="url(#linear-gradient)"
points="1355.9 27.19 1382.24 86.99 1486.93 86.99 1434.72 41.7 1539.77 86.99 1745.25 86.99 1620.57 45.23 1826.92 86.99 2053.61 86.99 1838.59 49.44 2402.64 86.99 2677.76 86.99 2701.1 68.51 2701.1 122.3 10.8 122.3 10.8 68.51 34.14 86.99 309.26 86.99 873.31 49.44 658.29 86.99 884.98 86.99 1091.33 45.23 966.65 86.99 1172.13 86.99 1277.18 41.7 1224.97 86.99 1329.66 86.99 1355.9 27.19"
/>
</BaseSvg>
);
}
}
@@ -0,0 +1,4 @@
import VehicleBlockShellCapInfernal from "./VehicleBlockShellCapInfernal";
export default VehicleBlockShellCapInfernal;
export { VehicleBlockShellCapInfernal };
@@ -0,0 +1,23 @@
import React from "react";
interface Props {
className: string;
invertY?: boolean;
invertX?: boolean;
}
export default class VehicleBlockShellCapShip extends React.PureComponent<Props> {
static defaultProps = {
className: "",
};
render() {
const { className } = this.props;
let classNames: Array<string> = [
"ct-vehicle-block__shell-cap-ship",
className,
];
return <div className={classNames.join(" ")} />;
}
}
@@ -0,0 +1,4 @@
import VehicleBlockShellCapShip from "./VehicleBlockShellCapShip";
export default VehicleBlockShellCapShip;
export { VehicleBlockShellCapShip };
@@ -0,0 +1,52 @@
import VehicleBlock from "./VehicleBlock";
import VehicleBlockAction from "./VehicleBlockAction";
import VehicleBlockActionStation from "./VehicleBlockActionStation";
import VehicleBlockActionStations from "./VehicleBlockActionStations";
import VehicleBlockActionStationsShell from "./VehicleBlockActionStationsShell";
import VehicleBlockActionSummaries from "./VehicleBlockActionSummaries";
import VehicleBlockActionSummary from "./VehicleBlockActionSummary";
import VehicleBlockActions from "./VehicleBlockActions";
import VehicleBlockAttribute from "./VehicleBlockAttribute";
import VehicleBlockComponent from "./VehicleBlockComponent";
import VehicleBlockComponents from "./VehicleBlockComponents";
import VehicleBlockComponentsShell from "./VehicleBlockComponentsShell";
import VehicleBlockFeatures from "./VehicleBlockFeatures";
import VehicleBlockHeader from "./VehicleBlockHeader";
import VehicleBlockPrimary from "./VehicleBlockPrimary";
import VehicleBlockPrimaryAttributes from "./VehicleBlockPrimaryAttributes";
import VehicleBlockSectionHeader from "./VehicleBlockSectionHeader";
import VehicleBlockSeparator from "./VehicleBlockSeparator";
import VehicleBlockSeparatorInfernal from "./VehicleBlockSeparatorInfernal";
import VehicleBlockSeparatorShip from "./VehicleBlockSeparatorShip";
import VehicleBlockShell from "./VehicleBlockShell";
import VehicleBlockShellCap from "./VehicleBlockShellCap";
import VehicleBlockShellCapInfernal from "./VehicleBlockShellCapInfernal";
import VehicleBlockShellCapShip from "./VehicleBlockShellCapShip";
export default VehicleBlock;
export {
VehicleBlock,
VehicleBlockAction,
VehicleBlockActions,
VehicleBlockActionStation,
VehicleBlockActionStations,
VehicleBlockActionStationsShell,
VehicleBlockActionSummaries,
VehicleBlockActionSummary,
VehicleBlockAttribute,
VehicleBlockComponent,
VehicleBlockComponents,
VehicleBlockComponentsShell,
VehicleBlockFeatures,
VehicleBlockHeader,
VehicleBlockPrimary,
VehicleBlockPrimaryAttributes,
VehicleBlockSectionHeader,
VehicleBlockSeparator,
VehicleBlockSeparatorInfernal,
VehicleBlockSeparatorShip,
VehicleBlockShell,
VehicleBlockShellCap,
VehicleBlockShellCapInfernal,
VehicleBlockShellCapShip,
};