From 365810a610b810c6b67ad46b822edb402d85d223 Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Wed, 15 Mar 2017 11:40:12 +0000 Subject: [PATCH] Working on shield recovery calculation --- src/app/components/BattleCentre.jsx | 4 +- src/app/components/Defence.jsx | 90 ++++++++++++++++++++++------- src/app/i18n/en.js | 7 +++ 3 files changed, 78 insertions(+), 23 deletions(-) diff --git a/src/app/components/BattleCentre.jsx b/src/app/components/BattleCentre.jsx index d0f19138..251a61af 100644 --- a/src/app/components/BattleCentre.jsx +++ b/src/app/components/BattleCentre.jsx @@ -113,10 +113,10 @@ export default class BattleCentre extends TranslatedComponent { const { sys, eng, wep, cargo, fuel, boost, engagementRange, opponent } = this.state; const { ship } = this.props; - // Markers are used to propagate state changes without requiring a deep comparison of the ship + // 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; + const shieldMarker = '' + ship.shield + ':' + ship.shieldCells + ':' + ship.shieldExplRes + ':' + ship.shieldKinRes + ':' + ship.shieldThermRes + ':' + ship.armour + ship.standard[4].m.getSystemsCapacity() + ':' + ship.standard[4].m.getSystemsRechargeRate(); return ( diff --git a/src/app/components/Defence.jsx b/src/app/components/Defence.jsx index 5a0566b1..0d13ad6c 100644 --- a/src/app/components/Defence.jsx +++ b/src/app/components/Defence.jsx @@ -29,8 +29,8 @@ export default class Defence extends TranslatedComponent { constructor(props) { super(props); - const { shield, armour, shielddamage } = this._calcMetrics(props.ship, props.opponent, props.sys); - this.state = { shield, armour, shielddamage }; + const { shield, armour, shielddamage, armourdamage } = this._calcMetrics(props.ship, props.opponent, props.sys); + this.state = { shield, armour, shielddamage, armourdamage }; } /** @@ -40,8 +40,8 @@ export default class Defence extends TranslatedComponent { */ componentWillReceiveProps(nextProps) { if (this.props.marker != nextProps.marker || this.props.sys != nextProps.sys) { - const { shield, armour, shielddamage } = this._calcMetrics(nextProps.ship, nextProps.opponent, nextProps.sys); - this.setState({ shield, armour, shielddamage }); + const { shield, armour, shielddamage, armourdamage } = this._calcMetrics(nextProps.ship, nextProps.opponent, nextProps.sys); + this.setState({ shield, armour, shielddamage, armourdamage }); return true; } } @@ -97,11 +97,49 @@ export default class Defence extends TranslatedComponent { 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 15-second wait before shields start to recover + // Note that the shields use the broken regeneration rate to define how much energy goes in to the shields, and the normal + // regeneration rate to define how much energy is taken from the SYS capacitor + const shieldToRecover = (generatorStrength + boostersStrength) / 2; + const powerDistributor = ship.standard[4].m; + // Our initial regeneration comes from the SYS capacitor store, which is replenished as it goes + const capacitorDrain = shieldGenerator.getRegenerationRate() - powerDistributor.getSystemsRechargeRate() * (sys / 4); +console.log(`shieldToRecover is ${shieldToRecover}`); +console.log(`Regeneration rate is ${shieldGenerator.getRegenerationRate()}`); +console.log(`Power distributor recharge is ${powerDistributor.getSystemsRechargeRate() * sys / 4}`); +console.log(`capacitor drain is ${capacitorDrain}`); + const capacitorLifetime = powerDistributor.getSystemsCapacity() / capacitorDrain; + +console.log(`Need to recover ${shieldToRecover}`); +console.log(`SYS contains ${powerDistributor.getSystemsCapacity()} and recharges at ${powerDistributor.getSystemsRechargeRate() * (sys / 4)}`); + let recover = 15; + if (capacitorDrain <= 0 || shieldToRecover < capacitorLifetime * shieldGenerator.getBrokenRegenerationRate()) { + // We can recover the entire shield from the capacitor store + recover += shieldToRecover / shieldGenerator.getBrokenRegenerationRate(); +console.log(`We can recover the entire shield before the capacitor drains - takes ${recover}`); + } else { + // We can recover some of the shield from the capacitor store + recover += capacitorLifetime; +console.log(`We can recover ${capacitorLifetime * shieldGenerator.getBrokenRegenerationRate()} before capacitor is empty`); +console.log(`Sys is ${sys}`); + const remainingShieldToRecover = shieldToRecover - capacitorLifetime * shieldGenerator.getBrokenRegenerationRate(); + if (sys === 0) { + // No system pips so will never recover shields +console.log(`Cannot recover shields`); + recover = Math.Inf; + } else { + // Recover remaining shields at the rate of the power distributor's recharge + recover += remainingShieldToRecover / (powerDistributor.getSystemsRechargeRate() * sys / 4); + } + } + shield = { generator: generatorStrength, boosters: boostersStrength, cells: ship.shieldCells, - total: generatorStrength + boostersStrength + ship.shieldCells + total: generatorStrength + boostersStrength + ship.shieldCells, + recover }; // Shield resistances have three components: the shield generator, the shield boosters and the SYS pips. @@ -149,8 +187,8 @@ export default class Defence extends TranslatedComponent { const armourBulkheads = ship.baseArmour + (ship.baseArmour * ship.bulkheads.m.getHullBoost()); let armourReinforcement = 0; - let modulearmour = 0; - let moduleprotection = 1; + let moduleArmour = 0; + let moduleProtection = 1; let hullExplDmg = 1; let hullKinDmg = 1; @@ -168,11 +206,11 @@ export default class Defence extends TranslatedComponent { hullThermDmg = hullThermDmg * (1 - slot.m.getThermalResistance()); } if (slot.m && slot.m.grp == 'mrp') { - modulearmour += slot.m.getIntegrity(); - moduleprotection = moduleprotection * (1 - slot.m.getProtection()); + moduleArmour += slot.m.getIntegrity(); + moduleProtection = moduleProtection * (1 - slot.m.getProtection()); } } - moduleprotection = 1 - moduleprotection; + moduleProtection = 1 - moduleProtection; // Apply diminishing returns hullExplDmg = hullExplDmg > 0.7 ? hullExplDmg : 0.7 - (0.7 - hullExplDmg) / 2; @@ -182,12 +220,11 @@ export default class Defence extends TranslatedComponent { const armour = { bulkheads: armourBulkheads, reinforcement: armourReinforcement, - modulearmour: modulearmour, - moduleprotection: moduleprotection, + modulearmour: moduleArmour, + moduleprotection: moduleProtection, total: armourBulkheads + armourReinforcement }; - // Armour resistances have two components: bulkheads and HRPs // We re-cast these as damage percentages armour.absolute = { @@ -214,7 +251,15 @@ export default class Defence extends TranslatedComponent { total: (1 - ship.bulkheads.m.getThermalResistance()) * hullThermDmg }; - return { shield, armour, shielddamage }; + 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 + }; + armourdamage.totalsdps = armourdamage.absolutesdps + armourdamage.explosivesdps + armourdamage.kineticsdps + armourdamage.thermalsdps; + + return { shield, armour, shielddamage, armourdamage }; } /** @@ -234,7 +279,7 @@ export default class Defence extends TranslatedComponent { const { ship, sys } = this.props; const { language, tooltip, termtip } = this.context; const { formats, translate, units } = language; - const { shield, armour, shielddamage } = this.state; + const { shield, armour, shielddamage, armourdamage } = this.state; const shieldSourcesData = []; const effectiveShieldData = []; @@ -284,7 +329,6 @@ export default class Defence extends TranslatedComponent { shieldDamageTakenData.push({ value: Math.round(shield.kinetic.total * 100), label: translate('kinetic') }); shieldDamageTakenData.push({ value: Math.round(shield.thermal.total * 100), label: translate('thermal') }); -console.log(`max effective shields are ${shield.absolute.max}/${shield.explosive.max}/${shield.kinetic.max}/${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); } @@ -329,17 +373,16 @@ console.log(`max effective shields are ${shield.absolute.max}/${shield.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') }); - // TODO versions of ship.calcShieldRecovery() and ship.calcShieldRecharge() that take account of SYS pips return ( {shield.total ?

{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_RECOVER_SHIELDS')} {formats.time(ship.calcShieldRecovery())}

-

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

+

{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_RECOVER_SHIELDS')}
{formats.time(shield.recover)}

+

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

{translate('shield sources')}

@@ -357,6 +400,11 @@ console.log(`max effective shields are ${shield.absolute.max}/${shield.explosive

{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('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/i18n/en.js b/src/app/i18n/en.js index 74e2a3b2..45c4ab41 100644 --- a/src/app/i18n/en.js +++ b/src/app/i18n/en.js @@ -45,8 +45,15 @@ export const terms = { PHRASE_SHIELD_SOURCES: 'Breakdown of the supply of shield energy', PHRASE_EFFECTIVE_SHIELD: 'Effective shield strength against different damage types', PHRASE_DAMAGE_TAKEN: '% of raw damage taken for different damage types', + PHRASE_TIME_TO_LOSE_ARMOUR: 'Armour will hold for', + PHRASE_MODULE_PROTECTION_EXTERNAL: 'Protection for hardpoints', + PHRASE_MODULE_PROTECTION_INTERNAL: 'Protection for all other modules', TT_TIME_TO_LOSE_SHIELDS: 'Against sustained fire from all opponent\'s weapons', + TT_TIME_TO_LOSE_ARMOUR: 'Against sustained fire from all opponent\'s weapons', + TT_MODULE_ARMOUR: 'Armour protecting against module damage', + TT_MODULE_PROTECTION_EXTERNAL: 'Percentage of damage diverted from hardpoints to module reinforcement packages', + TT_MODULE_PROTECTION_INTERNAL: 'Percentage of damage diverted from non-hardpoint modules to module reinforcement packages', HELP_MODIFICATIONS_MENU: 'Click on a number to enter a new value, or drag along the bar for small changes',