diff --git a/src/app/components/Defence.jsx b/src/app/components/Defence.jsx
index d26bbfc1..a74018e3 100644
--- a/src/app/components/Defence.jsx
+++ b/src/app/components/Defence.jsx
@@ -4,6 +4,8 @@ import TranslatedComponent from './TranslatedComponent';
import * as Calc from '../shipyard/Calculations';
import PieChart from './PieChart';
import VerticalBarChart from './VerticalBarChart';
+import autoBind from 'auto-bind';
+import { ARMOUR_METRICS, MODULE_PROTECTION_METRICS, SHIELD_METRICS } from 'ed-forge/lib/ship-stats';
/**
* Defence information
@@ -15,12 +17,10 @@ import VerticalBarChart from './VerticalBarChart';
*/
export default class Defence extends TranslatedComponent {
static propTypes = {
- marker: PropTypes.string.isRequired,
+ code: PropTypes.string.isRequired,
ship: PropTypes.object.isRequired,
opponent: PropTypes.object.isRequired,
- engagementrange: PropTypes.number.isRequired,
- sys: PropTypes.number.isRequired,
- opponentWep: PropTypes.number.isRequired
+ engagementRange: PropTypes.number.isRequired,
};
/**
@@ -29,22 +29,7 @@ export default class Defence extends TranslatedComponent {
*/
constructor(props) {
super(props);
-
- const { shield, armour, shielddamage, armourdamage } = Calc.defenceMetrics(props.ship, props.opponent, props.sys, props.opponentWep, props.engagementrange);
- this.state = { shield, armour, shielddamage, armourdamage };
- }
-
- /**
- * Update the state if our properties change
- * @param {Object} nextProps Incoming/Next properties
- * @return {boolean} Returns true if the component should be rerendered
- */
- componentWillReceiveProps(nextProps) {
- if (this.props.marker != nextProps.marker || this.props.sys != nextProps.sys) {
- const { shield, armour, shielddamage, armourdamage } = Calc.defenceMetrics(nextProps.ship, nextProps.opponent, nextProps.sys, nextProps.opponentWep, nextProps.engagementrange);
- this.setState({ shield, armour, shielddamage, armourdamage });
- }
- return true;
+ autoBind(this);
}
/**
@@ -52,187 +37,104 @@ export default class Defence extends TranslatedComponent {
* @return {React.Component} contents
*/
render() {
- const { opponent, sys, opponentWep } = this.props;
+ const { ship } = this.props;
const { language, tooltip, termtip } = this.context;
const { formats, translate, units } = language;
- const { shield, armour, shielddamage, armourdamage } = this.state;
- const pd = opponent.standard[4].m;
+ const shields = ship.get(SHIELD_METRICS);
- const shieldSourcesData = [];
- const effectiveShieldData = [];
- const shieldDamageTakenData = [];
- const shieldSourcesTt = [];
- const shieldDamageTakenAbsoluteTt = [];
- const shieldDamageTakenExplosiveTt = [];
- const shieldDamageTakenKineticTt = [];
- const shieldDamageTakenThermalTt = [];
- const effectiveShieldAbsoluteTt = [];
- const effectiveShieldExplosiveTt = [];
- const effectiveShieldKineticTt = [];
- const effectiveShieldThermalTt = [];
- 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') });
- shieldSourcesData.push({ value: Math.round(shield.addition), label: translate('shield addition') });
+ // Data for pie chart (absolute MJ)
+ const shieldSourcesData = [
+ 'byBoosters', 'byGenerator', 'byReinforcements', 'bySCBs',
+ ].map((key) => { return { label: key, value: Math.round(shields[key]) }; });
- if (shield.generator > 0) {
- shieldSourcesTt.push(
{translate('generator') + ' ' + formats.int(shield.generator)}{units.MJ}
);
- effectiveShieldAbsoluteTt.push({translate('generator') + ' ' + formats.int(shield.generator)}{units.MJ}
);
- effectiveShieldExplosiveTt.push({translate('generator') + ' ' + formats.int(shield.generator)}{units.MJ}
);
- effectiveShieldKineticTt.push({translate('generator') + ' ' + formats.int(shield.generator)}{units.MJ}
);
- effectiveShieldThermalTt.push({translate('generator') + ' ' + formats.int(shield.generator)}{units.MJ}
);
- if (shield.boosters > 0) {
- shieldSourcesTt.push({translate('boosters') + ' ' + formats.int(shield.boosters)}{units.MJ}
);
- effectiveShieldAbsoluteTt.push({translate('boosters') + ' ' + formats.int(shield.boosters)}{units.MJ}
);
- effectiveShieldExplosiveTt.push({translate('boosters') + ' ' + formats.int(shield.boosters)}{units.MJ}
);
- effectiveShieldKineticTt.push({translate('boosters') + ' ' + formats.int(shield.boosters)}{units.MJ}
);
- effectiveShieldThermalTt.push({translate('boosters') + ' ' + formats.int(shield.boosters)}{units.MJ}
);
- }
+ // Data for tooltip
+ const shieldSourcesTt = shieldSourcesData.map((o) => {
+ let { label, value } = o;
+ return
+ {translate(label)} {formats.int(value)}{units.MJ}
+
;
+ });
- if (shield.cells > 0) {
- shieldSourcesTt.push({translate('cells') + ' ' + formats.int(shield.cells)}{units.MJ}
);
- effectiveShieldAbsoluteTt.push({translate('cells') + ' ' + formats.int(shield.cells)}{units.MJ}
);
- effectiveShieldExplosiveTt.push({translate('cells') + ' ' + formats.int(shield.cells)}{units.MJ}
);
- effectiveShieldKineticTt.push({translate('cells') + ' ' + formats.int(shield.cells)}{units.MJ}
);
- effectiveShieldThermalTt.push({translate('cells') + ' ' + formats.int(shield.cells)}{units.MJ}
);
- }
+ // Shield resistances
+ const shieldDamageTakenData = [
+ 'absolute', 'explosive', 'kinetic', 'thermal',
+ ].map((label) => {
+ const dmgMult = shields[label];
+ const tooltip = ['byBoosters', 'byGenerator', 'bySys'].map(
+ (label) =>
+ {translate(label)} {formats.pct1(dmgMult[label])}
+
+ );
+ return { label, value: Math.round(100 * dmgMult.withSys), tooltip };
+ });
- // Add effective shield from resistances
- const rawMj = shield.generator + shield.boosters + shield.cells;
- const explosiveMj = rawMj / (shield.explosive.base) - rawMj;
- if (explosiveMj != 0) effectiveShieldExplosiveTt.push({translate('resistance') + ' ' + formats.int(explosiveMj)}{units.MJ}
);
- const kineticMj = rawMj / (shield.kinetic.base) - rawMj;
- if (kineticMj != 0) effectiveShieldKineticTt.push({translate('resistance') + ' ' + formats.int(kineticMj)}{units.MJ}
);
- const thermalMj = rawMj / (shield.thermal.base) - rawMj;
- if (thermalMj != 0) effectiveShieldThermalTt.push({translate('resistance') + ' ' + formats.int(thermalMj)}{units.MJ}
);
+ // Effective MJ
+ const effectiveShieldData = [
+ 'absolute', 'explosive', 'kinetic', 'thermal'
+ ].map((label) => {
+ const dmgMult = shields[label];
+ const raw = shields.withSCBs;
+ const tooltip = ['byBoosters', 'byGenerator', 'bySys'].map(
+ (label) =>
+ {translate(label)} {formats.int(raw * dmgMult[label])}{units.MJ}
+
+ );
+ return { label, value: Math.round(dmgMult.withSys * raw), tooltip };
+ });
+ const maxEffectiveShield = Math.max(...effectiveShieldData.map((o) => o.value));
- // Add effective shield from power distributor SYS pips
- if (shield.absolute.sys != 1) {
- effectiveShieldAbsoluteTt.push({translate('power distributor') + ' ' + formats.int(rawMj / shield.absolute.total - rawMj)}{units.MJ}
);
- effectiveShieldExplosiveTt.push({translate('power distributor') + ' ' + formats.int(rawMj / shield.explosive.total - rawMj / shield.explosive.base)}{units.MJ}
);
- effectiveShieldKineticTt.push({translate('power distributor') + ' ' + formats.int(rawMj / shield.kinetic.total - rawMj / shield.kinetic.base)}{units.MJ}
);
- effectiveShieldThermalTt.push({translate('power distributor') + ' ' + formats.int(rawMj / shield.thermal.total - rawMj / shield.thermal.base)}{units.MJ}
);
- }
- }
+ const armour = ship.get(ARMOUR_METRICS);
+ const moduleProtection = ship.get(MODULE_PROTECTION_METRICS);
- shieldDamageTakenAbsoluteTt.push({translate('generator') + ' ' + formats.pct1(shield.absolute.generator)}
);
- shieldDamageTakenAbsoluteTt.push({translate('boosters') + ' ' + formats.pct1(shield.absolute.boosters)}
);
- shieldDamageTakenAbsoluteTt.push({translate('power distributor') + ' ' + formats.pct1(shield.absolute.sys)}
);
+ // Data for pie chart (absolute HP)
+ const armourSourcesData = ['base', 'byAlloys', 'byHRPs',].map(
+ (key) => { return { label: key, value: Math.round(armour[key]) }; }
+ );
- shieldDamageTakenExplosiveTt.push({translate('generator') + ' ' + formats.pct1(shield.explosive.generator)}
);
- shieldDamageTakenExplosiveTt.push({translate('boosters') + ' ' + formats.pct1(shield.explosive.boosters)}
);
- shieldDamageTakenExplosiveTt.push({translate('power distributor') + ' ' + formats.pct1(shield.explosive.sys)}
);
+ // Data for tooltip
+ const armourSourcesTt = armourSourcesData.map((o) => {
+ let { label, value } = o;
+ return {translate(label)} {formats.int(value)}
;
+ });
- shieldDamageTakenKineticTt.push({translate('generator') + ' ' + formats.pct1(shield.kinetic.generator)}
);
- shieldDamageTakenKineticTt.push({translate('boosters') + ' ' + formats.pct1(shield.kinetic.boosters)}
);
- shieldDamageTakenKineticTt.push({translate('power distributor') + ' ' + formats.pct1(shield.kinetic.sys)}
);
+ // Armour resistances
+ const armourDamageTakenData = [
+ 'absolute', 'explosive', 'kinetic', 'thermal', 'caustic',
+ ].map((label) => {
+ const dmgMult = armour[label];
+ const tooltip = ['byAlloys', 'byHRPs'].map(
+ (label) =>
+ {translate(label)} {formats.pct1(dmgMult[label])}
+
+ );
+ return { label, value: Math.round(100 * dmgMult.damageMultiplier), tooltip };
+ });
- shieldDamageTakenThermalTt.push({translate('generator') + ' ' + formats.pct1(shield.thermal.generator)}
);
- shieldDamageTakenThermalTt.push({translate('boosters') + ' ' + formats.pct1(shield.thermal.boosters)}
);
- shieldDamageTakenThermalTt.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'), tooltip: effectiveShieldAbsoluteTt });
- const effectiveExplosiveShield = shield.total / shield.explosive.total;
- effectiveShieldData.push({ value: Math.round(effectiveExplosiveShield), label: translate('explosive'), tooltip: effectiveShieldExplosiveTt });
- const effectiveKineticShield = shield.total / shield.kinetic.total;
- effectiveShieldData.push({ value: Math.round(effectiveKineticShield), label: translate('kinetic'), tooltip: effectiveShieldKineticTt });
- const effectiveThermalShield = shield.total / shield.thermal.total;
- effectiveShieldData.push({ value: Math.round(effectiveThermalShield), label: translate('thermal'), tooltip: effectiveShieldThermalTt });
-
- shieldDamageTakenData.push({ value: Math.round(shield.absolute.total * 100), label: translate('absolute'), tooltip: shieldDamageTakenAbsoluteTt });
- shieldDamageTakenData.push({ value: Math.round(shield.explosive.total * 100), label: translate('explosive'), tooltip: shieldDamageTakenExplosiveTt });
- shieldDamageTakenData.push({ value: Math.round(shield.kinetic.total * 100), label: translate('kinetic'), tooltip: shieldDamageTakenKineticTt });
- shieldDamageTakenData.push({ value: Math.round(shield.thermal.total * 100), label: translate('thermal'), tooltip: shieldDamageTakenThermalTt });
-
- 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 = [];
- armourSourcesData.push({ value: Math.round(armour.bulkheads), label: translate('bulkheads') });
- armourSourcesData.push({ value: Math.round(armour.reinforcement), label: translate('reinforcement') });
-
- const armourSourcesTt = [];
- const effectiveArmourAbsoluteTt = [];
- const effectiveArmourExplosiveTt = [];
- const effectiveArmourKineticTt = [];
- const effectiveArmourThermalTt = [];
- const effectiveArmourCausticTt = [];
- if (armour.bulkheads > 0) {
- armourSourcesTt.push({translate('bulkheads') + ' ' + formats.int(armour.bulkheads)}
);
- effectiveArmourAbsoluteTt.push({translate('bulkheads') + ' ' + formats.int(armour.bulkheads)}
);
- effectiveArmourExplosiveTt.push({translate('bulkheads') + ' ' + formats.int(armour.bulkheads)}
);
- effectiveArmourKineticTt.push({translate('bulkheads') + ' ' + formats.int(armour.bulkheads)}
);
- effectiveArmourThermalTt.push({translate('bulkheads') + ' ' + formats.int(armour.bulkheads)}
);
- effectiveArmourCausticTt.push({translate('bulkheads') + ' ' + formats.int(armour.bulkheads)}
);
- if (armour.reinforcement > 0) {
- armourSourcesTt.push({translate('reinforcement') + ' ' + formats.int(armour.reinforcement)}
);
- effectiveArmourAbsoluteTt.push({translate('reinforcement') + ' ' + formats.int(armour.reinforcement)}
);
- effectiveArmourExplosiveTt.push({translate('reinforcement') + ' ' + formats.int(armour.reinforcement)}
);
- effectiveArmourKineticTt.push({translate('reinforcement') + ' ' + formats.int(armour.reinforcement)}
);
- effectiveArmourThermalTt.push({translate('reinforcement') + ' ' + formats.int(armour.reinforcement)}
);
- effectiveArmourCausticTt.push({translate('reinforcement') + ' ' + formats.int(armour.reinforcement)}
);
- }
- }
-
- const rawArmour = armour.bulkheads + armour.reinforcement;
-
- const armourDamageTakenTt = [];
- armourDamageTakenTt.push({translate('bulkheads') + ' ' + formats.pct1(armour.absolute.bulkheads)}
);
- armourDamageTakenTt.push({translate('reinforcement') + ' ' + formats.pct1(armour.absolute.reinforcement)}
);
-
- const armourDamageTakenExplosiveTt = [];
- armourDamageTakenExplosiveTt.push({translate('bulkheads') + ' ' + formats.pct1(armour.explosive.bulkheads)}
);
- armourDamageTakenExplosiveTt.push({translate('reinforcement') + ' ' + formats.pct1(armour.explosive.reinforcement)}
);
- if (armour.explosive.total != 1) effectiveArmourExplosiveTt.push({translate('resistance') + ' ' + formats.int(rawArmour / armour.explosive.total - rawArmour)}
);
-
- const armourDamageTakenKineticTt = [];
- armourDamageTakenKineticTt.push({translate('bulkheads') + ' ' + formats.pct1(armour.kinetic.bulkheads)}
);
- armourDamageTakenKineticTt.push({translate('reinforcement') + ' ' + formats.pct1(armour.kinetic.reinforcement)}
);
- if (armour.kinetic.total != 1) effectiveArmourKineticTt.push({translate('resistance') + ' ' + formats.int(rawArmour / armour.kinetic.total - rawArmour)}
);
-
- const armourDamageTakenThermalTt = [];
- armourDamageTakenThermalTt.push({translate('bulkheads') + ' ' + formats.pct1(armour.thermal.bulkheads)}
);
- armourDamageTakenThermalTt.push({translate('reinforcement') + ' ' + formats.pct1(armour.thermal.reinforcement)}
);
- if (armour.thermal.total != 1) effectiveArmourThermalTt.push({translate('resistance') + ' ' + formats.int(rawArmour / armour.thermal.total - rawArmour)}
);
-
- const armourDamageTakenCausticTt = [];
- armourDamageTakenCausticTt.push({translate('bulkheads') + ' ' + formats.pct1(armour.caustic.bulkheads)}
);
- armourDamageTakenCausticTt.push({translate('reinforcement') + ' ' + formats.pct1(armour.caustic.reinforcement)}
);
- if (armour.thermal.total != 1) effectiveArmourCausticTt.push({translate('resistance') + ' ' + formats.int(rawArmour / armour.caustic.total - rawArmour)}
);
-
- const effectiveArmourData = [];
- const effectiveAbsoluteArmour = armour.total / armour.absolute.total;
- effectiveArmourData.push({ value: Math.round(effectiveAbsoluteArmour), label: translate('absolute'), tooltip: effectiveArmourAbsoluteTt });
- const effectiveExplosiveArmour = armour.total / armour.explosive.total;
- effectiveArmourData.push({ value: Math.round(effectiveExplosiveArmour), label: translate('explosive'), tooltip: effectiveArmourExplosiveTt });
- const effectiveKineticArmour = armour.total / armour.kinetic.total;
- effectiveArmourData.push({ value: Math.round(effectiveKineticArmour), label: translate('kinetic'), tooltip: effectiveArmourKineticTt });
- const effectiveThermalArmour = armour.total / armour.thermal.total;
- effectiveArmourData.push({ value: Math.round(effectiveThermalArmour), label: translate('thermal'), tooltip: effectiveArmourThermalTt });
- const effectiveCausticArmour = armour.total / armour.caustic.total;
- effectiveArmourData.push({ value: Math.round(effectiveCausticArmour), label: translate('caustic'), tooltip: effectiveArmourCausticTt });
-
- const armourDamageTakenData = [];
- armourDamageTakenData.push({ value: Math.round(armour.absolute.total * 100), label: translate('absolute'), tooltip: armourDamageTakenTt });
- armourDamageTakenData.push({ value: Math.round(armour.explosive.total * 100), label: translate('explosive'), tooltip: armourDamageTakenExplosiveTt });
- armourDamageTakenData.push({ value: Math.round(armour.kinetic.total * 100), label: translate('kinetic'), tooltip: armourDamageTakenKineticTt });
- armourDamageTakenData.push({ value: Math.round(armour.thermal.total * 100), label: translate('thermal'), tooltip: armourDamageTakenThermalTt });
- armourDamageTakenData.push({ value: Math.round(armour.caustic.total * 100), label: translate('caustic'), tooltip: armourDamageTakenCausticTt });
+ // Effective HP
+ const effectiveArmourData = [
+ 'absolute', 'explosive', 'kinetic', 'thermal'
+ ].map((label) => {
+ const dmgMult = armour[label];
+ const raw = armour.armour;
+ const tooltip = ['byBoosters', 'byGenerator', 'bySys'].map(
+ (label) =>
+ {translate(label)} {formats.int(raw * dmgMult[label])}
+
+ );
+ return { label, value: Math.round(dmgMult.damageMultiplier * raw), tooltip };
+ });
return (
- {shield.total ?
+ {shields.withSCBs ?
{translate('shield metrics')}
- {shieldSourcesTt}
)} onMouseOut={tooltip.bind(null, null)} className='summary'>{translate('raw shield strength')}
{formats.int(shield.total)}{units.MJ}
- {translate('PHRASE_TIME_TO_LOSE_SHIELDS')}
{shielddamage.totalsdps == 0 ? translate('ever') : formats.time(Calc.timeToDeplete(shield.total, shielddamage.totalsdps, shielddamage.totalseps, pd.getWeaponsCapacity(), pd.getWeaponsRechargeRate() * opponentWep / 4))}
- {translate('PHRASE_TIME_TO_RECOVER_SHIELDS')}
{shield.recover === Math.Inf ? translate('never') : formats.time(shield.recover)}
- {translate('PHRASE_TIME_TO_RECHARGE_SHIELDS')}
{shield.recharge === Math.Inf ? translate('never') : formats.time(shield.recharge)}
+ {shieldSourcesTt})} onMouseOut={tooltip.bind(null, null)} className='summary'>{translate('raw shield strength')}
{formats.int(shields.withSCBs)}{units.MJ}
+ {translate('PHRASE_TIME_TO_LOSE_SHIELDS')}
TODO
+ {translate('PHRASE_TIME_TO_RECOVER_SHIELDS')}
{shields.recover ? formats.time(shields.recover) : translate('never')}
+ {translate('PHRASE_TIME_TO_RECHARGE_SHIELDS')}
{shields.recharge ? formats.time(shields.recharge) : translate('never')}
{translate('shield sources')}
@@ -250,11 +152,11 @@ export default class Defence extends TranslatedComponent {
{translate('armour metrics')}
- {armourSourcesTt}
)} 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('ever') : formats.time(Calc.timeToDeplete(armour.total, armourdamage.totalsdps, armourdamage.totalseps, pd.getWeaponsCapacity(), pd.getWeaponsRechargeRate() * opponentWep / 4))}
-
{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)}
+
{armourSourcesTt}
)} onMouseOut={tooltip.bind(null, null)} className='summary'>{translate('raw armour strength')}
{formats.int(armour.armour)}
+ {translate('PHRASE_TIME_TO_LOSE_ARMOUR')}
TODO
+ {translate('raw module armour')}
{formats.int(moduleProtection.moduleArmour)}
+ {translate('PHRASE_MODULE_PROTECTION_EXTERNAL')}
{formats.pct1((1 - moduleProtection.moduleProtection) / 2)}
+ {translate('PHRASE_MODULE_PROTECTION_INTERNAL')}
{formats.pct1(1 - moduleProtection.moduleProtection)}