diff --git a/src/app/components/Defence.jsx b/src/app/components/Defence.jsx
index 39b4a22f..0a1ba4fe 100644
--- a/src/app/components/Defence.jsx
+++ b/src/app/components/Defence.jsx
@@ -1,8 +1,6 @@
import React from 'react';
-import cn from 'classnames';
import TranslatedComponent from './TranslatedComponent';
import * as Calc from '../shipyard/Calculations';
-import { DamageAbsolute, DamageExplosive, DamageKinetic, DamageThermal } from './SvgIcons';
import PieChart from './PieChart';
import VerticalBarChart from './VerticalBarChart';
@@ -48,64 +46,7 @@ export default class Defence extends TranslatedComponent {
}
/**
- * Calculate the sustained DPS for a ship at a given range, excluding resistances
- * @param {Object} ship The ship
- * @param {Object} opponent The opponent ship
- * @param {int} engagementrange The range between the ship and opponent
- * @returns {Object} Sustained DPS for shield and armour
- */
- _calcSustainedDps(ship, opponent, engagementrange) {
- const shieldsdps = {
- absolute: 0,
- explosive: 0,
- kinetic: 0,
- thermal: 0
- };
-
- const armoursdps = {
- absolute: 0,
- explosive: 0,
- kinetic: 0,
- thermal: 0
- };
-
- for (let i = 0; i < ship.hardpoints.length; i++) {
- if (ship.hardpoints[i].m && ship.hardpoints[i].enabled && ship.hardpoints[i].maxClass > 0) {
- const m = ship.hardpoints[i].m;
- // Initial sustained DPS
- let sDps = m.getClip() ? (m.getClip() * m.getDps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload()) : m.getDps();
- // Take fall-off in to account
- const falloff = m.getFalloff();
- if (falloff && engagementrange > falloff) {
- const dropoffRange = m.getRange() - falloff;
- sDps *= 1 - Math.min((engagementrange - falloff) / dropoffRange, 1);
- }
- // Piercing/hardness modifier (for armour only)
- const armourMultiple = m.getPiercing() >= opponent.hardness ? 1 : m.getPiercing() / opponent.hardness;
- // Break out the damage according to type
- if (m.getDamageDist().A) {
- shieldsdps.absolute += sDps * m.getDamageDist().A;
- armoursdps.absolute += sDps * m.getDamageDist().A * armourMultiple;
- }
- if (m.getDamageDist().E) {
- shieldsdps.explosive += sDps * m.getDamageDist().E;
- armoursdps.explosive += sDps * m.getDamageDist().E * armourMultiple;
- }
- if (m.getDamageDist().K) {
- shieldsdps.kinetic += sDps * m.getDamageDist().K;
- armoursdps.kinetic += sDps * m.getDamageDist().K * armourMultiple;
- }
- if (m.getDamageDist().T) {
- shieldsdps.thermal += sDps * m.getDamageDist().T;
- armoursdps.thermal += sDps * m.getDamageDist().T * armourMultiple;
- }
- }
- }
- return { shieldsdps, armoursdps };
- }
-
- /**
- * Render shields
+ * Render defence
* @return {React.Component} contents
*/
render() {
diff --git a/src/app/components/Offence.jsx b/src/app/components/Offence.jsx
index 86307353..506b6fcd 100644
--- a/src/app/components/Offence.jsx
+++ b/src/app/components/Offence.jsx
@@ -1,13 +1,16 @@
import React from 'react';
-import cn from 'classnames';
import TranslatedComponent from './TranslatedComponent';
import * as Calc from '../shipyard/Calculations';
-import { DamageAbsolute, DamageExplosive, DamageKinetic, DamageThermal } from './SvgIcons';
import PieChart from './PieChart';
import VerticalBarChart from './VerticalBarChart';
/**
* Offence information
+ * Offence information consists of four panels:
+ * - textual information (time to drain cap, time to take down shields etc.)
+ * - breakdown of damage sources (pie chart)
+ * - comparison of shield resistances (table chart)
+ * - effective sustained DPS of weapons (bar chart)
*/
export default class Offence extends TranslatedComponent {
static propTypes = {
@@ -25,7 +28,7 @@ export default class Offence extends TranslatedComponent {
constructor(props) {
super(props);
- const { shield, armour, shielddamage, armourdamage } = this._calcMetrics(props.ship, props.opponent, props.sys, props.engagementrange);
+ const { shield, armour, shielddamage, armourdamage } = Calc.offenceMetrics(props.ship, props.opponent, props.eng, props.engagementrange);
this.state = { shield, armour, shielddamage, armourdamage };
}
@@ -36,419 +39,116 @@ export default class Offence extends TranslatedComponent {
*/
componentWillReceiveProps(nextProps) {
if (this.props.marker != nextProps.marker || this.props.sys != nextProps.sys) {
- const { shield, armour, shielddamage, armourdamage } = this._calcMetrics(nextProps.ship, nextProps.opponent, nextProps.sys, nextProps.engagementrange);
+ const { shield, armour, shielddamage, armourdamage } = Calc.offenceMetrics(nextProps.ship, nextProps.opponent, nextProps.wep, nextProps.engagementrange);
this.setState({ shield, armour, shielddamage, armourdamage });
- return true;
}
+ return true;
}
/**
- * Calculate the sustained DPS for a ship at a given range, excluding resistances
- * @param {Object} ship The ship
- * @param {Object} opponent The opponent ship
- * @param {int} engagementrange The range between the ship and opponent
- * @returns {Object} Sustained DPS for shield and armour
- */
- _calcSustainedDps(ship, opponent, engagementrange) {
- const shieldsdps = {
- absolute: 0,
- explosive: 0,
- kinetic: 0,
- thermal: 0
- };
-
- const armoursdps = {
- absolute: 0,
- explosive: 0,
- kinetic: 0,
- thermal: 0
- };
-
- for (let i = 0; i < ship.hardpoints.length; i++) {
- if (ship.hardpoints[i].m && ship.hardpoints[i].enabled && ship.hardpoints[i].maxClass > 0) {
- const m = ship.hardpoints[i].m;
- // Initial sustained DPS
- let sDps = m.getClip() ? (m.getClip() * m.getDps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload()) : m.getDps();
- // Take fall-off in to account
- const falloff = m.getFalloff();
- if (falloff && engagementrange > falloff) {
- const dropoffRange = m.getRange() - falloff;
- sDps *= 1 - Math.min((engagementrange - falloff) / dropoffRange, 1);
- }
- // Piercing/hardness modifier (for armour only)
- const armourMultiple = m.getPiercing() >= opponent.hardness ? 1 : m.getPiercing() / opponent.hardness;
- // Break out the damage according to type
- if (m.getDamageDist().A) {
- shieldsdps.absolute += sDps * m.getDamageDist().A;
- armoursdps.absolute += sDps * m.getDamageDist().A * armourMultiple;
- }
- if (m.getDamageDist().E) {
- shieldsdps.explosive += sDps * m.getDamageDist().E;
- armoursdps.explosive += sDps * m.getDamageDist().E * armourMultiple;
- }
- if (m.getDamageDist().K) {
- shieldsdps.kinetic += sDps * m.getDamageDist().K;
- armoursdps.kinetic += sDps * m.getDamageDist().K * armourMultiple;
- }
- if (m.getDamageDist().T) {
- shieldsdps.thermal += sDps * m.getDamageDist().T;
- armoursdps.thermal += sDps * m.getDamageDist().T * armourMultiple;
- }
- }
- }
- return { shieldsdps, armoursdps };
- }
-
- /**
- * Obtain the recharge rate of the SYS capacitor of a power distributor given pips
- * @param {Object} pd The power distributor
- * @param {number} sys The number of pips to SYS
- * @returns {number} The recharge rate in MJ/s
- */
- _calcSysRechargeRate(pd, sys) {
- return pd.getSystemsRechargeRate() * Math.pow(sys, 1.1) / Math.pow(4, 1.1);
- }
-
- /**
- * Calculate shield metrics
- * @param {Object} ship The ship
- * @param {Object} opponent The opponent ship
- * @param {int} sys The opponent ship
- * @param {int} engagementrange The range between the ship and opponent
- * @returns {Object} Shield metrics
- */
- _calcMetrics(ship, opponent, sys, engagementrange) {
- const sysResistance = this._calcSysResistance(sys);
- const maxSysResistance = this._calcSysResistance(4);
-
- // Obtain the opponent's sustained DPS on us for later damage calculations
- const { shieldsdps, armoursdps } = this._calcSustainedDps(opponent, ship, engagementrange);
-
- let shielddamage = {};
- let shield = {};
- const shieldGeneratorSlot = ship.findInternalByGroup('sg');
- if (shieldGeneratorSlot && shieldGeneratorSlot.enabled && shieldGeneratorSlot.m) {
- const shieldGenerator = shieldGeneratorSlot.m;
-
- // Boosters
- let boost = 1;
- let boosterExplDmg = 1;
- let boosterKinDmg = 1;
- let boosterThermDmg = 1;
- for (let slot of ship.hardpoints) {
- if (slot.enabled && slot.m && slot.m.grp == 'sb') {
- boost += slot.m.getShieldBoost();
- boosterExplDmg = boosterExplDmg * (1 - slot.m.getExplosiveResistance());
- boosterKinDmg = boosterKinDmg * (1 - slot.m.getKineticResistance());
- boosterThermDmg = boosterThermDmg * (1 - slot.m.getThermalResistance());
- }
- }
-
- // Calculate diminishing returns for boosters
- boost = Math.min(boost, (1 - Math.pow(Math.E, -0.7 * boost)) * 2.5);
- // Remove base shield generator strength
- boost -= 1;
- // Apply diminishing returns
- boosterExplDmg = boosterExplDmg > 0.7 ? boosterExplDmg : 0.7 - (0.7 - boosterExplDmg) / 2;
- boosterKinDmg = boosterKinDmg > 0.7 ? boosterKinDmg : 0.7 - (0.7 - boosterKinDmg) / 2;
- boosterThermDmg = boosterThermDmg > 0.7 ? boosterThermDmg : 0.7 - (0.7 - boosterThermDmg) / 2;
-
- const generatorStrength = Calc.shieldStrength(ship.hullMass, ship.baseShieldStrength, shieldGenerator, 1);
- const boostersStrength = generatorStrength * boost;
-
- // Recover time is the time taken to go from 0 to 50%. It includes a 16-second wait before shields start to recover
- const shieldToRecover = (generatorStrength + boostersStrength) / 2;
- const powerDistributor = ship.standard[4].m;
- const sysRechargeRate = this._calcSysRechargeRate(powerDistributor, sys);
-
- // Our initial regeneration comes from the SYS capacitor store, which is replenished as it goes
- // 0.6 is a magic number from FD: each 0.6 MW of energy from the power distributor recharges 1 MJ/s of regeneration
- let capacitorDrain = (shieldGenerator.getBrokenRegenerationRate() * 0.6) - sysRechargeRate;
- let capacitorLifetime = powerDistributor.getSystemsCapacity() / capacitorDrain;
-
- let recover = 16;
- if (capacitorDrain <= 0 || shieldToRecover < capacitorLifetime * shieldGenerator.getBrokenRegenerationRate()) {
- // We can recover the entire shield from the capacitor store
- recover += shieldToRecover / shieldGenerator.getBrokenRegenerationRate();
- } else {
- // We can recover some of the shield from the capacitor store
- recover += capacitorLifetime;
- const remainingShieldToRecover = shieldToRecover - capacitorLifetime * shieldGenerator.getBrokenRegenerationRate();
- if (sys === 0) {
- // No system pips so will never recover shields
- recover = Math.Inf;
- } else {
- // Recover remaining shields at the rate of the power distributor's recharge
- recover += remainingShieldToRecover / (sysRechargeRate / 0.6);
- }
- }
-
- // Recharge time is the time taken to go from 50% to 100%
- const shieldToRecharge = (generatorStrength + boostersStrength) / 2;
-
- // Our initial regeneration comes from the SYS capacitor store, which is replenished as it goes
- // 0.6 is a magic number from FD: each 0.6 MW of energy from the power distributor recharges 1 MJ/s of regeneration
- capacitorDrain = (shieldGenerator.getRegenerationRate() * 0.6) - sysRechargeRate;
- capacitorLifetime = powerDistributor.getSystemsCapacity() / capacitorDrain;
-
- let recharge = 0;
- if (capacitorDrain <= 0 || shieldToRecharge < capacitorLifetime * shieldGenerator.getRegenerationRate()) {
- // We can recharge the entire shield from the capacitor store
- recharge += shieldToRecharge / shieldGenerator.getRegenerationRate();
- } else {
- // We can recharge some of the shield from the capacitor store
- recharge += capacitorLifetime;
- const remainingShieldToRecharge = shieldToRecharge - capacitorLifetime * shieldGenerator.getRegenerationRate();
- if (sys === 0) {
- // No system pips so will never recharge shields
- recharge = Math.Inf;
- } else {
- // Recharge remaining shields at the rate of the power distributor's recharge
- recharge += remainingShieldToRecharge / (sysRechargeRate / 0.6);
- }
- }
-
- shield = {
- generator: generatorStrength,
- boosters: boostersStrength,
- cells: ship.shieldCells,
- total: generatorStrength + boostersStrength + ship.shieldCells,
- recover,
- recharge,
- };
-
- // Shield resistances have three components: the shield generator, the shield boosters and the SYS pips.
- // We re-cast these as damage percentages
- shield.absolute = {
- generator: 1,
- boosters: 1,
- sys: 1 - sysResistance,
- total: 1 - sysResistance,
- max: 1 - maxSysResistance
- };
-
- shield.explosive = {
- generator: 1 - shieldGenerator.getExplosiveResistance(),
- boosters: boosterExplDmg,
- sys: (1 - sysResistance),
- total: (1 - shieldGenerator.getExplosiveResistance()) * boosterExplDmg * (1 - sysResistance),
- max: (1 - shieldGenerator.getExplosiveResistance()) * boosterExplDmg * (1 - maxSysResistance)
- };
-
- shield.kinetic = {
- generator: 1 - shieldGenerator.getKineticResistance(),
- boosters: boosterKinDmg,
- sys: (1 - sysResistance),
- total: (1 - shieldGenerator.getKineticResistance()) * boosterKinDmg * (1 - sysResistance),
- max: (1 - shieldGenerator.getKineticResistance()) * boosterKinDmg * (1 - maxSysResistance)
- };
-
- shield.thermal = {
- generator: 1 - shieldGenerator.getThermalResistance(),
- boosters: boosterThermDmg,
- sys: (1 - sysResistance),
- total: (1 - shieldGenerator.getThermalResistance()) * boosterThermDmg * (1 - sysResistance),
- max: (1 - shieldGenerator.getThermalResistance()) * boosterThermDmg * (1 - maxSysResistance)
- };
-
- shielddamage.absolutesdps = shieldsdps.absolute *= shield.absolute.total;
- shielddamage.explosivesdps = shieldsdps.explosive *= shield.explosive.total;
- shielddamage.kineticsdps = shieldsdps.kinetic *= shield.kinetic.total;
- shielddamage.thermalsdps = shieldsdps.thermal *= shield.thermal.total;
- shielddamage.totalsdps = shielddamage.absolutesdps + shielddamage.explosivesdps + shielddamage.kineticsdps + shielddamage.thermalsdps;
- }
-
- // Armour from bulkheads
- const armourBulkheads = ship.baseArmour + (ship.baseArmour * ship.bulkheads.m.getHullBoost());
- let armourReinforcement = 0;
-
- let moduleArmour = 0;
- let moduleProtection = 1;
-
- let hullExplDmg = 1;
- let hullKinDmg = 1;
- let hullThermDmg = 1;
-
- // Armour from HRPs and module armour from MRPs
- for (let slot of ship.internal) {
- if (slot.m && slot.m.grp == 'hr') {
- armourReinforcement += slot.m.getHullReinforcement();
- // Hull boost for HRPs is applied against the ship's base armour
- armourReinforcement += ship.baseArmour * slot.m.getModValue('hullboost') / 10000;
-
- hullExplDmg = hullExplDmg * (1 - slot.m.getExplosiveResistance());
- hullKinDmg = hullKinDmg * (1 - slot.m.getKineticResistance());
- hullThermDmg = hullThermDmg * (1 - slot.m.getThermalResistance());
- }
- if (slot.m && slot.m.grp == 'mrp') {
- moduleArmour += slot.m.getIntegrity();
- moduleProtection = moduleProtection * (1 - slot.m.getProtection());
- }
- }
- moduleProtection = 1 - moduleProtection;
-
- // Apply diminishing returns
- hullExplDmg = hullExplDmg > 0.7 ? hullExplDmg : 0.7 - (0.7 - hullExplDmg) / 2;
- hullKinDmg = hullKinDmg > 0.7 ? hullKinDmg : 0.7 - (0.7 - hullKinDmg) / 2;
- hullThermDmg = hullThermDmg > 0.7 ? hullThermDmg : 0.7 - (0.7 - hullThermDmg) / 2;
-
- const armour = {
- bulkheads: armourBulkheads,
- reinforcement: armourReinforcement,
- modulearmour: moduleArmour,
- moduleprotection: moduleProtection,
- total: armourBulkheads + armourReinforcement
- };
-
- // Armour resistances have two components: bulkheads and HRPs
- // We re-cast these as damage percentages
- armour.absolute = {
- bulkheads: 1,
- reinforcement: 1,
- total: 1
- };
-
- armour.explosive = {
- bulkheads: 1 - ship.bulkheads.m.getExplosiveResistance(),
- reinforcement: hullExplDmg,
- total: (1 - ship.bulkheads.m.getExplosiveResistance()) * hullExplDmg
- };
-
- armour.kinetic = {
- bulkheads: 1 - ship.bulkheads.m.getKineticResistance(),
- reinforcement: hullKinDmg,
- total: (1 - ship.bulkheads.m.getKineticResistance()) * hullKinDmg
- };
-
- armour.thermal = {
- bulkheads: 1 - ship.bulkheads.m.getThermalResistance(),
- reinforcement: hullThermDmg,
- total: (1 - ship.bulkheads.m.getThermalResistance()) * hullThermDmg
- };
-
- const armourdamage = {
- absolutesdps: armoursdps.absolute *= armour.absolute.total,
- explosivesdps: armoursdps.explosive *= armour.explosive.total,
- kineticsdps: armoursdps.kinetic *= armour.kinetic.total,
- thermalsdps: armoursdps.thermal *= armour.thermal.total
- };
- armourdamage.totalsdps = armourdamage.absolutesdps + armourdamage.explosivesdps + armourdamage.kineticsdps + armourdamage.thermalsdps;
-
- return { shield, armour, shielddamage, armourdamage };
- }
-
- /**
- * Calculate the resistance provided by SYS pips
- * @param {integer} sys the value of the SYS pips
- * @returns {integer} the resistance for the given pips
- */
- _calcSysResistance(sys) {
- return Math.pow(sys,0.85) * 0.6 / Math.pow(4,0.85);
- }
-
- /**
- * Render shields
+ * Render offence
* @return {React.Component} contents
*/
render() {
- const { ship, sys } = this.props;
+ const { ship, wep } = this.props;
const { language, tooltip, termtip } = this.context;
const { formats, translate, units } = language;
- const { shield, armour, shielddamage, armourdamage } = this.state;
+// const { shield, armour, shielddamage, armourdamage } = this.state;
- const shieldSourcesData = [];
- const effectiveShieldData = [];
- const shieldDamageTakenData = [];
- const shieldTooltipDetails = [];
- const shieldAbsoluteTooltipDetails = [];
- const shieldExplosiveTooltipDetails = [];
- const shieldKineticTooltipDetails = [];
- const shieldThermalTooltipDetails = [];
- let maxEffectiveShield = 0;
- if (shield.total) {
- if (Math.round(shield.generator) > 0) shieldSourcesData.push({ value: Math.round(shield.generator), label: translate('generator') });
- if (Math.round(shield.boosters) > 0) shieldSourcesData.push({ value: Math.round(shield.boosters), label: translate('boosters') });
- if (Math.round(shield.cells) > 0) shieldSourcesData.push({ value: Math.round(shield.cells), label: translate('cells') });
+// const shieldSourcesData = [];
+// const effectiveShieldData = [];
+// const shieldDamageTakenData = [];
+// const shieldTooltipDetails = [];
+// const shieldAbsoluteTooltipDetails = [];
+// const shieldExplosiveTooltipDetails = [];
+// const shieldKineticTooltipDetails = [];
+// const shieldThermalTooltipDetails = [];
+// let maxEffectiveShield = 0;
+// if (shield.total) {
+// shieldSourcesData.push({ value: Math.round(shield.generator), label: translate('generator') });
+// shieldSourcesData.push({ value: Math.round(shield.boosters), label: translate('boosters') });
+// shieldSourcesData.push({ value: Math.round(shield.cells), label: translate('cells') });
- if (Math.round(shield.generator) > 0) shieldTooltipDetails.push(
{translate('generator') + ' ' + formats.int(shield.generator)}{units.MJ}
);
- if (Math.round(shield.boosters) > 0) shieldTooltipDetails.push({translate('boosters') + ' ' + formats.int(shield.boosters)}{units.MJ}
);
- if (Math.round(shield.cells) > 0) shieldTooltipDetails.push({translate('cells') + ' ' + formats.int(shield.cells)}{units.MJ}
);
+// shieldTooltipDetails.push({translate('generator') + ' ' + formats.int(shield.generator)}{units.MJ}
);
+// shieldTooltipDetails.push({translate('boosters') + ' ' + formats.int(shield.boosters)}{units.MJ}
);
+// shieldTooltipDetails.push({translate('cells') + ' ' + formats.int(shield.cells)}{units.MJ}
);
- shieldAbsoluteTooltipDetails.push({translate('generator') + ' ' + formats.pct1(shield.absolute.generator)}
);
- shieldAbsoluteTooltipDetails.push({translate('boosters') + ' ' + formats.pct1(shield.absolute.boosters)}
);
- shieldAbsoluteTooltipDetails.push({translate('power distributor') + ' ' + formats.pct1(shield.absolute.sys)}
);
+// shieldAbsoluteTooltipDetails.push({translate('generator') + ' ' + formats.pct1(shield.absolute.generator)}
);
+// shieldAbsoluteTooltipDetails.push({translate('boosters') + ' ' + formats.pct1(shield.absolute.boosters)}
);
+// shieldAbsoluteTooltipDetails.push({translate('power distributor') + ' ' + formats.pct1(shield.absolute.sys)}
);
- shieldExplosiveTooltipDetails.push({translate('generator') + ' ' + formats.pct1(shield.explosive.generator)}
);
- shieldExplosiveTooltipDetails.push({translate('boosters') + ' ' + formats.pct1(shield.explosive.boosters)}
);
- shieldExplosiveTooltipDetails.push({translate('power distributor') + ' ' + formats.pct1(shield.explosive.sys)}
);
+// shieldExplosiveTooltipDetails.push({translate('generator') + ' ' + formats.pct1(shield.explosive.generator)}
);
+// shieldExplosiveTooltipDetails.push({translate('boosters') + ' ' + formats.pct1(shield.explosive.boosters)}
);
+// shieldExplosiveTooltipDetails.push({translate('power distributor') + ' ' + formats.pct1(shield.explosive.sys)}
);
- shieldKineticTooltipDetails.push({translate('generator') + ' ' + formats.pct1(shield.kinetic.generator)}
);
- shieldKineticTooltipDetails.push({translate('boosters') + ' ' + formats.pct1(shield.kinetic.boosters)}
);
- shieldKineticTooltipDetails.push({translate('power distributor') + ' ' + formats.pct1(shield.kinetic.sys)}
);
+// shieldKineticTooltipDetails.push({translate('generator') + ' ' + formats.pct1(shield.kinetic.generator)}
);
+// shieldKineticTooltipDetails.push({translate('boosters') + ' ' + formats.pct1(shield.kinetic.boosters)}
);
+// shieldKineticTooltipDetails.push({translate('power distributor') + ' ' + formats.pct1(shield.kinetic.sys)}
);
- shieldThermalTooltipDetails.push({translate('generator') + ' ' + formats.pct1(shield.thermal.generator)}
);
- shieldThermalTooltipDetails.push({translate('boosters') + ' ' + formats.pct1(shield.thermal.boosters)}
);
- shieldThermalTooltipDetails.push({translate('power distributor') + ' ' + formats.pct1(shield.thermal.sys)}
);
+// shieldThermalTooltipDetails.push({translate('generator') + ' ' + formats.pct1(shield.thermal.generator)}
);
+// shieldThermalTooltipDetails.push({translate('boosters') + ' ' + formats.pct1(shield.thermal.boosters)}
);
+// shieldThermalTooltipDetails.push({translate('power distributor') + ' ' + formats.pct1(shield.thermal.sys)}
);
- const effectiveAbsoluteShield = shield.total / shield.absolute.total;
- effectiveShieldData.push({ value: Math.round(effectiveAbsoluteShield), label: translate('absolute') });
- const effectiveExplosiveShield = shield.total / shield.explosive.total;
- effectiveShieldData.push({ value: Math.round(effectiveExplosiveShield), label: translate('explosive') });
- const effectiveKineticShield = shield.total / shield.kinetic.total;
- effectiveShieldData.push({ value: Math.round(effectiveKineticShield), label: translate('kinetic') });
- const effectiveThermalShield = shield.total / shield.thermal.total;
- effectiveShieldData.push({ value: Math.round(effectiveThermalShield), label: translate('thermal') });
+// const effectiveAbsoluteShield = shield.total / shield.absolute.total;
+// effectiveShieldData.push({ value: Math.round(effectiveAbsoluteShield), label: translate('absolute') });
+// const effectiveExplosiveShield = shield.total / shield.explosive.total;
+// effectiveShieldData.push({ value: Math.round(effectiveExplosiveShield), label: translate('explosive') });
+// const effectiveKineticShield = shield.total / shield.kinetic.total;
+// effectiveShieldData.push({ value: Math.round(effectiveKineticShield), label: translate('kinetic') });
+// const effectiveThermalShield = shield.total / shield.thermal.total;
+// effectiveShieldData.push({ value: Math.round(effectiveThermalShield), label: translate('thermal') });
- shieldDamageTakenData.push({ value: Math.round(shield.absolute.total * 100), label: translate('absolute') });
- shieldDamageTakenData.push({ value: Math.round(shield.explosive.total * 100), label: translate('explosive') });
- shieldDamageTakenData.push({ value: Math.round(shield.kinetic.total * 100), label: translate('kinetic') });
- shieldDamageTakenData.push({ value: Math.round(shield.thermal.total * 100), label: translate('thermal') });
+// shieldDamageTakenData.push({ value: Math.round(shield.absolute.total * 100), label: translate('absolute') });
+// shieldDamageTakenData.push({ value: Math.round(shield.explosive.total * 100), label: translate('explosive') });
+// shieldDamageTakenData.push({ value: Math.round(shield.kinetic.total * 100), label: translate('kinetic') });
+// shieldDamageTakenData.push({ value: Math.round(shield.thermal.total * 100), label: translate('thermal') });
- maxEffectiveShield = Math.max(shield.total / shield.absolute.max, shield.total / shield.explosive.max, shield.total / shield.kinetic.max, shield.total / shield.thermal.max);
- }
+// maxEffectiveShield = Math.max(shield.total / shield.absolute.max, shield.total / shield.explosive.max, shield.total / shield.kinetic.max, shield.total / shield.thermal.max);
+// }
- const armourSourcesData = [];
- if (Math.round(armour.bulkheads) > 0) armourSourcesData.push({ value: Math.round(armour.bulkheads), label: translate('bulkheads') });
- if (Math.round(armour.reinforcement) > 0) armourSourcesData.push({ value: Math.round(armour.reinforcement), label: translate('reinforcement') });
+// const armourSourcesData = [];
+// armourSourcesData.push({ value: Math.round(armour.bulkheads), label: translate('bulkheads') });
+// armourSourcesData.push({ value: Math.round(armour.reinforcement), label: translate('reinforcement') });
- const armourTooltipDetails = [];
- if (armour.bulkheads > 0) armourTooltipDetails.push({translate('bulkheads') + ' ' + formats.int(armour.bulkheads)}
);
- if (armour.reinforcement > 0) armourTooltipDetails.push({translate('reinforcement') + ' ' + formats.int(armour.reinforcement)}
);
+// const armourTooltipDetails = [];
+// armourTooltipDetails.push({translate('bulkheads') + ' ' + formats.int(armour.bulkheads)}
);
+// armourTooltipDetails.push({translate('reinforcement') + ' ' + formats.int(armour.reinforcement)}
);
- const armourAbsoluteTooltipDetails = [];
- armourAbsoluteTooltipDetails.push({translate('bulkheads') + ' ' + formats.pct1(armour.absolute.bulkheads)}
);
- armourAbsoluteTooltipDetails.push({translate('reinforcement') + ' ' + formats.pct1(armour.absolute.reinforcement)}
);
+// const armourAbsoluteTooltipDetails = [];
+// armourAbsoluteTooltipDetails.push({translate('bulkheads') + ' ' + formats.pct1(armour.absolute.bulkheads)}
);
+// armourAbsoluteTooltipDetails.push({translate('reinforcement') + ' ' + formats.pct1(armour.absolute.reinforcement)}
);
- const armourExplosiveTooltipDetails = [];
- armourExplosiveTooltipDetails.push({translate('bulkheads') + ' ' + formats.pct1(armour.explosive.bulkheads)}
);
- armourExplosiveTooltipDetails.push({translate('reinforcement') + ' ' + formats.pct1(armour.explosive.reinforcement)}
);
+// const armourExplosiveTooltipDetails = [];
+// armourExplosiveTooltipDetails.push({translate('bulkheads') + ' ' + formats.pct1(armour.explosive.bulkheads)}
);
+// armourExplosiveTooltipDetails.push({translate('reinforcement') + ' ' + formats.pct1(armour.explosive.reinforcement)}
);
- const armourKineticTooltipDetails = [];
- armourKineticTooltipDetails.push({translate('bulkheads') + ' ' + formats.pct1(armour.kinetic.bulkheads)}
);
- armourKineticTooltipDetails.push({translate('reinforcement') + ' ' + formats.pct1(armour.kinetic.reinforcement)}
);
+// const armourKineticTooltipDetails = [];
+// armourKineticTooltipDetails.push({translate('bulkheads') + ' ' + formats.pct1(armour.kinetic.bulkheads)}
);
+// armourKineticTooltipDetails.push({translate('reinforcement') + ' ' + formats.pct1(armour.kinetic.reinforcement)}
);
- const armourThermalTooltipDetails = [];
- armourThermalTooltipDetails.push({translate('bulkheads') + ' ' + formats.pct1(armour.thermal.bulkheads)}
);
- armourThermalTooltipDetails.push({translate('reinforcement') + ' ' + formats.pct1(armour.thermal.reinforcement)}
);
+// const armourThermalTooltipDetails = [];
+// armourThermalTooltipDetails.push({translate('bulkheads') + ' ' + formats.pct1(armour.thermal.bulkheads)}
);
+// armourThermalTooltipDetails.push({translate('reinforcement') + ' ' + formats.pct1(armour.thermal.reinforcement)}
);
- const effectiveArmourData = [];
- const effectiveAbsoluteArmour = armour.total / armour.absolute.total;
- effectiveArmourData.push({ value: Math.round(effectiveAbsoluteArmour), label: translate('absolute') });
- const effectiveExplosiveArmour = armour.total / armour.explosive.total;
- effectiveArmourData.push({ value: Math.round(effectiveExplosiveArmour), label: translate('explosive') });
- const effectiveKineticArmour = armour.total / armour.kinetic.total;
- effectiveArmourData.push({ value: Math.round(effectiveKineticArmour), label: translate('kinetic') });
- const effectiveThermalArmour = armour.total / armour.thermal.total;
- effectiveArmourData.push({ value: Math.round(effectiveThermalArmour), label: translate('thermal') });
+// const effectiveArmourData = [];
+// const effectiveAbsoluteArmour = armour.total / armour.absolute.total;
+// effectiveArmourData.push({ value: Math.round(effectiveAbsoluteArmour), label: translate('absolute') });
+// const effectiveExplosiveArmour = armour.total / armour.explosive.total;
+// effectiveArmourData.push({ value: Math.round(effectiveExplosiveArmour), label: translate('explosive') });
+// const effectiveKineticArmour = armour.total / armour.kinetic.total;
+// effectiveArmourData.push({ value: Math.round(effectiveKineticArmour), label: translate('kinetic') });
+// const effectiveThermalArmour = armour.total / armour.thermal.total;
+// effectiveArmourData.push({ value: Math.round(effectiveThermalArmour), label: translate('thermal') });
- const armourDamageTakenData = [];
- armourDamageTakenData.push({ value: Math.round(armour.absolute.total * 100), label: translate('absolute') });
- armourDamageTakenData.push({ value: Math.round(armour.explosive.total * 100), label: translate('explosive') });
- armourDamageTakenData.push({ value: Math.round(armour.kinetic.total * 100), label: translate('kinetic') });
- armourDamageTakenData.push({ value: Math.round(armour.thermal.total * 100), label: translate('thermal') });
+// const armourDamageTakenData = [];
+// armourDamageTakenData.push({ value: Math.round(armour.absolute.total * 100), label: translate('absolute') });
+// armourDamageTakenData.push({ value: Math.round(armour.explosive.total * 100), label: translate('explosive') });
+// armourDamageTakenData.push({ value: Math.round(armour.kinetic.total * 100), label: translate('kinetic') });
+// armourDamageTakenData.push({ value: Math.round(armour.thermal.total * 100), label: translate('thermal') });
return (
-
+
+ {/*
{shield.total ?
{translate('shield metrics')}
@@ -475,7 +175,7 @@ export default class Offence extends TranslatedComponent {
{translate('armour metrics')}
{armourTooltipDetails}
)} onMouseOut={tooltip.bind(null, null)} className='summary'>{translate('raw armour strength')}
{formats.int(armour.total)}
-
{translate('PHRASE_TIME_TO_LOSE_ARMOUR')}
{armourdamage.totalsdps == 0 ? translate('infinity') : formats.time(armour.total / armourdamage.totalsdps)}
+
{translate('PHRASE_TIME_TO_LOSE_ARMOUR')}
{armourdamage.totalsdps == 0 ? translate('ever') : formats.time(armour.total / armourdamage.totalsdps)}
{translate('raw module armour')}
{formats.int(armour.modulearmour)}
{translate('PHRASE_MODULE_PROTECTION_EXTERNAL')}
{formats.pct1(armour.moduleprotection / 2)}
{translate('PHRASE_MODULE_PROTECTION_INTERNAL')}
{formats.pct1(armour.moduleprotection)}
@@ -493,6 +193,7 @@ export default class Offence extends TranslatedComponent {
{translate('effective armour')}
+ */}
);
}
}
diff --git a/src/app/components/OutfittingSubpages.jsx b/src/app/components/OutfittingSubpages.jsx
index 6a760833..18f7ac47 100644
--- a/src/app/components/OutfittingSubpages.jsx
+++ b/src/app/components/OutfittingSubpages.jsx
@@ -10,6 +10,7 @@ import CostSection from './CostSection';
import EngineProfile from './EngineProfile';
import FSDProfile from './FSDProfile';
import Movement from './Movement';
+import Offence from './Offence';
import Defence from './Defence';
import WeaponDamageChart from './WeaponDamageChart';
@@ -117,8 +118,10 @@ export default class OutfittingSubpages extends TranslatedComponent {
_offenceTab() {
const { ship, sys, eng, wep, cargo, fuel, boost, engagementRange, opponent, opponentBuild } = this.props;
+ const marker = `${ship.toString()}:${opponent.name}:${opponentBuild}:${engagementRange}`;
+
return
-
Offence goes here
+
;
}
diff --git a/src/app/components/WeaponDamageChart.jsx b/src/app/components/WeaponDamageChart.jsx
index 90121e5b..990225aa 100644
--- a/src/app/components/WeaponDamageChart.jsx
+++ b/src/app/components/WeaponDamageChart.jsx
@@ -100,13 +100,12 @@ export default class WeaponDamageChart extends TranslatedComponent {
*/
_calcMaxSDps(ship, opponent, hull) {
// Additional information to allow effectiveness calculations
- const { shield, armour, shielddamage, armourdamage } = Calc.defenceMetrics(opponent, ship, 4, 0);
-
+ const defence = hull ? Calc.armourMetrics(opponent) : Calc.shieldMetrics(opponent, 4);
let maxSDps = 0;
for (let i = 0; i < ship.hardpoints.length; i++) {
if (ship.hardpoints[i].maxClass > 0 && ship.hardpoints[i].m && ship.hardpoints[i].enabled) {
const m = ship.hardpoints[i].m;
- const thisSDps = this._calcWeaponSDps(ship, m, opponent, hull, shield, armour, 0);
+ const thisSDps = this._calcWeaponSDps(ship, m, opponent, defence, 0);
if (thisSDps > maxSDps) {
maxSDps = thisSDps;
}
@@ -156,14 +155,14 @@ export default class WeaponDamageChart extends TranslatedComponent {
*/
_calcSDps(ship, weaponNames, opponent, hull, engagementRange) {
// Additional information to allow effectiveness calculations
- const { shield, armour, shielddamage, armourdamage } = Calc.defenceMetrics(opponent, ship, 4, engagementRange);
+ const defence = hull ? Calc.armourMetrics(opponent) : Calc.shieldMetrics(opponent, 4);
let results = {};
let weaponNum = 0;
for (let i = 0; i < ship.hardpoints.length; i++) {
if (ship.hardpoints[i].maxClass > 0 && ship.hardpoints[i].m && ship.hardpoints[i].enabled) {
const m = ship.hardpoints[i].m;
- results[weaponNames[weaponNum++]] = this._calcWeaponSDps(ship, m, opponent, hull, shield, armour, engagementRange);
+ results[weaponNames[weaponNum++]] = this._calcWeaponSDps(ship, m, opponent, defence, engagementRange);
}
}
return results;
@@ -174,13 +173,11 @@ export default class WeaponDamageChart extends TranslatedComponent {
* @param {Object} ship The ship that will deal the damage
* @param {Object} m The weapon that will deal the damage
* @param {Object} opponent The ship against which damage will be dealt
- * @param {boolean} hull True if hitting hull
- * @param {Object} shield Shield defence metrics
- * @param {Object} armour Armour defence metrics
+ * @param {Object} defence defence metrics (either shield or hull)
* @param {Object} engagementRange The engagement range
* @return {object} Returns the sustained DPS for the weapon
*/
- _calcWeaponSDps(ship, m, opponent, hull, shield, armour, engagementRange) {
+ _calcWeaponSDps(ship, m, opponent, defence, engagementRange) {
let falloff = 1;
if (m.getFalloff()) {
// Calculate the falloff % due to range
@@ -199,16 +196,16 @@ export default class WeaponDamageChart extends TranslatedComponent {
let effectiveness = 0;
if (m.getDamageDist().E) {
- effectiveness += m.getDamageDist().E * (hull ? armour.explosive.total : shield.explosive.total);
+ effectiveness += m.getDamageDist().E * defence.explosive.total;
}
if (m.getDamageDist().K) {
- effectiveness += m.getDamageDist().K * (hull ? armour.kinetic.total : shield.kinetic.total);
+ effectiveness += m.getDamageDist().K * defence.kinetic.total;
}
if (m.getDamageDist().T) {
- effectiveness += m.getDamageDist().T * (hull ? armour.thermal.total : shield.thermal.total);
+ effectiveness += m.getDamageDist().T * defence.thermal.total;
}
if (m.getDamageDist().A) {
- effectiveness += m.getDamageDist().A * (hull ? armour.absolute.total : shield.absolute.total);
+ effectiveness += m.getDamageDist().A * defence.absolute.total;
}
// Return the final effective SDPS
diff --git a/src/app/shipyard/Calculations.js b/src/app/shipyard/Calculations.js
index 8c7a5368..b27be63e 100644
--- a/src/app/shipyard/Calculations.js
+++ b/src/app/shipyard/Calculations.js
@@ -310,23 +310,18 @@ export function calcYaw(mass, baseYaw, thrusters, engpip, eng, boostFactor, boos
return result;
}
- /**
- * Calculate defence metrics
- * @param {Object} ship The ship
- * @param {Object} opponent The opponent ship
- * @param {int} sys The pips to SYS
- * @param {int} engagementrange The range between the ship and opponent
- * @returns {Object} Defence metrics
- */
-export function defenceMetrics(ship, opponent, sys, engagementrange) {
+/**
+ * Calculate shield metrics
+ * @param {Object} ship The ship
+ * @param {int} sys The pips to SYS
+ * @returns {Object} Shield metrics
+ */
+export function shieldMetrics(ship, sys) {
const sysResistance = this.sysResistance(sys);
const maxSysResistance = this.sysResistance(4);
- // Obtain the opponent's sustained DPS on us for later damage calculations
- const { shieldsdps, armoursdps } = this._sustainedDps(opponent, ship, engagementrange);
-
- let shielddamage = {};
let shield = {};
+
const shieldGeneratorSlot = ship.findInternalByGroup('sg');
if (shieldGeneratorSlot && shieldGeneratorSlot.enabled && shieldGeneratorSlot.m) {
const shieldGenerator = shieldGeneratorSlot.m;
@@ -451,14 +446,17 @@ export function defenceMetrics(ship, opponent, sys, engagementrange) {
total: (1 - shieldGenerator.getThermalResistance()) * boosterThermDmg * (1 - sysResistance),
max: (1 - shieldGenerator.getThermalResistance()) * boosterThermDmg * (1 - maxSysResistance)
};
-
- shielddamage.absolutesdps = shieldsdps.absolute *= shield.absolute.total;
- shielddamage.explosivesdps = shieldsdps.explosive *= shield.explosive.total;
- shielddamage.kineticsdps = shieldsdps.kinetic *= shield.kinetic.total;
- shielddamage.thermalsdps = shieldsdps.thermal *= shield.thermal.total;
- shielddamage.totalsdps = shielddamage.absolutesdps + shielddamage.explosivesdps + shielddamage.kineticsdps + shielddamage.thermalsdps;
}
+ return shield;
+}
+
+/**
+ * Calculate armour metrics
+ * @param {Object} ship The ship
+ * @returns {Object} Armour metrics
+ */
+export function armourMetrics(ship) {
// Armour from bulkheads
const armourBulkheads = ship.baseArmour + (ship.baseArmour * ship.bulkheads.m.getHullBoost());
let armourReinforcement = 0;
@@ -527,6 +525,35 @@ export function defenceMetrics(ship, opponent, sys, engagementrange) {
total: (1 - ship.bulkheads.m.getThermalResistance()) * hullThermDmg
};
+ return armour;
+}
+
+/**
+ * Calculate defence metrics for a ship
+ * @param {Object} ship The ship
+ * @param {Object} opponent The opponent ship
+ * @param {int} sys The pips to SYS
+ * @param {int} engagementrange The range between the ship and opponent
+ * @returns {Object} Defence metrics
+ */
+export function defenceMetrics(ship, opponent, sys, engagementrange) {
+ // Obtain the shield metrics
+ const shield = this.shieldMetrics(ship, sys);
+
+ // Obtain the armour metrics
+ const armour = this.armourMetrics(ship);
+
+ // Obtain the opponent's sustained DPS on us
+ const { shieldsdps, armoursdps } = this._sustainedDps(opponent, ship, engagementrange);
+
+ const shielddamage = shield.generatorStrength ? {
+ absolutesdps: shieldsdps.absolute *= shield.absolute.total,
+ explosivesdps: shieldsdps.explosive *= shield.explosive.total,
+ kineticsdps: shieldsdps.kinetic *= shield.kinetic.total,
+ thermalsdps: shieldsdps.thermal *= shield.thermal.total,
+ totalsdps: shielddamage.absolutesdps + shielddamage.explosivesdps + shielddamage.kineticsdps + shielddamage.thermalsdps
+ } : {} ;
+
const armourdamage = {
absolutesdps: armoursdps.absolute *= armour.absolute.total,
explosivesdps: armoursdps.explosive *= armour.explosive.total,
@@ -538,6 +565,37 @@ export function defenceMetrics(ship, opponent, sys, engagementrange) {
return { shield, armour, shielddamage, armourdamage };
}
+/**
+ * Calculate offence metrics for a ship
+ * @param {Object} ship The ship
+ * @param {Object} opponent The opponent ship
+ * @param {int} wep The pips to WEP
+ * @param {int} engagementrange The range between the ship and opponent
+ * @returns {Object} Offence metrics
+ */
+export function offenceMetrics(ship, opponent, wep, engagementrange) {
+
+ // Per-weapon and total damage to armour
+ const armourdamage = {};
+
+ // Obtain the opponent's shield and armour metrics
+ const opponentShields = this.shieldMetrics(opponent, 4);
+ const opponentArmour = this.armourMetrics(opponent);
+
+ // Per-weapon and total damage to shields
+ const shielddamage = opponentShields.generatorStrength ? {
+ absolute: {
+ weapon1: 10,
+ weapon2: 10,
+ weapon3: 10,
+ weapon4: 10,
+ total: 40
+ }
+ } : {};
+
+ return { shielddamage, armourdamage };
+}
+
/**
* Calculate the resistance provided by SYS pips
* @param {integer} sys the value of the SYS pips