mirror of
https://github.com/EDCD/coriolis.git
synced 2025-12-09 06:43:24 +00:00
Break out metric calculations
This commit is contained in:
@@ -1,8 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import cn from 'classnames';
|
|
||||||
import TranslatedComponent from './TranslatedComponent';
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
import * as Calc from '../shipyard/Calculations';
|
import * as Calc from '../shipyard/Calculations';
|
||||||
import { DamageAbsolute, DamageExplosive, DamageKinetic, DamageThermal } from './SvgIcons';
|
|
||||||
import PieChart from './PieChart';
|
import PieChart from './PieChart';
|
||||||
import VerticalBarChart from './VerticalBarChart';
|
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
|
* Render defence
|
||||||
* @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
|
|
||||||
* @return {React.Component} contents
|
* @return {React.Component} contents
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import cn from 'classnames';
|
|
||||||
import TranslatedComponent from './TranslatedComponent';
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
import * as Calc from '../shipyard/Calculations';
|
import * as Calc from '../shipyard/Calculations';
|
||||||
import { DamageAbsolute, DamageExplosive, DamageKinetic, DamageThermal } from './SvgIcons';
|
|
||||||
import PieChart from './PieChart';
|
import PieChart from './PieChart';
|
||||||
import VerticalBarChart from './VerticalBarChart';
|
import VerticalBarChart from './VerticalBarChart';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Offence information
|
* 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 {
|
export default class Offence extends TranslatedComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
@@ -25,7 +28,7 @@ export default class Offence extends TranslatedComponent {
|
|||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(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 };
|
this.state = { shield, armour, shielddamage, armourdamage };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,419 +39,116 @@ export default class Offence extends TranslatedComponent {
|
|||||||
*/
|
*/
|
||||||
componentWillReceiveProps(nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
if (this.props.marker != nextProps.marker || this.props.sys != nextProps.sys) {
|
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 });
|
this.setState({ shield, armour, shielddamage, armourdamage });
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate the sustained DPS for a ship at a given range, excluding resistances
|
* Render offence
|
||||||
* @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
|
|
||||||
* @return {React.Component} contents
|
* @return {React.Component} contents
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
const { ship, sys } = this.props;
|
const { ship, wep } = this.props;
|
||||||
const { language, tooltip, termtip } = this.context;
|
const { language, tooltip, termtip } = this.context;
|
||||||
const { formats, translate, units } = language;
|
const { formats, translate, units } = language;
|
||||||
const { shield, armour, shielddamage, armourdamage } = this.state;
|
// const { shield, armour, shielddamage, armourdamage } = this.state;
|
||||||
|
|
||||||
const shieldSourcesData = [];
|
// const shieldSourcesData = [];
|
||||||
const effectiveShieldData = [];
|
// const effectiveShieldData = [];
|
||||||
const shieldDamageTakenData = [];
|
// const shieldDamageTakenData = [];
|
||||||
const shieldTooltipDetails = [];
|
// const shieldTooltipDetails = [];
|
||||||
const shieldAbsoluteTooltipDetails = [];
|
// const shieldAbsoluteTooltipDetails = [];
|
||||||
const shieldExplosiveTooltipDetails = [];
|
// const shieldExplosiveTooltipDetails = [];
|
||||||
const shieldKineticTooltipDetails = [];
|
// const shieldKineticTooltipDetails = [];
|
||||||
const shieldThermalTooltipDetails = [];
|
// const shieldThermalTooltipDetails = [];
|
||||||
let maxEffectiveShield = 0;
|
// let maxEffectiveShield = 0;
|
||||||
if (shield.total) {
|
// if (shield.total) {
|
||||||
if (Math.round(shield.generator) > 0) shieldSourcesData.push({ value: Math.round(shield.generator), label: translate('generator') });
|
// 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') });
|
// 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') });
|
// shieldSourcesData.push({ value: Math.round(shield.cells), label: translate('cells') });
|
||||||
|
|
||||||
if (Math.round(shield.generator) > 0) shieldTooltipDetails.push(<div key='generator'>{translate('generator') + ' ' + formats.int(shield.generator)}{units.MJ}</div>);
|
// shieldTooltipDetails.push(<div key='generator'>{translate('generator') + ' ' + formats.int(shield.generator)}{units.MJ}</div>);
|
||||||
if (Math.round(shield.boosters) > 0) shieldTooltipDetails.push(<div key='boosters'>{translate('boosters') + ' ' + formats.int(shield.boosters)}{units.MJ}</div>);
|
// shieldTooltipDetails.push(<div key='boosters'>{translate('boosters') + ' ' + formats.int(shield.boosters)}{units.MJ}</div>);
|
||||||
if (Math.round(shield.cells) > 0) shieldTooltipDetails.push(<div key='cells'>{translate('cells') + ' ' + formats.int(shield.cells)}{units.MJ}</div>);
|
// shieldTooltipDetails.push(<div key='cells'>{translate('cells') + ' ' + formats.int(shield.cells)}{units.MJ}</div>);
|
||||||
|
|
||||||
shieldAbsoluteTooltipDetails.push(<div key='generator'>{translate('generator') + ' ' + formats.pct1(shield.absolute.generator)}</div>);
|
// shieldAbsoluteTooltipDetails.push(<div key='generator'>{translate('generator') + ' ' + formats.pct1(shield.absolute.generator)}</div>);
|
||||||
shieldAbsoluteTooltipDetails.push(<div key='boosters'>{translate('boosters') + ' ' + formats.pct1(shield.absolute.boosters)}</div>);
|
// shieldAbsoluteTooltipDetails.push(<div key='boosters'>{translate('boosters') + ' ' + formats.pct1(shield.absolute.boosters)}</div>);
|
||||||
shieldAbsoluteTooltipDetails.push(<div key='power distributor'>{translate('power distributor') + ' ' + formats.pct1(shield.absolute.sys)}</div>);
|
// shieldAbsoluteTooltipDetails.push(<div key='power distributor'>{translate('power distributor') + ' ' + formats.pct1(shield.absolute.sys)}</div>);
|
||||||
|
|
||||||
shieldExplosiveTooltipDetails.push(<div key='generator'>{translate('generator') + ' ' + formats.pct1(shield.explosive.generator)}</div>);
|
// shieldExplosiveTooltipDetails.push(<div key='generator'>{translate('generator') + ' ' + formats.pct1(shield.explosive.generator)}</div>);
|
||||||
shieldExplosiveTooltipDetails.push(<div key='boosters'>{translate('boosters') + ' ' + formats.pct1(shield.explosive.boosters)}</div>);
|
// shieldExplosiveTooltipDetails.push(<div key='boosters'>{translate('boosters') + ' ' + formats.pct1(shield.explosive.boosters)}</div>);
|
||||||
shieldExplosiveTooltipDetails.push(<div key='power distributor'>{translate('power distributor') + ' ' + formats.pct1(shield.explosive.sys)}</div>);
|
// shieldExplosiveTooltipDetails.push(<div key='power distributor'>{translate('power distributor') + ' ' + formats.pct1(shield.explosive.sys)}</div>);
|
||||||
|
|
||||||
shieldKineticTooltipDetails.push(<div key='generator'>{translate('generator') + ' ' + formats.pct1(shield.kinetic.generator)}</div>);
|
// shieldKineticTooltipDetails.push(<div key='generator'>{translate('generator') + ' ' + formats.pct1(shield.kinetic.generator)}</div>);
|
||||||
shieldKineticTooltipDetails.push(<div key='boosters'>{translate('boosters') + ' ' + formats.pct1(shield.kinetic.boosters)}</div>);
|
// shieldKineticTooltipDetails.push(<div key='boosters'>{translate('boosters') + ' ' + formats.pct1(shield.kinetic.boosters)}</div>);
|
||||||
shieldKineticTooltipDetails.push(<div key='power distributor'>{translate('power distributor') + ' ' + formats.pct1(shield.kinetic.sys)}</div>);
|
// shieldKineticTooltipDetails.push(<div key='power distributor'>{translate('power distributor') + ' ' + formats.pct1(shield.kinetic.sys)}</div>);
|
||||||
|
|
||||||
shieldThermalTooltipDetails.push(<div key='generator'>{translate('generator') + ' ' + formats.pct1(shield.thermal.generator)}</div>);
|
// shieldThermalTooltipDetails.push(<div key='generator'>{translate('generator') + ' ' + formats.pct1(shield.thermal.generator)}</div>);
|
||||||
shieldThermalTooltipDetails.push(<div key='boosters'>{translate('boosters') + ' ' + formats.pct1(shield.thermal.boosters)}</div>);
|
// shieldThermalTooltipDetails.push(<div key='boosters'>{translate('boosters') + ' ' + formats.pct1(shield.thermal.boosters)}</div>);
|
||||||
shieldThermalTooltipDetails.push(<div key='power distributor'>{translate('power distributor') + ' ' + formats.pct1(shield.thermal.sys)}</div>);
|
// shieldThermalTooltipDetails.push(<div key='power distributor'>{translate('power distributor') + ' ' + formats.pct1(shield.thermal.sys)}</div>);
|
||||||
|
|
||||||
const effectiveAbsoluteShield = shield.total / shield.absolute.total;
|
// const effectiveAbsoluteShield = shield.total / shield.absolute.total;
|
||||||
effectiveShieldData.push({ value: Math.round(effectiveAbsoluteShield), label: translate('absolute') });
|
// effectiveShieldData.push({ value: Math.round(effectiveAbsoluteShield), label: translate('absolute') });
|
||||||
const effectiveExplosiveShield = shield.total / shield.explosive.total;
|
// const effectiveExplosiveShield = shield.total / shield.explosive.total;
|
||||||
effectiveShieldData.push({ value: Math.round(effectiveExplosiveShield), label: translate('explosive') });
|
// effectiveShieldData.push({ value: Math.round(effectiveExplosiveShield), label: translate('explosive') });
|
||||||
const effectiveKineticShield = shield.total / shield.kinetic.total;
|
// const effectiveKineticShield = shield.total / shield.kinetic.total;
|
||||||
effectiveShieldData.push({ value: Math.round(effectiveKineticShield), label: translate('kinetic') });
|
// effectiveShieldData.push({ value: Math.round(effectiveKineticShield), label: translate('kinetic') });
|
||||||
const effectiveThermalShield = shield.total / shield.thermal.total;
|
// const effectiveThermalShield = shield.total / shield.thermal.total;
|
||||||
effectiveShieldData.push({ value: Math.round(effectiveThermalShield), label: translate('thermal') });
|
// 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.absolute.total * 100), label: translate('absolute') });
|
||||||
shieldDamageTakenData.push({ value: Math.round(shield.explosive.total * 100), label: translate('explosive') });
|
// 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.kinetic.total * 100), label: translate('kinetic') });
|
||||||
shieldDamageTakenData.push({ value: Math.round(shield.thermal.total * 100), label: translate('thermal') });
|
// 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 = [];
|
// const armourSourcesData = [];
|
||||||
if (Math.round(armour.bulkheads) > 0) armourSourcesData.push({ value: Math.round(armour.bulkheads), label: translate('bulkheads') });
|
// 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') });
|
// armourSourcesData.push({ value: Math.round(armour.reinforcement), label: translate('reinforcement') });
|
||||||
|
|
||||||
const armourTooltipDetails = [];
|
// const armourTooltipDetails = [];
|
||||||
if (armour.bulkheads > 0) armourTooltipDetails.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.int(armour.bulkheads)}</div>);
|
// armourTooltipDetails.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.int(armour.bulkheads)}</div>);
|
||||||
if (armour.reinforcement > 0) armourTooltipDetails.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.int(armour.reinforcement)}</div>);
|
// armourTooltipDetails.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.int(armour.reinforcement)}</div>);
|
||||||
|
|
||||||
const armourAbsoluteTooltipDetails = [];
|
// const armourAbsoluteTooltipDetails = [];
|
||||||
armourAbsoluteTooltipDetails.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.pct1(armour.absolute.bulkheads)}</div>);
|
// armourAbsoluteTooltipDetails.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.pct1(armour.absolute.bulkheads)}</div>);
|
||||||
armourAbsoluteTooltipDetails.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.pct1(armour.absolute.reinforcement)}</div>);
|
// armourAbsoluteTooltipDetails.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.pct1(armour.absolute.reinforcement)}</div>);
|
||||||
|
|
||||||
const armourExplosiveTooltipDetails = [];
|
// const armourExplosiveTooltipDetails = [];
|
||||||
armourExplosiveTooltipDetails.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.pct1(armour.explosive.bulkheads)}</div>);
|
// armourExplosiveTooltipDetails.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.pct1(armour.explosive.bulkheads)}</div>);
|
||||||
armourExplosiveTooltipDetails.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.pct1(armour.explosive.reinforcement)}</div>);
|
// armourExplosiveTooltipDetails.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.pct1(armour.explosive.reinforcement)}</div>);
|
||||||
|
|
||||||
const armourKineticTooltipDetails = [];
|
// const armourKineticTooltipDetails = [];
|
||||||
armourKineticTooltipDetails.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.pct1(armour.kinetic.bulkheads)}</div>);
|
// armourKineticTooltipDetails.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.pct1(armour.kinetic.bulkheads)}</div>);
|
||||||
armourKineticTooltipDetails.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.pct1(armour.kinetic.reinforcement)}</div>);
|
// armourKineticTooltipDetails.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.pct1(armour.kinetic.reinforcement)}</div>);
|
||||||
|
|
||||||
const armourThermalTooltipDetails = [];
|
// const armourThermalTooltipDetails = [];
|
||||||
armourThermalTooltipDetails.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.pct1(armour.thermal.bulkheads)}</div>);
|
// armourThermalTooltipDetails.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.pct1(armour.thermal.bulkheads)}</div>);
|
||||||
armourThermalTooltipDetails.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.pct1(armour.thermal.reinforcement)}</div>);
|
// armourThermalTooltipDetails.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.pct1(armour.thermal.reinforcement)}</div>);
|
||||||
|
|
||||||
const effectiveArmourData = [];
|
// const effectiveArmourData = [];
|
||||||
const effectiveAbsoluteArmour = armour.total / armour.absolute.total;
|
// const effectiveAbsoluteArmour = armour.total / armour.absolute.total;
|
||||||
effectiveArmourData.push({ value: Math.round(effectiveAbsoluteArmour), label: translate('absolute') });
|
// effectiveArmourData.push({ value: Math.round(effectiveAbsoluteArmour), label: translate('absolute') });
|
||||||
const effectiveExplosiveArmour = armour.total / armour.explosive.total;
|
// const effectiveExplosiveArmour = armour.total / armour.explosive.total;
|
||||||
effectiveArmourData.push({ value: Math.round(effectiveExplosiveArmour), label: translate('explosive') });
|
// effectiveArmourData.push({ value: Math.round(effectiveExplosiveArmour), label: translate('explosive') });
|
||||||
const effectiveKineticArmour = armour.total / armour.kinetic.total;
|
// const effectiveKineticArmour = armour.total / armour.kinetic.total;
|
||||||
effectiveArmourData.push({ value: Math.round(effectiveKineticArmour), label: translate('kinetic') });
|
// effectiveArmourData.push({ value: Math.round(effectiveKineticArmour), label: translate('kinetic') });
|
||||||
const effectiveThermalArmour = armour.total / armour.thermal.total;
|
// const effectiveThermalArmour = armour.total / armour.thermal.total;
|
||||||
effectiveArmourData.push({ value: Math.round(effectiveThermalArmour), label: translate('thermal') });
|
// effectiveArmourData.push({ value: Math.round(effectiveThermalArmour), label: translate('thermal') });
|
||||||
|
|
||||||
const armourDamageTakenData = [];
|
// const armourDamageTakenData = [];
|
||||||
armourDamageTakenData.push({ value: Math.round(armour.absolute.total * 100), label: translate('absolute') });
|
// 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.explosive.total * 100), label: translate('explosive') });
|
||||||
armourDamageTakenData.push({ value: Math.round(armour.kinetic.total * 100), label: translate('kinetic') });
|
// armourDamageTakenData.push({ value: Math.round(armour.kinetic.total * 100), label: translate('kinetic') });
|
||||||
armourDamageTakenData.push({ value: Math.round(armour.thermal.total * 100), label: translate('thermal') });
|
// armourDamageTakenData.push({ value: Math.round(armour.thermal.total * 100), label: translate('thermal') });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span id='defence'>
|
<span id='offence'>
|
||||||
|
{/*
|
||||||
{shield.total ? <span>
|
{shield.total ? <span>
|
||||||
<div className='group quarter'>
|
<div className='group quarter'>
|
||||||
<h2>{translate('shield metrics')}</h2>
|
<h2>{translate('shield metrics')}</h2>
|
||||||
@@ -475,7 +175,7 @@ export default class Offence extends TranslatedComponent {
|
|||||||
<div className='group quarter'>
|
<div className='group quarter'>
|
||||||
<h2>{translate('armour metrics')}</h2>
|
<h2>{translate('armour metrics')}</h2>
|
||||||
<h2 onMouseOver={termtip.bind(null, <div>{armourTooltipDetails}</div>)} onMouseOut={tooltip.bind(null, null)} className='summary'>{translate('raw armour strength')}<br/>{formats.int(armour.total)}</h2>
|
<h2 onMouseOver={termtip.bind(null, <div>{armourTooltipDetails}</div>)} onMouseOut={tooltip.bind(null, null)} className='summary'>{translate('raw armour strength')}<br/>{formats.int(armour.total)}</h2>
|
||||||
<h2 onMouseOver={termtip.bind(null, translate('TT_TIME_TO_LOSE_ARMOUR'))} onMouseOut={tooltip.bind(null, null)}>{translate('PHRASE_TIME_TO_LOSE_ARMOUR')}<br/>{armourdamage.totalsdps == 0 ? translate('infinity') : formats.time(armour.total / armourdamage.totalsdps)}</h2>
|
<h2 onMouseOver={termtip.bind(null, translate('TT_TIME_TO_LOSE_ARMOUR'))} onMouseOut={tooltip.bind(null, null)}>{translate('PHRASE_TIME_TO_LOSE_ARMOUR')}<br/>{armourdamage.totalsdps == 0 ? translate('ever') : formats.time(armour.total / armourdamage.totalsdps)}</h2>
|
||||||
<h2 onMouseOver={termtip.bind(null, translate('TT_MODULE_ARMOUR'))} onMouseOut={tooltip.bind(null, null)}>{translate('raw module armour')}<br/>{formats.int(armour.modulearmour)}</h2>
|
<h2 onMouseOver={termtip.bind(null, translate('TT_MODULE_ARMOUR'))} onMouseOut={tooltip.bind(null, null)}>{translate('raw module armour')}<br/>{formats.int(armour.modulearmour)}</h2>
|
||||||
<h2 onMouseOver={termtip.bind(null, translate('TT_MODULE_PROTECTION_EXTERNAL'))} onMouseOut={tooltip.bind(null, null)}>{translate('PHRASE_MODULE_PROTECTION_EXTERNAL')}<br/>{formats.pct1(armour.moduleprotection / 2)}</h2>
|
<h2 onMouseOver={termtip.bind(null, translate('TT_MODULE_PROTECTION_EXTERNAL'))} onMouseOut={tooltip.bind(null, null)}>{translate('PHRASE_MODULE_PROTECTION_EXTERNAL')}<br/>{formats.pct1(armour.moduleprotection / 2)}</h2>
|
||||||
<h2 onMouseOver={termtip.bind(null, translate('TT_MODULE_PROTECTION_INTERNAL'))} onMouseOut={tooltip.bind(null, null)}>{translate('PHRASE_MODULE_PROTECTION_INTERNAL')}<br/>{formats.pct1(armour.moduleprotection)}</h2>
|
<h2 onMouseOver={termtip.bind(null, translate('TT_MODULE_PROTECTION_INTERNAL'))} onMouseOut={tooltip.bind(null, null)}>{translate('PHRASE_MODULE_PROTECTION_INTERNAL')}<br/>{formats.pct1(armour.moduleprotection)}</h2>
|
||||||
@@ -493,6 +193,7 @@ export default class Offence extends TranslatedComponent {
|
|||||||
<h2 onMouseOver={termtip.bind(null, translate('PHRASE_EFFECTIVE_ARMOUR'))} onMouseOut={tooltip.bind(null, null)}>{translate('effective armour')}</h2>
|
<h2 onMouseOver={termtip.bind(null, translate('PHRASE_EFFECTIVE_ARMOUR'))} onMouseOut={tooltip.bind(null, null)}>{translate('effective armour')}</h2>
|
||||||
<VerticalBarChart data={effectiveArmourData} />
|
<VerticalBarChart data={effectiveArmourData} />
|
||||||
</div>
|
</div>
|
||||||
|
*/}
|
||||||
</span>);
|
</span>);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import CostSection from './CostSection';
|
|||||||
import EngineProfile from './EngineProfile';
|
import EngineProfile from './EngineProfile';
|
||||||
import FSDProfile from './FSDProfile';
|
import FSDProfile from './FSDProfile';
|
||||||
import Movement from './Movement';
|
import Movement from './Movement';
|
||||||
|
import Offence from './Offence';
|
||||||
import Defence from './Defence';
|
import Defence from './Defence';
|
||||||
import WeaponDamageChart from './WeaponDamageChart';
|
import WeaponDamageChart from './WeaponDamageChart';
|
||||||
|
|
||||||
@@ -117,8 +118,10 @@ export default class OutfittingSubpages extends TranslatedComponent {
|
|||||||
_offenceTab() {
|
_offenceTab() {
|
||||||
const { ship, sys, eng, wep, cargo, fuel, boost, engagementRange, opponent, opponentBuild } = this.props;
|
const { ship, sys, eng, wep, cargo, fuel, boost, engagementRange, opponent, opponentBuild } = this.props;
|
||||||
|
|
||||||
|
const marker = `${ship.toString()}:${opponent.name}:${opponentBuild}:${engagementRange}`;
|
||||||
|
|
||||||
return <div>
|
return <div>
|
||||||
<h1>Offence goes here</h1>
|
<Offence marker={marker} ship={ship} opponent={opponent} wep={wep} engagementrange={engagementRange}/>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -100,13 +100,12 @@ export default class WeaponDamageChart extends TranslatedComponent {
|
|||||||
*/
|
*/
|
||||||
_calcMaxSDps(ship, opponent, hull) {
|
_calcMaxSDps(ship, opponent, hull) {
|
||||||
// Additional information to allow effectiveness calculations
|
// 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;
|
let maxSDps = 0;
|
||||||
for (let i = 0; i < ship.hardpoints.length; i++) {
|
for (let i = 0; i < ship.hardpoints.length; i++) {
|
||||||
if (ship.hardpoints[i].maxClass > 0 && ship.hardpoints[i].m && ship.hardpoints[i].enabled) {
|
if (ship.hardpoints[i].maxClass > 0 && ship.hardpoints[i].m && ship.hardpoints[i].enabled) {
|
||||||
const m = ship.hardpoints[i].m;
|
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) {
|
if (thisSDps > maxSDps) {
|
||||||
maxSDps = thisSDps;
|
maxSDps = thisSDps;
|
||||||
}
|
}
|
||||||
@@ -156,14 +155,14 @@ export default class WeaponDamageChart extends TranslatedComponent {
|
|||||||
*/
|
*/
|
||||||
_calcSDps(ship, weaponNames, opponent, hull, engagementRange) {
|
_calcSDps(ship, weaponNames, opponent, hull, engagementRange) {
|
||||||
// Additional information to allow effectiveness calculations
|
// 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 results = {};
|
||||||
let weaponNum = 0;
|
let weaponNum = 0;
|
||||||
for (let i = 0; i < ship.hardpoints.length; i++) {
|
for (let i = 0; i < ship.hardpoints.length; i++) {
|
||||||
if (ship.hardpoints[i].maxClass > 0 && ship.hardpoints[i].m && ship.hardpoints[i].enabled) {
|
if (ship.hardpoints[i].maxClass > 0 && ship.hardpoints[i].m && ship.hardpoints[i].enabled) {
|
||||||
const m = ship.hardpoints[i].m;
|
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;
|
return results;
|
||||||
@@ -174,13 +173,11 @@ export default class WeaponDamageChart extends TranslatedComponent {
|
|||||||
* @param {Object} ship The ship that will deal the damage
|
* @param {Object} ship The ship that will deal the damage
|
||||||
* @param {Object} m The weapon 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 {Object} opponent The ship against which damage will be dealt
|
||||||
* @param {boolean} hull True if hitting hull
|
* @param {Object} defence defence metrics (either shield or hull)
|
||||||
* @param {Object} shield Shield defence metrics
|
|
||||||
* @param {Object} armour Armour defence metrics
|
|
||||||
* @param {Object} engagementRange The engagement range
|
* @param {Object} engagementRange The engagement range
|
||||||
* @return {object} Returns the sustained DPS for the weapon
|
* @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;
|
let falloff = 1;
|
||||||
if (m.getFalloff()) {
|
if (m.getFalloff()) {
|
||||||
// Calculate the falloff % due to range
|
// Calculate the falloff % due to range
|
||||||
@@ -199,16 +196,16 @@ export default class WeaponDamageChart extends TranslatedComponent {
|
|||||||
|
|
||||||
let effectiveness = 0;
|
let effectiveness = 0;
|
||||||
if (m.getDamageDist().E) {
|
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) {
|
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) {
|
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) {
|
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
|
// Return the final effective SDPS
|
||||||
|
|||||||
@@ -310,23 +310,18 @@ export function calcYaw(mass, baseYaw, thrusters, engpip, eng, boostFactor, boos
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate defence metrics
|
* Calculate shield metrics
|
||||||
* @param {Object} ship The ship
|
* @param {Object} ship The ship
|
||||||
* @param {Object} opponent The opponent ship
|
* @param {int} sys The pips to SYS
|
||||||
* @param {int} sys The pips to SYS
|
* @returns {Object} Shield metrics
|
||||||
* @param {int} engagementrange The range between the ship and opponent
|
*/
|
||||||
* @returns {Object} Defence metrics
|
export function shieldMetrics(ship, sys) {
|
||||||
*/
|
|
||||||
export function defenceMetrics(ship, opponent, sys, engagementrange) {
|
|
||||||
const sysResistance = this.sysResistance(sys);
|
const sysResistance = this.sysResistance(sys);
|
||||||
const maxSysResistance = this.sysResistance(4);
|
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 = {};
|
let shield = {};
|
||||||
|
|
||||||
const shieldGeneratorSlot = ship.findInternalByGroup('sg');
|
const shieldGeneratorSlot = ship.findInternalByGroup('sg');
|
||||||
if (shieldGeneratorSlot && shieldGeneratorSlot.enabled && shieldGeneratorSlot.m) {
|
if (shieldGeneratorSlot && shieldGeneratorSlot.enabled && shieldGeneratorSlot.m) {
|
||||||
const shieldGenerator = shieldGeneratorSlot.m;
|
const shieldGenerator = shieldGeneratorSlot.m;
|
||||||
@@ -451,14 +446,17 @@ export function defenceMetrics(ship, opponent, sys, engagementrange) {
|
|||||||
total: (1 - shieldGenerator.getThermalResistance()) * boosterThermDmg * (1 - sysResistance),
|
total: (1 - shieldGenerator.getThermalResistance()) * boosterThermDmg * (1 - sysResistance),
|
||||||
max: (1 - shieldGenerator.getThermalResistance()) * boosterThermDmg * (1 - maxSysResistance)
|
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
|
// Armour from bulkheads
|
||||||
const armourBulkheads = ship.baseArmour + (ship.baseArmour * ship.bulkheads.m.getHullBoost());
|
const armourBulkheads = ship.baseArmour + (ship.baseArmour * ship.bulkheads.m.getHullBoost());
|
||||||
let armourReinforcement = 0;
|
let armourReinforcement = 0;
|
||||||
@@ -527,6 +525,35 @@ export function defenceMetrics(ship, opponent, sys, engagementrange) {
|
|||||||
total: (1 - ship.bulkheads.m.getThermalResistance()) * hullThermDmg
|
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 = {
|
const armourdamage = {
|
||||||
absolutesdps: armoursdps.absolute *= armour.absolute.total,
|
absolutesdps: armoursdps.absolute *= armour.absolute.total,
|
||||||
explosivesdps: armoursdps.explosive *= armour.explosive.total,
|
explosivesdps: armoursdps.explosive *= armour.explosive.total,
|
||||||
@@ -538,6 +565,37 @@ export function defenceMetrics(ship, opponent, sys, engagementrange) {
|
|||||||
return { shield, armour, shielddamage, armourdamage };
|
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
|
* Calculate the resistance provided by SYS pips
|
||||||
* @param {integer} sys the value of the SYS pips
|
* @param {integer} sys the value of the SYS pips
|
||||||
|
|||||||
Reference in New Issue
Block a user