From 2c45011664cba736b226960a07b0092e4a0a3c1a Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Sat, 22 Oct 2016 09:47:43 +0100 Subject: [PATCH 01/39] Rework modules to be individual objects rather than references to templates --- package.json | 2 + src/app/components/HardpointSlot.jsx | 7 +- src/app/components/InternalSlot.jsx | 5 +- src/app/components/PowerManagement.jsx | 12 +- src/app/components/ShipSummaryTable.jsx | 4 + src/app/components/StandardSlot.jsx | 7 +- src/app/components/SvgIcons.jsx | 19 +++ src/app/pages/ComparisonPage.jsx | 2 +- src/app/shipyard/Constants.js | 14 ++ src/app/shipyard/Modification.js | 15 ++ src/app/shipyard/Module.js | 107 ++++++++++++++ src/app/shipyard/ModuleSet.js | 12 +- src/app/shipyard/ModuleUtils.js | 48 +++++- src/app/shipyard/Ship.js | 188 ++++++++++++++++++++---- src/app/shipyard/ShipRoles.js | 2 +- src/app/utils/SlotFunctions.js | 4 +- 16 files changed, 397 insertions(+), 51 deletions(-) create mode 100755 src/app/shipyard/Modification.js create mode 100755 src/app/shipyard/Module.js diff --git a/package.json b/package.json index 4c12a8b1..aaf8d19e 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ ], "automock": true, "unmockedModulePathPatterns": [ + "/node_modules/lodash", "/node_modules/react", "/node_modules/react-dom", "/node_modules/react-addons-test-utils", @@ -86,6 +87,7 @@ "coriolis-data": "EDCD/coriolis-data", "d3": "3.5.16", "fbemitter": "^2.0.0", + "lodash": "^4.15.0", "lz-string": "^1.4.4", "react": "^15.0.1", "react-dom": "^15.0.1", diff --git a/src/app/components/HardpointSlot.jsx b/src/app/components/HardpointSlot.jsx index dd55be07..0c5773a8 100644 --- a/src/app/components/HardpointSlot.jsx +++ b/src/app/components/HardpointSlot.jsx @@ -1,6 +1,6 @@ import React from 'react'; import Slot from './Slot'; -import { DamageKinetic, DamageThermal, DamageExplosive, MountFixed, MountGimballed, MountTurret } from './SvgIcons'; +import { DamageKinetic, DamageThermal, DamageExplosive, MountFixed, MountGimballed, MountTurret, Modifications } from './SvgIcons'; /** * Hardpoint / Utility Slot @@ -48,17 +48,18 @@ export default class HardpointSlot extends Slot { {m.type && m.type == 'KT' ? : ''} {m.type && m.type == 'E' ? : ''} {classRating} {translate(m.name || m.grp)} -
{m.mass}{u.T}
+
{m.getMass()}{u.T}
{ m.dps ?
{translate('DPS')}: {formats.round1(m.dps)} { m.clip ? ({formats.round1((m.clip * m.dps / m.rof) / ((m.clip / m.rof) + m.reload)) }) : null }
: null } - { m.eps ?
{translate('EPS')}: {formats.round1(m.eps)} { m.clip ? ({formats.round1((m.clip * m.eps / m.rof) / ((m.clip / m.rof) + m.reload)) }) : null }
: null } + { m.eps ?
{translate('EPS')}: {formats.round1(m.eps)}{u.MW} { m.clip ? ({formats.round1((m.clip * m.eps / m.rof) / ((m.clip / m.rof) + m.reload)) }{u.MW}) : null }
: null } { m.hps ?
{translate('HPS')}: {formats.round1(m.hps)} { m.clip ? ({formats.round1((m.clip * m.hps / m.rof) / ((m.clip / m.rof) + m.reload)) }) : null }
: null } { m.dps && m.eps ?
{translate('DPE')}: {formats.round1(m.dps / m.eps)}
: null } { m.rof ?
{translate('ROF')}: {m.rof}{u.ps}
: null } { m.range && !m.dps ?
{translate('Range')} : {formats.round(m.range / 1000)}{u.km}
: null } { m.shieldmul ?
+{formats.rPct(m.shieldmul)}
: null } { m.ammo >= 0 ?
{translate('ammo')}: {formats.int(m.clip)}/{formats.int(m.ammo)}
: null } +
; } else { diff --git a/src/app/components/InternalSlot.jsx b/src/app/components/InternalSlot.jsx index 4c38afd7..fa29f27c 100644 --- a/src/app/components/InternalSlot.jsx +++ b/src/app/components/InternalSlot.jsx @@ -1,6 +1,6 @@ import React from 'react'; import Slot from './Slot'; -import { Infinite } from './SvgIcons'; +import { Modifications } from './SvgIcons'; /** * Internal Slot @@ -23,7 +23,7 @@ export default class InternalSlot extends Slot { return
{classRating} {translate(m.name || m.grp)}
-
{m.mass || m.cargo || m.fuel || 0}{u.T}
+
{m.getMass() || m.cargo || m.fuel || 0}{u.T}
{ m.optmass ?
{translate('optimal mass')}: {m.optmass}{u.T}
: null } @@ -42,6 +42,7 @@ export default class InternalSlot extends Slot { { m.rangeLS === null ?
∞{u.Ls}
: null } { m.rangeRating ?
{translate('range')}: {m.rangeRating}
: null } { m.armouradd ?
+{m.armouradd} {translate('armour')}
: null } +
; } else { diff --git a/src/app/components/PowerManagement.jsx b/src/app/components/PowerManagement.jsx index 0345d895..84b66bee 100644 --- a/src/app/components/PowerManagement.jsx +++ b/src/app/components/PowerManagement.jsx @@ -71,7 +71,7 @@ export default class PowerManagement extends TranslatedComponent { case 'n': comp = comp(null, desc); break; case 't': comp = comp((a, b) => a.type.localeCompare(b.type), desc); break; case 'pri': comp = comp((a, b) => a.priority - b.priority, desc); break; - case 'pwr': comp = comp((a, b) => a.m.power - b.m.power, desc); break; + case 'pwr': comp = comp((a, b) => a.m.getPowerUsage() - b.m.getPowerUsage(), desc); break; case 'r': comp = comp((a, b) => ship.getSlotStatus(a) - ship.getSlotStatus(b), desc); break; case 'd': comp = comp((a, b) => ship.getSlotStatus(a, true) - ship.getSlotStatus(b, true), desc); break; } @@ -113,7 +113,7 @@ export default class PowerManagement extends TranslatedComponent { for (let i = 0, l = ship.powerList.length; i < l; i++) { let slot = ship.powerList[i]; - if (slot.m && slot.m.power) { + if (slot.m && slot.m.getPowerUsage() > 0) { let m = slot.m; let toggleEnabled = this._toggleEnabled.bind(this, slot); let retractedElem = null, deployedElem = null; @@ -134,8 +134,8 @@ export default class PowerManagement extends TranslatedComponent { {' ' + (slot.priority + 1) + ' '} - {pwr(m.power)} - {pct(m.power / ship.powerAvailable)} + {pwr(m.getPowerUsage())} + {pct(m.getPowerUsage() / ship.powerAvailable)} {retractedElem} {deployedElem} ); @@ -214,7 +214,7 @@ export default class PowerManagement extends TranslatedComponent { {translate('pp')} {translate('SYS')} 1 - {pwr(pp.pGen)} + {pwr(pp.getPowerGeneration())} 100% @@ -223,7 +223,7 @@ export default class PowerManagement extends TranslatedComponent { {this._renderPowerRows(ship, translate, pwr, formats.pct1)} - + ); } diff --git a/src/app/components/ShipSummaryTable.jsx b/src/app/components/ShipSummaryTable.jsx index 2e8e57b0..7ebc8163 100644 --- a/src/app/components/ShipSummaryTable.jsx +++ b/src/app/components/ShipSummaryTable.jsx @@ -52,6 +52,8 @@ export default class ShipSummaryTable extends TranslatedComponent { {translate('speed')} {translate('boost')} {translate('DPS')} + {translate('EPS')} + {translate('HPS')} {translate('armour')} {translate('shields')} {translate('mass')} @@ -83,6 +85,8 @@ export default class ShipSummaryTable extends TranslatedComponent { { ship.canThrust() ? {int(ship.topSpeed)} {u['m/s']} : 0 } { ship.canBoost() ? {int(ship.topBoost)} {u['m/s']} : 0 } {round(ship.totalDps)} + {round(ship.totalEps)} + {round(ship.totalHps)} {int(ship.armour)} {armourDetails} {int(ship.shieldStrength)} {u.MJ} { ship.shieldMultiplier > 1 && ship.shieldStrength > 0 ? ({formats.rPct(ship.shieldMultiplier)}) : null } {sgRecover} diff --git a/src/app/components/StandardSlot.jsx b/src/app/components/StandardSlot.jsx index 00d81ac3..a62e28e7 100644 --- a/src/app/components/StandardSlot.jsx +++ b/src/app/components/StandardSlot.jsx @@ -4,6 +4,7 @@ import TranslatedComponent from './TranslatedComponent'; import { jumpRange } from '../shipyard/Calculations'; import { diffDetails } from '../utils/SlotFunctions'; import AvailableModulesMenu from './AvailableModulesMenu'; +import { Modifications } from './SvgIcons'; /** * Standard Slot @@ -49,7 +50,8 @@ export default class StandardSlot extends TranslatedComponent {
{slot.maxClass}
{classRating} {translate(m.grp == 'bh' ? m.grp : m.name || m.grp)}
-
{m.mass || m.fuel || 0}{units.T}
+
{m.getMass() || m.fuel || 0}{units.T}
+
{ m.grp == 'bh' && m.name ?
{translate(m.name)}
: null } { m.optmass ?
{translate('optimal mass')}: {m.optmass}{units.T}
: null } @@ -57,11 +59,12 @@ export default class StandardSlot extends TranslatedComponent { { m.range ?
{translate('range')}: {m.range}{units.km}
: null } { m.time ?
{translate('time')}: {formats.time(m.time)}
: null } { m.eff ?
{translate('efficiency')}: {m.eff}
: null } - { m.pGen ?
{translate('power')}: {m.pGen}{units.MW}
: null } + { m.getPowerGeneration() > 0 ?
{translate('power')}: {formats.round(m.getPowerGeneration())}{units.MW}
: null } { m.maxfuel ?
{translate('max')} {translate('fuel')}: {m.maxfuel}{units.T}
: null } { m.weaponcapacity ?
{translate('WEP')}: {m.weaponcapacity}{units.MJ} / {m.weaponrecharge}{units.MW}
: null } { m.systemcapacity ?
{translate('SYS')}: {m.systemcapacity}{units.MJ} / {m.systemrecharge}{units.MW}
: null } { m.enginecapacity ?
{translate('ENG')}: {m.enginecapacity}{units.MJ} / {m.enginerecharge}{units.MW}
: null } +
diff --git a/src/app/components/SvgIcons.jsx b/src/app/components/SvgIcons.jsx index 6e228026..984415d1 100644 --- a/src/app/components/SvgIcons.jsx +++ b/src/app/components/SvgIcons.jsx @@ -488,6 +488,25 @@ export class Rocket extends SvgIcon { } } +/** + * Modifications (engineers) + */ +export class Modifications extends SvgIcon { + /** + * Overriden view box + * @return {String} view box + */ + viewBox() { return '0 0 200 200'; } + /** + /** + * Generate the SVG + * @return {React.Component} SVG Contents + */ + svg() { + return ; + } +} + /** * Hammer */ diff --git a/src/app/pages/ComparisonPage.jsx b/src/app/pages/ComparisonPage.jsx index 1ab50fd6..5531525f 100644 --- a/src/app/pages/ComparisonPage.jsx +++ b/src/app/pages/ComparisonPage.jsx @@ -63,7 +63,7 @@ export default class ComparisonPage extends Page { * @return {Object} New state object */ _initState(context) { - let defaultFacets = [9, 6, 4, 1, 3, 2]; // Reverse order of Armour, Shields, Speed, Jump Range, Cargo Capacity, Cost + let defaultFacets = [13, 12, 11, 9, 6, 4, 1, 3, 2]; // Reverse order of Armour, Shields, Speed, Jump Range, Cargo Capacity, Cost, DPS, EPS, HPS let params = context.route.params; let code = params.code; let name = params.name ? decodeURIComponent(params.name) : null; diff --git a/src/app/shipyard/Constants.js b/src/app/shipyard/Constants.js index 70688066..66898802 100755 --- a/src/app/shipyard/Constants.js +++ b/src/app/shipyard/Constants.js @@ -193,6 +193,20 @@ export const ShipFacets = [ lbls: ['DPS'], fmt: 'round', i: 11 + }, + { // 12 + title: 'EPS', + props: ['totalEps'], + lbls: ['EPS'], + fmt: 'round', + i: 12 + }, + { // 13 + title: 'HPS', + props: ['totalHps'], + lbls: ['HPS'], + fmt: 'round', + i: 13 } ]; diff --git a/src/app/shipyard/Modification.js b/src/app/shipyard/Modification.js new file mode 100755 index 00000000..5c2d4566 --- /dev/null +++ b/src/app/shipyard/Modification.js @@ -0,0 +1,15 @@ +/** + * Modification - a modification and its value + */ +export default class Modification { + + /** + * @param {String} id Unique modification ID + * @param {Number} value Value of the modification + */ + constructor(id, value) { + this.id = id; + this.value = value; + } + +} diff --git a/src/app/shipyard/Module.js b/src/app/shipyard/Module.js new file mode 100755 index 00000000..9e301368 --- /dev/null +++ b/src/app/shipyard/Module.js @@ -0,0 +1,107 @@ +import * as ModuleUtils from './ModuleUtils'; +import * as _ from 'lodash'; + +/** + * Module - active module in a ship's buildout + */ +export default class Module { + + /** + * Construct a new module + * @param {Object} params Module parameters. Either grp/id or template + */ + constructor(params) { + let properties = Object.assign({ grp: null, id: null, template: null }, params); + + let template; + if (properties.template == undefined) { + return ModuleUtils.findModule(properties.grp, properties.id); + } else { + template = properties.template; + if (template) { + // Copy all properties from coriolis-data template + for (let p in template) { this[p] = template[p]; } + } + } + } + + /** + * Get a value for a given modification ID + * @param {Number} modId The ID of the modification + * @return {Number} The value of the modification + */ + _getModValue(modId) { + let result = null; + if (this.mods) { + let mod = _.find(this.mods, function(o) { return o.id == modId; }); + if (mod) { + result = mod.value; + } + } + return result; + } + + /** + * Get the power generation of this module, taking in to account modifications + * @return {Number} the power generation of this module + */ + getPowerGeneration() { + let result = 0; + if (this.pGen) { + result = this.pGen; + if (result) { + let mult = this._getModValue(1); + if (mult) { result = result * (1 + (mult / 1000)); } + } + } + return result; + } + + /** + * Get the power usage of this module, taking in to account modifications + * @return {Number} the power usage of this module + */ + getPowerUsage() { + let result = 0; + if (this.power) { + result = this.power; + if (result) { + let mult = this._getModValue(2); + if (mult) { result = result * (1 + (mult / 1000)); } + } + } + return result; + } + + /** + * Get the mass of this module, taking in to account modifications + * @return {Number} the mass of this module + */ + getMass() { + let result = 0; + if (this.mass) { + result = this.mass; + if (result) { + let mult = this._getModValue(3); + if (mult) { result = result * (1 + (mult / 1000)); } + } + } + return result; + } + + /** + * Get the integrity of this module, taking in to account modifications + * @return {Number} the integrity of this module + */ + getIntegrity() { + let result = 0; + if (this.health) { + result = this.health; + if (result) { + let mult = this._getModValue(4); + if (mult) { result = result * (1 + (mult / 1000)); } + } + } + return result; + } +} diff --git a/src/app/shipyard/ModuleSet.js b/src/app/shipyard/ModuleSet.js index 04711cb8..a9148a57 100755 --- a/src/app/shipyard/ModuleSet.js +++ b/src/app/shipyard/ModuleSet.js @@ -1,4 +1,4 @@ - +import Module from './Module'; import { BulkheadNames } from './Constants'; /** @@ -37,7 +37,7 @@ export default class ModuleSet { this.intClass = {}; this.bulkheads = shipData.bulkheads.map((b, i) => { - return Object.assign({ grp: 'bh', name: BulkheadNames[i], index: i, class: '', rating: '' }, b); + return Object.assign(new Module(), { grp: 'bh', id: i, name: BulkheadNames[i], index: i, class: '', rating: '' }, b); }); this.standard[0] = filter(stnd.pp, maxStandardArr[0], 0, mass); // Power Plant @@ -130,7 +130,7 @@ export default class ModuleSet { pd = p; } } - return pd; + return new Module({ template: pd }); }; /** @@ -146,7 +146,7 @@ export default class ModuleSet { th = t; } } - return th; + return new Module({ template: th }); }; /** @@ -162,7 +162,7 @@ export default class ModuleSet { sg = s; } } - return sg; + return new Module({ template: sg }); }; /** @@ -179,6 +179,6 @@ export default class ModuleSet { pp = p; } } - return pp; + return new Module({ template: pp }); } } diff --git a/src/app/shipyard/ModuleUtils.js b/src/app/shipyard/ModuleUtils.js index 4543bf32..d2066c61 100755 --- a/src/app/shipyard/ModuleUtils.js +++ b/src/app/shipyard/ModuleUtils.js @@ -1,7 +1,12 @@ import { ModuleNameToGroup, BulkheadNames, StandardArray } from './Constants'; import ModuleSet from './ModuleSet'; +import Module from './Module'; import { Ships, Modules } from 'coriolis-data/dist'; +/* + * All functions below must return a fresh Module rather than a definition or existing module, as + * the resultant object can be altered with modifications. + */ /** @@ -9,9 +14,45 @@ import { Ships, Modules } from 'coriolis-data/dist'; * @return {Object} Cargo hatch model */ export function cargoHatch() { - return { name: 'Cargo Hatch', class: 1, rating: 'H', power: 0.6 }; + let hatch = new Module(); + Object.assign(hatch, { name: 'Cargo Hatch', class: 1, rating: 'H', power: 0.6 }); + return hatch; }; +/** + * Finds the module with the specific group and ID + * @param {String} grp Module group (pp - power plant, pl - pulse laser etc) + * @param {String} id The module ID + * @return {Object} The module or null + */ +export function findModule(grp, id) { + // See if it's a standard module + if (Modules.standard[grp]) { + let standardmod = Modules.standard[grp].find(e => e.id == id); + if (standardmod != null) { + return new Module({ template: standardmod }); + } + } + + // See if it's an internal module + if (Modules.internal[grp]) { + let internalmod = Modules.internal[grp].find(e => e.id == id); + if (internalmod != null) { + return new Module({ template: internalmod }); + } + } + + // See if it's a hardpoint module + if (Modules.hardpoints[grp]) { + let hardpointmod = Modules.hardpoints[grp].find(e => e.id == id); + if (hardpointmod != null) { + return new Module({ template: hardpointmod }); + } + } + + return null; +} + /** * Finds the standard module type with the specified ID * @param {String|Number} type Standard Module Type (0/pp - Power Plant, 1/t - Thrusters, etc) @@ -24,6 +65,9 @@ export function standard(type, id) { } let s = Modules.standard[type].find(e => e.id == id || (e.class == id.charAt(0) && e.rating == id.charAt(1))); + if (s) { + s = new Module({ template: s }); + } return s || null; }; @@ -37,7 +81,7 @@ export function hardpoints(id) { let group = Modules.hardpoints[n]; for (let i = 0; i < group.length; i++) { if (group[i].id == id) { - return group[i]; + return new Module({ template: group[i] }); } } } diff --git a/src/app/shipyard/Ship.js b/src/app/shipyard/Ship.js index 3b006005..a997ec1d 100755 --- a/src/app/shipyard/Ship.js +++ b/src/app/shipyard/Ship.js @@ -1,14 +1,16 @@ import { ArmourMultiplier } from './Constants'; import * as Calc from './Calculations'; import * as ModuleUtils from './ModuleUtils'; +import Module from './Module'; import LZString from 'lz-string'; +import isEqual from 'lodash/lang'; const UNIQUE_MODULES = ['psg', 'sg', 'bsg', 'rf', 'fs', 'fh']; /** - * Returns the power usage type of a slot and it's particular modul + * Returns the power usage type of a slot and it's particular module * @param {Object} slot The Slot - * @param {Object} modul The modul in the slot + * @param {Object} modul The module in the slot * @return {String} The key for the power usage type */ function powerUsageType(slot, modul) { @@ -20,6 +22,25 @@ function powerUsageType(slot, modul) { return slot.cat != 1 ? 'retracted' : 'deployed'; } +/** + * Populate the modifications array with modification values from the code + * @param {String} code Serialized modification code + * @param {Array} arr Modification array + */ +function decodeModsToArray(code, arr) { + let moduleMods = code.split(','); + for (let i = 0; i < arr.length; i++) { + arr[i] = new Array(); + if (moduleMods.length > i && moduleMods[i] != '') { + let mods = moduleMods[i].split(';'); + for (let j = 0; j < mods.length; j++) { + let modElements = mods[j].split(':'); + arr[i].push({ id: Number(modElements[0]), value: Number(modElements[1]) }); + } + } + } +} + /** * Populates the category array with module IDs from * the provided code @@ -284,7 +305,9 @@ export default class Ship { '.', this.getPowerEnabledString(), '.', - this.getPowerPrioritesString() + this.getPowerPrioritiesString(), + '.', + this.getModificationsString() ].join(''); } @@ -324,6 +347,18 @@ export default class Ship { return this.serialized.hardpoints; } + + /** + * Serializes the modifications to a string + * @return {String} Serialized modifications 'code' + */ + getModificationsString() { + if(!this.serialized.modifications) { + this.updateModificationsString(); + } + return this.serialized.modifications; + } + /** * Get the serialized module active/inactive settings * @return {String} Serialized active/inactive settings @@ -336,7 +371,7 @@ export default class Ship { * Get the serialized module priority settings * @return {String} Serialized priority settings */ - getPowerPrioritesString() { + getPowerPrioritiesString() { return this.serialized.priorities; } @@ -373,9 +408,10 @@ export default class Ship { * @param {Object} comps Collection of ModuleUtils used to build the ship * @param {array} priorities Slot priorities * @param {Array} enabled Slot active/inactive + * @param {Array} mods Modifications * @return {this} The current ship instance for chaining */ - buildWith(comps, priorities, enabled) { + buildWith(comps, priorities, enabled, mods) { let internal = this.internal, standard = this.standard, hps = this.hardpoints, @@ -393,11 +429,15 @@ export default class Ship { this.totalCost = this.m.incCost ? this.m.discountedCost : 0; this.unladenMass = this.hullMass; this.totalDps = 0; + this.totalEps = 0; + this.totalHps = 0; this.bulkheads.m = null; this.useBulkhead(comps && comps.bulkheads ? comps.bulkheads : 0, true); + this.bulkheads.m.mods = mods && mods[0] ? mods[0] : []; this.cargoHatch.priority = priorities ? priorities[0] * 1 : 0; this.cargoHatch.enabled = enabled ? enabled[0] * 1 : true; + this.cargoHatch.mods = mods ? mods[0] : []; for (i = 0, l = this.priorityBands.length; i < l; i++) { this.priorityBands[i].deployed = 0; @@ -405,9 +445,10 @@ export default class Ship { } if (this.cargoHatch.enabled) { - bands[this.cargoHatch.priority].retracted += this.cargoHatch.m.power; + bands[this.cargoHatch.priority].retracted += this.cargoHatch.m.getPowerUsage(); } + for (i = 0; i < cl; i++) { standard[i].cat = 0; standard[i].enabled = enabled ? enabled[i + 1] * 1 : true; @@ -415,9 +456,10 @@ export default class Ship { standard[i].type = 'SYS'; standard[i].m = null; // Resetting 'old' modul if there was one standard[i].discountedCost = 0; - if (comps) { - this.use(standard[i], ModuleUtils.standard(i, comps.standard[i]), true); + let module = ModuleUtils.standard(i, comps.standard[i]); + if (module != null) { module.mods = mods && mods[i + 1] ? mods[i + 1] : []; } + this.use(standard[i], module, true); } } @@ -434,7 +476,9 @@ export default class Ship { hps[i].discountedCost = 0; if (comps && comps.hardpoints[i] !== 0) { - this.use(hps[i], ModuleUtils.hardpoints(comps.hardpoints[i]), true); + let module = ModuleUtils.hardpoints(comps.hardpoints[i]); + if (module != null) { module.mods = mods && mods[cl + i] ? mods[cl + i] : []; } + this.use(hps[i], module, true); } } @@ -449,7 +493,9 @@ export default class Ship { internal[i].discountedCost = 0; if (comps && comps.internal[i] !== 0) { - this.use(internal[i], ModuleUtils.internal(comps.internal[i]), true); + let module = ModuleUtils.internal(comps.internal[i]); + if (module != null) { module.mods = mods && mods[cl + i] ? mods[cl + i] : []; } + this.use(internal[i], module, true); } } @@ -461,7 +507,7 @@ export default class Ship { .updateTopSpeed(); } - return this.updatePowerPrioritesString().updatePowerEnabledString(); + return this.updatePowerPrioritesString().updatePowerEnabledString().updateModificationsString(); } /** @@ -475,6 +521,7 @@ export default class Ship { let standard = new Array(this.standard.length), hardpoints = new Array(this.hardpoints.length), internal = new Array(this.internal.length), + mods = new Array(1 + this.standard.length + this.hardpoints.length + this.internal.length), parts = serializedString.split('.'), priorities = null, enabled = null, @@ -488,6 +535,10 @@ export default class Ship { priorities = LZString.decompressFromBase64(parts[2].replace(/-/g, '/')).split(''); } + if (parts[3]) { + decodeModsToArray(parts[3], mods); + } + decodeToArray(code, internal, decodeToArray(code, hardpoints, decodeToArray(code, standard, 1))); return this.buildWith( @@ -498,7 +549,8 @@ export default class Ship { internal }, priorities, - enabled + enabled, + mods ); }; @@ -584,16 +636,23 @@ export default class Ship { if (slot.enabled != enabled) { // Enabled state is changing slot.enabled = enabled; if (slot.m) { - this.priorityBands[slot.priority][powerUsageType(slot, slot.m)] += enabled ? slot.m.power : -slot.m.power; + this.priorityBands[slot.priority][powerUsageType(slot, slot.m)] += enabled ? slot.m.getPowerUsage() : - slot.m.getPowerUsage(); if (ModuleUtils.isShieldGenerator(slot.m.grp)) { this.updateShieldStrength(); } else if (slot.m.grp == 'sb') { this.shieldMultiplier += slot.m.shieldmul * (enabled ? 1 : -1); this.updateShieldStrength(); - } else if (slot.m.dps) { + } + if (slot.m.dps) { this.totalDps += slot.m.dps * (enabled ? 1 : -1); } + if (slot.m.eps) { + this.totalEps += slot.m.eps * (enabled ? 1 : -1); + } + if (slot.m.hps) { + this.totalHps += slot.m.hps * (enabled ? 1 : -1); + } this.updatePower(); this.updatePowerEnabledString(); @@ -616,8 +675,8 @@ export default class Ship { if (slot.enabled) { // Only update power if the slot is enabled let usage = powerUsageType(slot, slot.m); - this.priorityBands[oldPriority][usage] -= slot.m.power; - this.priorityBands[newPriority][usage] += slot.m.power; + this.priorityBands[oldPriority][usage] -= slot.m.getPowerUsage(); + this.priorityBands[newPriority][usage] += slot.m.getPowerUsage(); this.updatePower(); } return true; @@ -656,15 +715,26 @@ export default class Ship { this.totalCost -= old.cost * this.moduleCostMultiplier; } - if (old.power && slot.enabled) { - this.priorityBands[slot.priority][powerUsageType(slot, old)] -= old.power; + if (!(old instanceof Module)) { + console.log(JSON.stringify(old) + ' is not a module'); + console.log(new Error().stack); + } + + if (old.getPowerUsage() > 0 && slot.enabled) { + this.priorityBands[slot.priority][powerUsageType(slot, old)] -= old.getPowerUsage(); powerChange = true; if (old.dps) { this.totalDps -= old.dps; } + if (old.eps) { + this.totalEps -= old.eps; + } + if (old.hps) { + this.totalHps -= old.hps; + } } - this.unladenMass -= old.mass || 0; + this.unladenMass -= old.getMass() || 0; } if (n) { @@ -688,14 +758,20 @@ export default class Ship { } if (n.power && slot.enabled) { - this.priorityBands[slot.priority][powerUsageType(slot, n)] += n.power; + this.priorityBands[slot.priority][powerUsageType(slot, n)] += n.getPowerUsage(); powerChange = true; if (n.dps) { this.totalDps += n.dps; } + if (n.eps) { + this.totalEps += n.eps; + } + if (n.hps) { + this.totalHps += n.hps; + } } - this.unladenMass += n.mass || 0; + this.unladenMass += n.getMass() || 0; } this.ladenMass = this.unladenMass + this.cargoCapacity + this.fuelCapacity; @@ -726,7 +802,7 @@ export default class Ship { prevDeployed = band.deployedSum = prevDeployed + band.deployed + band.retracted; } - this.powerAvailable = this.standard[0].m.pGen; + this.powerAvailable = this.standard[0].m.getPowerGeneration(); this.powerRetracted = prevRetracted; this.powerDeployed = prevDeployed; return this; @@ -811,16 +887,75 @@ export default class Ship { return this; } + /** + * Update the modifications string + * @return {this} The ship instance (for chaining operations) + */ + updateModificationsString() { + let allMods = new Array(); + + let bulkheadMods = new Array(); + if (this.bulkheads.m && this.bulkheads.m.mods) { + for (let mod of this.bulkheads.m.mods) { + bulkheadMods.push(mod.id + ':' + mod.value); + } + } + allMods.push(bulkheadMods.join(';')); + + for (let slot of this.standard) { + let slotMods = new Array(); + if (slot.m && slot.m.mods) { + for (let mod of slot.m.mods) { + slotMods.push(mod.id + ':' + mod.value); + } + } + allMods.push(slotMods.join(';')); + } + for (let slot of this.hardpoints) { + let slotMods = new Array(); + if (slot.m && slot.m.mods) { + for (let mod of slot.m.mods) { + slotMods.push(mod.id + ':' + mod.value); + } + } + allMods.push(slotMods.join(';')); + } + for (let slot of this.internal) { + let slotMods = new Array(); + if (slot.m && slot.m.mods) { + for (let mod of slot.m.mods) { + slotMods.push(mod.id + ':' + mod.value); + } + } + allMods.push(slotMods.join(';')); + } + this.serialized.modifications = allMods.join(',').replace(/,+$/, ''); + console.log('Final serialized string is ' + this.serialized.modifications); + return this; + } + /** * Update a slot with a the modul if the id is different from the current id for this slot. * Has logic handling ModuleUtils that you may only have 1 of (Shield Generator or Refinery). * * @param {Object} slot The modul slot - * @param {Object} m Properties for the selected module + * @param {Object} mdef Properties for the selected modul * @param {boolean} preventUpdate If true, do not update aggregated stats * @return {this} The ship instance (for chaining operations) */ - use(slot, m, preventUpdate) { + use(slot, mdef, preventUpdate) { + // See if the module passed in is really a module or just a definition, and fix it accordingly so that we have a module instance + let m; + if (mdef == null) { + m = null; + } else if (mdef instanceof Module) { + m = mdef; + } else { + // jgmjgm TODO see if we can use the module template instead of its group and id + // m = new Module({template: mdef}); + m = new Module({ grp: mdef.grp, id: mdef.id }); + } + if (slot.m != m) { // Selecting a different modul // Slot is an internal slot, is not being emptied, and the selected modul group/type must be of unique if (slot.cat == 2 && m && UNIQUE_MODULES.indexOf(m.grp) != -1) { @@ -843,6 +978,7 @@ export default class Ship { case 1: this.serialized.hardpoints = null; break; case 2: this.serialized.internal = null; } + this.serialized.modifications = null; } return this; } @@ -907,14 +1043,14 @@ export default class Ship { updated = false; // Find lightest Thruster that still works for the ship at max mass let th = m.th ? ModuleUtils.standard(1, m.th) : this.availCS.lightestThruster(this.ladenMass); - if (th !== standard[1].m) { + if (!isEqual.isEqual(th, standard[1].m)) { this.use(standard[1], th); updated = true; } // Find lightest Power plant that can power the ship let pp = m.pp ? ModuleUtils.standard(0, m.pp) : this.availCS.lightestPowerPlant(Math.max(this.powerRetracted, this.powerDeployed), m.ppRating); - if (pp !== standard[0].m) { + if (!isEqual.isEqual(pp, standard[0].m)) { this.use(standard[0], pp); updated = true; } diff --git a/src/app/shipyard/ShipRoles.js b/src/app/shipyard/ShipRoles.js index fc06fbe0..42b03ee6 100644 --- a/src/app/shipyard/ShipRoles.js +++ b/src/app/shipyard/ShipRoles.js @@ -115,7 +115,7 @@ export function explorer(ship, planetary) { if (sgSlot) { // The SG and Fuel scoop to not need to be powered at the same time - if (sgSlot.m.power > fuelScoopSlot.m.power) { // The Shield generator uses the most power + if (sgSlot.m.getPowerUsage() > fuelScoopSlot.m.getPowerUsage()) { // The Shield generator uses the most power ship.setSlotEnabled(fuelScoopSlot, false); } else { // The Fuel scoop uses the most power ship.setSlotEnabled(sgSlot, false); diff --git a/src/app/utils/SlotFunctions.js b/src/app/utils/SlotFunctions.js index fa63852c..f4a68822 100644 --- a/src/app/utils/SlotFunctions.js +++ b/src/app/utils/SlotFunctions.js @@ -210,14 +210,14 @@ export function diffDetails(language, m, mm) { let { formats, translate, units } = language; let propDiffs = []; let mMass = m.mass || 0; - let mmMass = mm.mass || 0; + let mmMass = mm ? mm.getMass() : 0; let massDiff = mMass - mmMass; let capDiff = (m.fuel || m.cargo || 0) - (mm.fuel || mm.cargo || 0); let mAffectsShield = isShieldGenerator(m.grp) || m.grp == 'sb'; let mmAffectsShield = isShieldGenerator(mm.grp) || mm.grp == 'sb'; propDiffs.push(
{translate('cost')}: {m.cost ? Math.round(m.cost * (1 - Persist.getModuleDiscount())) : 0}{units.CR}
); - propDiffs.push(
{translate('mass')}: {diff(formats.round, mMass, mmMass)}{units.T}
); + propDiffs.push(
{translate('mass')}: {diff(formats.round, mMass, mmMass)}{units.T}
); for (let p in m) { if (!PROP_BLACKLIST[p] && !isNaN(m[p])) { From 183f22c2230d098c3a02e9bd8a267a92690b65ee Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Mon, 24 Oct 2016 00:38:12 +0100 Subject: [PATCH 02/39] Updates to internal representation of modification information. Temporary power usage slider with standard modules for testing --- src/app/components/StandardSlot.jsx | 30 ++++++++++++++++ src/app/components/StandardSlotSection.jsx | 8 +++++ src/app/shipyard/Module.js | 42 +++++++++++++--------- src/app/shipyard/Ship.js | 36 +++++++++---------- src/app/utils/SlotFunctions.js | 3 +- 5 files changed, 83 insertions(+), 36 deletions(-) diff --git a/src/app/components/StandardSlot.jsx b/src/app/components/StandardSlot.jsx index a62e28e7..c9fa45ea 100644 --- a/src/app/components/StandardSlot.jsx +++ b/src/app/components/StandardSlot.jsx @@ -5,6 +5,7 @@ import { jumpRange } from '../shipyard/Calculations'; import { diffDetails } from '../utils/SlotFunctions'; import AvailableModulesMenu from './AvailableModulesMenu'; import { Modifications } from './SvgIcons'; +import Slider from './Slider'; /** * Standard Slot @@ -16,6 +17,7 @@ export default class StandardSlot extends TranslatedComponent { modules: React.PropTypes.array.isRequired, onSelect: React.PropTypes.func.isRequired, onOpen: React.PropTypes.func.isRequired, + onChange: React.PropTypes.func.isRequired, ship: React.PropTypes.object.isRequired, selected: React.PropTypes.bool, warning: React.PropTypes.func, @@ -66,10 +68,38 @@ export default class StandardSlot extends TranslatedComponent { { m.enginecapacity ?
{translate('ENG')}: {m.enginecapacity}{units.MJ} / {m.enginerecharge}{units.MW}
: null }
+
+ +
{menu} ); } + + /** + * Update power usage modification given a slider value. + * Note that this is a temporary function until we have a slider section + */ + _updateSliderValue(value) { + let m = this.props.slot.m; + if (m) { + m.setModValue(2, value * 2 - 1); + } + this.props.onChange(); + } + + /** + * Obtain slider value from a power usage modification. + * Note that this is a temporary function until we have a slider section + */ + _getSliderValue() { + let m = this.props.slot.m; + if (m && m.getModValue(2)) { + return (m.getModValue(2) + 1) / 2; + } + return 0; + } + } diff --git a/src/app/components/StandardSlotSection.jsx b/src/app/components/StandardSlotSection.jsx index ef6b0dc3..5426a1e6 100644 --- a/src/app/components/StandardSlotSection.jsx +++ b/src/app/components/StandardSlotSection.jsx @@ -103,6 +103,7 @@ export default class StandardSlotSection extends SlotSection { onOpen={open.bind(this, bh)} onSelect={this._selectBulkhead} selected={currentMenu == bh} + onChange={this.props.onChange} ship={ship} />; @@ -113,6 +114,7 @@ export default class StandardSlotSection extends SlotSection { onOpen={open.bind(this, st[0])} onSelect={select.bind(this, st[0])} selected={currentMenu == st[0]} + onChange={this.props.onChange} ship={ship} warning={m => m.pGen < ship.powerRetracted} />; @@ -124,6 +126,7 @@ export default class StandardSlotSection extends SlotSection { onOpen={open.bind(this, st[1])} onSelect={select.bind(this, st[1])} selected={currentMenu == st[1]} + onChange={this.props.onChange} ship={ship} warning={m => m.maxmass < (ship.ladenMass - st[1].mass + m.mass)} />; @@ -135,6 +138,7 @@ export default class StandardSlotSection extends SlotSection { modules={avail[2]} onOpen={open.bind(this, st[2])} onSelect={select.bind(this, st[2])} + onChange={this.props.onChange} ship={ship} selected={currentMenu == st[2]} />; @@ -145,6 +149,7 @@ export default class StandardSlotSection extends SlotSection { modules={avail[3]} onOpen={open.bind(this, st[3])} onSelect={select.bind(this, st[3])} + onChange={this.props.onChange} ship={ship} selected={currentMenu == st[3]} />; @@ -156,6 +161,7 @@ export default class StandardSlotSection extends SlotSection { onOpen={open.bind(this, st[4])} onSelect={select.bind(this, st[4])} selected={currentMenu == st[4]} + onChange={this.props.onChange} ship={ship} warning= {m => m.enginecapacity < ship.boostEnergy} />; @@ -167,6 +173,7 @@ export default class StandardSlotSection extends SlotSection { onOpen={open.bind(this, st[5])} onSelect={select.bind(this, st[5])} selected={currentMenu == st[5]} + onChange={this.props.onChange} ship={ship} warning= {m => m.enginecapacity < ship.boostEnergy} />; @@ -178,6 +185,7 @@ export default class StandardSlotSection extends SlotSection { onOpen={open.bind(this, st[6])} onSelect={select.bind(this, st[6])} selected={currentMenu == st[6]} + onChange={this.props.onChange} ship={ship} warning= {m => m.fuel < st[2].m.maxfuel} // Show warning when fuel tank is smaller than FSD Max Fuel />; diff --git a/src/app/shipyard/Module.js b/src/app/shipyard/Module.js index 9e301368..acf666a0 100755 --- a/src/app/shipyard/Module.js +++ b/src/app/shipyard/Module.js @@ -23,22 +23,30 @@ export default class Module { for (let p in template) { this[p] = template[p]; } } } + this.mods = {}; } /** * Get a value for a given modification ID * @param {Number} modId The ID of the modification - * @return {Number} The value of the modification + * @return {Number} The value of the modification, as a decimal value from -1 to 1 */ - _getModValue(modId) { - let result = null; - if (this.mods) { - let mod = _.find(this.mods, function(o) { return o.id == modId; }); - if (mod) { - result = mod.value; - } + getModValue(modId) { + return this.mods ? this.mods[modId] / 100000 : null; + } + + /** + * Set a value for a given modification ID + * @param {Number} modId The ID of the modification + * @param {Number} val The value of the modification, as a decimal value from -1 to 1 + */ + setModValue(modId, value) { + if (value == null || value == 0) { + delete this.mods[modId]; + } else { + // Store value with 3dp + this.mods[modId] = Math.round(value * 100000); } - return result; } /** @@ -50,8 +58,8 @@ export default class Module { if (this.pGen) { result = this.pGen; if (result) { - let mult = this._getModValue(1); - if (mult) { result = result * (1 + (mult / 1000)); } + let mult = this.getModValue(1); + if (mult) { result = result * (1 + mult); } } } return result; @@ -66,8 +74,8 @@ export default class Module { if (this.power) { result = this.power; if (result) { - let mult = this._getModValue(2); - if (mult) { result = result * (1 + (mult / 1000)); } + let mult = this.getModValue(2); + if (mult) { result = result * (1 + mult); } } } return result; @@ -82,8 +90,8 @@ export default class Module { if (this.mass) { result = this.mass; if (result) { - let mult = this._getModValue(3); - if (mult) { result = result * (1 + (mult / 1000)); } + let mult = this.getModValue(3); + if (mult) { result = result * (1 + mult); } } } return result; @@ -98,8 +106,8 @@ export default class Module { if (this.health) { result = this.health; if (result) { - let mult = this._getModValue(4); - if (mult) { result = result * (1 + (mult / 1000)); } + let mult = this.getModValue(4); + if (mult) { result = result * (1 + mult); } } } return result; diff --git a/src/app/shipyard/Ship.js b/src/app/shipyard/Ship.js index a997ec1d..5905170a 100755 --- a/src/app/shipyard/Ship.js +++ b/src/app/shipyard/Ship.js @@ -30,12 +30,12 @@ function powerUsageType(slot, modul) { function decodeModsToArray(code, arr) { let moduleMods = code.split(','); for (let i = 0; i < arr.length; i++) { - arr[i] = new Array(); + arr[i] = {}; if (moduleMods.length > i && moduleMods[i] != '') { let mods = moduleMods[i].split(';'); for (let j = 0; j < mods.length; j++) { let modElements = mods[j].split(':'); - arr[i].push({ id: Number(modElements[0]), value: Number(modElements[1]) }); + arr[i][Number(modElements[0])] = Number(modElements[1]); } } } @@ -353,9 +353,9 @@ export default class Ship { * @return {String} Serialized modifications 'code' */ getModificationsString() { - if(!this.serialized.modifications) { - this.updateModificationsString(); - } + // Modifications can be updated outside of the ship's direct knowledge, for example when sliders change the value, + // so always recreate it from scratch + this.updateModificationsString(); return this.serialized.modifications; } @@ -434,10 +434,10 @@ export default class Ship { this.bulkheads.m = null; this.useBulkhead(comps && comps.bulkheads ? comps.bulkheads : 0, true); - this.bulkheads.m.mods = mods && mods[0] ? mods[0] : []; + this.bulkheads.m.mods = mods && mods[0] ? mods[0] : {} this.cargoHatch.priority = priorities ? priorities[0] * 1 : 0; this.cargoHatch.enabled = enabled ? enabled[0] * 1 : true; - this.cargoHatch.mods = mods ? mods[0] : []; + this.cargoHatch.mods = mods ? mods[0] : {}; for (i = 0, l = this.priorityBands.length; i < l; i++) { this.priorityBands[i].deployed = 0; @@ -458,7 +458,7 @@ export default class Ship { standard[i].discountedCost = 0; if (comps) { let module = ModuleUtils.standard(i, comps.standard[i]); - if (module != null) { module.mods = mods && mods[i + 1] ? mods[i + 1] : []; } + if (module != null) { module.mods = mods && mods[i + 1] ? mods[i + 1] : {}; } this.use(standard[i], module, true); } } @@ -477,7 +477,7 @@ export default class Ship { if (comps && comps.hardpoints[i] !== 0) { let module = ModuleUtils.hardpoints(comps.hardpoints[i]); - if (module != null) { module.mods = mods && mods[cl + i] ? mods[cl + i] : []; } + if (module != null) { module.mods = mods && mods[cl + i] ? mods[cl + i] : {}; } this.use(hps[i], module, true); } } @@ -494,7 +494,7 @@ export default class Ship { if (comps && comps.internal[i] !== 0) { let module = ModuleUtils.internal(comps.internal[i]); - if (module != null) { module.mods = mods && mods[cl + i] ? mods[cl + i] : []; } + if (module != null) { module.mods = mods && mods[cl + i] ? mods[cl + i] : {}; } this.use(internal[i], module, true); } } @@ -896,8 +896,8 @@ export default class Ship { let bulkheadMods = new Array(); if (this.bulkheads.m && this.bulkheads.m.mods) { - for (let mod of this.bulkheads.m.mods) { - bulkheadMods.push(mod.id + ':' + mod.value); + for (var modKey in this.bulkheads.m.mods) { + bulkheadMods.push(modKey + ':' + Math.round(this.bulkheads.m.getModValue(modKey) * 100000)); } } allMods.push(bulkheadMods.join(';')); @@ -905,8 +905,8 @@ export default class Ship { for (let slot of this.standard) { let slotMods = new Array(); if (slot.m && slot.m.mods) { - for (let mod of slot.m.mods) { - slotMods.push(mod.id + ':' + mod.value); + for (var modKey in slot.m.mods) { + slotMods.push(modKey + ':' + Math.round(slot.m.getModValue(modKey) * 100000)); } } allMods.push(slotMods.join(';')); @@ -914,8 +914,8 @@ export default class Ship { for (let slot of this.hardpoints) { let slotMods = new Array(); if (slot.m && slot.m.mods) { - for (let mod of slot.m.mods) { - slotMods.push(mod.id + ':' + mod.value); + for (var modKey in slot.m.mods) { + slotMods.push(modKey + ':' + Math.round(slot.m.getModValue(modKey) * 100000)); } } allMods.push(slotMods.join(';')); @@ -923,8 +923,8 @@ export default class Ship { for (let slot of this.internal) { let slotMods = new Array(); if (slot.m && slot.m.mods) { - for (let mod of slot.m.mods) { - slotMods.push(mod.id + ':' + mod.value); + for (var modKey in slot.m.mods) { + slotMods.push(modKey + ':' + Math.round(slot.m.getModValue(modKey) * 100000)); } } allMods.push(slotMods.join(';')); diff --git a/src/app/utils/SlotFunctions.js b/src/app/utils/SlotFunctions.js index f4a68822..944ba617 100644 --- a/src/app/utils/SlotFunctions.js +++ b/src/app/utils/SlotFunctions.js @@ -1,6 +1,7 @@ import React from 'react'; import cn from 'classnames'; import { isShieldGenerator } from '../shipyard/ModuleUtils'; +import Module from '../shipyard/Module'; import { Infinite } from '../components/SvgIcons'; import Persist from '../stores/Persist'; @@ -206,7 +207,7 @@ function diff(format, mVal, mmVal) { * @return {React.Component} Component to be rendered */ export function diffDetails(language, m, mm) { - mm = mm || {}; + mm = mm || new Module(); let { formats, translate, units } = language; let propDiffs = []; let mMass = m.mass || 0; From adc5d1c039480d683f9a5e5a4ca066fe46a50893 Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Mon, 24 Oct 2016 18:40:12 +0100 Subject: [PATCH 03/39] Only show modifications icon if the module has valid modifications. Use no more than two decimal places for modification values. --- src/app/components/HardpointSlot.jsx | 7 +++++-- src/app/components/InternalSlot.jsx | 6 ++++-- src/app/components/StandardSlot.jsx | 12 +++++++----- src/app/components/SvgIcons.jsx | 4 ++-- src/app/shipyard/Module.js | 8 ++++---- src/app/shipyard/Ship.js | 18 +++++++++--------- 6 files changed, 31 insertions(+), 24 deletions(-) diff --git a/src/app/components/HardpointSlot.jsx b/src/app/components/HardpointSlot.jsx index 0c5773a8..c9fc6ece 100644 --- a/src/app/components/HardpointSlot.jsx +++ b/src/app/components/HardpointSlot.jsx @@ -1,6 +1,8 @@ import React from 'react'; import Slot from './Slot'; -import { DamageKinetic, DamageThermal, DamageExplosive, MountFixed, MountGimballed, MountTurret, Modifications } from './SvgIcons'; +import { DamageKinetic, DamageThermal, DamageExplosive, MountFixed, MountGimballed, MountTurret, ListModifications } from './SvgIcons'; +import { Modifications } from 'coriolis-data/dist'; + /** * Hardpoint / Utility Slot @@ -36,6 +38,7 @@ export default class HardpointSlot extends Slot { if (m) { let classRating = `${m.class}${m.rating}${m.missile ? '/' + m.missile : ''}`; let { drag, drop } = this.props; + let validMods = Modifications.validity[m.grp] || []; return
@@ -59,7 +62,7 @@ export default class HardpointSlot extends Slot { { m.range && !m.dps ?
{translate('Range')} : {formats.round(m.range / 1000)}{u.km}
: null } { m.shieldmul ?
+{formats.rPct(m.shieldmul)}
: null } { m.ammo >= 0 ?
{translate('ammo')}: {formats.int(m.clip)}/{formats.int(m.ammo)}
: null } -
+ { validMods.length > 0 ?
: null }
; } else { diff --git a/src/app/components/InternalSlot.jsx b/src/app/components/InternalSlot.jsx index fa29f27c..6dcc25f3 100644 --- a/src/app/components/InternalSlot.jsx +++ b/src/app/components/InternalSlot.jsx @@ -1,6 +1,7 @@ import React from 'react'; import Slot from './Slot'; -import { Modifications } from './SvgIcons'; +import { ListModifications } from './SvgIcons'; +import { Modifications } from 'coriolis-data/dist'; /** * Internal Slot @@ -19,6 +20,7 @@ export default class InternalSlot extends Slot { if (m) { let classRating = m.class + m.rating; let { drag, drop } = this.props; + let validMods = Modifications.validity[m.grp] || []; return
@@ -42,7 +44,7 @@ export default class InternalSlot extends Slot { { m.rangeLS === null ?
∞{u.Ls}
: null } { m.rangeRating ?
{translate('range')}: {m.rangeRating}
: null } { m.armouradd ?
+{m.armouradd} {translate('armour')}
: null } -
+ { validMods.length > 0 ?
: null }
; } else { diff --git a/src/app/components/StandardSlot.jsx b/src/app/components/StandardSlot.jsx index c9fa45ea..94e177e4 100644 --- a/src/app/components/StandardSlot.jsx +++ b/src/app/components/StandardSlot.jsx @@ -4,8 +4,9 @@ import TranslatedComponent from './TranslatedComponent'; import { jumpRange } from '../shipyard/Calculations'; import { diffDetails } from '../utils/SlotFunctions'; import AvailableModulesMenu from './AvailableModulesMenu'; -import { Modifications } from './SvgIcons'; +import { ListModifications } from './SvgIcons'; import Slider from './Slider'; +import { Modifications } from 'coriolis-data/dist'; /** * Standard Slot @@ -33,6 +34,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] || []); if (this.props.selected) { menu = {translate('WEP')}: {m.weaponcapacity}{units.MJ} / {m.weaponrecharge}{units.MW} : null } { m.systemcapacity ?
{translate('SYS')}: {m.systemcapacity}{units.MJ} / {m.systemrecharge}{units.MW}
: null } { m.enginecapacity ?
{translate('ENG')}: {m.enginecapacity}{units.MJ} / {m.enginerecharge}{units.MW}
: null } -
- -
- + { validMods.length > 0 ?
: null }
@@ -77,10 +76,12 @@ export default class StandardSlot extends TranslatedComponent { ); } + // {validMods.length > 0 ?
: null } /** * Update power usage modification given a slider value. * Note that this is a temporary function until we have a slider section + * @param {Number} value The value of the slider */ _updateSliderValue(value) { let m = this.props.slot.m; @@ -93,6 +94,7 @@ export default class StandardSlot extends TranslatedComponent { /** * Obtain slider value from a power usage modification. * Note that this is a temporary function until we have a slider section + * @return {Number} value The value of the slider */ _getSliderValue() { let m = this.props.slot.m; diff --git a/src/app/components/SvgIcons.jsx b/src/app/components/SvgIcons.jsx index 984415d1..50c13e16 100644 --- a/src/app/components/SvgIcons.jsx +++ b/src/app/components/SvgIcons.jsx @@ -489,9 +489,9 @@ export class Rocket extends SvgIcon { } /** - * Modifications (engineers) + * ListModifications (engineers) */ -export class Modifications extends SvgIcon { +export class ListModifications extends SvgIcon { /** * Overriden view box * @return {String} view box diff --git a/src/app/shipyard/Module.js b/src/app/shipyard/Module.js index acf666a0..d3377e6b 100755 --- a/src/app/shipyard/Module.js +++ b/src/app/shipyard/Module.js @@ -32,20 +32,20 @@ export default class Module { * @return {Number} The value of the modification, as a decimal value from -1 to 1 */ getModValue(modId) { - return this.mods ? this.mods[modId] / 100000 : null; + return this.mods ? this.mods[modId] / 10000 : null; } /** * Set a value for a given modification ID * @param {Number} modId The ID of the modification - * @param {Number} val The value of the modification, as a decimal value from -1 to 1 + * @param {Number} value The value of the modification, as a decimal value from -1 to 1 */ setModValue(modId, value) { if (value == null || value == 0) { delete this.mods[modId]; } else { - // Store value with 3dp - this.mods[modId] = Math.round(value * 100000); + // Store value with 2dp + this.mods[modId] = Math.round(value * 10000); } } diff --git a/src/app/shipyard/Ship.js b/src/app/shipyard/Ship.js index 5905170a..36e6fe85 100755 --- a/src/app/shipyard/Ship.js +++ b/src/app/shipyard/Ship.js @@ -434,7 +434,7 @@ export default class Ship { this.bulkheads.m = null; this.useBulkhead(comps && comps.bulkheads ? comps.bulkheads : 0, true); - this.bulkheads.m.mods = mods && mods[0] ? mods[0] : {} + this.bulkheads.m.mods = mods && mods[0] ? mods[0] : {}; this.cargoHatch.priority = priorities ? priorities[0] * 1 : 0; this.cargoHatch.enabled = enabled ? enabled[0] * 1 : true; this.cargoHatch.mods = mods ? mods[0] : {}; @@ -896,8 +896,8 @@ export default class Ship { let bulkheadMods = new Array(); if (this.bulkheads.m && this.bulkheads.m.mods) { - for (var modKey in this.bulkheads.m.mods) { - bulkheadMods.push(modKey + ':' + Math.round(this.bulkheads.m.getModValue(modKey) * 100000)); + for (let modKey in this.bulkheads.m.mods) { + bulkheadMods.push(modKey + ':' + Math.round(this.bulkheads.m.getModValue(modKey) * 10000)); } } allMods.push(bulkheadMods.join(';')); @@ -905,8 +905,8 @@ export default class Ship { for (let slot of this.standard) { let slotMods = new Array(); if (slot.m && slot.m.mods) { - for (var modKey in slot.m.mods) { - slotMods.push(modKey + ':' + Math.round(slot.m.getModValue(modKey) * 100000)); + for (let modKey in slot.m.mods) { + slotMods.push(modKey + ':' + Math.round(slot.m.getModValue(modKey) * 10000)); } } allMods.push(slotMods.join(';')); @@ -914,8 +914,8 @@ export default class Ship { for (let slot of this.hardpoints) { let slotMods = new Array(); if (slot.m && slot.m.mods) { - for (var modKey in slot.m.mods) { - slotMods.push(modKey + ':' + Math.round(slot.m.getModValue(modKey) * 100000)); + for (let modKey in slot.m.mods) { + slotMods.push(modKey + ':' + Math.round(slot.m.getModValue(modKey) * 10000)); } } allMods.push(slotMods.join(';')); @@ -923,8 +923,8 @@ export default class Ship { for (let slot of this.internal) { let slotMods = new Array(); if (slot.m && slot.m.mods) { - for (var modKey in slot.m.mods) { - slotMods.push(modKey + ':' + Math.round(slot.m.getModValue(modKey) * 100000)); + for (let modKey in slot.m.mods) { + slotMods.push(modKey + ':' + Math.round(slot.m.getModValue(modKey) * 10000)); } } allMods.push(slotMods.join(';')); From 45337913bad10097e478398118f2d6a1e8c19825 Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Tue, 25 Oct 2016 13:36:46 +0100 Subject: [PATCH 04/39] Make modifications icon clickable --- src/app/components/StandardSlot.jsx | 12 ++++++++++-- src/app/shipyard/Ship.js | 1 - 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/app/components/StandardSlot.jsx b/src/app/components/StandardSlot.jsx index 94e177e4..55d554fb 100644 --- a/src/app/components/StandardSlot.jsx +++ b/src/app/components/StandardSlot.jsx @@ -7,6 +7,7 @@ import AvailableModulesMenu from './AvailableModulesMenu'; import { ListModifications } from './SvgIcons'; import Slider from './Slider'; import { Modifications } from 'coriolis-data/dist'; +import { stopCtxPropagation } from '../utils/UtilityFunctions'; /** * Standard Slot @@ -29,6 +30,7 @@ export default class StandardSlot extends TranslatedComponent { * @return {React.Component} Slot component */ render() { + let { termtip, tooltip } = this.context; let { translate, formats, units } = this.context.language; let { modules, slot, warning, onSelect, ladenMass, ship } = this.props; let m = slot.m; @@ -49,7 +51,7 @@ export default class StandardSlot extends TranslatedComponent { } return ( -
+
{slot.maxClass}
@@ -68,7 +70,7 @@ export default class StandardSlot extends TranslatedComponent { { m.weaponcapacity ?
{translate('WEP')}: {m.weaponcapacity}{units.MJ} / {m.weaponrecharge}{units.MW}
: null } { m.systemcapacity ?
{translate('SYS')}: {m.systemcapacity}{units.MJ} / {m.systemrecharge}{units.MW}
: null } { m.enginecapacity ?
{translate('ENG')}: {m.enginecapacity}{units.MJ} / {m.enginerecharge}{units.MW}
: null } - { validMods.length > 0 ?
: null } + { validMods.length > 0 ?
: null }
@@ -78,6 +80,12 @@ export default class StandardSlot extends TranslatedComponent { } // {validMods.length > 0 ?
: null } + _showModificationsMenu(m, e) { + let validMods = m == null ? [] : (Modifications.validity[m.grp] || []); + // TODO set up the modifications + e.stopPropagation(); + } + /** * Update power usage modification given a slider value. * Note that this is a temporary function until we have a slider section diff --git a/src/app/shipyard/Ship.js b/src/app/shipyard/Ship.js index 36e6fe85..5123f087 100755 --- a/src/app/shipyard/Ship.js +++ b/src/app/shipyard/Ship.js @@ -930,7 +930,6 @@ export default class Ship { allMods.push(slotMods.join(';')); } this.serialized.modifications = allMods.join(',').replace(/,+$/, ''); - console.log('Final serialized string is ' + this.serialized.modifications); return this; } From 3114852c63cf4883b13b53d975a42365c1cff9d2 Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Thu, 27 Oct 2016 13:26:09 +0100 Subject: [PATCH 05/39] Updates for mods UI --- package.json | 1 + src/app/components/ModSlider.jsx | 55 ++++++ src/app/components/ModificationsMenu.jsx | 208 +++++++++++++++++++++++ src/app/components/ShipSummaryTable.jsx | 4 +- src/app/components/Slot.jsx | 7 + src/app/components/StandardSlot.jsx | 51 ++---- src/app/pages/OutfittingPage.jsx | 6 +- src/app/shipyard/Module.js | 20 +-- src/app/shipyard/Ship.js | 32 +++- src/less/app.less | 2 +- 10 files changed, 331 insertions(+), 55 deletions(-) create mode 100644 src/app/components/ModSlider.jsx create mode 100644 src/app/components/ModificationsMenu.jsx diff --git a/package.json b/package.json index aaf8d19e..5ce7ae59 100644 --- a/package.json +++ b/package.json @@ -89,6 +89,7 @@ "fbemitter": "^2.0.0", "lodash": "^4.15.0", "lz-string": "^1.4.4", + "react-numeric-input": "^2.0.6", "react": "^15.0.1", "react-dom": "^15.0.1", "superagent": "^1.4.0" diff --git a/src/app/components/ModSlider.jsx b/src/app/components/ModSlider.jsx new file mode 100644 index 00000000..057742c4 --- /dev/null +++ b/src/app/components/ModSlider.jsx @@ -0,0 +1,55 @@ +import React from 'react'; +import { findDOMNode } from 'react-dom'; +import Slider from './Slider'; + +const MARGIN_LR = 8; // Left/ Right margin + +/** + * Horizontal Slider for modifications + */ +export default class ModSlider extends Slider { + + /** + * Constructor + * @param {Object} props React Component properties + */ + constructor(props) { + super(props); + } + + /** + * Render the slider + * @return {React.Component} The slider + */ + render() { + let outerWidth = this.state.outerWidth; + let { axis, axisUnit, min, max, scale } = this.props; + + let style = { + width: '100%', + height: axis ? '2.5em' : '1.5em', + boxSizing: 'border-box' + }; + + if (!outerWidth) { + return ; + } + + let margin = MARGIN_LR * scale; + let width = outerWidth - (margin * 2); + let pctPos = width * this.props.percent + margin; + + // TODO add this back in from middle to point + // + return + + + + {axis && + {min + axisUnit} + {(min + max / 2) + axisUnit} + {max + axisUnit} + } + ; + } +} diff --git a/src/app/components/ModificationsMenu.jsx b/src/app/components/ModificationsMenu.jsx new file mode 100644 index 00000000..c66df4e1 --- /dev/null +++ b/src/app/components/ModificationsMenu.jsx @@ -0,0 +1,208 @@ +import React from 'react'; +import { findDOMNode } from 'react-dom'; +import NumericInput from 'react-numeric-input'; +import TranslatedComponent from './TranslatedComponent'; +import { stopCtxPropagation } from '../utils/UtilityFunctions'; +import cn from 'classnames'; +import { MountFixed, MountGimballed, MountTurret } from './SvgIcons'; +import { Modifications } from 'coriolis-data/dist'; +import ModSlider from './ModSlider'; + +const PRESS_THRESHOLD = 500; // mouse/touch down threshold + +/** + * Modifications menu + */ +export default class ModificationsMenu extends TranslatedComponent { + + static propTypes = { + ship: React.PropTypes.object.isRequired, + m: React.PropTypes.object.isRequired, + onChange: React.PropTypes.func.isRequired + }; + + /** + * Constructor + * @param {Object} props React Component properties + * @param {Object} context React Component context + */ + constructor(props, context) { + super(props); + this.state = this._initState(props, context); + } + + /** + * Initiate the list of modifications + * @param {Object} props React Component properties + * @param {Object} context React Component context + * @return {Object} list: Array of React Components + */ + _initState(props, context) { + let translate = context.language.translate; + let formats = context.language.formats; + let { m } = props; + let list = []; + + for (let modId of Modifications.validity[m.grp]) { + list.push(
+
{translate(Modifications.modifiers[modId].name)}
+ {formats.pct(m.getModValue(modId) || 0)} + +
); + } + // + + return { list }; + } + + /** + * Generate React Components for Module Group + * @param {Function} translate Translate function + * @param {Objecy} mountedModule Mounted Module + * @param {Funciton} warningFunc Warning function + * @param {number} mass Mass + * @param {function} onSelect Select/Mount callback + * @param {string} grp Group name + * @param {Array} modules Available modules + * @return {React.Component} Available Module Group contents + */ + _buildGroup(translate, mountedModule, warningFunc, mass, onSelect, grp, modules) { + let prevClass = null, prevRating = null; + let elems = []; + + for (let i = 0; i < modules.length; i++) { + let m = modules[i]; + let mount = null; + let disabled = m.maxmass && (mass + (m.mass ? m.mass : 0)) > m.maxmass; + let active = mountedModule && mountedModule === m; + let classes = cn(m.name ? 'lc' : 'c', { + warning: !disabled && warningFunc && warningFunc(m), + active, + disabled + }); + let eventHandlers; + + if (disabled || active) { + eventHandlers = {}; + } else { + let showDiff = this._showDiff.bind(this, mountedModule, m); + let select = onSelect.bind(null, m); + + eventHandlers = { + onMouseEnter: this._over.bind(this, showDiff), + onTouchStart: this._touchStart.bind(this, showDiff), + onTouchEnd: this._touchEnd.bind(this, select), + onMouseLeave: this._hideDiff, + onClick: select + }; + } + + switch(m.mount) { + case 'F': mount = ; break; + case 'G': mount = ; break; + case 'T': mount = ; break; + } + + if (i > 0 && modules.length > 3 && m.class != prevClass && (m.rating != prevRating || m.mount) && m.grp != 'pa') { + elems.push(
); + } + + elems.push( +
  • + {mount} + {(mount ? ' ' : '') + m.class + m.rating + (m.missile ? '/' + m.missile : '') + (m.name ? ' ' + translate(m.name) : '')} +
  • + ); + prevClass = m.class; + prevRating = m.rating; + } + + return
      {elems}
    ; + } + + + /** + * Touch Start - Show diff after press, otherwise treat as tap + * @param {Function} showDiff diff tooltip callback + * @param {SyntheticEvent} event Event + */ + _touchStart(showDiff, event) { + event.preventDefault(); + let rect = event.currentTarget.getBoundingClientRect(); + this.touchTimeout = setTimeout(showDiff.bind(this, rect), PRESS_THRESHOLD); + } + + /** + * Touch End - Select module on tap + * @param {Function} select Select module callback + * @param {SyntheticEvent} event Event + */ + _touchEnd(select, event) { + event.preventDefault(); + if (this.touchTimeout !== null) { // If timeout has not fired (been nulled out) yet + select(); + } + } + + /** + * Scroll to mounted (if it exists) module group on mount + */ + componentDidMount() { + if (this.groupElem) { // Scroll to currently selected group + findDOMNode(this).scrollTop = this.groupElem.offsetTop; + } + } + + /** + * Update state based on property and context changes + * @param {Object} nextProps Incoming/Next properties + * @param {Object} nextContext Incoming/Next conext + */ + componentWillReceiveProps(nextProps, nextContext) { + this.setState(this._initState(nextProps, nextContext)); + } + + + /** + * Update modification given a value. + * @param {Number} modId The ID of the modification + * @param {Number} value The value to set, in the range [0,1] + */ + _updateValue(modId, value) { + let scaledValue = (value - 0.5) * 2; + let m = this.props.m; + let ship = this.props.ship; + ship.setModification(m, modId, scaledValue); + this.props.onChange(); + } + + /** + * Obtain slider value from a modification. + * @param {Number} modId The ID of the modification + * @return {Number} value The value of the slider, in the range [0,1] + */ + _getSliderPercent(modId) { + let m = this.props.m; + if (m.getModValue(modId)) { + return (m.getModValue(modId) / 2) + 0.5; + } + return 0.5; + } + + /** + * Render the list + * @return {React.Component} List + */ + render() { + return ( +
    e.stopPropagation() } + onContextMenu={stopCtxPropagation} + > + {this.state.list} +
    + ); + } + +} diff --git a/src/app/components/ShipSummaryTable.jsx b/src/app/components/ShipSummaryTable.jsx index 7ebc8163..84aee6b8 100644 --- a/src/app/components/ShipSummaryTable.jsx +++ b/src/app/components/ShipSummaryTable.jsx @@ -92,8 +92,8 @@ export default class ShipSummaryTable extends TranslatedComponent { {sgRecover} {sgRecharge} {ship.hullMass} {u.T} - {round(ship.unladenMass)} {u.T} - {round(ship.ladenMass)} {u.T} + {int(ship.unladenMass)} {u.T} + {int(ship.ladenMass)} {u.T} {round(ship.cargoCapacity)} {u.T} {round(ship.fuelCapacity)} {u.T} {round(ship.unladenRange)} {u.LY} diff --git a/src/app/components/Slot.jsx b/src/app/components/Slot.jsx index f0d64ccb..0453e23a 100644 --- a/src/app/components/Slot.jsx +++ b/src/app/components/Slot.jsx @@ -2,6 +2,7 @@ import React from 'react'; import TranslatedComponent from './TranslatedComponent'; import cn from 'classnames'; import AvailableModulesMenu from './AvailableModulesMenu'; +import ModificationsMenu from './ModificationsMenu'; import { diffDetails } from '../utils/SlotFunctions'; import { wrapCtxMenu } from '../utils/UtilityFunctions'; @@ -94,6 +95,12 @@ export default class Slot extends TranslatedComponent { />; } + if (this.props.selected) { + menu = ; + } // TODO: implement touch dragging return ( diff --git a/src/app/components/StandardSlot.jsx b/src/app/components/StandardSlot.jsx index 55d554fb..1d15d6f4 100644 --- a/src/app/components/StandardSlot.jsx +++ b/src/app/components/StandardSlot.jsx @@ -4,8 +4,8 @@ import TranslatedComponent from './TranslatedComponent'; import { jumpRange } from '../shipyard/Calculations'; import { diffDetails } from '../utils/SlotFunctions'; import AvailableModulesMenu from './AvailableModulesMenu'; +import ModificationsMenu from './ModificationsMenu'; import { ListModifications } from './SvgIcons'; -import Slider from './Slider'; import { Modifications } from 'coriolis-data/dist'; import { stopCtxPropagation } from '../utils/UtilityFunctions'; @@ -32,7 +32,7 @@ export default class StandardSlot extends TranslatedComponent { render() { let { termtip, tooltip } = this.context; let { translate, formats, units } = this.context.language; - let { modules, slot, warning, onSelect, ladenMass, ship } = this.props; + let { modules, slot, warning, onSelect, onChange, ladenMass, ship } = this.props; let m = slot.m; let classRating = m.class + m.rating; let menu; @@ -50,13 +50,22 @@ export default class StandardSlot extends TranslatedComponent { />; } + if (this.props.selected) { + menu = ; + } + return (
    {slot.maxClass}
    {classRating} {translate(m.grp == 'bh' ? m.grp : m.name || m.grp)}
    -
    {m.getMass() || m.fuel || 0}{units.T}
    +
    {formats.round1(m.getMass()) || m.fuel || 0}{units.T}
    { m.grp == 'bh' && m.name ?
    {translate(m.name)}
    : null } @@ -70,46 +79,12 @@ export default class StandardSlot extends TranslatedComponent { { m.weaponcapacity ?
    {translate('WEP')}: {m.weaponcapacity}{units.MJ} / {m.weaponrecharge}{units.MW}
    : null } { m.systemcapacity ?
    {translate('SYS')}: {m.systemcapacity}{units.MJ} / {m.systemrecharge}{units.MW}
    : null } { m.enginecapacity ?
    {translate('ENG')}: {m.enginecapacity}{units.MJ} / {m.enginerecharge}{units.MW}
    : null } - { validMods.length > 0 ?
    : null }
    {menu}
    ); + //{ validMods.length > 0 ?
    : null } } - // {validMods.length > 0 ?
    : null } - - _showModificationsMenu(m, e) { - let validMods = m == null ? [] : (Modifications.validity[m.grp] || []); - // TODO set up the modifications - e.stopPropagation(); - } - - /** - * Update power usage modification given a slider value. - * Note that this is a temporary function until we have a slider section - * @param {Number} value The value of the slider - */ - _updateSliderValue(value) { - let m = this.props.slot.m; - if (m) { - m.setModValue(2, value * 2 - 1); - } - this.props.onChange(); - } - - /** - * Obtain slider value from a power usage modification. - * Note that this is a temporary function until we have a slider section - * @return {Number} value The value of the slider - */ - _getSliderValue() { - let m = this.props.slot.m; - if (m && m.getModValue(2)) { - return (m.getModValue(2) + 1) / 2; - } - return 0; - } - } diff --git a/src/app/pages/OutfittingPage.jsx b/src/app/pages/OutfittingPage.jsx index e4ee5633..839c6b00 100644 --- a/src/app/pages/OutfittingPage.jsx +++ b/src/app/pages/OutfittingPage.jsx @@ -284,9 +284,9 @@ export default class OutfittingPage extends Page { canSave = (newBuildName || buildName) && code !== savedCode, canRename = buildName && newBuildName && buildName != newBuildName, canReload = savedCode && canSave, - hStr = ship.getHardpointsString(), - sStr = ship.getStandardString(), - iStr = ship.getInternalString(); + hStr = ship.getHardpointsString() + '.' + ship.getModificationsString(), + sStr = ship.getStandardString() + '.' + ship.getModificationsString(), + iStr = ship.getInternalString() + '.' + ship.getModificationsString(); return (
    diff --git a/src/app/shipyard/Module.js b/src/app/shipyard/Module.js index d3377e6b..3af7768a 100755 --- a/src/app/shipyard/Module.js +++ b/src/app/shipyard/Module.js @@ -82,13 +82,13 @@ export default class Module { } /** - * Get the mass of this module, taking in to account modifications - * @return {Number} the mass of this module + * Get the integrity of this module, taking in to account modifications + * @return {Number} the integrity of this module */ - getMass() { + getIntegrity() { let result = 0; - if (this.mass) { - result = this.mass; + if (this.health) { + result = this.health; if (result) { let mult = this.getModValue(3); if (mult) { result = result * (1 + mult); } @@ -98,13 +98,13 @@ export default class Module { } /** - * Get the integrity of this module, taking in to account modifications - * @return {Number} the integrity of this module + * Get the mass of this module, taking in to account modifications + * @return {Number} the mass of this module */ - getIntegrity() { + getMass() { let result = 0; - if (this.health) { - result = this.health; + if (this.mass) { + result = this.mass; if (result) { let mult = this.getModValue(4); if (mult) { result = result * (1 + mult); } diff --git a/src/app/shipyard/Ship.js b/src/app/shipyard/Ship.js index 5123f087..50d4018f 100755 --- a/src/app/shipyard/Ship.js +++ b/src/app/shipyard/Ship.js @@ -347,7 +347,6 @@ export default class Ship { return this.serialized.hardpoints; } - /** * Serializes the modifications to a string * @return {String} Serialized modifications 'code' @@ -403,6 +402,37 @@ export default class Ship { return this; } + /** + * Set a modification value + * @param {Object} m The module to change + * @param {Object} mId The ID of the modification to change + * @param {Number} value The new value of the modification + */ + setModification(m, mId, value) { + // Handle special cases + if (mId == 1) { + // Power generation + m.setModValue(mId, value); + this.updatePower(); + } else if (mId == 2) { + // Power usage + m.setModValue(mId, value); + this.updatePower(); + } else if (mId == 4) { + // Mass + let oldMass = m.getMass(); + m.setModValue(mId, value); + let newMass = m.getMass(); + this.unladenMass = this.unladenMass - oldMass + newMass; + this.ladenMass = this.ladenMass - oldMass + newMass; + this.updateTopSpeed(); + this.updateJumpStats(); + } else { + // Generic + m.setModValue(mId, value); + } + } + /** * Builds/Updates the ship instance with the ModuleUtils[comps] passed in. * @param {Object} comps Collection of ModuleUtils used to build the ship diff --git a/src/less/app.less b/src/less/app.less index b4a5443f..7f4564ec 100755 --- a/src/less/app.less +++ b/src/less/app.less @@ -163,4 +163,4 @@ footer { float: right; text-align: right; } -} \ No newline at end of file +} From 3656c7a18f8d511fec0834a1d21e175d567fdd5b Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Thu, 27 Oct 2016 15:11:23 +0100 Subject: [PATCH 06/39] Update naming to match Elite names --- src/app/components/InternalSlotSection.jsx | 2 +- src/app/components/StandardSlotSection.jsx | 2 +- src/app/i18n/en.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/components/InternalSlotSection.jsx b/src/app/components/InternalSlotSection.jsx index f35d81e4..c33cc451 100644 --- a/src/app/components/InternalSlotSection.jsx +++ b/src/app/components/InternalSlotSection.jsx @@ -16,7 +16,7 @@ export default class InternalSlotSection extends SlotSection { * @param {Object} context React Component context */ constructor(props, context) { - super(props, context, 'internal', 'internal compartments'); + super(props, context, 'internal', 'optional internal'); this._empty = this._empty.bind(this); this._fillWithCargo = this._fillWithCargo.bind(this); diff --git a/src/app/components/StandardSlotSection.jsx b/src/app/components/StandardSlotSection.jsx index 5426a1e6..0971cf15 100644 --- a/src/app/components/StandardSlotSection.jsx +++ b/src/app/components/StandardSlotSection.jsx @@ -18,7 +18,7 @@ export default class StandardSlotSection extends SlotSection { * @param {Object} context React Component context */ constructor(props, context) { - super(props, context, 'standard', 'standard'); + super(props, context, 'standard', 'core internal'); this._optimizeStandard = this._optimizeStandard.bind(this); this._selectBulkhead = this._selectBulkhead.bind(this); } diff --git a/src/app/i18n/en.js b/src/app/i18n/en.js index c29d0436..f35115bb 100644 --- a/src/app/i18n/en.js +++ b/src/app/i18n/en.js @@ -39,7 +39,7 @@ export const terms = { cc: 'Collector Limpet Controller', cm: 'Countermeasure', cr: 'Cargo Rack', - cs: 'Cargo Scanner', + cs: 'Manifest Scanner', dc: 'Docking Computer', fc: 'Fragment Cannon', fh: 'Fighter Hangar', From 7a33bd64f8e7f3e1dad088abbb0d4ac51af64291 Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Fri, 28 Oct 2016 01:46:21 +0100 Subject: [PATCH 07/39] Updates to modifications menu --- src/app/components/ModSlider.jsx | 3 +- src/app/components/ModificationsMenu.jsx | 118 +++-------------------- 2 files changed, 16 insertions(+), 105 deletions(-) diff --git a/src/app/components/ModSlider.jsx b/src/app/components/ModSlider.jsx index 057742c4..386164f2 100644 --- a/src/app/components/ModSlider.jsx +++ b/src/app/components/ModSlider.jsx @@ -39,8 +39,9 @@ export default class ModSlider extends Slider { let width = outerWidth - (margin * 2); let pctPos = width * this.props.percent + margin; - // TODO add this back in from middle to point + // TODO add this back in from zero point to value // + // TODO fix locations for labels (min, 0, max) return diff --git a/src/app/components/ModificationsMenu.jsx b/src/app/components/ModificationsMenu.jsx index c66df4e1..e93185f8 100644 --- a/src/app/components/ModificationsMenu.jsx +++ b/src/app/components/ModificationsMenu.jsx @@ -44,10 +44,11 @@ export default class ModificationsMenu extends TranslatedComponent { let list = []; for (let modId of Modifications.validity[m.grp]) { + let modifiers = Modifications.modifiers[modId] list.push(
    -
    {translate(Modifications.modifiers[modId].name)}
    +
    {translate(modifiers.name)}
    {formats.pct(m.getModValue(modId) || 0)} - +
    ); } // @@ -55,104 +56,6 @@ export default class ModificationsMenu extends TranslatedComponent { return { list }; } - /** - * Generate React Components for Module Group - * @param {Function} translate Translate function - * @param {Objecy} mountedModule Mounted Module - * @param {Funciton} warningFunc Warning function - * @param {number} mass Mass - * @param {function} onSelect Select/Mount callback - * @param {string} grp Group name - * @param {Array} modules Available modules - * @return {React.Component} Available Module Group contents - */ - _buildGroup(translate, mountedModule, warningFunc, mass, onSelect, grp, modules) { - let prevClass = null, prevRating = null; - let elems = []; - - for (let i = 0; i < modules.length; i++) { - let m = modules[i]; - let mount = null; - let disabled = m.maxmass && (mass + (m.mass ? m.mass : 0)) > m.maxmass; - let active = mountedModule && mountedModule === m; - let classes = cn(m.name ? 'lc' : 'c', { - warning: !disabled && warningFunc && warningFunc(m), - active, - disabled - }); - let eventHandlers; - - if (disabled || active) { - eventHandlers = {}; - } else { - let showDiff = this._showDiff.bind(this, mountedModule, m); - let select = onSelect.bind(null, m); - - eventHandlers = { - onMouseEnter: this._over.bind(this, showDiff), - onTouchStart: this._touchStart.bind(this, showDiff), - onTouchEnd: this._touchEnd.bind(this, select), - onMouseLeave: this._hideDiff, - onClick: select - }; - } - - switch(m.mount) { - case 'F': mount = ; break; - case 'G': mount = ; break; - case 'T': mount = ; break; - } - - if (i > 0 && modules.length > 3 && m.class != prevClass && (m.rating != prevRating || m.mount) && m.grp != 'pa') { - elems.push(
    ); - } - - elems.push( -
  • - {mount} - {(mount ? ' ' : '') + m.class + m.rating + (m.missile ? '/' + m.missile : '') + (m.name ? ' ' + translate(m.name) : '')} -
  • - ); - prevClass = m.class; - prevRating = m.rating; - } - - return
      {elems}
    ; - } - - - /** - * Touch Start - Show diff after press, otherwise treat as tap - * @param {Function} showDiff diff tooltip callback - * @param {SyntheticEvent} event Event - */ - _touchStart(showDiff, event) { - event.preventDefault(); - let rect = event.currentTarget.getBoundingClientRect(); - this.touchTimeout = setTimeout(showDiff.bind(this, rect), PRESS_THRESHOLD); - } - - /** - * Touch End - Select module on tap - * @param {Function} select Select module callback - * @param {SyntheticEvent} event Event - */ - _touchEnd(select, event) { - event.preventDefault(); - if (this.touchTimeout !== null) { // If timeout has not fired (been nulled out) yet - select(); - } - } - - /** - * Scroll to mounted (if it exists) module group on mount - */ - componentDidMount() { - if (this.groupElem) { // Scroll to currently selected group - findDOMNode(this).scrollTop = this.groupElem.offsetTop; - } - } - /** * Update state based on property and context changes * @param {Object} nextProps Incoming/Next properties @@ -162,16 +65,20 @@ export default class ModificationsMenu extends TranslatedComponent { this.setState(this._initState(nextProps, nextContext)); } - /** * Update modification given a value. * @param {Number} modId The ID of the modification * @param {Number} value The value to set, in the range [0,1] */ _updateValue(modId, value) { - let scaledValue = (value - 0.5) * 2; let m = this.props.m; let ship = this.props.ship; + + let modifiers = Modifications.modifiers[modId]; + let max = modifiers.max || 1; + let min = modifiers.min || -1; + let scaledValue = min + ((max - min) * value); + ship.setModification(m, modId, scaledValue); this.props.onChange(); } @@ -182,11 +89,14 @@ export default class ModificationsMenu extends TranslatedComponent { * @return {Number} value The value of the slider, in the range [0,1] */ _getSliderPercent(modId) { + let modifiers = Modifications.modifiers[modId]; + let max = modifiers.max || 1; + let min = modifiers.min || -1; let m = this.props.m; if (m.getModValue(modId)) { - return (m.getModValue(modId) / 2) + 0.5; + return (m.getModValue(modId) - min) / (max - min); } - return 0.5; + return -min / (max - min); } /** From 2b464f34e58f90f79fa796bc9cb09b27acda90c1 Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Sat, 29 Oct 2016 14:14:57 +0100 Subject: [PATCH 08/39] Use react-number-editor for modifications --- package.json | 2 +- src/app/components/ModSlider.jsx | 56 ------------------------ src/app/components/ModificationsMenu.jsx | 33 +++----------- src/app/shipyard/Ship.js | 2 + 4 files changed, 8 insertions(+), 85 deletions(-) delete mode 100644 src/app/components/ModSlider.jsx diff --git a/package.json b/package.json index 5ce7ae59..addce06a 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,7 @@ "fbemitter": "^2.0.0", "lodash": "^4.15.0", "lz-string": "^1.4.4", - "react-numeric-input": "^2.0.6", + "react-number-editor": "^4.0.2", "react": "^15.0.1", "react-dom": "^15.0.1", "superagent": "^1.4.0" diff --git a/src/app/components/ModSlider.jsx b/src/app/components/ModSlider.jsx deleted file mode 100644 index 386164f2..00000000 --- a/src/app/components/ModSlider.jsx +++ /dev/null @@ -1,56 +0,0 @@ -import React from 'react'; -import { findDOMNode } from 'react-dom'; -import Slider from './Slider'; - -const MARGIN_LR = 8; // Left/ Right margin - -/** - * Horizontal Slider for modifications - */ -export default class ModSlider extends Slider { - - /** - * Constructor - * @param {Object} props React Component properties - */ - constructor(props) { - super(props); - } - - /** - * Render the slider - * @return {React.Component} The slider - */ - render() { - let outerWidth = this.state.outerWidth; - let { axis, axisUnit, min, max, scale } = this.props; - - let style = { - width: '100%', - height: axis ? '2.5em' : '1.5em', - boxSizing: 'border-box' - }; - - if (!outerWidth) { - return ; - } - - let margin = MARGIN_LR * scale; - let width = outerWidth - (margin * 2); - let pctPos = width * this.props.percent + margin; - - // TODO add this back in from zero point to value - // - // TODO fix locations for labels (min, 0, max) - return - - - - {axis && - {min + axisUnit} - {(min + max / 2) + axisUnit} - {max + axisUnit} - } - ; - } -} diff --git a/src/app/components/ModificationsMenu.jsx b/src/app/components/ModificationsMenu.jsx index e93185f8..024cf290 100644 --- a/src/app/components/ModificationsMenu.jsx +++ b/src/app/components/ModificationsMenu.jsx @@ -1,12 +1,11 @@ import React from 'react'; import { findDOMNode } from 'react-dom'; -import NumericInput from 'react-numeric-input'; import TranslatedComponent from './TranslatedComponent'; import { stopCtxPropagation } from '../utils/UtilityFunctions'; import cn from 'classnames'; import { MountFixed, MountGimballed, MountTurret } from './SvgIcons'; import { Modifications } from 'coriolis-data/dist'; -import ModSlider from './ModSlider'; +import NumberEditor from 'react-number-editor'; const PRESS_THRESHOLD = 500; // mouse/touch down threshold @@ -46,12 +45,10 @@ export default class ModificationsMenu extends TranslatedComponent { for (let modId of Modifications.validity[m.grp]) { let modifiers = Modifications.modifiers[modId] list.push(
    -
    {translate(modifiers.name)}
    - {formats.pct(m.getModValue(modId) || 0)} - +
    {translate(modifiers.name)}{' (%)'}
    +
    ); } - // return { list }; } @@ -71,34 +68,15 @@ export default class ModificationsMenu extends TranslatedComponent { * @param {Number} value The value to set, in the range [0,1] */ _updateValue(modId, value) { + let scaledValue = Math.floor(value * 100) / 10000; + let m = this.props.m; let ship = this.props.ship; - let modifiers = Modifications.modifiers[modId]; - let max = modifiers.max || 1; - let min = modifiers.min || -1; - let scaledValue = min + ((max - min) * value); - ship.setModification(m, modId, scaledValue); this.props.onChange(); } - /** - * Obtain slider value from a modification. - * @param {Number} modId The ID of the modification - * @return {Number} value The value of the slider, in the range [0,1] - */ - _getSliderPercent(modId) { - let modifiers = Modifications.modifiers[modId]; - let max = modifiers.max || 1; - let min = modifiers.min || -1; - let m = this.props.m; - if (m.getModValue(modId)) { - return (m.getModValue(modId) - min) / (max - min); - } - return -min / (max - min); - } - /** * Render the list * @return {React.Component} List @@ -114,5 +92,4 @@ export default class ModificationsMenu extends TranslatedComponent {
    ); } - } diff --git a/src/app/shipyard/Ship.js b/src/app/shipyard/Ship.js index 50d4018f..7c35e54e 100755 --- a/src/app/shipyard/Ship.js +++ b/src/app/shipyard/Ship.js @@ -566,6 +566,7 @@ export default class Ship { } if (parts[3]) { + //decodeModsToArray(LZString.decompressFromBase64(parts[3].replace(/-/g, '/')), mods); decodeModsToArray(parts[3], mods); } @@ -959,6 +960,7 @@ export default class Ship { } allMods.push(slotMods.join(';')); } + //this.serialized.modifications = LZString.compressToBase64(allMods.join(',').replace(/,+$/, '')).replace(/\//g, '-'); this.serialized.modifications = allMods.join(',').replace(/,+$/, ''); return this; } From 0df051e40f176736dacaffeeb25e62c2642a6ede Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Sat, 29 Oct 2016 23:32:52 +0100 Subject: [PATCH 09/39] Move more values to functions over direct variable access --- src/app/components/ModificationsMenu.jsx | 15 +- src/app/components/StandardSlot.jsx | 18 +- src/app/components/StandardSlotSection.jsx | 3 +- src/app/shipyard/Calculations.js | 10 +- src/app/shipyard/Module.js | 276 +++++++++++++++++---- src/app/shipyard/ModuleSet.js | 2 +- src/app/shipyard/Serializer.js | 2 +- src/app/shipyard/Ship.js | 41 +-- src/app/utils/SlotFunctions.js | 24 +- 9 files changed, 289 insertions(+), 102 deletions(-) diff --git a/src/app/components/ModificationsMenu.jsx b/src/app/components/ModificationsMenu.jsx index 024cf290..c844f253 100644 --- a/src/app/components/ModificationsMenu.jsx +++ b/src/app/components/ModificationsMenu.jsx @@ -42,11 +42,10 @@ export default class ModificationsMenu extends TranslatedComponent { let { m } = props; let list = []; - for (let modId of Modifications.validity[m.grp]) { - let modifiers = Modifications.modifiers[modId] - list.push(
    -
    {translate(modifiers.name)}{' (%)'}
    - + for (let modName of Modifications.validity[m.grp]) { + list.push(
    +
    {translate(modName)}{' (%)'}
    +
    ); } @@ -64,16 +63,16 @@ export default class ModificationsMenu extends TranslatedComponent { /** * Update modification given a value. - * @param {Number} modId The ID of the modification + * @param {Number} name The name of the modification * @param {Number} value The value to set, in the range [0,1] */ - _updateValue(modId, value) { + _updateValue(name, value) { let scaledValue = Math.floor(value * 100) / 10000; let m = this.props.m; let ship = this.props.ship; - ship.setModification(m, modId, scaledValue); + ship.setModification(m, name, scaledValue); this.props.onChange(); } diff --git a/src/app/components/StandardSlot.jsx b/src/app/components/StandardSlot.jsx index 1d15d6f4..534c1c38 100644 --- a/src/app/components/StandardSlot.jsx +++ b/src/app/components/StandardSlot.jsx @@ -69,22 +69,22 @@ export default class StandardSlot extends TranslatedComponent {
    { m.grp == 'bh' && m.name ?
    {translate(m.name)}
    : null } - { m.optmass ?
    {translate('optimal mass')}: {m.optmass}{units.T}
    : null } - { m.maxmass ?
    {translate('max mass')}: {m.maxmass}{units.T}
    : null } - { m.range ?
    {translate('range')}: {m.range}{units.km}
    : null } + { m.getOptimalMass() ?
    {translate('optimal mass')}: {m.getOptimalMass()}{units.T}
    : null } + { m.getMaxMass() ?
    {translate('max mass')}: {m.getMaxMass()}{units.T}
    : null } + { m.getRange() ?
    {translate('range')}: {m.getRange()}{units.km}
    : null } { m.time ?
    {translate('time')}: {formats.time(m.time)}
    : null } - { m.eff ?
    {translate('efficiency')}: {m.eff}
    : null } + { m.getThermalEfficiency() ?
    {translate('efficiency')}: {m.getThermalEfficiency()}
    : null } { m.getPowerGeneration() > 0 ?
    {translate('power')}: {formats.round(m.getPowerGeneration())}{units.MW}
    : null } - { m.maxfuel ?
    {translate('max')} {translate('fuel')}: {m.maxfuel}{units.T}
    : null } - { m.weaponcapacity ?
    {translate('WEP')}: {m.weaponcapacity}{units.MJ} / {m.weaponrecharge}{units.MW}
    : null } - { m.systemcapacity ?
    {translate('SYS')}: {m.systemcapacity}{units.MJ} / {m.systemrecharge}{units.MW}
    : null } - { m.enginecapacity ?
    {translate('ENG')}: {m.enginecapacity}{units.MJ} / {m.enginerecharge}{units.MW}
    : null } + { m.getMaxFuelPerJump() ?
    {translate('max')} {translate('fuel')}: {m.getMaxFuelPerJump()}{units.T}
    : null } + { m.getWeaponsCapacity() ?
    {translate('WEP')}: {m.getWeaponsCapacity()}{units.MJ} / {m.getWeaponsRechargeRate()}{units.MW}
    : null } + { m.getSystemsCapacity() ?
    {translate('SYS')}: {m.getSystemsCapacity()}{units.MJ} / {m.getSystemsRechargeRate()}{units.MW}
    : null } + { m.getEnginesCapacity() ?
    {translate('ENG')}: {m.getEnginesCapacity()}{units.MJ} / {m.getEnginesRechargeRate()}{units.MW}
    : null }
    {menu}
    ); - //{ validMods.length > 0 ?
    : null } + // { validMods.length > 0 ?
    : null } } } diff --git a/src/app/components/StandardSlotSection.jsx b/src/app/components/StandardSlotSection.jsx index 0971cf15..6e60046b 100644 --- a/src/app/components/StandardSlotSection.jsx +++ b/src/app/components/StandardSlotSection.jsx @@ -163,7 +163,7 @@ export default class StandardSlotSection extends SlotSection { selected={currentMenu == st[4]} onChange={this.props.onChange} ship={ship} - warning= {m => m.enginecapacity < ship.boostEnergy} + warning= {m => m.getEnginesCapacity() < ship.boostEnergy} />; slots[6] = m.enginecapacity < ship.boostEnergy} />; slots[7] = 0 ? Math.pow(fuelRemaining / fsd.fuelmul, 1 / fsd.fuelpower) * fsd.optmass / mass : 0; + let fastestRange = fuelRemaining > 0 ? Math.pow(fuelRemaining / fsd.fuelmul, 1 / fsd.fuelpower) * fsd.getOptimalMass() / mass : 0; // For each max fuel jump, calculate the max jump range based on fuel mass left in the tank for (let j = 0; j < jumps; j++) { mass += fsd.maxfuel; - fastestRange += Math.pow(fsd.maxfuel / fsd.fuelmul, 1 / fsd.fuelpower) * fsd.optmass / mass; + fastestRange += Math.pow(fsd.getMaxFuelPerJump() / fsd.fuelmul, 1 / fsd.fuelpower) * fsd.getOptimalMass() / mass; } return fastestRange; }; diff --git a/src/app/shipyard/Module.js b/src/app/shipyard/Module.js index 3af7768a..9010e61c 100755 --- a/src/app/shipyard/Module.js +++ b/src/app/shipyard/Module.js @@ -27,42 +27,50 @@ export default class Module { } /** - * Get a value for a given modification ID - * @param {Number} modId The ID of the modification - * @return {Number} The value of the modification, as a decimal value from -1 to 1 + * Get a value for a given modification + * @param {Number} name The name of the modification + * @return {Number} The value of the modification, as a decimal value from -1 to 1 */ - getModValue(modId) { - return this.mods ? this.mods[modId] / 10000 : null; + getModValue(name) { + return this.mods ? this.mods[name] / 10000 : null; } /** * Set a value for a given modification ID - * @param {Number} modId The ID of the modification - * @param {Number} value The value of the modification, as a decimal value from -1 to 1 + * @param {Number} name The name of the modification + * @param {Number} value The value of the modification, as a decimal value from -1 to 1 */ - setModValue(modId, value) { + setModValue(name, value) { if (value == null || value == 0) { - delete this.mods[modId]; + delete this.mods[name]; } else { // Store value with 2dp - this.mods[modId] = Math.round(value * 10000); + this.mods[name] = Math.round(value * 10000); } } + /** + * Helper to obtain a modified value using standard multipliers + * @param {String} name the name of the modifier to obtain + * @return {Number} the mass of this module + */ + _getModifiedValue(name) { + let result = 0; + if (this[name]) { + result = this[name]; + if (result) { + let mult = this.getModValue(name); + if (mult) { result = result * (1 + mult); } + } + } + return result; + } /** * Get the power generation of this module, taking in to account modifications * @return {Number} the power generation of this module */ getPowerGeneration() { - let result = 0; - if (this.pGen) { - result = this.pGen; - if (result) { - let mult = this.getModValue(1); - if (mult) { result = result * (1 + mult); } - } - } - return result; + return this._getModifiedValue('pGen'); } /** @@ -70,15 +78,7 @@ export default class Module { * @return {Number} the power usage of this module */ getPowerUsage() { - let result = 0; - if (this.power) { - result = this.power; - if (result) { - let mult = this.getModValue(2); - if (mult) { result = result * (1 + mult); } - } - } - return result; + return this._getModifiedValue('power'); } /** @@ -86,15 +86,7 @@ export default class Module { * @return {Number} the integrity of this module */ getIntegrity() { - let result = 0; - if (this.health) { - result = this.health; - if (result) { - let mult = this.getModValue(3); - if (mult) { result = result * (1 + mult); } - } - } - return result; + return this._getModifiedValue('integrity'); } /** @@ -102,14 +94,206 @@ export default class Module { * @return {Number} the mass of this module */ getMass() { - let result = 0; - if (this.mass) { - result = this.mass; - if (result) { - let mult = this.getModValue(4); - if (mult) { result = result * (1 + mult); } - } - } - return result; + return this._getModifiedValue('mass'); + } + + /** + * Get the thermal efficiency of this module, taking in to account modifications + * @return {Number} the thermal efficiency of this module + */ + getThermalEfficiency() { + return this._getModifiedValue('eff'); + } + + /** + * Get the maximum mass of this module, taking in to account modifications + * @return {Number} the maximum mass of this module + */ + getMaxMass() { + return this._getModifiedValue('maxmass'); + } + + /** + * Get the optimal mass of this module, taking in to account modifications + * @return {Number} the optimal mass of this module + */ + getOptimalMass() { + return this._getModifiedValue('optmass'); + } + + /** + * Get the optimal multiplier of this module, taking in to account modifications + * @return {Number} the optimal multiplier of this module + */ + getOptimalMultiplier() { + return this._getModifiedValue('optmult'); + } + + /** + * Get the damage per second for this module, taking in to account modifications + * @return {Number} the damage per second of this module + */ + getDamagePerSecond() { + return this._getModifiedValue('dps'); + } + + /** + * Get the energy per second for this module, taking in to account modifications + * @return {Number} the energy per second of this module + */ + getEnergyPerSecond() { + return this._getModifiedValue('eps'); + } + + /** + * Get the heat per second for this module, taking in to account modifications + * @return {Number} the heat per second of this module + */ + getHeatPerSecond() { + return this._getModifiedValue('hps'); + } + + /** + * Get the maximum fuel per jump for this module, taking in to account modifications + * @return {Number} the maximum fuel per jump of this module + */ + getMaxFuelPerJump() { + return this._getModifiedValue('maxfuel'); + } + + /** + * Get the systems capacity for this module, taking in to account modifications + * @return {Number} the systems capacity of this module + */ + getSystemsCapacity() { + return this._getModifiedValue('syscap'); + } + + /** + * Get the engines capacity for this module, taking in to account modifications + * @return {Number} the engines capacity of this module + */ + getEnginesCapacity() { + return this._getModifiedValue('engcap'); + } + + /** + * Get the weapons capacity for this module, taking in to account modifications + * @return {Number} the weapons capacity of this module + */ + getWeaponsCapacity() { + return this._getModifiedValue('wepcap'); + } + + /** + * Get the systems recharge rate for this module, taking in to account modifications + * @return {Number} the systems recharge rate of this module + */ + getSystemsRechargeRate() { + return this._getModifiedValue('sysrate'); + } + + /** + * Get the engines recharge rate for this module, taking in to account modifications + * @return {Number} the engines recharge rate of this module + */ + getEnginesRechargeRate() { + return this._getModifiedValue('engrate'); + } + + /** + * Get the weapons recharge rate for this module, taking in to account modifications + * @return {Number} the weapons recharge rate of this module + */ + getWeaponsRechargeRate() { + return this._getModifiedValue('weprate'); + } + + /** + * Get the kinetic resistance for this module, taking in to account modifications + * @return {Number} the kinetic resistance of this module + */ + getKineticResistance() { + return this._getModifiedValue('kinres'); + } + + /** + * Get the thermal resistance for this module, taking in to account modifications + * @return {Number} the thermal resistance of this module + */ + getThermalResistance() { + return this._getModifiedValue('thermres'); + } + + /** + * Get the explosive resistance for this module, taking in to account modifications + * @return {Number} the explosive resistance of this module + */ + getExplosiveResistance() { + return this._getModifiedValue('explres'); + } + + /** + * Get the regeneration rate for this module, taking in to account modifications + * @return {Number} the regeneration rate of this module + */ + getRegenerationRate() { + return this._getModifiedValue('regen'); + } + + /** + * Get the broken regeneration rate for this module, taking in to account modifications + * @return {Number} the broken regeneration rate of this module + */ + getBrokenRegenerationRate() { + return this._getModifiedValue('brokenregen'); + } + + /** + * Get the range rate for this module, taking in to account modifications + * @return {Number} the range rate of this module + */ + getRange() { + return this._getModifiedValue('range'); + } + + /** + * Get the capture arc for this module, taking in to account modifications + * @return {Number} the capture arc of this module + */ + getCaptureArc() { + return this._getModifiedValue('arc'); + } + + /** + * Get the armour for this module, taking in to account modifications + * @return {Number} the armour of this module + */ + getArmour() { + return this._getModifiedValue('armour'); + } + + /** + * Get the delay for this module, taking in to account modifications + * @return {Number} the delay of this module + */ + getDelay() { + return this._getModifiedValue('delay'); + } + + /** + * Get the duration for this module, taking in to account modifications + * @return {Number} the duration of this module + */ + getDuration() { + return this._getModifiedValue('duration'); + } + + /** + * Get the shield reinforcement for this module, taking in to account modifications + * @return {Number} the shield reinforcement of this module + */ + getShieldReinforcement() { + return this._getModifiedValue('shieldreinforcement'); } } diff --git a/src/app/shipyard/ModuleSet.js b/src/app/shipyard/ModuleSet.js index a9148a57..b3ddf314 100755 --- a/src/app/shipyard/ModuleSet.js +++ b/src/app/shipyard/ModuleSet.js @@ -126,7 +126,7 @@ export default class ModuleSet { let pd = this.standard[4][0]; for (let p of this.standard[4]) { - if (p.mass < pd.mass && p.enginecapacity >= boostEnergy) { + if (p.mass < pd.mass && p.getEnginesCapacity() >= boostEnergy) { pd = p; } } diff --git a/src/app/shipyard/Serializer.js b/src/app/shipyard/Serializer.js index 99c47323..7b8284be 100644 --- a/src/app/shipyard/Serializer.js +++ b/src/app/shipyard/Serializer.js @@ -76,7 +76,7 @@ export function toDetailedBuild(buildName, ship) { ship: ship.name, references: [{ name: 'Coriolis.io', - url: `https://coriolis.io/outfit/${ship.id}/${code}?bn=${encodeURIComponent(buildName)}`, + url: `https://coriolis.edcd.io/outfit/${ship.id}/${code}?bn=${encodeURIComponent(buildName)}`, code, shipId: ship.id }], diff --git a/src/app/shipyard/Ship.js b/src/app/shipyard/Ship.js index 7c35e54e..9074f96f 100755 --- a/src/app/shipyard/Ship.js +++ b/src/app/shipyard/Ship.js @@ -35,7 +35,7 @@ function decodeModsToArray(code, arr) { let mods = moduleMods[i].split(';'); for (let j = 0; j < mods.length; j++) { let modElements = mods[j].split(':'); - arr[i][Number(modElements[0])] = Number(modElements[1]); + arr[i][modElements[0]] = Number(modElements[1]); } } } @@ -135,7 +135,7 @@ export default class Ship { */ canThrust() { return this.getSlotStatus(this.standard[1]) == 3 && // Thrusters are powered - this.ladenMass < this.standard[1].m.maxmass; // Max mass not exceeded + this.ladenMass < this.standard[1].m.getMaxMass(); // Max mass not exceeded } /** @@ -143,9 +143,9 @@ export default class Ship { * @return {[type]} True if boost capable */ canBoost() { - return this.canThrust() && // Thrusters operational - this.getSlotStatus(this.standard[4]) == 3 && // Power distributor operational - this.boostEnergy <= this.standard[4].m.enginecapacity; // PD capacitor is sufficient for boost + return this.canThrust() && // Thrusters operational + this.getSlotStatus(this.standard[4]) == 3 && // Power distributor operational + this.boostEnergy <= this.standard[4].m.getEnginesCapacity(); // PD capacitor is sufficient for boost } /** @@ -179,7 +179,7 @@ export default class Ship { */ calcUnladenRange(massDelta, fuel, fsd) { fsd = fsd || this.standard[2].m; - return Calc.jumpRange(this.unladenMass + (massDelta || 0) + Math.min(fsd.maxfuel, fuel || this.fuelCapacity), fsd || this.standard[2].m, fuel); + return Calc.jumpRange(this.unladenMass + (massDelta || 0) + Math.min(fsd.getMaxFuelPerJump(), fuel || this.fuelCapacity), fsd || this.standard[2].m, fuel); } /** @@ -405,31 +405,37 @@ export default class Ship { /** * Set a modification value * @param {Object} m The module to change - * @param {Object} mId The ID of the modification to change + * @param {Object} name The name of the modification to change * @param {Number} value The new value of the modification */ - setModification(m, mId, value) { + setModification(m, name, value) { // Handle special cases - if (mId == 1) { + if (name == 'pGen') { // Power generation - m.setModValue(mId, value); + m.setModValue(name, value); this.updatePower(); - } else if (mId == 2) { + } else if (name == 'power') { // Power usage - m.setModValue(mId, value); + m.setModValue(name, value); this.updatePower(); - } else if (mId == 4) { + } else if (name == 'mass') { // Mass let oldMass = m.getMass(); - m.setModValue(mId, value); + m.setModValue(name, value); let newMass = m.getMass(); this.unladenMass = this.unladenMass - oldMass + newMass; this.ladenMass = this.ladenMass - oldMass + newMass; this.updateTopSpeed(); this.updateJumpStats(); + } else if (name == 'maxfuel') { + m.setModValue(name, value); + this.updateJumpStats(); + } else if (name == 'optmass') { + m.setModValue(name, value); + this.updateJumpStats(); } else { // Generic - m.setModValue(mId, value); + m.setModValue(name, value); } } @@ -566,7 +572,7 @@ export default class Ship { } if (parts[3]) { - //decodeModsToArray(LZString.decompressFromBase64(parts[3].replace(/-/g, '/')), mods); + // decodeModsToArray(LZString.decompressFromBase64(parts[3].replace(/-/g, '/')), mods); decodeModsToArray(parts[3], mods); } @@ -960,7 +966,7 @@ export default class Ship { } allMods.push(slotMods.join(';')); } - //this.serialized.modifications = LZString.compressToBase64(allMods.join(',').replace(/,+$/, '')).replace(/\//g, '-'); + // this.serialized.modifications = LZString.compressToBase64(allMods.join(',').replace(/,+$/, '')).replace(/\//g, '-'); this.serialized.modifications = allMods.join(',').replace(/,+$/, ''); return this; } @@ -1132,5 +1138,4 @@ export default class Ship { } return this; } - } diff --git a/src/app/utils/SlotFunctions.js b/src/app/utils/SlotFunctions.js index 944ba617..3ea9681c 100644 --- a/src/app/utils/SlotFunctions.js +++ b/src/app/utils/SlotFunctions.js @@ -123,12 +123,12 @@ const PROP_BLACKLIST = { mass: 1, cost: 1, recover: 1, - weaponcapacity: 1, - weaponrecharge: 1, - enginecapacity: 1, - enginerecharge: 1, - systemcapacity: 1, - systemrecharge: 1, + wepcap: 1, + weprate: 1, + engcap: 1, + engrate: 1, + syscap: 1, + sysrate: 1, breachdps: 1, breachmin: 1, breachmax: 1, @@ -251,21 +251,21 @@ export function diffDetails(language, m, mm) { if (m.grp == 'pd') { propDiffs.push(
    {`${translate('WEP')}: `} - {m.weaponcapacity}{units.MJ} + {m.getWeaponsCapacity()}{units.MJ} {' / '} - {m.weaponrecharge}{units.MW} + {m.getWeaponsRechargeRate()}{units.MW}
    ); propDiffs.push(
    {`${translate('SYS')}: `} - {m.systemcapacity}{units.MJ} + {m.getSystemsCapacity()}{units.MJ} {' / '} - {m.systemrecharge}{units.MW} + {m.getSystemsRechargeRate()}{units.MW}
    ); propDiffs.push(
    {`${translate('ENG')}: `} - {m.enginecapacity}{units.MJ} + {m.getEnginesCapacity()}{units.MJ} {' / '} - {m.enginerecharge}{units.MW} + {m.getEnginesRechargeRate()}{units.MW}
    ); } From 4b14f617ec229c09f5510ab0df647e494335f2a4 Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Sun, 30 Oct 2016 20:54:36 +0000 Subject: [PATCH 10/39] Changes for mods --- src/app/components/HardpointSlot.jsx | 4 +- src/app/components/InternalSlot.jsx | 5 +- src/app/components/InternalSlotSection.jsx | 1 + src/app/components/Slot.jsx | 57 ++++++++----- src/app/components/StandardSlot.jsx | 55 ++++++++----- src/app/i18n/en.js | 40 ++++++++- src/app/shipyard/Calculations.js | 77 +++++++++++------- src/app/shipyard/Module.js | 94 ++++++++++++++++++++++ src/app/shipyard/Ship.js | 10 ++- 9 files changed, 270 insertions(+), 73 deletions(-) diff --git a/src/app/components/HardpointSlot.jsx b/src/app/components/HardpointSlot.jsx index c9fc6ece..18436ec1 100644 --- a/src/app/components/HardpointSlot.jsx +++ b/src/app/components/HardpointSlot.jsx @@ -2,6 +2,7 @@ import React from 'react'; import Slot from './Slot'; import { DamageKinetic, DamageThermal, DamageExplosive, MountFixed, MountGimballed, MountTurret, ListModifications } from './SvgIcons'; import { Modifications } from 'coriolis-data/dist'; +import { stopCtxPropagation } from '../utils/UtilityFunctions'; /** @@ -38,6 +39,7 @@ export default class HardpointSlot extends Slot { if (m) { 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] || []; return
    @@ -62,7 +64,7 @@ export default class HardpointSlot extends Slot { { m.range && !m.dps ?
    {translate('Range')} : {formats.round(m.range / 1000)}{u.km}
    : null } { m.shieldmul ?
    +{formats.rPct(m.shieldmul)}
    : null } { m.ammo >= 0 ?
    {translate('ammo')}: {formats.int(m.clip)}/{formats.int(m.ammo)}
    : null } - { validMods.length > 0 ?
    : null } + { m && validMods.length > 0 ?
    : null }
    ; } else { diff --git a/src/app/components/InternalSlot.jsx b/src/app/components/InternalSlot.jsx index 6dcc25f3..84fbc1c5 100644 --- a/src/app/components/InternalSlot.jsx +++ b/src/app/components/InternalSlot.jsx @@ -2,6 +2,7 @@ import React from 'react'; import Slot from './Slot'; import { ListModifications } from './SvgIcons'; import { Modifications } from 'coriolis-data/dist'; +import { stopCtxPropagation } from '../utils/UtilityFunctions'; /** * Internal Slot @@ -20,6 +21,7 @@ export default class InternalSlot extends Slot { if (m) { let classRating = m.class + m.rating; let { drag, drop } = this.props; + let { termtip, tooltip } = this.context; let validMods = Modifications.validity[m.grp] || []; return
    @@ -44,7 +46,8 @@ export default class InternalSlot extends Slot { { m.rangeLS === null ?
    ∞{u.Ls}
    : null } { m.rangeRating ?
    {translate('range')}: {m.rangeRating}
    : null } { m.armouradd ?
    +{m.armouradd} {translate('armour')}
    : null } - { validMods.length > 0 ?
    : null } + { m.passengers ?
    {translate('passengers')}: {m.passengers}
    : null } + { m && validMods.length > 0 ?
    : null }
    ; } else { diff --git a/src/app/components/InternalSlotSection.jsx b/src/app/components/InternalSlotSection.jsx index c33cc451..66beab58 100644 --- a/src/app/components/InternalSlotSection.jsx +++ b/src/app/components/InternalSlotSection.jsx @@ -110,6 +110,7 @@ export default class InternalSlotSection extends SlotSection { maxClass={s.maxClass} availableModules={() => availableModules.getInts(ship, s.maxClass, s.eligible)} onOpen={this._openMenu.bind(this,s)} + onChange={this.props.onChange} onSelect={this._selectModule.bind(this, s)} selected={currentMenu == s} enabled={s.enabled} diff --git a/src/app/components/Slot.jsx b/src/app/components/Slot.jsx index 0453e23a..6310ac15 100644 --- a/src/app/components/Slot.jsx +++ b/src/app/components/Slot.jsx @@ -3,8 +3,11 @@ import TranslatedComponent from './TranslatedComponent'; import cn from 'classnames'; import AvailableModulesMenu from './AvailableModulesMenu'; import ModificationsMenu from './ModificationsMenu'; +import { Modifications } from 'coriolis-data/dist'; +import { ListModifications } from './SvgIcons'; import { diffDetails } from '../utils/SlotFunctions'; import { wrapCtxMenu } from '../utils/UtilityFunctions'; +import { stopCtxPropagation } from '../utils/UtilityFunctions'; /** * Abstract Slot @@ -32,6 +35,8 @@ export default class Slot extends TranslatedComponent { constructor(props) { super(props); + this._modificationsSelected = false; + this._contextMenu = wrapCtxMenu(this._contextMenu.bind(this)); this._getMaxClassLabel = this._getMaxClassLabel.bind(this); } @@ -73,9 +78,16 @@ export default class Slot extends TranslatedComponent { */ render() { let language = this.context.language; + let { termtip, tooltip } = this.context; let translate = language.translate; - let { ship, m, dropClass, dragOver, onOpen, selected, onSelect, warning, shipMass, availableModules } = this.props; + let { ship, m, dropClass, dragOver, onOpen, onChange, selected, onSelect, warning, shipMass, availableModules } = this.props; let slotDetails, menu; + let validMods = m == null ? [] : (Modifications.validity[m.grp] || []); + + if (!selected) { + // If not selected then sure that modifications flag is unset + this._modificationsSelected = false; + } if (m) { slotDetails = this._getSlotDetails(m, translate, language.formats, language.units); // Must be implemented by sub classes @@ -83,34 +95,41 @@ export default class Slot extends TranslatedComponent { slotDetails =
    {translate('empty')}
    ; } - if (this.props.selected) { - menu = ; + if (selected) { + if (this._modificationsSelected) { + menu = ; + } else { + menu = ; + } } - if (this.props.selected) { - menu = ; - } // TODO: implement touch dragging return (
    {this._getMaxClassLabel(translate)}
    - {slotDetails} -
    + {slotDetails} +
    {menu} ); } + + _toggleModifications(event) { + this._modificationsSelected = !this._modificationsSelected; + } } diff --git a/src/app/components/StandardSlot.jsx b/src/app/components/StandardSlot.jsx index 534c1c38..49ae73fd 100644 --- a/src/app/components/StandardSlot.jsx +++ b/src/app/components/StandardSlot.jsx @@ -25,6 +25,11 @@ export default class StandardSlot extends TranslatedComponent { warning: React.PropTypes.func, }; + constructor(props) { + super(props); + this._modificationsSelected = false; + } + /** * Render the slot * @return {React.Component} Slot component @@ -32,31 +37,36 @@ export default class StandardSlot extends TranslatedComponent { render() { let { termtip, tooltip } = this.context; let { translate, formats, units } = this.context.language; - let { modules, slot, warning, onSelect, onChange, ladenMass, ship } = this.props; + let { modules, slot, selected, warning, onSelect, onChange, ladenMass, ship } = this.props; let m = slot.m; let classRating = m.class + m.rating; let menu; let validMods = m == null ? [] : (Modifications.validity[m.grp] || []); - if (this.props.selected) { - menu = ; + if (!selected) { + // If not selected then sure that modifications flag is unset + this._modificationsSelected = false; } - if (this.props.selected) { - menu = ; + if (selected) { + if (this._modificationsSelected) { + menu = ; + } else { + menu = ; + } } return ( @@ -74,17 +84,22 @@ export default class StandardSlot extends TranslatedComponent { { m.getRange() ?
    {translate('range')}: {m.getRange()}{units.km}
    : null } { m.time ?
    {translate('time')}: {formats.time(m.time)}
    : null } { m.getThermalEfficiency() ?
    {translate('efficiency')}: {m.getThermalEfficiency()}
    : null } - { m.getPowerGeneration() > 0 ?
    {translate('power')}: {formats.round(m.getPowerGeneration())}{units.MW}
    : null } + { m.getPowerGeneration() > 0 ?
    {translate('pGen')}: {formats.round(m.getPowerGeneration())}{units.MW}
    : null } { m.getMaxFuelPerJump() ?
    {translate('max')} {translate('fuel')}: {m.getMaxFuelPerJump()}{units.T}
    : null } { m.getWeaponsCapacity() ?
    {translate('WEP')}: {m.getWeaponsCapacity()}{units.MJ} / {m.getWeaponsRechargeRate()}{units.MW}
    : null } { m.getSystemsCapacity() ?
    {translate('SYS')}: {m.getSystemsCapacity()}{units.MJ} / {m.getSystemsRechargeRate()}{units.MW}
    : null } { m.getEnginesCapacity() ?
    {translate('ENG')}: {m.getEnginesCapacity()}{units.MJ} / {m.getEnginesRechargeRate()}{units.MW}
    : null } + + { validMods.length > 0 ?
    : null } {menu} ); - // { validMods.length > 0 ?
    : null } + } + + _toggleModifications() { + this._modificationsSelected = !this._modificationsSelected; } } diff --git a/src/app/i18n/en.js b/src/app/i18n/en.js index f35115bb..ce8a535f 100644 --- a/src/app/i18n/en.js +++ b/src/app/i18n/en.js @@ -78,5 +78,43 @@ export const terms = { t: 'thrusters', tp: 'Torpedo Pylon', ul: 'Burst Laser', - ws: 'Frame Shift Wake Scanner' + ws: 'Frame Shift Wake Scanner', + + // Modifications + mass: 'Mass', + integrity: 'Integrity', + pGen: 'Power generation', + eff: 'Efficiency', + power: 'Power draw', + optmass: 'Optimal mass', + optmul: 'Optimal multiplier', + dps: 'Damage per second', + eps: 'Energy per second', + thermload: 'Thermal load', + boot: 'Boot time', + maxfuel: 'Maximum fuel per jump', + syscap: 'Systems capacity', + engcap: 'Engines capacity', + wepcap: 'Weapons capacity', + sysrate: 'Systems recharge rate', + engrate: 'Engines recharge rate', + weprate: 'Weapons recharge rate', + kinres: 'Kinetic resistance', + thermres: 'Thermal resistance', + explres: 'Explosive resistance', + regen: 'Regeneration rate', + brokenregen: 'Broken regeneration rate', + delay: 'Delay', + duration: 'Duration', + shield: 'Shield', + shieldboost: 'Shield boost', + distdraw: 'Distributor draw', + damage: 'Damage', + armourpen: 'Armour penetration', + range: 'Range', + rof: 'Rate of fire', + clip: 'Ammunition clip', + ammo: 'Ammunition maximum', + jitter: 'Jitter', + reload: 'Reload time' }; diff --git a/src/app/shipyard/Calculations.js b/src/app/shipyard/Calculations.js index 16beefb4..6fa9c88d 100644 --- a/src/app/shipyard/Calculations.js +++ b/src/app/shipyard/Calculations.js @@ -1,3 +1,4 @@ +import Module from './Module'; /** * Calculate the maximum single jump range based on mass and a specific FSD @@ -8,7 +9,9 @@ * @return {number} Distance in Light Years */ export function jumpRange(mass, fsd, fuel) { - return Math.pow(Math.min(fuel === undefined ? fsd.getMaxFuelPerJump() : fuel, fsd.getMaxFuelPerJump()) / fsd.fuelmul, 1 / fsd.fuelpower) * fsd.getOptimalMass() / mass; + let fsdMaxFuelPerJump = fsd instanceof Module ? fsd.getMaxFuelPerJump() : fsd.maxfuel; + let fsdOptimalMass = fsd instanceof Module ? fsd.getOptimalMass() : fsd.optmass; + return Math.pow(Math.min(fuel === undefined ? fsdMaxFuelPerJump : fuel, fsdMaxFuelPerJump) / fsd.fuelmul, 1 / fsd.fuelpower) * fsdOptimalMass / mass; } /** @@ -20,15 +23,17 @@ export function jumpRange(mass, fsd, fuel) { * @return {number} Distance in Light Years */ export function fastestRange(mass, fsd, fuel) { - let fuelRemaining = fuel % fsd.getMaxFuelPerJump(); // Fuel left after making N max jumps - let jumps = Math.floor(fuel / fsd.getMaxFuelPerJump()); + let fsdMaxFuelPerJump = fsd instanceof Module ? fsd.getMaxFuelPerJump() : fsd.maxfuel; + let fsdOptimalMass = fsd instanceof Module ? fsd.getOptimalMass() : fsd.optmass; + let fuelRemaining = fuel % fsdMaxFuelPerJump; // Fuel left after making N max jumps + let jumps = Math.floor(fuel / fsdMaxFuelPerJump); mass += fuelRemaining; // Going backwards, start with the last jump using the remaining fuel - let fastestRange = fuelRemaining > 0 ? Math.pow(fuelRemaining / fsd.fuelmul, 1 / fsd.fuelpower) * fsd.getOptimalMass() / mass : 0; + let fastestRange = fuelRemaining > 0 ? Math.pow(fuelRemaining / fsd.fuelmul, 1 / fsd.fuelpower) * fsdOptimalMass / mass : 0; // For each max fuel jump, calculate the max jump range based on fuel mass left in the tank for (let j = 0; j < jumps; j++) { mass += fsd.maxfuel; - fastestRange += Math.pow(fsd.getMaxFuelPerJump() / fsd.fuelmul, 1 / fsd.fuelpower) * fsd.getOptimalMass() / mass; + fastestRange += Math.pow(fsdMaxFuelPerJump / fsd.fuelmul, 1 / fsd.fuelpower) * fsdOptimalMass / mass; } return fastestRange; }; @@ -36,29 +41,30 @@ export function fastestRange(mass, fsd, fuel) { /** * Calculate the a ships shield strength based on mass, shield generator and shield boosters used. * - * @param {number} mass Current mass of the ship - * @param {number} shields Base Shield strength MJ for ship - * @param {object} sg The shield generator used - * @param {number} multiplier Shield multiplier for ship (1 + shield boosters if any) - * @return {number} Approximate shield strengh in MJ + * @param {number} mass Current mass of the ship + * @param {number} baseShield Base Shield strength MJ for ship + * @param {object} sg The shield generator used + * @param {number} multiplier Shield multiplier for ship (1 + shield boosters if any) + * @return {number} Approximate shield strengh in MJ */ -export function shieldStrength(mass, shields, sg, multiplier) { - let opt; - if (mass < sg.minmass) { - return shields * multiplier * sg.minmul; - } - if (mass > sg.maxmass) { - return shields * multiplier * sg.maxmul; - } - if (mass < sg.optmass) { - opt = (sg.optmass - mass) / (sg.optmass - sg.minmass); - opt = 1 - Math.pow(1 - opt, 0.87); - return shields * multiplier * ((opt * sg.minmul) + ((1 - opt) * sg.optmul)); - } else { - opt = (sg.optmass - mass) / (sg.maxmass - sg.optmass); - opt = -1 + Math.pow(1 + opt, 2.425); - return shields * multiplier * ((-1 * opt * sg.maxmul) + ((1 + opt) * sg.optmul)); - } +export function shieldStrength(mass, baseShield, sg, multiplier) { + // sg might be a module or a template; handle either here + let minMass = sg instanceof Module ? sg.getMinMass() : sg.minmass; + let optMass = sg instanceof Module ? sg.getOptMass() : sg.optmass; + let maxMass = sg instanceof Module ? sg.getMaxMass() : sg.maxmass; + let minMul = sg instanceof Module ? sg.getMinMul() : sg.minmul; + let optMul = sg instanceof Module ? sg.getOptMul() : sg.optmul; + let maxMul = sg instanceof Module ? sg.getMaxMul() : sg.maxmul; + + let xnorm = Math.min(1, (maxMass - mass) / (maxMass - minMass)); + let exponent = Math.log((optMul - minMul) / (maxMul - minMul)) / Math.log(Math.min(1, (maxMass - optMass) / (maxMass - minMass))) + let ynorm = Math.pow(xnorm, exponent); + let mul = minMul + ynorm * (maxMul - minMul); + let strength = baseShield * mul; + + // TODO handle multiplier + + return strength; } /** @@ -72,13 +78,24 @@ export function shieldStrength(mass, shields, sg, multiplier) { * @return {object} Approximate speed by pips */ export function speed(mass, baseSpeed, baseBoost, thrusters, pipSpeed) { - let multiplier = mass > thrusters.maxmass ? 0 : ((1 - thrusters.M) + (thrusters.M * Math.pow(3 - (2 * Math.max(0.5, mass / thrusters.optmass)), thrusters.P))); - let speed = baseSpeed * multiplier; + // thrusters might be a module or a template; handle either here + let minMass = thrusters instanceof Module ? thrusters.getMinMass() : thrusters.minmass; + let optMass = thrusters instanceof Module ? thrusters.getOptMass() : thrusters.optmass; + let maxMass = thrusters instanceof Module ? thrusters.getMaxMass() : thrusters.maxmass; + let minMul = thrusters instanceof Module ? thrusters.getMinMul() : thrusters.minmul; + let optMul = thrusters instanceof Module ? thrusters.getOptMul() : thrusters.optmul; + let maxMul = thrusters instanceof Module ? thrusters.getMaxMul() : thrusters.maxmul; + + let xnorm = Math.min(1, (maxMass - mass) / (maxMass - minMass)); + let exponent = Math.log((optMul - minMul) / (maxMul - minMul)) / Math.log(Math.min(1, (maxMass - optMass) / (maxMass - minMass))) + let ynorm = Math.pow(xnorm, exponent); + let mul = minMul + ynorm * (maxMul - minMul); + let speed = baseSpeed * mul; return { '0 Pips': speed * (1 - (pipSpeed * 4)), '2 Pips': speed * (1 - (pipSpeed * 2)), '4 Pips': speed, - 'boost': baseBoost * multiplier + 'boost': baseBoost * mul }; } diff --git a/src/app/shipyard/Module.js b/src/app/shipyard/Module.js index 9010e61c..efae87ca 100755 --- a/src/app/shipyard/Module.js +++ b/src/app/shipyard/Module.js @@ -150,7 +150,17 @@ export default class Module { * @return {Number} the heat per second of this module */ getHeatPerSecond() { + // Modifier for hps is thermload return this._getModifiedValue('hps'); + let result = 0; + if (this['hps']) { + result = this['hps']; + if (result) { + let mult = this.getModValue('thermload'); + if (mult) { result = result * (1 + mult); } + } + } + return result; } /** @@ -296,4 +306,88 @@ export default class Module { getShieldReinforcement() { return this._getModifiedValue('shieldreinforcement'); } + + /** + * Get the minimum mass for this module, taking in to account modifications + * @return {Number} the minimum mass of this module + */ + getMinMass() { + // Modifier is optmass + let result = 0; + if (this['minmass']) { + result = this['minmass']; + if (result) { + let mult = this.getModValue('optmass'); + if (mult) { result = result * (1 + mult); } + } + } + return result; + } + + /** + * Get the optimum mass for this module, taking in to account modifications + * @return {Number} the optimum mass of this module + */ + getOptMass() { + return this._getModifiedValue('optmass'); + } + + /** + * Get the maximum mass for this module, taking in to account modifications + * @return {Number} the maximum mass of this module + */ + getMaxMass() { + // Modifier is optmass + let result = 0; + if (this['maxmass']) { + result = this['maxmass']; + if (result) { + let mult = this.getModValue('optmass'); + if (mult) { result = result * (1 + mult); } + } + } + return result; + } + + /** + * Get the minimum multiplier for this module, taking in to account modifications + * @return {Number} the minimum multiplier of this module + */ + getMinMul() { + // Modifier is optmul + let result = 0; + if (this['minmul']) { + result = this['minmul']; + if (result) { + let mult = this.getModValue('optmul'); + if (mult) { result = result * (1 + mult); } + } + } + return result; + } + + /** + * Get the optimum multiplier for this module, taking in to account modifications + * @return {Number} the optimum multiplier of this module + */ + getOptMul() { + return this._getModifiedValue('optmul'); + } + + /** + * Get the maximum multiplier for this module, taking in to account modifications + * @return {Number} the maximum multiplier of this module + */ + getMaxMul() { + // Modifier is optmul + let result = 0; + if (this['maxmul']) { + result = this['maxmul']; + if (result) { + let mult = this.getModValue('optmul'); + if (mult) { result = result * (1 + mult); } + } + } + return result; + } } diff --git a/src/app/shipyard/Ship.js b/src/app/shipyard/Ship.js index 9074f96f..09085b9a 100755 --- a/src/app/shipyard/Ship.js +++ b/src/app/shipyard/Ship.js @@ -179,7 +179,8 @@ export default class Ship { */ calcUnladenRange(massDelta, fuel, fsd) { fsd = fsd || this.standard[2].m; - return Calc.jumpRange(this.unladenMass + (massDelta || 0) + Math.min(fsd.getMaxFuelPerJump(), fuel || this.fuelCapacity), fsd || this.standard[2].m, fuel); + let fsdMaxFuelPerJump = fsd instanceof Module ? fsd.getMaxFuelPerJump() : fsd.maxfuel; + return Calc.jumpRange(this.unladenMass + (massDelta || 0) + Math.min(fsdMaxFuelPerJump, fuel || this.fuelCapacity), fsd || this.standard[2].m, fuel); } /** @@ -432,6 +433,13 @@ export default class Ship { this.updateJumpStats(); } else if (name == 'optmass') { m.setModValue(name, value); + // Could be for either thrusters or FSD + this.updateTopSpeed(); + this.updateJumpStats(); + } else if (name == 'optmul') { + m.setModValue(name, value); + // Could be for either thrusters or FSD + this.updateTopSpeed(); this.updateJumpStats(); } else { // Generic From e5cc3e269e7ee4b4ad481c0d7517128bfefe7751 Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Mon, 31 Oct 2016 11:22:36 +0000 Subject: [PATCH 11/39] Updates for modifications --- src/app/components/HardpointSlot.jsx | 16 ++--- src/app/components/HardpointsSlotSection.jsx | 1 + src/app/components/InternalSlot.jsx | 8 +-- src/app/components/InternalSlotSection.jsx | 1 + src/app/components/ModificationsMenu.jsx | 26 ++++++-- src/app/components/Slot.jsx | 5 +- src/app/components/StandardSlot.jsx | 18 ++--- src/app/components/UtilitySlotSection.jsx | 1 + src/app/i18n/en.js | 69 ++++++++++--------- src/app/shipyard/Calculations.js | 5 +- src/app/shipyard/Module.js | 70 +++++++++++++++++++- src/app/shipyard/ModuleSet.js | 2 +- src/app/shipyard/Ship.js | 9 ++- 13 files changed, 162 insertions(+), 69 deletions(-) diff --git a/src/app/components/HardpointSlot.jsx b/src/app/components/HardpointSlot.jsx index 18436ec1..18f35d36 100644 --- a/src/app/components/HardpointSlot.jsx +++ b/src/app/components/HardpointSlot.jsx @@ -56,14 +56,14 @@ export default class HardpointSlot extends Slot {
    {m.getMass()}{u.T}
    - { m.dps ?
    {translate('DPS')}: {formats.round1(m.dps)} { m.clip ? ({formats.round1((m.clip * m.dps / m.rof) / ((m.clip / m.rof) + m.reload)) }) : null }
    : null } - { m.eps ?
    {translate('EPS')}: {formats.round1(m.eps)}{u.MW} { m.clip ? ({formats.round1((m.clip * m.eps / m.rof) / ((m.clip / m.rof) + m.reload)) }{u.MW}) : null }
    : null } - { m.hps ?
    {translate('HPS')}: {formats.round1(m.hps)} { m.clip ? ({formats.round1((m.clip * m.hps / m.rof) / ((m.clip / m.rof) + m.reload)) }) : null }
    : null } - { m.dps && m.eps ?
    {translate('DPE')}: {formats.round1(m.dps / m.eps)}
    : null } - { m.rof ?
    {translate('ROF')}: {m.rof}{u.ps}
    : null } - { m.range && !m.dps ?
    {translate('Range')} : {formats.round(m.range / 1000)}{u.km}
    : null } - { m.shieldmul ?
    +{formats.rPct(m.shieldmul)}
    : null } - { m.ammo >= 0 ?
    {translate('ammo')}: {formats.int(m.clip)}/{formats.int(m.ammo)}
    : null } + { m.getDps() ?
    {translate('DPS')}: {formats.round1(m.getDps())} { m.getClip() ? ({formats.round1((m.getClip() * m.getDps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload())) }) : null }
    : null } + { m.getEps() ?
    {translate('EPS')}: {formats.round1(m.getEps())}{u.MW} { m.getClip() ? ({formats.round1((m.getClip() * m.getEps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload())) }{u.MW}) : null }
    : null } + { m.getHps() ?
    {translate('HPS')}: {formats.round1(m.getHps())} { m.getClip() ? ({formats.round1((m.getClip() * m.getHps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload())) }) : null }
    : null } + { m.getDps() && m.getEps() ?
    {translate('DPE')}: {formats.round1(m.getDps() / m.getEps())}
    : null } + { m.getRoF() ?
    {translate('ROF')}: {m.getRoF()}{u.ps}
    : null } + { m.getRange() && !m.getDps() ?
    {translate('Range')} : {formats.round(m.getRange() / 1000)}{u.km}
    : null } + { m.getShieldMul() ?
    +{formats.rPct(m.getShieldMul())}
    : null } + { m.getAmmo() ?
    {translate('ammunition')}: {formats.int(m.getClip())}/{formats.int(m.getAmmo())}
    : null } { m && validMods.length > 0 ?
    : null }
    ; diff --git a/src/app/components/HardpointsSlotSection.jsx b/src/app/components/HardpointsSlotSection.jsx index 3c51b3f2..378300ee 100644 --- a/src/app/components/HardpointsSlotSection.jsx +++ b/src/app/components/HardpointsSlotSection.jsx @@ -69,6 +69,7 @@ export default class HardpointsSlotSection extends SlotSection { availableModules={() => availableModules.getHps(h.maxClass)} onOpen={this._openMenu.bind(this, h)} onSelect={this._selectModule.bind(this, h)} + onChange={this.props.onChange} selected={currentMenu == h} drag={this._drag.bind(this, h)} dragOver={this._dragOverSlot.bind(this, h)} diff --git a/src/app/components/InternalSlot.jsx b/src/app/components/InternalSlot.jsx index 84fbc1c5..19b5b0c2 100644 --- a/src/app/components/InternalSlot.jsx +++ b/src/app/components/InternalSlot.jsx @@ -30,16 +30,16 @@ export default class InternalSlot extends Slot {
    {m.getMass() || m.cargo || m.fuel || 0}{u.T}
    - { m.optmass ?
    {translate('optimal mass')}: {m.optmass}{u.T}
    : null } - { m.maxmass ?
    {translate('max mass')}: {m.maxmass}{u.T}
    : null } + { m.getOptMass() ?
    {translate('optimal mass')}: {m.getOptMass()}{u.T}
    : null } + { m.getMaxMass() ?
    {translate('max mass')}: {m.getMaxMass()}{u.T}
    : null } { m.bins ?
    {m.bins} {translate('bins')}
    : null } { m.bays ?
    {translate('bays')}: {m.bays}
    : null } { m.rate ?
    {translate('rate')}: {m.rate}{u.kgs}   {translate('refuel time')}: {formats.time(this.props.fuel * 1000 / m.rate)}
    : null } - { m.ammo ?
    {translate('ammo')}: {formats.gen(m.ammo)}
    : null } + { m.getAmmo() ?
    {translate('ammunition')}: {formats.gen(m.getAmmo())}
    : null } { m.cells ?
    {translate('cells')}: {m.cells}
    : null } { m.recharge ?
    {translate('recharge')}: {m.recharge} MJ   {translate('total')}: {m.cells * m.recharge}{u.MJ}
    : null } { m.repair ?
    {translate('repair')}: {m.repair}
    : null } - { m.range ?
    {translate('range')} {m.range}{u.km}
    : null } + { m.getRange() ?
    {translate('range')} {m.getRange()}{u.km}
    : null } { m.time ?
    {translate('time')}: {formats.time(m.time)}
    : null } { m.maximum ?
    {translate('max')}: {(m.maximum)}
    : null } { m.rangeLS ?
    {translate('range')}: {m.rangeLS}{u.Ls}
    : null } diff --git a/src/app/components/InternalSlotSection.jsx b/src/app/components/InternalSlotSection.jsx index 66beab58..90e2ca73 100644 --- a/src/app/components/InternalSlotSection.jsx +++ b/src/app/components/InternalSlotSection.jsx @@ -114,6 +114,7 @@ export default class InternalSlotSection extends SlotSection { onSelect={this._selectModule.bind(this, s)} selected={currentMenu == s} enabled={s.enabled} + eligible={s.eligible} m={s.m} drag={this._drag.bind(this, s)} dragOver={this._dragOverSlot.bind(this, s)} diff --git a/src/app/components/ModificationsMenu.jsx b/src/app/components/ModificationsMenu.jsx index c844f253..9be631ac 100644 --- a/src/app/components/ModificationsMenu.jsx +++ b/src/app/components/ModificationsMenu.jsx @@ -41,15 +41,18 @@ export default class ModificationsMenu extends TranslatedComponent { let formats = context.language.formats; let { m } = props; let list = []; + let values = {}; for (let modName of Modifications.validity[m.grp]) { + values[modName] = m.getModValue(modName) * 100; list.push(
    {translate(modName)}{' (%)'}
    - +
    ); } + // - return { list }; + return { list, values }; } /** @@ -61,19 +64,30 @@ export default class ModificationsMenu extends TranslatedComponent { this.setState(this._initState(nextProps, nextContext)); } + _getValue(name, defaultValue) { + let values = this.state ? this.state.values : null; + return values ? values[name] : defaultValue; + } + /** * Update modification given a value. * @param {Number} name The name of the modification * @param {Number} value The value to set, in the range [0,1] */ _updateValue(name, value) { - let scaledValue = Math.floor(value * 100) / 10000; - let m = this.props.m; - let ship = this.props.ship; + let values = this.state.values; + values[name] = value; - ship.setModification(m, name, scaledValue); + // Only update the modification if this is a valid number + if (!isNaN(Number(value)) && !value.endsWith('.')) { + let scaledValue = Math.floor(Number(value) * 100) / 10000; + let m = this.props.m; + let ship = this.props.ship; + ship.setModification(m, name, scaledValue); + } this.props.onChange(); + this.setState({values}); } /** diff --git a/src/app/components/Slot.jsx b/src/app/components/Slot.jsx index 6310ac15..7df6808a 100644 --- a/src/app/components/Slot.jsx +++ b/src/app/components/Slot.jsx @@ -22,6 +22,7 @@ export default class Slot extends TranslatedComponent { selected: React.PropTypes.bool, m: React.PropTypes.object, ship: React.PropTypes.object.isRequired, + eligible: React.PropTypes.object, warning: React.PropTypes.func, drag: React.PropTypes.func, drop: React.PropTypes.func, @@ -80,7 +81,7 @@ export default class Slot extends TranslatedComponent { let language = this.context.language; let { termtip, tooltip } = this.context; let translate = language.translate; - let { ship, m, dropClass, dragOver, onOpen, onChange, selected, onSelect, warning, shipMass, availableModules } = this.props; + let { ship, m, dropClass, dragOver, onOpen, onChange, selected, eligible, onSelect, warning, shipMass, availableModules } = this.props; let slotDetails, menu; let validMods = m == null ? [] : (Modifications.validity[m.grp] || []); @@ -92,7 +93,7 @@ export default class Slot extends TranslatedComponent { if (m) { slotDetails = this._getSlotDetails(m, translate, language.formats, language.units); // Must be implemented by sub classes } else { - slotDetails =
    {translate('empty')}
    ; + slotDetails =
    {translate(eligible ? 'emptyrestricted' : 'empty')}
    ; } if (selected) { diff --git a/src/app/components/StandardSlot.jsx b/src/app/components/StandardSlot.jsx index 49ae73fd..83492b90 100644 --- a/src/app/components/StandardSlot.jsx +++ b/src/app/components/StandardSlot.jsx @@ -75,20 +75,20 @@ export default class StandardSlot extends TranslatedComponent {
    {slot.maxClass}
    {classRating} {translate(m.grp == 'bh' ? m.grp : m.name || m.grp)}
    -
    {formats.round1(m.getMass()) || m.fuel || 0}{units.T}
    +
    {formats.round(m.getMass()) || m.fuel || 0}{units.T}
    { m.grp == 'bh' && m.name ?
    {translate(m.name)}
    : null } - { m.getOptimalMass() ?
    {translate('optimal mass')}: {m.getOptimalMass()}{units.T}
    : null } - { m.getMaxMass() ?
    {translate('max mass')}: {m.getMaxMass()}{units.T}
    : null } - { m.getRange() ?
    {translate('range')}: {m.getRange()}{units.km}
    : null } + { m.getOptimalMass() ?
    {translate('optimal mass')}: {formats.round(m.getOptimalMass())}{units.T}
    : null } + { m.getMaxMass() ?
    {translate('max mass')}: {formats.round(m.getMaxMass())}{units.T}
    : null } + { m.getRange() ?
    {translate('range')}: {formats.round(m.getRange())}{units.km}
    : null } { m.time ?
    {translate('time')}: {formats.time(m.time)}
    : null } - { m.getThermalEfficiency() ?
    {translate('efficiency')}: {m.getThermalEfficiency()}
    : null } + { m.getThermalEfficiency() ?
    {translate('efficiency')}: {formats.round(m.getThermalEfficiency())}
    : null } { m.getPowerGeneration() > 0 ?
    {translate('pGen')}: {formats.round(m.getPowerGeneration())}{units.MW}
    : null } - { m.getMaxFuelPerJump() ?
    {translate('max')} {translate('fuel')}: {m.getMaxFuelPerJump()}{units.T}
    : null } - { m.getWeaponsCapacity() ?
    {translate('WEP')}: {m.getWeaponsCapacity()}{units.MJ} / {m.getWeaponsRechargeRate()}{units.MW}
    : null } - { m.getSystemsCapacity() ?
    {translate('SYS')}: {m.getSystemsCapacity()}{units.MJ} / {m.getSystemsRechargeRate()}{units.MW}
    : null } - { m.getEnginesCapacity() ?
    {translate('ENG')}: {m.getEnginesCapacity()}{units.MJ} / {m.getEnginesRechargeRate()}{units.MW}
    : null } + { m.getMaxFuelPerJump() ?
    {translate('max')} {translate('fuel')}: {formats.round(m.getMaxFuelPerJump())}{units.T}
    : null } + { m.getWeaponsCapacity() ?
    {translate('WEP')}: {formats.round(m.getWeaponsCapacity())}{units.MJ} / {formats.round(m.getWeaponsRechargeRate())}{units.MW}
    : null } + { m.getSystemsCapacity() ?
    {translate('SYS')}: {formats.round(m.getSystemsCapacity())}{units.MJ} / {formats.round(m.getSystemsRechargeRate())}{units.MW}
    : null } + { m.getEnginesCapacity() ?
    {translate('ENG')}: {formats.round(m.getEnginesCapacity())}{units.MJ} / {formats.round(m.getEnginesRechargeRate())}{units.MW}
    : null } { validMods.length > 0 ?
    : null }
    diff --git a/src/app/components/UtilitySlotSection.jsx b/src/app/components/UtilitySlotSection.jsx index 0e922f83..7741a2c4 100644 --- a/src/app/components/UtilitySlotSection.jsx +++ b/src/app/components/UtilitySlotSection.jsx @@ -68,6 +68,7 @@ export default class UtilitySlotSection extends SlotSection { availableModules={() => availableModules.getHps(h.maxClass)} onOpen={this._openMenu.bind(this,h)} onSelect={this._selectModule.bind(this, h)} + onChange={this.props.onChange} selected={currentMenu == h} drag={this._drag.bind(this, h)} dragOver={this._dragOverSlot.bind(this, h)} diff --git a/src/app/i18n/en.js b/src/app/i18n/en.js index ce8a535f..1c9a7de6 100644 --- a/src/app/i18n/en.js +++ b/src/app/i18n/en.js @@ -80,41 +80,48 @@ export const terms = { ul: 'Burst Laser', ws: 'Frame Shift Wake Scanner', + // Items on the outfitting page + // Notification of restricted slot for Orca/Beluga + emptyrestricted: 'empty (restricted)', + // 'ammo' was overloaded for outfitting page and modul info, so changed to ammunition for outfitting page + ammunition: 'Ammo', + // Modifications - mass: 'Mass', - integrity: 'Integrity', - pGen: 'Power generation', + ammo: 'Ammunition maximum', + armourpen: 'Armour penetration', + boot: 'Boot time', + brokenregen: 'Broken regeneration rate', + burst: 'Burst', + clip: 'Ammunition clip', + damage: 'Damage', + delay: 'Delay', + dps: 'Damage per second', + distdraw: 'Distributor draw', + duration: 'Duration', eff: 'Efficiency', - power: 'Power draw', + engcap: 'Engines capacity', + engrate: 'Engines recharge rate', + eps: 'Energy per second', + explres: 'Explosive resistance', + integrity: 'Integrity', + jitter: 'Jitter', + kinres: 'Kinetic resistance', + maxfuel: 'Maximum fuel per jump', + mass: 'Mass', optmass: 'Optimal mass', optmul: 'Optimal multiplier', - dps: 'Damage per second', - eps: 'Energy per second', - thermload: 'Thermal load', - boot: 'Boot time', - maxfuel: 'Maximum fuel per jump', - syscap: 'Systems capacity', - engcap: 'Engines capacity', - wepcap: 'Weapons capacity', - sysrate: 'Systems recharge rate', - engrate: 'Engines recharge rate', - weprate: 'Weapons recharge rate', - kinres: 'Kinetic resistance', - thermres: 'Thermal resistance', - explres: 'Explosive resistance', - regen: 'Regeneration rate', - brokenregen: 'Broken regeneration rate', - delay: 'Delay', - duration: 'Duration', - shield: 'Shield', - shieldboost: 'Shield boost', - distdraw: 'Distributor draw', - damage: 'Damage', - armourpen: 'Armour penetration', + pGen: 'Power generation', + power: 'Power draw', range: 'Range', + regen: 'Regeneration rate', + reload: 'Reload time', rof: 'Rate of fire', - clip: 'Ammunition clip', - ammo: 'Ammunition maximum', - jitter: 'Jitter', - reload: 'Reload time' + shield: 'Shield', + shieldmul: 'Shield boost', + syscap: 'Systems capacity', + sysrate: 'Systems recharge rate', + thermload: 'Thermal load', + thermres: 'Thermal resistance', + wepcap: 'Weapons capacity', + weprate: 'Weapons recharge rate', }; diff --git a/src/app/shipyard/Calculations.js b/src/app/shipyard/Calculations.js index 6fa9c88d..21d818d6 100644 --- a/src/app/shipyard/Calculations.js +++ b/src/app/shipyard/Calculations.js @@ -60,11 +60,8 @@ export function shieldStrength(mass, baseShield, sg, multiplier) { let exponent = Math.log((optMul - minMul) / (maxMul - minMul)) / Math.log(Math.min(1, (maxMass - optMass) / (maxMass - minMass))) let ynorm = Math.pow(xnorm, exponent); let mul = minMul + ynorm * (maxMul - minMul); - let strength = baseShield * mul; - // TODO handle multiplier - - return strength; + return baseShield * mul * multiplier; } /** diff --git a/src/app/shipyard/Module.js b/src/app/shipyard/Module.js index efae87ca..c4eccac6 100755 --- a/src/app/shipyard/Module.js +++ b/src/app/shipyard/Module.js @@ -32,7 +32,7 @@ export default class Module { * @return {Number} The value of the modification, as a decimal value from -1 to 1 */ getModValue(name) { - return this.mods ? this.mods[name] / 10000 : null; + return this.mods && this.mods[name] ? this.mods[name] / 10000 : null; } /** @@ -390,4 +390,72 @@ export default class Module { } return result; } + + /** + * Get the shield multiplier for this module, taking in to account modifications + * @return {Number} the shield multiplier of this module + */ + getShieldMul() { + return this._getModifiedValue('shieldmul'); + } + + + /** + * Get the DPS for this module, taking in to account modifications + * @return {Number} the DPS of this module + */ + getDps() { + // TODO this is not correct; need to include other factors such as rate of fire, damage, etc. + return this._getModifiedValue('dps'); + } + + /** + * Get the heat generated per second for this module, taking in to account modifications + * @return {Number} the heat generated per second of this module + */ + getHps() { + // TODO this is not correct; need to include other factors such as rate of fire, damage, etc. + return this._getModifiedValue('hps'); + } + + /** + * Get the energy used per second for this module, taking in to account modifications + * @return {Number} the energy used per second of this module + */ + getEps() { + // TODO this is not correct; need to include other factors such as rate of fire, damage, etc. + return this._getModifiedValue('eps'); + } + + /** + * Get the clip size for this module, taking in to account modifications + * @return {Number} the clip size of this module + */ + getClip() { + return this._getModifiedValue('clip'); + } + + /** + * Get the ammo size for this module, taking in to account modifications + * @return {Number} the ammo size of this module + */ + getAmmo() { + return this._getModifiedValue('ammo'); + } + + /** + * Get the reload time for this module, taking in to account modifications + * @return {Number} the reload time of this module + */ + getReload() { + return this._getModifiedValue('reload'); + } + + /** + * Get the rate of fire for this module, taking in to account modifications + * @return {Number} the rate of fire for this module + */ + getRoF() { + return this._getModifiedValue('rof'); + } } diff --git a/src/app/shipyard/ModuleSet.js b/src/app/shipyard/ModuleSet.js index b3ddf314..9fa480ee 100755 --- a/src/app/shipyard/ModuleSet.js +++ b/src/app/shipyard/ModuleSet.js @@ -126,7 +126,7 @@ export default class ModuleSet { let pd = this.standard[4][0]; for (let p of this.standard[4]) { - if (p.mass < pd.mass && p.getEnginesCapacity() >= boostEnergy) { + if (p.mass < pd.mass && p.engcap >= boostEnergy) { pd = p; } } diff --git a/src/app/shipyard/Ship.js b/src/app/shipyard/Ship.js index 09085b9a..6d1720dd 100755 --- a/src/app/shipyard/Ship.js +++ b/src/app/shipyard/Ship.js @@ -441,6 +441,9 @@ export default class Ship { // Could be for either thrusters or FSD this.updateTopSpeed(); this.updateJumpStats(); + } else if (name == 'shieldmul') { + m.setModValue(name, value); + this.updateShieldStrength(); } else { // Generic m.setModValue(name, value); @@ -686,7 +689,7 @@ export default class Ship { if (ModuleUtils.isShieldGenerator(slot.m.grp)) { this.updateShieldStrength(); } else if (slot.m.grp == 'sb') { - this.shieldMultiplier += slot.m.shieldmul * (enabled ? 1 : -1); + this.shieldMultiplier += slot.m.getShieldMul() * (enabled ? 1 : -1); this.updateShieldStrength(); } if (slot.m.dps) { @@ -752,7 +755,7 @@ export default class Ship { this.armourAdded -= old.armouradd; break; case 'sb': - this.shieldMultiplier -= slot.enabled ? old.shieldmul : 0; + this.shieldMultiplier -= slot.enabled ? old.getShieldMul() : 0; break; } @@ -794,7 +797,7 @@ export default class Ship { this.armourAdded += n.armouradd; break; case 'sb': - this.shieldMultiplier += slot.enabled ? n.shieldmul : 0; + this.shieldMultiplier += slot.enabled ? n.getShieldMul() : 0; break; } From 3ec9679893a5bdf35227c4794659c5cc03971022 Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Mon, 31 Oct 2016 12:13:03 +0000 Subject: [PATCH 12/39] Tidy up diff --- src/app/components/StandardSlotSection.jsx | 2 +- src/app/utils/SlotFunctions.js | 70 +++++++++++++--------- 2 files changed, 44 insertions(+), 28 deletions(-) diff --git a/src/app/components/StandardSlotSection.jsx b/src/app/components/StandardSlotSection.jsx index 6e60046b..af99ce43 100644 --- a/src/app/components/StandardSlotSection.jsx +++ b/src/app/components/StandardSlotSection.jsx @@ -163,7 +163,7 @@ export default class StandardSlotSection extends SlotSection { selected={currentMenu == st[4]} onChange={this.props.onChange} ship={ship} - warning= {m => m.getEnginesCapacity() < ship.boostEnergy} + warning= {m => m.engcap < ship.boostEnergy} />; slots[6] = {translate('cost')}: {mCost ? Math.round(mCost * (1 - Persist.getModuleDiscount())) : 0}{units.CR}
    ); + let mMass = m.mass || 0; let mmMass = mm ? mm.getMass() : 0; - let massDiff = mMass - mmMass; - let capDiff = (m.fuel || m.cargo || 0) - (mm.fuel || mm.cargo || 0); + if (mMass != mmMass) propDiffs.push(
    {translate('mass')}: {diff(formats.round, mMass, mmMass)}{units.T}
    ); + + let mPowerUsage = m.power || 0; + let mmPowerUsage = mm ? mm.getPowerUsage() : 0; + if (mPowerUsage != mmPowerUsage) propDiffs.push(
    {translate('power')}: {diff(formats.round, mPowerUsage, mmPowerUsage)}{units.MJ}
    ); + +// for (let p in m) { +// if (!PROP_BLACKLIST[p] && !isNaN(m[p])) { +// let mVal = m[p] === null ? Infinity : m[p]; +// let mmVal = mm[p] === null ? Infinity : mm[p]; +// let format = formats[FORMAT_LOOKUP[p]] || formats.round; +// propDiffs.push(
    +// {`${translate(TERM_LOOKUP[p] || p)}: `} +// {diff(format, mVal, mmVal)}{units[UNIT_LOOKUP[p]]} +//
    ); +// } +// } + + let mDps = m.dps || 0; + let mmDps = mm ? mm.getDps() || 0 : 0; + if (mDps != mmDps) propDiffs.push(
    {translate('dps')}: {diff(formats.round, mDps, mmDps)}
    ); + let mAffectsShield = isShieldGenerator(m.grp) || m.grp == 'sb'; - let mmAffectsShield = isShieldGenerator(mm.grp) || mm.grp == 'sb'; - - propDiffs.push(
    {translate('cost')}: {m.cost ? Math.round(m.cost * (1 - Persist.getModuleDiscount())) : 0}{units.CR}
    ); - propDiffs.push(
    {translate('mass')}: {diff(formats.round, mMass, mmMass)}{units.T}
    ); - - for (let p in m) { - if (!PROP_BLACKLIST[p] && !isNaN(m[p])) { - let mVal = m[p] === null ? Infinity : m[p]; - let mmVal = mm[p] === null ? Infinity : mm[p]; - let format = formats[FORMAT_LOOKUP[p]] || formats.round; - propDiffs.push(
    - {`${translate(TERM_LOOKUP[p] || p)}: `} - {diff(format, mVal, mmVal)}{units[UNIT_LOOKUP[p]]} -
    ); - } - } - + let mmAffectsShield = isShieldGenerator(mm ? mm.grp : null) || mm && mm.grp == 'sb'; if (mAffectsShield || mmAffectsShield) { let shield = this.calcShieldStrengthWith(); // Get shield strength regardless of slot active / inactive let newShield = 0; if (mAffectsShield) { if (m.grp == 'sb') { // Both m and mm must be utility modules if this is true - newShield = this.calcShieldStrengthWith(null, m.shieldmul - (mm.shieldmul || 0)); + newShield = this.calcShieldStrengthWith(null, m.shieldmul - (mm ? mm.getShieldMul() || 0 : 0)); } else { newShield = this.calcShieldStrengthWith(m); } + } else { + // Old module must be a shield booster + newShield = this.calcShieldStrengthWith(null, -mm.getShieldMul()); } + let sgDiffClass = Math.round((newShield - shield) * 100) / 100 == 0 ? 'muted' : (newShield > shield ? 'secondary' : 'warning'); propDiffs.push(
    {translate('shields')}: {diff(formats.int, newShield, shield)}{units.MJ}
    ); @@ -251,24 +263,28 @@ export function diffDetails(language, m, mm) { if (m.grp == 'pd') { propDiffs.push(
    {`${translate('WEP')}: `} - {m.getWeaponsCapacity()}{units.MJ} + {m.wepcap}{units.MJ} {' / '} - {m.getWeaponsRechargeRate()}{units.MW} + {m.weprate}{units.MW}
    ); propDiffs.push(
    {`${translate('SYS')}: `} - {m.getSystemsCapacity()}{units.MJ} + {m.syscap}{units.MJ} {' / '} - {m.getSystemsRechargeRate()}{units.MW} + {m.sysrate}{units.MW}
    ); propDiffs.push(
    {`${translate('ENG')}: `} - {m.getEnginesCapacity()}{units.MJ} + {m.engcap}{units.MJ} {' / '} - {m.getEnginesRechargeRate()}{units.MW} + {m.engrate}{units.MW}
    ); } + let massDiff = mMass - mmMass; + let mCap = m.fuel || m.cargo || 0; + let mmCap = mm? mm.fuel || mm.cargo || 0 : 0; + let capDiff = mCap - mmCap; if (m.grp == 'fsd' || massDiff || capDiff) { let fsd = m.grp == 'fsd' ? m : null; let maxRange = this.calcUnladenRange(massDiff, m.fuel, fsd); From 1a0f05511b5a326333b55693029a47e1251bb20b Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Mon, 31 Oct 2016 21:08:15 +0000 Subject: [PATCH 13/39] Various bug fixes --- src/app/components/HardpointSlot.jsx | 2 +- src/app/components/InternalSlot.jsx | 3 ++- src/app/components/ModificationsMenu.jsx | 10 ++++++--- src/app/components/ShipSummaryTable.jsx | 21 +++++++++--------- src/app/components/Slot.jsx | 5 ++++- src/app/components/StandardSlot.jsx | 27 +++++++++++++++--------- src/app/i18n/Language.jsx | 1 + src/app/shipyard/Calculations.js | 4 ++-- src/app/shipyard/ModuleUtils.js | 2 +- src/app/shipyard/Ship.js | 1 - src/app/utils/SlotFunctions.js | 4 ++-- 11 files changed, 47 insertions(+), 33 deletions(-) diff --git a/src/app/components/HardpointSlot.jsx b/src/app/components/HardpointSlot.jsx index 18f35d36..e2589d5d 100644 --- a/src/app/components/HardpointSlot.jsx +++ b/src/app/components/HardpointSlot.jsx @@ -53,7 +53,7 @@ export default class HardpointSlot extends Slot { {m.type && m.type == 'KT' ? : ''} {m.type && m.type == 'E' ? : ''} {classRating} {translate(m.name || m.grp)}
    -
    {m.getMass()}{u.T}
    +
    {formats.round1(m.getMass())}{u.T}
    { m.getDps() ?
    {translate('DPS')}: {formats.round1(m.getDps())} { m.getClip() ? ({formats.round1((m.getClip() * m.getDps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload())) }) : null }
    : null } diff --git a/src/app/components/InternalSlot.jsx b/src/app/components/InternalSlot.jsx index 19b5b0c2..a5467b01 100644 --- a/src/app/components/InternalSlot.jsx +++ b/src/app/components/InternalSlot.jsx @@ -27,7 +27,7 @@ export default class InternalSlot extends Slot { return
    {classRating} {translate(m.name || m.grp)}
    -
    {m.getMass() || m.cargo || m.fuel || 0}{u.T}
    +
    {formats.round1(m.getMass()) || m.cargo || m.fuel || 0}{u.T}
    { m.getOptMass() ?
    {translate('optimal mass')}: {m.getOptMass()}{u.T}
    : null } @@ -48,6 +48,7 @@ export default class InternalSlot extends Slot { { m.armouradd ?
    +{m.armouradd} {translate('armour')}
    : null } { m.passengers ?
    {translate('passengers')}: {m.passengers}
    : null } { m && validMods.length > 0 ?
    : null } +
    ; } else { diff --git a/src/app/components/ModificationsMenu.jsx b/src/app/components/ModificationsMenu.jsx index 9be631ac..a70de410 100644 --- a/src/app/components/ModificationsMenu.jsx +++ b/src/app/components/ModificationsMenu.jsx @@ -50,7 +50,6 @@ export default class ModificationsMenu extends TranslatedComponent {
    ); } - // return { list, values }; } @@ -64,6 +63,12 @@ export default class ModificationsMenu extends TranslatedComponent { this.setState(this._initState(nextProps, nextContext)); } + /** + * Get the locally stored value of the modifier + * @param {string} name the name of the value to obtain + * @param {Number} defaultValue the value to use if none is held locally + * @return {Number} the value + */ _getValue(name, defaultValue) { let values = this.state ? this.state.values : null; return values ? values[name] : defaultValue; @@ -75,7 +80,6 @@ export default class ModificationsMenu extends TranslatedComponent { * @param {Number} value The value to set, in the range [0,1] */ _updateValue(name, value) { - let values = this.state.values; values[name] = value; @@ -87,7 +91,7 @@ export default class ModificationsMenu extends TranslatedComponent { ship.setModification(m, name, scaledValue); } this.props.onChange(); - this.setState({values}); + this.setState({ values }); } /** diff --git a/src/app/components/ShipSummaryTable.jsx b/src/app/components/ShipSummaryTable.jsx index 84aee6b8..1b01c28c 100644 --- a/src/app/components/ShipSummaryTable.jsx +++ b/src/app/components/ShipSummaryTable.jsx @@ -23,8 +23,7 @@ export default class ShipSummaryTable extends TranslatedComponent { let translate = language.translate; let u = language.units; let formats = language.formats; - let round = formats.round; - let { time, int } = formats; + let { time, int, round, f1, f2 } = formats; let armourDetails = null; let sgClassNames = cn({ warning: ship.sgSlot && !ship.shieldStrength, muted: !ship.sgSlot }); let sgRecover = '-'; @@ -84,9 +83,9 @@ export default class ShipSummaryTable extends TranslatedComponent { {ship.agility}/10 { ship.canThrust() ? {int(ship.topSpeed)} {u['m/s']} : 0 } { ship.canBoost() ? {int(ship.topBoost)} {u['m/s']} : 0 } - {round(ship.totalDps)} - {round(ship.totalEps)} - {round(ship.totalHps)} + {f1(ship.totalDps)} + {f1(ship.totalEps)} + {f1(ship.totalHps)} {int(ship.armour)} {armourDetails} {int(ship.shieldStrength)} {u.MJ} { ship.shieldMultiplier > 1 && ship.shieldStrength > 0 ? ({formats.rPct(ship.shieldMultiplier)}) : null } {sgRecover} @@ -96,12 +95,12 @@ export default class ShipSummaryTable extends TranslatedComponent { {int(ship.ladenMass)} {u.T} {round(ship.cargoCapacity)} {u.T} {round(ship.fuelCapacity)} {u.T} - {round(ship.unladenRange)} {u.LY} - {round(ship.fullTankRange)} {u.LY} - {round(ship.ladenRange)} {u.LY} - {round(ship.maxJumpCount)} - {round(ship.unladenFastestRange)} {u.LY} - {round(ship.ladenFastestRange)} {u.LY} + {f2(ship.unladenRange)} {u.LY} + {f2(ship.fullTankRange)} {u.LY} + {f2(ship.ladenRange)} {u.LY} + {int(ship.maxJumpCount)} + {f2(ship.unladenFastestRange)} {u.LY} + {f2(ship.ladenFastestRange)} {u.LY} {ship.masslock} diff --git a/src/app/components/Slot.jsx b/src/app/components/Slot.jsx index 7df6808a..f15bfaf4 100644 --- a/src/app/components/Slot.jsx +++ b/src/app/components/Slot.jsx @@ -130,7 +130,10 @@ export default class Slot extends TranslatedComponent { ); } - _toggleModifications(event) { + /** + * Toggle the modifications flag when selecting the modifications icon + */ + _toggleModifications() { this._modificationsSelected = !this._modificationsSelected; } } diff --git a/src/app/components/StandardSlot.jsx b/src/app/components/StandardSlot.jsx index 83492b90..98771aca 100644 --- a/src/app/components/StandardSlot.jsx +++ b/src/app/components/StandardSlot.jsx @@ -25,6 +25,10 @@ export default class StandardSlot extends TranslatedComponent { warning: React.PropTypes.func, }; + /** + * Construct the slot + * @param {object} props Object properties + */ constructor(props) { super(props); this._modificationsSelected = false; @@ -75,20 +79,20 @@ export default class StandardSlot extends TranslatedComponent {
    {slot.maxClass}
    {classRating} {translate(m.grp == 'bh' ? m.grp : m.name || m.grp)}
    -
    {formats.round(m.getMass()) || m.fuel || 0}{units.T}
    +
    {formats.round1(m.getMass()) || m.fuel || 0}{units.T}
    { m.grp == 'bh' && m.name ?
    {translate(m.name)}
    : null } - { m.getOptimalMass() ?
    {translate('optimal mass')}: {formats.round(m.getOptimalMass())}{units.T}
    : null } - { m.getMaxMass() ?
    {translate('max mass')}: {formats.round(m.getMaxMass())}{units.T}
    : null } - { m.getRange() ?
    {translate('range')}: {formats.round(m.getRange())}{units.km}
    : null } + { m.getOptimalMass() ?
    {translate('optimal mass')}: {formats.int(m.getOptimalMass())}{units.T}
    : null } + { m.getMaxMass() ?
    {translate('max mass')}: {formats.int(m.getMaxMass())}{units.T}
    : null } + { m.getRange() ?
    {translate('range')}: {formats.f2(m.getRange())}{units.km}
    : null } { m.time ?
    {translate('time')}: {formats.time(m.time)}
    : null } - { m.getThermalEfficiency() ?
    {translate('efficiency')}: {formats.round(m.getThermalEfficiency())}
    : null } - { m.getPowerGeneration() > 0 ?
    {translate('pGen')}: {formats.round(m.getPowerGeneration())}{units.MW}
    : null } - { m.getMaxFuelPerJump() ?
    {translate('max')} {translate('fuel')}: {formats.round(m.getMaxFuelPerJump())}{units.T}
    : null } - { m.getWeaponsCapacity() ?
    {translate('WEP')}: {formats.round(m.getWeaponsCapacity())}{units.MJ} / {formats.round(m.getWeaponsRechargeRate())}{units.MW}
    : null } - { m.getSystemsCapacity() ?
    {translate('SYS')}: {formats.round(m.getSystemsCapacity())}{units.MJ} / {formats.round(m.getSystemsRechargeRate())}{units.MW}
    : null } - { m.getEnginesCapacity() ?
    {translate('ENG')}: {formats.round(m.getEnginesCapacity())}{units.MJ} / {formats.round(m.getEnginesRechargeRate())}{units.MW}
    : null } + { m.getThermalEfficiency() ?
    {translate('efficiency')}: {formats.f1(m.getThermalEfficiency())}
    : null } + { m.getPowerGeneration() > 0 ?
    {translate('pGen')}: {formats.f1(m.getPowerGeneration())}{units.MW}
    : null } + { m.getMaxFuelPerJump() ?
    {translate('max')} {translate('fuel')}: {formats.f1(m.getMaxFuelPerJump())}{units.T}
    : null } + { m.getWeaponsCapacity() ?
    {translate('WEP')}: {formats.f1(m.getWeaponsCapacity())}{units.MJ} / {formats.f1(m.getWeaponsRechargeRate())}{units.MW}
    : null } + { m.getSystemsCapacity() ?
    {translate('SYS')}: {formats.f1(m.getSystemsCapacity())}{units.MJ} / {formats.f1(m.getSystemsRechargeRate())}{units.MW}
    : null } + { m.getEnginesCapacity() ?
    {translate('ENG')}: {formats.f1(m.getEnginesCapacity())}{units.MJ} / {formats.f1(m.getEnginesRechargeRate())}{units.MW}
    : null } { validMods.length > 0 ?
    : null }
    @@ -99,6 +103,9 @@ export default class StandardSlot extends TranslatedComponent { ); } + /** + * Toggle the modifications flag when selecting the modifications icon + */ _toggleModifications() { this._modificationsSelected = !this._modificationsSelected; } diff --git a/src/app/i18n/Language.jsx b/src/app/i18n/Language.jsx index 6d4d21a8..fb4ea958 100644 --- a/src/app/i18n/Language.jsx +++ b/src/app/i18n/Language.jsx @@ -46,6 +46,7 @@ export function getLanguage(langCode) { s2: d3Locale.numberFormat('.2s'), // SI Format to 2 decimal places (.e.g 1.1k) pct: d3Locale.numberFormat('.2%'), // % to 2 decimal places (.e.g 5.40%) pct1: d3Locale.numberFormat('.1%'), // % to 1 decimal places (.e.g 5.4%) + r1: d3Locale.numberFormat('.1r'), // Rounded to 1 significant number (.e.g 512 => 500, 4.122 => 4) r2: d3Locale.numberFormat('.2r'), // Rounded to 2 significant numbers (.e.g 512 => 510, 4.122 => 4.1) rPct: d3.format('%'), // % to 0 decimal places (.e.g 5%) round1: (d) => gen(d3.round(d, 1)), // Rounded to 0-1 decimal places (.e.g 5.1, 4) diff --git a/src/app/shipyard/Calculations.js b/src/app/shipyard/Calculations.js index 21d818d6..96c7b0fe 100644 --- a/src/app/shipyard/Calculations.js +++ b/src/app/shipyard/Calculations.js @@ -57,7 +57,7 @@ export function shieldStrength(mass, baseShield, sg, multiplier) { let maxMul = sg instanceof Module ? sg.getMaxMul() : sg.maxmul; let xnorm = Math.min(1, (maxMass - mass) / (maxMass - minMass)); - let exponent = Math.log((optMul - minMul) / (maxMul - minMul)) / Math.log(Math.min(1, (maxMass - optMass) / (maxMass - minMass))) + let exponent = Math.log((optMul - minMul) / (maxMul - minMul)) / Math.log(Math.min(1, (maxMass - optMass) / (maxMass - minMass))); let ynorm = Math.pow(xnorm, exponent); let mul = minMul + ynorm * (maxMul - minMul); @@ -84,7 +84,7 @@ export function speed(mass, baseSpeed, baseBoost, thrusters, pipSpeed) { let maxMul = thrusters instanceof Module ? thrusters.getMaxMul() : thrusters.maxmul; let xnorm = Math.min(1, (maxMass - mass) / (maxMass - minMass)); - let exponent = Math.log((optMul - minMul) / (maxMul - minMul)) / Math.log(Math.min(1, (maxMass - optMass) / (maxMass - minMass))) + let exponent = Math.log((optMul - minMul) / (maxMul - minMul)) / Math.log(Math.min(1, (maxMass - optMass) / (maxMass - minMass))); let ynorm = Math.pow(xnorm, exponent); let mul = minMul + ynorm * (maxMul - minMul); let speed = baseSpeed * mul; diff --git a/src/app/shipyard/ModuleUtils.js b/src/app/shipyard/ModuleUtils.js index d2066c61..dc0b95e6 100755 --- a/src/app/shipyard/ModuleUtils.js +++ b/src/app/shipyard/ModuleUtils.js @@ -98,7 +98,7 @@ export function internal(id) { let group = Modules.internal[n]; for (let i = 0; i < group.length; i++) { if (group[i].id == id) { - return group[i]; + return new Module({ template: group[i] }); } } } diff --git a/src/app/shipyard/Ship.js b/src/app/shipyard/Ship.js index 6d1720dd..9367f689 100755 --- a/src/app/shipyard/Ship.js +++ b/src/app/shipyard/Ship.js @@ -495,7 +495,6 @@ export default class Ship { bands[this.cargoHatch.priority].retracted += this.cargoHatch.m.getPowerUsage(); } - for (i = 0; i < cl; i++) { standard[i].cat = 0; standard[i].enabled = enabled ? enabled[i + 1] * 1 : true; diff --git a/src/app/utils/SlotFunctions.js b/src/app/utils/SlotFunctions.js index 6309f04e..647d8ef7 100644 --- a/src/app/utils/SlotFunctions.js +++ b/src/app/utils/SlotFunctions.js @@ -252,7 +252,7 @@ export function diffDetails(language, m, mm) { } } else { // Old module must be a shield booster - newShield = this.calcShieldStrengthWith(null, -mm.getShieldMul()); + newShield = this.calcShieldStrengthWith(null, -mm.getShieldMul()); } let sgDiffClass = Math.round((newShield - shield) * 100) / 100 == 0 ? 'muted' : (newShield > shield ? 'secondary' : 'warning'); @@ -283,7 +283,7 @@ export function diffDetails(language, m, mm) { let massDiff = mMass - mmMass; let mCap = m.fuel || m.cargo || 0; - let mmCap = mm? mm.fuel || mm.cargo || 0 : 0; + let mmCap = mm ? mm.fuel || mm.cargo || 0 : 0; let capDiff = mCap - mmCap; if (m.grp == 'fsd' || massDiff || capDiff) { let fsd = m.grp == 'fsd' ? m : null; From 258701c3776eeffb38d43c0b1391a446397bce56 Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Mon, 31 Oct 2016 23:19:22 +0000 Subject: [PATCH 14/39] Separate modification slider in to its own component --- src/app/components/HardpointSlot.jsx | 2 +- src/app/components/InternalSlot.jsx | 6 +-- src/app/components/Modification.jsx | 60 ++++++++++++++++++++++++ src/app/components/ModificationsMenu.jsx | 53 ++------------------- src/app/shipyard/Module.js | 18 ++++++- 5 files changed, 84 insertions(+), 55 deletions(-) create mode 100644 src/app/components/Modification.jsx diff --git a/src/app/components/HardpointSlot.jsx b/src/app/components/HardpointSlot.jsx index e2589d5d..ede05551 100644 --- a/src/app/components/HardpointSlot.jsx +++ b/src/app/components/HardpointSlot.jsx @@ -60,7 +60,7 @@ export default class HardpointSlot extends Slot { { m.getEps() ?
    {translate('EPS')}: {formats.round1(m.getEps())}{u.MW} { m.getClip() ? ({formats.round1((m.getClip() * m.getEps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload())) }{u.MW}) : null }
    : null } { m.getHps() ?
    {translate('HPS')}: {formats.round1(m.getHps())} { m.getClip() ? ({formats.round1((m.getClip() * m.getHps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload())) }) : null }
    : null } { m.getDps() && m.getEps() ?
    {translate('DPE')}: {formats.round1(m.getDps() / m.getEps())}
    : null } - { m.getRoF() ?
    {translate('ROF')}: {m.getRoF()}{u.ps}
    : null } + { m.getRoF() ?
    {translate('ROF')}: {formats.f1(m.getRoF())}{u.ps}
    : null } { m.getRange() && !m.getDps() ?
    {translate('Range')} : {formats.round(m.getRange() / 1000)}{u.km}
    : null } { m.getShieldMul() ?
    +{formats.rPct(m.getShieldMul())}
    : null } { m.getAmmo() ?
    {translate('ammunition')}: {formats.int(m.getClip())}/{formats.int(m.getAmmo())}
    : null } diff --git a/src/app/components/InternalSlot.jsx b/src/app/components/InternalSlot.jsx index a5467b01..e5771787 100644 --- a/src/app/components/InternalSlot.jsx +++ b/src/app/components/InternalSlot.jsx @@ -30,8 +30,8 @@ export default class InternalSlot extends Slot {
    {formats.round1(m.getMass()) || m.cargo || m.fuel || 0}{u.T}
    - { m.getOptMass() ?
    {translate('optimal mass')}: {m.getOptMass()}{u.T}
    : null } - { m.getMaxMass() ?
    {translate('max mass')}: {m.getMaxMass()}{u.T}
    : null } + { m.getOptMass() ?
    {translate('optimal mass')}: {formats.int(m.getOptMass())}{u.T}
    : null } + { m.getMaxMass() ?
    {translate('max mass')}: {formats.int(m.getMaxMass())}{u.T}
    : null } { m.bins ?
    {m.bins} {translate('bins')}
    : null } { m.bays ?
    {translate('bays')}: {m.bays}
    : null } { m.rate ?
    {translate('rate')}: {m.rate}{u.kgs}   {translate('refuel time')}: {formats.time(this.props.fuel * 1000 / m.rate)}
    : null } @@ -39,7 +39,7 @@ export default class InternalSlot extends Slot { { m.cells ?
    {translate('cells')}: {m.cells}
    : null } { m.recharge ?
    {translate('recharge')}: {m.recharge} MJ   {translate('total')}: {m.cells * m.recharge}{u.MJ}
    : null } { m.repair ?
    {translate('repair')}: {m.repair}
    : null } - { m.getRange() ?
    {translate('range')} {m.getRange()}{u.km}
    : null } + { m.getRange() ?
    {translate('range')} {formats.f2(m.getRange())}{u.km}
    : null } { m.time ?
    {translate('time')}: {formats.time(m.time)}
    : null } { m.maximum ?
    {translate('max')}: {(m.maximum)}
    : null } { m.rangeLS ?
    {translate('range')}: {m.rangeLS}{u.Ls}
    : null } diff --git a/src/app/components/Modification.jsx b/src/app/components/Modification.jsx new file mode 100644 index 00000000..072b6d92 --- /dev/null +++ b/src/app/components/Modification.jsx @@ -0,0 +1,60 @@ +import React from 'react'; +import { findDOMNode } from 'react-dom'; +import TranslatedComponent from './TranslatedComponent'; +import cn from 'classnames'; +import NumberEditor from 'react-number-editor'; + +/** + * Modification + */ +export default class ModificationsMenu extends TranslatedComponent { + + static propTypes = { + ship: React.PropTypes.object.isRequired, + m: React.PropTypes.object.isRequired, + name: React.PropTypes.string.isRequired, + onChange: React.PropTypes.func.isRequired + }; + + /** + * Constructor + * @param {Object} props React Component properties + * @param {Object} context React Component context + */ + constructor(props, context) { + super(props); + this.state = {}; + this.state.value = this.props.m.getModValue(this.props.name) * 100 || 0; + } + + /** + * Update modification given a value. + * @param {Number} value The value to set + */ + _updateValue(value) { + let scaledValue = Math.floor(Number(value) * 100) / 10000; + let m = this.props.m; + let name = this.props.name; + let ship = this.props.ship; + ship.setModification(m, name, scaledValue); + + this.setState({ value }); + this.props.onChange(); + } + + /** + * Render the modification + * @return {React.Component} modification + */ + render() { + let translate = this.context.language.translate; + let name = this.props.name; + + return ( +
    +
    {translate(name)}{' (%)'}
    + +
    + ); + } +} diff --git a/src/app/components/ModificationsMenu.jsx b/src/app/components/ModificationsMenu.jsx index a70de410..871b6a08 100644 --- a/src/app/components/ModificationsMenu.jsx +++ b/src/app/components/ModificationsMenu.jsx @@ -5,7 +5,7 @@ import { stopCtxPropagation } from '../utils/UtilityFunctions'; import cn from 'classnames'; import { MountFixed, MountGimballed, MountTurret } from './SvgIcons'; import { Modifications } from 'coriolis-data/dist'; -import NumberEditor from 'react-number-editor'; +import Modification from './Modification'; const PRESS_THRESHOLD = 500; // mouse/touch down threshold @@ -39,59 +39,14 @@ export default class ModificationsMenu extends TranslatedComponent { _initState(props, context) { let translate = context.language.translate; let formats = context.language.formats; - let { m } = props; + let { m, onChange, ship } = props; let list = []; - let values = {}; for (let modName of Modifications.validity[m.grp]) { - values[modName] = m.getModValue(modName) * 100; - list.push(
    -
    {translate(modName)}{' (%)'}
    - -
    ); + list.push(); } - return { list, values }; - } - - /** - * Update state based on property and context changes - * @param {Object} nextProps Incoming/Next properties - * @param {Object} nextContext Incoming/Next conext - */ - componentWillReceiveProps(nextProps, nextContext) { - this.setState(this._initState(nextProps, nextContext)); - } - - /** - * Get the locally stored value of the modifier - * @param {string} name the name of the value to obtain - * @param {Number} defaultValue the value to use if none is held locally - * @return {Number} the value - */ - _getValue(name, defaultValue) { - let values = this.state ? this.state.values : null; - return values ? values[name] : defaultValue; - } - - /** - * Update modification given a value. - * @param {Number} name The name of the modification - * @param {Number} value The value to set, in the range [0,1] - */ - _updateValue(name, value) { - let values = this.state.values; - values[name] = value; - - // Only update the modification if this is a valid number - if (!isNaN(Number(value)) && !value.endsWith('.')) { - let scaledValue = Math.floor(Number(value) * 100) / 10000; - let m = this.props.m; - let ship = this.props.ship; - ship.setModification(m, name, scaledValue); - } - this.props.onChange(); - this.setState({ values }); + return { list }; } /** diff --git a/src/app/shipyard/Module.js b/src/app/shipyard/Module.js index c4eccac6..a8074409 100755 --- a/src/app/shipyard/Module.js +++ b/src/app/shipyard/Module.js @@ -405,8 +405,22 @@ export default class Module { * @return {Number} the DPS of this module */ getDps() { - // TODO this is not correct; need to include other factors such as rate of fire, damage, etc. - return this._getModifiedValue('dps'); + // Modifications are not made to DPS directly, but to damage and rate of fire + + // Obtain unmodified rate of fire + let rof = this['rof']; + + // Obtain unmodified damage + let damage = this['dps'] / rof; + + // Obtain modified rate of fire + let modRof = this._getModifiedValue('rof'); + + // Obtain modified damage + let damageMult = this.getModValue('damage'); + let modDamage = damageMult ? damage * (1 + damageMult) : damage; + + return modDamage * modRof; } /** From f55448c0a8f824c66ec16853593d97728727b69a Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Tue, 1 Nov 2016 07:24:38 +0000 Subject: [PATCH 15/39] Ensure that fuel tank mass is displayed --- src/app/components/InternalSlot.jsx | 3 ++- src/app/components/StandardSlot.jsx | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/app/components/InternalSlot.jsx b/src/app/components/InternalSlot.jsx index e5771787..9266c430 100644 --- a/src/app/components/InternalSlot.jsx +++ b/src/app/components/InternalSlot.jsx @@ -24,10 +24,11 @@ export default class InternalSlot extends Slot { let { termtip, tooltip } = this.context; let validMods = Modifications.validity[m.grp] || []; + let mass = m.getMass() || m.cargo || m.fuel || 0; return
    {classRating} {translate(m.name || m.grp)}
    -
    {formats.round1(m.getMass()) || m.cargo || m.fuel || 0}{u.T}
    +
    {formats.round1(mass)}{u.T}
    { m.getOptMass() ?
    {translate('optimal mass')}: {formats.int(m.getOptMass())}{u.T}
    : null } diff --git a/src/app/components/StandardSlot.jsx b/src/app/components/StandardSlot.jsx index 98771aca..154a9b19 100644 --- a/src/app/components/StandardSlot.jsx +++ b/src/app/components/StandardSlot.jsx @@ -46,6 +46,7 @@ export default class StandardSlot extends TranslatedComponent { let classRating = m.class + m.rating; let menu; let validMods = m == null ? [] : (Modifications.validity[m.grp] || []); + let mass = m.getMass() || m.cargo || m.fuel || 0; if (!selected) { // If not selected then sure that modifications flag is unset @@ -79,7 +80,7 @@ export default class StandardSlot extends TranslatedComponent {
    {slot.maxClass}
    {classRating} {translate(m.grp == 'bh' ? m.grp : m.name || m.grp)}
    -
    {formats.round1(m.getMass()) || m.fuel || 0}{units.T}
    +
    {formats.round1(mass)}{units.T}
    { m.grp == 'bh' && m.name ?
    {translate(m.name)}
    : null } From 06841b1485160f9f3e3a74f24942944241221d3f Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Tue, 1 Nov 2016 12:22:03 +0000 Subject: [PATCH 16/39] Change arc to facinglimit --- src/app/i18n/en.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/i18n/en.js b/src/app/i18n/en.js index 1c9a7de6..a9a52303 100644 --- a/src/app/i18n/en.js +++ b/src/app/i18n/en.js @@ -103,6 +103,7 @@ export const terms = { engrate: 'Engines recharge rate', eps: 'Energy per second', explres: 'Explosive resistance', + facinglimit: 'Facing limit', integrity: 'Integrity', jitter: 'Jitter', kinres: 'Kinetic resistance', From ff0d8dcceac431d615b68a5537219069164eed57 Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Wed, 2 Nov 2016 09:54:52 +0000 Subject: [PATCH 17/39] Handle bulkheads --- src/app/components/StandardSlot.jsx | 2 +- src/app/i18n/en.js | 2 ++ src/app/shipyard/ModuleSet.js | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/app/components/StandardSlot.jsx b/src/app/components/StandardSlot.jsx index 154a9b19..335df054 100644 --- a/src/app/components/StandardSlot.jsx +++ b/src/app/components/StandardSlot.jsx @@ -88,7 +88,7 @@ export default class StandardSlot extends TranslatedComponent { { m.getMaxMass() ?
    {translate('max mass')}: {formats.int(m.getMaxMass())}{units.T}
    : null } { m.getRange() ?
    {translate('range')}: {formats.f2(m.getRange())}{units.km}
    : null } { m.time ?
    {translate('time')}: {formats.time(m.time)}
    : null } - { m.getThermalEfficiency() ?
    {translate('efficiency')}: {formats.f1(m.getThermalEfficiency())}
    : null } + { m.getThermalEfficiency() ?
    {translate('efficiency')}: {formats.f2(m.getThermalEfficiency())}
    : null } { m.getPowerGeneration() > 0 ?
    {translate('pGen')}: {formats.f1(m.getPowerGeneration())}{units.MW}
    : null } { m.getMaxFuelPerJump() ?
    {translate('max')} {translate('fuel')}: {formats.f1(m.getMaxFuelPerJump())}{units.T}
    : null } { m.getWeaponsCapacity() ?
    {translate('WEP')}: {formats.f1(m.getWeaponsCapacity())}{units.MJ} / {formats.f1(m.getWeaponsRechargeRate())}{units.MW}
    : null } diff --git a/src/app/i18n/en.js b/src/app/i18n/en.js index a9a52303..c7604678 100644 --- a/src/app/i18n/en.js +++ b/src/app/i18n/en.js @@ -88,6 +88,7 @@ export const terms = { // Modifications ammo: 'Ammunition maximum', + armour: 'Armour', armourpen: 'Armour penetration', boot: 'Boot time', brokenregen: 'Broken regeneration rate', @@ -104,6 +105,7 @@ export const terms = { eps: 'Energy per second', explres: 'Explosive resistance', facinglimit: 'Facing limit', + hullboost: 'Hull boost', integrity: 'Integrity', jitter: 'Jitter', kinres: 'Kinetic resistance', diff --git a/src/app/shipyard/ModuleSet.js b/src/app/shipyard/ModuleSet.js index 9fa480ee..77a170e3 100755 --- a/src/app/shipyard/ModuleSet.js +++ b/src/app/shipyard/ModuleSet.js @@ -66,7 +66,7 @@ export default class ModuleSet { * @return {Object} Bulkhead module details */ getBulkhead(index) { - return this.bulkheads[index] || null; + return this.bulkheads[index] ? new Module({ template: this.bulkheads[index] }) : null; } /** From b3c82ac2dea9a766fd3e246d69656eab0d167cb3 Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Wed, 2 Nov 2016 10:35:55 +0000 Subject: [PATCH 18/39] Added (non-functional) resistances --- src/app/components/ShipSummaryTable.jsx | 20 +++++++++++++++----- src/app/i18n/en.js | 2 +- src/app/shipyard/Ship.js | 6 ++++++ 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/app/components/ShipSummaryTable.jsx b/src/app/components/ShipSummaryTable.jsx index 1b01c28c..85dadbaf 100644 --- a/src/app/components/ShipSummaryTable.jsx +++ b/src/app/components/ShipSummaryTable.jsx @@ -23,7 +23,7 @@ export default class ShipSummaryTable extends TranslatedComponent { let translate = language.translate; let u = language.units; let formats = language.formats; - let { time, int, round, f1, f2 } = formats; + let { time, int, round, f1, f2, pct } = formats; let armourDetails = null; let sgClassNames = cn({ warning: ship.sgSlot && !ship.shieldStrength, muted: !ship.sgSlot }); let sgRecover = '-'; @@ -42,12 +42,24 @@ export default class ShipSummaryTable extends TranslatedComponent { sgRecharge = time(ship.calcShieldRecharge()); } + //{translate('shield resistance')} + //{translate('hull resistance')} + //{translate('explosive')} + //{translate('kinetic')} + //{translate('thermal')} + //{translate('explosive')} + //{translate('kinetic')} + //{translate('thermal')} + //{pct(ship.shieldExplRes)} + //{pct(ship.shieldKinRes)} + //{pct(ship.shieldThermRes)} + //{pct(ship.hullExplRes)} + //{pct(ship.hullKinRes)} + //{pct(ship.hullThermRes)} return
    - - @@ -79,8 +91,6 @@ export default class ShipSummaryTable extends TranslatedComponent { - - diff --git a/src/app/i18n/en.js b/src/app/i18n/en.js index c7604678..a0e86c82 100644 --- a/src/app/i18n/en.js +++ b/src/app/i18n/en.js @@ -95,7 +95,6 @@ export const terms = { burst: 'Burst', clip: 'Ammunition clip', damage: 'Damage', - delay: 'Delay', dps: 'Damage per second', distdraw: 'Distributor draw', duration: 'Duration', @@ -121,6 +120,7 @@ export const terms = { rof: 'Rate of fire', shield: 'Shield', shieldmul: 'Shield boost', + spinup: 'Spin up time', syscap: 'Systems capacity', sysrate: 'Systems recharge rate', thermload: 'Thermal load', diff --git a/src/app/shipyard/Ship.js b/src/app/shipyard/Ship.js index 9367f689..123efe3c 100755 --- a/src/app/shipyard/Ship.js +++ b/src/app/shipyard/Ship.js @@ -478,6 +478,12 @@ export default class Ship { this.totalDps = 0; this.totalEps = 0; this.totalHps = 0; + this.shieldExplRes = 0; + this.shieldKinRes = 0; + this.shieldThermRes = 0; + this.hullExplRes = 0; + this.hullKinRes = 0; + this.hullThermRes = 0; this.bulkheads.m = null; this.useBulkhead(comps && comps.bulkheads ? comps.bulkheads : 0, true); From 35538f971a1af6a1ca95165d1f709c81e24caf86 Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Wed, 2 Nov 2016 10:47:10 +0000 Subject: [PATCH 19/39] Tidy-ups --- src/app/components/InternalSlotSection.jsx | 2 +- src/app/components/ModificationsMenu.jsx | 4 ---- src/app/components/Slot.jsx | 4 +--- src/app/components/StandardSlot.jsx | 2 +- src/app/components/StandardSlotSection.jsx | 2 -- src/app/shipyard/Module.js | 1 - src/app/shipyard/Ship.js | 2 +- 7 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/app/components/InternalSlotSection.jsx b/src/app/components/InternalSlotSection.jsx index 90e2ca73..5a59a069 100644 --- a/src/app/components/InternalSlotSection.jsx +++ b/src/app/components/InternalSlotSection.jsx @@ -99,7 +99,7 @@ export default class InternalSlotSection extends SlotSection { let slots = []; let { currentMenu, ship } = this.props; let { originSlot, targetSlot } = this.state; - let { internal, fuelCapacity, ladenMass } = ship; + let { internal, fuelCapacity } = ship; let availableModules = ship.getAvailableModules(); for (let i = 0, l = internal.length; i < l; i++) { diff --git a/src/app/components/ModificationsMenu.jsx b/src/app/components/ModificationsMenu.jsx index 871b6a08..bdf633d3 100644 --- a/src/app/components/ModificationsMenu.jsx +++ b/src/app/components/ModificationsMenu.jsx @@ -7,8 +7,6 @@ import { MountFixed, MountGimballed, MountTurret } from './SvgIcons'; import { Modifications } from 'coriolis-data/dist'; import Modification from './Modification'; -const PRESS_THRESHOLD = 500; // mouse/touch down threshold - /** * Modifications menu */ @@ -37,8 +35,6 @@ export default class ModificationsMenu extends TranslatedComponent { * @return {Object} list: Array of React Components */ _initState(props, context) { - let translate = context.language.translate; - let formats = context.language.formats; let { m, onChange, ship } = props; let list = []; diff --git a/src/app/components/Slot.jsx b/src/app/components/Slot.jsx index f15bfaf4..b1c66ba1 100644 --- a/src/app/components/Slot.jsx +++ b/src/app/components/Slot.jsx @@ -79,11 +79,9 @@ export default class Slot extends TranslatedComponent { */ render() { let language = this.context.language; - let { termtip, tooltip } = this.context; let translate = language.translate; - let { ship, m, dropClass, dragOver, onOpen, onChange, selected, eligible, onSelect, warning, shipMass, availableModules } = this.props; + let { ship, m, dropClass, dragOver, onOpen, onChange, selected, eligible, onSelect, warning, availableModules } = this.props; let slotDetails, menu; - let validMods = m == null ? [] : (Modifications.validity[m.grp] || []); if (!selected) { // If not selected then sure that modifications flag is unset diff --git a/src/app/components/StandardSlot.jsx b/src/app/components/StandardSlot.jsx index 335df054..1073e548 100644 --- a/src/app/components/StandardSlot.jsx +++ b/src/app/components/StandardSlot.jsx @@ -41,7 +41,7 @@ export default class StandardSlot extends TranslatedComponent { render() { let { termtip, tooltip } = this.context; let { translate, formats, units } = this.context.language; - let { modules, slot, selected, warning, onSelect, onChange, ladenMass, ship } = this.props; + let { modules, slot, selected, warning, onSelect, onChange, ship } = this.props; let m = slot.m; let classRating = m.class + m.rating; let menu; diff --git a/src/app/components/StandardSlotSection.jsx b/src/app/components/StandardSlotSection.jsx index af99ce43..24b25926 100644 --- a/src/app/components/StandardSlotSection.jsx +++ b/src/app/components/StandardSlotSection.jsx @@ -86,12 +86,10 @@ export default class StandardSlotSection extends SlotSection { * @return {Array} Array of Slots */ _getSlots() { - let { translate, units, formats } = this.context.language; let { ship, currentMenu } = this.props; let slots = new Array(8); let open = this._openMenu; let select = this._selectModule; - let selBulkhead = this._selectBulkhead; let st = ship.standard; let avail = ship.getAvailableModules().standard; let bh = ship.bulkheads; diff --git a/src/app/shipyard/Module.js b/src/app/shipyard/Module.js index a8074409..dca61b1e 100755 --- a/src/app/shipyard/Module.js +++ b/src/app/shipyard/Module.js @@ -151,7 +151,6 @@ export default class Module { */ getHeatPerSecond() { // Modifier for hps is thermload - return this._getModifiedValue('hps'); let result = 0; if (this['hps']) { result = this['hps']; diff --git a/src/app/shipyard/Ship.js b/src/app/shipyard/Ship.js index 123efe3c..bd2c8fec 100755 --- a/src/app/shipyard/Ship.js +++ b/src/app/shipyard/Ship.js @@ -888,7 +888,7 @@ export default class Ship { */ updateJumpStats() { let fsd = this.standard[2].m; // Frame Shift Drive; - let { unladenMass, ladenMass, fuelCapacity } = this; + let { unladenMass, fuelCapacity } = this; this.unladenRange = this.calcUnladenRange(); // Includes fuel weight for jump this.fullTankRange = Calc.jumpRange(unladenMass + fuelCapacity, fsd); // Full Tank this.ladenRange = this.calcLadenRange(); // Includes full tank and caro From 7c71555384f48ec086a6ad23495a5e67938ea654 Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Wed, 2 Nov 2016 11:03:29 +0000 Subject: [PATCH 20/39] Added facing limit and range for fsdi --- src/app/components/InternalSlot.jsx | 2 ++ src/app/i18n/Language.jsx | 1 + src/app/i18n/en.js | 1 + src/app/shipyard/Module.js | 18 +++++++++++++++++- 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/app/components/InternalSlot.jsx b/src/app/components/InternalSlot.jsx index 9266c430..a1f8b4a1 100644 --- a/src/app/components/InternalSlot.jsx +++ b/src/app/components/InternalSlot.jsx @@ -40,7 +40,9 @@ export default class InternalSlot extends Slot { { m.cells ?
    {translate('cells')}: {m.cells}
    : null } { m.recharge ?
    {translate('recharge')}: {m.recharge} MJ   {translate('total')}: {m.cells * m.recharge}{u.MJ}
    : null } { m.repair ?
    {translate('repair')}: {m.repair}
    : null } + { m.getFacingLimit() ?
    {translate('facinglimit')} {formats.f1(m.getFacingLimit())}°
    : null } { m.getRange() ?
    {translate('range')} {formats.f2(m.getRange())}{u.km}
    : null } + { m.getRangeT() ?
    {translate('ranget')} {formats.f1(m.getRangeT())}{u.s}
    : null } { m.time ?
    {translate('time')}: {formats.time(m.time)}
    : null } { m.maximum ?
    {translate('max')}: {(m.maximum)}
    : null } { m.rangeLS ?
    {translate('range')}: {m.rangeLS}{u.Ls}
    : null } diff --git a/src/app/i18n/Language.jsx b/src/app/i18n/Language.jsx index fb4ea958..da82304f 100644 --- a/src/app/i18n/Language.jsx +++ b/src/app/i18n/Language.jsx @@ -66,6 +66,7 @@ export function getLanguage(langCode) { MW: {translate('MW')}, // Mega Watts (same as Mega Joules per second) ps: {translate('/s')}, // per second pm: {translate('/min')}, // per minute + s: {translate('secs')}, // Seconds T: {translate('T')}, // Metric Tons } }; diff --git a/src/app/i18n/en.js b/src/app/i18n/en.js index a0e86c82..7018e20c 100644 --- a/src/app/i18n/en.js +++ b/src/app/i18n/en.js @@ -115,6 +115,7 @@ export const terms = { pGen: 'Power generation', power: 'Power draw', range: 'Range', + ranget: 'Range', // Range in time (for FSD interdictor) regen: 'Regeneration rate', reload: 'Reload time', rof: 'Rate of fire', diff --git a/src/app/shipyard/Module.js b/src/app/shipyard/Module.js index dca61b1e..ca25fa76 100755 --- a/src/app/shipyard/Module.js +++ b/src/app/shipyard/Module.js @@ -259,13 +259,21 @@ export default class Module { } /** - * Get the range rate for this module, taking in to account modifications + * Get the range for this module, taking in to account modifications * @return {Number} the range rate of this module */ getRange() { return this._getModifiedValue('range'); } + /** + * Get the range (in terms of seconds, for FSDI) for this module, taking in to account modifications + * @return {Number} the range of this module + */ + getRangeT() { + return this._getModifiedValue('ranget'); + } + /** * Get the capture arc for this module, taking in to account modifications * @return {Number} the capture arc of this module @@ -471,4 +479,12 @@ export default class Module { getRoF() { return this._getModifiedValue('rof'); } + + /** + * Get the facing limit for this module, taking in to account modifications + * @return {Number} the facing limit for this module + */ + getFacingLimit() { + return this._getModifiedValue('facinglimit'); + } } From c490f97c22aee6fecb48d221ee0e05e9b5c7052e Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Wed, 2 Nov 2016 11:11:45 +0000 Subject: [PATCH 21/39] Use modifiers when calculating regen rates --- src/app/shipyard/Module.js | 4 ++-- src/app/shipyard/Ship.js | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/app/shipyard/Module.js b/src/app/shipyard/Module.js index ca25fa76..964c659f 100755 --- a/src/app/shipyard/Module.js +++ b/src/app/shipyard/Module.js @@ -29,7 +29,7 @@ export default class Module { /** * Get a value for a given modification * @param {Number} name The name of the modification - * @return {Number} The value of the modification, as a decimal value from -1 to 1 + * @return {Number} The value of the modification, as a decimal value where 1 is 100% */ getModValue(name) { return this.mods && this.mods[name] ? this.mods[name] / 10000 : null; @@ -38,7 +38,7 @@ export default class Module { /** * Set a value for a given modification ID * @param {Number} name The name of the modification - * @param {Number} value The value of the modification, as a decimal value from -1 to 1 + * @param {Number} value The value of the modification, as a decimal value where 1 is 100% */ setModValue(name, value) { if (value == null || value == 0) { diff --git a/src/app/shipyard/Ship.js b/src/app/shipyard/Ship.js index bd2c8fec..69c064a0 100755 --- a/src/app/shipyard/Ship.js +++ b/src/app/shipyard/Ship.js @@ -212,8 +212,10 @@ export default class Ship { */ calcShieldRecovery() { if (this.shieldStrength && this.sgSlot) { + let brokenRegenRate = 1 + this.sgSlot.m.getModValue('brokenregen'); + console.log('Broken regen rate is ' + brokenRegenRate); // 50% of shield strength / recovery recharge rate + 15 second delay before recharge starts - return ((this.shieldStrength / 2) / this.sgSlot.m.recover) + 15; + return ((this.shieldStrength / 2) / (this.sgSlot.m.recover * brokenRegenRate)) + 15; } return 0; } @@ -226,8 +228,9 @@ export default class Ship { */ calcShieldRecharge() { if (this.shieldStrength && this.sgSlot) { + let regenRate = 1 + this.sgSlot.m.getModValue('regen'); // 50% -> 100% recharge time, Bi-Weave shields charge at 1.8 MJ/s - return (this.shieldStrength / 2) / (this.sgSlot.m.grp == 'bsg' ? 1.8 : 1); + return (this.shieldStrength / 2) / ((this.sgSlot.m.grp == 'bsg' ? 1.8 : 1) * regenRate); } return 0; } From c88e4369c83376e83ceee7053acdb4cb9077a85a Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Wed, 2 Nov 2016 13:32:49 +0000 Subject: [PATCH 22/39] Update to handle armour --- src/app/components/InternalSlot.jsx | 4 +-- src/app/components/ShipSummaryTable.jsx | 10 +----- src/app/i18n/en.js | 7 ++-- src/app/shipyard/Constants.js | 8 ----- src/app/shipyard/Module.js | 17 +++++++--- src/app/shipyard/Ship.js | 43 ++++++++++++++++++------- 6 files changed, 52 insertions(+), 37 deletions(-) diff --git a/src/app/components/InternalSlot.jsx b/src/app/components/InternalSlot.jsx index a1f8b4a1..cb813c88 100644 --- a/src/app/components/InternalSlot.jsx +++ b/src/app/components/InternalSlot.jsx @@ -20,7 +20,7 @@ export default class InternalSlot extends Slot { _getSlotDetails(m, translate, formats, u) { if (m) { let classRating = m.class + m.rating; - let { drag, drop } = this.props; + let { drag, drop, ship } = this.props; let { termtip, tooltip } = this.context; let validMods = Modifications.validity[m.grp] || []; @@ -48,7 +48,7 @@ export default class InternalSlot extends Slot { { m.rangeLS ?
    {translate('range')}: {m.rangeLS}{u.Ls}
    : null } { m.rangeLS === null ?
    ∞{u.Ls}
    : null } { m.rangeRating ?
    {translate('range')}: {m.rangeRating}
    : null } - { m.armouradd ?
    +{m.armouradd} {translate('armour')}
    : null } + { m.getHullReinforcement() ?
    +{formats.int(m.getHullReinforcement() + ship.baseArmour * m.getModValue('hullboost'))} {translate('armour')}
    : null } { m.passengers ?
    {translate('passengers')}: {m.passengers}
    : null } { m && validMods.length > 0 ?
    : null } diff --git a/src/app/components/ShipSummaryTable.jsx b/src/app/components/ShipSummaryTable.jsx index 85dadbaf..5f5f415e 100644 --- a/src/app/components/ShipSummaryTable.jsx +++ b/src/app/components/ShipSummaryTable.jsx @@ -24,19 +24,11 @@ export default class ShipSummaryTable extends TranslatedComponent { let u = language.units; let formats = language.formats; let { time, int, round, f1, f2, pct } = formats; - let armourDetails = null; let sgClassNames = cn({ warning: ship.sgSlot && !ship.shieldStrength, muted: !ship.sgSlot }); let sgRecover = '-'; let sgRecharge = '-'; let hide = tooltip.bind(null, null); - if (ship.armourMultiplier > 1 || ship.armourAdded) { - armourDetails = ({ - (ship.armourMultiplier > 1 ? formats.rPct(ship.armourMultiplier) : '') + - (ship.armourAdded ? ' + ' + ship.armourAdded : '') - }); - } - if (ship.shieldStrength) { sgRecover = time(ship.calcShieldRecovery()); sgRecharge = time(ship.calcShieldRecharge()); @@ -96,7 +88,7 @@ export default class ShipSummaryTable extends TranslatedComponent {
    - + diff --git a/src/app/i18n/en.js b/src/app/i18n/en.js index 7018e20c..9f3b18f4 100644 --- a/src/app/i18n/en.js +++ b/src/app/i18n/en.js @@ -37,10 +37,11 @@ export const terms = { bsg: 'Bi-Weave Shield Generator', c: 'Cannon', cc: 'Collector Limpet Controller', - cm: 'Countermeasure', + ch: 'Chaff Launcher', cr: 'Cargo Rack', cs: 'Manifest Scanner', dc: 'Docking Computer', + ec: 'Electronic Countermeasure', fc: 'Fragment Cannon', fh: 'Fighter Hangar', fi: 'FSD Interdictor', @@ -50,6 +51,7 @@ export const terms = { fx: 'Fuel Transfer Limpet Controller', hb: 'Hatch Breaker Limpet Controller', hr: 'Hull Reinforcement Package', + hs: 'Heat Sink Launcher', kw: 'Kill Warrant Scanner', ls: 'Life Support', mc: 'Multi-cannon', @@ -65,6 +67,7 @@ export const terms = { pcq: 'Luxury Passenger Cabin', pd: 'power distributor', pl: 'Pulse Laser', + po: 'Point Defence', pp: 'Power Plant', psg: 'Prismatic Shield Generator', pv: 'Planetary Vehicle Hangar', @@ -88,7 +91,6 @@ export const terms = { // Modifications ammo: 'Ammunition maximum', - armour: 'Armour', armourpen: 'Armour penetration', boot: 'Boot time', brokenregen: 'Broken regeneration rate', @@ -105,6 +107,7 @@ export const terms = { explres: 'Explosive resistance', facinglimit: 'Facing limit', hullboost: 'Hull boost', + hullreinforcement: 'Hull reinforcement', integrity: 'Integrity', jitter: 'Jitter', kinres: 'Kinetic resistance', diff --git a/src/app/shipyard/Constants.js b/src/app/shipyard/Constants.js index 66898802..566bcf06 100755 --- a/src/app/shipyard/Constants.js +++ b/src/app/shipyard/Constants.js @@ -1,12 +1,4 @@ -export const ArmourMultiplier = [ - 1, // Lightweight - 1.4, // Reinforced - 1.945, // Military - 1.945, // Mirrored - 1.945 // Reactive -]; - export const SizeMap = ['', 'small', 'medium', 'large', 'capital']; export const StandardArray = [ diff --git a/src/app/shipyard/Module.js b/src/app/shipyard/Module.js index 964c659f..5666b2bc 100755 --- a/src/app/shipyard/Module.js +++ b/src/app/shipyard/Module.js @@ -283,11 +283,11 @@ export default class Module { } /** - * Get the armour for this module, taking in to account modifications - * @return {Number} the armour of this module + * Get the hull reinforcement for this module, taking in to account modifications + * @return {Number} the hull reinforcement of this module */ - getArmour() { - return this._getModifiedValue('armour'); + getHullReinforcement() { + return this._getModifiedValue('hullreinforcement'); } /** @@ -487,4 +487,13 @@ export default class Module { getFacingLimit() { return this._getModifiedValue('facinglimit'); } + + /** + * Get the hull boost for this module, taking in to account modifications + * @return {Number} the hull boost for this module + */ + getHullBoost() { + return this._getModifiedValue('hullboost'); + } + } diff --git a/src/app/shipyard/Ship.js b/src/app/shipyard/Ship.js index 69c064a0..20dc485e 100755 --- a/src/app/shipyard/Ship.js +++ b/src/app/shipyard/Ship.js @@ -1,4 +1,3 @@ -import { ArmourMultiplier } from './Constants'; import * as Calc from './Calculations'; import * as ModuleUtils from './ModuleUtils'; import Module from './Module'; @@ -213,7 +212,6 @@ export default class Ship { calcShieldRecovery() { if (this.shieldStrength && this.sgSlot) { let brokenRegenRate = 1 + this.sgSlot.m.getModValue('brokenregen'); - console.log('Broken regen rate is ' + brokenRegenRate); // 50% of shield strength / recovery recharge rate + 15 second delay before recharge starts return ((this.shieldStrength / 2) / (this.sgSlot.m.recover * brokenRegenRate)) + 15; } @@ -447,6 +445,9 @@ export default class Ship { } else if (name == 'shieldmul') { m.setModValue(name, value); this.updateShieldStrength(); + } else if (name == 'hullboost') { + m.setModValue(name, value); + this.updateArmour(); } else { // Generic m.setModValue(name, value); @@ -473,8 +474,7 @@ export default class Ship { this.fuelCapacity = 0; this.cargoCapacity = 0; this.ladenMass = 0; - this.armourAdded = 0; - this.armourMultiplier = 1; + this.armour = this.baseArmour; this.shieldMultiplier = 1; this.totalCost = this.m.incCost ? this.m.discountedCost : 0; this.unladenMass = this.hullMass; @@ -559,6 +559,7 @@ export default class Ship { this.updatePower() .updateJumpStats() .updateShieldStrength() + .updateArmour() .updateTopSpeed(); } @@ -751,6 +752,8 @@ export default class Ship { updateStats(slot, n, old, preventUpdate) { let powerChange = slot == this.standard[0]; + let armourChange = (slot == this.bulkheads) || (n && n.grp == 'hr') || (old && old.grp == 'hr'); + if (old) { // Old modul now being removed switch (old.grp) { case 'ft': @@ -759,9 +762,6 @@ export default class Ship { case 'cr': this.cargoCapacity -= old.cargo; break; - case 'hr': - this.armourAdded -= old.armouradd; - break; case 'sb': this.shieldMultiplier -= slot.enabled ? old.getShieldMul() : 0; break; @@ -801,9 +801,6 @@ export default class Ship { case 'cr': this.cargoCapacity += n.cargo; break; - case 'hr': - this.armourAdded += n.armouradd; - break; case 'sb': this.shieldMultiplier += slot.enabled ? n.getShieldMul() : 0; break; @@ -831,12 +828,14 @@ export default class Ship { } this.ladenMass = this.unladenMass + this.cargoCapacity + this.fuelCapacity; - this.armour = this.armourAdded + Math.round(this.baseArmour * this.armourMultiplier); if (!preventUpdate) { if (powerChange) { this.updatePower(); } + if (armourChange) { + } + this.updateArmour(); this.updateTopSpeed(); this.updateJumpStats(); this.updateShieldStrength(); @@ -885,6 +884,27 @@ export default class Ship { return this; } + /** + * Update armour + * @return {this} The ship instance (for chaining operations) + */ + updateArmour() { + // Armour from bulkheads + let bulkhead = this.bulkheads.m; + let armour = this.baseArmour + (this.baseArmour * bulkhead.getHullBoost()); + + // Armour from HRPs + for (let slot of this.internal) { + if (slot.m && slot.m.grp == 'hr') { + armour += slot.m.getHullReinforcement(); + // Hull boost for HRPs is applied against the ship's base armour + armour += this.baseArmour * slot.m.getModValue('hullboost'); + } + } + this.armour = armour; + return this; + } + /** * Jump Range and total range calculations * @return {this} The ship instance (for chaining operations) @@ -1049,7 +1069,6 @@ export default class Ship { let oldBulkhead = this.bulkheads.m; this.bulkheads.m = this.availCS.getBulkhead(index); this.bulkheads.discountedCost = this.bulkheads.m.cost * this.moduleCostMultiplier; - this.armourMultiplier = ArmourMultiplier[index]; this.updateStats(this.bulkheads, this.bulkheads.m, oldBulkhead, preventUpdate); this.serialized.standard = null; return this; From ba1f11a88b035f1165f9dc70e62dd92f31be7784 Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Wed, 2 Nov 2016 14:14:15 +0000 Subject: [PATCH 23/39] Updates for shield strength --- src/app/components/ShipSummaryTable.jsx | 34 ++++++------ src/app/shipyard/Module.js | 8 +-- src/app/shipyard/Ship.js | 73 +++++++++++++++---------- 3 files changed, 65 insertions(+), 50 deletions(-) diff --git a/src/app/components/ShipSummaryTable.jsx b/src/app/components/ShipSummaryTable.jsx index 5f5f415e..a98dd690 100644 --- a/src/app/components/ShipSummaryTable.jsx +++ b/src/app/components/ShipSummaryTable.jsx @@ -24,30 +24,30 @@ export default class ShipSummaryTable extends TranslatedComponent { let u = language.units; let formats = language.formats; let { time, int, round, f1, f2, pct } = formats; - let sgClassNames = cn({ warning: ship.sgSlot && !ship.shieldStrength, muted: !ship.sgSlot }); + let sgClassNames = cn({ warning: ship.sgSlot && !ship.shield, muted: !ship.sgSlot }); let sgRecover = '-'; let sgRecharge = '-'; let hide = tooltip.bind(null, null); - if (ship.shieldStrength) { + if (ship.shield) { sgRecover = time(ship.calcShieldRecovery()); sgRecharge = time(ship.calcShieldRecharge()); } - // - // - // - // - // - // - // - // - // - // - // - // - // - // + // + // + // + // + // + // + // + // + // + // + // + // + // + // return
    {translate('size')}{translate('MNV')} {translate('speed')} {translate('boost')} {translate('DPS')}
    {translate(SizeMap[ship.class])}{ship.agility}/10 { ship.canThrust() ? {int(ship.topSpeed)} {u['m/s']} : 0 } { ship.canBoost() ? {int(ship.topBoost)} {u['m/s']} : 0 } {f1(ship.totalDps)}{f1(ship.totalDps)} {f1(ship.totalEps)} {f1(ship.totalHps)}{int(ship.armour)} {armourDetails}{int(ship.armour)} {int(ship.shieldStrength)} {u.MJ} { ship.shieldMultiplier > 1 && ship.shieldStrength > 0 ? ({formats.rPct(ship.shieldMultiplier)}) : null } {sgRecover} {sgRecharge}{translate('shield resistance')}{translate('hull resistance')}{translate('explosive')}{translate('kinetic')}{translate('thermal')}{translate('explosive')}{translate('kinetic')}{translate('thermal')}{pct(ship.shieldExplRes)}{pct(ship.shieldKinRes)}{pct(ship.shieldThermRes)}{pct(ship.hullExplRes)}{pct(ship.hullKinRes)}{pct(ship.hullThermRes)}{translate('shield resistance')}{translate('hull resistance')}{translate('explosive')}{translate('kinetic')}{translate('thermal')}{translate('explosive')}{translate('kinetic')}{translate('thermal')}{pct(ship.shieldExplRes)}{pct(ship.shieldKinRes)}{pct(ship.shieldThermRes)}{pct(ship.hullExplRes)}{pct(ship.hullKinRes)}{pct(ship.hullThermRes)}
    @@ -89,7 +89,7 @@ export default class ShipSummaryTable extends TranslatedComponent { - + diff --git a/src/app/shipyard/Module.js b/src/app/shipyard/Module.js index 5666b2bc..5bc804b0 100755 --- a/src/app/shipyard/Module.js +++ b/src/app/shipyard/Module.js @@ -307,11 +307,11 @@ export default class Module { } /** - * Get the shield reinforcement for this module, taking in to account modifications - * @return {Number} the shield reinforcement of this module + * Get the shield multiplier for this module, taking in to account modifications + * @return {Number} the shield multiplier of this module */ - getShieldReinforcement() { - return this._getModifiedValue('shieldreinforcement'); + getShieldMultiplier() { + return this._getModifiedValue('shieldmul'); } /** diff --git a/src/app/shipyard/Ship.js b/src/app/shipyard/Ship.js index 20dc485e..16a6aa43 100755 --- a/src/app/shipyard/Ship.js +++ b/src/app/shipyard/Ship.js @@ -210,10 +210,11 @@ export default class Ship { * @return {Number} Recovery time in seconds */ calcShieldRecovery() { - if (this.shieldStrength && this.sgSlot) { - let brokenRegenRate = 1 + this.sgSlot.m.getModValue('brokenregen'); + if (this.shield > 0) { + let sgSlot = this.findInternalByGroup('sg'); + let brokenRegenRate = 1 + sgSlot.m.getModValue('brokenregen'); // 50% of shield strength / recovery recharge rate + 15 second delay before recharge starts - return ((this.shieldStrength / 2) / (this.sgSlot.m.recover * brokenRegenRate)) + 15; + return ((this.shield / 2) / (sgSlot.m.recover * brokenRegenRate)) + 15; } return 0; } @@ -225,10 +226,11 @@ export default class Ship { * @return {Number} 50 - 100% Recharge time in seconds */ calcShieldRecharge() { - if (this.shieldStrength && this.sgSlot) { - let regenRate = 1 + this.sgSlot.m.getModValue('regen'); + if (this.shield > 0) { + let sgSlot = this.findInternalByGroup('sg'); + let regenRate = 1 + sgSlot.m.getModValue('regen'); // 50% -> 100% recharge time, Bi-Weave shields charge at 1.8 MJ/s - return (this.shieldStrength / 2) / ((this.sgSlot.m.grp == 'bsg' ? 1.8 : 1) * regenRate); + return (this.shield / 2) / ((sgSlot.m.grp == 'bsg' ? 1.8 : 1) * regenRate); } return 0; } @@ -241,12 +243,16 @@ export default class Ship { */ calcShieldStrengthWith(sg, multiplierDelta) { if (!sg) { - if (!this.sgSlot) { + let sgSlot = this.findInternalByGroup('sg'); + if (!sgSlot) { return 0; } - sg = this.sgSlot.m; + sg = sgSlot.m; } - return Calc.shieldStrength(this.hullMass, this.baseShieldStrength, sg, this.shieldMultiplier + (multiplierDelta || 0)); + + // TODO obtain shieldMultiplier + //return Calc.shieldStrength(this.hullMass, this.baseShieldStrength, sg, this.shieldMultiplier + (multiplierDelta || 0)); + return Calc.shieldStrength(this.hullMass, this.baseShieldStrength, sg, 0 + (multiplierDelta || 0)); } /** @@ -444,7 +450,7 @@ export default class Ship { this.updateJumpStats(); } else if (name == 'shieldmul') { m.setModValue(name, value); - this.updateShieldStrength(); + this.updateShield(); } else if (name == 'hullboost') { m.setModValue(name, value); this.updateArmour(); @@ -475,7 +481,7 @@ export default class Ship { this.cargoCapacity = 0; this.ladenMass = 0; this.armour = this.baseArmour; - this.shieldMultiplier = 1; + this.shield = this.baseShieldStrength; this.totalCost = this.m.incCost ? this.m.discountedCost : 0; this.unladenMass = this.hullMass; this.totalDps = 0; @@ -558,7 +564,7 @@ export default class Ship { if (comps) { this.updatePower() .updateJumpStats() - .updateShieldStrength() + .updateShield() .updateArmour() .updateTopSpeed(); } @@ -695,11 +701,8 @@ export default class Ship { if (slot.m) { this.priorityBands[slot.priority][powerUsageType(slot, slot.m)] += enabled ? slot.m.getPowerUsage() : - slot.m.getPowerUsage(); - if (ModuleUtils.isShieldGenerator(slot.m.grp)) { - this.updateShieldStrength(); - } else if (slot.m.grp == 'sb') { - this.shieldMultiplier += slot.m.getShieldMul() * (enabled ? 1 : -1); - this.updateShieldStrength(); + if (ModuleUtils.isShieldGenerator(slot.m.grp) || slot.m.grp == 'sb') { + this.updateShield(); } if (slot.m.dps) { this.totalDps += slot.m.dps * (enabled ? 1 : -1); @@ -754,6 +757,8 @@ export default class Ship { let armourChange = (slot == this.bulkheads) || (n && n.grp == 'hr') || (old && old.grp == 'hr'); + let shieldChange = (n && n.grp == 'bsg') || (old && old.grp == 'bsg') || (n && n.grp == 'psg') || (old && old.grp == 'psg') || (n && n.grp == 'sg') || (old && old.grp == 'sg') || (n && n.grp == 'sb') || (old && old.grp == 'sb'); + if (old) { // Old modul now being removed switch (old.grp) { case 'ft': @@ -762,9 +767,6 @@ export default class Ship { case 'cr': this.cargoCapacity -= old.cargo; break; - case 'sb': - this.shieldMultiplier -= slot.enabled ? old.getShieldMul() : 0; - break; } if (slot.incCost && old.cost) { @@ -801,9 +803,6 @@ export default class Ship { case 'cr': this.cargoCapacity += n.cargo; break; - case 'sb': - this.shieldMultiplier += slot.enabled ? n.getShieldMul() : 0; - break; } if (slot.incCost && n.cost) { @@ -834,11 +833,13 @@ export default class Ship { this.updatePower(); } if (armourChange) { + this.updateArmour(); + } + if (shieldChange) { + this.updateShield(); } - this.updateArmour(); this.updateTopSpeed(); this.updateJumpStats(); - this.updateShieldStrength(); } return this; } @@ -875,12 +876,26 @@ export default class Ship { } /** - * Update Shield strength + * Update shield * @return {this} The ship instance (for chaining operations) */ - updateShieldStrength() { - this.sgSlot = this.findInternalByGroup('sg'); // Find Shield Generator slot Index if any - this.shieldStrength = this.sgSlot && this.sgSlot.enabled ? Calc.shieldStrength(this.hullMass, this.baseShieldStrength, this.sgSlot.m, this.shieldMultiplier) : 0; + updateShield() { + // Base shield from generator + let baseShield = 0; + let sgSlot = this.findInternalByGroup('sg'); + if (sgSlot && sgSlot.enabled) { + baseShield = Calc.shieldStrength(this.hullMass, this.baseShieldStrength, sgSlot.m, 1); + } + + let shield = baseShield; + + // Shield from boosters + for (let slot of this.hardpoints) { + if (slot.m && slot.m.grp == 'sb') { + shield += baseShield * slot.m.getShieldMultiplier(); + } + } + this.shield = shield; return this; } From 6727c2fc134a5c3ae898fc1705666879b2d8abcb Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Thu, 3 Nov 2016 10:52:54 +0000 Subject: [PATCH 24/39] Move to pure damage/distdraw/thermload numbers --- src/app/components/HardpointSlot.jsx | 2 +- src/app/shipyard/Module.js | 121 +++++++++++++-------------- src/app/shipyard/Ship.js | 36 ++++---- 3 files changed, 79 insertions(+), 80 deletions(-) diff --git a/src/app/components/HardpointSlot.jsx b/src/app/components/HardpointSlot.jsx index ede05551..11c1b0f4 100644 --- a/src/app/components/HardpointSlot.jsx +++ b/src/app/components/HardpointSlot.jsx @@ -59,7 +59,7 @@ export default class HardpointSlot extends Slot { { m.getDps() ?
    {translate('DPS')}: {formats.round1(m.getDps())} { m.getClip() ? ({formats.round1((m.getClip() * m.getDps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload())) }) : null }
    : null } { m.getEps() ?
    {translate('EPS')}: {formats.round1(m.getEps())}{u.MW} { m.getClip() ? ({formats.round1((m.getClip() * m.getEps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload())) }{u.MW}) : null }
    : null } { m.getHps() ?
    {translate('HPS')}: {formats.round1(m.getHps())} { m.getClip() ? ({formats.round1((m.getClip() * m.getHps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload())) }) : null }
    : null } - { m.getDps() && m.getEps() ?
    {translate('DPE')}: {formats.round1(m.getDps() / m.getEps())}
    : null } + { m.getDps() && m.getEps() ?
    {translate('DPE')}: {formats.f1(m.getDps() / m.getEps())}
    : null } { m.getRoF() ?
    {translate('ROF')}: {formats.f1(m.getRoF())}{u.ps}
    : null } { m.getRange() && !m.getDps() ?
    {translate('Range')} : {formats.round(m.getRange() / 1000)}{u.km}
    : null } { m.getShieldMul() ?
    +{formats.rPct(m.getShieldMul())}
    : null } diff --git a/src/app/shipyard/Module.js b/src/app/shipyard/Module.js index 5bc804b0..d97b7580 100755 --- a/src/app/shipyard/Module.js +++ b/src/app/shipyard/Module.js @@ -129,39 +129,6 @@ export default class Module { return this._getModifiedValue('optmult'); } - /** - * Get the damage per second for this module, taking in to account modifications - * @return {Number} the damage per second of this module - */ - getDamagePerSecond() { - return this._getModifiedValue('dps'); - } - - /** - * Get the energy per second for this module, taking in to account modifications - * @return {Number} the energy per second of this module - */ - getEnergyPerSecond() { - return this._getModifiedValue('eps'); - } - - /** - * Get the heat per second for this module, taking in to account modifications - * @return {Number} the heat per second of this module - */ - getHeatPerSecond() { - // Modifier for hps is thermload - let result = 0; - if (this['hps']) { - result = this['hps']; - if (result) { - let mult = this.getModValue('thermload'); - if (mult) { result = result * (1 + mult); } - } - } - return result; - } - /** * Get the maximum fuel per jump for this module, taking in to account modifications * @return {Number} the maximum fuel per jump of this module @@ -406,46 +373,78 @@ export default class Module { return this._getModifiedValue('shieldmul'); } + /** + * Get the damage for this module, taking in to account modifications + * @return {Number} the damage of this module + */ + getDamage() { + return this._getModifiedValue('damage'); + } + + /** + * Get the distributor draw for this module, taking in to account modifications + * @return {Number} the distributor draw of this module + */ + getDistDraw() { + return this._getModifiedValue('distdraw'); + } + + /** + * Get the thermal load for this module, taking in to account modifications + * @return {Number} the thermal load of this module + */ + getThermalLoad() { + return this._getModifiedValue('thermload'); + } + + /** + * Get the rounds per shot for this module, taking in to account modifications + * @return {Number} the rounds per shot of this module + */ + getRoundsPerShot() { + return this._getModifiedValue('roundspershot'); + } /** * Get the DPS for this module, taking in to account modifications * @return {Number} the DPS of this module */ getDps() { - // Modifications are not made to DPS directly, but to damage and rate of fire - - // Obtain unmodified rate of fire - let rof = this['rof']; + // DPS is a synthetic value + let damage = this.getDamage(); + let rpshot = this.getRoundsPerShot() || 1; + let rof = this.getRoF(); - // Obtain unmodified damage - let damage = this['dps'] / rof; - - // Obtain modified rate of fire - let modRof = this._getModifiedValue('rof'); - - // Obtain modified damage - let damageMult = this.getModValue('damage'); - let modDamage = damageMult ? damage * (1 + damageMult) : damage; - - return modDamage * modRof; + return damage * rpshot * rof; } /** - * Get the heat generated per second for this module, taking in to account modifications - * @return {Number} the heat generated per second of this module - */ - getHps() { - // TODO this is not correct; need to include other factors such as rate of fire, damage, etc. - return this._getModifiedValue('hps'); - } - - /** - * Get the energy used per second for this module, taking in to account modifications - * @return {Number} the energy used per second of this module + * Get the EPS for this module, taking in to account modifications + * @return {Number} the EPS of this module */ getEps() { - // TODO this is not correct; need to include other factors such as rate of fire, damage, etc. - return this._getModifiedValue('eps'); + // EPS is a synthetic value + let distdraw = this.getDistDraw(); + console.log('distdraw is ' + distdraw); + let rpshot = this.getRoundsPerShot() || 1; + console.log('rpshot is ' + rpshot); + let rof = this.getRoF(); + console.log('rof is ' + rof); + + return distdraw * rpshot * rof; + } + + /** + * Get the HPS for this module, taking in to account modifications + * @return {Number} the HPS of this module + */ + getHps() { + // HPS is a synthetic value + let heat = this.getThermalLoad(); + let rpshot = this.getRoundsPerShot() || 1; + let rof = this.getRoF(); + + return heat * rpshot * rof; } /** diff --git a/src/app/shipyard/Ship.js b/src/app/shipyard/Ship.js index 16a6aa43..d9b5c2b5 100755 --- a/src/app/shipyard/Ship.js +++ b/src/app/shipyard/Ship.js @@ -704,14 +704,14 @@ export default class Ship { if (ModuleUtils.isShieldGenerator(slot.m.grp) || slot.m.grp == 'sb') { this.updateShield(); } - if (slot.m.dps) { - this.totalDps += slot.m.dps * (enabled ? 1 : -1); + if (slot.m.getDps()) { + this.totalDps += slot.m.getDps() * (enabled ? 1 : -1); } - if (slot.m.eps) { - this.totalEps += slot.m.eps * (enabled ? 1 : -1); + if (slot.m.getEps()) { + this.totalEps += slot.m.getEps() * (enabled ? 1 : -1); } - if (slot.m.hps) { - this.totalHps += slot.m.hps * (enabled ? 1 : -1); + if (slot.m.getHps()) { + this.totalHps += slot.m.getHps() * (enabled ? 1 : -1); } this.updatePower(); @@ -782,14 +782,14 @@ export default class Ship { this.priorityBands[slot.priority][powerUsageType(slot, old)] -= old.getPowerUsage(); powerChange = true; - if (old.dps) { - this.totalDps -= old.dps; + if (old.getDps()) { + this.totalDps -= old.getDps(); } - if (old.eps) { - this.totalEps -= old.eps; + if (old.getEps()) { + this.totalEps -= old.getEps(); } - if (old.hps) { - this.totalHps -= old.hps; + if (old.getHps()) { + this.totalHps -= old.getHps(); } } this.unladenMass -= old.getMass() || 0; @@ -813,14 +813,14 @@ export default class Ship { this.priorityBands[slot.priority][powerUsageType(slot, n)] += n.getPowerUsage(); powerChange = true; - if (n.dps) { - this.totalDps += n.dps; + if (n.getDps()) { + this.totalDps += n.getDps(); } - if (n.eps) { - this.totalEps += n.eps; + if (n.getEps()) { + this.totalEps += n.getEps(); } - if (n.hps) { - this.totalHps += n.hps; + if (n.getHps()) { + this.totalHps += n.getHps(); } } this.unladenMass += n.getMass() || 0; From f8ff9a6a87899220bd017f986f6d78fadb9cacc8 Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Thu, 3 Nov 2016 11:29:41 +0000 Subject: [PATCH 25/39] Add more fillouts --- src/app/components/HardpointsSlotSection.jsx | 10 +++ src/app/components/InternalSlot.jsx | 1 + src/app/components/InternalSlotSection.jsx | 92 +++++++++++++++++++- src/app/components/SlotSection.jsx | 2 +- src/app/components/UtilitySlotSection.jsx | 12 ++- src/app/i18n/en.js | 3 + 6 files changed, 116 insertions(+), 4 deletions(-) diff --git a/src/app/components/HardpointsSlotSection.jsx b/src/app/components/HardpointsSlotSection.jsx index 378300ee..367d1279 100644 --- a/src/app/components/HardpointsSlotSection.jsx +++ b/src/app/components/HardpointsSlotSection.jsx @@ -127,6 +127,16 @@ export default class HardpointsSlotSection extends SlotSection {
  • +
    {translate('fc')}
    +
      +
    • +
    • +
    • +
    +
    {translate('nc')}
    +
      +
    • {translate('nl')}
    • +
    ; } diff --git a/src/app/components/InternalSlot.jsx b/src/app/components/InternalSlot.jsx index cb813c88..cf3eb767 100644 --- a/src/app/components/InternalSlot.jsx +++ b/src/app/components/InternalSlot.jsx @@ -43,6 +43,7 @@ export default class InternalSlot extends Slot { { m.getFacingLimit() ?
    {translate('facinglimit')} {formats.f1(m.getFacingLimit())}°
    : null } { m.getRange() ?
    {translate('range')} {formats.f2(m.getRange())}{u.km}
    : null } { m.getRangeT() ?
    {translate('ranget')} {formats.f1(m.getRangeT())}{u.s}
    : null } + { m.spinup ?
    {translate('spinup')}: {formats.f1(m.spinup)}{u.s}
    : null } { m.time ?
    {translate('time')}: {formats.time(m.time)}
    : null } { m.maximum ?
    {translate('max')}: {(m.maximum)}
    : null } { m.rangeLS ?
    {translate('range')}: {m.rangeLS}{u.Ls}
    : null } diff --git a/src/app/components/InternalSlotSection.jsx b/src/app/components/InternalSlotSection.jsx index 5a59a069..70ee500f 100644 --- a/src/app/components/InternalSlotSection.jsx +++ b/src/app/components/InternalSlotSection.jsx @@ -22,6 +22,11 @@ export default class InternalSlotSection extends SlotSection { this._fillWithCargo = this._fillWithCargo.bind(this); this._fillWithCells = this._fillWithCells.bind(this); this._fillWithArmor = this._fillWithArmor.bind(this); + this._fillWithFuelTanks = this._fillWithFuelTanks.bind(this); + this._fillWithLuxuryCabins = this._fillWithLuxuryCabins.bind(this); + this._fillWithFirstClassCabins = this._fillWithFirstClassCabins.bind(this); + this._fillWithBusinessClassCabins = this._fillWithBusinessClassCabins.bind(this); + this._fillWithEconomyClassCabins = this._fillWithEconomyClassCabins.bind(this); } /** @@ -49,6 +54,86 @@ export default class InternalSlotSection extends SlotSection { this._close(); } + /** + * Fill all slots with fuel tanks + * @param {SyntheticEvent} event Event + */ + _fillWithFuelTanks(event) { + let clobber = event.getModifierState('Alt'); + let ship = this.props.ship; + ship.internal.forEach((slot) => { + if (clobber || !slot.m) { + ship.use(slot, ModuleUtils.findInternal('ft', slot.maxClass, 'C')); + } + }); + this.props.onChange(); + this._close(); + } + + /** + * Fill all slots with luxury passenger cabins + * @param {SyntheticEvent} event Event + */ + _fillWithLuxuryCabins(event) { + let clobber = event.getModifierState('Alt'); + let ship = this.props.ship; + ship.internal.forEach((slot) => { + if (clobber || !slot.m) { + ship.use(slot, ModuleUtils.findInternal('pcq', Math.min(slot.maxClass, 6), 'B')); // Passenger cabins top out at 6 + } + }); + this.props.onChange(); + this._close(); + } + + /** + * Fill all slots with first class passenger cabins + * @param {SyntheticEvent} event Event + */ + _fillWithFirstClassCabins(event) { + let clobber = event.getModifierState('Alt'); + let ship = this.props.ship; + ship.internal.forEach((slot) => { + if (clobber || !slot.m) { + ship.use(slot, ModuleUtils.findInternal('pcm', Math.min(slot.maxClass, 6), 'C')); // Passenger cabins top out at 6 + } + }); + this.props.onChange(); + this._close(); + } + + /** + * Fill all slots with business class passenger cabins + * @param {SyntheticEvent} event Event + */ + _fillWithBusinessClassCabins(event) { + let clobber = event.getModifierState('Alt'); + let ship = this.props.ship; + ship.internal.forEach((slot) => { + if (clobber || !slot.m) { + ship.use(slot, ModuleUtils.findInternal('pci', Math.min(slot.maxClass, 6), 'D')); // Passenger cabins top out at 6 + } + }); + this.props.onChange(); + this._close(); + } + + /** + * Fill all slots with economy class passenger cabins + * @param {SyntheticEvent} event Event + */ + _fillWithEconomyClassCabins(event) { + let clobber = event.getModifierState('Alt'); + let ship = this.props.ship; + ship.internal.forEach((slot) => { + if (clobber || !slot.m) { + ship.use(slot, ModuleUtils.findInternal('pce', Math.min(slot.maxClass, 6), 'E')); // Passenger cabins top out at 6 + } + }); + this.props.onChange(); + this._close(); + } + /** * Fill all slots with Shield Cell Banks * @param {SyntheticEvent} event Event @@ -133,13 +218,18 @@ export default class InternalSlotSection extends SlotSection { * @param {Function} translate Translate function * @return {React.Component} Section menu */ - _getSectionMenu(translate) { + _getSectionMenu(translate, ship) { return
    e.stopPropagation()} onContextMenu={stopCtxPropagation}>
    • {translate('empty all')}
    • {translate('cargo')}
    • {translate('scb')}
    • {translate('hr')}
    • +
    • {translate('ft')}
    • +
    • {translate('pce')}
    • +
    • {translate('pci')}
    • +
    • {translate('pcm')}
    • + { ship.luxuryCabins ?
    • {translate('pcq')}
    • : ''}
    • {translate('PHRASE_ALT_ALL')}
    ; diff --git a/src/app/components/SlotSection.jsx b/src/app/components/SlotSection.jsx index 29ea806e..4a339a33 100644 --- a/src/app/components/SlotSection.jsx +++ b/src/app/components/SlotSection.jsx @@ -180,7 +180,7 @@ export default class SlotSection extends TranslatedComponent {

    {translate(this.sectionName)}

    - {sectionMenuOpened ? this._getSectionMenu(translate) : null } + {sectionMenuOpened ? this._getSectionMenu(translate, this.props.ship) : null }
    {this._getSlots()}
    diff --git a/src/app/components/UtilitySlotSection.jsx b/src/app/components/UtilitySlotSection.jsx index 7741a2c4..0351b4ab 100644 --- a/src/app/components/UtilitySlotSection.jsx +++ b/src/app/components/UtilitySlotSection.jsx @@ -105,9 +105,17 @@ export default class UtilitySlotSection extends SlotSection {
  • B
  • A
  • -
    {translate('cm')}
    +
    {translate('hs')}
      -
    • {translate('Heat Sink Launcher')}
    • +
    • {translate('Heat Sink Launcher')}
    • +
    +
    {translate('ch')}
    +
      +
    • {translate('Chaff Launcher')}
    • +
    +
    {translate('po')}
    +
      +
    • {translate('Point Defence')}
    ; } diff --git a/src/app/i18n/en.js b/src/app/i18n/en.js index 9f3b18f4..d59aea17 100644 --- a/src/app/i18n/en.js +++ b/src/app/i18n/en.js @@ -89,6 +89,9 @@ export const terms = { // 'ammo' was overloaded for outfitting page and modul info, so changed to ammunition for outfitting page ammunition: 'Ammo', + // Unit for seconds + secs: 's', + // Modifications ammo: 'Ammunition maximum', armourpen: 'Armour penetration', From 2aab31d317df6bf356f331969980f2bc636fe095 Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Thu, 3 Nov 2016 11:33:00 +0000 Subject: [PATCH 26/39] Fix typo --- src/app/components/HardpointsSlotSection.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/components/HardpointsSlotSection.jsx b/src/app/components/HardpointsSlotSection.jsx index 367d1279..8eb212da 100644 --- a/src/app/components/HardpointsSlotSection.jsx +++ b/src/app/components/HardpointsSlotSection.jsx @@ -133,7 +133,7 @@ export default class HardpointsSlotSection extends SlotSection {
  • -
    {translate('nc')}
    +
    {translate('nl')}
    • {translate('nl')}
    From 3d129946ce8111ddbab4a0b6f93bc9f3d29a2810 Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Thu, 3 Nov 2016 12:23:25 +0000 Subject: [PATCH 27/39] Updates module diff details --- src/app/components/AvailableModulesMenu.jsx | 2 +- src/app/utils/SlotFunctions.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/components/AvailableModulesMenu.jsx b/src/app/components/AvailableModulesMenu.jsx index 534a722a..f72a475d 100644 --- a/src/app/components/AvailableModulesMenu.jsx +++ b/src/app/components/AvailableModulesMenu.jsx @@ -97,7 +97,7 @@ export default class AvailableModulesMenu extends TranslatedComponent { let m = modules[i]; let mount = null; let disabled = m.maxmass && (mass + (m.mass ? m.mass : 0)) > m.maxmass; - let active = mountedModule && mountedModule === m; + let active = mountedModule && mountedModule.id === m.id; let classes = cn(m.name ? 'lc' : 'c', { warning: !disabled && warningFunc && warningFunc(m), active, diff --git a/src/app/utils/SlotFunctions.js b/src/app/utils/SlotFunctions.js index 647d8ef7..af34aee4 100644 --- a/src/app/utils/SlotFunctions.js +++ b/src/app/utils/SlotFunctions.js @@ -234,7 +234,7 @@ export function diffDetails(language, m, mm) { // } // } - let mDps = m.dps || 0; + let mDps = m.damage * (m.rpshot || 1) * m.rof || 0; let mmDps = mm ? mm.getDps() || 0 : 0; if (mDps != mmDps) propDiffs.push(
    {translate('dps')}: {diff(formats.round, mDps, mmDps)}
    ); @@ -298,5 +298,5 @@ export function diffDetails(language, m, mm) { } } - return
    {propDiffs}
    ; + return propDiffs ?
    {propDiffs}
    : null; } From abfc33824063e67dfcbea4b2ef4b9acd5f5e1772 Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Thu, 3 Nov 2016 16:19:13 +0000 Subject: [PATCH 28/39] Tidy-ups --- src/app/components/HardpointSlot.jsx | 10 +- src/app/components/StandardSlot.jsx | 2 +- src/app/components/StandardSlotSection.jsx | 2 +- src/app/i18n/en.js | 15 +- src/app/shipyard/Module.js | 11 +- src/app/shipyard/ModuleSet.js | 2 +- src/app/shipyard/Ship.js | 170 +++++++++++++++++---- src/app/utils/SlotFunctions.js | 4 +- 8 files changed, 166 insertions(+), 50 deletions(-) diff --git a/src/app/components/HardpointSlot.jsx b/src/app/components/HardpointSlot.jsx index 11c1b0f4..7d876bf8 100644 --- a/src/app/components/HardpointSlot.jsx +++ b/src/app/components/HardpointSlot.jsx @@ -56,11 +56,11 @@ export default class HardpointSlot extends Slot {
    {formats.round1(m.getMass())}{u.T}
    - { m.getDps() ?
    {translate('DPS')}: {formats.round1(m.getDps())} { m.getClip() ? ({formats.round1((m.getClip() * m.getDps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload())) }) : null }
    : null } - { m.getEps() ?
    {translate('EPS')}: {formats.round1(m.getEps())}{u.MW} { m.getClip() ? ({formats.round1((m.getClip() * m.getEps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload())) }{u.MW}) : null }
    : null } - { m.getHps() ?
    {translate('HPS')}: {formats.round1(m.getHps())} { m.getClip() ? ({formats.round1((m.getClip() * m.getHps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload())) }) : null }
    : null } - { m.getDps() && m.getEps() ?
    {translate('DPE')}: {formats.f1(m.getDps() / m.getEps())}
    : null } - { m.getRoF() ?
    {translate('ROF')}: {formats.f1(m.getRoF())}{u.ps}
    : null } + { m.getDps() ?
    {translate('DPS')}: {formats.round1(m.getDps())} { m.getClip() ? ({formats.round1((m.getClip() * m.getDps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload())) }) : null }
    : null } + { m.getEps() ?
    {translate('EPS')}: {formats.round1(m.getEps())}{u.MW} { m.getClip() ? ({formats.round1((m.getClip() * m.getEps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload())) }{u.MW}) : null }
    : null } + { m.getHps() ?
    {translate('HPS')}: {formats.round1(m.getHps())} { m.getClip() ? ({formats.round1((m.getClip() * m.getHps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload())) }) : null }
    : null } + { m.getDps() && m.getEps() ?
    {translate('DPE')}: {formats.f1(m.getDps() / m.getEps())}
    : null } + { m.getRoF() ?
    {translate('ROF')}: {formats.f1(m.getRoF())}{u.ps}
    : null } { m.getRange() && !m.getDps() ?
    {translate('Range')} : {formats.round(m.getRange() / 1000)}{u.km}
    : null } { m.getShieldMul() ?
    +{formats.rPct(m.getShieldMul())}
    : null } { m.getAmmo() ?
    {translate('ammunition')}: {formats.int(m.getClip())}/{formats.int(m.getAmmo())}
    : null } diff --git a/src/app/components/StandardSlot.jsx b/src/app/components/StandardSlot.jsx index 1073e548..29142085 100644 --- a/src/app/components/StandardSlot.jsx +++ b/src/app/components/StandardSlot.jsx @@ -89,7 +89,7 @@ export default class StandardSlot extends TranslatedComponent { { m.getRange() ?
    {translate('range')}: {formats.f2(m.getRange())}{units.km}
    : null } { m.time ?
    {translate('time')}: {formats.time(m.time)}
    : null } { m.getThermalEfficiency() ?
    {translate('efficiency')}: {formats.f2(m.getThermalEfficiency())}
    : null } - { m.getPowerGeneration() > 0 ?
    {translate('pGen')}: {formats.f1(m.getPowerGeneration())}{units.MW}
    : null } + { m.getPowerGeneration() > 0 ?
    {translate('pgen')}: {formats.f1(m.getPowerGeneration())}{units.MW}
    : null } { m.getMaxFuelPerJump() ?
    {translate('max')} {translate('fuel')}: {formats.f1(m.getMaxFuelPerJump())}{units.T}
    : null } { m.getWeaponsCapacity() ?
    {translate('WEP')}: {formats.f1(m.getWeaponsCapacity())}{units.MJ} / {formats.f1(m.getWeaponsRechargeRate())}{units.MW}
    : null } { m.getSystemsCapacity() ?
    {translate('SYS')}: {formats.f1(m.getSystemsCapacity())}{units.MJ} / {formats.f1(m.getSystemsRechargeRate())}{units.MW}
    : null } diff --git a/src/app/components/StandardSlotSection.jsx b/src/app/components/StandardSlotSection.jsx index 24b25926..48e53656 100644 --- a/src/app/components/StandardSlotSection.jsx +++ b/src/app/components/StandardSlotSection.jsx @@ -114,7 +114,7 @@ export default class StandardSlotSection extends SlotSection { selected={currentMenu == st[0]} onChange={this.props.onChange} ship={ship} - warning={m => m.pGen < ship.powerRetracted} + warning={m => m.pgen < ship.powerRetracted} />; slots[2] = = powerNeeded && (p.mass < pp.mass || (p.mass == pp.mass && p.pGen > pp.pGen))) { + if (p.pgen >= powerNeeded && (p.mass < pp.mass || (p.mass == pp.mass && p.pgen > pp.pgen))) { pp = p; } } diff --git a/src/app/shipyard/Ship.js b/src/app/shipyard/Ship.js index d9b5c2b5..90483da0 100755 --- a/src/app/shipyard/Ship.js +++ b/src/app/shipyard/Ship.js @@ -3,6 +3,7 @@ import * as ModuleUtils from './ModuleUtils'; import Module from './Module'; import LZString from 'lz-string'; import isEqual from 'lodash/lang'; +import { Modifications } from 'coriolis-data/dist'; const UNIQUE_MODULES = ['psg', 'sg', 'bsg', 'rf', 'fs', 'fh']; @@ -21,25 +22,6 @@ function powerUsageType(slot, modul) { return slot.cat != 1 ? 'retracted' : 'deployed'; } -/** - * Populate the modifications array with modification values from the code - * @param {String} code Serialized modification code - * @param {Array} arr Modification array - */ -function decodeModsToArray(code, arr) { - let moduleMods = code.split(','); - for (let i = 0; i < arr.length; i++) { - arr[i] = {}; - if (moduleMods.length > i && moduleMods[i] != '') { - let mods = moduleMods[i].split(';'); - for (let j = 0; j < mods.length; j++) { - let modElements = mods[j].split(':'); - arr[i][modElements[0]] = Number(modElements[1]); - } - } - } -} - /** * Populates the category array with module IDs from * the provided code @@ -418,7 +400,7 @@ export default class Ship { */ setModification(m, name, value) { // Handle special cases - if (name == 'pGen') { + if (name == 'pgen') { // Power generation m.setModValue(name, value); this.updatePower(); @@ -598,8 +580,9 @@ export default class Ship { } if (parts[3]) { - // decodeModsToArray(LZString.decompressFromBase64(parts[3].replace(/-/g, '/')), mods); - decodeModsToArray(parts[3], mods); + const buf = Buffer.from(parts[3].replace(/-/g, '/'), 'base64'); + this.decodeModificationsString(Buffer.from(parts[3].replace(/-/g, '/'), 'base64'), mods); + //decodeModificationsString(LZString.decompressFromBase64(parts[3].replace(/-/g, '/')), mods); } decodeToArray(code, internal, decodeToArray(code, hardpoints, decodeToArray(code, standard, 1))); @@ -982,13 +965,13 @@ export default class Ship { * Update the modifications string * @return {this} The ship instance (for chaining operations) */ - updateModificationsString() { + oldupdateModificationsString() { let allMods = new Array(); let bulkheadMods = new Array(); if (this.bulkheads.m && this.bulkheads.m.mods) { for (let modKey in this.bulkheads.m.mods) { - bulkheadMods.push(modKey + ':' + Math.round(this.bulkheads.m.getModValue(modKey) * 10000)); + bulkheadMods.push(Modifications.modifiers.indexOf(modKey) + ':' + Math.round(this.bulkheads.m.getModValue(modKey) * 10000)); } } allMods.push(bulkheadMods.join(';')); @@ -997,7 +980,7 @@ export default class Ship { let slotMods = new Array(); if (slot.m && slot.m.mods) { for (let modKey in slot.m.mods) { - slotMods.push(modKey + ':' + Math.round(slot.m.getModValue(modKey) * 10000)); + slotMods.push(Modifications.modifiers.indexOf(modKey) + ':' + Math.round(slot.m.getModValue(modKey) * 10000)); } } allMods.push(slotMods.join(';')); @@ -1006,7 +989,7 @@ export default class Ship { let slotMods = new Array(); if (slot.m && slot.m.mods) { for (let modKey in slot.m.mods) { - slotMods.push(modKey + ':' + Math.round(slot.m.getModValue(modKey) * 10000)); + slotMods.push(Modifications.modifiers.indexOf(modKey) + ':' + Math.round(slot.m.getModValue(modKey) * 10000)); } } allMods.push(slotMods.join(';')); @@ -1015,16 +998,145 @@ export default class Ship { let slotMods = new Array(); if (slot.m && slot.m.mods) { for (let modKey in slot.m.mods) { - slotMods.push(modKey + ':' + Math.round(slot.m.getModValue(modKey) * 10000)); + slotMods.push(Modifications.modifiers.indexOf(modKey) + ':' + Math.round(slot.m.getModValue(modKey) * 10000)); } } allMods.push(slotMods.join(';')); } - // this.serialized.modifications = LZString.compressToBase64(allMods.join(',').replace(/,+$/, '')).replace(/\//g, '-'); - this.serialized.modifications = allMods.join(',').replace(/,+$/, ''); + this.serialized.modifications = LZString.compressToBase64(allMods.join(',').replace(/,+$/, '')).replace(/\//g, '-'); return this; } + /** + * Populate the modifications array with modification values from the code + * @param {String} code Serialized modification code + * @param {Array} arr Modification array + */ + olddecodeModificationsString(code, arr) { + let moduleMods = code.split(','); + for (let i = 0; i < arr.length; i++) { + arr[i] = {}; + if (moduleMods.length > i && moduleMods[i] != '') { + let mods = moduleMods[i].split(';'); + for (let j = 0; j < mods.length; j++) { + let modElements = mods[j].split(':'); + if (modElements[0].match('[0-9]+')) { + arr[i][Modifications.modifiers[modElements[0]]] = Number(modElements[1]); + } else { + arr[i][modElements[0]] = Number(modElements[1]); + } + } + } + } + } + + /** + * Update the modifications string + * @return {this} The ship instance (for chaining operations) + */ + updateModificationsString() { + // Start off by gathering the information that we need + let modules = new Array(); + + let bulkheadMods = new Array(); + if (this.bulkheads.m && this.bulkheads.m.mods) { + for (let modKey in this.bulkheads.m.mods) { + bulkheadMods.push({ id: Modifications.modifiers.indexOf(modKey), value: Math.round(this.bulkheads.m.getModValue(modKey) * 10000) }); + } + } + modules.push(bulkheadMods); + + for (let slot of this.standard) { + let slotMods = new Array(); + if (slot.m && slot.m.mods) { + for (let modKey in slot.m.mods) { + slotMods.push({ id: Modifications.modifiers.indexOf(modKey), value: Math.round(slot.m.getModValue(modKey) * 10000) }); + } + } + modules.push(slotMods); + } + + for (let slot of this.hardpoints) { + let slotMods = new Array(); + if (slot.m && slot.m.mods) { + for (let modKey in slot.m.mods) { + slotMods.push({ id: Modifications.modifiers.indexOf(modKey), value: Math.round(slot.m.getModValue(modKey) * 10000) }); + } + } + modules.push(slotMods); + } + + for (let slot of this.internal) { + let slotMods = new Array(); + if (slot.m && slot.m.mods) { + for (let modKey in slot.m.mods) { + slotMods.push({ id: Modifications.modifiers.indexOf(modKey), value: Math.round(slot.m.getModValue(modKey) * 10000) }); + } + } + modules.push(slotMods); + } + + // Now work out the size of the binary buffer from our modifications array + let bufsize = 0; + for (let module of modules) { + if (module.length > 0) { + bufsize = bufsize + 1 + (3 * module.length) + 1; // + } + } + + if (bufsize > 0) { + bufsize = bufsize + 1; // For end marker + // Now create and populate the buffer + let buffer = Buffer.alloc(bufsize); + let curpos = 0; + let i = 1; + for (let module of modules) { + if (module.length > 0) { + buffer.writeInt8(i, curpos++); + for (let modification of module) { + buffer.writeInt8(modification.id, curpos++); + console.log('modification value is ' + modification.value); + buffer.writeInt16BE(modification.value, curpos); + curpos += 2; + } + buffer.writeInt8(0, curpos++); + } + i++; + } + if (curpos > 0) { + buffer.writeInt8(0, curpos++); + } + + this.serialized.modifications = buffer.toString('base64').replace(/\//g, '-'); + } else { + this.serialized.modifications = null; + } + return this; + } + + /** + * Populate the modifications array with modification values from the code + * @param {String} buffer Buffer holding modification info + * @param {Array} arr Modification array + */ + decodeModificationsString(buffer, arr) { + let curpos = 0; + let module = buffer.readInt8(curpos++); + while (module != 0) { + module = module - 1; // Fix offset to make the rest of the code easy + let modifications = []; + let modificationId = buffer.readInt8(curpos++); + while (modificationId != 0) { + let modificationValue = buffer.readInt16BE(curpos); + curpos += 2; + modifications[Modifications.modifiers[modificationId]] = modificationValue; + modificationId = buffer.readInt8(curpos++); + } + arr[module] = modifications; + module = buffer.readInt8(curpos++); + } + } + /** * Update a slot with a the modul if the id is different from the current id for this slot. * Has logic handling ModuleUtils that you may only have 1 of (Shield Generator or Refinery). diff --git a/src/app/utils/SlotFunctions.js b/src/app/utils/SlotFunctions.js index af34aee4..211e3be5 100644 --- a/src/app/utils/SlotFunctions.js +++ b/src/app/utils/SlotFunctions.js @@ -136,7 +136,7 @@ const PROP_BLACKLIST = { }; const TERM_LOOKUP = { - pGen: 'power', + pgen: 'power', armouradd: 'armour', shieldmul: 'multiplier', rof: 'ROF', @@ -156,7 +156,7 @@ const UNIT_LOOKUP = { recharge: 'MJ', rangeLS: 'Ls', power: 'MJ', - pGen: 'MJ', + pgen: 'MJ', rof: 'ps' }; From 97fc4ce45dbfffa8a67ae5ffd415d6ff0ee0db1c Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Fri, 4 Nov 2016 11:31:48 +0000 Subject: [PATCH 29/39] Fixes and tidy-ups --- src/app/components/ShipSummaryTable.jsx | 2 +- src/app/shipyard/Serializer.js | 9 +- src/app/shipyard/Ship.js | 210 ++++++++++++++---------- 3 files changed, 131 insertions(+), 90 deletions(-) diff --git a/src/app/components/ShipSummaryTable.jsx b/src/app/components/ShipSummaryTable.jsx index a98dd690..e5069369 100644 --- a/src/app/components/ShipSummaryTable.jsx +++ b/src/app/components/ShipSummaryTable.jsx @@ -24,7 +24,7 @@ export default class ShipSummaryTable extends TranslatedComponent { let u = language.units; let formats = language.formats; let { time, int, round, f1, f2, pct } = formats; - let sgClassNames = cn({ warning: ship.sgSlot && !ship.shield, muted: !ship.sgSlot }); + let sgClassNames = cn({ warning: ship.findInternalByGroup('sg') && !ship.shield, muted: !ship.findInternalByGroup('sg') }); let sgRecover = '-'; let sgRecharge = '-'; let hide = tooltip.bind(null, null); diff --git a/src/app/shipyard/Serializer.js b/src/app/shipyard/Serializer.js index 7b8284be..8fa9eff6 100644 --- a/src/app/shipyard/Serializer.js +++ b/src/app/shipyard/Serializer.js @@ -24,6 +24,10 @@ function standardToSchema(standard) { o.name = standard.m.name; } + if (standard.m.mods && standard.m.mods != {}) { + o.modifications = standard.m.mods; + } + return o; } return null; @@ -53,6 +57,9 @@ function slotToSchema(slot) { if (slot.m.missile) { o.missile = slot.m.missile; } + if (slot.m.mods && slot.m.mods != {}) { + o.modifications = slot.m.mods; + } return o; } return null; @@ -71,7 +78,7 @@ export function toDetailedBuild(buildName, ship) { code = ship.toString(); let data = { - $schema: 'http://cdn.coriolis.io/schemas/ship-loadout/3.json#', + $schema: 'http://cdn.coriolis.io/schemas/ship-loadout/4.json#', name: buildName, ship: ship.name, references: [{ diff --git a/src/app/shipyard/Ship.js b/src/app/shipyard/Ship.js index 90483da0..07fa4915 100755 --- a/src/app/shipyard/Ship.js +++ b/src/app/shipyard/Ship.js @@ -403,11 +403,11 @@ export default class Ship { if (name == 'pgen') { // Power generation m.setModValue(name, value); - this.updatePower(); + this.updatePowerGenerated(); } else if (name == 'power') { // Power usage m.setModValue(name, value); - this.updatePower(); + this.updatePowerUsed(); } else if (name == 'mass') { // Mass let oldMass = m.getMass(); @@ -454,7 +454,6 @@ export default class Ship { let internal = this.internal, standard = this.standard, hps = this.hardpoints, - bands = this.priorityBands, cl = standard.length, i, l; @@ -483,15 +482,6 @@ export default class Ship { this.cargoHatch.enabled = enabled ? enabled[0] * 1 : true; this.cargoHatch.mods = mods ? mods[0] : {}; - for (i = 0, l = this.priorityBands.length; i < l; i++) { - this.priorityBands[i].deployed = 0; - this.priorityBands[i].retracted = 0; - } - - if (this.cargoHatch.enabled) { - bands[this.cargoHatch.priority].retracted += this.cargoHatch.m.getPowerUsage(); - } - for (i = 0; i < cl; i++) { standard[i].cat = 0; standard[i].enabled = enabled ? enabled[i + 1] * 1 : true; @@ -544,7 +534,8 @@ export default class Ship { // Update aggragated stats if (comps) { - this.updatePower() + this.updatePowerGenerated() + .updatePowerUsed() .updateJumpStats() .updateShield() .updateArmour() @@ -580,9 +571,12 @@ export default class Ship { } if (parts[3]) { - const buf = Buffer.from(parts[3].replace(/-/g, '/'), 'base64'); - this.decodeModificationsString(Buffer.from(parts[3].replace(/-/g, '/'), 'base64'), mods); - //decodeModificationsString(LZString.decompressFromBase64(parts[3].replace(/-/g, '/')), mods); + const modstr = parts[3].replace(/-/g, '/'); + if (modstr.match(':')) { + this.decodeModificationsString(LZString.decompressFromBase64(modstr), mods); + } else { + this.decodeModificationsStruct(Buffer.from(modstr, 'base64'), mods); + } } decodeToArray(code, internal, decodeToArray(code, hardpoints, decodeToArray(code, standard, 1))); @@ -682,8 +676,6 @@ export default class Ship { if (slot.enabled != enabled) { // Enabled state is changing slot.enabled = enabled; if (slot.m) { - this.priorityBands[slot.priority][powerUsageType(slot, slot.m)] += enabled ? slot.m.getPowerUsage() : - slot.m.getPowerUsage(); - if (ModuleUtils.isShieldGenerator(slot.m.grp) || slot.m.grp == 'sb') { this.updateShield(); } @@ -697,7 +689,7 @@ export default class Ship { this.totalHps += slot.m.getHps() * (enabled ? 1 : -1); } - this.updatePower(); + this.updatePowerUsed(); this.updatePowerEnabledString(); } } @@ -717,10 +709,7 @@ export default class Ship { this.updatePowerPrioritesString(); if (slot.enabled) { // Only update power if the slot is enabled - let usage = powerUsageType(slot, slot.m); - this.priorityBands[oldPriority][usage] -= slot.m.getPowerUsage(); - this.priorityBands[newPriority][usage] += slot.m.getPowerUsage(); - this.updatePower(); + this.updatePowerUsed(); } return true; } @@ -736,7 +725,8 @@ export default class Ship { * @return {this} The ship instance (for chaining operations) */ updateStats(slot, n, old, preventUpdate) { - let powerChange = slot == this.standard[0]; + let powerGeneratedChange = slot == this.standard[0]; + let powerUsedChange = false; let armourChange = (slot == this.bulkheads) || (n && n.grp == 'hr') || (old && old.grp == 'hr'); @@ -756,25 +746,20 @@ export default class Ship { this.totalCost -= old.cost * this.moduleCostMultiplier; } - if (!(old instanceof Module)) { - console.log(JSON.stringify(old) + ' is not a module'); - console.log(new Error().stack); - } - if (old.getPowerUsage() > 0 && slot.enabled) { - this.priorityBands[slot.priority][powerUsageType(slot, old)] -= old.getPowerUsage(); - powerChange = true; - - if (old.getDps()) { - this.totalDps -= old.getDps(); - } - if (old.getEps()) { - this.totalEps -= old.getEps(); - } - if (old.getHps()) { - this.totalHps -= old.getHps(); - } + powerUsedChange = true; } + + if (old.getDps()) { + this.totalDps -= old.getDps(); + } + if (old.getEps()) { + this.totalEps -= old.getEps(); + } + if (old.getHps()) { + this.totalHps -= old.getHps(); + } + this.unladenMass -= old.getMass() || 0; } @@ -793,27 +778,30 @@ export default class Ship { } if (n.power && slot.enabled) { - this.priorityBands[slot.priority][powerUsageType(slot, n)] += n.getPowerUsage(); - powerChange = true; - - if (n.getDps()) { - this.totalDps += n.getDps(); - } - if (n.getEps()) { - this.totalEps += n.getEps(); - } - if (n.getHps()) { - this.totalHps += n.getHps(); - } + powerUsedChange = true; } + + if (n.getDps()) { + this.totalDps += n.getDps(); + } + if (n.getEps()) { + this.totalEps += n.getEps(); + } + if (n.getHps()) { + this.totalHps += n.getHps(); + } + this.unladenMass += n.getMass() || 0; } this.ladenMass = this.unladenMass + this.cargoCapacity + this.fuelCapacity; if (!preventUpdate) { - if (powerChange) { - this.updatePower(); + if (powerGeneratedChange) { + this.updatePowerGenerated(); + } + if (powerUsedChange) { + this.updatePowerUsed(); } if (armourChange) { this.updateArmour(); @@ -828,24 +816,68 @@ export default class Ship { } /** - * Update all power calculations + * Update power calculations when amount generated changes * @return {this} The ship instance (for chaining operations) */ - updatePower() { - let bands = this.priorityBands; - let prevRetracted = 0, prevDeployed = 0; + updatePowerGenerated() { + this.powerAvailable = this.standard[0].m.getPowerGeneration(); + return this; + }; + /** + * Update power calculations when amount consumed changes + * @return {this} The ship instance (for chaining operations) + */ + updatePowerUsed() { + let bands = [ + { deployed: 0, retracted: 0, }, + { deployed: 0, retracted: 0, }, + { deployed: 0, retracted: 0, }, + { deployed: 0, retracted: 0, }, + { deployed: 0, retracted: 0, } + ]; + + if (this.cargoHatch.enabled) { + bands[this.cargoHatch.priority].retracted += this.cargoHatch.m.getPowerUsage(); + } + + for (let slotNum in this.standard) { + const slot = this.standard[slotNum]; + if (slot.m && slot.enabled) { + bands[slot.priority][powerUsageType(slot, slot.m)] += slot.m.getPowerUsage(); + + } + } + + for (let slotNum in this.internal) { + const slot = this.internal[slotNum]; + if (slot.m && slot.enabled) { + bands[slot.priority][powerUsageType(slot, slot.m)] += slot.m.getPowerUsage(); + } + } + + for (let slotNum in this.hardpoints) { + const slot = this.hardpoints[slotNum]; + if (slot.m && slot.enabled) { + bands[slot.priority][powerUsageType(slot, slot.m)] += slot.m.getPowerUsage(); + } + } + + // Work out the running totals + let prevRetracted = 0, prevDeployed = 0; for (let i = 0, l = bands.length; i < l; i++) { let band = bands[i]; prevRetracted = band.retractedSum = prevRetracted + band.retracted; prevDeployed = band.deployedSum = prevDeployed + band.deployed + band.retracted; } - this.powerAvailable = this.standard[0].m.getPowerGeneration(); + // Update global stats this.powerRetracted = prevRetracted; this.powerDeployed = prevDeployed; + this.priorityBands = bands; + return this; - }; + } /** * Update top speed and boost @@ -1012,7 +1044,7 @@ export default class Ship { * @param {String} code Serialized modification code * @param {Array} arr Modification array */ - olddecodeModificationsString(code, arr) { + decodeModificationsString(code, arr) { let moduleMods = code.split(','); for (let i = 0; i < arr.length; i++) { arr[i] = {}; @@ -1031,12 +1063,17 @@ export default class Ship { } /** - * Update the modifications string + * Update the modifications string. + * This is a binary structure. It starts with a byte that identifies a slot, with bulkheads being ID 0 and moving through + * standard modules, hardpoints, and finally internal modules. It then contains one or more modifications, with each + * modification being a one-byte modification ID and at two-byte modification value. Modification IDs are based on the array + * in Modifications.modifiers. The list of modifications is terminated by a modification ID of -1. The structure then repeats + * for the next module, and the next, and is terminated by a slot ID of -1. * @return {this} The ship instance (for chaining operations) */ updateModificationsString() { // Start off by gathering the information that we need - let modules = new Array(); + let slots = new Array(); let bulkheadMods = new Array(); if (this.bulkheads.m && this.bulkheads.m.mods) { @@ -1044,7 +1081,7 @@ export default class Ship { bulkheadMods.push({ id: Modifications.modifiers.indexOf(modKey), value: Math.round(this.bulkheads.m.getModValue(modKey) * 10000) }); } } - modules.push(bulkheadMods); + slots.push(bulkheadMods); for (let slot of this.standard) { let slotMods = new Array(); @@ -1053,7 +1090,7 @@ export default class Ship { slotMods.push({ id: Modifications.modifiers.indexOf(modKey), value: Math.round(slot.m.getModValue(modKey) * 10000) }); } } - modules.push(slotMods); + slots.push(slotMods); } for (let slot of this.hardpoints) { @@ -1063,7 +1100,7 @@ export default class Ship { slotMods.push({ id: Modifications.modifiers.indexOf(modKey), value: Math.round(slot.m.getModValue(modKey) * 10000) }); } } - modules.push(slotMods); + slots.push(slotMods); } for (let slot of this.internal) { @@ -1073,14 +1110,14 @@ export default class Ship { slotMods.push({ id: Modifications.modifiers.indexOf(modKey), value: Math.round(slot.m.getModValue(modKey) * 10000) }); } } - modules.push(slotMods); + slots.push(slotMods); } // Now work out the size of the binary buffer from our modifications array let bufsize = 0; - for (let module of modules) { - if (module.length > 0) { - bufsize = bufsize + 1 + (3 * module.length) + 1; // + for (let slot of slots) { + if (slot.length > 0) { + bufsize = bufsize + 1 + (3 * slot.length) + 1; // } } @@ -1089,22 +1126,21 @@ export default class Ship { // Now create and populate the buffer let buffer = Buffer.alloc(bufsize); let curpos = 0; - let i = 1; - for (let module of modules) { - if (module.length > 0) { + let i = 0; + for (let slot of slots) { + if (slot.length > 0) { buffer.writeInt8(i, curpos++); - for (let modification of module) { - buffer.writeInt8(modification.id, curpos++); - console.log('modification value is ' + modification.value); - buffer.writeInt16BE(modification.value, curpos); + for (let slotMod of slot) { + buffer.writeInt8(slotMod.id, curpos++); + buffer.writeInt16BE(slotMod.value, curpos); curpos += 2; } - buffer.writeInt8(0, curpos++); + buffer.writeInt8(-1, curpos++); } i++; } if (curpos > 0) { - buffer.writeInt8(0, curpos++); + buffer.writeInt8(-1, curpos++); } this.serialized.modifications = buffer.toString('base64').replace(/\//g, '-'); @@ -1115,18 +1151,18 @@ export default class Ship { } /** - * Populate the modifications array with modification values from the code + * Populate the modifications array with modification values from the code. + * See updateModificationsString() for details of the structure. * @param {String} buffer Buffer holding modification info * @param {Array} arr Modification array */ - decodeModificationsString(buffer, arr) { + decodeModificationsStruct(buffer, arr) { let curpos = 0; let module = buffer.readInt8(curpos++); - while (module != 0) { - module = module - 1; // Fix offset to make the rest of the code easy - let modifications = []; + while (module != -1) { + let modifications = {}; let modificationId = buffer.readInt8(curpos++); - while (modificationId != 0) { + while (modificationId != -1) { let modificationValue = buffer.readInt16BE(curpos); curpos += 2; modifications[Modifications.modifiers[modificationId]] = modificationValue; @@ -1154,8 +1190,6 @@ export default class Ship { } else if (mdef instanceof Module) { m = mdef; } else { - // jgmjgm TODO see if we can use the module template instead of its group and id - // m = new Module({template: mdef}); m = new Module({ grp: mdef.grp, id: mdef.id }); } From c17c7125e31e3bc5036c7f9c085b0f382453ce33 Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Fri, 4 Nov 2016 11:34:39 +0000 Subject: [PATCH 30/39] Tidy-ups --- src/app/components/InternalSlotSection.jsx | 1 + src/app/shipyard/Ship.js | 9 ++++----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/app/components/InternalSlotSection.jsx b/src/app/components/InternalSlotSection.jsx index 70ee500f..ae6b8bec 100644 --- a/src/app/components/InternalSlotSection.jsx +++ b/src/app/components/InternalSlotSection.jsx @@ -216,6 +216,7 @@ export default class InternalSlotSection extends SlotSection { /** * Generate the section drop-down menu * @param {Function} translate Translate function + * @param {Function} ship The ship * @return {React.Component} Section menu */ _getSectionMenu(translate, ship) { diff --git a/src/app/shipyard/Ship.js b/src/app/shipyard/Ship.js index 07fa4915..459824ff 100755 --- a/src/app/shipyard/Ship.js +++ b/src/app/shipyard/Ship.js @@ -221,7 +221,7 @@ export default class Ship { * Calculate the hypothetical shield strength for the ship using the specified parameters * @param {Object} sg [optional] Shield Generator to use * @param {Number} multiplierDelta [optional] Change to shield multiplier (+0.2, - 0.12, etc) - * @return {Number} Shield strength in MH + * @return {Number} Shield strength in MJ */ calcShieldStrengthWith(sg, multiplierDelta) { if (!sg) { @@ -845,7 +845,6 @@ export default class Ship { const slot = this.standard[slotNum]; if (slot.m && slot.enabled) { bands[slot.priority][powerUsageType(slot, slot.m)] += slot.m.getPowerUsage(); - } } @@ -1164,9 +1163,9 @@ export default class Ship { let modificationId = buffer.readInt8(curpos++); while (modificationId != -1) { let modificationValue = buffer.readInt16BE(curpos); - curpos += 2; - modifications[Modifications.modifiers[modificationId]] = modificationValue; - modificationId = buffer.readInt8(curpos++); + curpos += 2; + modifications[Modifications.modifiers[modificationId]] = modificationValue; + modificationId = buffer.readInt8(curpos++); } arr[module] = modifications; module = buffer.readInt8(curpos++); From c1f4a8d416eb553514ab66c7a1b739edd97c667e Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Fri, 4 Nov 2016 13:12:44 +0000 Subject: [PATCH 31/39] Add tooltips for icons; fix import of old-style modification strings --- src/app/components/HardpointSlot.jsx | 13 ++++++------- src/app/shipyard/Ship.js | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/app/components/HardpointSlot.jsx b/src/app/components/HardpointSlot.jsx index 7d876bf8..796fc2c0 100644 --- a/src/app/components/HardpointSlot.jsx +++ b/src/app/components/HardpointSlot.jsx @@ -45,13 +45,12 @@ export default class HardpointSlot extends Slot { return
    - {m.mount && m.mount == 'F' ? : ''} - {m.mount && m.mount == 'G' ? : ''} - {m.mount && m.mount == 'T' ? : ''} - {m.type && m.type == 'K' ? : ''} - {m.type && m.type == 'T' ? : ''} - {m.type && m.type == 'KT' ? : ''} - {m.type && m.type == 'E' ? : ''} + {m.mount && m.mount == 'F' ? : ''} + {m.mount && m.mount == 'G' ? : ''} + {m.mount && m.mount == 'T' ? : ''} + {m.type && m.type.match('K') ? : ''} + {m.type && m.type.match('T') ? : ''} + {m.type && m.type.match('E') ? : ''} {classRating} {translate(m.name || m.grp)}
    {formats.round1(m.getMass())}{u.T}
    diff --git a/src/app/shipyard/Ship.js b/src/app/shipyard/Ship.js index 459824ff..f7949c5e 100755 --- a/src/app/shipyard/Ship.js +++ b/src/app/shipyard/Ship.js @@ -573,7 +573,7 @@ export default class Ship { if (parts[3]) { const modstr = parts[3].replace(/-/g, '/'); if (modstr.match(':')) { - this.decodeModificationsString(LZString.decompressFromBase64(modstr), mods); + this.decodeModificationsString(modstr, mods); } else { this.decodeModificationsStruct(Buffer.from(modstr, 'base64'), mods); } From d08e5e28588c9a8c7f4d93715435c032a680734a Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Fri, 4 Nov 2016 17:13:28 +0000 Subject: [PATCH 32/39] Fixes for serialization --- src/app/components/Modification.jsx | 9 +++++++++ src/app/shipyard/Serializer.js | 21 ++++++++++++++++++--- src/app/shipyard/Ship.js | 18 +++++++++--------- 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/src/app/components/Modification.jsx b/src/app/components/Modification.jsx index 072b6d92..bbcaaf7a 100644 --- a/src/app/components/Modification.jsx +++ b/src/app/components/Modification.jsx @@ -33,6 +33,15 @@ export default class ModificationsMenu extends TranslatedComponent { */ _updateValue(value) { let scaledValue = Math.floor(Number(value) * 100) / 10000; + // Limit to +1000% / -100% + if (scaledValue > 10) { + scaledValue = 10; + value = 1000; + } + if (scaledValue < -1) { + scaledValue = -1; + value = -100; + } let m = this.props.m; let name = this.props.name; let ship = this.props.ship; diff --git a/src/app/shipyard/Serializer.js b/src/app/shipyard/Serializer.js index 8fa9eff6..2608b4ea 100644 --- a/src/app/shipyard/Serializer.js +++ b/src/app/shipyard/Serializer.js @@ -24,7 +24,7 @@ function standardToSchema(standard) { o.name = standard.m.name; } - if (standard.m.mods && standard.m.mods != {}) { + if (standard.m.mods && Object.keys(standard.m.mods).length > 0) { o.modifications = standard.m.mods; } @@ -57,7 +57,7 @@ function slotToSchema(slot) { if (slot.m.missile) { o.missile = slot.m.missile; } - if (slot.m.mods && slot.m.mods != {}) { + if (slot.m.mods && Object.keys(slot.m.mods).length > 0) { o.modifications = slot.m.mods; } return o; @@ -115,12 +115,27 @@ export function toDetailedBuild(buildName, ship) { return data; }; +export function fromDetailedBuild(detailedBuild) { + let shipId = Object.keys(Ships).find((shipId) => Ships[shipId].properties.name.toLowerCase() == detailedBuild.ship.toLowerCase()); + + if (!shipId) { + throw 'No such ship: ' + detailedBuild.ship; + } + + let comps = detailedBuild.components; + let stn = comps.standard; + let shipData = Ships[shipId]; + let ship = new Ship(shipId, shipData.properties, shipData.slots); + + return ship.buildFrom(detailedBuild.references[0].code); +}; + /** * Instantiates a ship from a ship-loadout object * @param {Object} detailedBuild ship-loadout object * @return {Ship} Ship instance */ -export function fromDetailedBuild(detailedBuild) { +export function oldfromDetailedBuild(detailedBuild) { let shipId = Object.keys(Ships).find((shipId) => Ships[shipId].properties.name.toLowerCase() == detailedBuild.ship.toLowerCase()); if (!shipId) { diff --git a/src/app/shipyard/Ship.js b/src/app/shipyard/Ship.js index f7949c5e..cdd04045 100755 --- a/src/app/shipyard/Ship.js +++ b/src/app/shipyard/Ship.js @@ -1116,7 +1116,7 @@ export default class Ship { let bufsize = 0; for (let slot of slots) { if (slot.length > 0) { - bufsize = bufsize + 1 + (3 * slot.length) + 1; // + bufsize = bufsize + 1 + (5 * slot.length) + 1; } } @@ -1131,8 +1131,8 @@ export default class Ship { buffer.writeInt8(i, curpos++); for (let slotMod of slot) { buffer.writeInt8(slotMod.id, curpos++); - buffer.writeInt16BE(slotMod.value, curpos); - curpos += 2; + buffer.writeInt32LE(slotMod.value, curpos); + curpos += 4; } buffer.writeInt8(-1, curpos++); } @@ -1157,18 +1157,18 @@ export default class Ship { */ decodeModificationsStruct(buffer, arr) { let curpos = 0; - let module = buffer.readInt8(curpos++); - while (module != -1) { + let slot = buffer.readInt8(curpos++); + while (slot != -1) { let modifications = {}; let modificationId = buffer.readInt8(curpos++); while (modificationId != -1) { - let modificationValue = buffer.readInt16BE(curpos); - curpos += 2; + let modificationValue = buffer.readInt32LE(curpos); + curpos += 4; modifications[Modifications.modifiers[modificationId]] = modificationValue; modificationId = buffer.readInt8(curpos++); } - arr[module] = modifications; - module = buffer.readInt8(curpos++); + arr[slot] = modifications; + slot = buffer.readInt8(curpos++); } } From 6d6ef2a93e59d089414799b4419f5bbe13c20bd4 Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Fri, 4 Nov 2016 20:53:05 +0000 Subject: [PATCH 33/39] Provide weights to 2dp --- src/app/components/HardpointSlot.jsx | 2 +- src/app/components/InternalSlot.jsx | 2 +- src/app/components/StandardSlot.jsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/components/HardpointSlot.jsx b/src/app/components/HardpointSlot.jsx index 796fc2c0..c5b8d457 100644 --- a/src/app/components/HardpointSlot.jsx +++ b/src/app/components/HardpointSlot.jsx @@ -52,7 +52,7 @@ export default class HardpointSlot extends Slot { {m.type && m.type.match('T') ? : ''} {m.type && m.type.match('E') ? : ''} {classRating} {translate(m.name || m.grp)}
    -
    {formats.round1(m.getMass())}{u.T}
    +
    {formats.round(m.getMass())}{u.T}
    { m.getDps() ?
    {translate('DPS')}: {formats.round1(m.getDps())} { m.getClip() ? ({formats.round1((m.getClip() * m.getDps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload())) }) : null }
    : null } diff --git a/src/app/components/InternalSlot.jsx b/src/app/components/InternalSlot.jsx index cf3eb767..318bb85b 100644 --- a/src/app/components/InternalSlot.jsx +++ b/src/app/components/InternalSlot.jsx @@ -28,7 +28,7 @@ export default class InternalSlot extends Slot { return
    {classRating} {translate(m.name || m.grp)}
    -
    {formats.round1(mass)}{u.T}
    +
    {formats.round(mass)}{u.T}
    { m.getOptMass() ?
    {translate('optimal mass')}: {formats.int(m.getOptMass())}{u.T}
    : null } diff --git a/src/app/components/StandardSlot.jsx b/src/app/components/StandardSlot.jsx index 29142085..8140f7f0 100644 --- a/src/app/components/StandardSlot.jsx +++ b/src/app/components/StandardSlot.jsx @@ -80,7 +80,7 @@ export default class StandardSlot extends TranslatedComponent {
    {slot.maxClass}
    {classRating} {translate(m.grp == 'bh' ? m.grp : m.name || m.grp)}
    -
    {formats.round1(mass)}{units.T}
    +
    {formats.round(mass)}{units.T}
    { m.grp == 'bh' && m.name ?
    {translate(m.name)}
    : null } From b99fbf07f52898f099e1060617d269c2ad76dbae Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Fri, 4 Nov 2016 22:34:08 +0000 Subject: [PATCH 34/39] Fix up name of shield boost --- src/app/components/HardpointSlot.jsx | 2 +- src/app/i18n/en.js | 2 +- src/app/shipyard/Module.js | 16 ++++------------ src/app/shipyard/Ship.js | 6 +++--- src/app/utils/SlotFunctions.js | 10 +++------- 5 files changed, 12 insertions(+), 24 deletions(-) diff --git a/src/app/components/HardpointSlot.jsx b/src/app/components/HardpointSlot.jsx index c5b8d457..beb8dae3 100644 --- a/src/app/components/HardpointSlot.jsx +++ b/src/app/components/HardpointSlot.jsx @@ -61,7 +61,7 @@ export default class HardpointSlot extends Slot { { m.getDps() && m.getEps() ?
    {translate('DPE')}: {formats.f1(m.getDps() / m.getEps())}
    : null } { m.getRoF() ?
    {translate('ROF')}: {formats.f1(m.getRoF())}{u.ps}
    : null } { m.getRange() && !m.getDps() ?
    {translate('Range')} : {formats.round(m.getRange() / 1000)}{u.km}
    : null } - { m.getShieldMul() ?
    +{formats.rPct(m.getShieldMul())}
    : null } + { m.getShieldBoost() ?
    +{formats.rPct(m.getShieldBoost())}
    : null } { m.getAmmo() ?
    {translate('ammunition')}: {formats.int(m.getClip())}/{formats.int(m.getAmmo())}
    : null } { m && validMods.length > 0 ?
    : null }
    diff --git a/src/app/i18n/en.js b/src/app/i18n/en.js index e5e37774..6276f0d1 100644 --- a/src/app/i18n/en.js +++ b/src/app/i18n/en.js @@ -133,7 +133,7 @@ export const terms = { reload: 'Reload time', rof: 'Rate of fire', shield: 'Shield', - shieldmul: 'Shield boost', + shieldboost: 'Shield boost', spinup: 'Spin up time', syscap: 'Systems capacity', sysrate: 'Systems recharge rate', diff --git a/src/app/shipyard/Module.js b/src/app/shipyard/Module.js index d9c8a6e7..d367cc9a 100755 --- a/src/app/shipyard/Module.js +++ b/src/app/shipyard/Module.js @@ -274,11 +274,11 @@ export default class Module { } /** - * Get the shield multiplier for this module, taking in to account modifications - * @return {Number} the shield multiplier of this module + * Get the shield boost for this module, taking in to account modifications + * @return {Number} the shield boost of this module */ - getShieldMultiplier() { - return this._getModifiedValue('shieldmul'); + getShieldBoost() { + return this._getModifiedValue('shieldboost'); } /** @@ -365,14 +365,6 @@ export default class Module { return result; } - /** - * Get the shield multiplier for this module, taking in to account modifications - * @return {Number} the shield multiplier of this module - */ - getShieldMul() { - return this._getModifiedValue('shieldmul'); - } - /** * Get the damage for this module, taking in to account modifications * @return {Number} the damage of this module diff --git a/src/app/shipyard/Ship.js b/src/app/shipyard/Ship.js index cdd04045..05784e45 100755 --- a/src/app/shipyard/Ship.js +++ b/src/app/shipyard/Ship.js @@ -232,7 +232,7 @@ export default class Ship { sg = sgSlot.m; } - // TODO obtain shieldMultiplier + // TODO obtain shield boost //return Calc.shieldStrength(this.hullMass, this.baseShieldStrength, sg, this.shieldMultiplier + (multiplierDelta || 0)); return Calc.shieldStrength(this.hullMass, this.baseShieldStrength, sg, 0 + (multiplierDelta || 0)); } @@ -430,7 +430,7 @@ export default class Ship { // Could be for either thrusters or FSD this.updateTopSpeed(); this.updateJumpStats(); - } else if (name == 'shieldmul') { + } else if (name == 'shieldboost') { m.setModValue(name, value); this.updateShield(); } else if (name == 'hullboost') { @@ -906,7 +906,7 @@ export default class Ship { // Shield from boosters for (let slot of this.hardpoints) { if (slot.m && slot.m.grp == 'sb') { - shield += baseShield * slot.m.getShieldMultiplier(); + shield += baseShield * slot.m.getShieldBoost(); } } this.shield = shield; diff --git a/src/app/utils/SlotFunctions.js b/src/app/utils/SlotFunctions.js index 211e3be5..74697c7f 100644 --- a/src/app/utils/SlotFunctions.js +++ b/src/app/utils/SlotFunctions.js @@ -118,8 +118,6 @@ const PROP_BLACKLIST = { ssdam: 1, mjdps: 1, mjeps: 1, - M: 1, - P: 1, mass: 1, cost: 1, recover: 1, @@ -138,14 +136,12 @@ const PROP_BLACKLIST = { const TERM_LOOKUP = { pgen: 'power', armouradd: 'armour', - shieldmul: 'multiplier', rof: 'ROF', dps: 'DPS' }; const FORMAT_LOOKUP = { - time: 'time', - shieldmul: 'rPct' + time: 'time' }; const UNIT_LOOKUP = { @@ -246,13 +242,13 @@ export function diffDetails(language, m, mm) { if (mAffectsShield) { if (m.grp == 'sb') { // Both m and mm must be utility modules if this is true - newShield = this.calcShieldStrengthWith(null, m.shieldmul - (mm ? mm.getShieldMul() || 0 : 0)); + newShield = this.calcShieldStrengthWith(null, m.shieldboost - (mm ? mm.getShieldBoost() || 0 : 0)); } else { newShield = this.calcShieldStrengthWith(m); } } else { // Old module must be a shield booster - newShield = this.calcShieldStrengthWith(null, -mm.getShieldMul()); + newShield = this.calcShieldStrengthWith(null, -mm.getShieldBoost()); } let sgDiffClass = Math.round((newShield - shield) * 100) / 100 == 0 ? 'muted' : (newShield > shield ? 'secondary' : 'warning'); From 916a6d5e485913ccb6c6a4a30d33547977b9ac8b Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Fri, 4 Nov 2016 23:00:19 +0000 Subject: [PATCH 35/39] Show shield boost decimal place --- src/app/components/HardpointSlot.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/components/HardpointSlot.jsx b/src/app/components/HardpointSlot.jsx index beb8dae3..9ecc6dae 100644 --- a/src/app/components/HardpointSlot.jsx +++ b/src/app/components/HardpointSlot.jsx @@ -61,7 +61,7 @@ export default class HardpointSlot extends Slot { { m.getDps() && m.getEps() ?
    {translate('DPE')}: {formats.f1(m.getDps() / m.getEps())}
    : null } { m.getRoF() ?
    {translate('ROF')}: {formats.f1(m.getRoF())}{u.ps}
    : null } { m.getRange() && !m.getDps() ?
    {translate('Range')} : {formats.round(m.getRange() / 1000)}{u.km}
    : null } - { m.getShieldBoost() ?
    +{formats.rPct(m.getShieldBoost())}
    : null } + { m.getShieldBoost() ?
    +{formats.pct1(m.getShieldBoost())}
    : null } { m.getAmmo() ?
    {translate('ammunition')}: {formats.int(m.getClip())}/{formats.int(m.getAmmo())}
    : null } { m && validMods.length > 0 ?
    : null }
    From 87ab684ba3f4005daf9cce309d3506a92e881c7b Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Fri, 4 Nov 2016 23:58:03 +0000 Subject: [PATCH 36/39] Use degree symbol for jitter --- src/app/components/Modification.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/components/Modification.jsx b/src/app/components/Modification.jsx index bbcaaf7a..c73d0607 100644 --- a/src/app/components/Modification.jsx +++ b/src/app/components/Modification.jsx @@ -61,7 +61,7 @@ export default class ModificationsMenu extends TranslatedComponent { return (
    -
    {translate(name)}{' (%)'}
    +
    {translate(name)}{name === 'jitter' ? ' (°)' : ' (%)'}
    ); From be45637435844358713872b5f4053fc182a4c7d0 Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Sat, 5 Nov 2016 13:43:57 +0000 Subject: [PATCH 37/39] Use GZIP to decrease length of modifications URL string --- src/app/shipyard/Ship.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/app/shipyard/Ship.js b/src/app/shipyard/Ship.js index 05784e45..7aed5db8 100755 --- a/src/app/shipyard/Ship.js +++ b/src/app/shipyard/Ship.js @@ -4,6 +4,7 @@ import Module from './Module'; import LZString from 'lz-string'; import isEqual from 'lodash/lang'; import { Modifications } from 'coriolis-data/dist'; +var zlib = require('zlib'); const UNIQUE_MODULES = ['psg', 'sg', 'bsg', 'rf', 'fs', 'fh']; @@ -575,7 +576,11 @@ export default class Ship { if (modstr.match(':')) { this.decodeModificationsString(modstr, mods); } else { - this.decodeModificationsStruct(Buffer.from(modstr, 'base64'), mods); + try { + this.decodeModificationsStruct(zlib.gunzipSync(new Buffer(modstr, 'base64')), mods); + } catch (err) { + // Could be out-of-date URL; ignore + } } } @@ -1142,7 +1147,7 @@ export default class Ship { buffer.writeInt8(-1, curpos++); } - this.serialized.modifications = buffer.toString('base64').replace(/\//g, '-'); + this.serialized.modifications = zlib.gzipSync(buffer).toString('base64').replace(/\//g, '-'); } else { this.serialized.modifications = null; } From 07df44a90744196a2a7f97b6ed791469ce6b97c3 Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Sat, 5 Nov 2016 13:59:23 +0000 Subject: [PATCH 38/39] Allow 3dp for ship and module discounts --- src/app/components/Header.jsx | 4 ++-- src/app/shipyard/Serializer.js | 5 +++++ src/app/shipyard/Ship.js | 4 ++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/app/components/Header.jsx b/src/app/components/Header.jsx index dd309546..db2294d5 100644 --- a/src/app/components/Header.jsx +++ b/src/app/components/Header.jsx @@ -27,7 +27,7 @@ function normalizePercent(val) { if (val === '' || isNaN(val)) { return 0; } - val = Math.round(val * 100) / 100; + val = Math.round(val * 1000) / 1000; return val >= 100 ? 100 : val; } @@ -512,4 +512,4 @@ export default class Header extends TranslatedComponent { ); } -} \ No newline at end of file +} diff --git a/src/app/shipyard/Serializer.js b/src/app/shipyard/Serializer.js index 2608b4ea..1fb3e477 100644 --- a/src/app/shipyard/Serializer.js +++ b/src/app/shipyard/Serializer.js @@ -115,6 +115,11 @@ export function toDetailedBuild(buildName, ship) { return data; }; +/** + * Instantiates a ship from a ship-loadout object, using the code + * @param {Object} detailedBuild ship-loadout object + * @return {Ship} Ship instance + */ export function fromDetailedBuild(detailedBuild) { let shipId = Object.keys(Ships).find((shipId) => Ships[shipId].properties.name.toLowerCase() == detailedBuild.ship.toLowerCase()); diff --git a/src/app/shipyard/Ship.js b/src/app/shipyard/Ship.js index 7aed5db8..25e35110 100755 --- a/src/app/shipyard/Ship.js +++ b/src/app/shipyard/Ship.js @@ -4,7 +4,7 @@ import Module from './Module'; import LZString from 'lz-string'; import isEqual from 'lodash/lang'; import { Modifications } from 'coriolis-data/dist'; -var zlib = require('zlib'); +const zlib = require('zlib'); const UNIQUE_MODULES = ['psg', 'sg', 'bsg', 'rf', 'fs', 'fh']; @@ -234,7 +234,7 @@ export default class Ship { } // TODO obtain shield boost - //return Calc.shieldStrength(this.hullMass, this.baseShieldStrength, sg, this.shieldMultiplier + (multiplierDelta || 0)); + // return Calc.shieldStrength(this.hullMass, this.baseShieldStrength, sg, this.shieldMultiplier + (multiplierDelta || 0)); return Calc.shieldStrength(this.hullMass, this.baseShieldStrength, sg, 0 + (multiplierDelta || 0)); } From 2fa96fc1a5c19c414481ceb6b7033846ec6f819c Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Sat, 5 Nov 2016 14:37:37 +0000 Subject: [PATCH 39/39] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index addce06a..674af442 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "coriolis_shipyard", - "version": "2.2.0", + "version": "2.2.1", "repository": { "type": "git", "url": "https://github.com/EDCD/coriolis"
    {f1(ship.totalEps)} {f1(ship.totalHps)} {int(ship.armour)}{int(ship.shieldStrength)} {u.MJ} { ship.shieldMultiplier > 1 && ship.shieldStrength > 0 ? ({formats.rPct(ship.shieldMultiplier)}) : null }{int(ship.shield)} {u.MJ} {sgRecover} {sgRecharge} {ship.hullMass} {u.T}