Make outfitting page working

This commit is contained in:
Felix Linker
2020-04-10 13:19:53 +02:00
parent 00c525e6ab
commit 409be7374c
13 changed files with 511 additions and 838 deletions

View File

@@ -123,6 +123,7 @@
"sideEffects": false, "sideEffects": false,
"dependencies": { "dependencies": {
"@babel/polyfill": "^7.0.0", "@babel/polyfill": "^7.0.0",
"auto-bind": "^2.1.1",
"browserify-zlib-next": "^1.0.1", "browserify-zlib-next": "^1.0.1",
"classnames": "^2.2.6", "classnames": "^2.2.6",
"coriolis-data": "../coriolis-data", "coriolis-data": "../coriolis-data",

View File

@@ -24,7 +24,6 @@ export default class AvailableModulesMenu extends TranslatedComponent {
onSelect: PropTypes.func.isRequired, onSelect: PropTypes.func.isRequired,
diffDetails: PropTypes.func, diffDetails: PropTypes.func,
m: PropTypes.object, m: PropTypes.object,
ship: PropTypes.object.isRequired,
warning: PropTypes.func, warning: PropTypes.func,
slotDiv: PropTypes.object slotDiv: PropTypes.object
}; };
@@ -49,7 +48,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
*/ */
_initState(props, context) { _initState(props, context) {
const { translate } = context.language; const { translate } = context.language;
const { m, warning, onSelect, ship } = props; const { m } = props;
const list = [], fuzzy = []; const list = [], fuzzy = [];
let currentGroup; let currentGroup;
@@ -63,13 +62,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
list.push( list.push(
<div key={'div-' + category} className="select-group cap">{catName}</div>, <div key={'div-' + category} className="select-group cap">{catName}</div>,
this._buildGroup( this._buildGroup(
ship,
m, m,
warning,
(m, event) => {
this._hideDiff(event);
onSelect(m);
},
category, category,
infos, infos,
), ),
@@ -91,15 +84,14 @@ export default class AvailableModulesMenu extends TranslatedComponent {
/** /**
* Generate React Components for Module Group * Generate React Components for Module Group
* @param {Ship} ship Ship the selection is for
* @param {Object} mountedModule Mounted Module * @param {Object} mountedModule Mounted Module
* @param {Function} warningFunc Warning function
* @param {function} onSelect Select/Mount callback
* @param {String} category Category key * @param {String} category Category key
* @param {Array} modules Available modules * @param {Array} modules Available modules
* @return {React.Component} Available Module Group contents * @return {React.Component} Available Module Group contents
*/ */
_buildGroup(ship, mountedModule, warningFunc, onSelect, category, modules) { _buildGroup(mountedModule, category, modules) {
const { warning } = this.props;
const ship = mountedModule.getShip();
const classMapping = groupBy(modules, (info) => info.meta.class); const classMapping = groupBy(modules, (info) => info.meta.class);
const itemsPerClass = Math.max( const itemsPerClass = Math.max(
@@ -133,7 +125,10 @@ export default class AvailableModulesMenu extends TranslatedComponent {
let eventHandlers = {}; let eventHandlers = {};
if (!disabled) { if (!disabled) {
const showDiff = this._showDiff.bind(this, mountedModule, info); const showDiff = this._showDiff.bind(this, mountedModule, info);
const select = onSelect.bind(null, info); const select = (event) => {
this._hideDiff(event);
this.props.onSelect(Item);
};
eventHandlers = { eventHandlers = {
onMouseEnter: this._over.bind(this, showDiff), onMouseEnter: this._over.bind(this, showDiff),
@@ -149,7 +144,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
<li key={Item} data-id={Item} <li key={Item} data-id={Item}
ref={Item === mountedModule.getItem() ? (ref) => { this.activeSlotRef = ref; } : undefined} ref={Item === mountedModule.getItem() ? (ref) => { this.activeSlotRef = ref; } : undefined}
className={cn('c', { className={cn('c', {
warning: !disabled && warningFunc && warningFunc(info), warning: !disabled && warning && warning(info),
active: mountedModule.getItem() === Item, active: mountedModule.getItem() === Item,
disabled, disabled,
hardpoint: mountSymbol, hardpoint: mountSymbol,
@@ -187,10 +182,11 @@ export default class AvailableModulesMenu extends TranslatedComponent {
_showDiff(mountedModule, hoveringModule, rect) { _showDiff(mountedModule, hoveringModule, rect) {
if (this.props.diffDetails) { if (this.props.diffDetails) {
this.touchTimeout = null; this.touchTimeout = null;
this.context.tooltip( // TODO:
this.props.diffDetails(hoveringModule, mountedModule), // this.context.tooltip(
rect, // this.props.diffDetails(hoveringModule, mountedModule),
); // rect,
// );
} }
} }

View File

@@ -3,40 +3,27 @@ import SlotSection from './SlotSection';
import Slot from './Slot'; import Slot from './Slot';
import { MountFixed, MountGimballed, MountTurret } from '../components/SvgIcons'; import { MountFixed, MountGimballed, MountTurret } from '../components/SvgIcons';
import { stopCtxPropagation } from '../utils/UtilityFunctions'; import { stopCtxPropagation } from '../utils/UtilityFunctions';
import autoBind from 'auto-bind';
/** /**
* Hardpoint slot section * Hardpoint slot section
*/ */
export default class HardpointSlotSection extends SlotSection { export default class HardpointSlotSection extends SlotSection {
/** /**
* Constructor * Constructor
* @param {Object} props React Component properties * @param {Object} props React Component properties
* @param {Object} context React Component context
*/ */
constructor(props, context) { constructor(props) {
super(props, context, 'hardpoints', 'hardpoints'); super(props, 'hardpoints');
this._empty = this._empty.bind(this); autoBind(this);
this.selectedRefId = null;
this.firstRefId = 'emptyall';
this.lastRefId = 'nl-F';
}
/**
* Handle focus when component updates
* @param {Object} prevProps React Component properties
*/
componentDidUpdate(prevProps) {
this._handleSectionFocus(prevProps,this.firstRefId, this.lastRefId);
} }
/** /**
* Empty all slots * Empty all slots
*/ */
_empty() { _empty() {
this.selectedRefId = 'emptyall'; // TODO:
this.props.ship.emptyWeapons(); // this.props.ship.emptyWeapons();
this.props.onChange();
this._close(); this._close();
} }
@@ -47,9 +34,8 @@ export default class HardpointSlotSection extends SlotSection {
* @param {SyntheticEvent} event Event * @param {SyntheticEvent} event Event
*/ */
_fill(group, mount, event) { _fill(group, mount, event) {
this.selectedRefId = group + '-' + mount; // TODO:
this.props.ship.useWeapon(group, mount, null, event.getModifierState('Alt')); // this.props.ship.useWeapon(group, mount, null, event.getModifierState('Alt'));
this.props.onChange();
this._close(); this._close();
} }
@@ -73,16 +59,12 @@ export default class HardpointSlotSection extends SlotSection {
slots.push(<Slot slots.push(<Slot
key={h.object.Slot} key={h.object.Slot}
maxClass={h.getSize()} maxClass={h.getSize()}
onOpen={this._openMenu.bind(this, h)} currentMenu={currentMenu}
onSelect={this._selectModule.bind(this, h)}
onChange={this.props.onChange}
selected={currentMenu == h}
drag={this._drag.bind(this, h)} drag={this._drag.bind(this, h)}
dragOver={this._dragOverSlot.bind(this, h)} dragOver={this._dragOverSlot.bind(this, h)}
drop={this._drop} drop={this._drop}
dropClass={this._dropClass(h, originSlot, targetSlot)} dropClass={this._dropClass(h, originSlot, targetSlot)}
ship={ship} m={h}
slot={h}
enabled={h.enabled ? true : false} enabled={h.enabled ? true : false}
/>); />);
} }
@@ -95,66 +77,67 @@ export default class HardpointSlotSection extends SlotSection {
* @param {Function} translate Translate function * @param {Function} translate Translate function
* @return {React.Component} Section menu * @return {React.Component} Section menu
*/ */
_getSectionMenu(translate) { _getSectionMenu() {
const { translate } = this.context.language;
let _fill = this._fill; let _fill = this._fill;
return <div className='select hardpoint' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}> return <div className='select hardpoint' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
<ul> <ul>
<li className='lc' tabIndex='0' onClick={this._empty} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['emptyall'] = smRef}>{translate('empty all')}</li> <li className='lc' tabIndex="0" onClick={this._empty} ref={smRef => this.sectionRefArr['emptyall'] = smRef}>{translate('empty all')}</li>
<li className='optional-hide' style={{ textAlign: 'center', marginTop: '1em' }}>{translate('PHRASE_ALT_ALL')}</li> <li className='optional-hide' style={{ textAlign: 'center', marginTop: '1em' }}>{translate('PHRASE_ALT_ALL')}</li>
</ul> </ul>
<div className='select-group cap'>{translate('pl')}</div> <div className='select-group cap'>{translate('pl')}</div>
<ul> <ul>
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'pl', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['pl-F'] = smRef}><MountFixed className='lg'/></li> <li className="c" tabIndex="0" onClick={_fill.bind(this, 'pl', 'F')}><MountFixed className='lg'/></li>
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'pl', 'G')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['pl-G'] = smRef}><MountGimballed className='lg'/></li> <li className="c" tabIndex="0" onClick={_fill.bind(this, 'pl', 'G')}><MountGimballed className='lg'/></li>
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'pl', 'T')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['pl-T'] = smRef}><MountTurret className='lg'/></li> <li className="c" tabIndex="0" onClick={_fill.bind(this, 'pl', 'T')}><MountTurret className='lg'/></li>
</ul> </ul>
<div className='select-group cap'>{translate('ul')}</div> <div className='select-group cap'>{translate('ul')}</div>
<ul> <ul>
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'ul', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['ul-F'] = smRef}><MountFixed className='lg'/></li> <li className="c" tabIndex="0" onClick={_fill.bind(this, 'ul', 'F')}><MountFixed className='lg'/></li>
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'ul', 'G')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['ul-G'] = smRef}><MountGimballed className='lg'/></li> <li className="c" tabIndex="0" onClick={_fill.bind(this, 'ul', 'G')}><MountGimballed className='lg'/></li>
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'ul', 'T')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['ul-T'] = smRef}><MountTurret className='lg'/></li> <li className="c" tabIndex="0" onClick={_fill.bind(this, 'ul', 'T')}><MountTurret className='lg'/></li>
</ul> </ul>
<div className='select-group cap'>{translate('bl')}</div> <div className='select-group cap'>{translate('bl')}</div>
<ul> <ul>
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'bl', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['bl-F'] = smRef}><MountFixed className='lg'/></li> <li className="c" tabIndex="0" onClick={_fill.bind(this, 'bl', 'F')}><MountFixed className='lg'/></li>
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'bl', 'G')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['bl-G'] = smRef}><MountGimballed className='lg'/></li> <li className="c" tabIndex="0" onClick={_fill.bind(this, 'bl', 'G')}><MountGimballed className='lg'/></li>
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'bl', 'T')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['bl-T'] = smRef}><MountTurret className='lg'/></li> <li className="c" tabIndex="0" onClick={_fill.bind(this, 'bl', 'T')}><MountTurret className='lg'/></li>
</ul> </ul>
<div className='select-group cap'>{translate('mc')}</div> <div className='select-group cap'>{translate('mc')}</div>
<ul> <ul>
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'mc', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['mc-F'] = smRef}><MountFixed className='lg'/></li> <li className="c" tabIndex="0" onClick={_fill.bind(this, 'mc', 'F')}><MountFixed className='lg'/></li>
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'mc', 'G')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['mc-G'] = smRef}><MountGimballed className='lg'/></li> <li className="c" tabIndex="0" onClick={_fill.bind(this, 'mc', 'G')}><MountGimballed className='lg'/></li>
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'mc', 'T')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['mc-T'] = smRef}><MountTurret className='lg'/></li> <li className="c" tabIndex="0" onClick={_fill.bind(this, 'mc', 'T')}><MountTurret className='lg'/></li>
</ul> </ul>
<div className='select-group cap'>{translate('c')}</div> <div className='select-group cap'>{translate('c')}</div>
<ul> <ul>
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'c', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['c-F'] = smRef}><MountFixed className='lg'/></li> <li className="c" tabIndex="0" onClick={_fill.bind(this, 'c', 'F')}><MountFixed className='lg'/></li>
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'c', 'G')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['c-G'] = smRef}><MountGimballed className='lg'/></li> <li className="c" tabIndex="0" onClick={_fill.bind(this, 'c', 'G')}><MountGimballed className='lg'/></li>
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'c', 'T')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['c-T'] = smRef}><MountTurret className='lg'/></li> <li className="c" tabIndex="0" onClick={_fill.bind(this, 'c', 'T')}><MountTurret className='lg'/></li>
</ul> </ul>
<div className='select-group cap'>{translate('fc')}</div> <div className='select-group cap'>{translate('fc')}</div>
<ul> <ul>
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'fc', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['fc-F'] = smRef}><MountFixed className='lg'/></li> <li className="c" tabIndex="0" onClick={_fill.bind(this, 'fc', 'F')}><MountFixed className='lg'/></li>
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'fc', 'G')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['fc-G'] = smRef}><MountGimballed className='lg'/></li> <li className="c" tabIndex="0" onClick={_fill.bind(this, 'fc', 'G')}><MountGimballed className='lg'/></li>
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'fc', 'T')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['fc-T'] = smRef}><MountTurret className='lg'/></li> <li className="c" tabIndex="0" onClick={_fill.bind(this, 'fc', 'T')}><MountTurret className='lg'/></li>
</ul> </ul>
<div className='select-group cap'>{translate('pa')}</div> <div className='select-group cap'>{translate('pa')}</div>
<ul> <ul>
<li className='lc' tabIndex='0' onClick={_fill.bind(this, 'pa', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['pa-F'] = smRef}>{translate('pa')}</li> <li className='lc' tabIndex="0" onClick={_fill.bind(this, 'pa', 'F')}>{translate('pa')}</li>
</ul> </ul>
<div className='select-group cap'>{translate('rg')}</div> <div className='select-group cap'>{translate('rg')}</div>
<ul> <ul>
<li className='lc' tabIndex='0' onClick={_fill.bind(this, 'rg', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['rg-F'] = smRef}>{translate('rg')}</li> <li className='lc' tabIndex="0" onClick={_fill.bind(this, 'rg', 'F')}>{translate('rg')}</li>
</ul> </ul>
<div className='select-group cap'>{translate('nl')}</div> <div className='select-group cap'>{translate('nl')}</div>
<ul> <ul>
<li className='lc' tabIndex='0' onClick={_fill.bind(this, 'nl', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['nl-F'] = smRef}>{translate('nl')}</li> <li className='lc' tabIndex="0" onClick={_fill.bind(this, 'nl', 'F')}>{translate('nl')}</li>
</ul> </ul>
<div className='select-group cap'>{translate('rfl')}</div> <div className='select-group cap'>{translate('rfl')}</div>
<ul> <ul>
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'rfl', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['rfl-F'] = smRef}><MountFixed className='lg'/></li> <li className="c" tabIndex="0" onClick={_fill.bind(this, 'rfl', 'F')}><MountFixed className='lg'/></li>
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'rfl', 'T')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['rfl-T'] = smRef}><MountTurret className='lg'/></li> <li className="c" tabIndex="0" onClick={_fill.bind(this, 'rfl', 'T')}><MountTurret className='lg'/></li>
</ul> </ul>
</div>; </div>;
} }

