Compare commits

..

17 Commits
2.2.6 ... 2.2.9

Author SHA1 Message Date
Cmdr McDonald
dd1175abf4 Merge branch 'release/2.2.9' 2017-01-14 16:24:09 +00:00
Cmdr McDonald
619976230d Bump version 2017-01-14 16:24:04 +00:00
Cmdr McDonald
2cb0d5209b Merge branch 'feature/falloff' into develop 2017-01-14 16:23:12 +00:00
Cmdr McDonald
ad06e23afa Add total DPS and effectiveness information to 'Damage Dealt' section 2017-01-14 16:20:48 +00:00
Cmdr McDonald
3def84e435 Use better DPE calculation methodology 2017-01-14 13:15:15 +00:00
Cmdr McDonald
7f377d6345 Add and use range when calculating weapon effectiveness for damage dealt 2017-01-14 13:10:09 +00:00
Cmdr McDonald
53137e0ae1 Add falloff for weapons 2017-01-14 09:52:31 +00:00
Cmdr McDonald
792eda2572 Use SSL-enabled server for shortlinks 2017-01-13 20:05:45 +00:00
Cmdr McDonald
550c94fa94 Merge branch 'release/2.2.8' into develop 2017-01-13 11:42:36 +00:00
Cmdr McDonald
2a841281d4 Merge branch 'release/2.2.8' 2017-01-13 11:42:31 +00:00
Cmdr McDonald
260f29834a Update release notes 2017-01-13 11:35:01 +00:00
Cmdr McDonald
ddb35d321c Fix issue where filling all internals with cargo racks would include restricted slots 2017-01-13 11:34:18 +00:00
Cmdr McDonald
6017c1ecff Merge branch 'release/2.2.7' into develop 2017-01-11 21:59:09 +00:00
Cmdr McDonald
05e06f30f5 Merge branch 'release/2.2.7' 2017-01-11 21:59:01 +00:00
Cmdr McDonald
fb5ba6a0b2 Update damage dealt to use actual resistances 2017-01-11 21:57:49 +00:00
Cmdr McDonald
80656a7a78 Fix resistance diminishing return calculations 2017-01-11 21:33:31 +00:00
Cmdr McDonald
ce980cf091 Merge branch 'release/2.2.6' into develop 2017-01-10 19:34:18 +00:00
16 changed files with 238 additions and 116 deletions

View File

@@ -1,3 +1,25 @@
#2.2.9
* Use SSL-enabled server for shortlinks
* Add falloff for weapons
* Use falloff when calculating weapon effectiveness in damage dealt
* Add engagement range slider to 'Damage Dealt' section to allow user to see change in weapon effectiveness with range
* Use better DPE calculation methodology
* Add total DPS and effectiveness information to 'Damage Dealt' section
* Use coriolis-data 2.2.9:
* Add falloff metric for weapons
* Add falloff from range modification
#2.2.8
* Fix issue where filling all internals with cargo racks would include restricted slots
* Use coriolis-data 2.2.8:
* Set military slot of Viper Mk IV to class 3; was incorrectly set as class 2
* Update base regeneration rate of prismatic shield generators to values in 2.2.03
* Update specials with information in 2.2.03
#2.2.7
* Fix resistance diminishing return calculations
* Do not allow -100% to be entered as a modification value
#2.2.6 #2.2.6
* Add pitch/roll/yaw information * Add pitch/roll/yaw information
* Use combination of pitch, roll and yaw to provide a more useful agility metric * Use combination of pitch, roll and yaw to provide a more useful agility metric

View File

@@ -285,10 +285,10 @@
"totalThermSDps": 53.82, "totalThermSDps": 53.82,
"baseShieldStrength": 350, "baseShieldStrength": 350,
"baseArmour": 945, "baseArmour": 945,
"hullExplRes": 0.78, "hullExplRes": 0.22,
"hullKinRes": 0.73, "hullKinRes": 0.27,
"hullMass": 400, "hullMass": 400,
"hullThermRes": 1.37, "hullThermRes": -0.36,
"masslock": 23, "masslock": 23,
"pipSpeed": 0.14, "pipSpeed": 0.14,
"pitch": 25, "pitch": 25,
@@ -316,7 +316,7 @@
"shield": 833, "shield": 833,
"shieldCells": 1840, "shieldCells": 1840,
"shieldExplRes": 0.5, "shieldExplRes": 0.5,
"shieldKinRes": 0.6, "shieldKinRes": 0.4,
"shieldThermRes": 1.2 "shieldThermRes": -0.2
} }
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "coriolis_shipyard", "name": "coriolis_shipyard",
"version": "2.2.6", "version": "2.2.9",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/EDCD/coriolis" "url": "https://github.com/EDCD/coriolis"

View File

