diff --git a/ChangeLog.md b/ChangeLog.md
index 069f7fa3..777085b7 100644
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -16,4 +16,5 @@
* Enable boost display even if power distributor is disabled
* Calculate breakdown of ship offensive and defensive stats
* Add 'Offence summary' and 'Defence summary' components
- * Add ability to import directly from companion API output
+ * Add ability to import from companion API output through import feature
+ * Add ability to import from companion API output through URL
diff --git a/src/app/Coriolis.jsx b/src/app/Coriolis.jsx
index ad17d1d5..303f94d2 100644
--- a/src/app/Coriolis.jsx
+++ b/src/app/Coriolis.jsx
@@ -7,6 +7,8 @@ import Persist from './stores/Persist';
import Header from './components/Header';
import Tooltip from './components/Tooltip';
import ModalImport from './components/ModalImport';
+import * as CompanionApiUtils from './utils/CompanionApiUtils';
+import * as Utils from './utils/UtilityFunctions';
import AboutPage from './pages/AboutPage';
import NotFoundPage from './pages/NotFoundPage';
@@ -15,6 +17,8 @@ import ComparisonPage from './pages/ComparisonPage';
import ShipyardPage from './pages/ShipyardPage';
import ErrorDetails from './pages/ErrorDetails';
+const zlib = require('zlib');
+
/**
* Coriolis App
*/
@@ -52,6 +56,7 @@ export default class Coriolis extends React.Component {
this._onLanguageChange = this._onLanguageChange.bind(this);
this._onSizeRatioChange = this._onSizeRatioChange.bind(this);
this._keyDown = this._keyDown.bind(this);
+ this._importBuild = this._importBuild.bind(this);
this.emitter = new EventEmitter();
this.state = {
@@ -63,6 +68,7 @@ export default class Coriolis extends React.Component {
};
Router('', (r) => this._setPage(ShipyardPage, r));
+ Router('/import/:data', (r) => this._importBuild(r));
Router('/outfit/:ship/:code?', (r) => this._setPage(OutfittingPage, r));
Router('/compare/:name?', (r) => this._setPage(ComparisonPage, r));
Router('/comparison/:code', (r) => this._setPage(ComparisonPage, r));
@@ -70,6 +76,23 @@ export default class Coriolis extends React.Component {
Router('*', (r) => this._setPage(null, r));
}
+ /**
+ * Import a build directly
+ */
+ _importBuild(r) {
+ try {
+ // Need to decode and gunzip the data
+ const data = zlib.gunzipSync(new Buffer(Utils.fromUrlSafe(r.params.data), 'base64'))
+ const json = JSON.parse(data);
+ const ship = CompanionApiUtils.shipFromJson(json);
+ r.params.ship = ship.id;
+ r.params.code = ship.toString();
+ this._setPage(OutfittingPage, r);
+ } catch (err) {
+ this.setState({ error: err });
+ }
+ }
+
/**
* Updates / Sets the page and route context
* @param {[type]} page The page to be shown
diff --git a/src/app/components/HardpointSlot.jsx b/src/app/components/HardpointSlot.jsx
index a9f4155c..eaf5b0f5 100644
--- a/src/app/components/HardpointSlot.jsx
+++ b/src/app/components/HardpointSlot.jsx
@@ -64,7 +64,7 @@ export default class HardpointSlot extends Slot {
{ m.getHps() ?
{translate('HPS')}: {formats.round1(m.getHps())} { m.getClip() ? ({formats.round1((m.getClip() * m.getHps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload())) }) : null }
: null }
{ m.getDps() && m.getEps() ? {translate('DPE')}: {formats.f1(m.getDps() / m.getEps())}
: null }
{ m.getRoF() ? {translate('ROF')}: {formats.f1(m.getRoF())}{u.ps}
: null }
- { m.getRange() && !m.getDps() ? {translate('Range')} : {formats.round(m.getRange() / 1000)}{u.km}
: null }
+ { m.getRange() ? {translate('range')} {formats.f1(m.getRange() / 1000)}{u.km}
: null }
{ m.getShieldBoost() ? +{formats.pct1(m.getShieldBoost())}
: null }
{ m.getAmmo() ? {translate('ammunition')}: {formats.int(m.getClip())}/{formats.int(m.getAmmo())}
: null }
{ showModuleResistances && m.getExplosiveResistance() ? {translate('explres')}: {formats.pct(m.getExplosiveResistance())}
: null }
diff --git a/src/app/components/InternalSlot.jsx b/src/app/components/InternalSlot.jsx
index 9cde4e9c..f936b6be 100644
--- a/src/app/components/InternalSlot.jsx
+++ b/src/app/components/InternalSlot.jsx
@@ -40,7 +40,7 @@ export default class InternalSlot extends Slot {
{ m.rate ? {translate('rate')}: {m.rate}{u.kgs} {translate('refuel time')}: {formats.time(this.props.fuel * 1000 / m.rate)}
: null }
{ m.getAmmo() ? {translate('ammunition')}: {formats.gen(m.getAmmo())}
: null }
{ m.cells ? {translate('cells')}: {m.cells}
: null }
- { m.recharge ? {translate('recharge')}: {m.recharge} MJ {translate('total')}: {m.cells * m.recharge}{u.MJ}
: null }
+ { m.shieldreinforcement ? {translate('shieldreinforcement')}: {formats.int(m.getShieldReinforcement())} MJ {translate('total')}: {formats.int(m.cells * m.getShieldReinforcement())}{u.MJ}
: null }
{ m.repair ? {translate('repair')}: {m.repair}
: null }
{ m.getFacingLimit() ? {translate('facinglimit')} {formats.f1(m.getFacingLimit())}°
: null }
{ m.getRange() ? {translate('range')} {formats.f2(m.getRange())}{u.km}
: null }
diff --git a/src/app/shipyard/Serializer.js b/src/app/shipyard/Serializer.js
index 00719f00..58f849d5 100644
--- a/src/app/shipyard/Serializer.js
+++ b/src/app/shipyard/Serializer.js
@@ -2,6 +2,7 @@ import { ModuleGroupToName, MountMap, BulkheadNames } from './Constants';
import { Ships } from 'coriolis-data/dist';
import Ship from './Ship';
import * as ModuleUtils from './ModuleUtils';
+import * as Utils from '../utils/UtilityFunctions';
import LZString from 'lz-string';
const STANDARD = ['powerPlant', 'thrusters', 'frameShiftDrive', 'lifeSupport', 'powerDistributor', 'sensors', 'fuelTank'];
@@ -209,13 +210,13 @@ export function toDetailedExport(builds) {
* @return {string} Zipped Base 64 encoded JSON
*/
export function fromComparison(name, builds, facets, predicate, desc) {
- return LZString.compressToBase64(JSON.stringify({
+ return Utils.toUrlSafe(LZString.compressToBase64(JSON.stringify({
n: name,
b: builds.map((b) => { return { s: b.id, n: b.buildName, c: b.toString() }; }),
f: facets,
p: predicate,
d: desc ? 1 : 0
- })).replace(/\//g, '-');
+ })));
};
/**
@@ -224,5 +225,5 @@ export function fromComparison(name, builds, facets, predicate, desc) {
* @return {Object} Comparison data object
*/
export function toComparison(code) {
- return JSON.parse(LZString.decompressFromBase64(code.replace(/-/g, '/')));
+ return JSON.parse(LZString.decompressFromBase64(Utils.fromUrlSafe(code)));
};
diff --git a/src/app/shipyard/Ship.js b/src/app/shipyard/Ship.js
index ad5301a8..53145317 100755
--- a/src/app/shipyard/Ship.js
+++ b/src/app/shipyard/Ship.js
@@ -1,5 +1,6 @@
import * as Calc from './Calculations';
import * as ModuleUtils from './ModuleUtils';
+import * as Utils from '../utils/UtilityFunctions';
import Module from './Module';
import LZString from 'lz-string';
import isEqual from 'lodash/lang';
@@ -601,15 +602,15 @@ export default class Ship {
code = parts[0];
if (parts[1]) {
- enabled = LZString.decompressFromBase64(parts[1].replace(/-/g, '/')).split('');
+ enabled = Utils.fromUrlSafe(LZString.decompressFromBase64(parts[1])).split('');
}
if (parts[2]) {
- priorities = LZString.decompressFromBase64(parts[2].replace(/-/g, '/')).split('');
+ priorities = Utils.fromUrlSafe(LZString.decompressFromBase64(parts[2])).split('');
}
if (parts[3]) {
- const modstr = parts[3].replace(/-/g, '/');
+ const modstr = Utils.fromUrlSafe(parts[3]);
if (modstr.match(':')) {
this.decodeModificationsString(modstr, modifications);
} else {
@@ -1198,7 +1199,7 @@ export default class Ship {
priorities.push(slot.priority);
}
- this.serialized.priorities = LZString.compressToBase64(priorities.join('')).replace(/\//g, '-');
+ this.serialized.priorities = Utils.toUrlSafe(LZString.compressToBase64(priorities.join('')));
return this;
}
@@ -1219,7 +1220,7 @@ export default class Ship {
enabled.push(slot.enabled ? 1 : 0);
}
- this.serialized.enabled = LZString.compressToBase64(enabled.join('')).replace(/\//g, '-');
+ this.serialized.enabled = Utils.toUrlSafe(LZString.compressToBase64(enabled.join('')));
return this;
}
@@ -1265,7 +1266,7 @@ export default class Ship {
}
allMods.push(slotMods.join(';'));
}
- this.serialized.modifications = LZString.compressToBase64(allMods.join(',').replace(/,+$/, '')).replace(/\//g, '-');
+ this.serialized.modifications = Utils.toUrlSafe(LZString.compressToBase64(allMods.join(',').replace(/,+$/, '')));
return this;
}
@@ -1386,7 +1387,7 @@ export default class Ship {
buffer.writeInt8(-1, curpos++);
}
- this.serialized.modifications = zlib.gzipSync(buffer).toString('base64').replace(/\//g, '-');
+ this.serialized.modifications = Utils.toUrlSafe(zlib.gzipSync(buffer).toString('base64'));
} else {
this.serialized.modifications = null;
}
diff --git a/src/app/utils/CompanionApiUtils.js b/src/app/utils/CompanionApiUtils.js
index 7fed861f..8de85bf7 100644
--- a/src/app/utils/CompanionApiUtils.js
+++ b/src/app/utils/CompanionApiUtils.js
@@ -151,6 +151,7 @@ export function shipFromJson(json) {
throw 'Unknown bulkheads "' + armourJson.name + '"';
}
ship.bulkheads.enabled = true;
+ if (armourJson.modifiers) _addModifications(ship.bulkheads.m, armourJson.modifiers);
// Add the standard modules
// Power plant
@@ -159,7 +160,7 @@ export function shipFromJson(json) {
if (powerplantJson.modifiers) _addModifications(powerplant, powerplantJson.modifiers);
ship.use(ship.standard[0], powerplant, true);
ship.standard[0].enabled = powerplantJson.on === true;
- ship.standard[0].priority = powerplantJson.priority + 1;
+ ship.standard[0].priority = powerplantJson.priority;
// Thrusters
const thrustersJson = json.modules.MainEngines.module;
@@ -167,7 +168,7 @@ export function shipFromJson(json) {
if (thrustersJson.modifiers) _addModifications(thrusters, thrustersJson.modifiers);
ship.use(ship.standard[1], thrusters, true);
ship.standard[1].enabled = thrustersJson.on === true;
- ship.standard[1].priority = thrustersJson.priority + 1;
+ ship.standard[1].priority = thrustersJson.priority;
// FSD
const frameshiftdriveJson = json.modules.FrameShiftDrive.module;
@@ -175,7 +176,7 @@ export function shipFromJson(json) {
if (frameshiftdriveJson.modifiers) _addModifications(frameshiftdrive, frameshiftdriveJson.modifiers);
ship.use(ship.standard[2], frameshiftdrive, true);
ship.standard[2].enabled = frameshiftdriveJson.on === true;
- ship.standard[2].priority = frameshiftdriveJson.priority + 1;
+ ship.standard[2].priority = frameshiftdriveJson.priority;
// Life support
const lifesupportJson = json.modules.LifeSupport.module;
@@ -183,7 +184,7 @@ export function shipFromJson(json) {
if (lifesupportJson.modifiers)_addModifications(lifesupport, lifesupportJson.modifiers);
ship.use(ship.standard[3], lifesupport, true);
ship.standard[3].enabled = lifesupportJson.on === true;
- ship.standard[3].priority = lifesupportJson.priority + 1;
+ ship.standard[3].priority = lifesupportJson.priority;
// Power distributor
const powerdistributorJson = json.modules.PowerDistributor.module;
@@ -191,7 +192,7 @@ export function shipFromJson(json) {
if (powerdistributorJson.modifiers) _addModifications(powerdistributor, powerdistributorJson.modifiers);
ship.use(ship.standard[4], powerdistributor, true);
ship.standard[4].enabled = powerdistributorJson.on === true;
- ship.standard[4].priority = powerdistributorJson.priority + 1;
+ ship.standard[4].priority = powerdistributorJson.priority;
// Sensors
const sensorsJson = json.modules.Radar.module;
@@ -199,14 +200,14 @@ export function shipFromJson(json) {
if (sensorsJson.modifiers) _addModifications(sensors, sensorsJson.modifiers);
ship.use(ship.standard[5], sensors, true);
ship.standard[5].enabled = sensorsJson.on === true;
- ship.standard[5].priority = sensorsJson.priority + 1;
+ ship.standard[5].priority = sensorsJson.priority;
// Fuel tank
const fueltankJson = json.modules.FuelTank.module;
const fueltank = _moduleFromEdId(fueltankJson.id);
ship.use(ship.standard[6], fueltank, true);
ship.standard[6].enabled = true;
- ship.standard[6].priority = 1;
+ ship.standard[6].priority = 0;
// Add hardpoints
let hardpointClassNum = -1;
@@ -285,11 +286,11 @@ function _addModifications(module, modifiers) {
// Carry out the required changes
for (const action in modifierActions) {
const actionValue = modifierActions[action] * value;
- let mod = module.getModValue(action);
+ let mod = module.getModValue(action) / 10000;
if (!mod) {
mod = 0;
}
- module.setModValue(action, ((1 + mod / 10000) * (1 + actionValue) - 1) * 10000);
+ module.setModValue(action, ((1 + mod) * (1 + actionValue) - 1) * 10000);
}
}
@@ -299,20 +300,20 @@ function _addModifications(module, modifiers) {
// being a 4% boost they are a 104% multiplier. Unfortunately this means that our % modification
// is incorrect so we fix it
if (module.grp === 'sb' && module.getModValue('shieldboost')) {
- const alteredBoost = (1 + module.shieldboost) * (module.getModValue('shieldboost'));
- module.setModValue('shieldboost', alteredBoost / module.shieldboost);
+ const alteredBoost = (1 + module.shieldboost) * (module.getModValue('shieldboost') / 10000);
+ module.setModValue('shieldboost', alteredBoost * 10000 / module.shieldboost);
}
// Shield booster resistance is actually a damage modifier, so needs to be inverted.
if (module.grp === 'sb') {
if (module.getModValue('explres')) {
- module.setModValue('explres', module.getModValue('explres') * -1);
+ module.setModValue('explres', ((module.getModValue('explres') / 10000) * -1) * 10000);
}
if (module.getModValue('kinres')) {
- module.setModValue('kinres', module.getModValue('kinres') * -1);
+ module.setModValue('kinres', ((module.getModValue('kinres') / 10000) * -1) * 10000);
}
if (module.getModValue('thermres')) {
- module.setModValue('thermres', module.getModValue('thermres') * -1);
+ module.setModValue('thermres', ((module.getModValue('thermres') / 10000) * -1) * 10000);
}
}
@@ -320,17 +321,43 @@ function _addModifications(module, modifiers) {
// In addition, the modification is based off the inherent resistance of the module
if (module.isShieldGenerator()) {
if (module.getModValue('explres')) {
- module.setModValue('explres', (1 - (1 - module.explres) * (1 + module.getModValue('explres'))) - module.explres);
+ module.setModValue('explres', ((1 - (1 - module.explres) * (1 + module.getModValue('explres') / 10000)) - module.explres) * 10000);
}
if (module.getModValue('kinres')) {
- module.setModValue('kinres', (1 - (1 - module.kinres) * (1 + module.getModValue('kinres')))- module.kinres);
+ module.setModValue('kinres', ((1 - (1 - module.kinres) * (1 + module.getModValue('kinres') / 10000)) - module.kinres) * 10000);
}
if (module.getModValue('thermres')) {
- module.setModValue('thermres', (1 - (1 - module.thermres) * (1 + module.getModValue('thermres'))) - module.thermres);
+ module.setModValue('thermres', ((1 - (1 - module.thermres) * (1 + module.getModValue('thermres') / 10000)) - module.thermres) * 10000);
+ }
+ }
+
+ // Hull reinforcement package resistance is actually a damage modifier, so needs to be inverted.
+ if (module.grp === 'hr') {
+ if (module.getModValue('explres')) {
+ module.setModValue('explres', ((module.getModValue('explres') / 10000) * -1) * 10000);
+ }
+ if (module.getModValue('kinres')) {
+ module.setModValue('kinres', ((module.getModValue('kinres') / 10000) * -1) * 10000);
+ }
+ if (module.getModValue('thermres')) {
+ module.setModValue('thermres', ((module.getModValue('thermres') / 10000) * -1) * 10000);
+ }
+ }
+
+ // Bulkhead resistance is actually a damage modifier, so needs to be inverted.
+ // In addition, the modification is based off the inherent resistance of the module
+ if (module.grp == 'bh') {
+ if (module.getModValue('explres')) {
+ module.setModValue('explres', ((1 - (1 - module.explres) * (1 + module.getModValue('explres') / 10000)) - module.explres) * 10000);
+ }
+ if (module.getModValue('kinres')) {
+ module.setModValue('kinres', ((1 - (1 - module.kinres) * (1 + module.getModValue('kinres') / 10000)) - module.kinres) * 10000);
+ }
+ if (module.getModValue('thermres')) {
+ module.setModValue('thermres', ((1 - (1 - module.thermres) * (1 + module.getModValue('thermres') / 10000)) - module.thermres) * 10000);
}
}
- //TODO do this for armour resistances as well ?
// Jitter is in degrees not % so need to divide it by 100 to obtain the correct number
if (module.getModValue('jitter')) {
@@ -339,7 +366,6 @@ function _addModifications(module, modifiers) {
// FD uses interval between bursts internally, so we need to translate this to a real rate of fire
if (module.getModValue('rof')) {
- module.setModValue('rof', (1 / (1 + module.getModValue('jitter'))) - 1);
+ module.setModValue('rof', ((1 / (1 + module.getModValue('rof') / 10000)) - 1) * 10000);
}
-
}
diff --git a/src/app/utils/UtilityFunctions.js b/src/app/utils/UtilityFunctions.js
index fd51b434..7a389a59 100644
--- a/src/app/utils/UtilityFunctions.js
+++ b/src/app/utils/UtilityFunctions.js
@@ -58,3 +58,21 @@ export function shallowEqual(objA, objB) {
return true;
}
+
+/**
+ * Turn a base-64 encoded string in to a URL-safe version
+ * @param {string} data the string
+ * @return {string} the converted string
+ */
+export function toUrlSafe(data) {
+ return data.replace(/\\/g, '-').replace(/\+/g, '_');
+}
+
+/**
+ * Turn a URL-safe base-64 encoded string in to a normal version
+ * @param {string} data the string
+ * @return {string} the converted string
+ */
+export function fromUrlSafe(data) {
+ return data.replace(/-/g, '/').replace(/_/g, '+');
+}