From be8934da809c3c36b78a2eef1f0e6e5fafdf6d1e Mon Sep 17 00:00:00 2001 From: felixlinker Date: Sun, 23 Sep 2018 22:30:05 +0100 Subject: [PATCH] Added multi crew pips --- src/app/components/Pips.jsx | 305 +++++++++++++++---------------- src/app/i18n/en.json | 1 + src/app/pages/OutfittingPage.jsx | 45 +++-- src/less/pips.less | 6 + 4 files changed, 185 insertions(+), 172 deletions(-) diff --git a/src/app/components/Pips.jsx b/src/app/components/Pips.jsx index 287295f2..8a63731b 100644 --- a/src/app/components/Pips.jsx +++ b/src/app/components/Pips.jsx @@ -1,13 +1,8 @@ import React from 'react'; import PropTypes from 'prop-types'; import TranslatedComponent from './TranslatedComponent'; -import { Ships } from 'coriolis-data/dist'; -import { nameComparator } from '../utils/SlotFunctions'; import { Pip } from './SvgIcons'; -import LineChart from '../components/LineChart'; -import Slider from '../components/Slider'; -import * as ModuleUtils from '../shipyard/ModuleUtils'; -import Module from '../shipyard/Module'; +import autoBind from 'auto-bind'; /** * Pips displays SYS/ENG/WEP pips and allows users to change them with key presses by clicking on the relevant area. @@ -18,6 +13,9 @@ export default class Pips extends TranslatedComponent { sys: PropTypes.number.isRequired, eng: PropTypes.number.isRequired, wep: PropTypes.number.isRequired, + mcSys: PropTypes.number.isRequired, + mcEng: PropTypes.number.isRequired, + mcWep: PropTypes.number.isRequired, onChange: PropTypes.func.isRequired }; @@ -28,9 +26,7 @@ export default class Pips extends TranslatedComponent { */ constructor(props, context) { super(props); - const { sys, eng, wep } = props; - - this._keyDown = this._keyDown.bind(this); + autoBind(this); } /** @@ -74,30 +70,21 @@ export default class Pips extends TranslatedComponent { } } - /** - * Handle a click - * @param {string} which Which item was clicked - */ - onClick(which) { - if (which == 'SYS') { - this._incSys(); - } else if (which == 'ENG') { - this._incEng(); - } else if (which == 'WEP') { - this._incWep(); - } else if (which == 'RST') { - this._reset(); - } - } - /** * Reset the capacitor */ - _reset() { - let { sys, eng, wep } = this.props; - if (sys != 2 || eng != 2 || wep != 2) { + _reset(isMc) { + let { sys, eng, wep, mcSys, mcEng, mcWep } = this.props; + if (isMc) { + if (mcSys || mcEng || mcWep) { + sys -= mcSys; + eng -= mcEng; + wep -= mcWep; + this.props.onChange(sys, eng, wep, 0, 0, 0); + } + } else if (sys != 2 || eng != 2 || wep != 2) { sys = eng = wep = 2; - this.props.onChange(sys, eng, wep); + this.props.onChange(sys + mcSys, eng + mcEng, wep + mcWep, mcSys, mcEng, mcWep); } } @@ -105,151 +92,133 @@ export default class Pips extends TranslatedComponent { * Increment the SYS capacitor */ _incSys() { - let { sys, eng, wep } = this.props; - - const required = Math.min(1, 4 - sys); - if (required > 0) { - if (required == 0.5) { - // Take from whichever is larger - if (eng > wep) { - eng -= 0.5; - sys += 0.5; - } else { - wep -= 0.5; - sys += 0.5; - } - } else { - // Required is 1 - take from both if possible - if (eng == 0) { - wep -= 1; - sys += 1; - } else if (wep == 0) { - eng -= 1; - sys += 1; - } else { - eng -= 0.5; - wep -= 0.5; - sys += 1; - } - } - this.props.onChange(sys, eng, wep); - } + this._inc('sys', false); } /** * Increment the ENG capacitor */ _incEng() { - let { sys, eng, wep } = this.props; - - const required = Math.min(1, 4 - eng); - if (required > 0) { - if (required == 0.5) { - // Take from whichever is larger - if (sys > wep) { - sys -= 0.5; - eng += 0.5; - } else { - wep -= 0.5; - eng += 0.5; - } - } else { - // Required is 1 - take from both if possible - if (sys == 0) { - wep -= 1; - eng += 1; - } else if (wep == 0) { - sys -= 1; - eng += 1; - } else { - sys -= 0.5; - wep -= 0.5; - eng += 1; - } - } - this.props.onChange(sys, eng, wep); - } + this._inc('eng', false); } /** * Increment the WEP capacitor */ _incWep() { - let { sys, eng, wep } = this.props; + this._inc('wep', false); + } - const required = Math.min(1, 4 - wep); - if (required > 0) { - if (required == 0.5) { - // Take from whichever is larger - if (sys > eng) { - sys -= 0.5; - wep += 0.5; - } else { - eng -= 0.5; - wep += 0.5; - } + _wrapMcClick(key) { + return (event) => { + event.stopPropagation(); + event.preventDefault(); + if (key == 'rst') { + this._reset(true); } else { - // Required is 1 - take from both if possible - if (sys == 0) { - eng -= 1; - wep += 1; - } else if (eng == 0) { - sys -= 1; - wep += 1; + this._inc(key, true); + } + }; + } + + /** + * Increases a given capacitor + * @param {String} key Pip name to increase (one of 'sys', 'eng', 'wep') + * @param {Boolean} isMc True when increase is by multi crew + */ + _inc(key, isMc) { + if (!['sys', 'eng', 'wep'].includes(key)) { + return; + } + + let { sys, eng, wep, mcSys, mcEng, mcWep } = this.props; + let mc = key == 'sys' ? mcSys : (key == 'eng' ? mcEng : mcWep); + let pips = this.props[key] - mc; + let other1 = key == 'sys' ? eng - mcEng : sys - mcSys; + let other2 = key == 'wep' ? eng - mcEng : wep - mcWep; + + const required = Math.min(1, 4 - mc - pips); + if (isMc) { + // We can only set full pips in multi-crew also we can only set two pips + if (required > 0.5 && mcSys + mcEng + mcWep < 2) { + if (key == 'sys') { + mcSys += 1; + } else if (key == 'eng') { + mcEng += 1; } else { - sys -= 0.5; - eng -= 0.5; - wep += 1; + mcWep += 1; } } - this.props.onChange(sys, eng, wep); + } else if (required > 0) { + if (required == 0.5) { + // Take from whichever is larger + if (other1 > other2) { + other1 -= 0.5; + } else { + other2 -= 0.5; + } + pips += 0.5; + } else { + // Required is 1 - take from both if possible + if (other1 == 0) { + other2 -= 1; + } else if (other2 == 0) { + other1 -= 1; + } else { + other1 -= 0.5; + other2 -= 0.5; + } + pips += 1; + } } + + sys = mcSys + (key == 'sys' ? pips : other1); + eng = mcEng + (key == 'eng' ? pips : (key == 'sys' ? other1 : other2)); + wep = mcWep + (key == 'wep' ? pips : other2); + this.props.onChange(sys, eng, wep, mcSys, mcEng, mcWep); } /** * Set up the rendering for pips - * @param {int} sys the SYS pips - * @param {int} eng the ENG pips - * @param {int} wep the WEP pips + * @param {Number} sys the SYS pips + * @param {Number} eng the ENG pips + * @param {Number} wep the WEP pips + * @param {Number} mcSys SYS pips from multi-crew + * @param {Number} mcEng ENG pips from multi-crew + * @param {Number} mcWep WEP pips from multi-crew * @returns {Object} Object containing the rendering for the pips */ - _renderPips(sys, eng, wep) { - const pipsSvg = {}; + _renderPips(sys, eng, wep, mcSys, mcEng, mcWep) { + const pipsSvg = { + SYS: [], + ENG: [], + WEP: [], + }; - // SYS - pipsSvg['SYS'] = []; - for (let i = 0; i < Math.floor(sys); i++) { - pipsSvg['SYS'].push(); - } - if (sys > Math.floor(sys)) { - pipsSvg['SYS'].push(); - } - for (let i = Math.floor(sys + 0.5); i < 4; i++) { - pipsSvg['SYS'].push(); - } + // Multi-crew pipsSettings actually are included in the overall pip count therefore + // we can consider [0, sys - mcSys] as normal pipsSettings whilst [sys - mcSys, sys] + // are the multi-crew pipsSettings in what follows. - // ENG - pipsSvg['ENG'] = []; - for (let i = 0; i < Math.floor(eng); i++) { - pipsSvg['ENG'].push(); - } - if (eng > Math.floor(eng)) { - pipsSvg['ENG'].push(); - } - for (let i = Math.floor(eng + 0.5); i < 4; i++) { - pipsSvg['ENG'].push(); - } + let pipsSettings = { + SYS: [sys, mcSys], + ENG: [eng, mcEng], + WEP: [wep, mcWep], + }; - // WEP - pipsSvg['WEP'] = []; - for (let i = 0; i < Math.floor(wep); i++) { - pipsSvg['WEP'].push(); - } - if (wep > Math.floor(wep)) { - pipsSvg['WEP'].push(); - } - for (let i = Math.floor(wep + 0.5); i < 4; i++) { - pipsSvg['WEP'].push(); + for (let pipName in pipsSettings) { + let [pips, mcPips] = pipsSettings[pipName]; + for (let i = 0; i < Math.floor(pips - mcPips); i++) { + pipsSvg[pipName].push(); + } + if (pips > Math.floor(pips)) { + pipsSvg[pipName].push(); + } + for (let i = pips - mcPips; i < Math.floor(pips); i++) { + pipsSvg[pipName].push(); + } + for (let i = Math.floor(pips + 0.5); i < 4; i++) { + pipsSvg[pipName].push(); + } } return pipsSvg; @@ -260,15 +229,11 @@ export default class Pips extends TranslatedComponent { * @return {React.Component} contents */ render() { + const { tooltip, termtip } = this.context; const { formats, translate, units } = this.context.language; - const { sys, eng, wep } = this.props; + const { sys, eng, wep, mcSys, mcEng, mcWep } = this.props; - const onSysClicked = this.onClick.bind(this, 'SYS'); - const onEngClicked = this.onClick.bind(this, 'ENG'); - const onWepClicked = this.onClick.bind(this, 'WEP'); - const onRstClicked = this.onClick.bind(this, 'RST'); - - const pipsSvg = this._renderPips(sys, eng, wep); + const pipsSvg = this._renderPips(sys, eng, wep, mcSys, mcEng, mcWep); return ( @@ -276,20 +241,38 @@ export default class Pips extends TranslatedComponent { - + - - - + + + - - - + + + + + + + + +
   {pipsSvg['ENG']} this._inc('eng')} + onContextMenu={this._wrapMcClick('eng')}>{pipsSvg['ENG']}  
 {pipsSvg['SYS']}{translate('ENG')}{pipsSvg['WEP']}{pipsSvg['SYS']}{translate('ENG')}{pipsSvg['WEP']}
 {translate('SYS')}{translate('RST')}{translate('WEP')}{translate('SYS')} + {translate('RST')} + {translate('WEP')}
   + {translate('RST')} +  
diff --git a/src/app/i18n/en.json b/src/app/i18n/en.json index d142c819..1cc1a777 100644 --- a/src/app/i18n/en.json +++ b/src/app/i18n/en.json @@ -43,6 +43,7 @@ "PHRASE_SHIELD_DAMAGE": "Breakdown of sources for sustained DPS against shields", "PHRASE_ARMOUR_DAMAGE": "Breakdown of sources for sustained DPS against armour", "PHRASE_TIME_TO_REMOVE_SHIELDS": "Will remove shields in", + "PHRASE_MULTI_CREW_CAPACITOR_POINTS": "Right click a capacitor to assign multi-crew capacitor points.", "TT_TIME_TO_REMOVE_SHIELDS": "With sustained fire by all weapons", "PHRASE_TIME_TO_REMOVE_ARMOUR": "Will remove armour in", "TT_TIME_TO_REMOVE_ARMOUR": "With sustained fire by all weapons", diff --git a/src/app/pages/OutfittingPage.jsx b/src/app/pages/OutfittingPage.jsx index 7fc02bc5..d292449b 100644 --- a/src/app/pages/OutfittingPage.jsx +++ b/src/app/pages/OutfittingPage.jsx @@ -100,7 +100,7 @@ export default class OutfittingPage extends Page { this._getTitle = getTitle.bind(this, data.properties.name); // Obtain ship control from code - const { sys, eng, wep, boost, fuel, cargo, opponent, opponentBuild, opponentSys, opponentEng, opponentWep, engagementRange } = this._obtainControlFromCode(ship, code); + const { sys, eng, wep, mcSys, mcEng, mcWep, boost, fuel, cargo, opponent, opponentBuild, opponentSys, opponentEng, opponentWep, engagementRange } = this._obtainControlFromCode(ship, code); return { error: null, title: this._getTitle(buildName), @@ -114,6 +114,9 @@ export default class OutfittingPage extends Page { sys, eng, wep, + mcSys, + mcEng, + mcWep, boost, fuel, cargo, @@ -176,6 +179,9 @@ export default class OutfittingPage extends Page { let sys = 2; let eng = 2; let wep = 2; + let mcSys = 0; + let mcEng = 0; + let mcWep = 0; let boost = false; let fuel = ship.fuelCapacity; let cargo = ship.cargoCapacity; @@ -222,20 +228,31 @@ export default class OutfittingPage extends Page { } } engagementRange = parseInt(control[8]); + + // Multi-crew pips were introduced later on so assign default values + // because those values might not be present. + mcSys = parseInt(control[9]) || mcSys; + mcEng = parseInt(control[10]) || mcEng; + mcWep = parseInt(control[11]) || mcWep; } } - return { sys, eng, wep, boost, fuel, cargo, opponent, opponentBuild, opponentSys, opponentEng, opponentWep, engagementRange }; + return { sys, eng, wep, mcSys, mcEng, mcWep, boost, fuel, cargo, opponent, opponentBuild, opponentSys, opponentEng, opponentWep, engagementRange }; } /** - * Triggered when pips have been updated + * Triggered when pips have been updated. Multi-crew pips are already included + * in sys, eng and wep but mcSys, mcEng and mcWep make clear where each pip + * comes from. * @param {number} sys SYS pips * @param {number} eng ENG pips * @param {number} wep WEP pips + * @param {number} mcSys SYS pips from multi-crew + * @param {number} mcEng ENG pips from multi-crew + * @param {number} mcWep WEP pips from multi-crew */ - _pipsUpdated(sys, eng, wep) { - this.setState({ sys, eng, wep }, () => this._updateRouteOnControlChange()); + _pipsUpdated(sys, eng, wep, mcSys, mcEng, mcWep) { + this.setState({ sys, eng, wep, mcSys, mcEng, mcWep }, () => this._updateRouteOnControlChange()); } /** @@ -309,8 +326,8 @@ export default class OutfittingPage extends Page { * @returns {string} The control code */ _controlCode(fuel, cargo) { - const { sys, eng, wep, boost, opponent, opponentBuild, engagementRange } = this.state; - const code = `${sys}/${eng}/${wep}/${boost ? 1 : 0}/${fuel || this.state.fuel}/${cargo || this.state.cargo}/${opponent.id}/${opponentBuild ? opponentBuild : ''}/${engagementRange}`; + const { sys, eng, wep, mcSys, mcEng, mcWep, boost, opponent, opponentBuild, engagementRange } = this.state; + const code = `${sys}/${eng}/${wep}/${boost ? 1 : 0}/${fuel || this.state.fuel}/${cargo || this.state.cargo}/${opponent.id}/${opponentBuild ? opponentBuild : ''}/${engagementRange}/${mcSys}/${mcEng}/${mcWep}`; return code; } @@ -373,12 +390,15 @@ export default class OutfittingPage extends Page { ship.buildWith(Ships[shipId].defaults); // Reset controls const code = ship.toString(); - const { sys, eng, wep, boost, fuel, cargo, opponent, opponentBuild, engagementRange } = this._obtainControlFromCode(ship, code); + const { sys, eng, wep, mcSys, mcEng, mcWep, boost, fuel, cargo, opponent, opponentBuild, engagementRange } = this._obtainControlFromCode(ship, code); // Update state, and refresh the ship this.setState({ sys, eng, wep, + mcSys, + mcEng, + mcWep, boost, fuel, cargo, @@ -430,12 +450,15 @@ export default class OutfittingPage extends Page { this.state.ship.buildFrom(code); // Obtain controls from the code - const { sys, eng, wep, boost, fuel, cargo, opponent, opponentBuild, engagementRange } = this._obtainControlFromCode(ship, code); + const { sys, eng, wep, mcSys, mcEng, mcWep, boost, fuel, cargo, opponent, opponentBuild, engagementRange } = this._obtainControlFromCode(ship, code); // Update state, and refresh the route when complete this.setState({ sys, eng, wep, + mcSys, + mcEng, + mcWep, boost, fuel, cargo, @@ -567,7 +590,7 @@ export default class OutfittingPage extends Page { let state = this.state, { language, termtip, tooltip, sizeRatio, onWindowResize } = this.context, { translate, units, formats } = language, - { ship, code, savedCode, buildName, newBuildName, sys, eng, wep, boost, fuel, cargo, opponent, opponentBuild, opponentSys, opponentEng, opponentWep, engagementRange } = state, + { ship, code, savedCode, buildName, newBuildName, sys, eng, wep, mcSys, mcEng, mcWep, boost, fuel, cargo, opponent, opponentBuild, opponentSys, opponentEng, opponentWep, engagementRange } = state, hide = tooltip.bind(null, null), menu = this.props.currentMenu, shipUpdated = this._shipUpdated, @@ -671,7 +694,7 @@ export default class OutfittingPage extends Page {
- +
diff --git a/src/less/pips.less b/src/less/pips.less index 2d3dccbd..1e7b5ace 100755 --- a/src/less/pips.less +++ b/src/less/pips.less @@ -12,6 +12,12 @@ cursor: pointer; } + // A multi-crew pip + .mc { + stroke: @secondary; + fill: @secondary; + } + // A full pip .full { stroke: @primary;