mirror of
https://github.com/EDCD/coriolis.git
synced 2025-12-09 14:45:35 +00:00
200 lines
7.5 KiB
JavaScript
200 lines
7.5 KiB
JavaScript
import React from 'react';
|
|
import PropTypes from 'prop-types';
|
|
import TranslatedComponent from './TranslatedComponent';
|
|
import LineChart from '../components/LineChart';
|
|
import * as Calc from '../shipyard/Calculations';
|
|
|
|
const DAMAGE_DEALT_COLORS = ['#FFFFFF', '#FF0000', '#00FF00', '#7777FF', '#FFFF00', '#FF00FF', '#00FFFF', '#777777'];
|
|
|
|
/**
|
|
* Weapon damage chart
|
|
*/
|
|
export default class WeaponDamageChart extends TranslatedComponent {
|
|
static propTypes = {
|
|
ship: PropTypes.object.isRequired,
|
|
opponent: PropTypes.object.isRequired,
|
|
hull: PropTypes.bool.isRequired,
|
|
engagementRange: PropTypes.number.isRequired,
|
|
opponentSys: PropTypes.number.isRequired,
|
|
marker: PropTypes.string.isRequired
|
|
};
|
|
|
|
/**
|
|
* Constructor
|
|
* @param {Object} props React Component properties
|
|
* @param {Object} context React Component context
|
|
*/
|
|
constructor(props, context) {
|
|
super(props);
|
|
}
|
|
|
|
/**
|
|
* Set the initial weapons state
|
|
*/
|
|
componentWillMount() {
|
|
const weaponNames = this._weaponNames(this.props.ship, this.context);
|
|
const opponentShields = Calc.shieldMetrics(this.props.opponent, this.props.opponentSys);
|
|
const opponentArmour = Calc.armourMetrics(this.props.opponent);
|
|
const maxRange = this._calcMaxRange(this.props.ship);
|
|
const maxDps = this._calcMaxSDps(this.props.ship, this.props.opponent, opponentShields, opponentArmour);
|
|
|
|
this.setState({ maxRange, maxDps, weaponNames, opponentShields, opponentArmour, calcSDpsFunc: this._calcSDps.bind(this, this.props.ship, weaponNames, this.props.opponent, opponentShields, opponentArmour, this.props.hull) });
|
|
}
|
|
|
|
/**
|
|
* Set the updated weapons state if our ship changes
|
|
* @param {Object} nextProps Incoming/Next properties
|
|
* @param {Object} nextContext Incoming/Next conext
|
|
* @return {boolean} Returns true if the component should be rerendered
|
|
*/
|
|
componentWillReceiveProps(nextProps, nextContext) {
|
|
if (nextProps.marker != this.props.marker) {
|
|
const weaponNames = this._weaponNames(nextProps.ship, nextContext);
|
|
const opponentShields = Calc.shieldMetrics(nextProps.opponent, nextProps.opponentSys);
|
|
const opponentArmour = Calc.armourMetrics(nextProps.opponent);
|
|
const maxRange = this._calcMaxRange(nextProps.ship);
|
|
const maxDps = this._calcMaxSDps(nextProps.ship, nextProps.opponent, opponentShields, opponentArmour);
|
|
this.setState({ weaponNames,
|
|
opponentShields,
|
|
opponentArmour,
|
|
maxRange,
|
|
maxDps,
|
|
calcSDpsFunc: this._calcSDps.bind(this, nextProps.ship, weaponNames, nextProps.opponent, opponentShields, opponentArmour, nextProps.hull)
|
|
});
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Calculate the maximum range of a ship's weapons
|
|
* @param {Object} ship The ship
|
|
* @returns {int} The maximum range, in metres
|
|
*/
|
|
_calcMaxRange(ship) {
|
|
let maxRange = 1000; // Minimum
|
|
for (let i = 0; i < ship.hardpoints.length; i++) {
|
|
if (ship.hardpoints[i].maxClass > 0 && ship.hardpoints[i].m && ship.hardpoints[i].enabled) {
|
|
const thisRange = ship.hardpoints[i].m.getRange();
|
|
if (thisRange > maxRange) {
|
|
maxRange = thisRange;
|
|
}
|
|
}
|
|
}
|
|
|
|
return maxRange;
|
|
}
|
|
|
|
/**
|
|
* Calculate the maximum sustained single-weapon DPS for this ship
|
|
* @param {Object} ship The ship
|
|
* @param {Object} opponent The opponent ship
|
|
* @param {Object} opponentShields The opponent's shields
|
|
* @param {Object} opponentArmour The opponent's armour
|
|
* @return {number} The maximum sustained single-weapon DPS
|
|
*/
|
|
_calcMaxSDps(ship, opponent, opponentShields, opponentArmour) {
|
|
// Additional information to allow effectiveness calculations
|
|
let maxSDps = 0;
|
|
for (let i = 0; i < ship.hardpoints.length; i++) {
|
|
if (ship.hardpoints[i].maxClass > 0 && ship.hardpoints[i].m && ship.hardpoints[i].enabled) {
|
|
const m = ship.hardpoints[i].m;
|
|
|
|
const sustainedDps = Calc._weaponSustainedDps(m, opponent, opponentShields, opponentArmour, 0);
|
|
const thisSDps = sustainedDps.damage.armour.total > sustainedDps.damage.shields.total ? sustainedDps.damage.armour.total : sustainedDps.damage.shields.total;
|
|
if (thisSDps > maxSDps) {
|
|
maxSDps = thisSDps;
|
|
}
|
|
}
|
|
}
|
|
return maxSDps;
|
|
}
|
|
|
|
/**
|
|
* Obtain the weapon names for this ship
|
|
* @param {Object} ship The ship
|
|
* @param {Object} context The context
|
|
* @return {array} The weapon names
|
|
*/
|
|
_weaponNames(ship, context) {
|
|
const translate = context.language.translate;
|
|
let names = [];
|
|
let num = 1;
|
|
for (let i = 0; i < ship.hardpoints.length; i++) {
|
|
if (ship.hardpoints[i].maxClass > 0 && ship.hardpoints[i].m && ship.hardpoints[i].enabled) {
|
|
const m = ship.hardpoints[i].m;
|
|
let name = '' + num++ + ': ' + m.class + m.rating + (m.missile ? '/' + m.missile : '') + ' ' + translate(m.name || m.grp);
|
|
let engineering;
|
|
if (m.blueprint && m.blueprint.name) {
|
|
engineering = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
|
|
if (m.blueprint.special && m.blueprint.special.id) {
|
|
engineering += ', ' + translate(m.blueprint.special.name);
|
|
}
|
|
}
|
|
if (engineering) {
|
|
name = name + ' (' + engineering + ')';
|
|
}
|
|
names.push(name);
|
|
}
|
|
}
|
|
return names;
|
|
}
|
|
|
|
/**
|
|
* Calculate the per-weapon sustained DPS for this ship against another ship at a given range
|
|
* @param {Object} ship The ship
|
|
* @param {Object} weaponNames The names of the weapons for which to calculate DPS
|
|
* @param {Object} opponent The target
|
|
* @param {Object} opponentShields The opponent's shields
|
|
* @param {Object} opponentArmour The opponent's armour
|
|
* @param {bool} hull true if to calculate against hull, false if to calculate against shields
|
|
* @param {Object} engagementRange The engagement range
|
|
* @return {array} The array of weapon DPS
|
|
*/
|
|
_calcSDps(ship, weaponNames, opponent, opponentShields, opponentArmour, hull, engagementRange) {
|
|
let results = {};
|
|
let weaponNum = 0;
|
|
for (let i = 0; i < ship.hardpoints.length; i++) {
|
|
if (ship.hardpoints[i].maxClass > 0 && ship.hardpoints[i].m && ship.hardpoints[i].enabled) {
|
|
const m = ship.hardpoints[i].m;
|
|
const sustainedDps = Calc._weaponSustainedDps(m, opponent, opponentShields, opponentArmour, engagementRange);
|
|
results[weaponNames[weaponNum++]] = hull ? sustainedDps.damage.armour.total : sustainedDps.damage.shields.total;
|
|
}
|
|
}
|
|
return results;
|
|
}
|
|
|
|
/**
|
|
* Render damage dealt
|
|
* @return {React.Component} contents
|
|
*/
|
|
render() {
|
|
const { language, onWindowResize, sizeRatio, tooltip, termtip } = this.context;
|
|
const { formats, translate, units } = language;
|
|
const { maxRange } = this.state;
|
|
const { ship, opponent, engagementRange } = this.props;
|
|
|
|
const sortOrder = this._sortOrder;
|
|
const onCollapseExpand = this._onCollapseExpand;
|
|
|
|
const code = `${ship.toString()}:${opponent.toString()}`;
|
|
|
|
return (
|
|
<div>
|
|
<LineChart
|
|
xMax={maxRange}
|
|
yMax={this.state.maxDps}
|
|
xLabel={translate('range')}
|
|
xUnit={translate('m')}
|
|
yLabel={translate('sdps')}
|
|
series={this.state.weaponNames}
|
|
xMark={this.props.engagementRange}
|
|
colors={DAMAGE_DEALT_COLORS}
|
|
func={this.state.calcSDpsFunc}
|
|
points={200}
|
|
code={code}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|
|
}
|