From a65dae16317165e58dd295cec18523499c312956 Mon Sep 17 00:00:00 2001 From: Cmdr McDonald Date: Sun, 13 Nov 2016 16:42:59 +0000 Subject: [PATCH] Various fixes; allow direct import from URL --- ChangeLog.md | 3 +- src/app/Coriolis.jsx | 23 ++++++++++ src/app/components/HardpointSlot.jsx | 2 +- src/app/components/InternalSlot.jsx | 2 +- src/app/shipyard/Serializer.js | 7 +-- src/app/shipyard/Ship.js | 15 ++++--- src/app/utils/CompanionApiUtils.js | 66 +++++++++++++++++++--------- src/app/utils/UtilityFunctions.js | 18 ++++++++ 8 files changed, 103 insertions(+), 33 deletions(-) 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, '+'); +}