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)}