Merge remote-tracking branch 'origin/develop' into develop

This commit is contained in:
willyb321
2018-06-10 06:26:17 +10:00
8 changed files with 2407 additions and 2406 deletions

View File

@@ -8,7 +8,8 @@ cache:
directories: directories:
- node_modules - node_modules
before_script: before_install:
- git clone https://github.com/EDCD/coriolis-data.git ../coriolis-data
script: script:
- npm run lint - npm run lint

File diff suppressed because it is too large Load Diff

View File

@@ -1,476 +1,476 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import * as _ from 'lodash'; import * as _ from 'lodash';
import TranslatedComponent from './TranslatedComponent'; import TranslatedComponent from './TranslatedComponent';
import { isEmpty, stopCtxPropagation } from '../utils/UtilityFunctions'; import { isEmpty, stopCtxPropagation } from '../utils/UtilityFunctions';
import cn from 'classnames'; import cn from 'classnames';
import { Modifications } from 'coriolis-data/dist'; import { Modifications } from 'coriolis-data/dist';
import Modification from './Modification'; import Modification from './Modification';
import { import {
getBlueprint, getBlueprint,
blueprintTooltip, blueprintTooltip,
setPercent, setPercent,
getPercent, getPercent,
setRandom, setRandom,
specialToolTip specialToolTip
} from '../utils/BlueprintFunctions'; } from '../utils/BlueprintFunctions';
/** /**
* Modifications menu * Modifications menu
*/ */
export default class ModificationsMenu extends TranslatedComponent { export default class ModificationsMenu extends TranslatedComponent {
static propTypes = { static propTypes = {
ship: PropTypes.object.isRequired, ship: PropTypes.object.isRequired,
m: PropTypes.object.isRequired, m: PropTypes.object.isRequired,
marker: PropTypes.string.isRequired, marker: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired, onChange: PropTypes.func.isRequired,
modButton:PropTypes.object modButton:PropTypes.object
}; };
/** /**
* Constructor * Constructor
* @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, context) {
super(props); super(props);
this._toggleBlueprintsMenu = this._toggleBlueprintsMenu.bind(this); this._toggleBlueprintsMenu = this._toggleBlueprintsMenu.bind(this);
this._toggleSpecialsMenu = this._toggleSpecialsMenu.bind(this); this._toggleSpecialsMenu = this._toggleSpecialsMenu.bind(this);
this._rollFifty = this._rollFifty.bind(this); this._rollFifty = this._rollFifty.bind(this);
this._rollRandom = this._rollRandom.bind(this); this._rollRandom = this._rollRandom.bind(this);
this._rollBest = this._rollBest.bind(this); this._rollBest = this._rollBest.bind(this);
this._rollWorst = this._rollWorst.bind(this); this._rollWorst = this._rollWorst.bind(this);
this._reset = this._reset.bind(this); this._reset = this._reset.bind(this);
this._keyDown = this._keyDown.bind(this); this._keyDown = this._keyDown.bind(this);
this.modItems = [];// Array to hold various element refs (<li>, <div>, <ul>, etc.) this.modItems = [];// Array to hold various element refs (<li>, <div>, <ul>, etc.)
this.firstModId = null; this.firstModId = null;
this.firstBPLabel = null;// First item in mod menu this.firstBPLabel = null;// First item in mod menu
this.lastModId = null; 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.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.modValDidChange = false; // used to determine if component update was caused by change in modification value.
this._handleModChange = this._handleModChange.bind(this); this._handleModChange = this._handleModChange.bind(this);
this.state = { this.state = {
blueprintMenuOpened: !(props.m.blueprint && props.m.blueprint.name), blueprintMenuOpened: !(props.m.blueprint && props.m.blueprint.name),
specialMenuOpened: false specialMenuOpened: false
}; };
} }
/** /**
* Render the blueprints * Render the blueprints
* @param {Object} props React component properties * @param {Object} props React component properties
* @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
*/ */
_renderBlueprints(props, context) { _renderBlueprints(props, context) {
const { m } = props; const { m } = props;
const { language, tooltip, termtip } = context; const { language, tooltip, termtip } = context;
const translate = language.translate; const translate = language.translate;
const blueprints = []; const blueprints = [];
for (const blueprintName in Modifications.modules[m.grp].blueprints) { for (const blueprintName in Modifications.modules[m.grp].blueprints) {
const blueprint = getBlueprint(blueprintName, m); const blueprint = getBlueprint(blueprintName, m);
let blueprintGrades = []; let blueprintGrades = [];
for (let grade in Modifications.modules[m.grp].blueprints[blueprintName].grades) { for (let grade in Modifications.modules[m.grp].blueprints[blueprintName].grades) {
// Grade is a string in the JSON so make it a number // Grade is a string in the JSON so make it a number
grade = Number(grade); grade = Number(grade);
const classes = cn('c', { 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 close = this._blueprintSelected.bind(this, blueprintName, grade);
const key = blueprintName + ':' + grade; const key = blueprintName + ':' + grade;
const tooltipContent = blueprintTooltip(translate, blueprint.grades[grade], Modifications.modules[m.grp].blueprints[blueprintName].grades[grade].engineers, m.grp); const tooltipContent = blueprintTooltip(translate, blueprint.grades[grade], Modifications.modules[m.grp].blueprints[blueprintName].grades[grade].engineers, m.grp);
blueprintGrades.unshift(<li key={key} tabIndex="0" data-id={key} className={classes} style={{ width: '2em' }} onMouseOver={termtip.bind(null, tooltipContent)} onMouseOut={tooltip.bind(null, null)} onClick={close} onKeyDown={this._keyDown} ref={modItem => this.modItems[key] = modItem}>{grade}</li>); blueprintGrades.unshift(<li key={key} tabIndex="0" data-id={key} className={classes} style={{ width: '2em' }} onMouseOver={termtip.bind(null, tooltipContent)} onMouseOut={tooltip.bind(null, null)} onClick={close} onKeyDown={this._keyDown} ref={modItem => this.modItems[key] = modItem}>{grade}</li>);
} }
if (blueprintGrades) { if (blueprintGrades) {
const thisLen = blueprintGrades.length; const thisLen = blueprintGrades.length;
if (this.firstModId == null) this.firstModId = blueprintGrades[0].key; if (this.firstModId == null) this.firstModId = blueprintGrades[0].key;
this.lastModId = blueprintGrades[thisLen - 1].key; this.lastModId = blueprintGrades[thisLen - 1].key;
blueprints.push(<div key={blueprint.name} className={'select-group cap'}>{translate(blueprint.name)}</div>); blueprints.push(<div key={blueprint.name} className={'select-group cap'}>{translate(blueprint.name)}</div>);
blueprints.push(<ul key={blueprintName}>{blueprintGrades}</ul>); blueprints.push(<ul key={blueprintName}>{blueprintGrades}</ul>);
} }
} }
return blueprints; return blueprints;
} }
/** /**
* Key down - select module on Enter key, move to next/previous module on Tab/Shift-Tab, close on Esc * Key down - select module on Enter key, move to next/previous module on Tab/Shift-Tab, close on Esc
* @param {Function} event Select module callback * @param {Function} event Select module callback
*/ */
_keyDown(event) { _keyDown(event) {
let className = null; let className = null;
let elemId = null; let elemId = null;
if (event.currentTarget.attributes['class']) className = event.currentTarget.attributes['class'].value; 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.currentTarget.attributes['data-id']) elemId = event.currentTarget.attributes['data-id'].value;
if (event.key == 'Enter' && className.indexOf('disabled') < 0 && className.indexOf('active') < 0) { if (event.key == 'Enter' && className.indexOf('disabled') < 0 && className.indexOf('active') < 0) {
event.stopPropagation(); event.stopPropagation();
if (elemId != null) { if (elemId != null) {
this.modItems[elemId].click(); this.modItems[elemId].click();
} else { } else {
event.currentTarget.click(); event.currentTarget.click();
} }
return; return;
} }
if (event.key == 'Tab') { if (event.key == 'Tab') {
// Shift-Tab // Shift-Tab
if(event.shiftKey) { if(event.shiftKey) {
if (elemId == this.firstModId && elemId != null) { if (elemId == this.firstModId && elemId != null) {
// Initial modification menu // Initial modification menu
event.preventDefault(); event.preventDefault();
this.modItems[this.lastModId].focus(); this.modItems[this.lastModId].focus();
return; return;
} else if (event.currentTarget.className.indexOf('button-inline-menu') >= 0 && event.currentTarget.previousElementSibling == null && this.lastNeId != null && this.modItems[this.lastNeId] != null) { } 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 // shift-tab on first element in modifications menu. set focus to last number editor field if open
event.preventDefault(); event.preventDefault();
this.modItems[this.lastNeId].lastChild.focus(); this.modItems[this.lastNeId].lastChild.focus();
return; 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 // shift-tab on button-inline-menu with no number editor
event.preventDefault(); event.preventDefault();
event.currentTarget.parentElement.lastElementChild.focus(); event.currentTarget.parentElement.lastElementChild.focus();
} }
} else { } else {
if (elemId == this.lastModId && elemId != null) { if (elemId == this.lastModId && elemId != null) {
// Initial modification menu // Initial modification menu
event.preventDefault(); event.preventDefault();
this.modItems[this.firstModId].focus(); this.modItems[this.firstModId].focus();
return; 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 // Experimental menu
event.preventDefault(); event.preventDefault();
event.currentTarget.parentElement.firstElementChild.focus(); event.currentTarget.parentElement.firstElementChild.focus();
return; return;
} else if (event.currentTarget.className == 'cb' && event.currentTarget.parentElement.nextSibling == null) { } else if (event.currentTarget.className == 'cb' && event.currentTarget.parentElement.nextSibling == null) {
event.preventDefault(); event.preventDefault();
this.modItems[this.firstBPLabel].focus(); this.modItems[this.firstBPLabel].focus();
} }
} }
} }
} }
/** /**
* Render the specials * Render the specials
* @param {Object} props React component properties * @param {Object} props React component properties
* @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(props, context) {
const { m } = props; const { m } = props;
const { language, tooltip, termtip } = context; const { language, tooltip, termtip } = context;
const translate = language.translate; const translate = language.translate;
const specials = []; const specials = [];
const specialsId = m.missile && Modifications.modules[m.grp]['specials_' + m.missile] ? 'specials_' + m.missile : 'specials'; const specialsId = m.missile && Modifications.modules[m.grp]['specials_' + m.missile] ? 'specials_' + m.missile : 'specials';
if (Modifications.modules[m.grp][specialsId] && Modifications.modules[m.grp][specialsId].length > 0) { if (Modifications.modules[m.grp][specialsId] && Modifications.modules[m.grp][specialsId].length > 0) {
const close = this._specialSelected.bind(this, null); const close = this._specialSelected.bind(this, null);
specials.push(<div tabIndex="0" style={{ cursor: 'pointer', fontWeight: 'bold' }} className={ 'button-inline-menu warning' } key={ 'none' } data-id={ 'none' } onClick={ close } onKeyDown={this._keyDown} ref={modItem => this.modItems['none'] = modItem}>{translate('PHRASE_NO_SPECIAL')}</div>); specials.push(<div tabIndex="0" style={{ cursor: 'pointer', fontWeight: 'bold' }} className={ 'button-inline-menu warning' } key={ 'none' } data-id={ 'none' } onClick={ close } onKeyDown={this._keyDown} ref={modItem => this.modItems['none'] = modItem}>{translate('PHRASE_NO_SPECIAL')}</div>);
for (const specialName of Modifications.modules[m.grp][specialsId]) { for (const specialName of Modifications.modules[m.grp][specialsId]) {
if (Modifications.specials[specialName].name.search('Legacy') >= 0) { if (Modifications.specials[specialName].name.search('Legacy') >= 0) {
continue; continue;
} }
const classes = cn('button-inline-menu', { 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); const close = this._specialSelected.bind(this, specialName);
if (m.blueprint && m.blueprint.name) { if (m.blueprint && m.blueprint.name) {
let tmp = {}; let tmp = {};
if (m.blueprint.special) { if (m.blueprint.special) {
tmp = m.blueprint.special; tmp = m.blueprint.special;
} else { } else {
tmp = undefined; tmp = undefined;
} }
m.blueprint.special = Modifications.specials[specialName]; m.blueprint.special = Modifications.specials[specialName];
let specialTt = specialToolTip(translate, m.blueprint.grades[m.blueprint.grade], m.grp, m, specialName); let specialTt = specialToolTip(translate, m.blueprint.grades[m.blueprint.grade], m.grp, m, specialName);
m.blueprint.special = tmp; m.blueprint.special = tmp;
specials.push(<div tabIndex="0" style={{ cursor: 'pointer' }} className={classes} key={ specialName } data-id={ specialName } onMouseOver={termtip.bind(null, specialTt)} onMouseOut={tooltip.bind(null, null)} onClick={ close } onKeyDown={this._keyDown} ref={modItem => this.modItems[specialName] = modItem}>{translate(Modifications.specials[specialName].name)}</div>); specials.push(<div tabIndex="0" style={{ cursor: 'pointer' }} className={classes} key={ specialName } data-id={ specialName } onMouseOver={termtip.bind(null, specialTt)} onMouseOut={tooltip.bind(null, null)} onClick={ close } onKeyDown={this._keyDown} ref={modItem => this.modItems[specialName] = modItem}>{translate(Modifications.specials[specialName].name)}</div>);
} else { } else {
specials.push(<div tabIndex="0" style={{ cursor: 'pointer' }} className={classes} key={ specialName } data-id={ specialName }onClick={ close } onKeyDown={this._keyDown} ref={modItem => this.modItems[specialName] = modItem}>{translate(Modifications.specials[specialName].name)}</div>); specials.push(<div tabIndex="0" style={{ cursor: 'pointer' }} className={classes} key={ specialName } data-id={ specialName }onClick={ close } onKeyDown={this._keyDown} ref={modItem => this.modItems[specialName] = modItem}>{translate(Modifications.specials[specialName].name)}</div>);
} }
} }
} }
console.log('_renderSpecials. specials: %O', specials); console.log('_renderSpecials. specials: %O', specials);
return specials; return specials;
} }
/** /**
* Render the modifications * Render the modifications
* @param {Object} props React Component properties * @param {Object} props React Component properties
* @return {Object} list: Array of React Components * @return {Object} list: Array of React Components
*/ */
_renderModifications(props) { _renderModifications(props) {
const { m, onChange, ship } = props; const { m, onChange, ship } = props;
const modifications = []; const modifications = [];
for (const modName of Modifications.modules[m.grp].modifications) { for (const modName of Modifications.modules[m.grp].modifications) {
if (!Modifications.modifications[modName].hidden) { if (!Modifications.modifications[modName].hidden) {
const key = modName + (m.getModValue(modName) / 100 || 0); const key = modName + (m.getModValue(modName) / 100 || 0);
this.lastNeId = modName; this.lastNeId = modName;
modifications.push(<Modification key={ key } ship={ ship } m={ m } name={ modName } value={ m.getModValue(modName) / 100 || 0 } onChange={ onChange } onKeyDown={ this._keyDown } modItems={ this.modItems } handleModChange = {this._handleModChange} />); modifications.push(<Modification key={ key } ship={ ship } m={ m } name={ modName } value={ m.getModValue(modName) / 100 || 0 } onChange={ onChange } onKeyDown={ this._keyDown } modItems={ this.modItems } handleModChange = {this._handleModChange} />);
} }
} }
console.log('_renderModifications. modItems: %O', this.modItems); console.log('_renderModifications. modItems: %O', this.modItems);
return modifications; return modifications;
} }
/** /**
* Toggle the blueprints menu * Toggle the blueprints menu
*/ */
_toggleBlueprintsMenu() { _toggleBlueprintsMenu() {
const blueprintMenuOpened = !this.state.blueprintMenuOpened; const blueprintMenuOpened = !this.state.blueprintMenuOpened;
this.setState({ blueprintMenuOpened }); this.setState({ blueprintMenuOpened });
} }
/** /**
* Activated when a blueprint is selected * Activated when a blueprint is selected
* @param {int} fdname The Frontier name of the blueprint * @param {int} fdname The Frontier name of the blueprint
* @param {int} grade The grade of the selected blueprint * @param {int} grade The grade of the selected blueprint
*/ */
_blueprintSelected(fdname, grade) { _blueprintSelected(fdname, grade) {
this.context.tooltip(null); this.context.tooltip(null);
const { m, ship } = this.props; const { m, ship } = this.props;
const blueprint = getBlueprint(fdname, m); const blueprint = getBlueprint(fdname, m);
blueprint.grade = grade; blueprint.grade = grade;
ship.setModuleBlueprint(m, blueprint); ship.setModuleBlueprint(m, blueprint);
setPercent(ship, m, 100); setPercent(ship, m, 100);
this.setState({ blueprintMenuOpened: false, specialMenuOpened: true }); this.setState({ blueprintMenuOpened: false, specialMenuOpened: true });
this.props.onChange(); this.props.onChange();
} }
/** /**
* Toggle the specials menu * Toggle the specials menu
*/ */
_toggleSpecialsMenu() { _toggleSpecialsMenu() {
const specialMenuOpened = !this.state.specialMenuOpened; const specialMenuOpened = !this.state.specialMenuOpened;
this.setState({ specialMenuOpened }); this.setState({ specialMenuOpened });
} }
/** /**
* Activated when a special is selected * Activated when a special is selected
* @param {int} special The name of the selected special * @param {int} special The name of the selected special
*/ */
_specialSelected(special) { _specialSelected(special) {
this.context.tooltip(null); this.context.tooltip(null);
const { m, ship } = this.props; const { m, ship } = this.props;
if (special === null) { if (special === null) {
ship.clearModuleSpecial(m); ship.clearModuleSpecial(m);
} else { } else {
ship.setModuleSpecial(m, Modifications.specials[special]); ship.setModuleSpecial(m, Modifications.specials[special]);
} }
this.setState({ specialMenuOpened: false }); this.setState({ specialMenuOpened: false });
this.props.onChange(); this.props.onChange();
} }
/** /**
* Provide a '50%' roll within the information we have * Provide a '50%' roll within the information we have
*/ */
_rollFifty() { _rollFifty() {
const { m, ship } = this.props; const { m, ship } = this.props;
setPercent(ship, m, 50); setPercent(ship, m, 50);
// this will change the values in the modifications. Set modDidChange to true to prevent focus change when component updates // this will change the values in the modifications. Set modDidChange to true to prevent focus change when component updates
this._handleModChange(true); this._handleModChange(true);
this.props.onChange(); this.props.onChange();
} }
/** /**
* Provide a random roll within the information we have * Provide a random roll within the information we have
*/ */
_rollRandom() { _rollRandom() {
const { m, ship } = this.props; const { m, ship } = this.props;
setRandom(ship, m); setRandom(ship, m);
// this will change the values in the modifications. Set modDidChange to true to prevent focus change when component updates // this will change the values in the modifications. Set modDidChange to true to prevent focus change when component updates
this._handleModChange(true); this._handleModChange(true);
this.props.onChange(); this.props.onChange();
} }
/** /**
* Provide a 'best' roll within the information we have * Provide a 'best' roll within the information we have
*/ */
_rollBest() { _rollBest() {
const { m, ship } = this.props; const { m, ship } = this.props;
setPercent(ship, m, 100); setPercent(ship, m, 100);
// this will change the values in the modifications. Set modDidChange to true to prevent focus change when component updates // this will change the values in the modifications. Set modDidChange to true to prevent focus change when component updates
this._handleModChange(true); this._handleModChange(true);
this.props.onChange(); this.props.onChange();
} }
/** /**
* Provide a 'worst' roll within the information we have * Provide a 'worst' roll within the information we have
*/ */
_rollWorst() { _rollWorst() {
const { m, ship } = this.props; const { m, ship } = this.props;
setPercent(ship, m, 0); setPercent(ship, m, 0);
// this will change the values in the modifications. Set modDidChange to true to prevent focus change when component updates // this will change the values in the modifications. Set modDidChange to true to prevent focus change when component updates
this._handleModChange(true); this._handleModChange(true);
this.props.onChange(); this.props.onChange();
} }
/** /**
* Reset modification information * Reset modification information
*/ */
_reset() { _reset() {
const { m, ship } = this.props; const { m, ship } = this.props;
ship.clearModifications(m); ship.clearModifications(m);
ship.clearModuleBlueprint(m); ship.clearModuleBlueprint(m);
this.props.onChange(); this.props.onChange();
} }
/** /**
* set mod did change boolean * set mod did change boolean
* @param {Boolean} b Did the mod value change? * @param {Boolean} b Did the mod value change?
*/ */
_handleModChange(b) { _handleModChange(b) {
this.modValDidChange = b; this.modValDidChange = b;
} }
/** /**
* Set focus on first element in modifications menu * Set focus on first element in modifications menu
* after it first mounts * after it first mounts
*/ */
componentDidMount() { componentDidMount() {
let firstEleCn = this.modItems['modMainDiv'].children.length > 0 ? this.modItems['modMainDiv'].children[0].className : null; let firstEleCn = this.modItems['modMainDiv'].children.length > 0 ? this.modItems['modMainDiv'].children[0].className : null;
if (firstEleCn.indexOf('select-group cap') >= 0) { if (firstEleCn.indexOf('select-group cap') >= 0) {
this.modItems['modMainDiv'].children[1].firstElementChild.focus(); this.modItems['modMainDiv'].children[1].firstElementChild.focus();
} else { } else {
this.modItems['modMainDiv'].firstElementChild.focus(); this.modItems['modMainDiv'].firstElementChild.focus();
} }
} }
/** /**
* Set focus on first element in modifications menu * Set focus on first element in modifications menu
* if component updates, unless update is due to value change * if component updates, unless update is due to value change
* in a modification * in a modification
*/ */
componentDidUpdate() { componentDidUpdate() {
if (!this.modValDidChange) { if (!this.modValDidChange) {
if (this.modItems['modMainDiv'].children.length > 0) { if (this.modItems['modMainDiv'].children.length > 0) {
let firstEleCn = this.modItems['modMainDiv'].children[0].className; let firstEleCn = this.modItems['modMainDiv'].children[0].className;
if (firstEleCn.indexOf('button-inline-menu') >= 0) { if (firstEleCn.indexOf('button-inline-menu') >= 0) {
this.modItems['modMainDiv'].firstElementChild.focus(); this.modItems['modMainDiv'].firstElementChild.focus();
} else if (firstEleCn.indexOf('select-group cap') >= 0) { } else if (firstEleCn.indexOf('select-group cap') >= 0) {
this.modItems['modMainDiv'].children[1].firstElementChild.focus(); this.modItems['modMainDiv'].children[1].firstElementChild.focus();
} }
} }
} else { } 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 * Set focus to the modification menu icon
*/ */
componentWillUnmount() { componentWillUnmount() {
if (this.props.modButton) { if (this.props.modButton) {
this.props.modButton.focus();// set focus to the modification menu icon after mod menu is unmounted. this.props.modButton.focus();// set focus to the modification menu icon after mod menu is unmounted.
} }
} }
/** /**
* Render the list * Render the list
* @return {React.Component} List * @return {React.Component} List
*/ */
render() { render() {
const { language, tooltip, termtip } = this.context; const { language, tooltip, termtip } = this.context;
const translate = language.translate; const translate = language.translate;
const { m } = this.props; const { m } = this.props;
const { blueprintMenuOpened, specialMenuOpened } = this.state; const { blueprintMenuOpened, specialMenuOpened } = this.state;
const _toggleBlueprintsMenu = this._toggleBlueprintsMenu; const _toggleBlueprintsMenu = this._toggleBlueprintsMenu;
const _toggleSpecialsMenu = this._toggleSpecialsMenu; const _toggleSpecialsMenu = this._toggleSpecialsMenu;
const _rollFull = this._rollBest; const _rollFull = this._rollBest;
const _rollWorst = this._rollWorst; const _rollWorst = this._rollWorst;
const _rollFifty = this._rollFifty; const _rollFifty = this._rollFifty;
const _rollRandom = this._rollRandom; const _rollRandom = this._rollRandom;
const _reset = this._reset; const _reset = this._reset;
let blueprintLabel; let blueprintLabel;
let haveBlueprint = false; let haveBlueprint = false;
let blueprintTt; let blueprintTt;
let blueprintCv; 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]) { 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.clearModuleBlueprint(m);
this.props.ship.clearModuleSpecial(m); this.props.ship.clearModuleSpecial(m);
} }
if (m.blueprint && m.blueprint.name && Modifications.modules[m.grp].blueprints[m.blueprint.fdname].grades[m.blueprint.grade]) { if (m.blueprint && m.blueprint.name && Modifications.modules[m.grp].blueprints[m.blueprint.fdname].grades[m.blueprint.grade]) {
blueprintLabel = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade; blueprintLabel = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
haveBlueprint = true; haveBlueprint = true;
blueprintTt = blueprintTooltip(translate, m.blueprint.grades[m.blueprint.grade], Modifications.modules[m.grp].blueprints[m.blueprint.fdname].grades[m.blueprint.grade].engineers, m.grp); blueprintTt = blueprintTooltip(translate, m.blueprint.grades[m.blueprint.grade], Modifications.modules[m.grp].blueprints[m.blueprint.fdname].grades[m.blueprint.grade].engineers, m.grp);
blueprintCv = getPercent(m); blueprintCv = getPercent(m);
} }
let specialLabel; let specialLabel;
let specialTt; let specialTt;
if (m.blueprint && m.blueprint.special) { if (m.blueprint && m.blueprint.special) {
specialLabel = m.blueprint.special.name; specialLabel = m.blueprint.special.name;
specialTt = specialToolTip(translate, m.blueprint.grades[m.blueprint.grade], m.grp, m, m.blueprint.special.edname); specialTt = specialToolTip(translate, m.blueprint.grades[m.blueprint.grade], m.grp, m, m.blueprint.special.edname);
} else { } else {
specialLabel = translate('PHRASE_SELECT_SPECIAL'); specialLabel = translate('PHRASE_SELECT_SPECIAL');
} }
const specials = this._renderSpecials(this.props, this.context); const specials = this._renderSpecials(this.props, this.context);
/** /**
* pnellesen - 05/28/2018 - added additional checks for specials.length below to ensure menus * pnellesen - 05/28/2018 - added additional checks for specials.length below to ensure menus
* display correctly in cases where there are no specials (ex: AFMUs.) * display correctly in cases where there are no specials (ex: AFMUs.)
*/ */
const showBlueprintsMenu = blueprintMenuOpened; const showBlueprintsMenu = blueprintMenuOpened;
const showSpecial = haveBlueprint && specials.length && !blueprintMenuOpened; const showSpecial = haveBlueprint && specials.length && !blueprintMenuOpened;
const showSpecialsMenu = specialMenuOpened && specials.length; const showSpecialsMenu = specialMenuOpened && specials.length;
const showRolls = haveBlueprint && !blueprintMenuOpened && (!specialMenuOpened || !specials.length); const showRolls = haveBlueprint && !blueprintMenuOpened && (!specialMenuOpened || !specials.length);
const showReset = !blueprintMenuOpened && (!specialMenuOpened || !specials.length) && haveBlueprint; const showReset = !blueprintMenuOpened && (!specialMenuOpened || !specials.length) && haveBlueprint;
const showMods = !blueprintMenuOpened && (!specialMenuOpened || !specials.length) && haveBlueprint; const showMods = !blueprintMenuOpened && (!specialMenuOpened || !specials.length) && haveBlueprint;
if (haveBlueprint) { if (haveBlueprint) {
this.firstBPLabel = blueprintLabel; this.firstBPLabel = blueprintLabel;
} else { } else {
this.firstBPLabel = 'selectBP'; this.firstBPLabel = 'selectBP';
} }
return ( return (
<div <div
className={cn('select', this.props.className)} className={cn('select', this.props.className)}
onClick={(e) => e.stopPropagation() } onClick={(e) => e.stopPropagation() }
onContextMenu={stopCtxPropagation} onContextMenu={stopCtxPropagation}
ref={modItem => this.modItems['modMainDiv'] = modItem} ref={modItem => this.modItems['modMainDiv'] = modItem}
> >
{ showBlueprintsMenu | showSpecialsMenu ? '' : haveBlueprint ? { showBlueprintsMenu | showSpecialsMenu ? '' : haveBlueprint ?
<div tabIndex="0" className={ cn('section-menu button-inline-menu', { selected: blueprintMenuOpened })} style={{ cursor: 'pointer' }} onMouseOver={termtip.bind(null, blueprintTt)} onMouseOut={tooltip.bind(null, null)} onClick={_toggleBlueprintsMenu} onKeyDown={ this._keyDown } ref={modItems => this.modItems[this.firstBPLabel] = modItems}>{blueprintLabel}</div> : <div tabIndex="0" className={ cn('section-menu button-inline-menu', { selected: blueprintMenuOpened })} style={{ cursor: 'pointer' }} onMouseOver={termtip.bind(null, blueprintTt)} onMouseOut={tooltip.bind(null, null)} onClick={_toggleBlueprintsMenu} onKeyDown={ this._keyDown } ref={modItems => this.modItems[this.firstBPLabel] = modItems}>{blueprintLabel}</div> :
<div tabIndex="0" className={ cn('section-menu button-inline-menu', { selected: blueprintMenuOpened })} style={{ cursor: 'pointer' }} onClick={_toggleBlueprintsMenu} onKeyDown={ this._keyDown } ref={modItems => this.modItems[this.firstBPLabel] = modItems}>{translate('PHRASE_SELECT_BLUEPRINT')}</div> } <div tabIndex="0" className={ cn('section-menu button-inline-menu', { selected: blueprintMenuOpened })} style={{ cursor: 'pointer' }} onClick={_toggleBlueprintsMenu} onKeyDown={ this._keyDown } ref={modItems => this.modItems[this.firstBPLabel] = modItems}>{translate('PHRASE_SELECT_BLUEPRINT')}</div> }
{ showBlueprintsMenu ? this._renderBlueprints(this.props, this.context) : null } { showBlueprintsMenu ? this._renderBlueprints(this.props, this.context) : null }
{ showSpecial & !showSpecialsMenu ? <div tabIndex="0" className={ cn('section-menu button-inline-menu', { selected: specialMenuOpened })} style={{ cursor: 'pointer' }} onMouseOver={specialTt ? termtip.bind(null, specialTt) : null} onMouseOut={specialTt ? tooltip.bind(null, null) : null} onClick={_toggleSpecialsMenu} onKeyDown={ this._keyDown }>{specialLabel}</div> : null } { showSpecial & !showSpecialsMenu ? <div tabIndex="0" className={ cn('section-menu button-inline-menu', { selected: specialMenuOpened })} style={{ cursor: 'pointer' }} onMouseOver={specialTt ? termtip.bind(null, specialTt) : null} onMouseOut={specialTt ? tooltip.bind(null, null) : null} onClick={_toggleSpecialsMenu} onKeyDown={ this._keyDown }>{specialLabel}</div> : null }
{ showSpecialsMenu ? specials : null } { showSpecialsMenu ? specials : null }
{ showReset ? <div tabIndex="0" className={'section-menu button-inline-menu warning'} style={{ cursor: 'pointer' }} onClick={_reset} onKeyDown={ this._keyDown } onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_RESET')} onMouseOut={tooltip.bind(null, null)}> { translate('reset') } </div> : null } { showReset ? <div tabIndex="0" className={'section-menu button-inline-menu warning'} style={{ cursor: 'pointer' }} onClick={_reset} onKeyDown={ this._keyDown } onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_RESET')} onMouseOut={tooltip.bind(null, null)}> { translate('reset') } </div> : null }
{ showRolls ? { showRolls ?
<table style={{ width: '100%', backgroundColor: 'transparent' }}> <table style={{ width: '100%', backgroundColor: 'transparent' }}>
<tbody> <tbody>
{ showRolls ? { showRolls ?
<tr> <tr>
<td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: false })}> { translate('roll') }: </td> <td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: false })}> { translate('roll') }: </td>
<td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: blueprintCv === 0 })} style={{ cursor: 'pointer' }} onClick={_rollWorst} onKeyDown={ this._keyDown } onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_WORST')} onMouseOut={tooltip.bind(null, null)}> { translate('0%') } </td> <td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: blueprintCv === 0 })} style={{ cursor: 'pointer' }} onClick={_rollWorst} onKeyDown={ this._keyDown } onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_WORST')} onMouseOut={tooltip.bind(null, null)}> { translate('0%') } </td>
<td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: blueprintCv === 50 })} style={{ cursor: 'pointer' }} onClick={_rollFifty} onKeyDown={ this._keyDown } onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_FIFTY')} onMouseOut={tooltip.bind(null, null)}> { translate('50%') } </td> <td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: blueprintCv === 50 })} style={{ cursor: 'pointer' }} onClick={_rollFifty} onKeyDown={ this._keyDown } onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_FIFTY')} onMouseOut={tooltip.bind(null, null)}> { translate('50%') } </td>
<td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: blueprintCv === 100 })} style={{ cursor: 'pointer' }} onClick={_rollFull} onKeyDown={ this._keyDown } onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_BEST')} onMouseOut={tooltip.bind(null, null)}> { translate('100%') } </td> <td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: blueprintCv === 100 })} style={{ cursor: 'pointer' }} onClick={_rollFull} onKeyDown={ this._keyDown } onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_BEST')} onMouseOut={tooltip.bind(null, null)}> { translate('100%') } </td>
<td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: blueprintCv === null || blueprintCv % 50 != 0 })} style={{ cursor: 'pointer' }} onClick={_rollRandom} onKeyDown={ this._keyDown } onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_RANDOM')} onMouseOut={tooltip.bind(null, null)}> { translate('random') } </td> <td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: blueprintCv === null || blueprintCv % 50 != 0 })} style={{ cursor: 'pointer' }} onClick={_rollRandom} onKeyDown={ this._keyDown } onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_RANDOM')} onMouseOut={tooltip.bind(null, null)}> { translate('random') } </td>
</tr> : null } </tr> : null }
</tbody> </tbody>
</table> : null } </table> : null }
{ showMods ? <hr /> : null } { showMods ? <hr /> : null }
{ showMods ? { showMods ?
<span onMouseOver={termtip.bind(null, 'HELP_MODIFICATIONS_MENU')} onMouseOut={tooltip.bind(null, null)} > <span onMouseOver={termtip.bind(null, 'HELP_MODIFICATIONS_MENU')} onMouseOut={tooltip.bind(null, null)} >
{ this._renderModifications(this.props) } { this._renderModifications(this.props) }
</span> : null } </span> : null }
</div> </div>
); );
} }
} }

