Compare commits

...

25 Commits

Author SHA1 Message Date
Cmdr McDonald
99c0bfcee1 Merge branch 'hotfix/2.2.10b' 2017-01-23 14:17:50 +00:00
Cmdr McDonald
37bfc700e9 Fix for direct entering of modifications 2017-01-23 14:17:42 +00:00
Cmdr McDonald
fcf0494df6 Merge branch 'release/2.10' 2017-01-23 13:05:21 +00:00
Cmdr McDonald
20ba6eb822 Tidy-ups prior to release 2017-01-23 13:05:14 +00:00
Cmdr McDonald
0e2c0349e0 Merge branch 'feature/blueprints' into develop 2017-01-22 17:16:40 +00:00
Cmdr McDonald
c49e2cff03 Lint and test fixes 2017-01-22 17:12:48 +00:00
Cmdr McDonald
d79313bfbe Fix shield boost values; add tooltips 2017-01-22 14:15:33 +00:00
Cmdr McDonald
fd404b5155 Handle heavy duty shield booster special modification value 2017-01-22 10:59:36 +00:00
Cmdr McDonald
49e72146b4 Only update relevant modification parameters 2017-01-22 09:02:32 +00:00
Cmdr McDonald
9b534b62c8 Add modification functionality 2017-01-21 10:34:08 +00:00
Cmdr McDonald
0be59af9b0 Allow selection of blueprints type and grade 2017-01-20 17:53:49 +00:00
Cmdr McDonald
029ba63aa5 Use new-style modification information 2017-01-19 22:25:13 +00:00
Cmdr McDonald
99e9e0c76f Merge branch 'feature/fixes' into develop 2017-01-15 23:06:13 +00:00
Cmdr McDonald
5bbc6e1cbe Use damage distribution numbers 2017-01-15 17:10:46 +00:00
Cmdr McDonald
1e5f66e528 Fix detailed export of module reinforcement packages 2017-01-15 17:07:12 +00:00
Cmdr McDonald
cdb837a25a Merge branch 'release/2.2.9' into develop 2017-01-14 16:24:18 +00:00
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
20 changed files with 474 additions and 128 deletions

View File

@@ -1,3 +1,26 @@
#2.2.10
* Fix detailed export of module reinforcement packages
* Use damagedist for exact breakdown of weapons that have more than one type of damage
* Use new-style modification validity data
* Provide ability to select engineering blueprint and roll sample values for them
* Use coriolis-data 2.2.10:
* Fix incorrect base shield values for Cutter and Corvette
* Update weapons to have %-based damage distributions
* Remove power draw for detailed surface scanner - although shown in outfitting it is not part of active power
* Fix incorrect names for lightweight and kinetic armour
* Add engineering blueprints
#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 #2.2.8
* Fix issue where filling all internals with cargo racks would include restricted slots * Fix issue where filling all internals with cargo racks would include restricted slots
* Use coriolis-data 2.2.8: * Use coriolis-data 2.2.8:

View File

@@ -269,20 +269,20 @@
"topSpeed": 187.01, "topSpeed": 187.01,
"totalCost": 882362058, "totalCost": 882362058,
"totalDpe": 142.68, "totalDpe": 142.68,
"totalDps": 101.13, "totalDps": 103.8,
"totalEps": 22.71, "totalEps": 22.71,
"totalHps": 677.29, "totalHps": 677.29,
"totalExplDpe": 0, "totalExplDpe": 0,
"totalExplDps": 0, "totalExplDps": 0,
"totalExplSDps": 0, "totalExplSDps": 0,
"totalHps": 33.62, "totalHps": 33.62,
"totalKinDpe": 116.29, "totalKinDpe": 117.48,
"totalKinDps": 16.01, "totalKinDps": 24.94,
"totalKinSDps": 12.09, "totalKinSDps": 18.76,
"totalSDps": 89.99, "totalSDps": 91.84,
"totalThermDpe": 20.44, "totalThermDpe": 21.63,
"totalThermDps": 53.82, "totalThermDps": 60.08,
"totalThermSDps": 53.82, "totalThermSDps": 58.64,
"baseShieldStrength": 350, "baseShieldStrength": 350,
"baseArmour": 945, "baseArmour": 945,
"hullExplRes": 0.22, "hullExplRes": 0.22,

View File

