diff --git a/.travis.yml b/.travis.yml index 89bc3d76..49fe7b3e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,8 @@ cache: directories: - node_modules -before_script: +before_install: + - git clone https://github.com/EDCD/coriolis-data.git ../coriolis-data script: - npm run lint diff --git a/src/app/components/AvailableModulesMenu.jsx b/src/app/components/AvailableModulesMenu.jsx index 2906a736..a31960cd 100644 --- a/src/app/components/AvailableModulesMenu.jsx +++ b/src/app/components/AvailableModulesMenu.jsx @@ -1,504 +1,504 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import * as ModuleUtils from '../shipyard/ModuleUtils'; -import TranslatedComponent from './TranslatedComponent'; -import { stopCtxPropagation } from '../utils/UtilityFunctions'; -import cn from 'classnames'; -import { MountFixed, MountGimballed, MountTurret } from './SvgIcons'; - -const PRESS_THRESHOLD = 500; // mouse/touch down threshold - -/* - * Categorisation of module groups - */ -const GRPCAT = { - 'sg': 'shields', - 'bsg': 'shields', - 'psg': 'shields', - 'scb': 'shields', - 'cc': 'limpet controllers', - 'fx': 'limpet controllers', - 'hb': 'limpet controllers', - 'pc': 'limpet controllers', - 'rpl': 'limpet controllers', - 'pce': 'passenger cabins', - 'pci': 'passenger cabins', - 'pcm': 'passenger cabins', - 'pcq': 'passenger cabins', - 'fh': 'hangars', - 'pv': 'hangars', - 'fs': 'fuel', - 'ft': 'fuel', - 'hr': 'structural reinforcement', - 'mrp': 'structural reinforcement', - 'bl': 'lasers', - 'pl': 'lasers', - 'ul': 'lasers', - 'ml': 'lasers', - 'c': 'projectiles', - 'mc': 'projectiles', - 'axmc': 'experimental', - 'fc': 'projectiles', - 'rfl': 'experimental', - 'pa': 'projectiles', - 'rg': 'projectiles', - 'mr': 'ordnance', - 'axmr': 'experimental', - 'rcpl': 'experimental', - 'tp': 'ordnance', - 'nl': 'ordnance', - 'sc': 'scanners', - 'ss': 'scanners', - // Utilities - 'cs': 'scanners', - 'kw': 'scanners', - 'ws': 'scanners', - 'xs': 'scanners', - 'ch': 'defence', - 'po': 'defence', - 'ec': 'defence', - 'sfn': 'defence', - // Standard - 'gpp': 'guardian', - 'gpc': 'guardian', - 'ggc': 'guardian' -}; -// Order here is the order in which items will be shown in the modules menu -const CATEGORIES = { - // Internals - 'am': ['am'], - 'cr': ['cr'], - 'fi': ['fi'], - 'fuel': ['ft', 'fs'], - 'hangars': ['fh', 'pv'], - 'limpet controllers': ['cc', 'fx', 'hb', 'pc', 'rpl'], - 'passenger cabins': ['pce', 'pci', 'pcm', 'pcq'], - 'rf': ['rf'], - 'shields': ['sg', 'bsg', 'psg', 'scb'], - 'structural reinforcement': ['hr', 'mrp'], - 'dc': ['dc'], - // Hardpoints - 'lasers': ['pl', 'ul', 'bl', 'ml'], - 'projectiles': ['mc', 'c', 'fc', 'pa', 'rg'], - 'ordnance': ['mr', 'tp', 'nl'], - // Utilities - 'sb': ['sb'], - 'hs': ['hs'], - 'defence': ['ch', 'po', 'ec'], - 'scanners': ['sc', 'ss', 'cs', 'kw', 'ws'], // Overloaded with internal scanners - // Experimental - 'experimental': ['axmc', 'axmr', 'rfl', 'xs', 'sfn', 'rcpl'], - - // Guardian - 'guardian': ['gpp', 'gpc', 'ggc'] -}; - -/** - * Available modules menu - */ -export default class AvailableModulesMenu extends TranslatedComponent { - - static propTypes = { - modules: PropTypes.oneOfType([PropTypes.object, PropTypes.array]).isRequired, - onSelect: PropTypes.func.isRequired, - diffDetails: PropTypes.func, - m: PropTypes.object, - shipMass: PropTypes.number, - warning: PropTypes.func, - firstSlotId: PropTypes.string, - lastSlotId: PropTypes.string, - activeSlotId: PropTypes.string, - slotDiv: PropTypes.object - }; - - static defaultProps = { - shipMass: 0 - }; - - /** - * Constructor - * @param {Object} props React Component properties - * @param {Object} context React Component context - */ - constructor(props, context) { - super(props); - this._hideDiff = this._hideDiff.bind(this); - this.state = this._initState(props, context); - this.slotItems = [];// Array to hold
  • refs. - } - - /** - * Initiate the list of available moduels - * @param {Object} props React Component properties - * @param {Object} context React Component context - * @return {Object} list: Array of React Components, currentGroup Component if any - */ - _initState(props, context) { - let translate = context.language.translate; - let { m, warning, shipMass, onSelect, modules, firstSlotId, lastSlotId } = props; - let list, currentGroup; - - let buildGroup = this._buildGroup.bind( - this, - translate, - m, - warning, - shipMass - (m && m.mass ? m.mass : 0), - (m, event) => { - this._hideDiff(event); - onSelect(m); - } - ); - - if (modules instanceof Array) { - list = buildGroup(modules[0].grp, modules); - } else { - list = []; - // At present time slots with grouped options (Hardpoints and Internal) can be empty - if (m) { - let emptyId = 'empty'; - if(this.firstSlotId == null) this.firstSlotId = emptyId; - let keyDown = this._keyDown.bind(this, onSelect); - list.push(
    this.slotItems[emptyId] = slotItem} >{translate('empty')}
    ); - } - - // Need to regroup the modules by our own categorisation - let catmodules = {}; - // Pre-create to preserve ordering - for (let cat in CATEGORIES) { - catmodules[cat] = []; - } - for (let g in modules) { - const moduleCategory = GRPCAT[g] || g; - const existing = catmodules[moduleCategory] || []; - catmodules[moduleCategory] = existing.concat(modules[g]); - } - - for (let category in catmodules) { - let categoryHeader = false; - // Order through CATEGORIES if present - const categories = CATEGORIES[category] || [category]; - if (categories && categories.length) { - for (let n in categories) { - const grp = categories[n]; - // We now have the group and the category. We might not have any modules, though... - if (modules[grp]) { - // Decide if we need a category header as well as a group header - if (categories.length === 1) { - // Show category header instead of group header - if (m && grp == m.grp) { - list.push(
    this.groupElem = elem} key={category} className={'select-category upp'}>{translate(category)}
    ); - } else { - list.push(
    {translate(category)}
    ); - } - } else { - // Show category header as well as group header - if (!categoryHeader) { - list.push(
    {translate(category)}
    ); - categoryHeader = true; - } - if (m && grp == m.grp) { - list.push(
    this.groupElem = elem} key={grp} className={'select-group cap'}>{translate(grp)}
    ); - } else { - list.push(
    {translate(grp)}
    ); - } - } - list.push(buildGroup(grp, modules[grp])); - } - } - } - } - } - let trackingFocus = false; - return { list, currentGroup, trackingFocus }; - } - - /** - * Generate React Components for Module Group - * @param {Function} translate Translate function - * @param {Object} mountedModule Mounted Module - * @param {Function} warningFunc Warning function - * @param {number} mass Mass - * @param {function} onSelect Select/Mount callback - * @param {string} grp Group name - * @param {Array} modules Available modules - * @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])); - - let itemsOnThisRow = 0; - for (let i = 0; i < sortedModules.length; i++) { - let m = sortedModules[i]; - let mount = null; - let disabled = false; - prevName = m.name; - if (ModuleUtils.isShieldGenerator(m.grp)) { - // Shield generators care about maximum hull mass - disabled = mass > m.maxmass; - } else if (m.maxmass) { - // Thrusters care about total mass - disabled = mass + m.mass > m.maxmass; - } - let active = mountedModule && mountedModule.id === m.id; - let classes = cn(m.name ? 'lc' : 'c', { - warning: !disabled && warningFunc && warningFunc(m), - active, - disabled - }); - let eventHandlers; - - if (disabled) { - eventHandlers = { - onKeyDown: this._keyDown.bind(this, null), - onKeyUp: this._keyUp.bind(this, null) - - }; - } else { - /** - * Get the ids of the first and last
  • elements in the