View File

@@ -1,164 +1,164 @@
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 AvailableModulesMenu from './AvailableModulesMenu'; import AvailableModulesMenu from './AvailableModulesMenu';
import ModificationsMenu from './ModificationsMenu'; import ModificationsMenu from './ModificationsMenu';
import { diffDetails } from '../utils/SlotFunctions'; import { diffDetails } from '../utils/SlotFunctions';
import { wrapCtxMenu } from '../utils/UtilityFunctions'; import { wrapCtxMenu } from '../utils/UtilityFunctions';
import { stopCtxPropagation } from '../utils/UtilityFunctions'; import { stopCtxPropagation } from '../utils/UtilityFunctions';
/** /**
* Abstract Slot * Abstract Slot
*/ */
export default class Slot extends TranslatedComponent { export default class Slot extends TranslatedComponent {
static propTypes = { static propTypes = {
availableModules: PropTypes.func.isRequired, availableModules: PropTypes.func.isRequired,
onSelect: PropTypes.func.isRequired, onSelect: PropTypes.func.isRequired,
onOpen: PropTypes.func.isRequired, onOpen: PropTypes.func.isRequired,
maxClass: PropTypes.number.isRequired, maxClass: PropTypes.number.isRequired,
selected: PropTypes.bool, selected: PropTypes.bool,
m: PropTypes.object, m: PropTypes.object,
enabled: PropTypes.bool.isRequired, enabled: PropTypes.bool.isRequired,
ship: PropTypes.object.isRequired, ship: PropTypes.object.isRequired,
eligible: PropTypes.object, eligible: PropTypes.object,
warning: PropTypes.func, warning: PropTypes.func,
drag: PropTypes.func, drag: PropTypes.func,
drop: PropTypes.func, drop: PropTypes.func,
dropClass: PropTypes.string dropClass: PropTypes.string
}; };
/** /**
* Constructor * Constructor
* @param {Object} props React Component properties * @param {Object} props React Component properties
*/ */
constructor(props) { constructor(props) {
super(props); super(props);
this._modificationsSelected = false; this._modificationsSelected = false;
this._contextMenu = wrapCtxMenu(this._contextMenu.bind(this)); this._contextMenu = wrapCtxMenu(this._contextMenu.bind(this));
this._getMaxClassLabel = this._getMaxClassLabel.bind(this); this._getMaxClassLabel = this._getMaxClassLabel.bind(this);
this._keyDown = this._keyDown.bind(this); this._keyDown = this._keyDown.bind(this);
this.slotDiv = null; this.slotDiv = null;
} }
// Must be implemented by subclasses: // Must be implemented by subclasses:
// _getSlotDetails() // _getSlotDetails()
/** /**
* Get the CSS class name for the slot. Can/should be overriden * Get the CSS class name for the slot. Can/should be overriden
* as necessary. * as necessary.
* @return {string} CSS Class name * @return {string} CSS Class name
*/ */
_getClassNames() { _getClassNames() {
return null; 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() {
return this.props.maxClass; return this.props.maxClass;
} }
/** /**
* Empty slot on right-click * Empty slot on right-click
* @param {SyntheticEvent} event Event * @param {SyntheticEvent} event Event
*/ */
_contextMenu(event) { _contextMenu(event) {
event.stopPropagation(); event.stopPropagation();
event.preventDefault(); event.preventDefault();
this.props.onSelect(null,null); this.props.onSelect(null,null);
} }
/** Key Down handler /** Key Down handler
* @param {SyntheticEvent} event Event * @param {SyntheticEvent} event Event
* ToDo: see if this can be moved up * ToDo: see if this can be moved up
* we do more or less the same thing * we do more or less the same thing
* in every section when Enter key is pressed * in every section when Enter key is pressed
* on a focusable item * on a focusable item
* *
*/ */
_keyDown(event) { _keyDown(event) {
if (event.key == 'Enter') { if (event.key == 'Enter') {
if(event.target.className == 'r') { if(event.target.className == 'r') {
console.log('Slot: Enter key pressed on mod icon'); console.log('Slot: Enter key pressed on mod icon');
this._toggleModifications(); this._toggleModifications();
} else { } else {
console.log('Slot: Enter key pressed on: %O', event.target); console.log('Slot: Enter key pressed on: %O', event.target);
} }
this.props.onOpen(event); this.props.onOpen(event);
} }
} }
/** /**
* Render the slot * Render the slot
* @return {React.Component} The slot * @return {React.Component} The slot
*/ */
render() { render() {
let language = this.context.language; let language = this.context.language;
let translate = language.translate; let translate = language.translate;
let { ship, m, enabled, dropClass, dragOver, onOpen, onChange, selected, eligible, onSelect, warning, availableModules } = this.props; let { ship, m, enabled, dropClass, dragOver, onOpen, onChange, selected, eligible, onSelect, warning, availableModules } = this.props;
let slotDetails, modificationsMarker, menu; let slotDetails, modificationsMarker, menu;
if (!selected) { if (!selected) {
// If not selected then sure that modifications flag is unset // If not selected then sure that modifications flag is unset
this._modificationsSelected = false; this._modificationsSelected = false;
} }
if (m) { if (m) {
slotDetails = this._getSlotDetails(m, enabled, translate, language.formats, language.units); // Must be implemented by sub classes slotDetails = this._getSlotDetails(m, enabled, translate, language.formats, language.units); // Must be implemented by sub classes
modificationsMarker = JSON.stringify(m); modificationsMarker = JSON.stringify(m);
} else { } else {
slotDetails = <div className={'empty'}>{translate(eligible ? 'emptyrestricted' : 'empty')}</div>; slotDetails = <div className={'empty'}>{translate(eligible ? 'emptyrestricted' : 'empty')}</div>;
modificationsMarker = ''; modificationsMarker = '';
} }
if (selected) { if (selected) {
if (this._modificationsSelected) { if (this._modificationsSelected) {
menu = <ModificationsMenu menu = <ModificationsMenu
className={this._getClassNames()} className={this._getClassNames()}
onChange={onChange} onChange={onChange}
ship={ship} ship={ship}
m={m} m={m}
marker={modificationsMarker} marker={modificationsMarker}
modButton = {this.modButton} modButton = {this.modButton}
/>; />;
} else { } else {
menu = <AvailableModulesMenu menu = <AvailableModulesMenu
className={this._getClassNames()} className={this._getClassNames()}
modules={availableModules()} modules={availableModules()}
shipMass={ship.hullMass} shipMass={ship.hullMass}
m={m} m={m}
onSelect={onSelect} onSelect={onSelect}
warning={warning} warning={warning}
diffDetails={diffDetails.bind(ship, this.context.language)} diffDetails={diffDetails.bind(ship, this.context.language)}
slotDiv = {this.slotDiv} slotDiv = {this.slotDiv}
/>; />;
} }
} }
// TODO: implement touch dragging // TODO: implement touch dragging
return ( return (
<div className={cn('slot', dropClass, { selected })} onClick={onOpen} onKeyDown={this._keyDown} onContextMenu={this._contextMenu} onDragOver={dragOver} tabIndex="0" ref={slotDiv => this.slotDiv = slotDiv}> <div className={cn('slot', dropClass, { selected })} onClick={onOpen} onKeyDown={this._keyDown} onContextMenu={this._contextMenu} onDragOver={dragOver} tabIndex="0" ref={slotDiv => this.slotDiv = slotDiv}>
<div className='details-container'> <div className='details-container'>
<div className='sz'>{this._getMaxClassLabel(translate)}</div> <div className='sz'>{this._getMaxClassLabel(translate)}</div>
{slotDetails} {slotDetails}
</div> </div>
{menu} {menu}
</div> </div>
); );
} }
/** /**
* Toggle the modifications flag when selecting the modifications icon * Toggle the modifications flag when selecting the modifications icon
*/ */
_toggleModifications() { _toggleModifications() {
this._modificationsSelected = !this._modificationsSelected; this._modificationsSelected = !this._modificationsSelected;
} }
} }

View File

@@ -1,161 +1,161 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import cn from 'classnames'; import cn from 'classnames';
import Persist from '../stores/Persist'; import Persist from '../stores/Persist';
import TranslatedComponent from './TranslatedComponent'; import TranslatedComponent from './TranslatedComponent';
import { diffDetails } from '../utils/SlotFunctions'; import { diffDetails } from '../utils/SlotFunctions';
import AvailableModulesMenu from './AvailableModulesMenu'; import AvailableModulesMenu from './AvailableModulesMenu';
import ModificationsMenu from './ModificationsMenu'; import ModificationsMenu from './ModificationsMenu';
import * as ModuleUtils from '../shipyard/ModuleUtils'; import * as ModuleUtils from '../shipyard/ModuleUtils';
import { ListModifications, Modified } from './SvgIcons'; import { ListModifications, Modified } from './SvgIcons';
import { Modifications } from 'coriolis-data/dist'; import { Modifications } from 'coriolis-data/dist';
import { stopCtxPropagation } from '../utils/UtilityFunctions'; import { stopCtxPropagation } from '../utils/UtilityFunctions';
import { blueprintTooltip } from '../utils/BlueprintFunctions'; import { blueprintTooltip } from '../utils/BlueprintFunctions';
/** /**
* Standard Slot * Standard Slot
*/ */
export default class StandardSlot extends TranslatedComponent { export default class StandardSlot extends TranslatedComponent {
static propTypes = { static propTypes = {
slot: PropTypes.object, slot: PropTypes.object,
modules: PropTypes.array.isRequired, modules: PropTypes.array.isRequired,
onSelect: PropTypes.func.isRequired, onSelect: PropTypes.func.isRequired,
onOpen: PropTypes.func.isRequired, onOpen: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired, onChange: PropTypes.func.isRequired,
ship: PropTypes.object.isRequired, ship: PropTypes.object.isRequired,
selected: PropTypes.bool, selected: PropTypes.bool,
warning: PropTypes.func, warning: PropTypes.func,
}; };
/** /**
* Construct the slot * Construct the slot
* @param {object} props Object properties * @param {object} props Object properties
*/ */
constructor(props) { constructor(props) {
super(props); super(props);
this._modificationsSelected = false; this._modificationsSelected = false;
this._keyDown = this._keyDown.bind(this); this._keyDown = this._keyDown.bind(this);
this.modButton = null; this.modButton = null;
this.slotDiv = null; this.slotDiv = null;
} }
/** /**
* Fired on key down * Fired on key down
* @param {KeyboardEvent} event The keydown event * @param {KeyboardEvent} event The keydown event
* @private * @private
*/ */
_keyDown(event) { _keyDown(event) {
if (event.key == 'Enter') { if (event.key == 'Enter') {
if(event.target.className == 'r') { if(event.target.className == 'r') {
this._toggleModifications(); this._toggleModifications();
} }
this.props.onOpen(event); this.props.onOpen(event);
} }
} }
/** /**
* Render the slot * Render the slot
* @return {React.Component} Slot component * @return {React.Component} Slot component
*/ */
render() { render() {
let { termtip, tooltip } = this.context; let { termtip, tooltip } = this.context;
let { translate, formats, units } = this.context.language; let { translate, formats, units } = this.context.language;
let { modules, slot, selected, warning, onSelect, onChange, ship } = this.props; let { modules, slot, selected, warning, onSelect, onChange, ship } = this.props;
let m = slot.m; let m = slot.m;
let classRating = m.class + m.rating; let classRating = m.class + m.rating;
let menu; let menu;
let validMods = m == null || !Modifications.modules[m.grp] ? [] : (Modifications.modules[m.grp].modifications || []); let validMods = m == null || !Modifications.modules[m.grp] ? [] : (Modifications.modules[m.grp].modifications || []);
if (m && m.name && m.name === 'Guardian Hybrid Power Plant') { if (m && m.name && m.name === 'Guardian Hybrid Power Plant') {
validMods = []; validMods = [];
} }
let showModuleResistances = Persist.showModuleResistances(); let showModuleResistances = Persist.showModuleResistances();
let mass = m.getMass() || m.cargo || m.fuel || 0; let mass = m.getMass() || m.cargo || m.fuel || 0;
// Modifications tooltip shows blueprint and grade, if available // Modifications tooltip shows blueprint and grade, if available
let modTT = translate('modified'); let modTT = translate('modified');
if (m && m.blueprint && m.blueprint.name) { if (m && m.blueprint && m.blueprint.name) {
modTT = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade; modTT = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
if (m.blueprint.special && m.blueprint.special.id >= 0) { if (m.blueprint.special && m.blueprint.special.id >= 0) {
modTT += ', ' + translate(m.blueprint.special.name); modTT += ', ' + translate(m.blueprint.special.name);
} }
modTT = ( modTT = (
<div> <div>
<div>{modTT}</div> <div>{modTT}</div>
{blueprintTooltip(translate, m.blueprint.grades[m.blueprint.grade], null, m.grp, m)} {blueprintTooltip(translate, m.blueprint.grades[m.blueprint.grade], null, m.grp, m)}
</div> </div>
); );
} }
if (!selected) { if (!selected) {
// If not selected then sure that modifications flag is unset // If not selected then sure that modifications flag is unset
this._modificationsSelected = false; this._modificationsSelected = false;
} }
const modificationsMarker = JSON.stringify(m); const modificationsMarker = JSON.stringify(m);
if (selected) { if (selected) {
if (this._modificationsSelected) { if (this._modificationsSelected) {
menu = <ModificationsMenu menu = <ModificationsMenu
className='standard' className='standard'
onChange={onChange} onChange={onChange}
ship={ship} ship={ship}
m={m} m={m}
marker={modificationsMarker} marker={modificationsMarker}
modButton = {this.modButton} modButton = {this.modButton}
/>; />;
} else { } else {
menu = <AvailableModulesMenu menu = <AvailableModulesMenu
className='standard' className='standard'
modules={modules} modules={modules}
shipMass={ModuleUtils.isShieldGenerator(m.grp) ? ship.hullMass : ship.unladenMass} shipMass={ModuleUtils.isShieldGenerator(m.grp) ? ship.hullMass : ship.unladenMass}
m={m} m={m}
onSelect={onSelect} onSelect={onSelect}
warning={warning} warning={warning}
diffDetails={diffDetails.bind(ship, this.context.language)} diffDetails={diffDetails.bind(ship, this.context.language)}
slotDiv = {this.slotDiv} slotDiv = {this.slotDiv}
/>; />;
} }
} }
return ( return (
<div className={cn('slot', { selected: this.props.selected })} onClick={this.props.onOpen} onKeyDown={this._keyDown} onContextMenu={stopCtxPropagation} tabIndex="0" ref={ slotDiv => this.slotDiv = slotDiv }> <div className={cn('slot', { selected: this.props.selected })} onClick={this.props.onOpen} onKeyDown={this._keyDown} onContextMenu={stopCtxPropagation} tabIndex="0" ref={ slotDiv => this.slotDiv = slotDiv }>
<div className={cn('details-container', { warning: warning && warning(slot.m), disabled: m.grp !== 'bh' && !slot.enabled })}> <div className={cn('details-container', { warning: warning && warning(slot.m), disabled: m.grp !== 'bh' && !slot.enabled })}>
<div className={'sz'}>{m.grp == 'bh' ? m.name.charAt(0) : slot.maxClass}</div> <div className={'sz'}>{m.grp == 'bh' ? m.name.charAt(0) : slot.maxClass}</div>
<div> <div>
<div className={'l'}>{classRating} {translate(m.name || m.grp)}{m.mods && Object.keys(m.mods).length > 0 ? <span className='r' onMouseOver={termtip.bind(null, modTT)} onMouseOut={tooltip.bind(null, null)}><Modified /></span> : null }</div> <div className={'l'}>{classRating} {translate(m.name || m.grp)}{m.mods && Object.keys(m.mods).length > 0 ? <span className='r' onMouseOver={termtip.bind(null, modTT)} onMouseOut={tooltip.bind(null, null)}><Modified /></span> : null }</div>
<div className={'r'}>{formats.round(mass)}{units.T}</div> <div className={'r'}>{formats.round(mass)}{units.T}</div>
<div/> <div/>
<div className={'cb'}> <div className={'cb'}>
{ m.getMinMass() ? <div className='l'>{translate('minimum mass')}: {formats.int(m.getMinMass())}{units.T}</div> : null } { m.getMinMass() ? <div className='l'>{translate('minimum mass')}: {formats.int(m.getMinMass())}{units.T}</div> : null }
{ m.getOptMass() ? <div className='l'>{translate('optimal mass')}: {formats.int(m.getOptMass())}{units.T}</div> : null } { m.getOptMass() ? <div className='l'>{translate('optimal mass')}: {formats.int(m.getOptMass())}{units.T}</div> : null }
{ m.getMaxMass() ? <div className='l'>{translate('max mass')}: {formats.int(m.getMaxMass())}{units.T}</div> : null } { m.getMaxMass() ? <div className='l'>{translate('max mass')}: {formats.int(m.getMaxMass())}{units.T}</div> : null }
{ m.getOptMul() ? <div className='l'>{translate('optimal multiplier')}: {formats.rPct(m.getOptMul())}</div> : null } { m.getOptMul() ? <div className='l'>{translate('optimal multiplier')}: {formats.rPct(m.getOptMul())}</div> : null }
{ m.getRange() ? <div className='l'>{translate('range', m.grp)}: {formats.f2(m.getRange())}{units.km}</div> : null } { m.getRange() ? <div className='l'>{translate('range', m.grp)}: {formats.f2(m.getRange())}{units.km}</div> : null }
{ m.time ? <div className='l'>{translate('time')}: {formats.time(m.time)}</div> : null } { m.time ? <div className='l'>{translate('time')}: {formats.time(m.time)}</div> : null }
{ m.getThermalEfficiency() ? <div className='l'>{translate('efficiency')}: {formats.f2(m.getThermalEfficiency())}</div> : null } { m.getThermalEfficiency() ? <div className='l'>{translate('efficiency')}: {formats.f2(m.getThermalEfficiency())}</div> : null }
{ m.getPowerGeneration() > 0 ? <div className='l'>{translate('pgen')}: {formats.f1(m.getPowerGeneration())}{units.MW}</div> : null } { m.getPowerGeneration() > 0 ? <div className='l'>{translate('pgen')}: {formats.f1(m.getPowerGeneration())}{units.MW}</div> : null }
{ m.getMaxFuelPerJump() ? <div className='l'>{translate('max')} {translate('fuel')}: {formats.f1(m.getMaxFuelPerJump())}{units.T}</div> : null } { m.getMaxFuelPerJump() ? <div className='l'>{translate('max')} {translate('fuel')}: {formats.f1(m.getMaxFuelPerJump())}{units.T}</div> : null }
{ m.getWeaponsCapacity() ? <div className='l'>{translate('WEP')}: {formats.f1(m.getWeaponsCapacity())}{units.MJ} / {formats.f1(m.getWeaponsRechargeRate())}{units.MW}</div> : null } { m.getWeaponsCapacity() ? <div className='l'>{translate('WEP')}: {formats.f1(m.getWeaponsCapacity())}{units.MJ} / {formats.f1(m.getWeaponsRechargeRate())}{units.MW}</div> : null }
{ m.getSystemsCapacity() ? <div className='l'>{translate('SYS')}: {formats.f1(m.getSystemsCapacity())}{units.MJ} / {formats.f1(m.getSystemsRechargeRate())}{units.MW}</div> : null } { m.getSystemsCapacity() ? <div className='l'>{translate('SYS')}: {formats.f1(m.getSystemsCapacity())}{units.MJ} / {formats.f1(m.getSystemsRechargeRate())}{units.MW}</div> : null }
{ m.getEnginesCapacity() ? <div className='l'>{translate('ENG')}: {formats.f1(m.getEnginesCapacity())}{units.MJ} / {formats.f1(m.getEnginesRechargeRate())}{units.MW}</div> : null } { m.getEnginesCapacity() ? <div className='l'>{translate('ENG')}: {formats.f1(m.getEnginesCapacity())}{units.MJ} / {formats.f1(m.getEnginesRechargeRate())}{units.MW}</div> : null }
{ showModuleResistances && m.getExplosiveResistance() ? <div className='l'>{translate('explres')}: {formats.pct(m.getExplosiveResistance())}</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.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.getThermalResistance() ? <div className='l'>{translate('thermres')}: {formats.pct(m.getThermalResistance())}</div> : null }
{ m.getIntegrity() ? <div className='l'>{translate('integrity')}: {formats.int(m.getIntegrity())}</div> : null } { m.getIntegrity() ? <div className='l'>{translate('integrity')}: {formats.int(m.getIntegrity())}</div> : null }
{ validMods.length > 0 ? <div className='r' tabIndex="0" ref={ modButton => this.modButton = modButton }><button tabIndex="-1" onClick={this._toggleModifications.bind(this)} onContextMenu={stopCtxPropagation} onMouseOver={termtip.bind(null, 'modifications')} onMouseOut={tooltip.bind(null, null)}><ListModifications /></button></div> : null } { validMods.length > 0 ? <div className='r' tabIndex="0" ref={ modButton => this.modButton = modButton }><button tabIndex="-1" onClick={this._toggleModifications.bind(this)} onContextMenu={stopCtxPropagation} onMouseOver={termtip.bind(null, 'modifications')} onMouseOut={tooltip.bind(null, null)}><ListModifications /></button></div> : null }
</div> </div>
</div> </div>
</div> </div>
{menu} {menu}
</div> </div>
); );
} }
/** /**
* Toggle the modifications flag when selecting the modifications icon * Toggle the modifications flag when selecting the modifications icon
*/ */
_toggleModifications() { _toggleModifications() {
this._modificationsSelected = !this._modificationsSelected; this._modificationsSelected = !this._modificationsSelected;
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -396,4 +396,4 @@ export default class ShipyardPage extends Page {
</div> </div>
); );
} }
} }