@@ -238,7 +238,7 @@ describe('Import Modal', function() {
expect(modal.state.singleBuild).toBe(true); expect(modal.state.singleBuild).toBe(true);
clickProceed(); clickProceed();
expect(MockRouter.go.mock.calls.length).toBe(1); expect(MockRouter.go.mock.calls.length).toBe(1);
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/federal_corvette?code=A2putsFklndzsxf50x0x7l28281919040404040402020l06p05sf63c5ifr--v66g2f.AwRj4zNaqA%3D%3D.CwRgDBldUExuBiIlUA%3D%3D.H4sIAAAAAAAAA02SO0sDQRSFbxJ389jgJOsaN%2FGVmPXVKKRQC8FSA9oJWihWWgV%2FgIWFYBNb8RcIWiiIYiF2NqksIqaRoD%2FBQghB41zPFVa3OdzZ890zM3snpBeI6DsEyZgGkbpg5tg2lhzWGbEikN6aSVS0HSL3Ogxo6IvZmdbM9hFsjuickGgn%2B8SGv%2FvJ7DpxIqeCHjb0vJ80GrWIxu5RFmqARnYQEj%2FrMCdesFQzSOeYXvPP1BmGZPeiREa9xWyW0WifwnFX0MMJve4Hd5IQo4I9TcclGrxCUmoO34qVDaK%2BJuiJfD6%2FytZ%2Fj%2FGQAL7fD%2Fyiy8fbcNQdjsXJAFn9DRbyQchZIS9RqZJcGpckt4xjsdKL%2FtndJYjVQJkW8URUVYJTAegLat0IJOKJqCeB0gHoQ6BHgUQ8EdUNZvgghj3tAPmKa1vPQhIqTyp1KHE9AWgKV7Ka8NMinoiywLCrV%2F5Gvolo61ySpMn7xMdwsc3cf4w48w2Is40fwFld9oPzLbyL6CT88QaWoZpMcyDg32Jo0br4EaxDJXk8BT3o%2B7ktGS9B3GU8puS7zJh%2FAHGMT2qjAgAA&bn=Imported%20Federal%20Corvette'); expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/federal_corvette?code=A2putsFklndzsxf50x0x7l28281919040404040402020l06p05sf63c5ifr--v66g2f.AwRj4zNaqA%3D%3D.CwRgDBldUExuBiIlUA%3D%3D.H4sIAAAAAAAAA02Svy9DURTHT1vvtfoat32eekVV9fm1kBgwSIw0YWYgBqmpMZkMBomFVfwFEoZKhBjE1qWTgegiDX%2BCQdKI1j2%2BR%2FJ4yzfnvu%2FnfO%2B979yQXiCi7xAkbRpEqsLMsRKWHNZpsSKQnppJVLAdIvc6DGiwxexMaWb7GDZHdJ%2BQaCf71Ia%2F88XsOp1EThk9bOh5P2kkahGN3qPM1wANbyOk87zNHH%2FBUs0gnWN61T9TOwfJ7EWJjMcms1lEo30Gx11BD8f1mh%2FcTkCMMvY0HZcoe4Wk5By%2BFcrrRL0N0OOlrd0Ntv57jGoc%2BH4%2F8EqHj3%2FCUXc4FicC5NFvsJBVIWeFvESlpuXSuCS5RRyLlV70z%2B4uQaw6ypSIJ6KOJDgZgFpQ60YgEU9EPQmUCkAfAj0IJOKJqC4wuYMY9rQD5CuubT0LSag8qdShxHUHoElcyWrAT4l4IsoCw65e%2BRv5BqKtC0mSJu8LH8OFT%2Bb%2BE8SZb0CcEn4AZ3TRDx5q4l1EJ%2BCP1bEM1WSaAwH%2FFkOLPoofwTo0LY8nr7O%2B37cp4yWIu4zHlHiXGfMPmat5gqMCAAA%3D&bn=Imported%20Federal%20Corvette');
}); });
it('imports a valid v4 build', function() { it('imports a valid v4 build', function() {

View File

@@ -1,6 +1,6 @@
{ {
"name": "coriolis_shipyard", "name": "coriolis_shipyard",
"version": "2.2.8", "version": "2.2.10",
"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, 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

@@ -93,7 +93,7 @@ export default class DamageReceived extends TranslatedComponent {
let weapons = []; let weapons = [];
for (let grp in Modules.hardpoints) { for (let grp in Modules.hardpoints) {
if (Modules.hardpoints[grp][0].damage && Modules.hardpoints[grp][0].type) { if (Modules.hardpoints[grp][0].damage && Modules.hardpoints[grp][0].damagedist) {
for (let mId in Modules.hardpoints[grp]) { for (let mId in Modules.hardpoints[grp]) {
const m = new Module(Modules.hardpoints[grp][mId]); const m = new Module(Modules.hardpoints[grp][mId]);
const classRating = `${m.class}${m.rating}${m.missile ? '/' + m.missile : ''}`; const classRating = `${m.class}${m.rating}${m.missile ? '/' + m.missile : ''}`;
@@ -104,37 +104,35 @@ 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.getDamageDist().E) {
effectivenessShields += (1 - ship.shieldExplRes); effectivenessShields += m.getDamageDist().E * (1 - ship.shieldExplRes);
} }
if (m.getDamageType().indexOf('K') != -1) { if (m.getDamageDist().K) {
effectivenessShields += (1 - ship.shieldKinRes); effectivenessShields += m.getDamageDist().K * (1 - ship.shieldKinRes);
} }
if (m.getDamageType().indexOf('T') != -1) { if (m.getDamageDist().T) {
effectivenessShields += (1 - ship.shieldThermRes); effectivenessShields += m.getDamageDist().T * (1 - ship.shieldThermRes);
} }
if (m.getDamageType().indexOf('A') != -1) { if (m.getDamageDist().A) {
effectivenessShields += 1; effectivenessShields += m.getDamageDist().A;
} }
effectivenessShields /= m.getDamageType().length;
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.getDamageDist().E) {
effectivenessHull += (1 - ship.hullExplRes); effectivenessHull += m.getDamageDist().E * (1 - ship.hullExplRes);
} }
if (m.getDamageType().indexOf('K') != -1) { if (m.getDamageDist().K) {
effectivenessHull += (1 - ship.hullKinRes); effectivenessHull += m.getDamageDist().K * (1 - ship.hullKinRes);
} }
if (m.getDamageType().indexOf('T') != -1) { if (m.getDamageDist().T) {
effectivenessHull += (1 - ship.hullThermRes); effectivenessHull += m.getDamageDist().T * (1 - ship.hullThermRes);
} }
if (m.getDamageType().indexOf('A') != -1) { if (m.getDamageDist().A) {
effectivenessHull += 1; effectivenessHull += m.getDamageDist().A;
} }
effectivenessHull /= m.getDamageType().length;
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

@@ -41,7 +41,7 @@ export default class HardpointSlot extends Slot {
let classRating = `${m.class}${m.rating}${m.missile ? '/' + m.missile : ''}`; let classRating = `${m.class}${m.rating}${m.missile ? '/' + m.missile : ''}`;
let { drag, drop } = this.props; let { drag, drop } = this.props;
let { termtip, tooltip } = this.context; let { termtip, tooltip } = this.context;
let validMods = Modifications.validity[m.grp] || []; let validMods = Modifications.modules[m.grp].modifications || [];
let showModuleResistances = Persist.showModuleResistances(); let showModuleResistances = Persist.showModuleResistances();
// Modifications tooltip shows blueprint and grade, if available // Modifications tooltip shows blueprint and grade, if available
@@ -59,9 +59,9 @@ export default class HardpointSlot extends Slot {
{m.mount && m.mount == 'F' ? <span onMouseOver={termtip.bind(null, 'fixed')} onMouseOut={tooltip.bind(null, null)}><MountFixed /></span> : ''} {m.mount && m.mount == 'F' ? <span onMouseOver={termtip.bind(null, 'fixed')} onMouseOut={tooltip.bind(null, null)}><MountFixed /></span> : ''}
{m.mount && m.mount == 'G' ? <span onMouseOver={termtip.bind(null, 'gimballed')} onMouseOut={tooltip.bind(null, null)}><MountGimballed /></span> : ''} {m.mount && m.mount == 'G' ? <span onMouseOver={termtip.bind(null, 'gimballed')} onMouseOut={tooltip.bind(null, null)}><MountGimballed /></span> : ''}
{m.mount && m.mount == 'T' ? <span onMouseOver={termtip.bind(null, 'turreted')} onMouseOut={tooltip.bind(null, null)}><MountTurret /></span> : ''} {m.mount && m.mount == 'T' ? <span onMouseOver={termtip.bind(null, 'turreted')} onMouseOut={tooltip.bind(null, null)}><MountTurret /></span> : ''}
{m.getDamageType() && m.getDamageType().match('K') ? <span onMouseOver={termtip.bind(null, 'kinetic')} onMouseOut={tooltip.bind(null, null)}><DamageKinetic /></span> : ''} {m.getDamageDist() && m.getDamageDist().K ? <span onMouseOver={termtip.bind(null, 'kinetic')} onMouseOut={tooltip.bind(null, null)}><DamageKinetic /></span> : ''}
{m.getDamageType() && m.getDamageType().match('T') ? <span onMouseOver={termtip.bind(null, 'thermal')} onMouseOut={tooltip.bind(null, null)}><DamageThermal /></span> : ''} {m.getDamageDist() && m.getDamageDist().T ? <span onMouseOver={termtip.bind(null, 'thermal')} onMouseOut={tooltip.bind(null, null)}><DamageThermal /></span> : ''}
{m.getDamageType() && m.getDamageType().match('E') ? <span onMouseOver={termtip.bind(null, 'explosive')} onMouseOut={tooltip.bind(null, null)}><DamageExplosive /></span> : ''} {m.getDamageDist() && m.getDamageDist().E ? <span onMouseOver={termtip.bind(null, 'explosive')} onMouseOut={tooltip.bind(null, null)}><DamageExplosive /></span> : ''}
{classRating} {translate(m.name || m.grp)}{ m.mods && Object.keys(m.mods).length > 0 ? <span className='r' onMouseOver={termtip.bind(null, modTT)} onMouseOut={tooltip.bind(null, null)}><Modified /></span> : null } {classRating} {translate(m.name || m.grp)}{ m.mods && Object.keys(m.mods).length > 0 ? <span className='r' onMouseOver={termtip.bind(null, modTT)} onMouseOut={tooltip.bind(null, null)}><Modified /></span> : null }
</div> </div>
@@ -74,8 +74,10 @@ 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.getShotSpeed() ? <div className={'l'}>{translate('shotspeed')}: {formats.int(m.getShotSpeed())}{u.mps}</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 }
{ m.getJitter() ? <div className={'l'}>{translate('jitter')}: {formats.f2(m.getJitter())}°</div> : null } { m.getJitter() ? <div className={'l'}>{translate('jitter')}: {formats.f2(m.getJitter())}°</div> : null }
{ showModuleResistances && m.getExplosiveResistance() ? <div className='l'>{translate('explres')}: {formats.pct(m.getExplosiveResistance())}</div> : null } { showModuleResistances && m.getExplosiveResistance() ? <div className='l'>{translate('explres')}: {formats.pct(m.getExplosiveResistance())}</div> : null }

View File

@@ -23,7 +23,7 @@ export default class InternalSlot extends Slot {
let classRating = m.class + m.rating; let classRating = m.class + m.rating;
let { drag, drop, ship } = this.props; let { drag, drop, ship } = this.props;
let { termtip, tooltip } = this.context; let { termtip, tooltip } = this.context;
let validMods = Modifications.validity[m.grp] || []; let validMods = Modifications.modules[m.grp].modifications || [];
let showModuleResistances = Persist.showModuleResistances(); let showModuleResistances = Persist.showModuleResistances();
// Modifications tooltip shows blueprint and grade, if available // Modifications tooltip shows blueprint and grade, if available

View File

@@ -13,6 +13,7 @@ export default class Modification extends TranslatedComponent {
ship: React.PropTypes.object.isRequired, ship: React.PropTypes.object.isRequired,
m: React.PropTypes.object.isRequired, m: React.PropTypes.object.isRequired,
name: React.PropTypes.string.isRequired, name: React.PropTypes.string.isRequired,
value: React.PropTypes.number.isRequired,
onChange: React.PropTypes.func.isRequired onChange: React.PropTypes.func.isRequired
}; };
@@ -24,7 +25,7 @@ export default class Modification extends TranslatedComponent {
constructor(props, context) { constructor(props, context) {
super(props); super(props);
this.state = {}; this.state = {};
this.state.value = this.props.m.getModValue(this.props.name) / 100 || 0; this.state.value = props.value;
} }
/** /**
@@ -61,10 +62,10 @@ export default class Modification extends TranslatedComponent {
*/ */
render() { render() {
let translate = this.context.language.translate; let translate = this.context.language.translate;
let name = this.props.name; let { m, name } = this.props;
if (name === 'type') { if (name === 'damagedist') {
// We don't show type // We don't show damage distribution
return null; return null;
} }

View File

@@ -1,7 +1,8 @@
import React from 'react'; import React from 'react';
import * as _ from 'lodash';
import { findDOMNode } from 'react-dom'; import { findDOMNode } from 'react-dom';
import TranslatedComponent from './TranslatedComponent'; import TranslatedComponent from './TranslatedComponent';
import { stopCtxPropagation } from '../utils/UtilityFunctions'; import { isEmpty, stopCtxPropagation } from '../utils/UtilityFunctions';
import cn from 'classnames'; import cn from 'classnames';
import { MountFixed, MountGimballed, MountTurret } from './SvgIcons'; import { MountFixed, MountGimballed, MountTurret } from './SvgIcons';
import { Modifications } from 'coriolis-data/dist'; import { Modifications } from 'coriolis-data/dist';
@@ -26,23 +27,178 @@ export default class ModificationsMenu extends TranslatedComponent {
constructor(props, context) { constructor(props, context) {
super(props); super(props);
this.state = this._initState(props, context); this.state = this._initState(props, context);
this._toggleBlueprintsMenu = this._toggleBlueprintsMenu.bind(this);
this._rollWorst = this._rollWorst.bind(this);
this._rollRandom = this._rollRandom.bind(this);
this._rollBest = this._rollBest.bind(this);
this._reset = this._reset.bind(this);
} }
/** /**
* Initiate the list of modifications * Initialise state
* @param {Object} props React Component properties * @param {Object} props React Component properties
* @param {Object} context React Component context * @param {Object} context React Component context
* @return {Object} list: Array of React Components * @return {Object} list: Array of React Components
*/ */
_initState(props, context) { _initState(props, context) {
let { m, onChange, ship } = props; let { m, onChange, ship } = props;
let list = [];
for (let modName of Modifications.validity[m.grp]) { let blueprints = [];
list.push(<Modification key={ modName } ship={ ship } m={ m } name={ modName } onChange={ onChange }/>); for (const blueprintName in Modifications.modules[m.grp].blueprints) {
for (const grade of Modifications.modules[m.grp].blueprints[blueprintName]) {
const close = this._blueprintSelected.bind(this, Modifications.blueprints[blueprintName].id, grade);
const key = blueprintName + ':' + grade;
blueprints.push(<div style={{ cursor: 'pointer' }} key={ key } onClick={ close }>{Modifications.blueprints[blueprintName].name} grade {grade}</div>);
}
} }
return { list }; // Set up the modifications
const modifications = this._setModifications(props);
const blueprintMenuOpened = false;
// Set up the specials for this module
// const specials = _selectSpecials(m);
return { blueprintMenuOpened, blueprints, modifications };
}
/**
* Initialise the modifications
* @param {Object} props React Component properties
* @return {Object} list: Array of React Components
*/
_setModifications(props) {
const { m, onChange, ship } = props;
let modifications = [];
for (const modName of Modifications.modules[m.grp].modifications) {
if (Modifications.modifications[modName].type === 'percentage' || Modifications.modifications[modName].type === 'numeric') {
const key = modName + (m.getModValue(modName) / 100 || 0);
modifications.push(<Modification key={ key } ship={ ship } m={ m } name={ modName } value={ m.getModValue(modName) / 100 || 0 } onChange={ onChange }/>);
}
}
return modifications;
}
/**
* Toggle the blueprints menu
*/
_toggleBlueprintsMenu() {
const blueprintMenuOpened = !this.state.blueprintMenuOpened;
this.setState({ blueprintMenuOpened });
}
/**
* Activated when a blueprint is selected
* @param {int} blueprintId The ID of the selected blueprint
* @param {int} grade The grade of the selected blueprint
*/
_blueprintSelected(blueprintId, grade) {
const { m } = this.props;
const blueprint = Object.assign({}, _.find(Modifications.blueprints, function(o) { return o.id === blueprintId; }));
blueprint.grade = grade;
m.blueprint = blueprint;
const blueprintMenuOpened = false;
this.setState({ blueprintMenuOpened });
this.props.onChange();
}
/**
* Provide a 'worst' roll within the information we have
*/
_rollWorst() {
const { m, ship } = this.props;
const features = m.blueprint.features[m.blueprint.grade];
for (const featureName in features) {
if (Modifications.modifications[featureName].method == 'overwrite') {
ship.setModification(m, featureName, features[featureName][1]);
} else {
let value = features[featureName][0];
if (m.grp == 'sb' && featureName == 'shieldboost') {
// Shield boosters are a special case. Their boost is dependent on their base so we need to calculate the value here
value = ((1 + m.shieldboost) * (1 + value) - 1) / m.shieldboost - 1;
}
if (Modifications.modifications[featureName].type == 'percentage') {
ship.setModification(m, featureName, value * 10000);
} else if (Modifications.modifications[featureName].type == 'numeric') {
ship.setModification(m, featureName, value * 100);
}
}
}
this.setState({ modifications: this._setModifications(this.props) });
this.props.onChange();
}
/**
* Provide a random roll within the information we have
*/
_rollRandom() {
const { m, ship } = this.props;
const features = m.blueprint.features[m.blueprint.grade];
for (const featureName in features) {
if (Modifications.modifications[featureName].method == 'overwrite') {
ship.setModification(m, featureName, features[featureName][1]);
} else {
let value = features[featureName][0] + (Math.random() * (features[featureName][1] - features[featureName][0]));
if (m.grp == 'sb' && featureName == 'shieldboost') {
// Shield boosters are a special case. Their boost is dependent on their base so we need to calculate the value here
value = ((1 + m.shieldboost) * (1 + value) - 1) / m.shieldboost - 1;
}
if (Modifications.modifications[featureName].type == 'percentage') {
ship.setModification(m, featureName, value * 10000);
} else if (Modifications.modifications[featureName].type == 'numeric') {
ship.setModification(m, featureName, value * 100);
}
}
}
this.setState({ modifications: this._setModifications(this.props) });
this.props.onChange();
}
/**
* Provide a 'best' roll within the information we have
*/
_rollBest() {
const { m, ship } = this.props;
const features = m.blueprint.features[m.blueprint.grade];
for (const featureName in features) {
if (Modifications.modifications[featureName].method == 'overwrite') {
ship.setModification(m, featureName, features[featureName][1]);
} else {
let value = features[featureName][1];
if (m.grp == 'sb' && featureName == 'shieldboost') {
// Shield boosters are a special case. Their boost is dependent on their base so we need to calculate the value here
value = ((1 + m.shieldboost) * (1 + value) - 1) / m.shieldboost - 1;
}
if (Modifications.modifications[featureName].type == 'percentage') {
ship.setModification(m, featureName, value * 10000);
} else if (Modifications.modifications[featureName].type == 'numeric') {
ship.setModification(m, featureName, value * 100);
}
}
}
this.setState({ modifications: this._setModifications(this.props) });
this.props.onChange();
}
/**
* Reset modification information
*/
_reset() {
const { m, ship } = this.props;
ship.clearModifications(m);
ship.clearBlueprint(m);
this.setState({ modifications: this._setModifications(this.props) });
this.props.onChange();
} }
/** /**
@@ -50,15 +206,51 @@ export default class ModificationsMenu extends TranslatedComponent {
* @return {React.Component} List * @return {React.Component} List
*/ */
render() { render() {
let { tooltip, termtip } = this.context; const language = this.context.language;
const translate = language.translate;
const { tooltip, termtip } = this.context;
const { m } = this.props;
const { blueprintMenuOpened } = this.state;
const _toggleBlueprintsMenu = this._toggleBlueprintsMenu;
const _rollBest = this._rollBest;
const _rollWorst = this._rollWorst;
const _rollRandom = this._rollRandom;
const _reset = this._reset;
let blueprintLabel;
let haveBlueprint = false;
if (m.blueprint && !isEmpty(m.blueprint)) {
blueprintLabel = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
haveBlueprint = true;
} else {
blueprintLabel = translate('PHRASE_SELECT_BLUEPRINT');
}
return ( return (
<div <div
className={cn('select', this.props.className)} className={cn('select', this.props.className)}
onClick={(e) => e.stopPropagation() } onClick={(e) => e.stopPropagation() }
onContextMenu={stopCtxPropagation} onContextMenu={stopCtxPropagation}
onMouseOver={termtip.bind(null, 'HELP_MODIFICATIONS_MENU')} onMouseOut={tooltip.bind(null, null)}
> >
{this.state.list} <div className={ cn('section-menu', { selected: true })} style={{ cursor: 'pointer' }} onClick={_toggleBlueprintsMenu}>{blueprintLabel}</div>
{ blueprintMenuOpened ? this.state.blueprints : '' }
{ haveBlueprint ?
<table style={{ width: '100%', backgroundColor: 'transparent' }}>
<tbody>
<tr>
<td> { translate('roll') }: </td>
<td style={{ cursor: 'pointer' }} onClick={_rollWorst} onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_WORST')} onMouseOut={tooltip.bind(null, null)}> { translate('worst') } </td>
<td style={{ cursor: 'pointer' }} onClick={_rollRandom} onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_RANDOM')} onMouseOut={tooltip.bind(null, null)}> { translate('random') } </td>
<td style={{ cursor: 'pointer' }} onClick={_rollBest}onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_BEST')} onMouseOut={tooltip.bind(null, null)}> { translate('best') } </td>
<td style={{ cursor: 'pointer' }} onClick={_reset}onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_RESET')} onMouseOut={tooltip.bind(null, null)}> { translate('reset') } </td>
</tr>
</tbody>
</table> : '' }
{ blueprintMenuOpened ? '' :
<span onMouseOver={termtip.bind(null, 'HELP_MODIFICATIONS_MENU')} onMouseOut={tooltip.bind(null, null)} >
{ this.state.modifications }
</span> }
</div> </div>
); );
} }

View File

@@ -46,7 +46,7 @@ export default class StandardSlot extends TranslatedComponent {
let m = slot.m; let m = slot.m;
let classRating = m.class + m.rating; let classRating = m.class + m.rating;
let menu; let menu;
let validMods = m == null ? [] : (Modifications.validity[m.grp] || []); let validMods = m == null ? [] : (Modifications.modules[m.grp].modifications || []);
let showModuleResistances = Persist.showModuleResistances(); let showModuleResistances = Persist.showModuleResistances();
let mass = m.getMass() || m.cargo || m.fuel || 0; let mass = m.getMass() || m.cargo || m.fuel || 0;

View File

@@ -67,6 +67,7 @@ export function getLanguage(langCode) {
'm/s': <u> {translate('m/s')}</u>, // Meters per second 'm/s': <u> {translate('m/s')}</u>, // Meters per second
'°/s': <u> {translate('°/s')}</u>, // Degrees per second '°/s': <u> {translate('°/s')}</u>, // Degrees per second
MW: <u> {translate('MW')}</u>, // Mega Watts (same as Mega Joules per second) MW: <u> {translate('MW')}</u>, // Mega Watts (same as Mega Joules per second)
mps: <u>{translate('m/s')}</u>, // Metres per second
ps: <u>{translate('/s')}</u>, // per second ps: <u>{translate('/s')}</u>, // per second
pm: <u>{translate('/min')}</u>, // per minute pm: <u>{translate('/min')}</u>, // per minute
s: <u>{translate('secs')}</u>, // Seconds s: <u>{translate('secs')}</u>, // Seconds

View File

@@ -28,6 +28,12 @@ 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',
PHRASE_SELECT_BLUEPRINT: 'Click to select a blueprint',
PHRASE_BLUEPRINT_WORST: 'Worst primary values for this blueprint',
PHRASE_BLUEPRINT_RANDOM: 'Random selection between worst and best primary values for this blueprint',
PHRASE_BLUEPRINT_BEST: 'Best primary values for this blueprint',
PHRASE_BLUEPRINT_RESET: 'Remove all modifications and blueprint',
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',
@@ -101,6 +107,12 @@ export const terms = {
rebuildsperbay: 'Rebuilds per bay', rebuildsperbay: 'Rebuilds per bay',
// Blueprint rolls
worst: 'Worst',
random: 'Random',
best: 'Best',
reset: 'Reset',
// Weapon, offence, defence and movement // Weapon, offence, defence and movement
dpe: 'Damage per MJ of energy', dpe: 'Damage per MJ of energy',
dps: 'Damage per second', dps: 'Damage per second',
@@ -123,6 +135,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',
@@ -160,6 +174,7 @@ export const terms = {
shield: 'Shield', shield: 'Shield',
shieldboost: 'Shield boost', shieldboost: 'Shield boost',
shieldreinforcement: 'Shield reinforcement', shieldreinforcement: 'Shield reinforcement',
shotspeed: 'Shot speed',
spinup: 'Spin up time', spinup: 'Spin up time',
syscap: 'Systems capacity', syscap: 'Systems capacity',
sysrate: 'Systems recharge rate', sysrate: 'Systems recharge rate',

View File

@@ -33,6 +33,7 @@ export const ModuleGroupToName = {
fi: 'Frame Shift Drive Interdictor', fi: 'Frame Shift Drive Interdictor',
hb: 'Hatch Breaker Limpet Controller', hb: 'Hatch Breaker Limpet Controller',
hr: 'Hull Reinforcement Package', hr: 'Hull Reinforcement Package',
mrp: 'Module Reinforcement Package',
rf: 'Refinery', rf: 'Refinery',
scb: 'Shield Cell Bank', scb: 'Shield Cell Bank',
sg: 'Shield Generator', sg: 'Shield Generator',

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
@@ -636,10 +650,18 @@ export default class Module {
} }
/** /**
* Get the damage type for this module, taking in to account modifications * Get the damage distribution for this module, taking in to account modifications
* @return {string} the damage types for this module; any combination of E T and K * @return {string} the damage distribution for this module
*/ */
getDamageType() { getDamageDist() {
return this.getModValue('type') || this.type; return this.getModValue('damagedist') || this.damagedist;
}
/**
* Get the shot speed for this module, taking in to account modifications
* @return {string} the damage distribution for this module
*/
getShotSpeed() {
return this._getModifiedValue('shotspeed');
} }
} }

View File

@@ -411,6 +411,32 @@ export default class Ship {
return this; return this;
} }
/**
* Clear all modification values for a module
* @param {Number} m The module for which to clear the modifications
*/
clearModifications(m) {
m.mods = {};
this.updatePowerGenerated()
.updatePowerUsed()
.updateJumpStats()
.recalculateShield()
.recalculateShieldCells()
.recalculateArmour()
.recalculateDps()
.recalculateEps()
.recalculateHps()
.updateMovement();
}
/**
* Clear blueprint for a module
* @param {Number} m The module for which to clear the modifications
*/
clearBlueprint(m) {
m.blueprint = {};
}
/** /**
* Set a modification value * Set a modification value
* @param {Object} m The module to change * @param {Object} m The module to change
@@ -947,51 +973,29 @@ 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.getDamageDist()) {
totalExplDpe += dpe; if (slot.m.getDamageDist().E) {
totalExplDps += dps; totalExplDpe += dpe * slot.m.getDamageDist().E;
totalExplSDps += sdps; totalExplDps += dps * slot.m.getDamageDist().E;
} totalExplSDps += sdps * slot.m.getDamageDist().E;
if (slot.m.getDamageType() === 'K') { }
totalKinDpe += dpe; if (slot.m.getDamageDist().K) {
totalKinDps += dps; totalKinDpe += dpe * slot.m.getDamageDist().K;
totalKinSDps += sdps; totalKinDps += dps * slot.m.getDamageDist().K;
} totalKinSDps += sdps * slot.m.getDamageDist().K;
if (slot.m.getDamageType() === 'T') { }
totalThermDpe += dpe; if (slot.m.getDamageDist().T) {
totalThermDps += dps; totalThermDpe += dpe * slot.m.getDamageDist().T;
totalThermSDps += sdps; totalThermDps += dps * slot.m.getDamageDist().T;
} totalThermSDps += sdps * slot.m.getDamageDist().T;
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;
} }
} }
} }
@@ -1357,7 +1361,7 @@ export default class Ship {
if (this.bulkheads.m && this.bulkheads.m.mods) { if (this.bulkheads.m && this.bulkheads.m.mods) {
for (let modKey in this.bulkheads.m.mods) { for (let modKey in this.bulkheads.m.mods) {
// Filter out invalid modifications // Filter out invalid modifications
if (Modifications.validity['bh'] && Modifications.validity['bh'].indexOf(modKey) != -1) { if (Modifications.modules['bh'] && Modifications.modules['bh'].modifications.indexOf(modKey) != -1) {
bulkheadMods.push({ id: Modifications.modifications[modKey].id, value: this.bulkheads.m.getModValue(modKey) }); bulkheadMods.push({ id: Modifications.modifications[modKey].id, value: this.bulkheads.m.getModValue(modKey) });
} }
} }
@@ -1372,7 +1376,7 @@ export default class Ship {
if (slot.m && slot.m.mods) { if (slot.m && slot.m.mods) {
for (let modKey in slot.m.mods) { for (let modKey in slot.m.mods) {
// Filter out invalid modifications // Filter out invalid modifications
if (Modifications.validity[slot.m.grp] && Modifications.validity[slot.m.grp].indexOf(modKey) != -1) { if (Modifications.modules[slot.m.grp] && Modifications.modules[slot.m.grp].modifications.indexOf(modKey) != -1) {
slotMods.push({ id: Modifications.modifications[modKey].id, value: slot.m.getModValue(modKey) }); slotMods.push({ id: Modifications.modifications[modKey].id, value: slot.m.getModValue(modKey) });
} }
} }
@@ -1387,7 +1391,7 @@ export default class Ship {
if (slot.m && slot.m.mods) { if (slot.m && slot.m.mods) {
for (let modKey in slot.m.mods) { for (let modKey in slot.m.mods) {
// Filter out invalid modifications // Filter out invalid modifications
if (Modifications.validity[slot.m.grp] && Modifications.validity[slot.m.grp].indexOf(modKey) != -1) { if (Modifications.modules[slot.m.grp] && Modifications.modules[slot.m.grp].modifications.indexOf(modKey) != -1) {
slotMods.push({ id: Modifications.modifications[modKey].id, value: slot.m.getModValue(modKey) }); slotMods.push({ id: Modifications.modifications[modKey].id, value: slot.m.getModValue(modKey) });
} }
} }
@@ -1402,7 +1406,7 @@ export default class Ship {
if (slot.m && slot.m.mods) { if (slot.m && slot.m.mods) {
for (let modKey in slot.m.mods) { for (let modKey in slot.m.mods) {
// Filter out invalid modifications // Filter out invalid modifications
if (Modifications.validity[slot.m.grp] && Modifications.validity[slot.m.grp].indexOf(modKey) != -1) { if (Modifications.modules[slot.m.grp] && Modifications.modules[slot.m.grp].modifications.indexOf(modKey) != -1) {
slotMods.push({ id: Modifications.modifications[modKey].id, value: slot.m.getModValue(modKey) }); slotMods.push({ id: Modifications.modifications[modKey].id, value: slot.m.getModValue(modKey) });
} }
} }

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

@@ -38,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

View File

@@ -153,7 +153,7 @@ export function diffDetails(language, m, mm) {
let mPowerGeneration = m.pgen || 0; let mPowerGeneration = m.pgen || 0;
let mmPowerGeneration = mm ? mm.getPowerGeneration() : 0; let mmPowerGeneration = mm ? mm.getPowerGeneration() : 0;
if (mPowerGeneration != mmPowerGeneration) propDiffs.push(<div key='pgen'>{translate('pgen')}: <span className={diffClass(mPowerGeneration, mmPowerGeneration, true)}>{diff(formats.round, mPowerGeneration, mmPowerGeneration)}{units.MJ}</span></div>); if (mPowerGeneration != mmPowerGeneration) propDiffs.push(<div key='pgen'>{translate('pgen')}: <span className={diffClass(mPowerGeneration, mmPowerGeneration)}>{diff(formats.round, mPowerGeneration, mmPowerGeneration)}{units.MJ}</span></div>);
let mPowerUsage = m.power || 0; let mPowerUsage = m.power || 0;
let mmPowerUsage = mm ? mm.getPowerUsage() : 0; let mmPowerUsage = mm ? mm.getPowerUsage() : 0;

View File

@@ -71,3 +71,15 @@ export function shallowEqual(objA, objB) {
export function fromUrlSafe(data) { export function fromUrlSafe(data) {
return data ? data.replace(/-/g, '/').replace(/_/g, '+') : null; return data ? data.replace(/-/g, '/').replace(/_/g, '+') : null;
} }
/**
* Check if an object is empty
* @param {object} obj the object
* @return {bool} true if the object is empty, otherwise false
*/
export function isEmpty(obj) {
for (let key in obj) {
if (obj.hasOwnProperty(key)) return false;
}
return true;
};