View File

@@ -4,6 +4,7 @@ import Slot from './Slot';
import * as ModuleUtils from '../shipyard/ModuleUtils'; import * as ModuleUtils from '../shipyard/ModuleUtils';
import { stopCtxPropagation } from '../utils/UtilityFunctions'; import { stopCtxPropagation } from '../utils/UtilityFunctions';
import { canMount } from '../utils/SlotFunctions'; import { canMount } from '../utils/SlotFunctions';
import autoBind from 'auto-bind';
/** /**
* Internal slot section * Internal slot section
@@ -12,40 +13,18 @@ export default class InternalSlotSection extends SlotSection {
/** /**
* Constructor * Constructor
* @param {Object} props React Component properties * @param {Object} props React Component properties
* @param {Object} context React Component context
*/ */
constructor(props, context) { constructor(props) {
super(props, context, 'internal', 'optional internal'); super(props, 'optional internal');
this._empty = this._empty.bind(this); autoBind(this);
this._fillWithCargo = this._fillWithCargo.bind(this);
this._fillWithCells = this._fillWithCells.bind(this);
this._fillWithArmor = this._fillWithArmor.bind(this);
this._fillWithModuleReinforcementPackages = this._fillWithModuleReinforcementPackages.bind(this);
this._fillWithFuelTanks = this._fillWithFuelTanks.bind(this);
this._fillWithLuxuryCabins = this._fillWithLuxuryCabins.bind(this);
this._fillWithFirstClassCabins = this._fillWithFirstClassCabins.bind(this);
this._fillWithBusinessClassCabins = this._fillWithBusinessClassCabins.bind(this);
this._fillWithEconomyClassCabins = this._fillWithEconomyClassCabins.bind(this);
this.selectedRefId = null;
this.firstRefId = 'emptyall';
this.lastRefId = this.sectionRefArr['pcq'] ? 'pcq' : 'pcm';
}
/**
* Handle focus when component updates
* @param {Object} prevProps React Component properties
*/
componentDidUpdate(prevProps) {
this._handleSectionFocus(prevProps,this.firstRefId, this.lastRefId);
} }
/** /**
* Empty all slots * Empty all slots
*/ */
_empty() { _empty() {
this.selectedRefId = 'emptyall'; // TODO:
this.props.ship.emptyInternal(); // this.props.ship.emptyInternal();
this.props.onChange();
this._close(); this._close();
} }
@@ -54,7 +33,6 @@ export default class InternalSlotSection extends SlotSection {
* @param {SyntheticEvent} event Event * @param {SyntheticEvent} event Event
*/ */
_fillWithCargo(event) { _fillWithCargo(event) {
this.selectedRefId = 'cargo';
let clobber = event.getModifierState('Alt'); let clobber = event.getModifierState('Alt');
let ship = this.props.ship; let ship = this.props.ship;
ship.internal.forEach((slot) => { ship.internal.forEach((slot) => {
@@ -62,7 +40,6 @@ export default class InternalSlotSection extends SlotSection {
ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E')); ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E'));
} }
}); });
this.props.onChange();
this._close(); this._close();
} }
@@ -71,7 +48,6 @@ export default class InternalSlotSection extends SlotSection {
* @param {SyntheticEvent} event Event * @param {SyntheticEvent} event Event
*/ */
_fillWithFuelTanks(event) { _fillWithFuelTanks(event) {
this.selectedRefId = 'ft';
let clobber = event.getModifierState('Alt'); let clobber = event.getModifierState('Alt');
let ship = this.props.ship; let ship = this.props.ship;
ship.internal.forEach((slot) => { ship.internal.forEach((slot) => {
@@ -79,7 +55,6 @@ export default class InternalSlotSection extends SlotSection {
ship.use(slot, ModuleUtils.findInternal('ft', slot.maxClass, 'C')); ship.use(slot, ModuleUtils.findInternal('ft', slot.maxClass, 'C'));
} }
}); });
this.props.onChange();
this._close(); this._close();
} }
@@ -88,7 +63,6 @@ export default class InternalSlotSection extends SlotSection {
* @param {SyntheticEvent} event Event * @param {SyntheticEvent} event Event
*/ */
_fillWithLuxuryCabins(event) { _fillWithLuxuryCabins(event) {
this.selectedRefId = 'pcq';
let clobber = event.getModifierState('Alt'); let clobber = event.getModifierState('Alt');
let ship = this.props.ship; let ship = this.props.ship;
ship.internal.forEach((slot) => { ship.internal.forEach((slot) => {
@@ -96,7 +70,6 @@ export default class InternalSlotSection extends SlotSection {
ship.use(slot, ModuleUtils.findInternal('pcq', Math.min(slot.maxClass, 6), 'B')); // Passenger cabins top out at 6 ship.use(slot, ModuleUtils.findInternal('pcq', Math.min(slot.maxClass, 6), 'B')); // Passenger cabins top out at 6
} }
}); });
this.props.onChange();
this._close(); this._close();
} }
@@ -105,7 +78,6 @@ export default class InternalSlotSection extends SlotSection {
* @param {SyntheticEvent} event Event * @param {SyntheticEvent} event Event
*/ */
_fillWithFirstClassCabins(event) { _fillWithFirstClassCabins(event) {
this.selectedRefId = 'pcm';
let clobber = event.getModifierState('Alt'); let clobber = event.getModifierState('Alt');
let ship = this.props.ship; let ship = this.props.ship;
ship.internal.forEach((slot) => { ship.internal.forEach((slot) => {
@@ -113,7 +85,6 @@ export default class InternalSlotSection extends SlotSection {
ship.use(slot, ModuleUtils.findInternal('pcm', Math.min(slot.maxClass, 6), 'C')); // Passenger cabins top out at 6 ship.use(slot, ModuleUtils.findInternal('pcm', Math.min(slot.maxClass, 6), 'C')); // Passenger cabins top out at 6
} }
}); });
this.props.onChange();
this._close(); this._close();
} }
@@ -122,7 +93,6 @@ export default class InternalSlotSection extends SlotSection {
* @param {SyntheticEvent} event Event * @param {SyntheticEvent} event Event
*/ */
_fillWithBusinessClassCabins(event) { _fillWithBusinessClassCabins(event) {
this.selectedRefId = 'pci';
let clobber = event.getModifierState('Alt'); let clobber = event.getModifierState('Alt');
let ship = this.props.ship; let ship = this.props.ship;
ship.internal.forEach((slot) => { ship.internal.forEach((slot) => {
@@ -130,7 +100,6 @@ export default class InternalSlotSection extends SlotSection {
ship.use(slot, ModuleUtils.findInternal('pci', Math.min(slot.maxClass, 6), 'D')); // Passenger cabins top out at 6 ship.use(slot, ModuleUtils.findInternal('pci', Math.min(slot.maxClass, 6), 'D')); // Passenger cabins top out at 6
} }
}); });
this.props.onChange();
this._close(); this._close();
} }
@@ -139,7 +108,6 @@ export default class InternalSlotSection extends SlotSection {
* @param {SyntheticEvent} event Event * @param {SyntheticEvent} event Event
*/ */
_fillWithEconomyClassCabins(event) { _fillWithEconomyClassCabins(event) {
this.selectedRefId = 'pce';
let clobber = event.getModifierState('Alt'); let clobber = event.getModifierState('Alt');
let ship = this.props.ship; let ship = this.props.ship;
ship.internal.forEach((slot) => { ship.internal.forEach((slot) => {
@@ -147,7 +115,6 @@ export default class InternalSlotSection extends SlotSection {
ship.use(slot, ModuleUtils.findInternal('pce', Math.min(slot.maxClass, 6), 'E')); // Passenger cabins top out at 6 ship.use(slot, ModuleUtils.findInternal('pce', Math.min(slot.maxClass, 6), 'E')); // Passenger cabins top out at 6
} }
}); });
this.props.onChange();
this._close(); this._close();
} }
@@ -156,7 +123,6 @@ export default class InternalSlotSection extends SlotSection {
* @param {SyntheticEvent} event Event * @param {SyntheticEvent} event Event
*/ */
_fillWithCells(event) { _fillWithCells(event) {
this.selectedRefId = 'scb';
let clobber = event.getModifierState('Alt'); let clobber = event.getModifierState('Alt');
let ship = this.props.ship; let ship = this.props.ship;
let chargeCap = 0; // Capacity of single activation let chargeCap = 0; // Capacity of single activation
@@ -167,7 +133,6 @@ export default class InternalSlotSection extends SlotSection {
chargeCap += slot.m.recharge; chargeCap += slot.m.recharge;
} }
}); });
this.props.onChange();
this._close(); this._close();
} }
@@ -176,7 +141,6 @@ export default class InternalSlotSection extends SlotSection {
* @param {SyntheticEvent} event Event * @param {SyntheticEvent} event Event
*/ */
_fillWithArmor(event) { _fillWithArmor(event) {
this.selectedRefId = 'hr';
let clobber = event.getModifierState('Alt'); let clobber = event.getModifierState('Alt');
let ship = this.props.ship; let ship = this.props.ship;
ship.internal.forEach((slot) => { ship.internal.forEach((slot) => {
@@ -184,7 +148,6 @@ export default class InternalSlotSection extends SlotSection {
ship.use(slot, ModuleUtils.findInternal('hr', Math.min(slot.maxClass, 5), 'D')); // Hull reinforcements top out at 5D ship.use(slot, ModuleUtils.findInternal('hr', Math.min(slot.maxClass, 5), 'D')); // Hull reinforcements top out at 5D
} }
}); });
this.props.onChange();
this._close(); this._close();
} }
@@ -193,7 +156,6 @@ export default class InternalSlotSection extends SlotSection {
* @param {SyntheticEvent} event Event * @param {SyntheticEvent} event Event
*/ */
_fillWithModuleReinforcementPackages(event) { _fillWithModuleReinforcementPackages(event) {
this.selectedRefId = 'mrp';
let clobber = event.getModifierState('Alt'); let clobber = event.getModifierState('Alt');
let ship = this.props.ship; let ship = this.props.ship;
ship.internal.forEach((slot) => { ship.internal.forEach((slot) => {
@@ -201,7 +163,6 @@ export default class InternalSlotSection extends SlotSection {
ship.use(slot, ModuleUtils.findInternal('mrp', Math.min(slot.maxClass, 5), 'D')); // Module reinforcements top out at 5D ship.use(slot, ModuleUtils.findInternal('mrp', Math.min(slot.maxClass, 5), 'D')); // Module reinforcements top out at 5D
} }
}); });
this.props.onChange();
this._close(); this._close();
} }
@@ -220,24 +181,16 @@ export default class InternalSlotSection extends SlotSection {
let slots = []; let slots = [];
let { currentMenu, ship } = this.props; let { currentMenu, ship } = this.props;
let { originSlot, targetSlot } = this.state; let { originSlot, targetSlot } = this.state;
let { fuelCapacity } = ship;
for (const slot of ship.getInternals(undefined, true)) { for (const m of ship.getInternals(undefined, true)) {
slots.push(<Slot slots.push(<Slot
key={slot.object.Slot} key={m.object.Slot}
maxClass={slot.getSize()} currentMenu={currentMenu}
onOpen={this._openMenu.bind(this, slot)} m={m}
onChange={this.props.onChange} drag={this._drag.bind(this, m)}
onSelect={this._selectModule.bind(this, slot)} dragOver={this._dragOverSlot.bind(this, m)}
selected={currentMenu == slot}
slot={slot}
drag={this._drag.bind(this, slot)}
dragOver={this._dragOverSlot.bind(this, slot)}
drop={this._drop} drop={this._drop}
dropClass={this._dropClass(slot, originSlot, targetSlot)} dropClass={this._dropClass(m, originSlot, targetSlot)}
fuel={fuelCapacity}
ship={ship}
enabled={slot.isEnabled()}
/>); />);
} }
@@ -250,19 +203,21 @@ export default class InternalSlotSection extends SlotSection {
* @param {Function} ship The ship * @param {Function} ship The ship
* @return {React.Component} Section menu * @return {React.Component} Section menu
*/ */
_getSectionMenu(translate, ship) { _getSectionMenu() {
const { ship } = this.props;
const { translate } = this.context.language;
return <div className='select' onClick={e => e.stopPropagation()} onContextMenu={stopCtxPropagation}> return <div className='select' onClick={e => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
<ul> <ul>
<li className='lc' tabIndex='0' onClick={this._empty} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['emptyall'] = smRef}>{translate('empty all')}</li> <li className='lc' tabIndex='0' onClick={this._empty}>{translate('empty all')}</li>
<li className='lc' tabIndex='0' onClick={this._fillWithCargo} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['cargo'] = smRef}>{translate('cargo')}</li> <li className='lc' tabIndex='0' onClick={this._fillWithCargo}>{translate('cargo')}</li>
<li className='lc' tabIndex='0' onClick={this._fillWithCells} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['scb'] = smRef}>{translate('scb')}</li> <li className='lc' tabIndex='0' onClick={this._fillWithCells}>{translate('scb')}</li>
<li className='lc' tabIndex='0' onClick={this._fillWithArmor} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['hr'] = smRef}>{translate('hr')}</li> <li className='lc' tabIndex='0' onClick={this._fillWithArmor}>{translate('hr')}</li>
<li className='lc' tabIndex='0' onClick={this._fillWithModuleReinforcementPackages} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['mrp'] = smRef}>{translate('mrp')}</li> <li className='lc' tabIndex='0' onClick={this._fillWithModuleReinforcementPackages}>{translate('mrp')}</li>
<li className='lc' tabIndex='0' onClick={this._fillWithFuelTanks} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['ft'] = smRef}>{translate('ft')}</li> <li className='lc' tabIndex='0' onClick={this._fillWithFuelTanks}>{translate('ft')}</li>
<li className='lc' tabIndex='0' onClick={this._fillWithEconomyClassCabins} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['pce'] = smRef}>{translate('pce')}</li> <li className='lc' tabIndex='0' onClick={this._fillWithEconomyClassCabins}>{translate('pce')}</li>
<li className='lc' tabIndex='0' onClick={this._fillWithBusinessClassCabins} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['pci'] = smRef}>{translate('pci')}</li> <li className='lc' tabIndex='0' onClick={this._fillWithBusinessClassCabins}>{translate('pci')}</li>
<li className='lc' tabIndex='0' onClick={this._fillWithFirstClassCabins} onKeyDown={ship.luxuryCabins ? '' : this._keyDown} ref={smRef => this.sectionRefArr['pcm'] = smRef}>{translate('pcm')}</li> <li className='lc' tabIndex='0' onClick={this._fillWithFirstClassCabins} onKeyDown={ship.luxuryCabins ? '' : this._keyDown}>{translate('pcm')}</li>
{ ship.luxuryCabins ? <li className='lc' tabIndex='0' onClick={this._fillWithLuxuryCabins} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['pcq'] = smRef}>{translate('pcq')}</li> : ''} { ship.luxuryCabins ? <li className='lc' tabIndex='0' onClick={this._fillWithLuxuryCabins}>{translate('pcq')}</li> : ''}
<li className='optional-hide' style={{ textAlign: 'center', marginTop: '1em' }}>{translate('PHRASE_ALT_ALL')}</li> <li className='optional-hide' style={{ textAlign: 'center', marginTop: '1em' }}>{translate('PHRASE_ALT_ALL')}</li>
</ul> </ul>
</div>; </div>;

