From 750d23b10a311d015d89a245be2ee907952e0edb Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Thu, 16 Mar 2017 07:35:52 +0000 Subject: [PATCH] Provide live sustained DPS for opponent --- src/app/components/BattleCentre.jsx | 8 +-- src/app/components/Defence.jsx | 105 +++++++++++++++++++++------- src/app/components/ShipPicker.jsx | 5 +- 3 files changed, 87 insertions(+), 31 deletions(-) diff --git a/src/app/components/BattleCentre.jsx b/src/app/components/BattleCentre.jsx index 251a61af..2ffdfd59 100644 --- a/src/app/components/BattleCentre.jsx +++ b/src/app/components/BattleCentre.jsx @@ -44,7 +44,7 @@ export default class BattleCentre extends TranslatedComponent { cargo: ship.cargoCapacity, boost: false, engagementRange: 1500, - opponent: new Ship('anaconda', Ships['anaconda'].properties, Ships['anaconda'].slots) + opponent: new Ship('anaconda', Ships['anaconda'].properties, Ships['anaconda'].slots).buildWith(Ships['anaconda'].defaults) }; } @@ -110,13 +110,13 @@ export default class BattleCentre extends TranslatedComponent { render() { const { language, onWindowResize, sizeRatio, tooltip, termtip } = this.context; const { formats, translate, units } = language; - const { sys, eng, wep, cargo, fuel, boost, engagementRange, opponent } = this.state; + const { sys, eng, wep, cargo, fuel, boost, engagementRange, opponent, opponentBuild } = this.state; const { ship } = this.props; // Markers are used to propagate state changes without requiring a deep comparison of the ship, as that takes a long time const pipsMarker = '' + ship.canBoost(); const movementMarker = '' + ship.topSpeed + ':' + ship.pitch + ':' + ship.roll + ':' + ship.yaw + ':' + ship.canBoost(); - const shieldMarker = '' + ship.shield + ':' + ship.shieldCells + ':' + ship.shieldExplRes + ':' + ship.shieldKinRes + ':' + ship.shieldThermRes + ':' + ship.armour + ship.standard[4].m.getSystemsCapacity() + ':' + ship.standard[4].m.getSystemsRechargeRate(); + const shieldMarker = '' + ship.shield + ':' + ship.shieldCells + ':' + ship.shieldExplRes + ':' + ship.shieldKinRes + ':' + ship.shieldThermRes + ':' + ship.armour + ship.standard[4].m.getSystemsCapacity() + ':' + ship.standard[4].m.getSystemsRechargeRate() + ':' + opponent.name + ':' + opponentBuild + ':' + engagementRange; return ( @@ -136,7 +136,7 @@ export default class BattleCentre extends TranslatedComponent {

{translate('defence')}

- +
); diff --git a/src/app/components/Defence.jsx b/src/app/components/Defence.jsx index 0d13ad6c..19a2e0c9 100644 --- a/src/app/components/Defence.jsx +++ b/src/app/components/Defence.jsx @@ -19,6 +19,7 @@ export default class Defence extends TranslatedComponent { marker: React.PropTypes.string.isRequired, ship: React.PropTypes.object.isRequired, opponent: React.PropTypes.object.isRequired, + engagementrange: React.PropTypes.number.isRequired, sys: React.PropTypes.number.isRequired }; @@ -29,7 +30,7 @@ export default class Defence extends TranslatedComponent { constructor(props) { super(props); - const { shield, armour, shielddamage, armourdamage } = this._calcMetrics(props.ship, props.opponent, props.sys); + const { shield, armour, shielddamage, armourdamage } = this._calcMetrics(props.ship, props.opponent, props.sys, props.engagementrange); this.state = { shield, armour, shielddamage, armourdamage }; } @@ -40,31 +41,83 @@ export default class Defence 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); + const { shield, armour, shielddamage, armourdamage } = this._calcMetrics(nextProps.ship, nextProps.opponent, nextProps.sys, nextProps.engagementrange); this.setState({ shield, armour, shielddamage, armourdamage }); return true; } } /** - * Calculate shield metrics - * @param {Object} ship The ship - * @param {Object} opponent The opponent ship - * @param {int} sys The opponent ship - * @returns {Object} Shield metrics + * 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 */ - _calcMetrics(ship, opponent, sys) { + _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 }; + } + + /** + * 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 for later damage calculations - // const opponentSDps = Calc.sustainedDps(opponent, range); - const opponentSDps = { - absolute: 62.1, - explosive: 0, - kinetic: 7.4, - thermal: 7.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 = {}; @@ -176,10 +229,10 @@ console.log(`Cannot recover shields`); max: (1 - shieldGenerator.getThermalResistance()) * boosterThermDmg * (1 - maxSysResistance) }; - shielddamage.absolutesdps = opponentSDps.absolute *= shield.absolute.total; - shielddamage.explosivesdps = opponentSDps.explosive *= shield.explosive.total; - shielddamage.kineticsdps = opponentSDps.kinetic *= shield.kinetic.total; - shielddamage.thermalsdps = opponentSDps.thermal *= shield.thermal.total; + 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; } @@ -252,10 +305,10 @@ console.log(`Cannot recover shields`); }; const armourdamage = { - absolutesdps: opponentSDps.absolute *= armour.absolute.total, - explosivesdps: opponentSDps.explosive *= armour.explosive.total, - kineticsdps: opponentSDps.kinetic *= armour.kinetic.total, - thermalsdps: opponentSDps.thermal *= armour.thermal.total + 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; @@ -380,7 +433,7 @@ console.log(`Cannot recover shields`);

{translate('shield metrics')}


{shieldTooltipDetails})} onMouseOut={tooltip.bind(null, null)} className='summary'>{translate('raw shield strength')}
{formats.int(shield.total)}{units.MJ}

-

{translate('PHRASE_TIME_TO_LOSE_SHIELDS')}
{formats.time(shield.total / shielddamage.totalsdps)}

+

{translate('PHRASE_TIME_TO_LOSE_SHIELDS')}
{shielddamage.totalsdps == 0 ? translate('infinity') : formats.time(shield.total / shielddamage.totalsdps)}

{translate('PHRASE_TIME_TO_RECOVER_SHIELDS')}
{formats.time(shield.recover)}

{translate('PHRASE_TIME_TO_RECHARGE_SHIELDS')}
{formats.time(ship.calcShieldRecharge())}

@@ -401,7 +454,7 @@ console.log(`Cannot recover shields`);

{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')}
{formats.time(armour.total / armourdamage.totalsdps)}

+

{translate('PHRASE_TIME_TO_LOSE_ARMOUR')}
{armourdamage.totalsdps == 0 ? translate('infinity') : 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)}

diff --git a/src/app/components/ShipPicker.jsx b/src/app/components/ShipPicker.jsx index 9f319a14..5b0529a5 100644 --- a/src/app/components/ShipPicker.jsx +++ b/src/app/components/ShipPicker.jsx @@ -18,7 +18,7 @@ export default class ShipPicker extends TranslatedComponent { }; static defaultProps = { - ship: new Ship('anaconda', Ships['anaconda'].properties, Ships['anaconda'].slots) + ship: new Ship('anaconda', Ships['anaconda'].properties, Ships['anaconda'].slots).buildWith(Ships['anaconda'].defaults) } /** @@ -64,6 +64,9 @@ export default class ShipPicker extends TranslatedComponent { if (build) { // Ship is a particular build ship.buildFrom(Persist.getBuild(shipId, build)); + } else { + // Ship is a stock build + ship.buildWith(Ships[shipId].defaults); } this._closeMenu(); this.setState({ ship, build });