From c1ce07e039a28f3a6219a80aeb14495535715840 Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Wed, 30 Nov 2016 12:19:40 +0000 Subject: [PATCH 1/4] Calculate rate of fire for multi-burst weapons --- ChangeLog.md | 3 +++ .../anaconda-test-detailed-export-v4.json | 2 +- src/app/shipyard/Module.js | 27 +++++++++++++++++-- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 2cf6c679..8fb6b19f 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,3 +1,6 @@ +#2.2.5 + * Calculate rate of fire for multi-burst weapons + #2.2.4 * Add shortlink for outfitting page * Use coriolis-data 2.2.4: diff --git a/__tests__/fixtures/anaconda-test-detailed-export-v4.json b/__tests__/fixtures/anaconda-test-detailed-export-v4.json index 4bda3ad3..c925f052 100644 --- a/__tests__/fixtures/anaconda-test-detailed-export-v4.json +++ b/__tests__/fixtures/anaconda-test-detailed-export-v4.json @@ -271,7 +271,7 @@ "totalExplDpe": 0, "totalExplDps": 0, "totalExplSDps": 0, - "totalHps": 33.28, + "totalHps": 33.62, "totalKinDpe": 103.97, "totalKinDps": 28.92, "totalKinSDps": 21.23, diff --git a/src/app/shipyard/Module.js b/src/app/shipyard/Module.js index 6e8533da..72a1ec1f 100755 --- a/src/app/shipyard/Module.js +++ b/src/app/shipyard/Module.js @@ -482,11 +482,34 @@ export default class Module { } /** - * Get the rate of fire for this module, taking in to account modifications + * Get the burst size for this module, taking in to account modifications + * @return {Number} the burst size of this module + */ + getBurst() { + return this._getModifiedValue('burst'); + } + + /** + * Get the burst rate of fire for this module, taking in to account modifications + * @return {Number} the burst rate of fire of this module + */ + getBurstRoF() { + return this._getModifiedValue('burstrof'); + } + + /** + * Get the rate of fire for this module, taking in to account modifications. + * The rate of fire is a combination value, and needs to take in to account + * bursts of fire. + * Firing goes [burst 1] [burst interval] [burst 2] [burst interval] ... [burst n] [interval] + * where 'n' is 'burst', 'burst interval' is '1/burstrof' and 'interval' is '1/rof' * @return {Number} the rate of fire for this module */ getRoF() { - return this._getModifiedValue('rof'); + const burst = this.getBurst() || 1; + const burstRoF = this.getBurstRoF() || 1; + const intRoF = this._getModifiedValue('rof'); + return burst / (((burst - 1)/burstRoF) + 1/intRoF); } /** From f457fd0bff10692cf0fa5db15d6f93d010d1a736 Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Wed, 30 Nov 2016 14:51:19 +0000 Subject: [PATCH 2/4] Fixes for burst calculations --- src/app/components/ModalImport.jsx | 1 + src/app/components/Modification.jsx | 17 +++++++++++- src/app/shipyard/Module.js | 35 +++++++++++++++++++++---- src/app/shipyard/Ship.js | 2 +- src/app/utils/CompanionApiUtils.js | 40 +++++++++++++++++++---------- 5 files changed, 75 insertions(+), 20 deletions(-) diff --git a/src/app/components/ModalImport.jsx b/src/app/components/ModalImport.jsx index b698a9d9..9d5f4ff6 100644 --- a/src/app/components/ModalImport.jsx +++ b/src/app/components/ModalImport.jsx @@ -346,6 +346,7 @@ export default class ModalImport extends TranslatedComponent { } } } catch (e) { + // console.log(e.stack); this.setState({ errorMsg: (typeof e == 'string') ? e : 'Cannot Parse the data!' }); return; } diff --git a/src/app/components/Modification.jsx b/src/app/components/Modification.jsx index 6a1464d4..b6a6aea0 100644 --- a/src/app/components/Modification.jsx +++ b/src/app/components/Modification.jsx @@ -63,9 +63,24 @@ export default class Modification extends TranslatedComponent { let translate = this.context.language.translate; let name = this.props.name; + if (name === 'type') { + // We don't show type + return null; + } + + var symbol; + if (name === 'jitter') { + symbol = '°'; + } else if (name !== 'burst') { + symbol = '%'; + } + if (symbol) { + symbol = ' (' + symbol + ')'; + } + return (
-
{translate(name)}{name === 'jitter' ? ' (°)' : ' (%)'}
+
{translate(name)}{symbol}
); diff --git a/src/app/shipyard/Module.js b/src/app/shipyard/Module.js index 72a1ec1f..d20a2cf9 100755 --- a/src/app/shipyard/Module.js +++ b/src/app/shipyard/Module.js @@ -1,5 +1,6 @@ import * as ModuleUtils from './ModuleUtils'; import * as _ from 'lodash'; +import { Modifications } from 'coriolis-data/dist'; /** * Module - active module in a ship's buildout @@ -65,8 +66,20 @@ export default class Module { _getModifiedValue(name, additive) { let result = this[name] || (additive ? 0 : null); // Additive NULL === 0 if (result != null) { - // Jitter is special, being the only non-percentage value (it is in fact degrees) - const modValue = name === 'jitter' ? this.getModValue(name) / 100 : this.getModValue(name) / 10000; + const modification = Modifications.modifications[name]; + if (!modification) { + throw 'Unknown modification ' + name; + } + // We store percentages as decimals, so to get them back we need to divide by 10000. Otherwise + // we divide by 100. Both ways we end up with a value with two decimal places + var modValue; + if (modification.type === 'percentage') { + modValue = this.getModValue(name) / 10000; + } else if (modification.type === 'numeric') { + modValue = this.getModValue(name) / 100; + } else { + modValue = this.getModValue(name); + } if (modValue) { if (additive) { result = result + modValue; @@ -74,7 +87,18 @@ export default class Module { result = result * (1 + modValue); } } + } else { + if (name === 'burst') { + // Burst is special, as if it can not exist but have a modification + const modValue = this.getModValue(name) / 100; + return modValue; + } else if (name === 'burstrof') { + // Burst rate of fire is special, as if it can not exist but have a modification + const modValue = this.getModValue(name) / 100; + return modValue; + } } + return result; } @@ -425,7 +449,7 @@ export default class Module { getDps() { // DPS is a synthetic value let damage = this.getDamage(); - let rpshot = this.getRoundsPerShot() || 1; + let rpshot = this.roundspershot || 1; let rof = this.getRoF() || 1; return damage * rpshot * rof; @@ -438,7 +462,7 @@ export default class Module { getEps() { // EPS is a synthetic value let distdraw = this.getDistDraw(); - let rpshot = this.getRoundsPerShot() || 1; + let rpshot = this.roundspershot || 1; let rof = this.getRoF() || 1; return distdraw * rpshot * rof; @@ -451,7 +475,7 @@ export default class Module { getHps() { // HPS is a synthetic value let heat = this.getThermalLoad(); - let rpshot = this.getRoundsPerShot() || 1; + let rpshot = this.roundspershot || 1; let rof = this.getRoF() || 1; return heat * rpshot * rof; @@ -509,6 +533,7 @@ export default class Module { const burst = this.getBurst() || 1; const burstRoF = this.getBurstRoF() || 1; const intRoF = this._getModifiedValue('rof'); + return burst / (((burst - 1)/burstRoF) + 1/intRoF); } diff --git a/src/app/shipyard/Ship.js b/src/app/shipyard/Ship.js index 1a9a386e..3a1acd16 100755 --- a/src/app/shipyard/Ship.js +++ b/src/app/shipyard/Ship.js @@ -458,7 +458,7 @@ export default class Ship { } else if (name === 'shieldreinforcement') { m.setModValue(name, value); this.recalculateShieldCells(); - } else if (name === 'burst' || name === 'clip' || name === 'damage' || name === 'distdraw' || name === 'jitter' || name === 'piercing' || name === 'range' || name === 'reload' || name === 'rof' || name === 'thermload') { + } else if (name === 'burst' || name == 'burstrof' || name === 'clip' || name === 'damage' || name === 'distdraw' || name === 'jitter' || name === 'piercing' || name === 'range' || name === 'reload' || name === 'rof' || name === 'thermload') { m.setModValue(name, value); this.recalculateDps(); this.recalculateHps(); diff --git a/src/app/utils/CompanionApiUtils.js b/src/app/utils/CompanionApiUtils.js index c11783ab..91eccc54 100644 --- a/src/app/utils/CompanionApiUtils.js +++ b/src/app/utils/CompanionApiUtils.js @@ -279,21 +279,35 @@ function _addModifications(module, modifiers, blueprint, grade) { var special; for (const i in modifiers.modifiers) { - // Look up the modifiers to find what we need to do - const modifierActions = Modifications.modifierActions[modifiers.modifiers[i].name]; - const value = modifiers.modifiers[i].value; + // Some special modifications + if (modifiers.modifiers[i].name === 'mod_weapon_clip_size_override') { + // This is a numeric addition to the clip size, but we need to work it out in terms of being a percentage so + // that it works the same as other modifications + const origClip = module.clip || 1; + module.setModValue('clip', ((modifiers.modifiers[i].value - origClip) / origClip ) * 10000); + } else if (modifiers.modifiers[i].name === 'mod_weapon_burst_size') { + // This is an absolute number that acts as an override + module.setModValue('burst', modifiers.modifiers[i].value * 100); + } else if (modifiers.modifiers[i].name === 'mod_weapon_burst_rof') { + // For some reason this is a non-normalised percentage (i.e. 12.23% is 12.23 value rather than 0.1223 as everywhere else), so fix that here + module.setModValue('burstrof', modifiers.modifiers[i].value * 100); + } else { + // Look up the modifiers to find what we need to do + const modifierActions = Modifications.modifierActions[modifiers.modifiers[i].name]; + const value = modifiers.modifiers[i].value; - // Carry out the required changes - for (const action in modifierActions) { - if (isNaN(modifierActions[action])) { - module.setModValue(action, modifierActions[action]); - } else { - const actionValue = modifierActions[action] * value; - let mod = module.getModValue(action) / 10000; - if (!mod) { - mod = 0; + // Carry out the required changes + for (const action in modifierActions) { + if (isNaN(modifierActions[action])) { + module.setModValue(action, modifierActions[action]); + } else { + const actionValue = modifierActions[action] * value; + let mod = module.getModValue(action) / 10000; + if (!mod) { + mod = 0; + } + module.setModValue(action, ((1 + mod) * (1 + actionValue) - 1) * 10000); } - module.setModValue(action, ((1 + mod) * (1 + actionValue) - 1) * 10000); } } From ee19e9af509b85e511a92077fd880b2130ad9824 Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Wed, 30 Nov 2016 14:56:33 +0000 Subject: [PATCH 3/4] Lint --- src/app/Coriolis.jsx | 2 +- src/app/components/ComparisonTable.jsx | 2 +- src/app/components/HardpointSlot.jsx | 18 ++++++------ src/app/components/Modification.jsx | 2 +- src/app/shipyard/Module.js | 10 +++---- src/app/shipyard/Serializer.js | 2 +- src/app/shipyard/Ship.js | 40 +++++++++++++------------- src/app/utils/CompanionApiUtils.js | 6 ++-- 8 files changed, 41 insertions(+), 41 deletions(-) diff --git a/src/app/Coriolis.jsx b/src/app/Coriolis.jsx index 2e180587..0e796d23 100644 --- a/src/app/Coriolis.jsx +++ b/src/app/Coriolis.jsx @@ -8,7 +8,7 @@ import Header from './components/Header'; import Tooltip from './components/Tooltip'; import ModalImport from './components/ModalImport'; import * as CompanionApiUtils from './utils/CompanionApiUtils'; -import { outfitURL } from './utils/UrlGenerators' +import { outfitURL } from './utils/UrlGenerators'; import AboutPage from './pages/AboutPage'; import NotFoundPage from './pages/NotFoundPage'; diff --git a/src/app/components/ComparisonTable.jsx b/src/app/components/ComparisonTable.jsx index 3f831a3a..f93064d3 100644 --- a/src/app/components/ComparisonTable.jsx +++ b/src/app/components/ComparisonTable.jsx @@ -72,7 +72,7 @@ export default class ComparisonTable extends TranslatedComponent { * @return {React.Component} Table row */ _buildRow(build, facets, formats, units) { - let url = outfitURL(build.id, build.toString(), build.buildName) + let url = outfitURL(build.id, build.toString(), build.buildName); let cells = [ {build.name}, {build.buildName} diff --git a/src/app/components/HardpointSlot.jsx b/src/app/components/HardpointSlot.jsx index 57f38bbb..6b4b28eb 100644 --- a/src/app/components/HardpointSlot.jsx +++ b/src/app/components/HardpointSlot.jsx @@ -48,22 +48,22 @@ export default class HardpointSlot extends Slot { let modTT = translate('modified'); if (m && m.blueprint) { modTT = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade; - if (m.blueprint.special && m.blueprint.special.id) { + if (m.blueprint.special && m.blueprint.special.id) { modTT += ', ' + translate(m.blueprint.special.name); - } + } } return
- {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.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') ? : ''} {classRating} {translate(m.name || m.grp)}{ m.mods && Object.keys(m.mods).length > 0 ? : null } -
+
{formats.round(m.getMass())}{u.T}
diff --git a/src/app/components/Modification.jsx b/src/app/components/Modification.jsx index b6a6aea0..8c3c6062 100644 --- a/src/app/components/Modification.jsx +++ b/src/app/components/Modification.jsx @@ -68,7 +68,7 @@ export default class Modification extends TranslatedComponent { return null; } - var symbol; + let symbol; if (name === 'jitter') { symbol = '°'; } else if (name !== 'burst') { diff --git a/src/app/shipyard/Module.js b/src/app/shipyard/Module.js index d20a2cf9..a95dc300 100755 --- a/src/app/shipyard/Module.js +++ b/src/app/shipyard/Module.js @@ -72,13 +72,13 @@ export default class Module { } // We store percentages as decimals, so to get them back we need to divide by 10000. Otherwise // we divide by 100. Both ways we end up with a value with two decimal places - var modValue; + let modValue; if (modification.type === 'percentage') { - modValue = this.getModValue(name) / 10000; + modValue = this.getModValue(name) / 10000; } else if (modification.type === 'numeric') { - modValue = this.getModValue(name) / 100; + modValue = this.getModValue(name) / 100; } else { - modValue = this.getModValue(name); + modValue = this.getModValue(name); } if (modValue) { if (additive) { @@ -534,7 +534,7 @@ export default class Module { const burstRoF = this.getBurstRoF() || 1; const intRoF = this._getModifiedValue('rof'); - return burst / (((burst - 1)/burstRoF) + 1/intRoF); + return burst / (((burst - 1) / burstRoF) + 1 / intRoF); } /** diff --git a/src/app/shipyard/Serializer.js b/src/app/shipyard/Serializer.js index 6732049c..cd9951a6 100644 --- a/src/app/shipyard/Serializer.js +++ b/src/app/shipyard/Serializer.js @@ -8,7 +8,7 @@ import { outfitURL } from '../utils/UrlGenerators'; const STANDARD = ['powerPlant', 'thrusters', 'frameShiftDrive', 'lifeSupport', 'powerDistributor', 'sensors', 'fuelTank']; -const STANDARD_GROUPS = {'powerPlant': 'pp', 'thrusters': 't', 'frameShiftDrive': 'fsd', 'lifeSupport': 'ls', 'powerDistributor': 'pd', 'sensors': 's', 'fuelTank': 'ft'}; +const STANDARD_GROUPS = { 'powerPlant': 'pp', 'thrusters': 't', 'frameShiftDrive': 'fsd', 'lifeSupport': 'ls', 'powerDistributor': 'pd', 'sensors': 's', 'fuelTank': 'ft' }; /** * Generates ship-loadout JSON Schema standard object diff --git a/src/app/shipyard/Ship.js b/src/app/shipyard/Ship.js index 3a1acd16..be771cbc 100755 --- a/src/app/shipyard/Ship.js +++ b/src/app/shipyard/Ship.js @@ -1260,7 +1260,7 @@ export default class Ship { let modElements = mods[j].split(':'); if (modElements[0].match('[0-9]+')) { const modification = _.find(Modifications.modifications, function(o) { return o.id === modElements[0]; }); - if (modification != null) arr[i][modification.name] = Number(modElements[1]); + if (modification != null) arr[i][modification.name] = Number(modElements[1]); } else { arr[i][modElements[0]] = Number(modElements[1]); } @@ -1297,7 +1297,7 @@ export default class Ship { bulkheadBlueprint = this.bulkheads.m.blueprint; } slots.push(bulkheadMods); - blueprints.push(bulkheadBlueprint) + blueprints.push(bulkheadBlueprint); specials.push(bulkheadBlueprint ? bulkheadBlueprint.special : null); for (let slot of this.standard) { @@ -1356,7 +1356,7 @@ export default class Ship { for (let special of specials) { if (special) { // Length is 5 for each special - bufsize += 5; + bufsize += 5; } } @@ -1376,20 +1376,20 @@ export default class Ship { buffer.writeInt8(MODIFICATION_ID_GRADE, curpos++); buffer.writeInt32LE(blueprints[i].grade, curpos); curpos += 4; - } + } if (specials[i]) { buffer.writeInt8(MODIFICATION_ID_SPECIAL, curpos++); buffer.writeInt32LE(specials[i].id, curpos); curpos += 4; - } + } for (let slotMod of slot) { buffer.writeInt8(slotMod.id, curpos++); - if (isNaN(slotMod.value)) { - // Need to write the string with exactly four characters, so pad with whitespace - buffer.write((" " + slotMod.value).slice(-4), curpos, 4); - } else { - buffer.writeInt32LE(slotMod.value, curpos); - } + if (isNaN(slotMod.value)) { + // Need to write the string with exactly four characters, so pad with whitespace + buffer.write((' ' + slotMod.value).slice(-4), curpos, 4); + } else { + buffer.writeInt32LE(slotMod.value, curpos); + } // const modification = _.find(Modifications.modifications, function(o) { return o.id === slotMod.id; }); // console.log('ENCODE Slot ' + i + ': ' + modification.name + ' = ' + slotMod.value); curpos += 4; @@ -1414,7 +1414,7 @@ export default class Ship { * See updateModificationsString() for details of the structure. * @param {String} buffer Buffer holding modification info * @param {Array} modArr Modification array - * @param {Array} bluprintArr Blueprint array + * @param {Array} blueprintArr Blueprint array */ decodeModificationsStruct(buffer, modArr, blueprintArr) { let curpos = 0; @@ -1428,22 +1428,22 @@ export default class Ship { if (modificationId === 40) { // Type is special, in that it's a character string modificationValue = buffer.toString('utf8', curpos, curpos + 4).trim(); - } else { + } else { modificationValue = buffer.readInt32LE(curpos); - } + } curpos += 4; - // There are a number of 'special' modification IDs, check for them here - if (modificationId === MODIFICATION_ID_BLUEPRINT) { + // There are a number of 'special' modification IDs, check for them here + if (modificationId === MODIFICATION_ID_BLUEPRINT) { blueprint = Object.assign(blueprint, _.find(Modifications.blueprints, function(o) { return o.id === modificationValue; })); - } else if (modificationId === MODIFICATION_ID_GRADE) { + } else if (modificationId === MODIFICATION_ID_GRADE) { blueprint.grade = modificationValue; - } else if (modificationId === MODIFICATION_ID_SPECIAL) { + } else if (modificationId === MODIFICATION_ID_SPECIAL) { blueprint.special = _.find(Modifications.specials, function(o) { return o.id === modificationValue; }); - } else { + } else { const modification = _.find(Modifications.modifications, function(o) { return o.id === modificationId; }); // console.log('DECODE Slot ' + slot + ': ' + modification.name + ' = ' + modificationValue); modifications[modification.name] = modificationValue; - } + } modificationId = buffer.readInt8(curpos++); } modArr[slot] = modifications; diff --git a/src/app/utils/CompanionApiUtils.js b/src/app/utils/CompanionApiUtils.js index 91eccc54..6d90bc7f 100644 --- a/src/app/utils/CompanionApiUtils.js +++ b/src/app/utils/CompanionApiUtils.js @@ -277,14 +277,14 @@ export function shipFromJson(json) { function _addModifications(module, modifiers, blueprint, grade) { if (!modifiers || !modifiers.modifiers) return; - var special; + let special; for (const i in modifiers.modifiers) { // Some special modifications if (modifiers.modifiers[i].name === 'mod_weapon_clip_size_override') { // This is a numeric addition to the clip size, but we need to work it out in terms of being a percentage so // that it works the same as other modifications const origClip = module.clip || 1; - module.setModValue('clip', ((modifiers.modifiers[i].value - origClip) / origClip ) * 10000); + module.setModValue('clip', ((modifiers.modifiers[i].value - origClip) / origClip) * 10000); } else if (modifiers.modifiers[i].name === 'mod_weapon_burst_size') { // This is an absolute number that acts as an override module.setModValue('burst', modifiers.modifiers[i].value * 100); @@ -313,7 +313,7 @@ function _addModifications(module, modifiers, blueprint, grade) { // Note the special if present if (modifiers.modifiers[i].name && modifiers.modifiers[i].name.startsWith('special_')) { - special = Modifications.specials[modifiers.modifiers[i].name]; + special = Modifications.specials[modifiers.modifiers[i].name]; } } From ca2136544cef3ebba2fd3f1ada08fa2fdedc49c4 Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Wed, 30 Nov 2016 15:08:32 +0000 Subject: [PATCH 4/4] Handle unmodifiable values --- src/app/shipyard/Module.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/shipyard/Module.js b/src/app/shipyard/Module.js index a95dc300..bf38e8ec 100755 --- a/src/app/shipyard/Module.js +++ b/src/app/shipyard/Module.js @@ -68,7 +68,7 @@ export default class Module { if (result != null) { const modification = Modifications.modifications[name]; if (!modification) { - throw 'Unknown modification ' + name; + return result; } // We store percentages as decimals, so to get them back we need to divide by 10000. Otherwise // we divide by 100. Both ways we end up with a value with two decimal places