diff --git a/.eslintrc b/.eslintrc index 683bc59a..8e0f6cd1 100644 --- a/.eslintrc +++ b/.eslintrc @@ -5,7 +5,7 @@ "jsx": true, "classes": true, "modules": true - }, + } }, "env": { "browser": true, @@ -33,7 +33,6 @@ "ClassDeclaration": true } }], - "no-console": 2, "brace-style": [2, "1tbs", { "allowSingleLine": true }], "comma-style": [2, "last"], "indent": [2, 2, { "SwitchCase": 1, "VariableDeclarator": 2 }], diff --git a/src/app/Coriolis.jsx b/src/app/Coriolis.jsx index a44bbd9b..75d65ab7 100644 --- a/src/app/Coriolis.jsx +++ b/src/app/Coriolis.jsx @@ -93,8 +93,8 @@ export default class Coriolis extends React.Component { // Need to decode and gunzip the data, then build the ship const data = zlib.inflate(new Buffer(r.params.data, 'base64'), { to: 'string' }); const json = JSON.parse(data); - console.log('Ship import data: '); - console.log(json); + console.info('Ship import data: '); + console.info(json); let ship; if (json && json.modules) { ship = CompanionApiUtils.shipFromJson(json); @@ -356,7 +356,7 @@ export default class Coriolis extends React.Component {
{window.CORIOLIS_VERSION} - {window.CORIOLIS_DATE}
- Commits since last release ({window.CORIOLIS_DATE}) + Commits since last release ({window.CORIOLIS_DATE})
; diff --git a/src/app/components/AvailableModulesMenu.jsx b/src/app/components/AvailableModulesMenu.jsx index b07b884a..2906a736 100644 --- a/src/app/components/AvailableModulesMenu.jsx +++ b/src/app/components/AvailableModulesMenu.jsx @@ -137,7 +137,7 @@ export default class AvailableModulesMenu extends TranslatedComponent { let translate = context.language.translate; let { m, warning, shipMass, onSelect, modules, firstSlotId, lastSlotId } = props; let list, currentGroup; - + let buildGroup = this._buildGroup.bind( this, translate, @@ -149,7 +149,7 @@ export default class AvailableModulesMenu extends TranslatedComponent { onSelect(m); } ); - + if (modules instanceof Array) { list = buildGroup(modules[0].grp, modules); } else { @@ -210,7 +210,7 @@ export default class AvailableModulesMenu extends TranslatedComponent { } } let trackingFocus = false; - return { list, currentGroup, trackingFocus}; + return { list, currentGroup, trackingFocus }; } /** @@ -222,15 +222,17 @@ export default class AvailableModulesMenu extends TranslatedComponent { * @param {function} onSelect Select/Mount callback * @param {string} grp Group name * @param {Array} modules Available modules - * @return {React.Component} Available Module Group contents + * @return {*} Available Module Group contents + * @param {*} firstSlotId ID of the first slot + * @param {*} lastSlotId ID of the last slot */ _buildGroup(translate, mountedModule, warningFunc, mass, onSelect, grp, modules, firstSlotId, lastSlotId) { let prevClass = null, prevRating = null, prevName; let elems = []; - + const sortedModules = modules.sort(this._moduleOrder); - - + + // Calculate the number of items per class. Used so we don't have long lists with only a few items in each row const tmp = sortedModules.map((v, i) => v['class']).reduce((count, cls) => { count[cls] = ++count[cls] || 1; return count; }, {}); const itemsPerClass = Math.max.apply(null, Object.keys(tmp).map(key => tmp[key])); @@ -240,7 +242,7 @@ export default class AvailableModulesMenu extends TranslatedComponent { let m = sortedModules[i]; let mount = null; let disabled = false; - prevName = m.name + prevName = m.name; if (ModuleUtils.isShieldGenerator(m.grp)) { // Shield generators care about maximum hull mass disabled = mass > m.maxmass; @@ -305,7 +307,7 @@ export default class AvailableModulesMenu extends TranslatedComponent { {(mount ? ' ' : '') + m.class + m.rating + (m.missile ? '/' + m.missile : '') + (m.name ? ' ' + translate(m.name) : '')} ); - + itemsOnThisRow++; prevClass = m.class; prevRating = m.rating; @@ -367,23 +369,22 @@ export default class AvailableModulesMenu extends TranslatedComponent { * @param {Function} select Select module callback * @param {SyntheticEvent} event Event */ - _keyDown(select, event) { - var className = event.currentTarget.attributes['class'].value; - if (event.key == 'Enter' && className.indexOf('disabled') < 0 && className.indexOf('active') < 0) { + let className = event.currentTarget.attributes['class'].value; + if (event.key == 'Enter' && className.indexOf('disabled') < 0 && className.indexOf('active') < 0) { select(); - return + return; } - var elemId = event.currentTarget.attributes['data-id'].value; + let elemId = event.currentTarget.attributes['data-id'].value; if (className.indexOf('disabled') < 0 && event.key == 'Tab') { if (event.shiftKey && elemId == this.firstSlotId) { event.preventDefault(); this.slotItems[this.lastSlotId].focus(); - return; + return; } if (!event.shiftKey && elemId == this.lastSlotId) { event.preventDefault(); - this.slotItems[this.firstSlotId].focus(); + this.slotItems[this.firstSlotId].focus(); return; } } @@ -391,10 +392,11 @@ export default class AvailableModulesMenu extends TranslatedComponent { /** * Key Up - * + * @param {Function} select Select function + * @param {SyntheticEvent} event Event */ _keyUp(select,event) { - //nothing here yet + // nothing here yet } /** @@ -463,11 +465,10 @@ export default class AvailableModulesMenu extends TranslatedComponent { this.slotItems[this.firstSlotId].focus(); } } - + /** + * Set focus to slot element ref (if we have one) after modules component unmounts + */ componentWillUnmount() { - /** - * Set focus to slot element ref (if we have one) after modules component unmounts - */ if(this.props.slotDiv) { this.props.slotDiv.focus(); } @@ -487,7 +488,7 @@ export default class AvailableModulesMenu extends TranslatedComponent { * @return {React.Component} List */ render() { - console.log("Tracking focus? " + this.state.trackingFocus); + console.log('Tracking focus? ' + this.state.trackingFocus); return (
this.node = node} className={cn('select', this.props.className)} diff --git a/src/app/components/Header.jsx b/src/app/components/Header.jsx index cd6db5e5..4b886596 100644 --- a/src/app/components/Header.jsx +++ b/src/app/components/Header.jsx @@ -505,8 +505,8 @@ export default class Header extends TranslatedComponent { return (
{this.props.appCacheUpdate &&
window.location.reload() }>{translate('PHRASE_UPDATE_RDY')}
} - {this.props.appCache ? - {"View Release Changes"} + {this.props.appCache ? + {'View Release Changes'} : null} diff --git a/src/app/components/InternalSlot.jsx b/src/app/components/InternalSlot.jsx index e297ec0c..2bf0feb8 100644 --- a/src/app/components/InternalSlot.jsx +++ b/src/app/components/InternalSlot.jsx @@ -21,8 +21,6 @@ export default class InternalSlot extends Slot { * @param {Object} u Localized Units Map * @return {React.Component} Slot contents */ - - _getSlotDetails(m, enabled, translate, formats, u) { if (m) { let classRating = m.class + m.rating; diff --git a/src/app/components/ModificationsMenu.jsx b/src/app/components/ModificationsMenu.jsx index fea6cd28..56fd7a88 100644 --- a/src/app/components/ModificationsMenu.jsx +++ b/src/app/components/ModificationsMenu.jsx @@ -13,7 +13,7 @@ import { getPercent, setRandom, specialToolTip -} from '../utils/BlueprintFunctions' +} from '../utils/BlueprintFunctions'; /** * Modifications menu @@ -50,8 +50,8 @@ export default class ModificationsMenu extends TranslatedComponent { this.firstModId = null; this.firstBPLabel = null;// First item in mod menu this.lastModId = null; - this.lastNeId = null;//Last number editor id. Used to set focus to last number editor when shift-tab pressed on first element in mod menu. - this.modValDidChange = false; //used to determine if component update was caused by change in modification value. + this.lastNeId = null;// Last number editor id. Used to set focus to last number editor when shift-tab pressed on first element in mod menu. + this.modValDidChange = false; // used to determine if component update was caused by change in modification value. this._handleModChange = this._handleModChange.bind(this); this.state = { @@ -78,19 +78,19 @@ export default class ModificationsMenu extends TranslatedComponent { // Grade is a string in the JSON so make it a number grade = Number(grade); const classes = cn('c', { - active: m.blueprint && blueprint.id === m.blueprint.id && grade === m.blueprint.grade + active: m.blueprint && blueprint.id === m.blueprint.id && grade === m.blueprint.grade }); const close = this._blueprintSelected.bind(this, blueprintName, grade); const key = blueprintName + ':' + grade; const tooltipContent = blueprintTooltip(translate, blueprint.grades[grade], Modifications.modules[m.grp].blueprints[blueprintName].grades[grade].engineers, m.grp); - - + + blueprintGrades.unshift(
  • this.modItems[key] = modItem}>{grade}
  • ); } if (blueprintGrades) { const thisLen = blueprintGrades.length; if (this.firstModId == null) this.firstModId = blueprintGrades[0].key; - this.lastModId = blueprintGrades[thisLen-1].key; + this.lastModId = blueprintGrades[thisLen - 1].key; blueprints.push(
    {translate(blueprint.name)}
    ); blueprints.push(
      {blueprintGrades}
    ); } @@ -100,41 +100,37 @@ export default class ModificationsMenu extends TranslatedComponent { /** * Key down - select module on Enter key, move to next/previous module on Tab/Shift-Tab, close on Esc - * @param {Function} select Select module callback - * @param {SyntheticEvent} event Event - * + * @param {Function} event Select module callback */ - _keyDown(event) { - var className = null; - var elemId = null; + let className = null; + let elemId = null; if (event.currentTarget.attributes['class']) className = event.currentTarget.attributes['class'].value; if (event.currentTarget.attributes['data-id']) elemId = event.currentTarget.attributes['data-id'].value; - + if (event.key == 'Enter' && className.indexOf('disabled') < 0 && className.indexOf('active') < 0) { event.stopPropagation(); if (elemId != null) { this.modItems[elemId].click(); } else { - event.currentTarget.click(); } - return + return; } if (event.key == 'Tab') { - //Shift-Tab + // Shift-Tab if(event.shiftKey) { if (elemId == this.firstModId && elemId != null) { // Initial modification menu event.preventDefault(); this.modItems[this.lastModId].focus(); - return; - } else if (event.currentTarget.className.indexOf("button-inline-menu") >= 0 && event.currentTarget.previousElementSibling == null && this.lastNeId != null && this.modItems[this.lastNeId] != null) { + return; + } else if (event.currentTarget.className.indexOf('button-inline-menu') >= 0 && event.currentTarget.previousElementSibling == null && this.lastNeId != null && this.modItems[this.lastNeId] != null) { // shift-tab on first element in modifications menu. set focus to last number editor field if open event.preventDefault(); this.modItems[this.lastNeId].lastChild.focus(); return; - } else if (event.currentTarget.className.indexOf("button-inline-menu") >= 0 && event.currentTarget.previousElementSibling == null) { + } else if (event.currentTarget.className.indexOf('button-inline-menu') >= 0 && event.currentTarget.previousElementSibling == null) { // shift-tab on button-inline-menu with no number editor event.preventDefault(); event.currentTarget.parentElement.lastElementChild.focus(); @@ -143,9 +139,9 @@ export default class ModificationsMenu extends TranslatedComponent { if (elemId == this.lastModId && elemId != null) { // Initial modification menu event.preventDefault(); - this.modItems[this.firstModId].focus(); + this.modItems[this.firstModId].focus(); return; - } else if (event.currentTarget.className.indexOf("button-inline-menu") >= 0 && event.currentTarget.nextSibling == null && event.currentTarget.nodeName != "TD") { + } else if (event.currentTarget.className.indexOf('button-inline-menu') >= 0 && event.currentTarget.nextSibling == null && event.currentTarget.nodeName != 'TD') { // Experimental menu event.preventDefault(); event.currentTarget.parentElement.firstElementChild.focus(); @@ -154,11 +150,10 @@ export default class ModificationsMenu extends TranslatedComponent { event.preventDefault(); this.modItems[this.firstBPLabel].focus(); } - } } } - + /** * Render the specials @@ -167,7 +162,6 @@ export default class ModificationsMenu extends TranslatedComponent { * @return {Object} list: Array of React Components */ _renderSpecials(props, context) { - const { m } = props; const { language, tooltip, termtip } = context; const translate = language.translate; @@ -181,7 +175,7 @@ export default class ModificationsMenu extends TranslatedComponent { continue; } const classes = cn('button-inline-menu', { - active: m.blueprint && m.blueprint.special && m.blueprint.special.edname == specialName + active: m.blueprint && m.blueprint.special && m.blueprint.special.edname == specialName }); const close = this._specialSelected.bind(this, specialName); if (m.blueprint && m.blueprint.name) { @@ -200,7 +194,7 @@ export default class ModificationsMenu extends TranslatedComponent { } } } - console.log("_renderSpecials. specials: %O", specials); + console.log('_renderSpecials. specials: %O', specials); return specials; } @@ -219,7 +213,7 @@ export default class ModificationsMenu extends TranslatedComponent { modifications.push(); } } - console.log("_renderModifications. modItems: %O", this.modItems); + console.log('_renderModifications. modItems: %O', this.modItems); return modifications; } @@ -280,10 +274,10 @@ export default class ModificationsMenu extends TranslatedComponent { _rollFifty() { const { m, ship } = this.props; setPercent(ship, m, 50); - + // this will change the values in the modifications. Set modDidChange to true to prevent focus change when component updates this._handleModChange(true); - + this.props.onChange(); } @@ -293,10 +287,10 @@ export default class ModificationsMenu extends TranslatedComponent { _rollRandom() { const { m, ship } = this.props; setRandom(ship, m); - + // this will change the values in the modifications. Set modDidChange to true to prevent focus change when component updates this._handleModChange(true); - + this.props.onChange(); } @@ -306,10 +300,10 @@ export default class ModificationsMenu extends TranslatedComponent { _rollBest() { const { m, ship } = this.props; setPercent(ship, m, 100); - + // this will change the values in the modifications. Set modDidChange to true to prevent focus change when component updates this._handleModChange(true); - + this.props.onChange(); } @@ -319,13 +313,11 @@ export default class ModificationsMenu extends TranslatedComponent { _rollWorst() { const { m, ship } = this.props; setPercent(ship, m, 0); - + // this will change the values in the modifications. Set modDidChange to true to prevent focus change when component updates this._handleModChange(true); - - this.props.onChange(); - + this.props.onChange(); } /** @@ -338,18 +330,18 @@ export default class ModificationsMenu extends TranslatedComponent { this.props.onChange(); } -/** + /** * set mod did change boolean + * @param {Boolean} b Did the mod value change? */ _handleModChange(b) { this.modValDidChange = b; } - + /** + * Set focus on first element in modifications menu + * after it first mounts + */ componentDidMount() { - /** - * Set focus on first element in modifications menu - * after it first mounts - */ let firstEleCn = this.modItems['modMainDiv'].children.length > 0 ? this.modItems['modMainDiv'].children[0].className : null; if (firstEleCn.indexOf('select-group cap') >= 0) { this.modItems['modMainDiv'].children[1].firstElementChild.focus(); @@ -357,13 +349,12 @@ export default class ModificationsMenu extends TranslatedComponent { this.modItems['modMainDiv'].firstElementChild.focus(); } } - + /** + * Set focus on first element in modifications menu + * if component updates, unless update is due to value change + * in a modification + */ componentDidUpdate() { - /** - * Set focus on first element in modifications menu - * if component updates, unless update is due to value change - * in a modification - */ if (!this.modValDidChange) { if (this.modItems['modMainDiv'].children.length > 0) { let firstEleCn = this.modItems['modMainDiv'].children[0].className; @@ -374,11 +365,13 @@ export default class ModificationsMenu extends TranslatedComponent { } } } else { - this._handleModChange(false);//Need to reset if component update due to value change + this._handleModChange(false);// Need to reset if component update due to value change } - } + /** + * Set focus to the modification menu icon + */ componentWillUnmount() { if (this.props.modButton) { this.props.modButton.focus();// set focus to the modification menu icon after mod menu is unmounted. @@ -407,7 +400,7 @@ export default class ModificationsMenu extends TranslatedComponent { let haveBlueprint = false; let blueprintTt; let blueprintCv; - //TODO: Fix this to actually find the correct blueprint. + // TODO: Fix this to actually find the correct blueprint. if (!m.blueprint || !m.blueprint.name || !m.blueprint.fdname || !Modifications.modules[m.grp].blueprints || !Modifications.modules[m.grp].blueprints[m.blueprint.fdname]) { this.props.ship.clearModuleBlueprint(m); this.props.ship.clearModuleSpecial(m); @@ -440,7 +433,7 @@ export default class ModificationsMenu extends TranslatedComponent { const showReset = !blueprintMenuOpened && (!specialMenuOpened || !specials.length) && haveBlueprint; const showMods = !blueprintMenuOpened && (!specialMenuOpened || !specials.length) && haveBlueprint; if (haveBlueprint) { - this.firstBPLabel = blueprintLabel + this.firstBPLabel = blueprintLabel; } else { this.firstBPLabel = 'selectBP'; } @@ -451,20 +444,20 @@ export default class ModificationsMenu extends TranslatedComponent { onContextMenu={stopCtxPropagation} ref={modItem => this.modItems['modMainDiv'] = modItem} > - { showBlueprintsMenu | showSpecialsMenu ? '' : haveBlueprint ? -
    this.modItems[this.firstBPLabel] = modItems}>{blueprintLabel}
    : + { showBlueprintsMenu | showSpecialsMenu ? '' : haveBlueprint ? +
    this.modItems[this.firstBPLabel] = modItems}>{blueprintLabel}
    :
    this.modItems[this.firstBPLabel] = modItems}>{translate('PHRASE_SELECT_BLUEPRINT')}
    } { showBlueprintsMenu ? this._renderBlueprints(this.props, this.context) : null } { showSpecial & !showSpecialsMenu ?
    {specialLabel}
    : null } { showSpecialsMenu ? specials : null } { showReset ?
    { translate('reset') }
    : null } { showRolls ? - + { showRolls ? - + diff --git a/src/app/components/ShipSummaryTable.jsx b/src/app/components/ShipSummaryTable.jsx index e22863c0..2788927b 100644 --- a/src/app/components/ShipSummaryTable.jsx +++ b/src/app/components/ShipSummaryTable.jsx @@ -18,12 +18,16 @@ export default class ShipSummaryTable extends TranslatedComponent { pips: PropTypes.object.isRequired }; + /** + * The ShipSummaryTable constructor + * @param {Object} props The props + */ constructor(props) { - super(props) + super(props); this.didContextChange = this.didContextChange.bind(this); this.state = { shieldColour: 'blue' - } + }; } /** @@ -56,7 +60,7 @@ export default class ShipSummaryTable extends TranslatedComponent { } this.state = { shieldColour - } + }; return
    { translate('roll') }: { translate('roll') }: { translate('0%') } { translate('50%') } { translate('100%') }
    @@ -140,8 +144,8 @@ export default class ShipSummaryTable extends TranslatedComponent { - - + + diff --git a/src/app/components/Slider.jsx b/src/app/components/Slider.jsx index a1efbf94..7d6b5583 100644 --- a/src/app/components/Slider.jsx +++ b/src/app/components/Slider.jsx @@ -17,12 +17,12 @@ export default class Slider extends React.Component { static propTypes = { axis: PropTypes.bool, - axisUnit: PropTypes.string,//units (T, M, etc.) + axisUnit: PropTypes.string,// units (T, M, etc.) max: PropTypes.number, min: PropTypes.number, onChange: PropTypes.func.isRequired,// function which determins percent value onResize: PropTypes.func, - percent: PropTypes.number.isRequired,//value of slider + percent: PropTypes.number.isRequired,// value of slider scale: PropTypes.number }; @@ -51,7 +51,6 @@ export default class Slider extends React.Component { * @param {SyntheticEvent} event Event */ _down(event) { - let rect = event.currentTarget.getBoundingClientRect(); this.left = rect.left; this.width = rect.width; @@ -95,28 +94,27 @@ export default class Slider extends React.Component { case 'Enter': event.preventDefault(); this.sliderInputBox._setDisplay('block'); - //this.enterTimer = setTimeout(() => this.sliderInputBox.sliderVal.focus(), 10); + // this.enterTimer = setTimeout(() => this.sliderInputBox.sliderVal.focus(), 10); return; default: return; } - } /** * Key down handler * increment slider position by +/- 1 when right/left arrow key is pressed or held - * @param {Event} event + * @param {Event} event The key down event. */ _keydown(event) { - + let newVal; switch (event.key) { case 'ArrowRight': - var newVal = this.props.percent*this.props.max + 1; - if (newVal <= this.props.max) this.props.onChange(newVal/this.props.max); + newVal = this.props.percent * this.props.max + 1; + if (newVal <= this.props.max) this.props.onChange(newVal / this.props.max); return; case 'ArrowLeft': - var newVal = this.props.percent*this.props.max - 1; - if (newVal >= 0) this.props.onChange(newVal/this.props.max); + newVal = this.props.percent * this.props.max - 1; + if (newVal >= 0) this.props.onChange(newVal / this.props.max); return; default: return; @@ -126,12 +124,16 @@ export default class Slider extends React.Component { /** * Touch start handler * @param {Event} event DOM Event - * + * */ _touchstart(event) { this.touchStartTimer = setTimeout(() => this.sliderInputBox._setDisplay('block'), 1500); } + /** + * Touch end handler + * @param {Event} event DOM Event + */ _touchend(event) { this.sliderInputBox.sliderVal.focus(); clearTimeout(this.touchStartTimer); @@ -181,7 +183,6 @@ export default class Slider extends React.Component { */ componentDidMount() { this._updateDimensions(); - } /** @@ -215,7 +216,7 @@ export default class Slider extends React.Component { let width = outerWidth - (margin * 2); let pctPos = width * this.props.percent; - return
    this.node = node} tabIndex="0"> @@ -233,7 +234,7 @@ export default class Slider extends React.Component { axisUnit={this.props.axisUnit} scale={this.props.scale} max={this.props.max} - + />
    ; } @@ -242,100 +243,140 @@ export default class Slider extends React.Component { /** * New component to add keyboard support for sliders - works on all devices (desktop, iOS, Android) **/ - class TextInputBox extends React.Component { +class TextInputBox extends React.Component { static propTypes = { - axisUnit: PropTypes.string,//units (T, M, etc.) + axisUnit: PropTypes.string,// units (T, M, etc.) max: PropTypes.number, onChange: PropTypes.func.isRequired,// function which determins percent value - percent: PropTypes.number.isRequired,//value of slider + percent: PropTypes.number.isRequired,// value of slider scale: PropTypes.number }; + + /** + * Constructor for TextInputBox + * @param {Object} props The props + */ constructor(props) { super(props); - this._handleFocus = this._handleFocus.bind(this); - this._handleBlur = this._handleBlur.bind(this); - this._handleChange = this._handleChange.bind(this); - //this._keydown = this._keydown.bind(this); - this._keyup = this._keyup.bind(this); - this.state = this._getInitialState(); - this.percent = this.props.percent; - this.max = this.props.max; - this.state.inputValue = this.percent * this.max; - } + this._handleFocus = this._handleFocus.bind(this); + this._handleBlur = this._handleBlur.bind(this); + this._handleChange = this._handleChange.bind(this); + // this._keydown = this._keydown.bind(this); + this._keyup = this._keyup.bind(this); + this.state = this._getInitialState(); + this.percent = this.props.percent; + this.max = this.props.max; + this.setState({ inputValue: this.percent * this.max }); + } - componentWillReceiveProps(nextProps, nextState) { - var nextValue = nextProps.percent * nextProps.max; + /** + * Slider willrecieveprops + * @param {Object} nextProps The next props + * @param {Object} nextState The next state + */ + componentWillReceiveProps(nextProps, nextState) { + let nextValue = nextProps.percent * nextProps.max; // See https://stackoverflow.com/questions/32414308/updating-state-on-props-change-in-react-form - if (nextValue !== this.state.inputValue && nextValue <= nextProps.max) { - this.setState({ inputValue: nextValue }); - } + if (nextValue !== this.state.inputValue && nextValue <= nextProps.max) { + this.setState({ inputValue: nextValue }); } - componentDidUpdate(prevProps, prevState) { + } - if (prevState.divStyle.display == 'none' && this.state.divStyle.display == 'block') { - this.enterTimer = setTimeout(() => this.sliderVal.focus(), 10); - } - - if (prevProps.max !== this.props.max && this.state.inputValue > this.props.max) { + /** + * Slider Component did update + * @param {Object} prevProps The prev props + * @param {Object} prevState The prev state + */ + componentDidUpdate(prevProps, prevState) { + if (prevState.divStyle.display == 'none' && this.state.divStyle.display == 'block') { + this.enterTimer = setTimeout(() => this.sliderVal.focus(), 10); + } + + if (prevProps.max !== this.props.max && this.state.inputValue > this.props.max) { // they chose a different module - this.setState({ inputValue: this.props.max }); - } - - if (this.state.inputValue != prevState.inputValue && prevProps.max == this.props.max) { - this.props.onChange(this.state.inputValue/this.props.max); - } - + this.setState({ inputValue: this.props.max }); } - _getInitialState() { - return { - divStyle: {display:'none'}, - inputStyle: {width:'4em'}, - labelStyle: {marginLeft: '.1em'}, - maxLength:5, - size:5, - min:0, - tabIndex:-1, - type:'number', - readOnly: true - } + if (this.state.inputValue != prevState.inputValue && prevProps.max == this.props.max) { + this.props.onChange(this.state.inputValue / this.props.max); } + } - _setDisplay(val) { - this.setState({ - divStyle: {display:val} - }); - } + /** + * Get the initial state. + * @returns {{divStyle: {display: string}, inputStyle: {width: string}, labelStyle: {marginLeft: string}, maxLength: number, size: number, min: number, tabIndex: number, type: string, readOnly: boolean}} Initial state. + * @private + */ + _getInitialState() { + return { + divStyle: { display:'none' }, + inputStyle: { width:'4em' }, + labelStyle: { marginLeft: '.1em' }, + maxLength:5, + size:5, + min:0, + tabIndex:-1, + type:'number', + readOnly: true + }; + } - _handleFocus() { - this.setState({ - inputValue:this._getValue() - }); - } + /** + * Set display style + * @param {string} val The display CSS code. + * @private + */ + _setDisplay(val) { + this.setState({ + divStyle: { display:val } + }); + } - _handleBlur() { - this._setDisplay('none'); - if (this.state.inputValue !== '') { - this.props.onChange(this.state.inputValue/this.props.max); - } else { - this.state.inputValue = this.props.percent * this.props.max; - } - - } + /** + * Focus handler + * @private + */ + _handleFocus() { + this.setState({ + inputValue:this._getValue() + }); + } - _getValue() { - return this.state.inputValue; + /** + * Handles blurring + */ + _handleBlur() { + this._setDisplay('none'); + if (this.state.inputValue !== '') { + this.props.onChange(this.state.inputValue / this.props.max); + } else { + this.setState({ inputValue: this.props.percent * this.props.max }); } + } - _handleChange(event) { - if (event.target.value < 0) { - this.setState({inputValue: 0}); - } else if (event.target.value <= this.props.max) { - this.setState({inputValue: event.target.value}); - } else { - this.setState({inputValue: this.props.max}); - } + /** + * Get inputValue + * @returns {number|Number|*} inputValue + * @private + */ + _getValue() { + return this.state.inputValue; + } + + /** + * Handle changes + * @param {Event} event DOM Event + * @private + */ + _handleChange(event) { + if (event.target.value < 0) { + this.setState({ inputValue: 0 }); + } else if (event.target.value <= this.props.max) { + this.setState({ inputValue: event.target.value }); + } else { + this.setState({ inputValue: this.props.max }); } + } /** * Key up handler for input field. @@ -350,12 +391,15 @@ export default class Slider extends React.Component { default: return; } - } - render() { - let { axisUnit, onChange, percent, scale } = this.props; - return
    {this._handleBlur()}} onFocus={() => {this._handleFocus()}} type={this.state.type} ref={(ip) => this.sliderVal = ip}/>{this.props.axisUnit}
    ; - } + /** + * JSX Render handler + * @returns {*} Slider + */ + render() { + let { axisUnit, onChange, percent, scale } = this.props; + return
    {this._handleBlur();}} onFocus={() => {this._handleFocus();}} type={this.state.type} ref={(ip) => this.sliderVal = ip}/>{this.props.axisUnit}
    ; + } } diff --git a/src/app/components/Slot.jsx b/src/app/components/Slot.jsx index c8b4c32f..206a06d3 100644 --- a/src/app/components/Slot.jsx +++ b/src/app/components/Slot.jsx @@ -85,13 +85,13 @@ export default class Slot extends TranslatedComponent { */ _keyDown(event) { if (event.key == 'Enter') { - if(event.target.className == 'r') { - console.log("Slot: Enter key pressed on mod icon"); - this._toggleModifications(); - } else { - console.log("Slot: Enter key pressed on: %O", event.target); - } - this.props.onOpen(event); + if(event.target.className == 'r') { + console.log('Slot: Enter key pressed on mod icon'); + this._toggleModifications(); + } else { + console.log('Slot: Enter key pressed on: %O', event.target); + } + this.props.onOpen(event); } } /** diff --git a/src/app/components/StandardSlot.jsx b/src/app/components/StandardSlot.jsx index cfca8530..663a25b3 100644 --- a/src/app/components/StandardSlot.jsx +++ b/src/app/components/StandardSlot.jsx @@ -40,12 +40,17 @@ export default class StandardSlot extends TranslatedComponent { this.slotDiv = null; } - _keyDown(event) { + /** + * Fired on key down + * @param {KeyboardEvent} event The keydown event + * @private + */ + _keyDown(event) { if (event.key == 'Enter') { - if(event.target.className == 'r') { - this._toggleModifications(); - } - this.props.onOpen(event); + if(event.target.className == 'r') { + this._toggleModifications(); + } + this.props.onOpen(event); } } @@ -151,7 +156,6 @@ export default class StandardSlot extends TranslatedComponent { * Toggle the modifications flag when selecting the modifications icon */ _toggleModifications() { - this._modificationsSelected = !this._modificationsSelected; } } diff --git a/src/app/pages/OutfittingPage.jsx b/src/app/pages/OutfittingPage.jsx index 5b6d5a61..afb7df89 100644 --- a/src/app/pages/OutfittingPage.jsx +++ b/src/app/pages/OutfittingPage.jsx @@ -554,17 +554,23 @@ export default class OutfittingPage extends Page { const shipSummaryMarker = `${ship.name}${_sStr}${_iStr}${_hStr}${_pStr}${_mStr}${ship.ladenMass}${cargo}${fuel}`; const requirements = Ships[ship.id].requirements; - var requirementElements = []; + let requirementElements = []; + /** + * Render the requirements for a ship / etc + * @param {string} className Class names + * @param {string} textKey The key for translating + * @param {String} tooltipTextKey Tooltip key + */ function renderRequirement(className, textKey, tooltipTextKey) { - requirementElements.push(
    {translate(textKey)}
    ); + requirementElements.push(
    {translate(textKey)}
    ); } if (requirements) { requirements.federationRank && renderRequirement('federation', 'federation rank ' + requirements.federationRank, 'federation rank required'); requirements.empireRank && renderRequirement('empire', 'empire rank ' + requirements.empireRank, 'empire rank required'); requirements.horizons && renderRequirement('horizons', 'horizons', 'horizons required'); - requirements.horizonsEarlyAdoption && renderRequirement('horizons', 'horizons early adoption', 'horizons early adoption required'); + requirements.horizonsEarlyAdoption && renderRequirement('horizons', 'horizons early adoption', 'horizons early adoption required'); } return ( @@ -602,7 +608,7 @@ export default class OutfittingPage extends Page { {/* Main tables */} - + diff --git a/src/app/pages/ShipyardPage.jsx b/src/app/pages/ShipyardPage.jsx index 09a09182..842ca5bf 100644 --- a/src/app/pages/ShipyardPage.jsx +++ b/src/app/pages/ShipyardPage.jsx @@ -274,11 +274,11 @@ export default class ShipyardPage extends Page { for (let s of shipSummaries) { let shipSortValue = s[shipPredicate]; - if( shipPredicateIndex != undefined ) { + if(shipPredicateIndex != undefined) { shipSortValue = shipSortValue[shipPredicateIndex]; } - if( shipSortValue != lastShipSortValue ) { + if(shipSortValue != lastShipSortValue) { backgroundHighlight = !backgroundHighlight; lastShipSortValue = shipSortValue; } diff --git a/src/app/shipyard/Calculations.js b/src/app/shipyard/Calculations.js index 2976c794..fe1574e6 100644 --- a/src/app/shipyard/Calculations.js +++ b/src/app/shipyard/Calculations.js @@ -45,7 +45,8 @@ export function totalJumpRange(mass, fsd, fuel) { * @param {number} baseShield Base Shield strength MJ for ship * @param {object} sg The shield generator used * @param {number} multiplier Shield multiplier for ship (1 + shield boosters if any) - * @return {number} Approximate shield strengh in MJ + * @param {Object} ship The ship object + * @return {number} Approximate shield strengh in MJ */ export function shieldStrength(mass, baseShield, sg, multiplier, ship) { // sg might be a module or a template; handle either here diff --git a/src/app/shipyard/Module.js b/src/app/shipyard/Module.js index 440604b3..c5cde25d 100755 --- a/src/app/shipyard/Module.js +++ b/src/app/shipyard/Module.js @@ -136,7 +136,7 @@ export default class Module { if (modification.type === 'percentage') { modValue = this.getModValue(name) / 10000; } else if (modification.type === 'numeric') { - modValue = this.getModValue(name)/ 100; + modValue = this.getModValue(name) / 100; } else { modValue = this.getModValue(name); } diff --git a/src/app/shipyard/ShipRoles.js b/src/app/shipyard/ShipRoles.js index a30cb5ae..62c4e00c 100644 --- a/src/app/shipyard/ShipRoles.js +++ b/src/app/shipyard/ShipRoles.js @@ -1,5 +1,5 @@ -import * as ModuleUtils from './ModuleUtils' -import { canMount } from '../utils/SlotFunctions' +import * as ModuleUtils from './ModuleUtils'; +import { canMount } from '../utils/SlotFunctions'; /** * Standard / typical role for multi-purpose or combat (if shielded with better bulkheads) @@ -7,20 +7,20 @@ import { canMount } from '../utils/SlotFunctions' * @param {Boolean} shielded True if shield generator should be included * @param {integer} bulkheadIndex Bulkhead to use see Constants.BulkheadNames */ -export function multiPurpose (ship, shielded, bulkheadIndex) { +export function multiPurpose(ship, shielded, bulkheadIndex) { ship.useStandard('A') .use(ship.standard[3], ModuleUtils.standard(3, ship.standard[3].maxClass + 'D')) // D Life Support .use(ship.standard[5], ModuleUtils.standard(5, ship.standard[5].maxClass + 'D')) // D Sensors - .useBulkhead(bulkheadIndex) + .useBulkhead(bulkheadIndex); if (shielded) { - ship.internal.some(function (slot) { + ship.internal.some(function(slot) { if (canMount(ship, slot, 'sg')) { // Assuming largest slot can hold an eligible shield - ship.use(slot, ModuleUtils.findInternal('sg', slot.maxClass, 'A')) - ship.setSlotEnabled(slot, true) - return true + ship.use(slot, ModuleUtils.findInternal('sg', slot.maxClass, 'A')); + ship.setSlotEnabled(slot, true); + return true; } - }) + }); } } @@ -30,51 +30,51 @@ export function multiPurpose (ship, shielded, bulkheadIndex) { * @param {Boolean} shielded True if shield generator should be included * @param {Object} standardOpts [Optional] Standard module optional overrides */ -export function trader (ship, shielded, standardOpts) { - let usedSlots = [] - let bstCount = 2 - let sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass) +export function trader(ship, shielded, standardOpts) { + let usedSlots = []; + let bstCount = 2; + let sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass); ship.useStandard('A') .use(ship.standard[3], ModuleUtils.standard(3, ship.standard[3].maxClass + 'D')) // D Life Support .use(ship.standard[1], ModuleUtils.standard(1, ship.standard[1].maxClass + 'D')) // D Life Support .use(ship.standard[4], ModuleUtils.standard(4, ship.standard[4].maxClass + 'D')) // D Life Support - .use(ship.standard[5], ModuleUtils.standard(5, ship.standard[5].maxClass + 'D')) // D Sensors + .use(ship.standard[5], ModuleUtils.standard(5, ship.standard[5].maxClass + 'D')); // D Sensors - const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8] + const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8]; const shieldInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1) .filter(a => (!a.eligible) || a.eligible.sg) .filter(a => a.maxClass >= sg.class) - .sort((a, b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass)) - shieldInternals.some(function (slot) { + .sort((a, b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass)); + shieldInternals.some(function(slot) { if (canMount(ship, slot, 'sg')) { // Assuming largest slot can hold an eligible shield - const shield = ModuleUtils.findInternal('sg', slot.maxClass, 'A') + const shield = ModuleUtils.findInternal('sg', slot.maxClass, 'A'); if (shield && shield.maxmass > ship.hullMass) { - ship.use(slot, shield) - ship.setSlotEnabled(slot, true) - usedSlots.push(slot) - return true + ship.use(slot, shield); + ship.setSlotEnabled(slot, true); + usedSlots.push(slot); + return true; } } - }) + }); // Fill the empty internals with cargo racks for (let i = ship.internal.length; i--;) { - let slot = ship.internal[i] + let slot = ship.internal[i]; if (usedSlots.indexOf(slot) == -1 && canMount(ship, slot, 'cr')) { - ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E')) + ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E')); } } // Empty the hardpoints for (let s of ship.hardpoints) { - ship.use(s, null) + ship.use(s, null); } for (let s of ship.hardpoints) { if (s.maxClass == 0 && bstCount) { // Mount up to 2 boosters - ship.use(s, ModuleUtils.hardpoints('04')) - bstCount-- + ship.use(s, ModuleUtils.hardpoints('04')); + bstCount--; } else { - ship.use(s, null) + ship.use(s, null); } } // ship.useLightestStandard(standardOpts); @@ -85,127 +85,127 @@ export function trader (ship, shielded, standardOpts) { * @param {Ship} ship Ship instance * @param {Boolean} planetary True if Planetary Vehicle Hangar (PVH) should be included */ -export function explorer (ship, planetary) { - let standardOpts = {ppRating: 'A'}, - heatSinkCount = 2, // Fit 2 heat sinks if possible - usedSlots = [], - sgSlot, - fuelScoopSlot, - sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass) +export function explorer(ship, planetary) { + let standardOpts = { ppRating: 'A' }, + heatSinkCount = 2, // Fit 2 heat sinks if possible + usedSlots = [], + sgSlot, + fuelScoopSlot, + sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass); if (!planetary) { // Non-planetary explorers don't really need to boost - standardOpts.pd = '1D' + standardOpts.pd = '1D'; } // Cargo hatch can be disabled - ship.setSlotEnabled(ship.cargoHatch, false) + ship.setSlotEnabled(ship.cargoHatch, false); // Advanced Discovery Scanner - class 1 or higher - const adsOrder = [1, 2, 3, 4, 5, 6, 7, 8] + const adsOrder = [1, 2, 3, 4, 5, 6, 7, 8]; const adsInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1) .filter(a => (!a.eligible) || a.eligible.sc) - .sort((a, b) => adsOrder.indexOf(a.maxClass) - adsOrder.indexOf(b.maxClass)) + .sort((a, b) => adsOrder.indexOf(a.maxClass) - adsOrder.indexOf(b.maxClass)); for (let i = 0; i < adsInternals.length; i++) { if (canMount(ship, adsInternals[i], 'sc')) { - ship.use(adsInternals[i], ModuleUtils.internal('2f')) - usedSlots.push(adsInternals[i]) - break + ship.use(adsInternals[i], ModuleUtils.internal('2f')); + usedSlots.push(adsInternals[i]); + break; } } if (planetary) { // Planetary Vehicle Hangar - class 2 or higher - const pvhOrder = [2, 3, 4, 5, 6, 7, 8, 1] + const pvhOrder = [2, 3, 4, 5, 6, 7, 8, 1]; const pvhInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1) .filter(a => (!a.eligible) || a.eligible.pv) - .sort((a, b) => pvhOrder.indexOf(a.maxClass) - pvhOrder.indexOf(b.maxClass)) + .sort((a, b) => pvhOrder.indexOf(a.maxClass) - pvhOrder.indexOf(b.maxClass)); for (let i = 0; i < pvhInternals.length; i++) { if (canMount(ship, pvhInternals[i], 'pv')) { // Planetary Vehical Hangar only has even classes - const pvhClass = pvhInternals[i].maxClass % 2 === 1 ? pvhInternals[i].maxClass - 1 : pvhInternals[i].maxClass - ship.use(pvhInternals[i], ModuleUtils.findInternal('pv', pvhClass, 'G')) // G is lower mass - ship.setSlotEnabled(pvhInternals[i], false) // Disable power for Planetary Vehical Hangar - usedSlots.push(pvhInternals[i]) - break + const pvhClass = pvhInternals[i].maxClass % 2 === 1 ? pvhInternals[i].maxClass - 1 : pvhInternals[i].maxClass; + ship.use(pvhInternals[i], ModuleUtils.findInternal('pv', pvhClass, 'G')); // G is lower mass + ship.setSlotEnabled(pvhInternals[i], false); // Disable power for Planetary Vehical Hangar + usedSlots.push(pvhInternals[i]); + break; } } } // Shield generator - const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8] + const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8]; const shieldInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1) .filter(a => (!a.eligible) || a.eligible.sg) .filter(a => a.maxClass >= sg.class) - .sort((a, b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass)) + .sort((a, b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass)); for (let i = 0; i < shieldInternals.length; i++) { if (canMount(ship, shieldInternals[i], 'sg')) { - ship.use(shieldInternals[i], sg) - usedSlots.push(shieldInternals[i]) - sgSlot = shieldInternals[i] - break + ship.use(shieldInternals[i], sg); + usedSlots.push(shieldInternals[i]); + sgSlot = shieldInternals[i]; + break; } } // Detailed Surface Scanner - const dssOrder = [1, 2, 3, 4, 5, 6, 7, 8] + const dssOrder = [1, 2, 3, 4, 5, 6, 7, 8]; const dssInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1) .filter(a => (!a.eligible) || a.eligible.sc) - .sort((a, b) => dssOrder.indexOf(a.maxClass) - dssOrder.indexOf(b.maxClass)) + .sort((a, b) => dssOrder.indexOf(a.maxClass) - dssOrder.indexOf(b.maxClass)); for (let i = 0; i < dssInternals.length; i++) { if (canMount(ship, dssInternals[i], 'sc')) { - ship.use(dssInternals[i], ModuleUtils.internal('2i')) - usedSlots.push(dssInternals[i]) - break + ship.use(dssInternals[i], ModuleUtils.internal('2i')); + usedSlots.push(dssInternals[i]); + break; } } // Fuel scoop - best possible - const fuelScoopOrder = [8, 7, 6, 5, 4, 3, 2, 1] + const fuelScoopOrder = [8, 7, 6, 5, 4, 3, 2, 1]; const fuelScoopInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1) .filter(a => (!a.eligible) || a.eligible.fs) - .sort((a, b) => fuelScoopOrder.indexOf(a.maxClass) - fuelScoopOrder.indexOf(b.maxClass)) + .sort((a, b) => fuelScoopOrder.indexOf(a.maxClass) - fuelScoopOrder.indexOf(b.maxClass)); for (let i = 0; i < fuelScoopInternals.length; i++) { if (canMount(ship, fuelScoopInternals[i], 'fs')) { - ship.use(fuelScoopInternals[i], ModuleUtils.findInternal('fs', fuelScoopInternals[i].maxClass, 'A')) - usedSlots.push(fuelScoopInternals[i]) - fuelScoopSlot = fuelScoopInternals[i] - break + ship.use(fuelScoopInternals[i], ModuleUtils.findInternal('fs', fuelScoopInternals[i].maxClass, 'A')); + usedSlots.push(fuelScoopInternals[i]); + fuelScoopSlot = fuelScoopInternals[i]; + break; } } // AFMUs - fill as they are 0-weight - const afmuOrder = [8, 7, 6, 5, 4, 3, 2, 1] + const afmuOrder = [8, 7, 6, 5, 4, 3, 2, 1]; const afmuInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1) .filter(a => (!a.eligible) || a.eligible.pc) - .sort((a, b) => afmuOrder.indexOf(a.maxClass) - afmuOrder.indexOf(b.maxClass)) + .sort((a, b) => afmuOrder.indexOf(a.maxClass) - afmuOrder.indexOf(b.maxClass)); for (let i = 0; i < afmuInternals.length; i++) { if (canMount(ship, afmuInternals[i], 'am')) { - ship.use(afmuInternals[i], ModuleUtils.findInternal('am', afmuInternals[i].maxClass, 'A')) - usedSlots.push(afmuInternals[i]) - ship.setSlotEnabled(afmuInternals[i], false) // Disable power for AFM Unit + ship.use(afmuInternals[i], ModuleUtils.findInternal('am', afmuInternals[i].maxClass, 'A')); + usedSlots.push(afmuInternals[i]); + ship.setSlotEnabled(afmuInternals[i], false); // Disable power for AFM Unit } } for (let s of ship.hardpoints) { if (s.maxClass == 0 && heatSinkCount) { // Mount up to 2 heatsinks - ship.use(s, ModuleUtils.hardpoints('02')) - ship.setSlotEnabled(s, heatSinkCount == 2) // Only enable a single Heatsink - heatSinkCount-- + ship.use(s, ModuleUtils.hardpoints('02')); + ship.setSlotEnabled(s, heatSinkCount == 2); // Only enable a single Heatsink + heatSinkCount--; } else { - ship.use(s, null) + ship.use(s, null); } } if (sgSlot && fuelScoopSlot) { // The SG and Fuel scoop to not need to be powered at the same time if (sgSlot.m.getPowerUsage() > fuelScoopSlot.m.getPowerUsage()) { // The Shield generator uses the most power - ship.setSlotEnabled(fuelScoopSlot, false) + ship.setSlotEnabled(fuelScoopSlot, false); } else { // The Fuel scoop uses the most power - ship.setSlotEnabled(sgSlot, false) + ship.setSlotEnabled(sgSlot, false); } } - ship.useLightestStandard(standardOpts) + ship.useLightestStandard(standardOpts); } /** @@ -213,188 +213,188 @@ export function explorer (ship, planetary) { * @param {Ship} ship Ship instance * @param {Boolean} shielded True if shield generator should be included */ -export function miner (ship, shielded) { - shielded = true - let standardOpts = {ppRating: 'A'}, - miningLaserCount = 2, - usedSlots = [], - sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass) +export function miner(ship, shielded) { + shielded = true; + let standardOpts = { ppRating: 'A' }, + miningLaserCount = 2, + usedSlots = [], + sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass); // Cargo hatch should be enabled - ship.setSlotEnabled(ship.cargoHatch, true) + ship.setSlotEnabled(ship.cargoHatch, true); // Largest possible refinery - const refineryOrder = [4, 5, 6, 7, 8, 3, 2, 1] + const refineryOrder = [4, 5, 6, 7, 8, 3, 2, 1]; const refineryInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1) .filter(a => (!a.eligible) || a.eligible.rf) - .sort((a, b) => refineryOrder.indexOf(a.maxClass) - refineryOrder.indexOf(b.maxClass)) + .sort((a, b) => refineryOrder.indexOf(a.maxClass) - refineryOrder.indexOf(b.maxClass)); for (let i = 0; i < refineryInternals.length; i++) { if (canMount(ship, refineryInternals[i], 'rf')) { - ship.use(refineryInternals[i], ModuleUtils.findInternal('rf', Math.min(refineryInternals[i].maxClass, 4), 'A')) - usedSlots.push(refineryInternals[i]) - break + ship.use(refineryInternals[i], ModuleUtils.findInternal('rf', Math.min(refineryInternals[i].maxClass, 4), 'A')); + usedSlots.push(refineryInternals[i]); + break; } } // Prospector limpet controller - 3A if possible - const prospectorOrder = [3, 4, 5, 6, 7, 8, 2, 1] + const prospectorOrder = [3, 4, 5, 6, 7, 8, 2, 1]; const prospectorInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1) .filter(a => (!a.eligible) || a.eligible.pc) - .sort((a, b) => prospectorOrder.indexOf(a.maxClass) - prospectorOrder.indexOf(b.maxClass)) + .sort((a, b) => prospectorOrder.indexOf(a.maxClass) - prospectorOrder.indexOf(b.maxClass)); for (let i = 0; i < prospectorInternals.length; i++) { if (canMount(ship, prospectorInternals[i], 'pc')) { // Prospector only has odd classes - const prospectorClass = prospectorInternals[i].maxClass % 2 === 0 ? prospectorInternals[i].maxClass - 1 : prospectorInternals[i].maxClass - ship.use(prospectorInternals[i], ModuleUtils.findInternal('pc', prospectorClass, 'A')) - usedSlots.push(prospectorInternals[i]) - break + const prospectorClass = prospectorInternals[i].maxClass % 2 === 0 ? prospectorInternals[i].maxClass - 1 : prospectorInternals[i].maxClass; + ship.use(prospectorInternals[i], ModuleUtils.findInternal('pc', prospectorClass, 'A')); + usedSlots.push(prospectorInternals[i]); + break; } } // Shield generator if required if (shielded) { - const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8] + const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8]; const shieldInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1) .filter(a => (!a.eligible) || a.eligible.sg) .filter(a => a.maxClass >= sg.class) - .sort((a, b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass)) + .sort((a, b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass)); for (let i = 0; i < shieldInternals.length; i++) { if (canMount(ship, shieldInternals[i], 'sg')) { - ship.use(shieldInternals[i], sg) - usedSlots.push(shieldInternals[i]) - break + ship.use(shieldInternals[i], sg); + usedSlots.push(shieldInternals[i]); + break; } } } // Dual mining lasers of highest possible class; remove anything else - const miningLaserOrder = [2, 3, 4, 1, 0] - const miningLaserHardpoints = ship.hardpoints.concat().sort(function (a, b) { - return miningLaserOrder.indexOf(a.maxClass) - miningLaserOrder.indexOf(b.maxClass) - }) + const miningLaserOrder = [2, 3, 4, 1, 0]; + const miningLaserHardpoints = ship.hardpoints.concat().sort(function(a, b) { + return miningLaserOrder.indexOf(a.maxClass) - miningLaserOrder.indexOf(b.maxClass); + }); for (let s of miningLaserHardpoints) { if (s.maxClass >= 1 && miningLaserCount) { - ship.use(s, ModuleUtils.hardpoints(s.maxClass >= 2 ? '2m' : '2l')) - miningLaserCount-- + ship.use(s, ModuleUtils.hardpoints(s.maxClass >= 2 ? '2m' : '2l')); + miningLaserCount--; } else { - ship.use(s, null) + ship.use(s, null); } } // Number of collector limpets required to be active is a function of the size of the ship and the power of the lasers const miningLaserDps = ship.hardpoints.filter(h => h.m != null) - .reduce(function (a, b) { - return a + b.m.getDps() - }, 0) + .reduce(function(a, b) { + return a + b.m.getDps(); + }, 0); // Find out how many internal slots we have, and their potential cargo size const potentialCargo = ship.internal.filter(a => usedSlots.indexOf(a) == -1) .filter(a => (!a.eligible) || a.eligible.cr) - .map(b => Math.pow(2, b.maxClass)) + .map(b => Math.pow(2, b.maxClass)); // One collector for each 1.25 DPS, multiply by 1.25 for medium ships and 1.5 for large ships as they have further to travel // 0 if we only have 1 cargo slot, otherwise minium of 1 and maximum of 6 (excluding size modifier) - const sizeModifier = ship.class == 2 ? 1.2 : ship.class == 3 ? 1.5 : 1 - let collectorLimpetsRequired = potentialCargo.length == 1 ? 0 : Math.ceil(sizeModifier * Math.min(6, Math.floor(miningLaserDps / 1.25))) + const sizeModifier = ship.class == 2 ? 1.2 : ship.class == 3 ? 1.5 : 1; + let collectorLimpetsRequired = potentialCargo.length == 1 ? 0 : Math.ceil(sizeModifier * Math.min(6, Math.floor(miningLaserDps / 1.25))); if (collectorLimpetsRequired > 0) { - const collectorOrder = [1, 2, 3, 4, 5, 6, 7, 8] + const collectorOrder = [1, 2, 3, 4, 5, 6, 7, 8]; const collectorInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1) .filter(a => (!a.eligible) || a.eligible.cc) - .sort((a, b) => collectorOrder.indexOf(a.maxClass) - collectorOrder.indexOf(b.maxClass)) + .sort((a, b) => collectorOrder.indexOf(a.maxClass) - collectorOrder.indexOf(b.maxClass)); // Always keep at least 2 slots free for cargo racks (1 for shielded) for (let i = 0; i < collectorInternals.length - (shielded ? 1 : 2) && collectorLimpetsRequired > 0; i++) { if (canMount(ship, collectorInternals[i], 'cc')) { // Collector only has odd classes - const collectorClass = collectorInternals[i].maxClass % 2 === 0 ? collectorInternals[i].maxClass - 1 : collectorInternals[i].maxClass - ship.use(collectorInternals[i], ModuleUtils.findInternal('cc', collectorClass, 'D')) - usedSlots.push(collectorInternals[i]) - collectorLimpetsRequired -= collectorInternals[i].m.maximum + const collectorClass = collectorInternals[i].maxClass % 2 === 0 ? collectorInternals[i].maxClass - 1 : collectorInternals[i].maxClass; + ship.use(collectorInternals[i], ModuleUtils.findInternal('cc', collectorClass, 'D')); + usedSlots.push(collectorInternals[i]); + collectorLimpetsRequired -= collectorInternals[i].m.maximum; } } } // Power distributor to power the mining lasers indefinitely const wepRateRequired = ship.hardpoints.filter(h => h.m != null) - .reduce(function (a, b) { - return a + b.m.getEps() - }, 0) - standardOpts.pd = ship.getAvailableModules().matchingPowerDist({weprate: wepRateRequired}).id + .reduce(function(a, b) { + return a + b.m.getEps(); + }, 0); + standardOpts.pd = ship.getAvailableModules().matchingPowerDist({ weprate: wepRateRequired }).id; // Fill the empty internals with cargo racks for (let i = ship.internal.length; i--;) { - let slot = ship.internal[i] + let slot = ship.internal[i]; if (usedSlots.indexOf(slot) == -1 && canMount(ship, slot, 'cr')) { - ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E')) + ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E')); } } - ship.useLightestStandard(standardOpts) + ship.useLightestStandard(standardOpts); } /** * Racer Role * @param {Ship} ship Ship instance */ -export function racer (ship) { +export function racer(ship) { let standardOpts = {}, - usedSlots = [], - sgSlot, - sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass) + usedSlots = [], + sgSlot, + sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass); // Cargo hatch can be disabled - ship.setSlotEnabled(ship.cargoHatch, false) + ship.setSlotEnabled(ship.cargoHatch, false); // Shield generator - const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8] + const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8]; const shieldInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1) .filter(a => (!a.eligible) || a.eligible.sg) .filter(a => a.maxClass >= sg.class) - .sort((a, b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass)) + .sort((a, b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass)); for (let i = 0; i < shieldInternals.length; i++) { if (canMount(ship, shieldInternals[i], 'sg')) { - ship.use(shieldInternals[i], sg) - usedSlots.push(shieldInternals[i]) - sgSlot = shieldInternals[i] - break + ship.use(shieldInternals[i], sg); + usedSlots.push(shieldInternals[i]); + sgSlot = shieldInternals[i]; + break; } } // Empty the hardpoints for (let s of ship.hardpoints) { - ship.use(s, null) + ship.use(s, null); } // Empty the internals for (let i = ship.internal.length; i--;) { - let slot = ship.internal[i] + let slot = ship.internal[i]; if (usedSlots.indexOf(slot) == -1) { - ship.use(slot, null) + ship.use(slot, null); } } // Best thrusters if (ship.standard[1].maxClass === 3) { - standardOpts.th = 'tz' + standardOpts.th = 'tz'; } else if (ship.standard[1].maxClass === 2) { - standardOpts.th = 'u0' + standardOpts.th = 'u0'; } else { - standardOpts.th = ship.standard[1].maxClass + 'A' + standardOpts.th = ship.standard[1].maxClass + 'A'; } // Best power distributor for more boosting - standardOpts.pd = ship.standard[4].maxClass + 'A' + standardOpts.pd = ship.standard[4].maxClass + 'A'; // Smallest possible FSD drive - standardOpts.fsd = '2D' + standardOpts.fsd = '2D'; // Minimal fuel tank - standardOpts.ft = '1C' + standardOpts.ft = '1C'; // Disable nearly everything - standardOpts.fsdDisabled = true - standardOpts.sDisabled = true - standardOpts.pdDisabled = true - standardOpts.lsDisabled = true + standardOpts.fsdDisabled = true; + standardOpts.sDisabled = true; + standardOpts.pdDisabled = true; + standardOpts.lsDisabled = true; - ship.useLightestStandard(standardOpts) + ship.useLightestStandard(standardOpts); // Apply engineering to each module // ship.standard[1].m.blueprint = getBlueprint('Engine_Dirty', ship.standard[0]); diff --git a/src/app/utils/BlueprintFunctions.js b/src/app/utils/BlueprintFunctions.js index 53b02f69..a597dae7 100644 --- a/src/app/utils/BlueprintFunctions.js +++ b/src/app/utils/BlueprintFunctions.js @@ -7,7 +7,7 @@ import { Modifications } from 'coriolis-data/dist'; * @param {Object} blueprint The blueprint at the required grade * @param {string} grp The group of the module * @param {Object} m The module to compare with - * @param specialName + * @param {string} specialName The name of the special * @returns {Object} The react components */ export function specialToolTip(translate, blueprint, grp, m, specialName) { @@ -18,36 +18,35 @@ export function specialToolTip(translate, blueprint, grp, m, specialName) { if (m) { // We also add in any benefits from specials that aren't covered above if (m.blueprint) { - for (const feature in Modifications.modifierActions[specialName]) { + for (const feature in Modifications.modifierActions[specialName]) { // if (!blueprint.features[feature] && !m.mods.feature) { - const featureDef = Modifications.modifications[feature]; - if (featureDef && !featureDef.hidden) { - let symbol = ''; - if (feature === 'jitter') { - symbol = '°'; - } else if (featureDef.type === 'percentage') { - symbol = '%'; - } - let current = m.getModValue(feature) - m.getModValue(feature, true); - if (featureDef.type === 'percentage') { - current = Math.round(current / 10) / 10; - } else if (featureDef.type === 'numeric') { - current /= 100; - } - const currentIsBeneficial = isValueBeneficial(feature, current); + const featureDef = Modifications.modifications[feature]; + if (featureDef && !featureDef.hidden) { + let symbol = ''; + if (feature === 'jitter') { + symbol = '°'; + } else if (featureDef.type === 'percentage') { + symbol = '%'; + } + let current = m.getModValue(feature) - m.getModValue(feature, true); + if (featureDef.type === 'percentage') { + current = Math.round(current / 10) / 10; + } else if (featureDef.type === 'numeric') { + current /= 100; + } + const currentIsBeneficial = isValueBeneficial(feature, current); - effects.push( + effects.push( - + + style={{ textAlign: 'right' }}>{current}{symbol} ); - } + } } - } } @@ -289,7 +288,7 @@ export function isValueBeneficial(feature, value) { */ export function getBlueprint(name, module) { // Start with a copy of the blueprint - const findMod = val => Object.keys(Modifications.blueprints).find(elem => elem.toString().toLowerCase().search(val.toString().toLowerCase().replace(/(OutfittingFieldType_|persecond)/igm, '')) >= 0) + const findMod = val => Object.keys(Modifications.blueprints).find(elem => elem.toString().toLowerCase().search(val.toString().toLowerCase().replace(/(OutfittingFieldType_|persecond)/igm, '')) >= 0); const found = Modifications.blueprints[findMod(name)]; if (!found || !found.fdname) { return {}; @@ -382,36 +381,35 @@ export function getPercent(m) { let result = null; const features = m.blueprint.grades[m.blueprint.grade].features; for (const featureName in features) { - - if (features[featureName][0] === features[featureName][1]) { - continue; - } - - let value = _getValue(m, featureName); - let mult; + if (features[featureName][0] === features[featureName][1]) { + continue; + } + + let value = _getValue(m, featureName); + let mult; if (Modifications.modifications[featureName].higherbetter) { // Higher is better, but is this making it better or worse? if (features[featureName][0] < 0 || (features[featureName][0] === 0 && features[featureName][1] < 0)) { - mult = Math.round((value - features[featureName][1]) / (features[featureName][0] - features[featureName][1]) * 100); + mult = Math.round((value - features[featureName][1]) / (features[featureName][0] - features[featureName][1]) * 100); } else { - mult = Math.round((value - features[featureName][0]) / (features[featureName][1] - features[featureName][0]) * 100); + mult = Math.round((value - features[featureName][0]) / (features[featureName][1] - features[featureName][0]) * 100); } } else { // Higher is worse, but is this making it better or worse? if (features[featureName][0] < 0 || (features[featureName][0] === 0 && features[featureName][1] < 0)) { - mult = Math.round((value - features[featureName][0]) / (features[featureName][1] - features[featureName][0]) * 100); + mult = Math.round((value - features[featureName][0]) / (features[featureName][1] - features[featureName][0]) * 100); } else { - mult = Math.round((value - features[featureName][1]) / (features[featureName][0] - features[featureName][1]) * 100); + mult = Math.round((value - features[featureName][1]) / (features[featureName][0] - features[featureName][1]) * 100); } } - - if (result && result != mult) { - return null; - } else if (result != mult) { - result = mult; - } + + if (result && result != mult) { + return null; + } else if (result != mult) { + result = mult; + } } - + return result; } @@ -419,6 +417,7 @@ export function getPercent(m) { * Query a feature value * @param {Object} m The module for which to perform the query * @param {string} featureName The feature being queried + * @returns {number} The value of the modification as a % */ function _getValue(m, featureName) { if (Modifications.modifications[featureName].type == 'percentage') { diff --git a/src/app/utils/CompanionApiUtils.js b/src/app/utils/CompanionApiUtils.js index 4f6d86e0..1755bb27 100644 --- a/src/app/utils/CompanionApiUtils.js +++ b/src/app/utils/CompanionApiUtils.js @@ -318,7 +318,7 @@ function _addModifications(module, modifiers, blueprint, grade, specialModificat if (!modifiers) return; let special; if (specialModifications) { - special = Modifications.specials[Object.keys(specialModifications)[0]] + special = Modifications.specials[Object.keys(specialModifications)[0]]; } for (const i in modifiers) { // Some special modifications @@ -345,7 +345,7 @@ function _addModifications(module, modifiers, blueprint, grade, specialModificat let value; if (i === 'OutfittingFieldType_DefenceModifierShieldMultiplier') { value = modifiers[i].value - 1; - } else if (i === 'OutfittingFieldType_DefenceModifierHealthMultiplier' && blueprint.startsWith('Armour_')) { + } else if (i === 'OutfittingFieldType_DefenceModifierHealthMultiplier' && blueprint.startsWith('Armour_')) { value = (modifiers[i].value - module.hullboost) / module.hullboost; } else if (i === 'OutfittingFieldType_DefenceModifierHealthMultiplier') { value = modifiers[i].value / module.hullboost; diff --git a/src/app/utils/JournalUtils.js b/src/app/utils/JournalUtils.js index 08b39a96..94c1b6be 100644 --- a/src/app/utils/JournalUtils.js +++ b/src/app/utils/JournalUtils.js @@ -1,292 +1,291 @@ -import Ship from '../shipyard/Ship' -import { HARDPOINT_NUM_TO_CLASS, shipModelFromJson } from './CompanionApiUtils' -import { Ships } from 'coriolis-data/dist' -import Module from '../shipyard/Module' -import { Modules } from 'coriolis-data/dist' -import { Modifications } from 'coriolis-data/dist' -import { getBlueprint } from './BlueprintFunctions' - -/** - * Obtain a module given its FD Name - * @param {string} fdname the FD Name of the module - * @return {Module} the module - */ -function _moduleFromFdName (fdname) { - if (!fdname) return null - fdname = fdname.toLowerCase() - // Check standard modules - for (const grp in Modules.standard) { - if (Modules.standard.hasOwnProperty(grp)) { - for (const i in Modules.standard[grp]) { - if (Modules.standard[grp][i].symbol && Modules.standard[grp][i].symbol.toLowerCase() === fdname) { - // Found it - return new Module({template: Modules.standard[grp][i]}) - } - } - } - } - - // Check hardpoint modules - for (const grp in Modules.hardpoints) { - if (Modules.hardpoints.hasOwnProperty(grp)) { - for (const i in Modules.hardpoints[grp]) { - if (Modules.hardpoints[grp][i].symbol && Modules.hardpoints[grp][i].symbol.toLowerCase() === fdname) { - // Found it - return new Module({template: Modules.hardpoints[grp][i]}) - } - } - } - } - - // Check internal modules - for (const grp in Modules.internal) { - if (Modules.internal.hasOwnProperty(grp)) { - for (const i in Modules.internal[grp]) { - if (Modules.internal[grp][i].symbol && Modules.internal[grp][i].symbol.toLowerCase() === fdname) { - // Found it - return new Module({template: Modules.internal[grp][i]}) - } - } - } - } - - // Not found - return null -} - -/** - * Build a ship from the journal Loadout event JSON - * @param {object} json the Loadout event JSON - * @return {Ship} the built ship - */ -export function shipFromLoadoutJSON (json) { -// Start off building a basic ship - const shipModel = shipModelFromJson(json) - if (!shipModel) { - throw 'No such ship found: "' + json.Ship + '"' - } - const shipTemplate = Ships[shipModel] - - let ship = new Ship(shipModel, shipTemplate.properties, shipTemplate.slots) - ship.buildWith(null) - // Initial Ship building, don't do engineering yet. - let opts = []; - - for (const module of json.Modules) { - switch (module.Slot.toLowerCase()) { - // Cargo Hatch. - case 'cargohatch': - ship.cargoHatch.enabled = module.On - ship.cargoHatch.priority = module.Priority - break - // Add the bulkheads - case 'armour': - if (module.Item.toLowerCase().endsWith('_armour_grade1')) { - ship.useBulkhead(0, true) - } else if (module.Item.toLowerCase().endsWith('_armour_grade2')) { - ship.useBulkhead(1, true) - } else if (module.Item.toLowerCase().endsWith('_armour_grade3')) { - ship.useBulkhead(2, true) - } else if (module.Item.toLowerCase().endsWith('_armour_mirrored')) { - ship.useBulkhead(3, true) - } else if (module.Item.toLowerCase().endsWith('_armour_reactive')) { - ship.useBulkhead(4, true) - } else { - throw 'Unknown bulkheads "' + module.Item + '"' - } - ship.bulkheads.enabled = true - if (module.Engineering) _addModifications(ship.bulkheads.m, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect) - break - case 'powerplant': - const powerplant = _moduleFromFdName(module.Item) - ship.use(ship.standard[0], powerplant, true) - ship.standard[0].enabled = module.On - ship.standard[0].priority = module.Priority - if (module.Engineering) _addModifications(powerplant, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect) - break - case 'mainengines': - const thrusters = _moduleFromFdName(module.Item) - ship.use(ship.standard[1], thrusters, true) - ship.standard[1].enabled = module.On - ship.standard[1].priority = module.Priority - if (module.Engineering) _addModifications(thrusters, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect) - break - case 'frameshiftdrive': - const frameshiftdrive = _moduleFromFdName(module.Item) - ship.use(ship.standard[2], frameshiftdrive, true) - ship.standard[2].enabled = module.On - ship.standard[2].priority = module.Priority - if (module.Engineering) _addModifications(frameshiftdrive, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect) - break - case 'lifesupport': - const lifesupport = _moduleFromFdName(module.Item) - ship.use(ship.standard[3], lifesupport, true) - ship.standard[3].enabled = module.On === true - ship.standard[3].priority = module.Priority - if (module.Engineering) _addModifications(lifesupport, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect) - break - case 'powerdistributor': - const powerdistributor = _moduleFromFdName(module.Item) - ship.use(ship.standard[4], powerdistributor, true) - ship.standard[4].enabled = module.On - ship.standard[4].priority = module.Priority - if (module.Engineering) _addModifications(powerdistributor, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect) - break - case 'radar': - const sensors = _moduleFromFdName(module.Item) - ship.use(ship.standard[5], sensors, true) - ship.standard[5].enabled = module.On - ship.standard[5].priority = module.Priority - if (module.Engineering) _addModifications(sensors, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect) - break - case 'fueltank': - const fueltank = _moduleFromFdName(module.Item) - ship.use(ship.standard[6], fueltank, true) - ship.standard[6].enabled = true - ship.standard[6].priority = 0 - break - default: - } - for (const module of json.Modules) { - if (module.Slot.toLowerCase().search(/hardpoint/) !== -1) { - // Add hardpoints - let hardpoint; - let hardpointClassNum = -1 - let hardpointSlotNum = -1 - let hardpointArrayNum = 0 - for (let i in shipTemplate.slots.hardpoints) { - if (shipTemplate.slots.hardpoints[i] === hardpointClassNum) { - // Another slot of the same class - hardpointSlotNum++ - } else { - // The first slot of a new class - hardpointClassNum = shipTemplate.slots.hardpoints[i] - hardpointSlotNum = 1 - } - - // Now that we know what we're looking for, find it - const hardpointName = HARDPOINT_NUM_TO_CLASS[hardpointClassNum] + 'Hardpoint' + hardpointSlotNum - const hardpointSlot = json.Modules.find(elem => elem.Slot.toLowerCase() === hardpointName.toLowerCase()) - if (!hardpointSlot) { - // This can happen with old imports that don't contain new hardpoints - } else if (!hardpointSlot) { - // No module - } else { - hardpoint = _moduleFromFdName(hardpointSlot.Item) - ship.use(ship.hardpoints[hardpointArrayNum], hardpoint, true) - ship.hardpoints[hardpointArrayNum].enabled = hardpointSlot.On - ship.hardpoints[hardpointArrayNum].priority = hardpointSlot.Priority - opts.push({coriolisMod: hardpoint, json: hardpointSlot}); - } - hardpointArrayNum++ - } - } - if (module.Slot.toLowerCase().search(/slot\d/) !== -1) { - let internalSlotNum = 1 - let militarySlotNum = 1 - for (let i in shipTemplate.slots.internal) { - const isMilitary = isNaN(shipTemplate.slots.internal[i]) ? shipTemplate.slots.internal[i].name = 'military' : false - - // The internal slot might be a standard or a military slot. Military slots have a different naming system - let internalSlot = null - if (isMilitary) { - const internalName = 'Military0' + militarySlotNum - internalSlot = json.Modules.find(elem => elem.Slot.toLowerCase() === internalName.toLowerCase()) - militarySlotNum++ - } else { - // Slot numbers are not contiguous so handle skips. - while (internalSlot === null && internalSlotNum < 99) { - // Slot sizes have no relationship to the actual size, either, so check all possibilities - for (let slotsize = 0; slotsize < 9; slotsize++) { - const internalName = 'Slot' + (internalSlotNum <= 9 ? '0' : '') + internalSlotNum + '_Size' + slotsize - if (json.Modules.find(elem => elem.Slot.toLowerCase() === internalName.toLowerCase())) { - internalSlot = json.Modules.find(elem => elem.Slot.toLowerCase() === internalName.toLowerCase()); - break - } - } - internalSlotNum++ - } - } - - if (!internalSlot) { - // This can happen with old imports that don't contain new slots - } else if (!internalSlot) { - // No module - } else { - const internalJson = internalSlot - const internal = _moduleFromFdName(internalJson.Item) - ship.use(ship.internal[i], internal, true) - ship.internal[i].enabled = internalJson.On === true - ship.internal[i].priority = internalJson.Priority - opts.push({coriolisMod: internal, json: internalSlot}); - } - } - } - } - } - - for (const i of opts) { - if (i.json.Engineering) _addModifications(i.coriolisMod, i.json.Engineering.Modifiers, i.json.Engineering.BlueprintName, i.json.Engineering.Level, i.json.Engineering.ExperimentalEffect) - } - // We don't have any information on it so guess it's priority 5 and disabled - if (!ship.cargoHatch) { - ship.cargoHatch.enabled = false - ship.cargoHatch.priority = 4 - } - console.log(ship) - - // Now update the ship's codes before returning it - return ship.updatePowerPrioritesString().updatePowerEnabledString().updateModificationsString() -} - -/** - * Add the modifications for a module - * @param {Module} module the module - * @param {Object} modifiers the modifiers - * @param {Object} blueprint the blueprint of the modification - * @param {Object} grade the grade of the modification - * @param {Object} specialModifications special modification - */ -function _addModifications (module, modifiers, blueprint, grade, specialModifications) { - if (!modifiers) return - let special - if (specialModifications) { - special = Modifications.specials[specialModifications] - } - for (const i in modifiers) { - // Some special modifications - // Look up the modifiers to find what we need to do - const findMod = val => Object.keys(Modifications.modifierActions).find(elem => elem.toString().toLowerCase().replace(/(outfittingfieldtype_|persecond)/igm, '') === val.toString().toLowerCase().replace(/(outfittingfieldtype_|persecond)/igm, '')) - const modifierActions = Modifications.modifierActions[findMod(modifiers[i].Label)] - //TODO: Figure out how to scale this value. - if (!!modifiers[i].LessIsGood) { - - } - let value = (modifiers[i].Value / modifiers[i].OriginalValue * 100 - 100) * 100; - if (value === Infinity) { - value = modifiers[i].Value * 100; - } - if (modifiers[i].Label.search('Resistance') >= 0) { - value = (modifiers[i].Value * 100) - (modifiers[i].OriginalValue * 100) - } - // Carry out the required changes - for (const action in modifierActions) { - if (isNaN(modifierActions[action])) { - module.setModValue(action, modifierActions[action]) - } else { - module.setModValue(action, value) - } - } - } - - // Add the blueprint definition, grade and special - if (blueprint) { - module.blueprint = getBlueprint(blueprint, module) - if (grade) { - module.blueprint.grade = Number(grade) - } - if (special) { - module.blueprint.special = special - } - } -} +import Ship from '../shipyard/Ship'; +import { HARDPOINT_NUM_TO_CLASS, shipModelFromJson } from './CompanionApiUtils'; +import { Ships } from 'coriolis-data/dist'; +import Module from '../shipyard/Module'; +import { Modules } from 'coriolis-data/dist'; +import { Modifications } from 'coriolis-data/dist'; +import { getBlueprint } from './BlueprintFunctions'; + +/** + * Obtain a module given its FD Name + * @param {string} fdname the FD Name of the module + * @return {Module} the module + */ +function _moduleFromFdName(fdname) { + if (!fdname) return null; + fdname = fdname.toLowerCase(); + // Check standard modules + for (const grp in Modules.standard) { + if (Modules.standard.hasOwnProperty(grp)) { + for (const i in Modules.standard[grp]) { + if (Modules.standard[grp][i].symbol && Modules.standard[grp][i].symbol.toLowerCase() === fdname) { + // Found it + return new Module({ template: Modules.standard[grp][i] }); + } + } + } + } + + // Check hardpoint modules + for (const grp in Modules.hardpoints) { + if (Modules.hardpoints.hasOwnProperty(grp)) { + for (const i in Modules.hardpoints[grp]) { + if (Modules.hardpoints[grp][i].symbol && Modules.hardpoints[grp][i].symbol.toLowerCase() === fdname) { + // Found it + return new Module({ template: Modules.hardpoints[grp][i] }); + } + } + } + } + + // Check internal modules + for (const grp in Modules.internal) { + if (Modules.internal.hasOwnProperty(grp)) { + for (const i in Modules.internal[grp]) { + if (Modules.internal[grp][i].symbol && Modules.internal[grp][i].symbol.toLowerCase() === fdname) { + // Found it + return new Module({ template: Modules.internal[grp][i] }); + } + } + } + } + + // Not found + return null; +} + +/** + * Build a ship from the journal Loadout event JSON + * @param {object} json the Loadout event JSON + * @return {Ship} the built ship + */ +export function shipFromLoadoutJSON(json) { +// Start off building a basic ship + const shipModel = shipModelFromJson(json); + if (!shipModel) { + throw 'No such ship found: "' + json.Ship + '"'; + } + const shipTemplate = Ships[shipModel]; + + let ship = new Ship(shipModel, shipTemplate.properties, shipTemplate.slots); + ship.buildWith(null); + // Initial Ship building, don't do engineering yet. + let opts = []; + + for (const module of json.Modules) { + switch (module.Slot.toLowerCase()) { + // Cargo Hatch. + case 'cargohatch': + ship.cargoHatch.enabled = module.On; + ship.cargoHatch.priority = module.Priority; + break; + // Add the bulkheads + case 'armour': + if (module.Item.toLowerCase().endsWith('_armour_grade1')) { + ship.useBulkhead(0, true); + } else if (module.Item.toLowerCase().endsWith('_armour_grade2')) { + ship.useBulkhead(1, true); + } else if (module.Item.toLowerCase().endsWith('_armour_grade3')) { + ship.useBulkhead(2, true); + } else if (module.Item.toLowerCase().endsWith('_armour_mirrored')) { + ship.useBulkhead(3, true); + } else if (module.Item.toLowerCase().endsWith('_armour_reactive')) { + ship.useBulkhead(4, true); + } else { + throw 'Unknown bulkheads "' + module.Item + '"'; + } + ship.bulkheads.enabled = true; + if (module.Engineering) _addModifications(ship.bulkheads.m, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect); + break; + case 'powerplant': + const powerplant = _moduleFromFdName(module.Item); + ship.use(ship.standard[0], powerplant, true); + ship.standard[0].enabled = module.On; + ship.standard[0].priority = module.Priority; + if (module.Engineering) _addModifications(powerplant, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect); + break; + case 'mainengines': + const thrusters = _moduleFromFdName(module.Item); + ship.use(ship.standard[1], thrusters, true); + ship.standard[1].enabled = module.On; + ship.standard[1].priority = module.Priority; + if (module.Engineering) _addModifications(thrusters, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect); + break; + case 'frameshiftdrive': + const frameshiftdrive = _moduleFromFdName(module.Item); + ship.use(ship.standard[2], frameshiftdrive, true); + ship.standard[2].enabled = module.On; + ship.standard[2].priority = module.Priority; + if (module.Engineering) _addModifications(frameshiftdrive, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect); + break; + case 'lifesupport': + const lifesupport = _moduleFromFdName(module.Item); + ship.use(ship.standard[3], lifesupport, true); + ship.standard[3].enabled = module.On === true; + ship.standard[3].priority = module.Priority; + if (module.Engineering) _addModifications(lifesupport, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect); + break; + case 'powerdistributor': + const powerdistributor = _moduleFromFdName(module.Item); + ship.use(ship.standard[4], powerdistributor, true); + ship.standard[4].enabled = module.On; + ship.standard[4].priority = module.Priority; + if (module.Engineering) _addModifications(powerdistributor, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect); + break; + case 'radar': + const sensors = _moduleFromFdName(module.Item); + ship.use(ship.standard[5], sensors, true); + ship.standard[5].enabled = module.On; + ship.standard[5].priority = module.Priority; + if (module.Engineering) _addModifications(sensors, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect); + break; + case 'fueltank': + const fueltank = _moduleFromFdName(module.Item); + ship.use(ship.standard[6], fueltank, true); + ship.standard[6].enabled = true; + ship.standard[6].priority = 0; + break; + default: + } + for (const module of json.Modules) { + if (module.Slot.toLowerCase().search(/hardpoint/) !== -1) { + // Add hardpoints + let hardpoint; + let hardpointClassNum = -1; + let hardpointSlotNum = -1; + let hardpointArrayNum = 0; + for (let i in shipTemplate.slots.hardpoints) { + if (shipTemplate.slots.hardpoints[i] === hardpointClassNum) { + // Another slot of the same class + hardpointSlotNum++; + } else { + // The first slot of a new class + hardpointClassNum = shipTemplate.slots.hardpoints[i]; + hardpointSlotNum = 1; + } + + // Now that we know what we're looking for, find it + const hardpointName = HARDPOINT_NUM_TO_CLASS[hardpointClassNum] + 'Hardpoint' + hardpointSlotNum; + const hardpointSlot = json.Modules.find(elem => elem.Slot.toLowerCase() === hardpointName.toLowerCase()); + if (!hardpointSlot) { + // This can happen with old imports that don't contain new hardpoints + } else if (!hardpointSlot) { + // No module + } else { + hardpoint = _moduleFromFdName(hardpointSlot.Item); + ship.use(ship.hardpoints[hardpointArrayNum], hardpoint, true); + ship.hardpoints[hardpointArrayNum].enabled = hardpointSlot.On; + ship.hardpoints[hardpointArrayNum].priority = hardpointSlot.Priority; + opts.push({ coriolisMod: hardpoint, json: hardpointSlot }); + } + hardpointArrayNum++; + } + } + if (module.Slot.toLowerCase().search(/slot\d/) !== -1) { + let internalSlotNum = 1; + let militarySlotNum = 1; + for (let i in shipTemplate.slots.internal) { + const isMilitary = isNaN(shipTemplate.slots.internal[i]) ? shipTemplate.slots.internal[i].name = 'military' : false; + + // The internal slot might be a standard or a military slot. Military slots have a different naming system + let internalSlot = null; + if (isMilitary) { + const internalName = 'Military0' + militarySlotNum; + internalSlot = json.Modules.find(elem => elem.Slot.toLowerCase() === internalName.toLowerCase()); + militarySlotNum++; + } else { + // Slot numbers are not contiguous so handle skips. + while (internalSlot === null && internalSlotNum < 99) { + // Slot sizes have no relationship to the actual size, either, so check all possibilities + for (let slotsize = 0; slotsize < 9; slotsize++) { + const internalName = 'Slot' + (internalSlotNum <= 9 ? '0' : '') + internalSlotNum + '_Size' + slotsize; + if (json.Modules.find(elem => elem.Slot.toLowerCase() === internalName.toLowerCase())) { + internalSlot = json.Modules.find(elem => elem.Slot.toLowerCase() === internalName.toLowerCase()); + break; + } + } + internalSlotNum++; + } + } + + if (!internalSlot) { + // This can happen with old imports that don't contain new slots + } else if (!internalSlot) { + // No module + } else { + const internalJson = internalSlot; + const internal = _moduleFromFdName(internalJson.Item); + ship.use(ship.internal[i], internal, true); + ship.internal[i].enabled = internalJson.On === true; + ship.internal[i].priority = internalJson.Priority; + opts.push({ coriolisMod: internal, json: internalSlot }); + } + } + } + } + } + + for (const i of opts) { + if (i.json.Engineering) _addModifications(i.coriolisMod, i.json.Engineering.Modifiers, i.json.Engineering.BlueprintName, i.json.Engineering.Level, i.json.Engineering.ExperimentalEffect); + } + // We don't have any information on it so guess it's priority 5 and disabled + if (!ship.cargoHatch) { + ship.cargoHatch.enabled = false; + ship.cargoHatch.priority = 4; + } + + // Now update the ship's codes before returning it + return ship.updatePowerPrioritesString().updatePowerEnabledString().updateModificationsString(); +} + +/** + * Add the modifications for a module + * @param {Module} module the module + * @param {Object} modifiers the modifiers + * @param {Object} blueprint the blueprint of the modification + * @param {Object} grade the grade of the modification + * @param {Object} specialModifications special modification + */ +function _addModifications(module, modifiers, blueprint, grade, specialModifications) { + if (!modifiers) return; + let special; + if (specialModifications) { + special = Modifications.specials[specialModifications]; + } + for (const i in modifiers) { + // Some special modifications + // Look up the modifiers to find what we need to do + const findMod = val => Object.keys(Modifications.modifierActions).find(elem => elem.toString().toLowerCase().replace(/(outfittingfieldtype_|persecond)/igm, '') === val.toString().toLowerCase().replace(/(outfittingfieldtype_|persecond)/igm, '')); + const modifierActions = Modifications.modifierActions[findMod(modifiers[i].Label)]; + // TODO: Figure out how to scale this value. + if (!!modifiers[i].LessIsGood) { + + } + let value = (modifiers[i].Value / modifiers[i].OriginalValue * 100 - 100) * 100; + if (value === Infinity) { + value = modifiers[i].Value * 100; + } + if (modifiers[i].Label.search('Resistance') >= 0) { + value = (modifiers[i].Value * 100) - (modifiers[i].OriginalValue * 100); + } + // Carry out the required changes + for (const action in modifierActions) { + if (isNaN(modifierActions[action])) { + module.setModValue(action, modifierActions[action]); + } else { + module.setModValue(action, value); + } + } + } + + // Add the blueprint definition, grade and special + if (blueprint) { + module.blueprint = getBlueprint(blueprint, module); + if (grade) { + module.blueprint.grade = Number(grade); + } + if (special) { + module.blueprint.special = special; + } + } +}
    {int(ship.shieldThermRes * 100) + '%'} {int(sgMetrics && sgMetrics.generator ? sgMetrics.total / sgMetrics.absolute.total : 0)} {int(sgMetrics && sgMetrics.generator ? sgMetrics.total / sgMetrics.explosive.total : 0)}{int(sgMetrics && sgMetrics.generator ? sgMetrics.total / sgMetrics.kinetic.total : 0 )}{int(sgMetrics && sgMetrics.generator ? sgMetrics.total / sgMetrics.thermal.total : 0 )}{int(sgMetrics && sgMetrics.generator ? sgMetrics.total / sgMetrics.kinetic.total : 0)}{int(sgMetrics && sgMetrics.generator ? sgMetrics.total / sgMetrics.thermal.total : 0)} {sgMetrics && sgMetrics.recover ? formats.time(sgMetrics.recover) : 0} {sgMetrics && sgMetrics.recharge ? formats.time(sgMetrics.recharge) : 0}
    {translate(feature, grp)}{translate(feature, grp)}   {current}{symbol}