diff --git a/ChangeLog.md b/ChangeLog.md
index 517c85d1..bc86add2 100644
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,3 +1,15 @@
+#2.2.10
+ * Fix detailed export of module reinforcement packages
+ * Use damagedist for exact breakdown of weapons that have more than one type of damage
+ * Use new-style modification validity data
+ * Provide ability to select engineering blueprint and roll sample values for them
+ * Use coriolis-data 2.2.10:
+ * Fix incorrect base shield values for Cutter and Corvette
+ * Update weapons to have %-based damage distributions
+ * Remove power draw for detailed surface scanner - although shown in outfitting it is not part of active power
+ * Fix incorrect names for lightweight and kinetic armour
+ * Add engineering blueprints
+
#2.2.9
* Use SSL-enabled server for shortlinks
* Add falloff for weapons
diff --git a/__tests__/fixtures/anaconda-test-detailed-export-v4.json b/__tests__/fixtures/anaconda-test-detailed-export-v4.json
index 12327c14..402c67f2 100644
--- a/__tests__/fixtures/anaconda-test-detailed-export-v4.json
+++ b/__tests__/fixtures/anaconda-test-detailed-export-v4.json
@@ -269,20 +269,20 @@
"topSpeed": 187.01,
"totalCost": 882362058,
"totalDpe": 142.68,
- "totalDps": 101.13,
+ "totalDps": 103.8,
"totalEps": 22.71,
"totalHps": 677.29,
"totalExplDpe": 0,
"totalExplDps": 0,
"totalExplSDps": 0,
"totalHps": 33.62,
- "totalKinDpe": 116.29,
- "totalKinDps": 16.01,
- "totalKinSDps": 12.09,
- "totalSDps": 89.99,
- "totalThermDpe": 20.44,
- "totalThermDps": 53.82,
- "totalThermSDps": 53.82,
+ "totalKinDpe": 117.48,
+ "totalKinDps": 24.94,
+ "totalKinSDps": 18.76,
+ "totalSDps": 91.84,
+ "totalThermDpe": 21.63,
+ "totalThermDps": 60.08,
+ "totalThermSDps": 58.64,
"baseShieldStrength": 350,
"baseArmour": 945,
"hullExplRes": 0.22,
diff --git a/__tests__/test-import.js b/__tests__/test-import.js
index a6a44dc6..42d6fe06 100644
--- a/__tests__/test-import.js
+++ b/__tests__/test-import.js
@@ -238,7 +238,7 @@ describe('Import Modal', function() {
expect(modal.state.singleBuild).toBe(true);
clickProceed();
expect(MockRouter.go.mock.calls.length).toBe(1);
- expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/federal_corvette?code=A2putsFklndzsxf50x0x7l28281919040404040402020l06p05sf63c5ifr--v66g2f.AwRj4zNaqA%3D%3D.CwRgDBldUExuBiIlUA%3D%3D.H4sIAAAAAAAAA02SO0sDQRSFbxJ389jgJOsaN%2FGVmPXVKKRQC8FSA9oJWihWWgV%2FgIWFYBNb8RcIWiiIYiF2NqksIqaRoD%2FBQghB41zPFVa3OdzZ890zM3snpBeI6DsEyZgGkbpg5tg2lhzWGbEikN6aSVS0HSL3Ogxo6IvZmdbM9hFsjuickGgn%2B8SGv%2FvJ7DpxIqeCHjb0vJ80GrWIxu5RFmqARnYQEj%2FrMCdesFQzSOeYXvPP1BmGZPeiREa9xWyW0WifwnFX0MMJve4Hd5IQo4I9TcclGrxCUmoO34qVDaK%2BJuiJfD6%2FytZ%2Fj%2FGQAL7fD%2Fyiy8fbcNQdjsXJAFn9DRbyQchZIS9RqZJcGpckt4xjsdKL%2FtndJYjVQJkW8URUVYJTAegLat0IJOKJqCeB0gHoQ6BHgUQ8EdUNZvgghj3tAPmKa1vPQhIqTyp1KHE9AWgKV7Ka8NMinoiywLCrV%2F5Gvolo61ySpMn7xMdwsc3cf4w48w2Is40fwFld9oPzLbyL6CT88QaWoZpMcyDg32Jo0br4EaxDJXk8BT3o%2B7ktGS9B3GU8puS7zJh%2FAHGMT2qjAgAA&bn=Imported%20Federal%20Corvette');
+ expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/federal_corvette?code=A2putsFklndzsxf50x0x7l28281919040404040402020l06p05sf63c5ifr--v66g2f.AwRj4zNaqA%3D%3D.CwRgDBldUExuBiIlUA%3D%3D.H4sIAAAAAAAAA02Svy9DURTHT1vvtfoat32eekVV9fm1kBgwSIw0YWYgBqmpMZkMBomFVfwFEoZKhBjE1qWTgegiDX%2BCQdKI1j2%2BR%2FJ4yzfnvu%2FnfO%2B979yQXiCi7xAkbRpEqsLMsRKWHNZpsSKQnppJVLAdIvc6DGiwxexMaWb7GDZHdJ%2BQaCf71Ia%2F88XsOp1EThk9bOh5P2kkahGN3qPM1wANbyOk87zNHH%2FBUs0gnWN61T9TOwfJ7EWJjMcms1lEo30Gx11BD8f1mh%2FcTkCMMvY0HZcoe4Wk5By%2BFcrrRL0N0OOlrd0Ntv57jGoc%2BH4%2F8EqHj3%2FCUXc4FicC5NFvsJBVIWeFvESlpuXSuCS5RRyLlV70z%2B4uQaw6ypSIJ6KOJDgZgFpQ60YgEU9EPQmUCkAfAj0IJOKJqC4wuYMY9rQD5CuubT0LSag8qdShxHUHoElcyWrAT4l4IsoCw65e%2BRv5BqKtC0mSJu8LH8OFT%2Bb%2BE8SZb0CcEn4AZ3TRDx5q4l1EJ%2BCP1bEM1WSaAwH%2FFkOLPoofwTo0LY8nr7O%2B37cp4yWIu4zHlHiXGfMPmat5gqMCAAA%3D&bn=Imported%20Federal%20Corvette');
});
it('imports a valid v4 build', function() {
diff --git a/package.json b/package.json
index 7738c704..880c6da2 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "coriolis_shipyard",
- "version": "2.2.9",
+ "version": "2.2.10",
"repository": {
"type": "git",
"url": "https://github.com/EDCD/coriolis"
diff --git a/src/app/components/DamageDealt.jsx b/src/app/components/DamageDealt.jsx
index eb27dc20..5d0f27b2 100644
--- a/src/app/components/DamageDealt.jsx
+++ b/src/app/components/DamageDealt.jsx
@@ -153,7 +153,7 @@ export default class DamageDealt extends TranslatedComponent {
}
totals.effectiveness = totals.effectiveDps / totalDps;
- return {weapons: weapons, totals: totals};
+ return { weapons, totals };
}
/**
diff --git a/src/app/components/DamageReceived.jsx b/src/app/components/DamageReceived.jsx
index 7268d5c4..3b254025 100644
--- a/src/app/components/DamageReceived.jsx
+++ b/src/app/components/DamageReceived.jsx
@@ -93,7 +93,7 @@ export default class DamageReceived extends TranslatedComponent {
let weapons = [];
for (let grp in Modules.hardpoints) {
- if (Modules.hardpoints[grp][0].damage && Modules.hardpoints[grp][0].type) {
+ if (Modules.hardpoints[grp][0].damage && Modules.hardpoints[grp][0].damagedist) {
for (let mId in Modules.hardpoints[grp]) {
const m = new Module(Modules.hardpoints[grp][mId]);
const classRating = `${m.class}${m.rating}${m.missile ? '/' + m.missile : ''}`;
@@ -104,37 +104,35 @@ export default class DamageReceived extends TranslatedComponent {
// Effective DPS taking in to account shield resistance
let effectivenessShields = 0;
- if (m.getDamageType().indexOf('E') != -1) {
- effectivenessShields += (1 - ship.shieldExplRes);
+ if (m.getDamageDist().E) {
+ effectivenessShields += m.getDamageDist().E * (1 - ship.shieldExplRes);
}
- if (m.getDamageType().indexOf('K') != -1) {
- effectivenessShields += (1 - ship.shieldKinRes);
+ if (m.getDamageDist().K) {
+ effectivenessShields += m.getDamageDist().K * (1 - ship.shieldKinRes);
}
- if (m.getDamageType().indexOf('T') != -1) {
- effectivenessShields += (1 - ship.shieldThermRes);
+ if (m.getDamageDist().T) {
+ effectivenessShields += m.getDamageDist().T * (1 - ship.shieldThermRes);
}
- if (m.getDamageType().indexOf('A') != -1) {
- effectivenessShields += 1;
+ if (m.getDamageDist().A) {
+ effectivenessShields += m.getDamageDist().A;
}
- effectivenessShields /= m.getDamageType().length;
const effectiveDpsShields = baseDps * effectivenessShields;
const effectiveSDpsShields = baseSDps * effectivenessShields;
// Effective DPS taking in to account hull hardness and resistance
let effectivenessHull = 0;
- if (m.getDamageType().indexOf('E') != -1) {
- effectivenessHull += (1 - ship.hullExplRes);
+ if (m.getDamageDist().E) {
+ effectivenessHull += m.getDamageDist().E * (1 - ship.hullExplRes);
}
- if (m.getDamageType().indexOf('K') != -1) {
- effectivenessHull += (1 - ship.hullKinRes);
+ if (m.getDamageDist().K) {
+ effectivenessHull += m.getDamageDist().K * (1 - ship.hullKinRes);
}
- if (m.getDamageType().indexOf('T') != -1) {
- effectivenessHull += (1 - ship.hullThermRes);
+ if (m.getDamageDist().T) {
+ effectivenessHull += m.getDamageDist().T * (1 - ship.hullThermRes);
}
- if (m.getDamageType().indexOf('A') != -1) {
- effectivenessHull += 1;
+ if (m.getDamageDist().A) {
+ effectivenessHull += m.getDamageDist().A;
}
- effectivenessHull /= m.getDamageType().length;
effectivenessHull *= Math.min(m.getPiercing() / ship.hardness, 1);
const effectiveDpsHull = baseDps * effectivenessHull;
const effectiveSDpsHull = baseSDps * effectivenessHull;
diff --git a/src/app/components/HardpointSlot.jsx b/src/app/components/HardpointSlot.jsx
index e4d75d6e..f6232568 100644
--- a/src/app/components/HardpointSlot.jsx
+++ b/src/app/components/HardpointSlot.jsx
@@ -41,7 +41,7 @@ export default class HardpointSlot extends Slot {
let classRating = `${m.class}${m.rating}${m.missile ? '/' + m.missile : ''}`;
let { drag, drop } = this.props;
let { termtip, tooltip } = this.context;
- let validMods = Modifications.validity[m.grp] || [];
+ let validMods = Modifications.modules[m.grp].modifications || [];
let showModuleResistances = Persist.showModuleResistances();
// Modifications tooltip shows blueprint and grade, if available
@@ -59,9 +59,9 @@ export default class HardpointSlot extends Slot {
{m.mount && m.mount == 'F' ? : ''}
{m.mount && m.mount == 'G' ? : ''}
{m.mount && m.mount == 'T' ? : ''}
- {m.getDamageType() && m.getDamageType().match('K') ? : ''}
- {m.getDamageType() && m.getDamageType().match('T') ? : ''}
- {m.getDamageType() && m.getDamageType().match('E') ? : ''}
+ {m.getDamageDist() && m.getDamageDist().K ? : ''}
+ {m.getDamageDist() && m.getDamageDist().T ? : ''}
+ {m.getDamageDist() && m.getDamageDist().E ? : ''}
{classRating} {translate(m.name || m.grp)}{ m.mods && Object.keys(m.mods).length > 0 ? : null }
@@ -77,6 +77,7 @@ export default class HardpointSlot extends Slot {
{ m.getFalloff() ?
{translate('falloff')} {formats.f1(m.getFalloff() / 1000)}{u.km}
: null }
{ m.getShieldBoost() ? +{formats.pct1(m.getShieldBoost())}
: null }
{ m.getAmmo() ? {translate('ammunition')}: {formats.int(m.getClip())}/{formats.int(m.getAmmo())}
: null }
+ { m.getShotSpeed() ? {translate('shotspeed')}: {formats.int(m.getShotSpeed())}{u.mps}
: null }
{ m.getPiercing() ? {translate('piercing')}: {formats.int(m.getPiercing())}
: null }
{ m.getJitter() ? {translate('jitter')}: {formats.f2(m.getJitter())}°
: null }
{ showModuleResistances && m.getExplosiveResistance() ? {translate('explres')}: {formats.pct(m.getExplosiveResistance())}
: null }
diff --git a/src/app/components/InternalSlot.jsx b/src/app/components/InternalSlot.jsx
index 1bb7bf4b..4ce53f83 100644
--- a/src/app/components/InternalSlot.jsx
+++ b/src/app/components/InternalSlot.jsx
@@ -23,7 +23,7 @@ export default class InternalSlot extends Slot {
let classRating = m.class + m.rating;
let { drag, drop, ship } = this.props;
let { termtip, tooltip } = this.context;
- let validMods = Modifications.validity[m.grp] || [];
+ let validMods = Modifications.modules[m.grp].modifications || [];
let showModuleResistances = Persist.showModuleResistances();
// Modifications tooltip shows blueprint and grade, if available
diff --git a/src/app/components/Modification.jsx b/src/app/components/Modification.jsx
index 3813681e..c191b96c 100644
--- a/src/app/components/Modification.jsx
+++ b/src/app/components/Modification.jsx
@@ -13,6 +13,7 @@ export default class Modification extends TranslatedComponent {
ship: React.PropTypes.object.isRequired,
m: React.PropTypes.object.isRequired,
name: React.PropTypes.string.isRequired,
+ value: React.PropTypes.number.isRequired,
onChange: React.PropTypes.func.isRequired
};
@@ -24,7 +25,7 @@ export default class Modification extends TranslatedComponent {
constructor(props, context) {
super(props);
this.state = {};
- this.state.value = this.props.m.getModValue(this.props.name) / 100 || 0;
+ this.state.value = props.value;
}
/**
@@ -61,10 +62,10 @@ export default class Modification extends TranslatedComponent {
*/
render() {
let translate = this.context.language.translate;
- let name = this.props.name;
+ let { m, name } = this.props;
- if (name === 'type') {
- // We don't show type
+ if (name === 'damagedist') {
+ // We don't show damage distribution
return null;
}
@@ -81,7 +82,7 @@ export default class Modification extends TranslatedComponent {
return (
{translate(name)}{symbol}
-
+
);
}
diff --git a/src/app/components/ModificationsMenu.jsx b/src/app/components/ModificationsMenu.jsx
index 1a56180f..76bf5218 100644
--- a/src/app/components/ModificationsMenu.jsx
+++ b/src/app/components/ModificationsMenu.jsx
@@ -1,7 +1,8 @@
import React from 'react';
+import * as _ from 'lodash';
import { findDOMNode } from 'react-dom';
import TranslatedComponent from './TranslatedComponent';
-import { stopCtxPropagation } from '../utils/UtilityFunctions';
+import { isEmpty, stopCtxPropagation } from '../utils/UtilityFunctions';
import cn from 'classnames';
import { MountFixed, MountGimballed, MountTurret } from './SvgIcons';
import { Modifications } from 'coriolis-data/dist';
@@ -26,25 +27,177 @@ export default class ModificationsMenu extends TranslatedComponent {
constructor(props, context) {
super(props);
this.state = this._initState(props, context);
+
+ this._toggleBlueprintsMenu = this._toggleBlueprintsMenu.bind(this);
+ this._rollWorst = this._rollWorst.bind(this);
+ this._rollRandom = this._rollRandom.bind(this);
+ this._rollBest = this._rollBest.bind(this);
+ this._reset = this._reset.bind(this);
}
/**
- * Initiate the list of modifications
+ * Initialise state
* @param {Object} props React Component properties
* @param {Object} context React Component context
* @return {Object} list: Array of React Components
*/
_initState(props, context) {
let { m, onChange, ship } = props;
- let list = [];
- for (let modName of Modifications.validity[m.grp]) {
- if (Modifications.modifications[modName].type != 'hidden') {
- list.push();
+ let blueprints = [];
+ for (const blueprintName in Modifications.modules[m.grp].blueprints) {
+ for (const grade of Modifications.modules[m.grp].blueprints[blueprintName]) {
+ const close = this._blueprintSelected.bind(this, Modifications.blueprints[blueprintName].id, grade);
+ const key = blueprintName + ':' + grade;
+ blueprints.push({Modifications.blueprints[blueprintName].name} grade {grade}
);
+ }
+ }
+
+ // Set up the modifications
+ const modifications = this._setModifications(props);
+
+ const blueprintMenuOpened = false;
+
+ // Set up the specials for this module
+ // const specials = _selectSpecials(m);
+
+ return { blueprintMenuOpened, blueprints, modifications };
+ }
+
+ /**
+ * Initialise the modifications
+ * @param {Object} props React Component properties
+ * @return {Object} list: Array of React Components
+ */
+ _setModifications(props) {
+ const { m, onChange, ship } = props;
+ let modifications = [];
+ for (const modName of Modifications.modules[m.grp].modifications) {
+ if (Modifications.modifications[modName].type === 'percentage' || Modifications.modifications[modName].type === 'numeric') {
+ modifications.push();
+ }
+ }
+ return modifications;
+ }
+
+ /**
+ * Toggle the blueprints menu
+ */
+ _toggleBlueprintsMenu() {
+ const blueprintMenuOpened = !this.state.blueprintMenuOpened;
+ this.setState({ blueprintMenuOpened });
+ }
+
+ /**
+ * Activated when a blueprint is selected
+ * @param {int} blueprintId The ID of the selected blueprint
+ * @param {int} grade The grade of the selected blueprint
+ */
+ _blueprintSelected(blueprintId, grade) {
+ const { m } = this.props;
+ const blueprint = Object.assign({}, _.find(Modifications.blueprints, function(o) { return o.id === blueprintId; }));
+ blueprint.grade = grade;
+ m.blueprint = blueprint;
+
+ const blueprintMenuOpened = false;
+ this.setState({ blueprintMenuOpened });
+ this.props.onChange();
+ }
+
+ /**
+ * Provide a 'worst' roll within the information we have
+ */
+ _rollWorst() {
+ const { m, ship } = this.props;
+ const features = m.blueprint.features[m.blueprint.grade];
+ for (const featureName in features) {
+ if (Modifications.modifications[featureName].method == 'overwrite') {
+ ship.setModification(m, featureName, features[featureName][1]);
+ } else {
+ let value = features[featureName][0];
+ if (m.grp == 'sb' && featureName == 'shieldboost') {
+ // Shield boosters are a special case. Their boost is dependent on their base so we need to calculate the value here
+ value = ((1 + m.shieldboost) * (1 + value) - 1) / m.shieldboost - 1;
+ }
+
+ if (Modifications.modifications[featureName].type == 'percentage') {
+ ship.setModification(m, featureName, value * 10000);
+ } else if (Modifications.modifications[featureName].type == 'numeric') {
+ ship.setModification(m, featureName, value * 100);
+ }
+ }
+ }
+
+ this.setState({ modifications: this._setModifications(this.props) });
+ this.props.onChange();
+ }
+
+ /**
+ * Provide a random roll within the information we have
+ */
+ _rollRandom() {
+ const { m, ship } = this.props;
+ const features = m.blueprint.features[m.blueprint.grade];
+ for (const featureName in features) {
+ if (Modifications.modifications[featureName].method == 'overwrite') {
+ ship.setModification(m, featureName, features[featureName][1]);
+ } else {
+ let value = features[featureName][0] + (Math.random() * (features[featureName][1] - features[featureName][0]));
+ if (m.grp == 'sb' && featureName == 'shieldboost') {
+ // Shield boosters are a special case. Their boost is dependent on their base so we need to calculate the value here
+ value = ((1 + m.shieldboost) * (1 + value) - 1) / m.shieldboost - 1;
+ }
+
+ if (Modifications.modifications[featureName].type == 'percentage') {
+ ship.setModification(m, featureName, value * 10000);
+ } else if (Modifications.modifications[featureName].type == 'numeric') {
+ ship.setModification(m, featureName, value * 100);
+ }
}
}
- return { list };
+ this.setState({ modifications: this._setModifications(this.props) });
+ this.props.onChange();
+ }
+
+ /**
+ * Provide a 'best' roll within the information we have
+ */
+ _rollBest() {
+ const { m, ship } = this.props;
+ const features = m.blueprint.features[m.blueprint.grade];
+ for (const featureName in features) {
+ if (Modifications.modifications[featureName].method == 'overwrite') {
+ ship.setModification(m, featureName, features[featureName][1]);
+ } else {
+ let value = features[featureName][1];
+ if (m.grp == 'sb' && featureName == 'shieldboost') {
+ // Shield boosters are a special case. Their boost is dependent on their base so we need to calculate the value here
+ value = ((1 + m.shieldboost) * (1 + value) - 1) / m.shieldboost - 1;
+ }
+
+ if (Modifications.modifications[featureName].type == 'percentage') {
+ ship.setModification(m, featureName, value * 10000);
+ } else if (Modifications.modifications[featureName].type == 'numeric') {
+ ship.setModification(m, featureName, value * 100);
+ }
+ }
+ }
+
+ this.setState({ modifications: this._setModifications(this.props) });
+ this.props.onChange();
+ }
+
+ /**
+ * Reset modification information
+ */
+ _reset() {
+ const { m, ship } = this.props;
+ ship.clearModifications(m);
+ ship.clearBlueprint(m);
+
+ this.setState({ modifications: this._setModifications(this.props) });
+ this.props.onChange();
}
/**
@@ -52,15 +205,51 @@ export default class ModificationsMenu extends TranslatedComponent {
* @return {React.Component} List
*/
render() {
- let { tooltip, termtip } = this.context;
+ const language = this.context.language;
+ const translate = language.translate;
+ const { tooltip, termtip } = this.context;
+ const { m } = this.props;
+ const { blueprintMenuOpened } = this.state;
+
+ const _toggleBlueprintsMenu = this._toggleBlueprintsMenu;
+ const _rollBest = this._rollBest;
+ const _rollWorst = this._rollWorst;
+ const _rollRandom = this._rollRandom;
+ const _reset = this._reset;
+
+ let blueprintLabel;
+ let haveBlueprint = false;
+ if (m.blueprint && !isEmpty(m.blueprint)) {
+ blueprintLabel = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
+ haveBlueprint = true;
+ } else {
+ blueprintLabel = translate('PHRASE_SELECT_BLUEPRINT');
+ }
+
return (
e.stopPropagation() }
onContextMenu={stopCtxPropagation}
- onMouseOver={termtip.bind(null, 'HELP_MODIFICATIONS_MENU')} onMouseOut={tooltip.bind(null, null)}
>
- {this.state.list}
+
{blueprintLabel}
+ { blueprintMenuOpened ? this.state.blueprints : '' }
+ { haveBlueprint ?
+
+
+
+ { translate('roll') }: |
+ { translate('worst') } |
+ { translate('random') } |
+ { translate('best') } |
+ { translate('reset') } |
+
+
+
: '' }
+ { blueprintMenuOpened ? '' :
+
+ { this.state.modifications }
+ }
);
}
diff --git a/src/app/components/StandardSlot.jsx b/src/app/components/StandardSlot.jsx
index ebbd5485..d032fc00 100644
--- a/src/app/components/StandardSlot.jsx
+++ b/src/app/components/StandardSlot.jsx
@@ -46,7 +46,7 @@ export default class StandardSlot extends TranslatedComponent {
let m = slot.m;
let classRating = m.class + m.rating;
let menu;
- let validMods = m == null ? [] : (Modifications.validity[m.grp] || []);
+ let validMods = m == null ? [] : (Modifications.modules[m.grp].modifications || []);
let showModuleResistances = Persist.showModuleResistances();
let mass = m.getMass() || m.cargo || m.fuel || 0;
diff --git a/src/app/i18n/Language.jsx b/src/app/i18n/Language.jsx
index d16b2e1f..4fbac2e4 100644
--- a/src/app/i18n/Language.jsx
+++ b/src/app/i18n/Language.jsx
@@ -67,6 +67,7 @@ export function getLanguage(langCode) {
'm/s': {translate('m/s')}, // Meters per second
'°/s': {translate('°/s')}, // Degrees per second
MW: {translate('MW')}, // Mega Watts (same as Mega Joules per second)
+ mps: {translate('m/s')}, // Metres per second
ps: {translate('/s')}, // per second
pm: {translate('/min')}, // per minute
s: {translate('secs')}, // Seconds
diff --git a/src/app/i18n/en.js b/src/app/i18n/en.js
index 091cb560..03c96387 100644
--- a/src/app/i18n/en.js
+++ b/src/app/i18n/en.js
@@ -29,6 +29,11 @@ export const terms = {
PHRASE_UNLADEN: 'Ship mass excluding fuel and cargo',
PHRASE_UPDATE_RDY: 'Update Available! Click to refresh',
PHRASE_ENGAGEMENT_RANGE: 'The distance between your ship and its target',
+ PHRASE_SELECT_BLUEPRINT: 'Click to select a blueprint',
+ PHRASE_BLUEPRINT_WORST: 'Worst primary values for this blueprint',
+ PHRASE_BLUEPRINT_RANDOM: 'Random selection between worst and best primary values for this blueprint',
+ PHRASE_BLUEPRINT_BEST: 'Best primary values for this blueprint',
+ PHRASE_BLUEPRINT_RESET: 'Remove all modifications and blueprint',
HELP_MODIFICATIONS_MENU: 'Click on a number to enter a new value, or drag along the bar for small changes',
@@ -102,6 +107,12 @@ export const terms = {
rebuildsperbay: 'Rebuilds per bay',
+ // Blueprint rolls
+ worst: 'Worst',
+ random: 'Random',
+ best: 'Best',
+ reset: 'Reset',
+
// Weapon, offence, defence and movement
dpe: 'Damage per MJ of energy',
dps: 'Damage per second',
@@ -163,6 +174,7 @@ export const terms = {
shield: 'Shield',
shieldboost: 'Shield boost',
shieldreinforcement: 'Shield reinforcement',
+ shotspeed: 'Shot speed',
spinup: 'Spin up time',
syscap: 'Systems capacity',
sysrate: 'Systems recharge rate',
diff --git a/src/app/shipyard/Constants.js b/src/app/shipyard/Constants.js
index 35901ee3..a273c578 100755
--- a/src/app/shipyard/Constants.js
+++ b/src/app/shipyard/Constants.js
@@ -33,6 +33,7 @@ export const ModuleGroupToName = {
fi: 'Frame Shift Drive Interdictor',
hb: 'Hatch Breaker Limpet Controller',
hr: 'Hull Reinforcement Package',
+ mrp: 'Module Reinforcement Package',
rf: 'Refinery',
scb: 'Shield Cell Bank',
sg: 'Shield Generator',
diff --git a/src/app/shipyard/Module.js b/src/app/shipyard/Module.js
index 36911b33..7b58e6b4 100755
--- a/src/app/shipyard/Module.js
+++ b/src/app/shipyard/Module.js
@@ -650,10 +650,18 @@ export default class Module {
}
/**
- * Get the damage type for this module, taking in to account modifications
- * @return {string} the damage types for this module; any combination of E T and K
+ * Get the damage distribution for this module, taking in to account modifications
+ * @return {string} the damage distribution for this module
*/
- getDamageType() {
- return this.getModValue('type') || this.type;
+ getDamageDist() {
+ return this.getModValue('damagedist') || this.damagedist;
+ }
+
+ /**
+ * Get the shot speed for this module, taking in to account modifications
+ * @return {string} the damage distribution for this module
+ */
+ getShotSpeed() {
+ return this._getModifiedValue('shotspeed');
}
}
diff --git a/src/app/shipyard/Ship.js b/src/app/shipyard/Ship.js
index 1342ff20..88ffb9b0 100755
--- a/src/app/shipyard/Ship.js
+++ b/src/app/shipyard/Ship.js
@@ -411,6 +411,32 @@ export default class Ship {
return this;
}
+ /**
+ * Clear all modification values for a module
+ * @param {Number} m The module for which to clear the modifications
+ */
+ clearModifications(m) {
+ m.mods = {};
+ this.updatePowerGenerated()
+ .updatePowerUsed()
+ .updateJumpStats()
+ .recalculateShield()
+ .recalculateShieldCells()
+ .recalculateArmour()
+ .recalculateDps()
+ .recalculateEps()
+ .recalculateHps()
+ .updateMovement();
+ }
+
+ /**
+ * Clear blueprint for a module
+ * @param {Number} m The module for which to clear the modifications
+ */
+ clearBlueprint(m) {
+ m.blueprint = {};
+ }
+
/**
* Set a modification value
* @param {Object} m The module to change
@@ -954,20 +980,22 @@ export default class Ship {
totalDpe += dpe;
totalDps += dps;
totalSDps += sdps;
- if (slot.m.getDamageType().indexOf('E') != -1) {
- totalExplDpe += dpe / slot.m.getDamageType().length;
- totalExplDps += dps / slot.m.getDamageType().length;
- totalExplSDps += sdps / slot.m.getDamageType().length;
- }
- if (slot.m.getDamageType().indexOf('K') != -1) {
- totalKinDpe += dpe / slot.m.getDamageType().length;
- totalKinDps += dps / slot.m.getDamageType().length;
- totalKinSDps += sdps / slot.m.getDamageType().length;
- }
- if (slot.m.getDamageType().indexOf('T') != -1) {
- totalThermDpe += dpe / slot.m.getDamageType().length;
- totalThermDps += dps / slot.m.getDamageType().length;
- totalThermSDps += sdps / slot.m.getDamageType().length;
+ if (slot.m.getDamageDist()) {
+ if (slot.m.getDamageDist().E) {
+ totalExplDpe += dpe * slot.m.getDamageDist().E;
+ totalExplDps += dps * slot.m.getDamageDist().E;
+ totalExplSDps += sdps * slot.m.getDamageDist().E;
+ }
+ if (slot.m.getDamageDist().K) {
+ totalKinDpe += dpe * slot.m.getDamageDist().K;
+ totalKinDps += dps * slot.m.getDamageDist().K;
+ totalKinSDps += sdps * slot.m.getDamageDist().K;
+ }
+ if (slot.m.getDamageDist().T) {
+ totalThermDpe += dpe * slot.m.getDamageDist().T;
+ totalThermDps += dps * slot.m.getDamageDist().T;
+ totalThermSDps += sdps * slot.m.getDamageDist().T;
+ }
}
}
}
@@ -1333,7 +1361,7 @@ export default class Ship {
if (this.bulkheads.m && this.bulkheads.m.mods) {
for (let modKey in this.bulkheads.m.mods) {
// Filter out invalid modifications
- if (Modifications.validity['bh'] && Modifications.validity['bh'].indexOf(modKey) != -1) {
+ if (Modifications.modules['bh'] && Modifications.modules['bh'].modifications.indexOf(modKey) != -1) {
bulkheadMods.push({ id: Modifications.modifications[modKey].id, value: this.bulkheads.m.getModValue(modKey) });
}
}
@@ -1348,7 +1376,7 @@ export default class Ship {
if (slot.m && slot.m.mods) {
for (let modKey in slot.m.mods) {
// Filter out invalid modifications
- if (Modifications.validity[slot.m.grp] && Modifications.validity[slot.m.grp].indexOf(modKey) != -1) {
+ if (Modifications.modules[slot.m.grp] && Modifications.modules[slot.m.grp].modifications.indexOf(modKey) != -1) {
slotMods.push({ id: Modifications.modifications[modKey].id, value: slot.m.getModValue(modKey) });
}
}
@@ -1363,7 +1391,7 @@ export default class Ship {
if (slot.m && slot.m.mods) {
for (let modKey in slot.m.mods) {
// Filter out invalid modifications
- if (Modifications.validity[slot.m.grp] && Modifications.validity[slot.m.grp].indexOf(modKey) != -1) {
+ if (Modifications.modules[slot.m.grp] && Modifications.modules[slot.m.grp].modifications.indexOf(modKey) != -1) {
slotMods.push({ id: Modifications.modifications[modKey].id, value: slot.m.getModValue(modKey) });
}
}
@@ -1378,7 +1406,7 @@ export default class Ship {
if (slot.m && slot.m.mods) {
for (let modKey in slot.m.mods) {
// Filter out invalid modifications
- if (Modifications.validity[slot.m.grp] && Modifications.validity[slot.m.grp].indexOf(modKey) != -1) {
+ if (Modifications.modules[slot.m.grp] && Modifications.modules[slot.m.grp].modifications.indexOf(modKey) != -1) {
slotMods.push({ id: Modifications.modifications[modKey].id, value: slot.m.getModValue(modKey) });
}
}
diff --git a/src/app/utils/SlotFunctions.js b/src/app/utils/SlotFunctions.js
index a9481315..1c064c67 100644
--- a/src/app/utils/SlotFunctions.js
+++ b/src/app/utils/SlotFunctions.js
@@ -153,7 +153,7 @@ export function diffDetails(language, m, mm) {
let mPowerGeneration = m.pgen || 0;
let mmPowerGeneration = mm ? mm.getPowerGeneration() : 0;
- if (mPowerGeneration != mmPowerGeneration) propDiffs.push({translate('pgen')}: {diff(formats.round, mPowerGeneration, mmPowerGeneration)}{units.MJ}
);
+ if (mPowerGeneration != mmPowerGeneration) propDiffs.push({translate('pgen')}: {diff(formats.round, mPowerGeneration, mmPowerGeneration)}{units.MJ}
);
let mPowerUsage = m.power || 0;
let mmPowerUsage = mm ? mm.getPowerUsage() : 0;
diff --git a/src/app/utils/UtilityFunctions.js b/src/app/utils/UtilityFunctions.js
index 1137af26..4f0e2e7c 100644
--- a/src/app/utils/UtilityFunctions.js
+++ b/src/app/utils/UtilityFunctions.js
@@ -71,3 +71,15 @@ export function shallowEqual(objA, objB) {
export function fromUrlSafe(data) {
return data ? data.replace(/-/g, '/').replace(/_/g, '+') : null;
}
+
+/**
+ * Check if an object is empty
+ * @param {object} obj the object
+ * @return {bool} true if the object is empty, otherwise false
+ */
+export function isEmpty(obj) {
+ for (let key in obj) {
+ if (obj.hasOwnProperty(key)) return false;
+ }
+ return true;
+};