View File

@@ -12,33 +12,28 @@ export default class Modification extends TranslatedComponent {
static propTypes = { static propTypes = {
m: PropTypes.instanceOf(Module).isRequired, m: PropTypes.instanceOf(Module).isRequired,
property: PropTypes.string.isRequired, property: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
highlight: PropTypes.bool, highlight: PropTypes.bool,
}; };
/** /**
* Constructor * Constructor
* @param {Object} props React Component properties * @param {Object} props React Component properties
* @param {Object} context React Component context
*/ */
constructor(props, context) { constructor(props) {
super(props); super(props);
const { m, property } = props; this.state = {};
const originalValue = m.get(property);
this.state = { originalValue, value: String(originalValue) };
} }
/** /**
* Notify listeners that a new value has been entered and commited. * Notify listeners that a new value has been entered and commited.
*/ */
_updateFinished() { _updateFinished() {
const { m, property } = this.props; const { m, property, value } = this.props;
const { value, originalValue } = this.state; const { inputValue } = this.state;
const numValue = Number(value); const numValue = Number(inputValue);
if (!isNaN(numValue) && originalValue !== numValue) { if (!isNaN(numValue) && value !== numValue) {
m.set(property, numValue); m.set(property, numValue);
this.props.onChange(); this.setState({ inputValue: undefined });
this.setState({ originalValue: numValue });
} }
} }
@@ -48,13 +43,13 @@ export default class Modification extends TranslatedComponent {
*/ */
render() { render() {
const { translate, formats } = this.context.language; const { translate, formats } = this.context.language;
const { m, property, highlight } = this.props; const { m, property, highlight, value } = this.props;
const { originalValue, value } = this.state; const { inputValue } = this.state;
// Some features only apply to specific modules; these features will be // Some features only apply to specific modules; these features will be
// undefined on items that do not belong to the same class. Filter these // undefined on items that do not belong to the same class. Filter these
// features here // features here
if (originalValue === undefined) { if (value === undefined) {
return null; return null;
} }
@@ -69,7 +64,7 @@ export default class Modification extends TranslatedComponent {
<tr> <tr>
<td className="input-container"> <td className="input-container">
<span> <span>
<NumberEditor value={value} stepModifier={1} <NumberEditor value={inputValue || value} stepModifier={1}
decimals={2} step={0.01} style={{ textAlign: 'right' }} decimals={2} step={0.01} style={{ textAlign: 'right' }}
className={cn( className={cn(
'cb', 'cb',
@@ -81,9 +76,9 @@ export default class Modification extends TranslatedComponent {
event.stopPropagation(); event.stopPropagation();
} }
}} }}
onValueChange={(value) => { onValueChange={(inputValue) => {
if (value.length <= 15) { if (inputValue.length <= 15) {
this.setState({ value }); this.setState({ inputValue });
} }
}} /> }} />
{/* TODO: support unit */} {/* TODO: support unit */}

View File

@@ -18,10 +18,7 @@ import { getModuleInfo } from 'ed-forge/lib/data/items';
export default class ModificationsMenu extends TranslatedComponent { export default class ModificationsMenu extends TranslatedComponent {
static propTypes = { static propTypes = {
className: PropTypes.string, className: PropTypes.string,
ship: PropTypes.object.isRequired,
m: PropTypes.object.isRequired, m: PropTypes.object.isRequired,
onChange: PropTypes.func.isRequired,
modButton:PropTypes.object
}; };
/** /**
@@ -47,13 +44,11 @@ export default class ModificationsMenu extends TranslatedComponent {
/** /**
* Render the blueprints * Render the blueprints
* @param {Object} props React component properties
* @param {Object} context React component context
* @return {Object} list: Array of React Components * @return {Object} list: Array of React Components
*/ */
_renderBlueprints(props, context) { _renderBlueprints() {
const { m } = props; const { m } = this.props;
const { language, tooltip, termtip } = context; const { language, tooltip, termtip } = this.context;
const { translate } = language; const { translate } = language;
const blueprints = []; const blueprints = [];
@@ -71,13 +66,13 @@ export default class ModificationsMenu extends TranslatedComponent {
style={{ width: '2em' }} style={{ width: '2em' }}
// onMouseOver={termtip.bind(null, tooltipContent)} // onMouseOver={termtip.bind(null, tooltipContent)}
// onMouseOut={tooltip.bind(null, null)} // onMouseOut={tooltip.bind(null, null)}
onClick={this._change(() => { onClick={() => {
m.setBlueprint(blueprint, grade); m.setBlueprint(blueprint, grade);
this.setState({ this.setState({
blueprintMenuOpened: false, blueprintMenuOpened: false,
specialMenuOpened: true, specialMenuOpened: true,
}); });
})} }}
ref={active ? (ref) => { this.selectedModRef = ref; } : undefined} ref={active ? (ref) => { this.selectedModRef = ref; } : undefined}
>{grade}</li> >{grade}</li>
); );
@@ -102,9 +97,9 @@ export default class ModificationsMenu extends TranslatedComponent {
* @param {Object} context React component context * @param {Object} context React component context
* @return {Object} list: Array of React Components * @return {Object} list: Array of React Components
*/ */
_renderSpecials(props, context) { _renderSpecials() {
const { m } = props; const { m } = this.props;
const { language, tooltip, termtip } = context; const { language, tooltip, termtip } = this.context;
const translate = language.translate; const translate = language.translate;
const applied = m.getExperimental(); const applied = m.getExperimental();
@@ -150,8 +145,7 @@ export default class ModificationsMenu extends TranslatedComponent {
_mkModification(property, highlight) { _mkModification(property, highlight) {
const { m } = this.props; const { m } = this.props;
return <Modification key={property} highlight={highlight} m={m} return <Modification key={property} highlight={highlight} m={m}
property={property} onChange={this._change()} property={property} value={m.get(property)} />;
/>;
} }
/** /**
@@ -182,22 +176,6 @@ export default class ModificationsMenu extends TranslatedComponent {
this.setState({ blueprintMenuOpened: !this.state.blueprintMenuOpened }); this.setState({ blueprintMenuOpened: !this.state.blueprintMenuOpened });
} }
/**
* Returns a callback that performs an action in form of a callback given as
* arguments and notifiers listeners.
* @param {function} cb Action to perform
* @returns {function} Change callback
*/
_change(cb) {
return (...args) => {
this.context.tooltip(null);
if (cb) {
cb(...args);
}
this.props.onChange();
};
}
/** /**
* Toggle the specials menu * Toggle the specials menu
*/ */
@@ -211,11 +189,11 @@ export default class ModificationsMenu extends TranslatedComponent {
* @returns {function} Callback * @returns {function} Callback
*/ */
_specialSelected(special) { _specialSelected(special) {
return this._change(() => { return () => {
const { m } = this.props; const { m } = this.props;
m.setExperimental(special); m.setExperimental(special);
this.setState({ specialMenuOpened: false }); this.setState({ specialMenuOpened: false });
}); };
} }
/** /**
@@ -232,14 +210,6 @@ export default class ModificationsMenu extends TranslatedComponent {
return; return;
} }
} }
/**
* set focus to the modification menu icon after mod menu is unmounted.
*/
componentWillUnmount() {
if (this.props.modButton) {
this.props.modButton.focus();
}
}
/** /**
* Render the list * Render the list
@@ -259,10 +229,10 @@ export default class ModificationsMenu extends TranslatedComponent {
let renderComponents = []; let renderComponents = [];
switch (true) { switch (true) {
case !appliedBlueprint || blueprintMenuOpened: case !appliedBlueprint || blueprintMenuOpened:
renderComponents = this._renderBlueprints(this.props, this.context); renderComponents = this._renderBlueprints();
break; break;
case specialMenuOpened: case specialMenuOpened:
renderComponents = this._renderSpecials(this.props, this.context); renderComponents = this._renderSpecials();
break; break;
default: default:
// Since the first case didn't apply, there is a blueprint applied so // Since the first case didn't apply, there is a blueprint applied so
@@ -301,12 +271,12 @@ export default class ModificationsMenu extends TranslatedComponent {
<div <div
className="section-menu button-inline-menu warning" className="section-menu button-inline-menu warning"
style={{ cursor: 'pointer' }} style={{ cursor: 'pointer' }}
onClick={this._change(() => { onClick={() => {
m.resetEngineering(); m.resetEngineering();
this.selectedModRef = null; this.selectedModRef = null;
this.selectedSpecialRef = null; this.selectedSpecialRef = null;
this.setState({ blueprintProgress: undefined }); this.setState({ blueprintProgress: undefined });
})} }}
onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_RESET')} onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_RESET')}
onMouseOut={tooltip.bind(null, null)} onMouseOut={tooltip.bind(null, null)}
>{translate('reset')}</div>, >{translate('reset')}</div>,
@@ -324,10 +294,10 @@ export default class ModificationsMenu extends TranslatedComponent {
'section-menu button-inline-menu', 'section-menu button-inline-menu',
{ active: blueprintProgress === 0 }, { active: blueprintProgress === 0 },
)} style={{ cursor: 'pointer' }} )} style={{ cursor: 'pointer' }}
onClick={this._change(() => { onClick={() => {
m.setBlueprintProgress(0); m.setBlueprintProgress(0);
this.setState({ blueprintProgress: 0 }); this.setState({ blueprintProgress: 0 });
})} }}
onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_WORST')} onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_WORST')}
onMouseOut={tooltip.bind(null, null)} onMouseOut={tooltip.bind(null, null)}
>{translate('0%')}</td> >{translate('0%')}</td>
@@ -336,10 +306,10 @@ export default class ModificationsMenu extends TranslatedComponent {
'section-menu button-inline-menu', 'section-menu button-inline-menu',
{ active: blueprintProgress === 0.5 }, { active: blueprintProgress === 0.5 },
)} style={{ cursor: 'pointer' }} )} style={{ cursor: 'pointer' }}
onClick={this._change(() => { onClick={() => {
m.setBlueprintProgress(0.5); m.setBlueprintProgress(0.5);
this.setState({ blueprintProgress: 0.5 }); this.setState({ blueprintProgress: 0.5 });
})} }}
onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_FIFTY')} onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_FIFTY')}
onMouseOut={tooltip.bind(null, null)} onMouseOut={tooltip.bind(null, null)}
>{translate('50%')}</td> >{translate('50%')}</td>
@@ -349,10 +319,10 @@ export default class ModificationsMenu extends TranslatedComponent {
{ active: blueprintProgress === 1 }, { active: blueprintProgress === 1 },
)} )}
style={{ cursor: 'pointer' }} style={{ cursor: 'pointer' }}
onClick={this._change(() => { onClick={() => {
m.setBlueprintProgress(1); m.setBlueprintProgress(1);
this.setState({ blueprintProgress: 1 }); this.setState({ blueprintProgress: 1 });
})} }}
onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_BEST')} onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_BEST')}
onMouseOut={tooltip.bind(null, null)} onMouseOut={tooltip.bind(null, null)}
>{translate('100%')}</td> >{translate('100%')}</td>
@@ -362,11 +332,11 @@ export default class ModificationsMenu extends TranslatedComponent {
{ active: blueprintProgress % 0.5 !== 0 }, { active: blueprintProgress % 0.5 !== 0 },
)} )}
style={{ cursor: 'pointer' }} style={{ cursor: 'pointer' }}
onClick={this._change(() => { onClick={() => {
const blueprintProgress = Math.random(); const blueprintProgress = Math.random();
m.setBlueprintProgress(blueprintProgress); m.setBlueprintProgress(blueprintProgress);
this.setState({ blueprintProgress }); this.setState({ blueprintProgress });
})} }}
onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_RANDOM')} onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_RANDOM')}
onMouseOut={tooltip.bind(null, null)} onMouseOut={tooltip.bind(null, null)}
>{translate('random')}</td> >{translate('random')}</td>

View File

@@ -1,24 +1,24 @@
import autoBind from 'auto-bind';
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import TranslatedComponent from './TranslatedComponent'; import TranslatedComponent from './TranslatedComponent';
import cn from 'classnames'; import cn from 'classnames';
import { Warning } from './SvgIcons'; import { Warning } from './SvgIcons';
import * as Calc from '../shipyard/Calculations';
import { ShipProps } from 'ed-forge'; import { ShipProps } from 'ed-forge';
const { const {
SPEED, JUMP_RANGE, TOTAL_RANGE, SHIELD_METRICS, ARMOUR_METRICS, MODULE_ARMOUR, MAX_SPEED, BOOST_SPEED, DAMAGE_METRICS, JUMP_METRICS, SHIELD_METRICS,
MODULE_PROTECTION ARMOUR_METRICS, CARGO_CAPACITY, FUEL_CAPACITY, UNLADEN_MASS, MAXIMUM_MASS,
MODULE_PROTECTION_METRICS
} = ShipProps; } = ShipProps;
import { OBJECT_EVENT } from 'ed-forge/lib/Ship';
/** /**
* Ship Summary Table / Stats * Ship Summary Table / Stats
*/ */
export default class ShipSummaryTable extends TranslatedComponent { export default class ShipSummaryTable extends TranslatedComponent {
static propTypes = { static propTypes = {
ship: PropTypes.object.isRequired, ship: PropTypes.object.isRequired,
marker: PropTypes.string.isRequired,
}; };
/** /**
@@ -27,12 +27,24 @@ export default class ShipSummaryTable extends TranslatedComponent {
*/ */
constructor(props) { constructor(props) {
super(props); super(props);
this.didContextChange = this.didContextChange.bind(this); autoBind(this);
this.state = { this.state = {
shieldColour: 'blue' shieldColour: 'blue'
}; };
} }
_onChange() {
this.forceUpdate();
}
componentWillMount() {
this.props.ship.on(OBJECT_EVENT, this._onChange);
}
componentWillUnmount() {
this.props.ship.removeListener(OBJECT_EVENT, this._onChange);
}
/** /**
* Render the table * Render the table
* @return {React.Component} Summary table * @return {React.Component} Summary table
@@ -45,31 +57,39 @@ export default class ShipSummaryTable extends TranslatedComponent {
let formats = language.formats; let formats = language.formats;
let { time, int, round, f1, f2 } = formats; let { time, int, round, f1, f2 } = formats;
let hide = tooltip.bind(null, null); let hide = tooltip.bind(null, null);
const shieldGenerator = ship.getShieldGenerator();
const sgClassNames = cn({ warning: shieldGenerator && !ship.shield, muted: !shieldGenerator }); const speed = ship.get(MAX_SPEED);
const sgTooltip = shieldGenerator ? 'TT_SUMMARY_SHIELDS' : 'TT_SUMMARY_SHIELDS_NONFUNCTIONAL'; const shipBoost = ship.get(BOOST_SPEED);
const timeToDrain = Calc.timeToDrainWep(ship, 4); const canThrust = 0 < speed;
const canThrust = ship.canThrust(cargo, ship.fuelCapacity); const canBoost = canThrust && !isNaN(shipBoost);
const speedTooltip = canThrust ? 'TT_SUMMARY_SPEED' : 'TT_SUMMARY_SPEED_NONFUNCTIONAL'; const speedTooltip = canThrust ? 'TT_SUMMARY_SPEED' : 'TT_SUMMARY_SPEED_NONFUNCTIONAL';
const canBoost = ship.canBoost(cargo, ship.fuelCapacity);
const boostTooltip = canBoost ? 'TT_SUMMARY_BOOST' : canThrust ? 'TT_SUMMARY_BOOST_NONFUNCTIONAL' : 'TT_SUMMARY_SPEED_NONFUNCTIONAL'; const boostTooltip = canBoost ? 'TT_SUMMARY_BOOST' : canThrust ? 'TT_SUMMARY_BOOST_NONFUNCTIONAL' : 'TT_SUMMARY_SPEED_NONFUNCTIONAL';
const canJump = ship.getSlotStatus(ship.standard[2]) == 3;
const sgMetrics = Calc.shieldMetrics(ship, pips.sys); const sgMetrics = ship.get(SHIELD_METRICS);
const shipBoost = canBoost ? Calc.calcBoost(ship) : 'No Boost'; const armourMetrics = ship.get(ARMOUR_METRICS);
const restingHeat = Math.sqrt(((ship.standard[0].m.pgen * ship.standard[0].m.eff) / ship.heatCapacity) / 0.2); const damageMetrics = ship.get(DAMAGE_METRICS);
const armourMetrics = Calc.armourMetrics(ship); const moduleProtectionMetrics = ship.get(MODULE_PROTECTION_METRICS);
let shieldColour = 'blue'; const timeToDrain = damageMetrics.timeToDrain[8];
if (shieldGenerator && shieldGenerator.m.grp === 'psg') {
shieldColour = 'green'; const shieldGenerator = ship.getShieldGenerator();
} else if (shieldGenerator && shieldGenerator.m.grp === 'bsg') { const sgClassNames = cn({
shieldColour = 'purple'; warning: shieldGenerator && !shieldGenerator.isEnabled(),
muted: !shieldGenerator,
});
const sgTooltip = shieldGenerator ? 'TT_SUMMARY_SHIELDS' : 'TT_SUMMARY_SHIELDS_NONFUNCTIONAL';
let shieldColour;
switch (shieldGenerator.readMeta('type')) {
case 'biweaveshieldgen': shieldColour = 'purple'; break;
case 'prismaticshieldgen': shieldColour = 'green'; break;
default: shieldColour = 'blue';
} }
this.state = { this.state = {
shieldColour shieldColour
}; };
let speed = ship.get(SPEED); const jumpRangeMetrics = ship.getMetrics(JUMP_METRICS);
let jumpRange = ship.get(JUMP_RANGE); // TODO:
const canJump = true;
return <div id='summary'> return <div id='summary'>
<div style={{display: "table", width: "100%"}}> <div style={{display: "table", width: "100%"}}>
@@ -79,7 +99,7 @@ export default class ShipSummaryTable extends TranslatedComponent {
<tr className='main'> <tr className='main'>
<th rowSpan={2} className={ cn({ 'bg-warning-disabled': speed == 0 }) }>{translate('speed')}</th> <th rowSpan={2} className={ cn({ 'bg-warning-disabled': speed == 0 }) }>{translate('speed')}</th>
<th rowSpan={2} className={ cn({ 'bg-warning-disabled': !canBoost }) }>{translate('boost')}</th> <th rowSpan={2} className={ cn({ 'bg-warning-disabled': !canBoost }) }>{translate('boost')}</th>
<th colSpan={5} className={ cn({ 'bg-warning-disabled': jumpRange == 0 }) }>{translate('jump range')}</th> <th colSpan={5} className={ cn({ 'bg-warning-disabled': jumpRangeMetrics.jumpRange == 0 }) }>{translate('jump range')}</th>
<th rowSpan={2}>{translate('shield')}</th> <th rowSpan={2}>{translate('shield')}</th>
<th rowSpan={2}>{translate('integrity')}</th> <th rowSpan={2}>{translate('integrity')}</th>
<th rowSpan={2}>{translate('DPS')}</th> <th rowSpan={2}>{translate('DPS')}</th>
@@ -97,11 +117,11 @@ export default class ShipSummaryTable extends TranslatedComponent {
<th rowSpan={2}>{translate('resting heat (Beta)')}</th> <th rowSpan={2}>{translate('resting heat (Beta)')}</th>
</tr> </tr>
<tr> <tr>
<th className={ cn({ 'lft': true, 'bg-warning-disabled': !canJump }) }>{translate('max')}</th> <th className="lft">{translate('max')}</th>
<th className={ cn({ 'bg-warning-disabled': !canJump }) }>{translate('unladen')}</th> <th>{translate('unladen')}</th>
<th className={ cn({ 'bg-warning-disabled': !canJump }) }>{translate('laden')}</th> <th>{translate('laden')}</th>
<th className={ cn({ 'bg-warning-disabled': !canJump }) }>{translate('total unladen')}</th> <th>{translate('total unladen')}</th>
<th className={ cn({ 'bg-warning-disabled': !canJump }) }>{translate('total laden')}</th> <th>{translate('total laden')}</th>
<th className='lft'>{translate('hull')}</th> <th className='lft'>{translate('hull')}</th>
<th>{translate('unladen')}</th> <th>{translate('unladen')}</th>
<th>{translate('laden')}</th> <th>{translate('laden')}</th>
@@ -109,30 +129,88 @@ export default class ShipSummaryTable extends TranslatedComponent {
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<td onMouseEnter={termtip.bind(null, speedTooltip, { cap: 0 })} onMouseLeave={hide}>{ canThrust ? <span>{int(ship.calcSpeed(4, ship.fuelCapacity, 0, false))}{u['m/s']}</span> : <span className='warning'>0 <Warning/></span> }</td> <td onMouseEnter={termtip.bind(null, speedTooltip, { cap: 0 })}
<td onMouseEnter={termtip.bind(null, boostTooltip, { cap: 0 })} onMouseLeave={hide}>{ canBoost ? <span>{int(ship.calcSpeed(4, ship.fuelCapacity, 0, true))}{u['m/s']}</span> : <span className='warning'>0 <Warning/></span> }</td> onMouseLeave={hide}
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_MAX_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{ canJump ? <span>{ f2(Calc.jumpRange(ship.unladenMass + ship.standard[2].m.getMaxFuelPerJump(), ship.standard[2].m, ship.standard[2].m.getMaxFuelPerJump(), ship))}{u.LY}</span> : <span className='warning'>0 <Warning/></span> }</td> >{canThrust
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{ canJump ? <span>{f2(Calc.jumpRange(ship.unladenMass + ship.fuelCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</span> : <span className='warning'>0 <Warning/></span> }</td> ? <span>{int(speed)}{u['m/s']}</span>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{ canJump ? <span>{f2(Calc.jumpRange(ship.unladenMass + ship.fuelCapacity + ship.cargoCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</span> : <span className='warning'>0 <Warning/></span> }</td> : <span className='warning'>0<Warning/></span>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_TOTAL_JUMP', { cap: 0 })} onMouseLeave={hide}>{ canJump ? <span>{f2(Calc.totalJumpRange(ship.unladenMass + ship.fuelCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</span> : <span className='warning'>0 <Warning/></span> }</td> }</td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_TOTAL_JUMP', { cap: 0 })} onMouseLeave={hide}>{ canJump ? <span>{f2(Calc.totalJumpRange(ship.unladenMass + ship.fuelCapacity + ship.cargoCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</span> : <span className='warning'>0 <Warning/></span> }</td> <td onMouseEnter={termtip.bind(null, boostTooltip, { cap: 0 })}
<td className={sgClassNames} onMouseEnter={termtip.bind(null, sgTooltip, { cap: 0 })} onMouseLeave={hide}>{int(ship.shield)}{u.MJ}</td> onMouseLeave={hide}
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_INTEGRITY', { cap: 0 })} onMouseLeave={hide}>{int(ship.armour)}</td> >{canBoost
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_DPS', { cap: 0 })} onMouseLeave={hide}>{f1(ship.totalDps)}</td> ? <span>{int(shipBoost)}{u['m/s']}</span>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_EPS', { cap: 0 })} onMouseLeave={hide}>{f1(ship.totalEps)}</td> : <span className='warning'>0<Warning/></span>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_TTD', { cap: 0 })} onMouseLeave={hide}>{timeToDrain === Infinity ? '∞' : time(timeToDrain)}</td> }</td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_MAX_SINGLE_JUMP', { cap: 0 })}
onMouseLeave={hide}
>{canJump
// TODO:
? <span>{NaN}{u.LY}</span>
: <span className='warning'>0<Warning/></span>
}</td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_SINGLE_JUMP', { cap: 0 })}
onMouseLeave={hide}
>{canJump
// TODO:
? <span>{NaN}{u.LY}</span>
: <span className='warning'>0<Warning/></span>
}</td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_SINGLE_JUMP', { cap: 0 })}
onMouseLeave={hide}
>{canJump
? <span>{f2(jumpRangeMetrics.jumpRange)}{u.LY}</span>
: <span className='warning'>0<Warning/></span>
}</td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_TOTAL_JUMP', { cap: 0 })}
onMouseLeave={hide}
>{canJump
// TODO:
? <span>{NaN}{u.LY}</span>
: <span className='warning'>0 <Warning/></span>
}</td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_TOTAL_JUMP', { cap: 0 })}
onMouseLeave={hide}
>{canJump
? <span>{f2(jumpRangeMetrics.totalRange)}{u.LY}</span>
: <span className='warning'>0<Warning/></span>
}</td>
<td className={sgClassNames}
onMouseEnter={termtip.bind(null, sgTooltip, { cap: 0 })}
onMouseLeave={hide}
>{int(sgMetrics.shieldStrength)}{u.MJ}</td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_INTEGRITY', { cap: 0 })}
onMouseLeave={hide}
>{int(armourMetrics.armour)}</td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_DPS', { cap: 0 })}
onMouseLeave={hide}
>{f1(damageMetrics.dps)}</td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_EPS', { cap: 0 })}
onMouseLeave={hide}
>{f1(damageMetrics.eps)}</td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_TTD', { cap: 0 })}
onMouseLeave={hide}
>{timeToDrain === Infinity ? '∞' : time(timeToDrain)}</td>
{/* <td>{f1(ship.totalHps)}</td> */} {/* <td>{f1(ship.totalHps)}</td> */}
<td>{round(ship.cargoCapacity)}{u.T}</td> <td>{round(ship.get(CARGO_CAPACITY))}{u.T}</td>
<td>{ship.passengerCapacity}</td> {/* TODO: PAX */}
<td>{round(ship.fuelCapacity)}{u.T}</td> <td>{NaN}</td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_HULL_MASS', { cap: 0 })} onMouseLeave={hide}>{ship.hullMass}{u.T}</td> <td>{round(ship.get(FUEL_CAPACITY))}{u.T}</td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_MASS', { cap: 0 })} onMouseLeave={hide}>{int(ship.unladenMass)}{u.T}</td> <td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_HULL_MASS', { cap: 0 })}
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_MASS', { cap: 0 })} onMouseLeave={hide}>{int(ship.ladenMass)}{u.T}</td> onMouseLeave={hide}
<td>{int(ship.hardness)}</td> >{ship.getBaseProperty('hullmass')}{u.T}</td>
<td>{ship.crew}</td> <td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_MASS', { cap: 0 })}
<td>{ship.masslock}</td> onMouseLeave={hide}
<td>{shipBoost !== 'No Boost' ? formats.time(shipBoost) : 'No Boost'}</td> >{int(ship.get(UNLADEN_MASS))}{u.T}</td>
<td>{formats.pct(restingHeat)}</td> <td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_MASS', { cap: 0 })}
onMouseLeave={hide}
>{int(ship.get(MAXIMUM_MASS))}{u.T}</td>
<td>{int(ship.getBaseProperty('hardness'))}</td>
<td>{ship.readMeta('crew')}</td>
<td>{ship.getBaseProperty('masslock')}</td>
{/* TODO: boost intervall */}
<td>{NaN}</td>
{/* TODO: resting heat */}
<td>{NaN}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@@ -161,19 +239,19 @@ export default class ShipSummaryTable extends TranslatedComponent {
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<td>{translate(shieldGenerator && shieldGenerator.m.grp || 'No Shield')}</td> <td>{translate(shieldGenerator.readMeta('type') || 'No Shield')}</td>
<td>{formats.pct1(ship.shieldExplRes)}</td> <td>{formats.pct1(1 - sgMetrics.explosive.damageMultiplier)}</td>
<td>{formats.pct1(ship.shieldKinRes)}</td> <td>{formats.pct1(1 - sgMetrics.kinetic.damageMultiplier)}</td>
<td>{formats.pct1(ship.shieldThermRes)}</td> <td>{formats.pct1(1 - sgMetrics.thermal.damageMultiplier)}</td>
<td></td> <td></td>
<td>{int(ship && sgMetrics.summary > 0 ? sgMetrics.summary : 0)}{u.MJ}</td> <td>{int(sgMetrics.shieldStrength || 0)}{u.MJ}</td>
<td>{int(ship && sgMetrics.summary > 0 ? sgMetrics.summary / sgMetrics.explosive.base : 0)}{u.MJ}</td> <td>{int(sgMetrics.shieldStrength / sgMetrics.explosive.damageMultiplier || 0)}{u.MJ}</td>
<td>{int(ship && sgMetrics.summary ? sgMetrics.summary / sgMetrics.kinetic.base : 0)}{u.MJ}</td> <td>{int(sgMetrics.shieldStrength / sgMetrics.kinetic.damageMultiplier || 0)}{u.MJ}</td>
<td>{int(ship && sgMetrics.summary ? sgMetrics.summary / sgMetrics.thermal.base : 0)}{u.MJ}</td> <td>{int(sgMetrics.shieldStrength / sgMetrics.thermal.damageMultiplier || 0)}{u.MJ}</td>
<td></td> <td></td>
<td>{sgMetrics && sgMetrics.recover === Math.Inf ? translate('Never') : formats.time(sgMetrics.recover)}</td> <td>{formats.time(sgMetrics.recover) || translate('Never')}</td>
<td>{sgMetrics && sgMetrics.recharge === Math.Inf ? translate('Never') : formats.time(sgMetrics.recharge)}</td> <td>{formats.time(sgMetrics.recharge) || translate('Never')}</td>
</tr> </tr>
</tbody> </tbody>
<thead> <thead>
@@ -200,19 +278,18 @@ export default class ShipSummaryTable extends TranslatedComponent {
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<td>{translate(ship && ship.bulkheads && ship.bulkheads.m && ship.bulkheads.m.name || 'No Armour')}</td> <td>{translate(ship.getAlloys().readMeta('type') || 'No Armour')}</td>
<td>{formats.pct1(ship.hullExplRes)}</td> <td>{formats.pct1(1 - armourMetrics.explosive.damageMultiplier)}</td>
<td>{formats.pct1(ship.hullKinRes)}</td> <td>{formats.pct1(1 - armourMetrics.kinetic.damageMultiplier)}</td>
<td>{formats.pct1(ship.hullThermRes)}</td> <td>{formats.pct1(1 - armourMetrics.thermal.damageMultiplier)}</td>
<td>{formats.pct1(ship.hullCausRes)}</td> <td>{formats.pct1(1 - armourMetrics.caustic.damageMultiplier)}</td>
<td>{int(armourMetrics.total)}</td> <td>{int(armourMetrics.armour)}</td>
<td>{int(armourMetrics.total / armourMetrics.explosive.total)}</td> <td>{int(armourMetrics.armour / armourMetrics.explosive.damageMultiplier)}</td>
<td>{int(armourMetrics.total/ armourMetrics.kinetic.total)}</td> <td>{int(armourMetrics.armour / armourMetrics.kinetic.damageMultiplier)}</td>
<td>{int(armourMetrics.total / armourMetrics.thermal.total)}</td> <td>{int(armourMetrics.armour / armourMetrics.thermal.damageMultiplier)}</td>
<td>{int(armourMetrics.total/ armourMetrics.caustic.total)}</td> <td>{int(armourMetrics.armour / armourMetrics.caustic.damageMultiplier)}</td>
<td>{int(armourMetrics.modulearmour)}</td> <td>{int(moduleProtectionMetrics.moduleArmour)}</td>
<td>{int(armourMetrics.moduleprotection * 100) + '%'}</td> <td>{formats.pct1(1 - moduleProtectionMetrics.moduleProtection)}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@@ -1,5 +1,4 @@
import React from 'react'; import React from 'react';
import Persist from '../stores/Persist';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import TranslatedComponent from './TranslatedComponent'; import TranslatedComponent from './TranslatedComponent';
import cn from 'classnames'; import cn from 'classnames';
@@ -9,8 +8,9 @@ import ModificationsMenu from './ModificationsMenu';
import { diffDetails } from '../utils/SlotFunctions'; import { diffDetails } from '../utils/SlotFunctions';
import { stopCtxPropagation, wrapCtxMenu } from '../utils/UtilityFunctions'; import { stopCtxPropagation, wrapCtxMenu } from '../utils/UtilityFunctions';
import { blueprintTooltip } from '../utils/BlueprintFunctions'; import { blueprintTooltip } from '../utils/BlueprintFunctions';
import { Ship, Module } from 'ed-forge'; import { Module } from 'ed-forge';
import { REG_MILITARY_SLOT, REG_HARDPOINT_SLOT } from 'ed-forge/lib/data/slots'; import { REG_MILITARY_SLOT, REG_HARDPOINT_SLOT } from 'ed-forge/lib/data/slots';
import autoBind from 'auto-bind';
const HARDPOINT_SLOT_LABELS = { const HARDPOINT_SLOT_LABELS = {
1: 'S', 1: 'S',
@@ -24,11 +24,8 @@ const HARDPOINT_SLOT_LABELS = {
*/ */
export default class Slot extends TranslatedComponent { export default class Slot extends TranslatedComponent {
static propTypes = { static propTypes = {
onSelect: PropTypes.func.isRequired, currentMenu: PropTypes.any,
onOpen: PropTypes.func.isRequired, m: PropTypes.instanceOf(Module),
selected: PropTypes.bool,
slot: PropTypes.instanceOf(Module),
ship: PropTypes.instanceOf(Ship),
warning: PropTypes.func, warning: PropTypes.func,
drag: PropTypes.func, drag: PropTypes.func,
drop: PropTypes.func, drop: PropTypes.func,
@@ -41,31 +38,53 @@ export default class Slot extends TranslatedComponent {
*/ */
constructor(props) { constructor(props) {
super(props); super(props);
autoBind(this);
this.state = { menuIndex: 0 };
}
this._modificationsSelected = false; /**
* Opens a menu while setting state.
this._contextMenu = wrapCtxMenu(this._contextMenu.bind(this)); * @param {Object} newMenuIndex New menu index
this._getMaxClassLabel = this._getMaxClassLabel.bind(this); * @param {Event} event Event object
*/
_openMenu(newMenuIndex, event) {
const slotName = this.props.m.getSlot();
if (
this.props.currentMenu === slotName &&
newMenuIndex === this.state.menuIndex
) {
this.context.closeMenu();
} {
this.setState({ menuIndex: newMenuIndex });
this.context.openMenu(slotName);
}
// If we don't stop event propagation, the underlying divs also might
// get clicked which would open up other menus
event.stopPropagation();
} }
/** /**
* Generate the slot contents * Generate the slot contents
* @param {Object} m Mounted Module
* @param {Function} translate Translate function
* @param {Object} formats Localized Formats map
* @param {Object} u Localized Units Map
* @return {React.Component} Slot contents * @return {React.Component} Slot contents
*/ */
_getSlotDetails(m, translate, formats, u) { _getSlotDetails() {
if (m) { const { m } = this.props;
let classRating = String(m.getSize()) + m.getRating(); let { termtip, tooltip, language } = this.context;
let { drag, drop, ship } = this.props; const { translate, units, formats } = language;
let { termtip, tooltip } = this.context;
let showModuleResistances = Persist.showModuleResistances(); if (m.isEmpty()) {
return <div className="empty">
{translate(
m.getSlot().match(REG_MILITARY_SLOT) ? 'emptyrestricted' : 'empty'
)}
</div>;
} else {
let classRating = String(m.getClass()) + m.getRating();
let { drag, drop } = this.props;
// Modifications tooltip shows blueprint and grade, if available // Modifications tooltip shows blueprint and grade, if available
// let modTT = translate('modified'); let modTT = translate('modified');
// const blueprint = m.getBlueprint(); const blueprint = m.getBlueprint();
// const experimental = m.getExperimental(); // const experimental = m.getExperimental();
// const grade = m.getGrade(); // const grade = m.getGrade();
// if (blueprint) { // if (blueprint) {
@@ -82,12 +101,10 @@ export default class Slot extends TranslatedComponent {
// } // }
let mass = m.get('mass') || m.get('cargo') || m.get('fuel') || 0; let mass = m.get('mass') || m.get('cargo') || m.get('fuel') || 0;
const enabled = m.isEnabled(); const disabled = !m.isEnabled();
const className = cn('details', enabled ? '' : 'disabled');
return ( return (
<div <div
className={className} className={cn('details', { disabled })}
draggable="true" draggable="true"
onDragStart={drag} onDragStart={drag}
onDragEnd={drop} onDragEnd={drop}
@@ -95,65 +112,26 @@ export default class Slot extends TranslatedComponent {
<div className={'cb'}> <div className={'cb'}>
<div className={'l'}> <div className={'l'}>
{classRating} {translate(m.readMeta('type'))} {classRating} {translate(m.readMeta('type'))}
{m.mods && Object.keys(m.mods).length > 0 ? ( {blueprint && (
<span <span
onMouseOver={termtip.bind(null, modTT)} onMouseOver={termtip.bind(null, modTT)}
onMouseOut={tooltip.bind(null, null)} onMouseOut={tooltip.bind(null, null)}
> >
<Modified /> <Modified />
</span> </span>
) : (
''
)} )}
</div> </div>
<div className={'r'}> <div className={'r'}>
{formats.round(mass)} {formats.round(mass)}
{u.T} {units.T}
</div> </div>
</div> </div>
<div className={'cb'}> <div className={'cb'}>
{/* { m.getOptMass() ? <div className={'l'}>{translate('optmass', 'sg')}: {formats.int(m.getOptMass())}{u.T}</div> : null }
{ m.getMaxMass() ? <div className={'l'}>{translate('maxmass', 'sg')}: {formats.int(m.getMaxMass())}{u.T}</div> : null }
{ m.bins ? <div className={'l'}>{m.bins} <u>{translate('bins')}</u></div> : null }
{ m.bays ? <div className={'l'}>{translate('bays')}: {m.bays}</div> : null }
{ m.rebuildsperbay ? <div className={'l'}>{translate('rebuildsperbay')}: {m.rebuildsperbay}</div> : null }
{ m.rate ? <div className={'l'}>{translate('rate')}: {m.rate}{u.kgs}&nbsp;&nbsp;&nbsp;{translate('refuel time')}: {formats.time(this.props.fuel * 1000 / m.rate)}</div> : null }
{ m.getAmmo() && m.grp !== 'scb' ? <div className={'l'}>{translate('ammunition')}: {formats.gen(m.getAmmo())}</div> : null }
{ m.getSpinup() ? <div className={'l'}>{translate('spinup')}: {formats.f1(m.getSpinup())}{u.s}</div> : null }
{ m.getDuration() ? <div className={'l'}>{translate('duration')}: {formats.f1(m.getDuration())}{u.s}</div> : null }
{ m.grp === 'scb' ? <div className={'l'}>{translate('cells')}: {formats.int(m.getAmmo() + 1)}</div> : null }
{ m.grp === 'gsrp' ? <div className={'l'}>{translate('shield addition')}: {formats.f1(m.getShieldAddition())}{u.MJ}</div> : null }
{ m.grp === 'gfsb' ? <div className={'l'}>{translate('jump addition')}: {formats.f1(m.getJumpBoost())}{u.LY}</div> : null }
{ m.grp === 'gs' ? <div className={'l'}>{translate('shield addition')}: {formats.f1(m.getShieldAddition())}{u.MJ}</div> : null }
{ m.getShieldReinforcement() ? <div className={'l'}>{translate('shieldreinforcement')}: {formats.f1(m.getDuration() * m.getShieldReinforcement())}{u.MJ}</div> : null }
{ m.getShieldReinforcement() ? <div className={'l'}>{translate('total')}: {formats.int((m.getAmmo() + 1) * (m.getDuration() * m.getShieldReinforcement()))}{u.MJ}</div> : null }
{ m.repair ? <div className={'l'}>{translate('repair')}: {m.repair}</div> : null }
{ m.getFacingLimit() ? <div className={'l'}>{translate('facinglimit')} {formats.f1(m.getFacingLimit())}°</div> : null }
{ m.getRange() ? <div className={'l'}>{translate('range')} {formats.f2(m.getRange())}{u.km}</div> : null }
{ m.getRangeT() ? <div className={'l'}>{translate('ranget')} {formats.f1(m.getRangeT())}{u.s}</div> : null }
{ m.getTime() ? <div className={'l'}>{translate('time')}: {formats.time(m.getTime())}</div> : null }
{ m.getHackTime() ? <div className={'l'}>{translate('hacktime')}: {formats.time(m.getHackTime())}</div> : null }
{ m.maximum ? <div className={'l'}>{translate('max')}: {(m.maximum)}</div> : null }
{ m.rangeLS ? <div className={'l'}>{translate('range')}: {m.rangeLS}{u.Ls}</div> : null }
{ m.rangeLS === null ? <div className={'l'}>∞{u.Ls}</div> : null }
{ m.rangeRating ? <div className={'l'}>{translate('range')}: {m.rangeRating}</div> : null }
{ m.passengers ? <div className={'l'}>{translate('passengers')}: {m.passengers}</div> : null }
{ m.getRegenerationRate() ? <div className='l'>{translate('regen')}: {formats.round1(m.getRegenerationRate())}{u.ps}</div> : null }
{ m.getBrokenRegenerationRate() ? <div className='l'>{translate('brokenregen')}: {formats.round1(m.getBrokenRegenerationRate())}{u.ps}</div> : null }
{ showModuleResistances && m.getExplosiveResistance() ? <div className='l'>{translate('explres')}: {formats.pct(m.getExplosiveResistance())}</div> : null }
{ showModuleResistances && m.getKineticResistance() ? <div className='l'>{translate('kinres')}: {formats.pct(m.getKineticResistance())}</div> : null }
{ showModuleResistances && m.getThermalResistance() ? <div className='l'>{translate('thermres')}: {formats.pct(m.getThermalResistance())}</div> : null }
{ showModuleResistances && m.getCausticResistance() ? <div className='l'>{translate('causres')}: {formats.pct(m.getCausticResistance())}</div> : null }
{ m.getHullReinforcement() ? <div className='l'>{translate('armour')}: {formats.int(m.getHullReinforcement() + ship.baseArmour * m.getModValue('hullboost') / 10000)}</div> : null }
{ m.getProtection() ? <div className='l'>{translate('protection')}: {formats.rPct(m.getProtection())}</div> : null }
{ m.getIntegrity() ? <div className='l'>{translate('integrity')}: {formats.int(m.getIntegrity())}</div> : null } */}
{(m.getApplicableBlueprints() || []).length > 0 ? ( {(m.getApplicableBlueprints() || []).length > 0 ? (
<div className="r" <div className="r">
ref={(modButton) => (this.modButton = modButton)} <button onClick={this._openMenu.bind(this, 1)}
>
<button onClick={this._toggleModifications.bind(this)}
onContextMenu={stopCtxPropagation} onContextMenu={stopCtxPropagation}
onMouseOver={termtip.bind(null, 'modifications')} onMouseOver={termtip.bind(null, translate('modifications'))}
onMouseOut={tooltip.bind(null, null)} onMouseOut={tooltip.bind(null, null)}
> >
<ListModifications /> <ListModifications />
@@ -163,35 +141,24 @@ export default class Slot extends TranslatedComponent {
</div> </div>
</div> </div>
); );
} else {
return <div className={'empty'}>{translate('empty')}</div>;
} }
} }
/**
* Get the CSS class name for the slot. Can/should be overriden
* as necessary.
* @return {string} CSS Class name
*/
_getClassNames() {
return null;
}
/** /**
* Get the label for the slot size/class * Get the label for the slot size/class
* Should be overriden if necessary * Should be overriden if necessary
* @return {string} label * @return {string} label
*/ */
_getMaxClassLabel() { _getMaxClassLabel() {
const { slot } = this.props; const { m } = this.props;
let size = slot.getSize(); let size = m.getSize();
switch (true) { switch (true) {
case slot.getSlot() === 'armour': case m.getSlot() === 'armour':
return ''; return '';
case size === 0: case size === 0:
// This can also happen for armour but that case was handled above // This can also happen for armour but that case was handled above
return 'U'; return 'U';
case Boolean(slot.getSlot().match(REG_HARDPOINT_SLOT)): case Boolean(m.getSlot().match(REG_HARDPOINT_SLOT)):
return HARDPOINT_SLOT_LABELS[size]; return HARDPOINT_SLOT_LABELS[size];
default: default:
return size; return size;
@@ -205,7 +172,13 @@ export default class Slot extends TranslatedComponent {
_contextMenu(event) { _contextMenu(event) {
event.stopPropagation(); event.stopPropagation();
event.preventDefault(); event.preventDefault();
this.props.onSelect(null,null); const { m } = this.props;
m.reset();
if (this.props.currentMenu === m.getSlot()) {
this.context.closeMenu();
} else {
this.forceUpdate();
}
} }
/** /**
@@ -215,65 +188,38 @@ export default class Slot extends TranslatedComponent {
render() { render() {
let language = this.context.language; let language = this.context.language;
let translate = language.translate; let translate = language.translate;
let { ship, slot, dropClass, dragOver, onOpen, onChange, selected, onSelect, warning } = this.props; let { currentMenu, m, dropClass, dragOver, warning } = this.props;
let slotDetails, modificationsMarker, menu; const { menuIndex } = this.state;
if (!selected) {
// If not selected then sure that modifications flag is unset
this._modificationsSelected = false;
}
if (!slot.isEmpty()) {
slotDetails = this._getSlotDetails(slot, translate, language.formats, language.units); // Must be implemented by sub classes
} else {
slotDetails = <div className={'empty'}>
{translate(
slot.getSlot().match(REG_MILITARY_SLOT) ? 'emptyrestricted' : 'empty'
)}
</div>;
}
if (selected) {
if (this._modificationsSelected) {
menu = <ModificationsMenu
className={this._getClassNames()}
onChange={onChange}
ship={ship}
m={slot}
modButton = {this.modButton}
/>;
} else {
menu = <AvailableModulesMenu
className={this._getClassNames()}
m={slot}
ship={ship}
onSelect={onSelect}
warning={warning}
diffDetails={diffDetails.bind(ship, this.context.language)}
/>;
}
}
// TODO: implement touch dragging // TODO: implement touch dragging
const selected = currentMenu === m.getSlot();
return ( return (
<div className={cn('slot', dropClass, { selected })} onClick={onOpen} <div
className={cn('slot', dropClass, { selected })}
onContextMenu={this._contextMenu} onContextMenu={this._contextMenu}
onDragOver={dragOver} tabIndex="0" onDragOver={dragOver} tabIndex="0"
onClick={this._openMenu.bind(this, 0)}
> >
<div className='details-container'> <div className={cn(
<div className='sz'>{this._getMaxClassLabel(translate)}</div> 'details-container',
{slotDetails} { warning: warning && warning(m) },
</div> )}>
{menu} <div className="sz">{this._getMaxClassLabel(translate)}</div>
{this._getSlotDetails()}
</div>
{selected && menuIndex === 0 &&
<AvailableModulesMenu
m={m}
onSelect={(item) => {
m.setItem(item);
this.context.closeMenu();
}}
warning={warning}
// diffDetails={diffDetails.bind(ship, this.context.language)}
/>}
{selected && menuIndex === 1 &&
<ModificationsMenu m={m} />}
</div> </div>
); );
} }
/**
* Toggle the modifications flag when selecting the modifications icon
*/
_toggleModifications() {
this._modificationsSelected = !this._modificationsSelected;
}
} }

View File

@@ -6,6 +6,7 @@ import { canMount } from '../utils/SlotFunctions';
import { Equalizer } from '../components/SvgIcons'; import { Equalizer } from '../components/SvgIcons';
import cn from 'classnames'; import cn from 'classnames';
import { Ship } from 'ed-forge'; import { Ship } from 'ed-forge';
import autoBind from 'auto-bind';
const browser = require('detect-browser'); const browser = require('detect-browser');
/** /**
@@ -14,36 +15,20 @@ const browser = require('detect-browser');
export default class SlotSection extends TranslatedComponent { export default class SlotSection extends TranslatedComponent {
static propTypes = { static propTypes = {
ship: PropTypes.instanceOf(Ship), ship: PropTypes.instanceOf(Ship),
onChange: PropTypes.func.isRequired,
// code: PropTypes.string.isRequired,
togglePwr: PropTypes.func, togglePwr: PropTypes.func,
sectionMenuRefs: PropTypes.object
}; };
/** /**
* Constructor * Constructor
* @param {Object} props React Component properties * @param {Object} props React Component properties
* @param {Object} context React Component context
* @param {string} sectionId Section DOM Id
* @param {string} sectionName Section name * @param {string} sectionName Section name
*/ */
constructor(props, context, sectionId, sectionName) { constructor(props, sectionName) {
super(props); super(props);
this.sectionId = sectionId; autoBind(this);
this.sectionName = sectionName;
this.ssHeadRef = null; this.sectionName = sectionName;
this.sectionRefArr = this.props.sectionMenuRefs[this.sectionId] = [];
this.sectionRefArr['selectedRef'] = null;
this._getSlots = this._getSlots.bind(this);
this._selectModule = this._selectModule.bind(this);
this._getSectionMenu = this._getSectionMenu.bind(this);
this._contextMenu = this._contextMenu.bind(this);
this._drop = this._drop.bind(this);
this._dragOverNone = this._dragOverNone.bind(this);
this._close = this._close.bind(this);
this._keyDown = this._keyDown.bind(this);
this._handleSectionFocus = this._handleSectionFocus.bind(this);
this.state = {}; this.state = {};
} }
@@ -53,83 +38,6 @@ export default class SlotSection extends TranslatedComponent {
// _contextMenu() // _contextMenu()
// componentDidUpdate(prevProps) // componentDidUpdate(prevProps)
/**
* TODO: May either need to send the function to be triggered when Enter key is pressed, or else
* may need a separate keyDown handler for each subclass (StandardSlotSection, HardpointSlotSection, etc.)
* ex: _keyDown(_keyDownfn, event)
*
* @param {SyntheticEvent} event KeyDown event
*/
_keyDown(event) {
if (event.key == 'Enter') {
event.stopPropagation();
if (event.currentTarget.nodeName === 'H1') {
this._openMenu(this.sectionName, event);
} else {
event.currentTarget.click();
}
return;
}
if (event.key == 'Tab') {
if (event.shiftKey) {
if ((event.currentTarget === this.sectionRefArr[this.firstRefId]) && this.sectionRefArr[this.lastRefId]) {
event.preventDefault();
this.sectionRefArr[this.lastRefId].focus();
}
} else {
if ((event.currentTarget === this.sectionRefArr[this.lastRefId]) && this.sectionRefArr[this.firstRefId]) {
event.preventDefault();
this.sectionRefArr[this.firstRefId].focus();
}
}
}
}
/**
* Set focus on appropriate Slot Section Menu element
* @param {Object} focusPrevProps prevProps for componentDidUpdate() from ...SlotSection.jsx
* @param {String} firstRef id of the first ref in ...SlotSection.jsx
* @param {String} lastRef id of the last ref in ...SlotSection.jsx
*
*/
_handleSectionFocus(focusPrevProps, firstRef, lastRef) {
if (this.selectedRefId !== null && this.sectionRefArr[this.selectedRefId]) {
// set focus on the previously selected option for the currently open section menu
this.sectionRefArr[this.selectedRefId].focus();
} else if (this.sectionRefArr[firstRef] && this.sectionRefArr[firstRef] != null) {
// set focus on the first option in the currently open section menu if none have been selected previously
this.sectionRefArr[firstRef].focus();
} else if (this.props.currentMenu == null && focusPrevProps.currentMenu == this.sectionName && this.sectionRefArr['ssHeadRef']) {
// set focus on the section menu header when section menu is closed
this.sectionRefArr['ssHeadRef'].focus();
}
}
/**
* Open a menu
* @param {string} menu Menu name
* @param {SyntheticEvent} event Event
*/
_openMenu(menu, event) {
event.preventDefault();
event.stopPropagation();
if (this.props.currentMenu === menu) {
menu = null;
}
this.context.openMenu(menu);
}
/**
* Mount/Use the specified module in the slot
* @param {Object} slot Slot
* @param {Object} m Selected module
*/
_selectModule(slot, m) {
slot.setItem(m);
this.props.onChange();
this._close();
}
/** /**
* Slot Drag Handler * Slot Drag Handler
* @param {object} originSlot Origin slot model * @param {object} originSlot Origin slot model
@@ -206,7 +114,6 @@ export default class SlotSection extends TranslatedComponent {
// Copy power info // Copy power info
targetSlot.enabled = originSlot.enabled; targetSlot.enabled = originSlot.enabled;
targetSlot.priority = originSlot.priority; targetSlot.priority = originSlot.priority;
this.props.onChange();
} }
} else { } else {
// Store power info // Store power info
@@ -235,7 +142,6 @@ export default class SlotSection extends TranslatedComponent {
targetSlot.enabled = targetEnabled; targetSlot.enabled = targetEnabled;
targetSlot.priority = targetPriority; targetSlot.priority = targetPriority;
} }
this.props.onChange();
this.props.ship this.props.ship
.updatePowerGenerated() .updatePowerGenerated()
.updatePowerUsed() .updatePowerUsed()
@@ -281,6 +187,17 @@ export default class SlotSection extends TranslatedComponent {
return 'ineligible'; // Cannot be dropped / invalid drop slot return 'ineligible'; // Cannot be dropped / invalid drop slot
} }
_open(newMenu, event) {
event.preventDefault();
event.stopPropagation();
const { currentMenu } = this.props;
if (currentMenu === newMenu) {
this.context.closeMenu();
} else {
this.context.openMenu(newMenu);
}
}
/** /**
* Close current menu * Close current menu
*/ */
@@ -297,14 +214,13 @@ export default class SlotSection extends TranslatedComponent {
render() { render() {
let translate = this.context.language.translate; let translate = this.context.language.translate;
let sectionMenuOpened = this.props.currentMenu === this.sectionName; let sectionMenuOpened = this.props.currentMenu === this.sectionName;
let open = this._openMenu.bind(this, this.sectionName);
let ctx = wrapCtxMenu(this._contextMenu);
return ( return (
<div id={this.sectionId} className={'group'} onDragLeave={this._dragOverNone}> <div className="group" onDragLeave={this._dragOverNone}>
<div className={cn('section-menu', { selected: sectionMenuOpened })} onClick={open} onContextMenu={ctx}> <div className={cn('section-menu', { selected: sectionMenuOpened })}
<h1 tabIndex="0" onKeyDown={this._keyDown} ref={ssHead => this.sectionRefArr['ssHeadRef'] = ssHead}>{translate(this.sectionName)} <Equalizer/></h1> onContextMenu={wrapCtxMenu(this._contextMenu)} onClick={this._open.bind(this, this.sectionName)}>
{sectionMenuOpened ? this._getSectionMenu(translate, this.props.ship) : null } <h1 tabIndex="0">{translate(this.sectionName)}<Equalizer/></h1>
{sectionMenuOpened && this._getSectionMenu()}
</div> </div>
{this._getSlots()} {this._getSlots()}
</div> </div>

View File

@@ -4,8 +4,10 @@ import SlotSection from './SlotSection';
import Slot from './Slot'; import Slot from './Slot';
import Module from '../shipyard/Module'; import Module from '../shipyard/Module';
import * as ShipRoles from '../shipyard/ShipRoles'; import * as ShipRoles from '../shipyard/ShipRoles';
import { stopCtxPropagation } from '../utils/UtilityFunctions'; import autoBind from 'auto-bind';
import { stopCtxPropagation, moduleGet } from '../utils/UtilityFunctions';
import { ShipProps } from 'ed-forge'; import { ShipProps } from 'ed-forge';
const { CONSUMED_RETR, LADEN_MASS } = ShipProps;
/** /**
* Standard Slot section * Standard Slot section
@@ -16,29 +18,16 @@ export default class StandardSlotSection extends SlotSection {
* @param {Object} props React Component properties * @param {Object} props React Component properties
* @param {Object} context React Component context * @param {Object} context React Component context
*/ */
constructor(props, context) { constructor(props) {
super(props, context, 'standard', 'core internal'); super(props, 'core internal');
this._optimizeStandard = this._optimizeStandard.bind(this); autoBind(this);
this._selectBulkhead = this._selectBulkhead.bind(this);
this.selectedRefId = null;
this.firstRefId = 'maxjump';
this.lastRefId = 'racer';
}
/**
* Handle focus if the component updates
* @param {Object} prevProps React Component properties
*/
componentDidUpdate(prevProps) {
this._handleSectionFocus(prevProps,this.firstRefId, this.lastRefId);
} }
/** /**
* Use the lightest/optimal available standard modules * Use the lightest/optimal available standard modules
*/ */
_optimizeStandard() { _optimizeStandard() {
this.selectedRefId = 'maxjump';
this.props.ship.useLightestStandard(); this.props.ship.useLightestStandard();
this.props.onChange();
this._close(); this._close();
} }
@@ -48,10 +37,7 @@ export default class StandardSlotSection extends SlotSection {
* @param {integer} bulkheadIndex Bulkhead to use see Constants.BulkheadNames * @param {integer} bulkheadIndex Bulkhead to use see Constants.BulkheadNames
*/ */
_multiPurpose(shielded, bulkheadIndex) { _multiPurpose(shielded, bulkheadIndex) {
this.selectedRefId = 'multipurpose';
if (bulkheadIndex === 2) this.selectedRefId = 'combat';
ShipRoles.multiPurpose(this.props.ship, shielded, bulkheadIndex); ShipRoles.multiPurpose(this.props.ship, shielded, bulkheadIndex);
this.props.onChange();
this._close(); this._close();
} }
@@ -60,9 +46,7 @@ export default class StandardSlotSection extends SlotSection {
* @param {Boolean} shielded True if shield generator should be included * @param {Boolean} shielded True if shield generator should be included
*/ */
_optimizeCargo(shielded) { _optimizeCargo(shielded) {
this.selectedRefId = 'trader';
ShipRoles.trader(this.props.ship, shielded); ShipRoles.trader(this.props.ship, shielded);
this.props.onChange();
this._close(); this._close();
} }
@@ -71,9 +55,7 @@ export default class StandardSlotSection extends SlotSection {
* @param {Boolean} shielded True if shield generator should be included * @param {Boolean} shielded True if shield generator should be included
*/ */
_optimizeMiner(shielded) { _optimizeMiner(shielded) {
this.selectedRefId = 'miner';
ShipRoles.miner(this.props.ship, shielded); ShipRoles.miner(this.props.ship, shielded);
this.props.onChange();
this._close(); this._close();
} }
@@ -82,10 +64,7 @@ export default class StandardSlotSection extends SlotSection {
* @param {Boolean} planetary True if Planetary Vehicle Hangar (PVH) should be included * @param {Boolean} planetary True if Planetary Vehicle Hangar (PVH) should be included
*/ */
_optimizeExplorer(planetary) { _optimizeExplorer(planetary) {
this.selectedRefId = 'explorer';
if (planetary) this.selectedRefId = 'planetary';
ShipRoles.explorer(this.props.ship, planetary); ShipRoles.explorer(this.props.ship, planetary);
this.props.onChange();
this._close(); this._close();
} }
@@ -93,20 +72,7 @@ export default class StandardSlotSection extends SlotSection {
* Racer role * Racer role
*/ */
_optimizeRacer() { _optimizeRacer() {
this.selectedRefId = 'racer';
ShipRoles.racer(this.props.ship); ShipRoles.racer(this.props.ship);
this.props.onChange();
this._close();
}
/**
* Use the specified bulkhead
* @param {Object} bulkhead Bulkhead module details
*/
_selectBulkhead(bulkhead) {
this.props.ship.useBulkhead(bulkhead.index);
this.context.tooltip();
this.props.onChange();
this._close(); this._close();
} }
@@ -117,122 +83,48 @@ export default class StandardSlotSection extends SlotSection {
this._optimizeStandard(); this._optimizeStandard();
} }
/**
* Creates a new slot for a given module.
* @param {Module} m Module to create the slot for
* @param {function} warning Warning callback
* @return {React.Component} Slot component
*/
_mkSlot(m, warning) {
const { currentMenu } = this.props;
return <Slot key={m.getSlot()} m={m} warning={warning}
currentMenu={currentMenu}
/>;
}
/** /**
* Generate the slot React Components * Generate the slot React Components
* @return {Array} Array of Slots * @return {Array} Array of Slots
*/ */
_getSlots() { _getSlots() {
let { ship, currentMenu } = this.props; const { ship } = this.props;
let slots = new Array(8);
let open = this._openMenu;
let select = this._selectModule;
// let st = ship.standard;
// let avail = ship.getAvailableModules().standard;
// let bh = ship.bulkheads;
let armour = ship.getAlloys();
slots[0] = <Slot
key='bh'
slot={armour}
modules={armour.getApplicableItems()}
onOpen={open.bind(this, armour)}
onSelect={this._selectBulkhead}
selected={currentMenu == armour}
onChange={this.props.onChange}
ship={ship}
/>;
const powerPlant = ship.getPowerPlant();
slots[1] = <Slot
key='pp'
slot={powerPlant}
modules={powerPlant.getApplicableItems()}
onOpen={open.bind(this, powerPlant)}
onSelect={select.bind(this, powerPlant)}
selected={currentMenu == powerPlant}
onChange={this.props.onChange}
ship={ship}
warning={m => ship.get(ShipProps.CONSUMED_RETR) < m.get('powercapacity')}
/>;
const thrusters = ship.getThrusters();
slots[2] = <Slot
key='th'
slot={thrusters}
modules={thrusters.getApplicableItems()}
onOpen={open.bind(this, thrusters)}
onSelect={select.bind(this, thrusters)}
selected={currentMenu == thrusters}
onChange={this.props.onChange}
ship={ship}
warning={m => m.get('enginemaximalmass') < ship.get(ShipProps.LADEN_MASS)}
/>;
const fsd = ship.getFSD(); const fsd = ship.getFSD();
slots[3] = <Slot return [
key='fsd' this._mkSlot(ship.getAlloys()),
slot={fsd} this._mkSlot(
modules={fsd.getApplicableItems()} ship.getPowerPlant(),
onOpen={open.bind(this, fsd)} (m) => moduleGet(m, 'powercapacity') < ship.get(CONSUMED_RETR),
onSelect={select.bind(this, fsd)} ),
onChange={this.props.onChange} this._mkSlot(
ship={ship} ship.getThrusters(),
selected={currentMenu == fsd} (m) => moduleGet(m, 'enginemaximalmass') < ship.get(LADEN_MASS),
/>; ),
this._mkSlot(fsd),
const lifeSupport = ship.getLifeSupport(); this._mkSlot(
slots[4] = <Slot ship.getPowerDistributor(),
key='ls' (m) => moduleGet(m, 'enginescapacity') <= ship.getBaseProperty('boostenergy'),
slot={lifeSupport} ),
modules={lifeSupport.getApplicableItems()} this._mkSlot(ship.getLifeSupport()),
onOpen={open.bind(this, lifeSupport)} this._mkSlot(ship.getSensors()),
onSelect={select.bind(this, lifeSupport)} this._mkSlot(
onChange={this.props.onChange} ship.getCoreFuelTank(),
ship={ship} (m) => moduleGet(m, 'fuel') < fsd.get('maxfuel')
selected={currentMenu == lifeSupport} ),
/>; ];
const powerDistributor = ship.getPowerDistributor();
slots[5] = <Slot
key='pd'
slot={powerDistributor}
modules={powerDistributor.getApplicableItems()}
onOpen={open.bind(this, powerDistributor)}
onSelect={select.bind(this, powerDistributor)}
selected={currentMenu == powerDistributor}
onChange={this.props.onChange}
ship={ship}
warning={m => m instanceof Module ? m.getEnginesCapacity() <= ship.boostEnergy : m.engcap <= ship.boostEnergy}
/>;
const sensors = ship.getSensors();
slots[6] = <Slot
key='ss'
slot={sensors}
modules={sensors.getApplicableItems()}
onOpen={open.bind(this, sensors)}
onSelect={select.bind(this, sensors)}
selected={currentMenu == sensors}
onChange={this.props.onChange}
ship={ship}
/>;
const fuelTank = ship.getCoreFuelTank();
slots[7] = <Slot
key='ft'
slot={fuelTank}
modules={fuelTank.getApplicableItems()}
onOpen={open.bind(this, fuelTank)}
onSelect={select.bind(this, fuelTank)}
selected={currentMenu == fuelTank}
onChange={this.props.onChange}
ship={ship}
// Show warning when fuel tank is smaller than FSD Max Fuel
warning= {m => m.get('fuel') < fsd.get('maxfuel')}
/>;
return slots;
} }
/** /**
@@ -240,21 +132,21 @@ export default class StandardSlotSection extends SlotSection {
* @param {Function} translate Translate function * @param {Function} translate Translate function
* @return {React.Component} Section menu * @return {React.Component} Section menu
*/ */
_getSectionMenu(translate) { _getSectionMenu() {
let planetaryDisabled = this.props.ship.internal.length < 4; const { translate } = this.context.language;
return <div className='select' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}> return <div className='select' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
<ul> <ul>
<li className='lc' tabIndex="0" onClick={this._optimizeStandard} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['maxjump'] = smRef}>{translate('Maximize Jump Range')}</li> <li className='lc' tabIndex="0" onClick={this._optimizeStandard}>{translate('Maximize Jump Range')}</li>
</ul> </ul>
<div className='select-group cap'>{translate('roles')}</div> <div className='select-group cap'>{translate('roles')}</div>
<ul> <ul>
<li className='lc' tabIndex="0" onClick={this._multiPurpose.bind(this, false, 0)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['multipurpose'] = smRef}>{translate('Multi-purpose')}</li> <li className='lc' tabIndex="0" onClick={this._multiPurpose.bind(this, false, 0)}>{translate('Multi-purpose')}</li>
<li className='lc' tabIndex="0" onClick={this._multiPurpose.bind(this, true, 2)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['combat'] = smRef}>{translate('Combat')}</li> <li className='lc' tabIndex="0" onClick={this._multiPurpose.bind(this, true, 2)}>{translate('Combat')}</li>
<li className='lc' tabIndex="0" onClick={this._optimizeCargo.bind(this, true)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['trader'] = smRef}>{translate('Trader')}</li> <li className='lc' tabIndex="0" onClick={this._optimizeCargo.bind(this, true)}>{translate('Trader')}</li>
<li className='lc' tabIndex="0" onClick={this._optimizeExplorer.bind(this, false)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['explorer'] = smRef}>{translate('Explorer')}</li> <li className='lc' tabIndex="0" onClick={this._optimizeExplorer.bind(this, false)}>{translate('Explorer')}</li>
<li className={cn('lc', { disabled: planetaryDisabled })} tabIndex={planetaryDisabled ? '' : '0'} onClick={!planetaryDisabled && this._optimizeExplorer.bind(this, true)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['planetary'] = smRef}>{translate('Planetary Explorer')}</li> <li className='lc' tabIndex="0" onClick={this._optimizeExplorer.bind(this, true)}>{translate('Planetary Explorer')}</li>
<li className='lc' tabIndex="0" onClick={this._optimizeMiner.bind(this, true)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['miner'] = smRef}>{translate('Miner')}</li> <li className='lc' tabIndex="0" onClick={this._optimizeMiner.bind(this, true)}>{translate('Miner')}</li>
<li className='lc' tabIndex="0" onClick={this._optimizeRacer.bind(this)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['racer'] = smRef}>{translate('Racer')}</li> <li className='lc' tabIndex="0" onClick={this._optimizeRacer.bind(this)}>{translate('Racer')}</li>
</ul> </ul>
</div>; </div>;
} }

View File

@@ -2,6 +2,7 @@ import React from 'react';
import SlotSection from './SlotSection'; import SlotSection from './SlotSection';
import Slot from './Slot'; import Slot from './Slot';
import { stopCtxPropagation } from '../utils/UtilityFunctions'; import { stopCtxPropagation } from '../utils/UtilityFunctions';
import autoBind from 'auto-bind';
/** /**
* Utility Slot Section * Utility Slot Section
@@ -10,28 +11,16 @@ export default class UtilitySlotSection extends SlotSection {
/** /**
* Constructor * Constructor
* @param {Object} props React Component properties * @param {Object} props React Component properties
* @param {Object} context React Component context
*/ */
constructor(props, context) { constructor(props) {
super(props, context, 'utility', 'utility mounts'); super(props, 'utility mounts');
this._empty = this._empty.bind(this); autoBind(this);
this.selectedRefId = null;
this.firstRefId = 'emptyall';
this.lastRefId = 'po';
}
/**
* Handle focus if the component updates
* @param {Object} prevProps React Component properties
*/
componentDidUpdate(prevProps) {
this._handleSectionFocus(prevProps,this.firstRefId, this.lastRefId);
} }
/** /**
* Empty all utility slots and close the menu * Empty all utility slots and close the menu
*/ */
_empty() { _empty() {
this.selectedRefId = this.firstRefId;
this.props.ship.emptyUtility(); this.props.ship.emptyUtility();
this.props.onChange(); this.props.onChange();
this._close(); this._close();
@@ -45,9 +34,6 @@ export default class UtilitySlotSection extends SlotSection {
* @param {Synthetic} event Event * @param {Synthetic} event Event
*/ */
_use(group, rating, name, event) { _use(group, rating, name, event) {
this.selectedRefId = group;
if (rating !== null) this.selectedRefId += '-' + rating;
this.props.ship.useUtility(group, rating, name, event.getModifierState('Alt')); this.props.ship.useUtility(group, rating, name, event.getModifierState('Alt'));
this.props.onChange(); this.props.onChange();
this._close(); this._close();
@@ -73,16 +59,13 @@ export default class UtilitySlotSection extends SlotSection {
slots.push(<Slot slots.push(<Slot
key={h.object.Slot} key={h.object.Slot}
maxClass={h.getSize()} maxClass={h.getSize()}
onOpen={this._openMenu.bind(this,h)}
onSelect={this._selectModule.bind(this, h)}
onChange={this.props.onChange} onChange={this.props.onChange}
selected={currentMenu == h} currentMenu={currentMenu}
drag={this._drag.bind(this, h)} drag={this._drag.bind(this, h)}
dragOver={this._dragOverSlot.bind(this, h)} dragOver={this._dragOverSlot.bind(this, h)}
drop={this._drop} drop={this._drop}
dropClass={this._dropClass(h, originSlot, targetSlot)} dropClass={this._dropClass(h, originSlot, targetSlot)}
ship={ship} m={h}
slot={h}
enabled={h.enabled ? true : false} enabled={h.enabled ? true : false}
/>); />);
} }
@@ -95,33 +78,34 @@ export default class UtilitySlotSection extends SlotSection {
* @param {Function} translate Translate function * @param {Function} translate Translate function
* @return {React.Component} Section menu * @return {React.Component} Section menu
*/ */
_getSectionMenu(translate) { _getSectionMenu() {
const { translate } = this.context.language;
let _use = this._use; let _use = this._use;
return <div className='select' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}> return <div className='select' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
<ul> <ul>
<li className='lc' tabIndex='0' onClick={this._empty} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['emptyall'] = smRef}>{translate('empty all')}</li> <li className='lc' tabIndex='0' onClick={this._empty}>{translate('empty all')}</li>
<li className='optional-hide' style={{ textAlign: 'center', marginTop: '1em' }}>{translate('PHRASE_ALT_ALL')}</li> <li className='optional-hide' style={{ textAlign: 'center', marginTop: '1em' }}>{translate('PHRASE_ALT_ALL')}</li>
</ul> </ul>
<div className='select-group cap'>{translate('sb')}</div> <div className='select-group cap'>{translate('sb')}</div>
<ul> <ul>
<li className='c' tabIndex='0' onClick={_use.bind(this, 'sb', 'A', null)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['sb-A'] = smRef}>A</li> <li className='c' tabIndex='0' onClick={_use.bind(this, 'sb', 'A', null)}>A</li>
<li className='c' tabIndex='0' onClick={_use.bind(this, 'sb', 'B', null)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['sb-B'] = smRef}>B</li> <li className='c' tabIndex='0' onClick={_use.bind(this, 'sb', 'B', null)}>B</li>
<li className='c' tabIndex='0' onClick={_use.bind(this, 'sb', 'C', null)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['sb-C'] = smRef}>C</li> <li className='c' tabIndex='0' onClick={_use.bind(this, 'sb', 'C', null)}>C</li>
<li className='c' tabIndex='0' onClick={_use.bind(this, 'sb', 'D', null)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['sb-D'] = smRef}>D</li> <li className='c' tabIndex='0' onClick={_use.bind(this, 'sb', 'D', null)}>D</li>
<li className='c' tabIndex='0' onClick={_use.bind(this, 'sb', 'E', null)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['sb-E'] = smRef}>E</li> <li className='c' tabIndex='0' onClick={_use.bind(this, 'sb', 'E', null)}>E</li>
</ul> </ul>
<div className='select-group cap'>{translate('hs')}</div> <div className='select-group cap'>{translate('hs')}</div>
<ul> <ul>
<li className='lc' tabIndex='0' onClick={_use.bind(this, 'hs', null, 'Heat Sink Launcher')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['hs'] = smRef}>{translate('Heat Sink Launcher')}</li> <li className='lc' tabIndex='0' onClick={_use.bind(this, 'hs', null, 'Heat Sink Launcher')}>{translate('Heat Sink Launcher')}</li>
</ul> </ul>
<div className='select-group cap'>{translate('ch')}</div> <div className='select-group cap'>{translate('ch')}</div>
<ul> <ul>
<li className='lc' tabIndex='0' onClick={_use.bind(this, 'ch', null, 'Chaff Launcher')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['ch'] = smRef}>{translate('Chaff Launcher')}</li> <li className='lc' tabIndex='0' onClick={_use.bind(this, 'ch', null, 'Chaff Launcher')}>{translate('Chaff Launcher')}</li>
</ul> </ul>
<div className='select-group cap'>{translate('po')}</div> <div className='select-group cap'>{translate('po')}</div>
<ul> <ul>
<li className='lc' tabIndex='0' onClick={_use.bind(this, 'po', null, 'Point Defence')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['po'] = smRef}>{translate('Point Defence')}</li> <li className='lc' tabIndex='0' onClick={_use.bind(this, 'po', null, 'Point Defence')}>{translate('Point Defence')}</li>
</ul> </ul>
</div>; </div>;
} }

View File

@@ -7,6 +7,7 @@ import Router from '../Router';
import Persist from '../stores/Persist'; import Persist from '../stores/Persist';
import * as Utils from '../utils/UtilityFunctions'; import * as Utils from '../utils/UtilityFunctions';
import { Factory, Ship } from 'ed-forge'; import { Factory, Ship } from 'ed-forge';
import { STATE_EVENT, OBJECT_EVENT } from 'ed-forge/lib/Ship';
import * as _ from 'lodash'; import * as _ from 'lodash';
import { toDetailedBuild } from '../shipyard/Serializer'; import { toDetailedBuild } from '../shipyard/Serializer';
import { outfitURL } from '../utils/UrlGenerators'; import { outfitURL } from '../utils/UrlGenerators';
@@ -38,16 +39,7 @@ import ModalExport from '../components/ModalExport';
import ModalPermalink from '../components/ModalPermalink'; import ModalPermalink from '../components/ModalPermalink';
import ModalShoppingList from '../components/ModalShoppingList'; import ModalShoppingList from '../components/ModalShoppingList';
import ModalOrbis from '../components/ModalOrbis'; import ModalOrbis from '../components/ModalOrbis';
import autoBind from 'auto-bind';
/**
* Document Title Generator
* @param {String} shipName Ship Name
* @param {String} buildName Build Name
* @return {String} Document title
*/
function getTitle(shipName, buildName) {
return buildName ? buildName : shipName;
}
/** /**
* The Outfitting Page * The Outfitting Page
@@ -60,13 +52,8 @@ export default class OutfittingPage extends Page {
*/ */
constructor(props, context) { constructor(props, context) {
super(props, context); super(props, context);
// window.Perf = Perf; autoBind(this);
this.state = this._initState(props, context); this.state = this._initState(props, context);
this._keyDown = this._keyDown.bind(this);
this._exportBuild = this._exportBuild.bind(this);
this._opponentUpdated = this._opponentUpdated.bind(this);
this._engagementRangeUpdated = this._engagementRangeUpdated.bind(this);
this._sectionMenuRefs = {};
} }
/** /**
@@ -81,24 +68,32 @@ export default class OutfittingPage extends Page {
let buildName = params.bn; let buildName = params.bn;
let savedCode = Persist.getBuild(shipId, buildName); let savedCode = Persist.getBuild(shipId, buildName);
let code = params.code || savedCode; let code = params.code || savedCode;
let ship = code ? new Ship(code) : Factory.newShip(shipId); // Create a new Ship instance // Create a new Ship instance
code = ship.compress(); const ship = code ? new Ship(code) : Factory.newShip(shipId);
ship.on(STATE_EVENT, this._shipUpdated);
this._getTitle = getTitle.bind(this, ship.getShipType()); ship.on(OBJECT_EVENT, this._shipUpdated);
return { return {
error: null, error: null,
title: this._getTitle(buildName),
costTab: Persist.getCostTab() || 'costs', costTab: Persist.getCostTab() || 'costs',
buildName, buildName,
newBuildName: buildName, newBuildName: buildName,
shipId,
ship, ship,
code, code: ship.compress(),
savedCode, savedCode,
}; };
} }
/**
* Get this pages title for the browser.
* @returns {string} Page title
*/
_getTitle() {
const { buildName } = this.state;
const { translate } = this.context.language;
return buildName || translate(this.ship.getShipType());
}
/** /**
* Handle build name change and update state * Handle build name change and update state
* @param {SyntheticEvent} event React Event * @param {SyntheticEvent} event React Event
@@ -108,10 +103,12 @@ export default class OutfittingPage extends Page {
newBuildName: event.target.value newBuildName: event.target.value
}; };
if (Persist.hasBuild(this.state.shipId, stateChanges.newBuildName)) { const { ship } = this.state;
const shipId = ship.getShipType();
if (Persist.hasBuild(shipId, stateChanges.newBuildName)) {
stateChanges.savedCode = Persist.getBuild( stateChanges.savedCode = Persist.getBuild(
this.state.shipId, shipId,
stateChanges.newBuildName stateChanges.newBuildName,
); );
} else { } else {
stateChanges.savedCode = null; stateChanges.savedCode = null;
@@ -124,9 +121,9 @@ export default class OutfittingPage extends Page {
* Update the control part of the route * Update the control part of the route
*/ */
_updateRoute() { _updateRoute() {
const { ship, shipId, buildName } = this.state; const { ship } = this.state;
const code = ship.compress(); const code = ship.compress();
this._updateRoute(shipId, buildName, code); this._setRoute();
this.setState({ code }); this.setState({ code });
} }
@@ -193,13 +190,14 @@ export default class OutfittingPage extends Page {
* Save the current build * Save the current build
*/ */
_saveBuild() { _saveBuild() {
const { ship, buildName, newBuildName, shipId } = this.state; const { ship, buildName, newBuildName } = this.state;
const shipId = ship.getShipType();
// If this is a stock ship the code won't be set, so ensure that we have it // If this is a stock ship the code won't be set, so ensure that we have it
const code = this.state.code || ship.compress(); const code = this.state.code || ship.compress();
Persist.saveBuild(shipId, newBuildName, code); Persist.saveBuild(shipId, newBuildName, code);
this._updateRoute(shipId, newBuildName, code); this._setRoute();
let opponent, opponentBuild, opponentSys, opponentEng, opponentWep; let opponent, opponentBuild, opponentSys, opponentEng, opponentWep;
if ( if (
@@ -232,7 +230,6 @@ export default class OutfittingPage extends Page {
opponentSys, opponentSys,
opponentEng, opponentEng,
opponentWep, opponentWep,
title: this._getTitle(newBuildName)
}); });
} }
@@ -240,11 +237,12 @@ export default class OutfittingPage extends Page {
* Rename the current build * Rename the current build
*/ */
_renameBuild() { _renameBuild() {
const { code, buildName, newBuildName, shipId, ship } = this.state; const { code, buildName, newBuildName, ship } = this.state;
const shipId = ship.getShipType();
if (buildName != newBuildName && newBuildName.length) { if (buildName != newBuildName && newBuildName.length) {
Persist.deleteBuild(shipId, buildName); Persist.deleteBuild(shipId, buildName);
Persist.saveBuild(shipId, newBuildName, code); Persist.saveBuild(shipId, newBuildName, code);
this._updateRoute(shipId, newBuildName, code); this._setRoute();
this.setState({ this.setState({
buildName: newBuildName, buildName: newBuildName,
code, code,
@@ -265,20 +263,19 @@ export default class OutfittingPage extends Page {
* Reset build to Stock/Factory defaults * Reset build to Stock/Factory defaults
*/ */
_resetBuild() { _resetBuild() {
const { ship, shipId, buildName } = this.state; let { ship } = this.state;
// Rebuild ship // Rebuild ship
ship.buildWith(Ships[shipId].defaults); ship = Factory.newShip(ship.getShipType());
// Reset controls
const code = ship.compress();
// Update state, and refresh the ship // Update state, and refresh the ship
this._updateRoute(shipId, buildName, code); this.setState({ ship, code: undefined }, () => this._setRoute());
} }
/** /**
* Delete the build * Delete the build
*/ */
_deleteBuild() { _deleteBuild() {
const { shipId, buildName } = this.state; const { ship, buildName } = this.state;
const shipId = ship.getShipType();
Persist.deleteBuild(shipId, buildName); Persist.deleteBuild(shipId, buildName);
let opponentBuild; let opponentBuild;
@@ -291,7 +288,7 @@ export default class OutfittingPage extends Page {
} else { } else {
opponentBuild = this.state.opponentBuild; opponentBuild = this.state.opponentBuild;
} }
Router.go(outfitURL(this.state.shipId)); Router.go(outfitURL(shipId));
this.setState({ opponentBuild }); this.setState({ opponentBuild });
} }
@@ -315,24 +312,24 @@ export default class OutfittingPage extends Page {
* Called when the code for the ship has been updated, to synchronise the rest of the data * Called when the code for the ship has been updated, to synchronise the rest of the data
*/ */
_codeUpdated() { _codeUpdated() {
const { code, shipId, buildName } = this.state; const { ship, code, buildName } = this.state;
const shipId = ship.getShipType();
this.setState({ this.setState(
ship: new Ship(code), { ship: new Ship(code), },
}, () => this._updateRoute(shipId, buildName, code)); () => this._setRoute(),
);
} }
/** /**
* Called when the ship has been updated, to set the code and then update accordingly * Called when the ship has been updated, to set the code and then update accordingly
*/ */
_shipUpdated() { _shipUpdated() {
let { ship, shipId, buildName } = this.state; let { ship } = this.state;
const code = ship.compress(); const code = ship.compress();
// Only update the state if this really has been updated // Only update the state if this really has been updated
if (this.state.code != code) { if (this.state.code != code) {
this.setState({ code }, () => this.setState({ code }, () => this._setRoute());
this._updateRoute(shipId, buildName, code)
);
} }
} }
@@ -342,8 +339,9 @@ export default class OutfittingPage extends Page {
* @param {string} buildName Current build name * @param {string} buildName Current build name
* @param {string} code Serialized ship 'code' * @param {string} code Serialized ship 'code'
*/ */
_updateRoute(shipId, buildName, code) { _setRoute() {
Router.replace(outfitURL(shipId, code, buildName)); const { ship, code, buildName } = this.state;
Router.replace(outfitURL(ship.getShipType(), code, buildName));
} }
/** /**
@@ -463,31 +461,10 @@ export default class OutfittingPage extends Page {
} = state, } = state,
hide = tooltip.bind(null, null), hide = tooltip.bind(null, null),
menu = this.props.currentMenu, menu = this.props.currentMenu,
shipUpdated = this._shipUpdated,
canSave = (newBuildName || buildName) && code !== savedCode, canSave = (newBuildName || buildName) && code !== savedCode,
canRename = buildName && newBuildName && buildName != newBuildName, canRename = buildName && newBuildName && buildName != newBuildName,
canReload = savedCode && canSave; canReload = savedCode && canSave;
// Code can be blank for a default loadout. Prefix it with the ship name to ensure that changes in default ships is picked up
code = ship.name + (code || '');
// Markers are used to propagate state changes without requiring a deep comparison of the ship, as that takes a long time
// const _sStr = ship.getStandardString();
// const _iStr = ship.getInternalString();
// const _hStr = ship.getHardpointsString();
// const _pStr = `${ship.getPowerEnabledString()}${ship.getPowerPrioritiesString()}`;
// const _mStr = ship.getModificationsString();
// const standardSlotMarker = `${ship.name}${_sStr}${_pStr}${_mStr}${
// ship.ladenMass
// }${cargo}${fuel}`;
// const internalSlotMarker = `${ship.name}${_iStr}${_pStr}${_mStr}`;
// const hardpointsSlotMarker = `${ship.name}${_hStr}${_pStr}${_mStr}`;
// const boostMarker = `${ship.canBoost(cargo, fuel)}`;
// const shipSummaryMarker = `${
// ship.name
// }${_sStr}${_iStr}${_hStr}${_pStr}${_mStr}${ship.ladenMass}${cargo}${fuel}`;
// const requirements = Ships[ship.id].requirements; // const requirements = Ships[ship.id].requirements;
// let requirementElements = []; // let requirementElements = [];
// /** // /**
@@ -654,46 +631,11 @@ export default class OutfittingPage extends Page {
</div> </div>
{/* Main tables */} {/* Main tables */}
{/* <ShipSummaryTable <ShipSummaryTable ship={ship} />
ship={ship} <StandardSlotSection ship={ship} currentMenu={menu} />
marker={shipSummaryMarker} <InternalSlotSection ship={ship} currentMenu={menu} />
/> */} <HardpointSlotSection ship={ship} currentMenu={menu} />
<StandardSlotSection <UtilitySlotSection ship={ship} currentMenu={menu} />
ship={ship}
// code={standardSlotMarker}
onChange={shipUpdated}
onCargoChange={this._cargoUpdated}
onFuelChange={this._fuelUpdated}
currentMenu={menu}
sectionMenuRefs={this._sectionMenuRefs}
/>
<InternalSlotSection
ship={ship}
// code={internalSlotMarker}
onChange={shipUpdated}
onCargoChange={this._cargoUpdated}
onFuelChange={this._fuelUpdated}
currentMenu={menu}
sectionMenuRefs={this._sectionMenuRefs}
/>
<HardpointSlotSection
ship={ship}
// code={hardpointsSlotMarker}
onChange={shipUpdated}
onCargoChange={this._cargoUpdated}
onFuelChange={this._fuelUpdated}
currentMenu={menu}
sectionMenuRefs={this._sectionMenuRefs}
/>
<UtilitySlotSection
ship={ship}
// code={hardpointsSlotMarker}
onChange={shipUpdated}
onCargoChange={this._cargoUpdated}
onFuelChange={this._fuelUpdated}
currentMenu={menu}
sectionMenuRefs={this._sectionMenuRefs}
/>
{/* Control of ship and opponent */} {/* Control of ship and opponent */}
{/* <div className="group quarter"> {/* <div className="group quarter">

View File

@@ -1,3 +1,4 @@
import { Module } from 'ed-forge';
/** /**
* Wraps the callback/context menu handler such that the default * Wraps the callback/context menu handler such that the default
@@ -83,3 +84,18 @@ export function isEmpty(obj) {
} }
return true; return true;
}; };
/**
* Fetches a property from either a Module or a moduleInfo object
* @param {Object} m Either a Module or a moduleInfo object
* @param {string} property Property name
* @returns {number} Property value
*/
export function moduleGet(m, property) {
if (m instanceof Module) {
return m.get(property);
} else {
// Assume its a moduleInfo object
return m.props[property];
}
}