mirror of
https://github.com/EDCD/coriolis.git
synced 2025-12-09 14:45:35 +00:00
195 lines
8.7 KiB
JavaScript
195 lines
8.7 KiB
JavaScript
import React from 'react';
|
|
import cn from 'classnames';
|
|
import TranslatedComponent from './TranslatedComponent';
|
|
import * as Calc from '../shipyard/Calculations';
|
|
import { DamageAbsolute, DamageExplosive, DamageKinetic, DamageThermal } from './SvgIcons';
|
|
|
|
/**
|
|
* Shields
|
|
* Effective shield strength (including SCBs)
|
|
* Time for opponent to down shields
|
|
* - need sustained DPS for each type of damage (K/T/E/R)
|
|
* - turn in to % of shields removed per second
|
|
*/
|
|
export default class Shields extends TranslatedComponent {
|
|
static propTypes = {
|
|
marker: React.PropTypes.string.isRequired,
|
|
ship: React.PropTypes.object.isRequired,
|
|
opponent: React.PropTypes.object.isRequired,
|
|
sys: React.PropTypes.number.isRequired
|
|
};
|
|
|
|
/**
|
|
* Constructor
|
|
* @param {Object} props React Component properties
|
|
*/
|
|
constructor(props) {
|
|
super(props);
|
|
|
|
const { shield, absolute, explosive, kinetic, thermal } = this._calcMetrics(props.ship, props.opponent, props.sys);
|
|
this.state = { shield, absolute, explosive, kinetic, thermal };
|
|
}
|
|
|
|
/**
|
|
* 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, absolute, explosive, kinetic, thermal } = this._calcMetrics(nextProps.ship, nextProps.opponent, nextProps.sys);
|
|
this.setState({ shield, absolute, explosive, kinetic, thermal });
|
|
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
|
|
*/
|
|
_calcMetrics(ship, opponent, sys) {
|
|
const sysResistance = this._calcSysResistance(sys);
|
|
|
|
const shieldGenerator = ship.findShieldGenerator();
|
|
|
|
// 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;
|
|
boosterExplDmg = boosterExplDmg > 0.7 ? boosterExplDmg : 0.7 - (0.7 - boosterExplDmg) / 2;
|
|
boosterKinDmg = boosterKinDmg > 0.7 ? boosterExplDmg : 0.7 - (0.7 - boosterKinDmg) / 2;
|
|
boosterThermDmg = boosterThermDmg > 0.7 ? boosterExplDmg : 0.7 - (0.7 - boosterThermDmg) / 2;
|
|
|
|
const generatorStrength = Calc.shieldStrength(ship.hullMass, ship.baseShieldStrength, shieldGenerator, 1);
|
|
const boostersStrength = generatorStrength * boost;
|
|
const shield = {
|
|
generator: generatorStrength,
|
|
boosters: boostersStrength,
|
|
total: generatorStrength + boostersStrength
|
|
};
|
|
|
|
// Resistances have three components: the shield generator, the shield boosters and the SYS pips.
|
|
// We re-cast these as damage percentages
|
|
const absolute = {
|
|
generator: 1,
|
|
boosters: 1,
|
|
sys: 1 - sysResistance,
|
|
total: 1 - sysResistance
|
|
};
|
|
|
|
const explosive = {
|
|
generator: 1 - shieldGenerator.getExplosiveResistance(),
|
|
boosters: boosterExplDmg,
|
|
sys: (1 - sysResistance),
|
|
total: (1 - shieldGenerator.getExplosiveResistance()) * boosterExplDmg * (1 - sysResistance)
|
|
};
|
|
|
|
const kinetic = {
|
|
generator: 1 - shieldGenerator.getKineticResistance(),
|
|
boosters: boosterKinDmg,
|
|
sys: (1 - sysResistance),
|
|
total: (1 - shieldGenerator.getKineticResistance()) * boosterKinDmg * (1 - sysResistance)
|
|
};
|
|
|
|
const thermal = {
|
|
generator: 1 - shieldGenerator.getThermalResistance(),
|
|
boosters: boosterThermDmg,
|
|
sys: (1 - sysResistance),
|
|
total: (1 - shieldGenerator.getThermalResistance()) * boosterThermDmg * (1 - sysResistance)
|
|
};
|
|
|
|
return { shield, absolute, explosive, kinetic, thermal };
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
render() {
|
|
const { ship, sys } = this.props;
|
|
const { language, tooltip, termtip } = this.context;
|
|
const { formats, translate, units } = language;
|
|
const { shield, absolute, explosive, kinetic, thermal } = this.state;
|
|
|
|
const shieldTooltipDetails = [];
|
|
shieldTooltipDetails.push(<div key='generator'>{translate('generator') + ' ' + formats.int(shield.generator)}{units.MJ}</div>);
|
|
shieldTooltipDetails.push(<div key='boosters'>{translate('boosters') + ' ' + formats.int(shield.boosters)}{units.MJ}</div>);
|
|
|
|
const absoluteTooltipDetails = [];
|
|
absoluteTooltipDetails.push(<div key='generator'>{translate('generator') + ' ' + formats.pct1(absolute.generator)}</div>);
|
|
absoluteTooltipDetails.push(<div key='boosters'>{translate('boosters') + ' ' + formats.pct1(absolute.boosters)}</div>);
|
|
absoluteTooltipDetails.push(<div key='power distributor'>{translate('power distributor') + ' ' + formats.pct1(absolute.sys)}</div>);
|
|
|
|
const explosiveTooltipDetails = [];
|
|
explosiveTooltipDetails.push(<div key='generator'>{translate('generator') + ' ' + formats.pct1(explosive.generator)}</div>);
|
|
explosiveTooltipDetails.push(<div key='boosters'>{translate('boosters') + ' ' + formats.pct1(explosive.boosters)}</div>);
|
|
explosiveTooltipDetails.push(<div key='power distributor'>{translate('power distributor') + ' ' + formats.pct1(explosive.sys)}</div>);
|
|
|
|
const kineticTooltipDetails = [];
|
|
kineticTooltipDetails.push(<div key='generator'>{translate('generator') + ' ' + formats.pct1(kinetic.generator)}</div>);
|
|
kineticTooltipDetails.push(<div key='boosters'>{translate('boosters') + ' ' + formats.pct1(kinetic.boosters)}</div>);
|
|
kineticTooltipDetails.push(<div key='power distributor'>{translate('power distributor') + ' ' + formats.pct1(kinetic.sys)}</div>);
|
|
|
|
const thermalTooltipDetails = [];
|
|
thermalTooltipDetails.push(<div key='generator'>{translate('generator') + ' ' + formats.pct1(thermal.generator)}</div>);
|
|
thermalTooltipDetails.push(<div key='boosters'>{translate('boosters') + ' ' + formats.pct1(thermal.boosters)}</div>);
|
|
thermalTooltipDetails.push(<div key='power distributor'>{translate('power distributor') + ' ' + formats.pct1(thermal.sys)}</div>);
|
|
|
|
return (
|
|
<span id='shields'>
|
|
<table>
|
|
<tbody>
|
|
<tr>
|
|
<td colSpan='5' onMouseOver={termtip.bind(null, <div>{shieldTooltipDetails}</div>)} onMouseOut={tooltip.bind(null, null)} className='summary'>{translate('shields')}: {formats.int(shield.total)}{units.MJ}</td>
|
|
</tr>
|
|
<tr>
|
|
<td className='le'>{translate('damage from')}</td>
|
|
<td className='ri'>
|
|
<span onMouseOver={termtip.bind(null, 'absolute')} onMouseOut={tooltip.bind(null, null)}><DamageAbsolute /></span>
|
|
<span onMouseOver={termtip.bind(null, <div>{absoluteTooltipDetails}</div>)} onMouseOut={tooltip.bind(null, null)}>{formats.pct1(absolute.total)}</span>
|
|
</td>
|
|
<td className='ri'>
|
|
<span onMouseOver={termtip.bind(null, 'explosive')} onMouseOut={tooltip.bind(null, null)}><DamageExplosive /></span>
|
|
<span onMouseOver={termtip.bind(null, <div>{explosiveTooltipDetails}</div>)} onMouseOut={tooltip.bind(null, null)}>{formats.pct1(explosive.total)}</span>
|
|
</td>
|
|
<td className='ri'>
|
|
<span onMouseOver={termtip.bind(null, 'kinetic')} onMouseOut={tooltip.bind(null, null)}><DamageKinetic /></span>
|
|
<span onMouseOver={termtip.bind(null, <div>{kineticTooltipDetails}</div>)} onMouseOut={tooltip.bind(null, null)}>{formats.pct1(kinetic.total)}</span>
|
|
</td>
|
|
<td className='ri'>
|
|
<span onMouseOver={termtip.bind(null, 'thermal')} onMouseOut={tooltip.bind(null, null)}><DamageThermal /></span>
|
|
<span onMouseOver={termtip.bind(null, <div>{thermalTooltipDetails}</div>)} onMouseOut={tooltip.bind(null, null)}>{formats.pct1(thermal.total)}</span>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</span>);
|
|
}
|
|
}
|