Compare commits

...

4 Commits

Author SHA1 Message Date
Felix Linker
2eed1bc85b Add time to deplete armour/shields 2021-05-15 20:21:00 +02:00
Felix Linker
3469af10b6 Update ShipPicker 2021-05-15 15:23:36 +02:00
Felix Linker
629ba35bc5 Update engagement range and slider 2021-05-15 14:58:10 +02:00
Felix Linker
e453ff73b7 Migrate changes to slot-representation 2021-05-14 10:22:28 +02:00
5 changed files with 160 additions and 340 deletions

View File

@@ -2,6 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import TranslatedComponent from './TranslatedComponent';
import Slider from '../components/Slider';
import { moduleReduce } from 'ed-forge/lib/helper';
/**
* Engagement range slider
@@ -21,35 +22,18 @@ export default class EngagementRange extends TranslatedComponent {
*/
constructor(props, context) {
super(props);
const { ship } = props;
const maxRange = Math.round(this._calcMaxRange(ship));
this.state = {
maxRange
maxRange: moduleReduce(
this.props.ship.getHardpoints(),
'maximumrange',
true,
// Don't use plain `Math.max` because callback will be passed four args
(a, v) => Math.max(a, v),
1000,
),
};
}
/**
* Calculate the maximum range of a ship's weapons
* @param {Object} ship The ship
* @returns {int} The maximum range, in metres
*/
_calcMaxRange(ship) {
let maxRange = 1000;
for (let i = 0; i < ship.hardpoints.length; i++) {
if (ship.hardpoints[i].maxClass > 0 && ship.hardpoints[i].m && ship.hardpoints[i].enabled) {
const thisRange = ship.hardpoints[i].m.getRange();
if (thisRange > maxRange) {
maxRange = thisRange;
}
}
}
return maxRange;
}
/**
* Update range
* @param {number} rangeLevel percentage level from 0 to 1
@@ -61,7 +45,9 @@ export default class EngagementRange extends TranslatedComponent {
const range = Math.round(rangeLevel * maxRange);
if (range !== this.props.engagementRange) {
this.props.onChange(range);
const { onChange, ship } = this.props;
ship.setEngagementRange(range);
onChange(range);
}
}
@@ -70,8 +56,8 @@ export default class EngagementRange extends TranslatedComponent {
* @return {React.Component} contents
*/
render() {
const { language, onWindowResize, sizeRatio, tooltip, termtip } = this.context;
const { formats, translate, units } = language;
const { language, onWindowResize, sizeRatio } = this.context;
const { formats, translate } = language;
const { engagementRange } = this.props;
const { maxRange } = this.state;

View File

@@ -82,12 +82,14 @@ export default class Offence extends TranslatedComponent {
const { formats, translate, units } = language;
const sortOrder = this._sortOrder;
const damage = ship.getMetrics(DAMAGE_METRICS);
const {
drained, sustained, rangeMultiplier, hardnessMultiplier, timeToDrain
} = ship.getMetrics(DAMAGE_METRICS);
const portions = {
Absolute: damage.types.abs,
Explosive: damage.types.expl,
Kinetic: damage.types.kin,
Thermic: damage.types.therm,
Absolute: sustained.types.abs,
Explosive: sustained.types.expl,
Kinetic: sustained.types.kin,
Thermic: sustained.types.therm,
};
const oppShield = ship.getOpponent().getShield();
@@ -106,8 +108,8 @@ export default class Offence extends TranslatedComponent {
Thermic: oppArmour.thermal.damageMultiplier,
};
let rows = [];
for (let weapon of ship.getHardpoints()) {
const weapons = sortBy(ship.getHardpoints(), (m) => m.get('distributordraw'));
let rows = weapons.map((weapon) => {
const sdps = weapon.get('sustaineddamagepersecond');
const byRange = weapon.getRangeEffectiveness();
const weaponPortions = {
@@ -176,7 +178,7 @@ export default class Offence extends TranslatedComponent {
if (exp) {
bpTitle += `, ${translate(exp)}`;
}
rows.push({
return {
slot: weapon.getSlot(),
mount: weapon.mount,
classRating: weapon.getClassRating(),
@@ -192,8 +194,9 @@ export default class Offence extends TranslatedComponent {
armourEft,
armourSdpsTooltip,
armourEftTooltip,
});
}
};
});
const { predicate, desc } = this.state;
rows = sortBy(rows, (row) => row[predicate]);
if (desc) {
@@ -202,18 +205,18 @@ export default class Offence extends TranslatedComponent {
const sdpsTooltip = objToTooltip(
translate,
mapValues(portions, (p) => formats.f1(damage.sustained.dps * p)),
mapValues(portions, (p) => formats.f1(sustained.dps * p)),
);
const sdpsPie = objToPie(
translate,
mapValues(portions, (p) => Math.round(damage.sustained.dps * p)),
mapValues(portions, (p) => Math.round(sustained.dps * p)),
);
const shieldSdpsSrcs = mergeWith(
clone(portions),
shieldMults,
(objV, srcV) => damage.sustained.dps * oppShield.absolute.bySys *
damage.rangeMultiplier * objV * srcV,
(objV, srcV) => sustained.dps * oppShield.absolute.bySys *
rangeMultiplier * objV * srcV,
);
const shieldsSdps = sum(values(shieldSdpsSrcs));
const shieldsSdpsTooltip = objToTooltip(
@@ -228,8 +231,8 @@ export default class Offence extends TranslatedComponent {
const armourSdpsSrcs = mergeWith(
clone(portions),
armourMults,
(objV, srcV) => damage.sustained.dps * damage.hardnessMultiplier *
damage.rangeMultiplier * objV * srcV,
(objV, srcV) => sustained.dps * hardnessMultiplier * rangeMultiplier *
objV * srcV,
);
const armourSdps = sum(values(armourSdpsSrcs));
const totalArmourSDpsTooltipDetails = objToTooltip(
@@ -241,10 +244,45 @@ export default class Offence extends TranslatedComponent {
mapValues(armourSdpsSrcs, (v) => Math.round(v)),
);
const pd = ship.getPowerDistributor();
const timeToDrain = damage.sustained.timeToDrain[ship.getDistributorSettings().Wep];
// const timeToDepleteShields = Calc.timeToDeplete(opponentShields.total, shieldsSdps, totalSEps, pd.getWeaponsCapacity(), pd.getWeaponsRechargeRate() * (wep / 4));
// const timeToDepleteArmour = Calc.timeToDeplete(opponentArmour.total, armourSdps, totalSEps, pd.getWeaponsCapacity(), pd.getWeaponsRechargeRate() * (wep / 4));
const drainedPortions = {
Absolute: drained.types.abs,
Explosive: drained.types.expl,
Kinetic: drained.types.kin,
Thermic: drained.types.therm,
};
// How much damage do we deal, before the capacitor is empty?
const armourLeft = oppArmour.armour - (timeToDrain * armourSdps);
// If we can't kill the enemy on one capacitor, factor in drained damage
let timeToDepleteArmour;
if (armourLeft > 0) {
const effectiveDrainedDps = sum(values(mergeWith(
clone(drainedPortions),
armourMults,
(objV, srcV) => objV * srcV,
))) * drained.dps * rangeMultiplier *
hardnessMultiplier;
timeToDepleteArmour = effectiveDrainedDps === 0 ? Infinity :
timeToDrain + (armourLeft / effectiveDrainedDps);
} else {
timeToDepleteArmour = oppArmour.armour / armourSdps;
}
// How much damage do we deal, before the capacitor is empty?
const shieldsLeft = oppShield.withSCBs - (timeToDrain * shieldsSdps);
// If we can't kill the enemy on one capacitor, factor in drained damage
let timeToDepleteShields;
if (shieldsLeft > 0) {
const effectiveDrainedDps = sum(values(mergeWith(
clone(drainedPortions),
shieldMults,
(objV, srcV) => objV * srcV,
))) * drained.dps * rangeMultiplier;
timeToDepleteShields = effectiveDrainedDps === 0 ? Infinity :
timeToDrain + (shieldsLeft / effectiveDrainedDps);
} else {
timeToDepleteShields = oppShield.withSCBs / shieldsSdps;
}
return (
<span id='offence'>
@@ -307,7 +345,7 @@ export default class Offence extends TranslatedComponent {
<td></td>
<td className='ri'>
<span onMouseOver={termtip.bind(null, sdpsTooltip)} onMouseOut={tooltip.bind(null, null)}>
={formats.f1(damage.sustained.dps)}
={formats.f1(sustained.dps)}
</span>
</td>
<td className='ri'>
@@ -342,8 +380,7 @@ export default class Offence extends TranslatedComponent {
<h2 onMouseOver={termtip.bind(null, translate('TT_TIME_TO_REMOVE_SHIELDS'))}
onMouseOut={tooltip.bind(null, null)}>
{translate('PHRASE_TIME_TO_REMOVE_SHIELDS')}<br/>
ToDo
{/* {timeToDepleteShields === Infinity ? translate('never') : formats.time(timeToDepleteShields)} */}
{timeToDepleteShields === Infinity ? translate('never') : formats.time(timeToDepleteShields)}
</h2>
<h2 onMouseOver={termtip.bind(null, translate('TT_EFFECTIVE_SDPS_ARMOUR'))}
onMouseOut={tooltip.bind(null, null)}>
@@ -353,8 +390,7 @@ export default class Offence extends TranslatedComponent {
<h2 onMouseOver={termtip.bind(null, translate('TT_TIME_TO_REMOVE_ARMOUR'))}
onMouseOut={tooltip.bind(null, null)}>
{translate('PHRASE_TIME_TO_REMOVE_ARMOUR')}<br/>
ToDo
{/* {timeToDepleteArmour === Infinity ? translate('never') : formats.time(timeToDepleteArmour)} */}
{timeToDepleteArmour === Infinity ? translate('never') : formats.time(timeToDepleteArmour)}
</h2>
</div>
<div className='group quarter'>

View File

@@ -1,11 +1,12 @@
import React from 'react';
import PropTypes from 'prop-types';
import TranslatedComponent from './TranslatedComponent';
import { Ships } from 'coriolis-data/dist';
import { Rocket } from './SvgIcons';
import Persist from '../stores/Persist';
import cn from 'classnames';
import { Factory, Ship } from 'ed-forge';
import autoBind from 'auto-bind';
import { isEqual } from 'lodash';
/**
* Ship picker
@@ -14,14 +15,9 @@ import autoBind from 'auto-bind';
export default class ShipPicker extends TranslatedComponent {
static propTypes = {
onChange: PropTypes.func.isRequired,
ship: PropTypes.string.isRequired,
build: PropTypes.string
ship: PropTypes.instanceOf(Ship).isRequired,
};
static defaultProps = {
ship: 'eagle'
}
/**
* constructor
* @param {object} props Properties react
@@ -30,20 +26,38 @@ export default class ShipPicker extends TranslatedComponent {
constructor(props, context) {
super(props);
autoBind(this);
this.state = { menuOpen: false };
this.state = {
menuOpen: false,
opponent: {
self: true,
type: props.ship.getShipType(),
stock: false,
id: undefined,
},
};
}
/**
* Update ship
* @param {object} ship the ship
* @param {string} build the build, if present
* @param {boolean} self True to compare with ship itself
* @param {object} type The ship type
* @param {boolean} stock True to compare with a stock version of given type
* @param {string} id The build's stored ID
*/
_shipChange(ship, build) {
this._closeMenu();
// Ensure that the ship has changed
if (ship !== this.props.ship || build !== this.props.build) {
this.props.onChange(ship, build);
_shipChange(self, type, stock = false, id = null) {
const opponent = { self, type, stock, id };
if (isEqual(opponent, this.state.opponent)) {
this.setState({ menuOpen: false });
} else {
const { onChange } = this.props;
if (self) {
onChange(this.props.ship);
} else if (stock) {
onChange(Factory.newShip(type));
} else {
onChange(new Ship(Persist.getBuild(type, id)));
}
this.setState({ menuOpen: false, opponent });
}
}
@@ -52,26 +66,41 @@ export default class ShipPicker extends TranslatedComponent {
* @returns {object} the picker menu
*/
_renderPickerMenu() {
const { ship, build } = this.props;
const _shipChange = this._shipChange;
const builds = Persist.getBuilds();
const buildList = [];
for (let shipId of this.shipOrder) {
const shipBuilds = [];
// Add stock build
const stockSelected = (ship == shipId && !build);
shipBuilds.push(<li key={shipId} className={ cn({ 'selected': stockSelected })} onClick={_shipChange.bind(this, shipId, null)}>Stock</li>);
if (builds[shipId]) {
let buildNameOrder = Object.keys(builds[shipId]).sort();
for (let buildName of buildNameOrder) {
const buildSelected = ship === shipId && build === buildName;
shipBuilds.push(<li key={shipId + '-' + buildName} className={ cn({ 'selected': buildSelected })} onClick={_shipChange.bind(this, shipId, buildName)}>{buildName}</li>);
}
}
buildList.push(<ul key={shipId} className='block'>{Ships[shipId].properties.name}{shipBuilds}</ul>);
const { menuOpen } = this.state;
if (!menuOpen) {
return null;
}
return buildList;
const { translate } = this.context.language;
const { self, type, stock, id } = this.state.opponent;
return <div className='menu-list' onClick={(e) => e.stopPropagation()}>
<div className='quad'>
{Factory.getAllShipTypes().sort().map((shipType) =>
<ul key={shipType} className='block'>
{translate(shipType)}
{/* Add stock build */}
<li key={shipType}
onClick={this._shipChange.bind(this, false, shipType, true)}
className={cn({ selected: stock && type === shipType })}>
{translate('stock')}
</li>
{Persist.getBuildsNamesFor(shipType).sort().map((storedId) =>
<li key={`${shipType}-${storedId}`}
onClick={this._shipChange.bind(this, false, shipType, false, storedId)}
className={ cn({ selected: type === shipType && id === storedId })}>
{storedId}
</li>)}
{/* Add ship itself */}
{(this.props.ship.getShipType() === shipType ?
<li key='self'
onClick={this._shipChange.bind(this, true, shipType)}
className={cn({ selected: self })}>
{translate('THIS_SHIP')}
</li> :
null)}
</ul>)}
</div>
</div>;
}
/**
@@ -82,40 +111,35 @@ export default class ShipPicker extends TranslatedComponent {
this.setState({ menuOpen: !menuOpen });
}
/**
* Close the menu
*/
_closeMenu() {
const { menuOpen } = this.state;
if (menuOpen) {
this._toggleMenu();
}
}
/**
* Render picker
* @return {React.Component} contents
*/
render() {
const { language, onWindowResize, sizeRatio, tooltip, termtip } = this.context;
const { formats, translate, units } = language;
const { ship, build } = this.props;
const { translate } = this.context.language;
const { ship } = this.props;
const { menuOpen } = this.state;
const { self, type, stock, id } = this.state.opponent;
let label;
if (self) {
label = translate('THIS_SHIP');
} else if (stock) {
label = translate('stock');
} else {
label = id;
}
const shipString = ship + ': ' + (build ? build : translate('stock'));
return (
<div className='shippicker' onClick={ (e) => e.stopPropagation() }>
<div className='menu'>
<div className={cn('menu-header', { selected: menuOpen })} onClick={this._toggleMenu}>
<span><Rocket className='warning' /></span>
<span className='menu-item-label'>{shipString}</span>
<span className='menu-item-label'>
{`${translate(type)}: ${label}`}
</span>
</div>
{ menuOpen ?
<div className='menu-list' onClick={ (e) => e.stopPropagation() }>
<div className='quad'>
{this._renderPickerMenu()}
</div>
</div> : null }
{this._renderPickerMenu()}
</div>
</div>
);

View File

@@ -1,5 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import autoBind from 'auto-bind';
const MARGIN_LR = 8; // Left/ Right margin
@@ -7,7 +8,6 @@ const MARGIN_LR = 8; // Left/ Right margin
* Horizontal Slider
*/
export default class Slider extends React.Component {
static defaultProps = {
axis: false,
min: 0,
@@ -32,16 +32,7 @@ export default class Slider extends React.Component {
*/
constructor(props) {
super(props);
this._down = this._down.bind(this);
this._move = this._move.bind(this);
this._up = this._up.bind(this);
this._keyup = this._keyup.bind(this);
this._keydown = this._keydown.bind(this);
this._touchstart = this._touchstart.bind(this);
this._touchend = this._touchend.bind(this);
this._updatePercent = this._updatePercent.bind(this);
this._updateDimensions = this._updateDimensions.bind(this);
autoBind(this);
this.state = { width: 0 };
}
@@ -55,7 +46,6 @@ export default class Slider extends React.Component {
this.left = rect.left;
this.width = rect.width;
this._move(event);
this.touchStartTimer = setTimeout(() => this.sliderInputBox._setDisplay('block'), 1500);
}
/**
@@ -75,70 +65,11 @@ export default class Slider extends React.Component {
* @param {Event} event DOM Event
*/
_up(event) {
this.sliderInputBox.sliderVal.focus();
clearTimeout(this.touchStartTimer);
event.preventDefault();
this.left = null;
this.width = null;
}
/**
* Key up handler for keyboard.
* display the number field then set focus to it
* when "Enter" key is pressed
* @param {Event} event Keyboard event
*/
_keyup(event) {
switch (event.key) {
case 'Enter':
event.preventDefault();
this.sliderInputBox._setDisplay('block');
return;
default:
return;
}
}
/**
* Key down handler
* increment slider position by +/- 1 when right/left arrow key is pressed or held
* @param {Event} event Keyboard even
*/
_keydown(event) {
let newVal = this.props.percent * this.props.max;
switch (event.key) {
case 'ArrowRight':
newVal += 1;
if (newVal <= this.props.max) this.props.onChange(newVal / this.props.max);
return;
case 'ArrowLeft':
newVal -= 1;
if (newVal >= 0) this.props.onChange(newVal / this.props.max);
return;
default:
return;
}
}
/**
* Touch start handler
* @param {Event} event DOM Event
*
*/
_touchstart(event) {
this.touchStartTimer = setTimeout(() => this.sliderInputBox._setDisplay('block'), 1500);
}
/**
* Touch end handler
* @param {Event} event DOM Event
*
*/
_touchend(event) {
this.sliderInputBox.sliderVal.focus();
clearTimeout(this.touchStartTimer);
}
/**
* Determine if the user is still dragging
* @param {SyntheticEvent} event Event
@@ -213,7 +144,7 @@ export default class Slider extends React.Component {
let width = outerWidth - (margin * 2);
let pctPos = width * this.props.percent;
return <div><svg
onMouseUp={this._up} onMouseEnter={this._enter.bind(this)} onMouseMove={this._move} onKeyUp={this._keyup} onKeyDown={this._keydown} style={style} ref={node => this.node = node} tabIndex="0">
onMouseUp={this._up} onMouseEnter={this._enter.bind(this)} onMouseMove={this._move} style={style} ref={node => this.node = node} tabIndex="0">
<rect className='primary' style={{ opacity: 0.3 }} x={margin} y='0.25em' rx='0.3em' ry='0.3em' width={width} height='0.7em' />
<rect className='primary-disabled' x={margin} y='0.45em' rx='0.15em' ry='0.15em' width={pctPos} height='0.3em' />
<circle className='primary' r={margin} cy='0.6em' cx={pctPos + margin} />
@@ -224,163 +155,6 @@ export default class Slider extends React.Component {
<text className='primary-disabled' y='3em' x='100%' style={{ textAnchor: 'end' }}>{max + axisUnit}</text>
</g>}
</svg>
<TextInputBox ref={(tb) => this.sliderInputBox = tb}
onChange={this.props.onChange}
percent={this.props.percent}
axisUnit={this.props.axisUnit}
scale={this.props.scale}
max={this.props.max}
/>
</div>;
}
}
/**
* New component to add keyboard support for sliders - works on all devices (desktop, iOS, Android)
**/
class TextInputBox extends React.Component {
static propTypes = {
axisUnit: PropTypes.string,// units (T, M, etc.)
max: PropTypes.number,
onChange: PropTypes.func.isRequired,// function which determins percent value
percent: PropTypes.number.isRequired,// value of slider
scale: PropTypes.number
};
/**
* Determine if the user is still dragging
* @param {Object} props React Component properties
*/
constructor(props) {
super(props);
this._handleFocus = this._handleFocus.bind(this);
this._handleBlur = this._handleBlur.bind(this);
this._handleChange = this._handleChange.bind(this);
this._keyup = this._keyup.bind(this);
this.state = this._getInitialState();
}
/**
* Update input value if slider changes will change props/state
* @param {Object} nextProps React Component properites
* @param {Object} nextState React Component state values
*/
componentWillReceiveProps(nextProps, nextState) {
let nextValue = nextProps.percent * nextProps.max;
// See https://stackoverflow.com/questions/32414308/updating-state-on-props-change-in-react-form
if (nextValue !== this.state.inputValue && nextValue <= nextProps.max) {
this.setState({ inputValue: nextValue });
}
}
/**
* Update slider textbox visibility/values if changes are made to slider
* @param {Object} prevProps React Component properites
* @param {Object} prevState React Component state values
*/
componentDidUpdate(prevProps, prevState) {
if (prevState.divStyle.display == 'none' && this.state.divStyle.display == 'block') {
this.enterTimer = setTimeout(() => this.sliderVal.focus(), 10);
}
if (prevProps.max !== this.props.max && this.state.inputValue > this.props.max) {
// they chose a different module
this.setState({ inputValue: this.props.max });
}
if (this.state.inputValue != prevState.inputValue && prevProps.max == this.props.max) {
this.props.onChange(this.state.inputValue / this.props.max);
}
}
/**
* Set initial state for the textbox.
* We may want to rethink this to
* try and make it a stateless component
* @returns {object} React state object with initial values set
*/
_getInitialState() {
return {
divStyle: { display:'none' },
inputStyle: { width:'4em' },
labelStyle: { marginLeft: '.1em' },
maxLength:5,
size:5,
min:0,
tabIndex:-1,
type:'number',
readOnly: true,
inputValue: this.props.percent * this.props.max
};
}
/**
*
* @param {string} val block or none
*/
_setDisplay(val) {
this.setState({
divStyle: { display:val }
});
}
/**
* Update the input value
* when textbox gets focus
*/
_handleFocus() {
this.setState({
inputValue:this._getValue()
});
}
/**
* Update inputValue when textbox loses focus
*/
_handleBlur() {
this._setDisplay('none');
if (this.state.inputValue !== '') {
this.props.onChange(this.state.inputValue / this.props.max);
} else {
this.setState({
inputValue: this.props.percent * this.props.max
});
}
}
/**
* Get the value in the text box
* @returns {number} inputValue Value of the input box
*/
_getValue() {
return this.state.inputValue;
}
/**
* Update and set limits on input box
* values depending on what user
* has selected
*
* @param {SyntheticEvent} event ReactJs onChange event
*/
_handleChange(event) {
if (event.target.value < 0) {
this.setState({ inputValue: 0 });
} else if (event.target.value <= this.props.max) {
this.setState({ inputValue: event.target.value });
} else {
this.setState({ inputValue: this.props.max });
}
}
/**
* Key up handler for input field.
* If user hits Enter key, blur/close the input field
* @param {Event} event Keyboard event
*/
_keyup(event) {
switch (event.key) {
case 'Enter':
this.sliderVal.blur();
return;
default:
return;
}
}
/**
* Get the value in the text box
* @return {React.Component} Text Input component for Slider
*/
render() {
let { axisUnit, onChange, percent, scale } = this.props;
return <div style={this.state.divStyle}><input style={this.state.inputStyle} value={this._getValue()} min={this.state.min} max={this.props.max} onChange={this._handleChange} onKeyUp={this._keyup} tabIndex={this.state.tabIndex} maxLength={this.state.maxLength} size={this.state.size} onBlur={() => {this._handleBlur();}} onFocus={() => {this._handleFocus();}} type={this.state.type} ref={(ip) => this.sliderVal = ip}/><text className="primary upp" style={this.state.labelStyle}>{this.props.axisUnit}</text></div>;
}
}

View File

@@ -9,7 +9,7 @@ import { diffDetails } from '../utils/SlotFunctions';
import { stopCtxPropagation, wrapCtxMenu } from '../utils/UtilityFunctions';
import { blueprintTooltip } from '../utils/BlueprintFunctions';
import { Module } from 'ed-forge';
import { REG_MILITARY_SLOT, REG_HARDPOINT_SLOT } from 'ed-forge/lib/data/slots';
import { TYPES } from 'ed-forge/lib/data/slots';
import autoBind from 'auto-bind';
import { toPairs } from 'lodash';
@@ -79,7 +79,7 @@ export default class Slot extends TranslatedComponent {
if (m.isEmpty()) {
return <div className="empty">
{translate(
m.getSlot().match(REG_MILITARY_SLOT) ? 'emptyrestricted' : 'empty'
m.isOnSlot(TYPES.MILITARY) ? 'emptyrestricted' : 'empty'
)}
</div>;
} else {
@@ -174,7 +174,7 @@ export default class Slot extends TranslatedComponent {
case size === 0:
// This can also happen for armour but that case was handled above
return 'U';
case Boolean(m.getSlot().match(REG_HARDPOINT_SLOT)):
case m.isOnSlot(TYPES.HARDPOINT):
return HARDPOINT_SLOT_LABELS[size];
default:
return size;