mirror of
https://github.com/EDCD/coriolis.git
synced 2025-12-09 06:43:24 +00:00
Remove unused components
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import { Ships } from 'coriolis-data/dist';
|
||||
import ShipSelector from './ShipSelector';
|
||||
import { nameComparator } from '../utils/SlotFunctions';
|
||||
import { Pip } from './SvgIcons';
|
||||
import LineChart from '../components/LineChart';
|
||||
|
||||
@@ -1,589 +0,0 @@
|
||||
import React from 'react';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import { Ships } from 'coriolis-data/dist';
|
||||
import ShipSelector from './ShipSelector';
|
||||
import { nameComparator } from '../utils/SlotFunctions';
|
||||
import { CollapseSection, ExpandSection, MountFixed, MountGimballed, MountTurret } from './SvgIcons';
|
||||
import LineChart from '../components/LineChart';
|
||||
import Slider from '../components/Slider';
|
||||
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
||||
import Module from '../shipyard/Module';
|
||||
|
||||
const DAMAGE_DEALT_COLORS = ['#FFFFFF', '#FF0000', '#00FF00', '#7777FF', '#FFFF00', '#FF00FF', '#00FFFF', '#777777'];
|
||||
|
||||
/**
|
||||
* Generates an internationalization friendly weapon comparator that will
|
||||
* sort by specified property (if provided) then by name/group, class, rating
|
||||
* @param {function} translate Translation function
|
||||
* @param {function} propComparator Optional property comparator
|
||||
* @param {boolean} desc Use descending order
|
||||
* @return {function} Comparator function for names
|
||||
*/
|
||||
export function weaponComparator(translate, propComparator, desc) {
|
||||
return (a, b) => {
|
||||
if (!desc) { // Flip A and B if ascending order
|
||||
let t = a;
|
||||
a = b;
|
||||
b = t;
|
||||
}
|
||||
|
||||
// If a property comparator is provided use it first
|
||||
let diff = propComparator ? propComparator(a, b) : nameComparator(translate, a, b);
|
||||
|
||||
if (diff) {
|
||||
return diff;
|
||||
}
|
||||
|
||||
// Property matches so sort by name / group, then class, rating
|
||||
if (a.name === b.name && a.grp === b.grp) {
|
||||
if(a.class == b.class) {
|
||||
return a.rating > b.rating ? 1 : -1;
|
||||
}
|
||||
return a.class - b.class;
|
||||
}
|
||||
|
||||
return nameComparator(translate, a, b);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Damage against a selected ship
|
||||
*/
|
||||
export default class DamageDealt extends TranslatedComponent {
|
||||
static propTypes = {
|
||||
ship: React.PropTypes.object.isRequired,
|
||||
code: React.PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
static DEFAULT_AGAINST = Ships['anaconda'];
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param {Object} props React Component properties
|
||||
* @param {Object} context React Component context
|
||||
*/
|
||||
constructor(props, context) {
|
||||
super(props);
|
||||
|
||||
this._sort = this._sort.bind(this);
|
||||
this._onShipChange = this._onShipChange.bind(this);
|
||||
this._onCollapseExpand = this._onCollapseExpand.bind(this);
|
||||
|
||||
const ship = this.props.ship;
|
||||
const against = DamageDealt.DEFAULT_AGAINST;
|
||||
const maxRange = this._calcMaxRange(ship);
|
||||
const range = 1000 / maxRange;
|
||||
const maxDps = this._calcMaxSDps(ship, against);
|
||||
const weaponNames = this._weaponNames(ship, context);
|
||||
|
||||
this.state = {
|
||||
predicate: 'n',
|
||||
desc: true,
|
||||
against,
|
||||
expanded: false,
|
||||
range,
|
||||
maxRange,
|
||||
maxDps,
|
||||
weaponNames,
|
||||
calcHullDpsFunc: this._calcDps.bind(this, context, ship, weaponNames, against, true),
|
||||
calcShieldsDpsFunc: this._calcDps.bind(this, context, ship, weaponNames, against, false)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the initial weapons state
|
||||
*/
|
||||
componentWillMount() {
|
||||
const data = this._calcWeaponsDps(this.props.ship, this.state.against, this.state.range * this.state.maxRange, true);
|
||||
this.setState({ weapons: data.weapons, totals: data.totals });
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.code != this.props.code) {
|
||||
const data = this._calcWeaponsDps(nextProps.ship, this.state.against, this.state.range * this.state.maxRange, this.props.hull);
|
||||
const weaponNames = this._weaponNames(nextProps.ship, nextContext);
|
||||
const maxRange = this._calcMaxRange(nextProps.ship);
|
||||
const maxDps = this._calcMaxSDps(nextProps.ship, this.state.against);
|
||||
this.setState({ weapons: data.weapons,
|
||||
totals: data.totals,
|
||||
weaponNames,
|
||||
maxRange,
|
||||
maxDps,
|
||||
calcHullDpsFunc: this._calcDps.bind(this, nextContext, nextProps.ship, weaponNames, this.state.against, true),
|
||||
calcShieldsDpsFunc: this._calcDps.bind(this, nextContext, nextProps.ship, weaponNames, this.state.against, false) });
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the maximum sustained single-weapon DPS for this ship against another ship
|
||||
* @param {Object} ship The ship
|
||||
* @param {Object} against The target
|
||||
* @return {number} The maximum sustained single-weapon DPS
|
||||
*/
|
||||
_calcMaxSDps(ship, against) {
|
||||
let maxSDps = 0;
|
||||
for (let i = 0; i < ship.hardpoints.length; i++) {
|
||||
if (ship.hardpoints[i].m && ship.hardpoints[i].enabled) {
|
||||
const m = ship.hardpoints[i].m;
|
||||
const thisSDps = m.getClip() ? (m.getClip() * m.getDps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload()) : m.getDps();
|
||||
if (thisSDps > maxSDps) {
|
||||
maxSDps = thisSDps;
|
||||
}
|
||||
}
|
||||
}
|
||||
return maxSDps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the per-weapon DPS for this ship against another ship at a given range
|
||||
* @param {Object} context The context
|
||||
* @param {Object} ship The ship
|
||||
* @param {Object} weaponNames The names of the weapons for which to calculate DPS
|
||||
* @param {Object} against The target
|
||||
* @param {bool} hull true if to calculate against hull, false if to calculate against shields
|
||||
* @param {Object} range The engagement range
|
||||
* @return {array} The array of weapon DPS
|
||||
*/
|
||||
_calcDps(context, ship, weaponNames, against, hull, range) {
|
||||
let results = {};
|
||||
let weaponNum = 0;
|
||||
for (let i = 0; i < ship.hardpoints.length; i++) {
|
||||
if (ship.hardpoints[i].m && ship.hardpoints[i].enabled) {
|
||||
const m = ship.hardpoints[i].m;
|
||||
results[weaponNames[weaponNum++]] = this._calcWeaponDps(context, m, against, hull, range);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 a specific weapon DPS for this ship against another ship at a given range
|
||||
* @param {Object} context The context
|
||||
* @param {Object} m The weapon
|
||||
* @param {Object} against The target
|
||||
* @param {bool} hull true if to calculate against hull, false if to calculate against shields
|
||||
* @param {Object} range The engagement range
|
||||
* @return {number} The weapon DPS
|
||||
*/
|
||||
_calcWeaponDps(context, m, against, hull, range) {
|
||||
const translate = context.language.translate;
|
||||
let dropoff = 1;
|
||||
if (m.getFalloff()) {
|
||||
// Calculate the dropoff % due to range
|
||||
if (range > m.getRange()) {
|
||||
// Weapon is out of range
|
||||
dropoff = 0;
|
||||
} else {
|
||||
const falloff = m.getFalloff();
|
||||
if (range > falloff) {
|
||||
const dropoffRange = m.getRange() - falloff;
|
||||
// Assuming straight-line falloff
|
||||
dropoff = 1 - (range - falloff) / dropoffRange;
|
||||
}
|
||||
}
|
||||
}
|
||||
const classRating = `${m.class}${m.rating}${m.missile ? '/' + m.missile : ''}`;
|
||||
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);
|
||||
}
|
||||
}
|
||||
const effectivenessShields = dropoff;
|
||||
const effectiveDpsShields = m.getDps() * effectivenessShields;
|
||||
const effectiveSDpsShields = (m.getClip() ? (m.getClip() * m.getDps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload()) * effectivenessShields : effectiveDpsShields);
|
||||
const effectivenessHull = (m.getPiercing() >= against.properties.hardness ? 1 : m.getPiercing() / against.properties.hardness) * dropoff;
|
||||
const effectiveDpsHull = m.getDps() * effectivenessHull;
|
||||
const effectiveSDpsHull = (m.getClip() ? (m.getClip() * m.getDps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload()) * effectivenessHull : effectiveDpsHull);
|
||||
|
||||
return hull ? effectiveSDpsHull : effectiveSDpsShields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the damage dealt by a ship
|
||||
* @param {Object} ship The ship which will deal the damage
|
||||
* @param {Object} against The ship against which damage will be dealt
|
||||
* @param {Object} range The engagement range
|
||||
* @return {object} Returns the per-weapon damage
|
||||
*/
|
||||
_calcWeaponsDps(ship, against, range) {
|
||||
const translate = this.context.language.translate;
|
||||
|
||||
// Tidy up the range so that it's to 4 decimal places
|
||||
range = Math.round(10000 * range) / 10000;
|
||||
|
||||
// Track totals
|
||||
let totals = {};
|
||||
totals.effectivenessShields = 0;
|
||||
totals.effectiveDpsShields = 0;
|
||||
totals.effectiveSDpsShields = 0;
|
||||
totals.effectivenessHull = 0;
|
||||
totals.effectiveDpsHull = 0;
|
||||
totals.effectiveSDpsHull = 0;
|
||||
let totalDps = 0;
|
||||
|
||||
let weapons = [];
|
||||
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;
|
||||
if (m.getDamage() && m.grp !== 'po') {
|
||||
let dropoff = 1;
|
||||
if (m.getFalloff()) {
|
||||
// Calculate the dropoff % due to range
|
||||
if (range > m.getRange()) {
|
||||
// Weapon is out of range
|
||||
dropoff = 0;
|
||||
} else {
|
||||
const falloff = m.getFalloff();
|
||||
if (range > falloff) {
|
||||
const dropoffRange = m.getRange() - falloff;
|
||||
// Assuming straight-line falloff
|
||||
dropoff = 1 - (range - falloff) / dropoffRange;
|
||||
}
|
||||
}
|
||||
}
|
||||
const classRating = `${m.class}${m.rating}${m.missile ? '/' + m.missile : ''}`;
|
||||
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 >= 0) {
|
||||
engineering += ', ' + translate(m.blueprint.special.name);
|
||||
}
|
||||
}
|
||||
|
||||
// Alter effectiveness as per standard shields (all have the same resistances)
|
||||
const sg = ModuleUtils.findModule('sg', '3v');
|
||||
let effectivenessShields = 0;
|
||||
if (m.getDamageDist().E) {
|
||||
effectivenessShields += m.getDamageDist().E * (1 - sg.getExplosiveResistance());
|
||||
}
|
||||
if (m.getDamageDist().K) {
|
||||
effectivenessShields += m.getDamageDist().K * (1 - sg.getKineticResistance());
|
||||
}
|
||||
if (m.getDamageDist().T) {
|
||||
effectivenessShields += m.getDamageDist().T * (1 - sg.getThermalResistance());
|
||||
}
|
||||
if (m.getDamageDist().A) {
|
||||
effectivenessShields += m.getDamageDist().A;
|
||||
}
|
||||
effectivenessShields *= dropoff;
|
||||
const effectiveDpsShields = m.getDps() * effectivenessShields;
|
||||
const effectiveSDpsShields = (m.getClip() ? (m.getClip() * m.getDps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload()) * effectivenessShields : effectiveDpsShields);
|
||||
|
||||
// Alter effectiveness as per standard hull
|
||||
const bulkheads = new Module({ template: against.bulkheads });
|
||||
let effectivenessHull = 0;
|
||||
if (m.getDamageDist().E) {
|
||||
effectivenessHull += m.getDamageDist().E * (1 - bulkheads.getExplosiveResistance());
|
||||
}
|
||||
if (m.getDamageDist().K) {
|
||||
effectivenessHull += m.getDamageDist().K * (1 - bulkheads.getKineticResistance());
|
||||
}
|
||||
if (m.getDamageDist().T) {
|
||||
effectivenessHull += m.getDamageDist().T * (1 - bulkheads.getThermalResistance());
|
||||
}
|
||||
if (m.getDamageDist().A) {
|
||||
effectivenessHull += m.getDamageDist().A;
|
||||
}
|
||||
effectivenessHull *= Math.min(m.getPiercing() / against.properties.hardness, 1) * dropoff;
|
||||
const effectiveDpsHull = m.getDps() * effectivenessHull;
|
||||
const effectiveSDpsHull = (m.getClip() ? (m.getClip() * m.getDps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload()) * effectivenessHull : effectiveDpsHull);
|
||||
totals.effectiveDpsShields += effectiveDpsShields;
|
||||
totals.effectiveSDpsShields += effectiveSDpsShields;
|
||||
totals.effectiveDpsHull += effectiveDpsHull;
|
||||
totals.effectiveSDpsHull += effectiveSDpsHull;
|
||||
totalDps += m.getDps();
|
||||
|
||||
weapons.push({ id: i,
|
||||
mount: m.mount,
|
||||
name: m.name || m.grp,
|
||||
classRating,
|
||||
engineering,
|
||||
effectiveDpsShields,
|
||||
effectiveSDpsShields,
|
||||
effectivenessShields,
|
||||
effectiveDpsHull,
|
||||
effectiveSDpsHull,
|
||||
effectivenessHull });
|
||||
}
|
||||
}
|
||||
}
|
||||
totals.effectivenessShields = totalDps == 0 ? 0 : totals.effectiveDpsShields / totalDps;
|
||||
totals.effectivenessHull = totalDps == 0 ? 0 : totals.effectiveDpsHull / totalDps;
|
||||
|
||||
return { weapons, totals };
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggered when the collapse or expand icons are clicked
|
||||
*/
|
||||
_onCollapseExpand() {
|
||||
this.setState({ expanded: !this.state.expanded });
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggered when the ship we compare against changes
|
||||
* @param {string} s the new ship ID
|
||||
*/
|
||||
_onShipChange(s) {
|
||||
const against = Ships[s];
|
||||
const data = this._calcWeaponsDps(this.props.ship, against, this.state.range * this.state.maxRange);
|
||||
this.setState({ against,
|
||||
weapons: data.weapons,
|
||||
totals: data.totals,
|
||||
calcHullDpsFunc: this._calcDps.bind(this, this.context, this.props.ship, this.state.weaponNames, against, true),
|
||||
calcShieldsDpsFunc: this._calcDps.bind(this, this.context, this.props.ship, this.state.weaponNames, against, false) });
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the sort order and sort
|
||||
* @param {string} predicate Sort predicate
|
||||
*/
|
||||
_sortOrder(predicate) {
|
||||
let desc = this.state.desc;
|
||||
|
||||
if (predicate == this.state.predicate) {
|
||||
desc = !desc;
|
||||
} else {
|
||||
desc = true;
|
||||
}
|
||||
|
||||
this._sort(this.props.ship, predicate, desc);
|
||||
this.setState({ predicate, desc });
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the weapon list
|
||||
* @param {Ship} ship Ship instance
|
||||
* @param {string} predicate Sort predicate
|
||||
* @param {Boolean} desc Sort order descending
|
||||
*/
|
||||
_sort(ship, predicate, desc) {
|
||||
let comp = weaponComparator.bind(null, this.context.language.translate);
|
||||
|
||||
switch (predicate) {
|
||||
case 'n': comp = comp(null, desc); break;
|
||||
case 'edpss': comp = comp((a, b) => a.effectiveDpsShields - b.effectiveDpsShields, desc); break;
|
||||
case 'esdpss': comp = comp((a, b) => a.effectiveSDpsShields - b.effectiveSDpsShields, desc); break;
|
||||
case 'es': comp = comp((a, b) => a.effectivenessShields - b.effectivenessShields, desc); break;
|
||||
case 'edpsh': comp = comp((a, b) => a.effectiveDpsHull - b.effectiveDpsHull, desc); break;
|
||||
case 'esdpsh': comp = comp((a, b) => a.effectiveSDpsHull - b.effectiveSDpsHull, desc); break;
|
||||
case 'eh': comp = comp((a, b) => a.effectivenessHull - b.effectivenessHull, desc); break;
|
||||
}
|
||||
|
||||
this.state.weapons.sort(comp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render individual rows for hardpoints
|
||||
* @param {Function} translate Translate function
|
||||
* @param {Object} formats Localised formats map
|
||||
* @return {array} The individual rows
|
||||
*
|
||||
*/
|
||||
_renderRows(translate, formats) {
|
||||
const { termtip, tooltip } = this.context;
|
||||
|
||||
let rows = [];
|
||||
|
||||
if (this.state.weapons) {
|
||||
for (let i = 0; i < this.state.weapons.length; i++) {
|
||||
const weapon = this.state.weapons[i];
|
||||
|
||||
rows.push(<tr key={weapon.id}>
|
||||
<td className='ri'>
|
||||
{weapon.mount == 'F' ? <span onMouseOver={termtip.bind(null, 'fixed')} onMouseOut={tooltip.bind(null, null)}><MountFixed className='icon'/></span> : null}
|
||||
{weapon.mount == 'G' ? <span onMouseOver={termtip.bind(null, 'gimballed')} onMouseOut={tooltip.bind(null, null)}><MountGimballed /></span> : null}
|
||||
{weapon.mount == 'T' ? <span onMouseOver={termtip.bind(null, 'turreted')} onMouseOut={tooltip.bind(null, null)}><MountTurret /></span> : null}
|
||||
{weapon.classRating} {translate(weapon.name)}
|
||||
{weapon.engineering ? ' (' + weapon.engineering + ')' : null }
|
||||
</td>
|
||||
<td className='ri'>{formats.f1(weapon.effectiveDpsShields)}</td>
|
||||
<td className='ri'>{formats.f1(weapon.effectiveSDpsShields)}</td>
|
||||
<td className='ri'>{formats.pct(weapon.effectivenessShields)}</td>
|
||||
<td className='ri'>{formats.f1(weapon.effectiveDpsHull)}</td>
|
||||
<td className='ri'>{formats.f1(weapon.effectiveSDpsHull)}</td>
|
||||
<td className='ri'>{formats.pct(weapon.effectivenessHull)}</td>
|
||||
</tr>);
|
||||
}
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update current range
|
||||
* @param {number} range Range 0-1
|
||||
*/
|
||||
_rangeChange(range) {
|
||||
const data = this._calcWeaponsDps(this.props.ship, this.state.against, this.state.range * this.state.maxRange);
|
||||
this.setState({ range,
|
||||
weapons: data.weapons,
|
||||
totals: data.totals });
|
||||
}
|
||||
|
||||
/**
|
||||
* Render damage dealt
|
||||
* @return {React.Component} contents
|
||||
*/
|
||||
render() {
|
||||
const { language, onWindowResize, sizeRatio, tooltip, termtip } = this.context;
|
||||
const { formats, translate, units } = language;
|
||||
const { against, expanded, maxRange, range, totals } = this.state;
|
||||
const { ship } = this.props;
|
||||
|
||||
const sortOrder = this._sortOrder;
|
||||
const onCollapseExpand = this._onCollapseExpand;
|
||||
|
||||
const code = ship.getHardpointsString() + '.' + ship.getModificationsString() + '.' + ship.getPowerEnabledString() + '.' + against.properties.name;
|
||||
|
||||
return (
|
||||
<span>
|
||||
<h1>{translate('damage dealt to')} {expanded ? <span onClick={onCollapseExpand}><CollapseSection className='summary'/></span> : <span onClick={onCollapseExpand}><ExpandSection className='summary'/></span>}</h1>
|
||||
{expanded ? <span>
|
||||
<ShipSelector initial={against} currentMenu={this.props.currentMenu} onChange={this._onShipChange} />
|
||||
<table className='summary' style={{ width: '100%' }}>
|
||||
<thead>
|
||||
<tr className='main'>
|
||||
<th rowSpan='2' className='sortable' onClick={sortOrder.bind(this, 'n')}>{translate('weapon')}</th>
|
||||
<th colSpan='3'>{translate('standard shields')}</th>
|
||||
<th colSpan='3'>{translate('standard armour')}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th className='lft sortable' onClick={sortOrder.bind(this, 'edpss')}>{translate('effective dps')}</th>
|
||||
<th className='sortable' onClick={sortOrder.bind(this, 'esdpss')}>{translate('effective sdps')}</th>
|
||||
<th className='sortable' onClick={sortOrder.bind(this, 'es')}>{translate('effectiveness')}</th>
|
||||
<th className='lft sortable' onClick={sortOrder.bind(this, 'edpsh')}>{translate('effective dps')}</th>
|
||||
<th className='sortable' onClick={sortOrder.bind(this, 'esdpsh')}>{translate('effective sdps')}</th>
|
||||
<th className='sortable' onClick={sortOrder.bind(this, 'eh')}>{translate('effectiveness')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{this._renderRows(translate, formats)}
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr className='main'>
|
||||
<td className='ri'><i>{translate('total')}</i></td>
|
||||
<td className='ri'><i>{formats.f1(totals.effectiveDpsShields)}</i></td>
|
||||
<td className='ri'><i>{formats.f1(totals.effectiveSDpsShields)}</i></td>
|
||||
<td className='ri'><i>{formats.pct(totals.effectivenessShields)}</i></td>
|
||||
<td className='ri'><i>{formats.f1(totals.effectiveDpsHull)}</i></td>
|
||||
<td className='ri'><i>{formats.f1(totals.effectiveSDpsHull)}</i></td>
|
||||
<td className='ri'><i>{formats.pct(totals.effectivenessHull)}</i></td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
<table style={{ width: '80%', lineHeight: '1em', backgroundColor: 'transparent', margin: 'auto' }}>
|
||||
<tbody >
|
||||
<tr>
|
||||
<td style={{ verticalAlign: 'top', padding: 0, width: '2.5em' }} onMouseEnter={termtip.bind(null, 'PHRASE_ENGAGEMENT_RANGE')} onMouseLeave={tooltip.bind(null, null)}>{translate('engagement range')}</td>
|
||||
<td>
|
||||
<Slider
|
||||
axis={true}
|
||||
onChange={this._rangeChange.bind(this)}
|
||||
axisUnit={translate('m')}
|
||||
percent={range}
|
||||
max={maxRange}
|
||||
scale={sizeRatio}
|
||||
onResize={onWindowResize}
|
||||
/>
|
||||
</td>
|
||||
<td className='primary' style={{ width: '10em', verticalAlign: 'top', fontSize: '0.9em', textAlign: 'left' }}>
|
||||
{formats.f2(range * maxRange / 1000)}{units.km}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div className='group half'>
|
||||
<h1>{translate('sustained dps against standard shields')}</h1>
|
||||
<LineChart
|
||||
xMax={maxRange}
|
||||
yMax={this.state.maxDps}
|
||||
xLabel={translate('range')}
|
||||
xUnit={translate('m')}
|
||||
yLabel={translate('sdps')}
|
||||
series={this.state.weaponNames}
|
||||
colors={DAMAGE_DEALT_COLORS}
|
||||
func={this.state.calcShieldsDpsFunc}
|
||||
points={200}
|
||||
code={code}
|
||||
/>
|
||||
</div>
|
||||
<div className='group half'>
|
||||
<h1>{translate('sustained dps against standard armour')}</h1>
|
||||
<LineChart
|
||||
xMax={maxRange}
|
||||
yMax={this.state.maxDps}
|
||||
xLabel={translate('range')}
|
||||
xUnit={translate('m')}
|
||||
yLabel={translate('sdps')}
|
||||
series={this.state.weaponNames}
|
||||
colors={DAMAGE_DEALT_COLORS}
|
||||
func={this.state.calcHullDpsFunc}
|
||||
points={200}
|
||||
code={code}
|
||||
/>
|
||||
</div>
|
||||
</span> : null }
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,327 +0,0 @@
|
||||
import React from 'react';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import { Modules } from 'coriolis-data/dist';
|
||||
import { nameComparator } from '../utils/SlotFunctions';
|
||||
import { CollapseSection, ExpandSection, MountFixed, MountGimballed, MountTurret } from './SvgIcons';
|
||||
import Module from '../shipyard/Module';
|
||||
import Slider from '../components/Slider';
|
||||
|
||||
/**
|
||||
* Generates an internationalization friendly weapon comparator that will
|
||||
* sort by specified property (if provided) then by name/group, class, rating
|
||||
* @param {function} translate Translation function
|
||||
* @param {function} propComparator Optional property comparator
|
||||
* @param {boolean} desc Use descending order
|
||||
* @return {function} Comparator function for names
|
||||
*/
|
||||
export function weaponComparator(translate, propComparator, desc) {
|
||||
return (a, b) => {
|
||||
if (!desc) { // Flip A and B if ascending order
|
||||
let t = a;
|
||||
a = b;
|
||||
b = t;
|
||||
}
|
||||
|
||||
// If a property comparator is provided use it first
|
||||
let diff = propComparator ? propComparator(a, b) : nameComparator(translate, a, b);
|
||||
|
||||
if (diff) {
|
||||
return diff;
|
||||
}
|
||||
|
||||
// Property matches so sort by name / group, then class, rating
|
||||
if (a.name === b.name && a.grp === b.grp) {
|
||||
if(a.class == b.class) {
|
||||
return a.rating > b.rating ? 1 : -1;
|
||||
}
|
||||
return a.class - b.class;
|
||||
}
|
||||
|
||||
return nameComparator(translate, a, b);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Damage received by a selected ship
|
||||
*/
|
||||
export default class DamageReceived extends TranslatedComponent {
|
||||
static propTypes = {
|
||||
ship: React.PropTypes.object.isRequired,
|
||||
code: React.PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param {Object} props React Component properties
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this._sort = this._sort.bind(this);
|
||||
this._onCollapseExpand = this._onCollapseExpand.bind(this);
|
||||
|
||||
this.state = {
|
||||
predicate: 'n',
|
||||
desc: true,
|
||||
expanded: false,
|
||||
range: 0.1667,
|
||||
maxRange: 6000
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the initial weapons state
|
||||
*/
|
||||
componentWillMount() {
|
||||
this.setState({ weapons: this._calcWeapons(this.props.ship, this.state.range * this.state.maxRange) });
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the updated weapons state
|
||||
* @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.code != this.props.code) {
|
||||
this.setState({ weapons: this._calcWeapons(nextProps.ship, this.state.range * this.state.maxRange) });
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the damage received by a ship
|
||||
* @param {Object} ship The ship which will receive the damage
|
||||
* @param {Object} range The engagement range
|
||||
* @return {boolean} Returns the per-weapon damage
|
||||
*/
|
||||
_calcWeapons(ship, range) {
|
||||
// Tidy up the range so that it's to 4 decimal places
|
||||
range = Math.round(10000 * range) / 10000;
|
||||
|
||||
let weapons = [];
|
||||
for (let grp in Modules.hardpoints) {
|
||||
if (Modules.hardpoints[grp][0].damage && Modules.hardpoints[grp][0].damagedist) {
|
||||
for (let mId in Modules.hardpoints[grp]) {
|
||||
const m = new Module(Modules.hardpoints[grp][mId]);
|
||||
let dropoff = 1;
|
||||
if (m.getFalloff()) {
|
||||
// Calculate the dropoff % due to range
|
||||
if (range > m.getRange()) {
|
||||
// Weapon is out of range
|
||||
dropoff = 0;
|
||||
} else {
|
||||
const falloff = m.getFalloff();
|
||||
if (range > falloff) {
|
||||
const dropoffRange = m.getRange() - falloff;
|
||||
// Assuming straight-line falloff
|
||||
dropoff = 1 - (range - falloff) / dropoffRange;
|
||||
}
|
||||
}
|
||||
}
|
||||
const classRating = `${m.class}${m.rating}${m.missile ? '/' + m.missile : ''}`;
|
||||
|
||||
// Base DPS
|
||||
const baseDps = m.getDps() * dropoff;
|
||||
const baseSDps = m.getClip() ? ((m.getClip() * baseDps / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload())) * dropoff : baseDps;
|
||||
|
||||
// Effective DPS taking in to account shield resistance
|
||||
let effectivenessShields = 0;
|
||||
if (m.getDamageDist().E) {
|
||||
effectivenessShields += m.getDamageDist().E * (1 - ship.shieldExplRes);
|
||||
}
|
||||
if (m.getDamageDist().K) {
|
||||
effectivenessShields += m.getDamageDist().K * (1 - ship.shieldKinRes);
|
||||
}
|
||||
if (m.getDamageDist().T) {
|
||||
effectivenessShields += m.getDamageDist().T * (1 - ship.shieldThermRes);
|
||||
}
|
||||
if (m.getDamageDist().A) {
|
||||
effectivenessShields += m.getDamageDist().A;
|
||||
}
|
||||
effectivenessShields *= dropoff;
|
||||
const effectiveDpsShields = baseDps * effectivenessShields;
|
||||
const effectiveSDpsShields = baseSDps * effectivenessShields;
|
||||
|
||||
// Effective DPS taking in to account hull hardness and resistance
|
||||
let effectivenessHull = 0;
|
||||
if (m.getDamageDist().E) {
|
||||
effectivenessHull += m.getDamageDist().E * (1 - ship.hullExplRes);
|
||||
}
|
||||
if (m.getDamageDist().K) {
|
||||
effectivenessHull += m.getDamageDist().K * (1 - ship.hullKinRes);
|
||||
}
|
||||
if (m.getDamageDist().T) {
|
||||
effectivenessHull += m.getDamageDist().T * (1 - ship.hullThermRes);
|
||||
}
|
||||
if (m.getDamageDist().A) {
|
||||
effectivenessHull += m.getDamageDist().A;
|
||||
}
|
||||
effectivenessHull *= Math.min(m.getPiercing() / ship.hardness, 1) * dropoff;
|
||||
const effectiveDpsHull = baseDps * effectivenessHull;
|
||||
const effectiveSDpsHull = baseSDps * effectivenessHull;
|
||||
|
||||
weapons.push({ id: m.id,
|
||||
classRating,
|
||||
name: m.name || m.grp,
|
||||
mount: m.mount,
|
||||
effectiveDpsShields,
|
||||
effectiveSDpsShields,
|
||||
effectivenessShields,
|
||||
effectiveDpsHull,
|
||||
effectiveSDpsHull,
|
||||
effectivenessHull });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return weapons;
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggered when the collapse or expand icons are clicked
|
||||
*/
|
||||
_onCollapseExpand() {
|
||||
this.setState({ expanded: !this.state.expanded });
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the sort order and sort
|
||||
* @param {string} predicate Sort predicate
|
||||
*/
|
||||
_sortOrder(predicate) {
|
||||
let desc = this.state.desc;
|
||||
|
||||
if (predicate == this.state.predicate) {
|
||||
desc = !desc;
|
||||
} else {
|
||||
desc = true;
|
||||
}
|
||||
|
||||
this._sort(this.props.ship, predicate, desc);
|
||||
this.setState({ predicate, desc });
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the weapon list
|
||||
* @param {Ship} ship Ship instance
|
||||
* @param {string} predicate Sort predicate
|
||||
* @param {Boolean} desc Sort order descending
|
||||
*/
|
||||
_sort(ship, predicate, desc) {
|
||||
let comp = weaponComparator.bind(null, this.context.language.translate);
|
||||
|
||||
switch (predicate) {
|
||||
case 'n': comp = comp(null, desc); break;
|
||||
case 'edpss': comp = comp((a, b) => a.effectiveDpsShields - b.effectiveDpsShields, desc); break;
|
||||
case 'esdpss': comp = comp((a, b) => a.effectiveSDpsShields - b.effectiveSDpsShields, desc); break;
|
||||
case 'es': comp = comp((a, b) => a.effectivenessShields - b.effectivenessShields, desc); break;
|
||||
case 'edpsh': comp = comp((a, b) => a.effectiveDpsHull - b.effectiveDpsHull, desc); break;
|
||||
case 'esdpsh': comp = comp((a, b) => a.effectiveSDpsHull - b.effectiveSDpsHull, desc); break;
|
||||
case 'eh': comp = comp((a, b) => a.effectivenessHull - b.effectivenessHull, desc); break;
|
||||
}
|
||||
|
||||
this.state.weapons.sort(comp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render individual rows for weapons
|
||||
* @param {Function} translate Translate function
|
||||
* @param {Object} formats Localised formats map
|
||||
* @return {array} The individual rows
|
||||
*
|
||||
*/
|
||||
_renderRows(translate, formats) {
|
||||
const { termtip, tooltip } = this.context;
|
||||
|
||||
let rows = [];
|
||||
|
||||
for (let i = 0; i < this.state.weapons.length; i++) {
|
||||
const weapon = this.state.weapons[i];
|
||||
rows.push(<tr key={weapon.id}>
|
||||
<td className='ri'>
|
||||
{weapon.mount == 'F' ? <span onMouseOver={termtip.bind(null, 'fixed')} onMouseOut={tooltip.bind(null, null)}><MountFixed className='icon'/></span> : null}
|
||||
{weapon.mount == 'G' ? <span onMouseOver={termtip.bind(null, 'gimballed')} onMouseOut={tooltip.bind(null, null)}><MountGimballed /></span> : null}
|
||||
{weapon.mount == 'T' ? <span onMouseOver={termtip.bind(null, 'turreted')} onMouseOut={tooltip.bind(null, null)}><MountTurret /></span> : null}
|
||||
{weapon.classRating} {translate(weapon.name)}
|
||||
</td>
|
||||
<td>{formats.round1(weapon.effectiveDpsShields)}</td>
|
||||
<td>{formats.round1(weapon.effectiveSDpsShields)}</td>
|
||||
<td>{formats.pct(weapon.effectivenessShields)}</td>
|
||||
<td>{formats.round1(weapon.effectiveDpsHull)}</td>
|
||||
<td>{formats.round1(weapon.effectiveSDpsHull)}</td>
|
||||
<td>{formats.pct(weapon.effectivenessHull)}</td>
|
||||
</tr>);
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update current range
|
||||
* @param {number} range Range 0-1
|
||||
*/
|
||||
_rangeChange(range) {
|
||||
this.setState({ range, weapons: this._calcWeapons(this.props.ship, this.state.range * this.state.maxRange) });
|
||||
}
|
||||
|
||||
/**
|
||||
* Render damage received
|
||||
* @return {React.Component} contents
|
||||
*/
|
||||
render() {
|
||||
const { language, onWindowResize, sizeRatio, tooltip, termtip } = this.context;
|
||||
const { formats, translate, units } = language;
|
||||
const { expanded, maxRange, range } = this.state;
|
||||
|
||||
const sortOrder = this._sortOrder;
|
||||
const onCollapseExpand = this._onCollapseExpand;
|
||||
|
||||
return (
|
||||
<span>
|
||||
<h1>{translate('damage received from')} {expanded ? <span onClick={onCollapseExpand}><CollapseSection className='summary'/></span> : <span onClick={onCollapseExpand}><ExpandSection className='summary'/></span>}</h1>
|
||||
{expanded ? <span>
|
||||
<table className='summary' style={{ width: '100%' }}>
|
||||
<thead>
|
||||
<tr className='main'>
|
||||
<th rowSpan={2} className='sortable' onClick={sortOrder.bind(this, 'n')} >{translate('weapon')}</th>
|
||||
<th colSpan={3} >{translate('against shields')}</th>
|
||||
<th colSpan={3} >{translate('against hull')}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th className='sortable lft' onClick={sortOrder.bind(this, 'edpss')} onMouseOver={termtip.bind(null, 'dps')} onMouseOut={tooltip.bind(null, null)}>{translate('DPS')}</th>
|
||||
<th className='sortable' onClick={sortOrder.bind(this, 'esdpss')} onMouseOver={termtip.bind(null, 'sdps')} onMouseOut={tooltip.bind(null, null)}>{translate('SDPS')}</th>
|
||||
<th className='sortable' onClick={sortOrder.bind(this, 'es')} >{translate('effectiveness')}</th>
|
||||
<th className='sortable lft' onClick={sortOrder.bind(this, 'edpsh')} onMouseOver={termtip.bind(null, 'dps')} onMouseOut={tooltip.bind(null, null)}>{translate('DPS')}</th>
|
||||
<th className='sortable' onClick={sortOrder.bind(this, 'esdpsh')} onMouseOver={termtip.bind(null, 'sdps')} onMouseOut={tooltip.bind(null, null)}>{translate('SDPS')}</th>
|
||||
<th className='sortable' onClick={sortOrder.bind(this, 'eh')} >{translate('effectiveness')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{this._renderRows(translate, formats)}
|
||||
</tbody>
|
||||
</table>
|
||||
<table style={{ width: '80%', lineHeight: '1em', backgroundColor: 'transparent', margin: 'auto' }}>
|
||||
<tbody >
|
||||
<tr>
|
||||
<td style={{ verticalAlign: 'top', padding: 0, width: '2.5em' }} onMouseEnter={termtip.bind(null, 'PHRASE_ENGAGEMENT_RANGE')} onMouseLeave={tooltip.bind(null, null)}>{translate('engagement range')}</td>
|
||||
<td>
|
||||
<Slider
|
||||
axis={true}
|
||||
onChange={this._rangeChange.bind(this)}
|
||||
axisUnit={translate('m')}
|
||||
percent={range}
|
||||
max={maxRange}
|
||||
scale={sizeRatio}
|
||||
onResize={onWindowResize}
|
||||
/>
|
||||
</td>
|
||||
<td className='primary' style={{ width: '10em', verticalAlign: 'top', fontSize: '0.9em', textAlign: 'left' }}>
|
||||
{formats.f2(range * maxRange / 1000)}{units.km}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></span> : null }
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
import React from 'react';
|
||||
import cn from 'classnames';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import { DamageKinetic, DamageThermal, DamageExplosive } from './SvgIcons';
|
||||
|
||||
/**
|
||||
* Defence summary
|
||||
*/
|
||||
export default class DefenceSummary extends TranslatedComponent {
|
||||
static propTypes = {
|
||||
ship: React.PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param {Object} props React Component properties
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render defence summary
|
||||
* @return {React.Component} contents
|
||||
*/
|
||||
render() {
|
||||
let ship = this.props.ship;
|
||||
let { language, tooltip, termtip } = this.context;
|
||||
let { formats, translate, units } = language;
|
||||
let hide = tooltip.bind(null, null);
|
||||
|
||||
const shieldGenerator = ship.findShieldGenerator();
|
||||
|
||||
// Damage values are 1 - resistance values
|
||||
return (
|
||||
<span>
|
||||
<h1>{translate('defence summary')}</h1>
|
||||
<table className='summary' style={{ marginLeft: 'auto', marginRight: 'auto', lineHeight: '1em', backgroundColor: 'transparent' }}>
|
||||
<tbody>
|
||||
{ship.shield ?
|
||||
<tr>
|
||||
<td colSpan='4' className='summary'><h2>{translate('shields')}: {formats.int(ship.shield)} {units.MJ}</h2></td>
|
||||
</tr> : null }
|
||||
{ship.shield ?
|
||||
<tr>
|
||||
<td className='ri' onMouseEnter={termtip.bind(null, 'PHRASE_SG_RECOVER', { cap: 0 })} onMouseLeave={hide}>{translate('recovery')}</td>
|
||||
<td className='le'>{formats.time(ship.calcShieldRecovery())}</td>
|
||||
<td className='ri' onMouseEnter={termtip.bind(null, 'PHRASE_SG_RECHARGE', { cap: 0 })} onMouseLeave={hide}>{translate('recharge')}</td>
|
||||
<td className='le'>{formats.time(ship.calcShieldRecharge())}</td>
|
||||
</tr> : null }
|
||||
{ship.shield ?
|
||||
<tr>
|
||||
<td className='le'>{translate('damage from')}</td>
|
||||
<td className='ri'>
|
||||
<span onMouseOver={termtip.bind(null, 'explosive')} onMouseOut={tooltip.bind(null, null)}><DamageExplosive /></span>
|
||||
<span onMouseOver={termtip.bind(null, translate('base') + ' ' + formats.pct1(1 - shieldGenerator.explres))} onMouseOut={tooltip.bind(null, null)}>{formats.pct1(1 - ship.shieldExplRes)}</span>
|
||||
</td>
|
||||
<td className='ri'>
|
||||
<span onMouseOver={termtip.bind(null, 'kinetic')} onMouseOut={tooltip.bind(null, null)}><DamageKinetic /></span>
|
||||
<span onMouseOver={termtip.bind(null, translate('base') + ' ' + formats.pct1(1 - shieldGenerator.kinres))} onMouseOut={tooltip.bind(null, null)}>{formats.pct1(1 - ship.shieldKinRes)}</span>
|
||||
</td>
|
||||
<td className='ri'>
|
||||
<span onMouseOver={termtip.bind(null, 'thermal')} onMouseOut={tooltip.bind(null, null)}><DamageThermal /></span>
|
||||
<span onMouseOver={termtip.bind(null, translate('base') + ' ' + formats.pct1(1 - shieldGenerator.thermres))} onMouseOut={tooltip.bind(null, null)}>{formats.pct1(1 - ship.shieldThermRes)}</span>
|
||||
</td>
|
||||
</tr> : null }
|
||||
{ship.shield ?
|
||||
<tr>
|
||||
<td className='le'><span onMouseOver={termtip.bind(null,'PHRASE_TOTAL_EFFECTIVE_SHIELD')} onMouseOut={tooltip.bind(null, null)}>{translate('total effective shield')}</span></td>
|
||||
<td className='ri'>
|
||||
<span onMouseOver={termtip.bind(null, 'explosive')} onMouseOut={tooltip.bind(null, null)}><DamageExplosive /></span>
|
||||
{formats.int((ship.shield + ship.shieldCells) / (1 - ship.shieldExplRes))}{units.MJ}
|
||||
</td>
|
||||
<td className='ri'>
|
||||
<span onMouseOver={termtip.bind(null, 'kinetic')} onMouseOut={tooltip.bind(null, null)}><DamageKinetic /></span>
|
||||
{formats.int((ship.shield + ship.shieldCells) / (1 - ship.shieldKinRes))}{units.MJ}
|
||||
</td>
|
||||
<td className='ri'>
|
||||
<span onMouseOver={termtip.bind(null, 'thermal')} onMouseOut={tooltip.bind(null, null)}><DamageThermal /></span>
|
||||
{formats.int((ship.shield + ship.shieldCells) / (1 - ship.shieldThermRes))}{units.MJ}
|
||||
</td>
|
||||
</tr> : null }
|
||||
|
||||
{ ship.shield && ship.shieldCells ?
|
||||
<tr>
|
||||
<td colSpan='4'><h2>{translate('shield cells')}: {formats.int(ship.shieldCells)} {units.MJ}</h2></td>
|
||||
</tr> : null }
|
||||
|
||||
<tr>
|
||||
<td colSpan='4'><h2>{translate('armour')}: {formats.int(ship.armour)}</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className='le'>{translate('damage from')}</td>
|
||||
<td className='ri'>
|
||||
<span onMouseOver={termtip.bind(null, 'explosive')} onMouseOut={tooltip.bind(null, null)}><DamageExplosive /></span>
|
||||
<span onMouseOver={termtip.bind(null, translate('base') + ' ' + formats.pct1(1 - ship.bulkheads.m.explres))} onMouseOut={tooltip.bind(null, null)}>{formats.pct1(1 - ship.hullExplRes)}</span></td>
|
||||
<td className='ri'>
|
||||
<span onMouseOver={termtip.bind(null, 'kinetic')} onMouseOut={tooltip.bind(null, null)}><DamageKinetic /></span>
|
||||
<span onMouseOver={termtip.bind(null, translate('base') + ' ' + formats.pct1(1 - ship.bulkheads.m.kinres))} onMouseOut={tooltip.bind(null, null)}>{formats.pct1(1 - ship.hullKinRes)}</span>
|
||||
</td>
|
||||
<td className='ri'>
|
||||
<span onMouseOver={termtip.bind(null, 'thermal')} onMouseOut={tooltip.bind(null, null)}><DamageThermal /></span>
|
||||
<span onMouseOver={termtip.bind(null, translate('base') + ' ' + formats.pct1(1 - ship.bulkheads.m.thermres))} onMouseOut={tooltip.bind(null, null)}>{formats.pct1(1 - ship.hullThermRes)}</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{ship.modulearmour > 0 ?
|
||||
<tr>
|
||||
<td colSpan='4'><h2>{translate('module armour')}: {formats.int(ship.modulearmour)}</h2></td>
|
||||
</tr> : null }
|
||||
|
||||
{ship.moduleprotection > 0 ?
|
||||
<tr>
|
||||
<td colSpan='2' className='cn'>{translate('internal protection')} {formats.pct1(ship.moduleprotection)}</td>
|
||||
<td colSpan='2' className='cn'>{translate('external protection')} {formats.pct1(ship.moduleprotection / 2)}</td>
|
||||
</tr> : null }
|
||||
</tbody>
|
||||
</table>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import { Ships } from 'coriolis-data/dist';
|
||||
import ShipSelector from './ShipSelector';
|
||||
import { nameComparator } from '../utils/SlotFunctions';
|
||||
import LineChart from '../components/LineChart';
|
||||
import Slider from '../components/Slider';
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import { Ships } from 'coriolis-data/dist';
|
||||
import ShipSelector from './ShipSelector';
|
||||
import { nameComparator } from '../utils/SlotFunctions';
|
||||
import LineChart from '../components/LineChart';
|
||||
import Slider from '../components/Slider';
|
||||
|
||||
@@ -8,7 +8,7 @@ import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
||||
/**
|
||||
* Hardpoint slot section
|
||||
*/
|
||||
export default class HardpointsSlotSection extends SlotSection {
|
||||
export default class HardpointSlotSection extends SlotSection {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import { Ships } from 'coriolis-data/dist';
|
||||
import ShipSelector from './ShipSelector';
|
||||
import { nameComparator } from '../utils/SlotFunctions';
|
||||
import LineChart from '../components/LineChart';
|
||||
import Slider from '../components/Slider';
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
import React from 'react';
|
||||
import cn from 'classnames';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
|
||||
/**
|
||||
* Movement summary
|
||||
*/
|
||||
export default class MovementSummary extends TranslatedComponent {
|
||||
static propTypes = {
|
||||
ship: React.PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param {Object} props React Component properties
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render movement summary
|
||||
* @return {React.Component} contents
|
||||
*/
|
||||
render() {
|
||||
let ship = this.props.ship;
|
||||
let { language, tooltip, termtip } = this.context;
|
||||
let { formats, translate, units } = language;
|
||||
let boostMultiplier = ship.topBoost / ship.topSpeed;
|
||||
|
||||
return (
|
||||
<span>
|
||||
<h1>{translate('movement summary')}</h1>
|
||||
<table style={{ marginLeft: 'auto', marginRight: 'auto', lineHeight: '1em', backgroundColor: 'transparent', borderSpacing: '0.5em' }}>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td > </td>
|
||||
<td colSpan='6'>{translate('engine pips')}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>0</td>
|
||||
<td>1</td>
|
||||
<td>2</td>
|
||||
<td>3</td>
|
||||
<td>4</td>
|
||||
<td onMouseOver={termtip.bind(null, '4b')} onMouseOut={tooltip.bind(null, null)}>4B</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className='ri'>{translate('speed')} ({units['m/s']})</td>
|
||||
<td className='ri'>{formats.int(ship.speeds[0])}</td>
|
||||
<td className='ri'>{formats.int(ship.speeds[1])}</td>
|
||||
<td className='ri'>{formats.int(ship.speeds[2])}</td>
|
||||
<td className='ri'>{formats.int(ship.speeds[3])}</td>
|
||||
<td className='ri'>{formats.int(ship.speeds[4])}</td>
|
||||
<td className='ri'>{formats.int(ship.speeds[4] * boostMultiplier)}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className='ri'>{translate('pitch')} ({units['°/s']})</td>
|
||||
<td className='ri'>{formats.int(ship.pitches[0])}</td>
|
||||
<td className='ri'>{formats.int(ship.pitches[1])}</td>
|
||||
<td className='ri'>{formats.int(ship.pitches[2])}</td>
|
||||
<td className='ri'>{formats.int(ship.pitches[3])}</td>
|
||||
<td className='ri'>{formats.int(ship.pitches[4])}</td>
|
||||
<td className='ri'>{formats.int(ship.pitches[4] * boostMultiplier)}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className='ri'>{translate('roll')} ({units['°/s']})</td>
|
||||
<td className='ri'>{formats.int(ship.rolls[0])}</td>
|
||||
<td className='ri'>{formats.int(ship.rolls[1])}</td>
|
||||
<td className='ri'>{formats.int(ship.rolls[2])}</td>
|
||||
<td className='ri'>{formats.int(ship.rolls[3])}</td>
|
||||
<td className='ri'>{formats.int(ship.rolls[4])}</td>
|
||||
<td className='ri'>{formats.int(ship.rolls[4] * boostMultiplier)}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className='ri'>{translate('yaw')} ({units['°/s']})</td>
|
||||
<td className='ri'>{formats.int(ship.yaws[0])}</td>
|
||||
<td className='ri'>{formats.int(ship.yaws[1])}</td>
|
||||
<td className='ri'>{formats.int(ship.yaws[2])}</td>
|
||||
<td className='ri'>{formats.int(ship.yaws[3])}</td>
|
||||
<td className='ri'>{formats.int(ship.yaws[4])}</td>
|
||||
<td className='ri'>{formats.int(ship.yaws[4] * boostMultiplier)}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
import React from 'react';
|
||||
import cn from 'classnames';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import { DamageAbsolute, DamageKinetic, DamageThermal, DamageExplosive } from './SvgIcons';
|
||||
|
||||
/**
|
||||
* Offence summary
|
||||
*/
|
||||
export default class OffenceSummary extends TranslatedComponent {
|
||||
static propTypes = {
|
||||
ship: React.PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param {Object} props React Component properties
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render offence summary
|
||||
* @return {React.Component} contents
|
||||
*/
|
||||
render() {
|
||||
let ship = this.props.ship;
|
||||
let { language, tooltip, termtip } = this.context;
|
||||
let { formats, translate } = language;
|
||||
|
||||
return (
|
||||
<span>
|
||||
<h1>{translate('offence summary')}</h1>
|
||||
<table className='summary' style={{ marginLeft: 'auto', marginRight: 'auto', lineHeight: '1em', backgroundColor: 'transparent' }}>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colSpan='5' className='summary'><h2>{translate('dps')}: {formats.f1(ship.totalDps)}</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className='le'>{translate('damage by')}</td>
|
||||
<td className='ri' onMouseOver={termtip.bind(null, 'absolute')} onMouseOut={tooltip.bind(null, null)}><DamageAbsolute /> {formats.f1(ship.totalAbsDps)}</td>
|
||||
<td className='ri' onMouseOver={termtip.bind(null, 'explosive')} onMouseOut={tooltip.bind(null, null)}><DamageExplosive /> {formats.f1(ship.totalExplDps)}</td>
|
||||
<td className='ri' onMouseOver={termtip.bind(null, 'kinetic')} onMouseOut={tooltip.bind(null, null)}><DamageKinetic /> {formats.f1(ship.totalKinDps)}</td>
|
||||
<td className='ri' onMouseOver={termtip.bind(null, 'thermal')} onMouseOut={tooltip.bind(null, null)}><DamageThermal /> {formats.f1(ship.totalThermDps)}</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colSpan='5' className='summary'><h2>{translate('sdps')}: {formats.f1(ship.totalSDps)}</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className='le'>{translate('damage by')}</td>
|
||||
<td className='ri' onMouseOver={termtip.bind(null, 'absolute')} onMouseOut={tooltip.bind(null, null)}><DamageAbsolute /> {formats.f1(ship.totalAbsSDps)}</td>
|
||||
<td className='ri' onMouseOver={termtip.bind(null, 'explosive')} onMouseOut={tooltip.bind(null, null)}><DamageExplosive /> {formats.f1(ship.totalExplSDps)}</td>
|
||||
<td className='ri' onMouseOver={termtip.bind(null, 'kinetic')} onMouseOut={tooltip.bind(null, null)}><DamageKinetic /> {formats.f1(ship.totalKinSDps)}</td>
|
||||
<td className='ri' onMouseOver={termtip.bind(null, 'thermal')} onMouseOut={tooltip.bind(null, null)}><DamageThermal /> {formats.f1(ship.totalThermSDps)}</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colSpan='5' className='summary'><h2>{translate('dpe')}: {formats.f1(ship.totalDpe)}</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className='le'>{translate('damage by')}</td>
|
||||
<td className='ri' onMouseOver={termtip.bind(null, 'absolute')} onMouseOut={tooltip.bind(null, null)}><DamageAbsolute /> {formats.f1(ship.totalAbsDpe)}</td>
|
||||
<td className='ri' onMouseOver={termtip.bind(null, 'explosive')} onMouseOut={tooltip.bind(null, null)}><DamageExplosive /> {formats.f1(ship.totalExplDpe)}</td>
|
||||
<td className='ri' onMouseOver={termtip.bind(null, 'kinetic')} onMouseOut={tooltip.bind(null, null)}><DamageKinetic /> {formats.f1(ship.totalKinDpe)}</td>
|
||||
<td className='ri' onMouseOver={termtip.bind(null, 'thermal')} onMouseOut={tooltip.bind(null, null)}><DamageThermal /> {formats.f1(ship.totalThermDpe)}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import { Ships } from 'coriolis-data/dist';
|
||||
import ShipSelector from './ShipSelector';
|
||||
import { nameComparator } from '../utils/SlotFunctions';
|
||||
import { Pip } from './SvgIcons';
|
||||
import LineChart from '../components/LineChart';
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
import React from 'react';
|
||||
import cn from 'classnames';
|
||||
import { Ships } from 'coriolis-data/dist';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import { Rocket } from './SvgIcons';
|
||||
|
||||
/**
|
||||
* Selector for ships
|
||||
*/
|
||||
export default class ShipSelector extends TranslatedComponent {
|
||||
static propTypes = {
|
||||
initial: React.PropTypes.object.isRequired,
|
||||
onChange: React.PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param {Object} props React Component properties
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = { ship : this.props.initial };
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the ships menu
|
||||
* @return {React.Component} Menu
|
||||
*/
|
||||
_getShipsMenu() {
|
||||
const _selectShip = this._selectShip;
|
||||
|
||||
let shipList = [];
|
||||
|
||||
for (let s in Ships) {
|
||||
shipList.push(<div key={s} onClick={_selectShip.bind(this, s)} className='block' >{Ships[s].properties.name}</div>);
|
||||
}
|
||||
|
||||
return shipList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle opening the menu
|
||||
* @param {string} menu The ID of the opened menu
|
||||
* @param {SyntheticEvent} event Event
|
||||
*/
|
||||
_openMenu(menu, event) {
|
||||
event.stopPropagation();
|
||||
if (this.props.currentMenu == menu) {
|
||||
menu = null;
|
||||
}
|
||||
|
||||
this.context.openMenu(menu);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle selection of a ship
|
||||
* @param {string} s The selected ship ID
|
||||
*/
|
||||
_selectShip(s) {
|
||||
this.setState({ ship: Ships[s] });
|
||||
|
||||
this.context.openMenu(null);
|
||||
this.props.onChange(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render ship selector
|
||||
* @return {React.Component} contents
|
||||
*/
|
||||
render() {
|
||||
const currentMenu = this.props.currentMenu;
|
||||
const ship = this.state.ship;
|
||||
|
||||
return (
|
||||
<div className='shipselector'>
|
||||
<div className='menu'>
|
||||
<div className={cn('menu-header', { selected: currentMenu == 'wds' })} onClick={this._openMenu.bind(this, 'wds')}>
|
||||
<Rocket className='warning' /><span className='menu-item-label'>{ship.properties.name}</span>
|
||||
{currentMenu == 'wds' ?
|
||||
<div className='menu-list quad no-wrap' onClick={ (e) => e.stopPropagation() }>
|
||||
{this._getShipsMenu()}
|
||||
</div> : null }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import { Ships } from 'coriolis-data/dist';
|
||||
import ShipSelector from './ShipSelector';
|
||||
import { nameComparator } from '../utils/SlotFunctions';
|
||||
import { CollapseSection, ExpandSection, MountFixed, MountGimballed, MountTurret } from './SvgIcons';
|
||||
import LineChart from '../components/LineChart';
|
||||
|
||||
@@ -14,7 +14,7 @@ import { FloppyDisk, Bin, Switch, Download, Reload, LinkIcon, ShoppingIcon } fro
|
||||
import LZString from 'lz-string';
|
||||
import ShipSummaryTable from '../components/ShipSummaryTable';
|
||||
import StandardSlotSection from '../components/StandardSlotSection';
|
||||
import HardpointsSlotSection from '../components/HardpointsSlotSection';
|
||||
import HardpointSlotSection from '../components/HardpointSlotSection';
|
||||
import InternalSlotSection from '../components/InternalSlotSection';
|
||||
import UtilitySlotSection from '../components/UtilitySlotSection';
|
||||
import Pips from '../components/Pips';
|
||||
@@ -574,7 +574,7 @@ export default class OutfittingPage extends Page {
|
||||
<ShipSummaryTable ship={ship} marker={shipSummaryMarker} />
|
||||
<StandardSlotSection ship={ship} code={standardSlotMarker} onChange={shipUpdated} currentMenu={menu} />
|
||||
<InternalSlotSection ship={ship} code={internalSlotMarker} onChange={shipUpdated} currentMenu={menu} />
|
||||
<HardpointsSlotSection ship={ship} code={hardpointsSlotMarker} onChange={shipUpdated} currentMenu={menu} />
|
||||
<HardpointSlotSection ship={ship} code={hardpointsSlotMarker} onChange={shipUpdated} currentMenu={menu} />
|
||||
<UtilitySlotSection ship={ship} code={hardpointsSlotMarker} onChange={shipUpdated} currentMenu={menu} />
|
||||
|
||||
{/* Control of ship and opponent */}
|
||||
|
||||
@@ -221,39 +221,6 @@ export default class Ship {
|
||||
return Calc.calcYaw(this.unladenMass + fuel + cargo, this.yaw, this.standard[1].m, this.pipSpeed, eng, this.topBoost / this.topSpeed, boost);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the recovery time after losing or turning on shields
|
||||
* Thanks to CMDRs Al Gray, GIF, and Nomad Enigma for providing Shield recharge data and formulas
|
||||
*
|
||||
* @return {Number} Recovery time in seconds
|
||||
*/
|
||||
calcShieldRecovery() {
|
||||
const shieldGenerator = this.findShieldGenerator();
|
||||
if (shieldGenerator) {
|
||||
const brokenRegenRate = shieldGenerator.getBrokenRegenerationRate();
|
||||
// 50% of shield strength / broken recharge rate + 15 second delay before recharge starts
|
||||
return ((this.shield / 2) / brokenRegenRate) + 15;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the recharge time for a shield going from 50% to 100%
|
||||
* Thanks to CMDRs Al Gray, GIF, and Nomad Enigma for providing Shield recharge data and formulas
|
||||
*
|
||||
* @return {Number} 50 - 100% Recharge time in seconds
|
||||
*/
|
||||
calcShieldRecharge() {
|
||||
const shieldGenerator = this.findShieldGenerator();
|
||||
if (shieldGenerator) {
|
||||
const regenRate = shieldGenerator.getRegenerationRate();
|
||||
|
||||
// 50% of shield strength / recharge rate
|
||||
return (this.shield / 2) / regenRate;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the hypothetical shield strength for the ship using the specified parameters
|
||||
* @param {Object} sg [optional] Shield Generator to use
|
||||
|
||||
Reference in New Issue
Block a user