mirror of
https://github.com/EDCD/coriolis.git
synced 2025-12-09 06:43:24 +00:00
Merge branch 'release/2.2.9'
This commit is contained in:
11
ChangeLog.md
11
ChangeLog.md
@@ -1,3 +1,14 @@
|
||||
#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:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "coriolis_shipyard",
|
||||
"version": "2.2.8",
|
||||
"version": "2.2.9",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/EDCD/coriolis"
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Ships } from 'coriolis-data/dist';
|
||||
import ShipSelector from './ShipSelector';
|
||||
import { nameComparator } from '../utils/SlotFunctions';
|
||||
import { CollapseSection, ExpandSection, MountFixed, MountGimballed, MountTurret } from './SvgIcons';
|
||||
import Slider from '../components/Slider';
|
||||
|
||||
/**
|
||||
* Generates an internationalization friendly weapon comparator that will
|
||||
@@ -66,7 +67,9 @@ export default class DamageDealt extends TranslatedComponent {
|
||||
predicate: 'n',
|
||||
desc: true,
|
||||
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
|
||||
*/
|
||||
componentWillMount() {
|
||||
const weapons = this._calcWeapons(this.props.ship, this.state.against);
|
||||
this.setState({ weapons });
|
||||
const data = this._calcWeapons(this.props.ship, this.state.against, this.state.range * this.state.maxRange);
|
||||
this.setState({ weapons: data.weapons, totals: data.totals });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,8 +89,8 @@ export default class DamageDealt extends TranslatedComponent {
|
||||
*/
|
||||
componentWillReceiveProps(nextProps, nextContext) {
|
||||
if (nextProps.code != this.props.code) {
|
||||
const weapons = this._calcWeapons(this.props.ship, this.state.against);
|
||||
this.setState({ weapons });
|
||||
const data = this._calcWeapons(this.props.ship, this.state.against, this.state.range * this.state.maxRange);
|
||||
this.setState({ weapons: data.weapons, totals: data.totals });
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -96,30 +99,61 @@ export default class DamageDealt extends TranslatedComponent {
|
||||
* Calculate the damage dealt by a ship
|
||||
* @param {Object} ship The ship which will deal the damage
|
||||
* @param {Object} against The ship against which damage will be dealt
|
||||
* @param {Object} range The engagement range
|
||||
* @return {boolean} Returns the per-weapon damage
|
||||
*/
|
||||
_calcWeapons(ship, against) {
|
||||
let weapons = [];
|
||||
_calcWeapons(ship, against, range) {
|
||||
// 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++) {
|
||||
if (ship.hardpoints[i].m) {
|
||||
const m = ship.hardpoints[i].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;
|
||||
if (m.getDamage() && m.grp !== 'po') {
|
||||
let dropoff = 1;
|
||||
if (m.getFalloff()) {
|
||||
// Calculate the dropoff % due to range
|
||||
if (range > m.getRange()) {
|
||||
// Weapon is out of range
|
||||
dropoff = 0;
|
||||
} else {
|
||||
const falloff = m.getFalloff();
|
||||
if (range > falloff) {
|
||||
const dropoffRange = m.getRange() - falloff;
|
||||
// Assuming straight-line falloff
|
||||
dropoff = 1 - (range - falloff) / dropoffRange;
|
||||
}
|
||||
}
|
||||
}
|
||||
const classRating = `${m.class}${m.rating}${m.missile ? '/' + m.missile : ''}`;
|
||||
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,
|
||||
mount: m.mount,
|
||||
name: m.name || m.grp,
|
||||
classRating,
|
||||
effectiveDps,
|
||||
effectiveSDps,
|
||||
effectiveness });
|
||||
weapons.push({ id: i,
|
||||
mount: m.mount,
|
||||
name: m.name || m.grp,
|
||||
classRating,
|
||||
effectiveDps,
|
||||
effectiveSDps,
|
||||
effectiveness });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return weapons;
|
||||
totals.effectiveness = totals.effectiveDps / totalDps;
|
||||
|
||||
return {weapons: weapons, totals: totals};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -135,8 +169,8 @@ export default class DamageDealt extends TranslatedComponent {
|
||||
*/
|
||||
_onShipChange(s) {
|
||||
const against = Ships[s];
|
||||
const weapons = this._calcWeapons(this.props.ship, against);
|
||||
this.setState({ against, weapons });
|
||||
const data = this._calcWeapons(this.props.ship, against);
|
||||
this.setState({ against, weapons: data.weapons, totals: data.totals });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -208,14 +242,23 @@ export default class DamageDealt extends TranslatedComponent {
|
||||
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
|
||||
* @return {React.Component} contents
|
||||
*/
|
||||
render() {
|
||||
const { language, tooltip, termtip } = this.context;
|
||||
const { formats, translate } = language;
|
||||
const { expanded } = this.state;
|
||||
const { language, onWindowResize, sizeRatio, tooltip, termtip } = this.context;
|
||||
const { formats, translate, units } = language;
|
||||
const { expanded, maxRange, range, totals } = this.state;
|
||||
|
||||
const sortOrder = this._sortOrder;
|
||||
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} />
|
||||
<table className='summary' style={{ width: '100%' }}>
|
||||
<thead>
|
||||
<tr className='main'>
|
||||
<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, 'esdps')}>{translate('effective sdps')}</td>
|
||||
<td className='sortable' onClick={sortOrder.bind(this, 'e')}>{translate('effectiveness')}</td>
|
||||
</tr>
|
||||
<tr className='main'>
|
||||
<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, 'esdps')}>{translate('effective sdps')}</td>
|
||||
<td className='sortable' onClick={sortOrder.bind(this, 'e')}>{translate('effectiveness')}</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{this._renderRows(translate, formats)}
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr className='main'>
|
||||
<td className='ri'><i>{translate('total')}</i></td>
|
||||
<td className='ri'><i>{formats.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 }
|
||||
</span>
|
||||
);
|
||||
|
||||
@@ -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.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.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.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 }
|
||||
|
||||
@@ -39,7 +39,9 @@ export default class ModificationsMenu extends TranslatedComponent {
|
||||
let list = [];
|
||||
|
||||
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 };
|
||||
|
||||
@@ -28,6 +28,7 @@ export const terms = {
|
||||
PHRASE_SG_RECOVER: 'Recovery (to 50%) after collapse',
|
||||
PHRASE_UNLADEN: 'Ship mass excluding fuel and cargo',
|
||||
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',
|
||||
|
||||
@@ -123,6 +124,8 @@ export const terms = {
|
||||
'yaw': 'Yaw',
|
||||
'internal protection': 'Internal protection',
|
||||
'external protection': 'External protection',
|
||||
'engagement range': 'Engagement range',
|
||||
'total': 'Total',
|
||||
|
||||
// Modifications
|
||||
ammo: 'Ammunition maximum',
|
||||
|
||||
@@ -286,6 +286,20 @@ export default class Module {
|
||||
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
|
||||
* @return {Number} the range of this module
|
||||
|
||||
@@ -947,51 +947,27 @@ export default class Ship {
|
||||
for (let slotNum in this.hardpoints) {
|
||||
const slot = this.hardpoints[slotNum];
|
||||
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 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;
|
||||
totalDps += dps;
|
||||
totalSDps += sdps;
|
||||
if (slot.m.getDamageType() === 'E') {
|
||||
totalExplDpe += dpe;
|
||||
totalExplDps += dps;
|
||||
totalExplSDps += sdps;
|
||||
if (slot.m.getDamageType().indexOf('E') != -1) {
|
||||
totalExplDpe += dpe / slot.m.getDamageType().length;
|
||||
totalExplDps += dps / slot.m.getDamageType().length;
|
||||
totalExplSDps += sdps / slot.m.getDamageType().length;
|
||||
}
|
||||
if (slot.m.getDamageType() === 'K') {
|
||||
totalKinDpe += dpe;
|
||||
totalKinDps += dps;
|
||||
totalKinSDps += sdps;
|
||||
if (slot.m.getDamageType().indexOf('K') != -1) {
|
||||
totalKinDpe += dpe / slot.m.getDamageType().length;
|
||||
totalKinDps += dps / slot.m.getDamageType().length;
|
||||
totalKinSDps += sdps / slot.m.getDamageType().length;
|
||||
}
|
||||
if (slot.m.getDamageType() === 'T') {
|
||||
totalThermDpe += dpe;
|
||||
totalThermDps += dps;
|
||||
totalThermSDps += sdps;
|
||||
}
|
||||
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;
|
||||
if (slot.m.getDamageType().indexOf('T') != -1) {
|
||||
totalThermDpe += dpe / slot.m.getDamageType().length;
|
||||
totalThermDps += dps / slot.m.getDamageType().length;
|
||||
totalThermSDps += sdps / slot.m.getDamageType().length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,6 +305,9 @@ function _addModifications(module, modifiers, blueprint, grade) {
|
||||
} 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
|
||||
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 {
|
||||
// Look up the modifiers to find what we need to do
|
||||
const modifierActions = Modifications.modifierActions[modifiers.modifiers[i].name];
|
||||
|
||||
@@ -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
|
||||
* @param {string} url The URL to shorten
|
||||
|
||||
Reference in New Issue
Block a user