View File

@@ -1,430 +1,430 @@
import React from 'react'; import React from 'react';
import { Modifications } from 'coriolis-data/dist'; import { Modifications } from 'coriolis-data/dist';
/** /**
* Generate a tooltip with details of a blueprint's specials * Generate a tooltip with details of a blueprint's specials
* @param {Object} translate The translate object * @param {Object} translate The translate object
* @param {Object} blueprint The blueprint at the required grade * @param {Object} blueprint The blueprint at the required grade
* @param {string} grp The group of the module * @param {string} grp The group of the module
* @param {Object} m The module to compare with * @param {Object} m The module to compare with
* @param {string} specialName The name of the special * @param {string} specialName The name of the special
* @returns {Object} The react components * @returns {Object} The react components
*/ */
export function specialToolTip(translate, blueprint, grp, m, specialName) { export function specialToolTip(translate, blueprint, grp, m, specialName) {
const effects = []; const effects = [];
if (!blueprint || !blueprint.features) { if (!blueprint || !blueprint.features) {
return undefined; return undefined;
} }
if (m) { if (m) {
// We also add in any benefits from specials that aren't covered above // We also add in any benefits from specials that aren't covered above
if (m.blueprint) { if (m.blueprint) {
for (const feature in Modifications.modifierActions[specialName]) { for (const feature in Modifications.modifierActions[specialName]) {
// if (!blueprint.features[feature] && !m.mods.feature) { // if (!blueprint.features[feature] && !m.mods.feature) {
const featureDef = Modifications.modifications[feature]; const featureDef = Modifications.modifications[feature];
if (featureDef && !featureDef.hidden) { if (featureDef && !featureDef.hidden) {
let symbol = ''; let symbol = '';
if (feature === 'jitter') { if (feature === 'jitter') {
symbol = '°'; symbol = '°';
} else if (featureDef.type === 'percentage') { } else if (featureDef.type === 'percentage') {
symbol = '%'; symbol = '%';
} }
let current = m.getModValue(feature) - m.getModValue(feature, true); let current = m.getModValue(feature) - m.getModValue(feature, true);
if (featureDef.type === 'percentage') { if (featureDef.type === 'percentage') {
current = Math.round(current / 10) / 10; current = Math.round(current / 10) / 10;
} else if (featureDef.type === 'numeric') { } else if (featureDef.type === 'numeric') {
current /= 100; current /= 100;
} }
const currentIsBeneficial = isValueBeneficial(feature, current); const currentIsBeneficial = isValueBeneficial(feature, current);
effects.push( effects.push(
<tr key={feature + '_specialTT'}> <tr key={feature + '_specialTT'}>
<td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td> <td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td>
<td>&nbsp;</td> <td>&nbsp;</td>
<td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'} <td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'}
style={{ textAlign: 'right' }}>{current}{symbol}</td> style={{ textAlign: 'right' }}>{current}{symbol}</td>
<td>&nbsp;</td> <td>&nbsp;</td>
</tr> </tr>
); );
} }
} }
} }
} }
return ( return (
<div> <div>
<table width='100%'> <table width='100%'>
<tbody> <tbody>
{effects} {effects}
</tbody> </tbody>
</table> </table>
</div> </div>
); );
} }
/** /**
* Generate a tooltip with details of a blueprint's effects * Generate a tooltip with details of a blueprint's effects
* @param {Object} translate The translate object * @param {Object} translate The translate object
* @param {Object} blueprint The blueprint at the required grade * @param {Object} blueprint The blueprint at the required grade
* @param {Array} engineers The engineers supplying this blueprint * @param {Array} engineers The engineers supplying this blueprint
* @param {string} grp The group of the module * @param {string} grp The group of the module
* @param {Object} m The module to compare with * @param {Object} m The module to compare with
* @returns {Object} The react components * @returns {Object} The react components
*/ */
export function blueprintTooltip(translate, blueprint, engineers, grp, m) { export function blueprintTooltip(translate, blueprint, engineers, grp, m) {
const effects = []; const effects = [];
if (!blueprint || !blueprint.features) { if (!blueprint || !blueprint.features) {
return undefined; return undefined;
} }
for (const feature in blueprint.features) { for (const feature in blueprint.features) {
const featureIsBeneficial = isBeneficial(feature, blueprint.features[feature]); const featureIsBeneficial = isBeneficial(feature, blueprint.features[feature]);
const featureDef = Modifications.modifications[feature]; const featureDef = Modifications.modifications[feature];
if (!featureDef.hidden) { if (!featureDef.hidden) {
let symbol = ''; let symbol = '';
if (feature === 'jitter') { if (feature === 'jitter') {
symbol = '°'; symbol = '°';
} else if (featureDef.type === 'percentage') { } else if (featureDef.type === 'percentage') {
symbol = '%'; symbol = '%';
} }
let lowerBound = blueprint.features[feature][0]; let lowerBound = blueprint.features[feature][0];
let upperBound = blueprint.features[feature][1]; let upperBound = blueprint.features[feature][1];
if (featureDef.type === 'percentage') { if (featureDef.type === 'percentage') {
lowerBound = Math.round(lowerBound * 1000) / 10; lowerBound = Math.round(lowerBound * 1000) / 10;
upperBound = Math.round(upperBound * 1000) / 10; upperBound = Math.round(upperBound * 1000) / 10;
} }
const lowerIsBeneficial = isValueBeneficial(feature, lowerBound); const lowerIsBeneficial = isValueBeneficial(feature, lowerBound);
const upperIsBeneficial = isValueBeneficial(feature, upperBound); const upperIsBeneficial = isValueBeneficial(feature, upperBound);
if (m) { if (m) {
// We have a module - add in the current value // We have a module - add in the current value
let current = m.getModValue(feature); let current = m.getModValue(feature);
if (featureDef.type === 'percentage' || featureDef.name === 'burst' || featureDef.name === 'burstrof') { if (featureDef.type === 'percentage' || featureDef.name === 'burst' || featureDef.name === 'burstrof') {
current = Math.round(current / 10) / 10; current = Math.round(current / 10) / 10;
} else if (featureDef.type === 'numeric') { } else if (featureDef.type === 'numeric') {
current /= 100; current /= 100;
} }
const currentIsBeneficial = isValueBeneficial(feature, current); const currentIsBeneficial = isValueBeneficial(feature, current);
effects.push( effects.push(
<tr key={feature}> <tr key={feature}>
<td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td> <td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td>
<td className={lowerBound === 0 ? '' : lowerIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{lowerBound}{symbol}</td> <td className={lowerBound === 0 ? '' : lowerIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{lowerBound}{symbol}</td>
<td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{current}{symbol}</td> <td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{current}{symbol}</td>
<td className={upperBound === 0 ? '' : upperIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{upperBound}{symbol}</td> <td className={upperBound === 0 ? '' : upperIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{upperBound}{symbol}</td>
</tr> </tr>
); );
} else { } else {
// We do not have a module, no value // We do not have a module, no value
effects.push( effects.push(
<tr key={feature}> <tr key={feature}>
<td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td> <td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td>
<td className={lowerBound === 0 ? '' : lowerIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{lowerBound}{symbol}</td> <td className={lowerBound === 0 ? '' : lowerIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{lowerBound}{symbol}</td>
<td className={upperBound === 0 ? '' : upperIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{upperBound}{symbol}</td> <td className={upperBound === 0 ? '' : upperIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{upperBound}{symbol}</td>
</tr> </tr>
); );
} }
} }
} }
if (m) { if (m) {
// Because we have a module add in any benefits that aren't part of the primary blueprint // Because we have a module add in any benefits that aren't part of the primary blueprint
for (const feature in m.mods) { for (const feature in m.mods) {
if (!blueprint.features[feature]) { if (!blueprint.features[feature]) {
const featureDef = Modifications.modifications[feature]; const featureDef = Modifications.modifications[feature];
if (featureDef && !featureDef.hidden) { if (featureDef && !featureDef.hidden) {
let symbol = ''; let symbol = '';
if (feature === 'jitter') { if (feature === 'jitter') {
symbol = '°'; symbol = '°';
} else if (featureDef.type === 'percentage') { } else if (featureDef.type === 'percentage') {
symbol = '%'; symbol = '%';
} }
let current = m.getModValue(feature); let current = m.getModValue(feature);
if (featureDef.type === 'percentage' || featureDef.name === 'burst' || featureDef.name === 'burstrof') { if (featureDef.type === 'percentage' || featureDef.name === 'burst' || featureDef.name === 'burstrof') {
current = Math.round(current / 10) / 10; current = Math.round(current / 10) / 10;
} else if (featureDef.type === 'numeric') { } else if (featureDef.type === 'numeric') {
current /= 100; current /= 100;
} }
const currentIsBeneficial = isValueBeneficial(feature, current); const currentIsBeneficial = isValueBeneficial(feature, current);
effects.push( effects.push(
<tr key={feature}> <tr key={feature}>
<td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td> <td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td>
<td>&nbsp;</td> <td>&nbsp;</td>
<td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{current}{symbol}</td> <td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{current}{symbol}</td>
<td>&nbsp;</td> <td>&nbsp;</td>
</tr> </tr>
); );
} }
} }
} }
// We also add in any benefits from specials that aren't covered above // We also add in any benefits from specials that aren't covered above
if (m.blueprint && m.blueprint.special) { if (m.blueprint && m.blueprint.special) {
for (const feature in Modifications.modifierActions[m.blueprint.special.edname]) { for (const feature in Modifications.modifierActions[m.blueprint.special.edname]) {
if (!blueprint.features[feature] && !m.mods.feature) { if (!blueprint.features[feature] && !m.mods.feature) {
const featureDef = Modifications.modifications[feature]; const featureDef = Modifications.modifications[feature];
if (featureDef && !featureDef.hidden) { if (featureDef && !featureDef.hidden) {
let symbol = ''; let symbol = '';
if (feature === 'jitter') { if (feature === 'jitter') {
symbol = '°'; symbol = '°';
} else if (featureDef.type === 'percentage') { } else if (featureDef.type === 'percentage') {
symbol = '%'; symbol = '%';
} }
let current = m.getModValue(feature); let current = m.getModValue(feature);
if (featureDef.type === 'percentage' || featureDef.name === 'burst' || featureDef.name === 'burstrof') { if (featureDef.type === 'percentage' || featureDef.name === 'burst' || featureDef.name === 'burstrof') {
current = Math.round(current / 10) / 10; current = Math.round(current / 10) / 10;
} else if (featureDef.type === 'numeric') { } else if (featureDef.type === 'numeric') {
current /= 100; current /= 100;
} }
const currentIsBeneficial = isValueBeneficial(feature, current); const currentIsBeneficial = isValueBeneficial(feature, current);
effects.push( effects.push(
<tr key={feature}> <tr key={feature}>
<td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td> <td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td>
<td>&nbsp;</td> <td>&nbsp;</td>
<td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{current}{symbol}</td> <td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{current}{symbol}</td>
<td>&nbsp;</td> <td>&nbsp;</td>
</tr> </tr>
); );
} }
} }
} }
} }
} }
let components; let components;
if (!m) { if (!m) {
components = []; components = [];
for (const component in blueprint.components) { for (const component in blueprint.components) {
components.push( components.push(
<tr key={component}> <tr key={component}>
<td style={{ textAlign: 'left' }}>{translate(component)}</td> <td style={{ textAlign: 'left' }}>{translate(component)}</td>
<td style={{ textAlign: 'right' }}>{blueprint.components[component]}</td> <td style={{ textAlign: 'right' }}>{blueprint.components[component]}</td>
</tr> </tr>
); );
} }
} }
let engineersList; let engineersList;
if (engineers) { if (engineers) {
engineersList = []; engineersList = [];
for (const engineer of engineers) { for (const engineer of engineers) {
engineersList.push( engineersList.push(
<tr key={engineer}> <tr key={engineer}>
<td style={{ textAlign: 'left' }}>{engineer}</td> <td style={{ textAlign: 'left' }}>{engineer}</td>
</tr> </tr>
); );
} }
} }
return ( return (
<div> <div>
<table width='100%'> <table width='100%'>
<thead> <thead>
<tr> <tr>
<td>{translate('feature')}</td> <td>{translate('feature')}</td>
<td>{translate('worst')}</td> <td>{translate('worst')}</td>
{m ? <td>{translate('current')}</td> : null } {m ? <td>{translate('current')}</td> : null }
<td>{translate('best')}</td> <td>{translate('best')}</td>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{effects} {effects}
</tbody> </tbody>
</table> </table>
{ components ? <table width='100%'> { components ? <table width='100%'>
<thead> <thead>
<tr> <tr>
<td>{translate('component')}</td> <td>{translate('component')}</td>
<td>{translate('amount')}</td> <td>{translate('amount')}</td>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{components} {components}
</tbody> </tbody>
</table> : null } </table> : null }
{ engineersList ? <table width='100%'> { engineersList ? <table width='100%'>
<thead> <thead>
<tr> <tr>
<td>{translate('engineers')}</td> <td>{translate('engineers')}</td>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{engineersList} {engineersList}
</tbody> </tbody>
</table> : null } </table> : null }
</div> </div>
); );
} }
/** /**
* Is this blueprint feature beneficial? * Is this blueprint feature beneficial?
* @param {string} feature The name of the feature * @param {string} feature The name of the feature
* @param {array} values The value of the feature * @param {array} values The value of the feature
* @returns {boolean} True if this feature is beneficial * @returns {boolean} True if this feature is beneficial
*/ */
export function isBeneficial(feature, values) { export function isBeneficial(feature, values) {
const fact = (values[0] < 0 || (values[0] === 0 && values[1] < 0)); const fact = (values[0] < 0 || (values[0] === 0 && values[1] < 0));
if (Modifications.modifications[feature].higherbetter) { if (Modifications.modifications[feature].higherbetter) {
return !fact; return !fact;
} else { } else {
return fact; return fact;
} }
} }
/** /**
* Is this feature value beneficial? * Is this feature value beneficial?
* @param {string} feature The name of the feature * @param {string} feature The name of the feature
* @param {number} value The value of the feature * @param {number} value The value of the feature
* @returns {boolean} True if this value is beneficial * @returns {boolean} True if this value is beneficial
*/ */
export function isValueBeneficial(feature, value) { export function isValueBeneficial(feature, value) {
if (Modifications.modifications[feature].higherbetter) { if (Modifications.modifications[feature].higherbetter) {
return value > 0; return value > 0;
} else { } else {
return value < 0; return value < 0;
} }
} }
/** /**
* Get a blueprint with a given name and an optional module * Get a blueprint with a given name and an optional module
* @param {string} name The name of the blueprint * @param {string} name The name of the blueprint
* @param {Object} module The module for which to obtain this blueprint * @param {Object} module The module for which to obtain this blueprint
* @returns {Object} The matching blueprint * @returns {Object} The matching blueprint
*/ */
export function getBlueprint(name, module) { export function getBlueprint(name, module) {
// Start with a copy of the blueprint // 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)]; const found = Modifications.blueprints[findMod(name)];
if (!found || !found.fdname) { if (!found || !found.fdname) {
return {}; return {};
} }
const blueprint = JSON.parse(JSON.stringify(found)); const blueprint = JSON.parse(JSON.stringify(found));
if (module) { if (module) {
if (module.grp === 'sb') { if (module.grp === 'sb') {
// Shield boosters are treated internally as straight modifiers, so rather than (for example) // Shield boosters are treated internally as straight modifiers, so rather than (for example)
// being a 4% boost they are a 104% multiplier. We need to fix the values here so that they look // being a 4% boost they are a 104% multiplier. We need to fix the values here so that they look
// accurate as per the information in Elite // accurate as per the information in Elite
for (const grade in blueprint.grades) { for (const grade in blueprint.grades) {
for (const feature in blueprint.grades[grade].features) { for (const feature in blueprint.grades[grade].features) {
if (feature === 'shieldboost') { if (feature === 'shieldboost') {
blueprint.grades[grade].features[feature][0] = ((1 + blueprint.grades[grade].features[feature][0]) * (1 + module.shieldboost) - 1) / module.shieldboost - 1; blueprint.grades[grade].features[feature][0] = ((1 + blueprint.grades[grade].features[feature][0]) * (1 + module.shieldboost) - 1) / module.shieldboost - 1;
blueprint.grades[grade].features[feature][1] = ((1 + blueprint.grades[grade].features[feature][1]) * (1 + module.shieldboost) - 1) / module.shieldboost - 1; blueprint.grades[grade].features[feature][1] = ((1 + blueprint.grades[grade].features[feature][1]) * (1 + module.shieldboost) - 1) / module.shieldboost - 1;
} }
} }
} }
} }
} }
return blueprint; return blueprint;
} }
/** /**
* Provide 'percent' primary modifications * Provide 'percent' primary modifications
* @param {Object} ship The ship for which to perform the modifications * @param {Object} ship The ship for which to perform the modifications
* @param {Object} m The module for which to perform the modifications * @param {Object} m The module for which to perform the modifications
* @param {Number} percent The percent to set values to of full. * @param {Number} percent The percent to set values to of full.
*/ */
export function setPercent(ship, m, percent) { export function setPercent(ship, m, percent) {
ship.clearModifications(m); ship.clearModifications(m);
// Pick given value as multiplier // Pick given value as multiplier
const mult = percent / 100; const mult = percent / 100;
const features = m.blueprint.grades[m.blueprint.grade].features; const features = m.blueprint.grades[m.blueprint.grade].features;
for (const featureName in features) { for (const featureName in features) {
let value; let value;
if (Modifications.modifications[featureName].higherbetter) { if (Modifications.modifications[featureName].higherbetter) {
// Higher is better, but is this making it better or worse? // Higher is better, but is this making it better or worse?
if (features[featureName][0] < 0 || (features[featureName][0] === 0 && features[featureName][1] < 0)) { if (features[featureName][0] < 0 || (features[featureName][0] === 0 && features[featureName][1] < 0)) {
value = features[featureName][1] + ((features[featureName][0] - features[featureName][1]) * mult); value = features[featureName][1] + ((features[featureName][0] - features[featureName][1]) * mult);
} else { } else {
value = features[featureName][0] + ((features[featureName][1] - features[featureName][0]) * mult); value = features[featureName][0] + ((features[featureName][1] - features[featureName][0]) * mult);
} }
} else { } else {
// Higher is worse, but is this making it better or worse? // Higher is worse, but is this making it better or worse?
if (features[featureName][0] < 0 || (features[featureName][0] === 0 && features[featureName][1] < 0)) { if (features[featureName][0] < 0 || (features[featureName][0] === 0 && features[featureName][1] < 0)) {
value = features[featureName][0] + ((features[featureName][1] - features[featureName][0]) * mult); value = features[featureName][0] + ((features[featureName][1] - features[featureName][0]) * mult);
} else { } else {
value = features[featureName][1] + ((features[featureName][0] - features[featureName][1]) * mult); value = features[featureName][1] + ((features[featureName][0] - features[featureName][1]) * mult);
} }
} }
_setValue(ship, m, featureName, value); _setValue(ship, m, featureName, value);
} }
} }
/** /**
* Provide 'random' primary modifications * Provide 'random' primary modifications
* @param {Object} ship The ship for which to perform the modifications * @param {Object} ship The ship for which to perform the modifications
* @param {Object} m The module for which to perform the modifications * @param {Object} m The module for which to perform the modifications
*/ */
export function setRandom(ship, m) { export function setRandom(ship, m) {
// Pick a single value for our randomness // Pick a single value for our randomness
setPercent(ship, m, Math.random() * 100); setPercent(ship, m, Math.random() * 100);
} }
/** /**
* Set a modification feature value * Set a modification feature value
* @param {Object} ship The ship for which to perform the modifications * @param {Object} ship The ship for which to perform the modifications
* @param {Object} m The module for which to perform the modifications * @param {Object} m The module for which to perform the modifications
* @param {string} featureName The feature being set * @param {string} featureName The feature being set
* @param {number} value The value being set for the feature * @param {number} value The value being set for the feature
*/ */
function _setValue(ship, m, featureName, value) { function _setValue(ship, m, featureName, value) {
if (Modifications.modifications[featureName].type == 'percentage') { if (Modifications.modifications[featureName].type == 'percentage') {
ship.setModification(m, featureName, value * 10000); ship.setModification(m, featureName, value * 10000);
} else if (Modifications.modifications[featureName].type == 'numeric') { } else if (Modifications.modifications[featureName].type == 'numeric') {
ship.setModification(m, featureName, value * 100); ship.setModification(m, featureName, value * 100);
} else { } else {
ship.setModification(m, featureName, value); ship.setModification(m, featureName, value);
} }
} }
/** /**
* Provide 'percent' primary query * Provide 'percent' primary query
* @param {Object} m The module for which to perform the query * @param {Object} m The module for which to perform the query
* @returns {Number} percent The percentage indicator of current applied values. * @returns {Number} percent The percentage indicator of current applied values.
*/ */
export function getPercent(m) { export function getPercent(m) {
let result = null; let result = null;
const features = m.blueprint.grades[m.blueprint.grade].features; const features = m.blueprint.grades[m.blueprint.grade].features;
for (const featureName in features) { for (const featureName in features) {
if (features[featureName][0] === features[featureName][1]) { if (features[featureName][0] === features[featureName][1]) {
continue; continue;
} }
let value = _getValue(m, featureName); let value = _getValue(m, featureName);
let mult; let mult;
if (Modifications.modifications[featureName].higherbetter) { if (Modifications.modifications[featureName].higherbetter) {
// Higher is better, but is this making it better or worse? // Higher is better, but is this making it better or worse?
if (features[featureName][0] < 0 || (features[featureName][0] === 0 && features[featureName][1] < 0)) { 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 { } 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 { } else {
// Higher is worse, but is this making it better or worse? // Higher is worse, but is this making it better or worse?
if (features[featureName][0] < 0 || (features[featureName][0] === 0 && features[featureName][1] < 0)) { 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 { } 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) { if (result && result != mult) {
return null; return null;
} else if (result != mult) { } else if (result != mult) {
result = mult; result = mult;
} }
} }
return result; return result;
} }
/** /**
* Query a feature value * Query a feature value
* @param {Object} m The module for which to perform the query * @param {Object} m The module for which to perform the query
* @param {string} featureName The feature being queried * @param {string} featureName The feature being queried
* @returns {number} The value of the modification as a % * @returns {number} The value of the modification as a %
*/ */
function _getValue(m, featureName) { function _getValue(m, featureName) {
if (Modifications.modifications[featureName].type == 'percentage') { if (Modifications.modifications[featureName].type == 'percentage') {
return m.getModValue(featureName, true) / 10000; return m.getModValue(featureName, true) / 10000;
} else if (Modifications.modifications[featureName].type == 'numeric') { } else if (Modifications.modifications[featureName].type == 'numeric') {
return m.getModValue(featureName, true) / 100; return m.getModValue(featureName, true) / 100;
} else { } else {
return m.getModValue(featureName, true); return m.getModValue(featureName, true);
} }
} }