@@ -4,6 +4,7 @@ import { Ships } from 'coriolis-data/dist';
import ShipSelector from './ShipSelector'; import ShipSelector from './ShipSelector';
import { nameComparator } from '../utils/SlotFunctions'; import { nameComparator } from '../utils/SlotFunctions';
import { CollapseSection, ExpandSection, MountFixed, MountGimballed, MountTurret } from './SvgIcons'; import { CollapseSection, ExpandSection, MountFixed, MountGimballed, MountTurret } from './SvgIcons';
import Slider from '../components/Slider';
/** /**
* Generates an internationalization friendly weapon comparator that will * Generates an internationalization friendly weapon comparator that will
@@ -66,7 +67,9 @@ export default class DamageDealt extends TranslatedComponent {
predicate: 'n', predicate: 'n',
desc: true, desc: true,
against: DamageDealt.DEFAULT_AGAINST, against: DamageDealt.DEFAULT_AGAINST,
expanded: false expanded: false,
range: 0.1667,
maxRange: 6000
}; };
} }
@@ -74,8 +77,8 @@ export default class DamageDealt extends TranslatedComponent {
* Set the initial weapons state * Set the initial weapons state
*/ */
componentWillMount() { componentWillMount() {
const weapons = this._calcWeapons(this.props.ship, this.state.against); const data = this._calcWeapons(this.props.ship, this.state.against, this.state.range * this.state.maxRange);
this.setState({ weapons }); this.setState({ weapons: data.weapons, totals: data.totals });
} }
/** /**
@@ -86,8 +89,8 @@ export default class DamageDealt extends TranslatedComponent {
*/ */
componentWillReceiveProps(nextProps, nextContext) { componentWillReceiveProps(nextProps, nextContext) {
if (nextProps.code != this.props.code) { if (nextProps.code != this.props.code) {
const weapons = this._calcWeapons(this.props.ship, this.state.against); const data = this._calcWeapons(this.props.ship, this.state.against, this.state.range * this.state.maxRange);
this.setState({ weapons }); this.setState({ weapons: data.weapons, totals: data.totals });
} }
return true; return true;
} }
@@ -96,30 +99,61 @@ export default class DamageDealt extends TranslatedComponent {
* Calculate the damage dealt by a ship * Calculate the damage dealt by a ship
* @param {Object} ship The ship which will deal the damage * @param {Object} ship The ship which will deal the damage
* @param {Object} against The ship against which damage will be dealt * @param {Object} against The ship against which damage will be dealt
* @param {Object} range The engagement range
* @return {boolean} Returns the per-weapon damage * @return {boolean} Returns the per-weapon damage
*/ */
_calcWeapons(ship, against) { _calcWeapons(ship, against, range) {
let weapons = []; // Tidy up the range so that it's to 4 decimal places
range = Math.round(10000 * range) / 10000;
// Track totals
let totals = {};
totals.effectiveness = 0;
totals.effectiveDps = 0;
totals.effectiveSDps = 0;
let totalDps = 0;
let weapons = [];
for (let i = 0; i < ship.hardpoints.length; i++) { for (let i = 0; i < ship.hardpoints.length; i++) {
if (ship.hardpoints[i].m) { if (ship.hardpoints[i].m) {
const m = ship.hardpoints[i].m; const m = ship.hardpoints[i].m;
const classRating = `${m.class}${m.rating}${m.missile ? '/' + m.missile : ''}`; if (m.getDamage() && m.grp !== 'po') {
const effectiveness = m.getPiercing() >= against.properties.hardness ? 1 : m.getPiercing() / against.properties.hardness; let dropoff = 1;
const effectiveDps = m.getDps() * effectiveness; if (m.getFalloff()) {
const effectiveSDps = m.getClip() ? (m.getClip() * m.getDps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload()) * effectiveness : effectiveDps; // 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 : ''}`;
const effectiveness = (m.getPiercing() >= against.properties.hardness ? 1 : m.getPiercing() / against.properties.hardness) * dropoff;
const effectiveDps = m.getDps() * effectiveness * dropoff;
const effectiveSDps = (m.getClip() ? (m.getClip() * m.getDps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload()) * effectiveness : effectiveDps) * dropoff;
totals.effectiveDps += effectiveDps;
totals.effectiveSDps += effectiveSDps;
totalDps += m.getDps();
weapons.push({ id: i, weapons.push({ id: i,
mount: m.mount, mount: m.mount,
name: m.name || m.grp, name: m.name || m.grp,
classRating, classRating,
effectiveDps, effectiveDps,
effectiveSDps, effectiveSDps,
effectiveness }); effectiveness });
}
} }
} }
totals.effectiveness = totals.effectiveDps / totalDps;
return weapons;
return {weapons: weapons, totals: totals};
} }
/** /**
@@ -135,8 +169,8 @@ export default class DamageDealt extends TranslatedComponent {
*/ */
_onShipChange(s) { _onShipChange(s) {
const against = Ships[s]; const against = Ships[s];
const weapons = this._calcWeapons(this.props.ship, against); const data = this._calcWeapons(this.props.ship, against);
this.setState({ against, weapons }); this.setState({ against, weapons: data.weapons, totals: data.totals });
} }
/** /**
@@ -208,14 +242,23 @@ export default class DamageDealt extends TranslatedComponent {
return rows; return rows;
} }
/**
* Update current range
* @param {number} range Range 0-1
*/
_rangeChange(range) {
const data = this._calcWeapons(this.props.ship, this.state.against, this.state.range * this.state.maxRange);
this.setState({ range, weapons: data.weapons, totals: data.totals });
}
/** /**
* Render damage dealt * Render damage dealt
* @return {React.Component} contents * @return {React.Component} contents
*/ */
render() { render() {
const { language, tooltip, termtip } = this.context; const { language, onWindowResize, sizeRatio, tooltip, termtip } = this.context;
const { formats, translate } = language; const { formats, translate, units } = language;
const { expanded } = this.state; const { expanded, maxRange, range, totals } = this.state;
const sortOrder = this._sortOrder; const sortOrder = this._sortOrder;
const onCollapseExpand = this._onCollapseExpand; const onCollapseExpand = this._onCollapseExpand;
@@ -227,16 +270,45 @@ export default class DamageDealt extends TranslatedComponent {
<ShipSelector initial={this.state.against} currentMenu={this.props.currentMenu} onChange={this._onShipChange} /> <ShipSelector initial={this.state.against} currentMenu={this.props.currentMenu} onChange={this._onShipChange} />
<table className='summary' style={{ width: '100%' }}> <table className='summary' style={{ width: '100%' }}>
<thead> <thead>
<tr className='main'> <tr className='main'>
<td className='sortable' onClick={sortOrder.bind(this, 'n')}>{translate('weapon')}</td> <td className='sortable' onClick={sortOrder.bind(this, 'n')}>{translate('weapon')}</td>
<td className='sortable' onClick={sortOrder.bind(this, 'edps')}>{translate('effective dps')}</td> <td className='sortable' onClick={sortOrder.bind(this, 'edps')}>{translate('effective dps')}</td>
<td className='sortable' onClick={sortOrder.bind(this, 'esdps')}>{translate('effective sdps')}</td> <td className='sortable' onClick={sortOrder.bind(this, 'esdps')}>{translate('effective sdps')}</td>
<td className='sortable' onClick={sortOrder.bind(this, 'e')}>{translate('effectiveness')}</td> <td className='sortable' onClick={sortOrder.bind(this, 'e')}>{translate('effectiveness')}</td>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{this._renderRows(translate, formats)} {this._renderRows(translate, formats)}
</tbody> </tbody>
<tfoot>
<tr className='main'>
<td className='ri'><i>{translate('total')}</i></td>
<td className='ri'><i>{formats.round1(totals.effectiveDps)}</i></td>
<td className='ri'><i>{formats.round1(totals.effectiveSDps)}</i></td>
<td className='ri'><i>{formats.pct(totals.effectiveness)}</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></span> : null } </table></span> : null }
</span> </span>
); );

View File

@@ -105,34 +105,36 @@ export default class DamageReceived extends TranslatedComponent {
// Effective DPS taking in to account shield resistance // Effective DPS taking in to account shield resistance
let effectivenessShields = 0; let effectivenessShields = 0;
if (m.getDamageType().indexOf('E') != -1) { if (m.getDamageType().indexOf('E') != -1) {
effectivenessShields += ship.shieldExplRes; effectivenessShields += (1 - ship.shieldExplRes);
} }
if (m.getDamageType().indexOf('K') != -1) { if (m.getDamageType().indexOf('K') != -1) {
effectivenessShields += ship.shieldKinRes; effectivenessShields += (1 - ship.shieldKinRes);
} }
if (m.getDamageType().indexOf('T') != -1) { if (m.getDamageType().indexOf('T') != -1) {
effectivenessShields += ship.shieldThermRes; effectivenessShields += (1 - ship.shieldThermRes);
}
if (m.getDamageType().indexOf('A') != -1) {
effectivenessShields += 1;
} }
effectivenessShields /= m.getDamageType().length; effectivenessShields /= m.getDamageType().length;
// Plasma accelerators deal absolute damage
if (m.grp == 'pa') effectivenessShields = 1;
const effectiveDpsShields = baseDps * effectivenessShields; const effectiveDpsShields = baseDps * effectivenessShields;
const effectiveSDpsShields = baseSDps * effectivenessShields; const effectiveSDpsShields = baseSDps * effectivenessShields;
// Effective DPS taking in to account hull hardness and resistance // Effective DPS taking in to account hull hardness and resistance
let effectivenessHull = 0; let effectivenessHull = 0;
if (m.getDamageType().indexOf('E') != -1) { if (m.getDamageType().indexOf('E') != -1) {
effectivenessHull += ship.hullExplRes; effectivenessHull += (1 - ship.hullExplRes);
} }
if (m.getDamageType().indexOf('K') != -1) { if (m.getDamageType().indexOf('K') != -1) {
effectivenessHull += ship.hullKinRes; effectivenessHull += (1 - ship.hullKinRes);
} }
if (m.getDamageType().indexOf('T') != -1) { if (m.getDamageType().indexOf('T') != -1) {
effectivenessHull += ship.hullThermRes; effectivenessHull += (1 - ship.hullThermRes);
}
if (m.getDamageType().indexOf('A') != -1) {
effectivenessHull += 1;
} }
effectivenessHull /= m.getDamageType().length; effectivenessHull /= m.getDamageType().length;
// Plasma accelerators deal absolute damage (but could be reduced by hardness)
if (m.grp == 'pa') effectivenessHull = 1;
effectivenessHull *= Math.min(m.getPiercing() / ship.hardness, 1); effectivenessHull *= Math.min(m.getPiercing() / ship.hardness, 1);
const effectiveDpsHull = baseDps * effectivenessHull; const effectiveDpsHull = baseDps * effectivenessHull;
const effectiveSDpsHull = baseSDps * effectivenessHull; const effectiveSDpsHull = baseSDps * effectivenessHull;

View File

@@ -31,6 +31,7 @@ export default class DefenceSummary extends TranslatedComponent {
const shieldGenerator = ship.findShieldGenerator(); const shieldGenerator = ship.findShieldGenerator();
// Damage values are 1 - resistance values
return ( return (
<span> <span>
<h1>{translate('defence summary')}</h1> <h1>{translate('defence summary')}</h1>
@@ -52,15 +53,15 @@ export default class DefenceSummary extends TranslatedComponent {
<td className='le'>{translate('damage from')}</td> <td className='le'>{translate('damage from')}</td>
<td className='ri'> <td className='ri'>
<span onMouseOver={termtip.bind(null, 'explosive')} onMouseOut={tooltip.bind(null, null)}><DamageExplosive /></span>&nbsp; <span onMouseOver={termtip.bind(null, 'explosive')} onMouseOut={tooltip.bind(null, null)}><DamageExplosive /></span>&nbsp;
<span onMouseOver={termtip.bind(null, translate('base') + ' ' + formats.pct1(1 - shieldGenerator.explres))} onMouseOut={tooltip.bind(null, null)}>{formats.pct1(ship.shieldExplRes || 1)}</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>
<td className='ri'> <td className='ri'>
<span onMouseOver={termtip.bind(null, 'kinetic')} onMouseOut={tooltip.bind(null, null)}><DamageKinetic /></span>&nbsp; <span onMouseOver={termtip.bind(null, 'kinetic')} onMouseOut={tooltip.bind(null, null)}><DamageKinetic /></span>&nbsp;
<span onMouseOver={termtip.bind(null, translate('base') + ' ' + formats.pct1(1 - shieldGenerator.kinres))} onMouseOut={tooltip.bind(null, null)}>{formats.pct1(ship.shieldKinRes || 1)}</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>
<td className='ri'> <td className='ri'>
<span onMouseOver={termtip.bind(null, 'thermal')} onMouseOut={tooltip.bind(null, null)}><DamageThermal /></span>&nbsp; <span onMouseOver={termtip.bind(null, 'thermal')} onMouseOut={tooltip.bind(null, null)}><DamageThermal /></span>&nbsp;
<span onMouseOver={termtip.bind(null, translate('base') + ' ' + formats.pct1(1 - shieldGenerator.thermres))} onMouseOut={tooltip.bind(null, null)}>{formats.pct1(ship.shieldThermRes || 1)}</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> </td>
</tr> : null } </tr> : null }
@@ -76,14 +77,14 @@ export default class DefenceSummary extends TranslatedComponent {
<td className='le'>{translate('damage from')}</td> <td className='le'>{translate('damage from')}</td>
<td className='ri'> <td className='ri'>
<span onMouseOver={termtip.bind(null, 'explosive')} onMouseOut={tooltip.bind(null, null)}><DamageExplosive /></span>&nbsp; <span onMouseOver={termtip.bind(null, 'explosive')} onMouseOut={tooltip.bind(null, null)}><DamageExplosive /></span>&nbsp;
<span onMouseOver={termtip.bind(null, translate('base') + ' ' + formats.pct1(1 - ship.bulkheads.m.explres))} onMouseOut={tooltip.bind(null, null)}>{formats.pct1(ship.hullExplRes || 1)}</span></td> <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'> <td className='ri'>
<span onMouseOver={termtip.bind(null, 'kinetic')} onMouseOut={tooltip.bind(null, null)}><DamageKinetic /></span>&nbsp; <span onMouseOver={termtip.bind(null, 'kinetic')} onMouseOut={tooltip.bind(null, null)}><DamageKinetic /></span>&nbsp;
<span onMouseOver={termtip.bind(null, translate('base') + ' ' + formats.pct1(1 - ship.bulkheads.m.kinres))} onMouseOut={tooltip.bind(null, null)}>{formats.pct1(ship.hullKinRes || 1)}</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>
<td className='ri'> <td className='ri'>
<span onMouseOver={termtip.bind(null, 'thermal')} onMouseOut={tooltip.bind(null, null)}><DamageThermal /></span>&nbsp; <span onMouseOver={termtip.bind(null, 'thermal')} onMouseOut={tooltip.bind(null, null)}><DamageThermal /></span>&nbsp;
<span onMouseOver={termtip.bind(null, translate('base') + ' ' + formats.pct1(1 - ship.bulkheads.m.thermres))} onMouseOut={tooltip.bind(null, null)}>{formats.pct1(ship.hullThermRes || 1)}</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> </td>
</tr> </tr>

View File

@@ -74,6 +74,7 @@ export default class HardpointSlot extends Slot {
{ m.getDps() && m.getEps() ? <div className={'l'} onMouseOver={termtip.bind(null, 'dpe')} onMouseOut={tooltip.bind(null, null)}>{translate('DPE')}: {formats.f1(m.getDps() / m.getEps())}</div> : null } { m.getDps() && m.getEps() ? <div className={'l'} onMouseOver={termtip.bind(null, 'dpe')} onMouseOut={tooltip.bind(null, null)}>{translate('DPE')}: {formats.f1(m.getDps() / m.getEps())}</div> : null }
{ m.getRoF() ? <div className={'l'} onMouseOver={termtip.bind(null, 'rof')} onMouseOut={tooltip.bind(null, null)}>{translate('ROF')}: {formats.f1(m.getRoF())}{u.ps}</div> : null } { m.getRoF() ? <div className={'l'} onMouseOver={termtip.bind(null, 'rof')} onMouseOut={tooltip.bind(null, null)}>{translate('ROF')}: {formats.f1(m.getRoF())}{u.ps}</div> : null }
{ m.getRange() ? <div className={'l'}>{translate('range')} {formats.f1(m.getRange() / 1000)}{u.km}</div> : null } { m.getRange() ? <div className={'l'}>{translate('range')} {formats.f1(m.getRange() / 1000)}{u.km}</div> : null }
{ m.getFalloff() ? <div className={'l'}>{translate('falloff')} {formats.f1(m.getFalloff() / 1000)}{u.km}</div> : null }
{ m.getShieldBoost() ? <div className={'l'}>+{formats.pct1(m.getShieldBoost())}</div> : null } { m.getShieldBoost() ? <div className={'l'}>+{formats.pct1(m.getShieldBoost())}</div> : null }
{ m.getAmmo() ? <div className={'l'}>{translate('ammunition')}: {formats.int(m.getClip())}/{formats.int(m.getAmmo())}</div> : null } { m.getAmmo() ? <div className={'l'}>{translate('ammunition')}: {formats.int(m.getClip())}/{formats.int(m.getAmmo())}</div> : null }
{ m.getPiercing() ? <div className={'l'}>{translate('piercing')}: {formats.int(m.getPiercing())}</div> : null } { m.getPiercing() ? <div className={'l'}>{translate('piercing')}: {formats.int(m.getPiercing())}</div> : null }

View File

@@ -4,6 +4,7 @@ import SlotSection from './SlotSection';
import InternalSlot from './InternalSlot'; import InternalSlot from './InternalSlot';
import * as ModuleUtils from '../shipyard/ModuleUtils'; import * as ModuleUtils from '../shipyard/ModuleUtils';
import { stopCtxPropagation } from '../utils/UtilityFunctions'; import { stopCtxPropagation } from '../utils/UtilityFunctions';
import { canMount } from '../utils/SlotFunctions';
/** /**
* Internal slot section * Internal slot section
@@ -47,7 +48,7 @@ export default class InternalSlotSection extends SlotSection {
let clobber = event.getModifierState('Alt'); let clobber = event.getModifierState('Alt');
let ship = this.props.ship; let ship = this.props.ship;
ship.internal.forEach((slot) => { ship.internal.forEach((slot) => {
if ((clobber || !slot.m) && (!slot.eligible || slot.eligible.cr)) { if ((clobber || !slot.m) && canMount(ship, slot, 'cr')) {
ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E')); ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E'));
} }
}); });
@@ -63,7 +64,7 @@ export default class InternalSlotSection extends SlotSection {
let clobber = event.getModifierState('Alt'); let clobber = event.getModifierState('Alt');
let ship = this.props.ship; let ship = this.props.ship;
ship.internal.forEach((slot) => { ship.internal.forEach((slot) => {
if ((clobber || !slot.m) && (!slot.eligible || slot.eligible.ft)) { if ((clobber || !slot.m) && canMount(ship, slot, 'ft')) {
ship.use(slot, ModuleUtils.findInternal('ft', slot.maxClass, 'C')); ship.use(slot, ModuleUtils.findInternal('ft', slot.maxClass, 'C'));
} }
}); });
@@ -79,7 +80,7 @@ export default class InternalSlotSection extends SlotSection {
let clobber = event.getModifierState('Alt'); let clobber = event.getModifierState('Alt');
let ship = this.props.ship; let ship = this.props.ship;
ship.internal.forEach((slot) => { ship.internal.forEach((slot) => {
if ((clobber || !slot.m) && (!slot.eligible || slot.eligible.pcq)) { if ((clobber || !slot.m) && canMount(ship, slot, 'pcq')) {
ship.use(slot, ModuleUtils.findInternal('pcq', Math.min(slot.maxClass, 6), 'B')); // Passenger cabins top out at 6 ship.use(slot, ModuleUtils.findInternal('pcq', Math.min(slot.maxClass, 6), 'B')); // Passenger cabins top out at 6
} }
}); });
@@ -95,7 +96,7 @@ export default class InternalSlotSection extends SlotSection {
let clobber = event.getModifierState('Alt'); let clobber = event.getModifierState('Alt');
let ship = this.props.ship; let ship = this.props.ship;
ship.internal.forEach((slot) => { ship.internal.forEach((slot) => {
if ((clobber || !slot.m) && (!slot.eligible || slot.eligible.pcm)) { if ((clobber || !slot.m) && canMount(ship, slot, 'pcm')) {
ship.use(slot, ModuleUtils.findInternal('pcm', Math.min(slot.maxClass, 6), 'C')); // Passenger cabins top out at 6 ship.use(slot, ModuleUtils.findInternal('pcm', Math.min(slot.maxClass, 6), 'C')); // Passenger cabins top out at 6
} }
}); });
@@ -111,7 +112,7 @@ export default class InternalSlotSection extends SlotSection {
let clobber = event.getModifierState('Alt'); let clobber = event.getModifierState('Alt');
let ship = this.props.ship; let ship = this.props.ship;
ship.internal.forEach((slot) => { ship.internal.forEach((slot) => {
if ((clobber || !slot.m) && (!slot.eligible || slot.eligible.pci)) { if ((clobber || !slot.m) && canMount(ship, slot, 'pci')) {
ship.use(slot, ModuleUtils.findInternal('pci', Math.min(slot.maxClass, 6), 'D')); // Passenger cabins top out at 6 ship.use(slot, ModuleUtils.findInternal('pci', Math.min(slot.maxClass, 6), 'D')); // Passenger cabins top out at 6
} }
}); });
@@ -127,7 +128,7 @@ export default class InternalSlotSection extends SlotSection {
let clobber = event.getModifierState('Alt'); let clobber = event.getModifierState('Alt');
let ship = this.props.ship; let ship = this.props.ship;
ship.internal.forEach((slot) => { ship.internal.forEach((slot) => {
if ((clobber || !slot.m) && (!slot.eligible || slot.eligible.pce)) { if ((clobber || !slot.m) && canMount(ship, slot, 'pce')) {
ship.use(slot, ModuleUtils.findInternal('pce', Math.min(slot.maxClass, 6), 'E')); // Passenger cabins top out at 6 ship.use(slot, ModuleUtils.findInternal('pce', Math.min(slot.maxClass, 6), 'E')); // Passenger cabins top out at 6
} }
}); });
@@ -144,7 +145,7 @@ export default class InternalSlotSection extends SlotSection {
let ship = this.props.ship; let ship = this.props.ship;
let chargeCap = 0; // Capacity of single activation let chargeCap = 0; // Capacity of single activation
ship.internal.forEach(function(slot) { ship.internal.forEach(function(slot) {
if ((clobber || (!slot.m && !ModuleUtils.isShieldGenerator(slot.m.grp))) && (!slot.eligible || slot.eligible.scb)) { if ((clobber && !(slot.m && ModuleUtils.isShieldGenerator(slot.m.grp)) || !slot.m) && canMount(ship, slot, 'scb')) {
ship.use(slot, ModuleUtils.findInternal('scb', slot.maxClass, 'A')); ship.use(slot, ModuleUtils.findInternal('scb', slot.maxClass, 'A'));
ship.setSlotEnabled(slot, chargeCap <= ship.shieldStrength); // Don't waste cell capacity on overcharge ship.setSlotEnabled(slot, chargeCap <= ship.shieldStrength); // Don't waste cell capacity on overcharge
chargeCap += slot.m.recharge; chargeCap += slot.m.recharge;
@@ -162,7 +163,7 @@ export default class InternalSlotSection extends SlotSection {
let clobber = event.getModifierState('Alt'); let clobber = event.getModifierState('Alt');
let ship = this.props.ship; let ship = this.props.ship;
ship.internal.forEach((slot) => { ship.internal.forEach((slot) => {
if ((clobber || !slot.m) && (!slot.eligible || slot.eligible.hr)) { if ((clobber || !slot.m) && canMount(ship, slot, 'hr')) {
ship.use(slot, ModuleUtils.findInternal('hr', Math.min(slot.maxClass, 5), 'D')); // Hull reinforcements top out at 5D ship.use(slot, ModuleUtils.findInternal('hr', Math.min(slot.maxClass, 5), 'D')); // Hull reinforcements top out at 5D
} }
}); });
@@ -178,7 +179,7 @@ export default class InternalSlotSection extends SlotSection {
let clobber = event.getModifierState('Alt'); let clobber = event.getModifierState('Alt');
let ship = this.props.ship; let ship = this.props.ship;
ship.internal.forEach((slot) => { ship.internal.forEach((slot) => {
if ((clobber || !slot.m) && (!slot.eligible || slot.eligible.mrp)) { if ((clobber || !slot.m) && canMount(ship, slot, 'mrp')) {
ship.use(slot, ModuleUtils.findInternal('mrp', Math.min(slot.maxClass, 5), 'D')); // Module reinforcements top out at 5D ship.use(slot, ModuleUtils.findInternal('mrp', Math.min(slot.maxClass, 5), 'D')); // Module reinforcements top out at 5D
} }
}); });

View File

@@ -42,9 +42,9 @@ export default class Modification extends TranslatedComponent {
scaledValue = 100000; scaledValue = 100000;
value = 1000; value = 1000;
} }
if (scaledValue < -10000) { if (scaledValue < -9999) {
scaledValue = -10000; scaledValue = -9999;
value = -100; value = -99.99;
} }
let m = this.props.m; let m = this.props.m;

View File

@@ -39,7 +39,9 @@ export default class ModificationsMenu extends TranslatedComponent {
let list = []; let list = [];
for (let modName of Modifications.validity[m.grp]) { for (let modName of Modifications.validity[m.grp]) {
list.push(<Modification key={ modName } ship={ ship } m={ m } name={ modName } onChange={ onChange }/>); if (Modifications.modifications[modName].type != 'hidden') {
list.push(<Modification key={ modName } ship={ ship } m={ m } name={ modName } onChange={ onChange }/>);
}
} }
return { list }; return { list };

View File

@@ -28,6 +28,7 @@ export const terms = {
PHRASE_SG_RECOVER: 'Recovery (to 50%) after collapse', PHRASE_SG_RECOVER: 'Recovery (to 50%) after collapse',
PHRASE_UNLADEN: 'Ship mass excluding fuel and cargo', PHRASE_UNLADEN: 'Ship mass excluding fuel and cargo',
PHRASE_UPDATE_RDY: 'Update Available! Click to refresh', PHRASE_UPDATE_RDY: 'Update Available! Click to refresh',
PHRASE_ENGAGEMENT_RANGE: 'The distance between your ship and its target',
HELP_MODIFICATIONS_MENU: 'Click on a number to enter a new value, or drag along the bar for small changes', HELP_MODIFICATIONS_MENU: 'Click on a number to enter a new value, or drag along the bar for small changes',
@@ -123,6 +124,8 @@ export const terms = {
'yaw': 'Yaw', 'yaw': 'Yaw',
'internal protection': 'Internal protection', 'internal protection': 'Internal protection',
'external protection': 'External protection', 'external protection': 'External protection',
'engagement range': 'Engagement range',
'total': 'Total',
// Modifications // Modifications
ammo: 'Ammunition maximum', ammo: 'Ammunition maximum',

View File

@@ -286,6 +286,20 @@ export default class Module {
return this._getModifiedValue('range'); return this._getModifiedValue('range');
} }
/**
* Get the falloff for this module, taking in to account modifications
* @return {Number} the falloff of this module
*/
getFalloff() {
if (this.getModValue('fallofffromrange')) {
return this.getRange();
} else {
const falloff = this._getModifiedValue('falloff');
const range = this.getRange();
return (falloff > range ? range : falloff);
}
}
/** /**
* Get the range (in terms of seconds, for FSDI) for this module, taking in to account modifications * Get the range (in terms of seconds, for FSDI) for this module, taking in to account modifications
* @return {Number} the range of this module * @return {Number} the range of this module

View File

@@ -918,11 +918,10 @@ export default class Ship {
* @return {this} The ship instance (for chaining operations) * @return {this} The ship instance (for chaining operations)
*/ */
diminishingReturns(val, drll, drul) { diminishingReturns(val, drll, drul) {
if (val > drll) { if (val < drll) {
val = drll + (val - drll) / 2; val = drll;
} } else if (val < drul) {
if (val > drul) { val = drul - (drul - val) / 2;
val = drul;
} }
return val; return val;
} }
@@ -948,51 +947,27 @@ export default class Ship {
for (let slotNum in this.hardpoints) { for (let slotNum in this.hardpoints) {
const slot = this.hardpoints[slotNum]; const slot = this.hardpoints[slotNum];
if (slot.m && slot.enabled && slot.m.getDps()) { if (slot.m && slot.enabled && slot.m.getDps()) {
const dpe = slot.m.getDps() / slot.m.getEps(); const dpe = slot.m.getEps() === 0 ? 0 : slot.m.getDps() / slot.m.getEps();
const dps = slot.m.getDps(); const dps = slot.m.getDps();
const sdps = slot.m.getClip() ? (slot.m.getClip() * slot.m.getDps() / slot.m.getRoF()) / ((slot.m.getClip() / slot.m.getRoF()) + slot.m.getReload()) : dps; const sdps = slot.m.getClip() ? (slot.m.getClip() * slot.m.getDps() / slot.m.getRoF()) / ((slot.m.getClip() / slot.m.getRoF()) + slot.m.getReload()) : dps;
totalDpe += dpe; totalDpe += dpe;
totalDps += dps; totalDps += dps;
totalSDps += sdps; totalSDps += sdps;
if (slot.m.getDamageType() === 'E') { if (slot.m.getDamageType().indexOf('E') != -1) {
totalExplDpe += dpe; totalExplDpe += dpe / slot.m.getDamageType().length;
totalExplDps += dps; totalExplDps += dps / slot.m.getDamageType().length;
totalExplSDps += sdps; totalExplSDps += sdps / slot.m.getDamageType().length;
} }
if (slot.m.getDamageType() === 'K') { if (slot.m.getDamageType().indexOf('K') != -1) {
totalKinDpe += dpe; totalKinDpe += dpe / slot.m.getDamageType().length;
totalKinDps += dps; totalKinDps += dps / slot.m.getDamageType().length;
totalKinSDps += sdps; totalKinSDps += sdps / slot.m.getDamageType().length;
} }
if (slot.m.getDamageType() === 'T') { if (slot.m.getDamageType().indexOf('T') != -1) {
totalThermDpe += dpe; totalThermDpe += dpe / slot.m.getDamageType().length;
totalThermDps += dps; totalThermDps += dps / slot.m.getDamageType().length;
totalThermSDps += sdps; totalThermSDps += sdps / slot.m.getDamageType().length;
}
if (slot.m.getDamageType() === 'EK') {
totalExplDpe += dpe / 2;
totalKinDpe += dpe / 2;
totalExplDps += dps / 2;
totalKinDps += dps / 2;
totalExplSDps += sdps / 2;
totalKinSDps += sdps / 2;
}
if (slot.m.getDamageType() === 'ET') {
totalExplDpe += dpe / 2;
totalThermDpe += dpe / 2;
totalExplDps += dps / 2;
totalThermDps += dps / 2;
totalExplSDps += sdps / 2;
totalThermSDps += sdps / 2;
}
if (slot.m.getDamageType() === 'KT') {
totalKinDpe += dpe / 2;
totalThermDpe += dpe / 2;
totalKinDps += dps / 2;
totalThermDps += dps / 2;
totalKinSDps += sdps / 2;
totalThermSDps += sdps / 2;
} }
} }
} }
@@ -1143,14 +1118,26 @@ export default class Ship {
let shieldExplRes = null; let shieldExplRes = null;
let shieldKinRes = null; let shieldKinRes = null;
let shieldThermRes = null; let shieldThermRes = null;
let shieldExplDRStart = null;
let shieldExplDREnd = null;
let shieldKinDRStart = null;
let shieldKinDREnd = null;
let shieldThermDRStart = null;
let shieldThermDREnd = null;
const sgSlot = this.findInternalByGroup('sg'); const sgSlot = this.findInternalByGroup('sg');
if (sgSlot && sgSlot.enabled) { if (sgSlot && sgSlot.enabled) {
// Shield from generator // Shield from generator
shield = Calc.shieldStrength(this.hullMass, this.baseShieldStrength, sgSlot.m, 1); shield = Calc.shieldStrength(this.hullMass, this.baseShieldStrength, sgSlot.m, 1);
shieldExplRes = 1 - sgSlot.m.getExplosiveResistance(); shieldExplRes = 1 - sgSlot.m.getExplosiveResistance();
shieldExplDRStart = shieldExplRes * 0.7;
shieldExplDREnd = shieldExplRes * 0; // Currently don't know where this is
shieldKinRes = 1 - sgSlot.m.getKineticResistance(); shieldKinRes = 1 - sgSlot.m.getKineticResistance();
shieldKinDRStart = shieldKinRes * 0.7;
shieldKinDREnd = shieldKinRes * 0; // Currently don't know where this is
shieldThermRes = 1 - sgSlot.m.getThermalResistance(); shieldThermRes = 1 - sgSlot.m.getThermalResistance();
shieldThermDRStart = shieldThermRes * 0.7;
shieldThermDREnd = shieldThermRes * 0; // Currently don't know where this is
// Shield from boosters // Shield from boosters
for (let slot of this.hardpoints) { for (let slot of this.hardpoints) {
@@ -1170,9 +1157,9 @@ export default class Ship {
shield = shield * shieldBoost; shield = shield * shieldBoost;
this.shield = shield; this.shield = shield;
this.shieldExplRes = shieldExplRes ? 1 - this.diminishingReturns(1 - shieldExplRes, 0.5, 0.75) : null; this.shieldExplRes = shieldExplRes ? 1 - this.diminishingReturns(shieldExplRes, shieldExplDREnd, shieldExplDRStart) : null;
this.shieldKinRes = shieldKinRes ? 1 - this.diminishingReturns(1 - shieldKinRes, 0.5, 0.75) : null; this.shieldKinRes = shieldKinRes ? 1 - this.diminishingReturns(shieldKinRes, shieldKinDREnd, shieldKinDRStart) : null;
this.shieldThermRes = shieldThermRes ? 1 - this.diminishingReturns(1 - shieldThermRes, 0.5, 0.75) : null; this.shieldThermRes = shieldThermRes ? 1 - this.diminishingReturns(shieldThermRes, shieldThermDREnd, shieldThermDRStart) : null;
return this; return this;
} }
@@ -1206,8 +1193,14 @@ export default class Ship {
let modulearmour = 0; let modulearmour = 0;
let moduleprotection = 1; let moduleprotection = 1;
let hullExplRes = 1 - bulkhead.getExplosiveResistance(); let hullExplRes = 1 - bulkhead.getExplosiveResistance();
const hullExplResDRStart = hullExplRes * 0.7;
const hullExplResDREnd = hullExplRes * 0; // Currently don't know where this is
let hullKinRes = 1 - bulkhead.getKineticResistance(); let hullKinRes = 1 - bulkhead.getKineticResistance();
const hullKinResDRStart = hullKinRes * 0.7;
const hullKinResDREnd = hullKinRes * 0; // Currently don't know where this is
let hullThermRes = 1 - bulkhead.getThermalResistance(); let hullThermRes = 1 - bulkhead.getThermalResistance();
const hullThermResDRStart = hullThermRes * 0.7;
const hullThermResDREnd = hullThermRes * 0; // Currently don't know where this is
// Armour from HRPs and module armour from MRPs // Armour from HRPs and module armour from MRPs
for (let slot of this.internal) { for (let slot of this.internal) {
@@ -1230,9 +1223,9 @@ export default class Ship {
this.armour = armour; this.armour = armour;
this.modulearmour = modulearmour; this.modulearmour = modulearmour;
this.moduleprotection = moduleprotection; this.moduleprotection = moduleprotection;
this.hullExplRes = 1 - this.diminishingReturns(1 - hullExplRes, 0.5, 0.75); this.hullExplRes = 1 - this.diminishingReturns(hullExplRes, hullExplResDREnd, hullExplResDRStart);
this.hullKinRes = 1 - this.diminishingReturns(1 - hullKinRes, 0.5, 0.75); this.hullKinRes = 1 - this.diminishingReturns(hullKinRes, hullKinResDREnd, hullKinResDRStart);
this.hullThermRes = 1 - this.diminishingReturns(1 - hullThermRes, 0.5, 0.75); this.hullThermRes = 1 - this.diminishingReturns(hullThermRes, hullThermResDREnd, hullThermResDRStart);
return this; return this;
} }

View File

@@ -39,7 +39,9 @@ export function trader(ship, shielded, standardOpts) {
ship.use(slot, sg); ship.use(slot, sg);
sg = null; sg = null;
} else { } else {
ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E')); if (canMount(ship, slot, 'cr')) {
ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E'));
}
} }
} }

View File

@@ -305,6 +305,9 @@ function _addModifications(module, modifiers, blueprint, grade) {
} else if (modifiers.modifiers[i].name === 'mod_weapon_burst_rof') { } else if (modifiers.modifiers[i].name === 'mod_weapon_burst_rof') {
// For some reason this is a non-normalised percentage (i.e. 12.23% is 12.23 value rather than 0.1223 as everywhere else), so fix that here // For some reason this is a non-normalised percentage (i.e. 12.23% is 12.23 value rather than 0.1223 as everywhere else), so fix that here
module.setModValue('burstrof', modifiers.modifiers[i].value * 100); module.setModValue('burstrof', modifiers.modifiers[i].value * 100);
} else if (modifiers.modifiers[i].name === 'mod_weapon_falloffrange_from_range') {
// Obtain the falloff value directly from the range
module.setModValue('fallofffromrange', 1);
} else { } else {
// Look up the modifiers to find what we need to do // Look up the modifiers to find what we need to do
const modifierActions = Modifications.modifierActions[modifiers.modifiers[i].name]; const modifierActions = Modifications.modifierActions[modifiers.modifiers[i].name];

View File

@@ -1,6 +1,12 @@
import request from 'superagent'; import request from 'superagent';
/**
* Shorten a URL
* @param {string} url The URL to shorten
* @param {function} success Success callback
* @param {function} error Failure/Error callback
*/
export default function shorternUrl(url, success, error) { export default function shorternUrl(url, success, error) {
shortenUrlEddp(url, success, error); shortenUrlEddp(url, success, error);
} }
@@ -32,7 +38,7 @@ function shortenUrlGoogle(url, success, error) {
} }
} }
const SHORTEN_API_EDDP = 'http://eddp.co/u'; const SHORTEN_API_EDDP = 'https://eddp.co/u';
/** /**
* Shorten a URL using EDDP's URL shortener API * Shorten a URL using EDDP's URL shortener API
* @param {string} url The URL to shorten * @param {string} url The URL to shorten