mirror of
https://github.com/EDCD/coriolis.git
synced 2025-12-08 14:33:22 +00:00
Compare commits
148 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a37b36ec2 | ||
|
|
85e6796e88 | ||
|
|
fa1ef47b71 | ||
|
|
f31e3c09f4 | ||
|
|
e6ab536601 | ||
|
|
5bced9fe56 | ||
|
|
67742060d3 | ||
|
|
ca2136544c | ||
|
|
ee19e9af50 | ||
|
|
f457fd0bff | ||
|
|
c1ce07e039 | ||
|
|
c8d1536f77 | ||
|
|
231ad4af59 | ||
|
|
d5f61d7ae8 | ||
|
|
8a5d4a36bf | ||
|
|
2c9237626d | ||
|
|
37f889e317 | ||
|
|
f86ba48295 | ||
|
|
aac35633a3 | ||
|
|
e73e0a305d | ||
|
|
4b2b0efe37 | ||
|
|
8fe20f6f65 | ||
|
|
11af7f567a | ||
|
|
3b8444482f | ||
|
|
c09e1b1b3e | ||
|
|
5770cf8d39 | ||
|
|
6da09f2e5d | ||
|
|
294fadf7cd | ||
|
|
2a97678574 | ||
|
|
76b3bd34f5 | ||
|
|
02bfecb92d | ||
|
|
719759ad56 | ||
|
|
fd446b29ba | ||
|
|
e5552d3e10 | ||
|
|
50946eeeb8 | ||
|
|
faab41117c | ||
|
|
0ab59c1f9a | ||
|
|
1067dceaa3 | ||
|
|
9042de422a | ||
|
|
f0547feb93 | ||
|
|
f863daa347 | ||
|
|
fdb202e7d6 | ||
|
|
c6bde19052 | ||
|
|
f6aff3d3bb | ||
|
|
2f4a2ebe03 | ||
|
|
ca20e94b93 | ||
|
|
40a87dceeb | ||
|
|
95b7d60be4 | ||
|
|
24abd6583f | ||
|
|
8857aba53f | ||
|
|
e4830811b0 | ||
|
|
143380ac58 | ||
|
|
a2f6fb6ac0 | ||
|
|
0d3c128059 | ||
|
|
930a555425 | ||
|
|
d6f213fbe7 | ||
|
|
0571e8e099 | ||
|
|
54c61ecb7d | ||
|
|
030867c4f8 | ||
|
|
33a7c71fec | ||
|
|
4e0f682ad6 | ||
|
|
4486aa2e2b | ||
|
|
0c94c81746 | ||
|
|
5b037e3a00 | ||
|
|
42a2b907ce | ||
|
|
a65dae1631 | ||
|
|
7d4c534956 | ||
|
|
8397d3505b | ||
|
|
9556f28ba4 | ||
|
|
f489257f86 | ||
|
|
c96693c439 | ||
|
|
3d4f6d7861 | ||
|
|
606eabfec7 | ||
|
|
782603727a | ||
|
|
ad570534a0 | ||
|
|
87e903e473 | ||
|
|
cf6d32ea04 | ||
|
|
5b81a0b25f | ||
|
|
0688faac93 | ||
|
|
3719bb9696 | ||
|
|
21582d1598 | ||
|
|
9a9607fcfb | ||
|
|
8602c5667b | ||
|
|
f13a987388 | ||
|
|
38eaebefc0 | ||
|
|
c1bc514e6b | ||
|
|
42e98fd015 | ||
|
|
5f0b851de7 | ||
|
|
616ed0bf10 | ||
|
|
108ab3b1ee | ||
|
|
04caef9613 | ||
|
|
cae7edbe01 | ||
|
|
2fa96fc1a5 | ||
|
|
6b8a4d1f85 | ||
|
|
07df44a907 | ||
|
|
be45637435 | ||
|
|
87ab684ba3 | ||
|
|
916a6d5e48 | ||
|
|
b99fbf07f5 | ||
|
|
6d6ef2a93e | ||
|
|
d08e5e2858 | ||
|
|
c1f4a8d416 | ||
|
|
c17c7125e3 | ||
|
|
97fc4ce45d | ||
|
|
abfc338240 | ||
|
|
3d129946ce | ||
|
|
2aab31d317 | ||
|
|
f8ff9a6a87 | ||
|
|
6727c2fc13 | ||
|
|
ba1f11a88b | ||
|
|
c88e4369c8 | ||
|
|
c490f97c22 | ||
|
|
7c71555384 | ||
|
|
35538f971a | ||
|
|
b3c82ac2de | ||
|
|
ff0d8dccea | ||
|
|
06841b1485 | ||
|
|
f55448c0a8 | ||
|
|
258701c377 | ||
|
|
1a0f05511b | ||
|
|
3ec9679893 | ||
|
|
e5cc3e269e | ||
|
|
4b14f617ec | ||
|
|
0df051e40f | ||
|
|
2b464f34e5 | ||
|
|
7a33bd64f8 | ||
|
|
3656c7a18f | ||
|
|
3114852c63 | ||
|
|
45337913ba | ||
|
|
adc5d1c039 | ||
|
|
183f22c223 | ||
|
|
43eb4935e6 | ||
|
|
2c45011664 | ||
|
|
f29f3f4f8f | ||
|
|
c347336055 | ||
|
|
cd87184169 | ||
|
|
09895852c3 | ||
|
|
32759d3e0e | ||
|
|
965134fc02 | ||
|
|
7da5360349 | ||
|
|
bf0701340c | ||
|
|
a3a490f442 | ||
|
|
33b22b8280 | ||
|
|
e6278858b9 | ||
|
|
ed7a9ef037 | ||
|
|
9c42179617 | ||
|
|
5634fbd568 | ||
|
|
7d99d59790 |
50
ChangeLog.md
Normal file
50
ChangeLog.md
Normal file
@@ -0,0 +1,50 @@
|
||||
#2.2.5
|
||||
* Calculate rate of fire for multi-burst weapons
|
||||
* Add note to disable ghostery in error situation
|
||||
* Use coriolis-data 2.2.5:
|
||||
* Fix incorrect ID for emissive munitions special
|
||||
* Fix rate of fire for burst lasers
|
||||
* Add fragment cannon modifications
|
||||
* Fix internal name of dazzle shell
|
||||
|
||||
#2.2.4
|
||||
* Add shortlink for outfitting page
|
||||
* Use coriolis-data 2.2.4:
|
||||
* Fix incorrect ID for class 5 luxury passenger cabin
|
||||
* Add damage type modifier
|
||||
* Change modifications from simple strings to objects, to allow more data-driven behaviour
|
||||
* Add special effects
|
||||
* Modification tooltip now shows special effect
|
||||
|
||||
#2.2.3
|
||||
* Fix hull boost calculation - now shows correct % modifier and total armour
|
||||
* Fix import of DiamondBack - can now be imported
|
||||
* Fix import of Beluga - can now be imported
|
||||
* Use coriolis-data 2.2.3:
|
||||
* Fix mismatch between class 5 and class 7 fighter hangars - now shows correct module
|
||||
* Add details for concordant sequence special effect - now shows correct damage
|
||||
* Fix details for thermal shock special effect - now shows correct damage
|
||||
* Add engineer blueprints
|
||||
* Modification tooltip now shows name and grade of modifications for imported builds
|
||||
* Retain import URL unless user changes the build - allows future updates of Coriolis to take advantage of additional build information
|
||||
|
||||
#2.2.2
|
||||
* Update DPS/HPS/EPS in real-time as modifiers change
|
||||
* Use coriolis-data 2.2.2:
|
||||
* Add distributor draw modifier to shield generators
|
||||
* Remove modifiers for sensors
|
||||
* Add initial loadout passenger cabins for Beluga
|
||||
* Add initial loadout passenger cabins for Orca
|
||||
* Update costs and initial loadouts for Keelback and Type-7
|
||||
* Add resistances for hull reinforcement packages
|
||||
* Added modifier actions to create modifications from raw data
|
||||
* Show modification icon for modified modules
|
||||
* Take modifications in to account when deciding whether to issue a warning on a standard module
|
||||
* Fix hardpoint comparison DPS number when selecting an alternate module
|
||||
* Ensure that retrofit tab only shows changed modules
|
||||
* Fix import and export of ships with modifications, bump schema version to 4
|
||||
* Enable boost display even if power distributor is disabled
|
||||
* Calculate breakdown of ship offensive and defensive stats
|
||||
* Add 'Offence summary' and 'Defence summary' components
|
||||
* Add ability to import from companion API output through import feature
|
||||
* Add ability to import from companion API output through URL
|
||||
313
__tests__/fixtures/anaconda-test-detailed-export-v4.json
Normal file
313
__tests__/fixtures/anaconda-test-detailed-export-v4.json
Normal file
@@ -0,0 +1,313 @@
|
||||
{
|
||||
"$schema": "http://cdn.coriolis.io/schemas/ship-loadout/4.json#",
|
||||
"name": "Test My Ship",
|
||||
"ship": "Anaconda",
|
||||
"references": [
|
||||
{
|
||||
"name": "Coriolis.io",
|
||||
"url": "http://localhost:3300/outfit/anaconda/48A6A6A5A8A8A5C2c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.AwRj4zNKqA==.CwBhCYzBGW9qCTSqs5xA.H4sIAAAAAAAAA2MUe8HMwPD-PwDDhxeuCAAAAA==?bn=Test%20My%20Ship",
|
||||
"old-code": "48A6A6A5A8A8A5C2c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.AwRj4zNKqA==.CwBhCYzBGW9qCTSqs5xA.H4sIAAAAAAAAA2MUe8HMwPD-PwDDhxeuCAAAAA==",
|
||||
"code": "4putkFklkdzsuf52c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.AwRj4zNKqA==.CwBhCYzBGW9qCTSqs5xA.H4sIAAAAAAAAA2MUe8HMwPD-PwDDhxeuCAAAAA==",
|
||||
"shipId": "anaconda"
|
||||
}
|
||||
],
|
||||
"components": {
|
||||
"standard": {
|
||||
"bulkheads": "Reactive Surface Composite",
|
||||
"cargoHatch": {
|
||||
"enabled": false,
|
||||
"priority": 5
|
||||
},
|
||||
"powerPlant": {
|
||||
"class": 8,
|
||||
"rating": "A",
|
||||
"enabled": true,
|
||||
"priority": 1,
|
||||
"modifications": {
|
||||
"pgen": 1000
|
||||
}
|
||||
},
|
||||
"thrusters": {
|
||||
"class": 6,
|
||||
"rating": "A",
|
||||
"enabled": true,
|
||||
"priority": 1
|
||||
},
|
||||
"frameShiftDrive": {
|
||||
"class": 6,
|
||||
"rating": "A",
|
||||
"enabled": true,
|
||||
"priority": 3
|
||||
},
|
||||
"lifeSupport": {
|
||||
"class": 5,
|
||||
"rating": "A",
|
||||
"enabled": true,
|
||||
"priority": 1
|
||||
},
|
||||
"powerDistributor": {
|
||||
"class": 8,
|
||||
"rating": "A",
|
||||
"enabled": true,
|
||||
"priority": 1
|
||||
},
|
||||
"sensors": {
|
||||
"class": 8,
|
||||
"rating": "A",
|
||||
"enabled": true,
|
||||
"priority": 1
|
||||
},
|
||||
"fuelTank": {
|
||||
"class": 5,
|
||||
"rating": "C",
|
||||
"enabled": true,
|
||||
"priority": 1
|
||||
}
|
||||
},
|
||||
"hardpoints": [
|
||||
{
|
||||
"class": 4,
|
||||
"rating": "A",
|
||||
"enabled": true,
|
||||
"priority": 2,
|
||||
"group": "Plasma Accelerator",
|
||||
"mount": "Fixed"
|
||||
},
|
||||
{
|
||||
"class": 3,
|
||||
"rating": "D",
|
||||
"enabled": true,
|
||||
"priority": 2,
|
||||
"group": "Beam Laser",
|
||||
"mount": "Turret"
|
||||
},
|
||||
{
|
||||
"class": 3,
|
||||
"rating": "D",
|
||||
"enabled": true,
|
||||
"priority": 2,
|
||||
"group": "Beam Laser",
|
||||
"mount": "Turret"
|
||||
},
|
||||
{
|
||||
"class": 3,
|
||||
"rating": "D",
|
||||
"enabled": true,
|
||||
"priority": 2,
|
||||
"group": "Beam Laser",
|
||||
"mount": "Turret"
|
||||
},
|
||||
{
|
||||
"class": 2,
|
||||
"rating": "E",
|
||||
"enabled": true,
|
||||
"priority": 2,
|
||||
"group": "Cannon",
|
||||
"mount": "Turret"
|
||||
},
|
||||
{
|
||||
"class": 2,
|
||||
"rating": "E",
|
||||
"enabled": true,
|
||||
"priority": 2,
|
||||
"group": "Cannon",
|
||||
"mount": "Turret"
|
||||
},
|
||||
{
|
||||
"class": 1,
|
||||
"rating": "F",
|
||||
"enabled": true,
|
||||
"priority": 2,
|
||||
"group": "Beam Laser",
|
||||
"mount": "Turret"
|
||||
},
|
||||
{
|
||||
"class": 1,
|
||||
"rating": "F",
|
||||
"enabled": true,
|
||||
"priority": 2,
|
||||
"group": "Beam Laser",
|
||||
"mount": "Turret"
|
||||
}
|
||||
],
|
||||
"utility": [
|
||||
{
|
||||
"class": 0,
|
||||
"rating": "A",
|
||||
"enabled": true,
|
||||
"priority": 1,
|
||||
"group": "Shield Booster"
|
||||
},
|
||||
{
|
||||
"class": 0,
|
||||
"rating": "A",
|
||||
"enabled": true,
|
||||
"priority": 1,
|
||||
"group": "Shield Booster"
|
||||
},
|
||||
null,
|
||||
{
|
||||
"class": 0,
|
||||
"rating": "C",
|
||||
"enabled": true,
|
||||
"priority": 2,
|
||||
"group": "Kill Warrant Scanner"
|
||||
},
|
||||
{
|
||||
"class": 0,
|
||||
"rating": "C",
|
||||
"enabled": true,
|
||||
"priority": 2,
|
||||
"group": "Cargo Scanner"
|
||||
},
|
||||
{
|
||||
"class": 0,
|
||||
"rating": "F",
|
||||
"enabled": false,
|
||||
"priority": 1,
|
||||
"group": "Electronic Countermeasure",
|
||||
"name": "Electronic Countermeasure"
|
||||
},
|
||||
{
|
||||
"class": 0,
|
||||
"rating": "I",
|
||||
"enabled": true,
|
||||
"priority": 1,
|
||||
"group": "Chaff Launcher",
|
||||
"name": "Chaff Launcher"
|
||||
},
|
||||
{
|
||||
"class": 0,
|
||||
"rating": "I",
|
||||
"enabled": true,
|
||||
"priority": 2,
|
||||
"group": "Point Defence",
|
||||
"name": "Point Defence"
|
||||
}
|
||||
],
|
||||
"internal": [
|
||||
{
|
||||
"class": 7,
|
||||
"rating": "A",
|
||||
"enabled": true,
|
||||
"priority": 1,
|
||||
"group": "Shield Generator"
|
||||
},
|
||||
{
|
||||
"class": 6,
|
||||
"rating": "A",
|
||||
"enabled": true,
|
||||
"priority": 1,
|
||||
"group": "Shield Cell Bank"
|
||||
},
|
||||
{
|
||||
"class": 6,
|
||||
"rating": "E",
|
||||
"enabled": true,
|
||||
"priority": 1,
|
||||
"group": "Cargo Rack"
|
||||
},
|
||||
{
|
||||
"class": 5,
|
||||
"rating": "D",
|
||||
"enabled": true,
|
||||
"priority": 1,
|
||||
"group": "Hull Reinforcement Package"
|
||||
},
|
||||
{
|
||||
"class": 5,
|
||||
"rating": "E",
|
||||
"enabled": true,
|
||||
"priority": 1,
|
||||
"group": "Cargo Rack"
|
||||
},
|
||||
null,
|
||||
null,
|
||||
{
|
||||
"class": 4,
|
||||
"rating": "E",
|
||||
"enabled": true,
|
||||
"priority": 1,
|
||||
"group": "Cargo Rack"
|
||||
},
|
||||
{
|
||||
"class": 4,
|
||||
"rating": "E",
|
||||
"enabled": true,
|
||||
"priority": 1,
|
||||
"group": "Cargo Rack"
|
||||
},
|
||||
{
|
||||
"class": 4,
|
||||
"rating": "A",
|
||||
"enabled": true,
|
||||
"priority": 3,
|
||||
"group": "Fuel Scoop"
|
||||
},
|
||||
{
|
||||
"class": 2,
|
||||
"rating": "A",
|
||||
"enabled": true,
|
||||
"priority": 3,
|
||||
"group": "Frame Shift Drive Interdictor"
|
||||
}
|
||||
]
|
||||
},
|
||||
"stats": {
|
||||
"class": 3,
|
||||
"fighterHangars": 1,
|
||||
"hullCost": 141889930,
|
||||
"speed": 180,
|
||||
"topSpeed": 186.5,
|
||||
"boost": 240,
|
||||
"boostEnergy": 27,
|
||||
"topBoost": 248.62,
|
||||
"topSpeed": 186.46,
|
||||
"totalCost": 882362058,
|
||||
"totalDpe": 127.26,
|
||||
"totalDps": 97.74,
|
||||
"totalEps": 22.71,
|
||||
"totalHps": 677.29,
|
||||
"totalExplDpe": 0,
|
||||
"totalExplDps": 0,
|
||||
"totalExplSDps": 0,
|
||||
"totalHps": 33.62,
|
||||
"totalKinDpe": 103.97,
|
||||
"totalKinDps": 28.92,
|
||||
"totalKinSDps": 21.23,
|
||||
"totalSDps": 85.77,
|
||||
"totalThermDpe": 23.29,
|
||||
"totalThermDps": 68.82,
|
||||
"totalThermSDps": 64.53,
|
||||
"agility": 2,
|
||||
"baseShieldStrength": 350,
|
||||
"baseArmour": 945,
|
||||
"hullExplRes": 0.78,
|
||||
"hullKinRes": 0.73,
|
||||
"hullMass": 400,
|
||||
"hullThermRes": 1.37,
|
||||
"masslock": 23,
|
||||
"pipSpeed": 0.14,
|
||||
"moduleCostMultiplier": 1,
|
||||
"fuelCapacity": 32,
|
||||
"cargoCapacity": 128,
|
||||
"ladenMass": 1339.2,
|
||||
"armour": 2227.5,
|
||||
"baseArmour": 525,
|
||||
"unladenMass": 1179.2,
|
||||
"powerAvailable": 39.6,
|
||||
"powerRetracted": 23.33,
|
||||
"powerDeployed": 34.76,
|
||||
"unladenRange": 18.49,
|
||||
"fullTankRange": 18.12,
|
||||
"ladenRange": 16.39,
|
||||
"unladenFastestRange": 73.21,
|
||||
"ladenFastestRange": 66.15,
|
||||
"maxJumpCount": 4,
|
||||
"shield": 833,
|
||||
"shieldCells": 1840,
|
||||
"shieldExplRes": 0.5,
|
||||
"shieldKinRes": 0.6,
|
||||
"shieldThermRes": 1.2
|
||||
}
|
||||
}
|
||||
255
__tests__/fixtures/asp-test-detailed-export-v4.json
Normal file
255
__tests__/fixtures/asp-test-detailed-export-v4.json
Normal file
@@ -0,0 +1,255 @@
|
||||
{
|
||||
"$schema": "http://cdn.coriolis.io/schemas/ship-loadout/4.json#",
|
||||
"name": "Multi-purpose Asp Explorer",
|
||||
"ship": "Asp Explorer",
|
||||
"references": [
|
||||
{
|
||||
"name": "Coriolis.io",
|
||||
"url": "https://coriolis.edcd.io/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",
|
||||
"code": "0pftiFflfddsnf5------020202033c044002v62f2i.AwRj4yvI.CwRgDBldHnJA.H4sIAAAAAAAAA2P858DAwPCXEUhwHPvx/78YG5AltB7I/8/0TwImJboDSPJ/+/f/v/KlX///i3AwMTBIfARK/Gf+JwVSxArStVAYqOjvz///JVo5GRhE2IBc4SKQSSz/DGEmCa398P8//2+gTf//AwDFxwtofAAAAA==",
|
||||
"shipId": "asp"
|
||||
}
|
||||
],
|
||||
"components": {
|
||||
"standard": {
|
||||
"bulkheads": "Lightweight Alloy",
|
||||
"cargoHatch": {
|
||||
"enabled": false,
|
||||
"priority": 5
|
||||
},
|
||||
"powerPlant": {
|
||||
"class": 5,
|
||||
"rating": "A",
|
||||
"enabled": true,
|
||||
"priority": 2,
|
||||
"modifications": {
|
||||
"eff": -1850,
|
||||
"pgen": 6,
|
||||
"mass": 431
|
||||
},
|
||||
"blueprint": {
|
||||
"id": 64,
|
||||
"name": "Low emissions",
|
||||
"grade": 1
|
||||
}
|
||||
},
|
||||
"thrusters": {
|
||||
"class": 5,
|
||||
"rating": "D",
|
||||
"enabled": true,
|
||||
"priority": 1,
|
||||
"modifications": {
|
||||
"optmul": 440,
|
||||
"integrity": -266,
|
||||
"thermload": -1326,
|
||||
"optmass": 520,
|
||||
"power": 241
|
||||
},
|
||||
"blueprint": {
|
||||
"id": 24,
|
||||
"name": "Clean",
|
||||
"grade": 1
|
||||
}
|
||||
},
|
||||
"frameShiftDrive": {
|
||||
"class": 5,
|
||||
"rating": "A",
|
||||
"enabled": true,
|
||||
"priority": 1,
|
||||
"modifications": {
|
||||
"mass": 5025,
|
||||
"integrity": -1539,
|
||||
"power": 2437,
|
||||
"optmass": 4870,
|
||||
"maxfuel": 370
|
||||
},
|
||||
"blueprint": {
|
||||
"id": 26,
|
||||
"name": "Increased range",
|
||||
"grade": 5
|
||||
}
|
||||
},
|
||||
"lifeSupport": {
|
||||
"class": 4,
|
||||
"rating": "A",
|
||||
"enabled": true,
|
||||
"priority": 1,
|
||||
"modifications": {
|
||||
"mass": -3923,
|
||||
"integrity": -1797
|
||||
},
|
||||
"blueprint": {
|
||||
"id": 49,
|
||||
"name": "Lightweight",
|
||||
"grade": 1
|
||||
}
|
||||
},
|
||||
"powerDistributor": {
|
||||
"class": 3,
|
||||
"rating": "D",
|
||||
"enabled": true,
|
||||
"priority": 1
|
||||
},
|
||||
"sensors": {
|
||||
"class": 5,
|
||||
"rating": "D",
|
||||
"enabled": true,
|
||||
"priority": 1
|
||||
},
|
||||
"fuelTank": {
|
||||
"class": 5,
|
||||
"rating": "C",
|
||||
"enabled": true,
|
||||
"priority": 1
|
||||
}
|
||||
},
|
||||
"hardpoints": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
],
|
||||
"utility": [
|
||||
{
|
||||
"class": 0,
|
||||
"rating": "I",
|
||||
"enabled": true,
|
||||
"priority": 1,
|
||||
"group": "Heat Sink Launcher",
|
||||
"name": "Heat Sink Launcher"
|
||||
},
|
||||
{
|
||||
"class": 0,
|
||||
"rating": "I",
|
||||
"enabled": true,
|
||||
"priority": 1,
|
||||
"group": "Heat Sink Launcher",
|
||||
"name": "Heat Sink Launcher"
|
||||
},
|
||||
{
|
||||
"class": 0,
|
||||
"rating": "I",
|
||||
"enabled": true,
|
||||
"priority": 1,
|
||||
"group": "Heat Sink Launcher",
|
||||
"name": "Heat Sink Launcher"
|
||||
},
|
||||
{
|
||||
"class": 0,
|
||||
"rating": "I",
|
||||
"enabled": true,
|
||||
"priority": 1,
|
||||
"group": "Point Defence",
|
||||
"name": "Point Defence"
|
||||
}
|
||||
],
|
||||
"internal": [
|
||||
{
|
||||
"class": 6,
|
||||
"rating": "A",
|
||||
"enabled": true,
|
||||
"priority": 1,
|
||||
"group": "Fuel Scoop"
|
||||
},
|
||||
{
|
||||
"class": 5,
|
||||
"rating": "E",
|
||||
"enabled": true,
|
||||
"priority": 2,
|
||||
"group": "Cargo Rack"
|
||||
},
|
||||
{
|
||||
"class": 3,
|
||||
"rating": "A",
|
||||
"enabled": true,
|
||||
"priority": 1,
|
||||
"group": "Shield Generator"
|
||||
},
|
||||
{
|
||||
"class": 3,
|
||||
"rating": "E",
|
||||
"enabled": true,
|
||||
"priority": 2,
|
||||
"group": "Cargo Rack"
|
||||
},
|
||||
{
|
||||
"class": 2,
|
||||
"rating": "G",
|
||||
"enabled": true,
|
||||
"priority": 1,
|
||||
"group": "Planetary Vehicle Hangar"
|
||||
},
|
||||
{
|
||||
"class": 1,
|
||||
"rating": "C",
|
||||
"enabled": true,
|
||||
"priority": 2,
|
||||
"group": "Scanner",
|
||||
"name": "Advanced Discovery Scanner"
|
||||
},
|
||||
{
|
||||
"class": 1,
|
||||
"rating": "C",
|
||||
"enabled": true,
|
||||
"priority": 2,
|
||||
"group": "Scanner",
|
||||
"name": "Detailed Surface Scanner"
|
||||
}
|
||||
]
|
||||
},
|
||||
"stats": {
|
||||
"class": 2,
|
||||
"hullCost": 6135660,
|
||||
"speed": 250,
|
||||
"boost": 340,
|
||||
"boostEnergy": 13,
|
||||
"agility": 6,
|
||||
"baseShieldStrength": 140,
|
||||
"baseArmour": 210,
|
||||
"hullMass": 280,
|
||||
"masslock": 11,
|
||||
"pipSpeed": 0.13,
|
||||
"moduleCostMultiplier": 1,
|
||||
"fuelCapacity": 32,
|
||||
"cargoCapacity": 40,
|
||||
"ladenMass": 435.26,
|
||||
"armour": 378,
|
||||
"shield": 113.43,
|
||||
"shieldCells": 0,
|
||||
"totalCost": 48402550,
|
||||
"unladenMass": 363.26,
|
||||
"totalDpe": 0,
|
||||
"totalExplDpe": 0,
|
||||
"totalKinDpe": 0,
|
||||
"totalThermDpe": 0,
|
||||
"totalDps": 0,
|
||||
"totalExplDps": 0,
|
||||
"totalKinDps": 0,
|
||||
"totalThermDps": 0,
|
||||
"totalSDps": 0,
|
||||
"totalExplSDps": 0,
|
||||
"totalKinSDps": 0,
|
||||
"totalThermSDps": 0,
|
||||
"totalEps": 1.2,
|
||||
"totalHps": 1,
|
||||
"shieldExplRes": 0.5,
|
||||
"shieldKinRes": 0.6,
|
||||
"shieldThermRes": 1.2,
|
||||
"hullExplRes": 1.4,
|
||||
"hullKinRes": 1.2,
|
||||
"hullThermRes": 1,
|
||||
"powerAvailable": 20.41,
|
||||
"powerRetracted": 11.91,
|
||||
"powerDeployed": 11.91,
|
||||
"unladenRange": 50.45,
|
||||
"fullTankRange": 47.03,
|
||||
"ladenRange": 42.71,
|
||||
"unladenFastestRange": 317.24,
|
||||
"ladenFastestRange": 287.02,
|
||||
"maxJumpCount": 7,
|
||||
"topSpeed": 274.01,
|
||||
"topBoost": 372.65
|
||||
}
|
||||
}
|
||||
1288
__tests__/fixtures/companion-api-import-1.json
Normal file
1288
__tests__/fixtures/companion-api-import-1.json
Normal file
File diff suppressed because it is too large
Load Diff
552
__tests__/fixtures/companion-api-import-2.json
Normal file
552
__tests__/fixtures/companion-api-import-2.json
Normal file
@@ -0,0 +1,552 @@
|
||||
{
|
||||
"cargo": {
|
||||
"capacity": 32
|
||||
},
|
||||
"free": false,
|
||||
"fuel": {
|
||||
"main": {
|
||||
"capacity": 128
|
||||
},
|
||||
"reserve": {
|
||||
"capacity": 0.81
|
||||
}
|
||||
},
|
||||
"id": 31,
|
||||
"modules": {
|
||||
"Armour": {
|
||||
"module": {
|
||||
"free": false,
|
||||
"id": 128049346,
|
||||
"name": "BelugaLiner_Armour_Grade1",
|
||||
"on": true,
|
||||
"priority": 1,
|
||||
"unloaned": 0,
|
||||
"value": 0
|
||||
}
|
||||
},
|
||||
"Bobble01": [],
|
||||
"Bobble02": [],
|
||||
"Bobble03": [],
|
||||
"Bobble04": [],
|
||||
"Bobble05": [],
|
||||
"Bobble06": [],
|
||||
"Bobble07": [],
|
||||
"Bobble08": [],
|
||||
"Bobble09": [],
|
||||
"Bobble10": [],
|
||||
"Decal1": {
|
||||
"module": {
|
||||
"free": false,
|
||||
"id": 128667757,
|
||||
"name": "Decal_Explorer_Ranger",
|
||||
"on": true,
|
||||
"priority": 1,
|
||||
"unloaned": 0,
|
||||
"value": 0
|
||||
}
|
||||
},
|
||||
"Decal2": {
|
||||
"module": {
|
||||
"free": false,
|
||||
"id": 128667742,
|
||||
"name": "Decal_Combat_Deadly",
|
||||
"on": true,
|
||||
"priority": 1,
|
||||
"unloaned": 0,
|
||||
"value": 0
|
||||
}
|
||||
},
|
||||
"Decal3": {
|
||||
"module": {
|
||||
"free": false,
|
||||
"id": 128667750,
|
||||
"name": "Decal_Trade_Tycoon",
|
||||
"on": true,
|
||||
"priority": 1,
|
||||
"unloaned": 0,
|
||||
"value": 0
|
||||
}
|
||||
},
|
||||
"EngineColour": [],
|
||||
"FrameShiftDrive": {
|
||||
"module": {
|
||||
"free": false,
|
||||
"id": 128064132,
|
||||
"modifiers": {
|
||||
"engineerID": 300100,
|
||||
"id": 175,
|
||||
"modifiers": [
|
||||
{
|
||||
"name": "mod_mass",
|
||||
"type": 1,
|
||||
"value": 0.4457540512085
|
||||
},
|
||||
{
|
||||
"name": "mod_health",
|
||||
"type": 1,
|
||||
"value": -0.24584779143333
|
||||
},
|
||||
{
|
||||
"name": "mod_passive_power",
|
||||
"type": 1,
|
||||
"value": 0.24457727372646
|
||||
},
|
||||
{
|
||||
"name": "mod_fsd_optimised_mass",
|
||||
"type": 1,
|
||||
"value": 0.49257898330688
|
||||
},
|
||||
{
|
||||
"name": "mod_fsd_max_fuel_per_jump",
|
||||
"type": 2,
|
||||
"value": 0.028505677357316
|
||||
},
|
||||
{
|
||||
"name": "mod_fsd_heat_rate",
|
||||
"type": 2,
|
||||
"value": -0.079360365867615
|
||||
}
|
||||
],
|
||||
"moduleTags": [
|
||||
16
|
||||
],
|
||||
"recipeID": 128673694,
|
||||
"slotIndex": 53
|
||||
},
|
||||
"name": "Int_Hyperdrive_Size7_Class5",
|
||||
"on": true,
|
||||
"priority": 0,
|
||||
"recipeLevel": 5,
|
||||
"recipeName": "FSD_LongRange",
|
||||
"recipeValue": 0,
|
||||
"unloaned": 0,
|
||||
"value": 46160201
|
||||
}
|
||||
},
|
||||
"FuelTank": {
|
||||
"module": {
|
||||
"free": false,
|
||||
"id": 128064352,
|
||||
"name": "Int_FuelTank_Size7_Class3",
|
||||
"on": true,
|
||||
"priority": 1,
|
||||
"unloaned": 1602822,
|
||||
"value": 1602822
|
||||
}
|
||||
},
|
||||
"LifeSupport": {
|
||||
"module": {
|
||||
"free": false,
|
||||
"id": 128064174,
|
||||
"name": "Int_LifeSupport_Size8_Class2",
|
||||
"on": true,
|
||||
"priority": 0,
|
||||
"unloaned": 0,
|
||||
"value": 1569565
|
||||
}
|
||||
},
|
||||
"MainEngines": {
|
||||
"module": {
|
||||
"free": false,
|
||||
"id": 128064094,
|
||||
"modifiers": {
|
||||
"engineerID": 300100,
|
||||
"id": 253,
|
||||
"modifiers": [
|
||||
{
|
||||
"name": "mod_engine_mass_curve_multiplier",
|
||||
"type": 1,
|
||||
"value": 0.098235413432121
|
||||
},
|
||||
{
|
||||
"name": "mod_engine_heat",
|
||||
"type": 1,
|
||||
"value": 0.18069696426392
|
||||
},
|
||||
{
|
||||
"name": "mod_passive_power",
|
||||
"type": 1,
|
||||
"value": 0.033788848668337
|
||||
},
|
||||
{
|
||||
"name": "mod_health",
|
||||
"type": 1,
|
||||
"value": -0.056404989212751
|
||||
},
|
||||
{
|
||||
"name": "mod_engine_mass_curve",
|
||||
"type": 1,
|
||||
"value": -0.027384582906961
|
||||
},
|
||||
{
|
||||
"name": "mod_engine_heat",
|
||||
"type": 2,
|
||||
"value": -0.072683908045292
|
||||
}
|
||||
],
|
||||
"moduleTags": [
|
||||
17
|
||||
],
|
||||
"recipeID": 128673655,
|
||||
"slotIndex": 52
|
||||
},
|
||||
"name": "Int_Engine_Size7_Class2",
|
||||
"on": true,
|
||||
"priority": 0,
|
||||
"recipeLevel": 1,
|
||||
"recipeName": "Engine_Dirty",
|
||||
"recipeValue": 0,
|
||||
"unloaned": 0,
|
||||
"value": 1709638
|
||||
}
|
||||
},
|
||||
"MediumHardpoint1": {
|
||||
"module": {
|
||||
"free": false,
|
||||
"id": 128049436,
|
||||
"name": "Hpt_BeamLaser_Turret_Medium",
|
||||
"on": true,
|
||||
"priority": 0,
|
||||
"unloaned": 0,
|
||||
"value": 1889910
|
||||
}
|
||||
},
|
||||
"MediumHardpoint2": {
|
||||
"module": {
|
||||
"free": false,
|
||||
"id": 128049436,
|
||||
"name": "Hpt_BeamLaser_Turret_Medium",
|
||||
"on": true,
|
||||
"priority": 0,
|
||||
"unloaned": 0,
|
||||
"value": 1889910
|
||||
}
|
||||
},
|
||||
"MediumHardpoint3": {
|
||||
"module": {
|
||||
"free": false,
|
||||
"id": 128049460,
|
||||
"name": "Hpt_MultiCannon_Gimbal_Medium",
|
||||
"on": true,
|
||||
"priority": 0,
|
||||
"unloaned": 0,
|
||||
"value": 51300
|
||||
}
|
||||
},
|
||||
"MediumHardpoint4": {
|
||||
"module": {
|
||||
"free": false,
|
||||
"id": 128049460,
|
||||
"name": "Hpt_MultiCannon_Gimbal_Medium",
|
||||
"on": true,
|
||||
"priority": 0,
|
||||
"unloaned": 0,
|
||||
"value": 51300
|
||||
}
|
||||
},
|
||||
"MediumHardpoint5": {
|
||||
"module": {
|
||||
"free": false,
|
||||
"id": 128049460,
|
||||
"name": "Hpt_MultiCannon_Gimbal_Medium",
|
||||
"on": true,
|
||||
"priority": 0,
|
||||
"unloaned": 0,
|
||||
"value": 51300
|
||||
}
|
||||
},
|
||||
"PaintJob": {
|
||||
"module": {
|
||||
"free": false,
|
||||
"id": 128732290,
|
||||
"name": "PaintJob_BelugaLiner_Tactical_White",
|
||||
"on": true,
|
||||
"priority": 1,
|
||||
"unloaned": 0,
|
||||
"value": 0
|
||||
}
|
||||
},
|
||||
"PlanetaryApproachSuite": {
|
||||
"module": {
|
||||
"free": false,
|
||||
"id": 128672317,
|
||||
"name": "Int_PlanetApproachSuite",
|
||||
"on": true,
|
||||
"priority": 1,
|
||||
"unloaned": 450,
|
||||
"value": 450
|
||||
}
|
||||
},
|
||||
"PowerDistributor": {
|
||||
"module": {
|
||||
"free": false,
|
||||
"id": 128064207,
|
||||
"name": "Int_PowerDistributor_Size6_Class5",
|
||||
"on": true,
|
||||
"priority": 0,
|
||||
"unloaned": 0,
|
||||
"value": 3128120
|
||||
}
|
||||
},
|
||||
"PowerPlant": {
|
||||
"module": {
|
||||
"free": false,
|
||||
"id": 128064057,
|
||||
"modifiers": {
|
||||
"engineerID": 300100,
|
||||
"id": 277,
|
||||
"modifiers": [
|
||||
{
|
||||
"name": "mod_powerplant_power",
|
||||
"type": 1,
|
||||
"value": 0.054692290723324
|
||||
},
|
||||
{
|
||||
"name": "mod_health",
|
||||
"type": 1,
|
||||
"value": -0.033690698444843
|
||||
},
|
||||
{
|
||||
"name": "mod_powerplant_heat",
|
||||
"type": 1,
|
||||
"value": 0.027470717206597
|
||||
},
|
||||
{
|
||||
"name": "mod_powerplant_heat",
|
||||
"type": 2,
|
||||
"value": -0.056317910552025
|
||||
}
|
||||
],
|
||||
"moduleTags": [
|
||||
18
|
||||
],
|
||||
"recipeID": 128673765,
|
||||
"slotIndex": 51
|
||||
},
|
||||
"name": "Int_Powerplant_Size6_Class5",
|
||||
"on": true,
|
||||
"priority": 1,
|
||||
"recipeLevel": 1,
|
||||
"recipeName": "PowerPlant_Boosted",
|
||||
"recipeValue": 0,
|
||||
"unloaned": 0,
|
||||
"value": 14561578
|
||||
}
|
||||
},
|
||||
"Radar": {
|
||||
"module": {
|
||||
"free": false,
|
||||
"id": 128064239,
|
||||
"name": "Int_Sensors_Size5_Class2",
|
||||
"on": true,
|
||||
"priority": 0,
|
||||
"unloaned": 0,
|
||||
"value": 71500
|
||||
}
|
||||
},
|
||||
"Slot01_Size6": {
|
||||
"module": {
|
||||
"free": false,
|
||||
"id": 128666681,
|
||||
"name": "Int_FuelScoop_Size6_Class5",
|
||||
"on": true,
|
||||
"priority": 1,
|
||||
"unloaned": 0,
|
||||
"value": 25887249
|
||||
}
|
||||
},
|
||||
"Slot02_Size6": {
|
||||
"module": {
|
||||
"free": false,
|
||||
"id": 128064287,
|
||||
"name": "Int_ShieldGenerator_Size6_Class5",
|
||||
"on": true,
|
||||
"priority": 0,
|
||||
"unloaned": 0,
|
||||
"value": 14561578
|
||||
}
|
||||
},
|
||||
"Slot03_Size6": {
|
||||
"module": {
|
||||
"free": false,
|
||||
"id": 128727927,
|
||||
"name": "Int_PassengerCabin_Size6_Class2",
|
||||
"on": true,
|
||||
"priority": 1,
|
||||
"unloaned": 165808,
|
||||
"value": 165808
|
||||
}
|
||||
},
|
||||
"Slot04_Size6": {
|
||||
"module": {
|
||||
"free": false,
|
||||
"id": 128727928,
|
||||
"name": "Int_PassengerCabin_Size6_Class3",
|
||||
"on": true,
|
||||
"priority": 1,
|
||||
"unloaned": 0,
|
||||
"value": 497429
|
||||
}
|
||||
},
|
||||
"Slot05_Size5": {
|
||||
"module": {
|
||||
"free": false,
|
||||
"id": 128727925,
|
||||
"name": "Int_PassengerCabin_Size5_Class4",
|
||||
"on": true,
|
||||
"priority": 1,
|
||||
"unloaned": 0,
|
||||
"value": 1492286
|
||||
}
|
||||
},
|
||||
"Slot06_Size5": {
|
||||
"module": {
|
||||
"free": false,
|
||||
"id": 128064342,
|
||||
"name": "Int_CargoRack_Size5_Class1",
|
||||
"on": true,
|
||||
"priority": 1,
|
||||
"unloaned": 100409,
|
||||
"value": 100409
|
||||
}
|
||||
},
|
||||
"Slot07_Size4": {
|
||||
"module": {
|
||||
"free": false,
|
||||
"id": 128727922,
|
||||
"name": "Int_PassengerCabin_Size4_Class1",
|
||||
"on": true,
|
||||
"priority": 1,
|
||||
"unloaned": 0,
|
||||
"value": 17059
|
||||
}
|
||||
},
|
||||
"Slot08_Size3": {
|
||||
"module": {
|
||||
"free": false,
|
||||
"id": 128667632,
|
||||
"name": "Int_Repairer_Size3_Class5",
|
||||
"on": true,
|
||||
"priority": 1,
|
||||
"unloaned": 0,
|
||||
"value": 2361960
|
||||
}
|
||||
},
|
||||
"Slot09_Size3": {
|
||||
"module": {
|
||||
"free": false,
|
||||
"id": 128672289,
|
||||
"name": "Int_BuggyBay_Size2_Class2",
|
||||
"on": true,
|
||||
"priority": 1,
|
||||
"unloaned": 0,
|
||||
"value": 19440
|
||||
}
|
||||
},
|
||||
"Slot10_Size3": {
|
||||
"module": {
|
||||
"free": false,
|
||||
"id": 128666634,
|
||||
"name": "Int_DetailedSurfaceScanner_Tiny",
|
||||
"on": true,
|
||||
"priority": 1,
|
||||
"unloaned": 0,
|
||||
"value": 225000
|
||||
}
|
||||
},
|
||||
"Slot11_Size3": {
|
||||
"module": {
|
||||
"free": false,
|
||||
"id": 128663561,
|
||||
"name": "Int_StellarBodyDiscoveryScanner_Advanced",
|
||||
"on": true,
|
||||
"priority": 1,
|
||||
"unloaned": 0,
|
||||
"value": 1390500
|
||||
}
|
||||
},
|
||||
"TinyHardpoint1": {
|
||||
"module": {
|
||||
"free": false,
|
||||
"id": 128049513,
|
||||
"name": "Hpt_ChaffLauncher_Tiny",
|
||||
"on": true,
|
||||
"priority": 0,
|
||||
"unloaned": 0,
|
||||
"value": 7650
|
||||
}
|
||||
},
|
||||
"TinyHardpoint2": {
|
||||
"module": {
|
||||
"free": false,
|
||||
"id": 128668536,
|
||||
"name": "Hpt_ShieldBooster_Size0_Class5",
|
||||
"on": true,
|
||||
"priority": 0,
|
||||
"unloaned": 0,
|
||||
"value": 252900
|
||||
}
|
||||
},
|
||||
"TinyHardpoint3": {
|
||||
"module": {
|
||||
"free": false,
|
||||
"id": 128668536,
|
||||
"name": "Hpt_ShieldBooster_Size0_Class5",
|
||||
"on": true,
|
||||
"priority": 0,
|
||||
"unloaned": 0,
|
||||
"value": 252900
|
||||
}
|
||||
},
|
||||
"TinyHardpoint4": {
|
||||
"module": {
|
||||
"free": false,
|
||||
"id": 128668536,
|
||||
"name": "Hpt_ShieldBooster_Size0_Class5",
|
||||
"on": true,
|
||||
"priority": 0,
|
||||
"unloaned": 0,
|
||||
"value": 281000
|
||||
}
|
||||
},
|
||||
"TinyHardpoint5": {
|
||||
"module": {
|
||||
"free": false,
|
||||
"id": 128668536,
|
||||
"name": "Hpt_ShieldBooster_Size0_Class5",
|
||||
"on": true,
|
||||
"priority": 0,
|
||||
"unloaned": 0,
|
||||
"value": 281000
|
||||
}
|
||||
},
|
||||
"TinyHardpoint6": {
|
||||
"module": {
|
||||
"free": false,
|
||||
"id": 128668536,
|
||||
"name": "Hpt_ShieldBooster_Size0_Class5",
|
||||
"on": true,
|
||||
"priority": 0,
|
||||
"unloaned": 0,
|
||||
"value": 281000
|
||||
}
|
||||
},
|
||||
"WeaponColour": {
|
||||
"module": {
|
||||
"free": false,
|
||||
"id": 128732194,
|
||||
"name": "WeaponCustomisation_Purple",
|
||||
"on": true,
|
||||
"priority": 1,
|
||||
"unloaned": 0,
|
||||
"value": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "BelugaLiner",
|
||||
"value": {
|
||||
"hull": 71688743,
|
||||
"modules": 120812762,
|
||||
"unloaned": 1869489
|
||||
}
|
||||
}
|
||||
327
__tests__/fixtures/courier-test-detailed-export-v4.json
Normal file
327
__tests__/fixtures/courier-test-detailed-export-v4.json
Normal file
@@ -0,0 +1,327 @@
|
||||
{
|
||||
"$schema": "http://cdn.coriolis.io/schemas/ship-loadout/4.json#",
|
||||
"name": "Multi-purpose Imperial Courier",
|
||||
"ship": "Imperial Courier",
|
||||
"references": [
|
||||
{
|
||||
"name": "Coriolis.io",
|
||||
"url": "https://coriolis.edcd.io/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",
|
||||
"code": "0patzF5l0das8f31a1a270202000e402t0101-2f.AwRj4zKA.CwRgDBldLiQ=.H4sIAAAAAAAAA12OP0tCYRjFj9fuVbvF1du9ekkT8s/kIg4NElyIBBd321yaGvwUQTS3N7UFfYygIT9EoyQUJA36ns47XJCWA+/z+z3Pe3ImBbDNKaqNPSBoGrL4ngfomKpFGiJ+LgHteR1IPjxJT5pF11uSeXNsJVcRfgdC92syWUuK0iMdKZqrjJ/0aoA71lJ5oKf38knWcCiptCPdhJIerdS00vlK0qktlqoj983UmqqHjQ33VsW8eazFmaTyULP2hQ4lX8LBme6g/6v0TTdbxJ2KhdEIaCw15MF/NB0L+S2hwEwyFM8KgP+qEpWWA3Qu9Z3z9kPWHzakt7Dt+AeD7ghSTgEAAA==",
|
||||
"shipId": "imperial_courier"
|
||||
}
|
||||
],
|
||||
"components": {
|
||||
"standard": {
|
||||
"bulkheads": "Lightweight Alloy",
|
||||
"cargoHatch": {
|
||||
"enabled": false,
|
||||
"priority": 5
|
||||
},
|
||||
"powerPlant": {
|
||||
"class": 4,
|
||||
"rating": "A",
|
||||
"enabled": true,
|
||||
"priority": 2,
|
||||
"modifications": {
|
||||
"pgen": 1052,
|
||||
"integrity": -482,
|
||||
"eff": 974
|
||||
},
|
||||
"blueprint": {
|
||||
"id": 63,
|
||||
"name": "Overcharged",
|
||||
"grade": 1
|
||||
}
|
||||
},
|
||||
"thrusters": {
|
||||
"class": 3,
|
||||
"rating": "A",
|
||||
"enabled": true,
|
||||
"priority": 1,
|
||||
"name": "Enhanced Performance",
|
||||
"modifications": {
|
||||
"optmul": 2476,
|
||||
"thermload": 7023,
|
||||
"power": 1763,
|
||||
"integrity": 165,
|
||||
"optmass": -667
|
||||
},
|
||||
"blueprint": {
|
||||
"id": 22,
|
||||
"name": "Dirty",
|
||||
"grade": 4
|
||||
}
|
||||
},
|
||||
"frameShiftDrive": {
|
||||
"class": 3,
|
||||
"rating": "A",
|
||||
"enabled": true,
|
||||
"priority": 1,
|
||||
"modifications": {
|
||||
"mass": 4082,
|
||||
"integrity": -2422,
|
||||
"power": 1782,
|
||||
"optmass": 4927
|
||||
},
|
||||
"blueprint": {
|
||||
"id": 26,
|
||||
"name": "Increased range",
|
||||
"grade": 5
|
||||
}
|
||||
},
|
||||
"lifeSupport": {
|
||||
"class": 1,
|
||||
"rating": "A",
|
||||
"enabled": true,
|
||||
"priority": 1
|
||||
},
|
||||
"powerDistributor": {
|
||||
"class": 3,
|
||||
"rating": "A",
|
||||
"enabled": true,
|
||||
"priority": 1
|
||||
},
|
||||
"sensors": {
|
||||
"class": 2,
|
||||
"rating": "D",
|
||||
"enabled": true,
|
||||
"priority": 1
|
||||
},
|
||||
"fuelTank": {
|
||||
"class": 3,
|
||||
"rating": "C",
|
||||
"enabled": true,
|
||||
"priority": 1
|
||||
}
|
||||
},
|
||||
"hardpoints": [
|
||||
{
|
||||
"class": 2,
|
||||
"rating": "F",
|
||||
"enabled": true,
|
||||
"priority": 1,
|
||||
"group": "Pulse Laser",
|
||||
"mount": "Fixed",
|
||||
"modifications": {
|
||||
"rof": 5931,
|
||||
"damage": -184,
|
||||
"jitter": 50,
|
||||
"distdraw": -4689,
|
||||
"piercing": 3328
|
||||
},
|
||||
"blueprint": {
|
||||
"id": 89,
|
||||
"name": "Rapid fire",
|
||||
"grade": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"class": 2,
|
||||
"rating": "F",
|
||||
"enabled": true,
|
||||
"priority": 1,
|
||||
"group": "Pulse Laser",
|
||||
"mount": "Fixed",
|
||||
"modifications": {
|
||||
"rof": 4715,
|
||||
"damage": -97,
|
||||
"jitter": 30,
|
||||
"distdraw": -4548,
|
||||
"piercing": 1057,
|
||||
"integrity": 319
|
||||
},
|
||||
"blueprint": {
|
||||
"id": 89,
|
||||
"name": "Rapid fire",
|
||||
"grade": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"class": 2,
|
||||
"rating": "F",
|
||||
"enabled": true,
|
||||
"priority": 1,
|
||||
"group": "Multi-cannon",
|
||||
"mount": "Gimballed",
|
||||
"modifications": {
|
||||
"damage": 2437,
|
||||
"distdraw": 5487,
|
||||
"rof": 1120,
|
||||
"jitter": 58,
|
||||
"thermload": 1346,
|
||||
"power": 1009,
|
||||
"integrity": -202,
|
||||
"ammo": -2000
|
||||
},
|
||||
"blueprint": {
|
||||
"id": 88,
|
||||
"name": "Overcharged",
|
||||
"grade": 3,
|
||||
"special": {
|
||||
"id": 3,
|
||||
"name": "Corrosive shell"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"utility": [
|
||||
{
|
||||
"class": 0,
|
||||
"rating": "I",
|
||||
"enabled": true,
|
||||
"priority": 1,
|
||||
"group": "Heat Sink Launcher",
|
||||
"name": "Heat Sink Launcher",
|
||||
"modifications": {
|
||||
"ammo": 5000,
|
||||
"mass": 17684,
|
||||
"reload": 9707
|
||||
},
|
||||
"blueprint": {
|
||||
"id": 37,
|
||||
"name": "Ammo capacity",
|
||||
"grade": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"class": 0,
|
||||
"rating": "I",
|
||||
"enabled": true,
|
||||
"priority": 1,
|
||||
"group": "Heat Sink Launcher",
|
||||
"name": "Heat Sink Launcher",
|
||||
"modifications": {
|
||||
"ammo": 5000,
|
||||
"mass": 18520,
|
||||
"reload": 8715
|
||||
},
|
||||
"blueprint": {
|
||||
"id": 37,
|
||||
"name": "Ammo capacity",
|
||||
"grade": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"class": 0,
|
||||
"rating": "I",
|
||||
"enabled": true,
|
||||
"priority": 1,
|
||||
"group": "Chaff Launcher",
|
||||
"name": "Chaff Launcher"
|
||||
},
|
||||
{
|
||||
"class": 0,
|
||||
"rating": "A",
|
||||
"enabled": true,
|
||||
"priority": 1,
|
||||
"group": "Frame Shift Wake Scanner"
|
||||
}
|
||||
],
|
||||
"internal": [
|
||||
{
|
||||
"class": 3,
|
||||
"rating": "A",
|
||||
"enabled": true,
|
||||
"priority": 1,
|
||||
"group": "Shield Generator",
|
||||
"modifications": {
|
||||
"optmul": 1888,
|
||||
"explres": 455,
|
||||
"kinres": 546,
|
||||
"thermres": 1092,
|
||||
"brokenregen": -2614,
|
||||
"regen": -876,
|
||||
"distdraw": 463
|
||||
},
|
||||
"blueprint": {
|
||||
"id": 77,
|
||||
"name": "Reinforced",
|
||||
"grade": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"class": 3,
|
||||
"rating": "A",
|
||||
"enabled": true,
|
||||
"priority": 1,
|
||||
"group": "Fuel Scoop"
|
||||
},
|
||||
{
|
||||
"class": 2,
|
||||
"rating": "E",
|
||||
"enabled": true,
|
||||
"priority": 2,
|
||||
"group": "Cargo Rack"
|
||||
},
|
||||
{
|
||||
"class": 2,
|
||||
"rating": "E",
|
||||
"enabled": true,
|
||||
"priority": 2,
|
||||
"group": "Cargo Rack"
|
||||
},
|
||||
null,
|
||||
{
|
||||
"class": 1,
|
||||
"rating": "C",
|
||||
"enabled": true,
|
||||
"priority": 2,
|
||||
"group": "Scanner",
|
||||
"name": "Advanced Discovery Scanner"
|
||||
}
|
||||
]
|
||||
},
|
||||
"stats": {
|
||||
"class": 1,
|
||||
"hullCost": 2481550,
|
||||
"speed": 280,
|
||||
"boost": 380,
|
||||
"boostEnergy": 10,
|
||||
"agility": 6,
|
||||
"baseShieldStrength": 200,
|
||||
"baseArmour": 80,
|
||||
"hullMass": 35,
|
||||
"masslock": 7,
|
||||
"pipSpeed": 0.05,
|
||||
"moduleCostMultiplier": 1,
|
||||
"fuelCapacity": 8,
|
||||
"cargoCapacity": 8,
|
||||
"ladenMass": 104.25,
|
||||
"armour": 144,
|
||||
"shield": 404.19,
|
||||
"shieldCells": 0,
|
||||
"totalCost": 14059860,
|
||||
"unladenMass": 88.25,
|
||||
"totalDpe": 32.25,
|
||||
"totalExplDpe": 0,
|
||||
"totalKinDpe": 9.41,
|
||||
"totalThermDpe": 22.84,
|
||||
"totalDps": 53.8,
|
||||
"totalExplDps": 0,
|
||||
"totalKinDps": 17.44,
|
||||
"totalThermDps": 36.35,
|
||||
"totalSDps": 48.99,
|
||||
"totalExplSDps": 0,
|
||||
"totalKinSDps": 12.64,
|
||||
"totalThermSDps": 36.35,
|
||||
"totalEps": 9.84,
|
||||
"totalHps": 12.28,
|
||||
"shieldExplRes": 0.48,
|
||||
"shieldKinRes": 0.55,
|
||||
"shieldThermRes": 1.09,
|
||||
"hullExplRes": 1.4,
|
||||
"hullKinRes": 1.2,
|
||||
"hullThermRes": 1,
|
||||
"powerAvailable": 17.24,
|
||||
"powerRetracted": 11.3,
|
||||
"powerDeployed": 16.41,
|
||||
"unladenRange": 25.57,
|
||||
"fullTankRange": 23.92,
|
||||
"ladenRange": 22.09,
|
||||
"unladenFastestRange": 116.23,
|
||||
"ladenFastestRange": 107,
|
||||
"maxJumpCount": 5,
|
||||
"topSpeed": 386.56,
|
||||
"topBoost": 524.62
|
||||
}
|
||||
}
|
||||
@@ -2,31 +2,31 @@
|
||||
{
|
||||
"shipId": "anaconda",
|
||||
"buildName": "Imported Anaconda",
|
||||
"buildCode": "0pyttFolodDsyf5------1717--------05044j-03--2h--00.Iw18ZlA=.Aw18ZlA=",
|
||||
"buildCode": "0pyttFolodDsyf5------1717--------05044j-03--2h--00.Iw18ZlA=.Aw18ZlA=.",
|
||||
"buildText": "[Anaconda]\nS: 1F/F Pulse Laser\nS: 1F/F Pulse Laser\n\nBH: 1I Lightweight Alloy\nRB: 8E Power Plant\nTM: 7E Thrusters\nFH: 6E Frame Shift Drive\nEC: 5E Life Support\nPC: 8E Power Distributor\nSS: 8E Sensors\nFS: 5C Fuel Tank (Capacity: 32)\n\n7: 6E Cargo Rack (Capacity: 64)\n6: 5E Cargo Rack (Capacity: 32)\n6: 6E Shield Generator\n5: 4E Cargo Rack (Capacity: 16)\n4: 1E Basic Discovery Scanner\n2: 1E Cargo Rack (Capacity: 2)\n"
|
||||
},
|
||||
{
|
||||
"shipId": "anaconda",
|
||||
"buildName": "Imported Anaconda",
|
||||
"buildCode": "0pyttFolodDsyf5------1717--------05044j-03--2h--00.Iw18ZlA=.Aw18ZlA=",
|
||||
"buildCode": "0pyttFolodDsyf5------1717--------05044j-03--2h--00.Iw18ZlA=.Aw18ZlA=.",
|
||||
"buildText": "\n\n \t[Anaconda]\nS: 1F/F Pulse Laser\nS: 1F/F Pulse Laser\n\nBH: 1I Lightweight Alloy\nRB: 8E Power Plant\nTM: 7E Thrusters\nFH: 6E Frame Shift Drive\nEC: 5E Life Support\nPC: 8E Power Distributor\nSS: 8E Sensors\nFS: 5C Fuel Tank (Capacity: 32)\n\n7: 6E Cargo Rack (Capacity: 64)\n6: 5E Cargo Rack (Capacity: 32)\n6: 6E Shield Generator\n5: 4E Cargo Rack (Capacity: 16)\n4: 1E Basic Discovery Scanner\n2: 1E Cargo Rack (Capacity: 2)\n"
|
||||
},
|
||||
{
|
||||
"shipId": "cobra_mk_iii",
|
||||
"buildName": "Imported Cobra Mk III",
|
||||
"buildCode": "0patcFeldd5sdf41712222503040202490f242h.Iw1-kA==.Aw1-kA==",
|
||||
"buildCode": "0patcFeldd5sdf41712222503040202490f242h.Iw1-kA==.Aw1-kA==.",
|
||||
"buildText": "[Cobra Mk III]\nM: 1F/F Pulse Laser\nM: 1G/G Burst Laser\nS: 1E/T Fragment Cannon\nS: 1G/T Multi-cannon\nU: 0I Point Defence\nU: 0A Shield Booster\n\nBH: 1I Lightweight Alloy\nRB: 4A Power Plant\nTM: 4C Thrusters\nFH: 4E Frame Shift Drive\nEC: 3D Life Support\nPC: 2A Power Distributor\nSS: 3D Sensors\nFS: 4C Fuel Tank (Capacity: 16)\n\n4: 3E Cargo Rack (Capacity: 8)\n4: 3E Cargo Rack (Capacity: 8)\n4: 4E Shield Generator\n2: 2C Auto Field-Maintenance Unit\n2: 1E Standard Docking Computer\n2: 1E Basic Discovery Scanner\n---\nShield: 112.29 MJ\nPower : 10.45 MW retracted (67%)\n 12.16 MW deployed (78%)\n 15.60 MW available\nCargo : 16 T\nFuel : 16 T\nMass : 235.5 T empty\n 267.5 T full\nRange : 10.69 LY unladen\n 10.05 LY laden\nPrice : 2,929,040 CR\nRe-Buy: 146,452 CR @ 95% insurance\n"
|
||||
},
|
||||
{
|
||||
"shipId": "type_9_heavy",
|
||||
"buildName": "Imported Type-9 Heavy",
|
||||
"buildCode": "3pftsFklkdisif57e2k2f2h110001020306054j03022f01242i.Iw18eQ==.Aw18eQ==",
|
||||
"buildCode": "3pftsFklkdisif57e2k2f2h110001020306054j03022f01242i.Iw18eQ==.Aw18eQ==.",
|
||||
"buildText": "[Type-9 Heavy]\nM: 2D/G Fragment Cannon\nM: 2I/F Mine Launcher\nM: 2B/FD Missile Rack\nS: 1I/FS Torpedo Pylon\nS: 1F/F Burst Laser\nU: 0I Chaff Launcher\nU: 0F Electronic Countermeasure\nU: 0I Heat Sink Launcher\nU: 0I Point Defence\n\nBH: 1I Mirrored Surface Composite\nRB: 5A Power Plant\nTM: 7D Thrusters\nFH: 6A Frame Shift Drive\nEC: 5A Life Support\nPC: 4D Power Distributor\nSS: 4D Sensors\nFS: 5C Fuel Tank (Capacity: 32)\n\n8: 7E Cargo Rack (Capacity: 128)\n7: 6E Cargo Rack (Capacity: 64)\n6: 6E Shield Generator\n5: 4E Cargo Rack (Capacity: 16)\n4: 3E Cargo Rack (Capacity: 8)\n4: 1C Advanced Discovery Scanner\n3: 2E Cargo Rack (Capacity: 4)\n3: 1E Standard Docking Computer\n2: 1C Detailed Surface Scanner\n"
|
||||
},
|
||||
{
|
||||
"shipId": "vulture",
|
||||
"buildName": "Imported Vulture",
|
||||
"buildCode": "4patfFalddksif31e1e0e0j04044a0n532jf1.Iw19kA==.Aw19kA==",
|
||||
"buildCode": "4patfFalddksif31e1e0e0j04044a0n532jf1.Iw19kA==.Aw19kA==.",
|
||||
"buildText": "[Vulture]\nL: 3E/G Pulse Laser\nL: 3E/G Pulse Laser\nU: 0A Frame Shift Wake Scanner\nU: 0A Kill Warrant Scanner\nU: 0A Shield Booster\nU: 0A Shield Booster\n\nBH: 1I Reactive Surface Composite\nRB: 4A Power Plant\nTM: 5A Thrusters\nFH: 4A Frame Shift Drive\nEC: 3D Life Support\nPC: 5A Power Distributor\nSS: 4D Sensors\nFS: 3C Fuel Tank (Capacity: 8)\n\n5: 5A Shield Generator\n4: 4A Auto Field-Maintenance Unit\n2: 2A Shield Cell Bank\n1: 1A Fuel Scoop\n1: 1C Fuel Tank (Capacity: 2)"
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -1,50 +1,50 @@
|
||||
{
|
||||
"type_6_transporter": {
|
||||
"Cargo": "0p0tdFal8d8s8f4-----04040303430101.Iw1-kA==.Aw1-kA==",
|
||||
"Miner": "0p5tdFal8d8s8f42l2l---040403451q0101.Iw1-kA==.Aw1-kA==",
|
||||
"Hopper": "0p0tdFal8d0s8f41717---030302024300-.Iw1-kA==.Aw1-kA=="
|
||||
"Cargo": "0p0tdFal8d8s8f4-----04040303430101.Iw1/kA==.Aw1/kA==.",
|
||||
"Miner": "0p5tdFal8d8s8f42l2l---040403451q0101.Iw1/kA==.Aw1/kA==.",
|
||||
"Hopper": "0p0tdFal8d0s8f41717---030302024300-.Iw1/kA==.Aw1/kA==."
|
||||
},
|
||||
"type_7_transport": {
|
||||
"Cargo": "0p0tiFfliddsdf5--------0505040403480101.Iw18aQ==.Aw18aQ==",
|
||||
"Miner": "0pdtiFflid8sdf5--2l2l----0505041v03450000.Iw18aQ==.Aw18aQ=="
|
||||
"Cargo": "0p0tiFfliddsdf5--------0505040403480101.Iw18aQ==.Aw18aQ==.",
|
||||
"Miner": "0pdtiFflid8sdf5--2l2l----0505041v03450000.Iw18aQ==.Aw18aQ==."
|
||||
},
|
||||
"federal_dropship": {
|
||||
"Cargo": "0pdtiFflnddsif4-1717------05040448020201.Iw18aQ==.Aw18aQ=="
|
||||
"Cargo": "0pdtiFflnddsif4-1717------05040448020201.Iw18aQ==.Aw18aQ==."
|
||||
},
|
||||
"asp": {
|
||||
"Miner": "2pftfFflidfskf50s0s24242l2l---04054a1q02022o27.Iw18WQ==.Aw18WQ=="
|
||||
"Miner": "2pftfFflidfskf50s0s24242l2l---04054a1q02022o27.Iw18WQ==.Aw18WQ==."
|
||||
},
|
||||
"imperial_clipper": {
|
||||
"Cargo": "0p5tiFflndisnf4--0s0s----0605450302020101.Iw18aQ==.Aw18aQ==",
|
||||
"Dream": "2pktkFflndpskf40v0v0s0s0404040n4k5n5d2b29292o-.Iw18aQ==.Aw18aQ==",
|
||||
"Current": "0patkFflndfskf4----------------.Iw18aQ==.Aw18aQ=="
|
||||
"Cargo": "0p5tiFflndisnf4--0s0s----0605450302020101.Iw18aQ==.Aw18aQ==.",
|
||||
"Dream": "2pktkFflndpskf40v0v0s0s0404040n4k5n5d2b29292o-.Iw18aQ==.Aw18aQ==.",
|
||||
"Current": "0patkFflndfskf4----------------.Iw18aQ==.Aw18aQ==."
|
||||
},
|
||||
"type_9_heavy": {
|
||||
"Current": "0patsFklndnsif6---------0706054a0303020224.Iw18eQ==.Aw18eQ=="
|
||||
"Current": "0patsFklndnsif6---------0706054a0303020224.Iw18eQ==.Aw18eQ==."
|
||||
},
|
||||
"python": {
|
||||
"Cargo": "0patnFflidsssf5---------050505040448020201.Iw18eQ==.Aw18eQ==",
|
||||
"Miner": "0pktkFflidpspf50v0v0v2m2m0404--050505Ce4a1v02022o.Iw18eQ==.Aw18eQ==",
|
||||
"Dream": "2pptkFfliduspf50v0v0v27270404040m5n5n4f2d2d032t0201.Iw18eQ==.Aw18eQ==",
|
||||
"Missile": "0pttoFjljdystf52f2g2d2ePh----04044j03---002h.Iw18eQ==.Aw18eQ=="
|
||||
"Cargo": "0patnFflidsssf5---------050505040448020201.Iw18eQ==.Aw18eQ==.",
|
||||
"Miner": "0pktkFflidpspf50v0v0v2m2m0404--050505Ce4a1v02022o.Iw18eQ==.Aw18eQ==.",
|
||||
"Dream": "2pptkFfliduspf50v0v0v27270404040m5n5n4f2d2d032t0201.Iw18eQ==.Aw18eQ==.",
|
||||
"Missile": "0pttoFjljdystf52f2g2d2ePh----04044j03---002h.Iw18eQ==.Aw18eQ==."
|
||||
},
|
||||
"anaconda": {
|
||||
"Dream": "4putpFklndzsuf52c0o0o0o1m1m0q0q0404040l0b0100004k5n5n112d2d040303326b.Iw18ZlA=.Aw18ZlA=",
|
||||
"Cargo": "0patnFklndnsxf5----------------0605050504040445030301.Iw18ZlA=.Aw18ZlA=",
|
||||
"Current": "0patnFklndksxf5----------------0605050504040403034524.Iw18ZlA=.Aw18ZlA=",
|
||||
"Explorer": "0patnFklndksxf5--------0202------f7050505040s372f2i4524.Iw18ZlA=.Aw18ZlA=",
|
||||
"Test": "4putkFklkdzsuf52c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.Iw18ZlA=.Aw18ZlA="
|
||||
"Dream": "4putpFklndzsuf52c0o0o0o1m1m0q0q0404040l0b0100004k5n5n112d2d040303326b.Iw18ZlA=.Aw18ZlA=.",
|
||||
"Cargo": "0patnFklndnsxf5----------------0605050504040445030301.Iw18ZlA=.Aw18ZlA=.",
|
||||
"Current": "0patnFklndksxf5----------------0605050504040403034524.Iw18ZlA=.Aw18ZlA=.",
|
||||
"Explorer": "0patnFklndksxf5--------0202------f7050505040s372f2i4524.Iw18ZlA=.Aw18ZlA=.",
|
||||
"Test": "4putkFklkdzsuf52c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.Iw18ZlA=.Aw18ZlA=."
|
||||
},
|
||||
"diamondback_explorer": {
|
||||
"Explorer": "0p0tdFfldddsdf5---0202--320p432i2f.Iw1-kA==.Aw1-kA=="
|
||||
"Explorer": "0p0tdFfldddsdf5---0202--320p432i2f.Iw1/kA==.Aw1/kA==."
|
||||
},
|
||||
"vulture": {
|
||||
"Bounty Hunter": "3patcFalddksff31e1e0404-0l4a5d27662j.Iw19kA==.Aw19kA=="
|
||||
"Bounty Hunter": "3patcFalddksff31e1e0404-0l4a5d27662j.Iw19kA==.Aw19kA==."
|
||||
},
|
||||
"fer_de_lance": {
|
||||
"Attack": "2pfthFalidpsff31r0s0s0s0s000404-04-4a-5d27-.Iw18aQ==.Aw18aQ=="
|
||||
"Attack": "2pfthFalidpsff31r0s0s0s0s000404-04-4a-5d27-.Iw18aQ==.Aw18aQ==."
|
||||
},
|
||||
"eagle": {
|
||||
"Figther": "4p0t5F5l3d5s5f20p0p24-40532j-.Iw1-EA==.Aw1-EA=="
|
||||
"Figther": "4p0t5F5l3d5s5f20p0p24-40532j-.Iw1/EA==.Aw1/EA==."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,8 @@
|
||||
},
|
||||
"anaconda": {
|
||||
"Dream": "48A6A6A5A8A8A5C2c0o0o0o1m1m0q0q0404040l0b0100034k5n05050404040303326b.AwRj4yo5dig=.MwBhEYy6duwEziA=",
|
||||
"Cargo": "03A7D6A5D4D8D5C----------------060505054d040403030301.AwRj4yuqg===.Aw18ZlA="
|
||||
"Cargo": "03A7D6A5D4D8D5C----------------060505054d040403030301.AwRj4yuqg===.Aw18ZlA=",
|
||||
"Modified": "0pyttFolodDsyf5------1717--------05044j-03----2h00.Iw18ZlA=.Aw18ZlA=.H4sIAAAAAAAAA2MUe8HMwPD-PwDDhxeuCAAAAA=="
|
||||
},
|
||||
"diamondback_explorer": {
|
||||
"Explorer": "02A4D5A3D3D3D5C-------320p432i2f.AwRj4zTI.AwiMIypI"
|
||||
@@ -63,4 +64,4 @@
|
||||
1,
|
||||
1
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +108,8 @@ describe('Import Modal', function() {
|
||||
it('catches an invalid backup', function() {
|
||||
const importData = require('./fixtures/valid-backup');
|
||||
let invalidImportData = Object.assign({}, importData);
|
||||
invalidImportData.builds.asp = null; // Remove Asp Miner build used in comparison
|
||||
//invalidImportData.builds.asp = null; // Remove Asp Miner build used in comparison
|
||||
delete(invalidImportData.builds.asp);
|
||||
|
||||
pasteText('"this is not valid"');
|
||||
expect(modal.state.importValid).toBeFalsy();
|
||||
@@ -128,7 +129,7 @@ describe('Import Modal', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Import Detailed Build', function() {
|
||||
describe('Import Detailed V3 Build', function() {
|
||||
|
||||
beforeEach(reset);
|
||||
|
||||
@@ -141,7 +142,7 @@ describe('Import Modal', function() {
|
||||
expect(modal.state.singleBuild).toBe(true);
|
||||
clickProceed();
|
||||
expect(MockRouter.go.mock.calls.length).toBe(1);
|
||||
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/anaconda/4putkFklkdzsuf52c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.AwRj4zNKqA==.CwBhCYzBGW9qCTSqs5xA?bn=Test%20My%20Ship');
|
||||
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/anaconda?code=4putkFklkdzsuf52c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.AwRj4zNKqA%3D%3D.CwBhCYzBGW9qCTSqs5xA.&bn=Test%20My%20Ship');
|
||||
});
|
||||
|
||||
it('catches an invalid build', function() {
|
||||
@@ -153,6 +154,52 @@ describe('Import Modal', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Import Detailed V4 Build', function() {
|
||||
|
||||
beforeEach(reset);
|
||||
|
||||
it('imports a valid v4 build', function() {
|
||||
const importData = require('./fixtures/anaconda-test-detailed-export-v4');
|
||||
pasteText(JSON.stringify(importData));
|
||||
|
||||
expect(modal.state.importValid).toBeTruthy();
|
||||
expect(modal.state.errorMsg).toEqual(null);
|
||||
expect(modal.state.singleBuild).toBe(true);
|
||||
clickProceed();
|
||||
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');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Import Detailed Engineered V4 Build', function() {
|
||||
|
||||
beforeEach(reset);
|
||||
|
||||
it('imports a valid v4 build', function() {
|
||||
const importData = require('./fixtures/asp-test-detailed-export-v4');
|
||||
pasteText(JSON.stringify(importData));
|
||||
|
||||
expect(modal.state.importValid).toBeTruthy();
|
||||
expect(modal.state.errorMsg).toEqual(null);
|
||||
expect(modal.state.singleBuild).toBe(true);
|
||||
clickProceed();
|
||||
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');
|
||||
});
|
||||
|
||||
it('imports a valid v4 build with modifications', function() {
|
||||
const importData = require('./fixtures/courier-test-detailed-export-v4');
|
||||
pasteText(JSON.stringify(importData));
|
||||
|
||||
expect(modal.state.importValid).toBeTruthy();
|
||||
expect(modal.state.errorMsg).toEqual(null);
|
||||
expect(modal.state.singleBuild).toBe(true);
|
||||
clickProceed();
|
||||
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');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Import Detaild Builds Array', function() {
|
||||
|
||||
beforeEach(reset);
|
||||
@@ -178,22 +225,51 @@ describe('Import Modal', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Import Companion API Build', function() {
|
||||
|
||||
beforeEach(reset);
|
||||
|
||||
it('imports a valid v4 build', function() {
|
||||
const importData = require('./fixtures/companion-api-import-1');
|
||||
pasteText(JSON.stringify(importData));
|
||||
|
||||
expect(modal.state.importValid).toBeTruthy();
|
||||
expect(modal.state.errorMsg).toEqual(null);
|
||||
expect(modal.state.singleBuild).toBe(true);
|
||||
clickProceed();
|
||||
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');
|
||||
});
|
||||
|
||||
it('imports a valid v4 build', function() {
|
||||
const importData = require('./fixtures/companion-api-import-2');
|
||||
pasteText(JSON.stringify(importData));
|
||||
|
||||
expect(modal.state.importValid).toBeTruthy();
|
||||
expect(modal.state.errorMsg).toEqual(null);
|
||||
expect(modal.state.singleBuild).toBe(true);
|
||||
clickProceed();
|
||||
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');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Import E:D Shipyard Builds', function() {
|
||||
|
||||
it('imports a valid builds', function() {
|
||||
const imports = require('./fixtures/ed-shipyard-import-valid');
|
||||
|
||||
for (let i = 0; i < imports.length; i++ ) {
|
||||
reset();
|
||||
let fixture = imports[i];
|
||||
pasteText(fixture.buildText);
|
||||
expect(modal.state.importValid).toBeTruthy();
|
||||
expect(modal.state.errorMsg).toEqual(null);
|
||||
clickProceed();
|
||||
expect(MockRouter.go.mock.calls.length).toBe(1);
|
||||
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/' + fixture.shipId + '/' + fixture.buildCode + '?bn=' + encodeURIComponent(fixture.buildName));
|
||||
}
|
||||
});
|
||||
// it('imports a valid build', function() {
|
||||
// const imports = require('./fixtures/ed-shipyard-import-valid');
|
||||
//
|
||||
// for (let i = 0; i < imports.length; i++ ) {
|
||||
// reset();
|
||||
// let fixture = imports[i];
|
||||
// pasteText(fixture.buildText);
|
||||
// expect(modal.state.importValid).toBeTruthy();
|
||||
// expect(modal.state.errorMsg).toEqual(null);
|
||||
// clickProceed();
|
||||
// expect(MockRouter.go.mock.calls.length).toBe(1);
|
||||
// expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/' + fixture.shipId + '?code=' + encodeURIComponent(fixture.buildCode) + '&bn=' + encodeURIComponent(fixture.buildName));
|
||||
// }
|
||||
// });
|
||||
|
||||
it('catches invalid builds', function() {
|
||||
const imports = require('./fixtures/ed-shipyard-import-invalid');
|
||||
|
||||
@@ -4,16 +4,16 @@ import * as Serializer from '../src/app/shipyard/Serializer';
|
||||
import jsen from 'jsen';
|
||||
|
||||
describe("Serializer", function() {
|
||||
const anacondaTestExport = require.requireActual('./fixtures/anaconda-test-detailed-export-v3');
|
||||
const anacondaTestExport = require.requireActual('./fixtures/anaconda-test-detailed-export-v4');
|
||||
const code = anacondaTestExport.references[0].code;
|
||||
const anaconda = Ships.anaconda;
|
||||
const validate = jsen(require('../src/schemas/ship-loadout/3'));
|
||||
const validate = jsen(require('../src/schemas/ship-loadout/4'));
|
||||
|
||||
describe("To Detailed Build", function() {
|
||||
let testBuild = new Ship('anaconda', anaconda.properties, anaconda.slots).buildFrom(code);
|
||||
let exportData = Serializer.toDetailedBuild('Test My Ship', testBuild);
|
||||
|
||||
it("conforms to the v3 ship-loadout schema", function() {
|
||||
it("conforms to the v4 ship-loadout schema", function() {
|
||||
expect(validate(exportData)).toBe(true);
|
||||
});
|
||||
|
||||
@@ -31,7 +31,7 @@ describe("Serializer", function() {
|
||||
const builds = require('./fixtures/expected-builds');
|
||||
const exportData = Serializer.toDetailedExport(builds);
|
||||
|
||||
it("conforms to the v3 ship-loadout schema", function() {
|
||||
it("conforms to the v4 ship-loadout schema", function() {
|
||||
expect(exportData instanceof Array).toBe(true);
|
||||
|
||||
for (let detailedBuild of exportData) {
|
||||
|
||||
@@ -16,7 +16,7 @@ describe("Ship", function() {
|
||||
ship.buildWith(shipData.defaults);
|
||||
|
||||
expect(ship.totalCost).toEqual(shipData.retailCost, s + ' retail cost does not match default build cost');
|
||||
expect(ship.cargoCapacity).toBeDefined(s + ' cargo');
|
||||
expect(ship.cargoCapacity).toBeDefined();
|
||||
expect(ship.priorityBands[0].retracted).toBeGreaterThan(0, s + ' priorityBands');
|
||||
expect(ship.powerAvailable).toBeGreaterThan(0, s + ' powerAvailable');
|
||||
expect(ship.unladenRange).toBeGreaterThan(0, s + ' unladenRange');
|
||||
@@ -24,7 +24,7 @@ describe("Ship", function() {
|
||||
expect(ship.fuelCapacity).toBeGreaterThan(0, s + ' fuelCapacity');
|
||||
expect(ship.unladenFastestRange).toBeGreaterThan(0, s + ' unladenFastestRange');
|
||||
expect(ship.ladenFastestRange).toBeGreaterThan(0, s + ' ladenFastestRange');
|
||||
expect(ship.shieldStrength).toBeGreaterThan(0, s + ' shieldStrength');
|
||||
expect(ship.shield).toBeGreaterThan(0, s + ' shield');
|
||||
expect(ship.armour).toBeGreaterThan(0, s + ' armour');
|
||||
expect(ship.topSpeed).toBeGreaterThan(0, s + ' topSpeed');
|
||||
}
|
||||
|
||||
19
package.json
19
package.json
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "coriolis_shipyard",
|
||||
"version": "2.1.2",
|
||||
"version": "2.2.5",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/cmmcleod/coriolis"
|
||||
"url": "https://github.com/EDCD/coriolis"
|
||||
},
|
||||
"homepage": "https://coriolis.io",
|
||||
"bugs": "https://github.com/cmmcleod/coriolis/issues",
|
||||
"homepage": "https://coriolis.edcd.io",
|
||||
"bugs": "https://github.com/EDCD/coriolis/issues",
|
||||
"private": true,
|
||||
"engine": "node >= 4.0.0",
|
||||
"license": "MIT",
|
||||
@@ -24,15 +24,15 @@
|
||||
},
|
||||
"jest": {
|
||||
"scriptPreprocessor": "<rootDir>/node_modules/babel-jest",
|
||||
"testFileExtensions": [
|
||||
"js"
|
||||
],
|
||||
"testRegex": "(/__tests__/test-.*|\\.(test|spec))\\.js$",
|
||||
"moduleFileExtensions": [
|
||||
"js",
|
||||
"json",
|
||||
"jsx"
|
||||
],
|
||||
"automock": true,
|
||||
"unmockedModulePathPatterns": [
|
||||
"<rootDir>/node_modules/lodash",
|
||||
"<rootDir>/node_modules/react",
|
||||
"<rootDir>/node_modules/react-dom",
|
||||
"<rootDir>/node_modules/react-addons-test-utils",
|
||||
@@ -68,7 +68,7 @@
|
||||
"extract-text-webpack-plugin": "^0.9.1",
|
||||
"file-loader": "^0.8.4",
|
||||
"html-webpack-plugin": "^1.7.0",
|
||||
"jest-cli": "^0.9.2",
|
||||
"jest-cli": "^16.0.1",
|
||||
"jsen": "^0.6.0",
|
||||
"json-loader": "^0.5.3",
|
||||
"less": "^2.5.3",
|
||||
@@ -84,10 +84,13 @@
|
||||
"dependencies": {
|
||||
"babel-polyfill": "*",
|
||||
"classnames": "^2.2.0",
|
||||
"browserify-zlib": "ipfs/browserify-zlib",
|
||||
"coriolis-data": "EDCD/coriolis-data",
|
||||
"d3": "3.5.16",
|
||||
"fbemitter": "^2.0.0",
|
||||
"lodash": "^4.15.0",
|
||||
"lz-string": "^1.4.4",
|
||||
"react-number-editor": "Athanasius/react-number-editor.git#miggy",
|
||||
"react": "^15.0.1",
|
||||
"react-dom": "^15.0.1",
|
||||
"superagent": "^1.4.0"
|
||||
|
||||
@@ -7,6 +7,8 @@ import Persist from './stores/Persist';
|
||||
import Header from './components/Header';
|
||||
import Tooltip from './components/Tooltip';
|
||||
import ModalImport from './components/ModalImport';
|
||||
import * as CompanionApiUtils from './utils/CompanionApiUtils';
|
||||
import { outfitURL } from './utils/UrlGenerators';
|
||||
|
||||
import AboutPage from './pages/AboutPage';
|
||||
import NotFoundPage from './pages/NotFoundPage';
|
||||
@@ -15,6 +17,8 @@ import ComparisonPage from './pages/ComparisonPage';
|
||||
import ShipyardPage from './pages/ShipyardPage';
|
||||
import ErrorDetails from './pages/ErrorDetails';
|
||||
|
||||
const zlib = require('zlib');
|
||||
|
||||
/**
|
||||
* Coriolis App
|
||||
*/
|
||||
@@ -52,6 +56,7 @@ export default class Coriolis extends React.Component {
|
||||
this._onLanguageChange = this._onLanguageChange.bind(this);
|
||||
this._onSizeRatioChange = this._onSizeRatioChange.bind(this);
|
||||
this._keyDown = this._keyDown.bind(this);
|
||||
this._importBuild = this._importBuild.bind(this);
|
||||
|
||||
this.emitter = new EventEmitter();
|
||||
this.state = {
|
||||
@@ -63,13 +68,36 @@ export default class Coriolis extends React.Component {
|
||||
};
|
||||
|
||||
Router('', (r) => this._setPage(ShipyardPage, r));
|
||||
Router('/import?', (r) => this._importBuild(r));
|
||||
Router('/import/:data', (r) => this._importBuild(r));
|
||||
Router('/outfit/?', (r) => this._setPage(OutfittingPage, r));
|
||||
Router('/outfit/:ship/?', (r) => this._setPage(OutfittingPage, r));
|
||||
Router('/outfit/:ship/:code?', (r) => this._setPage(OutfittingPage, r));
|
||||
Router('/compare/:name?', (r) => this._setPage(ComparisonPage, r));
|
||||
Router('/comparison?', (r) => this._setPage(ComparisonPage, r));
|
||||
Router('/comparison/:code', (r) => this._setPage(ComparisonPage, r));
|
||||
Router('/about', (r) => this._setPage(AboutPage, r));
|
||||
Router('*', (r) => this._setPage(null, r));
|
||||
}
|
||||
|
||||
/**
|
||||
* Import a build directly
|
||||
* @param {Object} r The current route
|
||||
*/
|
||||
_importBuild(r) {
|
||||
try {
|
||||
// Need to decode and gunzip the data, then build the ship
|
||||
const data = zlib.gunzipSync(new Buffer(r.params.data, 'base64'));
|
||||
const json = JSON.parse(data);
|
||||
const ship = CompanionApiUtils.shipFromJson(json);
|
||||
r.params.ship = ship.id;
|
||||
r.params.code = ship.toString();
|
||||
this._setPage(OutfittingPage, r);
|
||||
} catch (err) {
|
||||
this._onError('Failed to import ship', r.path, 0, 0, err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates / Sets the page and route context
|
||||
* @param {[type]} page The page to be shown
|
||||
|
||||
@@ -97,7 +97,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
let m = modules[i];
|
||||
let mount = null;
|
||||
let disabled = m.maxmass && (mass + (m.mass ? m.mass : 0)) > m.maxmass;
|
||||
let active = mountedModule && mountedModule === m;
|
||||
let active = mountedModule && mountedModule.id === m.id;
|
||||
let classes = cn(m.name ? 'lc' : 'c', {
|
||||
warning: !disabled && warningFunc && warningFunc(m),
|
||||
active,
|
||||
|
||||
@@ -2,6 +2,7 @@ import React from 'react';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import Link from './Link';
|
||||
import cn from 'classnames';
|
||||
import { outfitURL } from '../utils/UrlGenerators';
|
||||
import { SizeMap } from '../shipyard/Constants';
|
||||
|
||||
|
||||
@@ -71,7 +72,7 @@ export default class ComparisonTable extends TranslatedComponent {
|
||||
* @return {React.Component} Table row
|
||||
*/
|
||||
_buildRow(build, facets, formats, units) {
|
||||
let url = `/outfit/${build.id}/${build.toString()}?bn=${build.buildName}`;
|
||||
let url = outfitURL(build.id, build.toString(), build.buildName);
|
||||
let cells = [
|
||||
<td key='s' className='tl'><Link href={url}>{build.name}</Link></td>,
|
||||
<td key='bn' className='tl'><Link href={url}>{build.buildName}</Link></td>
|
||||
|
||||
@@ -379,7 +379,7 @@ export default class CostSection extends TranslatedComponent {
|
||||
<td colSpan='4' className='lbl cap' >{translate('retrofit from')}</td>
|
||||
<td className='val cen' style={{ borderRight: 'none', width: '1em' }}><u className='primary-disabled'>▾</u></td>
|
||||
<td className='val' style={{ borderLeft:'none', padding: 0 }}>
|
||||
<select style={{ width: '100%', padding: 0 }} value={retrofitName} onChange={this._onBaseRetrofitChange}>
|
||||
<select style={{ width: '100%', padding: 0 }} value={retrofitName || translate('Stock')} onChange={this._onBaseRetrofitChange}>
|
||||
{options}
|
||||
</select>
|
||||
</td>
|
||||
@@ -419,7 +419,9 @@ export default class CostSection extends TranslatedComponent {
|
||||
let retroSlotGroup = retrofitShip[g];
|
||||
let slotGroup = ship[g];
|
||||
for (i = 0, l = slotGroup.length; i < l; i++) {
|
||||
if (slotGroup[i].m != retroSlotGroup[i].m) {
|
||||
const modId = slotGroup[i].m ? slotGroup[i].m.eddbID : null;
|
||||
const retroModId = retroSlotGroup[i].m ? retroSlotGroup[i].m.eddbID : null;
|
||||
if (modId != retroModId) {
|
||||
item = { netCost: 0, retroItem: retroSlotGroup[i] };
|
||||
if (slotGroup[i].m) {
|
||||
item.buyName = slotGroup[i].m.name || slotGroup[i].m.grp;
|
||||
@@ -505,19 +507,19 @@ export default class CostSection extends TranslatedComponent {
|
||||
scoop = true;
|
||||
break;
|
||||
case 'scb':
|
||||
q = slotGroup[i].m.cells;
|
||||
q = slotGroup[i].m.getCells();
|
||||
break;
|
||||
case 'am':
|
||||
q = slotGroup[i].m.ammo;
|
||||
q = slotGroup[i].m.getAmmo();
|
||||
break;
|
||||
case 'pv':
|
||||
srvs += slotGroup[i].m.vehicles;
|
||||
srvs += slotGroup[i].m.getBays();
|
||||
break;
|
||||
case 'fx': case 'hb': case 'cc': case 'pc':
|
||||
limpets = ship.cargoCapacity;
|
||||
break;
|
||||
default:
|
||||
q = slotGroup[i].m.clip + slotGroup[i].m.ammo;
|
||||
q = slotGroup[i].m.getClip() + slotGroup[i].m.getAmmo();
|
||||
}
|
||||
// Calculate ammo costs only if a cost is specified
|
||||
if (slotGroup[i].m.ammocost > 0) {
|
||||
@@ -530,6 +532,17 @@ export default class CostSection extends TranslatedComponent {
|
||||
ammoCosts.push(item);
|
||||
ammoTotal += item.total;
|
||||
}
|
||||
// Add fighters
|
||||
if (slotGroup[i].m.grp === 'fh') {
|
||||
item = {
|
||||
m: slotGroup[i].m,
|
||||
max: slotGroup[i].m.getRebuildsPerBay() * slotGroup[i].m.getBays(),
|
||||
cost: slotGroup[i].m.fightercost,
|
||||
total: slotGroup[i].m.getRebuildsPerBay() * slotGroup[i].m.getBays() * slotGroup[i].m.fightercost
|
||||
};
|
||||
ammoCosts.push(item);
|
||||
ammoTotal += item.total;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -550,12 +563,13 @@ export default class CostSection extends TranslatedComponent {
|
||||
item = {
|
||||
m: { name: 'SRVs', class: '', rating: '' },
|
||||
max: srvs,
|
||||
cost: 6005,
|
||||
total: srvs * 6005
|
||||
cost: 1030,
|
||||
total: srvs * 1030
|
||||
};
|
||||
ammoCosts.push(item);
|
||||
ammoTotal += item.total;
|
||||
}
|
||||
|
||||
// Calculate refuel costs if no scoop present
|
||||
if (!scoop) {
|
||||
item = {
|
||||
@@ -606,6 +620,7 @@ export default class CostSection extends TranslatedComponent {
|
||||
}
|
||||
|
||||
if (nextProps.ship != this.props.ship || nextProps.code != this.props.code) {
|
||||
nextProps.ship.applyDiscounts(Persist.getShipDiscount(), Persist.getModuleDiscount());
|
||||
this._updateAmmoCosts(nextProps.ship);
|
||||
this._updateRetrofit(nextProps.ship, retrofitShip);
|
||||
this._sortCost(nextProps.ship);
|
||||
|
||||
75
src/app/components/DefenceSummary.jsx
Normal file
75
src/app/components/DefenceSummary.jsx
Normal file
@@ -0,0 +1,75 @@
|
||||
import React from 'react';
|
||||
import cn from 'classnames';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import { DamageKinetic, DamageThermal, DamageExplosive } from './SvgIcons';
|
||||
|
||||
/**
|
||||
* Defence summary
|
||||
*/
|
||||
export default class DefenceSummary extends TranslatedComponent {
|
||||
static PropTypes = {
|
||||
ship: React.PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param {Object} props React Component properties
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render defence 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);
|
||||
|
||||
return (
|
||||
<span>
|
||||
<h1>{translate('defence summary')}</h1>
|
||||
<table className='summary' style={{ marginLeft: 'auto', marginRight: 'auto', lineHeight: '1em', backgroundColor: 'transparent' }}>
|
||||
<tbody>
|
||||
{ship.shield ?
|
||||
<tr>
|
||||
<td colSpan='4' className='summary'><h2>{translate('shields')}: {formats.int(ship.shield)} {units.MJ}</h2></td>
|
||||
</tr> : null }
|
||||
{ship.shield ?
|
||||
<tr>
|
||||
<td className='ri' onMouseEnter={termtip.bind(null, 'PHRASE_SG_RECOVER', { cap: 0 })} onMouseLeave={hide}>{translate('recovery')}</td>
|
||||
<td className='le'>{formats.time(ship.calcShieldRecovery())}</td>
|
||||
<td className='ri' onMouseEnter={termtip.bind(null, 'PHRASE_SG_RECHARGE', { cap: 0 })} onMouseLeave={hide}>{translate('recharge')}</td>
|
||||
<td className='le'>{formats.time(ship.calcShieldRecharge())}</td>
|
||||
</tr> : null }
|
||||
{ship.shield ?
|
||||
<tr>
|
||||
<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' onMouseOver={termtip.bind(null, 'kinetic')} onMouseOut={tooltip.bind(null, null)}><DamageKinetic /> {formats.pct1(ship.shieldKinRes || 1)}</td>
|
||||
<td className='ri' onMouseOver={termtip.bind(null, 'thermal')} onMouseOut={tooltip.bind(null, null)}><DamageThermal /> {formats.pct1(ship.shieldThermRes || 1)}</td>
|
||||
</tr> : null }
|
||||
|
||||
{ ship.shield && ship.shieldCells ?
|
||||
<tr>
|
||||
<td colSpan='4'><h2>{translate('shield cells')}: {formats.int(ship.shieldCells)} {units.MJ}</h2></td>
|
||||
</tr> : null }
|
||||
|
||||
<tr>
|
||||
<td colSpan='4'><h2>{translate('armour')}: {formats.int(ship.armour)}</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<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' onMouseOver={termtip.bind(null, 'kinetic')} onMouseOut={tooltip.bind(null, null)}><DamageKinetic /> {formats.pct1(ship.hullKinRes || 1)}</td>
|
||||
<td className='ri' onMouseOver={termtip.bind(null, 'thermal')} onMouseOut={tooltip.bind(null, null)}><DamageThermal /> {formats.pct1(ship.hullThermRes || 1)}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,10 @@
|
||||
import React from 'react';
|
||||
import Slot from './Slot';
|
||||
import Persist from '../stores/Persist';
|
||||
import { DamageKinetic, DamageThermal, DamageExplosive, MountFixed, MountGimballed, MountTurret, ListModifications, Modified } from './SvgIcons';
|
||||
import { Modifications } from 'coriolis-data/dist';
|
||||
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
||||
|
||||
|
||||
/**
|
||||
* Hardpoint / Utility Slot
|
||||
@@ -33,24 +38,50 @@ export default class HardpointSlot extends Slot {
|
||||
*/
|
||||
_getSlotDetails(m, translate, formats, u) {
|
||||
if (m) {
|
||||
let classRating = `${m.class}${m.rating}${m.mount ? '/' + m.mount : ''}${m.missile ? m.missile : ''}`;
|
||||
let classRating = `${m.class}${m.rating}${m.missile ? '/' + m.missile : ''}`;
|
||||
let { drag, drop } = this.props;
|
||||
let { termtip, tooltip } = this.context;
|
||||
let validMods = Modifications.validity[m.grp] || [];
|
||||
let showModuleResistances = Persist.showModuleResistances();
|
||||
|
||||
// Modifications tooltip shows blueprint and grade, if available
|
||||
let modTT = translate('modified');
|
||||
if (m && m.blueprint) {
|
||||
modTT = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
|
||||
if (m.blueprint.special && m.blueprint.special.id) {
|
||||
modTT += ', ' + translate(m.blueprint.special.name);
|
||||
}
|
||||
}
|
||||
|
||||
return <div className='details' draggable='true' onDragStart={drag} onDragEnd={drop}>
|
||||
<div className={'cb'}>
|
||||
<div className={'l'}>{classRating} {translate(m.name || m.grp)}</div>
|
||||
<div className={'r'}>{m.mass}{u.T}</div>
|
||||
<div className={'l'}>
|
||||
{m.mount && m.mount == 'F' ? <span onMouseOver={termtip.bind(null, 'fixed')} onMouseOut={tooltip.bind(null, null)}><MountFixed /></span> : ''}
|
||||
{m.mount && m.mount == 'G' ? <span onMouseOver={termtip.bind(null, 'gimballed')} onMouseOut={tooltip.bind(null, null)}><MountGimballed /></span> : ''}
|
||||
{m.mount && m.mount == 'T' ? <span onMouseOver={termtip.bind(null, 'turreted')} onMouseOut={tooltip.bind(null, null)}><MountTurret /></span> : ''}
|
||||
{m.getDamageType() && m.getDamageType().match('K') ? <span onMouseOver={termtip.bind(null, 'kinetic')} onMouseOut={tooltip.bind(null, null)}><DamageKinetic /></span> : ''}
|
||||
{m.getDamageType() && m.getDamageType().match('T') ? <span onMouseOver={termtip.bind(null, 'thermal')} onMouseOut={tooltip.bind(null, null)}><DamageThermal /></span> : ''}
|
||||
{m.getDamageType() && m.getDamageType().match('E') ? <span onMouseOver={termtip.bind(null, 'explosive')} onMouseOut={tooltip.bind(null, null)}><DamageExplosive /></span> : ''}
|
||||
{classRating} {translate(m.name || m.grp)}{ m.mods && Object.keys(m.mods).length > 0 ? <span className='r' onMouseOver={termtip.bind(null, modTT)} onMouseOut={tooltip.bind(null, null)}><Modified /></span> : null }
|
||||
</div>
|
||||
|
||||
<div className={'r'}>{formats.round(m.getMass())}{u.T}</div>
|
||||
</div>
|
||||
<div className={'cb'}>
|
||||
{ m.damage ? <div className={'l'}>{translate('damage')}: {m.damage} { m.ssdam ? <span>({formats.int(m.ssdam)} {u.MJ})</span> : null }</div> : null }
|
||||
{ m.dps ? <div className={'l'}>{translate('DPS')}: {m.dps} { m.mjdps ? <span>({formats.int(m.mjdps)} {u.MJ})</span> : null }</div> : null }
|
||||
{ m.thermload ? <div className={'l'}>{translate('T-Load')}: {m.thermload}</div> : null }
|
||||
{ m.type ? <div className={'l'}>{translate('type')}: {m.type}</div> : null }
|
||||
{ m.rof ? <div className={'l'}>{translate('ROF')}: {m.rof}{u.ps}</div> : null }
|
||||
{ m.armourpen ? <div className={'l'}>{translate('pen')}: {m.armourpen}</div> : null }
|
||||
{ m.shieldmul ? <div className={'l'}>+{formats.rPct(m.shieldmul)}</div> : null }
|
||||
{ m.range ? <div className={'l'}>{m.range} <u>km</u></div> : null }
|
||||
{ m.ammo >= 0 ? <div className={'l'}>{translate('ammo')}: {formats.int(m.clip)}+{formats.int(m.ammo)}</div> : null }
|
||||
{ m.getDps() ? <div className={'l'} onMouseOver={termtip.bind(null, m.getClip() ? 'dpssdps' : 'dps')} onMouseOut={tooltip.bind(null, null)}>{translate('DPS')}: {formats.round1(m.getDps())} { m.getClip() ? <span>({formats.round1((m.getClip() * m.getDps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload())) })</span> : null }</div> : null }
|
||||
{ m.getEps() ? <div className={'l'} onMouseOver={termtip.bind(null, m.getClip() ? 'epsseps' : 'eps')} onMouseOut={tooltip.bind(null, null)}>{translate('EPS')}: {formats.round1(m.getEps())}{u.MW} { m.getClip() ? <span>({formats.round1((m.getClip() * m.getEps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload())) }{u.MW})</span> : null }</div> : null }
|
||||
{ m.getHps() ? <div className={'l'} onMouseOver={termtip.bind(null, m.getClip() ? 'hpsshps' : 'hps')} onMouseOut={tooltip.bind(null, null)}>{translate('HPS')}: {formats.round1(m.getHps())} { m.getClip() ? <span>({formats.round1((m.getClip() * m.getHps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload())) })</span> : null }</div> : null }
|
||||
{ m.getDps() && m.getEps() ? <div className={'l'} onMouseOver={termtip.bind(null, 'dpe')} onMouseOut={tooltip.bind(null, null)}>{translate('DPE')}: {formats.f1(m.getDps() / m.getEps())}</div> : null }
|
||||
{ m.getRoF() ? <div className={'l'} onMouseOver={termtip.bind(null, 'rof')} onMouseOut={tooltip.bind(null, null)}>{translate('ROF')}: {formats.f1(m.getRoF())}{u.ps}</div> : null }
|
||||
{ m.getRange() ? <div className={'l'}>{translate('range')} {formats.f1(m.getRange() / 1000)}{u.km}</div> : null }
|
||||
{ m.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.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.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 }
|
||||
{ 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 }
|
||||
|
||||
</div>
|
||||
</div>;
|
||||
} else {
|
||||
|
||||
@@ -69,6 +69,7 @@ export default class HardpointsSlotSection extends SlotSection {
|
||||
availableModules={() => availableModules.getHps(h.maxClass)}
|
||||
onOpen={this._openMenu.bind(this, h)}
|
||||
onSelect={this._selectModule.bind(this, h)}
|
||||
onChange={this.props.onChange}
|
||||
selected={currentMenu == h}
|
||||
drag={this._drag.bind(this, h)}
|
||||
dragOver={this._dragOverSlot.bind(this, h)}
|
||||
@@ -126,6 +127,16 @@ export default class HardpointsSlotSection extends SlotSection {
|
||||
<li className='c' onClick={_fill.bind(this, 'c', 'G')}><MountGimballed className='lg'/></li>
|
||||
<li className='c' onClick={_fill.bind(this, 'c', 'T')}><MountTurret className='lg'/></li>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('fc')}</div>
|
||||
<ul>
|
||||
<li className='c' onClick={_fill.bind(this, 'fc', 'F')}><MountFixed 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>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('nl')}</div>
|
||||
<ul>
|
||||
<li className='lc' onClick={_fill.bind(this, 'nl', 'F')}>{translate('nl')}</li>
|
||||
</ul>
|
||||
</div>;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ function normalizePercent(val) {
|
||||
if (val === '' || isNaN(val)) {
|
||||
return 0;
|
||||
}
|
||||
val = Math.round(val * 100) / 100;
|
||||
val = Math.round(val * 1000) / 1000;
|
||||
return val >= 100 ? 100 : val;
|
||||
}
|
||||
|
||||
@@ -203,6 +203,13 @@ export default class Header extends TranslatedComponent {
|
||||
Persist.showTooltips(!Persist.showTooltips());
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle module resistances setting
|
||||
*/
|
||||
_toggleModuleResistances() {
|
||||
Persist.showModuleResistances(!Persist.showModuleResistances());
|
||||
}
|
||||
|
||||
/**
|
||||
* Show delete all modal
|
||||
* @param {SyntheticEvent} e Event
|
||||
@@ -359,6 +366,7 @@ export default class Header extends TranslatedComponent {
|
||||
_getSettingsMenu() {
|
||||
let translate = this.context.language.translate;
|
||||
let tips = Persist.showTooltips();
|
||||
let moduleResistances = Persist.showModuleResistances();
|
||||
|
||||
return (
|
||||
<div className='menu-list no-wrap cap' onClick={ (e) => e.stopPropagation() }>
|
||||
@@ -376,6 +384,10 @@ export default class Header extends TranslatedComponent {
|
||||
<td>{translate('tooltips')}</td>
|
||||
<td className={cn('ri', { disabled: !tips, 'primary-disabled': tips })}>{(tips ? '✓' : '✗')}</td>
|
||||
</tr>
|
||||
<tr className='cap ptr' onClick={this._toggleModuleResistances} >
|
||||
<td>{translate('module resistances')}</td>
|
||||
<td className={cn('ri', { disabled: !moduleResistances, 'primary-disabled': moduleResistances })}>{(moduleResistances ? '✓' : '✗')}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{translate('insurance')}</td>
|
||||
<td className='ri'>
|
||||
@@ -438,6 +450,7 @@ export default class Header extends TranslatedComponent {
|
||||
Persist.addListener('deletedAll', update);
|
||||
Persist.addListener('builds', update);
|
||||
Persist.addListener('tooltips', update);
|
||||
Persist.addListener('moduleresistances', update);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -512,4 +525,4 @@ export default class Header extends TranslatedComponent {
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import React from 'react';
|
||||
import Slot from './Slot';
|
||||
import { Infinite } from './SvgIcons';
|
||||
import Persist from '../stores/Persist';
|
||||
import { ListModifications, Modified } from './SvgIcons';
|
||||
import { Modifications } from 'coriolis-data/dist';
|
||||
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
||||
|
||||
/**
|
||||
* Internal Slot
|
||||
@@ -18,30 +21,50 @@ export default class InternalSlot extends Slot {
|
||||
_getSlotDetails(m, translate, formats, u) {
|
||||
if (m) {
|
||||
let classRating = m.class + m.rating;
|
||||
let { drag, drop } = this.props;
|
||||
let { drag, drop, ship } = this.props;
|
||||
let { termtip, tooltip } = this.context;
|
||||
let validMods = Modifications.validity[m.grp] || [];
|
||||
let showModuleResistances = Persist.showModuleResistances();
|
||||
|
||||
// Modifications tooltip shows blueprint and grade, if available
|
||||
let modTT = translate('modified');
|
||||
if (m && m.blueprint) {
|
||||
modTT = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
|
||||
}
|
||||
|
||||
let mass = m.getMass() || m.cargo || m.fuel || 0;
|
||||
return <div className='details' draggable='true' onDragStart={drag} onDragEnd={drop}>
|
||||
<div className={'cb'}>
|
||||
<div className={'l'}>{classRating} {translate(m.name || m.grp)}</div>
|
||||
<div className={'r'}>{m.mass || m.cargo || m.fuel || 0}{u.T}</div>
|
||||
<div className={'l'}>{classRating} {translate(m.name || m.grp)}{m.mods && Object.keys(m.mods).length > 0 ? <span onMouseOver={termtip.bind(null, modTT)} onMouseOut={tooltip.bind(null, null)}><Modified /></span> : ''}</div>
|
||||
<div className={'r'}>{formats.round(mass)}{u.T}</div>
|
||||
</div>
|
||||
<div className={'cb'}>
|
||||
{ m.optmass ? <div className={'l'}>{translate('optimal mass')}: {m.optmass}{u.T}</div> : null }
|
||||
{ m.maxmass ? <div className={'l'}>{translate('max mass')}: {m.maxmass}{u.T}</div> : null }
|
||||
{ m.getOptMass() ? <div className={'l'}>{translate('optimal mass')}: {formats.int(m.getOptMass())}{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.bays ? <div className={'l'}>{translate('bays')}: {m.bays}</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.ammo ? <div className={'l'}>{translate('ammo')}: {formats.gen(m.ammo)}</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.recharge ? <div className={'l'}>{translate('recharge')}: {m.recharge} <u>MJ</u> {translate('total')}: {m.cells * m.recharge}{u.MJ}</div> : null }
|
||||
{ m.shieldreinforcement ? <div className={'l'}>{translate('shieldreinforcement')}: {formats.int(m.getShieldReinforcement())} <u>MJ</u> {translate('total')}: {formats.int(m.cells * m.getShieldReinforcement())}{u.MJ}</div> : null }
|
||||
{ m.repair ? <div className={'l'}>{translate('repair')}: {m.repair}</div> : null }
|
||||
{ m.range ? <div className={'l'}>{translate('range')} {m.range}{u.km}</div> : null }
|
||||
{ m.getFacingLimit() ? <div className={'l'}>{translate('facinglimit')} {formats.f1(m.getFacingLimit())}°</div> : null }
|
||||
{ m.getRange() ? <div className={'l'}>{translate('range')} {formats.f2(m.getRange())}{u.km}</div> : null }
|
||||
{ m.getRangeT() ? <div className={'l'}>{translate('ranget')} {formats.f1(m.getRangeT())}{u.s}</div> : null }
|
||||
{ m.spinup ? <div className={'l'}>{translate('spinup')}: {formats.f1(m.spinup)}{u.s}</div> : null }
|
||||
{ m.time ? <div className={'l'}>{translate('time')}: {formats.time(m.time)}</div> : null }
|
||||
{ m.maximum ? <div className={'l'}>{translate('max')}: {(m.maximum)}</div> : null }
|
||||
{ m.rangeLS ? <div className={'l'}>{translate('range')}: {m.rangeLS}{u.Ls}</div> : null }
|
||||
{ m.rangeLS === null ? <div className={'l'}><Infinite/>{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.armouradd ? <div className={'l'}>+{m.armouradd} <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.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.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 }
|
||||
|
||||
{ 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 }
|
||||
|
||||
</div>
|
||||
</div>;
|
||||
} else {
|
||||
|
||||
@@ -16,12 +16,17 @@ export default class InternalSlotSection extends SlotSection {
|
||||
* @param {Object} context React Component context
|
||||
*/
|
||||
constructor(props, context) {
|
||||
super(props, context, 'internal', 'internal compartments');
|
||||
super(props, context, 'internal', 'optional internal');
|
||||
|
||||
this._empty = this._empty.bind(this);
|
||||
this._fillWithCargo = this._fillWithCargo.bind(this);
|
||||
this._fillWithCells = this._fillWithCells.bind(this);
|
||||
this._fillWithArmor = this._fillWithArmor.bind(this);
|
||||
this._fillWithFuelTanks = this._fillWithFuelTanks.bind(this);
|
||||
this._fillWithLuxuryCabins = this._fillWithLuxuryCabins.bind(this);
|
||||
this._fillWithFirstClassCabins = this._fillWithFirstClassCabins.bind(this);
|
||||
this._fillWithBusinessClassCabins = this._fillWithBusinessClassCabins.bind(this);
|
||||
this._fillWithEconomyClassCabins = this._fillWithEconomyClassCabins.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,6 +54,86 @@ export default class InternalSlotSection extends SlotSection {
|
||||
this._close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill all slots with fuel tanks
|
||||
* @param {SyntheticEvent} event Event
|
||||
*/
|
||||
_fillWithFuelTanks(event) {
|
||||
let clobber = event.getModifierState('Alt');
|
||||
let ship = this.props.ship;
|
||||
ship.internal.forEach((slot) => {
|
||||
if (clobber || !slot.m) {
|
||||
ship.use(slot, ModuleUtils.findInternal('ft', slot.maxClass, 'C'));
|
||||
}
|
||||
});
|
||||
this.props.onChange();
|
||||
this._close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill all slots with luxury passenger cabins
|
||||
* @param {SyntheticEvent} event Event
|
||||
*/
|
||||
_fillWithLuxuryCabins(event) {
|
||||
let clobber = event.getModifierState('Alt');
|
||||
let ship = this.props.ship;
|
||||
ship.internal.forEach((slot) => {
|
||||
if (clobber || !slot.m) {
|
||||
ship.use(slot, ModuleUtils.findInternal('pcq', Math.min(slot.maxClass, 6), 'B')); // Passenger cabins top out at 6
|
||||
}
|
||||
});
|
||||
this.props.onChange();
|
||||
this._close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill all slots with first class passenger cabins
|
||||
* @param {SyntheticEvent} event Event
|
||||
*/
|
||||
_fillWithFirstClassCabins(event) {
|
||||
let clobber = event.getModifierState('Alt');
|
||||
let ship = this.props.ship;
|
||||
ship.internal.forEach((slot) => {
|
||||
if (clobber || !slot.m) {
|
||||
ship.use(slot, ModuleUtils.findInternal('pcm', Math.min(slot.maxClass, 6), 'C')); // Passenger cabins top out at 6
|
||||
}
|
||||
});
|
||||
this.props.onChange();
|
||||
this._close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill all slots with business class passenger cabins
|
||||
* @param {SyntheticEvent} event Event
|
||||
*/
|
||||
_fillWithBusinessClassCabins(event) {
|
||||
let clobber = event.getModifierState('Alt');
|
||||
let ship = this.props.ship;
|
||||
ship.internal.forEach((slot) => {
|
||||
if (clobber || !slot.m) {
|
||||
ship.use(slot, ModuleUtils.findInternal('pci', Math.min(slot.maxClass, 6), 'D')); // Passenger cabins top out at 6
|
||||
}
|
||||
});
|
||||
this.props.onChange();
|
||||
this._close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill all slots with economy class passenger cabins
|
||||
* @param {SyntheticEvent} event Event
|
||||
*/
|
||||
_fillWithEconomyClassCabins(event) {
|
||||
let clobber = event.getModifierState('Alt');
|
||||
let ship = this.props.ship;
|
||||
ship.internal.forEach((slot) => {
|
||||
if (clobber || !slot.m) {
|
||||
ship.use(slot, ModuleUtils.findInternal('pce', Math.min(slot.maxClass, 6), 'E')); // Passenger cabins top out at 6
|
||||
}
|
||||
});
|
||||
this.props.onChange();
|
||||
this._close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill all slots with Shield Cell Banks
|
||||
* @param {SyntheticEvent} event Event
|
||||
@@ -58,7 +143,7 @@ export default class InternalSlotSection extends SlotSection {
|
||||
let ship = this.props.ship;
|
||||
let chargeCap = 0; // Capacity of single activation
|
||||
ship.internal.forEach(function(slot) {
|
||||
if ((!slot.m || (clobber && !ModuleUtils.isShieldGenerator(slot.m.grp))) && (!slot.eligible || slot.eligible.scb)) { // Check eligibility due to Orca special case
|
||||
if ((!slot.m || (clobber && !ModuleUtils.isShieldGenerator(slot.m.grp))) && (!slot.eligible || slot.eligible.scb)) { // Check eligibility due to passenger ships special case
|
||||
ship.use(slot, ModuleUtils.findInternal('scb', slot.maxClass, 'A'));
|
||||
ship.setSlotEnabled(slot, chargeCap <= ship.shieldStrength); // Don't waste cell capacity on overcharge
|
||||
chargeCap += slot.m.recharge;
|
||||
@@ -99,7 +184,7 @@ export default class InternalSlotSection extends SlotSection {
|
||||
let slots = [];
|
||||
let { currentMenu, ship } = this.props;
|
||||
let { originSlot, targetSlot } = this.state;
|
||||
let { internal, fuelCapacity, ladenMass } = ship;
|
||||
let { internal, fuelCapacity } = ship;
|
||||
let availableModules = ship.getAvailableModules();
|
||||
|
||||
for (let i = 0, l = internal.length; i < l; i++) {
|
||||
@@ -108,11 +193,13 @@ export default class InternalSlotSection extends SlotSection {
|
||||
slots.push(<InternalSlot
|
||||
key={i}
|
||||
maxClass={s.maxClass}
|
||||
availableModules={() => availableModules.getInts(s.maxClass, s.eligible)}
|
||||
availableModules={() => availableModules.getInts(ship, s.maxClass, s.eligible)}
|
||||
onOpen={this._openMenu.bind(this,s)}
|
||||
onChange={this.props.onChange}
|
||||
onSelect={this._selectModule.bind(this, s)}
|
||||
selected={currentMenu == s}
|
||||
enabled={s.enabled}
|
||||
eligible={s.eligible}
|
||||
m={s.m}
|
||||
drag={this._drag.bind(this, s)}
|
||||
dragOver={this._dragOverSlot.bind(this, s)}
|
||||
@@ -129,15 +216,21 @@ export default class InternalSlotSection extends SlotSection {
|
||||
/**
|
||||
* Generate the section drop-down menu
|
||||
* @param {Function} translate Translate function
|
||||
* @param {Function} ship The ship
|
||||
* @return {React.Component} Section menu
|
||||
*/
|
||||
_getSectionMenu(translate) {
|
||||
_getSectionMenu(translate, ship) {
|
||||
return <div className='select' onClick={e => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
|
||||
<ul>
|
||||
<li className='lc' onClick={this._empty}>{translate('empty all')}</li>
|
||||
<li className='lc' onClick={this._fillWithCargo}>{translate('cargo')}</li>
|
||||
<li className='lc' onClick={this._fillWithCells}>{translate('scb')}</li>
|
||||
<li className='lc' onClick={this._fillWithArmor}>{translate('hr')}</li>
|
||||
<li className='lc' onClick={this._fillWithFuelTanks}>{translate('ft')}</li>
|
||||
<li className='lc' onClick={this._fillWithEconomyClassCabins}>{translate('pce')}</li>
|
||||
<li className='lc' onClick={this._fillWithBusinessClassCabins}>{translate('pci')}</li>
|
||||
<li className='lc' onClick={this._fillWithFirstClassCabins}>{translate('pcm')}</li>
|
||||
{ ship.luxuryCabins ? <li className='lc' onClick={this._fillWithLuxuryCabins}>{translate('pcq')}</li> : ''}
|
||||
<li className='optional-hide' style={{ textAlign: 'center', marginTop: '1em' }}>{translate('PHRASE_ALT_ALL')}</li>
|
||||
</ul>
|
||||
</div>;
|
||||
|
||||
@@ -11,6 +11,7 @@ import * as ModuleUtils from '../shipyard/ModuleUtils';
|
||||
import { fromDetailedBuild } from '../shipyard/Serializer';
|
||||
import { Download } from './SvgIcons';
|
||||
import { outfitURL } from '../utils/UrlGenerators';
|
||||
import * as CompanionApiUtils from '../utils/CompanionApiUtils';
|
||||
|
||||
const textBuildRegex = new RegExp('^\\[([\\w \\-]+)\\]\n');
|
||||
const lineRegex = new RegExp('^([\\dA-Z]{1,2}): (\\d)([A-I])[/]?([FGT])?([SD])? ([\\w\\- ]+)');
|
||||
@@ -112,6 +113,7 @@ export default class ModalImport extends TranslatedComponent {
|
||||
this._importBackup = this._importBackup.bind(this);
|
||||
this._importDetailedArray = this._importDetailedArray.bind(this);
|
||||
this._importTextBuild = this._importTextBuild.bind(this);
|
||||
this._importCompanionApiBuild = this._importCompanionApiBuild.bind(this);
|
||||
this._validateImport = this._validateImport.bind(this);
|
||||
}
|
||||
|
||||
@@ -183,6 +185,21 @@ export default class ModalImport extends TranslatedComponent {
|
||||
this.setState({ builds });
|
||||
}
|
||||
|
||||
/**
|
||||
* Import a build direct from the companion API
|
||||
* @param {string} build JSON from the companion API information
|
||||
* @throws {string} if parse/import fails
|
||||
*/
|
||||
_importCompanionApiBuild(build) {
|
||||
const shipModel = CompanionApiUtils.shipModelFromJson(build);
|
||||
const ship = CompanionApiUtils.shipFromJson(build);
|
||||
|
||||
let builds = {};
|
||||
builds[shipModel] = {};
|
||||
builds[shipModel]['Imported ' + Ships[shipModel].properties.name] = ship.toString();
|
||||
this.setState({ builds, singleBuild: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* Import a text build from ED Shipyard
|
||||
* @param {string} buildStr Build string
|
||||
@@ -315,7 +332,11 @@ export default class ModalImport extends TranslatedComponent {
|
||||
throw 'Must be an object or array!';
|
||||
}
|
||||
|
||||
if (importData instanceof Array) { // Must be detailed export json
|
||||
if (importData.modules != null && importData.modules.Armour != null) { // Only the companion API has this information
|
||||
this._importCompanionApiBuild(importData); // Single sihp definition
|
||||
} else if (importData.ship != null && importData.ship.modules != null && importData.ship.modules.Armour != null) { // Only the companion API has this information
|
||||
this._importCompanionApiBuild(importData.ship); // Complete API dump
|
||||
} else if (importData instanceof Array) { // Must be detailed export json
|
||||
this._importDetailedArray(importData);
|
||||
} else if (importData.ship && typeof importData.name !== undefined) { // Using JSON from a single ship build export
|
||||
this._importDetailedArray([importData]); // Convert to array with singleobject
|
||||
@@ -325,6 +346,7 @@ export default class ModalImport extends TranslatedComponent {
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// console.log(e.stack);
|
||||
this.setState({ errorMsg: (typeof e == 'string') ? e : 'Cannot Parse the data!' });
|
||||
return;
|
||||
}
|
||||
|
||||
88
src/app/components/Modification.jsx
Normal file
88
src/app/components/Modification.jsx
Normal file
@@ -0,0 +1,88 @@
|
||||
import React from 'react';
|
||||
import { findDOMNode } from 'react-dom';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import cn from 'classnames';
|
||||
import NumberEditor from 'react-number-editor';
|
||||
|
||||
/**
|
||||
* Modification
|
||||
*/
|
||||
export default class Modification extends TranslatedComponent {
|
||||
|
||||
static propTypes = {
|
||||
ship: React.PropTypes.object.isRequired,
|
||||
m: React.PropTypes.object.isRequired,
|
||||
name: React.PropTypes.string.isRequired,
|
||||
onChange: React.PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param {Object} props React Component properties
|
||||
* @param {Object} context React Component context
|
||||
*/
|
||||
constructor(props, context) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
this.state.value = this.props.m.getModValue(this.props.name) / 100 || 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update modification given a value.
|
||||
* @param {Number} value The value to set. This comes in as a string and must be stored in state as a string,
|
||||
* because it needs to allow illegal 'numbers' ('-', '1.', etc) when the user is typing
|
||||
* in a value by hand
|
||||
*/
|
||||
_updateValue(value) {
|
||||
const name = this.props.name;
|
||||
|
||||
let scaledValue = Math.round(Number(value) * 100);
|
||||
// Limit to +1000% / -100%
|
||||
if (scaledValue > 100000) {
|
||||
scaledValue = 100000;
|
||||
value = 1000;
|
||||
}
|
||||
if (scaledValue < -10000) {
|
||||
scaledValue = -10000;
|
||||
value = -100;
|
||||
}
|
||||
|
||||
let m = this.props.m;
|
||||
let ship = this.props.ship;
|
||||
ship.setModification(m, name, scaledValue);
|
||||
|
||||
this.setState({ value });
|
||||
this.props.onChange();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the modification
|
||||
* @return {React.Component} modification
|
||||
*/
|
||||
render() {
|
||||
let translate = this.context.language.translate;
|
||||
let name = this.props.name;
|
||||
|
||||
if (name === 'type') {
|
||||
// We don't show type
|
||||
return null;
|
||||
}
|
||||
|
||||
let symbol;
|
||||
if (name === 'jitter') {
|
||||
symbol = '°';
|
||||
} else if (name !== 'burst') {
|
||||
symbol = '%';
|
||||
}
|
||||
if (symbol) {
|
||||
symbol = ' (' + symbol + ')';
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={'cb'} key={name}>
|
||||
<div className={'cb'}>{translate(name)}{symbol}</div>
|
||||
<NumberEditor className={'cb'} style={{ width: '90%', textAlign: 'center' }} step={0.01} stepModifier={1} decimals={2} value={this.state.value} onValueChange={this._updateValue.bind(this)} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
65
src/app/components/ModificationsMenu.jsx
Normal file
65
src/app/components/ModificationsMenu.jsx
Normal file
@@ -0,0 +1,65 @@
|
||||
import React from 'react';
|
||||
import { findDOMNode } from 'react-dom';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
||||
import cn from 'classnames';
|
||||
import { MountFixed, MountGimballed, MountTurret } from './SvgIcons';
|
||||
import { Modifications } from 'coriolis-data/dist';
|
||||
import Modification from './Modification';
|
||||
|
||||
/**
|
||||
* Modifications menu
|
||||
*/
|
||||
export default class ModificationsMenu extends TranslatedComponent {
|
||||
|
||||
static propTypes = {
|
||||
ship: React.PropTypes.object.isRequired,
|
||||
m: React.PropTypes.object.isRequired,
|
||||
onChange: React.PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param {Object} props React Component properties
|
||||
* @param {Object} context React Component context
|
||||
*/
|
||||
constructor(props, context) {
|
||||
super(props);
|
||||
this.state = this._initState(props, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate the list of modifications
|
||||
* @param {Object} props React Component properties
|
||||
* @param {Object} context React Component context
|
||||
* @return {Object} list: Array of React Components
|
||||
*/
|
||||
_initState(props, context) {
|
||||
let { m, onChange, ship } = props;
|
||||
let list = [];
|
||||
|
||||
for (let modName of Modifications.validity[m.grp]) {
|
||||
list.push(<Modification key={ modName } ship={ ship } m={ m } name={ modName } onChange={ onChange }/>);
|
||||
}
|
||||
|
||||
return { list };
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the list
|
||||
* @return {React.Component} List
|
||||
*/
|
||||
render() {
|
||||
let { tooltip, termtip } = this.context;
|
||||
return (
|
||||
<div
|
||||
className={cn('select', this.props.className)}
|
||||
onClick={(e) => e.stopPropagation() }
|
||||
onContextMenu={stopCtxPropagation}
|
||||
onMouseOver={termtip.bind(null, 'HELP_MODIFICATIONS_MENU')} onMouseOut={tooltip.bind(null, null)}
|
||||
>
|
||||
{this.state.list}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
70
src/app/components/OffenceSummary.jsx
Normal file
70
src/app/components/OffenceSummary.jsx
Normal file
@@ -0,0 +1,70 @@
|
||||
import React from 'react';
|
||||
import cn from 'classnames';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import { DamageKinetic, DamageThermal, DamageExplosive } from './SvgIcons';
|
||||
|
||||
/**
|
||||
* Offence summary
|
||||
*/
|
||||
export default class OffenceSummary extends TranslatedComponent {
|
||||
static PropTypes = {
|
||||
ship: React.PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param {Object} props React Component properties
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render offence summary
|
||||
* @return {React.Component} contents
|
||||
*/
|
||||
render() {
|
||||
let ship = this.props.ship;
|
||||
let { language, tooltip, termtip } = this.context;
|
||||
let { formats, translate } = language;
|
||||
|
||||
return (
|
||||
<span>
|
||||
<h1>{translate('offence summary')}</h1>
|
||||
<table className='summary' style={{ marginLeft: 'auto', marginRight: 'auto', lineHeight: '1em', backgroundColor: 'transparent' }}>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colSpan='4' className='summary'><h2>{translate('dps')}: {formats.f1(ship.totalDps)}</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className='le'>{translate('damage by')}</td>
|
||||
<td className='ri' onMouseOver={termtip.bind(null, 'explosive')} onMouseOut={tooltip.bind(null, null)}><DamageExplosive /> {formats.f1(ship.totalExplDps)}</td>
|
||||
<td className='ri' onMouseOver={termtip.bind(null, 'kinetic')} onMouseOut={tooltip.bind(null, null)}><DamageKinetic /> {formats.f1(ship.totalKinDps)}</td>
|
||||
<td className='ri' onMouseOver={termtip.bind(null, 'thermal')} onMouseOut={tooltip.bind(null, null)}><DamageThermal /> {formats.f1(ship.totalThermDps)}</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colSpan='4' className='summary'><h2>{translate('sdps')}: {formats.f1(ship.totalSDps)}</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className='le'>{translate('damage by')}</td>
|
||||
<td className='ri' onMouseOver={termtip.bind(null, 'explosive')} onMouseOut={tooltip.bind(null, null)}><DamageExplosive /> {formats.f1(ship.totalExplSDps)}</td>
|
||||
<td className='ri' onMouseOver={termtip.bind(null, 'kinetic')} onMouseOut={tooltip.bind(null, null)}><DamageKinetic /> {formats.f1(ship.totalKinSDps)}</td>
|
||||
<td className='ri' onMouseOver={termtip.bind(null, 'thermal')} onMouseOut={tooltip.bind(null, null)}><DamageThermal /> {formats.f1(ship.totalThermSDps)}</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colSpan='4' className='summary'><h2>{translate('dpe')}: {formats.f1(ship.totalDpe)}</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className='le'>{translate('damage by')}</td>
|
||||
<td className='ri' onMouseOver={termtip.bind(null, 'explosive')} onMouseOut={tooltip.bind(null, null)}><DamageExplosive /> {formats.f1(ship.totalExplDpe)}</td>
|
||||
<td className='ri' onMouseOver={termtip.bind(null, 'kinetic')} onMouseOut={tooltip.bind(null, null)}><DamageKinetic /> {formats.f1(ship.totalKinDpe)}</td>
|
||||
<td className='ri' onMouseOver={termtip.bind(null, 'thermal')} onMouseOut={tooltip.bind(null, null)}><DamageThermal /> {formats.f1(ship.totalThermDpe)}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -71,7 +71,7 @@ export default class PowerManagement extends TranslatedComponent {
|
||||
case 'n': comp = comp(null, desc); break;
|
||||
case 't': comp = comp((a, b) => a.type.localeCompare(b.type), desc); break;
|
||||
case 'pri': comp = comp((a, b) => a.priority - b.priority, desc); break;
|
||||
case 'pwr': comp = comp((a, b) => a.m.power - b.m.power, desc); break;
|
||||
case 'pwr': comp = comp((a, b) => a.m.getPowerUsage() - b.m.getPowerUsage(), desc); break;
|
||||
case 'r': comp = comp((a, b) => ship.getSlotStatus(a) - ship.getSlotStatus(b), desc); break;
|
||||
case 'd': comp = comp((a, b) => ship.getSlotStatus(a, true) - ship.getSlotStatus(b, true), desc); break;
|
||||
}
|
||||
@@ -113,7 +113,7 @@ export default class PowerManagement extends TranslatedComponent {
|
||||
for (let i = 0, l = ship.powerList.length; i < l; i++) {
|
||||
let slot = ship.powerList[i];
|
||||
|
||||
if (slot.m && slot.m.power) {
|
||||
if (slot.m && slot.m.getPowerUsage() > 0) {
|
||||
let m = slot.m;
|
||||
let toggleEnabled = this._toggleEnabled.bind(this, slot);
|
||||
let retractedElem = null, deployedElem = null;
|
||||
@@ -134,8 +134,8 @@ export default class PowerManagement extends TranslatedComponent {
|
||||
{' ' + (slot.priority + 1) + ' '}
|
||||
<span className='ptr btn' onClick={this._priority.bind(this, slot, 1)}>►</span>
|
||||
</td>
|
||||
<td className='ri ptr' style={{ width: '3.25em' }} onClick={toggleEnabled}>{pwr(m.power)}</td>
|
||||
<td className='ri ptr' style={{ width: '3em' }} onClick={toggleEnabled}><u>{pct(m.power / ship.powerAvailable)}</u></td>
|
||||
<td className='ri ptr' style={{ width: '3.25em' }} onClick={toggleEnabled}>{pwr(m.getPowerUsage())}</td>
|
||||
<td className='ri ptr' style={{ width: '3em' }} onClick={toggleEnabled}><u>{pct(m.getPowerUsage() / ship.powerAvailable)}</u></td>
|
||||
{retractedElem}
|
||||
{deployedElem}
|
||||
</tr>);
|
||||
@@ -214,7 +214,7 @@ export default class PowerManagement extends TranslatedComponent {
|
||||
<td className='le shorten cap' >{translate('pp')}</td>
|
||||
<td><u >{translate('SYS')}</u></td>
|
||||
<td>1</td>
|
||||
<td className='ri'>{pwr(pp.pGen)}</td>
|
||||
<td className='ri'>{pwr(pp.getPowerGeneration())}</td>
|
||||
<td className='ri'><u>100%</u></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
@@ -223,7 +223,7 @@ export default class PowerManagement extends TranslatedComponent {
|
||||
{this._renderPowerRows(ship, translate, pwr, formats.pct1)}
|
||||
</tbody>
|
||||
</table>
|
||||
<PowerBands width={this.state.width} code={code} available={ship.standard[0].m.pGen} bands={ship.priorityBands} />
|
||||
<PowerBands width={this.state.width} code={code} available={pp.getPowerGeneration()} bands={ship.priorityBands} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -23,22 +23,13 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
||||
let translate = language.translate;
|
||||
let u = language.units;
|
||||
let formats = language.formats;
|
||||
let round = formats.round;
|
||||
let { time, int } = formats;
|
||||
let armourDetails = null;
|
||||
let sgClassNames = cn({ warning: ship.sgSlot && !ship.shieldStrength, muted: !ship.sgSlot });
|
||||
let { time, int, round, f1, f2, pct } = formats;
|
||||
let sgClassNames = cn({ warning: ship.findInternalByGroup('sg') && !ship.shield, muted: !ship.findInternalByGroup('sg') });
|
||||
let sgRecover = '-';
|
||||
let sgRecharge = '-';
|
||||
let hide = tooltip.bind(null, null);
|
||||
|
||||
if (ship.armourMultiplier > 1 || ship.armourAdded) {
|
||||
armourDetails = <u>({
|
||||
(ship.armourMultiplier > 1 ? formats.rPct(ship.armourMultiplier) : '') +
|
||||
(ship.armourAdded ? ' + ' + ship.armourAdded : '')
|
||||
})</u>;
|
||||
}
|
||||
|
||||
if (ship.shieldStrength) {
|
||||
if (ship.shield) {
|
||||
sgRecover = time(ship.calcShieldRecovery());
|
||||
sgRecharge = time(ship.calcShieldRecharge());
|
||||
}
|
||||
@@ -47,13 +38,13 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
||||
<table id='summaryTable'>
|
||||
<thead>
|
||||
<tr className='main'>
|
||||
<th rowSpan={2}>{translate('size')}</th>
|
||||
<th onMouseEnter={termtip.bind(null, 'maneuverability')} onMouseLeave={hide} rowSpan={2}>{translate('MNV')}</th>
|
||||
<th rowSpan={2} className={ cn({ 'bg-warning-disabled': !ship.canThrust() }) }>{translate('speed')}</th>
|
||||
<th rowSpan={2} className={ cn({ 'bg-warning-disabled': !ship.canBoost() }) }>{translate('boost')}</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, 'heat per second')} onMouseLeave={hide} rowSpan={2}>{translate('HPS')}</th>
|
||||
<th rowSpan={2}>{translate('armour')}</th>
|
||||
<th colSpan={3}>{translate('shields')}</th>
|
||||
<th rowSpan={2}>{translate('shields')}</th>
|
||||
<th colSpan={3}>{translate('mass')}</th>
|
||||
<th rowSpan={2}>{translate('cargo')}</th>
|
||||
<th rowSpan={2}>{translate('fuel')}</th>
|
||||
@@ -62,9 +53,6 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
||||
<th onMouseEnter={termtip.bind(null, 'mass lock factor')} onMouseLeave={hide} rowSpan={2}>{translate('MLF')}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th className='lft'>{translate('strength')}</th>
|
||||
<th onMouseEnter={termtip.bind(null, 'PHRASE_SG_RECOVER', { cap: 0 })} onMouseLeave={hide}>{translate('recovery')}</th>
|
||||
<th onMouseEnter={termtip.bind(null, 'PHRASE_SG_RECHARGE', { cap: 0 })} onMouseLeave={hide}>{translate('recharge')}</th>
|
||||
<th className='lft'>{translate('hull')}</th>
|
||||
<th onMouseEnter={termtip.bind(null, 'PHRASE_UNLADEN', { cap: 0 })} onMouseLeave={hide}>{translate('unladen')}</th>
|
||||
<th onMouseEnter={termtip.bind(null, 'PHRASE_LADEN', { cap: 0 })} onMouseLeave={hide}>{translate('laden')}</th>
|
||||
@@ -78,26 +66,24 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className='cap'>{translate(SizeMap[ship.class])}</td>
|
||||
<td>{ship.agility}/10</td>
|
||||
<td>{ ship.canThrust() ? <span>{int(ship.topSpeed)} {u['m/s']}</span> : <span className='warning'>0 <Warning/></span> }</td>
|
||||
<td>{ ship.canBoost() ? <span>{int(ship.topBoost)} {u['m/s']}</span> : <span className='warning'>0 <Warning/></span> }</td>
|
||||
<td>{round(ship.totalDps)}</td>
|
||||
<td>{int(ship.armour)} {armourDetails}</td>
|
||||
<td className={sgClassNames}>{int(ship.shieldStrength)} {u.MJ} { ship.shieldMultiplier > 1 && ship.shieldStrength > 0 ? <u>({formats.rPct(ship.shieldMultiplier)})</u> : null }</td>
|
||||
<td className={sgClassNames}>{sgRecover}</td>
|
||||
<td className={sgClassNames}>{sgRecharge}</td>
|
||||
<td>{f1(ship.totalDps)}</td>
|
||||
<td>{f1(ship.totalEps)}</td>
|
||||
<td>{f1(ship.totalHps)}</td>
|
||||
<td>{int(ship.armour)}</td>
|
||||
<td className={sgClassNames}>{int(ship.shield)} {u.MJ}</td>
|
||||
<td>{ship.hullMass} {u.T}</td>
|
||||
<td>{round(ship.unladenMass)} {u.T}</td>
|
||||
<td>{round(ship.ladenMass)} {u.T}</td>
|
||||
<td>{int(ship.unladenMass)} {u.T}</td>
|
||||
<td>{int(ship.ladenMass)} {u.T}</td>
|
||||
<td>{round(ship.cargoCapacity)} {u.T}</td>
|
||||
<td>{round(ship.fuelCapacity)} {u.T}</td>
|
||||
<td>{round(ship.unladenRange)} {u.LY}</td>
|
||||
<td>{round(ship.fullTankRange)} {u.LY}</td>
|
||||
<td>{round(ship.ladenRange)} {u.LY}</td>
|
||||
<td>{round(ship.maxJumpCount)}</td>
|
||||
<td>{round(ship.unladenFastestRange)} {u.LY}</td>
|
||||
<td>{round(ship.ladenFastestRange)} {u.LY}</td>
|
||||
<td>{f2(ship.unladenRange)} {u.LY}</td>
|
||||
<td>{f2(ship.fullTankRange)} {u.LY}</td>
|
||||
<td>{f2(ship.ladenRange)} {u.LY}</td>
|
||||
<td>{int(ship.maxJumpCount)}</td>
|
||||
<td>{f2(ship.unladenFastestRange)} {u.LY}</td>
|
||||
<td>{f2(ship.ladenFastestRange)} {u.LY}</td>
|
||||
<td>{ship.masslock}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
@@ -2,8 +2,12 @@ import React from 'react';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import cn from 'classnames';
|
||||
import AvailableModulesMenu from './AvailableModulesMenu';
|
||||
import ModificationsMenu from './ModificationsMenu';
|
||||
import { Modifications } from 'coriolis-data/dist';
|
||||
import { ListModifications } from './SvgIcons';
|
||||
import { diffDetails } from '../utils/SlotFunctions';
|
||||
import { wrapCtxMenu } from '../utils/UtilityFunctions';
|
||||
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
||||
|
||||
/**
|
||||
* Abstract Slot
|
||||
@@ -18,6 +22,7 @@ export default class Slot extends TranslatedComponent {
|
||||
selected: React.PropTypes.bool,
|
||||
m: React.PropTypes.object,
|
||||
ship: React.PropTypes.object.isRequired,
|
||||
eligible: React.PropTypes.object,
|
||||
warning: React.PropTypes.func,
|
||||
drag: React.PropTypes.func,
|
||||
drop: React.PropTypes.func,
|
||||
@@ -31,6 +36,8 @@ export default class Slot extends TranslatedComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this._modificationsSelected = false;
|
||||
|
||||
this._contextMenu = wrapCtxMenu(this._contextMenu.bind(this));
|
||||
this._getMaxClassLabel = this._getMaxClassLabel.bind(this);
|
||||
}
|
||||
@@ -73,25 +80,39 @@ export default class Slot extends TranslatedComponent {
|
||||
render() {
|
||||
let language = this.context.language;
|
||||
let translate = language.translate;
|
||||
let { ship, m, dropClass, dragOver, onOpen, selected, onSelect, warning, shipMass, availableModules } = this.props;
|
||||
let { ship, m, dropClass, dragOver, onOpen, onChange, selected, eligible, onSelect, warning, availableModules } = this.props;
|
||||
let slotDetails, menu;
|
||||
|
||||
if (!selected) {
|
||||
// If not selected then sure that modifications flag is unset
|
||||
this._modificationsSelected = false;
|
||||
}
|
||||
|
||||
if (m) {
|
||||
slotDetails = this._getSlotDetails(m, translate, language.formats, language.units); // Must be implemented by sub classes
|
||||
} else {
|
||||
slotDetails = <div className={'empty'}>{translate('empty')}</div>;
|
||||
slotDetails = <div className={'empty'}>{translate(eligible ? 'emptyrestricted' : 'empty')}</div>;
|
||||
}
|
||||
|
||||
if (this.props.selected) {
|
||||
menu = <AvailableModulesMenu
|
||||
className={this._getClassNames()}
|
||||
modules={availableModules()}
|
||||
shipMass={ship.hullMass}
|
||||
m={m}
|
||||
onSelect={onSelect}
|
||||
warning={warning}
|
||||
diffDetails={diffDetails.bind(ship, this.context.language)}
|
||||
/>;
|
||||
if (selected) {
|
||||
if (this._modificationsSelected) {
|
||||
menu = <ModificationsMenu
|
||||
className={this._getClassNames()}
|
||||
onChange={onChange}
|
||||
ship={ship}
|
||||
m={m}
|
||||
/>;
|
||||
} else {
|
||||
menu = <AvailableModulesMenu
|
||||
className={this._getClassNames()}
|
||||
modules={availableModules()}
|
||||
shipMass={ship.hullMass}
|
||||
m={m}
|
||||
onSelect={onSelect}
|
||||
warning={warning}
|
||||
diffDetails={diffDetails.bind(ship, this.context.language)}
|
||||
/>;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: implement touch dragging
|
||||
@@ -100,10 +121,17 @@ export default class Slot extends TranslatedComponent {
|
||||
<div className={cn('slot', dropClass, { selected })} onClick={onOpen} onContextMenu={this._contextMenu} onDragOver={dragOver}>
|
||||
<div className='details-container'>
|
||||
<div className='sz'>{this._getMaxClassLabel(translate)}</div>
|
||||
{slotDetails}
|
||||
</div>
|
||||
{slotDetails}
|
||||
</div>
|
||||
{menu}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle the modifications flag when selecting the modifications icon
|
||||
*/
|
||||
_toggleModifications() {
|
||||
this._modificationsSelected = !this._modificationsSelected;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import { wrapCtxMenu } from '../utils/UtilityFunctions';
|
||||
import { canMount } from '../utils/SlotFunctions';
|
||||
import { Equalizer } from '../components/SvgIcons';
|
||||
import cn from 'classnames';
|
||||
|
||||
@@ -90,7 +91,7 @@ export default class SlotSection extends TranslatedComponent {
|
||||
e.stopPropagation();
|
||||
let os = this.state.originSlot;
|
||||
if (os) {
|
||||
e.dataTransfer.dropEffect = os != targetSlot && targetSlot.maxClass >= os.m.class ? 'copyMove' : 'none';
|
||||
e.dataTransfer.dropEffect = os != targetSlot && canMount(this.props.ship, targetSlot, os.m.grp, os.m.class) ? 'copyMove' : 'none';
|
||||
this.setState({ targetSlot });
|
||||
} else {
|
||||
e.dataTransfer.dropEffect = 'none';
|
||||
@@ -116,9 +117,9 @@ export default class SlotSection extends TranslatedComponent {
|
||||
let { originSlot, targetSlot } = this.state;
|
||||
let m = originSlot.m;
|
||||
|
||||
if (targetSlot && m && targetSlot.maxClass >= m.class) {
|
||||
if (targetSlot && m && canMount(this.props.ship, targetSlot, m.grp, m.class)) {
|
||||
// Swap modules if possible
|
||||
if (targetSlot.m && originSlot.maxClass >= targetSlot.m.class) {
|
||||
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
|
||||
@@ -141,12 +142,12 @@ export default class SlotSection extends TranslatedComponent {
|
||||
return null;
|
||||
}
|
||||
if (slot === originSlot) {
|
||||
if (targetSlot && targetSlot.m && originSlot.maxClass < targetSlot.m.class) {
|
||||
if (targetSlot && targetSlot.m && !canMount(this.props.ship, originSlot, targetSlot.m.grp, targetSlot.m.class)) {
|
||||
return 'dropEmpty'; // Origin slot will be emptied
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (originSlot.m && slot.maxClass >= originSlot.m.class) { // Eligble drop slot
|
||||
if (originSlot.m && canMount(this.props.ship, slot, originSlot.m.grp, originSlot.m.class)) { // Eligble drop slot
|
||||
if (slot === targetSlot) {
|
||||
return 'drop'; // Can drop
|
||||
}
|
||||
@@ -179,7 +180,7 @@ export default class SlotSection extends TranslatedComponent {
|
||||
<div id={this.sectionId} className={'group'} onDragLeave={this._dragOverNone}>
|
||||
<div className={cn('section-menu', { selected: sectionMenuOpened })} onClick={open} onContextMenu={ctx}>
|
||||
<h1>{translate(this.sectionName)} <Equalizer/></h1>
|
||||
{sectionMenuOpened ? this._getSectionMenu(translate) : null }
|
||||
{sectionMenuOpened ? this._getSectionMenu(translate, this.props.ship) : null }
|
||||
</div>
|
||||
{this._getSlots()}
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
import React from 'react';
|
||||
import cn from 'classnames';
|
||||
import Persist from '../stores/Persist';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import { jumpRange } from '../shipyard/Calculations';
|
||||
import { diffDetails } from '../utils/SlotFunctions';
|
||||
import AvailableModulesMenu from './AvailableModulesMenu';
|
||||
import ModificationsMenu from './ModificationsMenu';
|
||||
import { ListModifications, Modified } from './SvgIcons';
|
||||
import { Modifications } from 'coriolis-data/dist';
|
||||
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
||||
|
||||
/**
|
||||
* Standard Slot
|
||||
@@ -15,53 +20,92 @@ export default class StandardSlot extends TranslatedComponent {
|
||||
modules: React.PropTypes.array.isRequired,
|
||||
onSelect: React.PropTypes.func.isRequired,
|
||||
onOpen: React.PropTypes.func.isRequired,
|
||||
onChange: React.PropTypes.func.isRequired,
|
||||
ship: React.PropTypes.object.isRequired,
|
||||
selected: React.PropTypes.bool,
|
||||
warning: React.PropTypes.func,
|
||||
};
|
||||
|
||||
/**
|
||||
* Construct the slot
|
||||
* @param {object} props Object properties
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this._modificationsSelected = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the slot
|
||||
* @return {React.Component} Slot component
|
||||
*/
|
||||
render() {
|
||||
let { termtip, tooltip } = this.context;
|
||||
let { translate, formats, units } = this.context.language;
|
||||
let { modules, slot, warning, onSelect, ladenMass, ship } = this.props;
|
||||
let { modules, slot, selected, warning, onSelect, onChange, ship } = this.props;
|
||||
let m = slot.m;
|
||||
let classRating = m.class + m.rating;
|
||||
let menu;
|
||||
let validMods = m == null ? [] : (Modifications.validity[m.grp] || []);
|
||||
let showModuleResistances = Persist.showModuleResistances();
|
||||
let mass = m.getMass() || m.cargo || m.fuel || 0;
|
||||
|
||||
if (this.props.selected) {
|
||||
menu = <AvailableModulesMenu
|
||||
className='standard'
|
||||
modules={modules}
|
||||
shipMass={ship.ladenMass}
|
||||
m={m}
|
||||
onSelect={onSelect}
|
||||
warning={warning}
|
||||
diffDetails={diffDetails.bind(ship, this.context.language)}
|
||||
/>;
|
||||
// Modifications tooltip shows blueprint and grade, if available
|
||||
let modTT = translate('modified');
|
||||
if (m && m.blueprint) {
|
||||
modTT = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
|
||||
}
|
||||
|
||||
if (!selected) {
|
||||
// If not selected then sure that modifications flag is unset
|
||||
this._modificationsSelected = false;
|
||||
}
|
||||
|
||||
if (selected) {
|
||||
if (this._modificationsSelected) {
|
||||
menu = <ModificationsMenu
|
||||
className='standard'
|
||||
onChange={onChange}
|
||||
ship={ship}
|
||||
m={m}
|
||||
/>;
|
||||
} else {
|
||||
menu = <AvailableModulesMenu
|
||||
className='standard'
|
||||
modules={modules}
|
||||
shipMass={ship.ladenMass}
|
||||
m={m}
|
||||
onSelect={onSelect}
|
||||
warning={warning}
|
||||
diffDetails={diffDetails.bind(ship, this.context.language)}
|
||||
/>;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cn('slot', { selected: this.props.selected })} onClick={this.props.onOpen}>
|
||||
<div className={cn('slot', { selected: this.props.selected })} onClick={this.props.onOpen} onContextMenu={stopCtxPropagation}>
|
||||
<div className={cn('details-container', { warning: warning && warning(slot.m) })}>
|
||||
<div className={'sz'}>{slot.maxClass}</div>
|
||||
<div>
|
||||
<div className='l'>{classRating} {translate(m.grp == 'bh' ? m.grp : m.name || m.grp)}</div>
|
||||
<div className={'r'}>{m.mass || m.fuel || 0}{units.T}</div>
|
||||
<div className={'l'}>{classRating} {translate(m.name || m.grp)}{m.mods && Object.keys(m.mods).length > 0 ? <span className='r' onMouseOver={termtip.bind(null, modTT)} onMouseOut={tooltip.bind(null, null)}><Modified /></span> : null }</div>
|
||||
<div className={'r'}>{formats.round(mass)}{units.T}</div>
|
||||
<div/>
|
||||
<div className={'cb'}>
|
||||
{ m.grp == 'bh' && m.name ? <div className='l'>{translate(m.name)}</div> : null }
|
||||
{ m.optmass ? <div className='l'>{translate('optimal mass')}: {m.optmass}{units.T}</div> : null }
|
||||
{ m.maxmass ? <div className='l'>{translate('max mass')}: {m.maxmass}{units.T}</div> : null }
|
||||
{ m.range ? <div className='l'>{translate('range')}: {m.range}{units.km}</div> : null }
|
||||
{ m.getOptimalMass() ? <div className='l'>{translate('optimal mass')}: {formats.int(m.getOptimalMass())}{units.T}</div> : null }
|
||||
{ m.getMaxMass() ? <div className='l'>{translate('max mass')}: {formats.int(m.getMaxMass())}{units.T}</div> : null }
|
||||
{ m.getRange() ? <div className='l'>{translate('range')}: {formats.f2(m.getRange())}{units.km}</div> : null }
|
||||
{ m.time ? <div className='l'>{translate('time')}: {formats.time(m.time)}</div> : null }
|
||||
{ m.eff ? <div className='l'>{translate('efficiency')}: {m.eff}</div> : null }
|
||||
{ m.pGen ? <div className='l'>{translate('power')}: {m.pGen}{units.MW}</div> : null }
|
||||
{ m.maxfuel ? <div className='l'>{translate('max')} {translate('fuel')}: {m.maxfuel}{units.T}</div> : null }
|
||||
{ m.weaponcapacity ? <div className='l'>{translate('WEP')}: {m.weaponcapacity}{units.MJ} / {m.weaponrecharge}{units.MW}</div> : null }
|
||||
{ m.systemcapacity ? <div className='l'>{translate('SYS')}: {m.systemcapacity}{units.MJ} / {m.systemrecharge}{units.MW}</div> : null }
|
||||
{ m.enginecapacity ? <div className='l'>{translate('ENG')}: {m.enginecapacity}{units.MJ} / {m.enginerecharge}{units.MW}</div> : null }
|
||||
{ m.getThermalEfficiency() ? <div className='l'>{translate('efficiency')}: {formats.f2(m.getThermalEfficiency())}</div> : null }
|
||||
{ m.getPowerGeneration() > 0 ? <div className='l'>{translate('pgen')}: {formats.f1(m.getPowerGeneration())}{units.MW}</div> : null }
|
||||
{ m.getMaxFuelPerJump() ? <div className='l'>{translate('max')} {translate('fuel')}: {formats.f1(m.getMaxFuelPerJump())}{units.T}</div> : null }
|
||||
{ m.getWeaponsCapacity() ? <div className='l'>{translate('WEP')}: {formats.f1(m.getWeaponsCapacity())}{units.MJ} / {formats.f1(m.getWeaponsRechargeRate())}{units.MW}</div> : null }
|
||||
{ m.getSystemsCapacity() ? <div className='l'>{translate('SYS')}: {formats.f1(m.getSystemsCapacity())}{units.MJ} / {formats.f1(m.getSystemsRechargeRate())}{units.MW}</div> : null }
|
||||
{ m.getEnginesCapacity() ? <div className='l'>{translate('ENG')}: {formats.f1(m.getEnginesCapacity())}{units.MJ} / {formats.f1(m.getEnginesRechargeRate())}{units.MW}</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.getThermalResistance() ? <div className='l'>{translate('thermres')}: {formats.pct(m.getThermalResistance())}</div> : null }
|
||||
|
||||
{ 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 }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -69,4 +113,11 @@ export default class StandardSlot extends TranslatedComponent {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle the modifications flag when selecting the modifications icon
|
||||
*/
|
||||
_toggleModifications() {
|
||||
this._modificationsSelected = !this._modificationsSelected;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@ import React from 'react';
|
||||
import cn from 'classnames';
|
||||
import SlotSection from './SlotSection';
|
||||
import StandardSlot from './StandardSlot';
|
||||
import Module from '../shipyard/Module';
|
||||
import { diffDetails } from '../utils/SlotFunctions';
|
||||
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
||||
import * as ShipRoles from '../shipyard/ShipRoles';
|
||||
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
||||
|
||||
@@ -18,7 +18,7 @@ export default class StandardSlotSection extends SlotSection {
|
||||
* @param {Object} context React Component context
|
||||
*/
|
||||
constructor(props, context) {
|
||||
super(props, context, 'standard', 'standard');
|
||||
super(props, context, 'standard', 'core internal');
|
||||
this._optimizeStandard = this._optimizeStandard.bind(this);
|
||||
this._selectBulkhead = this._selectBulkhead.bind(this);
|
||||
}
|
||||
@@ -86,12 +86,10 @@ export default class StandardSlotSection extends SlotSection {
|
||||
* @return {Array} Array of Slots
|
||||
*/
|
||||
_getSlots() {
|
||||
let { translate, units, formats } = this.context.language;
|
||||
let { ship, currentMenu } = this.props;
|
||||
let slots = new Array(8);
|
||||
let open = this._openMenu;
|
||||
let select = this._selectModule;
|
||||
let selBulkhead = this._selectBulkhead;
|
||||
let st = ship.standard;
|
||||
let avail = ship.getAvailableModules().standard;
|
||||
let bh = ship.bulkheads;
|
||||
@@ -103,6 +101,7 @@ export default class StandardSlotSection extends SlotSection {
|
||||
onOpen={open.bind(this, bh)}
|
||||
onSelect={this._selectBulkhead}
|
||||
selected={currentMenu == bh}
|
||||
onChange={this.props.onChange}
|
||||
ship={ship}
|
||||
/>;
|
||||
|
||||
@@ -113,8 +112,9 @@ export default class StandardSlotSection extends SlotSection {
|
||||
onOpen={open.bind(this, st[0])}
|
||||
onSelect={select.bind(this, st[0])}
|
||||
selected={currentMenu == st[0]}
|
||||
onChange={this.props.onChange}
|
||||
ship={ship}
|
||||
warning={m => m.pGen < ship.powerRetracted}
|
||||
warning={m => m instanceof Module ? m.getPowerGeneration() < ship.powerRetracted : m.pgen < ship.powerRetracted}
|
||||
/>;
|
||||
|
||||
slots[2] = <StandardSlot
|
||||
@@ -124,8 +124,9 @@ export default class StandardSlotSection extends SlotSection {
|
||||
onOpen={open.bind(this, st[1])}
|
||||
onSelect={select.bind(this, st[1])}
|
||||
selected={currentMenu == st[1]}
|
||||
onChange={this.props.onChange}
|
||||
ship={ship}
|
||||
warning={m => m.maxmass < (ship.ladenMass - st[1].mass + m.mass)}
|
||||
warning={m => m instanceof Module ? m.getMaxMass() < (ship.ladenMass - st[1].mass + m.mass) : m.maxmass < (ship.ladenMass - st[1].mass + m.mass)}
|
||||
/>;
|
||||
|
||||
|
||||
@@ -135,6 +136,7 @@ export default class StandardSlotSection extends SlotSection {
|
||||
modules={avail[2]}
|
||||
onOpen={open.bind(this, st[2])}
|
||||
onSelect={select.bind(this, st[2])}
|
||||
onChange={this.props.onChange}
|
||||
ship={ship}
|
||||
selected={currentMenu == st[2]}
|
||||
/>;
|
||||
@@ -145,6 +147,7 @@ export default class StandardSlotSection extends SlotSection {
|
||||
modules={avail[3]}
|
||||
onOpen={open.bind(this, st[3])}
|
||||
onSelect={select.bind(this, st[3])}
|
||||
onChange={this.props.onChange}
|
||||
ship={ship}
|
||||
selected={currentMenu == st[3]}
|
||||
/>;
|
||||
@@ -156,8 +159,9 @@ export default class StandardSlotSection extends SlotSection {
|
||||
onOpen={open.bind(this, st[4])}
|
||||
onSelect={select.bind(this, st[4])}
|
||||
selected={currentMenu == st[4]}
|
||||
onChange={this.props.onChange}
|
||||
ship={ship}
|
||||
warning= {m => m.enginecapacity < ship.boostEnergy}
|
||||
warning={m => m instanceof Module ? m.getEnginesCapacity() < ship.boostEnergy : m.engcap < ship.boostEnergy}
|
||||
/>;
|
||||
|
||||
slots[6] = <StandardSlot
|
||||
@@ -167,8 +171,8 @@ export default class StandardSlotSection extends SlotSection {
|
||||
onOpen={open.bind(this, st[5])}
|
||||
onSelect={select.bind(this, st[5])}
|
||||
selected={currentMenu == st[5]}
|
||||
onChange={this.props.onChange}
|
||||
ship={ship}
|
||||
warning= {m => m.enginecapacity < ship.boostEnergy}
|
||||
/>;
|
||||
|
||||
slots[7] = <StandardSlot
|
||||
@@ -178,6 +182,7 @@ export default class StandardSlotSection extends SlotSection {
|
||||
onOpen={open.bind(this, st[6])}
|
||||
onSelect={select.bind(this, st[6])}
|
||||
selected={currentMenu == st[6]}
|
||||
onChange={this.props.onChange}
|
||||
ship={ship}
|
||||
warning= {m => m.fuel < st[2].m.maxfuel} // Show warning when fuel tank is smaller than FSD Max Fuel
|
||||
/>;
|
||||
|
||||
@@ -319,6 +319,90 @@ export class Warning extends SvgIcon {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thermal damage
|
||||
*/
|
||||
export class DamageThermal 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>
|
||||
<ellipse cx='100' cy='100' rx='90' ry='90' fillOpacity='0' />
|
||||
<ellipse cx='100' cy='100' rx='30' ry='30' fillOpacity='1' />
|
||||
<path d='M100 20v80' />
|
||||
</g>;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Kinetic damage
|
||||
*/
|
||||
export class DamageKinetic 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>
|
||||
<ellipse cx='100' cy='100' rx='90' ry='90' fillOpacity='0' />
|
||||
<ellipse cx='62' cy='67' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='62' cy='101' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='62' cy='135' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='100' cy='50' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='100' cy='84' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='100' cy='118' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='100' cy='152' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='138' cy='67' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='138' cy='101' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='138' cy='135' rx='5' ry='5' fillOpacity='1' />
|
||||
</g>;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Explosive damage
|
||||
*/
|
||||
export class DamageExplosive 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>
|
||||
<ellipse cx='100' cy='100' rx='50' ry='50' fillOpacity='0' />
|
||||
<ellipse cx='100' cy='20' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='156.57' cy='36.57' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='180' cy='100' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='156.57' cy='163.43' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='100' cy='180' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='43.43' cy='163.43' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='20' cy='100' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='43.43' cy='36.57' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='100' cy='75' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='125' cy='100' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='100' cy='125' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='75' cy='100' rx='5' ry='5' fillOpacity='1' />
|
||||
</g>;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixed mount hardpoint
|
||||
*/
|
||||
@@ -334,11 +418,11 @@ export class MountFixed extends SvgIcon {
|
||||
*/
|
||||
svg() {
|
||||
return <g>
|
||||
<circle fillOpacity='0' r='70' cy='100' cx='100' strokeWidth='5' />
|
||||
<line y2='60' x2='101' y1='0' x1='101' strokeWidth='5' />
|
||||
<line y2='101' x2='200' y1='101' x1='140' strokeWidth='5' />
|
||||
<line y2='101' x2='60' y1='101' x1='0' strokeWidth='5' />
|
||||
<line y2='200' x2='101' y1='140' x1='101' strokeWidth='5' />
|
||||
<circle cx='100' cy='100' r='76' fillOpacity='0' />
|
||||
<path d='M0 100h48' />
|
||||
<path d='M152 100h48' />
|
||||
<path d='M100 0v48' />
|
||||
<path d='M100 152v48' />
|
||||
</g>;
|
||||
}
|
||||
}
|
||||
@@ -358,8 +442,8 @@ export class MountGimballed extends SvgIcon {
|
||||
*/
|
||||
svg() {
|
||||
return <g>
|
||||
<ellipse ry='25' rx='95' cy='100' cx='100' fillOpacity='0' strokeWidth='5' />
|
||||
<ellipse ry='95' rx='25' cy='100' cx='100' fillOpacity='0' strokeWidth='5' />
|
||||
<ellipse cx='100' cy='100' rx='90' ry='25' fillOpacity='0' />
|
||||
<ellipse cx='100' cy='100' rx='20' ry='95' fillOpacity='0' />
|
||||
</g>;
|
||||
}
|
||||
}
|
||||
@@ -379,9 +463,14 @@ export class MountTurret extends SvgIcon {
|
||||
*/
|
||||
svg() {
|
||||
return <g>
|
||||
<line y2='170' x2='162' y1='170' x1='8' strokeWidth='6' />
|
||||
<path d='m13,138l144,0l0,-50l-27,-40l-90,0l-27,40l0,50z' id='svg_12' fillOpacity='0' strokeWidth='6' />
|
||||
<line y2='91' x2='200' y1='91' x1='159' strokeWidth='6' />
|
||||
<path d='M40 50 A 40 40 0 0 0 0 90' />
|
||||
<path d='M40 50h40' />
|
||||
<path d='M120 90 A 40 40 0 0 0 80 50' />
|
||||
<path d='M0 90v40' />
|
||||
<path d='M120 90v40' />
|
||||
<path d='M0 120h120' />
|
||||
<path d='M120 90h80' />
|
||||
<path d='M0 160h120' />
|
||||
</g>;
|
||||
}
|
||||
}
|
||||
@@ -399,6 +488,71 @@ export class Rocket extends SvgIcon {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ListModifications (engineers)
|
||||
*/
|
||||
export class ListModifications extends SvgIcon {
|
||||
/**
|
||||
* Overriden view box
|
||||
* @return {String} view box
|
||||
*/
|
||||
viewBox() { return '0 0 200 200'; }
|
||||
|
||||
/**
|
||||
* Render the Icon
|
||||
* @return {React.Component} SVG Icon
|
||||
*/
|
||||
render() {
|
||||
return (
|
||||
<svg className={cn('modicon', this.props.className)} style={this.props.style} viewBox={this.viewBox()}>
|
||||
{this.svg()}
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the SVG
|
||||
* @return {React.Component} SVG Contents
|
||||
*/
|
||||
svg() {
|
||||
return <path d='M20 180l90-100-90 100zM176 40a40 40 0 1 1-27-28'/>;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Modified (engineers)
|
||||
*/
|
||||
export class Modified extends SvgIcon {
|
||||
/**
|
||||
* Overriden view box
|
||||
* @return {String} view box
|
||||
*/
|
||||
viewBox() { return '0 0 200 200'; }
|
||||
|
||||
/**
|
||||
* Render the Icon
|
||||
* @return {React.Component} SVG Icon
|
||||
*/
|
||||
render() {
|
||||
return (
|
||||
<svg className={cn('modicon', this.props.className)} style={this.props.style} viewBox={this.viewBox()}>
|
||||
{this.svg()}
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the SVG
|
||||
* @return {React.Component} SVG Contents
|
||||
*/
|
||||
svg() {
|
||||
return <g>
|
||||
<path d="M100,5L18,52.5L18,147.5L100,195L182,147.5L182,52.5L100,5Z"/>
|
||||
<path d="M100,70L74,85L74,115L100,130L126,115L126,85L100,70Z"/>
|
||||
</g>;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hammer
|
||||
*/
|
||||
|
||||
@@ -68,6 +68,7 @@ export default class UtilitySlotSection extends SlotSection {
|
||||
availableModules={() => availableModules.getHps(h.maxClass)}
|
||||
onOpen={this._openMenu.bind(this,h)}
|
||||
onSelect={this._selectModule.bind(this, h)}
|
||||
onChange={this.props.onChange}
|
||||
selected={currentMenu == h}
|
||||
drag={this._drag.bind(this, h)}
|
||||
dragOver={this._dragOverSlot.bind(this, h)}
|
||||
@@ -104,9 +105,17 @@ export default class UtilitySlotSection extends SlotSection {
|
||||
<li className='c' onClick={_use.bind(this, 'sb', 'B', null)}>B</li>
|
||||
<li className='c' onClick={_use.bind(this, 'sb', 'A', null)}>A</li>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('cm')}</div>
|
||||
<div className='select-group cap'>{translate('hs')}</div>
|
||||
<ul>
|
||||
<li className='lc' onClick={_use.bind(this, 'cm', null, 'Heat Sink Launcher')}>{translate('Heat Sink Launcher')}</li>
|
||||
<li className='lc' onClick={_use.bind(this, 'hs', null, 'Heat Sink Launcher')}>{translate('Heat Sink Launcher')}</li>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('ch')}</div>
|
||||
<ul>
|
||||
<li className='lc' onClick={_use.bind(this, 'ch', null, 'Chaff Launcher')}>{translate('Chaff Launcher')}</li>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('po')}</div>
|
||||
<ul>
|
||||
<li className='lc' onClick={_use.bind(this, 'po', null, 'Point Defence')}>{translate('Point Defence')}</li>
|
||||
</ul>
|
||||
</div>;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import * as ES from './es';
|
||||
import * as FR from './fr';
|
||||
import * as IT from './it';
|
||||
import * as RU from './ru';
|
||||
import * as PL from './pl';
|
||||
import d3 from 'd3';
|
||||
|
||||
let fallbackTerms = EN.terms;
|
||||
@@ -23,6 +24,7 @@ export function getLanguage(langCode) {
|
||||
case 'fr': lang = FR; break;
|
||||
case 'it': lang = IT; break;
|
||||
case 'ru': lang = RU; break;
|
||||
case 'pl': lang = PL; break;
|
||||
default:
|
||||
lang = EN;
|
||||
}
|
||||
@@ -41,12 +43,15 @@ export function getLanguage(langCode) {
|
||||
formats: {
|
||||
gen, // General number format (.e.g 1,001,001.1234)
|
||||
int: d3Locale.numberFormat(',.0f'), // Fixed to 0 decimal places (.e.g 1,001)
|
||||
f1: d3Locale.numberFormat(',.1f'), // Fixed to 1 decimal place (.e.g 1,001.1)
|
||||
f2: d3Locale.numberFormat(',.2f'), // Fixed to 2 decimal places (.e.g 1,001.10)
|
||||
s2: d3Locale.numberFormat('.2s'), // SI Format to 2 decimal places (.e.g 1.1k)
|
||||
pct: d3Locale.numberFormat('.2%'), // % to 2 decimal places (.e.g 5.40%)
|
||||
pct1: d3Locale.numberFormat('.1%'), // % to 1 decimal places (.e.g 5.4%)
|
||||
r1: d3Locale.numberFormat('.1r'), // Rounded to 1 significant number (.e.g 512 => 500, 4.122 => 4)
|
||||
r2: d3Locale.numberFormat('.2r'), // Rounded to 2 significant numbers (.e.g 512 => 510, 4.122 => 4.1)
|
||||
rPct: d3.format('%'), // % to 0 decimal places (.e.g 5%)
|
||||
round1: (d) => gen(d3.round(d, 1)), // Rounded to 0-1 decimal places (.e.g 5.1, 4)
|
||||
round: (d) => gen(d3.round(d, 2)), // Rounded to 0-2 decimal places (.e.g 5.12, 4.1)
|
||||
time: (d) => (d < 0 ? '-' : '') + Math.floor(Math.abs(d) / 60) + ':' + ('00' + Math.floor(Math.abs(d) % 60)).substr(-2, 2)
|
||||
},
|
||||
@@ -63,6 +68,7 @@ export function getLanguage(langCode) {
|
||||
MW: <u> {translate('MW')}</u>, // Mega Watts (same as Mega Joules per second)
|
||||
ps: <u>{translate('/s')}</u>, // per second
|
||||
pm: <u>{translate('/min')}</u>, // per minute
|
||||
s: <u>{translate('secs')}</u>, // Seconds
|
||||
T: <u> {translate('T')}</u>, // Metric Tons
|
||||
}
|
||||
};
|
||||
@@ -78,5 +84,6 @@ export const Languages = {
|
||||
it: 'Italiano',
|
||||
es: 'Español',
|
||||
fr: 'Français',
|
||||
ru: 'ру́сский'
|
||||
ru: 'ру́сский',
|
||||
pl: 'polski'
|
||||
};
|
||||
|
||||
@@ -29,6 +29,8 @@ export const terms = {
|
||||
PHRASE_UNLADEN: 'Ship mass excluding fuel and cargo',
|
||||
PHRASE_UPDATE_RDY: 'Update Available! Click to refresh',
|
||||
|
||||
HELP_MODIFICATIONS_MENU: 'Click on a number to enter a new value, or drag along the bar for small changes',
|
||||
|
||||
// Other languages fallback to these values
|
||||
// Only Translate to other languages if the name is different in-game
|
||||
am: 'Auto Field-Maintenance Unit',
|
||||
@@ -37,11 +39,13 @@ export const terms = {
|
||||
bsg: 'Bi-Weave Shield Generator',
|
||||
c: 'Cannon',
|
||||
cc: 'Collector Limpet Controller',
|
||||
cm: 'Countermeasure',
|
||||
ch: 'Chaff Launcher',
|
||||
cr: 'Cargo Rack',
|
||||
cs: 'Cargo Scanner',
|
||||
cs: 'Manifest Scanner',
|
||||
dc: 'Docking Computer',
|
||||
ec: 'Electronic Countermeasure',
|
||||
fc: 'Fragment Cannon',
|
||||
fh: 'Fighter Hangar',
|
||||
fi: 'FSD Interdictor',
|
||||
fs: 'Fuel Scoop',
|
||||
fsd: 'Frame Shift Drive',
|
||||
@@ -49,6 +53,7 @@ export const terms = {
|
||||
fx: 'Fuel Transfer Limpet Controller',
|
||||
hb: 'Hatch Breaker Limpet Controller',
|
||||
hr: 'Hull Reinforcement Package',
|
||||
hs: 'Heat Sink Launcher',
|
||||
kw: 'Kill Warrant Scanner',
|
||||
ls: 'Life Support',
|
||||
mc: 'Multi-cannon',
|
||||
@@ -58,8 +63,13 @@ export const terms = {
|
||||
pa: 'Plasma Accelerator',
|
||||
pas: 'Planetary Approach Suite',
|
||||
pc: 'Prospector Limpet Controller',
|
||||
pce: 'Economy Class Passenger Cabin',
|
||||
pci: 'Business Class Passenger Cabin',
|
||||
pcm: 'First Class Passenger Cabin',
|
||||
pcq: 'Luxury Passenger Cabin',
|
||||
pd: 'power distributor',
|
||||
pl: 'Pulse Laser',
|
||||
po: 'Point Defence',
|
||||
pp: 'Power Plant',
|
||||
psg: 'Prismatic Shield Generator',
|
||||
pv: 'Planetary Vehicle Hangar',
|
||||
@@ -73,5 +83,69 @@ export const terms = {
|
||||
t: 'thrusters',
|
||||
tp: 'Torpedo Pylon',
|
||||
ul: 'Burst Laser',
|
||||
ws: 'Frame Shift Wake Scanner'
|
||||
ws: 'Frame Shift Wake Scanner',
|
||||
|
||||
// Items on the outfitting page
|
||||
// Notification of restricted slot for Orca/Beluga
|
||||
emptyrestricted: 'empty (restricted)',
|
||||
// 'ammo' was overloaded for outfitting page and modul info, so changed to ammunition for outfitting page
|
||||
ammunition: 'Ammo',
|
||||
|
||||
// Unit for seconds
|
||||
secs: 's',
|
||||
|
||||
// Weapon, offence and defence
|
||||
dpe: 'Damage per MJ of energy',
|
||||
dps: 'Damage per second',
|
||||
sdps: 'Sustained damage per second',
|
||||
dpssdps: 'Damage per second (sustained damage per second)',
|
||||
eps: 'Energy per second',
|
||||
epsseps: 'Energy per second (sustained energy per second)',
|
||||
hps: 'Heat per second',
|
||||
hpsshps: 'Heat per second (sustained heat per second)',
|
||||
'damage by': 'Damage by',
|
||||
'damage from': 'Damage from',
|
||||
'shield cells': 'Shield cells',
|
||||
|
||||
// Modifications
|
||||
ammo: 'Ammunition maximum',
|
||||
boot: 'Boot time',
|
||||
brokenregen: 'Broken regeneration rate',
|
||||
burst: 'Burst',
|
||||
clip: 'Ammunition clip',
|
||||
damage: 'Damage',
|
||||
distdraw: 'Distributor draw',
|
||||
duration: 'Duration',
|
||||
eff: 'Efficiency',
|
||||
engcap: 'Engines capacity',
|
||||
engrate: 'Engines recharge rate',
|
||||
explres: 'Explosive resistance',
|
||||
facinglimit: 'Facing limit',
|
||||
hullboost: 'Hull boost',
|
||||
hullreinforcement: 'Hull reinforcement',
|
||||
integrity: 'Integrity',
|
||||
jitter: 'Jitter',
|
||||
kinres: 'Kinetic resistance',
|
||||
maxfuel: 'Maximum fuel per jump',
|
||||
mass: 'Mass',
|
||||
optmass: 'Optimal mass',
|
||||
optmul: 'Optimal multiplier',
|
||||
pgen: 'Power generation',
|
||||
piercing: 'Piercing',
|
||||
power: 'Power draw',
|
||||
range: 'Range',
|
||||
ranget: 'Range', // Range in time (for FSD interdictor)
|
||||
regen: 'Regeneration rate',
|
||||
reload: 'Reload time',
|
||||
rof: 'Rate of fire',
|
||||
shield: 'Shield',
|
||||
shieldboost: 'Shield boost',
|
||||
shieldreinforcement: 'Shield reinforcement',
|
||||
spinup: 'Spin up time',
|
||||
syscap: 'Systems capacity',
|
||||
sysrate: 'Systems recharge rate',
|
||||
thermload: 'Thermal load',
|
||||
thermres: 'Thermal resistance',
|
||||
wepcap: 'Weapons capacity',
|
||||
weprate: 'Weapons recharge rate',
|
||||
};
|
||||
|
||||
77
src/app/i18n/pl.js
Normal file
77
src/app/i18n/pl.js
Normal file
@@ -0,0 +1,77 @@
|
||||
export const formats = {
|
||||
decimal: '.',
|
||||
thousands: ',',
|
||||
grouping: [3],
|
||||
currency: ['$', ''],
|
||||
dateTime: '%a %b %e %X %Y',
|
||||
date: '%m/%d/%Y',
|
||||
time: '%H:%M:%S',
|
||||
periods: ['AM', 'PM'],
|
||||
days: ['Niedziela', 'Poniedziałek', 'Wtorek', 'Środa', 'Czwartek', 'Piątek', 'Sobota'],
|
||||
shortDays: ['Nie', 'Pon', 'Wt', 'Śr', 'Czw', 'Pt', 'Sob'],
|
||||
months: ['Styczeń', 'Luty', 'Marzec', 'Kwiecień', 'Maj', 'Czerwiec', 'Lipiec', 'Sierpień', 'Wrzesień', 'Październik', 'Listopad', 'Grudzień'],
|
||||
shortMonths: ['Sty', 'Lut', 'Mar', 'Kwi', 'Maj', 'Cze', 'Lip', 'Sie', 'Wrz', 'Paź', 'Lis', 'Gru']
|
||||
};
|
||||
|
||||
export const terms = {
|
||||
PHRASE_ALT_ALL: 'Alt + kliknięcie by wypełnić wszystkie sloty',
|
||||
PHRASE_BACKUP_DESC: 'Kopia zapasowa wszystkich danych Coriolis w celu zapisu lub przeniesienia na inne urządzenie/przeglądarkę',
|
||||
PHRASE_CONFIRMATION: 'Czy jesteś pewien?',
|
||||
PHRASE_EXPORT_DESC: 'Szczegółowy eksport schematu w formacie JSON w celu użycia na innych stronach i narzędziach',
|
||||
PHRASE_FASTEST_RANGE: 'Maksymalna ilość skoków na najwyższym zasięgu',
|
||||
PHRASE_IMPORT: 'Wklej tu JSON lub importuj',
|
||||
PHRASE_LADEN: 'Masa statku + paliwo + ładunek',
|
||||
PHRASE_NO_BUILDS: 'Nie dodano schematu do porównania!',
|
||||
PHRASE_NO_RETROCH: 'Brak zmian retrofit',
|
||||
PHRASE_SELECT_BUILDS: 'Wybierz schematy do porównania',
|
||||
PHRASE_SG_RECHARGE: 'Czas od 50% do 100% naładowania',
|
||||
PHRASE_SG_RECOVER: 'Odnowienie (do 50%) po upadku',
|
||||
PHRASE_UNLADEN: 'Masa statku z wyłączeniem paliwa i ładunku',
|
||||
PHRASE_UPDATE_RDY: 'Dostępna aktualizacja! Naciśnij by odświeżyć',
|
||||
|
||||
// Other languages fallback to these values
|
||||
// Only Translate to other languages if the name is different in-game
|
||||
am: 'Auto Field-Maintenance Unit',
|
||||
bh: 'Bulkheads',
|
||||
bl: 'Beam Laser',
|
||||
bsg: 'Bi-Weave Shield Generator',
|
||||
c: 'Cannon',
|
||||
cc: 'Collector Limpet Controller',
|
||||
cm: 'Countermeasure',
|
||||
cr: 'Cargo Rack',
|
||||
cs: 'Cargo Scanner',
|
||||
dc: 'Docking Computer',
|
||||
fc: 'Fragment Cannon',
|
||||
fi: 'FSD Interdictor',
|
||||
fs: 'Fuel Scoop',
|
||||
fsd: 'Frame Shift Drive',
|
||||
ft: 'Fuel Tank',
|
||||
fx: 'Fuel Transfer Limpet Controller',
|
||||
hb: 'Hatch Breaker Limpet Controller',
|
||||
hr: 'Hull Reinforcement Package',
|
||||
kw: 'Kill Warrant Scanner',
|
||||
ls: 'Life Support',
|
||||
mc: 'Multi-cannon',
|
||||
ml: 'Mining Laser',
|
||||
mr: 'Missile Rack',
|
||||
nl: 'Mine Launcher',
|
||||
pa: 'Plasma Accelerator',
|
||||
pas: 'Planetary Approach Suite',
|
||||
pc: 'Prospector Limpet Controller',
|
||||
pd: 'power distributor',
|
||||
pl: 'Pulse Laser',
|
||||
pp: 'Power Plant',
|
||||
psg: 'Prismatic Shield Generator',
|
||||
pv: 'Planetary Vehicle Hangar',
|
||||
rf: 'Refinery',
|
||||
rg: 'Rail Gun',
|
||||
s: 'Sensors',
|
||||
sb: 'Shield Booster',
|
||||
sc: 'Scanner',
|
||||
scb: 'Shield Cell Bank',
|
||||
sg: 'Shield Generator',
|
||||
t: 'thrusters',
|
||||
tp: 'Torpedo Pylon',
|
||||
ul: 'Burst Laser',
|
||||
ws: 'Frame Shift Wake Scanner'
|
||||
};
|
||||
@@ -29,7 +29,7 @@ export default class AboutPage extends Page {
|
||||
<p>This is a clone of the Coriolis project, whose original author is currently unable to maintain it. This clone is maintained by the <a href="http://edcd.github.io/">EDCD community</a>.</p>
|
||||
<p>To recover your builds, go to <a href='https://coriolis.io/' target='_blank'>https://coriolis.io/</a>, backup your builds (Settings / Backup), copy the text, return here and import (Settings / Import).</p>
|
||||
<p>The Coriolis project was inspired by <a href='http://www.edshipyard.com/' target='_blank'>E:D Shipyard</a> and, of course, <a href='http://www.elitedangerous.com' target='_blank'>Elite Dangerous</a>. The ultimate goal of Coriolis is to provide rich features to support in-game play and planning while engaging the E:D community to support its development.</p>
|
||||
<p>Coriolis was created using assets and imagery from Elite: Dangerous, with the permission of Frontier Developments plc, for non-commercial purposes. It is not endorsed by nor reflects the views or opinions of Frontier Developments.</p>
|
||||
<p>Coriolis was created using assets and imagery from Elite: Dangerous, with the permission of Frontier Developments plc, for non-commercial purposes. It is not endorsed by nor reflects the views or opinions of Frontier Developments. A number of assets were sourced from <a href='http://edassets.org' target='_blank'>ED Assets</a></p>
|
||||
|
||||
<a style={{ display: 'block', textDecoration: 'none' }} href='https://github.com/EDCD/coriolis' target='_blank' title='Coriolis Github Project'>
|
||||
<GitHub style={{ margin: '0.4em' }} className='l fg xl'/>
|
||||
|
||||
@@ -63,7 +63,7 @@ export default class ComparisonPage extends Page {
|
||||
* @return {Object} New state object
|
||||
*/
|
||||
_initState(context) {
|
||||
let defaultFacets = [9, 6, 4, 1, 3, 2]; // Reverse order of Armour, Shields, Speed, Jump Range, Cargo Capacity, Cost
|
||||
let defaultFacets = [13, 12, 11, 9, 6, 4, 1, 3, 2]; // Reverse order of Armour, Shields, Speed, Jump Range, Cargo Capacity, Cost, DPS, EPS, HPS
|
||||
let params = context.route.params;
|
||||
let code = params.code;
|
||||
let name = params.name ? decodeURIComponent(params.name) : null;
|
||||
@@ -345,7 +345,7 @@ export default class ComparisonPage extends Page {
|
||||
|
||||
let code = fromComparison(name, builds, selectedFacets, predicate, desc);
|
||||
let loc = window.location;
|
||||
return `${loc.protocol}//${loc.host}/comparison/${code}`;
|
||||
return loc.protocol + '//' + loc.host + '/comparison?code=' + encodeURIComponent(code);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,7 +26,7 @@ export default class ErrorDetails extends React.Component {
|
||||
if (ed) {
|
||||
content = <div style={{ textAlign:'left', fontSize:'0.8em', width: '43em', margin: '0 auto' }}>
|
||||
<div className='cen'>
|
||||
<a href='https://github.com/cmmcleod/coriolis/issues' target='_blank' title='Coriolis Github Project'>Create an issue on Github</a>
|
||||
<a href='https://github.com/edcd/coriolis/issues' target='_blank' title='Coriolis Github Project'>Create an issue on Github</a>
|
||||
{' if this keeps happening. Add these details:'}
|
||||
</div>
|
||||
<div style={{ marginTop: '2em' }}>
|
||||
@@ -42,6 +42,7 @@ export default class ErrorDetails extends React.Component {
|
||||
return <div className='error'>
|
||||
<h1>Jameson, we have a problem..</h1>
|
||||
<h1><small>{error.message}</small></h1>
|
||||
<div>Please note that this site uses Google Analytics to track performance and usage. If you are blocking cookies, for example using Ghostery, please disable blocking for this site and try again.</div>
|
||||
{content}
|
||||
</div>;
|
||||
}
|
||||
|
||||
@@ -8,17 +8,19 @@ import Persist from '../stores/Persist';
|
||||
import Ship from '../shipyard/Ship';
|
||||
import { toDetailedBuild } from '../shipyard/Serializer';
|
||||
import { outfitURL } from '../utils/UrlGenerators';
|
||||
|
||||
import { FloppyDisk, Bin, Switch, Download, Reload, Fuel } from '../components/SvgIcons';
|
||||
import { FloppyDisk, Bin, Switch, Download, Reload, Fuel, LinkIcon } from '../components/SvgIcons';
|
||||
import ShipSummaryTable from '../components/ShipSummaryTable';
|
||||
import StandardSlotSection from '../components/StandardSlotSection';
|
||||
import HardpointsSlotSection from '../components/HardpointsSlotSection';
|
||||
import InternalSlotSection from '../components/InternalSlotSection';
|
||||
import UtilitySlotSection from '../components/UtilitySlotSection';
|
||||
import OffenceSummary from '../components/OffenceSummary';
|
||||
import DefenceSummary from '../components/DefenceSummary';
|
||||
import LineChart from '../components/LineChart';
|
||||
import PowerManagement from '../components/PowerManagement';
|
||||
import CostSection from '../components/CostSection';
|
||||
import ModalExport from '../components/ModalExport';
|
||||
import ModalPermalink from '../components/ModalPermalink';
|
||||
import Slider from '../components/Slider';
|
||||
|
||||
const SPEED_SERIES = ['boost', '4 Pips', '2 Pips', '0 Pips'];
|
||||
@@ -269,6 +271,13 @@ export default class OutfittingPage extends Page {
|
||||
this.resizeListener.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the short URL
|
||||
*/
|
||||
_genShortlink() {
|
||||
this.context.showModal(<ModalPermalink url={window.location.href}/>);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the Page
|
||||
* @return {React.Component} The page contents
|
||||
@@ -284,16 +293,16 @@ export default class OutfittingPage extends Page {
|
||||
canSave = (newBuildName || buildName) && code !== savedCode,
|
||||
canRename = buildName && newBuildName && buildName != newBuildName,
|
||||
canReload = savedCode && canSave,
|
||||
hStr = ship.getHardpointsString(),
|
||||
sStr = ship.getStandardString(),
|
||||
iStr = ship.getInternalString();
|
||||
hStr = ship.getHardpointsString() + '.' + ship.getModificationsString(),
|
||||
sStr = ship.getStandardString() + '.' + ship.getModificationsString(),
|
||||
iStr = ship.getInternalString() + '.' + ship.getModificationsString();
|
||||
|
||||
return (
|
||||
<div id='outfit' className={'page'} style={{ fontSize: (sizeRatio * 0.9) + 'em' }}>
|
||||
<div id='overview'>
|
||||
<h1>{ship.name}</h1>
|
||||
<div id='build'>
|
||||
<input value={newBuildName} onChange={this._buildNameChange} placeholder={translate('Enter Name')} maxsize={50} />
|
||||
<input value={newBuildName || ''} onChange={this._buildNameChange} placeholder={translate('Enter Name')} maxLength={50} />
|
||||
<button onClick={canSave && this._saveBuild} disabled={!canSave} onMouseOver={termtip.bind(null, 'save')} onMouseOut={hide}>
|
||||
<FloppyDisk className='lg' />
|
||||
</button>
|
||||
@@ -312,6 +321,9 @@ export default class OutfittingPage extends Page {
|
||||
<button onClick={buildName && this._exportBuild} disabled={!buildName} onMouseOver={termtip.bind(null, 'export')} onMouseOut={hide}>
|
||||
<Download className='lg'/>
|
||||
</button>
|
||||
<button onClick={this._genShortlink} onMouseOver={termtip.bind(null, 'shortlink')} onMouseOut={hide}>
|
||||
<LinkIcon className='lg' />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -324,31 +336,11 @@ export default class OutfittingPage extends Page {
|
||||
<CostSection ship={ship} buildName={buildName} code={sStr + hStr + iStr} />
|
||||
|
||||
<div ref='chartThird' className='group third'>
|
||||
<h1>{translate('jump range')}</h1>
|
||||
<LineChart
|
||||
width={chartWidth}
|
||||
xMax={ship.cargoCapacity}
|
||||
yMax={ship.unladenRange}
|
||||
xUnit={translate('T')}
|
||||
yUnit={translate('LY')}
|
||||
yLabel={translate('jump range')}
|
||||
xLabel={translate('cargo')}
|
||||
func={state.jumpRangeChartFunc}
|
||||
/>
|
||||
<OffenceSummary ship={ship} code={code}/>
|
||||
</div>
|
||||
|
||||
<div className='group third'>
|
||||
<h1>{translate('total range')}</h1>
|
||||
<LineChart
|
||||
width={chartWidth}
|
||||
xMax={ship.cargoCapacity}
|
||||
yMax={ship.unladenFastestRange}
|
||||
xUnit={translate('T')}
|
||||
yUnit={translate('LY')}
|
||||
yLabel={translate('fastest range')}
|
||||
xLabel={translate('cargo')}
|
||||
func={state.fastestRangeChartFunc}
|
||||
/>
|
||||
<DefenceSummary ship={ship} code={code}/>
|
||||
</div>
|
||||
|
||||
<div className='group third'>
|
||||
@@ -397,3 +389,16 @@ export default class OutfittingPage extends Page {
|
||||
);
|
||||
}
|
||||
}
|
||||
// <div ref='chartThird' className='group third'>
|
||||
// <h1>{translate('jump range')}</h1>
|
||||
// <LineChart
|
||||
// width={chartWidth}
|
||||
// xMax={ship.cargoCapacity}
|
||||
// yMax={ship.unladenRange}
|
||||
// xUnit={translate('T')}
|
||||
// yUnit={translate('LY')}
|
||||
// yLabel={translate('jump range')}
|
||||
// xLabel={translate('cargo')}
|
||||
// func={state.jumpRangeChartFunc}
|
||||
// />
|
||||
// </div>
|
||||
|
||||
@@ -55,6 +55,7 @@ function shipSummary(shipId, shipData) {
|
||||
ship.optimizeMass({ th: ship.standard[1].maxClass + 'A', fsd: '2D', ft: '1C' }); // Optmize mass with Max Thrusters
|
||||
summary.topSpeed = ship.topSpeed;
|
||||
summary.topBoost = ship.topBoost;
|
||||
summary.baseArmour = ship.armour;
|
||||
|
||||
return summary;
|
||||
}
|
||||
@@ -141,7 +142,7 @@ export default class ShipyardPage extends Page {
|
||||
<td>{s.agility}</td>
|
||||
<td className='ri'>{fInt(s.speed)}{u['m/s']}</td>
|
||||
<td className='ri'>{fInt(s.boost)}{u['m/s']}</td>
|
||||
<td className='ri'>{s.baseArmour}</td>
|
||||
<td className='ri'>{fInt(s.baseArmour)}</td>
|
||||
<td className='ri'>{fInt(s.baseShieldStrength)}{u.MJ}</td>
|
||||
<td className='ri'>{fInt(s.topSpeed)}{u['m/s']}</td>
|
||||
<td className='ri'>{fInt(s.topBoost)}{u['m/s']}</td>
|
||||
@@ -241,7 +242,7 @@ export default class ShipyardPage extends Page {
|
||||
|
||||
return (
|
||||
<div className='page' style={{ fontSize: sizeRatio + 'em' }}>
|
||||
<p style={{ textAlign: 'center' }}>This is <strong>Coriolis EDCD Edition</strong> - a temporary clone of <a href='https://coriolis.io/' target='_blank'>https://coriolis.io/</a> with added support for E:D 2.1. For more info see Settings / <Link href="/about" className='block'>About</Link></p>
|
||||
<p style={{ textAlign: 'center' }}>This is <strong>Coriolis EDCD Edition</strong> - a temporary clone of <a href='https://coriolis.io/' target='_blank'>https://coriolis.io/</a> with added support for E:D 2.2. For more info see Settings / <Link href="/about" className='block'>About</Link></p>
|
||||
<div style={{ whiteSpace: 'nowrap', margin: '0 auto', fontSize: '0.8em', position: 'relative', display: 'inline-block', maxWidth: '100%' }}>
|
||||
<table style={{ width: '12em', position: 'absolute', zIndex: 1 }}>
|
||||
<thead>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import Module from './Module';
|
||||
|
||||
/**
|
||||
* Calculate the maximum single jump range based on mass and a specific FSD
|
||||
@@ -8,7 +9,9 @@
|
||||
* @return {number} Distance in Light Years
|
||||
*/
|
||||
export function jumpRange(mass, fsd, fuel) {
|
||||
return Math.pow(Math.min(fuel === undefined ? fsd.maxfuel : fuel, fsd.maxfuel) / fsd.fuelmul, 1 / fsd.fuelpower) * fsd.optmass / mass;
|
||||
let fsdMaxFuelPerJump = fsd instanceof Module ? fsd.getMaxFuelPerJump() : fsd.maxfuel;
|
||||
let fsdOptimalMass = fsd instanceof Module ? fsd.getOptimalMass() : fsd.optmass;
|
||||
return Math.pow(Math.min(fuel === undefined ? fsdMaxFuelPerJump : fuel, fsdMaxFuelPerJump) / fsd.fuelmul, 1 / fsd.fuelpower) * fsdOptimalMass / mass;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -20,15 +23,17 @@ export function jumpRange(mass, fsd, fuel) {
|
||||
* @return {number} Distance in Light Years
|
||||
*/
|
||||
export function fastestRange(mass, fsd, fuel) {
|
||||
let fuelRemaining = fuel % fsd.maxfuel; // Fuel left after making N max jumps
|
||||
let jumps = Math.floor(fuel / fsd.maxfuel);
|
||||
let fsdMaxFuelPerJump = fsd instanceof Module ? fsd.getMaxFuelPerJump() : fsd.maxfuel;
|
||||
let fsdOptimalMass = fsd instanceof Module ? fsd.getOptimalMass() : fsd.optmass;
|
||||
let fuelRemaining = fuel % fsdMaxFuelPerJump; // Fuel left after making N max jumps
|
||||
let jumps = Math.floor(fuel / fsdMaxFuelPerJump);
|
||||
mass += fuelRemaining;
|
||||
// Going backwards, start with the last jump using the remaining fuel
|
||||
let fastestRange = fuelRemaining > 0 ? Math.pow(fuelRemaining / fsd.fuelmul, 1 / fsd.fuelpower) * fsd.optmass / mass : 0;
|
||||
let fastestRange = fuelRemaining > 0 ? Math.pow(fuelRemaining / fsd.fuelmul, 1 / fsd.fuelpower) * fsdOptimalMass / mass : 0;
|
||||
// For each max fuel jump, calculate the max jump range based on fuel mass left in the tank
|
||||
for (let j = 0; j < jumps; j++) {
|
||||
mass += fsd.maxfuel;
|
||||
fastestRange += Math.pow(fsd.maxfuel / fsd.fuelmul, 1 / fsd.fuelpower) * fsd.optmass / mass;
|
||||
fastestRange += Math.pow(fsdMaxFuelPerJump / fsd.fuelmul, 1 / fsd.fuelpower) * fsdOptimalMass / mass;
|
||||
}
|
||||
return fastestRange;
|
||||
};
|
||||
@@ -36,29 +41,27 @@ export function fastestRange(mass, fsd, fuel) {
|
||||
/**
|
||||
* Calculate the a ships shield strength based on mass, shield generator and shield boosters used.
|
||||
*
|
||||
* @param {number} mass Current mass of the ship
|
||||
* @param {number} shields Base Shield strength MJ for ship
|
||||
* @param {object} sg The shield generator used
|
||||
* @param {number} multiplier Shield multiplier for ship (1 + shield boosters if any)
|
||||
* @return {number} Approximate shield strengh in MJ
|
||||
* @param {number} mass Current mass of the ship
|
||||
* @param {number} baseShield Base Shield strength MJ for ship
|
||||
* @param {object} sg The shield generator used
|
||||
* @param {number} multiplier Shield multiplier for ship (1 + shield boosters if any)
|
||||
* @return {number} Approximate shield strengh in MJ
|
||||
*/
|
||||
export function shieldStrength(mass, shields, sg, multiplier) {
|
||||
let opt;
|
||||
if (mass < sg.minmass) {
|
||||
return shields * multiplier * sg.minmul;
|
||||
}
|
||||
if (mass > sg.maxmass) {
|
||||
return shields * multiplier * sg.maxmul;
|
||||
}
|
||||
if (mass < sg.optmass) {
|
||||
opt = (sg.optmass - mass) / (sg.optmass - sg.minmass);
|
||||
opt = 1 - Math.pow(1 - opt, 0.87);
|
||||
return shields * multiplier * ((opt * sg.minmul) + ((1 - opt) * sg.optmul));
|
||||
} else {
|
||||
opt = (sg.optmass - mass) / (sg.maxmass - sg.optmass);
|
||||
opt = -1 + Math.pow(1 + opt, 2.425);
|
||||
return shields * multiplier * ((-1 * opt * sg.maxmul) + ((1 + opt) * sg.optmul));
|
||||
}
|
||||
export function shieldStrength(mass, baseShield, sg, multiplier) {
|
||||
// sg might be a module or a template; handle either here
|
||||
let minMass = sg instanceof Module ? sg.getMinMass() : sg.minmass;
|
||||
let optMass = sg instanceof Module ? sg.getOptMass() : sg.optmass;
|
||||
let maxMass = sg instanceof Module ? sg.getMaxMass() : sg.maxmass;
|
||||
let minMul = sg instanceof Module ? sg.getMinMul() : sg.minmul;
|
||||
let optMul = sg instanceof Module ? sg.getOptMul() : sg.optmul;
|
||||
let maxMul = sg instanceof Module ? sg.getMaxMul() : sg.maxmul;
|
||||
|
||||
let xnorm = Math.min(1, (maxMass - mass) / (maxMass - minMass));
|
||||
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);
|
||||
|
||||
return baseShield * mul * multiplier;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -72,13 +75,24 @@ export function shieldStrength(mass, shields, sg, multiplier) {
|
||||
* @return {object} Approximate speed by pips
|
||||
*/
|
||||
export function speed(mass, baseSpeed, baseBoost, thrusters, pipSpeed) {
|
||||
let multiplier = mass > thrusters.maxmass ? 0 : ((1 - thrusters.M) + (thrusters.M * Math.pow(3 - (2 * Math.max(0.5, mass / thrusters.optmass)), thrusters.P)));
|
||||
let speed = baseSpeed * multiplier;
|
||||
// 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() : thrusters.minmul;
|
||||
let optMul = thrusters instanceof Module ? thrusters.getOptMul() : thrusters.optmul;
|
||||
let maxMul = thrusters instanceof Module ? thrusters.getMaxMul() : thrusters.maxmul;
|
||||
|
||||
let xnorm = Math.min(1, (maxMass - mass) / (maxMass - minMass));
|
||||
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;
|
||||
|
||||
return {
|
||||
'0 Pips': speed * (1 - (pipSpeed * 4)),
|
||||
'2 Pips': speed * (1 - (pipSpeed * 2)),
|
||||
'4 Pips': speed,
|
||||
'boost': baseBoost * multiplier
|
||||
'boost': baseBoost * mul
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,12 +1,4 @@
|
||||
|
||||
export const ArmourMultiplier = [
|
||||
1, // Lightweight
|
||||
1.4, // Reinforced
|
||||
1.945, // Military
|
||||
1.945, // Mirrored
|
||||
1.945 // Reactive
|
||||
];
|
||||
|
||||
export const SizeMap = ['', 'small', 'medium', 'large', 'capital'];
|
||||
|
||||
export const StandardArray = [
|
||||
@@ -37,6 +29,7 @@ export const ModuleGroupToName = {
|
||||
am: 'Auto Field-Maintenance Unit',
|
||||
bsg: 'Bi-Weave Shield Generator',
|
||||
cr: 'Cargo Rack',
|
||||
fh: 'Fighter Hangar',
|
||||
fi: 'Frame Shift Drive Interdictor',
|
||||
hb: 'Hatch Breaker Limpet Controller',
|
||||
hr: 'Hull Reinforcement Package',
|
||||
@@ -48,21 +41,29 @@ export const ModuleGroupToName = {
|
||||
dc: 'Docking Computer',
|
||||
fx: 'Fuel Transfer Limpet Controller',
|
||||
pc: 'Prospector Limpet Controller',
|
||||
pce: 'Economy Class Passenger Cabin',
|
||||
pci: 'Business Class Passenger Cabin',
|
||||
pcm: 'First Class Passenger Cabin',
|
||||
pcq: 'Luxury Passenger Cabin',
|
||||
cc: 'Collector Limpet Controller',
|
||||
|
||||
// Hard Points
|
||||
bl: 'Beam Laser',
|
||||
ul: 'Burst Laser',
|
||||
c: 'Cannon',
|
||||
ch: 'Chaff Launcher',
|
||||
cs: 'Cargo Scanner',
|
||||
cm: 'Countermeasure',
|
||||
ec: 'Electronic Countermeasure',
|
||||
fc: 'Fragment Cannon',
|
||||
hs: 'Heat Sink Launcher',
|
||||
ws: 'Frame Shift Wake Scanner',
|
||||
kw: 'Kill Warrant Scanner',
|
||||
nl: 'Mine Launcher',
|
||||
ml: 'Mining Laser',
|
||||
mr: 'Missile Rack',
|
||||
pa: 'Plasma Accelerator',
|
||||
po: 'Point Defence',
|
||||
mc: 'Multi-cannon',
|
||||
pl: 'Pulse Laser',
|
||||
rg: 'Rail Gun',
|
||||
@@ -124,7 +125,7 @@ export const ShipFacets = [
|
||||
},
|
||||
{ // 3
|
||||
title: 'shields',
|
||||
props: ['shieldStrength'],
|
||||
props: ['shield'],
|
||||
unit: 'MJ',
|
||||
fmt: 'int',
|
||||
i: 3
|
||||
@@ -188,6 +189,20 @@ export const ShipFacets = [
|
||||
lbls: ['DPS'],
|
||||
fmt: 'round',
|
||||
i: 11
|
||||
},
|
||||
{ // 12
|
||||
title: 'EPS',
|
||||
props: ['totalEps'],
|
||||
lbls: ['EPS'],
|
||||
fmt: 'round',
|
||||
i: 12
|
||||
},
|
||||
{ // 13
|
||||
title: 'HPS',
|
||||
props: ['totalHps'],
|
||||
lbls: ['HPS'],
|
||||
fmt: 'round',
|
||||
i: 13
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
15
src/app/shipyard/Modification.js
Executable file
15
src/app/shipyard/Modification.js
Executable file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Modification - a modification and its value
|
||||
*/
|
||||
export default class Modification {
|
||||
|
||||
/**
|
||||
* @param {String} id Unique modification ID
|
||||
* @param {Number} value Value of the modification
|
||||
*/
|
||||
constructor(id, value) {
|
||||
this.id = id;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
}
|
||||
603
src/app/shipyard/Module.js
Executable file
603
src/app/shipyard/Module.js
Executable file
@@ -0,0 +1,603 @@
|
||||
import * as ModuleUtils from './ModuleUtils';
|
||||
import * as _ from 'lodash';
|
||||
import { Modifications } from 'coriolis-data/dist';
|
||||
|
||||
/**
|
||||
* Module - active module in a ship's buildout
|
||||
*/
|
||||
export default class Module {
|
||||
|
||||
/**
|
||||
* Construct a new module
|
||||
* @param {Object} params Module parameters. Either grp/id or template
|
||||
*/
|
||||
constructor(params) {
|
||||
let properties = Object.assign({ grp: null, id: null, template: null, }, params);
|
||||
|
||||
let template;
|
||||
if (properties.template == undefined) {
|
||||
return ModuleUtils.findModule(properties.grp, properties.id);
|
||||
} else {
|
||||
template = properties.template;
|
||||
if (template) {
|
||||
// Copy all properties from coriolis-data template
|
||||
for (let p in template) { this[p] = template[p]; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a value for a given modification
|
||||
* @param {Number} name The name of the modification
|
||||
* @return {object} The value of the modification. If it is a numeric value then it is returned as an integer value scaled so that 1.23% == 123
|
||||
*/
|
||||
getModValue(name) {
|
||||
return this.mods && this.mods[name] ? this.mods[name] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a value for a given modification ID
|
||||
* @param {Number} name The name of the modification
|
||||
* @param {object} value The value of the modification. If it is a numeric value then it should be an integer scaled so that -2.34% == -234
|
||||
*/
|
||||
setModValue(name, value) {
|
||||
if (!this.mods) {
|
||||
this.mods = {};
|
||||
}
|
||||
|
||||
if (value == null || value == 0) {
|
||||
delete this.mods[name];
|
||||
} else {
|
||||
if (isNaN(value)) {
|
||||
this.mods[name] = value;
|
||||
} else {
|
||||
// Round just to be sure
|
||||
this.mods[name] = Math.round(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to obtain a modified value using standard multipliers
|
||||
* @param {String} name the name of the modifier to obtain
|
||||
* @param {Boolean} additive Optional true if the value is additive rather than multiplicative
|
||||
* @return {Number} the mass of this module
|
||||
*/
|
||||
_getModifiedValue(name, additive) {
|
||||
let result = this[name] || (additive ? 0 : null); // Additive NULL === 0
|
||||
if (result != null) {
|
||||
const modification = Modifications.modifications[name];
|
||||
if (!modification) {
|
||||
return result;
|
||||
}
|
||||
// We store percentages as decimals, so to get them back we need to divide by 10000. Otherwise
|
||||
// we divide by 100. Both ways we end up with a value with two decimal places
|
||||
let modValue;
|
||||
if (modification.type === 'percentage') {
|
||||
modValue = this.getModValue(name) / 10000;
|
||||
} else if (modification.type === 'numeric') {
|
||||
modValue = this.getModValue(name) / 100;
|
||||
} else {
|
||||
modValue = this.getModValue(name);
|
||||
}
|
||||
if (modValue) {
|
||||
if (additive) {
|
||||
result = result + modValue;
|
||||
} else {
|
||||
result = result * (1 + modValue);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (name === 'burst') {
|
||||
// Burst is special, as if it can not exist but have a modification
|
||||
const modValue = this.getModValue(name) / 100;
|
||||
return modValue;
|
||||
} else if (name === 'burstrof') {
|
||||
// Burst rate of fire is special, as if it can not exist but have a modification
|
||||
const modValue = this.getModValue(name) / 100;
|
||||
return modValue;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this is a shield generator
|
||||
* @return {Boolean} if this is a shield generator
|
||||
*/
|
||||
isShieldGenerator() {
|
||||
return (this.grp === 'sg' || this.grp === 'psg' || this.grp === 'bsg');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the power generation of this module, taking in to account modifications
|
||||
* @return {Number} the power generation of this module
|
||||
*/
|
||||
getPowerGeneration() {
|
||||
return this._getModifiedValue('pgen');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the power usage of this module, taking in to account modifications
|
||||
* @return {Number} the power usage of this module
|
||||
*/
|
||||
getPowerUsage() {
|
||||
return this._getModifiedValue('power');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the integrity of this module, taking in to account modifications
|
||||
* @return {Number} the integrity of this module
|
||||
*/
|
||||
getIntegrity() {
|
||||
return this._getModifiedValue('integrity');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mass of this module, taking in to account modifications
|
||||
* @return {Number} the mass of this module
|
||||
*/
|
||||
getMass() {
|
||||
return this._getModifiedValue('mass');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the thermal efficiency of this module, taking in to account modifications
|
||||
* @return {Number} the thermal efficiency of this module
|
||||
*/
|
||||
getThermalEfficiency() {
|
||||
return this._getModifiedValue('eff');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the maximum mass of this module, taking in to account modifications
|
||||
* @return {Number} the maximum mass of this module
|
||||
*/
|
||||
getMaxMass() {
|
||||
return this._getModifiedValue('maxmass');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optimal mass of this module, taking in to account modifications
|
||||
* @return {Number} the optimal mass of this module
|
||||
*/
|
||||
getOptimalMass() {
|
||||
return this._getModifiedValue('optmass');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optimal multiplier of this module, taking in to account modifications
|
||||
* @return {Number} the optimal multiplier of this module
|
||||
*/
|
||||
getOptimalMultiplier() {
|
||||
return this._getModifiedValue('optmult');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the maximum fuel per jump for this module, taking in to account modifications
|
||||
* @return {Number} the maximum fuel per jump of this module
|
||||
*/
|
||||
getMaxFuelPerJump() {
|
||||
return this._getModifiedValue('maxfuel');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the systems capacity for this module, taking in to account modifications
|
||||
* @return {Number} the systems capacity of this module
|
||||
*/
|
||||
getSystemsCapacity() {
|
||||
return this._getModifiedValue('syscap');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the engines capacity for this module, taking in to account modifications
|
||||
* @return {Number} the engines capacity of this module
|
||||
*/
|
||||
getEnginesCapacity() {
|
||||
return this._getModifiedValue('engcap');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the weapons capacity for this module, taking in to account modifications
|
||||
* @return {Number} the weapons capacity of this module
|
||||
*/
|
||||
getWeaponsCapacity() {
|
||||
return this._getModifiedValue('wepcap');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the systems recharge rate for this module, taking in to account modifications
|
||||
* @return {Number} the systems recharge rate of this module
|
||||
*/
|
||||
getSystemsRechargeRate() {
|
||||
return this._getModifiedValue('sysrate');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the engines recharge rate for this module, taking in to account modifications
|
||||
* @return {Number} the engines recharge rate of this module
|
||||
*/
|
||||
getEnginesRechargeRate() {
|
||||
return this._getModifiedValue('engrate');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the weapons recharge rate for this module, taking in to account modifications
|
||||
* @return {Number} the weapons recharge rate of this module
|
||||
*/
|
||||
getWeaponsRechargeRate() {
|
||||
return this._getModifiedValue('weprate');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the kinetic resistance for this module, taking in to account modifications
|
||||
* @return {Number} the kinetic resistance of this module
|
||||
*/
|
||||
getKineticResistance() {
|
||||
return this._getModifiedValue('kinres', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the thermal resistance for this module, taking in to account modifications
|
||||
* @return {Number} the thermal resistance of this module
|
||||
*/
|
||||
getThermalResistance() {
|
||||
return this._getModifiedValue('thermres', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the explosive resistance for this module, taking in to account modifications
|
||||
* @return {Number} the explosive resistance of this module
|
||||
*/
|
||||
getExplosiveResistance() {
|
||||
return this._getModifiedValue('explres', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the regeneration rate for this module, taking in to account modifications
|
||||
* @return {Number} the regeneration rate of this module
|
||||
*/
|
||||
getRegenerationRate() {
|
||||
return this._getModifiedValue('regen');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the broken regeneration rate for this module, taking in to account modifications
|
||||
* @return {Number} the broken regeneration rate of this module
|
||||
*/
|
||||
getBrokenRegenerationRate() {
|
||||
return this._getModifiedValue('brokenregen');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the range for this module, taking in to account modifications
|
||||
* @return {Number} the range rate of this module
|
||||
*/
|
||||
getRange() {
|
||||
return this._getModifiedValue('range');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the range (in terms of seconds, for FSDI) for this module, taking in to account modifications
|
||||
* @return {Number} the range of this module
|
||||
*/
|
||||
getRangeT() {
|
||||
return this._getModifiedValue('ranget');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the capture arc for this module, taking in to account modifications
|
||||
* @return {Number} the capture arc of this module
|
||||
*/
|
||||
getCaptureArc() {
|
||||
return this._getModifiedValue('arc');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the hull reinforcement for this module, taking in to account modifications
|
||||
* @return {Number} the hull reinforcement of this module
|
||||
*/
|
||||
getHullReinforcement() {
|
||||
return this._getModifiedValue('hullreinforcement');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the delay for this module, taking in to account modifications
|
||||
* @return {Number} the delay of this module
|
||||
*/
|
||||
getDelay() {
|
||||
return this._getModifiedValue('delay');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the duration for this module, taking in to account modifications
|
||||
* @return {Number} the duration of this module
|
||||
*/
|
||||
getDuration() {
|
||||
return this._getModifiedValue('duration');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the shield boost for this module, taking in to account modifications
|
||||
* @return {Number} the shield boost of this module
|
||||
*/
|
||||
getShieldBoost() {
|
||||
return this._getModifiedValue('shieldboost');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the minimum mass for this module, taking in to account modifications
|
||||
* @return {Number} the minimum mass of this module
|
||||
*/
|
||||
getMinMass() {
|
||||
// Modifier is optmass
|
||||
let result = 0;
|
||||
if (this['minmass']) {
|
||||
result = this['minmass'];
|
||||
if (result) {
|
||||
let mult = this.getModValue('optmass') / 10000;
|
||||
if (mult) { result = result * (1 + mult); }
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optimum mass for this module, taking in to account modifications
|
||||
* @return {Number} the optimum mass of this module
|
||||
*/
|
||||
getOptMass() {
|
||||
return this._getModifiedValue('optmass');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the maximum mass for this module, taking in to account modifications
|
||||
* @return {Number} the maximum mass of this module
|
||||
*/
|
||||
getMaxMass() {
|
||||
// Modifier is optmass
|
||||
let result = 0;
|
||||
if (this['maxmass']) {
|
||||
result = this['maxmass'];
|
||||
if (result) {
|
||||
let mult = this.getModValue('optmass') / 10000;
|
||||
if (mult) { result = result * (1 + mult); }
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the minimum multiplier for this module, taking in to account modifications
|
||||
* @return {Number} the minimum multiplier of this module
|
||||
*/
|
||||
getMinMul() {
|
||||
// Modifier is optmul
|
||||
let result = 0;
|
||||
if (this['minmul']) {
|
||||
result = this['minmul'];
|
||||
if (result) {
|
||||
let mult = this.getModValue('optmul') / 10000;
|
||||
if (mult) { result = result * (1 + mult); }
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optimum multiplier for this module, taking in to account modifications
|
||||
* @return {Number} the optimum multiplier of this module
|
||||
*/
|
||||
getOptMul() {
|
||||
return this._getModifiedValue('optmul');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the maximum multiplier for this module, taking in to account modifications
|
||||
* @return {Number} the maximum multiplier of this module
|
||||
*/
|
||||
getMaxMul() {
|
||||
// Modifier is optmul
|
||||
let result = 0;
|
||||
if (this['maxmul']) {
|
||||
result = this['maxmul'];
|
||||
if (result) {
|
||||
let mult = this.getModValue('optmul') / 10000;
|
||||
if (mult) { result = result * (1 + mult); }
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the damage for this module, taking in to account modifications
|
||||
* @return {Number} the damage of this module
|
||||
*/
|
||||
getDamage() {
|
||||
return this._getModifiedValue('damage');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the distributor draw for this module, taking in to account modifications
|
||||
* @return {Number} the distributor draw of this module
|
||||
*/
|
||||
getDistDraw() {
|
||||
return this._getModifiedValue('distdraw');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the thermal load for this module, taking in to account modifications
|
||||
* @return {Number} the thermal load of this module
|
||||
*/
|
||||
getThermalLoad() {
|
||||
return this._getModifiedValue('thermload');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the rounds per shot for this module, taking in to account modifications
|
||||
* @return {Number} the rounds per shot of this module
|
||||
*/
|
||||
getRoundsPerShot() {
|
||||
return this._getModifiedValue('roundspershot');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the DPS for this module, taking in to account modifications
|
||||
* @return {Number} the DPS of this module
|
||||
*/
|
||||
getDps() {
|
||||
// DPS is a synthetic value
|
||||
let damage = this.getDamage();
|
||||
let rpshot = this.roundspershot || 1;
|
||||
let rof = this.getRoF() || 1;
|
||||
|
||||
return damage * rpshot * rof;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the EPS for this module, taking in to account modifications
|
||||
* @return {Number} the EPS of this module
|
||||
*/
|
||||
getEps() {
|
||||
// EPS is a synthetic value
|
||||
let distdraw = this.getDistDraw();
|
||||
let rpshot = this.roundspershot || 1;
|
||||
let rof = this.getRoF() || 1;
|
||||
|
||||
return distdraw * rpshot * rof;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the HPS for this module, taking in to account modifications
|
||||
* @return {Number} the HPS of this module
|
||||
*/
|
||||
getHps() {
|
||||
// HPS is a synthetic value
|
||||
let heat = this.getThermalLoad();
|
||||
let rpshot = this.roundspershot || 1;
|
||||
let rof = this.getRoF() || 1;
|
||||
|
||||
return heat * rpshot * rof;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the clip size for this module, taking in to account modifications
|
||||
* @return {Number} the clip size of this module
|
||||
*/
|
||||
getClip() {
|
||||
return this._getModifiedValue('clip');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ammo size for this module, taking in to account modifications
|
||||
* @return {Number} the ammo size of this module
|
||||
*/
|
||||
getAmmo() {
|
||||
return this._getModifiedValue('ammo');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reload time for this module, taking in to account modifications
|
||||
* @return {Number} the reload time of this module
|
||||
*/
|
||||
getReload() {
|
||||
return this._getModifiedValue('reload');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the burst size for this module, taking in to account modifications
|
||||
* @return {Number} the burst size of this module
|
||||
*/
|
||||
getBurst() {
|
||||
return this._getModifiedValue('burst');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the burst rate of fire for this module, taking in to account modifications
|
||||
* @return {Number} the burst rate of fire of this module
|
||||
*/
|
||||
getBurstRoF() {
|
||||
return this._getModifiedValue('burstrof');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the rate of fire for this module, taking in to account modifications.
|
||||
* The rate of fire is a combination value, and needs to take in to account
|
||||
* bursts of fire.
|
||||
* Firing goes [burst 1] [burst interval] [burst 2] [burst interval] ... [burst n] [interval]
|
||||
* where 'n' is 'burst', 'burst interval' is '1/burstrof' and 'interval' is '1/rof'
|
||||
* @return {Number} the rate of fire for this module
|
||||
*/
|
||||
getRoF() {
|
||||
const burst = this.getBurst() || 1;
|
||||
const burstRoF = this.getBurstRoF() || 1;
|
||||
const intRoF = this._getModifiedValue('rof');
|
||||
|
||||
return burst / (((burst - 1) / burstRoF) + 1 / intRoF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the facing limit for this module, taking in to account modifications
|
||||
* @return {Number} the facing limit for this module
|
||||
*/
|
||||
getFacingLimit() {
|
||||
return this._getModifiedValue('facinglimit');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the hull boost for this module, taking in to account modifications
|
||||
* @return {Number} the hull boost for this module
|
||||
*/
|
||||
getHullBoost() {
|
||||
return this._getModifiedValue('hullboost');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the shield reinforcement for this module, taking in to account modifications
|
||||
* @return {Number} the shield reinforcement for this module
|
||||
*/
|
||||
getShieldReinforcement() {
|
||||
return this._getModifiedValue('shieldreinforcement');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the bays for this module, taking in to account modifications
|
||||
* @return {Number} the bays for this module
|
||||
*/
|
||||
getBays() {
|
||||
return this._getModifiedValue('bays');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the rebuilds per bay for this module, taking in to account modifications
|
||||
* @return {Number} the rebuilds per bay for this module
|
||||
*/
|
||||
getRebuildsPerBay() {
|
||||
return this._getModifiedValue('rebuildsperbay');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cells for this module, taking in to account modifications
|
||||
* @return {Number} the cells for this module
|
||||
*/
|
||||
getCells() {
|
||||
return this._getModifiedValue('cells');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the jitter for this module, taking in to account modifications
|
||||
* @return {Number} the jitter for this module
|
||||
*/
|
||||
getJitter() {
|
||||
return this._getModifiedValue('jitter', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the damage type for this module, taking in to account modifications
|
||||
* @return {string} the damage types for this module; any combination of E T and K
|
||||
*/
|
||||
getDamageType() {
|
||||
return this.getModValue('type') || this.type;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
|
||||
import Module from './Module';
|
||||
import { BulkheadNames } from './Constants';
|
||||
|
||||
/**
|
||||
@@ -37,7 +37,7 @@ export default class ModuleSet {
|
||||
this.intClass = {};
|
||||
|
||||
this.bulkheads = shipData.bulkheads.map((b, i) => {
|
||||
return Object.assign({ grp: 'bh', name: BulkheadNames[i], index: i, class: '', rating: '' }, b);
|
||||
return Object.assign(new Module(), { grp: 'bh', id: i, name: BulkheadNames[i], index: i, class: '', rating: '' }, b);
|
||||
});
|
||||
|
||||
this.standard[0] = filter(stnd.pp, maxStandardArr[0], 0, mass); // Power Plant
|
||||
@@ -66,21 +66,28 @@ export default class ModuleSet {
|
||||
* @return {Object} Bulkhead module details
|
||||
*/
|
||||
getBulkhead(index) {
|
||||
return this.bulkheads[index] || null;
|
||||
return this.bulkheads[index] ? new Module({ template: this.bulkheads[index] }) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the modules that areeligible for an internal slot
|
||||
* @param {Object} ship The ship
|
||||
* @param {integer} c The max class module that can be mounted in the slot
|
||||
* @param {Object} eligible) The map of eligible internal groups
|
||||
* @return {object} A map of all eligible modules by group
|
||||
*/
|
||||
getInts(c, eligible) {
|
||||
getInts(ship, c, eligible) {
|
||||
let o = {};
|
||||
for (let key in this.internal) {
|
||||
if (eligible && !eligible[key]) {
|
||||
continue;
|
||||
}
|
||||
if (key == 'pcq' && !(ship.luxuryCabins && ship.luxuryCabins === true)) {
|
||||
continue;
|
||||
}
|
||||
if (key == 'fh' && !(ship.fighterHangars && ship.fighterHangars === true)) {
|
||||
continue;
|
||||
}
|
||||
let data = filter(this.internal[key], c, 0, this.mass);
|
||||
if (data.length) { // If group is not empty
|
||||
o[key] = data;
|
||||
@@ -119,11 +126,11 @@ export default class ModuleSet {
|
||||
let pd = this.standard[4][0];
|
||||
|
||||
for (let p of this.standard[4]) {
|
||||
if (p.mass < pd.mass && p.enginecapacity >= boostEnergy) {
|
||||
if (p.mass < pd.mass && p.engcap >= boostEnergy) {
|
||||
pd = p;
|
||||
}
|
||||
}
|
||||
return pd;
|
||||
return new Module({ template: pd });
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -139,7 +146,7 @@ export default class ModuleSet {
|
||||
th = t;
|
||||
}
|
||||
}
|
||||
return th;
|
||||
return new Module({ template: th });
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -155,7 +162,7 @@ export default class ModuleSet {
|
||||
sg = s;
|
||||
}
|
||||
}
|
||||
return sg;
|
||||
return new Module({ template: sg });
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -168,10 +175,10 @@ export default class ModuleSet {
|
||||
|
||||
for (let p of this.standard[0]) {
|
||||
// Provides enough power, is lighter or the same mass as current power plant but better output/efficiency
|
||||
if (p.pGen >= powerNeeded && (p.mass < pp.mass || (p.mass == pp.mass && p.pGen > pp.pGen))) {
|
||||
if (p.pgen >= powerNeeded && (p.mass < pp.mass || (p.mass == pp.mass && p.pgen > pp.pgen))) {
|
||||
pp = p;
|
||||
}
|
||||
}
|
||||
return pp;
|
||||
return new Module({ template: pp });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import { ModuleNameToGroup, BulkheadNames, StandardArray } from './Constants';
|
||||
import ModuleSet from './ModuleSet';
|
||||
import Module from './Module';
|
||||
import { Ships, Modules } from 'coriolis-data/dist';
|
||||
|
||||
/*
|
||||
* All functions below must return a fresh Module rather than a definition or existing module, as
|
||||
* the resultant object can be altered with modifications.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
@@ -9,9 +14,45 @@ import { Ships, Modules } from 'coriolis-data/dist';
|
||||
* @return {Object} Cargo hatch model
|
||||
*/
|
||||
export function cargoHatch() {
|
||||
return { name: 'Cargo Hatch', class: 1, rating: 'H', power: 0.6 };
|
||||
let hatch = new Module();
|
||||
Object.assign(hatch, { name: 'Cargo Hatch', class: 1, rating: 'H', power: 0.6 });
|
||||
return hatch;
|
||||
};
|
||||
|
||||
/**
|
||||
* Finds the module with the specific group and ID
|
||||
* @param {String} grp Module group (pp - power plant, pl - pulse laser etc)
|
||||
* @param {String} id The module ID
|
||||
* @return {Object} The module or null
|
||||
*/
|
||||
export function findModule(grp, id) {
|
||||
// See if it's a standard module
|
||||
if (Modules.standard[grp]) {
|
||||
let standardmod = Modules.standard[grp].find(e => e.id == id);
|
||||
if (standardmod != null) {
|
||||
return new Module({ template: standardmod });
|
||||
}
|
||||
}
|
||||
|
||||
// See if it's an internal module
|
||||
if (Modules.internal[grp]) {
|
||||
let internalmod = Modules.internal[grp].find(e => e.id == id);
|
||||
if (internalmod != null) {
|
||||
return new Module({ template: internalmod });
|
||||
}
|
||||
}
|
||||
|
||||
// See if it's a hardpoint module
|
||||
if (Modules.hardpoints[grp]) {
|
||||
let hardpointmod = Modules.hardpoints[grp].find(e => e.id == id);
|
||||
if (hardpointmod != null) {
|
||||
return new Module({ template: hardpointmod });
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the standard module type with the specified ID
|
||||
* @param {String|Number} type Standard Module Type (0/pp - Power Plant, 1/t - Thrusters, etc)
|
||||
@@ -24,6 +65,9 @@ export function standard(type, id) {
|
||||
}
|
||||
|
||||
let s = Modules.standard[type].find(e => e.id == id || (e.class == id.charAt(0) && e.rating == id.charAt(1)));
|
||||
if (s) {
|
||||
s = new Module({ template: s });
|
||||
}
|
||||
return s || null;
|
||||
};
|
||||
|
||||
@@ -37,7 +81,7 @@ export function hardpoints(id) {
|
||||
let group = Modules.hardpoints[n];
|
||||
for (let i = 0; i < group.length; i++) {
|
||||
if (group[i].id == id) {
|
||||
return group[i];
|
||||
return new Module({ template: group[i] });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,13 +98,66 @@ export function internal(id) {
|
||||
let group = Modules.internal[n];
|
||||
for (let i = 0; i < group.length; i++) {
|
||||
if (group[i].id == id) {
|
||||
return group[i];
|
||||
return new Module({ template: group[i] });
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Finds a standard module based on Class, Rating, Group and/or name.
|
||||
* At least one of Group name or unique module name must be provided
|
||||
*
|
||||
* @param {String} groupName [Optional] Full name or abbreviated name for module group
|
||||
* @param {integer} clss module Class
|
||||
* @param {String} rating module Rating
|
||||
* @param {String} name [Optional] Long/unique name for module -e.g. 'Advanced Discover Scanner'
|
||||
* @return {Object} The module if found, null if not found
|
||||
*/
|
||||
export function findStandard(groupName, clss, rating, name) {
|
||||
let groups = {};
|
||||
|
||||
if (groupName) {
|
||||
if (Modules.standard[groupName]) {
|
||||
groups[groupName] = Modules.standard[groupName];
|
||||
} else {
|
||||
let grpCode = ModuleNameToGroup[groupName.toLowerCase()];
|
||||
if (grpCode && Modules.standard[grpCode]) {
|
||||
groups[grpCode] = Modules.standard[grpCode];
|
||||
}
|
||||
}
|
||||
} else if (name) {
|
||||
groups = Modules.standard;
|
||||
}
|
||||
|
||||
for (let g in groups) {
|
||||
let group = groups[g];
|
||||
for (let i = 0, l = group.length; i < l; i++) {
|
||||
if (group[i].class == clss && group[i].rating == rating && ((!name && !group[i].name) || group[i].name == name)) {
|
||||
return group[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a standard Module ID based on Class, Rating, Group and/or name.
|
||||
* At least one of Group name or unique module name must be provided
|
||||
*
|
||||
* @param {String} groupName [Optional] Full name or abbreviated name for module group
|
||||
* @param {integer} clss module Class
|
||||
* @param {String} rating Module Rating
|
||||
* @param {String} name [Optional] Long/unique name for module -e.g. 'Advanced Discover Scanner'
|
||||
* @return {String} The id of the module if found, null if not found
|
||||
*/
|
||||
export function findStandardId(groupName, clss, rating, name) {
|
||||
let i = this.findStandard(groupName, clss, rating, name);
|
||||
return i ? i.id : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds an internal module based on Class, Rating, Group and/or name.
|
||||
* At least one ofGroup name or unique module name must be provided
|
||||
@@ -168,6 +265,16 @@ export function findHardpoint(groupName, clss, rating, name, mount, missile) {
|
||||
*/
|
||||
export function findHardpointId(groupName, clss, rating, name, mount, missile) {
|
||||
let h = this.findHardpoint(groupName, clss, rating, name, mount, missile);
|
||||
if (h) {
|
||||
return h.id;
|
||||
}
|
||||
|
||||
// Countermeasures used to be lumped in a single group but have been broken, out. If we have been given a groupName of 'Countermeasure' then
|
||||
// rely on the unique name to find it
|
||||
if (groupName === 'cm' || groupName === 'Countermeasure') {
|
||||
h = this.findHardpoint(null, clss, rating, name, mount, missile);
|
||||
}
|
||||
|
||||
return h ? h.id : 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,45 @@ import { ModuleGroupToName, MountMap, BulkheadNames } from './Constants';
|
||||
import { Ships } from 'coriolis-data/dist';
|
||||
import Ship from './Ship';
|
||||
import * as ModuleUtils from './ModuleUtils';
|
||||
import * as Utils from '../utils/UtilityFunctions';
|
||||
import LZString from 'lz-string';
|
||||
import { outfitURL } from '../utils/UrlGenerators';
|
||||
|
||||
const STANDARD = ['powerPlant', 'thrusters', 'frameShiftDrive', 'lifeSupport', 'powerDistributor', 'sensors', 'fuelTank'];
|
||||
|
||||
const STANDARD_GROUPS = { 'powerPlant': 'pp', 'thrusters': 't', 'frameShiftDrive': 'fsd', 'lifeSupport': 'ls', 'powerDistributor': 'pd', 'sensors': 's', 'fuelTank': 'ft' };
|
||||
|
||||
/**
|
||||
* Generates ship-loadout JSON Schema standard object
|
||||
* @param {Object} standard model
|
||||
* @return {Object} JSON Schema
|
||||
*/
|
||||
function standardToSchema(standard) {
|
||||
if (standard.m) {
|
||||
let o = {
|
||||
class: standard.m.class,
|
||||
rating: standard.m.rating,
|
||||
enabled: Boolean(standard.enabled),
|
||||
priority: standard.priority + 1
|
||||
};
|
||||
|
||||
if (standard.m.name) {
|
||||
o.name = standard.m.name;
|
||||
}
|
||||
|
||||
if (standard.m.mods && Object.keys(standard.m.mods).length > 0) {
|
||||
o.modifications = standard.m.mods;
|
||||
}
|
||||
|
||||
if (standard.m.blueprint && Object.keys(standard.m.blueprint).length > 0) {
|
||||
o.blueprint = standard.m.blueprint;
|
||||
}
|
||||
|
||||
return o;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates ship-loadout JSON Schema slot object
|
||||
* @param {Object} slot Slot model
|
||||
@@ -30,6 +65,13 @@ function slotToSchema(slot) {
|
||||
if (slot.m.missile) {
|
||||
o.missile = slot.m.missile;
|
||||
}
|
||||
if (slot.m.mods && Object.keys(slot.m.mods).length > 0) {
|
||||
o.modifications = slot.m.mods;
|
||||
}
|
||||
if (slot.m.blueprint && Object.keys(slot.m.blueprint).length > 0) {
|
||||
o.blueprint = slot.m.blueprint;
|
||||
}
|
||||
|
||||
return o;
|
||||
}
|
||||
return null;
|
||||
@@ -48,12 +90,12 @@ export function toDetailedBuild(buildName, ship) {
|
||||
code = ship.toString();
|
||||
|
||||
let data = {
|
||||
$schema: 'http://cdn.coriolis.io/schemas/ship-loadout/3.json#',
|
||||
$schema: 'http://cdn.coriolis.io/schemas/ship-loadout/4.json#',
|
||||
name: buildName,
|
||||
ship: ship.name,
|
||||
references: [{
|
||||
name: 'Coriolis.io',
|
||||
url: `https://coriolis.io/outfit/${ship.id}/${code}?bn=${encodeURIComponent(buildName)}`,
|
||||
url: 'https://coriolis.edcd.io' + outfitURL(ship.id, code, buildName),
|
||||
code,
|
||||
shipId: ship.id
|
||||
}],
|
||||
@@ -61,13 +103,13 @@ export function toDetailedBuild(buildName, ship) {
|
||||
standard: {
|
||||
bulkheads: BulkheadNames[ship.bulkheads.m.index],
|
||||
cargoHatch: { enabled: Boolean(ship.cargoHatch.enabled), priority: ship.cargoHatch.priority + 1 },
|
||||
powerPlant: { class: standard[0].m.class, rating: standard[0].m.rating, enabled: Boolean(standard[0].enabled), priority: standard[0].priority + 1 },
|
||||
thrusters: { class: standard[1].m.class, rating: standard[1].m.rating, enabled: Boolean(standard[1].enabled), priority: standard[1].priority + 1 },
|
||||
frameShiftDrive: { class: standard[2].m.class, rating: standard[2].m.rating, enabled: Boolean(standard[2].enabled), priority: standard[2].priority + 1 },
|
||||
lifeSupport: { class: standard[3].m.class, rating: standard[3].m.rating, enabled: Boolean(standard[3].enabled), priority: standard[3].priority + 1 },
|
||||
powerDistributor: { class: standard[4].m.class, rating: standard[4].m.rating, enabled: Boolean(standard[4].enabled), priority: standard[4].priority + 1 },
|
||||
sensors: { class: standard[5].m.class, rating: standard[5].m.rating, enabled: Boolean(standard[5].enabled), priority: standard[5].priority + 1 },
|
||||
fuelTank: { class: standard[6].m.class, rating: standard[6].m.rating, enabled: Boolean(standard[6].enabled), priority: standard[6].priority + 1 }
|
||||
powerPlant: standardToSchema(standard[0]),
|
||||
thrusters: standardToSchema(standard[1]),
|
||||
frameShiftDrive: standardToSchema(standard[2]),
|
||||
lifeSupport: standardToSchema(standard[3]),
|
||||
powerDistributor: standardToSchema(standard[4]),
|
||||
sensors: standardToSchema(standard[5]),
|
||||
fuelTank: standardToSchema(standard[6])
|
||||
},
|
||||
hardpoints: hardpoints.filter(slot => slot.maxClass > 0).map(slotToSchema),
|
||||
utility: hardpoints.filter(slot => slot.maxClass === 0).map(slotToSchema),
|
||||
@@ -104,6 +146,8 @@ export function fromDetailedBuild(detailedBuild) {
|
||||
let shipData = Ships[shipId];
|
||||
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) {
|
||||
throw 'Invalid bulkheads: ' + stn.bulkheads;
|
||||
@@ -115,13 +159,14 @@ export function fromDetailedBuild(detailedBuild) {
|
||||
}
|
||||
priorities.push(stn[c].priority === undefined ? 0 : stn[c].priority - 1);
|
||||
enabled.push(stn[c].enabled === undefined ? true : stn[c].enabled);
|
||||
return stn[c].class + stn[c].rating;
|
||||
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)
|
||||
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
|
||||
@@ -135,8 +180,18 @@ export function fromDetailedBuild(detailedBuild) {
|
||||
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);
|
||||
ship.buildWith({ bulkheads, standard, hardpoints, internal }, priorities, enabled, modifications, blueprints);
|
||||
|
||||
return ship;
|
||||
};
|
||||
@@ -178,7 +233,7 @@ export function fromComparison(name, builds, facets, predicate, desc) {
|
||||
f: facets,
|
||||
p: predicate,
|
||||
d: desc ? 1 : 0
|
||||
})).replace(/\//g, '-');
|
||||
}));
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -187,5 +242,5 @@ export function fromComparison(name, builds, facets, predicate, desc) {
|
||||
* @return {Object} Comparison data object
|
||||
*/
|
||||
export function toComparison(code) {
|
||||
return JSON.parse(LZString.decompressFromBase64(code.replace(/-/g, '/')));
|
||||
return JSON.parse(LZString.decompressFromBase64(Utils.fromUrlSafe(code)));
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,7 +15,7 @@ export function multiPurpose(ship, shielded, bulkheadIndex) {
|
||||
|
||||
if (shielded) {
|
||||
ship.internal.some(function(slot) {
|
||||
if (canMount(slot, 'sg')) { // Assuming largest slot can hold an eligible shield
|
||||
if (canMount(ship, slot, 'sg')) { // Assuming largest slot can hold an eligible shield
|
||||
ship.use(slot, ModuleUtils.findInternal('sg', slot.maxClass, 'A'));
|
||||
ship.setSlotEnabled(slot, true);
|
||||
return true;
|
||||
@@ -35,7 +35,7 @@ export function trader(ship, shielded, standardOpts) {
|
||||
|
||||
for (let i = ship.internal.length; i--;) {
|
||||
let slot = ship.internal[i];
|
||||
if (sg && canMount(slot, 'sg', sg.class)) {
|
||||
if (sg && canMount(ship, slot, 'sg', sg.class)) {
|
||||
ship.use(slot, sg);
|
||||
sg = null;
|
||||
} else {
|
||||
@@ -77,23 +77,23 @@ export function explorer(ship, planetary) {
|
||||
let slot = ship.internal[i];
|
||||
let nextSlot = (i + 1) < intLength ? ship.internal[i + 1] : null;
|
||||
// Fit best possible Fuel Scoop
|
||||
if (!fuelScoopSlot && canMount(slot, 'fs')) {
|
||||
if (!fuelScoopSlot && canMount(ship, slot, 'fs')) {
|
||||
fuelScoopSlot = slot;
|
||||
ship.use(slot, ModuleUtils.findInternal('fs', slot.maxClass, 'A'));
|
||||
ship.setSlotEnabled(slot, true);
|
||||
// Mount a Shield generator if possible AND an AFM Unit has been mounted already (Guarantees at least 1 AFM Unit)
|
||||
} else if (!sgSlot && shieldNext && canMount(slot, 'sg', sg.class) && !canMount(nextSlot, 'sg', sg.class)) {
|
||||
} else if (!sgSlot && shieldNext && canMount(ship, slot, 'sg', sg.class) && !canMount(ship, nextSlot, 'sg', sg.class)) {
|
||||
sgSlot = slot;
|
||||
shieldNext = false;
|
||||
ship.use(slot, sg);
|
||||
ship.setSlotEnabled(slot, true);
|
||||
// if planetary explorer and the next slot cannot mount a PVH or the next modul to mount is a SG
|
||||
} else if (planetary && !pvhSlot && canMount(slot, 'pv') && (shieldNext || !canMount(nextSlot, 'pv', 2))) {
|
||||
} else if (planetary && !pvhSlot && canMount(ship, slot, 'pv') && (shieldNext || !canMount(ship, nextSlot, 'pv', 2))) {
|
||||
pvhSlot = slot;
|
||||
ship.use(slot, ModuleUtils.findInternal('pv', Math.min(Math.floor(pvhSlot.maxClass / 2) * 2, 6), 'G'));
|
||||
ship.setSlotEnabled(slot, false); // Disabled power for PVH
|
||||
shieldNext = !sgSlot;
|
||||
} else if (afmUnitCount > 0 && canMount(slot, 'am')) {
|
||||
} else if (afmUnitCount > 0 && canMount(ship, slot, 'am')) {
|
||||
afmUnitCount--;
|
||||
ship.use(slot, ModuleUtils.findInternal('am', slot.maxClass, 'A'));
|
||||
ship.setSlotEnabled(slot, false); // Disabled power for AFM Unit
|
||||
@@ -115,7 +115,7 @@ export function explorer(ship, planetary) {
|
||||
|
||||
if (sgSlot) {
|
||||
// The SG and Fuel scoop to not need to be powered at the same time
|
||||
if (sgSlot.m.power > fuelScoopSlot.m.power) { // The Shield generator uses the most power
|
||||
if (sgSlot.m.getPowerUsage() > fuelScoopSlot.m.getPowerUsage()) { // The Shield generator uses the most power
|
||||
ship.setSlotEnabled(fuelScoopSlot, false);
|
||||
} else { // The Fuel scoop uses the most power
|
||||
ship.setSlotEnabled(sgSlot, false);
|
||||
|
||||
@@ -11,6 +11,7 @@ const LS_KEY_MOD_DISCOUNT = 'moduleDiscount';
|
||||
const LS_KEY_STATE = 'state';
|
||||
const LS_KEY_SIZE_RATIO = 'sizeRatio';
|
||||
const LS_KEY_TOOLTIPS = 'tooltips';
|
||||
const LS_KEY_MODULE_RESISTANCES = 'moduleResistances';
|
||||
|
||||
let LS;
|
||||
|
||||
@@ -81,6 +82,7 @@ export class Persist extends EventEmitter {
|
||||
LS = null;
|
||||
}
|
||||
|
||||
let moduleResistances = _get(LS_KEY_MODULE_RESISTANCES);
|
||||
let tips = _get(LS_KEY_TOOLTIPS);
|
||||
let insurance = _getString(LS_KEY_INSURANCE);
|
||||
let shipDiscount = _get(LS_KEY_SHIP_DISCOUNT);
|
||||
@@ -99,6 +101,7 @@ export class Persist extends EventEmitter {
|
||||
this.state = _get(LS_KEY_STATE);
|
||||
this.sizeRatio = _get(LS_KEY_SIZE_RATIO) || 1;
|
||||
this.tooltipsEnabled = tips === null ? true : tips;
|
||||
this.moduleResistancesEnabled = moduleResistances === null ? true : moduleResistances;
|
||||
|
||||
if (LS) {
|
||||
window.addEventListener('storage', this.onStorageChange);
|
||||
@@ -143,6 +146,10 @@ export class Persist extends EventEmitter {
|
||||
this.tooltipsEnabled = !!newValue && newValue.toLowerCase() == 'true';
|
||||
this.emit('tooltips', this.tooltipsEnabled);
|
||||
break;
|
||||
case LS_KEY_MODULE_RESISTANCES:
|
||||
this.moduleResistancesEnabled = !!newValue && newValue.toLowerCase() == 'true';
|
||||
this.emit('moduleresistances', this.moduleResistancesEnabled);
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
// On JSON.Parse Error - don't sync or do anything
|
||||
@@ -183,6 +190,21 @@ export class Persist extends EventEmitter {
|
||||
return this.tooltipsEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show module resistances setting
|
||||
* @param {boolean} show Optional - update setting
|
||||
* @return {boolean} True if module resistances should be shown
|
||||
*/
|
||||
showModuleResistances(show) {
|
||||
if (show !== undefined) {
|
||||
this.moduleResistancesEnabled = !!show;
|
||||
_put(LS_KEY_MODULE_RESISTANCES, this.moduleResistancesEnabled);
|
||||
this.emit('moduleresistances', this.moduleResistancesEnabled);
|
||||
}
|
||||
|
||||
return this.moduleResistancesEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist a ship build in local storage.
|
||||
*
|
||||
|
||||
410
src/app/utils/CompanionApiUtils.js
Normal file
410
src/app/utils/CompanionApiUtils.js
Normal file
@@ -0,0 +1,410 @@
|
||||
import React from 'react';
|
||||
import { Modifications, Modules, Ships } from 'coriolis-data/dist';
|
||||
import Module from '../shipyard/Module';
|
||||
import Ship from '../shipyard/Ship';
|
||||
|
||||
// mapping from fd's ship model names to coriolis'
|
||||
const SHIP_FD_NAME_TO_CORIOLIS_NAME = {
|
||||
'Adder': 'adder',
|
||||
'Anaconda': 'anaconda',
|
||||
'Asp': 'asp',
|
||||
'Asp_Scout': 'asp_scout',
|
||||
'BelugaLiner': 'beluga',
|
||||
'CobraMkIII': 'cobra_mk_iii',
|
||||
'CobraMkIV': 'cobra_mk_iv',
|
||||
'Cutter': 'imperial_cutter',
|
||||
'DiamondBackXL': 'diamondback_explorer',
|
||||
'DiamondBack': 'diamondback',
|
||||
'Eagle': 'eagle',
|
||||
'Empire_Courier': 'imperial_courier',
|
||||
'Empire_Eagle': 'imperial_eagle',
|
||||
'Empire_Trader': 'imperial_clipper',
|
||||
'Federation_Corvette': 'federal_corvette',
|
||||
'Federation_Dropship': 'federal_dropship',
|
||||
'Federation_Dropship_MkII': 'federal_assault_ship',
|
||||
'Federation_Gunship': 'federal_gunship',
|
||||
'FerDeLance': 'fer_de_lance',
|
||||
'Hauler': 'hauler',
|
||||
'Independant_Trader': 'keelback',
|
||||
'Orca': 'orca',
|
||||
'Python': 'python',
|
||||
'SideWinder': 'sidewinder',
|
||||
'Type6': 'type_6_transporter',
|
||||
'Type7': 'type_7_transport',
|
||||
'Type9': 'type_9_heavy',
|
||||
'Viper': 'viper',
|
||||
'Viper_MKIV': 'viper_mk_iv',
|
||||
'Vulture': 'vulture'
|
||||
};
|
||||
|
||||
// Mapping from hardpoint class to name in companion API
|
||||
const HARDPOINT_NUM_TO_CLASS = {
|
||||
0: 'Tiny',
|
||||
1: 'Small',
|
||||
2: 'Medium',
|
||||
3: 'Large',
|
||||
4: 'Huge'
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Obtain a module given its ED ID
|
||||
* @param {Integer} edId the Elite ID of the module
|
||||
* @return {Module} the module
|
||||
*/
|
||||
function _moduleFromEdId(edId) {
|
||||
if (!edId) return null;
|
||||
|
||||
// Check standard modules
|
||||
for (const grp in Modules.standard) {
|
||||
if (Modules.standard.hasOwnProperty(grp)) {
|
||||
for (const i in Modules.standard[grp]) {
|
||||
if (Modules.standard[grp][i].edID === edId) {
|
||||
// Found it
|
||||
return new Module({ template: Modules.standard[grp][i] });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check hardpoint modules
|
||||
for (const grp in Modules.hardpoints) {
|
||||
if (Modules.hardpoints.hasOwnProperty(grp)) {
|
||||
for (const i in Modules.hardpoints[grp]) {
|
||||
if (Modules.hardpoints[grp][i].edID === edId) {
|
||||
// Found it
|
||||
return new Module({ template: Modules.hardpoints[grp][i] });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check internal modules
|
||||
for (const grp in Modules.internal) {
|
||||
if (Modules.internal.hasOwnProperty(grp)) {
|
||||
for (const i in Modules.internal[grp]) {
|
||||
if (Modules.internal[grp][i].edID === edId) {
|
||||
// Found it
|
||||
return new Module({ template: Modules.internal[grp][i] });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Not found
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the model of a ship given its ED name
|
||||
* @param {string} edName the Elite name of the ship
|
||||
* @return {string} the Coriolis model of the ship
|
||||
*/
|
||||
function _shipModelFromEDName(edName) {
|
||||
return SHIP_FD_NAME_TO_CORIOLIS_NAME[edName];
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a ship's model from the companion API JSON
|
||||
* @param {object} json the companion API JSON
|
||||
* @return {string} the Coriolis model of the ship
|
||||
*/
|
||||
export function shipModelFromJson(json) {
|
||||
return _shipModelFromEDName(json.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a ship from the companion API JSON
|
||||
* @param {object} json the companion API JSON
|
||||
* @return {Ship} the built ship
|
||||
*/
|
||||
export function shipFromJson(json) {
|
||||
// Start off building a basic ship
|
||||
const shipModel = shipModelFromJson(json);
|
||||
if (!shipModel) {
|
||||
throw 'No such ship found: "' + json.name + '"';
|
||||
}
|
||||
const shipTemplate = Ships[shipModel];
|
||||
|
||||
let ship = new Ship(shipModel, shipTemplate.properties, shipTemplate.slots);
|
||||
ship.buildWith(null);
|
||||
|
||||
// Set the cargo hatch. We don't have any information on it so guess it's priority 5 and disabled
|
||||
ship.cargoHatch.enabled = false;
|
||||
ship.cargoHatch.priority = 4;
|
||||
|
||||
// Add the bulkheads
|
||||
const armourJson = json.modules.Armour.module;
|
||||
if (armourJson.name.endsWith('_Armour_Grade1')) {
|
||||
ship.useBulkhead(0, true);
|
||||
} else if (armourJson.name.endsWith('_Armour_Grade2')) {
|
||||
ship.useBulkhead(1, true);
|
||||
} else if (armourJson.name.endsWith('_Armour_Grade3')) {
|
||||
ship.useBulkhead(2, true);
|
||||
} else if (armourJson.name.endsWith('_Armour_Mirrored')) {
|
||||
ship.useBulkhead(3, true);
|
||||
} else if (armourJson.name.endsWith('_Armour_Reactive')) {
|
||||
ship.useBulkhead(4, true);
|
||||
} else {
|
||||
throw 'Unknown bulkheads "' + armourJson.name + '"';
|
||||
}
|
||||
ship.bulkheads.enabled = true;
|
||||
if (armourJson.modifiers) _addModifications(ship.bulkheads.m, armourJson.modifiers, armourJson.recipeName, armourJson.recipeLevel);
|
||||
|
||||
// Add the standard modules
|
||||
// Power plant
|
||||
const powerplantJson = json.modules.PowerPlant.module;
|
||||
const powerplant = _moduleFromEdId(powerplantJson.id);
|
||||
if (powerplantJson.modifiers) _addModifications(powerplant, powerplantJson.modifiers, powerplantJson.recipeName, powerplantJson.recipeLevel);
|
||||
ship.use(ship.standard[0], powerplant, true);
|
||||
ship.standard[0].enabled = powerplantJson.on === true;
|
||||
ship.standard[0].priority = powerplantJson.priority;
|
||||
|
||||
// Thrusters
|
||||
const thrustersJson = json.modules.MainEngines.module;
|
||||
const thrusters = _moduleFromEdId(thrustersJson.id);
|
||||
if (thrustersJson.modifiers) _addModifications(thrusters, thrustersJson.modifiers, thrustersJson.recipeName, thrustersJson.recipeLevel);
|
||||
ship.use(ship.standard[1], thrusters, true);
|
||||
ship.standard[1].enabled = thrustersJson.on === true;
|
||||
ship.standard[1].priority = thrustersJson.priority;
|
||||
|
||||
// FSD
|
||||
const frameshiftdriveJson = json.modules.FrameShiftDrive.module;
|
||||
const frameshiftdrive = _moduleFromEdId(frameshiftdriveJson.id);
|
||||
if (frameshiftdriveJson.modifiers) _addModifications(frameshiftdrive, frameshiftdriveJson.modifiers, frameshiftdriveJson.recipeName, frameshiftdriveJson.recipeLevel);
|
||||
ship.use(ship.standard[2], frameshiftdrive, true);
|
||||
ship.standard[2].enabled = frameshiftdriveJson.on === true;
|
||||
ship.standard[2].priority = frameshiftdriveJson.priority;
|
||||
|
||||
// Life support
|
||||
const lifesupportJson = json.modules.LifeSupport.module;
|
||||
const lifesupport = _moduleFromEdId(lifesupportJson.id);
|
||||
if (lifesupportJson.modifiers)_addModifications(lifesupport, lifesupportJson.modifiers, lifesupportJson.recipeName, lifesupportJson.recipeLevel);
|
||||
ship.use(ship.standard[3], lifesupport, true);
|
||||
ship.standard[3].enabled = lifesupportJson.on === true;
|
||||
ship.standard[3].priority = lifesupportJson.priority;
|
||||
|
||||
// Power distributor
|
||||
const powerdistributorJson = json.modules.PowerDistributor.module;
|
||||
const powerdistributor = _moduleFromEdId(powerdistributorJson.id);
|
||||
if (powerdistributorJson.modifiers) _addModifications(powerdistributor, powerdistributorJson.modifiers, powerdistributorJson.recipeName, powerdistributorJson.recipeLevel);
|
||||
ship.use(ship.standard[4], powerdistributor, true);
|
||||
ship.standard[4].enabled = powerdistributorJson.on === true;
|
||||
ship.standard[4].priority = powerdistributorJson.priority;
|
||||
|
||||
// Sensors
|
||||
const sensorsJson = json.modules.Radar.module;
|
||||
const sensors = _moduleFromEdId(sensorsJson.id);
|
||||
if (sensorsJson.modifiers) _addModifications(sensors, sensorsJson.modifiers, sensorsJson.recipeName, sensorsJson.recipeLevel);
|
||||
ship.use(ship.standard[5], sensors, true);
|
||||
ship.standard[5].enabled = sensorsJson.on === true;
|
||||
ship.standard[5].priority = sensorsJson.priority;
|
||||
|
||||
// Fuel tank
|
||||
const fueltankJson = json.modules.FuelTank.module;
|
||||
const fueltank = _moduleFromEdId(fueltankJson.id);
|
||||
ship.use(ship.standard[6], fueltank, true);
|
||||
ship.standard[6].enabled = true;
|
||||
ship.standard[6].priority = 0;
|
||||
|
||||
// Add hardpoints
|
||||
let hardpointClassNum = -1;
|
||||
let hardpointSlotNum = -1;
|
||||
let hardpointArrayNum = 0;
|
||||
for (let i in shipTemplate.slots.hardpoints) {
|
||||
if (shipTemplate.slots.hardpoints[i] === hardpointClassNum) {
|
||||
// Another slot of the same class
|
||||
hardpointSlotNum++;
|
||||
} else {
|
||||
// The first slot of a new class
|
||||
hardpointClassNum = shipTemplate.slots.hardpoints[i];
|
||||
hardpointSlotNum = 1;
|
||||
}
|
||||
|
||||
// Now that we know what we're looking for, find it
|
||||
const hardpointName = HARDPOINT_NUM_TO_CLASS[hardpointClassNum] + 'Hardpoint' + hardpointSlotNum;
|
||||
const hardpointSlot = json.modules[hardpointName];
|
||||
if (!hardpointSlot.module) {
|
||||
// No module
|
||||
} else {
|
||||
const hardpointJson = hardpointSlot.module;
|
||||
const hardpoint = _moduleFromEdId(hardpointJson.id);
|
||||
if (hardpointJson.modifiers) _addModifications(hardpoint, hardpointJson.modifiers, hardpointJson.recipeName, hardpointJson.recipeLevel);
|
||||
ship.use(ship.hardpoints[hardpointArrayNum], hardpoint, true);
|
||||
ship.hardpoints[hardpointArrayNum].enabled = hardpointJson.on === true;
|
||||
ship.hardpoints[hardpointArrayNum].priority = hardpointJson.priority;
|
||||
}
|
||||
hardpointArrayNum++;
|
||||
}
|
||||
|
||||
// Add internal compartments
|
||||
let internalSlotNum = 1;
|
||||
for (let i in shipTemplate.slots.internal) {
|
||||
const internalClassNum = isNaN(shipTemplate.slots.internal[i]) ? shipTemplate.slots.internal[i].class : shipTemplate.slots.internal[i];
|
||||
|
||||
let internalSlot = null;
|
||||
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++;
|
||||
}
|
||||
if (!internalSlot.module) {
|
||||
// No module
|
||||
} else {
|
||||
const internalJson = internalSlot.module;
|
||||
const internal = _moduleFromEdId(internalJson.id);
|
||||
if (internalJson.modifiers) _addModifications(internal, internalJson.modifiers, internalJson.recipeName, internalJson.recipeLevel);
|
||||
ship.use(ship.internal[i], internal, true);
|
||||
ship.internal[i].enabled = internalJson.on === true;
|
||||
ship.internal[i].priority = internalJson.priority;
|
||||
}
|
||||
}
|
||||
|
||||
// Now update the ship's codes before returning it
|
||||
return ship.updatePowerPrioritesString().updatePowerEnabledString().updateModificationsString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the modifications for a module
|
||||
* @param {Module} module the module
|
||||
* @param {Object} modifiers the modifiers
|
||||
* @param {Object} blueprint the blueprint of the modification
|
||||
* @param {Object} grade the grade of the modification
|
||||
*/
|
||||
function _addModifications(module, modifiers, blueprint, grade) {
|
||||
if (!modifiers || !modifiers.modifiers) return;
|
||||
|
||||
let special;
|
||||
for (const i in modifiers.modifiers) {
|
||||
// Some special modifications
|
||||
if (modifiers.modifiers[i].name === 'mod_weapon_clip_size_override') {
|
||||
// This is a numeric addition to the clip size, but we need to work it out in terms of being a percentage so
|
||||
// that it works the same as other modifications
|
||||
const origClip = module.clip || 1;
|
||||
module.setModValue('clip', ((modifiers.modifiers[i].value - origClip) / origClip) * 10000);
|
||||
} else if (modifiers.modifiers[i].name === 'mod_weapon_burst_size') {
|
||||
// This is an absolute number that acts as an override
|
||||
module.setModValue('burst', modifiers.modifiers[i].value * 100);
|
||||
} else if (modifiers.modifiers[i].name === 'mod_weapon_burst_rof') {
|
||||
// For some reason this is a non-normalised percentage (i.e. 12.23% is 12.23 value rather than 0.1223 as everywhere else), so fix that here
|
||||
module.setModValue('burstrof', modifiers.modifiers[i].value * 100);
|
||||
} else {
|
||||
// Look up the modifiers to find what we need to do
|
||||
const modifierActions = Modifications.modifierActions[modifiers.modifiers[i].name];
|
||||
const value = modifiers.modifiers[i].value;
|
||||
|
||||
// Carry out the required changes
|
||||
for (const action in modifierActions) {
|
||||
if (isNaN(modifierActions[action])) {
|
||||
module.setModValue(action, modifierActions[action]);
|
||||
} else {
|
||||
const actionValue = modifierActions[action] * value;
|
||||
let mod = module.getModValue(action) / 10000;
|
||||
if (!mod) {
|
||||
mod = 0;
|
||||
}
|
||||
module.setModValue(action, ((1 + mod) * (1 + actionValue) - 1) * 10000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Note the special if present
|
||||
if (modifiers.modifiers[i].name && modifiers.modifiers[i].name.startsWith('special_')) {
|
||||
special = Modifications.specials[modifiers.modifiers[i].name];
|
||||
}
|
||||
}
|
||||
|
||||
// Add the blueprint ID, grade and special
|
||||
if (blueprint) {
|
||||
module.blueprint = Object.assign({}, Modifications.blueprints[blueprint]);
|
||||
if (grade) {
|
||||
module.blueprint.grade = Number(grade);
|
||||
}
|
||||
if (special) {
|
||||
module.blueprint.special = special;
|
||||
}
|
||||
}
|
||||
|
||||
// Need to fix up a few items
|
||||
|
||||
// Shield boosters are treated internally as straight modifiers, so rather than (for example)
|
||||
// being a 4% boost they are a 104% multiplier. Unfortunately this means that our % modification
|
||||
// is incorrect so we fix it
|
||||
if (module.grp === 'sb' && module.getModValue('shieldboost')) {
|
||||
const alteredBoost = (1 + module.shieldboost) * (module.getModValue('shieldboost') / 10000);
|
||||
module.setModValue('shieldboost', alteredBoost * 10000 / module.shieldboost);
|
||||
}
|
||||
|
||||
// Shield booster resistance is actually a damage modifier, so needs to be inverted.
|
||||
if (module.grp === 'sb') {
|
||||
if (module.getModValue('explres')) {
|
||||
module.setModValue('explres', ((module.getModValue('explres') / 10000) * -1) * 10000);
|
||||
}
|
||||
if (module.getModValue('kinres')) {
|
||||
module.setModValue('kinres', ((module.getModValue('kinres') / 10000) * -1) * 10000);
|
||||
}
|
||||
if (module.getModValue('thermres')) {
|
||||
module.setModValue('thermres', ((module.getModValue('thermres') / 10000) * -1) * 10000);
|
||||
}
|
||||
}
|
||||
|
||||
// Shield generator resistance is actually a damage modifier, so needs to be inverted.
|
||||
// In addition, the modification is based off the inherent resistance of the module
|
||||
if (module.isShieldGenerator()) {
|
||||
if (module.getModValue('explres')) {
|
||||
module.setModValue('explres', ((1 - (1 - module.explres) * (1 + module.getModValue('explres') / 10000)) - module.explres) * 10000);
|
||||
}
|
||||
if (module.getModValue('kinres')) {
|
||||
module.setModValue('kinres', ((1 - (1 - module.kinres) * (1 + module.getModValue('kinres') / 10000)) - module.kinres) * 10000);
|
||||
}
|
||||
if (module.getModValue('thermres')) {
|
||||
module.setModValue('thermres', ((1 - (1 - module.thermres) * (1 + module.getModValue('thermres') / 10000)) - module.thermres) * 10000);
|
||||
}
|
||||
}
|
||||
|
||||
// Hull reinforcement package resistance is actually a damage modifier, so needs to be inverted.
|
||||
if (module.grp === 'hr') {
|
||||
if (module.getModValue('explres')) {
|
||||
module.setModValue('explres', ((module.getModValue('explres') / 10000) * -1) * 10000);
|
||||
}
|
||||
if (module.getModValue('kinres')) {
|
||||
module.setModValue('kinres', ((module.getModValue('kinres') / 10000) * -1) * 10000);
|
||||
}
|
||||
if (module.getModValue('thermres')) {
|
||||
module.setModValue('thermres', ((module.getModValue('thermres') / 10000) * -1) * 10000);
|
||||
}
|
||||
}
|
||||
|
||||
// Bulkhead resistance is actually a damage modifier, so needs to be inverted.
|
||||
// In addition, the modification is based off the inherent resistance of the module
|
||||
if (module.grp == 'bh') {
|
||||
if (module.getModValue('explres')) {
|
||||
module.setModValue('explres', ((1 - (1 - module.explres) * (1 + module.getModValue('explres') / 10000)) - module.explres) * 10000);
|
||||
}
|
||||
if (module.getModValue('kinres')) {
|
||||
module.setModValue('kinres', ((1 - (1 - module.kinres) * (1 + module.getModValue('kinres') / 10000)) - module.kinres) * 10000);
|
||||
}
|
||||
if (module.getModValue('thermres')) {
|
||||
module.setModValue('thermres', ((1 - (1 - module.thermres) * (1 + module.getModValue('thermres') / 10000)) - module.thermres) * 10000);
|
||||
}
|
||||
}
|
||||
|
||||
// Bulkhead boost is based off the inherent boost of the module
|
||||
if (module.grp == 'bh') {
|
||||
const alteredBoost = (1 + module.hullboost) * (1 + module.getModValue('hullboost') / 10000) - 1;
|
||||
module.setModValue('hullboost', (alteredBoost / module.hullboost - 1) * 10000);
|
||||
}
|
||||
|
||||
// Jitter is an absolute number, so we need to divide it by 100
|
||||
if (module.getModValue('jitter')) {
|
||||
module.setModValue('jitter', module.getModValue('jitter') / 100);
|
||||
}
|
||||
|
||||
// FD uses interval between bursts internally, so we need to translate this to a real rate of fire
|
||||
if (module.getModValue('rof')) {
|
||||
module.setModValue('rof', ((1 / (1 + module.getModValue('rof') / 10000)) - 1) * 10000);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,24 @@
|
||||
import React from 'react';
|
||||
import cn from 'classnames';
|
||||
import { isShieldGenerator } from '../shipyard/ModuleUtils';
|
||||
import Module from '../shipyard/Module';
|
||||
import { Infinite } from '../components/SvgIcons';
|
||||
import Persist from '../stores/Persist';
|
||||
|
||||
/**
|
||||
* Determine if a slot can mount a module of a particular class and group
|
||||
* Determine if a slot on a ship can mount a module of a particular class and group
|
||||
* @param {Object} ship Ship object
|
||||
* @param {Object} slot Slot object
|
||||
* @param {String} group Module group/type abbrivation/code
|
||||
* @param {Integer} clazz [Optional] Module Class/Size
|
||||
* @return {Boolean} True if the slot can mount the module
|
||||
*/
|
||||
export function canMount(slot, group, clazz) {
|
||||
if (slot && (!slot.eligible || slot.eligible[group]) && (clazz === undefined || slot.maxClass >= clazz)) {
|
||||
export function canMount(ship, slot, group, clazz) {
|
||||
if (slot &&
|
||||
(!slot.eligible || slot.eligible[group]) &&
|
||||
(group != 'pcq' || (ship.luxuryCabins && ship.luxuryCabins === true)) &&
|
||||
(group != 'fh' || (ship.fighterHangars && ship.fighterHangars === true)) &&
|
||||
(clazz === undefined || slot.maxClass >= clazz)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -87,69 +93,6 @@ export function slotComparator(translate, propComparator, desc) {
|
||||
};
|
||||
}
|
||||
|
||||
const PROP_BLACKLIST = {
|
||||
eddbID: 1,
|
||||
edID: 1,
|
||||
id: 1,
|
||||
index: 1,
|
||||
'class': 1,
|
||||
rating: 1,
|
||||
maxfuel: 1,
|
||||
fuelmul: 1,
|
||||
fuelpower: 1,
|
||||
optmass: 1,
|
||||
maxmass: 1,
|
||||
minmass: 1,
|
||||
passive: 1,
|
||||
thermload: 1,
|
||||
ammocost: 1,
|
||||
activepower: 1,
|
||||
cooldown: 1,
|
||||
chargeup: 1,
|
||||
optmul: 1,
|
||||
minmul: 1,
|
||||
maxmul: 1,
|
||||
ssdam: 1,
|
||||
mjdps: 1,
|
||||
mjeps: 1,
|
||||
M: 1,
|
||||
P: 1,
|
||||
mass: 1,
|
||||
cost: 1,
|
||||
recover: 1,
|
||||
weaponcapacity: 1,
|
||||
weaponrecharge: 1,
|
||||
enginecapacity: 1,
|
||||
enginerecharge: 1,
|
||||
systemcapacity: 1,
|
||||
systemrecharge: 1
|
||||
};
|
||||
|
||||
const TERM_LOOKUP = {
|
||||
pGen: 'power',
|
||||
armouradd: 'armour',
|
||||
shieldmul: 'multiplier',
|
||||
rof: 'ROF',
|
||||
dps: 'DPS'
|
||||
};
|
||||
|
||||
const FORMAT_LOOKUP = {
|
||||
time: 'time',
|
||||
shieldmul: 'rPct'
|
||||
};
|
||||
|
||||
const UNIT_LOOKUP = {
|
||||
fuel: 'T',
|
||||
cargo: 'T',
|
||||
rate: 'kgs',
|
||||
range: 'km',
|
||||
recharge: 'MJ',
|
||||
rangeLS: 'Ls',
|
||||
power: 'MJ',
|
||||
pGen: 'MJ',
|
||||
rof: 'ps'
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine the appropriate class based on diff value
|
||||
* @param {Number} a Potential Module (cannot be null)
|
||||
@@ -175,7 +118,7 @@ function diffClass(a, b, negative) {
|
||||
*/
|
||||
function diff(format, mVal, mmVal) {
|
||||
if (mVal == Infinity) {
|
||||
return <Infinite/>;
|
||||
return '∞';
|
||||
} else {
|
||||
let diff = mVal - mmVal;
|
||||
if (!diff || mVal === undefined || diff == mVal || Math.abs(diff) == Infinity) {
|
||||
@@ -197,42 +140,46 @@ function diff(format, mVal, mmVal) {
|
||||
* @return {React.Component} Component to be rendered
|
||||
*/
|
||||
export function diffDetails(language, m, mm) {
|
||||
mm = mm || {};
|
||||
let { formats, translate, units } = language;
|
||||
let propDiffs = [];
|
||||
|
||||
let mCost = m.cost || 0;
|
||||
let mmCost = mm ? mm.cost : 0;
|
||||
if (mCost != mmCost) propDiffs.push(<div key='cost'>{translate('cost')}: <span className={diffClass(mCost, mmCost, true) }>{mCost ? Math.round(mCost * (1 - Persist.getModuleDiscount())) : 0}{units.CR}</span></div>);
|
||||
|
||||
let mMass = m.mass || 0;
|
||||
let mmMass = mm.mass || 0;
|
||||
let massDiff = mMass - mmMass;
|
||||
let capDiff = (m.fuel || m.cargo || 0) - (mm.fuel || mm.cargo || 0);
|
||||
let mmMass = mm ? mm.getMass() : 0;
|
||||
if (mMass != mmMass) propDiffs.push(<div key='mass'>{translate('mass')}: <span className={diffClass(mMass, mmMass, true)}>{diff(formats.round, mMass, mmMass)}{units.T}</span></div>);
|
||||
|
||||
let mPowerGeneration = m.pgen || 0;
|
||||
let mmPowerGeneration = mm ? mm.getPowerGeneration() : 0;
|
||||
if (mPowerGeneration != mmPowerGeneration) propDiffs.push(<div key='pgen'>{translate('pgen')}: <span className={diffClass(mPowerGeneration, mmPowerGeneration, true)}>{diff(formats.round, mPowerGeneration, mmPowerGeneration)}{units.MJ}</span></div>);
|
||||
|
||||
let mPowerUsage = m.power || 0;
|
||||
let mmPowerUsage = mm ? mm.getPowerUsage() : 0;
|
||||
if (mPowerUsage != mmPowerUsage) propDiffs.push(<div key='power'>{translate('power')}: <span className={diffClass(mPowerUsage, mmPowerUsage, true)}>{diff(formats.round, mPowerUsage, mmPowerUsage)}{units.MJ}</span></div>);
|
||||
|
||||
let mDps = m.damage * (m.rpshot || 1) * (m.rof || 1) || 0;
|
||||
let mmDps = mm ? mm.getDps() || 0 : 0;
|
||||
if (mDps != mmDps) propDiffs.push(<div key='dps'>{translate('dps')}: <span className={diffClass(mmDps, mDps, true)}>{diff(formats.round, mDps, mmDps)}</span></div>);
|
||||
|
||||
let mAffectsShield = isShieldGenerator(m.grp) || m.grp == 'sb';
|
||||
let mmAffectsShield = isShieldGenerator(mm.grp) || mm.grp == 'sb';
|
||||
|
||||
propDiffs.push(<div key='cost'>{translate('cost')}: <span className={diffClass(m.cost, mm.cost, true) }>{m.cost ? Math.round(m.cost * (1 - Persist.getModuleDiscount())) : 0}{units.CR}</span></div>);
|
||||
propDiffs.push(<div key='mass'>{translate('mass')}: <span className={diffClass(mMass, mm.mass, true)}>{diff(formats.round, mMass, mmMass)}{units.T}</span></div>);
|
||||
|
||||
for (let p in m) {
|
||||
if (!PROP_BLACKLIST[p] && !isNaN(m[p])) {
|
||||
let mVal = m[p] === null ? Infinity : m[p];
|
||||
let mmVal = mm[p] === null ? Infinity : mm[p];
|
||||
let format = formats[FORMAT_LOOKUP[p]] || formats.round;
|
||||
propDiffs.push(<div key={p}>
|
||||
{`${translate(TERM_LOOKUP[p] || p)}: `}
|
||||
<span className={diffClass(mVal, mmVal, p == 'power')}>{diff(format, mVal, mmVal)}{units[UNIT_LOOKUP[p]]}</span>
|
||||
</div>);
|
||||
}
|
||||
}
|
||||
|
||||
let mmAffectsShield = isShieldGenerator(mm ? mm.grp : null) || mm && mm.grp == 'sb';
|
||||
if (mAffectsShield || mmAffectsShield) {
|
||||
let shield = this.calcShieldStrengthWith(); // Get shield strength regardless of slot active / inactive
|
||||
let newShield = 0;
|
||||
|
||||
if (mAffectsShield) {
|
||||
if (m.grp == 'sb') { // Both m and mm must be utility modules if this is true
|
||||
newShield = this.calcShieldStrengthWith(null, m.shieldmul - (mm.shieldmul || 0));
|
||||
newShield = this.calcShieldStrengthWith(null, m.shieldboost - (mm ? mm.getShieldBoost() || 0 : 0));
|
||||
} else {
|
||||
newShield = this.calcShieldStrengthWith(m);
|
||||
}
|
||||
} else {
|
||||
// Old module must be a shield booster
|
||||
newShield = this.calcShieldStrengthWith(null, -mm.getShieldBoost());
|
||||
}
|
||||
|
||||
let sgDiffClass = Math.round((newShield - shield) * 100) / 100 == 0 ? 'muted' : (newShield > shield ? 'secondary' : 'warning');
|
||||
|
||||
propDiffs.push(<div key='shields'>{translate('shields')}: <span className={sgDiffClass}>{diff(formats.int, newShield, shield)}{units.MJ}</span></div>);
|
||||
@@ -241,24 +188,28 @@ export function diffDetails(language, m, mm) {
|
||||
if (m.grp == 'pd') {
|
||||
propDiffs.push(<div key='wep'>
|
||||
{`${translate('WEP')}: `}
|
||||
<span className={diffClass(m.weaponcapacity, mm.weaponcapacity)}>{m.weaponcapacity}{units.MJ}</span>
|
||||
<span className={diffClass(m.wepcap, mm.getWeaponsCapacity())}>{m.wepcap}{units.MJ}</span>
|
||||
{' / '}
|
||||
<span className={diffClass(m.weaponrecharge, mm.weaponrecharge)}>{m.weaponrecharge}{units.MW}</span>
|
||||
<span className={diffClass(m.weprate, mm.getWeaponsRechargeRate())}>{m.weprate}{units.MW}</span>
|
||||
</div>);
|
||||
propDiffs.push(<div key='sys'>
|
||||
{`${translate('SYS')}: `}
|
||||
<span className={diffClass(m.systemcapacity, mm.systemcapacity)}>{m.systemcapacity}{units.MJ}</span>
|
||||
<span className={diffClass(m.syscap, mm.getSystemsCapacity())}>{m.syscap}{units.MJ}</span>
|
||||
{' / '}
|
||||
<span className={diffClass(m.systemrecharge, mm.systemrecharge)}>{m.systemrecharge}{units.MW}</span>
|
||||
<span className={diffClass(m.sysrate, mm.getSystemsRechargeRate())}>{m.sysrate}{units.MW}</span>
|
||||
</div>);
|
||||
propDiffs.push(<div key='eng'>
|
||||
{`${translate('ENG')}: `}
|
||||
<span className={diffClass(m.enginecapacity, mm.enginecapacity)}>{m.enginecapacity}{units.MJ}</span>
|
||||
<span className={diffClass(m.engcap, mm.getEnginesCapacity())}>{m.engcap}{units.MJ}</span>
|
||||
{' / '}
|
||||
<span className={diffClass(m.enginerecharge, mm.enginerecharge)}>{m.enginerecharge}{units.MW}</span>
|
||||
<span className={diffClass(m.engrate, mm.getEnginesRechargeRate())}>{m.engrate}{units.MW}</span>
|
||||
</div>);
|
||||
}
|
||||
|
||||
let massDiff = mMass - mmMass;
|
||||
let mCap = m.fuel || m.cargo || 0;
|
||||
let mmCap = mm ? mm.fuel || mm.cargo || 0 : 0;
|
||||
let capDiff = mCap - mmCap;
|
||||
if (m.grp == 'fsd' || massDiff || capDiff) {
|
||||
let fsd = m.grp == 'fsd' ? m : null;
|
||||
let maxRange = this.calcUnladenRange(massDiff, m.fuel, fsd);
|
||||
@@ -272,5 +223,5 @@ export function diffDetails(language, m, mm) {
|
||||
}
|
||||
}
|
||||
|
||||
return <div className='cap' style={{ whiteSpace: 'nowrap' }}>{propDiffs}</div>;
|
||||
return propDiffs ? <div className='cap' style={{ whiteSpace: 'nowrap' }}>{propDiffs}</div> : null;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* Generates a URL for the outiffing page
|
||||
* @param {String} shipId Ship Id
|
||||
@@ -7,15 +6,18 @@
|
||||
* @return {String} URL
|
||||
*/
|
||||
export function outfitURL(shipId, code, buildName) {
|
||||
let parts = ['/outfit/', shipId];
|
||||
let path = '/outfit/' + shipId;
|
||||
|
||||
let sepChar = '?';
|
||||
|
||||
if (code) {
|
||||
parts.push('/', code);
|
||||
path = path + sepChar + 'code=' + encodeURIComponent(code);
|
||||
sepChar = '&';
|
||||
}
|
||||
|
||||
if (buildName) {
|
||||
parts.push('?bn=', encodeURIComponent(buildName));
|
||||
path = path + sepChar + 'bn=' + encodeURIComponent(buildName);
|
||||
}
|
||||
return parts.join('');
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
@@ -58,3 +58,16 @@ export function shallowEqual(objA, objB) {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn a URL-safe base-64 encoded string in to a normal version.
|
||||
* Coriolis used to use a different encoding system, and some old
|
||||
* data might be bookmarked or on local storage, so we keep this
|
||||
* around and use it when decoding data from the old-style URLs to
|
||||
* be safe.
|
||||
* @param {string} data the string
|
||||
* @return {string} the converted string
|
||||
*/
|
||||
export function fromUrlSafe(data) {
|
||||
return data ? data.replace(/-/g, '/').replace(/_/g, '+') : null;
|
||||
}
|
||||
|
||||
@@ -163,4 +163,4 @@ footer {
|
||||
float: right;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
|
||||
// Standard icons
|
||||
.icon {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
@@ -25,3 +26,39 @@
|
||||
height: 2em;
|
||||
}
|
||||
}
|
||||
|
||||
// Modifiction icons - hard-code stroke/fill
|
||||
.modicon {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 1.1em;
|
||||
height: 1em;
|
||||
stoke: @fg;
|
||||
fill: transparent;
|
||||
|
||||
&.sm {
|
||||
width: 0.8em;
|
||||
height: 0.75em;
|
||||
}
|
||||
|
||||
&.tn {
|
||||
width: 0.6em;
|
||||
height: 0.5em;
|
||||
}
|
||||
|
||||
&.lg {
|
||||
width: 1.6em;
|
||||
height: 1.5em;
|
||||
}
|
||||
|
||||
&.xl {
|
||||
width: 2.1em;
|
||||
height: 2em;
|
||||
}
|
||||
}
|
||||
|
||||
.summary {
|
||||
stroke: @fg;
|
||||
stroke-width: 10;
|
||||
fill: @fg;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
background-color: @primary-bg;
|
||||
border: 1px solid @primary-disabled;
|
||||
color: @fg;
|
||||
stroke: @fg;
|
||||
stroke-width: 20;
|
||||
fill: @fg;
|
||||
|
||||
.details-container {
|
||||
@@ -54,6 +56,8 @@
|
||||
font-size: 1.2em;
|
||||
width: 1.2em;
|
||||
color: @primary-disabled;
|
||||
stroke: @primary-disabled;
|
||||
stroke-width: 20;
|
||||
border-right: 1px solid @primary-disabled;
|
||||
box-sizing: border-box;
|
||||
padding-top: 0.2em;
|
||||
@@ -64,6 +68,8 @@
|
||||
text-transform: uppercase;
|
||||
font-size: 1.3em;
|
||||
color: lighten(@primary-bg, 12%);
|
||||
stroke: lighten(@primary-bg, 12%);
|
||||
stroke-width: 20;
|
||||
text-align: center;
|
||||
letter-spacing: 0.1em;
|
||||
line-height: 1.7em;
|
||||
@@ -71,12 +77,16 @@
|
||||
|
||||
&.selected {
|
||||
color: @primary-bg;
|
||||
stroke: @primary-bg;
|
||||
stroke-width: 20;
|
||||
fill: @primary-bg;
|
||||
background-color: @primary;
|
||||
border: 1px solid @primary;
|
||||
z-index: 1;
|
||||
.sz {
|
||||
color: @primary;
|
||||
stroke: @primary;
|
||||
stroke-width: 20;
|
||||
background-color: @primary-bg;
|
||||
border-right: 1px solid @primary;
|
||||
}
|
||||
|
||||
@@ -76,7 +76,11 @@
|
||||
"class": { "type": "integer", "minimum": 2, "maximum": 8 },
|
||||
"rating": { "$ref": "#/definitions/standardRatings" },
|
||||
"enabled": { "type": "boolean" },
|
||||
"priority": { "type": "integer", "minimum": 1, "maximum": 5 }
|
||||
"priority": { "type": "integer", "minimum": 1, "maximum": 5 },
|
||||
"name": {
|
||||
"description": "The name identifing the thrusters (if applicable), e.g. 'Enhanced Performance'",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"frameShiftDrive": {
|
||||
|
||||
366
src/schemas/ship-loadout/4.json
Normal file
366
src/schemas/ship-loadout/4.json
Normal file
@@ -0,0 +1,366 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"id": "http://cdn.coriolis.io/schemas/ship-loadout/4.json#",
|
||||
"title": "Ship Loadout",
|
||||
"type": "object",
|
||||
"description": "The details for a specific ship build/loadout",
|
||||
"required": ["name", "ship", "components"],
|
||||
"properties": {
|
||||
"name": {
|
||||
"description": "The name of the build/loadout",
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"ship": {
|
||||
"description": "The full display name of the ship",
|
||||
"type": "string",
|
||||
"minimum": 3
|
||||
},
|
||||
"manufacturer": {
|
||||
"description": "The ship manufacturer",
|
||||
"type": "string"
|
||||
},
|
||||
"references" : {
|
||||
"description": "3rd Party references and/or links to this build/loadout",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["name","url"],
|
||||
"additionalProperties": true,
|
||||
"properties": {
|
||||
"name": {
|
||||
"description": "The name of the 3rd party, .e.g 'Coriolis.io' or 'E:D Shipyard'",
|
||||
"type": "string"
|
||||
},
|
||||
"url": {
|
||||
"description": "The link/url to the 3rd party referencing this build/loadout",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"description": "The components used by this build",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["standard", "internal", "hardpoints", "utility"],
|
||||
"properties": {
|
||||
"standard": {
|
||||
"description": "The set of standard components across all ships",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["bulkheads", "powerPlant", "thrusters", "frameShiftDrive", "lifeSupport", "powerDistributor", "sensors", "fuelTank", "cargoHatch"],
|
||||
"properties": {
|
||||
"bulkheads": {
|
||||
"enum": ["Lightweight Alloy", "Reinforced Alloy", "Military Grade Composite", "Mirrored Surface Composite", "Reactive Surface Composite"]
|
||||
},
|
||||
"cargoHatch": {
|
||||
"required": ["enabled", "priority"],
|
||||
"properties": {
|
||||
"enabled": { "type": "boolean" },
|
||||
"priority": { "type": "integer", "minimum": 1, "maximum": 5 }
|
||||
}
|
||||
},
|
||||
"powerPlant": {
|
||||
"required": ["class", "rating", "enabled", "priority"],
|
||||
"properties": {
|
||||
"class": { "type": "integer", "minimum": 2, "maximum": 8 },
|
||||
"rating": { "$ref": "#/definitions/standardRatings" },
|
||||
"enabled": { "type": "boolean" },
|
||||
"priority": { "type": "integer", "minimum": 1, "maximum": 5 },
|
||||
"blueprint": { "type": "object" },
|
||||
"modifications": { "type": "object" }
|
||||
}
|
||||
},
|
||||
"thrusters": {
|
||||
"required": ["class", "rating", "enabled", "priority"],
|
||||
"properties": {
|
||||
"class": { "type": "integer", "minimum": 2, "maximum": 8 },
|
||||
"rating": { "$ref": "#/definitions/standardRatings" },
|
||||
"enabled": { "type": "boolean" },
|
||||
"priority": { "type": "integer", "minimum": 1, "maximum": 5 },
|
||||
"name": {
|
||||
"description": "The name identifing the thrusters (if applicable), e.g. 'Enhanced Performance'",
|
||||
"type": "string"
|
||||
},
|
||||
"blueprint": { "type": "object" },
|
||||
"modifications": { "type": "object" }
|
||||
}
|
||||
},
|
||||
"frameShiftDrive": {
|
||||
"required": ["class", "rating", "enabled", "priority"],
|
||||
"properties": {
|
||||
"class": { "type": "integer", "minimum": 2, "maximum": 8 },
|
||||
"rating": { "$ref": "#/definitions/standardRatings" },
|
||||
"enabled": { "type": "boolean" },
|
||||
"priority": { "type": "integer", "minimum": 1, "maximum": 5 },
|
||||
"blueprint": { "type": "object" },
|
||||
"modifications": { "type": "object" }
|
||||
}
|
||||
},
|
||||
"lifeSupport": {
|
||||
"required": ["class", "rating", "enabled", "priority"],
|
||||
"properties": {
|
||||
"class": { "type": "integer", "minimum": 1, "maximum": 6 },
|
||||
"rating": { "$ref": "#/definitions/standardRatings" },
|
||||
"enabled": { "type": "boolean" },
|
||||
"priority": { "type": "integer", "minimum": 1, "maximum": 5 },
|
||||
"blueprint": { "type": "object" },
|
||||
"modifications": { "type": "object" }
|
||||
}
|
||||
},
|
||||
"powerDistributor": {
|
||||
"required": ["class", "rating", "enabled", "priority"],
|
||||
"properties": {
|
||||
"class": { "type": "integer", "minimum": 1, "maximum": 8 },
|
||||
"rating": { "$ref": "#/definitions/standardRatings" },
|
||||
"enabled": { "type": "boolean" },
|
||||
"priority": { "type": "integer", "minimum": 1, "maximum": 5 },
|
||||
"blueprint": { "type": "object" },
|
||||
"modifications": { "type": "object" }
|
||||
}
|
||||
},
|
||||
"sensors": {
|
||||
"required": ["class", "rating", "enabled", "priority"],
|
||||
"properties": {
|
||||
"class": { "type": "integer", "minimum": 1, "maximum": 8 },
|
||||
"rating": { "$ref": "#/definitions/standardRatings" },
|
||||
"enabled": { "type": "boolean" },
|
||||
"priority": { "type": "integer", "minimum": 1, "maximum": 5 },
|
||||
"blueprint": { "type": "object" },
|
||||
"modifications": { "type": "object" }
|
||||
}
|
||||
},
|
||||
"fuelTank": {
|
||||
"required": ["class", "rating", "enabled", "priority"],
|
||||
"properties": {
|
||||
"class": { "type": "integer", "minimum": 1, "maximum": 6 },
|
||||
"rating": { "$ref": "#/definitions/standardRatings" },
|
||||
"enabled": { "type": "boolean" },
|
||||
"priority": { "type": "integer", "minimum": 1, "maximum": 5 },
|
||||
"blueprint": { "type": "object" },
|
||||
"modifications": { "type": "object" }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"internal": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": ["object", "null"],
|
||||
"required": ["class", "rating", "enabled", "priority", "group"],
|
||||
"properties" : {
|
||||
"class": { "type": "integer", "minimum": 1, "maximum": 8 },
|
||||
"rating": { "$ref": "#/definitions/standardRatings" },
|
||||
"enabled": { "type": "boolean" },
|
||||
"priority": { "type": "integer", "minimum": 1, "maximum": 5 },
|
||||
"group": {
|
||||
"description": "The group of the component, e.g. 'Shield Generator', or 'Cargo Rack'",
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"description": "The name identifying the component (if applicable), e.g. 'Advance Discovery Scanner', or 'Detailed Surface Scanner'",
|
||||
"type": "string"
|
||||
},
|
||||
"blueprint": { "type": "object" },
|
||||
"modifications": { "type": "object" }
|
||||
}
|
||||
},
|
||||
"minItems": 3
|
||||
},
|
||||
"hardpoints": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": ["object", "null"],
|
||||
"required": ["class", "rating", "enabled", "priority", "group", "mount"],
|
||||
"properties" : {
|
||||
"class": { "type": "integer", "minimum": 1, "maximum": 4 },
|
||||
"rating": { "$ref": "#/definitions/allRatings" },
|
||||
"enabled": { "type": "boolean" },
|
||||
"priority": { "type": "integer", "minimum": 1, "maximum": 5 },
|
||||
"mount": { "type": "string", "enum": ["Fixed", "Gimballed", "Turret"] },
|
||||
"group": {
|
||||
"description": "The group of the component, e.g. 'Beam Laser', or 'Missile Rack'",
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"description": "The name identifing the component (if applicable), e.g. 'Retributor', or 'Mining Lance'",
|
||||
"type": "string"
|
||||
},
|
||||
"blueprint": { "type": "object" },
|
||||
"modifications": { "type": "object" }
|
||||
}
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
"utility": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": ["object", "null"],
|
||||
"required": ["class", "rating", "enabled", "priority", "group"],
|
||||
"properties" : {
|
||||
"class": { "type": "integer", "minimum": 0, "maximum": 0 },
|
||||
"rating": { "$ref": "#/definitions/allRatings" },
|
||||
"enabled": { "type": "boolean" },
|
||||
"priority": { "type": "integer", "minimum": 1, "maximum": 5 },
|
||||
"group": {
|
||||
"description": "The group of the component, e.g. 'Shield Booster', or 'Kill Warrant Scanner'",
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"description": "The name identifing the component (if applicable), e.g. 'Point Defence', or 'Electronic Countermeasure'",
|
||||
"type": "string"
|
||||
},
|
||||
"blueprint": { "type": "object" },
|
||||
"modifications": { "type": "object" }
|
||||
}
|
||||
},
|
||||
"minItems": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"stats": {
|
||||
"description": "Optional statistics from the build",
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
"properties": {
|
||||
"agility": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"armour": {
|
||||
"description": "Sum of base armour + any hull reinforcements",
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"armourAdded":{
|
||||
"description": "Armour added through Hull reinforcement",
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"baseShieldStrength": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"baseArmour": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"boost": {
|
||||
"description": "Maximum boost speed of the ships (4 pips, straight-line)",
|
||||
"type": "number",
|
||||
"minimum": 0
|
||||
},
|
||||
"cargoCapacity": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"class": {
|
||||
"description": "Ship Class/Size [Small, Medium, Large]",
|
||||
"enum": [1,2,3]
|
||||
},
|
||||
"totalDps": {
|
||||
"description": "Total damage dealt per second of all weapons",
|
||||
"type": "number",
|
||||
"minimum": 0
|
||||
},
|
||||
"totalEps": {
|
||||
"description": "Total energy consumed per second of all weapons",
|
||||
"type": "number",
|
||||
"minimum": 0
|
||||
},
|
||||
"totalHps": {
|
||||
"description": "Total heat generated per second of all weapons",
|
||||
"type": "number",
|
||||
"minimum": 0
|
||||
},
|
||||
"hullCost": {
|
||||
"description": "Cost of the ship's hull",
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"hullMass": {
|
||||
"description": "Mass of the Ship hull only",
|
||||
"type": "number",
|
||||
"minimum": 1
|
||||
},
|
||||
"hullExplRes": {
|
||||
"description": "Multiplier for explosive damage to hull",
|
||||
"type": "number"
|
||||
},
|
||||
"hullKinRes": {
|
||||
"description": "Multiplier for kinetic damage to hull",
|
||||
"type": "number"
|
||||
},
|
||||
"hullThermRes": {
|
||||
"description": "Multiplier for thermal damage to hull",
|
||||
"type": "number"
|
||||
},
|
||||
"fuelCapacity": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"fullTankRange": {
|
||||
"description": "Single Jump range with a full tank (unladenMass + fuel)",
|
||||
"type": "number",
|
||||
"minimum": 0
|
||||
},
|
||||
"ladenMass": {
|
||||
"description": "Mass of the Ship + fuel + cargo (hull + all components + fuel tank + cargo capacity)",
|
||||
"type": "number",
|
||||
"minimum": 1
|
||||
},
|
||||
"ladenRange": {
|
||||
"description": "Single Jump range with full cargo load, see ladenMass",
|
||||
"type": "number",
|
||||
"minimum": 0
|
||||
},
|
||||
"masslock": {
|
||||
"description": "Mass Lock Factor of the Ship",
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"shield": {
|
||||
"description": "Shield strength in Mega Joules (Mj)",
|
||||
"type": "number",
|
||||
"minimum": 0
|
||||
},
|
||||
"shieldExplRes": {
|
||||
"description": "Multiplier for explosive damage to shields",
|
||||
"type": "number"
|
||||
},
|
||||
"shieldKinRes": {
|
||||
"description": "Multiplier for kinetic damage to shields",
|
||||
"type": "number"
|
||||
},
|
||||
"shieldThermRes": {
|
||||
"description": "Multiplier for thermal damage to shields",
|
||||
"type": "number"
|
||||
},
|
||||
"speed": {
|
||||
"description": "Maximum speed of the ships (4 pips, straight-line)",
|
||||
"type": "number",
|
||||
"minimum": 1
|
||||
},
|
||||
"totalCost": {
|
||||
"description": "Total cost of the loadout, including discounts",
|
||||
"type": "number"
|
||||
},
|
||||
"unladenRange": {
|
||||
"description": "Single Jump range when unladen, see unladenMass",
|
||||
"type": "number",
|
||||
"minimum": 0
|
||||
},
|
||||
"unladenMass": {
|
||||
"description": "Mass of the Ship (hull + all components)",
|
||||
"type": "number",
|
||||
"minimum": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"standardRatings": { "enum": ["A", "B", "C", "D", "E", "F", "G", "H"] },
|
||||
"allRatings": { "enum": ["A", "B", "C", "D", "E", "F", "G", "H", "I" ] }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user