mirror of
https://github.com/EDCD/coriolis.git
synced 2025-12-09 22:55:35 +00:00
Merge branch 'feature/changes' into develop
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -4,4 +4,5 @@ build
|
|||||||
*.log
|
*.log
|
||||||
nginx.pid
|
nginx.pid
|
||||||
.idea
|
.idea
|
||||||
/bin
|
/bin
|
||||||
|
env
|
||||||
|
|||||||
28
ChangeLog.md
28
ChangeLog.md
@@ -1,3 +1,31 @@
|
|||||||
|
#2.2.6
|
||||||
|
* Add pitch/roll/yaw information
|
||||||
|
* Use combination of pitch, roll and yaw to provide a more useful agility metric
|
||||||
|
* Add movement summary to outfitting page
|
||||||
|
* Add standard internal class sizes to shipyard page
|
||||||
|
* Fix issue when importing Viper Mk IV
|
||||||
|
* Ensure ordering of all types of modules (standard, internal, utilities) is consistent
|
||||||
|
* Add rebuilds per bay information for fighter hangars
|
||||||
|
* Add ability to show military compartments
|
||||||
|
* Add diminishing returns for shield boosters
|
||||||
|
* Show module reinforcement package results in defence summary
|
||||||
|
* Use separate speed/rotation/acceleration multipliers for thrusters if available
|
||||||
|
* Obey restricted slot rules when adding all for internal slots
|
||||||
|
* Version URLs to handle changes to ship specifications over time
|
||||||
|
* Do not include disabled shield boosters in calculations
|
||||||
|
* Add 'Damage dealt' section
|
||||||
|
* Add 'Damage received' section
|
||||||
|
* Add 'Piercing' information to hardpoints
|
||||||
|
* Add 'Hardness' information to ship summary
|
||||||
|
* Add module copy functionality - drag module whilst holding 'alt' to copy
|
||||||
|
* Add base resistances to defence summary tooltip
|
||||||
|
* Update shield recovery/regeneration calculations
|
||||||
|
* Pin menu to top of page
|
||||||
|
* Switch to custom shortlink method to avoid google length limitations
|
||||||
|
* Ensure that information is not lost on narrow screens
|
||||||
|
* Do not lose ship selector selection on narrow screens
|
||||||
|
* Reinstate jump range graph
|
||||||
|
|
||||||
#2.2.5
|
#2.2.5
|
||||||
* Calculate rate of fire for multi-burst weapons
|
* Calculate rate of fire for multi-burst weapons
|
||||||
* Add note to disable ghostery in error situation
|
* Add note to disable ghostery in error situation
|
||||||
|
|||||||
30
__tests__/fixtures/agility-data.json
Normal file
30
__tests__/fixtures/agility-data.json
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"adder": {
|
||||||
|
"t3": {"speed": 205, "boost": 298, "pitch": 35.37, "roll": 93.09, "yaw": 13.03},
|
||||||
|
"t2": {"speed": 209, "boost": 304, "pitch": 36.06, "roll": 94.90, "yaw": 13.29},
|
||||||
|
"t1": {"speed": 213, "boost": 310, "pitch": 36.80, "roll": 96.84, "yaw": 13.56},
|
||||||
|
"t0": {"speed": 218, "boost": 317, "pitch": 37.70, "roll": 99.20, "yaw": 13.89},
|
||||||
|
"t9": {"speed": 220, "boost": 321, "pitch": 38.08, "roll": 100.21, "yaw": 14.03},
|
||||||
|
"t8": {"speed": 225, "boost": 327, "pitch": 38.86, "roll": 102.26, "yaw": 14.32},
|
||||||
|
"t7": {"speed": 230, "boost": 334, "pitch": 39.69, "roll": 104.44, "yaw": 14.62},
|
||||||
|
"t6": {"speed": 234, "boost": 340, "pitch": 40.41, "roll": 106.34, "yaw": 14.89},
|
||||||
|
"t5": {"speed": 242, "boost": 351, "pitch": 41.71, "roll": 109.78, "yaw": 15.37}
|
||||||
|
},
|
||||||
|
"eagle": {
|
||||||
|
"t2": {"speed": 223, "boost": 325, "pitch": 46.45, "roll": 111.48, "yaw": 16.72},
|
||||||
|
"t1": {"speed": 229, "boost": 334, "pitch": 47.69, "roll": 114.46, "yaw": 17.17},
|
||||||
|
"t0": {"speed": 235, "boost": 343, "pitch": 49.00, "roll": 117.60, "yaw": 17.64},
|
||||||
|
"t9": {"speed": 239, "boost": 349, "pitch": 49.80, "roll": 119.53, "yaw": 17.93},
|
||||||
|
"t8": {"speed": 243, "boost": 355, "pitch": 50.70, "roll": 121.69, "yaw": 18.25},
|
||||||
|
"t7": {"speed": 248, "boost": 361, "pitch": 51.62, "roll": 123.89, "yaw": 18.58},
|
||||||
|
"t6": {"speed": 252, "boost": 367, "pitch": 52.46, "roll": 125.91, "yaw": 18.89},
|
||||||
|
"t5": {"speed": 259, "boost": 378, "pitch": 53.99, "roll": 129.56, "yaw": 19.43}
|
||||||
|
},
|
||||||
|
"hauler": {
|
||||||
|
"t4": {"speed": 203, "boost": 305, "pitch": 36.61, "roll": 101.71, "yaw": 14.24},
|
||||||
|
"t3": {"speed": 209, "boost": 314, "pitch": 37.63, "roll": 104.54, "yaw": 14.64},
|
||||||
|
"t2": {"speed": 216, "boost": 324, "pitch": 38.89, "roll": 108.03, "yaw": 15.12},
|
||||||
|
"t1": {"speed": 222, "boost": 333, "pitch": 39.97, "roll": 111.02, "yaw": 15.54},
|
||||||
|
"t0": {"speed": 232, "boost": 348, "pitch": 41.76, "roll": 116.00, "yaw": 16.24}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -264,22 +264,21 @@
|
|||||||
"topBoost": 248.62,
|
"topBoost": 248.62,
|
||||||
"topSpeed": 186.46,
|
"topSpeed": 186.46,
|
||||||
"totalCost": 882362058,
|
"totalCost": 882362058,
|
||||||
"totalDpe": 127.26,
|
"totalDpe": 143.01,
|
||||||
"totalDps": 97.74,
|
"totalDps": 102.83,
|
||||||
"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": 103.97,
|
"totalKinDpe": 119.43,
|
||||||
"totalKinDps": 28.92,
|
"totalKinDps": 32.51,
|
||||||
"totalKinSDps": 21.23,
|
"totalKinSDps": 24.79,
|
||||||
"totalSDps": 85.77,
|
"totalSDps": 91.3,
|
||||||
"totalThermDpe": 23.29,
|
"totalThermDpe": 23.58,
|
||||||
"totalThermDps": 68.82,
|
"totalThermDps": 70.32,
|
||||||
"totalThermSDps": 64.53,
|
"totalThermSDps": 66.51,
|
||||||
"agility": 2,
|
|
||||||
"baseShieldStrength": 350,
|
"baseShieldStrength": 350,
|
||||||
"baseArmour": 945,
|
"baseArmour": 945,
|
||||||
"hullExplRes": 0.78,
|
"hullExplRes": 0.78,
|
||||||
@@ -288,6 +287,7 @@
|
|||||||
"hullThermRes": 1.37,
|
"hullThermRes": 1.37,
|
||||||
"masslock": 23,
|
"masslock": 23,
|
||||||
"pipSpeed": 0.14,
|
"pipSpeed": 0.14,
|
||||||
|
"pitch": 25,
|
||||||
"moduleCostMultiplier": 1,
|
"moduleCostMultiplier": 1,
|
||||||
"fuelCapacity": 32,
|
"fuelCapacity": 32,
|
||||||
"cargoCapacity": 128,
|
"cargoCapacity": 128,
|
||||||
@@ -297,8 +297,10 @@
|
|||||||
"unladenMass": 1179.2,
|
"unladenMass": 1179.2,
|
||||||
"powerAvailable": 39.6,
|
"powerAvailable": 39.6,
|
||||||
"powerRetracted": 23.33,
|
"powerRetracted": 23.33,
|
||||||
"powerDeployed": 34.76,
|
"powerDeployed": 34.13,
|
||||||
|
"roll": 60,
|
||||||
"unladenRange": 18.49,
|
"unladenRange": 18.49,
|
||||||
|
"yaw": 10,
|
||||||
"fullTankRange": 18.12,
|
"fullTankRange": 18.12,
|
||||||
"ladenRange": 16.39,
|
"ladenRange": 16.39,
|
||||||
"unladenFastestRange": 73.21,
|
"unladenFastestRange": 73.21,
|
||||||
|
|||||||
@@ -1,50 +1,50 @@
|
|||||||
{
|
{
|
||||||
"type_6_transporter": {
|
"type_6_transporter": {
|
||||||
"Cargo": "0p0tdFal8d8s8f4-----04040303430101.Iw1/kA==.Aw1/kA==.",
|
"Cargo": "A0p0tdFal8d8s8f4-----04040303430101.Iw1/kA==.Aw1/kA==.",
|
||||||
"Miner": "0p5tdFal8d8s8f42l2l---040403451q0101.Iw1/kA==.Aw1/kA==.",
|
"Miner": "A0p5tdFal8d8s8f42l2l---040403451q0101.Iw1/kA==.Aw1/kA==.",
|
||||||
"Hopper": "0p0tdFal8d0s8f41717---030302024300-.Iw1/kA==.Aw1/kA==."
|
"Hopper": "A0p0tdFal8d0s8f41717---030302024300-.Iw1/kA==.Aw1/kA==."
|
||||||
},
|
},
|
||||||
"type_7_transport": {
|
"type_7_transport": {
|
||||||
"Cargo": "0p0tiFfliddsdf5--------0505040403480101.Iw18aQ==.Aw18aQ==.",
|
"Cargo": "A0p0tiFfliddsdf5--------0505040403480101.Iw18aQ==.Aw18aQ==.",
|
||||||
"Miner": "0pdtiFflid8sdf5--2l2l----0505041v03450000.Iw18aQ==.Aw18aQ==."
|
"Miner": "A0pdtiFflid8sdf5--2l2l----0505041v03450000.Iw18aQ==.Aw18aQ==."
|
||||||
},
|
},
|
||||||
"federal_dropship": {
|
"federal_dropship": {
|
||||||
"Cargo": "0pdtiFflnddsif4-1717------05040448020201.Iw18aQ==.Aw18aQ==."
|
"Cargo": "A0pdtiFflnddsif4-1717------05040448--020201.Iw18eQ==.Aw18eQ==."
|
||||||
},
|
},
|
||||||
"asp": {
|
"asp": {
|
||||||
"Miner": "2pftfFflidfskf50s0s24242l2l---04054a1q02022o27.Iw18WQ==.Aw18WQ==."
|
"Miner": "A2pftfFflidfskf50s0s24242l2l---04054a1q02022o27.Iw18WQ==.Aw18WQ==."
|
||||||
},
|
},
|
||||||
"imperial_clipper": {
|
"imperial_clipper": {
|
||||||
"Cargo": "0p5tiFflndisnf4--0s0s----0605450302020101.Iw18aQ==.Aw18aQ==.",
|
"Cargo": "A0p5tiFflndisnf4--0s0s----0605450302020101.Iw18aQ==.Aw18aQ==.",
|
||||||
"Dream": "2pktkFflndpskf40v0v0s0s0404040n4k5n5d2b29292o-.Iw18aQ==.Aw18aQ==.",
|
"Dream": "A2pktkFflndpskf40v0v0s0s0404040n4k5n5d2b29292o-.AwRj4yWU1I==.CwBhCYy6YRigzLIA.",
|
||||||
"Current": "0patkFflndfskf4----------------.Iw18aQ==.Aw18aQ==."
|
"Current": "A0patkFflndfskf4----------------.AwRj4yWU1I==.CwBhCYy6YRigzLIA."
|
||||||
},
|
},
|
||||||
"type_9_heavy": {
|
"type_9_heavy": {
|
||||||
"Current": "0patsFklndnsif6---------0706054a0303020224.Iw18eQ==.Aw18eQ==."
|
"Current": "A0patsFklndnsif6---------0706054a0303020224.AwRj4yoo.EwBhEYy6dsg=."
|
||||||
},
|
},
|
||||||
"python": {
|
"python": {
|
||||||
"Cargo": "0patnFflidsssf5---------050505040448020201.Iw18eQ==.Aw18eQ==.",
|
"Cargo": "A0patnFflidsssf5---------050505040448020201.Iw18eQ==.Aw18eQ==.",
|
||||||
"Miner": "0pktkFflidpspf50v0v0v2m2m0404--050505Ce4a1v02022o.Iw18eQ==.Aw18eQ==.",
|
"Miner": "A0pktkFflidpspf50v0v0v2m2m0404--050505Ce4a1v02022o.Iw18eQ==.IwBhBYy6dkCYg===.",
|
||||||
"Dream": "2pptkFfliduspf50v0v0v27270404040m5n5n4f2d2d032t0201.Iw18eQ==.Aw18eQ==.",
|
"Dream": "A2pptkFfliduspf50v0v0v27270404040m5n5n4f2d2d032t0201.Iw1+gDBxA===.EwBhEYy6e0WEA===.",
|
||||||
"Missile": "0pttoFjljdystf52f2g2d2ePh----04044j03---002h.Iw18eQ==.Aw18eQ==."
|
"Missile": "A0pttoFjljdystf52f2g2d2ePh----04044j03---002h.Iw18eQ==.Aw18eQ==."
|
||||||
},
|
},
|
||||||
"anaconda": {
|
"anaconda": {
|
||||||
"Dream": "4putpFklndzsuf52c0o0o0o1m1m0q0q0404040l0b0100004k5n5n112d2d040303326b.Iw18ZlA=.Aw18ZlA=.",
|
"Dream": "A4putpFklndzsuf52c0o0o0o1m1m0q0q0404040l0b0100004k5n5n112d2d04-0303326b.AwRj4yo5dyg=.MwBhCYy6duvARiA=.",
|
||||||
"Cargo": "0patnFklndnsxf5----------------0605050504040445030301.Iw18ZlA=.Aw18ZlA=.",
|
"Cargo": "A0patnFklndnsxf5----------------06050505040404-45030301.Iw18ZVA=.Aw18ZVA=.",
|
||||||
"Current": "0patnFklndksxf5----------------0605050504040403034524.Iw18ZlA=.Aw18ZlA=.",
|
"Current": "A0patnFklndksxf5----------------06050505040404-03034524.Iw18ZVA=.Aw18ZVA=.",
|
||||||
"Explorer": "0patnFklndksxf5--------0202------f7050505040s372f2i4524.Iw18ZlA=.Aw18ZlA=.",
|
"Explorer": "A0patnFklndksxf5--------0202------f7050505040s37-2f2i4524.AwRj4yVKJ9hA.AwhMIyumQRhEA===.",
|
||||||
"Test": "4putkFklkdzsuf52c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.Iw18ZlA=.Aw18ZlA=."
|
"Test": "A4putkFklkdzsuf52c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04---0303326b.Iw18ZVA=.Aw18ZVA=."
|
||||||
},
|
},
|
||||||
"diamondback_explorer": {
|
"diamondback_explorer": {
|
||||||
"Explorer": "0p0tdFfldddsdf5---0202--320p432i2f.Iw1/kA==.Aw1/kA==."
|
"Explorer": "A0p0tdFfldddsdf5---0202--320p432i2f.AwRj4zTI.AwiMIypI."
|
||||||
},
|
},
|
||||||
"vulture": {
|
"vulture": {
|
||||||
"Bounty Hunter": "3patcFalddksff31e1e0404-0l4a5d27662j.Iw19kA==.Aw19kA==."
|
"Bounty Hunter": "A3patcFalddksff31e1e0404-0l4a-5d27662j.AwRj4z2I.MwBhBYy6oJmAjLIA."
|
||||||
},
|
},
|
||||||
"fer_de_lance": {
|
"fer_de_lance": {
|
||||||
"Attack": "2pfthFalidpsff31r0s0s0s0s000404-04-4a-5d27-.Iw18aQ==.Aw18aQ==."
|
"Attack": "A2pfthFalidpsff31r0s0s0s0s000404-04-4a-5d27-.Iw18aQ==.CwBhrSu8EZyA."
|
||||||
},
|
},
|
||||||
"eagle": {
|
"eagle": {
|
||||||
"Figther": "4p0t5F5l3d5s5f20p0p24-40532j-.Iw1/EA==.Aw1/EA==."
|
"Figther": "A4p0t5F5l3d5s5f20p0p24-4053-2j-.Iw18kA==.Aw18kA==."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
90
__tests__/test-agility.js
Normal file
90
__tests__/test-agility.js
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
import Ship from '../src/app/shipyard/Ship';
|
||||||
|
import { Ships } from 'coriolis-data/dist';
|
||||||
|
import * as ModuleUtils from '../src/app/shipyard/ModuleUtils';
|
||||||
|
|
||||||
|
describe("Agility", function() {
|
||||||
|
|
||||||
|
it("correctly calculates speed", function() {
|
||||||
|
let agilityData = require('./fixtures/agility-data');
|
||||||
|
|
||||||
|
for (let shipId in agilityData) {
|
||||||
|
for (let thrusterId in agilityData[shipId]) {
|
||||||
|
const thrusterData = agilityData[shipId][thrusterId];
|
||||||
|
let shipData = Ships[shipId];
|
||||||
|
let ship = new Ship(shipId, shipData.properties, shipData.slots);
|
||||||
|
ship.buildWith(shipData.defaults);
|
||||||
|
ship.use(ship.standard[1], ModuleUtils.findModule('t', thrusterId));
|
||||||
|
|
||||||
|
expect(Math.round(ship.topSpeed)).toBe(thrusterData.speed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("correctly calculates boost", function() {
|
||||||
|
let agilityData = require('./fixtures/agility-data');
|
||||||
|
|
||||||
|
for (let shipId in agilityData) {
|
||||||
|
for (let thrusterId in agilityData[shipId]) {
|
||||||
|
const thrusterData = agilityData[shipId][thrusterId];
|
||||||
|
let shipData = Ships[shipId];
|
||||||
|
let ship = new Ship(shipId, shipData.properties, shipData.slots);
|
||||||
|
ship.buildWith(shipData.defaults);
|
||||||
|
// Turn off internals to ensure we have enough power to boost
|
||||||
|
for (let internal in ship.internal) {
|
||||||
|
ship.internal[internal].enabled = 0;
|
||||||
|
}
|
||||||
|
ship.use(ship.standard[1], ModuleUtils.findModule('t', thrusterId));
|
||||||
|
|
||||||
|
expect(Math.round(ship.topBoost)).toBe(thrusterData.boost);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("correctly calculates pitch", function() {
|
||||||
|
let agilityData = require('./fixtures/agility-data');
|
||||||
|
|
||||||
|
for (let shipId in agilityData) {
|
||||||
|
for (let thrusterId in agilityData[shipId]) {
|
||||||
|
const thrusterData = agilityData[shipId][thrusterId];
|
||||||
|
let shipData = Ships[shipId];
|
||||||
|
let ship = new Ship(shipId, shipData.properties, shipData.slots);
|
||||||
|
ship.buildWith(shipData.defaults);
|
||||||
|
ship.use(ship.standard[1], ModuleUtils.findModule('t', thrusterId));
|
||||||
|
|
||||||
|
expect(Math.round(ship.pitches[4] * 100) / 100).toBeCloseTo(thrusterData.pitch, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("correctly calculates roll", function() {
|
||||||
|
let agilityData = require('./fixtures/agility-data');
|
||||||
|
|
||||||
|
for (let shipId in agilityData) {
|
||||||
|
for (let thrusterId in agilityData[shipId]) {
|
||||||
|
const thrusterData = agilityData[shipId][thrusterId];
|
||||||
|
let shipData = Ships[shipId];
|
||||||
|
let ship = new Ship(shipId, shipData.properties, shipData.slots);
|
||||||
|
ship.buildWith(shipData.defaults);
|
||||||
|
ship.use(ship.standard[1], ModuleUtils.findModule('t', thrusterId));
|
||||||
|
|
||||||
|
expect(Math.round(ship.rolls[4] * 100) / 100).toBeCloseTo(thrusterData.roll, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("correctly calculates yaw", function() {
|
||||||
|
let agilityData = require('./fixtures/agility-data');
|
||||||
|
|
||||||
|
for (let shipId in agilityData) {
|
||||||
|
for (let thrusterId in agilityData[shipId]) {
|
||||||
|
const thrusterData = agilityData[shipId][thrusterId];
|
||||||
|
let shipData = Ships[shipId];
|
||||||
|
let ship = new Ship(shipId, shipData.properties, shipData.slots);
|
||||||
|
ship.buildWith(shipData.defaults);
|
||||||
|
ship.use(ship.standard[1], ModuleUtils.findModule('t', thrusterId));
|
||||||
|
|
||||||
|
expect(Math.round(ship.yaws[4] * 100) / 100).toBeCloseTo(thrusterData.yaw, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -142,12 +142,12 @@ 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/anaconda?code=4putkFklkdzsuf52c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.AwRj4zNKqA%3D%3D.CwBhCYzBGW9qCTSqs5xA.&bn=Test%20My%20Ship');
|
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/anaconda?code=A4putkFklkdzsuf52c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04---0303326b.AwRj4zNLaA%3D%3D.CwBhCYzBGW9qCTSqq5xA.&bn=Test%20My%20Ship');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('catches an invalid build', function() {
|
it('catches an invalid build', function() {
|
||||||
const importData = require('./fixtures/anaconda-test-detailed-export-v3');
|
const importData = require('./fixtures/anaconda-test-detailed-export-v3');
|
||||||
pasteText(JSON.stringify(importData).replace('components', 'comps'));
|
pasteText(JSON.stringify(importData).replace('references', 'refs'));
|
||||||
|
|
||||||
expect(modal.state.importValid).toBeFalsy();
|
expect(modal.state.importValid).toBeFalsy();
|
||||||
expect(modal.state.errorMsg).toEqual('Anaconda Build "Test My Ship": Invalid data');
|
expect(modal.state.errorMsg).toEqual('Anaconda Build "Test My Ship": Invalid data');
|
||||||
@@ -167,7 +167,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/anaconda?code=4putkFklkdzsuf52c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.AwRj4zNKqA%3D%3D.CwBhCYzBGW9qCTSqs5xA.H4sIAAAAAAAAA2P8xwAEf0GE2AtmBob%2F%2FwFvM%2BjKEgAAAA%3D%3D&bn=Test%20My%20Ship');
|
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/anaconda?code=A4putkFklkdzsuf52c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04---0303326b.AwRj4zNLaA%3D%3D.CwBhCYzBGW9qCTSqq5xA.H4sIAAAAAAAAA2MUe8HMwPD%2FPwMcAABTINwTEgAAAA%3D%3D&bn=Test%20My%20Ship');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -184,7 +184,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/asp?code=0pftiFflfddsnf5------020202033c044002v62f2i.AwRj4yvI.CwRgDBldHnJA.H4sIAAAAAAAAA2P858DAwPCXEUhwHPvx%2F78YG5AltB7I%2F8%2F0TwImJboDSPJ%2F%2B%2Ff%2Fv%2FKlX%2F%2F%2Fi3AwMTBIfARK%2FGf%2BJwVSxArStVAYqOjvz%2F%2F%2FJVo5GRhE2IBc4SKQSSz%2FDGEmCa398P8%2F%2F2%2BgTf%2F%2FAwDFxwtofAAAAA%3D%3D&bn=Multi-purpose%20Asp%20Explorer');
|
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/asp?code=A0pftiFflfddsnf5------020202033c044002v62f2i.AwRj4yvI.CwRgDBldHnJA.H4sIAAAAAAAAA2P858DAwPCXEUhwHPvx%2F78YG5AltB7I%2F8%2F0TwImJboDSPJ%2F%2B%2Ff%2Fv%2FKlX%2F%2F%2Fi3AwMTBIfARK%2FGf%2BJwVSxArStVAYqOjvz%2F%2F%2FJVo5GRhE2IBc4SKQSSz%2FDGEmCa398P8%2F%2F2%2BgTf%2F%2FAwDFxwtofAAAAA%3D%3D&bn=Multi-purpose%20Asp%20Explorer');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('imports a valid v4 build with modifications', function() {
|
it('imports a valid v4 build with modifications', function() {
|
||||||
@@ -196,7 +196,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/imperial_courier?code=0patzF5l0das8f31a1a270202000e402t0101-2f.AwRj4zKA.CwRgDBldLiQ%3D.H4sIAAAAAAAAA12OP0tCYRjFj9fuVbvF1du9ekkT8s%2FkIg4NElyIBBd321yaGvwUQTS3N7UFfYygIT9EoyQUJA36ns47XJCWA%2B%2Fz%2Bz3Pe3ImBbDNKaqNPSBoGrL4ngfomKpFGiJ%2BLgHteR1IPjxJT5pF11uSeXNsJVcRfgdC92syWUuK0iMdKZqrjJ%2F0aoA71lJ5oKf38knWcCiptCPdhJIerdS00vlK0qktlqoj983UmqqHjQ33VsW8eazFmaTyULP2hQ4lX8LBme6g%2F6v0TTdbxJ2KhdEIaCw15MF%2FNB0L%2BS2hwEwyFM8KgP%2BqEpWWA3Qu9Z3z9kPWHzakt7Dt%2BAeD7ghSTgEAAA%3D%3D&bn=Multi-purpose%20Imperial%20Courier');
|
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/imperial_courier?code=A0patzF5l0das8f31a1a270202000e402t0101-2f.AwRj4zKA.CwRgDBldLiQ%3D.H4sIAAAAAAAAA12OP0tCYRjFj9fuVbvF1du9ekkT8s%2FkIg4NElyIBBd321yaGvwUQTS3N7UFfYygIT9EoyQUJA36ns47XJCWA%2B%2Fz%2Bz3Pe3ImBbDNKaqNPSBoGrL4ngfomKpFGiJ%2BLgHteR1IPjxJT5pF11uSeXNsJVcRfgdC92syWUuK0iMdKZqrjJ%2F0aoA71lJ5oKf38knWcCiptCPdhJIerdS00vlK0qktlqoj983UmqqHjQ33VsW8eazFmaTyULP2hQ4lX8LBme6g%2F6v0TTdbxJ2KhdEIaCw15MF%2FNB0L%2BS2hwEwyFM8KgP%2BqEpWWA3Qu9Z3z9kPWHzakt7Dt%2BAeD7ghSTgEAAA%3D%3D&bn=Multi-purpose%20Imperial%20Courier');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -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=2putsFklndzsxf50x0x7l28281919040404040402020l06p05sf63c5ifrv66g2f.AwRj4zNaKA%3D%3D.CwRgDBldUExuBiQqA%3D%3D%3D.H4sIAAAAAAAAA02Svy9DURTHT1vvtfoat30eXlvV0ufXQmLAIDHSRDcJAzHV1PgDDAaJpVbxF0gYKhFiEFuXTgbCIsKfYJCItHWP75E83vLNue%2F7Od977zs3pBeJ6DsE6TcNIlVn5lgFSw7rfrEikL6mSVS0HSL3MgxoqM3sTGtm%2BxA2R3RGSLSTfWzD32kxu043kVNFDxt6wU8ajVpEY7coh5uARrYR0n3aYY4%2FY6lmkc4xveafqZOHpHejRMb9J7NZQqN9Ascto4fjet0P7iQgRhV7mo5LlLtAUnIe34rVDaKBF9AThUJhla3%2FHqMRB76XBV7v8vEvOOoGx%2BJEgKz9BgvZEHJOyHNUakYujUuSW8KxWOkl%2F%2BzuMsR6QpkS8URUTYKTAagNta4EEvFE1INAqQD0IdCdQCKeiOoBk9%2BPYU87QL7i2tajkITKk0odSFxvAJrClawX%2BCkRT0RZYNjV5b%2BRbyLaOpMkafJa%2BBgufjFnjxBnvgFxKvgBnNYlP7jwiXcRnYQ%2F%2FoRlqCnTHAz41xha9F78CNahGXk8eZ3z%2FcyWjJcg7goeU%2BJdZsw%2FFW2pAaMCAAA%3D&bn=Imported%20Federal%20Corvette');
|
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/federal_corvette?code=A2putsFklndzsxf50x0x7l28281919040404040402020l06p05sf63c5ifr-----.AwRj4zNaqA%3D%3D.CwRgDBldUExuBjpA.H4sIAAAAAAAAA02SP0vDUBTFb1qTtE3xtTFqav0Tbfy3KHRQB8FRO7gJOuioU%2FEDODgILrqKn0DQQUEUB3Hr0smhYhcp%2BiEEEVvf9VwhmuVw3zu%2Fe959eTH0EhF9G5A%2ByyRSl8yc2saSE7pPrCSkt24RlVyPyL9JABpuM3uzmtk9hs1JPSAk2sk9deHvfjH7XprIq6KHTb0YJY3bDtHEA8rROqCxHYSkzzvMmRcs1RzSOaXXo5k6I5DCnk1kNj6YrQoa3TM4%2Fip6OKM3ouBOFmJWcabl%2BURD10jKLWCvVN0k6m%2BBngqCYI2d%2Fx6zlgG%2BXwR%2B2RXhn3DUPcbibIw8%2Bg0WsibkvJBXqFRZLo1Lkl%2FBWKz0cjS7vwJxmijzIqGIOpLgXAxqQ51bgURCEfUkUD4GvQv0KJBIKKK6wYwcpHCmGyNfcW3nWUhCFUqlDiWuJwbN4EpOC35eJBRRDhj29erfk28h2rmQJGkKv7CZKH0yF08QZ70B8bbxAbigK1Fw8IH%2Fwp6GP9nE0qjLaw7G%2FDs8mt0QP4m1UZafh38AuKZDe4MCAAA%3D&bn=Imported%20Federal%20Corvette');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('imports a valid v4 build', function() {
|
it('imports a valid v4 build', function() {
|
||||||
@@ -250,7 +250,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/beluga?code=0pktsFplCdpsnf70t0t2727270004040404043c4fmimlmm04mc0iv62i2f.AwRj4yukg%3D%3D%3D.CwRgDBldHi8IUA%3D%3D.H4sIAAAAAAAAA2P8Z8%2FAwPCXEUiIKTMxMPCv%2F%2Ff%2FP8cFIPGf6Z8YTEr0GjMDg%2FJWICERBOTzn%2Fn7%2F7%2FIO5Ai5n9SIEWsQEIoSxAolfbt%2F3%2BJPk4GBhE7YQYGYVmgcuVnf4Aq%2FwMAIrEcGGsAAAA%3D&bn=Imported%20Beluga%20Liner');
|
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/beluga?code=A0pktsFplCdpsnf70t0t2727270004040404043c4fmimlmm04mc0iv62i2f.AwRj4yukg%3D%3D%3D.CwRgDBldHi8IUA%3D%3D.H4sIAAAAAAAAA2P8Z8%2FAwPCXEUiIKTMxMPCv%2F%2Ff%2FP8cFIPGf6Z8YTEr0GjMDg%2FJWICERBOTzn%2Fn7%2F7%2FIO5Ai5n9SIEWsQEIoSxAolfbt%2F3%2BJPk4GBhE7YQYGYVmgcuVnf4Aq%2FwMAIrEcGGsAAAA%3D&bn=Imported%20Beluga%20Liner');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "coriolis_shipyard",
|
"name": "coriolis_shipyard",
|
||||||
"version": "2.2.5",
|
"version": "2.2.6b",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/EDCD/coriolis"
|
"url": "https://github.com/EDCD/coriolis"
|
||||||
@@ -31,6 +31,7 @@
|
|||||||
"jsx"
|
"jsx"
|
||||||
],
|
],
|
||||||
"automock": true,
|
"automock": true,
|
||||||
|
"bail": false,
|
||||||
"unmockedModulePathPatterns": [
|
"unmockedModulePathPatterns": [
|
||||||
"<rootDir>/node_modules/lodash",
|
"<rootDir>/node_modules/lodash",
|
||||||
"<rootDir>/node_modules/react",
|
"<rootDir>/node_modules/react",
|
||||||
|
|||||||
@@ -93,8 +93,10 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
|||||||
let prevClass = null, prevRating = null;
|
let prevClass = null, prevRating = null;
|
||||||
let elems = [];
|
let elems = [];
|
||||||
|
|
||||||
for (let i = 0; i < modules.length; i++) {
|
const sortedModules = modules.sort(this._moduleOrder);
|
||||||
let m = modules[i];
|
|
||||||
|
for (let i = 0; i < sortedModules.length; i++) {
|
||||||
|
let m = sortedModules[i];
|
||||||
let mount = null;
|
let mount = null;
|
||||||
let disabled = m.maxmass && (mass + (m.mass ? m.mass : 0)) > m.maxmass;
|
let disabled = m.maxmass && (mass + (m.mass ? m.mass : 0)) > m.maxmass;
|
||||||
let active = mountedModule && mountedModule.id === m.id;
|
let active = mountedModule && mountedModule.id === m.id;
|
||||||
@@ -126,7 +128,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
|||||||
case 'T': mount = <MountTurret className={'lg'}/>; break;
|
case 'T': mount = <MountTurret className={'lg'}/>; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i > 0 && modules.length > 3 && m.class != prevClass && (m.rating != prevRating || m.mount) && m.grp != 'pa') {
|
if (i > 0 && sortedModules.length > 3 && m.class != prevClass && (m.rating != prevRating || m.mount) && m.grp != 'pa') {
|
||||||
elems.push(<br key={'b' + m.grp + i} />);
|
elems.push(<br key={'b' + m.grp + i} />);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,6 +203,46 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
|||||||
this.context.tooltip();
|
this.context.tooltip();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Order two modules suitably for display in module selection
|
||||||
|
* @param {Object} a the first module
|
||||||
|
* @param {Object} b the second module
|
||||||
|
* @return {int} -1 if the first module should go first, 1 if the second module should go first
|
||||||
|
*/
|
||||||
|
_moduleOrder(a, b) {
|
||||||
|
// Named modules go last
|
||||||
|
if (!a.name && b.name) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (a.name && !b.name) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
// Class ordered from highest (8) to lowest (1)
|
||||||
|
if (a.class < b.class) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (a.class > b.class) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// Mount type, if applicable
|
||||||
|
if (a.mount && b.mount && a.mount !== b.mount) {
|
||||||
|
if (a.mount === 'F' || (a.mount === 'G' && b.mount === 'T')) {
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Rating ordered from lowest (E) to highest (A)
|
||||||
|
if (a.rating < b.rating) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (a.rating > b.rating) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// Do not attempt to order by name at this point, as that mucks up the order of armour
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scroll to mounted (if it exists) module group on mount
|
* Scroll to mounted (if it exists) module group on mount
|
||||||
*/
|
*/
|
||||||
|
|||||||
244
src/app/components/DamageDealt.jsx
Normal file
244
src/app/components/DamageDealt.jsx
Normal file
@@ -0,0 +1,244 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
|
import { Ships } from 'coriolis-data/dist';
|
||||||
|
import ShipSelector from './ShipSelector';
|
||||||
|
import { nameComparator } from '../utils/SlotFunctions';
|
||||||
|
import { CollapseSection, ExpandSection, MountFixed, MountGimballed, MountTurret } from './SvgIcons';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates an internationalization friendly weapon comparator that will
|
||||||
|
* sort by specified property (if provided) then by name/group, class, rating
|
||||||
|
* @param {function} translate Translation function
|
||||||
|
* @param {function} propComparator Optional property comparator
|
||||||
|
* @param {boolean} desc Use descending order
|
||||||
|
* @return {function} Comparator function for names
|
||||||
|
*/
|
||||||
|
export function weaponComparator(translate, propComparator, desc) {
|
||||||
|
return (a, b) => {
|
||||||
|
if (!desc) { // Flip A and B if ascending order
|
||||||
|
let t = a;
|
||||||
|
a = b;
|
||||||
|
b = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a property comparator is provided use it first
|
||||||
|
let diff = propComparator ? propComparator(a, b) : nameComparator(translate, a, b);
|
||||||
|
|
||||||
|
if (diff) {
|
||||||
|
return diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Property matches so sort by name / group, then class, rating
|
||||||
|
if (a.name === b.name && a.grp === b.grp) {
|
||||||
|
if(a.class == b.class) {
|
||||||
|
return a.rating > b.rating ? 1 : -1;
|
||||||
|
}
|
||||||
|
return a.class - b.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nameComparator(translate, a, b);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Damage against a selected ship
|
||||||
|
*/
|
||||||
|
export default class DamageDealt extends TranslatedComponent {
|
||||||
|
static PropTypes = {
|
||||||
|
ship: React.PropTypes.object.isRequired,
|
||||||
|
code: React.PropTypes.string.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
static DEFAULT_AGAINST = Ships['anaconda'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param {Object} props React Component properties
|
||||||
|
*/
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this._sort = this._sort.bind(this);
|
||||||
|
this._onShipChange = this._onShipChange.bind(this);
|
||||||
|
this._onCollapseExpand = this._onCollapseExpand.bind(this);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
predicate: 'n',
|
||||||
|
desc: true,
|
||||||
|
against: DamageDealt.DEFAULT_AGAINST,
|
||||||
|
expanded: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the initial weapons state
|
||||||
|
*/
|
||||||
|
componentWillMount() {
|
||||||
|
const weapons = this._calcWeapons(this.props.ship, this.state.against);
|
||||||
|
this.setState({ weapons });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the updated weapons state if our ship changes
|
||||||
|
* @param {Object} nextProps Incoming/Next properties
|
||||||
|
* @param {Object} nextContext Incoming/Next conext
|
||||||
|
* @return {boolean} Returns true if the component should be rerendered
|
||||||
|
*/
|
||||||
|
componentWillReceiveProps(nextProps, nextContext) {
|
||||||
|
if (nextProps.code != this.props.code) {
|
||||||
|
const weapons = this._calcWeapons(this.props.ship, this.state.against);
|
||||||
|
this.setState({ weapons });
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* @return {boolean} Returns the per-weapon damage
|
||||||
|
*/
|
||||||
|
_calcWeapons(ship, against) {
|
||||||
|
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;
|
||||||
|
|
||||||
|
weapons.push({ id: i,
|
||||||
|
mount: m.mount,
|
||||||
|
name: m.name || m.grp,
|
||||||
|
classRating,
|
||||||
|
effectiveDps,
|
||||||
|
effectiveSDps,
|
||||||
|
effectiveness });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return weapons;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggered when the collapse or expand icons are clicked
|
||||||
|
*/
|
||||||
|
_onCollapseExpand() {
|
||||||
|
this.setState({ expanded: !this.state.expanded });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggered when the ship we compare against changes
|
||||||
|
* @param {string} s the new ship ID
|
||||||
|
*/
|
||||||
|
_onShipChange(s) {
|
||||||
|
const against = Ships[s];
|
||||||
|
const weapons = this._calcWeapons(this.props.ship, against);
|
||||||
|
this.setState({ against, weapons });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the sort order and sort
|
||||||
|
* @param {string} predicate Sort predicate
|
||||||
|
*/
|
||||||
|
_sortOrder(predicate) {
|
||||||
|
let desc = this.state.desc;
|
||||||
|
|
||||||
|
if (predicate == this.state.predicate) {
|
||||||
|
desc = !desc;
|
||||||
|
} else {
|
||||||
|
desc = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._sort(this.props.ship, predicate, desc);
|
||||||
|
this.setState({ predicate, desc });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts the weapon list
|
||||||
|
* @param {Ship} ship Ship instance
|
||||||
|
* @param {string} predicate Sort predicate
|
||||||
|
* @param {Boolean} desc Sort order descending
|
||||||
|
*/
|
||||||
|
_sort(ship, predicate, desc) {
|
||||||
|
let comp = weaponComparator.bind(null, this.context.language.translate);
|
||||||
|
|
||||||
|
switch (predicate) {
|
||||||
|
case 'n': comp = comp(null, desc); break;
|
||||||
|
case 'edps': comp = comp((a, b) => a.effectiveDps - b.effectiveDps, desc); break;
|
||||||
|
case 'esdps': comp = comp((a, b) => a.effectiveSDps - b.effectiveSDps, desc); break;
|
||||||
|
case 'e': comp = comp((a, b) => a.effectiveness - b.effectiveness, desc); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.state.weapons.sort(comp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render individual rows for hardpoints
|
||||||
|
* @param {Function} translate Translate function
|
||||||
|
* @param {Object} formats Localised formats map
|
||||||
|
* @return {array} The individual rows
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
_renderRows(translate, formats) {
|
||||||
|
const { termtip, tooltip } = this.context;
|
||||||
|
|
||||||
|
let rows = [];
|
||||||
|
|
||||||
|
if (this.state.weapons) {
|
||||||
|
for (let i = 0; i < this.state.weapons.length; i++) {
|
||||||
|
const weapon = this.state.weapons[i];
|
||||||
|
|
||||||
|
rows.push(<tr key={weapon.id}>
|
||||||
|
<td className='ri'>
|
||||||
|
{weapon.mount == 'F' ? <span onMouseOver={termtip.bind(null, 'fixed')} onMouseOut={tooltip.bind(null, null)}><MountFixed className='icon'/></span> : null}
|
||||||
|
{weapon.mount == 'G' ? <span onMouseOver={termtip.bind(null, 'gimballed')} onMouseOut={tooltip.bind(null, null)}><MountGimballed /></span> : null}
|
||||||
|
{weapon.mount == 'T' ? <span onMouseOver={termtip.bind(null, 'turreted')} onMouseOut={tooltip.bind(null, null)}><MountTurret /></span> : null}
|
||||||
|
{weapon.classRating} {translate(weapon.name)}
|
||||||
|
</td>
|
||||||
|
<td className='ri'>{formats.round1(weapon.effectiveDps)}</td>
|
||||||
|
<td className='ri'>{formats.round1(weapon.effectiveSDps)}</td>
|
||||||
|
<td className='ri'>{formats.pct(weapon.effectiveness)}</td>
|
||||||
|
</tr>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render damage dealt
|
||||||
|
* @return {React.Component} contents
|
||||||
|
*/
|
||||||
|
render() {
|
||||||
|
const { language, tooltip, termtip } = this.context;
|
||||||
|
const { formats, translate } = language;
|
||||||
|
const { expanded } = this.state;
|
||||||
|
|
||||||
|
const sortOrder = this._sortOrder;
|
||||||
|
const onCollapseExpand = this._onCollapseExpand;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
<h1>{translate('damage dealt against')} {expanded ? <span onClick={onCollapseExpand}><CollapseSection className='summary'/></span> : <span onClick={onCollapseExpand}><ExpandSection className='summary'/></span>}</h1>
|
||||||
|
{expanded ? <span>
|
||||||
|
<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>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{this._renderRows(translate, formats)}
|
||||||
|
</tbody>
|
||||||
|
</table></span> : null }
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
274
src/app/components/DamageReceived.jsx
Normal file
274
src/app/components/DamageReceived.jsx
Normal file
@@ -0,0 +1,274 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
|
import { Modules } from 'coriolis-data/dist';
|
||||||
|
import { nameComparator } from '../utils/SlotFunctions';
|
||||||
|
import { CollapseSection, ExpandSection, MountFixed, MountGimballed, MountTurret } from './SvgIcons';
|
||||||
|
import Module from '../shipyard/Module';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates an internationalization friendly weapon comparator that will
|
||||||
|
* sort by specified property (if provided) then by name/group, class, rating
|
||||||
|
* @param {function} translate Translation function
|
||||||
|
* @param {function} propComparator Optional property comparator
|
||||||
|
* @param {boolean} desc Use descending order
|
||||||
|
* @return {function} Comparator function for names
|
||||||
|
*/
|
||||||
|
export function weaponComparator(translate, propComparator, desc) {
|
||||||
|
return (a, b) => {
|
||||||
|
if (!desc) { // Flip A and B if ascending order
|
||||||
|
let t = a;
|
||||||
|
a = b;
|
||||||
|
b = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a property comparator is provided use it first
|
||||||
|
let diff = propComparator ? propComparator(a, b) : nameComparator(translate, a, b);
|
||||||
|
|
||||||
|
if (diff) {
|
||||||
|
return diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Property matches so sort by name / group, then class, rating
|
||||||
|
if (a.name === b.name && a.grp === b.grp) {
|
||||||
|
if(a.class == b.class) {
|
||||||
|
return a.rating > b.rating ? 1 : -1;
|
||||||
|
}
|
||||||
|
return a.class - b.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nameComparator(translate, a, b);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Damage received by a selected ship
|
||||||
|
*/
|
||||||
|
export default class DamageReceived extends TranslatedComponent {
|
||||||
|
static PropTypes = {
|
||||||
|
ship: React.PropTypes.object.isRequired,
|
||||||
|
code: React.PropTypes.string.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param {Object} props React Component properties
|
||||||
|
*/
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this._sort = this._sort.bind(this);
|
||||||
|
this._onCollapseExpand = this._onCollapseExpand.bind(this);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
predicate: 'n',
|
||||||
|
desc: true,
|
||||||
|
expanded: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the initial weapons state
|
||||||
|
*/
|
||||||
|
componentWillMount() {
|
||||||
|
this.setState({ weapons: this._calcWeapons(this.props.ship) });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the updated weapons state
|
||||||
|
* @param {Object} nextProps Incoming/Next properties
|
||||||
|
* @param {Object} nextContext Incoming/Next conext
|
||||||
|
* @return {boolean} Returns true if the component should be rerendered
|
||||||
|
*/
|
||||||
|
componentWillReceiveProps(nextProps, nextContext) {
|
||||||
|
this.setState({ weapons: this._calcWeapons(nextProps.ship) });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the damage received by a ship
|
||||||
|
* @param {Object} ship The ship which will receive the damage
|
||||||
|
* @return {boolean} Returns the per-weapon damage
|
||||||
|
*/
|
||||||
|
_calcWeapons(ship) {
|
||||||
|
let weapons = [];
|
||||||
|
|
||||||
|
for (let grp in Modules.hardpoints) {
|
||||||
|
if (Modules.hardpoints[grp][0].damage && Modules.hardpoints[grp][0].type) {
|
||||||
|
for (let mId in Modules.hardpoints[grp]) {
|
||||||
|
const m = new Module(Modules.hardpoints[grp][mId]);
|
||||||
|
const classRating = `${m.class}${m.rating}${m.missile ? '/' + m.missile : ''}`;
|
||||||
|
|
||||||
|
// Base DPS
|
||||||
|
const baseDps = m.getDps();
|
||||||
|
const baseSDps = m.getClip() ? (m.getClip() * baseDps / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload()) : baseDps;
|
||||||
|
|
||||||
|
// Effective DPS taking in to account shield resistance
|
||||||
|
let effectivenessShields = 0;
|
||||||
|
if (m.getDamageType().indexOf('E') != -1) {
|
||||||
|
effectivenessShields += ship.shieldExplRes;
|
||||||
|
}
|
||||||
|
if (m.getDamageType().indexOf('K') != -1) {
|
||||||
|
effectivenessShields += ship.shieldKinRes;
|
||||||
|
}
|
||||||
|
if (m.getDamageType().indexOf('T') != -1) {
|
||||||
|
effectivenessShields += ship.shieldThermRes;
|
||||||
|
}
|
||||||
|
effectivenessShields /= m.getDamageType().length;
|
||||||
|
// Plasma accelerators deal absolute damage
|
||||||
|
if (m.grp == 'pa') effectivenessShields = 1;
|
||||||
|
const effectiveDpsShields = baseDps * effectivenessShields;
|
||||||
|
const effectiveSDpsShields = baseSDps * effectivenessShields;
|
||||||
|
|
||||||
|
// Effective DPS taking in to account hull hardness and resistance
|
||||||
|
let effectivenessHull = 0;
|
||||||
|
if (m.getDamageType().indexOf('E') != -1) {
|
||||||
|
effectivenessHull += ship.hullExplRes;
|
||||||
|
}
|
||||||
|
if (m.getDamageType().indexOf('K') != -1) {
|
||||||
|
effectivenessHull += ship.hullKinRes;
|
||||||
|
}
|
||||||
|
if (m.getDamageType().indexOf('T') != -1) {
|
||||||
|
effectivenessHull += ship.hullThermRes;
|
||||||
|
}
|
||||||
|
effectivenessHull /= m.getDamageType().length;
|
||||||
|
// Plasma accelerators deal absolute damage (but could be reduced by hardness)
|
||||||
|
if (m.grp == 'pa') effectivenessHull = 1;
|
||||||
|
effectivenessHull *= Math.min(m.getPiercing() / ship.hardness, 1);
|
||||||
|
const effectiveDpsHull = baseDps * effectivenessHull;
|
||||||
|
const effectiveSDpsHull = baseSDps * effectivenessHull;
|
||||||
|
|
||||||
|
weapons.push({ id: m.id,
|
||||||
|
classRating,
|
||||||
|
name: m.name || m.grp,
|
||||||
|
mount: m.mount,
|
||||||
|
effectiveDpsShields,
|
||||||
|
effectiveSDpsShields,
|
||||||
|
effectivenessShields,
|
||||||
|
effectiveDpsHull,
|
||||||
|
effectiveSDpsHull,
|
||||||
|
effectivenessHull });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return weapons;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggered when the collapse or expand icons are clicked
|
||||||
|
*/
|
||||||
|
_onCollapseExpand() {
|
||||||
|
this.setState({ expanded: !this.state.expanded });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the sort order and sort
|
||||||
|
* @param {string} predicate Sort predicate
|
||||||
|
*/
|
||||||
|
_sortOrder(predicate) {
|
||||||
|
let desc = this.state.desc;
|
||||||
|
|
||||||
|
if (predicate == this.state.predicate) {
|
||||||
|
desc = !desc;
|
||||||
|
} else {
|
||||||
|
desc = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._sort(this.props.ship, predicate, desc);
|
||||||
|
this.setState({ predicate, desc });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts the weapon list
|
||||||
|
* @param {Ship} ship Ship instance
|
||||||
|
* @param {string} predicate Sort predicate
|
||||||
|
* @param {Boolean} desc Sort order descending
|
||||||
|
*/
|
||||||
|
_sort(ship, predicate, desc) {
|
||||||
|
let comp = weaponComparator.bind(null, this.context.language.translate);
|
||||||
|
|
||||||
|
switch (predicate) {
|
||||||
|
case 'n': comp = comp(null, desc); break;
|
||||||
|
case 'edpss': comp = comp((a, b) => a.effectiveDpsShields - b.effectiveDpsShields, desc); break;
|
||||||
|
case 'esdpss': comp = comp((a, b) => a.effectiveSDpsShields - b.effectiveSDpsShields, desc); break;
|
||||||
|
case 'es': comp = comp((a, b) => a.effectivenessShields - b.effectivenessShields, desc); break;
|
||||||
|
case 'edpsh': comp = comp((a, b) => a.effectiveDpsHull - b.effectiveDpsHull, desc); break;
|
||||||
|
case 'esdpsh': comp = comp((a, b) => a.effectiveSDpsHull - b.effectiveSDpsHull, desc); break;
|
||||||
|
case 'eh': comp = comp((a, b) => a.effectivenessHull - b.effectivenessHull, desc); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.state.weapons.sort(comp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render individual rows for weapons
|
||||||
|
* @param {Function} translate Translate function
|
||||||
|
* @param {Object} formats Localised formats map
|
||||||
|
* @return {array} The individual rows
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
_renderRows(translate, formats) {
|
||||||
|
const { termtip, tooltip } = this.context;
|
||||||
|
|
||||||
|
let rows = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < this.state.weapons.length; i++) {
|
||||||
|
const weapon = this.state.weapons[i];
|
||||||
|
rows.push(<tr key={weapon.id}>
|
||||||
|
<td className='ri'>
|
||||||
|
{weapon.mount == 'F' ? <span onMouseOver={termtip.bind(null, 'fixed')} onMouseOut={tooltip.bind(null, null)}><MountFixed className='icon'/></span> : null}
|
||||||
|
{weapon.mount == 'G' ? <span onMouseOver={termtip.bind(null, 'gimballed')} onMouseOut={tooltip.bind(null, null)}><MountGimballed /></span> : null}
|
||||||
|
{weapon.mount == 'T' ? <span onMouseOver={termtip.bind(null, 'turreted')} onMouseOut={tooltip.bind(null, null)}><MountTurret /></span> : null}
|
||||||
|
{weapon.classRating} {translate(weapon.name)}
|
||||||
|
</td>
|
||||||
|
<td>{formats.round1(weapon.effectiveDpsShields)}</td>
|
||||||
|
<td>{formats.round1(weapon.effectiveSDpsShields)}</td>
|
||||||
|
<td>{formats.pct(weapon.effectivenessShields)}</td>
|
||||||
|
<td>{formats.round1(weapon.effectiveDpsHull)}</td>
|
||||||
|
<td>{formats.round1(weapon.effectiveSDpsHull)}</td>
|
||||||
|
<td>{formats.pct(weapon.effectivenessHull)}</td>
|
||||||
|
</tr>);
|
||||||
|
}
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render damage received
|
||||||
|
* @return {React.Component} contents
|
||||||
|
*/
|
||||||
|
render() {
|
||||||
|
const { language, tooltip, termtip } = this.context;
|
||||||
|
const { formats, translate } = language;
|
||||||
|
const { expanded } = this.state;
|
||||||
|
|
||||||
|
const sortOrder = this._sortOrder;
|
||||||
|
const onCollapseExpand = this._onCollapseExpand;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
<h1>{translate('damage received by')} {expanded ? <span onClick={onCollapseExpand}><CollapseSection className='summary'/></span> : <span onClick={onCollapseExpand}><ExpandSection className='summary'/></span>}</h1>
|
||||||
|
{expanded ? <span>
|
||||||
|
<table className='summary' style={{ width: '100%' }}>
|
||||||
|
<thead>
|
||||||
|
<tr className='main'>
|
||||||
|
<th rowSpan={2} className='sortable' onClick={sortOrder.bind(this, 'n')} >{translate('weapon')}</th>
|
||||||
|
<th colSpan={3} >{translate('against shields')}</th>
|
||||||
|
<th colSpan={3} >{translate('against hull')}</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th className='sortable lft' onClick={sortOrder.bind(this, 'edpss')} onMouseOver={termtip.bind(null, 'dps')} onMouseOut={tooltip.bind(null, null)}>{translate('DPS')}</th>
|
||||||
|
<th className='sortable' onClick={sortOrder.bind(this, 'esdpss')} onMouseOver={termtip.bind(null, 'sdps')} onMouseOut={tooltip.bind(null, null)}>{translate('SDPS')}</th>
|
||||||
|
<th className='sortable' onClick={sortOrder.bind(this, 'es')} >{translate('effectiveness')}</th>
|
||||||
|
<th className='sortable lft' onClick={sortOrder.bind(this, 'edpsh')} onMouseOver={termtip.bind(null, 'dps')} onMouseOut={tooltip.bind(null, null)}>{translate('DPS')}</th>
|
||||||
|
<th className='sortable' onClick={sortOrder.bind(this, 'esdpsh')} onMouseOver={termtip.bind(null, 'sdps')} onMouseOut={tooltip.bind(null, null)}>{translate('SDPS')}</th>
|
||||||
|
<th className='sortable' onClick={sortOrder.bind(this, 'eh')} >{translate('effectiveness')}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{this._renderRows(translate, formats)}
|
||||||
|
</tbody>
|
||||||
|
</table></span> : null }
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,6 +29,8 @@ export default class DefenceSummary extends TranslatedComponent {
|
|||||||
let { formats, translate, units } = language;
|
let { formats, translate, units } = language;
|
||||||
let hide = tooltip.bind(null, null);
|
let hide = tooltip.bind(null, null);
|
||||||
|
|
||||||
|
const shieldGenerator = ship.findShieldGenerator();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
<h1>{translate('defence summary')}</h1>
|
<h1>{translate('defence summary')}</h1>
|
||||||
@@ -48,9 +50,18 @@ export default class DefenceSummary extends TranslatedComponent {
|
|||||||
{ship.shield ?
|
{ship.shield ?
|
||||||
<tr>
|
<tr>
|
||||||
<td className='le'>{translate('damage from')}</td>
|
<td className='le'>{translate('damage from')}</td>
|
||||||
<td className='ri' onMouseOver={termtip.bind(null, 'explosive')} onMouseOut={tooltip.bind(null, null)}><DamageExplosive /> {formats.pct1(ship.shieldExplRes || 1)}</td>
|
<td className='ri'>
|
||||||
<td className='ri' onMouseOver={termtip.bind(null, 'kinetic')} onMouseOut={tooltip.bind(null, null)}><DamageKinetic /> {formats.pct1(ship.shieldKinRes || 1)}</td>
|
<span onMouseOver={termtip.bind(null, 'explosive')} onMouseOut={tooltip.bind(null, null)}><DamageExplosive /></span>
|
||||||
<td className='ri' onMouseOver={termtip.bind(null, 'thermal')} onMouseOut={tooltip.bind(null, null)}><DamageThermal /> {formats.pct1(ship.shieldThermRes || 1)}</td>
|
<span onMouseOver={termtip.bind(null, translate('base') + ' ' + formats.pct1(1 - shieldGenerator.explres))} onMouseOut={tooltip.bind(null, null)}>{formats.pct1(ship.shieldExplRes || 1)}</span>
|
||||||
|
</td>
|
||||||
|
<td className='ri'>
|
||||||
|
<span onMouseOver={termtip.bind(null, 'kinetic')} onMouseOut={tooltip.bind(null, null)}><DamageKinetic /></span>
|
||||||
|
<span onMouseOver={termtip.bind(null, translate('base') + ' ' + formats.pct1(1 - shieldGenerator.kinres))} onMouseOut={tooltip.bind(null, null)}>{formats.pct1(ship.shieldKinRes || 1)}</span>
|
||||||
|
</td>
|
||||||
|
<td className='ri'>
|
||||||
|
<span onMouseOver={termtip.bind(null, 'thermal')} onMouseOut={tooltip.bind(null, null)}><DamageThermal /></span>
|
||||||
|
<span onMouseOver={termtip.bind(null, translate('base') + ' ' + formats.pct1(1 - shieldGenerator.thermres))} onMouseOut={tooltip.bind(null, null)}>{formats.pct1(ship.shieldThermRes || 1)}</span>
|
||||||
|
</td>
|
||||||
</tr> : null }
|
</tr> : null }
|
||||||
|
|
||||||
{ ship.shield && ship.shieldCells ?
|
{ ship.shield && ship.shieldCells ?
|
||||||
@@ -63,10 +74,29 @@ export default class DefenceSummary extends TranslatedComponent {
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td className='le'>{translate('damage from')}</td>
|
<td className='le'>{translate('damage from')}</td>
|
||||||
<td className='ri' onMouseOver={termtip.bind(null, 'explosive')} onMouseOut={tooltip.bind(null, null)}><DamageExplosive /> {formats.pct1(ship.hullExplRes || 1)}</td>
|
<td className='ri'>
|
||||||
<td className='ri' onMouseOver={termtip.bind(null, 'kinetic')} onMouseOut={tooltip.bind(null, null)}><DamageKinetic /> {formats.pct1(ship.hullKinRes || 1)}</td>
|
<span onMouseOver={termtip.bind(null, 'explosive')} onMouseOut={tooltip.bind(null, null)}><DamageExplosive /></span>
|
||||||
<td className='ri' onMouseOver={termtip.bind(null, 'thermal')} onMouseOut={tooltip.bind(null, null)}><DamageThermal /> {formats.pct1(ship.hullThermRes || 1)}</td>
|
<span onMouseOver={termtip.bind(null, translate('base') + ' ' + formats.pct1(1 - ship.bulkheads.m.explres))} onMouseOut={tooltip.bind(null, null)}>{formats.pct1(ship.hullExplRes || 1)}</span></td>
|
||||||
|
<td className='ri'>
|
||||||
|
<span onMouseOver={termtip.bind(null, 'kinetic')} onMouseOut={tooltip.bind(null, null)}><DamageKinetic /></span>
|
||||||
|
<span onMouseOver={termtip.bind(null, translate('base') + ' ' + formats.pct1(1 - ship.bulkheads.m.kinres))} onMouseOut={tooltip.bind(null, null)}>{formats.pct1(ship.hullKinRes || 1)}</span>
|
||||||
|
</td>
|
||||||
|
<td className='ri'>
|
||||||
|
<span onMouseOver={termtip.bind(null, 'thermal')} onMouseOut={tooltip.bind(null, null)}><DamageThermal /></span>
|
||||||
|
<span onMouseOver={termtip.bind(null, translate('base') + ' ' + formats.pct1(1 - ship.bulkheads.m.thermres))} onMouseOut={tooltip.bind(null, null)}>{formats.pct1(ship.hullThermRes || 1)}</span>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
{ship.modulearmour > 0 ?
|
||||||
|
<tr>
|
||||||
|
<td colSpan='4'><h2>{translate('module armour')}: {formats.int(ship.modulearmour)}</h2></td>
|
||||||
|
</tr> : null }
|
||||||
|
|
||||||
|
{ship.moduleprotection > 0 ?
|
||||||
|
<tr>
|
||||||
|
<td colSpan='2' className='cn'>{translate('internal protection')} {formats.pct1(ship.moduleprotection)}</td>
|
||||||
|
<td colSpan='2' className='cn'>{translate('external protection')} {formats.pct1(ship.moduleprotection / 2)}</td>
|
||||||
|
</tr> : null }
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export default class HardpointSlot extends Slot {
|
|||||||
|
|
||||||
// Modifications tooltip shows blueprint and grade, if available
|
// Modifications tooltip shows blueprint and grade, if available
|
||||||
let modTT = translate('modified');
|
let modTT = translate('modified');
|
||||||
if (m && m.blueprint) {
|
if (m && m.blueprint && m.blueprint.name) {
|
||||||
modTT = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
|
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);
|
modTT += ', ' + translate(m.blueprint.special.name);
|
||||||
@@ -76,6 +76,7 @@ export default class HardpointSlot extends Slot {
|
|||||||
{ 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.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.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 }
|
||||||
{ showModuleResistances && m.getKineticResistance() ? <div className='l'>{translate('kinres')}: {formats.pct(m.getKineticResistance())}</div> : null }
|
{ showModuleResistances && m.getKineticResistance() ? <div className='l'>{translate('kinres')}: {formats.pct(m.getKineticResistance())}</div> : null }
|
||||||
|
|||||||
@@ -133,6 +133,10 @@ export default class HardpointsSlotSection extends SlotSection {
|
|||||||
<li className='c' onClick={_fill.bind(this, 'fc', 'G')}><MountGimballed className='lg'/></li>
|
<li className='c' onClick={_fill.bind(this, 'fc', 'G')}><MountGimballed className='lg'/></li>
|
||||||
<li className='c' onClick={_fill.bind(this, 'fc', 'T')}><MountTurret className='lg'/></li>
|
<li className='c' onClick={_fill.bind(this, 'fc', 'T')}><MountTurret className='lg'/></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<div className='select-group cap'>{translate('pa')}</div>
|
||||||
|
<ul>
|
||||||
|
<li className='lc' onClick={_fill.bind(this, 'pa', 'F')}>{translate('pa')}</li>
|
||||||
|
</ul>
|
||||||
<div className='select-group cap'>{translate('nl')}</div>
|
<div className='select-group cap'>{translate('nl')}</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li className='lc' onClick={_fill.bind(this, 'nl', 'F')}>{translate('nl')}</li>
|
<li className='lc' onClick={_fill.bind(this, 'nl', 'F')}>{translate('nl')}</li>
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export default class InternalSlot extends Slot {
|
|||||||
|
|
||||||
// Modifications tooltip shows blueprint and grade, if available
|
// Modifications tooltip shows blueprint and grade, if available
|
||||||
let modTT = translate('modified');
|
let modTT = translate('modified');
|
||||||
if (m && m.blueprint) {
|
if (m && m.blueprint && m.blueprint.name) {
|
||||||
modTT = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
|
modTT = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,6 +43,7 @@ export default class InternalSlot extends Slot {
|
|||||||
{ m.getMaxMass() ? <div className={'l'}>{translate('max mass')}: {formats.int(m.getMaxMass())}{u.T}</div> : null }
|
{ m.getMaxMass() ? <div className={'l'}>{translate('max mass')}: {formats.int(m.getMaxMass())}{u.T}</div> : null }
|
||||||
{ m.bins ? <div className={'l'}>{m.bins} <u>{translate('bins')}</u></div> : null }
|
{ m.bins ? <div className={'l'}>{m.bins} <u>{translate('bins')}</u></div> : null }
|
||||||
{ m.bays ? <div className={'l'}>{translate('bays')}: {m.bays}</div> : null }
|
{ m.bays ? <div className={'l'}>{translate('bays')}: {m.bays}</div> : null }
|
||||||
|
{ m.rebuildsperbay ? <div className={'l'}>{translate('rebuildsperbay')}: {m.rebuildsperbay}</div> : null }
|
||||||
{ m.rate ? <div className={'l'}>{translate('rate')}: {m.rate}{u.kgs} {translate('refuel time')}: {formats.time(this.props.fuel * 1000 / m.rate)}</div> : null }
|
{ m.rate ? <div className={'l'}>{translate('rate')}: {m.rate}{u.kgs} {translate('refuel time')}: {formats.time(this.props.fuel * 1000 / m.rate)}</div> : null }
|
||||||
{ m.getAmmo() ? <div className={'l'}>{translate('ammunition')}: {formats.gen(m.getAmmo())}</div> : null }
|
{ m.getAmmo() ? <div className={'l'}>{translate('ammunition')}: {formats.gen(m.getAmmo())}</div> : null }
|
||||||
{ m.cells ? <div className={'l'}>{translate('cells')}: {m.cells}</div> : null }
|
{ m.cells ? <div className={'l'}>{translate('cells')}: {m.cells}</div> : null }
|
||||||
@@ -58,10 +59,13 @@ export default class InternalSlot extends Slot {
|
|||||||
{ m.rangeLS === null ? <div className={'l'}>∞{u.Ls}</div> : null }
|
{ m.rangeLS === null ? <div className={'l'}>∞{u.Ls}</div> : null }
|
||||||
{ m.rangeRating ? <div className={'l'}>{translate('range')}: {m.rangeRating}</div> : null }
|
{ m.rangeRating ? <div className={'l'}>{translate('range')}: {m.rangeRating}</div> : null }
|
||||||
{ m.getHullReinforcement() ? <div className={'l'}>+{formats.int(m.getHullReinforcement() + ship.baseArmour * m.getModValue('hullboost') / 10000)} <u className='cap'>{translate('armour')}</u></div> : null }
|
{ m.getHullReinforcement() ? <div className={'l'}>+{formats.int(m.getHullReinforcement() + ship.baseArmour * m.getModValue('hullboost') / 10000)} <u className='cap'>{translate('armour')}</u></div> : null }
|
||||||
|
{ m.getProtection() ? <div className={'l'}>{formats.rPct(m.getProtection())} <u className='cap'>{translate('protection')}</u></div> : null }
|
||||||
{ m.passengers ? <div className={'l'}>{translate('passengers')}: {m.passengers}</div> : null }
|
{ m.passengers ? <div className={'l'}>{translate('passengers')}: {m.passengers}</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 }
|
||||||
{ showModuleResistances && m.getKineticResistance() ? <div className='l'>{translate('kinres')}: {formats.pct(m.getKineticResistance())}</div> : null }
|
{ showModuleResistances && m.getKineticResistance() ? <div className='l'>{translate('kinres')}: {formats.pct(m.getKineticResistance())}</div> : null }
|
||||||
{ showModuleResistances && m.getThermalResistance() ? <div className='l'>{translate('thermres')}: {formats.pct(m.getThermalResistance())}</div> : null }
|
{ showModuleResistances && m.getThermalResistance() ? <div className='l'>{translate('thermres')}: {formats.pct(m.getThermalResistance())}</div> : null }
|
||||||
|
{ m.getRegenerationRate() ? <div className='l'>{translate('regen')}: {formats.round1(m.getRegenerationRate())}{u.ps}</div> : null }
|
||||||
|
{ m.getBrokenRegenerationRate() ? <div className='l'>{translate('brokenregen')}: {formats.round1(m.getBrokenRegenerationRate())}{u.ps}</div> : null }
|
||||||
|
|
||||||
{ m && validMods.length > 0 ? <div className='r' ><button onClick={this._toggleModifications.bind(this)} onContextMenu={stopCtxPropagation} onMouseOver={termtip.bind(null, 'modifications')} onMouseOut={tooltip.bind(null, null)}><ListModifications /></button></div> : null }
|
{ m && validMods.length > 0 ? <div className='r' ><button onClick={this._toggleModifications.bind(this)} onContextMenu={stopCtxPropagation} onMouseOver={termtip.bind(null, 'modifications')} onMouseOut={tooltip.bind(null, null)}><ListModifications /></button></div> : null }
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
this._fillWithCargo = this._fillWithCargo.bind(this);
|
this._fillWithCargo = this._fillWithCargo.bind(this);
|
||||||
this._fillWithCells = this._fillWithCells.bind(this);
|
this._fillWithCells = this._fillWithCells.bind(this);
|
||||||
this._fillWithArmor = this._fillWithArmor.bind(this);
|
this._fillWithArmor = this._fillWithArmor.bind(this);
|
||||||
|
this._fillWithModuleReinforcementPackages = this._fillWithModuleReinforcementPackages.bind(this);
|
||||||
this._fillWithFuelTanks = this._fillWithFuelTanks.bind(this);
|
this._fillWithFuelTanks = this._fillWithFuelTanks.bind(this);
|
||||||
this._fillWithLuxuryCabins = this._fillWithLuxuryCabins.bind(this);
|
this._fillWithLuxuryCabins = this._fillWithLuxuryCabins.bind(this);
|
||||||
this._fillWithFirstClassCabins = this._fillWithFirstClassCabins.bind(this);
|
this._fillWithFirstClassCabins = this._fillWithFirstClassCabins.bind(this);
|
||||||
@@ -46,7 +47,7 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
let clobber = event.getModifierState('Alt');
|
let clobber = event.getModifierState('Alt');
|
||||||
let ship = this.props.ship;
|
let ship = this.props.ship;
|
||||||
ship.internal.forEach((slot) => {
|
ship.internal.forEach((slot) => {
|
||||||
if (clobber || !slot.m) {
|
if ((clobber || !slot.m) && (!slot.eligible || slot.eligible.cr)) {
|
||||||
ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E'));
|
ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E'));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -62,7 +63,7 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
let clobber = event.getModifierState('Alt');
|
let clobber = event.getModifierState('Alt');
|
||||||
let ship = this.props.ship;
|
let ship = this.props.ship;
|
||||||
ship.internal.forEach((slot) => {
|
ship.internal.forEach((slot) => {
|
||||||
if (clobber || !slot.m) {
|
if ((clobber || !slot.m) && (!slot.eligible || slot.eligible.ft)) {
|
||||||
ship.use(slot, ModuleUtils.findInternal('ft', slot.maxClass, 'C'));
|
ship.use(slot, ModuleUtils.findInternal('ft', slot.maxClass, 'C'));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -78,7 +79,7 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
let clobber = event.getModifierState('Alt');
|
let clobber = event.getModifierState('Alt');
|
||||||
let ship = this.props.ship;
|
let ship = this.props.ship;
|
||||||
ship.internal.forEach((slot) => {
|
ship.internal.forEach((slot) => {
|
||||||
if (clobber || !slot.m) {
|
if ((clobber || !slot.m) && (!slot.eligible || slot.eligible.pcq)) {
|
||||||
ship.use(slot, ModuleUtils.findInternal('pcq', Math.min(slot.maxClass, 6), 'B')); // Passenger cabins top out at 6
|
ship.use(slot, ModuleUtils.findInternal('pcq', Math.min(slot.maxClass, 6), 'B')); // Passenger cabins top out at 6
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -94,7 +95,7 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
let clobber = event.getModifierState('Alt');
|
let clobber = event.getModifierState('Alt');
|
||||||
let ship = this.props.ship;
|
let ship = this.props.ship;
|
||||||
ship.internal.forEach((slot) => {
|
ship.internal.forEach((slot) => {
|
||||||
if (clobber || !slot.m) {
|
if ((clobber || !slot.m) && (!slot.eligible || slot.eligible.pcm)) {
|
||||||
ship.use(slot, ModuleUtils.findInternal('pcm', Math.min(slot.maxClass, 6), 'C')); // Passenger cabins top out at 6
|
ship.use(slot, ModuleUtils.findInternal('pcm', Math.min(slot.maxClass, 6), 'C')); // Passenger cabins top out at 6
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -110,7 +111,7 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
let clobber = event.getModifierState('Alt');
|
let clobber = event.getModifierState('Alt');
|
||||||
let ship = this.props.ship;
|
let ship = this.props.ship;
|
||||||
ship.internal.forEach((slot) => {
|
ship.internal.forEach((slot) => {
|
||||||
if (clobber || !slot.m) {
|
if ((clobber || !slot.m) && (!slot.eligible || slot.eligible.pci)) {
|
||||||
ship.use(slot, ModuleUtils.findInternal('pci', Math.min(slot.maxClass, 6), 'D')); // Passenger cabins top out at 6
|
ship.use(slot, ModuleUtils.findInternal('pci', Math.min(slot.maxClass, 6), 'D')); // Passenger cabins top out at 6
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -126,7 +127,7 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
let clobber = event.getModifierState('Alt');
|
let clobber = event.getModifierState('Alt');
|
||||||
let ship = this.props.ship;
|
let ship = this.props.ship;
|
||||||
ship.internal.forEach((slot) => {
|
ship.internal.forEach((slot) => {
|
||||||
if (clobber || !slot.m) {
|
if ((clobber || !slot.m) && (!slot.eligible || slot.eligible.pce)) {
|
||||||
ship.use(slot, ModuleUtils.findInternal('pce', Math.min(slot.maxClass, 6), 'E')); // Passenger cabins top out at 6
|
ship.use(slot, ModuleUtils.findInternal('pce', Math.min(slot.maxClass, 6), 'E')); // Passenger cabins top out at 6
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -143,7 +144,7 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
let ship = this.props.ship;
|
let ship = this.props.ship;
|
||||||
let chargeCap = 0; // Capacity of single activation
|
let chargeCap = 0; // Capacity of single activation
|
||||||
ship.internal.forEach(function(slot) {
|
ship.internal.forEach(function(slot) {
|
||||||
if ((!slot.m || (clobber && !ModuleUtils.isShieldGenerator(slot.m.grp))) && (!slot.eligible || slot.eligible.scb)) { // Check eligibility due to passenger ships special case
|
if ((clobber || (!slot.m && !ModuleUtils.isShieldGenerator(slot.m.grp))) && (!slot.eligible || slot.eligible.scb)) {
|
||||||
ship.use(slot, ModuleUtils.findInternal('scb', slot.maxClass, 'A'));
|
ship.use(slot, ModuleUtils.findInternal('scb', slot.maxClass, 'A'));
|
||||||
ship.setSlotEnabled(slot, chargeCap <= ship.shieldStrength); // Don't waste cell capacity on overcharge
|
ship.setSlotEnabled(slot, chargeCap <= ship.shieldStrength); // Don't waste cell capacity on overcharge
|
||||||
chargeCap += slot.m.recharge;
|
chargeCap += slot.m.recharge;
|
||||||
@@ -161,7 +162,7 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
let clobber = event.getModifierState('Alt');
|
let clobber = event.getModifierState('Alt');
|
||||||
let ship = this.props.ship;
|
let ship = this.props.ship;
|
||||||
ship.internal.forEach((slot) => {
|
ship.internal.forEach((slot) => {
|
||||||
if (clobber || !slot.m) {
|
if ((clobber || !slot.m) && (!slot.eligible || slot.eligible.hr)) {
|
||||||
ship.use(slot, ModuleUtils.findInternal('hr', Math.min(slot.maxClass, 5), 'D')); // Hull reinforcements top out at 5D
|
ship.use(slot, ModuleUtils.findInternal('hr', Math.min(slot.maxClass, 5), 'D')); // Hull reinforcements top out at 5D
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -169,6 +170,22 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
this._close();
|
this._close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fill all slots with Module Reinforcement Packages
|
||||||
|
* @param {SyntheticEvent} event Event
|
||||||
|
*/
|
||||||
|
_fillWithModuleReinforcementPackages(event) {
|
||||||
|
let clobber = event.getModifierState('Alt');
|
||||||
|
let ship = this.props.ship;
|
||||||
|
ship.internal.forEach((slot) => {
|
||||||
|
if ((clobber || !slot.m) && (!slot.eligible || slot.eligible.mrp)) {
|
||||||
|
ship.use(slot, ModuleUtils.findInternal('mrp', Math.min(slot.maxClass, 5), 'D')); // Module reinforcements top out at 5D
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.props.onChange();
|
||||||
|
this._close();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Empty all on section header right click
|
* Empty all on section header right click
|
||||||
*/
|
*/
|
||||||
@@ -226,6 +243,7 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
<li className='lc' onClick={this._fillWithCargo}>{translate('cargo')}</li>
|
<li className='lc' onClick={this._fillWithCargo}>{translate('cargo')}</li>
|
||||||
<li className='lc' onClick={this._fillWithCells}>{translate('scb')}</li>
|
<li className='lc' onClick={this._fillWithCells}>{translate('scb')}</li>
|
||||||
<li className='lc' onClick={this._fillWithArmor}>{translate('hr')}</li>
|
<li className='lc' onClick={this._fillWithArmor}>{translate('hr')}</li>
|
||||||
|
<li className='lc' onClick={this._fillWithModuleReinforcementPackages}>{translate('mrp')}</li>
|
||||||
<li className='lc' onClick={this._fillWithFuelTanks}>{translate('ft')}</li>
|
<li className='lc' onClick={this._fillWithFuelTanks}>{translate('ft')}</li>
|
||||||
<li className='lc' onClick={this._fillWithEconomyClassCabins}>{translate('pce')}</li>
|
<li className='lc' onClick={this._fillWithEconomyClassCabins}>{translate('pce')}</li>
|
||||||
<li className='lc' onClick={this._fillWithBusinessClassCabins}>{translate('pci')}</li>
|
<li className='lc' onClick={this._fillWithBusinessClassCabins}>{translate('pci')}</li>
|
||||||
|
|||||||
92
src/app/components/MovementSummary.jsx
Normal file
92
src/app/components/MovementSummary.jsx
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import cn from 'classnames';
|
||||||
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
|
import { DamageKinetic, DamageThermal, DamageExplosive } from './SvgIcons';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Movement summary
|
||||||
|
*/
|
||||||
|
export default class MovementSummary extends TranslatedComponent {
|
||||||
|
static PropTypes = {
|
||||||
|
ship: React.PropTypes.object.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param {Object} props React Component properties
|
||||||
|
*/
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render movement summary
|
||||||
|
* @return {React.Component} contents
|
||||||
|
*/
|
||||||
|
render() {
|
||||||
|
let ship = this.props.ship;
|
||||||
|
let { language, tooltip, termtip } = this.context;
|
||||||
|
let { formats, translate, units } = language;
|
||||||
|
let hide = tooltip.bind(null, null);
|
||||||
|
let boostMultiplier = ship.topBoost / ship.topSpeed;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
<h1>{translate('movement summary')}</h1>
|
||||||
|
<table style={{ marginLeft: 'auto', marginRight: 'auto', lineHeight: '1em', backgroundColor: 'transparent' }}>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td > </td>
|
||||||
|
<td colSpan='6'>{translate('engine pips')}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td> </td>
|
||||||
|
<td>0</td>
|
||||||
|
<td>1</td>
|
||||||
|
<td>2</td>
|
||||||
|
<td>3</td>
|
||||||
|
<td>4</td>
|
||||||
|
<td onMouseOver={termtip.bind(null, '4b')} onMouseOut={tooltip.bind(null, null)}>4B</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td className='ri'>{translate('speed')} ({units['m/s']})</td>
|
||||||
|
<td className='ri'>{formats.int(ship.speeds[0])}</td>
|
||||||
|
<td className='ri'>{formats.int(ship.speeds[1])}</td>
|
||||||
|
<td className='ri'>{formats.int(ship.speeds[2])}</td>
|
||||||
|
<td className='ri'>{formats.int(ship.speeds[3])}</td>
|
||||||
|
<td className='ri'>{formats.int(ship.speeds[4])}</td>
|
||||||
|
<td className='ri'>{formats.int(ship.speeds[4] * boostMultiplier)}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td className='ri'>{translate('pitch')} ({units['°/s']})</td>
|
||||||
|
<td className='ri'>{formats.int(ship.pitches[0])}</td>
|
||||||
|
<td className='ri'>{formats.int(ship.pitches[1])}</td>
|
||||||
|
<td className='ri'>{formats.int(ship.pitches[2])}</td>
|
||||||
|
<td className='ri'>{formats.int(ship.pitches[3])}</td>
|
||||||
|
<td className='ri'>{formats.int(ship.pitches[4])}</td>
|
||||||
|
<td className='ri'>{formats.int(ship.pitches[4] * boostMultiplier)}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td className='ri'>{translate('roll')} ({units['°/s']})</td>
|
||||||
|
<td className='ri'>{formats.int(ship.rolls[0])}</td>
|
||||||
|
<td className='ri'>{formats.int(ship.rolls[1])}</td>
|
||||||
|
<td className='ri'>{formats.int(ship.rolls[2])}</td>
|
||||||
|
<td className='ri'>{formats.int(ship.rolls[3])}</td>
|
||||||
|
<td className='ri'>{formats.int(ship.rolls[4])}</td>
|
||||||
|
<td className='ri'>{formats.int(ship.rolls[4] * boostMultiplier)}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td className='ri'>{translate('yaw')} ({units['°/s']})</td>
|
||||||
|
<td className='ri'>{formats.int(ship.yaws[0])}</td>
|
||||||
|
<td className='ri'>{formats.int(ship.yaws[1])}</td>
|
||||||
|
<td className='ri'>{formats.int(ship.yaws[2])}</td>
|
||||||
|
<td className='ri'>{formats.int(ship.yaws[3])}</td>
|
||||||
|
<td className='ri'>{formats.int(ship.yaws[4])}</td>
|
||||||
|
<td className='ri'>{formats.int(ship.yaws[4] * boostMultiplier)}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
90
src/app/components/ShipSelector.jsx
Normal file
90
src/app/components/ShipSelector.jsx
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import cn from 'classnames';
|
||||||
|
import { Ships } from 'coriolis-data/dist';
|
||||||
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
|
import { Rocket } from './SvgIcons';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selector for ships
|
||||||
|
*/
|
||||||
|
export default class ShipSelector extends TranslatedComponent {
|
||||||
|
static PropTypes = {
|
||||||
|
initial: React.PropTypes.object.isRequired,
|
||||||
|
onChange: React.PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param {Object} props React Component properties
|
||||||
|
*/
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = { ship : this.props.initial };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate the ships menu
|
||||||
|
* @return {React.Component} Menu
|
||||||
|
*/
|
||||||
|
_getShipsMenu() {
|
||||||
|
const _selectShip = this._selectShip;
|
||||||
|
const _openMenu = this._openMenu;
|
||||||
|
|
||||||
|
let shipList = [];
|
||||||
|
|
||||||
|
for (let s in Ships) {
|
||||||
|
shipList.push(<div key={s} onClick={_selectShip.bind(this, s)} className='block' >{Ships[s].properties.name}</div>);
|
||||||
|
}
|
||||||
|
|
||||||
|
return shipList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle opening the menu
|
||||||
|
* @param {string} menu The ID of the opened menu
|
||||||
|
* @param {SyntheticEvent} event Event
|
||||||
|
*/
|
||||||
|
_openMenu(menu, event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
if (this.props.currentMenu == menu) {
|
||||||
|
menu = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.context.openMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle selection of a ship
|
||||||
|
* @param {string} s The selected ship ID
|
||||||
|
*/
|
||||||
|
_selectShip(s) {
|
||||||
|
this.setState({ ship: Ships[s] });
|
||||||
|
|
||||||
|
this.context.openMenu(null);
|
||||||
|
this.props.onChange(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render ship selector
|
||||||
|
* @return {React.Component} contents
|
||||||
|
*/
|
||||||
|
render() {
|
||||||
|
const currentMenu = this.props.currentMenu;
|
||||||
|
const ship = this.state.ship;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='shipselector'>
|
||||||
|
<div className='menu'>
|
||||||
|
<div className={cn('menu-header', { selected: currentMenu == 'wds' })} onClick={this._openMenu.bind(this, 'wds')}>
|
||||||
|
<Rocket className='warning' /><span className='menu-item-label'>{ship.properties.name}</span>
|
||||||
|
{currentMenu == 'wds' ?
|
||||||
|
<div className='menu-list quad no-wrap' onClick={ (e) => e.stopPropagation() }>
|
||||||
|
{this._getShipsMenu()}
|
||||||
|
</div> : null }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -43,6 +43,7 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
|||||||
<th onMouseEnter={termtip.bind(null, 'damage per second')} onMouseLeave={hide} rowSpan={2}>{translate('DPS')}</th>
|
<th onMouseEnter={termtip.bind(null, 'damage per second')} onMouseLeave={hide} rowSpan={2}>{translate('DPS')}</th>
|
||||||
<th onMouseEnter={termtip.bind(null, 'energy per second')} onMouseLeave={hide} rowSpan={2}>{translate('EPS')}</th>
|
<th onMouseEnter={termtip.bind(null, 'energy per second')} onMouseLeave={hide} rowSpan={2}>{translate('EPS')}</th>
|
||||||
<th onMouseEnter={termtip.bind(null, 'heat per second')} onMouseLeave={hide} rowSpan={2}>{translate('HPS')}</th>
|
<th onMouseEnter={termtip.bind(null, 'heat per second')} onMouseLeave={hide} rowSpan={2}>{translate('HPS')}</th>
|
||||||
|
<th rowSpan={2}>{translate('hardness')}</th>
|
||||||
<th rowSpan={2}>{translate('armour')}</th>
|
<th rowSpan={2}>{translate('armour')}</th>
|
||||||
<th rowSpan={2}>{translate('shields')}</th>
|
<th rowSpan={2}>{translate('shields')}</th>
|
||||||
<th colSpan={3}>{translate('mass')}</th>
|
<th colSpan={3}>{translate('mass')}</th>
|
||||||
@@ -71,6 +72,7 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
|||||||
<td>{f1(ship.totalDps)}</td>
|
<td>{f1(ship.totalDps)}</td>
|
||||||
<td>{f1(ship.totalEps)}</td>
|
<td>{f1(ship.totalEps)}</td>
|
||||||
<td>{f1(ship.totalHps)}</td>
|
<td>{f1(ship.totalHps)}</td>
|
||||||
|
<td>{int(ship.hardness)}</td>
|
||||||
<td>{int(ship.armour)}</td>
|
<td>{int(ship.armour)}</td>
|
||||||
<td className={sgClassNames}>{int(ship.shield)} {u.MJ}</td>
|
<td className={sgClassNames}>{int(ship.shield)} {u.MJ}</td>
|
||||||
<td>{ship.hullMass} {u.T}</td>
|
<td>{ship.hullMass} {u.T}</td>
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ export default class SlotSection extends TranslatedComponent {
|
|||||||
_drag(originSlot, e) {
|
_drag(originSlot, e) {
|
||||||
e.dataTransfer.setData('text/html', e.currentTarget);
|
e.dataTransfer.setData('text/html', e.currentTarget);
|
||||||
e.dataTransfer.effectAllowed = 'all';
|
e.dataTransfer.effectAllowed = 'all';
|
||||||
this.setState({ originSlot });
|
this.setState({ originSlot, copy: e.getModifierState('Alt') });
|
||||||
this._close();
|
this._close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,7 +91,9 @@ export default class SlotSection extends TranslatedComponent {
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
let os = this.state.originSlot;
|
let os = this.state.originSlot;
|
||||||
if (os) {
|
if (os) {
|
||||||
e.dataTransfer.dropEffect = os != targetSlot && canMount(this.props.ship, targetSlot, os.m.grp, os.m.class) ? 'copyMove' : 'none';
|
// Show correct icon
|
||||||
|
const effect = this.state.copy ? 'copy' : 'move';
|
||||||
|
e.dataTransfer.dropEffect = os != targetSlot && canMount(this.props.ship, targetSlot, os.m.grp, os.m.class) ? effect : 'none';
|
||||||
this.setState({ targetSlot });
|
this.setState({ targetSlot });
|
||||||
} else {
|
} else {
|
||||||
e.dataTransfer.dropEffect = 'none';
|
e.dataTransfer.dropEffect = 'none';
|
||||||
@@ -114,20 +116,30 @@ export default class SlotSection extends TranslatedComponent {
|
|||||||
* the origin slot will be empty.
|
* the origin slot will be empty.
|
||||||
*/
|
*/
|
||||||
_drop() {
|
_drop() {
|
||||||
let { originSlot, targetSlot } = this.state;
|
let { originSlot, targetSlot, copy } = this.state;
|
||||||
let m = originSlot.m;
|
let m = originSlot.m;
|
||||||
|
|
||||||
if (targetSlot && m && canMount(this.props.ship, targetSlot, m.grp, m.class)) {
|
if (copy) {
|
||||||
// Swap modules if possible
|
// We want to copy the module in to the target slot
|
||||||
if (targetSlot.m && canMount(this.props.ship, originSlot, targetSlot.m.grp, targetSlot.m.class)) {
|
if (targetSlot && canMount(this.props.ship, targetSlot, m.grp, m.class)) {
|
||||||
this.props.ship.use(originSlot, targetSlot.m, true);
|
const mCopy = m.clone();
|
||||||
} else { // Otherwise empty the origin slot
|
this.props.ship.use(targetSlot, mCopy);
|
||||||
this.props.ship.use(originSlot, null, true); // Empty but prevent summary update
|
this.props.onChange();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// We want to move the module in to the target slot, and swap back any module that was originally in the target slot
|
||||||
|
if (targetSlot && m && canMount(this.props.ship, targetSlot, m.grp, m.class)) {
|
||||||
|
// Swap modules if possible
|
||||||
|
if (targetSlot.m && canMount(this.props.ship, originSlot, targetSlot.m.grp, targetSlot.m.class)) {
|
||||||
|
this.props.ship.use(originSlot, targetSlot.m, true);
|
||||||
|
} else { // Otherwise empty the origin slot
|
||||||
|
this.props.ship.use(originSlot, null, true); // Empty but prevent summary update
|
||||||
|
}
|
||||||
|
this.props.ship.use(targetSlot, m); // update target slot
|
||||||
|
this.props.onChange();
|
||||||
}
|
}
|
||||||
this.props.ship.use(targetSlot, m); // update target slot
|
|
||||||
this.props.onChange();
|
|
||||||
}
|
}
|
||||||
this.setState({ originSlot: null, targetSlot: null });
|
this.setState({ originSlot: null, targetSlot: null, copy: null });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ export default class StandardSlot extends TranslatedComponent {
|
|||||||
|
|
||||||
// Modifications tooltip shows blueprint and grade, if available
|
// Modifications tooltip shows blueprint and grade, if available
|
||||||
let modTT = translate('modified');
|
let modTT = translate('modified');
|
||||||
if (m && m.blueprint) {
|
if (m && m.blueprint && m.blueprint.name) {
|
||||||
modTT = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
|
modTT = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -475,6 +475,52 @@ export class MountTurret extends SvgIcon {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collapse section
|
||||||
|
*/
|
||||||
|
export class CollapseSection extends SvgIcon {
|
||||||
|
/**
|
||||||
|
* Overriden view box
|
||||||
|
* @return {String} view box
|
||||||
|
*/
|
||||||
|
viewBox() { return '0 0 200 200'; }
|
||||||
|
/**
|
||||||
|
* Generate the SVG
|
||||||
|
* @return {React.Component} SVG Contents
|
||||||
|
*/
|
||||||
|
svg() {
|
||||||
|
return <g>
|
||||||
|
<path d='m 100,180 0,-140' />
|
||||||
|
<path d='m 100,40 25,45' />
|
||||||
|
<path d='m 100,40 -25,45' />
|
||||||
|
<path d='m 20,20 160,0' />
|
||||||
|
</g>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expand section
|
||||||
|
*/
|
||||||
|
export class ExpandSection extends SvgIcon {
|
||||||
|
/**
|
||||||
|
* Overriden view box
|
||||||
|
* @return {String} view box
|
||||||
|
*/
|
||||||
|
viewBox() { return '0 0 200 200'; }
|
||||||
|
/**
|
||||||
|
* Generate the SVG
|
||||||
|
* @return {React.Component} SVG Contents
|
||||||
|
*/
|
||||||
|
svg() {
|
||||||
|
return <g>
|
||||||
|
<path d='m 100,20 0,140' />
|
||||||
|
<path d='m 100,160 25,-45' />
|
||||||
|
<path d='m 100,160 -25,-45' />
|
||||||
|
<path d='m 20,180 160,0' />
|
||||||
|
</g>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rocket ship
|
* Rocket ship
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ export function getLanguage(langCode) {
|
|||||||
LY: <u> {translate('LY')}</u>, // Light Years
|
LY: <u> {translate('LY')}</u>, // Light Years
|
||||||
MJ: <u> {translate('MJ')}</u>, // Mega Joules
|
MJ: <u> {translate('MJ')}</u>, // Mega Joules
|
||||||
'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
|
||||||
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)
|
||||||
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
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ export const terms = {
|
|||||||
mc: 'Multi-cannon',
|
mc: 'Multi-cannon',
|
||||||
ml: 'Mining Laser',
|
ml: 'Mining Laser',
|
||||||
mr: 'Missile Rack',
|
mr: 'Missile Rack',
|
||||||
|
mrp: 'Module Reinforcement Package',
|
||||||
nl: 'Mine Launcher',
|
nl: 'Mine Launcher',
|
||||||
pa: 'Plasma Accelerator',
|
pa: 'Plasma Accelerator',
|
||||||
pas: 'Planetary Approach Suite',
|
pas: 'Planetary Approach Suite',
|
||||||
@@ -86,15 +87,21 @@ export const terms = {
|
|||||||
ws: 'Frame Shift Wake Scanner',
|
ws: 'Frame Shift Wake Scanner',
|
||||||
|
|
||||||
// Items on the outfitting page
|
// Items on the outfitting page
|
||||||
// Notification of restricted slot for Orca/Beluga
|
// Notification of restricted slot
|
||||||
emptyrestricted: 'empty (restricted)',
|
emptyrestricted: 'empty (restricted)',
|
||||||
|
'damage dealt against': 'Damage dealt against',
|
||||||
|
'damage received by': 'Damage received by',
|
||||||
|
'against shields': 'Against shields',
|
||||||
|
'against hull': 'Against hull',
|
||||||
// 'ammo' was overloaded for outfitting page and modul info, so changed to ammunition for outfitting page
|
// 'ammo' was overloaded for outfitting page and modul info, so changed to ammunition for outfitting page
|
||||||
ammunition: 'Ammo',
|
ammunition: 'Ammo',
|
||||||
|
|
||||||
// Unit for seconds
|
// Unit for seconds
|
||||||
secs: 's',
|
secs: 's',
|
||||||
|
|
||||||
// Weapon, offence and defence
|
rebuildsperbay: 'Rebuilds per bay',
|
||||||
|
|
||||||
|
// 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',
|
||||||
sdps: 'Sustained damage per second',
|
sdps: 'Sustained damage per second',
|
||||||
@@ -106,12 +113,23 @@ export const terms = {
|
|||||||
'damage by': 'Damage by',
|
'damage by': 'Damage by',
|
||||||
'damage from': 'Damage from',
|
'damage from': 'Damage from',
|
||||||
'shield cells': 'Shield cells',
|
'shield cells': 'Shield cells',
|
||||||
|
'recovery': 'Recovery',
|
||||||
|
'recharge': 'Recharge',
|
||||||
|
'engine pips': 'Engine Pips',
|
||||||
|
'4b': '4 pips and boost',
|
||||||
|
'speed': 'Speed',
|
||||||
|
'pitch': 'Pitch',
|
||||||
|
'roll': 'Roll',
|
||||||
|
'yaw': 'Yaw',
|
||||||
|
'internal protection': 'Internal protection',
|
||||||
|
'external protection': 'External protection',
|
||||||
|
|
||||||
// Modifications
|
// Modifications
|
||||||
ammo: 'Ammunition maximum',
|
ammo: 'Ammunition maximum',
|
||||||
boot: 'Boot time',
|
boot: 'Boot time',
|
||||||
brokenregen: 'Broken regeneration rate',
|
brokenregen: 'Broken regeneration rate',
|
||||||
burst: 'Burst',
|
burst: 'Burst',
|
||||||
|
burstrof: 'Burst rate of fire',
|
||||||
clip: 'Ammunition clip',
|
clip: 'Ammunition clip',
|
||||||
damage: 'Damage',
|
damage: 'Damage',
|
||||||
distdraw: 'Distributor draw',
|
distdraw: 'Distributor draw',
|
||||||
@@ -133,10 +151,11 @@ export const terms = {
|
|||||||
pgen: 'Power generation',
|
pgen: 'Power generation',
|
||||||
piercing: 'Piercing',
|
piercing: 'Piercing',
|
||||||
power: 'Power draw',
|
power: 'Power draw',
|
||||||
|
protection: 'Protection',
|
||||||
range: 'Range',
|
range: 'Range',
|
||||||
ranget: 'Range', // Range in time (for FSD interdictor)
|
ranget: 'Range', // Range in time (for FSD interdictor)
|
||||||
regen: 'Regeneration rate',
|
regen: 'Regeneration rate',
|
||||||
reload: 'Reload time',
|
reload: 'Reload',
|
||||||
rof: 'Rate of fire',
|
rof: 'Rate of fire',
|
||||||
shield: 'Shield',
|
shield: 'Shield',
|
||||||
shieldboost: 'Shield boost',
|
shieldboost: 'Shield boost',
|
||||||
|
|||||||
@@ -39,15 +39,6 @@ export default class AboutPage extends Page {
|
|||||||
|
|
||||||
<p>Coriolis is an open source project. Checkout the list of upcoming features and to-do list on github. Any and all contributions and feedback are welcome. If you encounter any bugs please report them and provide as much detail as possible.</p>
|
<p>Coriolis is an open source project. Checkout the list of upcoming features and to-do list on github. Any and all contributions and feedback are welcome. If you encounter any bugs please report them and provide as much detail as possible.</p>
|
||||||
|
|
||||||
<form action='https://www.paypal.com/cgi-bin/webscr' method='post' target='_blank'>
|
|
||||||
<input type='hidden' name='cmd' value='_s-xclick' />
|
|
||||||
<input type='hidden' name='encrypted' value='-----BEGIN PKCS7-----MIIHLwYJKoZIhvcNAQcEoIIHIDCCBxwCAQExggEwMIIBLAIBADCBlDCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb20CAQAwDQYJKoZIhvcNAQEBBQAEgYCjl3XoqQ3Q+x/qS7Va1lwvF0IgUs8gBbrwj1/uEv/xFyPSB2G0kgWqiB2c/8vvfcjjyMr4nlzLUlmQ0yl1zZaeTXFciN5a+JsvaBISThIlN9UP7PXP61TVHCECtt/hBNtlOmg8/gG8khJCj8+qi81XsNAz5bEDpdahKW3fwGHD4jELMAkGBSsOAwIaBQAwgawGCSqGSIb3DQEHATAUBggqhkiG9w0DBwQI4EVkn3RE+9qAgYhg2sTmY1Gul2yJyLYJZPRMO/PwgzEogb2NlIcshJSO+KvBea5NjjTXN2EJNqJa24h4lGA1mdrSgzTGDrVbdcnuti9+7ggn5R5s5IwEEQnN4JQx3IAqsp3UmJbti5t776Ns50nQbjA8NzxI+gwUmIvUQaVs6wC4HYXG6q8QtqUIWeVDhvbnt+H8oIIDhzCCA4MwggLsoAMCAQICAQAwDQYJKoZIhvcNAQEFBQAwgY4xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLUGF5UGFsIEluYy4xEzARBgNVBAsUCmxpdmVfY2VydHMxETAPBgNVBAMUCGxpdmVfYXBpMRwwGgYJKoZIhvcNAQkBFg1yZUBwYXlwYWwuY29tMB4XDTA0MDIxMzEwMTMxNVoXDTM1MDIxMzEwMTMxNVowgY4xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLUGF5UGFsIEluYy4xEzARBgNVBAsUCmxpdmVfY2VydHMxETAPBgNVBAMUCGxpdmVfYXBpMRwwGgYJKoZIhvcNAQkBFg1yZUBwYXlwYWwuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDBR07d/ETMS1ycjtkpkvjXZe9k+6CieLuLsPumsJ7QC1odNz3sJiCbs2wC0nLE0uLGaEtXynIgRqIddYCHx88pb5HTXv4SZeuv0Rqq4+axW9PLAAATU8w04qqjaSXgbGLP3NmohqM6bV9kZZwZLR/klDaQGo1u9uDb9lr4Yn+rBQIDAQABo4HuMIHrMB0GA1UdDgQWBBSWn3y7xm8XvVk/UtcKG+wQ1mSUazCBuwYDVR0jBIGzMIGwgBSWn3y7xm8XvVk/UtcKG+wQ1mSUa6GBlKSBkTCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb22CAQAwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQCBXzpWmoBa5e9fo6ujionW1hUhPkOBakTr3YCDjbYfvJEiv/2P+IobhOGJr85+XHhN0v4gUkEDI8r2/rNk1m0GA8HKddvTjyGw/XqXa+LSTlDYkqI8OwR8GEYj4efEtcRpRYBxV8KxAW93YDWzFGvruKnnLbDAF6VR5w/cCMn5hzGCAZowggGWAgEBMIGUMIGOMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxFDASBgNVBAoTC1BheVBhbCBJbmMuMRMwEQYDVQQLFApsaXZlX2NlcnRzMREwDwYDVQQDFAhsaXZlX2FwaTEcMBoGCSqGSIb3DQEJARYNcmVAcGF5cGFsLmNvbQIBADAJBgUrDgMCGgUAoF0wGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTUwNjA1MjEwNDQzWjAjBgkqhkiG9w0BCQQxFgQUx6Bs50H7tbYJln13pP5J7J1KiSUwDQYJKoZIhvcNAQEBBQAEgYBiOr1RX38uvwghuIZxKpjXX4LG/GoyYM6citfsBD5vjUGj0udmsamjlur+dwxJNs9dULnJO6huoTvxqxTui0Mh3n21YKoMqVE/erfNk2XygrJw9bEtW+HXjU3F+OGKR7dfD9STp2ZlvTEvZR9JRV5A/udC9/9U9eD5iLKRRwkIBg==-----END PKCS7-----' />
|
|
||||||
<input type='image' src='https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif' name='submit' alt='PayPal - Donate to Coriolis.io' style={{ border:'none' }} />
|
|
||||||
<img alt='' border='0' src='https://www.paypalobjects.com/en_US/i/scr/pixel.gif' width='1' height='1' />
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<p>Help keep the lights on! Donations will be used to cover costs of running and maintaining Coriolis. Thanks for helping!</p>
|
|
||||||
|
|
||||||
<h3>Chat</h3>
|
<h3>Chat</h3>
|
||||||
<p>You can chat to us on our <a href='https://discord.gg/0uwCh6R62aPRjk9w' target='_blank'>EDCD Discord server</a>.</p>
|
<p>You can chat to us on our <a href='https://discord.gg/0uwCh6R62aPRjk9w' target='_blank'>EDCD Discord server</a>.</p>
|
||||||
</div>;
|
</div>;
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ import InternalSlotSection from '../components/InternalSlotSection';
|
|||||||
import UtilitySlotSection from '../components/UtilitySlotSection';
|
import UtilitySlotSection from '../components/UtilitySlotSection';
|
||||||
import OffenceSummary from '../components/OffenceSummary';
|
import OffenceSummary from '../components/OffenceSummary';
|
||||||
import DefenceSummary from '../components/DefenceSummary';
|
import DefenceSummary from '../components/DefenceSummary';
|
||||||
|
import MovementSummary from '../components/MovementSummary';
|
||||||
|
import DamageDealt from '../components/DamageDealt';
|
||||||
|
import DamageReceived from '../components/DamageReceived';
|
||||||
import LineChart from '../components/LineChart';
|
import LineChart from '../components/LineChart';
|
||||||
import PowerManagement from '../components/PowerManagement';
|
import PowerManagement from '../components/PowerManagement';
|
||||||
import CostSection from '../components/CostSection';
|
import CostSection from '../components/CostSection';
|
||||||
@@ -335,7 +338,7 @@ export default class OutfittingPage extends Page {
|
|||||||
<PowerManagement ship={ship} code={code} onChange={shipUpdated} />
|
<PowerManagement ship={ship} code={code} onChange={shipUpdated} />
|
||||||
<CostSection ship={ship} buildName={buildName} code={sStr + hStr + iStr} />
|
<CostSection ship={ship} buildName={buildName} code={sStr + hStr + iStr} />
|
||||||
|
|
||||||
<div ref='chartThird' className='group third'>
|
<div className='group third'>
|
||||||
<OffenceSummary ship={ship} code={code}/>
|
<OffenceSummary ship={ship} code={code}/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -344,22 +347,21 @@ export default class OutfittingPage extends Page {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='group third'>
|
<div className='group third'>
|
||||||
<h1>{translate('speed')}</h1>
|
<MovementSummary ship={ship} code={code}/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div ref='chartThird' className='group third'>
|
||||||
|
<h1>{translate('jump range')}</h1>
|
||||||
<LineChart
|
<LineChart
|
||||||
width={chartWidth}
|
width={chartWidth}
|
||||||
xMax={ship.cargoCapacity}
|
xMax={ship.cargoCapacity}
|
||||||
yMax={ship.topBoost + 10}
|
yMax={ship.unladenRange}
|
||||||
xUnit={translate('T')}
|
xUnit={translate('T')}
|
||||||
yUnit={translate('m/s')}
|
yUnit={translate('LY')}
|
||||||
yLabel={translate('speed')}
|
yLabel={translate('jump range')}
|
||||||
series={SPEED_SERIES}
|
|
||||||
colors={SPEED_COLORS}
|
|
||||||
xLabel={translate('cargo')}
|
xLabel={translate('cargo')}
|
||||||
func={state.speedChartFunc}
|
func={state.jumpRangeChartFunc}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='group half'>
|
|
||||||
<table style={{ width: '100%', lineHeight: '1em', backgroundColor: 'transparent' }}>
|
<table style={{ width: '100%', lineHeight: '1em', backgroundColor: 'transparent' }}>
|
||||||
<tbody >
|
<tbody >
|
||||||
<tr>
|
<tr>
|
||||||
@@ -385,7 +387,15 @@ export default class OutfittingPage extends Page {
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<DamageDealt ship={ship} code={code} currentMenu={menu}/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<DamageReceived ship={ship} code={code} currentMenu={menu}/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -402,3 +412,43 @@ export default class OutfittingPage extends Page {
|
|||||||
// func={state.jumpRangeChartFunc}
|
// func={state.jumpRangeChartFunc}
|
||||||
// />
|
// />
|
||||||
// </div>
|
// </div>
|
||||||
|
// <div className='group third'>
|
||||||
|
// <h1>{translate('speed')}</h1>
|
||||||
|
// <LineChart
|
||||||
|
// width={chartWidth}
|
||||||
|
// xMax={ship.cargoCapacity}
|
||||||
|
// yMax={ship.topBoost + 10}
|
||||||
|
// xUnit={translate('T')}
|
||||||
|
// yUnit={translate('m/s')}
|
||||||
|
// yLabel={translate('speed')}
|
||||||
|
// series={SPEED_SERIES}
|
||||||
|
// colors={SPEED_COLORS}
|
||||||
|
// xLabel={translate('cargo')}
|
||||||
|
// func={state.speedChartFunc}
|
||||||
|
// />
|
||||||
|
// </div>
|
||||||
|
// <div className='group half'>
|
||||||
|
// <table style={{ width: '100%', lineHeight: '1em', backgroundColor: 'transparent' }}>
|
||||||
|
// <tbody >
|
||||||
|
// <tr>
|
||||||
|
// <td style={{ verticalAlign: 'top', padding: 0, width: '2.5em' }} onMouseEnter={termtip.bind(null, 'fuel level')} onMouseLeave={hide}>
|
||||||
|
// <Fuel className='xl primary-disabled' />
|
||||||
|
// </td>
|
||||||
|
// <td>
|
||||||
|
// <Slider
|
||||||
|
// axis={true}
|
||||||
|
// onChange={this._fuelChange}
|
||||||
|
// axisUnit={translate('T')}
|
||||||
|
// percent={fuelLevel}
|
||||||
|
// max={fuelCapacity}
|
||||||
|
// scale={sizeRatio}
|
||||||
|
// onResize={onWindowResize}
|
||||||
|
// />
|
||||||
|
// </td>
|
||||||
|
// <td className='primary' style={{ width: '10em', verticalAlign: 'top', fontSize: '0.9em', textAlign: 'left' }}>
|
||||||
|
// {formats.f2(fuelLevel * fuelCapacity)}{units.T} {formats.pct1(fuelLevel)}
|
||||||
|
// </td>
|
||||||
|
// </tr>
|
||||||
|
// </tbody>
|
||||||
|
// </table>
|
||||||
|
// </div>
|
||||||
|
|||||||
@@ -40,7 +40,9 @@ function shipSummary(shipId, shipData) {
|
|||||||
intCount: 0,
|
intCount: 0,
|
||||||
maxCargo: 0,
|
maxCargo: 0,
|
||||||
hp: [0, 0, 0, 0, 0], // Utility, Small, Medium, Large, Huge
|
hp: [0, 0, 0, 0, 0], // Utility, Small, Medium, Large, Huge
|
||||||
int: [0, 0, 0, 0, 0, 0, 0, 0] // Sizes 1 - 8
|
int: [0, 0, 0, 0, 0, 0, 0, 0], // Sizes 1 - 8
|
||||||
|
standard: shipData.slots.standard,
|
||||||
|
agility: shipData.properties.pitch + shipData.properties.yaw + shipData.properties.roll
|
||||||
};
|
};
|
||||||
Object.assign(summary, shipData.properties);
|
Object.assign(summary, shipData.properties);
|
||||||
let ship = new Ship(shipId, shipData.properties, shipData.slots);
|
let ship = new Ship(shipId, shipData.properties, shipData.slots);
|
||||||
@@ -139,7 +141,8 @@ export default class ShipyardPage extends Page {
|
|||||||
>
|
>
|
||||||
<td className='le'>{s.manufacturer}</td>
|
<td className='le'>{s.manufacturer}</td>
|
||||||
<td className='cap'>{translate(SizeMap[s.class])}</td>
|
<td className='cap'>{translate(SizeMap[s.class])}</td>
|
||||||
<td>{s.agility}</td>
|
<td className='ri'>{fInt(s.agility)}</td>
|
||||||
|
<td className='ri'>{fInt(s.hardness)}</td>
|
||||||
<td className='ri'>{fInt(s.speed)}{u['m/s']}</td>
|
<td className='ri'>{fInt(s.speed)}{u['m/s']}</td>
|
||||||
<td className='ri'>{fInt(s.boost)}{u['m/s']}</td>
|
<td className='ri'>{fInt(s.boost)}{u['m/s']}</td>
|
||||||
<td className='ri'>{fInt(s.baseArmour)}</td>
|
<td className='ri'>{fInt(s.baseArmour)}</td>
|
||||||
@@ -148,6 +151,12 @@ export default class ShipyardPage extends Page {
|
|||||||
<td className='ri'>{fInt(s.topBoost)}{u['m/s']}</td>
|
<td className='ri'>{fInt(s.topBoost)}{u['m/s']}</td>
|
||||||
<td className='ri'>{fRound(s.maxJumpRange)}{u.LY}</td>
|
<td className='ri'>{fRound(s.maxJumpRange)}{u.LY}</td>
|
||||||
<td className='ri'>{fInt(s.maxCargo)}{u.T}</td>
|
<td className='ri'>{fInt(s.maxCargo)}{u.T}</td>
|
||||||
|
<td className='cn'>{s.standard[0]}</td>
|
||||||
|
<td className='cn'>{s.standard[1]}</td>
|
||||||
|
<td className='cn'>{s.standard[2]}</td>
|
||||||
|
<td className='cn'>{s.standard[3]}</td>
|
||||||
|
<td className='cn'>{s.standard[4]}</td>
|
||||||
|
<td className='cn'>{s.standard[5]}</td>
|
||||||
<td className={cn({ disabled: !s.hp[1] })}>{s.hp[1]}</td>
|
<td className={cn({ disabled: !s.hp[1] })}>{s.hp[1]}</td>
|
||||||
<td className={cn({ disabled: !s.hp[2] })}>{s.hp[2]}</td>
|
<td className={cn({ disabled: !s.hp[2] })}>{s.hp[2]}</td>
|
||||||
<td className={cn({ disabled: !s.hp[3] })}>{s.hp[3]}</td>
|
<td className={cn({ disabled: !s.hp[3] })}>{s.hp[3]}</td>
|
||||||
@@ -260,9 +269,11 @@ export default class ShipyardPage extends Page {
|
|||||||
<tr className='main'>
|
<tr className='main'>
|
||||||
<th rowSpan={2} className='sortable' onClick={sortShips('manufacturer')}>{translate('manufacturer')}</th>
|
<th rowSpan={2} className='sortable' onClick={sortShips('manufacturer')}>{translate('manufacturer')}</th>
|
||||||
<th rowSpan={2} className='sortable' onClick={sortShips('class')}>{translate('size')}</th>
|
<th rowSpan={2} className='sortable' onClick={sortShips('class')}>{translate('size')}</th>
|
||||||
<th rowSpan={2} className='sortable' onMouseEnter={termtip.bind(null, 'maneuverability')} onMouseLeave={hide} onClick={sortShips('agility')}>{translate('mnv')}</th>
|
<th rowSpan={2} className='sortable' onClick={sortShips('agility')}>{translate('agility')}</th>
|
||||||
|
<th rowSpan={2} className='sortable' onClick={sortShips('hardness')}>{translate('hardness')}</th>
|
||||||
<th colSpan={4}>{translate('base')}</th>
|
<th colSpan={4}>{translate('base')}</th>
|
||||||
<th colSpan={4}>{translate('max')}</th>
|
<th colSpan={4}>{translate('max')}</th>
|
||||||
|
<th colSpan={6}>{translate('core module classes')}</th>
|
||||||
<th colSpan={5} className='sortable' onClick={sortShips('hpCount')}>{translate('hardpoints')}</th>
|
<th colSpan={5} className='sortable' onClick={sortShips('hpCount')}>{translate('hardpoints')}</th>
|
||||||
<th colSpan={8} className='sortable' onClick={sortShips('intCount')}>{translate('internal compartments')}</th>
|
<th colSpan={8} className='sortable' onClick={sortShips('intCount')}>{translate('internal compartments')}</th>
|
||||||
<th rowSpan={2} className='sortable' onClick={sortShips('hullMass')}>{translate('hull')}</th>
|
<th rowSpan={2} className='sortable' onClick={sortShips('hullMass')}>{translate('hull')}</th>
|
||||||
@@ -280,6 +291,13 @@ export default class ShipyardPage extends Page {
|
|||||||
<th className='sortable' onClick={sortShips('maxJumpRange')}>{translate('jump')}</th>
|
<th className='sortable' onClick={sortShips('maxJumpRange')}>{translate('jump')}</th>
|
||||||
<th className='sortable' onClick={sortShips('maxCargo')}>{translate('cargo')}</th>
|
<th className='sortable' onClick={sortShips('maxCargo')}>{translate('cargo')}</th>
|
||||||
|
|
||||||
|
<th className='sortable lft' onMouseEnter={termtip.bind(null, 'power plant')} onMouseLeave={hide} onClick={sortShips('standard', 0)}>{'pp'}</th>
|
||||||
|
<th className='sortable' onMouseEnter={termtip.bind(null, 'thrusters')} onMouseLeave={hide} onClick={sortShips('standard', 1)}>{'th'}</th>
|
||||||
|
<th className='sortable' onMouseEnter={termtip.bind(null, 'frame shift drive')} onMouseLeave={hide} onClick={sortShips('standard', 2)}>{'fsd'}</th>
|
||||||
|
<th className='sortable' onMouseEnter={termtip.bind(null, 'life support')} onMouseLeave={hide} onClick={sortShips('standard', 3)}>{'ls'}</th>
|
||||||
|
<th className='sortable' onMouseEnter={termtip.bind(null, 'power distriubtor')} onMouseLeave={hide} onClick={sortShips('standard', 4)}>{'pd'}</th>
|
||||||
|
<th className='sortable' onMouseEnter={termtip.bind(null, 'sensors')} onMouseLeave={hide} onClick={sortShips('standard', 5)}>{'s'}</th>
|
||||||
|
|
||||||
<th className='sortable lft' onClick={sortShips('hp',1)}>{translate('S')}</th>
|
<th className='sortable lft' onClick={sortShips('hp',1)}>{translate('S')}</th>
|
||||||
<th className='sortable' onClick={sortShips('hp', 2)}>{translate('M')}</th>
|
<th className='sortable' onClick={sortShips('hp', 2)}>{translate('M')}</th>
|
||||||
<th className='sortable' onClick={sortShips('hp', 3)}>{translate('L')}</th>
|
<th className='sortable' onClick={sortShips('hp', 3)}>{translate('L')}</th>
|
||||||
|
|||||||
@@ -67,32 +67,109 @@ export function shieldStrength(mass, baseShield, sg, multiplier) {
|
|||||||
/**
|
/**
|
||||||
* Calculate the a ships speed based on mass, and thrusters.
|
* Calculate the a ships speed based on mass, and thrusters.
|
||||||
*
|
*
|
||||||
* @param {number} mass Current mass of the ship
|
* @param {number} mass the mass of the ship
|
||||||
* @param {number} baseSpeed Base speed m/s for ship
|
* @param {number} baseSpeed base speed m/s for ship
|
||||||
* @param {number} baseBoost Base boost speed m/s for ship
|
* @param {object} thrusters The ship's thrusters
|
||||||
* @param {object} thrusters The Thrusters used
|
* @param {number} engpip the multiplier per pip to engines
|
||||||
* @param {number} pipSpeed Speed pip multiplier
|
* @return {array} Speed by pips
|
||||||
* @return {object} Approximate speed by pips
|
|
||||||
*/
|
*/
|
||||||
export function speed(mass, baseSpeed, baseBoost, thrusters, pipSpeed) {
|
export function speed(mass, baseSpeed, thrusters, engpip) {
|
||||||
|
// thrusters might be a module or a template; handle either here
|
||||||
|
const minMass = thrusters instanceof Module ? thrusters.getMinMass() : thrusters.minmass;
|
||||||
|
const optMass = thrusters instanceof Module ? thrusters.getOptMass() : thrusters.optmass;
|
||||||
|
const maxMass = thrusters instanceof Module ? thrusters.getMaxMass() : thrusters.maxmass;
|
||||||
|
const minMul = thrusters instanceof Module ? thrusters.getMinMul('speed') : (thrusters.minmulspeed ? thrusters.minmulspeed : thrusters.minmul);
|
||||||
|
const optMul = thrusters instanceof Module ? thrusters.getOptMul('speed') : (thrusters.optmulspeed ? thrusters.minmulspeed : thrusters.minmul);
|
||||||
|
const maxMul = thrusters instanceof Module ? thrusters.getMaxMul('speed') : (thrusters.maxmulspeed ? thrusters.minmulspeed : thrusters.minmul);
|
||||||
|
|
||||||
|
let results = normValues(minMass, optMass, maxMass, minMul, optMul, maxMul, mass, baseSpeed, engpip);
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate pitch of a ship based on mass and thrusters
|
||||||
|
* @param {number} mass the mass of the ship
|
||||||
|
* @param {number} basePitch base pitch of the ship
|
||||||
|
* @param {object} thrusters the ship's thrusters
|
||||||
|
* @param {number} engpip the multiplier per pip to engines
|
||||||
|
* @return {array} Pitch by pips
|
||||||
|
*/
|
||||||
|
export function pitch(mass, basePitch, thrusters, engpip) {
|
||||||
// thrusters might be a module or a template; handle either here
|
// thrusters might be a module or a template; handle either here
|
||||||
let minMass = thrusters instanceof Module ? thrusters.getMinMass() : thrusters.minmass;
|
let minMass = thrusters instanceof Module ? thrusters.getMinMass() : thrusters.minmass;
|
||||||
let optMass = thrusters instanceof Module ? thrusters.getOptMass() : thrusters.optmass;
|
let optMass = thrusters instanceof Module ? thrusters.getOptMass() : thrusters.optmass;
|
||||||
let maxMass = thrusters instanceof Module ? thrusters.getMaxMass() : thrusters.maxmass;
|
let maxMass = thrusters instanceof Module ? thrusters.getMaxMass() : thrusters.maxmass;
|
||||||
let minMul = thrusters instanceof Module ? thrusters.getMinMul() : thrusters.minmul;
|
let minMul = thrusters instanceof Module ? thrusters.getMinMul('rotation') : (thrusters.minmulrotation ? thrusters.minmulrotation : thrusters.minmul);
|
||||||
let optMul = thrusters instanceof Module ? thrusters.getOptMul() : thrusters.optmul;
|
let optMul = thrusters instanceof Module ? thrusters.getOptMul('rotation') : (thrusters.optmulrotation ? thrusters.optmulrotation : thrusters.optmul);
|
||||||
let maxMul = thrusters instanceof Module ? thrusters.getMaxMul() : thrusters.maxmul;
|
let maxMul = thrusters instanceof Module ? thrusters.getMaxMul('rotation') : (thrusters.maxmulrotation ? thrusters.maxmulrotation : thrusters.maxmul);
|
||||||
|
|
||||||
let xnorm = Math.min(1, (maxMass - mass) / (maxMass - minMass));
|
return normValues(minMass, optMass, maxMass, minMul, optMul, maxMul, mass, basePitch, engpip);
|
||||||
let exponent = Math.log((optMul - minMul) / (maxMul - minMul)) / Math.log(Math.min(1, (maxMass - optMass) / (maxMass - minMass)));
|
}
|
||||||
let ynorm = Math.pow(xnorm, exponent);
|
|
||||||
let mul = minMul + ynorm * (maxMul - minMul);
|
/**
|
||||||
let speed = baseSpeed * mul;
|
* Calculate yaw of a ship based on mass and thrusters
|
||||||
|
* @param {number} mass the mass of the ship
|
||||||
return {
|
* @param {number} baseYaw base yaw of the ship
|
||||||
'0 Pips': speed * (1 - (pipSpeed * 4)),
|
* @param {object} thrusters the ship's thrusters
|
||||||
'2 Pips': speed * (1 - (pipSpeed * 2)),
|
* @param {number} engpip the multiplier per pip to engines
|
||||||
'4 Pips': speed,
|
* @return {array} Yaw by pips
|
||||||
'boost': baseBoost * mul
|
*/
|
||||||
};
|
export function yaw(mass, baseYaw, thrusters, engpip) {
|
||||||
|
// thrusters might be a module or a template; handle either here
|
||||||
|
let minMass = thrusters instanceof Module ? thrusters.getMinMass() : thrusters.minmass;
|
||||||
|
let optMass = thrusters instanceof Module ? thrusters.getOptMass() : thrusters.optmass;
|
||||||
|
let maxMass = thrusters instanceof Module ? thrusters.getMaxMass() : thrusters.maxmass;
|
||||||
|
let minMul = thrusters instanceof Module ? thrusters.getMinMul('rotation') : (thrusters.minmulrotation ? thrusters.minmulrotation : thrusters.minmul);
|
||||||
|
let optMul = thrusters instanceof Module ? thrusters.getOptMul('rotation') : (thrusters.optmulrotation ? thrusters.optmulrotation : thrusters.optmul);
|
||||||
|
let maxMul = thrusters instanceof Module ? thrusters.getMaxMul('rotation') : (thrusters.maxmulrotation ? thrusters.maxmulrotation : thrusters.maxmul);
|
||||||
|
|
||||||
|
return normValues(minMass, optMass, maxMass, minMul, optMul, maxMul, mass, baseYaw, engpip);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate roll of a ship based on mass and thrusters
|
||||||
|
* @param {number} mass the mass of the ship
|
||||||
|
* @param {number} baseRoll base roll of the ship
|
||||||
|
* @param {object} thrusters the ship's thrusters
|
||||||
|
* @param {number} engpip the multiplier per pip to engines
|
||||||
|
* @return {array} Roll by pips
|
||||||
|
*/
|
||||||
|
export function roll(mass, baseRoll, thrusters, engpip) {
|
||||||
|
// thrusters might be a module or a template; handle either here
|
||||||
|
let minMass = thrusters instanceof Module ? thrusters.getMinMass() : thrusters.minmass;
|
||||||
|
let optMass = thrusters instanceof Module ? thrusters.getOptMass() : thrusters.optmass;
|
||||||
|
let maxMass = thrusters instanceof Module ? thrusters.getMaxMass() : thrusters.maxmass;
|
||||||
|
let minMul = thrusters instanceof Module ? thrusters.getMinMul('rotation') : (thrusters.minmulrotation ? thrusters.minmulrotation : thrusters.minmul);
|
||||||
|
let optMul = thrusters instanceof Module ? thrusters.getOptMul('rotation') : (thrusters.optmulrotation ? thrusters.optmulrotation : thrusters.optmul);
|
||||||
|
let maxMul = thrusters instanceof Module ? thrusters.getMaxMul('rotation') : (thrusters.maxmulrotation ? thrusters.maxmulrotation : thrusters.maxmul);
|
||||||
|
|
||||||
|
return normValues(minMass, optMass, maxMass, minMul, optMul, maxMul, mass, baseRoll, engpip);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalise according to FD's calculations and return suitable values
|
||||||
|
* @param {number} minMass the minimum mass of the thrusters
|
||||||
|
* @param {number} optMass the optimum mass of the thrusters
|
||||||
|
* @param {number} maxMass the maximum mass of the thrusters
|
||||||
|
* @param {number} minMul the minimum multiplier of the thrusters
|
||||||
|
* @param {number} optMul the optimum multiplier of the thrusters
|
||||||
|
* @param {number} maxMul the maximum multiplier of the thrusters
|
||||||
|
* @param {number} mass the mass of the ship
|
||||||
|
* @param {base} base the base value from which to calculate
|
||||||
|
* @param {number} engpip the multiplier per pip to engines
|
||||||
|
* @return {array} values by pips
|
||||||
|
*/
|
||||||
|
function normValues(minMass, optMass, maxMass, minMul, optMul, maxMul, mass, base, engpip) {
|
||||||
|
const xnorm = Math.min(1, (maxMass - mass) / (maxMass - minMass));
|
||||||
|
const exponent = Math.log((optMul - minMul) / (maxMul - minMul)) / Math.log(Math.min(1, (maxMass - optMass) / (maxMass - minMass)));
|
||||||
|
const ynorm = Math.pow(xnorm, exponent);
|
||||||
|
const mul = minMul + ynorm * (maxMul - minMul);
|
||||||
|
const res = base * mul;
|
||||||
|
|
||||||
|
return [res * (1 - (engpip * 4)),
|
||||||
|
res * (1 - (engpip * 3)),
|
||||||
|
res * (1 - (engpip * 2)),
|
||||||
|
res * (1 - (engpip * 1)),
|
||||||
|
res];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,8 +105,9 @@ export const BulkheadNames = [
|
|||||||
export const ShipFacets = [
|
export const ShipFacets = [
|
||||||
{ // 0
|
{ // 0
|
||||||
title: 'agility',
|
title: 'agility',
|
||||||
props: ['agility'],
|
props: ['topPitch', 'topRoll', 'topYaw'],
|
||||||
fmt: 'int',
|
lbls: ['pitch', 'roll', 'yaw'],
|
||||||
|
fmt: 'f1',
|
||||||
i: 0
|
i: 0
|
||||||
},
|
},
|
||||||
{ // 1
|
{ // 1
|
||||||
@@ -185,11 +186,18 @@ export const ShipFacets = [
|
|||||||
},
|
},
|
||||||
{ // 11
|
{ // 11
|
||||||
title: 'DPS',
|
title: 'DPS',
|
||||||
props: ['totalDps'],
|
props: ['totalDps', 'totalExplDps', 'totalKinDps', 'totalThermDps'],
|
||||||
lbls: ['DPS'],
|
lbls: ['total', 'explosive', 'kinetic', 'thermal'],
|
||||||
fmt: 'round',
|
fmt: 'round',
|
||||||
i: 11
|
i: 11
|
||||||
},
|
},
|
||||||
|
{ // 14
|
||||||
|
title: 'Sustained DPS',
|
||||||
|
props: ['totalSDps', 'totalExplSDps', 'totalKinSDps', 'totalThermSDps'],
|
||||||
|
lbls: ['total', 'explosive', 'kinetic', 'thermal'],
|
||||||
|
fmt: 'round',
|
||||||
|
i: 14
|
||||||
|
},
|
||||||
{ // 12
|
{ // 12
|
||||||
title: 'EPS',
|
title: 'EPS',
|
||||||
props: ['totalEps'],
|
props: ['totalEps'],
|
||||||
|
|||||||
@@ -12,20 +12,28 @@ export default class Module {
|
|||||||
* @param {Object} params Module parameters. Either grp/id or template
|
* @param {Object} params Module parameters. Either grp/id or template
|
||||||
*/
|
*/
|
||||||
constructor(params) {
|
constructor(params) {
|
||||||
let properties = Object.assign({ grp: null, id: null, template: null, }, params);
|
let properties = Object.assign({ grp: null, id: null, template: null }, params);
|
||||||
|
|
||||||
let template;
|
if (properties.class != undefined) {
|
||||||
if (properties.template == undefined) {
|
// We already have a fully-formed module; copy the data over
|
||||||
return ModuleUtils.findModule(properties.grp, properties.id);
|
for (let p in properties) { this[p] = properties[p]; }
|
||||||
|
} else if (properties.template != undefined) {
|
||||||
|
// We have a template from coriolis-data; copy the data over
|
||||||
|
for (let p in properties.template) { this[p] = properties.template[p]; }
|
||||||
} else {
|
} else {
|
||||||
template = properties.template;
|
// We don't have a template; find it given the group and ID
|
||||||
if (template) {
|
return ModuleUtils.findModule(properties.grp, properties.id);
|
||||||
// Copy all properties from coriolis-data template
|
|
||||||
for (let p in template) { this[p] = template[p]; }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clone an existing module
|
||||||
|
* @return {Object} A clone of the existing module
|
||||||
|
*/
|
||||||
|
clone() {
|
||||||
|
return new Module(JSON.parse(JSON.stringify(this)));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a value for a given modification
|
* Get a value for a given modification
|
||||||
* @param {Number} name The name of the modification
|
* @param {Number} name The name of the modification
|
||||||
@@ -302,6 +310,14 @@ export default class Module {
|
|||||||
return this._getModifiedValue('hullreinforcement');
|
return this._getModifiedValue('hullreinforcement');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the protection for this module, taking in to account modifications
|
||||||
|
* @return {Number} the protection of this module
|
||||||
|
*/
|
||||||
|
getProtection() {
|
||||||
|
return this._getModifiedValue('protection');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the delay for this module, taking in to account modifications
|
* Get the delay for this module, taking in to account modifications
|
||||||
* @return {Number} the delay of this module
|
* @return {Number} the delay of this module
|
||||||
@@ -370,42 +386,60 @@ export default class Module {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the minimum multiplier for this module, taking in to account modifications
|
* Get the minimum multiplier for this module, taking in to account modifications
|
||||||
|
* @param {string} type the type for which we are obtaining the multiplier. Can be 'speed', 'rotation', 'acceleration', or null
|
||||||
* @return {Number} the minimum multiplier of this module
|
* @return {Number} the minimum multiplier of this module
|
||||||
*/
|
*/
|
||||||
getMinMul() {
|
getMinMul(type = null) {
|
||||||
// Modifier is optmul
|
// Modifier is optmul
|
||||||
let result = 0;
|
let result = 0;
|
||||||
if (this['minmul']) {
|
if (this['minmul' + type]) {
|
||||||
|
result = this['minmul' + type];
|
||||||
|
} else if (this['minmul']) {
|
||||||
result = this['minmul'];
|
result = this['minmul'];
|
||||||
if (result) {
|
}
|
||||||
let mult = this.getModValue('optmul') / 10000;
|
if (result) {
|
||||||
if (mult) { result = result * (1 + mult); }
|
let mult = this.getModValue('optmul') / 10000;
|
||||||
}
|
if (mult) { result = result * (1 + mult); }
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the optimum multiplier for this module, taking in to account modifications
|
* Get the optimum multiplier for this module, taking in to account modifications
|
||||||
|
* @param {string} type the type for which we are obtaining the multiplier. Can be 'speed', 'rotation', 'acceleration', or null
|
||||||
* @return {Number} the optimum multiplier of this module
|
* @return {Number} the optimum multiplier of this module
|
||||||
*/
|
*/
|
||||||
getOptMul() {
|
getOptMul(type = null) {
|
||||||
return this._getModifiedValue('optmul');
|
// Modifier is optmul
|
||||||
|
let result = 0;
|
||||||
|
if (this['optmul' + type]) {
|
||||||
|
result = this['optmul' + type];
|
||||||
|
} else if (this['optmul']) {
|
||||||
|
result = this['optmul'];
|
||||||
|
}
|
||||||
|
if (result) {
|
||||||
|
let mult = this.getModValue('optmul') / 10000;
|
||||||
|
if (mult) { result = result * (1 + mult); }
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the maximum multiplier for this module, taking in to account modifications
|
* Get the maximum multiplier for this module, taking in to account modifications
|
||||||
|
* @param {string} type the type for which we are obtaining the multiplier. Can be 'speed', 'rotation', 'acceleration', or null
|
||||||
* @return {Number} the maximum multiplier of this module
|
* @return {Number} the maximum multiplier of this module
|
||||||
*/
|
*/
|
||||||
getMaxMul() {
|
getMaxMul(type = null) {
|
||||||
// Modifier is optmul
|
// Modifier is optmul
|
||||||
let result = 0;
|
let result = 0;
|
||||||
if (this['maxmul']) {
|
if (this['maxmul' + type]) {
|
||||||
|
result = this['maxmul' + type];
|
||||||
|
} else if (this['maxmul']) {
|
||||||
result = this['maxmul'];
|
result = this['maxmul'];
|
||||||
if (result) {
|
}
|
||||||
let mult = this.getModValue('optmul') / 10000;
|
if (result) {
|
||||||
if (mult) { result = result * (1 + mult); }
|
let mult = this.getModValue('optmul') / 10000;
|
||||||
}
|
if (mult) { result = result * (1 + mult); }
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -561,6 +595,14 @@ export default class Module {
|
|||||||
return this._getModifiedValue('shieldreinforcement');
|
return this._getModifiedValue('shieldreinforcement');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the piercing for this module, taking in to account modifications
|
||||||
|
* @return {Number} the piercing for this module
|
||||||
|
*/
|
||||||
|
getPiercing() {
|
||||||
|
return this._getModifiedValue('piercing');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the bays for this module, taking in to account modifications
|
* Get the bays for this module, taking in to account modifications
|
||||||
* @return {Number} the bays for this module
|
* @return {Number} the bays for this module
|
||||||
|
|||||||
@@ -134,67 +134,21 @@ export function toDetailedBuild(buildName, ship) {
|
|||||||
*/
|
*/
|
||||||
export function fromDetailedBuild(detailedBuild) {
|
export function fromDetailedBuild(detailedBuild) {
|
||||||
let shipId = Object.keys(Ships).find((shipId) => Ships[shipId].properties.name.toLowerCase() == detailedBuild.ship.toLowerCase());
|
let shipId = Object.keys(Ships).find((shipId) => Ships[shipId].properties.name.toLowerCase() == detailedBuild.ship.toLowerCase());
|
||||||
|
|
||||||
if (!shipId) {
|
if (!shipId) {
|
||||||
throw 'No such ship: ' + detailedBuild.ship;
|
throw 'No such ship: ' + detailedBuild.ship;
|
||||||
}
|
}
|
||||||
|
|
||||||
let comps = detailedBuild.components;
|
|
||||||
let stn = comps.standard;
|
|
||||||
let priorities = [stn.cargoHatch && stn.cargoHatch.priority !== undefined ? stn.cargoHatch.priority - 1 : 0];
|
|
||||||
let enabled = [stn.cargoHatch && stn.cargoHatch.enabled !== undefined ? stn.cargoHatch.enabled : true];
|
|
||||||
let shipData = Ships[shipId];
|
let shipData = Ships[shipId];
|
||||||
let ship = new Ship(shipId, shipData.properties, shipData.slots);
|
let ship = new Ship(shipId, shipData.properties, shipData.slots);
|
||||||
let bulkheads = ModuleUtils.bulkheadIndex(stn.bulkheads);
|
|
||||||
let modifications = new Array(stn.bulkheads.modifications);
|
|
||||||
let blueprints = new Array(stn.bulkheads.blueprint);
|
|
||||||
|
|
||||||
if (bulkheads < 0) {
|
if (!detailedBuild.references[0] || !detailedBuild.references[0].code) {
|
||||||
throw 'Invalid bulkheads: ' + stn.bulkheads;
|
throw 'Missing reference code';
|
||||||
}
|
}
|
||||||
|
|
||||||
let standard = STANDARD.map((c) => {
|
ship.buildFrom(detailedBuild.references[0].code);
|
||||||
if (!stn[c].class || !stn[c].rating) {
|
|
||||||
throw 'Invalid value for ' + c;
|
|
||||||
}
|
|
||||||
priorities.push(stn[c].priority === undefined ? 0 : stn[c].priority - 1);
|
|
||||||
enabled.push(stn[c].enabled === undefined ? true : stn[c].enabled);
|
|
||||||
modifications.push(stn[c].modifications);
|
|
||||||
blueprints.push(stn[c].blueprint);
|
|
||||||
return ModuleUtils.findStandardId(STANDARD_GROUPS[c], stn[c].class, stn[c].rating, stn[c].name);
|
|
||||||
});
|
|
||||||
|
|
||||||
let internal = comps.internal.map(c => c ? ModuleUtils.findInternalId(c.group, c.class, c.rating, c.name) : 0);
|
|
||||||
|
|
||||||
let hardpoints = comps.hardpoints.map(c => c ? ModuleUtils.findHardpointId(c.group, c.class, c.rating, c.name, MountMap[c.mount], c.missile) : 0)
|
|
||||||
.concat(comps.utility.map(c => c ? ModuleUtils.findHardpointId(c.group, c.class, c.rating, c.name, MountMap[c.mount]) : 0));
|
|
||||||
|
|
||||||
// The ordering of these arrays must match the order in which they are read in Ship.buildWith
|
|
||||||
priorities = priorities.concat(
|
|
||||||
comps.hardpoints.map(c => (!c || c.priority === undefined) ? 0 : c.priority - 1),
|
|
||||||
comps.utility.map(c => (!c || c.priority === undefined) ? 0 : c.priority - 1),
|
|
||||||
comps.internal.map(c => (!c || c.priority === undefined) ? 0 : c.priority - 1)
|
|
||||||
);
|
|
||||||
enabled = enabled.concat(
|
|
||||||
comps.hardpoints.map(c => (!c || c.enabled === undefined) ? true : c.enabled * 1),
|
|
||||||
comps.utility.map(c => (!c || c.enabled === undefined) ? true : c.enabled * 1),
|
|
||||||
comps.internal.map(c => (!c || c.enabled === undefined) ? true : c.enabled * 1)
|
|
||||||
);
|
|
||||||
modifications = modifications.concat(
|
|
||||||
comps.hardpoints.map(c => (c ? c.modifications : null)),
|
|
||||||
comps.utility.map(c => (c ? c.modifications : null)),
|
|
||||||
comps.internal.map(c => (c ? c.modifications : null))
|
|
||||||
);
|
|
||||||
blueprints = blueprints.concat(
|
|
||||||
comps.hardpoints.map(c => (c ? c.blueprint : null)),
|
|
||||||
comps.utility.map(c => (c ? c.blueprint : null)),
|
|
||||||
comps.internal.map(c => (c ? c.blueprint : null))
|
|
||||||
);
|
|
||||||
|
|
||||||
ship.buildWith({ bulkheads, standard, hardpoints, internal }, priorities, enabled, modifications, blueprints);
|
|
||||||
|
|
||||||
return ship;
|
return ship;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates an array of ship-loadout JSON Schema object for export
|
* Generates an array of ship-loadout JSON Schema object for export
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import Module from './Module';
|
|||||||
import LZString from 'lz-string';
|
import LZString from 'lz-string';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import isEqual from 'lodash/lang';
|
import isEqual from 'lodash/lang';
|
||||||
import { Modifications } from 'coriolis-data/dist';
|
import { Ships, Modifications } from 'coriolis-data/dist';
|
||||||
const zlib = require('zlib');
|
const zlib = require('zlib');
|
||||||
|
|
||||||
const UNIQUE_MODULES = ['psg', 'sg', 'bsg', 'rf', 'fs', 'fh'];
|
const UNIQUE_MODULES = ['psg', 'sg', 'bsg', 'rf', 'fs', 'fh'];
|
||||||
@@ -188,10 +188,10 @@ export default class Ship {
|
|||||||
* Calculate the hypothetical top speeds at cargo and fuel tonnage
|
* Calculate the hypothetical top speeds at cargo and fuel tonnage
|
||||||
* @param {Number} fuel Fuel available in tons
|
* @param {Number} fuel Fuel available in tons
|
||||||
* @param {Number} cargo Cargo in tons
|
* @param {Number} cargo Cargo in tons
|
||||||
* @return {Object} Speed at pip settings and boost
|
* @return {array} Speed at pip settings
|
||||||
*/
|
*/
|
||||||
calcSpeedsWith(fuel, cargo) {
|
calcSpeedsWith(fuel, cargo) {
|
||||||
return Calc.speed(this.unladenMass + fuel + cargo, this.speed, this.boost, this.standard[1].m, this.pipSpeed);
|
return Calc.speed(this.unladenMass + fuel + cargo, this.speed, this.standard[1].m, this.pipSpeed);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -201,13 +201,11 @@ export default class Ship {
|
|||||||
* @return {Number} Recovery time in seconds
|
* @return {Number} Recovery time in seconds
|
||||||
*/
|
*/
|
||||||
calcShieldRecovery() {
|
calcShieldRecovery() {
|
||||||
if (this.shield > 0) {
|
const shieldGenerator = this.findShieldGenerator();
|
||||||
const sgSlot = this.findInternalByGroup('sg');
|
if (shieldGenerator) {
|
||||||
if (sgSlot != null) {
|
const brokenRegenRate = shieldGenerator.getBrokenRegenerationRate();
|
||||||
let brokenRegenRate = 1 + sgSlot.m.getModValue('brokenregen') / 10000;
|
// 50% of shield strength / broken recharge rate + 15 second delay before recharge starts
|
||||||
// 50% of shield strength / recovery recharge rate + 15 second delay before recharge starts
|
return ((this.shield / 2) / brokenRegenRate) + 15;
|
||||||
return ((this.shield / 2) / (sgSlot.m.recover * brokenRegenRate)) + 15;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -219,13 +217,12 @@ export default class Ship {
|
|||||||
* @return {Number} 50 - 100% Recharge time in seconds
|
* @return {Number} 50 - 100% Recharge time in seconds
|
||||||
*/
|
*/
|
||||||
calcShieldRecharge() {
|
calcShieldRecharge() {
|
||||||
if (this.shield > 0) {
|
const shieldGenerator = this.findShieldGenerator();
|
||||||
const sgSlot = this.findInternalByGroup('sg');
|
if (shieldGenerator) {
|
||||||
if (sgSlot != null) {
|
const regenRate = shieldGenerator.getRegenerationRate();
|
||||||
let regenRate = 1 + sgSlot.m.getModValue('regen') / 10000;
|
|
||||||
// 50% -> 100% recharge time, Bi-Weave shields charge at 1.8 MJ/s
|
// 50% of shield strength / recharge rate
|
||||||
return (this.shield / 2) / ((sgSlot.m.grp == 'bsg' ? 1.8 : 1) * regenRate);
|
return (this.shield / 2) / regenRate;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -295,12 +292,22 @@ export default class Ship {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the shield generator for this ship
|
||||||
|
* @return {object} The shield generator module for this ship
|
||||||
|
*/
|
||||||
|
findShieldGenerator() {
|
||||||
|
const slot = this.internal.find(slot => slot.m && ModuleUtils.isShieldGenerator(slot.m.grp));
|
||||||
|
return slot ? slot.m : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serializes the ship to a string
|
* Serializes the ship to a string
|
||||||
* @return {String} Serialized ship 'code'
|
* @return {String} Serialized ship 'code'
|
||||||
*/
|
*/
|
||||||
toString() {
|
toString() {
|
||||||
return [
|
return [
|
||||||
|
'A',
|
||||||
this.getStandardString(),
|
this.getStandardString(),
|
||||||
this.getHardpointsString(),
|
this.getHardpointsString(),
|
||||||
this.getInternalString(),
|
this.getInternalString(),
|
||||||
@@ -432,7 +439,7 @@ export default class Ship {
|
|||||||
let newMass = m.getMass();
|
let newMass = m.getMass();
|
||||||
this.unladenMass = this.unladenMass - oldMass + newMass;
|
this.unladenMass = this.unladenMass - oldMass + newMass;
|
||||||
this.ladenMass = this.ladenMass - oldMass + newMass;
|
this.ladenMass = this.ladenMass - oldMass + newMass;
|
||||||
this.updateTopSpeed();
|
this.updateMovement();
|
||||||
this.updateJumpStats();
|
this.updateJumpStats();
|
||||||
} else if (name === 'maxfuel') {
|
} else if (name === 'maxfuel') {
|
||||||
m.setModValue(name, value);
|
m.setModValue(name, value);
|
||||||
@@ -440,19 +447,19 @@ export default class Ship {
|
|||||||
} else if (name === 'optmass') {
|
} else if (name === 'optmass') {
|
||||||
m.setModValue(name, value);
|
m.setModValue(name, value);
|
||||||
// Could be for any of thrusters, FSD or shield
|
// Could be for any of thrusters, FSD or shield
|
||||||
this.updateTopSpeed();
|
this.updateMovement();
|
||||||
this.updateJumpStats();
|
this.updateJumpStats();
|
||||||
this.recalculateShield();
|
this.recalculateShield();
|
||||||
} else if (name === 'optmul') {
|
} else if (name === 'optmul') {
|
||||||
m.setModValue(name, value);
|
m.setModValue(name, value);
|
||||||
// Could be for any of thrusters, FSD or shield
|
// Could be for any of thrusters, FSD or shield
|
||||||
this.updateTopSpeed();
|
this.updateMovement();
|
||||||
this.updateJumpStats();
|
this.updateJumpStats();
|
||||||
this.recalculateShield();
|
this.recalculateShield();
|
||||||
} else if (name === 'shieldboost') {
|
} else if (name === 'shieldboost') {
|
||||||
m.setModValue(name, value);
|
m.setModValue(name, value);
|
||||||
this.recalculateShield();
|
this.recalculateShield();
|
||||||
} else if (name === 'hullboost' || name === 'hullreinforcement') {
|
} else if (name === 'hullboost' || name === 'hullreinforcement' || name === 'modulereinforcement') {
|
||||||
m.setModValue(name, value);
|
m.setModValue(name, value);
|
||||||
this.recalculateArmour();
|
this.recalculateArmour();
|
||||||
} else if (name === 'shieldreinforcement') {
|
} else if (name === 'shieldreinforcement') {
|
||||||
@@ -597,7 +604,7 @@ export default class Ship {
|
|||||||
.recalculateDps()
|
.recalculateDps()
|
||||||
.recalculateEps()
|
.recalculateEps()
|
||||||
.recalculateHps()
|
.recalculateHps()
|
||||||
.updateTopSpeed();
|
.updateMovement();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.updatePowerPrioritesString().updatePowerEnabledString().updateModificationsString();
|
return this.updatePowerPrioritesString().updatePowerEnabledString().updateModificationsString();
|
||||||
@@ -621,6 +628,19 @@ export default class Ship {
|
|||||||
enabled = null,
|
enabled = null,
|
||||||
code = parts[0];
|
code = parts[0];
|
||||||
|
|
||||||
|
// Code has a version ID embedded as the first character (if it is alphabetic)
|
||||||
|
let version;
|
||||||
|
if (code && code.match(/^[0-4]/)) {
|
||||||
|
// Starting with bulkhead number is version 1
|
||||||
|
version = 1;
|
||||||
|
} else {
|
||||||
|
// Version 2 (current version)
|
||||||
|
version = 2;
|
||||||
|
if (code) {
|
||||||
|
code = code.substring(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (parts[1]) {
|
if (parts[1]) {
|
||||||
enabled = LZString.decompressFromBase64(Utils.fromUrlSafe(parts[1])).split('');
|
enabled = LZString.decompressFromBase64(Utils.fromUrlSafe(parts[1])).split('');
|
||||||
}
|
}
|
||||||
@@ -644,6 +664,11 @@ export default class Ship {
|
|||||||
|
|
||||||
decodeToArray(code, internal, decodeToArray(code, hardpoints, decodeToArray(code, standard, 1)));
|
decodeToArray(code, internal, decodeToArray(code, hardpoints, decodeToArray(code, standard, 1)));
|
||||||
|
|
||||||
|
if (version != 2) {
|
||||||
|
// Alter as required due to changes in the (build) code from one version to the next
|
||||||
|
this.upgradeInternals(internal, 1 + this.standard.length + this.hardpoints.length, priorities, enabled, modifications, blueprints, version);
|
||||||
|
}
|
||||||
|
|
||||||
return this.buildWith(
|
return this.buildWith(
|
||||||
{
|
{
|
||||||
bulkheads: code.charAt(0) * 1,
|
bulkheads: code.charAt(0) * 1,
|
||||||
@@ -801,7 +826,7 @@ export default class Ship {
|
|||||||
let epsChanged = n && n.getEps() || old && old.getEps();
|
let epsChanged = n && n.getEps() || old && old.getEps();
|
||||||
let hpsChanged = n && n.getHps() || old && old.getHps();
|
let hpsChanged = n && n.getHps() || old && old.getHps();
|
||||||
|
|
||||||
let armourChange = (slot === this.bulkheads) || (n && n.grp === 'hr') || (old && old.grp === 'hr');
|
let armourChange = (slot === this.bulkheads) || (n && n.grp === 'hr') || (old && old.grp === 'hr') || (n && n.grp === 'mrp') || (old && old.grp === 'mrp');
|
||||||
|
|
||||||
let shieldChange = (n && n.grp === 'bsg') || (old && old.grp === 'bsg') || (n && n.grp === 'psg') || (old && old.grp === 'psg') || (n && n.grp === 'sg') || (old && old.grp === 'sg') || (n && n.grp === 'sb') || (old && old.grp === 'sb');
|
let shieldChange = (n && n.grp === 'bsg') || (old && old.grp === 'bsg') || (n && n.grp === 'psg') || (old && old.grp === 'psg') || (n && n.grp === 'sg') || (old && old.grp === 'sg') || (n && n.grp === 'sb') || (old && old.grp === 'sb');
|
||||||
|
|
||||||
@@ -876,7 +901,7 @@ export default class Ship {
|
|||||||
if (shieldCellsChange) {
|
if (shieldCellsChange) {
|
||||||
this.recalculateShieldCells();
|
this.recalculateShieldCells();
|
||||||
}
|
}
|
||||||
this.updateTopSpeed();
|
this.updateMovement();
|
||||||
this.updateJumpStats();
|
this.updateJumpStats();
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
@@ -1088,13 +1113,23 @@ export default class Ship {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update top speed and boost
|
* Update movement values
|
||||||
* @return {this} The ship instance (for chaining operations)
|
* @return {this} The ship instance (for chaining operations)
|
||||||
*/
|
*/
|
||||||
updateTopSpeed() {
|
updateMovement() {
|
||||||
let speeds = Calc.speed(this.unladenMass + this.fuelCapacity, this.speed, this.boost, this.standard[1].m, this.pipSpeed);
|
this.speeds = Calc.speed(this.unladenMass + this.fuelCapacity, this.speed, this.standard[1].m, this.pipSpeed);
|
||||||
this.topSpeed = speeds['4 Pips'];
|
this.topSpeed = this.speeds[4];
|
||||||
this.topBoost = this.canBoost() ? speeds.boost : 0;
|
this.topBoost = this.canBoost() ? this.speeds[4] * this.boost / this.speed : 0;
|
||||||
|
|
||||||
|
this.pitches = Calc.pitch(this.unladenMass + this.fuelCapacity, this.pitch, this.standard[1].m, this.pipSpeed);
|
||||||
|
this.topPitch = this.pitches[4];
|
||||||
|
|
||||||
|
this.rolls = Calc.roll(this.unladenMass + this.fuelCapacity, this.roll, this.standard[1].m, this.pipSpeed);
|
||||||
|
this.topRoll = this.rolls[4];
|
||||||
|
|
||||||
|
this.yaws = Calc.yaw(this.unladenMass + this.fuelCapacity, this.yaw, this.standard[1].m, this.pipSpeed);
|
||||||
|
this.topYaw = this.yaws[4];
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1104,6 +1139,7 @@ export default class Ship {
|
|||||||
*/
|
*/
|
||||||
recalculateShield() {
|
recalculateShield() {
|
||||||
let shield = 0;
|
let shield = 0;
|
||||||
|
let shieldBoost = 1;
|
||||||
let shieldExplRes = null;
|
let shieldExplRes = null;
|
||||||
let shieldKinRes = null;
|
let shieldKinRes = null;
|
||||||
let shieldThermRes = null;
|
let shieldThermRes = null;
|
||||||
@@ -1111,16 +1147,15 @@ export default class Ship {
|
|||||||
const sgSlot = this.findInternalByGroup('sg');
|
const sgSlot = this.findInternalByGroup('sg');
|
||||||
if (sgSlot && sgSlot.enabled) {
|
if (sgSlot && sgSlot.enabled) {
|
||||||
// Shield from generator
|
// Shield from generator
|
||||||
const baseShield = Calc.shieldStrength(this.hullMass, this.baseShieldStrength, sgSlot.m, 1);
|
shield = Calc.shieldStrength(this.hullMass, this.baseShieldStrength, sgSlot.m, 1);
|
||||||
shield = baseShield;
|
|
||||||
shieldExplRes = 1 - sgSlot.m.getExplosiveResistance();
|
shieldExplRes = 1 - sgSlot.m.getExplosiveResistance();
|
||||||
shieldKinRes = 1 - sgSlot.m.getKineticResistance();
|
shieldKinRes = 1 - sgSlot.m.getKineticResistance();
|
||||||
shieldThermRes = 1 - sgSlot.m.getThermalResistance();
|
shieldThermRes = 1 - sgSlot.m.getThermalResistance();
|
||||||
|
|
||||||
// Shield from boosters
|
// Shield from boosters
|
||||||
for (let slot of this.hardpoints) {
|
for (let slot of this.hardpoints) {
|
||||||
if (slot.m && slot.m.grp == 'sb') {
|
if (slot.enabled && slot.m && slot.m.grp == 'sb') {
|
||||||
shield += baseShield * slot.m.getShieldBoost();
|
shieldBoost += slot.m.getShieldBoost();
|
||||||
shieldExplRes *= (1 - slot.m.getExplosiveResistance());
|
shieldExplRes *= (1 - slot.m.getExplosiveResistance());
|
||||||
shieldKinRes *= (1 - slot.m.getKineticResistance());
|
shieldKinRes *= (1 - slot.m.getKineticResistance());
|
||||||
shieldThermRes *= (1 - slot.m.getThermalResistance());
|
shieldThermRes *= (1 - slot.m.getThermalResistance());
|
||||||
@@ -1128,6 +1163,12 @@ export default class Ship {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We apply diminishing returns to the boosted value
|
||||||
|
// (no we don't; FD pulled back on this idea. But leave this here in case they reinstate it)
|
||||||
|
// shieldBoost = Math.min(shieldBoost, (1 - Math.pow(Math.E, -0.7 * shieldBoost)) * 2.5);
|
||||||
|
|
||||||
|
shield = shield * shieldBoost;
|
||||||
|
|
||||||
this.shield = shield;
|
this.shield = shield;
|
||||||
this.shieldExplRes = shieldExplRes ? 1 - this.diminishingReturns(1 - shieldExplRes, 0.5, 0.75) : null;
|
this.shieldExplRes = shieldExplRes ? 1 - this.diminishingReturns(1 - shieldExplRes, 0.5, 0.75) : null;
|
||||||
this.shieldKinRes = shieldKinRes ? 1 - this.diminishingReturns(1 - shieldKinRes, 0.5, 0.75) : null;
|
this.shieldKinRes = shieldKinRes ? 1 - this.diminishingReturns(1 - shieldKinRes, 0.5, 0.75) : null;
|
||||||
@@ -1162,11 +1203,13 @@ export default class Ship {
|
|||||||
// Armour from bulkheads
|
// Armour from bulkheads
|
||||||
let bulkhead = this.bulkheads.m;
|
let bulkhead = this.bulkheads.m;
|
||||||
let armour = this.baseArmour + (this.baseArmour * bulkhead.getHullBoost());
|
let armour = this.baseArmour + (this.baseArmour * bulkhead.getHullBoost());
|
||||||
|
let modulearmour = 0;
|
||||||
|
let moduleprotection = 1;
|
||||||
let hullExplRes = 1 - bulkhead.getExplosiveResistance();
|
let hullExplRes = 1 - bulkhead.getExplosiveResistance();
|
||||||
let hullKinRes = 1 - bulkhead.getKineticResistance();
|
let hullKinRes = 1 - bulkhead.getKineticResistance();
|
||||||
let hullThermRes = 1 - bulkhead.getThermalResistance();
|
let hullThermRes = 1 - bulkhead.getThermalResistance();
|
||||||
|
|
||||||
// Armour from HRPs
|
// Armour from HRPs and module armour from MRPs
|
||||||
for (let slot of this.internal) {
|
for (let slot of this.internal) {
|
||||||
if (slot.m && slot.m.grp == 'hr') {
|
if (slot.m && slot.m.grp == 'hr') {
|
||||||
armour += slot.m.getHullReinforcement();
|
armour += slot.m.getHullReinforcement();
|
||||||
@@ -1177,9 +1220,16 @@ export default class Ship {
|
|||||||
hullKinRes *= (1 - slot.m.getKineticResistance());
|
hullKinRes *= (1 - slot.m.getKineticResistance());
|
||||||
hullThermRes *= (1 - slot.m.getThermalResistance());
|
hullThermRes *= (1 - slot.m.getThermalResistance());
|
||||||
}
|
}
|
||||||
|
if (slot.m && slot.m.grp == 'mrp') {
|
||||||
|
modulearmour += slot.m.getIntegrity();
|
||||||
|
moduleprotection = moduleprotection * (1 - slot.m.getProtection());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
moduleprotection = 1 - moduleprotection;
|
||||||
|
|
||||||
this.armour = armour;
|
this.armour = armour;
|
||||||
|
this.modulearmour = modulearmour;
|
||||||
|
this.moduleprotection = moduleprotection;
|
||||||
this.hullExplRes = 1 - this.diminishingReturns(1 - hullExplRes, 0.5, 0.75);
|
this.hullExplRes = 1 - this.diminishingReturns(1 - hullExplRes, 0.5, 0.75);
|
||||||
this.hullKinRes = 1 - this.diminishingReturns(1 - hullKinRes, 0.5, 0.75);
|
this.hullKinRes = 1 - this.diminishingReturns(1 - hullKinRes, 0.5, 0.75);
|
||||||
this.hullThermRes = 1 - this.diminishingReturns(1 - hullThermRes, 0.5, 0.75);
|
this.hullThermRes = 1 - this.diminishingReturns(1 - hullThermRes, 0.5, 0.75);
|
||||||
@@ -1369,7 +1419,7 @@ export default class Ship {
|
|||||||
for (let slot of slots) {
|
for (let slot of slots) {
|
||||||
if (slot.length > 0) {
|
if (slot.length > 0) {
|
||||||
buffer.writeInt8(i, curpos++);
|
buffer.writeInt8(i, curpos++);
|
||||||
if (blueprints[i]) {
|
if (blueprints[i] && blueprints[i].id) {
|
||||||
buffer.writeInt8(MODIFICATION_ID_BLUEPRINT, curpos++);
|
buffer.writeInt8(MODIFICATION_ID_BLUEPRINT, curpos++);
|
||||||
buffer.writeInt32LE(blueprints[i].id, curpos);
|
buffer.writeInt32LE(blueprints[i].id, curpos);
|
||||||
curpos += 4;
|
curpos += 4;
|
||||||
@@ -1434,9 +1484,13 @@ export default class Ship {
|
|||||||
curpos += 4;
|
curpos += 4;
|
||||||
// There are a number of 'special' modification IDs, check for them here
|
// There are a number of 'special' modification IDs, check for them here
|
||||||
if (modificationId === MODIFICATION_ID_BLUEPRINT) {
|
if (modificationId === MODIFICATION_ID_BLUEPRINT) {
|
||||||
blueprint = Object.assign(blueprint, _.find(Modifications.blueprints, function(o) { return o.id === modificationValue; }));
|
if (modificationValue !== 0) {
|
||||||
|
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;
|
if (modificationValue !== 0) {
|
||||||
|
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; });
|
blueprint.special = _.find(Modifications.specials, function(o) { return o.id === modificationValue; });
|
||||||
} else {
|
} else {
|
||||||
@@ -1447,7 +1501,9 @@ export default class Ship {
|
|||||||
modificationId = buffer.readInt8(curpos++);
|
modificationId = buffer.readInt8(curpos++);
|
||||||
}
|
}
|
||||||
modArr[slot] = modifications;
|
modArr[slot] = modifications;
|
||||||
blueprintArr[slot] = blueprint;
|
if (blueprint.id) {
|
||||||
|
blueprintArr[slot] = blueprint;
|
||||||
|
}
|
||||||
slot = buffer.readInt8(curpos++);
|
slot = buffer.readInt8(curpos++);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1615,4 +1671,37 @@ export default class Ship {
|
|||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upgrade information about internals with version changes
|
||||||
|
* @param {array} internals the internals from the ship code
|
||||||
|
* @param {int} offset the offset of the internals information in the priorities etc. arrays
|
||||||
|
* @param {array} priorities the existing priorities arrray
|
||||||
|
* @param {array} enableds the existing enableds arrray
|
||||||
|
* @param {array} modifications the existing modifications arrray
|
||||||
|
* @param {array} blueprints the existing blueprints arrray
|
||||||
|
* @param {int} version the version of the information
|
||||||
|
*/
|
||||||
|
upgradeInternals(internals, offset, priorities, enableds, modifications, blueprints, version) {
|
||||||
|
if (version == 1) {
|
||||||
|
// Version 2 reflects the addition of military slots. this means that we need to juggle the internals and their
|
||||||
|
// associated information around to make holes in the appropriate places
|
||||||
|
for (let slotId = 0; slotId < this.internal.length; slotId++) {
|
||||||
|
if (this.internal[slotId].eligible && this.internal[slotId].eligible.mrp) {
|
||||||
|
// Found a restricted military slot - push all of the existing items down one to compensate for the fact that they didn't exist before now
|
||||||
|
internals.push.apply(internals, [0].concat(internals.splice(slotId).slice(0, -1)));
|
||||||
|
|
||||||
|
const offsetSlotId = offset + slotId;
|
||||||
|
|
||||||
|
// Same for priorities etc.
|
||||||
|
if (priorities) { priorities.push.apply(priorities, [0].concat(priorities.splice(offsetSlotId))); }
|
||||||
|
if (enableds) { enableds.push.apply(enableds, [1].concat(enableds.splice(offsetSlotId))); }
|
||||||
|
if (modifications) { modifications.push.apply(modifications, [null].concat(modifications.splice(offsetSlotId).slice(0, -1))); }
|
||||||
|
if (blueprints) { blueprints.push.apply(blueprints, [null].concat(blueprints.splice(offsetSlotId).slice(0, -1))); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Ensure that all items are the correct length
|
||||||
|
internals.splice(Ships[this.id].slots.internal.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ const SHIP_FD_NAME_TO_CORIOLIS_NAME = {
|
|||||||
'Type7': 'type_7_transport',
|
'Type7': 'type_7_transport',
|
||||||
'Type9': 'type_9_heavy',
|
'Type9': 'type_9_heavy',
|
||||||
'Viper': 'viper',
|
'Viper': 'viper',
|
||||||
'Viper_MKIV': 'viper_mk_iv',
|
'Viper_MkIV': 'viper_mk_iv',
|
||||||
'Vulture': 'vulture'
|
'Vulture': 'vulture'
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -224,7 +224,9 @@ export function shipFromJson(json) {
|
|||||||
// Now that we know what we're looking for, find it
|
// Now that we know what we're looking for, find it
|
||||||
const hardpointName = HARDPOINT_NUM_TO_CLASS[hardpointClassNum] + 'Hardpoint' + hardpointSlotNum;
|
const hardpointName = HARDPOINT_NUM_TO_CLASS[hardpointClassNum] + 'Hardpoint' + hardpointSlotNum;
|
||||||
const hardpointSlot = json.modules[hardpointName];
|
const hardpointSlot = json.modules[hardpointName];
|
||||||
if (!hardpointSlot.module) {
|
if (!hardpointSlot) {
|
||||||
|
// This can happen with old imports that don't contain new hardpoints
|
||||||
|
} else if (!hardpointSlot.module) {
|
||||||
// No module
|
// No module
|
||||||
} else {
|
} else {
|
||||||
const hardpointJson = hardpointSlot.module;
|
const hardpointJson = hardpointSlot.module;
|
||||||
@@ -239,19 +241,31 @@ export function shipFromJson(json) {
|
|||||||
|
|
||||||
// Add internal compartments
|
// Add internal compartments
|
||||||
let internalSlotNum = 1;
|
let internalSlotNum = 1;
|
||||||
|
let militarySlotNum = 1;
|
||||||
for (let i in shipTemplate.slots.internal) {
|
for (let i in shipTemplate.slots.internal) {
|
||||||
const internalClassNum = isNaN(shipTemplate.slots.internal[i]) ? shipTemplate.slots.internal[i].class : shipTemplate.slots.internal[i];
|
const internalClassNum = isNaN(shipTemplate.slots.internal[i]) ? shipTemplate.slots.internal[i].class : shipTemplate.slots.internal[i];
|
||||||
|
const isMilitary = isNaN(shipTemplate.slots.internal[i]) ? shipTemplate.slots.internal[i].name == 'Military' : false;
|
||||||
|
|
||||||
|
// The internal slot might be a standard or a military slot. Military slots have a different naming system
|
||||||
let internalSlot = null;
|
let internalSlot = null;
|
||||||
while (internalSlot === null && internalSlotNum < 99) {
|
if (isMilitary) {
|
||||||
// Slot numbers are not contiguous so handle skips
|
const internalName = 'Military0' + militarySlotNum;
|
||||||
const internalName = 'Slot' + (internalSlotNum <= 9 ? '0' : '') + internalSlotNum + '_Size' + internalClassNum;
|
internalSlot = json.modules[internalName];
|
||||||
if (json.modules[internalName]) {
|
militarySlotNum++;
|
||||||
internalSlot = json.modules[internalName];
|
} else {
|
||||||
|
while (internalSlot === null && internalSlotNum < 99) {
|
||||||
|
// Slot numbers are not contiguous so handle skips
|
||||||
|
const internalName = 'Slot' + (internalSlotNum <= 9 ? '0' : '') + internalSlotNum + '_Size' + internalClassNum;
|
||||||
|
if (json.modules[internalName]) {
|
||||||
|
internalSlot = json.modules[internalName];
|
||||||
|
}
|
||||||
|
internalSlotNum++;
|
||||||
}
|
}
|
||||||
internalSlotNum++;
|
|
||||||
}
|
}
|
||||||
if (!internalSlot.module) {
|
|
||||||
|
if (!internalSlot) {
|
||||||
|
// This can happen with old imports that don't contain new slots
|
||||||
|
} else if (!internalSlot.module) {
|
||||||
// No module
|
// No module
|
||||||
} else {
|
} else {
|
||||||
const internalJson = internalSlot.module;
|
const internalJson = internalSlot.module;
|
||||||
|
|||||||
@@ -1,17 +1,21 @@
|
|||||||
import request from 'superagent';
|
import request from 'superagent';
|
||||||
|
|
||||||
const SHORTEN_API = 'https://www.googleapis.com/urlshortener/v1/url?key=';
|
|
||||||
|
|
||||||
|
export default function shorternUrl(url, success, error) {
|
||||||
|
shortenUrlEddp(url, success, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const SHORTEN_API_GOOGLE = 'https://www.googleapis.com/urlshortener/v1/url?key=';
|
||||||
/**
|
/**
|
||||||
* Shorten a URL using Google's URL shortener API
|
* Shorten a URL using Google's URL shortener API
|
||||||
* @param {string} url The URL to shorten
|
* @param {string} url The URL to shorten
|
||||||
* @param {function} success Success callback
|
* @param {function} success Success callback
|
||||||
* @param {function} error Failure/Error callback
|
* @param {function} error Failure/Error callback
|
||||||
*/
|
*/
|
||||||
export default function shortenUrl(url, success, error) {
|
function shortenUrlGoogle(url, success, error) {
|
||||||
if (window.navigator.onLine) {
|
if (window.navigator.onLine) {
|
||||||
try {
|
try {
|
||||||
request.post(SHORTEN_API + window.CORIOLIS_GAPI_KEY)
|
request.post(SHORTEN_API_GOOGLE + window.CORIOLIS_GAPI_KEY)
|
||||||
.send({ longUrl: url })
|
.send({ longUrl: url })
|
||||||
.end(function(err, response) {
|
.end(function(err, response) {
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -27,3 +31,30 @@ export default function shortenUrl(url, success, error) {
|
|||||||
error('Not Online');
|
error('Not Online');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SHORTEN_API_EDDP = 'http://eddp.co/u';
|
||||||
|
/**
|
||||||
|
* Shorten a URL using EDDP's URL shortener API
|
||||||
|
* @param {string} url The URL to shorten
|
||||||
|
* @param {function} success Success callback
|
||||||
|
* @param {function} error Failure/Error callback
|
||||||
|
*/
|
||||||
|
function shortenUrlEddp(url, success, error) {
|
||||||
|
if (window.navigator.onLine) {
|
||||||
|
try {
|
||||||
|
request.post(SHORTEN_API_EDDP)
|
||||||
|
.send(url)
|
||||||
|
.end(function(err, response) {
|
||||||
|
if (err) {
|
||||||
|
error('Bad Request');
|
||||||
|
} else {
|
||||||
|
success(response.header['location']);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
error(e.message ? e.message : e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error('Not Online');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
@import 'tooltip';
|
@import 'tooltip';
|
||||||
@import 'buttons';
|
@import 'buttons';
|
||||||
@import 'error';
|
@import 'error';
|
||||||
|
@import 'shipselector';
|
||||||
@import 'sortable';
|
@import 'sortable';
|
||||||
@import 'loader';
|
@import 'loader';
|
||||||
|
|
||||||
@@ -41,8 +42,10 @@ div, a, li {
|
|||||||
#coriolis {
|
#coriolis {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
padding-top: 48px;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
-webkit-overflow-scrolling: touch;
|
-webkit-overflow-scrolling: touch;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page {
|
.page {
|
||||||
|
|||||||
@@ -20,8 +20,12 @@ header {
|
|||||||
line-height: 3em;
|
line-height: 3em;
|
||||||
font-family: @fTitle;
|
font-family: @fTitle;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
position: relative;
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
left: 0px;
|
||||||
|
width: 100%;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
|
box-sizing: border-box;
|
||||||
.user-select-none();
|
.user-select-none();
|
||||||
|
|
||||||
.menu {
|
.menu {
|
||||||
@@ -186,6 +190,7 @@ header {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.no-wrap {
|
.no-wrap {
|
||||||
|
overflow-x: auto;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,4 +205,4 @@ header {
|
|||||||
margin:0px;
|
margin:0px;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,8 +34,10 @@
|
|||||||
width: 1.1em;
|
width: 1.1em;
|
||||||
height: 1em;
|
height: 1em;
|
||||||
stoke: @fg;
|
stoke: @fg;
|
||||||
|
stroke-width: 20;
|
||||||
fill: transparent;
|
fill: transparent;
|
||||||
|
|
||||||
|
|
||||||
&.sm {
|
&.sm {
|
||||||
width: 0.8em;
|
width: 0.8em;
|
||||||
height: 0.75em;
|
height: 0.75em;
|
||||||
|
|||||||
199
src/less/shipselector.less
Executable file
199
src/less/shipselector.less
Executable file
@@ -0,0 +1,199 @@
|
|||||||
|
.shipselector {
|
||||||
|
background-color: @bgBlack;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 0 0 1em;
|
||||||
|
height: 3em;
|
||||||
|
line-height: 3em;
|
||||||
|
font-family: @fTitle;
|
||||||
|
vertical-align: middle;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.user-select-none();
|
||||||
|
|
||||||
|
.menu {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
cursor: default;
|
||||||
|
|
||||||
|
&.r {
|
||||||
|
.menu-list {
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.smallTablet({
|
||||||
|
position: static;
|
||||||
|
position: initial;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-header {
|
||||||
|
padding : 0 1em;
|
||||||
|
cursor: pointer;
|
||||||
|
color: @warning;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-list {
|
||||||
|
font-family: @fStandard;
|
||||||
|
position: absolute;
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
box-sizing: border-box;
|
||||||
|
min-width: 100%;
|
||||||
|
overflow-x: hidden;
|
||||||
|
background-color: @bgBlack;
|
||||||
|
font-size: 0.9em;
|
||||||
|
overflow-y: auto;
|
||||||
|
z-index: 0;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
max-height: 500px;
|
||||||
|
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-track {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
background-color: @warning-disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
border: none;
|
||||||
|
background-color: transparent;
|
||||||
|
text-align: right;
|
||||||
|
font-size: 1em;
|
||||||
|
font-family: @fStandard;
|
||||||
|
}
|
||||||
|
|
||||||
|
.smallTablet({
|
||||||
|
max-height: 400px;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
border-bottom: 1px solid @bg;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
.tablet({
|
||||||
|
li, a {
|
||||||
|
padding: 0.3em 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
.dbl {
|
||||||
|
-webkit-column-count: 2; /* Chrome, Safari, Opera */
|
||||||
|
-moz-column-count: 2; /* Firefox */
|
||||||
|
column-count: 2;
|
||||||
|
ul {
|
||||||
|
min-width: 10em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.smallTablet({
|
||||||
|
-webkit-column-count: 3; /* Chrome, Safari, Opera */
|
||||||
|
-moz-column-count: 3; /* Firefox */
|
||||||
|
column-count: 3;
|
||||||
|
|
||||||
|
ul {
|
||||||
|
min-width: 20em;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
.largePhone({
|
||||||
|
-webkit-column-count: 2; /* Chrome, Safari, Opera */
|
||||||
|
-moz-column-count: 2; /* Firefox */
|
||||||
|
column-count: 2;
|
||||||
|
});
|
||||||
|
|
||||||
|
.smallPhone({
|
||||||
|
-webkit-column-count: 1; /* Chrome, Safari, Opera */
|
||||||
|
-moz-column-count: 1; /* Firefox */
|
||||||
|
column-count: 1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
.quad {
|
||||||
|
-webkit-column-count: 4; /* Chrome, Safari, Opera */
|
||||||
|
-moz-column-count: 4; /* Firefox */
|
||||||
|
column-count: 4;
|
||||||
|
ul {
|
||||||
|
min-width: 10em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.smallTablet({
|
||||||
|
-webkit-column-count: 3; /* Chrome, Safari, Opera */
|
||||||
|
-moz-column-count: 3; /* Firefox */
|
||||||
|
column-count: 3;
|
||||||
|
|
||||||
|
ul {
|
||||||
|
min-width: 20em;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
.largePhone({
|
||||||
|
-webkit-column-count: 2; /* Chrome, Safari, Opera */
|
||||||
|
-moz-column-count: 2; /* Firefox */
|
||||||
|
column-count: 2;
|
||||||
|
});
|
||||||
|
|
||||||
|
.smallPhone({
|
||||||
|
-webkit-column-count: 1; /* Chrome, Safari, Opera */
|
||||||
|
-moz-column-count: 1; /* Firefox */
|
||||||
|
column-count: 1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
display: inline-block;
|
||||||
|
white-space: nowrap;
|
||||||
|
margin: 0 0 0.5em;
|
||||||
|
padding: 0;
|
||||||
|
line-height: 1.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
white-space: normal;
|
||||||
|
list-style: none;
|
||||||
|
margin-left: 1em;
|
||||||
|
line-height: 1.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
vertical-align: middle;
|
||||||
|
color: @warning;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
&:visited {
|
||||||
|
color: @warning;
|
||||||
|
}
|
||||||
|
.no-touch &:hover {
|
||||||
|
color: teal;
|
||||||
|
}
|
||||||
|
&.active {
|
||||||
|
color: @primary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
border: none;
|
||||||
|
border-top: 1px solid @disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-wrap {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block {
|
||||||
|
display: block;
|
||||||
|
line-height: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 1.3em;
|
||||||
|
display: inline-block;
|
||||||
|
margin:0px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user