New source found from dndbeyond.com

This commit is contained in:
2025-07-09 01:00:11 -07:00
parent b7f7f4a655
commit 151590af7a
18 changed files with 150 additions and 45 deletions
@@ -15,6 +15,7 @@ export interface NumberDisplayProps extends HTMLAttributes<HTMLSpanElement> {
isModified?: boolean; isModified?: boolean;
size?: "large"; size?: "large";
numberFallback?: ReactNode; numberFallback?: ReactNode;
useMph?: boolean;
} }
export const NumberDisplay: FC<NumberDisplayProps> = ({ export const NumberDisplay: FC<NumberDisplayProps> = ({
@@ -23,6 +24,7 @@ export const NumberDisplay: FC<NumberDisplayProps> = ({
isModified = false, isModified = false,
size, size,
numberFallback = "--", numberFallback = "--",
useMph,
className, className,
...props ...props
}) => { }) => {
@@ -37,6 +39,9 @@ export const NumberDisplay: FC<NumberDisplayProps> = ({
if (number && number % FEET_IN_MILES === 0) { if (number && number % FEET_IN_MILES === 0) {
number = number / FEET_IN_MILES; number = number / FEET_IN_MILES;
label = `mile${Math.abs(number) === 1 ? "" : "s"}`; label = `mile${Math.abs(number) === 1 ? "" : "s"}`;
if (useMph) {
label = "mph";
}
} else { } else {
label = "ft."; label = "ft.";
} }
@@ -124,7 +124,7 @@ export function generateVehicleMeta(vehicle, ruleData) {
} }
let type = VehicleAccessors.getType(vehicle); let type = VehicleAccessors.getType(vehicle);
let typeName = type === null ? '' : RuleDataUtils.getObjectTypeName(type, ruleData); let typeName = type === null ? '' : RuleDataUtils.getObjectTypeName(type, ruleData);
typeName = typeName ? typeName.toLowerCase() : ''; typeName = typeName || '';
let primaryText = `${size} ${typeName}`.trim(); let primaryText = `${size} ${typeName}`.trim();
let movementTypesText = VehicleAccessors.getMovementNames(vehicle).join(', '); let movementTypesText = VehicleAccessors.getMovementNames(vehicle).join(', ');
metaText.push(`${primaryText}${movementTypesText ? ` (${movementTypesText})` : ''}`.trim()); metaText.push(`${primaryText}${movementTypesText ? ` (${movementTypesText})` : ''}`.trim());
@@ -109,12 +109,15 @@ export function stripTooltipTags(str, fallback = '') {
* Formats the specified number as a distance with the appropriate unit * Formats the specified number as a distance with the appropriate unit
* @param distance The distance measured in feet to format * @param distance The distance measured in feet to format
*/ */
export function renderDistance(distance) { export function renderDistance(distance, useMph) {
let displayNumber = distance; let displayNumber = distance;
let label = 'ft.'; let label = 'ft.';
if (distance !== 0 && distance % FEET_IN_MILES === 0) { if (distance !== 0 && distance % FEET_IN_MILES === 0) {
displayNumber = CoreUtils.convertFeetToMiles(distance); displayNumber = CoreUtils.convertFeetToMiles(distance);
label = `mile${Math.abs(displayNumber) === 1 ? '' : 's'}`; label = `mile${Math.abs(displayNumber) === 1 ? '' : 's'}`;
if (useMph) {
label = `mph`;
}
} }
return `${displayNumber} ${label}`; return `${displayNumber} ${label}`;
} }
@@ -25,6 +25,7 @@ export var VehicleConfigurationDisplayTypeEnum;
VehicleConfigurationDisplayTypeEnum["SHIP"] = "ship"; VehicleConfigurationDisplayTypeEnum["SHIP"] = "ship";
VehicleConfigurationDisplayTypeEnum["INFERNAL_WAR_MACHINE"] = "infernal-war-machine"; VehicleConfigurationDisplayTypeEnum["INFERNAL_WAR_MACHINE"] = "infernal-war-machine";
VehicleConfigurationDisplayTypeEnum["SPELLJAMMER"] = "spelljammer"; VehicleConfigurationDisplayTypeEnum["SPELLJAMMER"] = "spelljammer";
VehicleConfigurationDisplayTypeEnum["ELEMENTAL_AIRSHIP"] = "elemental-airship";
})(VehicleConfigurationDisplayTypeEnum || (VehicleConfigurationDisplayTypeEnum = {})); })(VehicleConfigurationDisplayTypeEnum || (VehicleConfigurationDisplayTypeEnum = {}));
export var VehicleConfigurationPrimaryComponentManageTypeEnum; export var VehicleConfigurationPrimaryComponentManageTypeEnum;
(function (VehicleConfigurationPrimaryComponentManageTypeEnum) { (function (VehicleConfigurationPrimaryComponentManageTypeEnum) {
@@ -2,7 +2,6 @@ export var ComponentAdjustmentEnum;
(function (ComponentAdjustmentEnum) { (function (ComponentAdjustmentEnum) {
ComponentAdjustmentEnum["HIT_POINT_SPEED_ADJUSTMENT"] = "speed"; ComponentAdjustmentEnum["HIT_POINT_SPEED_ADJUSTMENT"] = "speed";
})(ComponentAdjustmentEnum || (ComponentAdjustmentEnum = {})); })(ComponentAdjustmentEnum || (ComponentAdjustmentEnum = {}));
//TODO new for spelljammers
export var ComponentCostTypeEnum; export var ComponentCostTypeEnum;
(function (ComponentCostTypeEnum) { (function (ComponentCostTypeEnum) {
ComponentCostTypeEnum["COMPONENT"] = "component"; ComponentCostTypeEnum["COMPONENT"] = "component";
@@ -3,7 +3,7 @@ import { ActionAccessors } from "../engine/Action";
import { CharacterUtils } from "../engine/Character"; import { CharacterUtils } from "../engine/Character";
import { HelperUtils } from "../engine/Helper"; import { HelperUtils } from "../engine/Helper";
import { RuleDataAccessors } from "../engine/RuleData"; import { RuleDataAccessors } from "../engine/RuleData";
import { VehicleConfigurationPrimaryComponentManageTypeEnum } from "../engine/Vehicle"; import { VehicleConfigurationDisplayTypeEnum, VehicleConfigurationPrimaryComponentManageTypeEnum, } from "../engine/Vehicle";
import { VehicleComponentAccessors } from "../engine/VehicleComponent"; import { VehicleComponentAccessors } from "../engine/VehicleComponent";
import { rulesEngineSelectors } from "../selectors"; import { rulesEngineSelectors } from "../selectors";
import { BaseManager } from './BaseManager'; import { BaseManager } from './BaseManager';
@@ -133,7 +133,10 @@ export class VehicleComponentManager extends BaseManager {
allowComponentProperty() { allowComponentProperty() {
const primaryManageType = this.vehicle.getPrimaryComponentManageType(); const primaryManageType = this.vehicle.getPrimaryComponentManageType();
const isPrimaryComponent = this.getIsPrimary(); const isPrimaryComponent = this.getIsPrimary();
return !(primaryManageType === VehicleConfigurationPrimaryComponentManageTypeEnum.VEHICLE && isPrimaryComponent); const isElementalAirship = this.vehicle.getDisplayType() === VehicleConfigurationDisplayTypeEnum.ELEMENTAL_AIRSHIP;
return !(primaryManageType === VehicleConfigurationPrimaryComponentManageTypeEnum.VEHICLE &&
isPrimaryComponent &&
!isElementalAirship);
} }
generateVehicleComponentSpeedInfos() { generateVehicleComponentSpeedInfos() {
const enableSpeeds = this.vehicle.getEnableComponentSpeeds(); const enableSpeeds = this.vehicle.getEnableComponentSpeeds();
@@ -119,6 +119,7 @@ export class VehicleManager extends BaseManager {
this.isSpelljammer = () => this.getDisplayType() === VehicleConfigurationDisplayTypeEnum.SPELLJAMMER; this.isSpelljammer = () => this.getDisplayType() === VehicleConfigurationDisplayTypeEnum.SPELLJAMMER;
this.isInfernalWarMachine = () => this.getDisplayType() === VehicleConfigurationDisplayTypeEnum.INFERNAL_WAR_MACHINE; this.isInfernalWarMachine = () => this.getDisplayType() === VehicleConfigurationDisplayTypeEnum.INFERNAL_WAR_MACHINE;
this.isShip = () => this.getDisplayType() === VehicleConfigurationDisplayTypeEnum.SHIP; this.isShip = () => this.getDisplayType() === VehicleConfigurationDisplayTypeEnum.SHIP;
this.isElementalAirship = () => this.getDisplayType() === VehicleConfigurationDisplayTypeEnum.ELEMENTAL_AIRSHIP;
// Utils // Utils
this.generateVehicleMeta = () => { this.generateVehicleMeta = () => {
const ruleData = rulesEngineSelectors.getRuleData(this.state); const ruleData = rulesEngineSelectors.getRuleData(this.state);
@@ -7,6 +7,7 @@ import {
Constants, Constants,
Creature, Creature,
ExtraManager, ExtraManager,
VehicleManager,
} from "@dndbeyond/character-rules-engine/es"; } from "@dndbeyond/character-rules-engine/es";
import { NumberDisplay } from "~/components/NumberDisplay"; import { NumberDisplay } from "~/components/NumberDisplay";
@@ -75,7 +76,7 @@ export const ExtraRow: FC<Props> = ({
typeName = type; typeName = type;
} }
let description: string = `${size} ${typeName.toLowerCase()}`.trim(); let description: string = `${size} ${typeName}`.trim();
if (description) { if (description) {
metaItems.push(description); metaItems.push(description);
} }
@@ -164,12 +165,27 @@ export const ExtraRow: FC<Props> = ({
const { data, label, showTooltip } = movementInfo; const { data, label, showTooltip } = movementInfo;
let useMph = false;
const isVehicle = extra.isVehicle();
if (isVehicle) {
const vehicleData = extra.getExtraData() as VehicleManager;
const displayType = vehicleData?.getDisplayType();
const isElementalAirship =
displayType ===
Constants.VehicleConfigurationDisplayTypeEnum.ELEMENTAL_AIRSHIP;
useMph = isElementalAirship;
}
let contentNode: React.ReactNode = null; let contentNode: React.ReactNode = null;
if (data !== null) { if (data !== null) {
contentNode = ( contentNode = (
<React.Fragment> <React.Fragment>
<div className="ct-extra-row__speed-value"> <div className="ct-extra-row__speed-value">
<NumberDisplay type="distanceInFt" number={data.speed} /> <NumberDisplay
type="distanceInFt"
number={data.speed}
useMph={useMph}
/>
{showTooltip && renderAdditionalInfoTooltip()} {showTooltip && renderAdditionalInfoTooltip()}
</div> </div>
{data.movementId !== Constants.MovementTypeEnum.WALK && ( {data.movementId !== Constants.MovementTypeEnum.WALK && (
@@ -4,6 +4,7 @@ interface Props {
label: string; label: string;
value: React.ReactNode; value: React.ReactNode;
extraValue: React.ReactNode; extraValue: React.ReactNode;
displayColon?: boolean;
} }
export default class VehicleBlockAttribute extends React.PureComponent<Props> { export default class VehicleBlockAttribute extends React.PureComponent<Props> {
static defaultProps = { static defaultProps = {
@@ -11,11 +12,15 @@ export default class VehicleBlockAttribute extends React.PureComponent<Props> {
}; };
render() { render() {
const { label, value, extraValue } = this.props; const { label, value, extraValue, displayColon } = this.props;
const labelDisplay: string = displayColon ? `${label}:` : label;
return ( return (
<div className="ct-vehicle-block__attribute"> <div className="ct-vehicle-block__attribute">
<span className="ct-vehicle-block__attribute-label">{label}</span> <span className="ct-vehicle-block__attribute-label">
{labelDisplay}
</span>
<span className="ct-vehicle-block__attribute-data"> <span className="ct-vehicle-block__attribute-data">
<span className="ct-vehicle-block__attribute-data-value"> <span className="ct-vehicle-block__attribute-data-value">
{value} {value}
@@ -1,3 +1,4 @@
import clsx from "clsx";
import React from "react"; import React from "react";
import { ManageIcon } from "@dndbeyond/character-components/es"; import { ManageIcon } from "@dndbeyond/character-components/es";
@@ -42,6 +43,9 @@ export default class VehicleBlockComponent extends React.PureComponent<Props> {
const nameLabel: string = name !== null ? name : ""; const nameLabel: string = name !== null ? name : "";
const isSpelljammer = const isSpelljammer =
displayType === Constants.VehicleConfigurationDisplayTypeEnum.SPELLJAMMER; displayType === Constants.VehicleConfigurationDisplayTypeEnum.SPELLJAMMER;
const isElementalAirship =
displayType ===
Constants.VehicleConfigurationDisplayTypeEnum.ELEMENTAL_AIRSHIP;
let componentCount: string = ""; let componentCount: string = "";
if (count > 1) { if (count > 1) {
@@ -56,8 +60,11 @@ export default class VehicleBlockComponent extends React.PureComponent<Props> {
requiredCrew ? ` (${requiredCrew} Crew${count > 1 ? " Each" : ""})` : "" requiredCrew ? ` (${requiredCrew} Crew${count > 1 ? " Each" : ""})` : ""
}`; }`;
} else { } else {
let typesText = FormatUtils.renderNonOxfordCommaList(typeNames); const typesNames = FormatUtils.renderNonOxfordCommaList(typeNames);
primaryText = `${typesText}: ${nameLabel} ${componentCount}`; const typeText = `${typesNames}: `;
primaryText = `${
isElementalAirship ? "" : typeText
}${nameLabel} ${componentCount}`;
} }
let callout: React.ReactNode = null; let callout: React.ReactNode = null;
@@ -79,8 +86,8 @@ export default class VehicleBlockComponent extends React.PureComponent<Props> {
return ( return (
<div className="ct-vehicle-block-component__actions"> <div className="ct-vehicle-block-component__actions">
{actions.map((action) => ( {actions.map((action, idx) => (
<VehicleBlockAction action={action} key={action.key} /> <VehicleBlockAction action={action} key={idx + action.key} />
))} ))}
</div> </div>
); );
@@ -127,7 +134,14 @@ export default class VehicleBlockComponent extends React.PureComponent<Props> {
} = this.props; } = this.props;
return ( return (
<div className="ct-vehicle-block-component__attributes"> <div
className={clsx([
"ct-vehicle-block-component__attributes",
displayType ===
Constants.VehicleConfigurationDisplayTypeEnum.ELEMENTAL_AIRSHIP &&
"ct-vehicle-block-component__attributes--inline",
])}
>
<VehicleBlockPrimaryAttributes <VehicleBlockPrimaryAttributes
armorClassInfo={armorClassInfo} armorClassInfo={armorClassInfo}
hitPointInfo={hitPointInfo} hitPointInfo={hitPointInfo}
@@ -69,8 +69,11 @@ export default class VehicleBlockComponents extends React.PureComponent<Props> {
components = components.filter( components = components.filter(
(component) => (component) =>
!( !(
(component.displayType ===
Constants.VehicleConfigurationDisplayTypeEnum.SPELLJAMMER ||
component.displayType === component.displayType ===
Constants.VehicleConfigurationDisplayTypeEnum.SPELLJAMMER && Constants.VehicleConfigurationDisplayTypeEnum
.ELEMENTAL_AIRSHIP) &&
component.isPrimaryComponent component.isPrimaryComponent
) )
); );
@@ -15,7 +15,7 @@ export default class VehicleBlockHeader extends React.PureComponent<Props> {
} }
let size: string = sizeName !== null ? sizeName : ""; let size: string = sizeName !== null ? sizeName : "";
let type: string = typeName !== null ? typeName.toLowerCase() : ""; let type: string = typeName !== null ? typeName : "";
return [size, type].join(" ").trim(); return [size, type].join(" ").trim();
}; };
@@ -65,14 +65,18 @@ export default class VehicleBlockHeader extends React.PureComponent<Props> {
const typeInfo = this.renderTypeInfo(); const typeInfo = this.renderTypeInfo();
const dimensions = this.renderDimensions(); const dimensions = this.renderDimensions();
const showMeta =
displayType !==
Constants.VehicleConfigurationDisplayTypeEnum.SPELLJAMMER &&
displayType !==
Constants.VehicleConfigurationDisplayTypeEnum.ELEMENTAL_AIRSHIP;
return ( return (
<div className="ct-vehicle-block__header"> <div className="ct-vehicle-block__header">
<div className="ct-vehicle-block__name"> <div className="ct-vehicle-block__name">
{name !== null ? name : ""} {name !== null ? name : ""}
</div> </div>
{displayType !== {showMeta && (
Constants.VehicleConfigurationDisplayTypeEnum.SPELLJAMMER && (
<div className="ct-vehicle-block__meta"> <div className="ct-vehicle-block__meta">
{typeInfo} {dimensions} {typeInfo} {dimensions}
</div> </div>
@@ -120,6 +120,7 @@ export default class VehicleBlockPrimary extends React.PureComponent<Props> {
primaryProperties, primaryProperties,
shouldCoalesce, shouldCoalesce,
displayType, displayType,
creatureCapacityInfo,
} = this.props; } = this.props;
let cargoCapacity: Array<string> = []; let cargoCapacity: Array<string> = [];
@@ -138,6 +139,11 @@ export default class VehicleBlockPrimary extends React.PureComponent<Props> {
const isSpelljammer = const isSpelljammer =
displayType === Constants.VehicleConfigurationDisplayTypeEnum.SPELLJAMMER; displayType === Constants.VehicleConfigurationDisplayTypeEnum.SPELLJAMMER;
const isElementalAirship =
displayType ===
Constants.VehicleConfigurationDisplayTypeEnum.ELEMENTAL_AIRSHIP;
const showSeperator = !isSpelljammer && !isElementalAirship;
return ( return (
<> <>
@@ -164,18 +170,32 @@ export default class VehicleBlockPrimary extends React.PureComponent<Props> {
extraValue={this.renderEffectiveTravelDistance()} extraValue={this.renderEffectiveTravelDistance()}
/> />
)} )}
{!isElementalAirship ? (
<VehicleBlockAttribute <VehicleBlockAttribute
label={isSpelljammer ? "Crew" : "Creature Capacity"} label={isSpelljammer ? "Crew" : "Creature Capacity"}
value={creatureCapacityDescriptions.join(", ")} value={creatureCapacityDescriptions.join(", ")}
/> />
) : (
creatureCapacityInfo.map((capacity, idx) => (
<VehicleBlockAttribute
key={capacity.type || "Capacity" + idx}
label={capacity.type || "Capacity"}
value={capacity.capacity}
displayColon={true}
/>
))
)}
{cargoCapacity.length > 0 && ( {cargoCapacity.length > 0 && (
<VehicleBlockAttribute <VehicleBlockAttribute
label={isSpelljammer ? "Cargo" : "Cargo Capacity"} label={
isSpelljammer || isElementalAirship ? "Cargo" : "Cargo Capacity"
}
value={cargoCapacity.join(", ")} value={cargoCapacity.join(", ")}
displayColon={isElementalAirship}
/> />
)} )}
</div> </div>
{!isSpelljammer && <VehicleBlockSeparator displayType={displayType} />} {showSeperator && <VehicleBlockSeparator displayType={displayType} />}
</> </>
); );
}; };
@@ -110,16 +110,21 @@ export default class VehicleBlockPrimaryAttributes extends React.PureComponent<P
}; };
renderSpeedInfo = ( renderSpeedInfo = (
modes: Array<VehicleComponentSpeedModeInfo> modes: Array<VehicleComponentSpeedModeInfo>,
displayType: Constants.VehicleConfigurationDisplayTypeEnum
): React.ReactNode => { ): React.ReactNode => {
const isElementalAirship =
displayType ===
Constants.VehicleConfigurationDisplayTypeEnum.ELEMENTAL_AIRSHIP;
let speedDisplayStrings: Array<string> = modes.map((mode) => { let speedDisplayStrings: Array<string> = modes.map((mode) => {
const { value, description, restrictionsText, movementInfo } = mode; const { value, description, restrictionsText, movementInfo } = mode;
let stringParts: Array<string> = []; let stringParts: Array<string> = [];
if (movementInfo) { if (movementInfo && !isElementalAirship) {
stringParts.push(`${movementInfo.description} speed`); stringParts.push(`${movementInfo.description} speed`);
} }
stringParts.push(FormatUtils.renderDistance(value)); stringParts.push(FormatUtils.renderDistance(value, isElementalAirship));
if (description) { if (description) {
stringParts.push(description); stringParts.push(description);
} }
@@ -134,12 +139,16 @@ export default class VehicleBlockPrimaryAttributes extends React.PureComponent<P
}; };
renderArmorClassAttribute = (): React.ReactNode => { renderArmorClassAttribute = (): React.ReactNode => {
const { armorClassInfo } = this.props; const { armorClassInfo, displayType } = this.props;
if (armorClassInfo === null) { if (armorClassInfo === null) {
return null; return null;
} }
const isElementalAirship =
displayType ===
Constants.VehicleConfigurationDisplayTypeEnum.ELEMENTAL_AIRSHIP;
const { moving, base, description } = armorClassInfo; const { moving, base, description } = armorClassInfo;
let armorClassValue: number | null = base; let armorClassValue: number | null = base;
@@ -155,7 +164,7 @@ export default class VehicleBlockPrimaryAttributes extends React.PureComponent<P
return ( return (
<VehicleBlockAttribute <VehicleBlockAttribute
label="Armor Class" label={isElementalAirship ? "AC" : "Armor Class"}
value={ value={
description description
? `${armorClassValue} (${description})` ? `${armorClassValue} (${description})`
@@ -164,6 +173,7 @@ export default class VehicleBlockPrimaryAttributes extends React.PureComponent<P
: armorClassValue : armorClassValue
} }
extraValue={motionlessArmorClassValue} extraValue={motionlessArmorClassValue}
displayColon={isElementalAirship}
/> />
); );
}; };
@@ -206,7 +216,9 @@ export default class VehicleBlockPrimaryAttributes extends React.PureComponent<P
); );
const costValues: Array<string> = sortedCosts.map((cost) => { const costValues: Array<string> = sortedCosts.map((cost) => {
const value: string = cost.value ? `${cost.value} gp` : "--"; const value: string = cost.value
? `${FormatUtils.renderLocaleNumber(cost.value)} GP`
: "--";
const description: string = cost.description const description: string = cost.description
? `(${cost.description})` ? `(${cost.description})`
: ""; : "";
@@ -216,23 +228,31 @@ export default class VehicleBlockPrimaryAttributes extends React.PureComponent<P
const isSpelljammer = const isSpelljammer =
displayType === Constants.VehicleConfigurationDisplayTypeEnum.SPELLJAMMER; displayType === Constants.VehicleConfigurationDisplayTypeEnum.SPELLJAMMER;
const isElementalAirship =
displayType ===
Constants.VehicleConfigurationDisplayTypeEnum.ELEMENTAL_AIRSHIP;
return ( return (
<React.Fragment> <React.Fragment>
{armorClassInfo !== null && this.renderArmorClassAttribute()} {armorClassInfo !== null && this.renderArmorClassAttribute()}
{hitPointInfo !== null && ( {hitPointInfo !== null && (
<VehicleBlockAttribute <VehicleBlockAttribute
label="Hit Points" label={isElementalAirship ? "HP" : "Hit Points"}
value={this.renderHitPointInfo()} value={this.renderHitPointInfo()}
extraValue={ extraValue={
!isSpelljammer ? this.renderHitPointThresholdInfo() : undefined !isSpelljammer && !isElementalAirship
? this.renderHitPointThresholdInfo()
: undefined
} }
displayColon={isElementalAirship}
/> />
)} )}
{isSpelljammer && !!hitPointInfo?.damageThreshold && ( {(isSpelljammer || isElementalAirship) &&
!!hitPointInfo?.damageThreshold && (
<VehicleBlockAttribute <VehicleBlockAttribute
label="Damage Threshold" label={"Damage Threshold"}
value={hitPointInfo?.damageThreshold} value={hitPointInfo?.damageThreshold}
displayColon={isElementalAirship}
/> />
)} )}
{speedInfos.map((speedInfo, idx) => { {speedInfos.map((speedInfo, idx) => {
@@ -248,7 +268,8 @@ export default class VehicleBlockPrimaryAttributes extends React.PureComponent<P
<VehicleBlockAttribute <VehicleBlockAttribute
key={`${speedInfo.type}-${idx}`} key={`${speedInfo.type}-${idx}`}
label={speedAttributeLabel} label={speedAttributeLabel}
value={this.renderSpeedInfo(modes)} value={this.renderSpeedInfo(modes, displayType)}
displayColon={isElementalAirship}
/> />
); );
})} })}
@@ -258,7 +279,7 @@ export default class VehicleBlockPrimaryAttributes extends React.PureComponent<P
value={`Grants ${coverType} Cover`} value={`Grants ${coverType} Cover`}
/> />
)} )}
{requiredCrew !== null && !isSpelljammer && ( {requiredCrew !== null && !isSpelljammer && !isElementalAirship && (
<VehicleBlockAttribute label="Required Crew" value={requiredCrew} /> <VehicleBlockAttribute label="Required Crew" value={requiredCrew} />
)} )}
{isPrimaryComponent && {isPrimaryComponent &&
@@ -273,7 +294,11 @@ export default class VehicleBlockPrimaryAttributes extends React.PureComponent<P
/> />
)} )}
{costValues.length > 0 && ( {costValues.length > 0 && (
<VehicleBlockAttribute label="Cost" value={costValues.join(", ")} /> <VehicleBlockAttribute
label="Cost"
value={costValues.join(", ")}
displayColon={isElementalAirship}
/>
)} )}
</React.Fragment> </React.Fragment>
); );
@@ -20,6 +20,8 @@ export default class VehicleBlockSeparator extends React.PureComponent<Props> {
VehicleBlockSeparatorInfernal, VehicleBlockSeparatorInfernal,
[Constants.VehicleConfigurationDisplayTypeEnum.SPELLJAMMER]: [Constants.VehicleConfigurationDisplayTypeEnum.SPELLJAMMER]:
VehicleBlockSeparatorShip, VehicleBlockSeparatorShip,
[Constants.VehicleConfigurationDisplayTypeEnum.ELEMENTAL_AIRSHIP]:
VehicleBlockSeparatorShip,
}; };
//sets Ship style as default //sets Ship style as default
@@ -27,6 +27,8 @@ export default class VehicleBlockShellCap extends React.PureComponent<Props> {
VehicleBlockShellCapInfernal, VehicleBlockShellCapInfernal,
[Constants.VehicleConfigurationDisplayTypeEnum.SPELLJAMMER]: [Constants.VehicleConfigurationDisplayTypeEnum.SPELLJAMMER]:
VehicleBlockShellCapShip, VehicleBlockShellCapShip,
[Constants.VehicleConfigurationDisplayTypeEnum.ELEMENTAL_AIRSHIP]:
VehicleBlockShellCapShip,
}; };
//sets Ship style as default //sets Ship style as default
@@ -385,7 +385,7 @@ class VehiclePane extends React.PureComponent<Props, State> {
return null; return null;
} }
if (!vehicle.isSpelljammer()) { if (!vehicle.isSpelljammer() && !vehicle.isElementalAirship()) {
const primaryComponent = vehicle.getPrimaryComponent(); const primaryComponent = vehicle.getPrimaryComponent();
if (primaryComponent === null) { if (primaryComponent === null) {
return null; return null;
@@ -442,6 +442,7 @@ class VehiclePane extends React.PureComponent<Props, State> {
} }
return ( return (
<VehicleHealthAdjuster <VehicleHealthAdjuster
key={`${componentName}${componentNumber}`}
hitPointInfo={componentHitPointInfo} hitPointInfo={componentHitPointInfo}
onSave={(diff) => this.handleHealthAdjusterSave(diff, component)} onSave={(diff) => this.handleHealthAdjusterSave(diff, component)}
heading={`Hit Points (${componentName}${componentNumber})`} heading={`Hit Points (${componentName}${componentNumber})`}
@@ -507,7 +508,8 @@ class VehiclePane extends React.PureComponent<Props, State> {
const primaryComponentManageType = vehicle.getPrimaryComponentManageType(); const primaryComponentManageType = vehicle.getPrimaryComponentManageType();
const enableConditionTracking = vehicle.getEnableConditionTracking(); const enableConditionTracking = vehicle.getEnableConditionTracking();
const enableFuelTracking = vehicle.getEnableFuelTracking(); const enableFuelTracking = vehicle.getEnableFuelTracking();
const shouldCoalesce = vehicle.isSpelljammer(); const shouldCoalesce =
vehicle.isSpelljammer() || vehicle.isElementalAirship();
return ( return (
<React.Fragment> <React.Fragment>
@@ -550,7 +552,6 @@ class VehiclePane extends React.PureComponent<Props, State> {
renderVehicleName = (): React.ReactNode => { renderVehicleName = (): React.ReactNode => {
const { vehicle } = this.state; const { vehicle } = this.state;
const { theme } = this.props;
if (!vehicle) { if (!vehicle) {
return null; //TODO could render a default empty state return null; //TODO could render a default empty state
@@ -96,6 +96,7 @@ export function generateVehicleBlockPrimaryProps(
cargoCapacityInfo: vehicle.getVehicleCargoCapacityInfo(), cargoCapacityInfo: vehicle.getVehicleCargoCapacityInfo(),
conditionImmunities: vehicle.generateVehicleConditionImmunityNames(), conditionImmunities: vehicle.generateVehicleConditionImmunityNames(),
creatureCapacityDescriptions: vehicle.getCreatureCapacityDescriptions(), creatureCapacityDescriptions: vehicle.getCreatureCapacityDescriptions(),
creatureCapacityInfo: vehicle.getCreatureCapacity(),
damageImmunities: vehicle.generateVehicleDamageImmunityNames(), damageImmunities: vehicle.generateVehicleDamageImmunityNames(),
displayType: vehicle.getDisplayType(), displayType: vehicle.getDisplayType(),
primaryProperties: deriveVehicleComponentPrimaryPropertiesProps(vehicle), primaryProperties: deriveVehicleComponentPrimaryPropertiesProps(vehicle),