Merge branch 'feature/burst' into develop

This commit is contained in:
Cmdr McDonald
2016-11-30 15:09:04 +00:00
11 changed files with 138 additions and 57 deletions

View File

@@ -1,3 +1,6 @@
#2.2.5
* Calculate rate of fire for multi-burst weapons
#2.2.4
* Add shortlink for outfitting page
* Use coriolis-data 2.2.4:

View File

@@ -271,7 +271,7 @@
"totalExplDpe": 0,
"totalExplDps": 0,
"totalExplSDps": 0,
"totalHps": 33.28,
"totalHps": 33.62,
"totalKinDpe": 103.97,
"totalKinDps": 28.92,
"totalKinSDps": 21.23,

View File

@@ -8,7 +8,7 @@ import Header from './components/Header';
import Tooltip from './components/Tooltip';
import ModalImport from './components/ModalImport';
import * as CompanionApiUtils from './utils/CompanionApiUtils';
import { outfitURL } from './utils/UrlGenerators'
import { outfitURL } from './utils/UrlGenerators';
import AboutPage from './pages/AboutPage';
import NotFoundPage from './pages/NotFoundPage';

View File

@@ -72,7 +72,7 @@ export default class ComparisonTable extends TranslatedComponent {
* @return {React.Component} Table row
*/
_buildRow(build, facets, formats, units) {
let url = outfitURL(build.id, build.toString(), build.buildName)
let url = outfitURL(build.id, build.toString(), build.buildName);
let cells = [
<td key='s' className='tl'><Link href={url}>{build.name}</Link></td>,
<td key='bn' className='tl'><Link href={url}>{build.buildName}</Link></td>

View File

@@ -48,22 +48,22 @@ export default class HardpointSlot extends Slot {
let modTT = translate('modified');
if (m && m.blueprint) {
modTT = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
if (m.blueprint.special && m.blueprint.special.id) {
if (m.blueprint.special && m.blueprint.special.id) {
modTT += ', ' + translate(m.blueprint.special.name);
}
}
}
return <div className='details' draggable='true' onDragStart={drag} onDragEnd={drop}>
<div className={'cb'}>
<div className={'l'}>
{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 == '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.getDamageType() && m.getDamageType().match('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.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 == '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.getDamageType() && m.getDamageType().match('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> : ''}
{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>
<div className={'r'}>{formats.round(m.getMass())}{u.T}</div>
</div>

View File

@@ -346,6 +346,7 @@ export default class ModalImport extends TranslatedComponent {
}
}
} catch (e) {
// console.log(e.stack);
this.setState({ errorMsg: (typeof e == 'string') ? e : 'Cannot Parse the data!' });
return;
}

View File

@@ -63,9 +63,24 @@ export default class Modification extends TranslatedComponent {
let translate = this.context.language.translate;
let name = this.props.name;
if (name === 'type') {
// We don't show type
return null;
}
let symbol;
if (name === 'jitter') {
symbol = '°';
} else if (name !== 'burst') {
symbol = '%';
}
if (symbol) {
symbol = ' (' + symbol + ')';
}
return (
<div className={'cb'} key={name}>
<div className={'cb'}>{translate(name)}{name === 'jitter' ? ' (°)' : ' (%)'}</div>
<div className={'cb'}>{translate(name)}{symbol}</div>
<NumberEditor className={'cb'} style={{ width: '90%', textAlign: 'center' }} step={0.01} stepModifier={1} decimals={2} value={this.state.value} onValueChange={this._updateValue.bind(this)} />
</div>
);

View File

@@ -1,5 +1,6 @@
import * as ModuleUtils from './ModuleUtils';
import * as _ from 'lodash';
import { Modifications } from 'coriolis-data/dist';
/**
* Module - active module in a ship's buildout
@@ -65,8 +66,20 @@ export default class Module {
_getModifiedValue(name, additive) {
let result = this[name] || (additive ? 0 : null); // Additive NULL === 0
if (result != null) {
// Jitter is special, being the only non-percentage value (it is in fact degrees)
const modValue = name === 'jitter' ? this.getModValue(name) / 100 : this.getModValue(name) / 10000;
const modification = Modifications.modifications[name];
if (!modification) {
return result;
}
// We store percentages as decimals, so to get them back we need to divide by 10000. Otherwise
// we divide by 100. Both ways we end up with a value with two decimal places
let modValue;
if (modification.type === 'percentage') {
modValue = this.getModValue(name) / 10000;
} else if (modification.type === 'numeric') {
modValue = this.getModValue(name) / 100;
} else {
modValue = this.getModValue(name);
}
if (modValue) {
if (additive) {
result = result + modValue;
@@ -74,7 +87,18 @@ export default class Module {
result = result * (1 + modValue);
}
}
} else {
if (name === 'burst') {
// Burst is special, as if it can not exist but have a modification
const modValue = this.getModValue(name) / 100;
return modValue;
} else if (name === 'burstrof') {
// Burst rate of fire is special, as if it can not exist but have a modification
const modValue = this.getModValue(name) / 100;
return modValue;
}
}
return result;
}
@@ -425,7 +449,7 @@ export default class Module {
getDps() {
// DPS is a synthetic value
let damage = this.getDamage();
let rpshot = this.getRoundsPerShot() || 1;
let rpshot = this.roundspershot || 1;
let rof = this.getRoF() || 1;
return damage * rpshot * rof;
@@ -438,7 +462,7 @@ export default class Module {
getEps() {
// EPS is a synthetic value
let distdraw = this.getDistDraw();
let rpshot = this.getRoundsPerShot() || 1;
let rpshot = this.roundspershot || 1;
let rof = this.getRoF() || 1;
return distdraw * rpshot * rof;
@@ -451,7 +475,7 @@ export default class Module {
getHps() {
// HPS is a synthetic value
let heat = this.getThermalLoad();
let rpshot = this.getRoundsPerShot() || 1;
let rpshot = this.roundspershot || 1;
let rof = this.getRoF() || 1;
return heat * rpshot * rof;
@@ -482,11 +506,35 @@ export default class Module {
}
/**
* Get the rate of fire for this module, taking in to account modifications
* Get the burst size for this module, taking in to account modifications
* @return {Number} the burst size of this module
*/
getBurst() {
return this._getModifiedValue('burst');
}
/**
* Get the burst rate of fire for this module, taking in to account modifications
* @return {Number} the burst rate of fire of this module
*/
getBurstRoF() {
return this._getModifiedValue('burstrof');
}
/**
* Get the rate of fire for this module, taking in to account modifications.
* The rate of fire is a combination value, and needs to take in to account
* bursts of fire.
* Firing goes [burst 1] [burst interval] [burst 2] [burst interval] ... [burst n] [interval]
* where 'n' is 'burst', 'burst interval' is '1/burstrof' and 'interval' is '1/rof'
* @return {Number} the rate of fire for this module
*/
getRoF() {
return this._getModifiedValue('rof');
const burst = this.getBurst() || 1;
const burstRoF = this.getBurstRoF() || 1;
const intRoF = this._getModifiedValue('rof');
return burst / (((burst - 1) / burstRoF) + 1 / intRoF);
}
/**

View File

@@ -8,7 +8,7 @@ import { outfitURL } from '../utils/UrlGenerators';
const STANDARD = ['powerPlant', 'thrusters', 'frameShiftDrive', 'lifeSupport', 'powerDistributor', 'sensors', 'fuelTank'];
const STANDARD_GROUPS = {'powerPlant': 'pp', 'thrusters': 't', 'frameShiftDrive': 'fsd', 'lifeSupport': 'ls', 'powerDistributor': 'pd', 'sensors': 's', 'fuelTank': 'ft'};
const STANDARD_GROUPS = { 'powerPlant': 'pp', 'thrusters': 't', 'frameShiftDrive': 'fsd', 'lifeSupport': 'ls', 'powerDistributor': 'pd', 'sensors': 's', 'fuelTank': 'ft' };
/**
* Generates ship-loadout JSON Schema standard object

View File

@@ -458,7 +458,7 @@ export default class Ship {
} else if (name === 'shieldreinforcement') {
m.setModValue(name, value);
this.recalculateShieldCells();
} else if (name === 'burst' || name === 'clip' || name === 'damage' || name === 'distdraw' || name === 'jitter' || name === 'piercing' || name === 'range' || name === 'reload' || name === 'rof' || name === 'thermload') {
} else if (name === 'burst' || name == 'burstrof' || name === 'clip' || name === 'damage' || name === 'distdraw' || name === 'jitter' || name === 'piercing' || name === 'range' || name === 'reload' || name === 'rof' || name === 'thermload') {
m.setModValue(name, value);
this.recalculateDps();
this.recalculateHps();
@@ -1260,7 +1260,7 @@ export default class Ship {
let modElements = mods[j].split(':');
if (modElements[0].match('[0-9]+')) {
const modification = _.find(Modifications.modifications, function(o) { return o.id === modElements[0]; });
if (modification != null) arr[i][modification.name] = Number(modElements[1]);
if (modification != null) arr[i][modification.name] = Number(modElements[1]);
} else {
arr[i][modElements[0]] = Number(modElements[1]);
}
@@ -1297,7 +1297,7 @@ export default class Ship {
bulkheadBlueprint = this.bulkheads.m.blueprint;
}
slots.push(bulkheadMods);
blueprints.push(bulkheadBlueprint)
blueprints.push(bulkheadBlueprint);
specials.push(bulkheadBlueprint ? bulkheadBlueprint.special : null);
for (let slot of this.standard) {
@@ -1356,7 +1356,7 @@ export default class Ship {
for (let special of specials) {
if (special) {
// Length is 5 for each special
bufsize += 5;
bufsize += 5;
}
}
@@ -1376,20 +1376,20 @@ export default class Ship {
buffer.writeInt8(MODIFICATION_ID_GRADE, curpos++);
buffer.writeInt32LE(blueprints[i].grade, curpos);
curpos += 4;
}
}
if (specials[i]) {
buffer.writeInt8(MODIFICATION_ID_SPECIAL, curpos++);
buffer.writeInt32LE(specials[i].id, curpos);
curpos += 4;
}
}
for (let slotMod of slot) {
buffer.writeInt8(slotMod.id, curpos++);
if (isNaN(slotMod.value)) {
// Need to write the string with exactly four characters, so pad with whitespace
buffer.write((" " + slotMod.value).slice(-4), curpos, 4);
} else {
buffer.writeInt32LE(slotMod.value, curpos);
}
if (isNaN(slotMod.value)) {
// Need to write the string with exactly four characters, so pad with whitespace
buffer.write((' ' + slotMod.value).slice(-4), curpos, 4);
} else {
buffer.writeInt32LE(slotMod.value, curpos);
}
// const modification = _.find(Modifications.modifications, function(o) { return o.id === slotMod.id; });
// console.log('ENCODE Slot ' + i + ': ' + modification.name + ' = ' + slotMod.value);
curpos += 4;
@@ -1414,7 +1414,7 @@ export default class Ship {
* See updateModificationsString() for details of the structure.
* @param {String} buffer Buffer holding modification info
* @param {Array} modArr Modification array
* @param {Array} bluprintArr Blueprint array
* @param {Array} blueprintArr Blueprint array
*/
decodeModificationsStruct(buffer, modArr, blueprintArr) {
let curpos = 0;
@@ -1428,22 +1428,22 @@ export default class Ship {
if (modificationId === 40) {
// Type is special, in that it's a character string
modificationValue = buffer.toString('utf8', curpos, curpos + 4).trim();
} else {
} else {
modificationValue = buffer.readInt32LE(curpos);
}
}
curpos += 4;
// There are a number of 'special' modification IDs, check for them here
if (modificationId === MODIFICATION_ID_BLUEPRINT) {
// There are a number of 'special' modification IDs, check for them here
if (modificationId === MODIFICATION_ID_BLUEPRINT) {
blueprint = Object.assign(blueprint, _.find(Modifications.blueprints, function(o) { return o.id === modificationValue; }));
} else if (modificationId === MODIFICATION_ID_GRADE) {
} else if (modificationId === MODIFICATION_ID_GRADE) {
blueprint.grade = modificationValue;
} else if (modificationId === MODIFICATION_ID_SPECIAL) {
} else if (modificationId === MODIFICATION_ID_SPECIAL) {
blueprint.special = _.find(Modifications.specials, function(o) { return o.id === modificationValue; });
} else {
} else {
const modification = _.find(Modifications.modifications, function(o) { return o.id === modificationId; });
// console.log('DECODE Slot ' + slot + ': ' + modification.name + ' = ' + modificationValue);
modifications[modification.name] = modificationValue;
}
}
modificationId = buffer.readInt8(curpos++);
}
modArr[slot] = modifications;

View File

@@ -277,29 +277,43 @@ export function shipFromJson(json) {
function _addModifications(module, modifiers, blueprint, grade) {
if (!modifiers || !modifiers.modifiers) return;
var special;
let special;
for (const i in modifiers.modifiers) {
// Look up the modifiers to find what we need to do
const modifierActions = Modifications.modifierActions[modifiers.modifiers[i].name];
const value = modifiers.modifiers[i].value;
// Some special modifications
if (modifiers.modifiers[i].name === 'mod_weapon_clip_size_override') {
// This is a numeric addition to the clip size, but we need to work it out in terms of being a percentage so
// that it works the same as other modifications
const origClip = module.clip || 1;
module.setModValue('clip', ((modifiers.modifiers[i].value - origClip) / origClip) * 10000);
} else if (modifiers.modifiers[i].name === 'mod_weapon_burst_size') {
// This is an absolute number that acts as an override
module.setModValue('burst', modifiers.modifiers[i].value * 100);
} 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 {
// Look up the modifiers to find what we need to do
const modifierActions = Modifications.modifierActions[modifiers.modifiers[i].name];
const value = modifiers.modifiers[i].value;
// Carry out the required changes
for (const action in modifierActions) {
if (isNaN(modifierActions[action])) {
module.setModValue(action, modifierActions[action]);
} else {
const actionValue = modifierActions[action] * value;
let mod = module.getModValue(action) / 10000;
if (!mod) {
mod = 0;
// Carry out the required changes
for (const action in modifierActions) {
if (isNaN(modifierActions[action])) {
module.setModValue(action, modifierActions[action]);
} else {
const actionValue = modifierActions[action] * value;
let mod = module.getModValue(action) / 10000;
if (!mod) {
mod = 0;
}
module.setModValue(action, ((1 + mod) * (1 + actionValue) - 1) * 10000);
}
module.setModValue(action, ((1 + mod) * (1 + actionValue) - 1) * 10000);
}
}
// Note the special if present
if (modifiers.modifiers[i].name && modifiers.modifiers[i].name.startsWith('special_')) {
special = Modifications.specials[modifiers.modifiers[i].name];
special = Modifications.specials[modifiers.modifiers[i].name];
}
}