diff --git a/ChangeLog.md b/ChangeLog.md
index 0e821ec8..5f7d1c94 100644
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -13,6 +13,7 @@
* Obey restricted slot rules when adding all for internal slots
* Version URLs to handle changes to ship specifications over time
* Do not include disabled shield boosters in calculations
+ * Add 'Damage dealt' section
#2.2.5
* Calculate rate of fire for multi-burst weapons
diff --git a/src/app/components/DamageDealt.jsx b/src/app/components/DamageDealt.jsx
new file mode 100644
index 00000000..f0095013
--- /dev/null
+++ b/src/app/components/DamageDealt.jsx
@@ -0,0 +1,143 @@
+import React from 'react';
+import cn from 'classnames';
+import TranslatedComponent from './TranslatedComponent';
+import { Ships } from 'coriolis-data/dist';
+import { slotName, slotComparator } from '../utils/SlotFunctions';
+import ShipSelector from './ShipSelector';
+
+/**
+ * Damage against a selected ship
+ */
+export default class DamageDealt 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._onShipChange = this._onShipChange.bind(this);
+
+ this.state = {
+ predicate: 'n',
+ desc: true,
+ against: Ships['anaconda'],
+ };
+ }
+
+ /**
+ * Triggered when the comparator ship changes
+ */
+ _onShipChange(s) {
+ this.setState({ against: Ships[s] });
+ }
+
+ /**
+ * 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 {Ship} against The ship to compare against
+ * @param {string} predicate Sort predicate
+ * @param {Boolean} desc Sort order descending
+ */
+ _sort(ship, against, predicate, desc) {
+ let weaponList = ship.hardpoints;
+ let comp = slotComparator.bind(null, this.context.language.translate);
+
+ switch (predicate) {
+ case 'n': comp = comp(null, desc); break;
+ case 'd': comp = comp((a, b) => a.m.getDps() - b.m.getDps(), desc); break;
+ case 'e': comp = comp((a, b) => (a.m.getPiercing() > a.m.hardness ? a.m.getDps() : a.m.getDps() * a.m.getPiercing() / a.m.hardness) - (b.m.getPiercing() > b.m.hardness ? b.m.getDps() : b.m.getDps() * b.m.getPiercing() / b.m.hardness), desc); break;
+ }
+
+ weaponList.sort(comp);
+ }
+
+ /**
+ * Render individual rows for hardpoints
+ * @param {Function} translate Translate function
+ * @param {Object} formats Localised formats map
+ * @param {Object} ship Our ship
+ * @param {Object} against The ship against which to compare
+ * @return {array} The individual rows
+ *
+ */
+ _renderRows(translate, formats, ship, against) {
+ let rows = [];
+
+ for (let hardpoint in ship.hardpoints) {
+ if (ship.hardpoints[hardpoint].m) {
+ const m = ship.hardpoints[hardpoint].m;
+ const classRating = `${m.class}${m.rating}${m.missile ? '/' + m.missile : ''}`;
+ const effectiveness = m.getPiercing() >= against.properties.hardness ? 1 : m.getPiercing() / against.properties.hardness;
+ const effectiveDps = m.getDps() * effectiveness;
+ const effectiveSDps = m.getClip() ? (m.getClip() * m.getDps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload()) * effectiveness : effectiveDps;
+
+ rows.push(
+ | {classRating} {slotName(translate, ship.hardpoints[hardpoint])} |
+ {formats.round1(effectiveDps)} |
+ {formats.round1(effectiveSDps)} |
+ {formats.pct(effectiveness)} |
+
);
+ }
+ }
+
+
+ return rows;
+ }
+
+ /**
+ * Render damage dealt
+ * @return {React.Component} contents
+ */
+ render() {
+ const { language, tooltip, termtip } = this.context;
+ const { formats, translate } = language;
+
+ const ship = this.props.ship;
+ const against = this.state.against;
+ const hardness = against.properties.hardness;
+
+ return (
+
+ {translate('damage dealt against')}
+
+
+
+
+ | {translate('weapon')} |
+ {translate('effective dps')} |
+ {translate('effective sdps')} |
+ {translate('effectiveness')} |
+
+
+
+ {this._renderRows(translate, formats, ship, against)}
+
+
+
+ );
+ }
+}
diff --git a/src/app/components/HardpointsSlotSection.jsx b/src/app/components/HardpointsSlotSection.jsx
index 8eb212da..51bb79d4 100644
--- a/src/app/components/HardpointsSlotSection.jsx
+++ b/src/app/components/HardpointsSlotSection.jsx
@@ -133,6 +133,10 @@ export default class HardpointsSlotSection extends SlotSection {
+ {translate('pa')}
+
{translate('nl')}