mirror of
https://github.com/EDCD/coriolis.git
synced 2025-12-09 14:45:35 +00:00
Compare commits
145 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c02f674530 | ||
|
|
89f2273b5d | ||
|
|
351b084ce9 | ||
|
|
dd48d2d007 | ||
|
|
63e850b5aa | ||
|
|
46775879f7 | ||
|
|
c9ba4e3fcb | ||
|
|
8b855b62a1 | ||
|
|
d6a687300c | ||
|
|
ddc968129d | ||
|
|
e364560ca7 | ||
|
|
baf6f8130a | ||
|
|
f8f5cd2581 | ||
|
|
8019453d0d | ||
|
|
6ad17595dc | ||
|
|
50e1f7d4df | ||
|
|
fdb931fe00 | ||
|
|
86efb59237 | ||
|
|
f05aa66e53 | ||
|
|
f4e95cd150 | ||
|
|
5798c0e8a6 | ||
|
|
22f98fa8a7 | ||
|
|
897668c0d4 | ||
|
|
99c0bfcee1 | ||
|
|
37bfc700e9 | ||
|
|
8fcebf59f6 | ||
|
|
486c055631 | ||
|
|
92246302b9 | ||
|
|
0d646c6193 | ||
|
|
217ea3c5ac | ||
|
|
fcf0494df6 | ||
|
|
20ba6eb822 | ||
|
|
0e2c0349e0 | ||
|
|
c49e2cff03 | ||
|
|
d79313bfbe | ||
|
|
fd404b5155 | ||
|
|
49e72146b4 | ||
|
|
9b534b62c8 | ||
|
|
0be59af9b0 | ||
|
|
5d87a6cd56 | ||
|
|
c61c17d465 | ||
|
|
029ba63aa5 | ||
|
|
99e9e0c76f | ||
|
|
5bbc6e1cbe | ||
|
|
1e5f66e528 | ||
|
|
cdb837a25a | ||
|
|
dd1175abf4 | ||
|
|
619976230d | ||
|
|
2cb0d5209b | ||
|
|
ad06e23afa | ||
|
|
3def84e435 | ||
|
|
7f377d6345 | ||
|
|
53137e0ae1 | ||
|
|
792eda2572 | ||
|
|
550c94fa94 | ||
|
|
2a841281d4 | ||
|
|
260f29834a | ||
|
|
ddb35d321c | ||
|
|
6017c1ecff | ||
|
|
05e06f30f5 | ||
|
|
fb5ba6a0b2 | ||
|
|
80656a7a78 | ||
|
|
ce980cf091 | ||
|
|
a4656e223a | ||
|
|
66d4b5ac4c | ||
|
|
6961469ae5 | ||
|
|
06f4abdf8b | ||
|
|
7855d0e171 | ||
|
|
40f213c883 | ||
|
|
be1bfeb6f3 | ||
|
|
b40a2e96e0 | ||
|
|
5f036c586c | ||
|
|
091789c819 | ||
|
|
0ce8bfac79 | ||
|
|
e53ffd0273 | ||
|
|
bb7db144d6 | ||
|
|
2e42a328e0 | ||
|
|
f82122f29f | ||
|
|
5bf907809d | ||
|
|
51d7b6c9aa | ||
|
|
b8cff0c2fc | ||
|
|
6ac69a6388 | ||
|
|
32282141cf | ||
|
|
059c2badf4 | ||
|
|
fb090618da | ||
|
|
9ed0e30538 | ||
|
|
af82b8ca1e | ||
|
|
6e18793d82 | ||
|
|
22e74164c5 | ||
|
|
93ba1bf67a | ||
|
|
46ed9003dd | ||
|
|
5603315bf0 | ||
|
|
5bbc6be3d8 | ||
|
|
203e9c7b46 | ||
|
|
2a6850ded0 | ||
|
|
041f873f97 | ||
|
|
b944035541 | ||
|
|
7c6a4fc5f8 | ||
|
|
5426b55637 | ||
|
|
a6a10df39c | ||
|
|
0dc58bad7e | ||
|
|
794faacbd4 | ||
|
|
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 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -5,3 +5,4 @@ build
|
|||||||
nginx.pid
|
nginx.pid
|
||||||
.idea
|
.idea
|
||||||
/bin
|
/bin
|
||||||
|
env
|
||||||
|
|||||||
126
ChangeLog.md
126
ChangeLog.md
@@ -1,4 +1,128 @@
|
|||||||
#2.2.x
|
#2.2.11
|
||||||
|
* Add help system and initial help file
|
||||||
|
* Make absolute damage visible
|
||||||
|
* Add 'average' roll for blueprints
|
||||||
|
* Update spacing for movement summary to make it more readable
|
||||||
|
* Provide damage dealt statistics for both shields and hull
|
||||||
|
* Damage dealt panel only shows enabled weapons
|
||||||
|
* Add engagement range to damage received panel
|
||||||
|
* Handle burst rate of fire as an absolute number rather than a perentage modification
|
||||||
|
* Ensure that clip values are always rounded up
|
||||||
|
* Ensure that focused weapon mod uses range modifier to increase falloff as well
|
||||||
|
* Use coriolis-data 2.2.11:
|
||||||
|
* Remove non-existent chaff launcher capacity blueprint grades
|
||||||
|
* Fix incorrect values for charge enhanced power distributor
|
||||||
|
* Remove incorrect AFMU blueprints
|
||||||
|
* Correct fragment cannon Double Shot blueprint information
|
||||||
|
* Correct Focused weapon blueprint information
|
||||||
|
|
||||||
|
#2.2.10
|
||||||
|
* Fix detailed export of module reinforcement packages
|
||||||
|
* Use damagedist for exact breakdown of weapons that have more than one type of damage
|
||||||
|
* Use new-style modification validity data
|
||||||
|
* Provide ability to select engineering blueprint and roll sample values for them
|
||||||
|
* Use coriolis-data 2.2.10:
|
||||||
|
* Fix incorrect base shield values for Cutter and Corvette
|
||||||
|
* Update weapons to have %-based damage distributions
|
||||||
|
* Remove power draw for detailed surface scanner - although shown in outfitting it is not part of active power
|
||||||
|
* Fix incorrect names for lightweight and kinetic armour
|
||||||
|
* Add engineering blueprints
|
||||||
|
|
||||||
|
#2.2.9
|
||||||
|
* Use SSL-enabled server for shortlinks
|
||||||
|
* Add falloff for weapons
|
||||||
|
* Use falloff when calculating weapon effectiveness in damage dealt
|
||||||
|
* Add engagement range slider to 'Damage Dealt' section to allow user to see change in weapon effectiveness with range
|
||||||
|
* Use better DPE calculation methodology
|
||||||
|
* Add total DPS and effectiveness information to 'Damage Dealt' section
|
||||||
|
* Use coriolis-data 2.2.9:
|
||||||
|
* Add falloff metric for weapons
|
||||||
|
* Add falloff from range modification
|
||||||
|
|
||||||
|
#2.2.8
|
||||||
|
* Fix issue where filling all internals with cargo racks would include restricted slots
|
||||||
|
* Use coriolis-data 2.2.8:
|
||||||
|
* Set military slot of Viper Mk IV to class 3; was incorrectly set as class 2
|
||||||
|
* Update base regeneration rate of prismatic shield generators to values in 2.2.03
|
||||||
|
* Update specials with information in 2.2.03
|
||||||
|
|
||||||
|
#2.2.7
|
||||||
|
* Fix resistance diminishing return calculations
|
||||||
|
* Do not allow -100% to be entered as a modification value
|
||||||
|
|
||||||
|
#2.2.6
|
||||||
|
* Add pitch/roll/yaw information
|
||||||
|
* Use combination of pitch, roll and yaw to provide a more useful agility metric
|
||||||
|
* Add movement summary to outfitting page
|
||||||
|
* Add standard internal class sizes to shipyard page
|
||||||
|
* Fix issue when importing Viper Mk IV
|
||||||
|
* Ensure ordering of all types of modules (standard, internal, utilities) is consistent
|
||||||
|
* Add rebuilds per bay information for fighter hangars
|
||||||
|
* Add ability to show military compartments
|
||||||
|
* Show module reinforcement package results in defence summary
|
||||||
|
* Use separate speed/rotation/acceleration multipliers for thrusters if available
|
||||||
|
* Obey restricted slot rules when adding all for internal slots
|
||||||
|
* Version URLs to handle changes to ship specifications over time
|
||||||
|
* Do not include disabled shield boosters in calculations
|
||||||
|
* Add 'Damage dealt' section
|
||||||
|
* Add 'Damage received' section
|
||||||
|
* Add 'Piercing' information to hardpoints
|
||||||
|
* Add 'Hardness' information to ship summary
|
||||||
|
* Add module copy functionality - drag module whilst holding 'alt' to copy
|
||||||
|
* Add base resistances to defence summary tooltip
|
||||||
|
* Update shield recovery/regeneration calculations
|
||||||
|
* Pin menu to top of page
|
||||||
|
* Switch to custom shortlink method to avoid google length limitations
|
||||||
|
* Ensure that information is not lost on narrow screens
|
||||||
|
* Do not lose ship selector selection on narrow screens
|
||||||
|
* Reinstate jump range graph
|
||||||
|
* Use coriolis-data 2.2.6:
|
||||||
|
* Update weapons with changed values for 2.2.03
|
||||||
|
* Add individual pitch/roll/yaw statistics for each ship
|
||||||
|
* Remove old and meaningless agility stat
|
||||||
|
* Use sane order for multi-module JSON - coriolis can re-order as it sees fit when displaying modules
|
||||||
|
* Fix cost of fighter hangars
|
||||||
|
* Update Powerplay weapons with current statistics
|
||||||
|
* Add separate min/opt/max multipliers for enhanced thrusters for speed, acceleration and rotation
|
||||||
|
* Add module reinforcement packages
|
||||||
|
* Add military compartments
|
||||||
|
* Fix missing damage value for 2B dumbfires
|
||||||
|
* Update shield recharge rates
|
||||||
|
* Reduce hull mass of Viper to 50T
|
||||||
|
* Fix incorrect optimal mass value for 8A thrusters
|
||||||
|
* Add power draw for detailed surface scanner
|
||||||
|
|
||||||
|
#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
|
* Update DPS/HPS/EPS in real-time as modifiers change
|
||||||
* Use coriolis-data 2.2.2:
|
* Use coriolis-data 2.2.2:
|
||||||
* Add distributor draw modifier to shield generators
|
* Add distributor draw modifier to shield generators
|
||||||
|
|||||||
30
__tests__/fixtures/agility-data.json
Normal file
30
__tests__/fixtures/agility-data.json
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"adder": {
|
||||||
|
"t3": {"speed": 205, "boost": 298, "pitch": 35.37, "roll": 93.09, "yaw": 13.03},
|
||||||
|
"t2": {"speed": 209, "boost": 304, "pitch": 36.06, "roll": 94.90, "yaw": 13.29},
|
||||||
|
"t1": {"speed": 213, "boost": 310, "pitch": 36.80, "roll": 96.84, "yaw": 13.56},
|
||||||
|
"t0": {"speed": 218, "boost": 317, "pitch": 37.70, "roll": 99.20, "yaw": 13.89},
|
||||||
|
"t9": {"speed": 220, "boost": 321, "pitch": 38.08, "roll": 100.21, "yaw": 14.03},
|
||||||
|
"t8": {"speed": 225, "boost": 327, "pitch": 38.86, "roll": 102.26, "yaw": 14.32},
|
||||||
|
"t7": {"speed": 230, "boost": 334, "pitch": 39.69, "roll": 104.44, "yaw": 14.62},
|
||||||
|
"t6": {"speed": 234, "boost": 340, "pitch": 40.41, "roll": 106.34, "yaw": 14.89},
|
||||||
|
"t5": {"speed": 242, "boost": 351, "pitch": 41.71, "roll": 109.78, "yaw": 15.37}
|
||||||
|
},
|
||||||
|
"eagle": {
|
||||||
|
"t2": {"speed": 223, "boost": 325, "pitch": 46.45, "roll": 111.48, "yaw": 16.72},
|
||||||
|
"t1": {"speed": 229, "boost": 334, "pitch": 47.69, "roll": 114.46, "yaw": 17.17},
|
||||||
|
"t0": {"speed": 235, "boost": 343, "pitch": 49.00, "roll": 117.60, "yaw": 17.64},
|
||||||
|
"t9": {"speed": 239, "boost": 349, "pitch": 49.80, "roll": 119.53, "yaw": 17.93},
|
||||||
|
"t8": {"speed": 243, "boost": 355, "pitch": 50.70, "roll": 121.69, "yaw": 18.25},
|
||||||
|
"t7": {"speed": 248, "boost": 361, "pitch": 51.62, "roll": 123.89, "yaw": 18.58},
|
||||||
|
"t6": {"speed": 252, "boost": 367, "pitch": 52.46, "roll": 125.91, "yaw": 18.89},
|
||||||
|
"t5": {"speed": 259, "boost": 378, "pitch": 53.99, "roll": 129.56, "yaw": 19.43}
|
||||||
|
},
|
||||||
|
"hauler": {
|
||||||
|
"t4": {"speed": 203, "boost": 305, "pitch": 36.61, "roll": 101.71, "yaw": 14.24},
|
||||||
|
"t3": {"speed": 209, "boost": 314, "pitch": 37.63, "roll": 104.54, "yaw": 14.64},
|
||||||
|
"t2": {"speed": 216, "boost": 324, "pitch": 38.89, "roll": 108.03, "yaw": 15.12},
|
||||||
|
"t1": {"speed": 222, "boost": 333, "pitch": 39.97, "roll": 111.02, "yaw": 15.54},
|
||||||
|
"t0": {"speed": 232, "boost": 348, "pitch": 41.76, "roll": 116.00, "yaw": 16.24}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
"priority": 1,
|
"priority": 1,
|
||||||
"modifications": {
|
"modifications": {
|
||||||
"pgen": 1000
|
"pgen": 1000
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"thrusters": {
|
"thrusters": {
|
||||||
"class": 6,
|
"class": 6,
|
||||||
@@ -223,6 +223,7 @@
|
|||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
{
|
{
|
||||||
"class": 4,
|
"class": 4,
|
||||||
"rating": "E",
|
"rating": "E",
|
||||||
@@ -261,53 +262,64 @@
|
|||||||
"topSpeed": 186.5,
|
"topSpeed": 186.5,
|
||||||
"boost": 240,
|
"boost": 240,
|
||||||
"boostEnergy": 27,
|
"boostEnergy": 27,
|
||||||
"topBoost": 248.62,
|
"topBoost": 249.34,
|
||||||
"topSpeed": 186.46,
|
"topPitch": 25.97,
|
||||||
|
"topRoll": 62.34,
|
||||||
|
"topYaw": 10.39,
|
||||||
|
"topSpeed": 187.01,
|
||||||
"totalCost": 882362058,
|
"totalCost": 882362058,
|
||||||
"totalDpe": 127.26,
|
"totalDpe": 142.68,
|
||||||
"totalDps": 97.74,
|
"totalDps": 103.8,
|
||||||
"totalEps": 22.71,
|
"totalEps": 22.71,
|
||||||
"totalHps": 677.29,
|
"totalHps": 677.29,
|
||||||
"totalExplDpe": 0,
|
"totalExplDpe": 0,
|
||||||
"totalExplDps": 0,
|
"totalExplDps": 0,
|
||||||
"totalExplSDps": 0,
|
"totalExplSDps": 0,
|
||||||
"totalHps": 33.28,
|
"totalAbsDpe": 3.57,
|
||||||
"totalKinDpe": 103.97,
|
"totalAbsDps": 18.78,
|
||||||
"totalKinDps": 28.92,
|
"totalAbsSDps": 14.45,
|
||||||
"totalKinSDps": 21.23,
|
"totalHps": 33.62,
|
||||||
"totalSDps": 85.77,
|
"totalKinDpe": 117.48,
|
||||||
"totalThermDpe": 23.29,
|
"totalKinDps": 24.94,
|
||||||
"totalThermDps": 68.82,
|
"totalKinSDps": 18.76,
|
||||||
"totalThermSDps": 64.53,
|
"totalSDps": 91.84,
|
||||||
"agility": 2,
|
"totalThermDpe": 21.63,
|
||||||
|
"totalThermDps": 60.08,
|
||||||
|
"totalThermSDps": 58.64,
|
||||||
"baseShieldStrength": 350,
|
"baseShieldStrength": 350,
|
||||||
"baseArmour": 945,
|
"baseArmour": 945,
|
||||||
"hullExplRes": 0.78,
|
"hullExplRes": 0.22,
|
||||||
"hullKinRes": 0.73,
|
"hullKinRes": 0.27,
|
||||||
"hullMass": 400,
|
"hullMass": 400,
|
||||||
"hullThermRes": 1.37,
|
"hullThermRes": -0.36,
|
||||||
"masslock": 23,
|
"masslock": 23,
|
||||||
"pipSpeed": 0.14,
|
"pipSpeed": 0.14,
|
||||||
|
"pitch": 25,
|
||||||
"moduleCostMultiplier": 1,
|
"moduleCostMultiplier": 1,
|
||||||
|
"modulearmour": 0,
|
||||||
|
"moduleprotection": 0,
|
||||||
"fuelCapacity": 32,
|
"fuelCapacity": 32,
|
||||||
"cargoCapacity": 128,
|
"cargoCapacity": 128,
|
||||||
"ladenMass": 1339.2,
|
"ladenMass": 1323.2,
|
||||||
"armour": 2227.5,
|
"armour": 2227.5,
|
||||||
"baseArmour": 525,
|
"baseArmour": 525,
|
||||||
"unladenMass": 1179.2,
|
"unladenMass": 1163.2,
|
||||||
"powerAvailable": 39.6,
|
"powerAvailable": 39.6,
|
||||||
"powerRetracted": 23.33,
|
"powerRetracted": 23.33,
|
||||||
"powerDeployed": 34.76,
|
"powerDeployed": 34.13,
|
||||||
"unladenRange": 18.49,
|
"roll": 60,
|
||||||
"fullTankRange": 18.12,
|
"unladenRange": 18.74,
|
||||||
"ladenRange": 16.39,
|
"yaw": 10,
|
||||||
"unladenFastestRange": 73.21,
|
"fullTankRange": 18.36,
|
||||||
"ladenFastestRange": 66.15,
|
"hardness": 65,
|
||||||
|
"ladenRange": 16.59,
|
||||||
|
"unladenFastestRange": 74.2,
|
||||||
|
"ladenFastestRange": 66.96,
|
||||||
"maxJumpCount": 4,
|
"maxJumpCount": 4,
|
||||||
"shield": 833,
|
"shield": 833,
|
||||||
"shieldCells": 1840,
|
"shieldCells": 1840,
|
||||||
"shieldExplRes": 0.5,
|
"shieldExplRes": 0.5,
|
||||||
"shieldKinRes": 0.6,
|
"shieldKinRes": 0.4,
|
||||||
"shieldThermRes": 1.2
|
"shieldThermRes": -0.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
|
||||||
|
}
|
||||||
|
}
|
||||||
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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,50 +1,50 @@
|
|||||||
{
|
{
|
||||||
"type_6_transporter": {
|
"type_6_transporter": {
|
||||||
"Cargo": "0p0tdFal8d8s8f4-----04040303430101.Iw1-kA==.Aw1-kA==.",
|
"Cargo": "A0p0tdFal8d8s8f4-----04040303430101.Iw1/kA==.Aw1/kA==.",
|
||||||
"Miner": "0p5tdFal8d8s8f42l2l---040403451q0101.Iw1-kA==.Aw1-kA==.",
|
"Miner": "A0p5tdFal8d8s8f42l2l---040403451q0101.Iw1/kA==.Aw1/kA==.",
|
||||||
"Hopper": "0p0tdFal8d0s8f41717---030302024300-.Iw1-kA==.Aw1-kA==."
|
"Hopper": "A0p0tdFal8d0s8f41717---030302024300-.Iw1/kA==.Aw1/kA==."
|
||||||
},
|
},
|
||||||
"type_7_transport": {
|
"type_7_transport": {
|
||||||
"Cargo": "0p0tiFfliddsdf5--------0505040403480101.Iw18aQ==.Aw18aQ==.",
|
"Cargo": "A0p0tiFfliddsdf5--------0505040403480101.Iw18aQ==.Aw18aQ==.",
|
||||||
"Miner": "0pdtiFflid8sdf5--2l2l----0505041v03450000.Iw18aQ==.Aw18aQ==."
|
"Miner": "A0pdtiFflid8sdf5--2l2l----0505041v03450000.Iw18aQ==.Aw18aQ==."
|
||||||
},
|
},
|
||||||
"federal_dropship": {
|
"federal_dropship": {
|
||||||
"Cargo": "0pdtiFflnddsif4-1717------05040448020201.Iw18aQ==.Aw18aQ==."
|
"Cargo": "A0pdtiFflnddsif4-1717------05040448--020201.Iw18eQ==.Aw18eQ==."
|
||||||
},
|
},
|
||||||
"asp": {
|
"asp": {
|
||||||
"Miner": "2pftfFflidfskf50s0s24242l2l---04054a1q02022o27.Iw18WQ==.Aw18WQ==."
|
"Miner": "A2pftfFflidfskf50s0s24242l2l---04054a1q02022o27.Iw18WQ==.Aw18WQ==."
|
||||||
},
|
},
|
||||||
"imperial_clipper": {
|
"imperial_clipper": {
|
||||||
"Cargo": "0p5tiFflndisnf4--0s0s----0605450302020101.Iw18aQ==.Aw18aQ==.",
|
"Cargo": "A0p5tiFflndisnf4--0s0s----0605450302020101.Iw18aQ==.Aw18aQ==.",
|
||||||
"Dream": "2pktkFflndpskf40v0v0s0s0404040n4k5n5d2b29292o-.Iw18aQ==.Aw18aQ==.",
|
"Dream": "A2pktkFflndpskf40v0v0s0s0404040n4k5n5d2b29292o-.AwRj4yWU1I==.CwBhCYy6YRigzLIA.",
|
||||||
"Current": "0patkFflndfskf4----------------.Iw18aQ==.Aw18aQ==."
|
"Current": "A0patkFflndfskf4----------------.AwRj4yWU1I==.CwBhCYy6YRigzLIA."
|
||||||
},
|
},
|
||||||
"type_9_heavy": {
|
"type_9_heavy": {
|
||||||
"Current": "0patsFklndnsif6---------0706054a0303020224.Iw18eQ==.Aw18eQ==."
|
"Current": "A0patsFklndnsif6---------0706054a0303020224.AwRj4yoo.EwBhEYy6dsg=."
|
||||||
},
|
},
|
||||||
"python": {
|
"python": {
|
||||||
"Cargo": "0patnFflidsssf5---------050505040448020201.Iw18eQ==.Aw18eQ==.",
|
"Cargo": "A0patnFflidsssf5---------050505040448020201.Iw18eQ==.Aw18eQ==.",
|
||||||
"Miner": "0pktkFflidpspf50v0v0v2m2m0404--050505Ce4a1v02022o.Iw18eQ==.Aw18eQ==.",
|
"Miner": "A0pktkFflidpspf50v0v0v2m2m0404--050505Ce4a1v02022o.Iw18eQ==.IwBhBYy6dkCYg===.",
|
||||||
"Dream": "2pptkFfliduspf50v0v0v27270404040m5n5n4f2d2d032t0201.Iw18eQ==.Aw18eQ==.",
|
"Dream": "A2pptkFfliduspf50v0v0v27270404040m5n5n4f2d2d032t0201.Iw1+gDBxA===.EwBhEYy6e0WEA===.",
|
||||||
"Missile": "0pttoFjljdystf52f2g2d2ePh----04044j03---002h.Iw18eQ==.Aw18eQ==."
|
"Missile": "A0pttoFjljdystf52f2g2d2ePh----04044j03---002h.Iw18eQ==.Aw18eQ==."
|
||||||
},
|
},
|
||||||
"anaconda": {
|
"anaconda": {
|
||||||
"Dream": "4putpFklndzsuf52c0o0o0o1m1m0q0q0404040l0b0100004k5n5n112d2d040303326b.Iw18ZlA=.Aw18ZlA=.",
|
"Dream": "A4putpFklndzsuf52c0o0o0o1m1m0q0q0404040l0b0100004k5n5n112d2d04-0303326b.AwRj4yo5dyg=.MwBhCYy6duvARiA=.",
|
||||||
"Cargo": "0patnFklndnsxf5----------------0605050504040445030301.Iw18ZlA=.Aw18ZlA=.",
|
"Cargo": "A0patnFklndnsxf5----------------06050505040404-45030301.Iw18ZVA=.Aw18ZVA=.",
|
||||||
"Current": "0patnFklndksxf5----------------0605050504040403034524.Iw18ZlA=.Aw18ZlA=.",
|
"Current": "A0patnFklndksxf5----------------06050505040404-03034524.Iw18ZVA=.Aw18ZVA=.",
|
||||||
"Explorer": "0patnFklndksxf5--------0202------f7050505040s372f2i4524.Iw18ZlA=.Aw18ZlA=.",
|
"Explorer": "A0patnFklndksxf5--------0202------f7050505040s37-2f2i4524.AwRj4yVKJ9hA.AwhMIyumQRhEA===.",
|
||||||
"Test": "4putkFklkdzsuf52c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.Iw18ZlA=.Aw18ZlA=."
|
"Test": "A4putkFklkdzsuf52c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04---0303326b.Iw18ZVA=.Aw18ZVA=."
|
||||||
},
|
},
|
||||||
"diamondback_explorer": {
|
"diamondback_explorer": {
|
||||||
"Explorer": "0p0tdFfldddsdf5---0202--320p432i2f.Iw1-kA==.Aw1-kA==."
|
"Explorer": "A0p0tdFfldddsdf5---0202--320p432i2f.AwRj4zTI.AwiMIypI."
|
||||||
},
|
},
|
||||||
"vulture": {
|
"vulture": {
|
||||||
"Bounty Hunter": "3patcFalddksff31e1e0404-0l4a5d27662j.Iw19kA==.Aw19kA==."
|
"Bounty Hunter": "A3patcFalddksff31e1e0404-0l4a-5d27662j.AwRj4z2I.MwBhBYy6oJmAjLIA."
|
||||||
},
|
},
|
||||||
"fer_de_lance": {
|
"fer_de_lance": {
|
||||||
"Attack": "2pfthFalidpsff31r0s0s0s0s000404-04-4a-5d27-.Iw18aQ==.Aw18aQ==."
|
"Attack": "A2pfthFalidpsff31r0s0s0s0s000404-04-4a-5d27-.Iw18aQ==.CwBhrSu8EZyA."
|
||||||
},
|
},
|
||||||
"eagle": {
|
"eagle": {
|
||||||
"Figther": "4p0t5F5l3d5s5f20p0p24-40532j-.Iw1-EA==.Aw1-EA==."
|
"Figther": "A4p0t5F5l3d5s5f20p0p24-4053-2j-.Iw18kA==.Aw18kA==."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
90
__tests__/test-agility.js
Normal file
90
__tests__/test-agility.js
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
import Ship from '../src/app/shipyard/Ship';
|
||||||
|
import { Ships } from 'coriolis-data/dist';
|
||||||
|
import * as ModuleUtils from '../src/app/shipyard/ModuleUtils';
|
||||||
|
|
||||||
|
describe("Agility", function() {
|
||||||
|
|
||||||
|
it("correctly calculates speed", function() {
|
||||||
|
let agilityData = require('./fixtures/agility-data');
|
||||||
|
|
||||||
|
for (let shipId in agilityData) {
|
||||||
|
for (let thrusterId in agilityData[shipId]) {
|
||||||
|
const thrusterData = agilityData[shipId][thrusterId];
|
||||||
|
let shipData = Ships[shipId];
|
||||||
|
let ship = new Ship(shipId, shipData.properties, shipData.slots);
|
||||||
|
ship.buildWith(shipData.defaults);
|
||||||
|
ship.use(ship.standard[1], ModuleUtils.findModule('t', thrusterId));
|
||||||
|
|
||||||
|
expect(Math.round(ship.topSpeed)).toBe(thrusterData.speed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("correctly calculates boost", function() {
|
||||||
|
let agilityData = require('./fixtures/agility-data');
|
||||||
|
|
||||||
|
for (let shipId in agilityData) {
|
||||||
|
for (let thrusterId in agilityData[shipId]) {
|
||||||
|
const thrusterData = agilityData[shipId][thrusterId];
|
||||||
|
let shipData = Ships[shipId];
|
||||||
|
let ship = new Ship(shipId, shipData.properties, shipData.slots);
|
||||||
|
ship.buildWith(shipData.defaults);
|
||||||
|
// Turn off internals to ensure we have enough power to boost
|
||||||
|
for (let internal in ship.internal) {
|
||||||
|
ship.internal[internal].enabled = 0;
|
||||||
|
}
|
||||||
|
ship.use(ship.standard[1], ModuleUtils.findModule('t', thrusterId));
|
||||||
|
|
||||||
|
expect(Math.round(ship.topBoost)).toBe(thrusterData.boost);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("correctly calculates pitch", function() {
|
||||||
|
let agilityData = require('./fixtures/agility-data');
|
||||||
|
|
||||||
|
for (let shipId in agilityData) {
|
||||||
|
for (let thrusterId in agilityData[shipId]) {
|
||||||
|
const thrusterData = agilityData[shipId][thrusterId];
|
||||||
|
let shipData = Ships[shipId];
|
||||||
|
let ship = new Ship(shipId, shipData.properties, shipData.slots);
|
||||||
|
ship.buildWith(shipData.defaults);
|
||||||
|
ship.use(ship.standard[1], ModuleUtils.findModule('t', thrusterId));
|
||||||
|
|
||||||
|
expect(Math.round(ship.pitches[4] * 100) / 100).toBeCloseTo(thrusterData.pitch, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("correctly calculates roll", function() {
|
||||||
|
let agilityData = require('./fixtures/agility-data');
|
||||||
|
|
||||||
|
for (let shipId in agilityData) {
|
||||||
|
for (let thrusterId in agilityData[shipId]) {
|
||||||
|
const thrusterData = agilityData[shipId][thrusterId];
|
||||||
|
let shipData = Ships[shipId];
|
||||||
|
let ship = new Ship(shipId, shipData.properties, shipData.slots);
|
||||||
|
ship.buildWith(shipData.defaults);
|
||||||
|
ship.use(ship.standard[1], ModuleUtils.findModule('t', thrusterId));
|
||||||
|
|
||||||
|
expect(Math.round(ship.rolls[4] * 100) / 100).toBeCloseTo(thrusterData.roll, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("correctly calculates yaw", function() {
|
||||||
|
let agilityData = require('./fixtures/agility-data');
|
||||||
|
|
||||||
|
for (let shipId in agilityData) {
|
||||||
|
for (let thrusterId in agilityData[shipId]) {
|
||||||
|
const thrusterData = agilityData[shipId][thrusterId];
|
||||||
|
let shipData = Ships[shipId];
|
||||||
|
let ship = new Ship(shipId, shipData.properties, shipData.slots);
|
||||||
|
ship.buildWith(shipData.defaults);
|
||||||
|
ship.use(ship.standard[1], ModuleUtils.findModule('t', thrusterId));
|
||||||
|
|
||||||
|
expect(Math.round(ship.yaws[4] * 100) / 100).toBeCloseTo(thrusterData.yaw, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -142,12 +142,12 @@ describe('Import Modal', function() {
|
|||||||
expect(modal.state.singleBuild).toBe(true);
|
expect(modal.state.singleBuild).toBe(true);
|
||||||
clickProceed();
|
clickProceed();
|
||||||
expect(MockRouter.go.mock.calls.length).toBe(1);
|
expect(MockRouter.go.mock.calls.length).toBe(1);
|
||||||
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/anaconda/4putkFklkdzsuf52c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.AwRj4zNKqA==.CwBhCYzBGW9qCTSqs5xA.?bn=Test%20My%20Ship');
|
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/anaconda?code=A4putkFklkdzsuf52c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04---0303326b.AwRj4zNLaA%3D%3D.CwBhCYzBGW9qCTSqq5xA.&bn=Test%20My%20Ship');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('catches an invalid build', function() {
|
it('catches an invalid build', function() {
|
||||||
const importData = require('./fixtures/anaconda-test-detailed-export-v3');
|
const importData = require('./fixtures/anaconda-test-detailed-export-v3');
|
||||||
pasteText(JSON.stringify(importData).replace('components', 'comps'));
|
pasteText(JSON.stringify(importData).replace('references', 'refs'));
|
||||||
|
|
||||||
expect(modal.state.importValid).toBeFalsy();
|
expect(modal.state.importValid).toBeFalsy();
|
||||||
expect(modal.state.errorMsg).toEqual('Anaconda Build "Test My Ship": Invalid data');
|
expect(modal.state.errorMsg).toEqual('Anaconda Build "Test My Ship": Invalid data');
|
||||||
@@ -167,7 +167,36 @@ describe('Import Modal', function() {
|
|||||||
expect(modal.state.singleBuild).toBe(true);
|
expect(modal.state.singleBuild).toBe(true);
|
||||||
clickProceed();
|
clickProceed();
|
||||||
expect(MockRouter.go.mock.calls.length).toBe(1);
|
expect(MockRouter.go.mock.calls.length).toBe(1);
|
||||||
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/anaconda/4putkFklkdzsuf52c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.AwRj4zNKqA==.CwBhCYzBGW9qCTSqs5xA.H4sIAAAAAAAAA2MUe8HMwPD-PwDDhxeuCAAAAA==?bn=Test%20My%20Ship');
|
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/anaconda?code=A4putkFklkdzsuf52c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04---0303326b.AwRj4zNLaA%3D%3D.CwBhCYzBGW9qCTSqq5xA.H4sIAAAAAAAAA2MUe8HMwPD%2FPwMcAABTINwTEgAAAA%3D%3D&bn=Test%20My%20Ship');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
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=A0pftiFflfddsnf5------020202033c044002v62f2i.AwRj4yvI.CwRgDBldHnJA.H4sIAAAAAAAAA2P858DAwPCXEUhwHPvx%2F78YG5AltB7I%2F8%2F0TwImJboDSPJ%2F%2B%2Ff%2Fv%2FKlX%2F%2F%2Fi3AwMTBIfARK%2FGf%2BJwVSxArStVAYqOjvz%2F%2F%2FJVo5GRhE2IBc4SKQSSz%2FDGEmCa398P8%2F%2F2%2BgTf%2F%2FAwDFxwtofAAAAA%3D%3D&bn=Multi-purpose%20Asp%20Explorer');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('imports a valid v4 build with modifications', function() {
|
||||||
|
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=A0patzF5l0das8f31a1a270202000e402t0101-2f.AwRj4zKA.CwRgDBldLiQ%3D.H4sIAAAAAAAAA12OP0tCYRjFj9fuVbvF1du9ekkT8s%2FkIg4NElyIBBd321yaGvwUQTS3N7UFfYygIT9EoyQUJA36ns47XJCWA%2B%2Fz%2Bz3Pe3ImBbDNKaqNPSBoGrL4ngfomKpFGiJ%2BLgHteR1IPjxJT5pF11uSeXNsJVcRfgdC92syWUuK0iMdKZqrjJ%2F0aoA71lJ5oKf38knWcCiptCPdhJIerdS00vlK0qktlqoj983UmqqHjQ33VsW8eazFmaTyULP2hQ4lX8LBme6g%2F6v0TTdbxJ2KhdEIaCw15MF%2FNB0L%2BS2hwEwyFM8KgP%2BqEpWWA3Qu9Z3z9kPWHzakt7Dt%2BAeD7ghSTgEAAA%3D%3D&bn=Multi-purpose%20Imperial%20Courier');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -209,26 +238,38 @@ describe('Import Modal', function() {
|
|||||||
expect(modal.state.singleBuild).toBe(true);
|
expect(modal.state.singleBuild).toBe(true);
|
||||||
clickProceed();
|
clickProceed();
|
||||||
expect(MockRouter.go.mock.calls.length).toBe(1);
|
expect(MockRouter.go.mock.calls.length).toBe(1);
|
||||||
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/federal_corvette/2putsFklndzsxf50x0x7l28281919040404040402020l06p05sf63c5ifhv66g2f.AwRj4zNaKA==.CwRgDBldUExuBiQqA===.H4sIAAAAAAAAAx2Rzy4DURTGz7TuzHRu47ZjWreKlg5iQ9KFZ9CENyBWtWo8gIUFsamteAIJi0qEWIhdN11ZEN1IwyNYVKRpcXzH5su553f_XyfvKiLTYma-TkScyHVcokoYEdmbBNDsiDla-WUOT5LgyfAshHdvyGyjFFHUQCSrBU8TLT4gYq4DNL_LhNTFN3PwiqdZQyX2C-sekep-Mrs1RIbnDppsIogD1UAtN7JEM9eIzZg8hmhsEU32gFmrdgB_UARvjYEr4QMUMffoxGnV-M8X3hZ_lAO-gmWq2Eq2IVtDOzZ2Hbbuws6KxCKmKUUydgRb3woSiUXMs6Cs7Qt6FCQSi5hxkNKhj6qhfcPU_kU4wYrFMseSOmFXMKbuwZsViUWMlq1sbhvJ_lKyfqTqEJGJyoC5eIpU9x2TRnUswYXyF77BW4Z3qQuv05GDTpfvcDzvSbxJ5DtV_aHS1I4clyB2A5_b-pAL8x_enn626gEAAA==?bn=Imported%20Federal%20Corvette');
|
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/federal_corvette?code=A2putsFklndzsxf50x0x7l28281919040404040402020l06p05sf63c5ifr--v66g2f.AwRj4zNaqA%3D%3D.CwRgDBldUExuBiIlUA%3D%3D.H4sIAAAAAAAAA02Svy9DURTHT1vvtfoat32eekVV9fm1kBgwSIw0YWYgBqmpMZkMBomFVfwFEoZKhBjE1qWTgegiDX%2BCQdKI1j2%2BR%2FJ4yzfnvu%2FnfO%2B979yQXiCi7xAkbRpEqsLMsRKWHNZpsSKQnppJVLAdIvc6DGiwxexMaWb7GDZHdJ%2BQaCf71Ia%2F88XsOp1EThk9bOh5P2kkahGN3qPM1wANbyOk87zNHH%2FBUs0gnWN61T9TOwfJ7EWJjMcms1lEo30Gx11BD8f1mh%2FcTkCMMvY0HZcoe4Wk5By%2BFcrrRL0N0OOlrd0Ntv57jGoc%2BH4%2F8EqHj3%2FCUXc4FicC5NFvsJBVIWeFvESlpuXSuCS5RRyLlV70z%2B4uQaw6ypSIJ6KOJDgZgFpQ60YgEU9EPQmUCkAfAj0IJOKJqC4wuYMY9rQD5CuubT0LSag8qdShxHUHoElcyWrAT4l4IsoCw65e%2BRv5BqKtC0mSJu8LH8OFT%2Bb%2BE8SZb0CcEn4AZ3TRDx5q4l1EJ%2BCP1bEM1WSaAwH%2FFkOLPoofwTo0LY8nr7O%2B37cp4yWIu4zHlHiXGfMPmat5gqMCAAA%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=A0pktsFplCdpsnf70t0t2727270004040404043c4fmimlmm04mc0iv62i2f.AwRj4yukg%3D%3D%3D.CwRgDBldHi8IUA%3D%3D.H4sIAAAAAAAAA2P8Z8%2FAwPCXEUiIKTMxMPCv%2F%2Ff%2FP8cFIPGf6Z8YTEr0GjMDg%2FJWICERBOTzn%2Fn7%2F7%2FIO5Ai5n9SIEWsQEIoSxAolfbt%2F3%2BJPk4GBhE7YQYGYVmgcuVnf4Aq%2FwMAIrEcGGsAAAA%3D&bn=Imported%20Beluga%20Liner');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Import E:D Shipyard Builds', function() {
|
describe('Import E:D Shipyard Builds', function() {
|
||||||
|
|
||||||
it('imports a valid builds', function() {
|
// it('imports a valid build', function() {
|
||||||
const imports = require('./fixtures/ed-shipyard-import-valid');
|
// const imports = require('./fixtures/ed-shipyard-import-valid');
|
||||||
|
//
|
||||||
for (let i = 0; i < imports.length; i++ ) {
|
// for (let i = 0; i < imports.length; i++ ) {
|
||||||
reset();
|
// reset();
|
||||||
let fixture = imports[i];
|
// let fixture = imports[i];
|
||||||
pasteText(fixture.buildText);
|
// pasteText(fixture.buildText);
|
||||||
expect(modal.state.importValid).toBeTruthy();
|
// expect(modal.state.importValid).toBeTruthy();
|
||||||
expect(modal.state.errorMsg).toEqual(null);
|
// expect(modal.state.errorMsg).toEqual(null);
|
||||||
clickProceed();
|
// clickProceed();
|
||||||
expect(MockRouter.go.mock.calls.length).toBe(1);
|
// expect(MockRouter.go.mock.calls.length).toBe(1);
|
||||||
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/' + fixture.shipId + '/' + fixture.buildCode + '?bn=' + encodeURIComponent(fixture.buildName));
|
// expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/' + fixture.shipId + '?code=' + encodeURIComponent(fixture.buildCode) + '&bn=' + encodeURIComponent(fixture.buildName));
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
|
|
||||||
it('catches invalid builds', function() {
|
it('catches invalid builds', function() {
|
||||||
const imports = require('./fixtures/ed-shipyard-import-invalid');
|
const imports = require('./fixtures/ed-shipyard-import-invalid');
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "coriolis_shipyard",
|
"name": "coriolis_shipyard",
|
||||||
"version": "2.2.2",
|
"version": "2.2.11",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/EDCD/coriolis"
|
"url": "https://github.com/EDCD/coriolis"
|
||||||
@@ -31,6 +31,7 @@
|
|||||||
"jsx"
|
"jsx"
|
||||||
],
|
],
|
||||||
"automock": true,
|
"automock": true,
|
||||||
|
"bail": false,
|
||||||
"unmockedModulePathPatterns": [
|
"unmockedModulePathPatterns": [
|
||||||
"<rootDir>/node_modules/lodash",
|
"<rootDir>/node_modules/lodash",
|
||||||
"<rootDir>/node_modules/react",
|
"<rootDir>/node_modules/react",
|
||||||
|
|||||||
@@ -6,9 +6,12 @@ import Persist from './stores/Persist';
|
|||||||
|
|
||||||
import Header from './components/Header';
|
import Header from './components/Header';
|
||||||
import Tooltip from './components/Tooltip';
|
import Tooltip from './components/Tooltip';
|
||||||
|
import ModalExport from './components/ModalExport';
|
||||||
|
import ModalHelp from './components/ModalHelp';
|
||||||
import ModalImport from './components/ModalImport';
|
import ModalImport from './components/ModalImport';
|
||||||
|
import ModalPermalink from './components/ModalPermalink';
|
||||||
import * as CompanionApiUtils from './utils/CompanionApiUtils';
|
import * as CompanionApiUtils from './utils/CompanionApiUtils';
|
||||||
import { outfitURL } from './utils/UrlGenerators'
|
import { outfitURL } from './utils/UrlGenerators';
|
||||||
|
|
||||||
import AboutPage from './pages/AboutPage';
|
import AboutPage from './pages/AboutPage';
|
||||||
import NotFoundPage from './pages/NotFoundPage';
|
import NotFoundPage from './pages/NotFoundPage';
|
||||||
@@ -159,14 +162,25 @@ export default class Coriolis extends React.Component {
|
|||||||
this._hideModal();
|
this._hideModal();
|
||||||
this._closeMenu();
|
this._closeMenu();
|
||||||
break;
|
break;
|
||||||
|
case 72: // 'h'
|
||||||
|
if (e.ctrlKey || e.metaKey) { // CTRL/CMD + h
|
||||||
|
e.preventDefault();
|
||||||
|
this._showModal(<ModalHelp />);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case 73: // 'i'
|
case 73: // 'i'
|
||||||
if (e.ctrlKey || e.metaKey) { // CTRL/CMD + i
|
if (e.ctrlKey || e.metaKey) { // CTRL/CMD + i
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this._showModal(<ModalImport />);
|
this._showModal(<ModalImport />);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 101010: // 's'
|
case 76: // 'l'
|
||||||
if (e.ctrlKey || e.metaKey) { // CTRL/CMD + i
|
if (e.ctrlKey || e.metaKey) { // CTRL/CMD + l
|
||||||
|
e.preventDefault();
|
||||||
|
this._showModal(<ModalPermalink url={window.location.href}/>);
|
||||||
|
}
|
||||||
|
case 83: // 's'
|
||||||
|
if (e.ctrlKey || e.metaKey) { // CTRL/CMD + s
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.emitter.emit('command', 'save');
|
this.emitter.emit('command', 'save');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,8 +93,10 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
|||||||
let prevClass = null, prevRating = null;
|
let prevClass = null, prevRating = null;
|
||||||
let elems = [];
|
let elems = [];
|
||||||
|
|
||||||
for (let i = 0; i < modules.length; i++) {
|
const sortedModules = modules.sort(this._moduleOrder);
|
||||||
let m = modules[i];
|
|
||||||
|
for (let i = 0; i < sortedModules.length; i++) {
|
||||||
|
let m = sortedModules[i];
|
||||||
let mount = null;
|
let mount = null;
|
||||||
let disabled = m.maxmass && (mass + (m.mass ? m.mass : 0)) > m.maxmass;
|
let disabled = m.maxmass && (mass + (m.mass ? m.mass : 0)) > m.maxmass;
|
||||||
let active = mountedModule && mountedModule.id === m.id;
|
let active = mountedModule && mountedModule.id === m.id;
|
||||||
@@ -126,7 +128,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
|||||||
case 'T': mount = <MountTurret className={'lg'}/>; break;
|
case 'T': mount = <MountTurret className={'lg'}/>; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i > 0 && modules.length > 3 && m.class != prevClass && (m.rating != prevRating || m.mount) && m.grp != 'pa') {
|
if (i > 0 && sortedModules.length > 3 && m.class != prevClass && (m.rating != prevRating || m.mount) && m.grp != 'pa') {
|
||||||
elems.push(<br key={'b' + m.grp + i} />);
|
elems.push(<br key={'b' + m.grp + i} />);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,6 +203,46 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
|||||||
this.context.tooltip();
|
this.context.tooltip();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Order two modules suitably for display in module selection
|
||||||
|
* @param {Object} a the first module
|
||||||
|
* @param {Object} b the second module
|
||||||
|
* @return {int} -1 if the first module should go first, 1 if the second module should go first
|
||||||
|
*/
|
||||||
|
_moduleOrder(a, b) {
|
||||||
|
// Named modules go last
|
||||||
|
if (!a.name && b.name) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (a.name && !b.name) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
// Class ordered from highest (8) to lowest (1)
|
||||||
|
if (a.class < b.class) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (a.class > b.class) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// Mount type, if applicable
|
||||||
|
if (a.mount && b.mount && a.mount !== b.mount) {
|
||||||
|
if (a.mount === 'F' || (a.mount === 'G' && b.mount === 'T')) {
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Rating ordered from lowest (E) to highest (A)
|
||||||
|
if (a.rating < b.rating) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (a.rating > b.rating) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// Do not attempt to order by name at this point, as that mucks up the order of armour
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scroll to mounted (if it exists) module group on mount
|
* Scroll to mounted (if it exists) module group on mount
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ export default class ComparisonTable extends TranslatedComponent {
|
|||||||
* @return {React.Component} Table row
|
* @return {React.Component} Table row
|
||||||
*/
|
*/
|
||||||
_buildRow(build, facets, formats, units) {
|
_buildRow(build, facets, formats, units) {
|
||||||
let url = outfitURL(build.id, build.toString(), build.buildName)
|
let url = outfitURL(build.id, build.toString(), build.buildName);
|
||||||
let cells = [
|
let cells = [
|
||||||
<td key='s' className='tl'><Link href={url}>{build.name}</Link></td>,
|
<td key='s' className='tl'><Link href={url}>{build.name}</Link></td>,
|
||||||
<td key='bn' className='tl'><Link href={url}>{build.buildName}</Link></td>
|
<td key='bn' className='tl'><Link href={url}>{build.buildName}</Link></td>
|
||||||
|
|||||||
344
src/app/components/DamageDealt.jsx
Normal file
344
src/app/components/DamageDealt.jsx
Normal file
@@ -0,0 +1,344 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
|
import { Ships } from 'coriolis-data/dist';
|
||||||
|
import ShipSelector from './ShipSelector';
|
||||||
|
import { nameComparator } from '../utils/SlotFunctions';
|
||||||
|
import { CollapseSection, ExpandSection, MountFixed, MountGimballed, MountTurret } from './SvgIcons';
|
||||||
|
import Slider from '../components/Slider';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates an internationalization friendly weapon comparator that will
|
||||||
|
* sort by specified property (if provided) then by name/group, class, rating
|
||||||
|
* @param {function} translate Translation function
|
||||||
|
* @param {function} propComparator Optional property comparator
|
||||||
|
* @param {boolean} desc Use descending order
|
||||||
|
* @return {function} Comparator function for names
|
||||||
|
*/
|
||||||
|
export function weaponComparator(translate, propComparator, desc) {
|
||||||
|
return (a, b) => {
|
||||||
|
if (!desc) { // Flip A and B if ascending order
|
||||||
|
let t = a;
|
||||||
|
a = b;
|
||||||
|
b = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a property comparator is provided use it first
|
||||||
|
let diff = propComparator ? propComparator(a, b) : nameComparator(translate, a, b);
|
||||||
|
|
||||||
|
if (diff) {
|
||||||
|
return diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Property matches so sort by name / group, then class, rating
|
||||||
|
if (a.name === b.name && a.grp === b.grp) {
|
||||||
|
if(a.class == b.class) {
|
||||||
|
return a.rating > b.rating ? 1 : -1;
|
||||||
|
}
|
||||||
|
return a.class - b.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nameComparator(translate, a, b);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Damage against a selected ship
|
||||||
|
*/
|
||||||
|
export default class DamageDealt extends TranslatedComponent {
|
||||||
|
static PropTypes = {
|
||||||
|
ship: React.PropTypes.object.isRequired,
|
||||||
|
code: React.PropTypes.string.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
static DEFAULT_AGAINST = Ships['anaconda'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param {Object} props React Component properties
|
||||||
|
*/
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this._sort = this._sort.bind(this);
|
||||||
|
this._onShipChange = this._onShipChange.bind(this);
|
||||||
|
this._onCollapseExpand = this._onCollapseExpand.bind(this);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
predicate: 'n',
|
||||||
|
desc: true,
|
||||||
|
against: DamageDealt.DEFAULT_AGAINST,
|
||||||
|
expanded: false,
|
||||||
|
range: 0.1667,
|
||||||
|
maxRange: 6000
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the initial weapons state
|
||||||
|
*/
|
||||||
|
componentWillMount() {
|
||||||
|
const data = this._calcWeapons(this.props.ship, this.state.against, this.state.range * this.state.maxRange);
|
||||||
|
this.setState({ weapons: data.weapons, totals: data.totals });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the updated weapons state if our ship changes
|
||||||
|
* @param {Object} nextProps Incoming/Next properties
|
||||||
|
* @param {Object} nextContext Incoming/Next conext
|
||||||
|
* @return {boolean} Returns true if the component should be rerendered
|
||||||
|
*/
|
||||||
|
componentWillReceiveProps(nextProps, nextContext) {
|
||||||
|
if (nextProps.code != this.props.code) {
|
||||||
|
const data = this._calcWeapons(this.props.ship, this.state.against, this.state.range * this.state.maxRange);
|
||||||
|
this.setState({ weapons: data.weapons, totals: data.totals });
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the damage dealt by a ship
|
||||||
|
* @param {Object} ship The ship which will deal the damage
|
||||||
|
* @param {Object} against The ship against which damage will be dealt
|
||||||
|
* @param {Object} range The engagement range
|
||||||
|
* @return {boolean} Returns the per-weapon damage
|
||||||
|
*/
|
||||||
|
_calcWeapons(ship, against, range) {
|
||||||
|
// Tidy up the range so that it's to 4 decimal places
|
||||||
|
range = Math.round(10000 * range) / 10000;
|
||||||
|
|
||||||
|
// Track totals
|
||||||
|
let totals = {};
|
||||||
|
totals.effectivenessShields = 0;
|
||||||
|
totals.effectiveDpsShields = 0;
|
||||||
|
totals.effectiveSDpsShields = 0;
|
||||||
|
totals.effectivenessHull = 0;
|
||||||
|
totals.effectiveDpsHull = 0;
|
||||||
|
totals.effectiveSDpsHull = 0;
|
||||||
|
let totalDps = 0;
|
||||||
|
|
||||||
|
let weapons = [];
|
||||||
|
for (let i = 0; i < ship.hardpoints.length; i++) {
|
||||||
|
if (ship.hardpoints[i].m && ship.hardpoints[i].enabled) {
|
||||||
|
const m = ship.hardpoints[i].m;
|
||||||
|
if (m.getDamage() && m.grp !== 'po') {
|
||||||
|
let dropoff = 1;
|
||||||
|
if (m.getFalloff()) {
|
||||||
|
// Calculate the dropoff % due to range
|
||||||
|
if (range > m.getRange()) {
|
||||||
|
// Weapon is out of range
|
||||||
|
dropoff = 0;
|
||||||
|
} else {
|
||||||
|
const falloff = m.getFalloff();
|
||||||
|
if (range > falloff) {
|
||||||
|
const dropoffRange = m.getRange() - falloff;
|
||||||
|
// Assuming straight-line falloff
|
||||||
|
dropoff = 1 - (range - falloff) / dropoffRange;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const classRating = `${m.class}${m.rating}${m.missile ? '/' + m.missile : ''}`;
|
||||||
|
const effectivenessShields = dropoff;
|
||||||
|
const effectiveDpsShields = m.getDps() * effectivenessShields * dropoff;
|
||||||
|
const effectiveSDpsShields = (m.getClip() ? (m.getClip() * m.getDps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload()) * effectivenessShields : effectiveDpsShields) * dropoff;
|
||||||
|
const effectivenessHull = (m.getPiercing() >= against.properties.hardness ? 1 : m.getPiercing() / against.properties.hardness) * dropoff;
|
||||||
|
const effectiveDpsHull = m.getDps() * effectivenessHull * dropoff;
|
||||||
|
const effectiveSDpsHull = (m.getClip() ? (m.getClip() * m.getDps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload()) * effectivenessHull : effectiveDpsHull) * dropoff;
|
||||||
|
totals.effectiveDpsShields += effectiveDpsShields;
|
||||||
|
totals.effectiveSDpsShields += effectiveSDpsShields;
|
||||||
|
totals.effectiveDpsHull += effectiveDpsHull;
|
||||||
|
totals.effectiveSDpsHull += effectiveSDpsHull;
|
||||||
|
totalDps += m.getDps();
|
||||||
|
|
||||||
|
weapons.push({ id: i,
|
||||||
|
mount: m.mount,
|
||||||
|
name: m.name || m.grp,
|
||||||
|
classRating,
|
||||||
|
effectiveDpsShields,
|
||||||
|
effectiveSDpsShields,
|
||||||
|
effectivenessShields,
|
||||||
|
effectiveDpsHull,
|
||||||
|
effectiveSDpsHull,
|
||||||
|
effectivenessHull });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
totals.effectivenessShields = totalDps == 0 ? 0 : totals.effectiveDpsShields / totalDps;
|
||||||
|
totals.effectivenessHull = totalDps == 0 ? 0 : totals.effectiveDpsHull / totalDps;
|
||||||
|
|
||||||
|
return { weapons, totals };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggered when the collapse or expand icons are clicked
|
||||||
|
*/
|
||||||
|
_onCollapseExpand() {
|
||||||
|
this.setState({ expanded: !this.state.expanded });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggered when the ship we compare against changes
|
||||||
|
* @param {string} s the new ship ID
|
||||||
|
*/
|
||||||
|
_onShipChange(s) {
|
||||||
|
const against = Ships[s];
|
||||||
|
const data = this._calcWeapons(this.props.ship, against, this.state.range * this.state.maxRange);
|
||||||
|
this.setState({ against, weapons: data.weapons, totals: data.totals });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the sort order and sort
|
||||||
|
* @param {string} predicate Sort predicate
|
||||||
|
*/
|
||||||
|
_sortOrder(predicate) {
|
||||||
|
let desc = this.state.desc;
|
||||||
|
|
||||||
|
if (predicate == this.state.predicate) {
|
||||||
|
desc = !desc;
|
||||||
|
} else {
|
||||||
|
desc = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._sort(this.props.ship, predicate, desc);
|
||||||
|
this.setState({ predicate, desc });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts the weapon list
|
||||||
|
* @param {Ship} ship Ship instance
|
||||||
|
* @param {string} predicate Sort predicate
|
||||||
|
* @param {Boolean} desc Sort order descending
|
||||||
|
*/
|
||||||
|
_sort(ship, predicate, desc) {
|
||||||
|
let comp = weaponComparator.bind(null, this.context.language.translate);
|
||||||
|
|
||||||
|
switch (predicate) {
|
||||||
|
case 'n': comp = comp(null, desc); break;
|
||||||
|
case 'edpss': comp = comp((a, b) => a.effectiveDpsShields - b.effectiveDpsShields, desc); break;
|
||||||
|
case 'esdpss': comp = comp((a, b) => a.effectiveSDpsShields - b.effectiveSDpsShields, desc); break;
|
||||||
|
case 'es': comp = comp((a, b) => a.effectivenessShields - b.effectivenessShields, desc); break;
|
||||||
|
case 'edpsh': comp = comp((a, b) => a.effectiveDpsHull - b.effectiveDpsHull, desc); break;
|
||||||
|
case 'esdpsh': comp = comp((a, b) => a.effectiveSDpsHull - b.effectiveSDpsHull, desc); break;
|
||||||
|
case 'eh': comp = comp((a, b) => a.effectivenessHull - b.effectivenessHull, desc); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.state.weapons.sort(comp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render individual rows for hardpoints
|
||||||
|
* @param {Function} translate Translate function
|
||||||
|
* @param {Object} formats Localised formats map
|
||||||
|
* @return {array} The individual rows
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
_renderRows(translate, formats) {
|
||||||
|
const { termtip, tooltip } = this.context;
|
||||||
|
|
||||||
|
let rows = [];
|
||||||
|
|
||||||
|
if (this.state.weapons) {
|
||||||
|
for (let i = 0; i < this.state.weapons.length; i++) {
|
||||||
|
const weapon = this.state.weapons[i];
|
||||||
|
|
||||||
|
rows.push(<tr key={weapon.id}>
|
||||||
|
<td className='ri'>
|
||||||
|
{weapon.mount == 'F' ? <span onMouseOver={termtip.bind(null, 'fixed')} onMouseOut={tooltip.bind(null, null)}><MountFixed className='icon'/></span> : null}
|
||||||
|
{weapon.mount == 'G' ? <span onMouseOver={termtip.bind(null, 'gimballed')} onMouseOut={tooltip.bind(null, null)}><MountGimballed /></span> : null}
|
||||||
|
{weapon.mount == 'T' ? <span onMouseOver={termtip.bind(null, 'turreted')} onMouseOut={tooltip.bind(null, null)}><MountTurret /></span> : null}
|
||||||
|
{weapon.classRating} {translate(weapon.name)}
|
||||||
|
</td>
|
||||||
|
<td className='ri'>{formats.round1(weapon.effectiveDpsShields)}</td>
|
||||||
|
<td className='ri'>{formats.round1(weapon.effectiveSDpsShields)}</td>
|
||||||
|
<td className='ri'>{formats.pct(weapon.effectivenessShields)}</td>
|
||||||
|
<td className='ri'>{formats.round1(weapon.effectiveDpsHull)}</td>
|
||||||
|
<td className='ri'>{formats.round1(weapon.effectiveSDpsHull)}</td>
|
||||||
|
<td className='ri'>{formats.pct(weapon.effectivenessHull)}</td>
|
||||||
|
</tr>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update current range
|
||||||
|
* @param {number} range Range 0-1
|
||||||
|
*/
|
||||||
|
_rangeChange(range) {
|
||||||
|
const data = this._calcWeapons(this.props.ship, this.state.against, this.state.range * this.state.maxRange);
|
||||||
|
this.setState({ range, weapons: data.weapons, totals: data.totals });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render damage dealt
|
||||||
|
* @return {React.Component} contents
|
||||||
|
*/
|
||||||
|
render() {
|
||||||
|
const { language, onWindowResize, sizeRatio, tooltip, termtip } = this.context;
|
||||||
|
const { formats, translate, units } = language;
|
||||||
|
const { expanded, maxRange, range, totals } = this.state;
|
||||||
|
|
||||||
|
const sortOrder = this._sortOrder;
|
||||||
|
const onCollapseExpand = this._onCollapseExpand;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
<h1>{translate('damage dealt against')} {expanded ? <span onClick={onCollapseExpand}><CollapseSection className='summary'/></span> : <span onClick={onCollapseExpand}><ExpandSection className='summary'/></span>}</h1>
|
||||||
|
{expanded ? <span>
|
||||||
|
<ShipSelector initial={this.state.against} currentMenu={this.props.currentMenu} onChange={this._onShipChange} />
|
||||||
|
<table className='summary' style={{ width: '100%' }}>
|
||||||
|
<thead>
|
||||||
|
<tr className='main'>
|
||||||
|
<th rowSpan='2' className='sortable' onClick={sortOrder.bind(this, 'n')}>{translate('weapon')}</th>
|
||||||
|
<th colSpan='3'>{translate('shields')}</th>
|
||||||
|
<th colSpan='3'>{translate('armour')}</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th className='lft sortable' onClick={sortOrder.bind(this, 'edpss')}>{translate('effective dps')}</th>
|
||||||
|
<th className='sortable' onClick={sortOrder.bind(this, 'esdpss')}>{translate('effective sdps')}</th>
|
||||||
|
<th className='sortable' onClick={sortOrder.bind(this, 'es')}>{translate('effectiveness')}</th>
|
||||||
|
<th className='lft sortable' onClick={sortOrder.bind(this, 'edpsh')}>{translate('effective dps')}</th>
|
||||||
|
<th className='sortable' onClick={sortOrder.bind(this, 'esdpsh')}>{translate('effective sdps')}</th>
|
||||||
|
<th className='sortable' onClick={sortOrder.bind(this, 'eh')}>{translate('effectiveness')}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{this._renderRows(translate, formats)}
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr className='main'>
|
||||||
|
<td className='ri'><i>{translate('total')}</i></td>
|
||||||
|
<td className='ri'><i>{formats.round1(totals.effectiveDpsShields)}</i></td>
|
||||||
|
<td className='ri'><i>{formats.round1(totals.effectiveSDpsShields)}</i></td>
|
||||||
|
<td className='ri'><i>{formats.pct(totals.effectivenessShields)}</i></td>
|
||||||
|
<td className='ri'><i>{formats.round1(totals.effectiveDpsHull)}</i></td>
|
||||||
|
<td className='ri'><i>{formats.round1(totals.effectiveSDpsHull)}</i></td>
|
||||||
|
<td className='ri'><i>{formats.pct(totals.effectivenessHull)}</i></td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
<table style={{ width: '80%', lineHeight: '1em', backgroundColor: 'transparent', margin: 'auto' }}>
|
||||||
|
<tbody >
|
||||||
|
<tr>
|
||||||
|
<td style={{ verticalAlign: 'top', padding: 0, width: '2.5em' }} onMouseEnter={termtip.bind(null, 'PHRASE_ENGAGEMENT_RANGE')} onMouseLeave={tooltip.bind(null, null)}>{translate('engagement range')}</td>
|
||||||
|
<td>
|
||||||
|
<Slider
|
||||||
|
axis={true}
|
||||||
|
onChange={this._rangeChange.bind(this)}
|
||||||
|
axisUnit={translate('m')}
|
||||||
|
percent={range}
|
||||||
|
max={maxRange}
|
||||||
|
scale={sizeRatio}
|
||||||
|
onResize={onWindowResize}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td className='primary' style={{ width: '10em', verticalAlign: 'top', fontSize: '0.9em', textAlign: 'left' }}>
|
||||||
|
{formats.f2(range * maxRange / 1000)}{units.km}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table></span> : null }
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
327
src/app/components/DamageReceived.jsx
Normal file
327
src/app/components/DamageReceived.jsx
Normal file
@@ -0,0 +1,327 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
|
import { Modules } from 'coriolis-data/dist';
|
||||||
|
import { nameComparator } from '../utils/SlotFunctions';
|
||||||
|
import { CollapseSection, ExpandSection, MountFixed, MountGimballed, MountTurret } from './SvgIcons';
|
||||||
|
import Module from '../shipyard/Module';
|
||||||
|
import Slider from '../components/Slider';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates an internationalization friendly weapon comparator that will
|
||||||
|
* sort by specified property (if provided) then by name/group, class, rating
|
||||||
|
* @param {function} translate Translation function
|
||||||
|
* @param {function} propComparator Optional property comparator
|
||||||
|
* @param {boolean} desc Use descending order
|
||||||
|
* @return {function} Comparator function for names
|
||||||
|
*/
|
||||||
|
export function weaponComparator(translate, propComparator, desc) {
|
||||||
|
return (a, b) => {
|
||||||
|
if (!desc) { // Flip A and B if ascending order
|
||||||
|
let t = a;
|
||||||
|
a = b;
|
||||||
|
b = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a property comparator is provided use it first
|
||||||
|
let diff = propComparator ? propComparator(a, b) : nameComparator(translate, a, b);
|
||||||
|
|
||||||
|
if (diff) {
|
||||||
|
return diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Property matches so sort by name / group, then class, rating
|
||||||
|
if (a.name === b.name && a.grp === b.grp) {
|
||||||
|
if(a.class == b.class) {
|
||||||
|
return a.rating > b.rating ? 1 : -1;
|
||||||
|
}
|
||||||
|
return a.class - b.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nameComparator(translate, a, b);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Damage received by a selected ship
|
||||||
|
*/
|
||||||
|
export default class DamageReceived extends TranslatedComponent {
|
||||||
|
static PropTypes = {
|
||||||
|
ship: React.PropTypes.object.isRequired,
|
||||||
|
code: React.PropTypes.string.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param {Object} props React Component properties
|
||||||
|
*/
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this._sort = this._sort.bind(this);
|
||||||
|
this._onCollapseExpand = this._onCollapseExpand.bind(this);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
predicate: 'n',
|
||||||
|
desc: true,
|
||||||
|
expanded: false,
|
||||||
|
range: 0.1667,
|
||||||
|
maxRange: 6000
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the initial weapons state
|
||||||
|
*/
|
||||||
|
componentWillMount() {
|
||||||
|
this.setState({ weapons: this._calcWeapons(this.props.ship, this.state.range * this.state.maxRange) });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the updated weapons state
|
||||||
|
* @param {Object} nextProps Incoming/Next properties
|
||||||
|
* @param {Object} nextContext Incoming/Next conext
|
||||||
|
* @return {boolean} Returns true if the component should be rerendered
|
||||||
|
*/
|
||||||
|
componentWillReceiveProps(nextProps, nextContext) {
|
||||||
|
if (nextProps.code != this.props.code) {
|
||||||
|
this.setState({ weapons: this._calcWeapons(nextProps.ship, this.state.range * this.state.maxRange) });
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the damage received by a ship
|
||||||
|
* @param {Object} ship The ship which will receive the damage
|
||||||
|
* @param {Object} range The engagement range
|
||||||
|
* @return {boolean} Returns the per-weapon damage
|
||||||
|
*/
|
||||||
|
_calcWeapons(ship, range) {
|
||||||
|
// Tidy up the range so that it's to 4 decimal places
|
||||||
|
range = Math.round(10000 * range) / 10000;
|
||||||
|
|
||||||
|
let weapons = [];
|
||||||
|
for (let grp in Modules.hardpoints) {
|
||||||
|
if (Modules.hardpoints[grp][0].damage && Modules.hardpoints[grp][0].damagedist) {
|
||||||
|
for (let mId in Modules.hardpoints[grp]) {
|
||||||
|
const m = new Module(Modules.hardpoints[grp][mId]);
|
||||||
|
let dropoff = 1;
|
||||||
|
if (m.getFalloff()) {
|
||||||
|
// Calculate the dropoff % due to range
|
||||||
|
if (range > m.getRange()) {
|
||||||
|
// Weapon is out of range
|
||||||
|
dropoff = 0;
|
||||||
|
} else {
|
||||||
|
const falloff = m.getFalloff();
|
||||||
|
if (range > falloff) {
|
||||||
|
const dropoffRange = m.getRange() - falloff;
|
||||||
|
// Assuming straight-line falloff
|
||||||
|
dropoff = 1 - (range - falloff) / dropoffRange;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const classRating = `${m.class}${m.rating}${m.missile ? '/' + m.missile : ''}`;
|
||||||
|
|
||||||
|
// Base DPS
|
||||||
|
const baseDps = m.getDps() * dropoff;
|
||||||
|
const baseSDps = m.getClip() ? ((m.getClip() * baseDps / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload())) * dropoff : baseDps;
|
||||||
|
|
||||||
|
// Effective DPS taking in to account shield resistance
|
||||||
|
let effectivenessShields = 0;
|
||||||
|
if (m.getDamageDist().E) {
|
||||||
|
effectivenessShields += m.getDamageDist().E * (1 - ship.shieldExplRes);
|
||||||
|
}
|
||||||
|
if (m.getDamageDist().K) {
|
||||||
|
effectivenessShields += m.getDamageDist().K * (1 - ship.shieldKinRes);
|
||||||
|
}
|
||||||
|
if (m.getDamageDist().T) {
|
||||||
|
effectivenessShields += m.getDamageDist().T * (1 - ship.shieldThermRes);
|
||||||
|
}
|
||||||
|
if (m.getDamageDist().A) {
|
||||||
|
effectivenessShields += m.getDamageDist().A;
|
||||||
|
}
|
||||||
|
effectivenessShields *= dropoff;
|
||||||
|
const effectiveDpsShields = baseDps * effectivenessShields;
|
||||||
|
const effectiveSDpsShields = baseSDps * effectivenessShields;
|
||||||
|
|
||||||
|
// Effective DPS taking in to account hull hardness and resistance
|
||||||
|
let effectivenessHull = 0;
|
||||||
|
if (m.getDamageDist().E) {
|
||||||
|
effectivenessHull += m.getDamageDist().E * (1 - ship.hullExplRes);
|
||||||
|
}
|
||||||
|
if (m.getDamageDist().K) {
|
||||||
|
effectivenessHull += m.getDamageDist().K * (1 - ship.hullKinRes);
|
||||||
|
}
|
||||||
|
if (m.getDamageDist().T) {
|
||||||
|
effectivenessHull += m.getDamageDist().T * (1 - ship.hullThermRes);
|
||||||
|
}
|
||||||
|
if (m.getDamageDist().A) {
|
||||||
|
effectivenessHull += m.getDamageDist().A;
|
||||||
|
}
|
||||||
|
effectivenessHull *= Math.min(m.getPiercing() / ship.hardness, 1) * dropoff;
|
||||||
|
const effectiveDpsHull = baseDps * effectivenessHull;
|
||||||
|
const effectiveSDpsHull = baseSDps * effectivenessHull;
|
||||||
|
|
||||||
|
weapons.push({ id: m.id,
|
||||||
|
classRating,
|
||||||
|
name: m.name || m.grp,
|
||||||
|
mount: m.mount,
|
||||||
|
effectiveDpsShields,
|
||||||
|
effectiveSDpsShields,
|
||||||
|
effectivenessShields,
|
||||||
|
effectiveDpsHull,
|
||||||
|
effectiveSDpsHull,
|
||||||
|
effectivenessHull });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return weapons;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggered when the collapse or expand icons are clicked
|
||||||
|
*/
|
||||||
|
_onCollapseExpand() {
|
||||||
|
this.setState({ expanded: !this.state.expanded });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the sort order and sort
|
||||||
|
* @param {string} predicate Sort predicate
|
||||||
|
*/
|
||||||
|
_sortOrder(predicate) {
|
||||||
|
let desc = this.state.desc;
|
||||||
|
|
||||||
|
if (predicate == this.state.predicate) {
|
||||||
|
desc = !desc;
|
||||||
|
} else {
|
||||||
|
desc = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._sort(this.props.ship, predicate, desc);
|
||||||
|
this.setState({ predicate, desc });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts the weapon list
|
||||||
|
* @param {Ship} ship Ship instance
|
||||||
|
* @param {string} predicate Sort predicate
|
||||||
|
* @param {Boolean} desc Sort order descending
|
||||||
|
*/
|
||||||
|
_sort(ship, predicate, desc) {
|
||||||
|
let comp = weaponComparator.bind(null, this.context.language.translate);
|
||||||
|
|
||||||
|
switch (predicate) {
|
||||||
|
case 'n': comp = comp(null, desc); break;
|
||||||
|
case 'edpss': comp = comp((a, b) => a.effectiveDpsShields - b.effectiveDpsShields, desc); break;
|
||||||
|
case 'esdpss': comp = comp((a, b) => a.effectiveSDpsShields - b.effectiveSDpsShields, desc); break;
|
||||||
|
case 'es': comp = comp((a, b) => a.effectivenessShields - b.effectivenessShields, desc); break;
|
||||||
|
case 'edpsh': comp = comp((a, b) => a.effectiveDpsHull - b.effectiveDpsHull, desc); break;
|
||||||
|
case 'esdpsh': comp = comp((a, b) => a.effectiveSDpsHull - b.effectiveSDpsHull, desc); break;
|
||||||
|
case 'eh': comp = comp((a, b) => a.effectivenessHull - b.effectivenessHull, desc); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.state.weapons.sort(comp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render individual rows for weapons
|
||||||
|
* @param {Function} translate Translate function
|
||||||
|
* @param {Object} formats Localised formats map
|
||||||
|
* @return {array} The individual rows
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
_renderRows(translate, formats) {
|
||||||
|
const { termtip, tooltip } = this.context;
|
||||||
|
|
||||||
|
let rows = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < this.state.weapons.length; i++) {
|
||||||
|
const weapon = this.state.weapons[i];
|
||||||
|
rows.push(<tr key={weapon.id}>
|
||||||
|
<td className='ri'>
|
||||||
|
{weapon.mount == 'F' ? <span onMouseOver={termtip.bind(null, 'fixed')} onMouseOut={tooltip.bind(null, null)}><MountFixed className='icon'/></span> : null}
|
||||||
|
{weapon.mount == 'G' ? <span onMouseOver={termtip.bind(null, 'gimballed')} onMouseOut={tooltip.bind(null, null)}><MountGimballed /></span> : null}
|
||||||
|
{weapon.mount == 'T' ? <span onMouseOver={termtip.bind(null, 'turreted')} onMouseOut={tooltip.bind(null, null)}><MountTurret /></span> : null}
|
||||||
|
{weapon.classRating} {translate(weapon.name)}
|
||||||
|
</td>
|
||||||
|
<td>{formats.round1(weapon.effectiveDpsShields)}</td>
|
||||||
|
<td>{formats.round1(weapon.effectiveSDpsShields)}</td>
|
||||||
|
<td>{formats.pct(weapon.effectivenessShields)}</td>
|
||||||
|
<td>{formats.round1(weapon.effectiveDpsHull)}</td>
|
||||||
|
<td>{formats.round1(weapon.effectiveSDpsHull)}</td>
|
||||||
|
<td>{formats.pct(weapon.effectivenessHull)}</td>
|
||||||
|
</tr>);
|
||||||
|
}
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update current range
|
||||||
|
* @param {number} range Range 0-1
|
||||||
|
*/
|
||||||
|
_rangeChange(range) {
|
||||||
|
this.setState({ range, weapons: this._calcWeapons(this.props.ship, this.state.range * this.state.maxRange) });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render damage received
|
||||||
|
* @return {React.Component} contents
|
||||||
|
*/
|
||||||
|
render() {
|
||||||
|
const { language, onWindowResize, sizeRatio, tooltip, termtip } = this.context;
|
||||||
|
const { formats, translate, units } = language;
|
||||||
|
const { expanded, maxRange, range } = this.state;
|
||||||
|
|
||||||
|
const sortOrder = this._sortOrder;
|
||||||
|
const onCollapseExpand = this._onCollapseExpand;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
<h1>{translate('damage received by')} {expanded ? <span onClick={onCollapseExpand}><CollapseSection className='summary'/></span> : <span onClick={onCollapseExpand}><ExpandSection className='summary'/></span>}</h1>
|
||||||
|
{expanded ? <span>
|
||||||
|
<table className='summary' style={{ width: '100%' }}>
|
||||||
|
<thead>
|
||||||
|
<tr className='main'>
|
||||||
|
<th rowSpan={2} className='sortable' onClick={sortOrder.bind(this, 'n')} >{translate('weapon')}</th>
|
||||||
|
<th colSpan={3} >{translate('against shields')}</th>
|
||||||
|
<th colSpan={3} >{translate('against hull')}</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th className='sortable lft' onClick={sortOrder.bind(this, 'edpss')} onMouseOver={termtip.bind(null, 'dps')} onMouseOut={tooltip.bind(null, null)}>{translate('DPS')}</th>
|
||||||
|
<th className='sortable' onClick={sortOrder.bind(this, 'esdpss')} onMouseOver={termtip.bind(null, 'sdps')} onMouseOut={tooltip.bind(null, null)}>{translate('SDPS')}</th>
|
||||||
|
<th className='sortable' onClick={sortOrder.bind(this, 'es')} >{translate('effectiveness')}</th>
|
||||||
|
<th className='sortable lft' onClick={sortOrder.bind(this, 'edpsh')} onMouseOver={termtip.bind(null, 'dps')} onMouseOut={tooltip.bind(null, null)}>{translate('DPS')}</th>
|
||||||
|
<th className='sortable' onClick={sortOrder.bind(this, 'esdpsh')} onMouseOver={termtip.bind(null, 'sdps')} onMouseOut={tooltip.bind(null, null)}>{translate('SDPS')}</th>
|
||||||
|
<th className='sortable' onClick={sortOrder.bind(this, 'eh')} >{translate('effectiveness')}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{this._renderRows(translate, formats)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<table style={{ width: '80%', lineHeight: '1em', backgroundColor: 'transparent', margin: 'auto' }}>
|
||||||
|
<tbody >
|
||||||
|
<tr>
|
||||||
|
<td style={{ verticalAlign: 'top', padding: 0, width: '2.5em' }} onMouseEnter={termtip.bind(null, 'PHRASE_ENGAGEMENT_RANGE')} onMouseLeave={tooltip.bind(null, null)}>{translate('engagement range')}</td>
|
||||||
|
<td>
|
||||||
|
<Slider
|
||||||
|
axis={true}
|
||||||
|
onChange={this._rangeChange.bind(this)}
|
||||||
|
axisUnit={translate('m')}
|
||||||
|
percent={range}
|
||||||
|
max={maxRange}
|
||||||
|
scale={sizeRatio}
|
||||||
|
onResize={onWindowResize}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td className='primary' style={{ width: '10em', verticalAlign: 'top', fontSize: '0.9em', textAlign: 'left' }}>
|
||||||
|
{formats.f2(range * maxRange / 1000)}{units.km}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table></span> : null }
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,6 +29,9 @@ export default class DefenceSummary extends TranslatedComponent {
|
|||||||
let { formats, translate, units } = language;
|
let { formats, translate, units } = language;
|
||||||
let hide = tooltip.bind(null, null);
|
let hide = tooltip.bind(null, null);
|
||||||
|
|
||||||
|
const shieldGenerator = ship.findShieldGenerator();
|
||||||
|
|
||||||
|
// Damage values are 1 - resistance values
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
<h1>{translate('defence summary')}</h1>
|
<h1>{translate('defence summary')}</h1>
|
||||||
@@ -48,9 +51,18 @@ export default class DefenceSummary extends TranslatedComponent {
|
|||||||
{ship.shield ?
|
{ship.shield ?
|
||||||
<tr>
|
<tr>
|
||||||
<td className='le'>{translate('damage from')}</td>
|
<td className='le'>{translate('damage from')}</td>
|
||||||
<td className='ri' onMouseOver={termtip.bind(null, 'explosive')} onMouseOut={tooltip.bind(null, null)}><DamageExplosive /> {formats.pct1(ship.shieldExplRes || 1)}</td>
|
<td className='ri'>
|
||||||
<td className='ri' onMouseOver={termtip.bind(null, 'kinetic')} onMouseOut={tooltip.bind(null, null)}><DamageKinetic /> {formats.pct1(ship.shieldKinRes || 1)}</td>
|
<span onMouseOver={termtip.bind(null, 'explosive')} onMouseOut={tooltip.bind(null, null)}><DamageExplosive /></span>
|
||||||
<td className='ri' onMouseOver={termtip.bind(null, 'thermal')} onMouseOut={tooltip.bind(null, null)}><DamageThermal /> {formats.pct1(ship.shieldThermRes || 1)}</td>
|
<span onMouseOver={termtip.bind(null, translate('base') + ' ' + formats.pct1(1 - shieldGenerator.explres))} onMouseOut={tooltip.bind(null, null)}>{formats.pct1(1 - ship.shieldExplRes)}</span>
|
||||||
|
</td>
|
||||||
|
<td className='ri'>
|
||||||
|
<span onMouseOver={termtip.bind(null, 'kinetic')} onMouseOut={tooltip.bind(null, null)}><DamageKinetic /></span>
|
||||||
|
<span onMouseOver={termtip.bind(null, translate('base') + ' ' + formats.pct1(1 - shieldGenerator.kinres))} onMouseOut={tooltip.bind(null, null)}>{formats.pct1(1 - ship.shieldKinRes)}</span>
|
||||||
|
</td>
|
||||||
|
<td className='ri'>
|
||||||
|
<span onMouseOver={termtip.bind(null, 'thermal')} onMouseOut={tooltip.bind(null, null)}><DamageThermal /></span>
|
||||||
|
<span onMouseOver={termtip.bind(null, translate('base') + ' ' + formats.pct1(1 - shieldGenerator.thermres))} onMouseOut={tooltip.bind(null, null)}>{formats.pct1(1 - ship.shieldThermRes)}</span>
|
||||||
|
</td>
|
||||||
</tr> : null }
|
</tr> : null }
|
||||||
|
|
||||||
{ ship.shield && ship.shieldCells ?
|
{ ship.shield && ship.shieldCells ?
|
||||||
@@ -63,10 +75,29 @@ export default class DefenceSummary extends TranslatedComponent {
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td className='le'>{translate('damage from')}</td>
|
<td className='le'>{translate('damage from')}</td>
|
||||||
<td className='ri' onMouseOver={termtip.bind(null, 'explosive')} onMouseOut={tooltip.bind(null, null)}><DamageExplosive /> {formats.pct1(ship.hullExplRes || 1)}</td>
|
<td className='ri'>
|
||||||
<td className='ri' onMouseOver={termtip.bind(null, 'kinetic')} onMouseOut={tooltip.bind(null, null)}><DamageKinetic /> {formats.pct1(ship.hullKinRes || 1)}</td>
|
<span onMouseOver={termtip.bind(null, 'explosive')} onMouseOut={tooltip.bind(null, null)}><DamageExplosive /></span>
|
||||||
<td className='ri' onMouseOver={termtip.bind(null, 'thermal')} onMouseOut={tooltip.bind(null, null)}><DamageThermal /> {formats.pct1(ship.hullThermRes || 1)}</td>
|
<span onMouseOver={termtip.bind(null, translate('base') + ' ' + formats.pct1(1 - ship.bulkheads.m.explres))} onMouseOut={tooltip.bind(null, null)}>{formats.pct1(1 - ship.hullExplRes)}</span></td>
|
||||||
|
<td className='ri'>
|
||||||
|
<span onMouseOver={termtip.bind(null, 'kinetic')} onMouseOut={tooltip.bind(null, null)}><DamageKinetic /></span>
|
||||||
|
<span onMouseOver={termtip.bind(null, translate('base') + ' ' + formats.pct1(1 - ship.bulkheads.m.kinres))} onMouseOut={tooltip.bind(null, null)}>{formats.pct1(1 - ship.hullKinRes)}</span>
|
||||||
|
</td>
|
||||||
|
<td className='ri'>
|
||||||
|
<span onMouseOver={termtip.bind(null, 'thermal')} onMouseOut={tooltip.bind(null, null)}><DamageThermal /></span>
|
||||||
|
<span onMouseOver={termtip.bind(null, translate('base') + ' ' + formats.pct1(1 - ship.bulkheads.m.thermres))} onMouseOut={tooltip.bind(null, null)}>{formats.pct1(1 - ship.hullThermRes)}</span>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
{ship.modulearmour > 0 ?
|
||||||
|
<tr>
|
||||||
|
<td colSpan='4'><h2>{translate('module armour')}: {formats.int(ship.modulearmour)}</h2></td>
|
||||||
|
</tr> : null }
|
||||||
|
|
||||||
|
{ship.moduleprotection > 0 ?
|
||||||
|
<tr>
|
||||||
|
<td colSpan='2' className='cn'>{translate('internal protection')} {formats.pct1(ship.moduleprotection)}</td>
|
||||||
|
<td colSpan='2' className='cn'>{translate('external protection')} {formats.pct1(ship.moduleprotection / 2)}</td>
|
||||||
|
</tr> : null }
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Slot from './Slot';
|
import Slot from './Slot';
|
||||||
import Persist from '../stores/Persist';
|
import Persist from '../stores/Persist';
|
||||||
import { DamageKinetic, DamageThermal, DamageExplosive, MountFixed, MountGimballed, MountTurret, ListModifications, Modified } from './SvgIcons';
|
import { DamageAbsolute, DamageKinetic, DamageThermal, DamageExplosive, MountFixed, MountGimballed, MountTurret, ListModifications, Modified } from './SvgIcons';
|
||||||
import { Modifications } from 'coriolis-data/dist';
|
import { Modifications } from 'coriolis-data/dist';
|
||||||
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
||||||
|
|
||||||
@@ -41,20 +41,30 @@ export default class HardpointSlot extends Slot {
|
|||||||
let classRating = `${m.class}${m.rating}${m.missile ? '/' + m.missile : ''}`;
|
let classRating = `${m.class}${m.rating}${m.missile ? '/' + m.missile : ''}`;
|
||||||
let { drag, drop } = this.props;
|
let { drag, drop } = this.props;
|
||||||
let { termtip, tooltip } = this.context;
|
let { termtip, tooltip } = this.context;
|
||||||
let validMods = Modifications.validity[m.grp] || [];
|
let validMods = Modifications.modules[m.grp].modifications || [];
|
||||||
let showModuleResistances = Persist.showModuleResistances();
|
let showModuleResistances = Persist.showModuleResistances();
|
||||||
|
|
||||||
|
// Modifications tooltip shows blueprint and grade, if available
|
||||||
|
let modTT = translate('modified');
|
||||||
|
if (m && m.blueprint && m.blueprint.name) {
|
||||||
|
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}>
|
return <div className='details' draggable='true' onDragStart={drag} onDragEnd={drop}>
|
||||||
<div className={'cb'}>
|
<div className={'cb'}>
|
||||||
<div className={'l'}>
|
<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 == '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 == '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.mount && m.mount == 'T' ? <span onMouseOver={termtip.bind(null, 'turreted')} onMouseOut={tooltip.bind(null, null)}><MountTurret /></span> : ''}
|
||||||
{m.type && m.type.match('K') ? <span onMouseOver={termtip.bind(null, 'kinetic')} onMouseOut={tooltip.bind(null, null)}><DamageKinetic /></span> : ''}
|
{m.getDamageDist() && m.getDamageDist().K ? <span onMouseOver={termtip.bind(null, 'kinetic')} onMouseOut={tooltip.bind(null, null)}><DamageKinetic /></span> : ''}
|
||||||
{m.type && m.type.match('T') ? <span onMouseOver={termtip.bind(null, 'thermal')} onMouseOut={tooltip.bind(null, null)}><DamageThermal /></span> : ''}
|
{m.getDamageDist() && m.getDamageDist().T ? <span onMouseOver={termtip.bind(null, 'thermal')} onMouseOut={tooltip.bind(null, null)}><DamageThermal /></span> : ''}
|
||||||
{m.type && m.type.match('E') ? <span onMouseOver={termtip.bind(null, 'explosive')} onMouseOut={tooltip.bind(null, null)}><DamageExplosive /></span> : ''}
|
{m.getDamageDist() && m.getDamageDist().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, 'modified')} onMouseOut={tooltip.bind(null, null)}><Modified /></span> : null }
|
{m.getDamageDist() && m.getDamageDist().A ? <span onMouseOver={termtip.bind(null, 'absolute')} onMouseOut={tooltip.bind(null, null)}><DamageAbsolute /></span> : ''}
|
||||||
</div>
|
{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 className={'r'}>{formats.round(m.getMass())}{u.T}</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -65,8 +75,11 @@ export default class HardpointSlot extends Slot {
|
|||||||
{ 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.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.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.getRange() ? <div className={'l'}>{translate('range')} {formats.f1(m.getRange() / 1000)}{u.km}</div> : null }
|
||||||
|
{ m.getFalloff() ? <div className={'l'}>{translate('falloff')} {formats.round(m.getFalloff() / 1000)}{u.km}</div> : null }
|
||||||
{ m.getShieldBoost() ? <div className={'l'}>+{formats.pct1(m.getShieldBoost())}</div> : null }
|
{ m.getShieldBoost() ? <div className={'l'}>+{formats.pct1(m.getShieldBoost())}</div> : null }
|
||||||
{ m.getAmmo() ? <div className={'l'}>{translate('ammunition')}: {formats.int(m.getClip())}/{formats.int(m.getAmmo())}</div> : null }
|
{ m.getAmmo() ? <div className={'l'}>{translate('ammunition')}: {formats.int(m.getClip())}/{formats.int(m.getAmmo())}</div> : null }
|
||||||
|
{ m.getShotSpeed() ? <div className={'l'}>{translate('shotspeed')}: {formats.int(m.getShotSpeed())}{u.mps}</div> : null }
|
||||||
|
{ m.getPiercing() ? <div className={'l'}>{translate('piercing')}: {formats.int(m.getPiercing())}</div> : null }
|
||||||
{ m.getJitter() ? <div className={'l'}>{translate('jitter')}: {formats.f2(m.getJitter())}°</div> : null }
|
{ m.getJitter() ? <div className={'l'}>{translate('jitter')}: {formats.f2(m.getJitter())}°</div> : null }
|
||||||
{ showModuleResistances && m.getExplosiveResistance() ? <div className='l'>{translate('explres')}: {formats.pct(m.getExplosiveResistance())}</div> : null }
|
{ showModuleResistances && m.getExplosiveResistance() ? <div className='l'>{translate('explres')}: {formats.pct(m.getExplosiveResistance())}</div> : null }
|
||||||
{ showModuleResistances && m.getKineticResistance() ? <div className='l'>{translate('kinres')}: {formats.pct(m.getKineticResistance())}</div> : null }
|
{ showModuleResistances && m.getKineticResistance() ? <div className='l'>{translate('kinres')}: {formats.pct(m.getKineticResistance())}</div> : null }
|
||||||
|
|||||||
@@ -133,6 +133,10 @@ export default class HardpointsSlotSection extends SlotSection {
|
|||||||
<li className='c' onClick={_fill.bind(this, 'fc', 'G')}><MountGimballed className='lg'/></li>
|
<li className='c' onClick={_fill.bind(this, 'fc', 'G')}><MountGimballed className='lg'/></li>
|
||||||
<li className='c' onClick={_fill.bind(this, 'fc', 'T')}><MountTurret className='lg'/></li>
|
<li className='c' onClick={_fill.bind(this, 'fc', 'T')}><MountTurret className='lg'/></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<div className='select-group cap'>{translate('pa')}</div>
|
||||||
|
<ul>
|
||||||
|
<li className='lc' onClick={_fill.bind(this, 'pa', 'F')}>{translate('pa')}</li>
|
||||||
|
</ul>
|
||||||
<div className='select-group cap'>{translate('nl')}</div>
|
<div className='select-group cap'>{translate('nl')}</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li className='lc' onClick={_fill.bind(this, 'nl', 'F')}>{translate('nl')}</li>
|
<li className='lc' onClick={_fill.bind(this, 'nl', 'F')}>{translate('nl')}</li>
|
||||||
|
|||||||
@@ -5,12 +5,13 @@ import { Insurance } from '../shipyard/Constants';
|
|||||||
import Link from './Link';
|
import Link from './Link';
|
||||||
import ActiveLink from './ActiveLink';
|
import ActiveLink from './ActiveLink';
|
||||||
import cn from 'classnames';
|
import cn from 'classnames';
|
||||||
import { Cogs, CoriolisLogo, Hammer, Rocket, StatsBars } from './SvgIcons';
|
import { Cogs, CoriolisLogo, Hammer, Help, Rocket, StatsBars } from './SvgIcons';
|
||||||
import { Ships } from 'coriolis-data/dist';
|
import { Ships } from 'coriolis-data/dist';
|
||||||
import Persist from '../stores/Persist';
|
import Persist from '../stores/Persist';
|
||||||
import { toDetailedExport } from '../shipyard/Serializer';
|
import { toDetailedExport } from '../shipyard/Serializer';
|
||||||
import ModalDeleteAll from './ModalDeleteAll';
|
import ModalDeleteAll from './ModalDeleteAll';
|
||||||
import ModalExport from './ModalExport';
|
import ModalExport from './ModalExport';
|
||||||
|
import ModalHelp from './ModalHelp';
|
||||||
import ModalImport from './ModalImport';
|
import ModalImport from './ModalImport';
|
||||||
import Slider from './Slider';
|
import Slider from './Slider';
|
||||||
import { outfitURL } from '../utils/UrlGenerators';
|
import { outfitURL } from '../utils/UrlGenerators';
|
||||||
@@ -53,11 +54,11 @@ function selectAll(e) {
|
|||||||
*/
|
*/
|
||||||
export default class Header extends TranslatedComponent {
|
export default class Header extends TranslatedComponent {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
* @param {Object} props React Component properties
|
* @param {Object} props React Component properties
|
||||||
* @param {Object} context React Component context
|
* @param {Object} context React Component context
|
||||||
*/
|
*/
|
||||||
constructor(props, context) {
|
constructor(props, context) {
|
||||||
super(props);
|
super(props);
|
||||||
this.shipOrder = Object.keys(Ships).sort();
|
this.shipOrder = Object.keys(Ships).sort();
|
||||||
@@ -74,6 +75,7 @@ export default class Header extends TranslatedComponent {
|
|||||||
this._openBuilds = this._openMenu.bind(this, 'b');
|
this._openBuilds = this._openMenu.bind(this, 'b');
|
||||||
this._openComp = this._openMenu.bind(this, 'comp');
|
this._openComp = this._openMenu.bind(this, 'comp');
|
||||||
this._openSettings = this._openMenu.bind(this, 'settings');
|
this._openSettings = this._openMenu.bind(this, 'settings');
|
||||||
|
this._showHelp = this._showHelp.bind(this);
|
||||||
this.languageOptions = [];
|
this.languageOptions = [];
|
||||||
this.insuranceOptions = [];
|
this.insuranceOptions = [];
|
||||||
this.state = {
|
this.state = {
|
||||||
@@ -248,6 +250,17 @@ export default class Header extends TranslatedComponent {
|
|||||||
/>);
|
/>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show help modal
|
||||||
|
* @param {SyntheticEvent} e Event
|
||||||
|
*/
|
||||||
|
_showHelp(e) {
|
||||||
|
let translate = this.context.language.translate;
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
this.context.showModal(<ModalHelp title={translate('help')} />);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show import modal
|
* Show import modal
|
||||||
* @param {SyntheticEvent} e Event
|
* @param {SyntheticEvent} e Event
|
||||||
@@ -521,6 +534,12 @@ export default class Header extends TranslatedComponent {
|
|||||||
</div>
|
</div>
|
||||||
{openedMenu == 'settings' ? this._getSettingsMenu() : null}
|
{openedMenu == 'settings' ? this._getSettingsMenu() : null}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className='r menu'>
|
||||||
|
<div className={cn('menu-header')} onClick={this._showHelp}>
|
||||||
|
<Help className='xl warning'/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</header>
|
</header>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,13 +23,19 @@ export default class InternalSlot extends Slot {
|
|||||||
let classRating = m.class + m.rating;
|
let classRating = m.class + m.rating;
|
||||||
let { drag, drop, ship } = this.props;
|
let { drag, drop, ship } = this.props;
|
||||||
let { termtip, tooltip } = this.context;
|
let { termtip, tooltip } = this.context;
|
||||||
let validMods = Modifications.validity[m.grp] || [];
|
let validMods = Modifications.modules[m.grp].modifications || [];
|
||||||
let showModuleResistances = Persist.showModuleResistances();
|
let showModuleResistances = Persist.showModuleResistances();
|
||||||
|
|
||||||
|
// Modifications tooltip shows blueprint and grade, if available
|
||||||
|
let modTT = translate('modified');
|
||||||
|
if (m && m.blueprint && m.blueprint.name) {
|
||||||
|
modTT = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
|
||||||
|
}
|
||||||
|
|
||||||
let mass = m.getMass() || m.cargo || m.fuel || 0;
|
let mass = m.getMass() || m.cargo || m.fuel || 0;
|
||||||
return <div className='details' draggable='true' onDragStart={drag} onDragEnd={drop}>
|
return <div className='details' draggable='true' onDragStart={drag} onDragEnd={drop}>
|
||||||
<div className={'cb'}>
|
<div className={'cb'}>
|
||||||
<div className={'l'}>{classRating} {translate(m.name || m.grp)}{m.mods && Object.keys(m.mods).length > 0 ? <span onMouseOver={termtip.bind(null, 'modified')} onMouseOut={tooltip.bind(null, null)}><Modified /></span> : ''}</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 className={'r'}>{formats.round(mass)}{u.T}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={'cb'}>
|
<div className={'cb'}>
|
||||||
@@ -37,6 +43,7 @@ export default class InternalSlot extends Slot {
|
|||||||
{ m.getMaxMass() ? <div className={'l'}>{translate('max mass')}: {formats.int(m.getMaxMass())}{u.T}</div> : null }
|
{ m.getMaxMass() ? <div className={'l'}>{translate('max mass')}: {formats.int(m.getMaxMass())}{u.T}</div> : null }
|
||||||
{ m.bins ? <div className={'l'}>{m.bins} <u>{translate('bins')}</u></div> : null }
|
{ m.bins ? <div className={'l'}>{m.bins} <u>{translate('bins')}</u></div> : null }
|
||||||
{ m.bays ? <div className={'l'}>{translate('bays')}: {m.bays}</div> : null }
|
{ m.bays ? <div className={'l'}>{translate('bays')}: {m.bays}</div> : null }
|
||||||
|
{ m.rebuildsperbay ? <div className={'l'}>{translate('rebuildsperbay')}: {m.rebuildsperbay}</div> : null }
|
||||||
{ m.rate ? <div className={'l'}>{translate('rate')}: {m.rate}{u.kgs} {translate('refuel time')}: {formats.time(this.props.fuel * 1000 / m.rate)}</div> : null }
|
{ m.rate ? <div className={'l'}>{translate('rate')}: {m.rate}{u.kgs} {translate('refuel time')}: {formats.time(this.props.fuel * 1000 / m.rate)}</div> : null }
|
||||||
{ m.getAmmo() ? <div className={'l'}>{translate('ammunition')}: {formats.gen(m.getAmmo())}</div> : null }
|
{ m.getAmmo() ? <div className={'l'}>{translate('ammunition')}: {formats.gen(m.getAmmo())}</div> : null }
|
||||||
{ m.cells ? <div className={'l'}>{translate('cells')}: {m.cells}</div> : null }
|
{ m.cells ? <div className={'l'}>{translate('cells')}: {m.cells}</div> : null }
|
||||||
@@ -52,10 +59,13 @@ export default class InternalSlot extends Slot {
|
|||||||
{ m.rangeLS === null ? <div className={'l'}>∞{u.Ls}</div> : null }
|
{ m.rangeLS === null ? <div className={'l'}>∞{u.Ls}</div> : null }
|
||||||
{ m.rangeRating ? <div className={'l'}>{translate('range')}: {m.rangeRating}</div> : null }
|
{ m.rangeRating ? <div className={'l'}>{translate('range')}: {m.rangeRating}</div> : null }
|
||||||
{ m.getHullReinforcement() ? <div className={'l'}>+{formats.int(m.getHullReinforcement() + ship.baseArmour * m.getModValue('hullboost') / 10000)} <u className='cap'>{translate('armour')}</u></div> : null }
|
{ m.getHullReinforcement() ? <div className={'l'}>+{formats.int(m.getHullReinforcement() + ship.baseArmour * m.getModValue('hullboost') / 10000)} <u className='cap'>{translate('armour')}</u></div> : null }
|
||||||
|
{ m.getProtection() ? <div className={'l'}>{formats.rPct(m.getProtection())} <u className='cap'>{translate('protection')}</u></div> : null }
|
||||||
{ m.passengers ? <div className={'l'}>{translate('passengers')}: {m.passengers}</div> : null }
|
{ m.passengers ? <div className={'l'}>{translate('passengers')}: {m.passengers}</div> : null }
|
||||||
{ showModuleResistances && m.getExplosiveResistance() ? <div className='l'>{translate('explres')}: {formats.pct(m.getExplosiveResistance())}</div> : null }
|
{ showModuleResistances && m.getExplosiveResistance() ? <div className='l'>{translate('explres')}: {formats.pct(m.getExplosiveResistance())}</div> : null }
|
||||||
{ showModuleResistances && m.getKineticResistance() ? <div className='l'>{translate('kinres')}: {formats.pct(m.getKineticResistance())}</div> : null }
|
{ showModuleResistances && m.getKineticResistance() ? <div className='l'>{translate('kinres')}: {formats.pct(m.getKineticResistance())}</div> : null }
|
||||||
{ showModuleResistances && m.getThermalResistance() ? <div className='l'>{translate('thermres')}: {formats.pct(m.getThermalResistance())}</div> : null }
|
{ showModuleResistances && m.getThermalResistance() ? <div className='l'>{translate('thermres')}: {formats.pct(m.getThermalResistance())}</div> : null }
|
||||||
|
{ m.getRegenerationRate() ? <div className='l'>{translate('regen')}: {formats.round1(m.getRegenerationRate())}{u.ps}</div> : null }
|
||||||
|
{ m.getBrokenRegenerationRate() ? <div className='l'>{translate('brokenregen')}: {formats.round1(m.getBrokenRegenerationRate())}{u.ps}</div> : null }
|
||||||
|
|
||||||
{ m && validMods.length > 0 ? <div className='r' ><button onClick={this._toggleModifications.bind(this)} onContextMenu={stopCtxPropagation} onMouseOver={termtip.bind(null, 'modifications')} onMouseOut={tooltip.bind(null, null)}><ListModifications /></button></div> : null }
|
{ m && validMods.length > 0 ? <div className='r' ><button onClick={this._toggleModifications.bind(this)} onContextMenu={stopCtxPropagation} onMouseOver={termtip.bind(null, 'modifications')} onMouseOut={tooltip.bind(null, null)}><ListModifications /></button></div> : null }
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import SlotSection from './SlotSection';
|
|||||||
import InternalSlot from './InternalSlot';
|
import InternalSlot from './InternalSlot';
|
||||||
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
||||||
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
||||||
|
import { canMount } from '../utils/SlotFunctions';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal slot section
|
* Internal slot section
|
||||||
@@ -22,6 +23,7 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
this._fillWithCargo = this._fillWithCargo.bind(this);
|
this._fillWithCargo = this._fillWithCargo.bind(this);
|
||||||
this._fillWithCells = this._fillWithCells.bind(this);
|
this._fillWithCells = this._fillWithCells.bind(this);
|
||||||
this._fillWithArmor = this._fillWithArmor.bind(this);
|
this._fillWithArmor = this._fillWithArmor.bind(this);
|
||||||
|
this._fillWithModuleReinforcementPackages = this._fillWithModuleReinforcementPackages.bind(this);
|
||||||
this._fillWithFuelTanks = this._fillWithFuelTanks.bind(this);
|
this._fillWithFuelTanks = this._fillWithFuelTanks.bind(this);
|
||||||
this._fillWithLuxuryCabins = this._fillWithLuxuryCabins.bind(this);
|
this._fillWithLuxuryCabins = this._fillWithLuxuryCabins.bind(this);
|
||||||
this._fillWithFirstClassCabins = this._fillWithFirstClassCabins.bind(this);
|
this._fillWithFirstClassCabins = this._fillWithFirstClassCabins.bind(this);
|
||||||
@@ -46,7 +48,7 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
let clobber = event.getModifierState('Alt');
|
let clobber = event.getModifierState('Alt');
|
||||||
let ship = this.props.ship;
|
let ship = this.props.ship;
|
||||||
ship.internal.forEach((slot) => {
|
ship.internal.forEach((slot) => {
|
||||||
if (clobber || !slot.m) {
|
if ((clobber || !slot.m) && canMount(ship, slot, 'cr')) {
|
||||||
ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E'));
|
ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E'));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -62,7 +64,7 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
let clobber = event.getModifierState('Alt');
|
let clobber = event.getModifierState('Alt');
|
||||||
let ship = this.props.ship;
|
let ship = this.props.ship;
|
||||||
ship.internal.forEach((slot) => {
|
ship.internal.forEach((slot) => {
|
||||||
if (clobber || !slot.m) {
|
if ((clobber || !slot.m) && canMount(ship, slot, 'ft')) {
|
||||||
ship.use(slot, ModuleUtils.findInternal('ft', slot.maxClass, 'C'));
|
ship.use(slot, ModuleUtils.findInternal('ft', slot.maxClass, 'C'));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -78,7 +80,7 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
let clobber = event.getModifierState('Alt');
|
let clobber = event.getModifierState('Alt');
|
||||||
let ship = this.props.ship;
|
let ship = this.props.ship;
|
||||||
ship.internal.forEach((slot) => {
|
ship.internal.forEach((slot) => {
|
||||||
if (clobber || !slot.m) {
|
if ((clobber || !slot.m) && canMount(ship, slot, 'pcq')) {
|
||||||
ship.use(slot, ModuleUtils.findInternal('pcq', Math.min(slot.maxClass, 6), 'B')); // Passenger cabins top out at 6
|
ship.use(slot, ModuleUtils.findInternal('pcq', Math.min(slot.maxClass, 6), 'B')); // Passenger cabins top out at 6
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -94,7 +96,7 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
let clobber = event.getModifierState('Alt');
|
let clobber = event.getModifierState('Alt');
|
||||||
let ship = this.props.ship;
|
let ship = this.props.ship;
|
||||||
ship.internal.forEach((slot) => {
|
ship.internal.forEach((slot) => {
|
||||||
if (clobber || !slot.m) {
|
if ((clobber || !slot.m) && canMount(ship, slot, 'pcm')) {
|
||||||
ship.use(slot, ModuleUtils.findInternal('pcm', Math.min(slot.maxClass, 6), 'C')); // Passenger cabins top out at 6
|
ship.use(slot, ModuleUtils.findInternal('pcm', Math.min(slot.maxClass, 6), 'C')); // Passenger cabins top out at 6
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -110,7 +112,7 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
let clobber = event.getModifierState('Alt');
|
let clobber = event.getModifierState('Alt');
|
||||||
let ship = this.props.ship;
|
let ship = this.props.ship;
|
||||||
ship.internal.forEach((slot) => {
|
ship.internal.forEach((slot) => {
|
||||||
if (clobber || !slot.m) {
|
if ((clobber || !slot.m) && canMount(ship, slot, 'pci')) {
|
||||||
ship.use(slot, ModuleUtils.findInternal('pci', Math.min(slot.maxClass, 6), 'D')); // Passenger cabins top out at 6
|
ship.use(slot, ModuleUtils.findInternal('pci', Math.min(slot.maxClass, 6), 'D')); // Passenger cabins top out at 6
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -126,7 +128,7 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
let clobber = event.getModifierState('Alt');
|
let clobber = event.getModifierState('Alt');
|
||||||
let ship = this.props.ship;
|
let ship = this.props.ship;
|
||||||
ship.internal.forEach((slot) => {
|
ship.internal.forEach((slot) => {
|
||||||
if (clobber || !slot.m) {
|
if ((clobber || !slot.m) && canMount(ship, slot, 'pce')) {
|
||||||
ship.use(slot, ModuleUtils.findInternal('pce', Math.min(slot.maxClass, 6), 'E')); // Passenger cabins top out at 6
|
ship.use(slot, ModuleUtils.findInternal('pce', Math.min(slot.maxClass, 6), 'E')); // Passenger cabins top out at 6
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -143,7 +145,7 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
let ship = this.props.ship;
|
let ship = this.props.ship;
|
||||||
let chargeCap = 0; // Capacity of single activation
|
let chargeCap = 0; // Capacity of single activation
|
||||||
ship.internal.forEach(function(slot) {
|
ship.internal.forEach(function(slot) {
|
||||||
if ((!slot.m || (clobber && !ModuleUtils.isShieldGenerator(slot.m.grp))) && (!slot.eligible || slot.eligible.scb)) { // Check eligibility due to passenger ships special case
|
if ((clobber && !(slot.m && ModuleUtils.isShieldGenerator(slot.m.grp)) || !slot.m) && canMount(ship, slot, 'scb')) {
|
||||||
ship.use(slot, ModuleUtils.findInternal('scb', slot.maxClass, 'A'));
|
ship.use(slot, ModuleUtils.findInternal('scb', slot.maxClass, 'A'));
|
||||||
ship.setSlotEnabled(slot, chargeCap <= ship.shieldStrength); // Don't waste cell capacity on overcharge
|
ship.setSlotEnabled(slot, chargeCap <= ship.shieldStrength); // Don't waste cell capacity on overcharge
|
||||||
chargeCap += slot.m.recharge;
|
chargeCap += slot.m.recharge;
|
||||||
@@ -161,7 +163,7 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
let clobber = event.getModifierState('Alt');
|
let clobber = event.getModifierState('Alt');
|
||||||
let ship = this.props.ship;
|
let ship = this.props.ship;
|
||||||
ship.internal.forEach((slot) => {
|
ship.internal.forEach((slot) => {
|
||||||
if (clobber || !slot.m) {
|
if ((clobber || !slot.m) && canMount(ship, slot, 'hr')) {
|
||||||
ship.use(slot, ModuleUtils.findInternal('hr', Math.min(slot.maxClass, 5), 'D')); // Hull reinforcements top out at 5D
|
ship.use(slot, ModuleUtils.findInternal('hr', Math.min(slot.maxClass, 5), 'D')); // Hull reinforcements top out at 5D
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -169,6 +171,22 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
this._close();
|
this._close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fill all slots with Module Reinforcement Packages
|
||||||
|
* @param {SyntheticEvent} event Event
|
||||||
|
*/
|
||||||
|
_fillWithModuleReinforcementPackages(event) {
|
||||||
|
let clobber = event.getModifierState('Alt');
|
||||||
|
let ship = this.props.ship;
|
||||||
|
ship.internal.forEach((slot) => {
|
||||||
|
if ((clobber || !slot.m) && canMount(ship, slot, 'mrp')) {
|
||||||
|
ship.use(slot, ModuleUtils.findInternal('mrp', Math.min(slot.maxClass, 5), 'D')); // Module reinforcements top out at 5D
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.props.onChange();
|
||||||
|
this._close();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Empty all on section header right click
|
* Empty all on section header right click
|
||||||
*/
|
*/
|
||||||
@@ -226,6 +244,7 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
<li className='lc' onClick={this._fillWithCargo}>{translate('cargo')}</li>
|
<li className='lc' onClick={this._fillWithCargo}>{translate('cargo')}</li>
|
||||||
<li className='lc' onClick={this._fillWithCells}>{translate('scb')}</li>
|
<li className='lc' onClick={this._fillWithCells}>{translate('scb')}</li>
|
||||||
<li className='lc' onClick={this._fillWithArmor}>{translate('hr')}</li>
|
<li className='lc' onClick={this._fillWithArmor}>{translate('hr')}</li>
|
||||||
|
<li className='lc' onClick={this._fillWithModuleReinforcementPackages}>{translate('mrp')}</li>
|
||||||
<li className='lc' onClick={this._fillWithFuelTanks}>{translate('ft')}</li>
|
<li className='lc' onClick={this._fillWithFuelTanks}>{translate('ft')}</li>
|
||||||
<li className='lc' onClick={this._fillWithEconomyClassCabins}>{translate('pce')}</li>
|
<li className='lc' onClick={this._fillWithEconomyClassCabins}>{translate('pce')}</li>
|
||||||
<li className='lc' onClick={this._fillWithBusinessClassCabins}>{translate('pci')}</li>
|
<li className='lc' onClick={this._fillWithBusinessClassCabins}>{translate('pci')}</li>
|
||||||
|
|||||||
48
src/app/components/ModalHelp.jsx
Normal file
48
src/app/components/ModalHelp.jsx
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
/* eslint react/no-danger: 0 */
|
||||||
|
import React from 'react';
|
||||||
|
import { findDOMNode } from 'react-dom';
|
||||||
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Help Modal
|
||||||
|
*/
|
||||||
|
export default class ModalHelp extends TranslatedComponent {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
title: React.PropTypes.string
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param {Object} props React Component properties
|
||||||
|
*/
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Focus on textarea and select all
|
||||||
|
*/
|
||||||
|
componentDidMount() {
|
||||||
|
const e = findDOMNode(this.refs.exportField);
|
||||||
|
if (e) {
|
||||||
|
e.focus();
|
||||||
|
e.select();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the modal
|
||||||
|
* @return {React.Component} Modal Content
|
||||||
|
*/
|
||||||
|
render() {
|
||||||
|
const translate = this.context.language.translate;
|
||||||
|
const text = translate('HELP_TEXT');
|
||||||
|
|
||||||
|
return <div className='modal' onClick={ (e) => e.stopPropagation() }>
|
||||||
|
<h2>{translate(this.props.title || 'Help')}</h2>
|
||||||
|
<div dangerouslySetInnerHTML={{ __html: text }} />
|
||||||
|
<button className='r dismiss cap' onClick={this.context.hideModal}>{translate('close')}</button>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -346,6 +346,7 @@ export default class ModalImport extends TranslatedComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
// console.log(e.stack);
|
||||||
this.setState({ errorMsg: (typeof e == 'string') ? e : 'Cannot Parse the data!' });
|
this.setState({ errorMsg: (typeof e == 'string') ? e : 'Cannot Parse the data!' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export default class Modification extends TranslatedComponent {
|
|||||||
ship: React.PropTypes.object.isRequired,
|
ship: React.PropTypes.object.isRequired,
|
||||||
m: React.PropTypes.object.isRequired,
|
m: React.PropTypes.object.isRequired,
|
||||||
name: React.PropTypes.string.isRequired,
|
name: React.PropTypes.string.isRequired,
|
||||||
|
value: React.PropTypes.number.isRequired,
|
||||||
onChange: React.PropTypes.func.isRequired
|
onChange: React.PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -24,7 +25,7 @@ export default class Modification extends TranslatedComponent {
|
|||||||
constructor(props, context) {
|
constructor(props, context) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {};
|
this.state = {};
|
||||||
this.state.value = this.props.m.getModValue(this.props.name) / 100 || 0;
|
this.state.value = props.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -42,9 +43,9 @@ export default class Modification extends TranslatedComponent {
|
|||||||
scaledValue = 100000;
|
scaledValue = 100000;
|
||||||
value = 1000;
|
value = 1000;
|
||||||
}
|
}
|
||||||
if (scaledValue < -10000) {
|
if (scaledValue < -9999) {
|
||||||
scaledValue = -10000;
|
scaledValue = -9999;
|
||||||
value = -100;
|
value = -99.99;
|
||||||
}
|
}
|
||||||
|
|
||||||
let m = this.props.m;
|
let m = this.props.m;
|
||||||
@@ -61,11 +62,26 @@ export default class Modification extends TranslatedComponent {
|
|||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
let translate = this.context.language.translate;
|
let translate = this.context.language.translate;
|
||||||
let name = this.props.name;
|
let { m, name } = this.props;
|
||||||
|
|
||||||
|
if (name === 'damagedist') {
|
||||||
|
// We don't show damage distribution
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let symbol;
|
||||||
|
if (name === 'jitter') {
|
||||||
|
symbol = '°';
|
||||||
|
} else if (name !== 'burst' && name != 'burstrof') {
|
||||||
|
symbol = '%';
|
||||||
|
}
|
||||||
|
if (symbol) {
|
||||||
|
symbol = ' (' + symbol + ')';
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={'cb'} key={name}>
|
<div className={'cb'} key={name}>
|
||||||
<div className={'cb'}>{translate(name)}{name === 'jitter' ? ' (°)' : ' (%)'}</div>
|
<div className={'cb'}>{translate(name)}{symbol}</div>
|
||||||
<NumberEditor className={'cb'} style={{ width: '90%', textAlign: 'center' }} step={0.01} stepModifier={1} decimals={2} value={this.state.value} onValueChange={this._updateValue.bind(this)} />
|
<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>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import * as _ from 'lodash';
|
||||||
import { findDOMNode } from 'react-dom';
|
import { findDOMNode } from 'react-dom';
|
||||||
import TranslatedComponent from './TranslatedComponent';
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
import { isEmpty, stopCtxPropagation } from '../utils/UtilityFunctions';
|
||||||
import cn from 'classnames';
|
import cn from 'classnames';
|
||||||
import { MountFixed, MountGimballed, MountTurret } from './SvgIcons';
|
import { MountFixed, MountGimballed, MountTurret } from './SvgIcons';
|
||||||
import { Modifications } from 'coriolis-data/dist';
|
import { Modifications } from 'coriolis-data/dist';
|
||||||
@@ -26,23 +27,207 @@ export default class ModificationsMenu extends TranslatedComponent {
|
|||||||
constructor(props, context) {
|
constructor(props, context) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = this._initState(props, context);
|
this.state = this._initState(props, context);
|
||||||
|
|
||||||
|
this._toggleBlueprintsMenu = this._toggleBlueprintsMenu.bind(this);
|
||||||
|
this._rollWorst = this._rollWorst.bind(this);
|
||||||
|
this._rollRandom = this._rollRandom.bind(this);
|
||||||
|
this._rollAverage = this._rollAverage.bind(this);
|
||||||
|
this._rollBest = this._rollBest.bind(this);
|
||||||
|
this._reset = this._reset.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initiate the list of modifications
|
* Initialise state
|
||||||
* @param {Object} props React Component properties
|
* @param {Object} props React Component properties
|
||||||
* @param {Object} context React Component context
|
* @param {Object} context React Component context
|
||||||
* @return {Object} list: Array of React Components
|
* @return {Object} list: Array of React Components
|
||||||
*/
|
*/
|
||||||
_initState(props, context) {
|
_initState(props, context) {
|
||||||
let { m, onChange, ship } = props;
|
let { m, onChange, ship } = props;
|
||||||
let list = [];
|
|
||||||
|
|
||||||
for (let modName of Modifications.validity[m.grp]) {
|
let blueprints = [];
|
||||||
list.push(<Modification key={ modName } ship={ ship } m={ m } name={ modName } onChange={ onChange }/>);
|
for (const blueprintName in Modifications.modules[m.grp].blueprints) {
|
||||||
|
for (const grade of Modifications.modules[m.grp].blueprints[blueprintName]) {
|
||||||
|
const close = this._blueprintSelected.bind(this, Modifications.blueprints[blueprintName].id, grade);
|
||||||
|
const key = blueprintName + ':' + grade;
|
||||||
|
blueprints.push(<div style={{ cursor: 'pointer' }} key={ key } onClick={ close }>{Modifications.blueprints[blueprintName].name} grade {grade}</div>);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { list };
|
// Set up the modifications
|
||||||
|
const modifications = this._setModifications(props);
|
||||||
|
|
||||||
|
const blueprintMenuOpened = false;
|
||||||
|
|
||||||
|
// Set up the specials for this module
|
||||||
|
// const specials = _selectSpecials(m);
|
||||||
|
|
||||||
|
return { blueprintMenuOpened, blueprints, modifications };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise the modifications
|
||||||
|
* @param {Object} props React Component properties
|
||||||
|
* @return {Object} list: Array of React Components
|
||||||
|
*/
|
||||||
|
_setModifications(props) {
|
||||||
|
const { m, onChange, ship } = props;
|
||||||
|
let modifications = [];
|
||||||
|
for (const modName of Modifications.modules[m.grp].modifications) {
|
||||||
|
if (Modifications.modifications[modName].type === 'percentage' || Modifications.modifications[modName].type === 'numeric') {
|
||||||
|
const key = modName + (m.getModValue(modName) / 100 || 0);
|
||||||
|
modifications.push(<Modification key={ key } ship={ ship } m={ m } name={ modName } value={ m.getModValue(modName) / 100 || 0 } onChange={ onChange }/>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return modifications;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle the blueprints menu
|
||||||
|
*/
|
||||||
|
_toggleBlueprintsMenu() {
|
||||||
|
const blueprintMenuOpened = !this.state.blueprintMenuOpened;
|
||||||
|
this.setState({ blueprintMenuOpened });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activated when a blueprint is selected
|
||||||
|
* @param {int} blueprintId The ID of the selected blueprint
|
||||||
|
* @param {int} grade The grade of the selected blueprint
|
||||||
|
*/
|
||||||
|
_blueprintSelected(blueprintId, grade) {
|
||||||
|
const { m } = this.props;
|
||||||
|
const blueprint = Object.assign({}, _.find(Modifications.blueprints, function(o) { return o.id === blueprintId; }));
|
||||||
|
blueprint.grade = grade;
|
||||||
|
m.blueprint = blueprint;
|
||||||
|
|
||||||
|
const blueprintMenuOpened = false;
|
||||||
|
this.setState({ blueprintMenuOpened });
|
||||||
|
this.props.onChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide a 'worst' roll within the information we have
|
||||||
|
*/
|
||||||
|
_rollWorst() {
|
||||||
|
const { m, ship } = this.props;
|
||||||
|
const features = m.blueprint.features[m.blueprint.grade];
|
||||||
|
for (const featureName in features) {
|
||||||
|
if (Modifications.modifications[featureName].method == 'overwrite') {
|
||||||
|
ship.setModification(m, featureName, features[featureName][1]);
|
||||||
|
} else {
|
||||||
|
let value = features[featureName][0];
|
||||||
|
if (m.grp == 'sb' && featureName == 'shieldboost') {
|
||||||
|
// Shield boosters are a special case. Their boost is dependent on their base so we need to calculate the value here
|
||||||
|
value = ((1 + m.shieldboost) * (1 + value) - 1) / m.shieldboost - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Modifications.modifications[featureName].type == 'percentage') {
|
||||||
|
ship.setModification(m, featureName, value * 10000);
|
||||||
|
} else if (Modifications.modifications[featureName].type == 'numeric') {
|
||||||
|
ship.setModification(m, featureName, value * 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({ modifications: this._setModifications(this.props) });
|
||||||
|
this.props.onChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide an 'average' roll within the information we have
|
||||||
|
*/
|
||||||
|
_rollAverage() {
|
||||||
|
const { m, ship } = this.props;
|
||||||
|
const features = m.blueprint.features[m.blueprint.grade];
|
||||||
|
for (const featureName in features) {
|
||||||
|
if (Modifications.modifications[featureName].method == 'overwrite') {
|
||||||
|
ship.setModification(m, featureName, (features[featureName][0] + features[featureName][1]) / 2);
|
||||||
|
} else {
|
||||||
|
let value = (features[featureName][0] + features[featureName][1]) / 2;
|
||||||
|
if (m.grp == 'sb' && featureName == 'shieldboost') {
|
||||||
|
// Shield boosters are a special case. Their boost is dependent on their base so we need to calculate the value here
|
||||||
|
value = ((1 + m.shieldboost) * (1 + value) - 1) / m.shieldboost - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Modifications.modifications[featureName].type == 'percentage') {
|
||||||
|
ship.setModification(m, featureName, value * 10000);
|
||||||
|
} else if (Modifications.modifications[featureName].type == 'numeric') {
|
||||||
|
ship.setModification(m, featureName, value * 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({ modifications: this._setModifications(this.props) });
|
||||||
|
this.props.onChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide a random roll within the information we have
|
||||||
|
*/
|
||||||
|
_rollRandom() {
|
||||||
|
const { m, ship } = this.props;
|
||||||
|
const features = m.blueprint.features[m.blueprint.grade];
|
||||||
|
for (const featureName in features) {
|
||||||
|
if (Modifications.modifications[featureName].method == 'overwrite') {
|
||||||
|
ship.setModification(m, featureName, features[featureName][1]);
|
||||||
|
} else {
|
||||||
|
let value = features[featureName][0] + (Math.random() * (features[featureName][1] - features[featureName][0]));
|
||||||
|
if (m.grp == 'sb' && featureName == 'shieldboost') {
|
||||||
|
// Shield boosters are a special case. Their boost is dependent on their base so we need to calculate the value here
|
||||||
|
value = ((1 + m.shieldboost) * (1 + value) - 1) / m.shieldboost - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Modifications.modifications[featureName].type == 'percentage') {
|
||||||
|
ship.setModification(m, featureName, value * 10000);
|
||||||
|
} else if (Modifications.modifications[featureName].type == 'numeric') {
|
||||||
|
ship.setModification(m, featureName, value * 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({ modifications: this._setModifications(this.props) });
|
||||||
|
this.props.onChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide a 'best' roll within the information we have
|
||||||
|
*/
|
||||||
|
_rollBest() {
|
||||||
|
const { m, ship } = this.props;
|
||||||
|
const features = m.blueprint.features[m.blueprint.grade];
|
||||||
|
for (const featureName in features) {
|
||||||
|
if (Modifications.modifications[featureName].method == 'overwrite') {
|
||||||
|
ship.setModification(m, featureName, features[featureName][1]);
|
||||||
|
} else {
|
||||||
|
let value = features[featureName][1];
|
||||||
|
if (m.grp == 'sb' && featureName == 'shieldboost') {
|
||||||
|
// Shield boosters are a special case. Their boost is dependent on their base so we need to calculate the value here
|
||||||
|
value = ((1 + m.shieldboost) * (1 + value) - 1) / m.shieldboost - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Modifications.modifications[featureName].type == 'percentage') {
|
||||||
|
ship.setModification(m, featureName, value * 10000);
|
||||||
|
} else if (Modifications.modifications[featureName].type == 'numeric') {
|
||||||
|
ship.setModification(m, featureName, value * 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({ modifications: this._setModifications(this.props) });
|
||||||
|
this.props.onChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset modification information
|
||||||
|
*/
|
||||||
|
_reset() {
|
||||||
|
const { m, ship } = this.props;
|
||||||
|
ship.clearModifications(m);
|
||||||
|
ship.clearBlueprint(m);
|
||||||
|
|
||||||
|
this.setState({ modifications: this._setModifications(this.props) });
|
||||||
|
this.props.onChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -50,15 +235,53 @@ export default class ModificationsMenu extends TranslatedComponent {
|
|||||||
* @return {React.Component} List
|
* @return {React.Component} List
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
let { tooltip, termtip } = this.context;
|
const language = this.context.language;
|
||||||
|
const translate = language.translate;
|
||||||
|
const { tooltip, termtip } = this.context;
|
||||||
|
const { m } = this.props;
|
||||||
|
const { blueprintMenuOpened } = this.state;
|
||||||
|
|
||||||
|
const _toggleBlueprintsMenu = this._toggleBlueprintsMenu;
|
||||||
|
const _rollBest = this._rollBest;
|
||||||
|
const _rollWorst = this._rollWorst;
|
||||||
|
const _rollAverage = this._rollAverage;
|
||||||
|
const _rollRandom = this._rollRandom;
|
||||||
|
const _reset = this._reset;
|
||||||
|
|
||||||
|
let blueprintLabel;
|
||||||
|
let haveBlueprint = false;
|
||||||
|
if (m.blueprint && !isEmpty(m.blueprint)) {
|
||||||
|
blueprintLabel = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
|
||||||
|
haveBlueprint = true;
|
||||||
|
} else {
|
||||||
|
blueprintLabel = translate('PHRASE_SELECT_BLUEPRINT');
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn('select', this.props.className)}
|
className={cn('select', this.props.className)}
|
||||||
onClick={(e) => e.stopPropagation() }
|
onClick={(e) => e.stopPropagation() }
|
||||||
onContextMenu={stopCtxPropagation}
|
onContextMenu={stopCtxPropagation}
|
||||||
onMouseOver={termtip.bind(null, 'HELP_MODIFICATIONS_MENU')} onMouseOut={tooltip.bind(null, null)}
|
|
||||||
>
|
>
|
||||||
{this.state.list}
|
<div className={ cn('section-menu', { selected: true })} style={{ cursor: 'pointer' }} onClick={_toggleBlueprintsMenu}>{blueprintLabel}</div>
|
||||||
|
{ blueprintMenuOpened ? this.state.blueprints : '' }
|
||||||
|
{ haveBlueprint ?
|
||||||
|
<table style={{ width: '100%', backgroundColor: 'transparent' }}>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td> { translate('roll') }: </td>
|
||||||
|
<td style={{ cursor: 'pointer' }} onClick={_rollWorst} onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_WORST')} onMouseOut={tooltip.bind(null, null)}> { translate('worst') } </td>
|
||||||
|
<td style={{ cursor: 'pointer' }} onClick={_rollAverage}onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_AVERAGE')} onMouseOut={tooltip.bind(null, null)}> { translate('average') } </td>
|
||||||
|
<td style={{ cursor: 'pointer' }} onClick={_rollBest}onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_BEST')} onMouseOut={tooltip.bind(null, null)}> { translate('best') } </td>
|
||||||
|
<td style={{ cursor: 'pointer' }} onClick={_rollRandom} onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_RANDOM')} onMouseOut={tooltip.bind(null, null)}> { translate('random') } </td>
|
||||||
|
<td style={{ cursor: 'pointer' }} onClick={_reset}onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_RESET')} onMouseOut={tooltip.bind(null, null)}> { translate('reset') } </td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table> : '' }
|
||||||
|
{ blueprintMenuOpened ? '' :
|
||||||
|
<span onMouseOver={termtip.bind(null, 'HELP_MODIFICATIONS_MENU')} onMouseOut={tooltip.bind(null, null)} >
|
||||||
|
{ this.state.modifications }
|
||||||
|
</span> }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
91
src/app/components/MovementSummary.jsx
Normal file
91
src/app/components/MovementSummary.jsx
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import cn from 'classnames';
|
||||||
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Movement summary
|
||||||
|
*/
|
||||||
|
export default class MovementSummary extends TranslatedComponent {
|
||||||
|
static PropTypes = {
|
||||||
|
ship: React.PropTypes.object.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param {Object} props React Component properties
|
||||||
|
*/
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render movement summary
|
||||||
|
* @return {React.Component} contents
|
||||||
|
*/
|
||||||
|
render() {
|
||||||
|
let ship = this.props.ship;
|
||||||
|
let { language, tooltip, termtip } = this.context;
|
||||||
|
let { formats, translate, units } = language;
|
||||||
|
let hide = tooltip.bind(null, null);
|
||||||
|
let boostMultiplier = ship.topBoost / ship.topSpeed;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
<h1>{translate('movement summary')}</h1>
|
||||||
|
<table style={{ marginLeft: 'auto', marginRight: 'auto', lineHeight: '1em', backgroundColor: 'transparent', borderSpacing: '0.5em' }}>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td > </td>
|
||||||
|
<td colSpan='6'>{translate('engine pips')}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td> </td>
|
||||||
|
<td>0</td>
|
||||||
|
<td>1</td>
|
||||||
|
<td>2</td>
|
||||||
|
<td>3</td>
|
||||||
|
<td>4</td>
|
||||||
|
<td onMouseOver={termtip.bind(null, '4b')} onMouseOut={tooltip.bind(null, null)}>4B</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td className='ri'>{translate('speed')} ({units['m/s']})</td>
|
||||||
|
<td className='ri'>{formats.int(ship.speeds[0])}</td>
|
||||||
|
<td className='ri'>{formats.int(ship.speeds[1])}</td>
|
||||||
|
<td className='ri'>{formats.int(ship.speeds[2])}</td>
|
||||||
|
<td className='ri'>{formats.int(ship.speeds[3])}</td>
|
||||||
|
<td className='ri'>{formats.int(ship.speeds[4])}</td>
|
||||||
|
<td className='ri'>{formats.int(ship.speeds[4] * boostMultiplier)}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td className='ri'>{translate('pitch')} ({units['°/s']})</td>
|
||||||
|
<td className='ri'>{formats.int(ship.pitches[0])}</td>
|
||||||
|
<td className='ri'>{formats.int(ship.pitches[1])}</td>
|
||||||
|
<td className='ri'>{formats.int(ship.pitches[2])}</td>
|
||||||
|
<td className='ri'>{formats.int(ship.pitches[3])}</td>
|
||||||
|
<td className='ri'>{formats.int(ship.pitches[4])}</td>
|
||||||
|
<td className='ri'>{formats.int(ship.pitches[4] * boostMultiplier)}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td className='ri'>{translate('roll')} ({units['°/s']})</td>
|
||||||
|
<td className='ri'>{formats.int(ship.rolls[0])}</td>
|
||||||
|
<td className='ri'>{formats.int(ship.rolls[1])}</td>
|
||||||
|
<td className='ri'>{formats.int(ship.rolls[2])}</td>
|
||||||
|
<td className='ri'>{formats.int(ship.rolls[3])}</td>
|
||||||
|
<td className='ri'>{formats.int(ship.rolls[4])}</td>
|
||||||
|
<td className='ri'>{formats.int(ship.rolls[4] * boostMultiplier)}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td className='ri'>{translate('yaw')} ({units['°/s']})</td>
|
||||||
|
<td className='ri'>{formats.int(ship.yaws[0])}</td>
|
||||||
|
<td className='ri'>{formats.int(ship.yaws[1])}</td>
|
||||||
|
<td className='ri'>{formats.int(ship.yaws[2])}</td>
|
||||||
|
<td className='ri'>{formats.int(ship.yaws[3])}</td>
|
||||||
|
<td className='ri'>{formats.int(ship.yaws[4])}</td>
|
||||||
|
<td className='ri'>{formats.int(ship.yaws[4] * boostMultiplier)}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import cn from 'classnames';
|
import cn from 'classnames';
|
||||||
import TranslatedComponent from './TranslatedComponent';
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
import { DamageKinetic, DamageThermal, DamageExplosive } from './SvgIcons';
|
import { DamageAbsolute, DamageKinetic, DamageThermal, DamageExplosive } from './SvgIcons';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Offence summary
|
* Offence summary
|
||||||
@@ -34,30 +34,33 @@ export default class OffenceSummary extends TranslatedComponent {
|
|||||||
<table className='summary' style={{ marginLeft: 'auto', marginRight: 'auto', lineHeight: '1em', backgroundColor: 'transparent' }}>
|
<table className='summary' style={{ marginLeft: 'auto', marginRight: 'auto', lineHeight: '1em', backgroundColor: 'transparent' }}>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td colSpan='4' className='summary'><h2>{translate('dps')}: {formats.f1(ship.totalDps)}</h2></td>
|
<td colSpan='5' className='summary'><h2>{translate('dps')}: {formats.f1(ship.totalDps)}</h2></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td className='le'>{translate('damage by')}</td>
|
<td className='le'>{translate('damage by')}</td>
|
||||||
|
<td className='ri' onMouseOver={termtip.bind(null, 'absolute')} onMouseOut={tooltip.bind(null, null)}><DamageAbsolute /> {formats.f1(ship.totalAbsDps)}</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, '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, '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>
|
<td className='ri' onMouseOver={termtip.bind(null, 'thermal')} onMouseOut={tooltip.bind(null, null)}><DamageThermal /> {formats.f1(ship.totalThermDps)}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td colSpan='4' className='summary'><h2>{translate('sdps')}: {formats.f1(ship.totalSDps)}</h2></td>
|
<td colSpan='5' className='summary'><h2>{translate('sdps')}: {formats.f1(ship.totalSDps)}</h2></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td className='le'>{translate('damage by')}</td>
|
<td className='le'>{translate('damage by')}</td>
|
||||||
|
<td className='ri' onMouseOver={termtip.bind(null, 'absolute')} onMouseOut={tooltip.bind(null, null)}><DamageAbsolute /> {formats.f1(ship.totalAbsSDps)}</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, '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, '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>
|
<td className='ri' onMouseOver={termtip.bind(null, 'thermal')} onMouseOut={tooltip.bind(null, null)}><DamageThermal /> {formats.f1(ship.totalThermSDps)}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td colSpan='4' className='summary'><h2>{translate('dpe')}: {formats.f1(ship.totalDpe)}</h2></td>
|
<td colSpan='5' className='summary'><h2>{translate('dpe')}: {formats.f1(ship.totalDpe)}</h2></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td className='le'>{translate('damage by')}</td>
|
<td className='le'>{translate('damage by')}</td>
|
||||||
|
<td className='ri' onMouseOver={termtip.bind(null, 'absolute')} onMouseOut={tooltip.bind(null, null)}><DamageAbsolute /> {formats.f1(ship.totalAbsDpe)}</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, '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, '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>
|
<td className='ri' onMouseOver={termtip.bind(null, 'thermal')} onMouseOut={tooltip.bind(null, null)}><DamageThermal /> {formats.f1(ship.totalThermDpe)}</td>
|
||||||
|
|||||||
90
src/app/components/ShipSelector.jsx
Normal file
90
src/app/components/ShipSelector.jsx
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import cn from 'classnames';
|
||||||
|
import { Ships } from 'coriolis-data/dist';
|
||||||
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
|
import { Rocket } from './SvgIcons';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selector for ships
|
||||||
|
*/
|
||||||
|
export default class ShipSelector extends TranslatedComponent {
|
||||||
|
static PropTypes = {
|
||||||
|
initial: React.PropTypes.object.isRequired,
|
||||||
|
onChange: React.PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param {Object} props React Component properties
|
||||||
|
*/
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = { ship : this.props.initial };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate the ships menu
|
||||||
|
* @return {React.Component} Menu
|
||||||
|
*/
|
||||||
|
_getShipsMenu() {
|
||||||
|
const _selectShip = this._selectShip;
|
||||||
|
const _openMenu = this._openMenu;
|
||||||
|
|
||||||
|
let shipList = [];
|
||||||
|
|
||||||
|
for (let s in Ships) {
|
||||||
|
shipList.push(<div key={s} onClick={_selectShip.bind(this, s)} className='block' >{Ships[s].properties.name}</div>);
|
||||||
|
}
|
||||||
|
|
||||||
|
return shipList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle opening the menu
|
||||||
|
* @param {string} menu The ID of the opened menu
|
||||||
|
* @param {SyntheticEvent} event Event
|
||||||
|
*/
|
||||||
|
_openMenu(menu, event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
if (this.props.currentMenu == menu) {
|
||||||
|
menu = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.context.openMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle selection of a ship
|
||||||
|
* @param {string} s The selected ship ID
|
||||||
|
*/
|
||||||
|
_selectShip(s) {
|
||||||
|
this.setState({ ship: Ships[s] });
|
||||||
|
|
||||||
|
this.context.openMenu(null);
|
||||||
|
this.props.onChange(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render ship selector
|
||||||
|
* @return {React.Component} contents
|
||||||
|
*/
|
||||||
|
render() {
|
||||||
|
const currentMenu = this.props.currentMenu;
|
||||||
|
const ship = this.state.ship;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='shipselector'>
|
||||||
|
<div className='menu'>
|
||||||
|
<div className={cn('menu-header', { selected: currentMenu == 'wds' })} onClick={this._openMenu.bind(this, 'wds')}>
|
||||||
|
<Rocket className='warning' /><span className='menu-item-label'>{ship.properties.name}</span>
|
||||||
|
{currentMenu == 'wds' ?
|
||||||
|
<div className='menu-list quad no-wrap' onClick={ (e) => e.stopPropagation() }>
|
||||||
|
{this._getShipsMenu()}
|
||||||
|
</div> : null }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -43,6 +43,7 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
|||||||
<th onMouseEnter={termtip.bind(null, 'damage per second')} onMouseLeave={hide} rowSpan={2}>{translate('DPS')}</th>
|
<th onMouseEnter={termtip.bind(null, 'damage per second')} onMouseLeave={hide} rowSpan={2}>{translate('DPS')}</th>
|
||||||
<th onMouseEnter={termtip.bind(null, 'energy per second')} onMouseLeave={hide} rowSpan={2}>{translate('EPS')}</th>
|
<th onMouseEnter={termtip.bind(null, 'energy per second')} onMouseLeave={hide} rowSpan={2}>{translate('EPS')}</th>
|
||||||
<th onMouseEnter={termtip.bind(null, 'heat per second')} onMouseLeave={hide} rowSpan={2}>{translate('HPS')}</th>
|
<th onMouseEnter={termtip.bind(null, 'heat per second')} onMouseLeave={hide} rowSpan={2}>{translate('HPS')}</th>
|
||||||
|
<th rowSpan={2}>{translate('hardness')}</th>
|
||||||
<th rowSpan={2}>{translate('armour')}</th>
|
<th rowSpan={2}>{translate('armour')}</th>
|
||||||
<th rowSpan={2}>{translate('shields')}</th>
|
<th rowSpan={2}>{translate('shields')}</th>
|
||||||
<th colSpan={3}>{translate('mass')}</th>
|
<th colSpan={3}>{translate('mass')}</th>
|
||||||
@@ -71,6 +72,7 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
|||||||
<td>{f1(ship.totalDps)}</td>
|
<td>{f1(ship.totalDps)}</td>
|
||||||
<td>{f1(ship.totalEps)}</td>
|
<td>{f1(ship.totalEps)}</td>
|
||||||
<td>{f1(ship.totalHps)}</td>
|
<td>{f1(ship.totalHps)}</td>
|
||||||
|
<td>{int(ship.hardness)}</td>
|
||||||
<td>{int(ship.armour)}</td>
|
<td>{int(ship.armour)}</td>
|
||||||
<td className={sgClassNames}>{int(ship.shield)} {u.MJ}</td>
|
<td className={sgClassNames}>{int(ship.shield)} {u.MJ}</td>
|
||||||
<td>{ship.hullMass} {u.T}</td>
|
<td>{ship.hullMass} {u.T}</td>
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ export default class SlotSection extends TranslatedComponent {
|
|||||||
_drag(originSlot, e) {
|
_drag(originSlot, e) {
|
||||||
e.dataTransfer.setData('text/html', e.currentTarget);
|
e.dataTransfer.setData('text/html', e.currentTarget);
|
||||||
e.dataTransfer.effectAllowed = 'all';
|
e.dataTransfer.effectAllowed = 'all';
|
||||||
this.setState({ originSlot });
|
this.setState({ originSlot, copy: e.getModifierState('Alt') });
|
||||||
this._close();
|
this._close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,7 +91,9 @@ export default class SlotSection extends TranslatedComponent {
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
let os = this.state.originSlot;
|
let os = this.state.originSlot;
|
||||||
if (os) {
|
if (os) {
|
||||||
e.dataTransfer.dropEffect = os != targetSlot && canMount(this.props.ship, targetSlot, os.m.grp, os.m.class) ? 'copyMove' : 'none';
|
// Show correct icon
|
||||||
|
const effect = this.state.copy ? 'copy' : 'move';
|
||||||
|
e.dataTransfer.dropEffect = os != targetSlot && canMount(this.props.ship, targetSlot, os.m.grp, os.m.class) ? effect : 'none';
|
||||||
this.setState({ targetSlot });
|
this.setState({ targetSlot });
|
||||||
} else {
|
} else {
|
||||||
e.dataTransfer.dropEffect = 'none';
|
e.dataTransfer.dropEffect = 'none';
|
||||||
@@ -114,20 +116,30 @@ export default class SlotSection extends TranslatedComponent {
|
|||||||
* the origin slot will be empty.
|
* the origin slot will be empty.
|
||||||
*/
|
*/
|
||||||
_drop() {
|
_drop() {
|
||||||
let { originSlot, targetSlot } = this.state;
|
let { originSlot, targetSlot, copy } = this.state;
|
||||||
let m = originSlot.m;
|
let m = originSlot.m;
|
||||||
|
|
||||||
if (targetSlot && m && canMount(this.props.ship, targetSlot, m.grp, m.class)) {
|
if (copy) {
|
||||||
// Swap modules if possible
|
// We want to copy the module in to the target slot
|
||||||
if (targetSlot.m && canMount(this.props.ship, originSlot, targetSlot.m.grp, targetSlot.m.class)) {
|
if (targetSlot && canMount(this.props.ship, targetSlot, m.grp, m.class)) {
|
||||||
this.props.ship.use(originSlot, targetSlot.m, true);
|
const mCopy = m.clone();
|
||||||
} else { // Otherwise empty the origin slot
|
this.props.ship.use(targetSlot, mCopy);
|
||||||
this.props.ship.use(originSlot, null, true); // Empty but prevent summary update
|
this.props.onChange();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// We want to move the module in to the target slot, and swap back any module that was originally in the target slot
|
||||||
|
if (targetSlot && m && canMount(this.props.ship, targetSlot, m.grp, m.class)) {
|
||||||
|
// Swap modules if possible
|
||||||
|
if (targetSlot.m && canMount(this.props.ship, originSlot, targetSlot.m.grp, targetSlot.m.class)) {
|
||||||
|
this.props.ship.use(originSlot, targetSlot.m, true);
|
||||||
|
} else { // Otherwise empty the origin slot
|
||||||
|
this.props.ship.use(originSlot, null, true); // Empty but prevent summary update
|
||||||
|
}
|
||||||
|
this.props.ship.use(targetSlot, m); // update target slot
|
||||||
|
this.props.onChange();
|
||||||
}
|
}
|
||||||
this.props.ship.use(targetSlot, m); // update target slot
|
|
||||||
this.props.onChange();
|
|
||||||
}
|
}
|
||||||
this.setState({ originSlot: null, targetSlot: null });
|
this.setState({ originSlot: null, targetSlot: null, copy: null });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -46,10 +46,16 @@ export default class StandardSlot extends TranslatedComponent {
|
|||||||
let m = slot.m;
|
let m = slot.m;
|
||||||
let classRating = m.class + m.rating;
|
let classRating = m.class + m.rating;
|
||||||
let menu;
|
let menu;
|
||||||
let validMods = m == null ? [] : (Modifications.validity[m.grp] || []);
|
let validMods = m == null ? [] : (Modifications.modules[m.grp].modifications || []);
|
||||||
let showModuleResistances = Persist.showModuleResistances();
|
let showModuleResistances = Persist.showModuleResistances();
|
||||||
let mass = m.getMass() || m.cargo || m.fuel || 0;
|
let mass = m.getMass() || m.cargo || m.fuel || 0;
|
||||||
|
|
||||||
|
// Modifications tooltip shows blueprint and grade, if available
|
||||||
|
let modTT = translate('modified');
|
||||||
|
if (m && m.blueprint && m.blueprint.name) {
|
||||||
|
modTT = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
|
||||||
|
}
|
||||||
|
|
||||||
if (!selected) {
|
if (!selected) {
|
||||||
// If not selected then sure that modifications flag is unset
|
// If not selected then sure that modifications flag is unset
|
||||||
this._modificationsSelected = false;
|
this._modificationsSelected = false;
|
||||||
@@ -81,7 +87,7 @@ export default class StandardSlot extends TranslatedComponent {
|
|||||||
<div className={cn('details-container', { warning: warning && warning(slot.m) })}>
|
<div className={cn('details-container', { warning: warning && warning(slot.m) })}>
|
||||||
<div className={'sz'}>{slot.maxClass}</div>
|
<div className={'sz'}>{slot.maxClass}</div>
|
||||||
<div>
|
<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, 'modified')} onMouseOut={tooltip.bind(null, null)}><Modified /></span> : null }</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 className={'r'}>{formats.round(mass)}{units.T}</div>
|
||||||
<div/>
|
<div/>
|
||||||
<div className={'cb'}>
|
<div className={'cb'}>
|
||||||
|
|||||||
@@ -319,6 +319,24 @@ export class Warning extends SvgIcon {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Absolute damage
|
||||||
|
*/
|
||||||
|
export class DamageAbsolute 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 <ellipse cx='100' cy='100' rx='90' ry='90' fillOpacity='0' />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thermal damage
|
* Thermal damage
|
||||||
*/
|
*/
|
||||||
@@ -475,6 +493,52 @@ export class MountTurret extends SvgIcon {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collapse section
|
||||||
|
*/
|
||||||
|
export class CollapseSection extends SvgIcon {
|
||||||
|
/**
|
||||||
|
* Overriden view box
|
||||||
|
* @return {String} view box
|
||||||
|
*/
|
||||||
|
viewBox() { return '0 0 200 200'; }
|
||||||
|
/**
|
||||||
|
* Generate the SVG
|
||||||
|
* @return {React.Component} SVG Contents
|
||||||
|
*/
|
||||||
|
svg() {
|
||||||
|
return <g>
|
||||||
|
<path d='m 100,180 0,-140' />
|
||||||
|
<path d='m 100,40 25,45' />
|
||||||
|
<path d='m 100,40 -25,45' />
|
||||||
|
<path d='m 20,20 160,0' />
|
||||||
|
</g>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expand section
|
||||||
|
*/
|
||||||
|
export class ExpandSection extends SvgIcon {
|
||||||
|
/**
|
||||||
|
* Overriden view box
|
||||||
|
* @return {String} view box
|
||||||
|
*/
|
||||||
|
viewBox() { return '0 0 200 200'; }
|
||||||
|
/**
|
||||||
|
* Generate the SVG
|
||||||
|
* @return {React.Component} SVG Contents
|
||||||
|
*/
|
||||||
|
svg() {
|
||||||
|
return <g>
|
||||||
|
<path d='m 100,20 0,140' />
|
||||||
|
<path d='m 100,160 25,-45' />
|
||||||
|
<path d='m 100,160 -25,-45' />
|
||||||
|
<path d='m 20,180 160,0' />
|
||||||
|
</g>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rocket ship
|
* Rocket ship
|
||||||
*/
|
*/
|
||||||
@@ -488,6 +552,25 @@ export class Rocket extends SvgIcon {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Help
|
||||||
|
*/
|
||||||
|
export class Help 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 <path d="M90.381 140.644a225.03 225.03 0 0 1-.106-5.706c0-7.47 1.058-13.92 3.172-19.346 1.55-4.087 4.052-8.21 7.505-12.367 2.536-3.031 7.1-7.453 13.688-13.267 6.59-5.815 10.872-10.448 12.844-13.9 1.974-3.453 2.96-7.224 2.96-11.312 0-7.4-2.89-13.9-8.667-19.503-5.779-5.602-12.862-8.403-21.248-8.403-8.104 0-14.87 2.536-20.296 7.61-5.427 5.074-8.986 13.003-10.677 23.784L50 65.91c1.762-14.448 6.995-25.511 15.698-33.194 8.704-7.68 20.208-11.522 34.514-11.522 15.152 0 27.237 4.124 36.258 12.369C145.49 41.807 150 51.779 150 63.477c0 6.766-1.585 13.003-4.756 18.712-3.17 5.708-9.372 12.65-18.605 20.824-6.201 5.496-10.253 9.55-12.156 12.156-1.904 2.609-3.312 5.603-4.23 8.986-.915 3.383-1.443 8.88-1.584 16.49H90.38zm-1.163 38.162v-21.67h21.67v21.67h-21.67z"/>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ListModifications (engineers)
|
* ListModifications (engineers)
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -65,7 +65,9 @@ export function getLanguage(langCode) {
|
|||||||
LY: <u> {translate('LY')}</u>, // Light Years
|
LY: <u> {translate('LY')}</u>, // Light Years
|
||||||
MJ: <u> {translate('MJ')}</u>, // Mega Joules
|
MJ: <u> {translate('MJ')}</u>, // Mega Joules
|
||||||
'm/s': <u> {translate('m/s')}</u>, // Meters per second
|
'm/s': <u> {translate('m/s')}</u>, // Meters per second
|
||||||
|
'°/s': <u> {translate('°/s')}</u>, // Degrees per second
|
||||||
MW: <u> {translate('MW')}</u>, // Mega Watts (same as Mega Joules per second)
|
MW: <u> {translate('MW')}</u>, // Mega Watts (same as Mega Joules per second)
|
||||||
|
mps: <u>{translate('m/s')}</u>, // Metres per second
|
||||||
ps: <u>{translate('/s')}</u>, // per second
|
ps: <u>{translate('/s')}</u>, // per second
|
||||||
pm: <u>{translate('/min')}</u>, // per minute
|
pm: <u>{translate('/min')}</u>, // per minute
|
||||||
s: <u>{translate('secs')}</u>, // Seconds
|
s: <u>{translate('secs')}</u>, // Seconds
|
||||||
|
|||||||
@@ -28,6 +28,13 @@ export const terms = {
|
|||||||
PHRASE_SG_RECOVER: 'Recovery (to 50%) after collapse',
|
PHRASE_SG_RECOVER: 'Recovery (to 50%) after collapse',
|
||||||
PHRASE_UNLADEN: 'Ship mass excluding fuel and cargo',
|
PHRASE_UNLADEN: 'Ship mass excluding fuel and cargo',
|
||||||
PHRASE_UPDATE_RDY: 'Update Available! Click to refresh',
|
PHRASE_UPDATE_RDY: 'Update Available! Click to refresh',
|
||||||
|
PHRASE_ENGAGEMENT_RANGE: 'The distance between your ship and its target',
|
||||||
|
PHRASE_SELECT_BLUEPRINT: 'Click to select a blueprint',
|
||||||
|
PHRASE_BLUEPRINT_WORST: 'Worst primary values for this blueprint',
|
||||||
|
PHRASE_BLUEPRINT_AVERAGE: 'Average primary values for this blueprint',
|
||||||
|
PHRASE_BLUEPRINT_RANDOM: 'Random selection between worst and best primary values for this blueprint',
|
||||||
|
PHRASE_BLUEPRINT_BEST: 'Best primary values for this blueprint',
|
||||||
|
PHRASE_BLUEPRINT_RESET: 'Remove all modifications and blueprint',
|
||||||
|
|
||||||
HELP_MODIFICATIONS_MENU: 'Click on a number to enter a new value, or drag along the bar for small changes',
|
HELP_MODIFICATIONS_MENU: 'Click on a number to enter a new value, or drag along the bar for small changes',
|
||||||
|
|
||||||
@@ -59,6 +66,7 @@ export const terms = {
|
|||||||
mc: 'Multi-cannon',
|
mc: 'Multi-cannon',
|
||||||
ml: 'Mining Laser',
|
ml: 'Mining Laser',
|
||||||
mr: 'Missile Rack',
|
mr: 'Missile Rack',
|
||||||
|
mrp: 'Module Reinforcement Package',
|
||||||
nl: 'Mine Launcher',
|
nl: 'Mine Launcher',
|
||||||
pa: 'Plasma Accelerator',
|
pa: 'Plasma Accelerator',
|
||||||
pas: 'Planetary Approach Suite',
|
pas: 'Planetary Approach Suite',
|
||||||
@@ -86,15 +94,28 @@ export const terms = {
|
|||||||
ws: 'Frame Shift Wake Scanner',
|
ws: 'Frame Shift Wake Scanner',
|
||||||
|
|
||||||
// Items on the outfitting page
|
// Items on the outfitting page
|
||||||
// Notification of restricted slot for Orca/Beluga
|
// Notification of restricted slot
|
||||||
emptyrestricted: 'empty (restricted)',
|
emptyrestricted: 'empty (restricted)',
|
||||||
|
'damage dealt against': 'Damage dealt against',
|
||||||
|
'damage received by': 'Damage received by',
|
||||||
|
'against shields': 'Against shields',
|
||||||
|
'against hull': 'Against hull',
|
||||||
// 'ammo' was overloaded for outfitting page and modul info, so changed to ammunition for outfitting page
|
// 'ammo' was overloaded for outfitting page and modul info, so changed to ammunition for outfitting page
|
||||||
ammunition: 'Ammo',
|
ammunition: 'Ammo',
|
||||||
|
|
||||||
// Unit for seconds
|
// Unit for seconds
|
||||||
secs: 's',
|
secs: 's',
|
||||||
|
|
||||||
// Weapon, offence and defence
|
rebuildsperbay: 'Rebuilds per bay',
|
||||||
|
|
||||||
|
// Blueprint rolls
|
||||||
|
worst: 'Worst',
|
||||||
|
average: 'Average',
|
||||||
|
random: 'Random',
|
||||||
|
best: 'Best',
|
||||||
|
reset: 'Reset',
|
||||||
|
|
||||||
|
// Weapon, offence, defence and movement
|
||||||
dpe: 'Damage per MJ of energy',
|
dpe: 'Damage per MJ of energy',
|
||||||
dps: 'Damage per second',
|
dps: 'Damage per second',
|
||||||
sdps: 'Sustained damage per second',
|
sdps: 'Sustained damage per second',
|
||||||
@@ -106,12 +127,25 @@ export const terms = {
|
|||||||
'damage by': 'Damage by',
|
'damage by': 'Damage by',
|
||||||
'damage from': 'Damage from',
|
'damage from': 'Damage from',
|
||||||
'shield cells': 'Shield cells',
|
'shield cells': 'Shield cells',
|
||||||
|
'recovery': 'Recovery',
|
||||||
|
'recharge': 'Recharge',
|
||||||
|
'engine pips': 'Engine Pips',
|
||||||
|
'4b': '4 pips and boost',
|
||||||
|
'speed': 'Speed',
|
||||||
|
'pitch': 'Pitch',
|
||||||
|
'roll': 'Roll',
|
||||||
|
'yaw': 'Yaw',
|
||||||
|
'internal protection': 'Internal protection',
|
||||||
|
'external protection': 'External protection',
|
||||||
|
'engagement range': 'Engagement range',
|
||||||
|
'total': 'Total',
|
||||||
|
|
||||||
// Modifications
|
// Modifications
|
||||||
ammo: 'Ammunition maximum',
|
ammo: 'Ammunition maximum',
|
||||||
boot: 'Boot time',
|
boot: 'Boot time',
|
||||||
brokenregen: 'Broken regeneration rate',
|
brokenregen: 'Broken regeneration rate',
|
||||||
burst: 'Burst',
|
burst: 'Burst',
|
||||||
|
burstrof: 'Burst rate of fire',
|
||||||
clip: 'Ammunition clip',
|
clip: 'Ammunition clip',
|
||||||
damage: 'Damage',
|
damage: 'Damage',
|
||||||
distdraw: 'Distributor draw',
|
distdraw: 'Distributor draw',
|
||||||
@@ -133,14 +167,16 @@ export const terms = {
|
|||||||
pgen: 'Power generation',
|
pgen: 'Power generation',
|
||||||
piercing: 'Piercing',
|
piercing: 'Piercing',
|
||||||
power: 'Power draw',
|
power: 'Power draw',
|
||||||
|
protection: 'Protection',
|
||||||
range: 'Range',
|
range: 'Range',
|
||||||
ranget: 'Range', // Range in time (for FSD interdictor)
|
ranget: 'Range', // Range in time (for FSD interdictor)
|
||||||
regen: 'Regeneration rate',
|
regen: 'Regeneration rate',
|
||||||
reload: 'Reload time',
|
reload: 'Reload',
|
||||||
rof: 'Rate of fire',
|
rof: 'Rate of fire',
|
||||||
shield: 'Shield',
|
shield: 'Shield',
|
||||||
shieldboost: 'Shield boost',
|
shieldboost: 'Shield boost',
|
||||||
shieldreinforcement: 'Shield reinforcement',
|
shieldreinforcement: 'Shield reinforcement',
|
||||||
|
shotspeed: 'Shot speed',
|
||||||
spinup: 'Spin up time',
|
spinup: 'Spin up time',
|
||||||
syscap: 'Systems capacity',
|
syscap: 'Systems capacity',
|
||||||
sysrate: 'Systems recharge rate',
|
sysrate: 'Systems recharge rate',
|
||||||
@@ -148,4 +184,130 @@ export const terms = {
|
|||||||
thermres: 'Thermal resistance',
|
thermres: 'Thermal resistance',
|
||||||
wepcap: 'Weapons capacity',
|
wepcap: 'Weapons capacity',
|
||||||
weprate: 'Weapons recharge rate',
|
weprate: 'Weapons recharge rate',
|
||||||
|
|
||||||
|
// Help text
|
||||||
|
HELP_TEXT: `
|
||||||
|
<h1>Introduction</h1>
|
||||||
|
Coriolis is a ship builder for Elite: Dangerous. This help file provides you with the information you need to use Coriolis.
|
||||||
|
|
||||||
|
<h1>Importing Your Ship Into Coriolis</h1>
|
||||||
|
Often, you will want to start with your existing ship in Coriolis and see how particular changes might affect it, for example upgrading your FSD. There are a number of tools that can be used to import your ship without you having to create it manually. This has the added benefit of copying over any engineering modifications that have taken place as well. </p>
|
||||||
|
|
||||||
|
<h2>Importing Your Ship From EDDI</h2>
|
||||||
|
To import your ship from EDDI first ensure that your connection to the Frontier servers' companion API is working. To do this check the 'Companion App' tab where you should see "Your connection to the companion app is operational". If not then follow the instructions in the companion app tab in EDDI to connect to the Frontier servers.</p>
|
||||||
|
|
||||||
|
Once you have a working companion API connection go to the 'Shipyard' tab. At the right-hand side of each ship is an 'Export to Coriolis' button that will open your default web browser in Coriolis with the ship's build. </p>
|
||||||
|
|
||||||
|
Note that Internet Explorer and Edge might not import correctly, due to their internal restrictions on URL length. If you find that this is the case then please change your default browser to Chrome. </p>
|
||||||
|
|
||||||
|
<h2>Importing Your Ship From EDMC</h2>
|
||||||
|
To import your ship from EDMC once your connection to the Frontier servers' companion API is working go to 'Settings ->Configuration' and set the 'Preferred Shipyard' to 'Coriolis'. Once this is set up clicking on your ship in the main window will open your default web browser in Coriolis with the ship's build.</p>
|
||||||
|
|
||||||
|
Note that Internet Explorer and Edge might not import correctly, due to their internal restrictions on URL length. If you find that this is the case then please change your default browser to Chrome. </p>
|
||||||
|
|
||||||
|
<h1>Understanding And Using The Outfitting Panels</h1>
|
||||||
|
The outfitting page is where you will spend most of your time, and contains the information for your ship. Information on each of the panels is provided below. </p>
|
||||||
|
|
||||||
|
<h2>Key Values</h2>
|
||||||
|
Along the top of the screen are some of the key values for your build. This is a handy reference for the values, but more information is provided for the values in the further panels. </p>
|
||||||
|
|
||||||
|
Here, along with most places in Coriolis, acronyms will have tooltips explaining what they mean. Hover over the acronym to obtain more detail, or look in the glossary at the end of this help.</p>
|
||||||
|
|
||||||
|
<h2>Modules</h2>
|
||||||
|
The next set of panels laid out horizontally across the screen contain the modules you have put in your build. From left to right these are the core modules, the internal modules, the hardpoints and the utility mounts. These represent the available slots in your ship and cannot be altered. Each slot has a class, or size, and in general any module up to a given size can fit in a given slot (exceptions being bulkheads, life support and sensors in core modules and restricted internal slots, which can only take a subset of module depending on their restrictions). </p>
|
||||||
|
|
||||||
|
To add a module to a slot left-click on the slot and select the required module. Only the modules capable of fitting in the selected slot will be shown. </p>
|
||||||
|
|
||||||
|
To remove a module from a slot right-click on the module. </p>
|
||||||
|
|
||||||
|
To move a module from one slot to another drag it. If you instead want to copy the module drag it whilst holding down the 'Alt' key. </p>
|
||||||
|
|
||||||
|
<h2>Power Management</h2>
|
||||||
|
The power management panel provides information about power usage and priorities. It allows you to enable and disable individual modules, as well as set power priorities for each module.</p>
|
||||||
|
|
||||||
|
<h2>Costs</h2>
|
||||||
|
The costs panel provides information about the costs for each of your modules, and the total cost and insurance for your build. By default Coriolis uses the standard costs, however discounts for your ship, modules and insurance can be altered in the 'Settings' at the top-right of the page.</p>
|
||||||
|
|
||||||
|
The retrofit costs provides information about the costs of changing the base build for your ship, or your saved build, to the current build.</p>
|
||||||
|
|
||||||
|
The reload costs provides information about the costs of reloading your current build.</p>
|
||||||
|
|
||||||
|
<h2>Offence Summary</h2>
|
||||||
|
The offence summary panel provides information about the damage that you deal with your weapons.</p>
|
||||||
|
|
||||||
|
The first headline gives an overall damage per second rating: this is the optimum amount of damage the build will do per second according to weapon statistics. After that is a breakdown of the damage per second the build will do for each type of damage: absolute, explosive, kinetic, and thermal.</p>
|
||||||
|
|
||||||
|
The next headline gives an overall sustained damage per second rating: this is the optimum amount of damage the build will do per second over a longer period of time, taking in to account ammunition clip capacities and reload times. After that is a breakdown of the sustained damage per second the build will do for each type of damage: absolute, explosive, kinetic, and thermal.</p>
|
||||||
|
|
||||||
|
The final headline gives an overall damage per energy rating: this is the amount of damage the build will do per unit of weapon capacitor energy expended. After that is a breakdown of the damage per energy the build will do for each type of damage: absolute, explosive, kinetic, and thermal.</p>
|
||||||
|
|
||||||
|
<h2>Defence Summary</h2>
|
||||||
|
The defence summary panel provides information about the strength of your defences and the damage that you receive from opponents.</p>
|
||||||
|
|
||||||
|
The first headline gives your total shield strength (if you have shields), taking in to account your base shield plus boosters. After that are the details of how long it will take for your shields to recover from 0 to 50% (recovery time) and from 50% to 100% (recharge time). The next line provides a breakdown of the shield damage taken from different damage types. For example, if you damage from kinetic is 60% then it means that a weapon usually dealing 10 points of damage will only deal 6, the rest being resisted by the shield. Note that this does not include any resistance alterations due to pips in your SYS capacitor.</p>
|
||||||
|
|
||||||
|
The second headline gives your total shield cell strength (if you have shield cell banks). This is the sum of the recharge of all of equipped shield cell banks.</p>
|
||||||
|
|
||||||
|
The third headline gives your total armour strength, taking in to account your base armour plus hull reinforcement packages. The next line provides a breakdown of the hull damage taken from different damage types. For example, if you damage from kinetic is 120% then it means that a weapon usually dealing 10 points of damage will deal 12.</p>
|
||||||
|
|
||||||
|
The fourth headline gives your total module protection strength from module reinforcement packages. The next line provides a breakdown of the protection for both internal and external modules whilst all module reinforcement packages are functioning. For example, if external module protection is 20% then 10 points of damage will 2 points of damage to the module reinforcement packages and 8 points of damage to the module</p>
|
||||||
|
|
||||||
|
<h2>Movement Summary</h2>
|
||||||
|
The movement summary panel provides information about the build's speed and agility.</p>
|
||||||
|
|
||||||
|
Along the top of this panel are the number of pips you put in to your ENG capacitor, from 0 to 4 and also include 4 pips and boost (4b). Along the side of this panel are the names of the metrics. These are:
|
||||||
|
<dl>
|
||||||
|
<dt>Speed</dt><dd>The fastest the ship can move, in metres per second</dd>
|
||||||
|
<dt>Pitch</dt><dd>The fastest the ship can raise or lower its nose, in degrees per second</dd>
|
||||||
|
<dt>Roll</dt><dd>The fastest the ship can roll its body, in degrees per second</dd>
|
||||||
|
<dt>Yaw</dt><dd>The fastest the ship can turn its nose left or right, in degrees per second</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<h2>Jump Range</h2>
|
||||||
|
The jump range panel provides information about the build' jump range. The graph shows how the build's jump range changes with the amount of cargo on-board. The slider can be altered to change the amount of fuel you have on-board.
|
||||||
|
|
||||||
|
<h2>Damage Dealt</h2>
|
||||||
|
The damage dealt panel provides information about the effectiveness of your build's weapons against opponents' shields and hull at different engagement distances.</p>
|
||||||
|
|
||||||
|
The ship against which you want to check damage dealt can be selected by clicking on the red ship icon or the red ship name at the top of this panel.</p>
|
||||||
|
|
||||||
|
The main section of this panel is a table showing your weapons and their effectiveness. Effectiveness against shields takes in to account the weapon and its engagement range, but does not consider the target ship's resistances. Effectiveness against hull takes in to account the weapon and, its engagement range and the target's hardness, but does not consider the target ship's resistances. This is because resistances will alter significantly depending on the target's build.</p>
|
||||||
|
|
||||||
|
Effective DPS and effective SDPS are the equivalent of DPS and SDPS for the weapon. Effectiveness is a percentage value that shows how effective the DPS of the weapon is compared in reality against the given target compared to the weapon's stated DPS. Effectiveness can never go above 100%.</p>
|
||||||
|
|
||||||
|
Total effective DPS, SDPS and effectiveness against both shields and hull are provided at the bottom of the table.</p>
|
||||||
|
|
||||||
|
At the bottom of this panel you can change your engagement range. The engagement range is the distance between your ship and your target. Many weapons suffer from what is known as damage falloff, where their effectiveness decreases the further the distance between your ship and your target. This allows you to model the effect of engaging at different ranges.
|
||||||
|
|
||||||
|
Note that this panel only shows enabled weapons, so if you want to see your overall effectiveness for a subset of your weapons you can disable the undesired weapons in the power management panel.
|
||||||
|
|
||||||
|
<h2>Damage Received</h2>
|
||||||
|
The damage received panel provides information about the effectiveness of your build's defences against opponent's weapons at different engagement range. Features and functions are the same as the damage dealt panel, except that it does take in to account your build's resistances.</p>
|
||||||
|
|
||||||
|
<h1>Keyboard Shortcuts</h1>
|
||||||
|
<dl>
|
||||||
|
<dt>Ctrl-e</dt><dd>open export dialogue (outfitting page only)</dd>
|
||||||
|
<dt>Ctrl-h</dt><dd>open help dialogue</dd>
|
||||||
|
<dt>Ctrl-i</dt><dd>open import dialogue</dd>
|
||||||
|
<dt>Ctrl-l</dt><dd>open shortlink dialogue</dd>
|
||||||
|
<dt>Esc</dt><dd>close any open dialogue</dd>
|
||||||
|
</dl>
|
||||||
|
<h1>Glossary</h1>
|
||||||
|
<dl>
|
||||||
|
<dt>Absolute damage</dt><dd>A type of damage, without any protection. Absolute damage is always dealt at 100% regardless of if the damage is to shields, hull or modules, and irrespective of resistances</dd>
|
||||||
|
<dt>DPS</dt><dd>Damage per second; the amount of damage that a weapon or a ship can deal per second to a target under optimum conditions</dd>
|
||||||
|
<dt>EPS</dt><dd>Energy per second; the amount of energy that a weapon or a ship drains from the weapons capacitor per second when firing</dd>
|
||||||
|
<dt>HPS</dt><dd>Heat per second; the amount of heat that a weapon or a ship generates per second when firing</dd>
|
||||||
|
<dt>Effectivness</dt><dd>A comparison of the maximum DPS of a given weapon to the actual DPS of the given weapon in a specific situation. DPS can be reduced by range to the target, the target's hull and shield resistances, and the target's hardness</dd>
|
||||||
|
<dt>Explosive damage</dt><dd>A type of damage, protected against by explosive resistance</dd>
|
||||||
|
<dt>Hardness</dt><dd>The inherent resistance to damage of a ship's hull. Hardness is defined on a per-ship basis and there is currently nothing that can be done to change it. Hardness of a ship's hull is compared to the piercing of weapons: if piercing is higher than hardness the weapon does 100% damage, otherwise it does a fraction of its damage calculated as piercing/hardness</dd>
|
||||||
|
<dt>Falloff</dt><dd>The distance at which a weapons starts to do less damage than its stated DPS</dd>
|
||||||
|
<dt>Kinetic damage</dt><dd>A type of damage, protected against by kinetic resistance</dd>
|
||||||
|
<dt>SDPS</dt><dd>Sustained damage per second; the amount of damage that a weapon or a ship can deal per second to a target, taking in to account ammunition reload</dd>
|
||||||
|
<dt>SEPS</dt><dd>Sustained energy per second; the amount of energy that a weapon or a ship drains from the weapons capacitor per second when firing, taking in to account ammunition reload</dd>
|
||||||
|
<dt>SHPS</dt><dd>Sustained heat per second; the amount of heat that a weapon or a ship generates per second when firing, taking in to account ammunition reload</dd>
|
||||||
|
<dt>Thermal damage</dt><dd>A type of damage, protected against by thermal resistance</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
`,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -39,15 +39,6 @@ export default class AboutPage extends Page {
|
|||||||
|
|
||||||
<p>Coriolis is an open source project. Checkout the list of upcoming features and to-do list on github. Any and all contributions and feedback are welcome. If you encounter any bugs please report them and provide as much detail as possible.</p>
|
<p>Coriolis is an open source project. Checkout the list of upcoming features and to-do list on github. Any and all contributions and feedback are welcome. If you encounter any bugs please report them and provide as much detail as possible.</p>
|
||||||
|
|
||||||
<form action='https://www.paypal.com/cgi-bin/webscr' method='post' target='_blank'>
|
|
||||||
<input type='hidden' name='cmd' value='_s-xclick' />
|
|
||||||
<input type='hidden' name='encrypted' value='-----BEGIN PKCS7-----MIIHLwYJKoZIhvcNAQcEoIIHIDCCBxwCAQExggEwMIIBLAIBADCBlDCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb20CAQAwDQYJKoZIhvcNAQEBBQAEgYCjl3XoqQ3Q+x/qS7Va1lwvF0IgUs8gBbrwj1/uEv/xFyPSB2G0kgWqiB2c/8vvfcjjyMr4nlzLUlmQ0yl1zZaeTXFciN5a+JsvaBISThIlN9UP7PXP61TVHCECtt/hBNtlOmg8/gG8khJCj8+qi81XsNAz5bEDpdahKW3fwGHD4jELMAkGBSsOAwIaBQAwgawGCSqGSIb3DQEHATAUBggqhkiG9w0DBwQI4EVkn3RE+9qAgYhg2sTmY1Gul2yJyLYJZPRMO/PwgzEogb2NlIcshJSO+KvBea5NjjTXN2EJNqJa24h4lGA1mdrSgzTGDrVbdcnuti9+7ggn5R5s5IwEEQnN4JQx3IAqsp3UmJbti5t776Ns50nQbjA8NzxI+gwUmIvUQaVs6wC4HYXG6q8QtqUIWeVDhvbnt+H8oIIDhzCCA4MwggLsoAMCAQICAQAwDQYJKoZIhvcNAQEFBQAwgY4xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLUGF5UGFsIEluYy4xEzARBgNVBAsUCmxpdmVfY2VydHMxETAPBgNVBAMUCGxpdmVfYXBpMRwwGgYJKoZIhvcNAQkBFg1yZUBwYXlwYWwuY29tMB4XDTA0MDIxMzEwMTMxNVoXDTM1MDIxMzEwMTMxNVowgY4xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLUGF5UGFsIEluYy4xEzARBgNVBAsUCmxpdmVfY2VydHMxETAPBgNVBAMUCGxpdmVfYXBpMRwwGgYJKoZIhvcNAQkBFg1yZUBwYXlwYWwuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDBR07d/ETMS1ycjtkpkvjXZe9k+6CieLuLsPumsJ7QC1odNz3sJiCbs2wC0nLE0uLGaEtXynIgRqIddYCHx88pb5HTXv4SZeuv0Rqq4+axW9PLAAATU8w04qqjaSXgbGLP3NmohqM6bV9kZZwZLR/klDaQGo1u9uDb9lr4Yn+rBQIDAQABo4HuMIHrMB0GA1UdDgQWBBSWn3y7xm8XvVk/UtcKG+wQ1mSUazCBuwYDVR0jBIGzMIGwgBSWn3y7xm8XvVk/UtcKG+wQ1mSUa6GBlKSBkTCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb22CAQAwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQCBXzpWmoBa5e9fo6ujionW1hUhPkOBakTr3YCDjbYfvJEiv/2P+IobhOGJr85+XHhN0v4gUkEDI8r2/rNk1m0GA8HKddvTjyGw/XqXa+LSTlDYkqI8OwR8GEYj4efEtcRpRYBxV8KxAW93YDWzFGvruKnnLbDAF6VR5w/cCMn5hzGCAZowggGWAgEBMIGUMIGOMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxFDASBgNVBAoTC1BheVBhbCBJbmMuMRMwEQYDVQQLFApsaXZlX2NlcnRzMREwDwYDVQQDFAhsaXZlX2FwaTEcMBoGCSqGSIb3DQEJARYNcmVAcGF5cGFsLmNvbQIBADAJBgUrDgMCGgUAoF0wGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTUwNjA1MjEwNDQzWjAjBgkqhkiG9w0BCQQxFgQUx6Bs50H7tbYJln13pP5J7J1KiSUwDQYJKoZIhvcNAQEBBQAEgYBiOr1RX38uvwghuIZxKpjXX4LG/GoyYM6citfsBD5vjUGj0udmsamjlur+dwxJNs9dULnJO6huoTvxqxTui0Mh3n21YKoMqVE/erfNk2XygrJw9bEtW+HXjU3F+OGKR7dfD9STp2ZlvTEvZR9JRV5A/udC9/9U9eD5iLKRRwkIBg==-----END PKCS7-----' />
|
|
||||||
<input type='image' src='https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif' name='submit' alt='PayPal - Donate to Coriolis.io' style={{ border:'none' }} />
|
|
||||||
<img alt='' border='0' src='https://www.paypalobjects.com/en_US/i/scr/pixel.gif' width='1' height='1' />
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<p>Help keep the lights on! Donations will be used to cover costs of running and maintaining Coriolis. Thanks for helping!</p>
|
|
||||||
|
|
||||||
<h3>Chat</h3>
|
<h3>Chat</h3>
|
||||||
<p>You can chat to us on our <a href='https://discord.gg/0uwCh6R62aPRjk9w' target='_blank'>EDCD Discord server</a>.</p>
|
<p>You can chat to us on our <a href='https://discord.gg/0uwCh6R62aPRjk9w' target='_blank'>EDCD Discord server</a>.</p>
|
||||||
</div>;
|
</div>;
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ export default class ErrorDetails extends React.Component {
|
|||||||
return <div className='error'>
|
return <div className='error'>
|
||||||
<h1>Jameson, we have a problem..</h1>
|
<h1>Jameson, we have a problem..</h1>
|
||||||
<h1><small>{error.message}</small></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}
|
{content}
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import Persist from '../stores/Persist';
|
|||||||
import Ship from '../shipyard/Ship';
|
import Ship from '../shipyard/Ship';
|
||||||
import { toDetailedBuild } from '../shipyard/Serializer';
|
import { toDetailedBuild } from '../shipyard/Serializer';
|
||||||
import { outfitURL } from '../utils/UrlGenerators';
|
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 ShipSummaryTable from '../components/ShipSummaryTable';
|
||||||
import StandardSlotSection from '../components/StandardSlotSection';
|
import StandardSlotSection from '../components/StandardSlotSection';
|
||||||
import HardpointsSlotSection from '../components/HardpointsSlotSection';
|
import HardpointsSlotSection from '../components/HardpointsSlotSection';
|
||||||
@@ -16,10 +16,14 @@ import InternalSlotSection from '../components/InternalSlotSection';
|
|||||||
import UtilitySlotSection from '../components/UtilitySlotSection';
|
import UtilitySlotSection from '../components/UtilitySlotSection';
|
||||||
import OffenceSummary from '../components/OffenceSummary';
|
import OffenceSummary from '../components/OffenceSummary';
|
||||||
import DefenceSummary from '../components/DefenceSummary';
|
import DefenceSummary from '../components/DefenceSummary';
|
||||||
|
import MovementSummary from '../components/MovementSummary';
|
||||||
|
import DamageDealt from '../components/DamageDealt';
|
||||||
|
import DamageReceived from '../components/DamageReceived';
|
||||||
import LineChart from '../components/LineChart';
|
import LineChart from '../components/LineChart';
|
||||||
import PowerManagement from '../components/PowerManagement';
|
import PowerManagement from '../components/PowerManagement';
|
||||||
import CostSection from '../components/CostSection';
|
import CostSection from '../components/CostSection';
|
||||||
import ModalExport from '../components/ModalExport';
|
import ModalExport from '../components/ModalExport';
|
||||||
|
import ModalPermalink from '../components/ModalPermalink';
|
||||||
import Slider from '../components/Slider';
|
import Slider from '../components/Slider';
|
||||||
|
|
||||||
const SPEED_SERIES = ['boost', '4 Pips', '2 Pips', '0 Pips'];
|
const SPEED_SERIES = ['boost', '4 Pips', '2 Pips', '0 Pips'];
|
||||||
@@ -48,6 +52,8 @@ export default class OutfittingPage extends Page {
|
|||||||
constructor(props, context) {
|
constructor(props, context) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
this.state = this._initState(context);
|
this.state = this._initState(context);
|
||||||
|
this._keyDown = this._keyDown.bind(this);
|
||||||
|
this._exportBuild = this._exportBuild.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -177,7 +183,7 @@ export default class OutfittingPage extends Page {
|
|||||||
let translate = this.context.language.translate;
|
let translate = this.context.language.translate;
|
||||||
let { buildName, ship } = this.state;
|
let { buildName, ship } = this.state;
|
||||||
this.context.showModal(<ModalExport
|
this.context.showModal(<ModalExport
|
||||||
title={buildName + ' ' + translate('export')}
|
title={(buildName || ship.name) + ' ' + translate('export')}
|
||||||
description={translate('PHRASE_EXPORT_DESC')}
|
description={translate('PHRASE_EXPORT_DESC')}
|
||||||
data={toDetailedBuild(buildName, ship, ship.toString())}
|
data={toDetailedBuild(buildName, ship, ship.toString())}
|
||||||
/>);
|
/>);
|
||||||
@@ -254,6 +260,7 @@ export default class OutfittingPage extends Page {
|
|||||||
*/
|
*/
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
this.resizeListener = this.context.onWindowResize(this._updateDimensions);
|
this.resizeListener = this.context.onWindowResize(this._updateDimensions);
|
||||||
|
document.addEventListener('keydown', this._keyDown);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -270,6 +277,29 @@ export default class OutfittingPage extends Page {
|
|||||||
this.resizeListener.remove();
|
this.resizeListener.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the short URL
|
||||||
|
*/
|
||||||
|
_genShortlink() {
|
||||||
|
this.context.showModal(<ModalPermalink url={window.location.href}/>);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle Key Down
|
||||||
|
* @param {Event} e Keyboard Event
|
||||||
|
*/
|
||||||
|
_keyDown(e) {
|
||||||
|
// .keyCode will eventually be replaced with .key
|
||||||
|
switch (e.keyCode) {
|
||||||
|
case 69: // 'e'
|
||||||
|
if (e.ctrlKey || e.metaKey) { // CTRL/CMD + e
|
||||||
|
e.preventDefault();
|
||||||
|
this._exportBuild();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render the Page
|
* Render the Page
|
||||||
* @return {React.Component} The page contents
|
* @return {React.Component} The page contents
|
||||||
@@ -289,8 +319,6 @@ export default class OutfittingPage extends Page {
|
|||||||
sStr = ship.getStandardString() + '.' + ship.getModificationsString(),
|
sStr = ship.getStandardString() + '.' + ship.getModificationsString(),
|
||||||
iStr = ship.getInternalString() + '.' + ship.getModificationsString();
|
iStr = ship.getInternalString() + '.' + ship.getModificationsString();
|
||||||
|
|
||||||
Router.replace(outfitURL(ship.id, code, buildName));
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id='outfit' className={'page'} style={{ fontSize: (sizeRatio * 0.9) + 'em' }}>
|
<div id='outfit' className={'page'} style={{ fontSize: (sizeRatio * 0.9) + 'em' }}>
|
||||||
<div id='overview'>
|
<div id='overview'>
|
||||||
@@ -315,6 +343,9 @@ export default class OutfittingPage extends Page {
|
|||||||
<button onClick={buildName && this._exportBuild} disabled={!buildName} onMouseOver={termtip.bind(null, 'export')} onMouseOut={hide}>
|
<button onClick={buildName && this._exportBuild} disabled={!buildName} onMouseOver={termtip.bind(null, 'export')} onMouseOut={hide}>
|
||||||
<Download className='lg'/>
|
<Download className='lg'/>
|
||||||
</button>
|
</button>
|
||||||
|
<button onClick={this._genShortlink} onMouseOver={termtip.bind(null, 'shortlink')} onMouseOut={hide}>
|
||||||
|
<LinkIcon className='lg' />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -326,7 +357,7 @@ export default class OutfittingPage extends Page {
|
|||||||
<PowerManagement ship={ship} code={code} onChange={shipUpdated} />
|
<PowerManagement ship={ship} code={code} onChange={shipUpdated} />
|
||||||
<CostSection ship={ship} buildName={buildName} code={sStr + hStr + iStr} />
|
<CostSection ship={ship} buildName={buildName} code={sStr + hStr + iStr} />
|
||||||
|
|
||||||
<div ref='chartThird' className='group third'>
|
<div className='group third'>
|
||||||
<OffenceSummary ship={ship} code={code}/>
|
<OffenceSummary ship={ship} code={code}/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -335,22 +366,21 @@ export default class OutfittingPage extends Page {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='group third'>
|
<div className='group third'>
|
||||||
<h1>{translate('speed')}</h1>
|
<MovementSummary ship={ship} code={code}/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div ref='chartThird' className='group third'>
|
||||||
|
<h1>{translate('jump range')}</h1>
|
||||||
<LineChart
|
<LineChart
|
||||||
width={chartWidth}
|
width={chartWidth}
|
||||||
xMax={ship.cargoCapacity}
|
xMax={ship.cargoCapacity}
|
||||||
yMax={ship.topBoost + 10}
|
yMax={ship.unladenRange}
|
||||||
xUnit={translate('T')}
|
xUnit={translate('T')}
|
||||||
yUnit={translate('m/s')}
|
yUnit={translate('LY')}
|
||||||
yLabel={translate('speed')}
|
yLabel={translate('jump range')}
|
||||||
series={SPEED_SERIES}
|
|
||||||
colors={SPEED_COLORS}
|
|
||||||
xLabel={translate('cargo')}
|
xLabel={translate('cargo')}
|
||||||
func={state.speedChartFunc}
|
func={state.jumpRangeChartFunc}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='group half'>
|
|
||||||
<table style={{ width: '100%', lineHeight: '1em', backgroundColor: 'transparent' }}>
|
<table style={{ width: '100%', lineHeight: '1em', backgroundColor: 'transparent' }}>
|
||||||
<tbody >
|
<tbody >
|
||||||
<tr>
|
<tr>
|
||||||
@@ -376,7 +406,15 @@ export default class OutfittingPage extends Page {
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<DamageDealt ship={ship} code={code} currentMenu={menu}/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<DamageReceived ship={ship} code={code} currentMenu={menu}/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -393,3 +431,43 @@ export default class OutfittingPage extends Page {
|
|||||||
// func={state.jumpRangeChartFunc}
|
// func={state.jumpRangeChartFunc}
|
||||||
// />
|
// />
|
||||||
// </div>
|
// </div>
|
||||||
|
// <div className='group third'>
|
||||||
|
// <h1>{translate('speed')}</h1>
|
||||||
|
// <LineChart
|
||||||
|
// width={chartWidth}
|
||||||
|
// xMax={ship.cargoCapacity}
|
||||||
|
// yMax={ship.topBoost + 10}
|
||||||
|
// xUnit={translate('T')}
|
||||||
|
// yUnit={translate('m/s')}
|
||||||
|
// yLabel={translate('speed')}
|
||||||
|
// series={SPEED_SERIES}
|
||||||
|
// colors={SPEED_COLORS}
|
||||||
|
// xLabel={translate('cargo')}
|
||||||
|
// func={state.speedChartFunc}
|
||||||
|
// />
|
||||||
|
// </div>
|
||||||
|
// <div className='group half'>
|
||||||
|
// <table style={{ width: '100%', lineHeight: '1em', backgroundColor: 'transparent' }}>
|
||||||
|
// <tbody >
|
||||||
|
// <tr>
|
||||||
|
// <td style={{ verticalAlign: 'top', padding: 0, width: '2.5em' }} onMouseEnter={termtip.bind(null, 'fuel level')} onMouseLeave={hide}>
|
||||||
|
// <Fuel className='xl primary-disabled' />
|
||||||
|
// </td>
|
||||||
|
// <td>
|
||||||
|
// <Slider
|
||||||
|
// axis={true}
|
||||||
|
// onChange={this._fuelChange}
|
||||||
|
// axisUnit={translate('T')}
|
||||||
|
// percent={fuelLevel}
|
||||||
|
// max={fuelCapacity}
|
||||||
|
// scale={sizeRatio}
|
||||||
|
// onResize={onWindowResize}
|
||||||
|
// />
|
||||||
|
// </td>
|
||||||
|
// <td className='primary' style={{ width: '10em', verticalAlign: 'top', fontSize: '0.9em', textAlign: 'left' }}>
|
||||||
|
// {formats.f2(fuelLevel * fuelCapacity)}{units.T} {formats.pct1(fuelLevel)}
|
||||||
|
// </td>
|
||||||
|
// </tr>
|
||||||
|
// </tbody>
|
||||||
|
// </table>
|
||||||
|
// </div>
|
||||||
|
|||||||
@@ -40,7 +40,9 @@ function shipSummary(shipId, shipData) {
|
|||||||
intCount: 0,
|
intCount: 0,
|
||||||
maxCargo: 0,
|
maxCargo: 0,
|
||||||
hp: [0, 0, 0, 0, 0], // Utility, Small, Medium, Large, Huge
|
hp: [0, 0, 0, 0, 0], // Utility, Small, Medium, Large, Huge
|
||||||
int: [0, 0, 0, 0, 0, 0, 0, 0] // Sizes 1 - 8
|
int: [0, 0, 0, 0, 0, 0, 0, 0], // Sizes 1 - 8
|
||||||
|
standard: shipData.slots.standard,
|
||||||
|
agility: shipData.properties.pitch + shipData.properties.yaw + shipData.properties.roll
|
||||||
};
|
};
|
||||||
Object.assign(summary, shipData.properties);
|
Object.assign(summary, shipData.properties);
|
||||||
let ship = new Ship(shipId, shipData.properties, shipData.slots);
|
let ship = new Ship(shipId, shipData.properties, shipData.slots);
|
||||||
@@ -139,7 +141,8 @@ export default class ShipyardPage extends Page {
|
|||||||
>
|
>
|
||||||
<td className='le'>{s.manufacturer}</td>
|
<td className='le'>{s.manufacturer}</td>
|
||||||
<td className='cap'>{translate(SizeMap[s.class])}</td>
|
<td className='cap'>{translate(SizeMap[s.class])}</td>
|
||||||
<td>{s.agility}</td>
|
<td className='ri'>{fInt(s.agility)}</td>
|
||||||
|
<td className='ri'>{fInt(s.hardness)}</td>
|
||||||
<td className='ri'>{fInt(s.speed)}{u['m/s']}</td>
|
<td className='ri'>{fInt(s.speed)}{u['m/s']}</td>
|
||||||
<td className='ri'>{fInt(s.boost)}{u['m/s']}</td>
|
<td className='ri'>{fInt(s.boost)}{u['m/s']}</td>
|
||||||
<td className='ri'>{fInt(s.baseArmour)}</td>
|
<td className='ri'>{fInt(s.baseArmour)}</td>
|
||||||
@@ -148,6 +151,12 @@ export default class ShipyardPage extends Page {
|
|||||||
<td className='ri'>{fInt(s.topBoost)}{u['m/s']}</td>
|
<td className='ri'>{fInt(s.topBoost)}{u['m/s']}</td>
|
||||||
<td className='ri'>{fRound(s.maxJumpRange)}{u.LY}</td>
|
<td className='ri'>{fRound(s.maxJumpRange)}{u.LY}</td>
|
||||||
<td className='ri'>{fInt(s.maxCargo)}{u.T}</td>
|
<td className='ri'>{fInt(s.maxCargo)}{u.T}</td>
|
||||||
|
<td className='cn'>{s.standard[0]}</td>
|
||||||
|
<td className='cn'>{s.standard[1]}</td>
|
||||||
|
<td className='cn'>{s.standard[2]}</td>
|
||||||
|
<td className='cn'>{s.standard[3]}</td>
|
||||||
|
<td className='cn'>{s.standard[4]}</td>
|
||||||
|
<td className='cn'>{s.standard[5]}</td>
|
||||||
<td className={cn({ disabled: !s.hp[1] })}>{s.hp[1]}</td>
|
<td className={cn({ disabled: !s.hp[1] })}>{s.hp[1]}</td>
|
||||||
<td className={cn({ disabled: !s.hp[2] })}>{s.hp[2]}</td>
|
<td className={cn({ disabled: !s.hp[2] })}>{s.hp[2]}</td>
|
||||||
<td className={cn({ disabled: !s.hp[3] })}>{s.hp[3]}</td>
|
<td className={cn({ disabled: !s.hp[3] })}>{s.hp[3]}</td>
|
||||||
@@ -260,9 +269,11 @@ export default class ShipyardPage extends Page {
|
|||||||
<tr className='main'>
|
<tr className='main'>
|
||||||
<th rowSpan={2} className='sortable' onClick={sortShips('manufacturer')}>{translate('manufacturer')}</th>
|
<th rowSpan={2} className='sortable' onClick={sortShips('manufacturer')}>{translate('manufacturer')}</th>
|
||||||
<th rowSpan={2} className='sortable' onClick={sortShips('class')}>{translate('size')}</th>
|
<th rowSpan={2} className='sortable' onClick={sortShips('class')}>{translate('size')}</th>
|
||||||
<th rowSpan={2} className='sortable' onMouseEnter={termtip.bind(null, 'maneuverability')} onMouseLeave={hide} onClick={sortShips('agility')}>{translate('mnv')}</th>
|
<th rowSpan={2} className='sortable' onClick={sortShips('agility')}>{translate('agility')}</th>
|
||||||
|
<th rowSpan={2} className='sortable' onClick={sortShips('hardness')}>{translate('hardness')}</th>
|
||||||
<th colSpan={4}>{translate('base')}</th>
|
<th colSpan={4}>{translate('base')}</th>
|
||||||
<th colSpan={4}>{translate('max')}</th>
|
<th colSpan={4}>{translate('max')}</th>
|
||||||
|
<th colSpan={6}>{translate('core module classes')}</th>
|
||||||
<th colSpan={5} className='sortable' onClick={sortShips('hpCount')}>{translate('hardpoints')}</th>
|
<th colSpan={5} className='sortable' onClick={sortShips('hpCount')}>{translate('hardpoints')}</th>
|
||||||
<th colSpan={8} className='sortable' onClick={sortShips('intCount')}>{translate('internal compartments')}</th>
|
<th colSpan={8} className='sortable' onClick={sortShips('intCount')}>{translate('internal compartments')}</th>
|
||||||
<th rowSpan={2} className='sortable' onClick={sortShips('hullMass')}>{translate('hull')}</th>
|
<th rowSpan={2} className='sortable' onClick={sortShips('hullMass')}>{translate('hull')}</th>
|
||||||
@@ -280,6 +291,13 @@ export default class ShipyardPage extends Page {
|
|||||||
<th className='sortable' onClick={sortShips('maxJumpRange')}>{translate('jump')}</th>
|
<th className='sortable' onClick={sortShips('maxJumpRange')}>{translate('jump')}</th>
|
||||||
<th className='sortable' onClick={sortShips('maxCargo')}>{translate('cargo')}</th>
|
<th className='sortable' onClick={sortShips('maxCargo')}>{translate('cargo')}</th>
|
||||||
|
|
||||||
|
<th className='sortable lft' onMouseEnter={termtip.bind(null, 'power plant')} onMouseLeave={hide} onClick={sortShips('standard', 0)}>{'pp'}</th>
|
||||||
|
<th className='sortable' onMouseEnter={termtip.bind(null, 'thrusters')} onMouseLeave={hide} onClick={sortShips('standard', 1)}>{'th'}</th>
|
||||||
|
<th className='sortable' onMouseEnter={termtip.bind(null, 'frame shift drive')} onMouseLeave={hide} onClick={sortShips('standard', 2)}>{'fsd'}</th>
|
||||||
|
<th className='sortable' onMouseEnter={termtip.bind(null, 'life support')} onMouseLeave={hide} onClick={sortShips('standard', 3)}>{'ls'}</th>
|
||||||
|
<th className='sortable' onMouseEnter={termtip.bind(null, 'power distriubtor')} onMouseLeave={hide} onClick={sortShips('standard', 4)}>{'pd'}</th>
|
||||||
|
<th className='sortable' onMouseEnter={termtip.bind(null, 'sensors')} onMouseLeave={hide} onClick={sortShips('standard', 5)}>{'s'}</th>
|
||||||
|
|
||||||
<th className='sortable lft' onClick={sortShips('hp',1)}>{translate('S')}</th>
|
<th className='sortable lft' onClick={sortShips('hp',1)}>{translate('S')}</th>
|
||||||
<th className='sortable' onClick={sortShips('hp', 2)}>{translate('M')}</th>
|
<th className='sortable' onClick={sortShips('hp', 2)}>{translate('M')}</th>
|
||||||
<th className='sortable' onClick={sortShips('hp', 3)}>{translate('L')}</th>
|
<th className='sortable' onClick={sortShips('hp', 3)}>{translate('L')}</th>
|
||||||
|
|||||||
@@ -67,32 +67,109 @@ export function shieldStrength(mass, baseShield, sg, multiplier) {
|
|||||||
/**
|
/**
|
||||||
* Calculate the a ships speed based on mass, and thrusters.
|
* Calculate the a ships speed based on mass, and thrusters.
|
||||||
*
|
*
|
||||||
* @param {number} mass Current mass of the ship
|
* @param {number} mass the mass of the ship
|
||||||
* @param {number} baseSpeed Base speed m/s for ship
|
* @param {number} baseSpeed base speed m/s for ship
|
||||||
* @param {number} baseBoost Base boost speed m/s for ship
|
* @param {object} thrusters The ship's thrusters
|
||||||
* @param {object} thrusters The Thrusters used
|
* @param {number} engpip the multiplier per pip to engines
|
||||||
* @param {number} pipSpeed Speed pip multiplier
|
* @return {array} Speed by pips
|
||||||
* @return {object} Approximate speed by pips
|
|
||||||
*/
|
*/
|
||||||
export function speed(mass, baseSpeed, baseBoost, thrusters, pipSpeed) {
|
export function speed(mass, baseSpeed, thrusters, engpip) {
|
||||||
|
// thrusters might be a module or a template; handle either here
|
||||||
|
const minMass = thrusters instanceof Module ? thrusters.getMinMass() : thrusters.minmass;
|
||||||
|
const optMass = thrusters instanceof Module ? thrusters.getOptMass() : thrusters.optmass;
|
||||||
|
const maxMass = thrusters instanceof Module ? thrusters.getMaxMass() : thrusters.maxmass;
|
||||||
|
const minMul = thrusters instanceof Module ? thrusters.getMinMul('speed') : (thrusters.minmulspeed ? thrusters.minmulspeed : thrusters.minmul);
|
||||||
|
const optMul = thrusters instanceof Module ? thrusters.getOptMul('speed') : (thrusters.optmulspeed ? thrusters.minmulspeed : thrusters.minmul);
|
||||||
|
const maxMul = thrusters instanceof Module ? thrusters.getMaxMul('speed') : (thrusters.maxmulspeed ? thrusters.minmulspeed : thrusters.minmul);
|
||||||
|
|
||||||
|
let results = normValues(minMass, optMass, maxMass, minMul, optMul, maxMul, mass, baseSpeed, engpip);
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate pitch of a ship based on mass and thrusters
|
||||||
|
* @param {number} mass the mass of the ship
|
||||||
|
* @param {number} basePitch base pitch of the ship
|
||||||
|
* @param {object} thrusters the ship's thrusters
|
||||||
|
* @param {number} engpip the multiplier per pip to engines
|
||||||
|
* @return {array} Pitch by pips
|
||||||
|
*/
|
||||||
|
export function pitch(mass, basePitch, thrusters, engpip) {
|
||||||
// thrusters might be a module or a template; handle either here
|
// thrusters might be a module or a template; handle either here
|
||||||
let minMass = thrusters instanceof Module ? thrusters.getMinMass() : thrusters.minmass;
|
let minMass = thrusters instanceof Module ? thrusters.getMinMass() : thrusters.minmass;
|
||||||
let optMass = thrusters instanceof Module ? thrusters.getOptMass() : thrusters.optmass;
|
let optMass = thrusters instanceof Module ? thrusters.getOptMass() : thrusters.optmass;
|
||||||
let maxMass = thrusters instanceof Module ? thrusters.getMaxMass() : thrusters.maxmass;
|
let maxMass = thrusters instanceof Module ? thrusters.getMaxMass() : thrusters.maxmass;
|
||||||
let minMul = thrusters instanceof Module ? thrusters.getMinMul() : thrusters.minmul;
|
let minMul = thrusters instanceof Module ? thrusters.getMinMul('rotation') : (thrusters.minmulrotation ? thrusters.minmulrotation : thrusters.minmul);
|
||||||
let optMul = thrusters instanceof Module ? thrusters.getOptMul() : thrusters.optmul;
|
let optMul = thrusters instanceof Module ? thrusters.getOptMul('rotation') : (thrusters.optmulrotation ? thrusters.optmulrotation : thrusters.optmul);
|
||||||
let maxMul = thrusters instanceof Module ? thrusters.getMaxMul() : thrusters.maxmul;
|
let maxMul = thrusters instanceof Module ? thrusters.getMaxMul('rotation') : (thrusters.maxmulrotation ? thrusters.maxmulrotation : thrusters.maxmul);
|
||||||
|
|
||||||
let xnorm = Math.min(1, (maxMass - mass) / (maxMass - minMass));
|
return normValues(minMass, optMass, maxMass, minMul, optMul, maxMul, mass, basePitch, engpip);
|
||||||
let exponent = Math.log((optMul - minMul) / (maxMul - minMul)) / Math.log(Math.min(1, (maxMass - optMass) / (maxMass - minMass)));
|
}
|
||||||
let ynorm = Math.pow(xnorm, exponent);
|
|
||||||
let mul = minMul + ynorm * (maxMul - minMul);
|
/**
|
||||||
let speed = baseSpeed * mul;
|
* Calculate yaw of a ship based on mass and thrusters
|
||||||
|
* @param {number} mass the mass of the ship
|
||||||
return {
|
* @param {number} baseYaw base yaw of the ship
|
||||||
'0 Pips': speed * (1 - (pipSpeed * 4)),
|
* @param {object} thrusters the ship's thrusters
|
||||||
'2 Pips': speed * (1 - (pipSpeed * 2)),
|
* @param {number} engpip the multiplier per pip to engines
|
||||||
'4 Pips': speed,
|
* @return {array} Yaw by pips
|
||||||
'boost': baseBoost * mul
|
*/
|
||||||
};
|
export function yaw(mass, baseYaw, thrusters, engpip) {
|
||||||
|
// thrusters might be a module or a template; handle either here
|
||||||
|
let minMass = thrusters instanceof Module ? thrusters.getMinMass() : thrusters.minmass;
|
||||||
|
let optMass = thrusters instanceof Module ? thrusters.getOptMass() : thrusters.optmass;
|
||||||
|
let maxMass = thrusters instanceof Module ? thrusters.getMaxMass() : thrusters.maxmass;
|
||||||
|
let minMul = thrusters instanceof Module ? thrusters.getMinMul('rotation') : (thrusters.minmulrotation ? thrusters.minmulrotation : thrusters.minmul);
|
||||||
|
let optMul = thrusters instanceof Module ? thrusters.getOptMul('rotation') : (thrusters.optmulrotation ? thrusters.optmulrotation : thrusters.optmul);
|
||||||
|
let maxMul = thrusters instanceof Module ? thrusters.getMaxMul('rotation') : (thrusters.maxmulrotation ? thrusters.maxmulrotation : thrusters.maxmul);
|
||||||
|
|
||||||
|
return normValues(minMass, optMass, maxMass, minMul, optMul, maxMul, mass, baseYaw, engpip);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate roll of a ship based on mass and thrusters
|
||||||
|
* @param {number} mass the mass of the ship
|
||||||
|
* @param {number} baseRoll base roll of the ship
|
||||||
|
* @param {object} thrusters the ship's thrusters
|
||||||
|
* @param {number} engpip the multiplier per pip to engines
|
||||||
|
* @return {array} Roll by pips
|
||||||
|
*/
|
||||||
|
export function roll(mass, baseRoll, thrusters, engpip) {
|
||||||
|
// thrusters might be a module or a template; handle either here
|
||||||
|
let minMass = thrusters instanceof Module ? thrusters.getMinMass() : thrusters.minmass;
|
||||||
|
let optMass = thrusters instanceof Module ? thrusters.getOptMass() : thrusters.optmass;
|
||||||
|
let maxMass = thrusters instanceof Module ? thrusters.getMaxMass() : thrusters.maxmass;
|
||||||
|
let minMul = thrusters instanceof Module ? thrusters.getMinMul('rotation') : (thrusters.minmulrotation ? thrusters.minmulrotation : thrusters.minmul);
|
||||||
|
let optMul = thrusters instanceof Module ? thrusters.getOptMul('rotation') : (thrusters.optmulrotation ? thrusters.optmulrotation : thrusters.optmul);
|
||||||
|
let maxMul = thrusters instanceof Module ? thrusters.getMaxMul('rotation') : (thrusters.maxmulrotation ? thrusters.maxmulrotation : thrusters.maxmul);
|
||||||
|
|
||||||
|
return normValues(minMass, optMass, maxMass, minMul, optMul, maxMul, mass, baseRoll, engpip);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalise according to FD's calculations and return suitable values
|
||||||
|
* @param {number} minMass the minimum mass of the thrusters
|
||||||
|
* @param {number} optMass the optimum mass of the thrusters
|
||||||
|
* @param {number} maxMass the maximum mass of the thrusters
|
||||||
|
* @param {number} minMul the minimum multiplier of the thrusters
|
||||||
|
* @param {number} optMul the optimum multiplier of the thrusters
|
||||||
|
* @param {number} maxMul the maximum multiplier of the thrusters
|
||||||
|
* @param {number} mass the mass of the ship
|
||||||
|
* @param {base} base the base value from which to calculate
|
||||||
|
* @param {number} engpip the multiplier per pip to engines
|
||||||
|
* @return {array} values by pips
|
||||||
|
*/
|
||||||
|
function normValues(minMass, optMass, maxMass, minMul, optMul, maxMul, mass, base, engpip) {
|
||||||
|
const xnorm = Math.min(1, (maxMass - mass) / (maxMass - minMass));
|
||||||
|
const exponent = Math.log((optMul - minMul) / (maxMul - minMul)) / Math.log(Math.min(1, (maxMass - optMass) / (maxMass - minMass)));
|
||||||
|
const ynorm = Math.pow(xnorm, exponent);
|
||||||
|
const mul = minMul + ynorm * (maxMul - minMul);
|
||||||
|
const res = base * mul;
|
||||||
|
|
||||||
|
return [res * (1 - (engpip * 4)),
|
||||||
|
res * (1 - (engpip * 3)),
|
||||||
|
res * (1 - (engpip * 2)),
|
||||||
|
res * (1 - (engpip * 1)),
|
||||||
|
res];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ export const ModuleGroupToName = {
|
|||||||
fi: 'Frame Shift Drive Interdictor',
|
fi: 'Frame Shift Drive Interdictor',
|
||||||
hb: 'Hatch Breaker Limpet Controller',
|
hb: 'Hatch Breaker Limpet Controller',
|
||||||
hr: 'Hull Reinforcement Package',
|
hr: 'Hull Reinforcement Package',
|
||||||
|
mrp: 'Module Reinforcement Package',
|
||||||
rf: 'Refinery',
|
rf: 'Refinery',
|
||||||
scb: 'Shield Cell Bank',
|
scb: 'Shield Cell Bank',
|
||||||
sg: 'Shield Generator',
|
sg: 'Shield Generator',
|
||||||
@@ -105,8 +106,9 @@ export const BulkheadNames = [
|
|||||||
export const ShipFacets = [
|
export const ShipFacets = [
|
||||||
{ // 0
|
{ // 0
|
||||||
title: 'agility',
|
title: 'agility',
|
||||||
props: ['agility'],
|
props: ['topPitch', 'topRoll', 'topYaw'],
|
||||||
fmt: 'int',
|
lbls: ['pitch', 'roll', 'yaw'],
|
||||||
|
fmt: 'f1',
|
||||||
i: 0
|
i: 0
|
||||||
},
|
},
|
||||||
{ // 1
|
{ // 1
|
||||||
@@ -185,11 +187,18 @@ export const ShipFacets = [
|
|||||||
},
|
},
|
||||||
{ // 11
|
{ // 11
|
||||||
title: 'DPS',
|
title: 'DPS',
|
||||||
props: ['totalDps'],
|
props: ['totalDps', 'totalExplDps', 'totalKinDps', 'totalThermDps'],
|
||||||
lbls: ['DPS'],
|
lbls: ['total', 'explosive', 'kinetic', 'thermal'],
|
||||||
fmt: 'round',
|
fmt: 'round',
|
||||||
i: 11
|
i: 11
|
||||||
},
|
},
|
||||||
|
{ // 14
|
||||||
|
title: 'Sustained DPS',
|
||||||
|
props: ['totalSDps', 'totalExplSDps', 'totalKinSDps', 'totalThermSDps'],
|
||||||
|
lbls: ['total', 'explosive', 'kinetic', 'thermal'],
|
||||||
|
fmt: 'round',
|
||||||
|
i: 14
|
||||||
|
},
|
||||||
{ // 12
|
{ // 12
|
||||||
title: 'EPS',
|
title: 'EPS',
|
||||||
props: ['totalEps'],
|
props: ['totalEps'],
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import * as ModuleUtils from './ModuleUtils';
|
import * as ModuleUtils from './ModuleUtils';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
import { Modifications } from 'coriolis-data/dist';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Module - active module in a ship's buildout
|
* Module - active module in a ship's buildout
|
||||||
@@ -11,24 +12,32 @@ export default class Module {
|
|||||||
* @param {Object} params Module parameters. Either grp/id or template
|
* @param {Object} params Module parameters. Either grp/id or template
|
||||||
*/
|
*/
|
||||||
constructor(params) {
|
constructor(params) {
|
||||||
let properties = Object.assign({ grp: null, id: null, template: null, }, params);
|
let properties = Object.assign({ grp: null, id: null, template: null }, params);
|
||||||
|
|
||||||
let template;
|
if (properties.class != undefined) {
|
||||||
if (properties.template == undefined) {
|
// We already have a fully-formed module; copy the data over
|
||||||
return ModuleUtils.findModule(properties.grp, properties.id);
|
for (let p in properties) { this[p] = properties[p]; }
|
||||||
|
} else if (properties.template != undefined) {
|
||||||
|
// We have a template from coriolis-data; copy the data over
|
||||||
|
for (let p in properties.template) { this[p] = properties.template[p]; }
|
||||||
} else {
|
} else {
|
||||||
template = properties.template;
|
// We don't have a template; find it given the group and ID
|
||||||
if (template) {
|
return ModuleUtils.findModule(properties.grp, properties.id);
|
||||||
// Copy all properties from coriolis-data template
|
|
||||||
for (let p in template) { this[p] = template[p]; }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clone an existing module
|
||||||
|
* @return {Object} A clone of the existing module
|
||||||
|
*/
|
||||||
|
clone() {
|
||||||
|
return new Module(JSON.parse(JSON.stringify(this)));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a value for a given modification
|
* Get a value for a given modification
|
||||||
* @param {Number} name The name of the modification
|
* @param {Number} name The name of the modification
|
||||||
* @return {Number} The value of the modification, as an integer value scaled so that 1.23% == 123
|
* @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) {
|
getModValue(name) {
|
||||||
return this.mods && this.mods[name] ? this.mods[name] : null;
|
return this.mods && this.mods[name] ? this.mods[name] : null;
|
||||||
@@ -37,7 +46,7 @@ export default class Module {
|
|||||||
/**
|
/**
|
||||||
* Set a value for a given modification ID
|
* Set a value for a given modification ID
|
||||||
* @param {Number} name The name of the modification
|
* @param {Number} name The name of the modification
|
||||||
* @param {Number} value The value of the modification, as an integer scaled so that -2.34% == -234
|
* @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) {
|
setModValue(name, value) {
|
||||||
if (!this.mods) {
|
if (!this.mods) {
|
||||||
@@ -47,8 +56,12 @@ export default class Module {
|
|||||||
if (value == null || value == 0) {
|
if (value == null || value == 0) {
|
||||||
delete this.mods[name];
|
delete this.mods[name];
|
||||||
} else {
|
} else {
|
||||||
// Round just to be sure
|
if (isNaN(value)) {
|
||||||
this.mods[name] = Math.round(value);
|
this.mods[name] = value;
|
||||||
|
} else {
|
||||||
|
// Round just to be sure
|
||||||
|
this.mods[name] = Math.round(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,8 +74,21 @@ export default class Module {
|
|||||||
_getModifiedValue(name, additive) {
|
_getModifiedValue(name, additive) {
|
||||||
let result = this[name] || (additive ? 0 : null); // Additive NULL === 0
|
let result = this[name] || (additive ? 0 : null); // Additive NULL === 0
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
// Jitter is special, being the only non-percentage value (it is in fact degrees)
|
const modification = Modifications.modifications[name];
|
||||||
const modValue = name === 'jitter' ? this.getModValue(name) / 100 : this.getModValue(name) / 10000;
|
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 (modValue) {
|
||||||
if (additive) {
|
if (additive) {
|
||||||
result = result + modValue;
|
result = result + modValue;
|
||||||
@@ -70,7 +96,18 @@ export default class Module {
|
|||||||
result = result * (1 + modValue);
|
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 result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,6 +287,28 @@ export default class Module {
|
|||||||
return this._getModifiedValue('range');
|
return this._getModifiedValue('range');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the falloff for this module, taking in to account modifications
|
||||||
|
* @return {Number} the falloff of this module
|
||||||
|
*/
|
||||||
|
getFalloff() {
|
||||||
|
if (this.getModValue('fallofffromrange')) {
|
||||||
|
// Falloff from range means what it says, so use range instead of falloff
|
||||||
|
return this.getRange();
|
||||||
|
} else {
|
||||||
|
// Need to find out if we have a focused modification, in which case our falloff is scaled to range
|
||||||
|
if (this.blueprint && this.blueprint.name === 'Focused') {
|
||||||
|
const rangeMod = this.getModValue('range') / 10000;
|
||||||
|
return this.falloff * (1 + rangeMod);
|
||||||
|
} else {
|
||||||
|
// Standard falloff calculation
|
||||||
|
const range = this.getRange();
|
||||||
|
const falloff = this._getModifiedValue('falloff');
|
||||||
|
return (falloff > range ? range : falloff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the range (in terms of seconds, for FSDI) for this module, taking in to account modifications
|
* Get the range (in terms of seconds, for FSDI) for this module, taking in to account modifications
|
||||||
* @return {Number} the range of this module
|
* @return {Number} the range of this module
|
||||||
@@ -274,6 +333,14 @@ export default class Module {
|
|||||||
return this._getModifiedValue('hullreinforcement');
|
return this._getModifiedValue('hullreinforcement');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the protection for this module, taking in to account modifications
|
||||||
|
* @return {Number} the protection of this module
|
||||||
|
*/
|
||||||
|
getProtection() {
|
||||||
|
return this._getModifiedValue('protection');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the delay for this module, taking in to account modifications
|
* Get the delay for this module, taking in to account modifications
|
||||||
* @return {Number} the delay of this module
|
* @return {Number} the delay of this module
|
||||||
@@ -342,42 +409,60 @@ export default class Module {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the minimum multiplier for this module, taking in to account modifications
|
* Get the minimum multiplier for this module, taking in to account modifications
|
||||||
|
* @param {string} type the type for which we are obtaining the multiplier. Can be 'speed', 'rotation', 'acceleration', or null
|
||||||
* @return {Number} the minimum multiplier of this module
|
* @return {Number} the minimum multiplier of this module
|
||||||
*/
|
*/
|
||||||
getMinMul() {
|
getMinMul(type = null) {
|
||||||
// Modifier is optmul
|
// Modifier is optmul
|
||||||
let result = 0;
|
let result = 0;
|
||||||
if (this['minmul']) {
|
if (this['minmul' + type]) {
|
||||||
|
result = this['minmul' + type];
|
||||||
|
} else if (this['minmul']) {
|
||||||
result = this['minmul'];
|
result = this['minmul'];
|
||||||
if (result) {
|
}
|
||||||
let mult = this.getModValue('optmul') / 10000;
|
if (result) {
|
||||||
if (mult) { result = result * (1 + mult); }
|
let mult = this.getModValue('optmul') / 10000;
|
||||||
}
|
if (mult) { result = result * (1 + mult); }
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the optimum multiplier for this module, taking in to account modifications
|
* Get the optimum multiplier for this module, taking in to account modifications
|
||||||
|
* @param {string} type the type for which we are obtaining the multiplier. Can be 'speed', 'rotation', 'acceleration', or null
|
||||||
* @return {Number} the optimum multiplier of this module
|
* @return {Number} the optimum multiplier of this module
|
||||||
*/
|
*/
|
||||||
getOptMul() {
|
getOptMul(type = null) {
|
||||||
return this._getModifiedValue('optmul');
|
// Modifier is optmul
|
||||||
|
let result = 0;
|
||||||
|
if (this['optmul' + type]) {
|
||||||
|
result = this['optmul' + type];
|
||||||
|
} else if (this['optmul']) {
|
||||||
|
result = this['optmul'];
|
||||||
|
}
|
||||||
|
if (result) {
|
||||||
|
let mult = this.getModValue('optmul') / 10000;
|
||||||
|
if (mult) { result = result * (1 + mult); }
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the maximum multiplier for this module, taking in to account modifications
|
* Get the maximum multiplier for this module, taking in to account modifications
|
||||||
|
* @param {string} type the type for which we are obtaining the multiplier. Can be 'speed', 'rotation', 'acceleration', or null
|
||||||
* @return {Number} the maximum multiplier of this module
|
* @return {Number} the maximum multiplier of this module
|
||||||
*/
|
*/
|
||||||
getMaxMul() {
|
getMaxMul(type = null) {
|
||||||
// Modifier is optmul
|
// Modifier is optmul
|
||||||
let result = 0;
|
let result = 0;
|
||||||
if (this['maxmul']) {
|
if (this['maxmul' + type]) {
|
||||||
|
result = this['maxmul' + type];
|
||||||
|
} else if (this['maxmul']) {
|
||||||
result = this['maxmul'];
|
result = this['maxmul'];
|
||||||
if (result) {
|
}
|
||||||
let mult = this.getModValue('optmul') / 10000;
|
if (result) {
|
||||||
if (mult) { result = result * (1 + mult); }
|
let mult = this.getModValue('optmul') / 10000;
|
||||||
}
|
if (mult) { result = result * (1 + mult); }
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -421,7 +506,7 @@ export default class Module {
|
|||||||
getDps() {
|
getDps() {
|
||||||
// DPS is a synthetic value
|
// DPS is a synthetic value
|
||||||
let damage = this.getDamage();
|
let damage = this.getDamage();
|
||||||
let rpshot = this.getRoundsPerShot() || 1;
|
let rpshot = this.roundspershot || 1;
|
||||||
let rof = this.getRoF() || 1;
|
let rof = this.getRoF() || 1;
|
||||||
|
|
||||||
return damage * rpshot * rof;
|
return damage * rpshot * rof;
|
||||||
@@ -434,7 +519,7 @@ export default class Module {
|
|||||||
getEps() {
|
getEps() {
|
||||||
// EPS is a synthetic value
|
// EPS is a synthetic value
|
||||||
let distdraw = this.getDistDraw();
|
let distdraw = this.getDistDraw();
|
||||||
let rpshot = this.getRoundsPerShot() || 1;
|
let rpshot = this.roundspershot || 1;
|
||||||
let rof = this.getRoF() || 1;
|
let rof = this.getRoF() || 1;
|
||||||
|
|
||||||
return distdraw * rpshot * rof;
|
return distdraw * rpshot * rof;
|
||||||
@@ -447,7 +532,7 @@ export default class Module {
|
|||||||
getHps() {
|
getHps() {
|
||||||
// HPS is a synthetic value
|
// HPS is a synthetic value
|
||||||
let heat = this.getThermalLoad();
|
let heat = this.getThermalLoad();
|
||||||
let rpshot = this.getRoundsPerShot() || 1;
|
let rpshot = this.roundspershot || 1;
|
||||||
let rof = this.getRoF() || 1;
|
let rof = this.getRoF() || 1;
|
||||||
|
|
||||||
return heat * rpshot * rof;
|
return heat * rpshot * rof;
|
||||||
@@ -458,7 +543,10 @@ export default class Module {
|
|||||||
* @return {Number} the clip size of this module
|
* @return {Number} the clip size of this module
|
||||||
*/
|
*/
|
||||||
getClip() {
|
getClip() {
|
||||||
return this._getModifiedValue('clip');
|
// Clip size is always rounded up
|
||||||
|
let result = this._getModifiedValue('clip');
|
||||||
|
if (result) { result = Math.ceil(result); }
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -478,11 +566,35 @@ export default class Module {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the rate of fire for this module, taking in to account modifications
|
* Get the burst size for this module, taking in to account modifications
|
||||||
|
* @return {Number} the burst size of this module
|
||||||
|
*/
|
||||||
|
getBurst() {
|
||||||
|
return this._getModifiedValue('burst');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the burst rate of fire for this module, taking in to account modifications
|
||||||
|
* @return {Number} the burst rate of fire of this module
|
||||||
|
*/
|
||||||
|
getBurstRoF() {
|
||||||
|
return this._getModifiedValue('burstrof');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the rate of fire for this module, taking in to account modifications.
|
||||||
|
* The rate of fire is a combination value, and needs to take in to account
|
||||||
|
* bursts of fire.
|
||||||
|
* Firing goes [burst 1] [burst interval] [burst 2] [burst interval] ... [burst n] [interval]
|
||||||
|
* where 'n' is 'burst', 'burst interval' is '1/burstrof' and 'interval' is '1/rof'
|
||||||
* @return {Number} the rate of fire for this module
|
* @return {Number} the rate of fire for this module
|
||||||
*/
|
*/
|
||||||
getRoF() {
|
getRoF() {
|
||||||
return this._getModifiedValue('rof');
|
const burst = this.getBurst() || 1;
|
||||||
|
const burstRoF = this.getBurstRoF() || 1;
|
||||||
|
const intRoF = this._getModifiedValue('rof');
|
||||||
|
|
||||||
|
return burst / (((burst - 1) / burstRoF) + 1 / intRoF);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -509,6 +621,14 @@ export default class Module {
|
|||||||
return this._getModifiedValue('shieldreinforcement');
|
return this._getModifiedValue('shieldreinforcement');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the piercing for this module, taking in to account modifications
|
||||||
|
* @return {Number} the piercing for this module
|
||||||
|
*/
|
||||||
|
getPiercing() {
|
||||||
|
return this._getModifiedValue('piercing');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the bays for this module, taking in to account modifications
|
* Get the bays for this module, taking in to account modifications
|
||||||
* @return {Number} the bays for this module
|
* @return {Number} the bays for this module
|
||||||
@@ -533,7 +653,6 @@ export default class Module {
|
|||||||
return this._getModifiedValue('cells');
|
return this._getModifiedValue('cells');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the jitter for this module, taking in to account modifications
|
* Get the jitter for this module, taking in to account modifications
|
||||||
* @return {Number} the jitter for this module
|
* @return {Number} the jitter for this module
|
||||||
@@ -541,4 +660,20 @@ export default class Module {
|
|||||||
getJitter() {
|
getJitter() {
|
||||||
return this._getModifiedValue('jitter', true);
|
return this._getModifiedValue('jitter', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the damage distribution for this module, taking in to account modifications
|
||||||
|
* @return {string} the damage distribution for this module
|
||||||
|
*/
|
||||||
|
getDamageDist() {
|
||||||
|
return this.getModValue('damagedist') || this.damagedist;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the shot speed for this module, taking in to account modifications
|
||||||
|
* @return {string} the damage distribution for this module
|
||||||
|
*/
|
||||||
|
getShotSpeed() {
|
||||||
|
return this._getModifiedValue('shotspeed');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,6 +105,59 @@ export function internal(id) {
|
|||||||
return null;
|
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.
|
* Finds an internal module based on Class, Rating, Group and/or name.
|
||||||
* At least one ofGroup name or unique module name must be provided
|
* At least one ofGroup name or unique module name must be provided
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import { outfitURL } from '../utils/UrlGenerators';
|
|||||||
|
|
||||||
const STANDARD = ['powerPlant', 'thrusters', 'frameShiftDrive', 'lifeSupport', 'powerDistributor', 'sensors', 'fuelTank'];
|
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
|
* Generates ship-loadout JSON Schema standard object
|
||||||
* @param {Object} standard model
|
* @param {Object} standard model
|
||||||
@@ -30,6 +32,10 @@ function standardToSchema(standard) {
|
|||||||
o.modifications = standard.m.mods;
|
o.modifications = standard.m.mods;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (standard.m.blueprint && Object.keys(standard.m.blueprint).length > 0) {
|
||||||
|
o.blueprint = standard.m.blueprint;
|
||||||
|
}
|
||||||
|
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -62,6 +68,10 @@ function slotToSchema(slot) {
|
|||||||
if (slot.m.mods && Object.keys(slot.m.mods).length > 0) {
|
if (slot.m.mods && Object.keys(slot.m.mods).length > 0) {
|
||||||
o.modifications = slot.m.mods;
|
o.modifications = slot.m.mods;
|
||||||
}
|
}
|
||||||
|
if (slot.m.blueprint && Object.keys(slot.m.blueprint).length > 0) {
|
||||||
|
o.blueprint = slot.m.blueprint;
|
||||||
|
}
|
||||||
|
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -124,61 +134,21 @@ export function toDetailedBuild(buildName, ship) {
|
|||||||
*/
|
*/
|
||||||
export function fromDetailedBuild(detailedBuild) {
|
export function fromDetailedBuild(detailedBuild) {
|
||||||
let shipId = Object.keys(Ships).find((shipId) => Ships[shipId].properties.name.toLowerCase() == detailedBuild.ship.toLowerCase());
|
let shipId = Object.keys(Ships).find((shipId) => Ships[shipId].properties.name.toLowerCase() == detailedBuild.ship.toLowerCase());
|
||||||
|
|
||||||
if (!shipId) {
|
if (!shipId) {
|
||||||
throw 'No such ship: ' + detailedBuild.ship;
|
throw 'No such ship: ' + detailedBuild.ship;
|
||||||
}
|
}
|
||||||
|
|
||||||
let comps = detailedBuild.components;
|
|
||||||
let stn = comps.standard;
|
|
||||||
let priorities = [stn.cargoHatch && stn.cargoHatch.priority !== undefined ? stn.cargoHatch.priority - 1 : 0];
|
|
||||||
let enabled = [stn.cargoHatch && stn.cargoHatch.enabled !== undefined ? stn.cargoHatch.enabled : true];
|
|
||||||
let shipData = Ships[shipId];
|
let shipData = Ships[shipId];
|
||||||
let ship = new Ship(shipId, shipData.properties, shipData.slots);
|
let ship = new Ship(shipId, shipData.properties, shipData.slots);
|
||||||
let bulkheads = ModuleUtils.bulkheadIndex(stn.bulkheads);
|
|
||||||
let modifications = new Array(stn.bulkheads.modifications);
|
|
||||||
|
|
||||||
if (bulkheads < 0) {
|
if (!detailedBuild.references[0] || !detailedBuild.references[0].code) {
|
||||||
throw 'Invalid bulkheads: ' + stn.bulkheads;
|
throw 'Missing reference code';
|
||||||
}
|
}
|
||||||
|
|
||||||
let standard = STANDARD.map((c) => {
|
ship.buildFrom(detailedBuild.references[0].code);
|
||||||
if (!stn[c].class || !stn[c].rating) {
|
|
||||||
throw 'Invalid value for ' + c;
|
|
||||||
}
|
|
||||||
priorities.push(stn[c].priority === undefined ? 0 : stn[c].priority - 1);
|
|
||||||
enabled.push(stn[c].enabled === undefined ? true : stn[c].enabled);
|
|
||||||
modifications.push(stn[c].modifications);
|
|
||||||
return stn[c].class + stn[c].rating;
|
|
||||||
});
|
|
||||||
|
|
||||||
let internal = comps.internal.map(c => c ? ModuleUtils.findInternalId(c.group, c.class, c.rating, c.name) : 0);
|
|
||||||
|
|
||||||
let hardpoints = comps.hardpoints
|
|
||||||
.map(c => c ? ModuleUtils.findHardpointId(c.group, c.class, c.rating, c.name, MountMap[c.mount], c.missile) : 0)
|
|
||||||
.concat(comps.utility.map(c => c ? ModuleUtils.findHardpointId(c.group, c.class, c.rating, c.name, MountMap[c.mount]) : 0));
|
|
||||||
|
|
||||||
// The ordering of these arrays must match the order in which they are read in Ship.buildWith
|
|
||||||
priorities = priorities.concat(
|
|
||||||
comps.hardpoints.map(c => (!c || c.priority === undefined) ? 0 : c.priority - 1),
|
|
||||||
comps.utility.map(c => (!c || c.priority === undefined) ? 0 : c.priority - 1),
|
|
||||||
comps.internal.map(c => (!c || c.priority === undefined) ? 0 : c.priority - 1)
|
|
||||||
);
|
|
||||||
enabled = enabled.concat(
|
|
||||||
comps.hardpoints.map(c => (!c || c.enabled === undefined) ? true : c.enabled * 1),
|
|
||||||
comps.utility.map(c => (!c || c.enabled === undefined) ? true : c.enabled * 1),
|
|
||||||
comps.internal.map(c => (!c || c.enabled === undefined) ? true : c.enabled * 1)
|
|
||||||
);
|
|
||||||
modifications = modifications.concat(
|
|
||||||
comps.hardpoints.map(c => (c && c.m ? c.m.modifications : null)),
|
|
||||||
comps.utility.map(c => (c && c.m ? c.m.modifications : null)),
|
|
||||||
comps.internal.map(c => (c && c.m ? c.m.modifications : null))
|
|
||||||
);
|
|
||||||
|
|
||||||
ship.buildWith({ bulkheads, standard, hardpoints, internal }, priorities, enabled, modifications);
|
|
||||||
|
|
||||||
return ship;
|
return ship;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates an array of ship-loadout JSON Schema object for export
|
* Generates an array of ship-loadout JSON Schema object for export
|
||||||
|
|||||||
@@ -3,12 +3,20 @@ import * as ModuleUtils from './ModuleUtils';
|
|||||||
import * as Utils from '../utils/UtilityFunctions';
|
import * as Utils from '../utils/UtilityFunctions';
|
||||||
import Module from './Module';
|
import Module from './Module';
|
||||||
import LZString from 'lz-string';
|
import LZString from 'lz-string';
|
||||||
|
import * as _ from 'lodash';
|
||||||
import isEqual from 'lodash/lang';
|
import isEqual from 'lodash/lang';
|
||||||
import { Modifications } from 'coriolis-data/dist';
|
import { Ships, Modifications } from 'coriolis-data/dist';
|
||||||
const zlib = require('zlib');
|
const zlib = require('zlib');
|
||||||
|
|
||||||
const UNIQUE_MODULES = ['psg', 'sg', 'bsg', 'rf', 'fs', 'fh'];
|
const UNIQUE_MODULES = ['psg', 'sg', 'bsg', 'rf', 'fs', 'fh'];
|
||||||
|
|
||||||
|
// Constants for modifications struct
|
||||||
|
const SLOT_ID_DONE = -1;
|
||||||
|
const MODIFICATION_ID_DONE = -1;
|
||||||
|
const MODIFICATION_ID_BLUEPRINT = -2;
|
||||||
|
const MODIFICATION_ID_GRADE = -3;
|
||||||
|
const MODIFICATION_ID_SPECIAL = -4;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the power usage type of a slot and it's particular module
|
* Returns the power usage type of a slot and it's particular module
|
||||||
* @param {Object} slot The Slot
|
* @param {Object} slot The Slot
|
||||||
@@ -180,10 +188,10 @@ export default class Ship {
|
|||||||
* Calculate the hypothetical top speeds at cargo and fuel tonnage
|
* Calculate the hypothetical top speeds at cargo and fuel tonnage
|
||||||
* @param {Number} fuel Fuel available in tons
|
* @param {Number} fuel Fuel available in tons
|
||||||
* @param {Number} cargo Cargo in tons
|
* @param {Number} cargo Cargo in tons
|
||||||
* @return {Object} Speed at pip settings and boost
|
* @return {array} Speed at pip settings
|
||||||
*/
|
*/
|
||||||
calcSpeedsWith(fuel, cargo) {
|
calcSpeedsWith(fuel, cargo) {
|
||||||
return Calc.speed(this.unladenMass + fuel + cargo, this.speed, this.boost, this.standard[1].m, this.pipSpeed);
|
return Calc.speed(this.unladenMass + fuel + cargo, this.speed, this.standard[1].m, this.pipSpeed);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -193,13 +201,11 @@ export default class Ship {
|
|||||||
* @return {Number} Recovery time in seconds
|
* @return {Number} Recovery time in seconds
|
||||||
*/
|
*/
|
||||||
calcShieldRecovery() {
|
calcShieldRecovery() {
|
||||||
if (this.shield > 0) {
|
const shieldGenerator = this.findShieldGenerator();
|
||||||
const sgSlot = this.findInternalByGroup('sg');
|
if (shieldGenerator) {
|
||||||
if (sgSlot != null) {
|
const brokenRegenRate = shieldGenerator.getBrokenRegenerationRate();
|
||||||
let brokenRegenRate = 1 + sgSlot.m.getModValue('brokenregen') / 10000;
|
// 50% of shield strength / broken recharge rate + 15 second delay before recharge starts
|
||||||
// 50% of shield strength / recovery recharge rate + 15 second delay before recharge starts
|
return ((this.shield / 2) / brokenRegenRate) + 15;
|
||||||
return ((this.shield / 2) / (sgSlot.m.recover * brokenRegenRate)) + 15;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -211,13 +217,12 @@ export default class Ship {
|
|||||||
* @return {Number} 50 - 100% Recharge time in seconds
|
* @return {Number} 50 - 100% Recharge time in seconds
|
||||||
*/
|
*/
|
||||||
calcShieldRecharge() {
|
calcShieldRecharge() {
|
||||||
if (this.shield > 0) {
|
const shieldGenerator = this.findShieldGenerator();
|
||||||
const sgSlot = this.findInternalByGroup('sg');
|
if (shieldGenerator) {
|
||||||
if (sgSlot != null) {
|
const regenRate = shieldGenerator.getRegenerationRate();
|
||||||
let regenRate = 1 + sgSlot.m.getModValue('regen') / 10000;
|
|
||||||
// 50% -> 100% recharge time, Bi-Weave shields charge at 1.8 MJ/s
|
// 50% of shield strength / recharge rate
|
||||||
return (this.shield / 2) / ((sgSlot.m.grp == 'bsg' ? 1.8 : 1) * regenRate);
|
return (this.shield / 2) / regenRate;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -287,12 +292,22 @@ export default class Ship {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the shield generator for this ship
|
||||||
|
* @return {object} The shield generator module for this ship
|
||||||
|
*/
|
||||||
|
findShieldGenerator() {
|
||||||
|
const slot = this.internal.find(slot => slot.m && ModuleUtils.isShieldGenerator(slot.m.grp));
|
||||||
|
return slot ? slot.m : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serializes the ship to a string
|
* Serializes the ship to a string
|
||||||
* @return {String} Serialized ship 'code'
|
* @return {String} Serialized ship 'code'
|
||||||
*/
|
*/
|
||||||
toString() {
|
toString() {
|
||||||
return [
|
return [
|
||||||
|
'A',
|
||||||
this.getStandardString(),
|
this.getStandardString(),
|
||||||
this.getHardpointsString(),
|
this.getHardpointsString(),
|
||||||
this.getInternalString(),
|
this.getInternalString(),
|
||||||
@@ -396,6 +411,32 @@ export default class Ship {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear all modification values for a module
|
||||||
|
* @param {Number} m The module for which to clear the modifications
|
||||||
|
*/
|
||||||
|
clearModifications(m) {
|
||||||
|
m.mods = {};
|
||||||
|
this.updatePowerGenerated()
|
||||||
|
.updatePowerUsed()
|
||||||
|
.updateJumpStats()
|
||||||
|
.recalculateShield()
|
||||||
|
.recalculateShieldCells()
|
||||||
|
.recalculateArmour()
|
||||||
|
.recalculateDps()
|
||||||
|
.recalculateEps()
|
||||||
|
.recalculateHps()
|
||||||
|
.updateMovement();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear blueprint for a module
|
||||||
|
* @param {Number} m The module for which to clear the modifications
|
||||||
|
*/
|
||||||
|
clearBlueprint(m) {
|
||||||
|
m.blueprint = {};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a modification value
|
* Set a modification value
|
||||||
* @param {Object} m The module to change
|
* @param {Object} m The module to change
|
||||||
@@ -424,7 +465,7 @@ export default class Ship {
|
|||||||
let newMass = m.getMass();
|
let newMass = m.getMass();
|
||||||
this.unladenMass = this.unladenMass - oldMass + newMass;
|
this.unladenMass = this.unladenMass - oldMass + newMass;
|
||||||
this.ladenMass = this.ladenMass - oldMass + newMass;
|
this.ladenMass = this.ladenMass - oldMass + newMass;
|
||||||
this.updateTopSpeed();
|
this.updateMovement();
|
||||||
this.updateJumpStats();
|
this.updateJumpStats();
|
||||||
} else if (name === 'maxfuel') {
|
} else if (name === 'maxfuel') {
|
||||||
m.setModValue(name, value);
|
m.setModValue(name, value);
|
||||||
@@ -432,25 +473,25 @@ export default class Ship {
|
|||||||
} else if (name === 'optmass') {
|
} else if (name === 'optmass') {
|
||||||
m.setModValue(name, value);
|
m.setModValue(name, value);
|
||||||
// Could be for any of thrusters, FSD or shield
|
// Could be for any of thrusters, FSD or shield
|
||||||
this.updateTopSpeed();
|
this.updateMovement();
|
||||||
this.updateJumpStats();
|
this.updateJumpStats();
|
||||||
this.recalculateShield();
|
this.recalculateShield();
|
||||||
} else if (name === 'optmul') {
|
} else if (name === 'optmul') {
|
||||||
m.setModValue(name, value);
|
m.setModValue(name, value);
|
||||||
// Could be for any of thrusters, FSD or shield
|
// Could be for any of thrusters, FSD or shield
|
||||||
this.updateTopSpeed();
|
this.updateMovement();
|
||||||
this.updateJumpStats();
|
this.updateJumpStats();
|
||||||
this.recalculateShield();
|
this.recalculateShield();
|
||||||
} else if (name === 'shieldboost') {
|
} else if (name === 'shieldboost') {
|
||||||
m.setModValue(name, value);
|
m.setModValue(name, value);
|
||||||
this.recalculateShield();
|
this.recalculateShield();
|
||||||
} else if (name === 'hullboost' || name === 'hullreinforcement') {
|
} else if (name === 'hullboost' || name === 'hullreinforcement' || name === 'modulereinforcement') {
|
||||||
m.setModValue(name, value);
|
m.setModValue(name, value);
|
||||||
this.recalculateArmour();
|
this.recalculateArmour();
|
||||||
} else if (name === 'shieldreinforcement') {
|
} else if (name === 'shieldreinforcement') {
|
||||||
m.setModValue(name, value);
|
m.setModValue(name, value);
|
||||||
this.recalculateShieldCells();
|
this.recalculateShieldCells();
|
||||||
} else if (name === 'burst' || name === 'clip' || name === 'damage' || name === 'distdraw' || name === 'jitter' || name === 'piercing' || name === 'range' || name === 'reload' || name === 'rof' || name === 'thermload') {
|
} else if (name === 'burst' || name == 'burstrof' || name === 'clip' || name === 'damage' || name === 'distdraw' || name === 'jitter' || name === 'piercing' || name === 'range' || name === 'reload' || name === 'rof' || name === 'thermload') {
|
||||||
m.setModValue(name, value);
|
m.setModValue(name, value);
|
||||||
this.recalculateDps();
|
this.recalculateDps();
|
||||||
this.recalculateHps();
|
this.recalculateHps();
|
||||||
@@ -472,9 +513,10 @@ export default class Ship {
|
|||||||
* @param {array} priorities Slot priorities
|
* @param {array} priorities Slot priorities
|
||||||
* @param {Array} enabled Slot active/inactive
|
* @param {Array} enabled Slot active/inactive
|
||||||
* @param {Array} mods Modifications
|
* @param {Array} mods Modifications
|
||||||
|
* @param {Array} blueprints Blueprints for modifications
|
||||||
* @return {this} The current ship instance for chaining
|
* @return {this} The current ship instance for chaining
|
||||||
*/
|
*/
|
||||||
buildWith(comps, priorities, enabled, mods) {
|
buildWith(comps, priorities, enabled, mods, blueprints) {
|
||||||
let internal = this.internal,
|
let internal = this.internal,
|
||||||
standard = this.standard,
|
standard = this.standard,
|
||||||
hps = this.hardpoints,
|
hps = this.hardpoints,
|
||||||
@@ -491,14 +533,17 @@ export default class Ship {
|
|||||||
this.totalCost = this.m.incCost ? this.m.discountedCost : 0;
|
this.totalCost = this.m.incCost ? this.m.discountedCost : 0;
|
||||||
this.unladenMass = this.hullMass;
|
this.unladenMass = this.hullMass;
|
||||||
this.totalDpe = 0;
|
this.totalDpe = 0;
|
||||||
|
this.totalAbsDpe = 0;
|
||||||
this.totalExplDpe = 0;
|
this.totalExplDpe = 0;
|
||||||
this.totalKinDpe = 0;
|
this.totalKinDpe = 0;
|
||||||
this.totalThermDpe = 0;
|
this.totalThermDpe = 0;
|
||||||
this.totalDps = 0;
|
this.totalDps = 0;
|
||||||
|
this.totalAbsDps = 0;
|
||||||
this.totalExplDps = 0;
|
this.totalExplDps = 0;
|
||||||
this.totalKinDps = 0;
|
this.totalKinDps = 0;
|
||||||
this.totalThermDps = 0;
|
this.totalThermDps = 0;
|
||||||
this.totalSDps = 0;
|
this.totalSDps = 0;
|
||||||
|
this.totalAbsSDps = 0;
|
||||||
this.totalExplSDps = 0;
|
this.totalExplSDps = 0;
|
||||||
this.totalKinSDps = 0;
|
this.totalKinSDps = 0;
|
||||||
this.totalThermSDps = 0;
|
this.totalThermSDps = 0;
|
||||||
@@ -514,6 +559,7 @@ export default class Ship {
|
|||||||
this.bulkheads.m = null;
|
this.bulkheads.m = null;
|
||||||
this.useBulkhead(comps && comps.bulkheads ? comps.bulkheads : 0, true);
|
this.useBulkhead(comps && comps.bulkheads ? comps.bulkheads : 0, true);
|
||||||
this.bulkheads.m.mods = mods && mods[0] ? mods[0] : {};
|
this.bulkheads.m.mods = mods && mods[0] ? mods[0] : {};
|
||||||
|
this.bulkheads.m.blueprint = blueprints && blueprints[0] ? blueprints[0] : {};
|
||||||
this.cargoHatch.priority = priorities ? priorities[0] * 1 : 0;
|
this.cargoHatch.priority = priorities ? priorities[0] * 1 : 0;
|
||||||
this.cargoHatch.enabled = enabled ? enabled[0] * 1 : true;
|
this.cargoHatch.enabled = enabled ? enabled[0] * 1 : true;
|
||||||
|
|
||||||
@@ -526,7 +572,10 @@ export default class Ship {
|
|||||||
standard[i].discountedCost = 0;
|
standard[i].discountedCost = 0;
|
||||||
if (comps) {
|
if (comps) {
|
||||||
let module = ModuleUtils.standard(i, comps.standard[i]);
|
let module = ModuleUtils.standard(i, comps.standard[i]);
|
||||||
if (module != null) { module.mods = mods && mods[i + 1] ? mods[i + 1] : {}; }
|
if (module != null) {
|
||||||
|
module.mods = mods && mods[i + 1] ? mods[i + 1] : {};
|
||||||
|
module.blueprint = blueprints && blueprints[i + 1] ? blueprints[i + 1] : {};
|
||||||
|
}
|
||||||
this.use(standard[i], module, true);
|
this.use(standard[i], module, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -545,7 +594,10 @@ export default class Ship {
|
|||||||
|
|
||||||
if (comps && comps.hardpoints[i] !== 0) {
|
if (comps && comps.hardpoints[i] !== 0) {
|
||||||
let module = ModuleUtils.hardpoints(comps.hardpoints[i]);
|
let module = ModuleUtils.hardpoints(comps.hardpoints[i]);
|
||||||
if (module != null) { module.mods = mods && mods[cl + i] ? mods[cl + i] : {}; }
|
if (module != null) {
|
||||||
|
module.mods = mods && mods[cl + i] ? mods[cl + i] : {};
|
||||||
|
module.blueprint = blueprints && blueprints[cl + i] ? blueprints[cl + i] : {};
|
||||||
|
}
|
||||||
this.use(hps[i], module, true);
|
this.use(hps[i], module, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -562,7 +614,10 @@ export default class Ship {
|
|||||||
|
|
||||||
if (comps && comps.internal[i] !== 0) {
|
if (comps && comps.internal[i] !== 0) {
|
||||||
let module = ModuleUtils.internal(comps.internal[i]);
|
let module = ModuleUtils.internal(comps.internal[i]);
|
||||||
if (module != null) { module.mods = mods && mods[cl + i] ? mods[cl + i] : {}; }
|
if (module != null) {
|
||||||
|
module.mods = mods && mods[cl + i] ? mods[cl + i] : {};
|
||||||
|
module.blueprint = blueprints && blueprints[cl + i] ? blueprints[cl + i] : {};
|
||||||
|
}
|
||||||
this.use(internal[i], module, true);
|
this.use(internal[i], module, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -578,7 +633,7 @@ export default class Ship {
|
|||||||
.recalculateDps()
|
.recalculateDps()
|
||||||
.recalculateEps()
|
.recalculateEps()
|
||||||
.recalculateHps()
|
.recalculateHps()
|
||||||
.updateTopSpeed();
|
.updateMovement();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.updatePowerPrioritesString().updatePowerEnabledString().updateModificationsString();
|
return this.updatePowerPrioritesString().updatePowerEnabledString().updateModificationsString();
|
||||||
@@ -596,11 +651,25 @@ export default class Ship {
|
|||||||
hardpoints = new Array(this.hardpoints.length),
|
hardpoints = new Array(this.hardpoints.length),
|
||||||
internal = new Array(this.internal.length),
|
internal = new Array(this.internal.length),
|
||||||
modifications = new Array(1 + this.standard.length + this.hardpoints.length + this.internal.length),
|
modifications = new Array(1 + this.standard.length + this.hardpoints.length + this.internal.length),
|
||||||
|
blueprints = new Array(1 + this.standard.length + this.hardpoints.length + this.internal.length),
|
||||||
parts = serializedString.split('.'),
|
parts = serializedString.split('.'),
|
||||||
priorities = null,
|
priorities = null,
|
||||||
enabled = null,
|
enabled = null,
|
||||||
code = parts[0];
|
code = parts[0];
|
||||||
|
|
||||||
|
// Code has a version ID embedded as the first character (if it is alphabetic)
|
||||||
|
let version;
|
||||||
|
if (code && code.match(/^[0-4]/)) {
|
||||||
|
// Starting with bulkhead number is version 1
|
||||||
|
version = 1;
|
||||||
|
} else {
|
||||||
|
// Version 2 (current version)
|
||||||
|
version = 2;
|
||||||
|
if (code) {
|
||||||
|
code = code.substring(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (parts[1]) {
|
if (parts[1]) {
|
||||||
enabled = LZString.decompressFromBase64(Utils.fromUrlSafe(parts[1])).split('');
|
enabled = LZString.decompressFromBase64(Utils.fromUrlSafe(parts[1])).split('');
|
||||||
}
|
}
|
||||||
@@ -615,7 +684,7 @@ export default class Ship {
|
|||||||
this.decodeModificationsString(modstr, modifications);
|
this.decodeModificationsString(modstr, modifications);
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
this.decodeModificationsStruct(zlib.gunzipSync(new Buffer(Utils.fromUrlSafe(modstr), 'base64')), modifications);
|
this.decodeModificationsStruct(zlib.gunzipSync(new Buffer(Utils.fromUrlSafe(modstr), 'base64')), modifications, blueprints);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Could be out-of-date URL; ignore
|
// Could be out-of-date URL; ignore
|
||||||
}
|
}
|
||||||
@@ -624,6 +693,11 @@ export default class Ship {
|
|||||||
|
|
||||||
decodeToArray(code, internal, decodeToArray(code, hardpoints, decodeToArray(code, standard, 1)));
|
decodeToArray(code, internal, decodeToArray(code, hardpoints, decodeToArray(code, standard, 1)));
|
||||||
|
|
||||||
|
if (version != 2) {
|
||||||
|
// Alter as required due to changes in the (build) code from one version to the next
|
||||||
|
this.upgradeInternals(internal, 1 + this.standard.length + this.hardpoints.length, priorities, enabled, modifications, blueprints, version);
|
||||||
|
}
|
||||||
|
|
||||||
return this.buildWith(
|
return this.buildWith(
|
||||||
{
|
{
|
||||||
bulkheads: code.charAt(0) * 1,
|
bulkheads: code.charAt(0) * 1,
|
||||||
@@ -633,7 +707,8 @@ export default class Ship {
|
|||||||
},
|
},
|
||||||
priorities,
|
priorities,
|
||||||
enabled,
|
enabled,
|
||||||
modifications
|
modifications,
|
||||||
|
blueprints,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -780,7 +855,7 @@ export default class Ship {
|
|||||||
let epsChanged = n && n.getEps() || old && old.getEps();
|
let epsChanged = n && n.getEps() || old && old.getEps();
|
||||||
let hpsChanged = n && n.getHps() || old && old.getHps();
|
let hpsChanged = n && n.getHps() || old && old.getHps();
|
||||||
|
|
||||||
let armourChange = (slot === this.bulkheads) || (n && n.grp === 'hr') || (old && old.grp === 'hr');
|
let armourChange = (slot === this.bulkheads) || (n && n.grp === 'hr') || (old && old.grp === 'hr') || (n && n.grp === 'mrp') || (old && old.grp === 'mrp');
|
||||||
|
|
||||||
let shieldChange = (n && n.grp === 'bsg') || (old && old.grp === 'bsg') || (n && n.grp === 'psg') || (old && old.grp === 'psg') || (n && n.grp === 'sg') || (old && old.grp === 'sg') || (n && n.grp === 'sb') || (old && old.grp === 'sb');
|
let shieldChange = (n && n.grp === 'bsg') || (old && old.grp === 'bsg') || (n && n.grp === 'psg') || (old && old.grp === 'psg') || (n && n.grp === 'sg') || (old && old.grp === 'sg') || (n && n.grp === 'sb') || (old && old.grp === 'sb');
|
||||||
|
|
||||||
@@ -855,7 +930,7 @@ export default class Ship {
|
|||||||
if (shieldCellsChange) {
|
if (shieldCellsChange) {
|
||||||
this.recalculateShieldCells();
|
this.recalculateShieldCells();
|
||||||
}
|
}
|
||||||
this.updateTopSpeed();
|
this.updateMovement();
|
||||||
this.updateJumpStats();
|
this.updateJumpStats();
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
@@ -872,11 +947,10 @@ export default class Ship {
|
|||||||
* @return {this} The ship instance (for chaining operations)
|
* @return {this} The ship instance (for chaining operations)
|
||||||
*/
|
*/
|
||||||
diminishingReturns(val, drll, drul) {
|
diminishingReturns(val, drll, drul) {
|
||||||
if (val > drll) {
|
if (val < drll) {
|
||||||
val = drll + (val - drll) / 2;
|
val = drll;
|
||||||
}
|
} else if (val < drul) {
|
||||||
if (val > drul) {
|
val = drul - (drul - val) / 2;
|
||||||
val = drul;
|
|
||||||
}
|
}
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
@@ -887,14 +961,17 @@ export default class Ship {
|
|||||||
*/
|
*/
|
||||||
recalculateDps() {
|
recalculateDps() {
|
||||||
let totalDpe = 0;
|
let totalDpe = 0;
|
||||||
|
let totalAbsDpe = 0;
|
||||||
let totalExplDpe = 0;
|
let totalExplDpe = 0;
|
||||||
let totalKinDpe = 0;
|
let totalKinDpe = 0;
|
||||||
let totalThermDpe = 0;
|
let totalThermDpe = 0;
|
||||||
let totalDps = 0;
|
let totalDps = 0;
|
||||||
|
let totalAbsDps = 0;
|
||||||
let totalExplDps = 0;
|
let totalExplDps = 0;
|
||||||
let totalKinDps = 0;
|
let totalKinDps = 0;
|
||||||
let totalThermDps = 0;
|
let totalThermDps = 0;
|
||||||
let totalSDps = 0;
|
let totalSDps = 0;
|
||||||
|
let totalAbsSDps = 0;
|
||||||
let totalExplSDps = 0;
|
let totalExplSDps = 0;
|
||||||
let totalKinSDps = 0;
|
let totalKinSDps = 0;
|
||||||
let totalThermSDps = 0;
|
let totalThermSDps = 0;
|
||||||
@@ -902,64 +979,50 @@ export default class Ship {
|
|||||||
for (let slotNum in this.hardpoints) {
|
for (let slotNum in this.hardpoints) {
|
||||||
const slot = this.hardpoints[slotNum];
|
const slot = this.hardpoints[slotNum];
|
||||||
if (slot.m && slot.enabled && slot.m.getDps()) {
|
if (slot.m && slot.enabled && slot.m.getDps()) {
|
||||||
const dpe = slot.m.getDps() / slot.m.getEps();
|
const dpe = slot.m.getEps() === 0 ? 0 : slot.m.getDps() / slot.m.getEps();
|
||||||
const dps = slot.m.getDps();
|
const dps = slot.m.getDps();
|
||||||
const sdps = slot.m.getClip() ? (slot.m.getClip() * slot.m.getDps() / slot.m.getRoF()) / ((slot.m.getClip() / slot.m.getRoF()) + slot.m.getReload()) : dps;
|
const sdps = slot.m.getClip() ? (slot.m.getClip() * slot.m.getDps() / slot.m.getRoF()) / ((slot.m.getClip() / slot.m.getRoF()) + slot.m.getReload()) : dps;
|
||||||
|
|
||||||
totalDpe += dpe;
|
totalDpe += dpe;
|
||||||
totalDps += dps;
|
totalDps += dps;
|
||||||
totalSDps += sdps;
|
totalSDps += sdps;
|
||||||
if (slot.m.type === 'E') {
|
if (slot.m.getDamageDist()) {
|
||||||
totalExplDpe += dpe;
|
if (slot.m.getDamageDist().A) {
|
||||||
totalExplDps += dps;
|
totalAbsDpe += dpe * slot.m.getDamageDist().A;
|
||||||
totalExplSDps += sdps;
|
totalAbsDps += dps * slot.m.getDamageDist().A;
|
||||||
}
|
totalAbsSDps += sdps * slot.m.getDamageDist().A;
|
||||||
if (slot.m.type === 'K') {
|
}
|
||||||
totalKinDpe += dpe;
|
if (slot.m.getDamageDist().E) {
|
||||||
totalKinDps += dps;
|
totalExplDpe += dpe * slot.m.getDamageDist().E;
|
||||||
totalKinSDps += sdps;
|
totalExplDps += dps * slot.m.getDamageDist().E;
|
||||||
}
|
totalExplSDps += sdps * slot.m.getDamageDist().E;
|
||||||
if (slot.m.type === 'T') {
|
}
|
||||||
totalThermDpe += dpe;
|
if (slot.m.getDamageDist().K) {
|
||||||
totalThermDps += dps;
|
totalKinDpe += dpe * slot.m.getDamageDist().K;
|
||||||
totalThermSDps += sdps;
|
totalKinDps += dps * slot.m.getDamageDist().K;
|
||||||
}
|
totalKinSDps += sdps * slot.m.getDamageDist().K;
|
||||||
if (slot.m.type === 'EK') {
|
}
|
||||||
totalExplDpe += dpe / 2;
|
if (slot.m.getDamageDist().T) {
|
||||||
totalKinDpe += dpe / 2;
|
totalThermDpe += dpe * slot.m.getDamageDist().T;
|
||||||
totalExplDps += dps / 2;
|
totalThermDps += dps * slot.m.getDamageDist().T;
|
||||||
totalKinDps += dps / 2;
|
totalThermSDps += sdps * slot.m.getDamageDist().T;
|
||||||
totalExplSDps += sdps / 2;
|
}
|
||||||
totalKinSDps += sdps / 2;
|
|
||||||
}
|
|
||||||
if (slot.m.type === 'ET') {
|
|
||||||
totalExplDpe += dpe / 2;
|
|
||||||
totalThermDpe += dpe / 2;
|
|
||||||
totalExplDps += dps / 2;
|
|
||||||
totalThermDps += dps / 2;
|
|
||||||
totalExplSDps += sdps / 2;
|
|
||||||
totalThermSDps += sdps / 2;
|
|
||||||
}
|
|
||||||
if (slot.m.type === 'KT') {
|
|
||||||
totalKinDpe += dpe / 2;
|
|
||||||
totalThermDpe += dpe / 2;
|
|
||||||
totalKinDps += dps / 2;
|
|
||||||
totalThermDps += dps / 2;
|
|
||||||
totalKinSDps += sdps / 2;
|
|
||||||
totalThermSDps += sdps / 2;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.totalDpe = totalDpe;
|
this.totalDpe = totalDpe;
|
||||||
|
this.totalAbsDpe = totalAbsDpe;
|
||||||
this.totalExplDpe = totalExplDpe;
|
this.totalExplDpe = totalExplDpe;
|
||||||
this.totalKinDpe = totalKinDpe;
|
this.totalKinDpe = totalKinDpe;
|
||||||
this.totalThermDpe = totalThermDpe;
|
this.totalThermDpe = totalThermDpe;
|
||||||
this.totalDps = totalDps;
|
this.totalDps = totalDps;
|
||||||
|
this.totalAbsDps = totalAbsDps;
|
||||||
this.totalExplDps = totalExplDps;
|
this.totalExplDps = totalExplDps;
|
||||||
this.totalKinDps = totalKinDps;
|
this.totalKinDps = totalKinDps;
|
||||||
this.totalThermDps = totalThermDps;
|
this.totalThermDps = totalThermDps;
|
||||||
this.totalSDps = totalSDps;
|
this.totalSDps = totalSDps;
|
||||||
|
this.totalAbsSDps = totalAbsSDps;
|
||||||
this.totalExplSDps = totalExplSDps;
|
this.totalExplSDps = totalExplSDps;
|
||||||
this.totalKinSDps = totalKinSDps;
|
this.totalKinSDps = totalKinSDps;
|
||||||
this.totalThermSDps = totalThermSDps;
|
this.totalThermSDps = totalThermSDps;
|
||||||
@@ -1067,13 +1130,23 @@ export default class Ship {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update top speed and boost
|
* Update movement values
|
||||||
* @return {this} The ship instance (for chaining operations)
|
* @return {this} The ship instance (for chaining operations)
|
||||||
*/
|
*/
|
||||||
updateTopSpeed() {
|
updateMovement() {
|
||||||
let speeds = Calc.speed(this.unladenMass + this.fuelCapacity, this.speed, this.boost, this.standard[1].m, this.pipSpeed);
|
this.speeds = Calc.speed(this.unladenMass + this.fuelCapacity, this.speed, this.standard[1].m, this.pipSpeed);
|
||||||
this.topSpeed = speeds['4 Pips'];
|
this.topSpeed = this.speeds[4];
|
||||||
this.topBoost = this.canBoost() ? speeds.boost : 0;
|
this.topBoost = this.canBoost() ? this.speeds[4] * this.boost / this.speed : 0;
|
||||||
|
|
||||||
|
this.pitches = Calc.pitch(this.unladenMass + this.fuelCapacity, this.pitch, this.standard[1].m, this.pipSpeed);
|
||||||
|
this.topPitch = this.pitches[4];
|
||||||
|
|
||||||
|
this.rolls = Calc.roll(this.unladenMass + this.fuelCapacity, this.roll, this.standard[1].m, this.pipSpeed);
|
||||||
|
this.topRoll = this.rolls[4];
|
||||||
|
|
||||||
|
this.yaws = Calc.yaw(this.unladenMass + this.fuelCapacity, this.yaw, this.standard[1].m, this.pipSpeed);
|
||||||
|
this.topYaw = this.yaws[4];
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1083,23 +1156,35 @@ export default class Ship {
|
|||||||
*/
|
*/
|
||||||
recalculateShield() {
|
recalculateShield() {
|
||||||
let shield = 0;
|
let shield = 0;
|
||||||
|
let shieldBoost = 1;
|
||||||
let shieldExplRes = null;
|
let shieldExplRes = null;
|
||||||
let shieldKinRes = null;
|
let shieldKinRes = null;
|
||||||
let shieldThermRes = null;
|
let shieldThermRes = null;
|
||||||
|
let shieldExplDRStart = null;
|
||||||
|
let shieldExplDREnd = null;
|
||||||
|
let shieldKinDRStart = null;
|
||||||
|
let shieldKinDREnd = null;
|
||||||
|
let shieldThermDRStart = null;
|
||||||
|
let shieldThermDREnd = null;
|
||||||
|
|
||||||
const sgSlot = this.findInternalByGroup('sg');
|
const sgSlot = this.findInternalByGroup('sg');
|
||||||
if (sgSlot && sgSlot.enabled) {
|
if (sgSlot && sgSlot.enabled) {
|
||||||
// Shield from generator
|
// Shield from generator
|
||||||
const baseShield = Calc.shieldStrength(this.hullMass, this.baseShieldStrength, sgSlot.m, 1);
|
shield = Calc.shieldStrength(this.hullMass, this.baseShieldStrength, sgSlot.m, 1);
|
||||||
shield = baseShield;
|
|
||||||
shieldExplRes = 1 - sgSlot.m.getExplosiveResistance();
|
shieldExplRes = 1 - sgSlot.m.getExplosiveResistance();
|
||||||
|
shieldExplDRStart = shieldExplRes * 0.7;
|
||||||
|
shieldExplDREnd = shieldExplRes * 0; // Currently don't know where this is
|
||||||
shieldKinRes = 1 - sgSlot.m.getKineticResistance();
|
shieldKinRes = 1 - sgSlot.m.getKineticResistance();
|
||||||
|
shieldKinDRStart = shieldKinRes * 0.7;
|
||||||
|
shieldKinDREnd = shieldKinRes * 0; // Currently don't know where this is
|
||||||
shieldThermRes = 1 - sgSlot.m.getThermalResistance();
|
shieldThermRes = 1 - sgSlot.m.getThermalResistance();
|
||||||
|
shieldThermDRStart = shieldThermRes * 0.7;
|
||||||
|
shieldThermDREnd = shieldThermRes * 0; // Currently don't know where this is
|
||||||
|
|
||||||
// Shield from boosters
|
// Shield from boosters
|
||||||
for (let slot of this.hardpoints) {
|
for (let slot of this.hardpoints) {
|
||||||
if (slot.m && slot.m.grp == 'sb') {
|
if (slot.enabled && slot.m && slot.m.grp == 'sb') {
|
||||||
shield += baseShield * slot.m.getShieldBoost();
|
shieldBoost += slot.m.getShieldBoost();
|
||||||
shieldExplRes *= (1 - slot.m.getExplosiveResistance());
|
shieldExplRes *= (1 - slot.m.getExplosiveResistance());
|
||||||
shieldKinRes *= (1 - slot.m.getKineticResistance());
|
shieldKinRes *= (1 - slot.m.getKineticResistance());
|
||||||
shieldThermRes *= (1 - slot.m.getThermalResistance());
|
shieldThermRes *= (1 - slot.m.getThermalResistance());
|
||||||
@@ -1107,10 +1192,16 @@ export default class Ship {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We apply diminishing returns to the boosted value
|
||||||
|
// (no we don't; FD pulled back on this idea. But leave this here in case they reinstate it)
|
||||||
|
// shieldBoost = Math.min(shieldBoost, (1 - Math.pow(Math.E, -0.7 * shieldBoost)) * 2.5);
|
||||||
|
|
||||||
|
shield = shield * shieldBoost;
|
||||||
|
|
||||||
this.shield = shield;
|
this.shield = shield;
|
||||||
this.shieldExplRes = shieldExplRes ? 1 - this.diminishingReturns(1 - shieldExplRes, 0.5, 0.75) : null;
|
this.shieldExplRes = shieldExplRes ? 1 - this.diminishingReturns(shieldExplRes, shieldExplDREnd, shieldExplDRStart) : null;
|
||||||
this.shieldKinRes = shieldKinRes ? 1 - this.diminishingReturns(1 - shieldKinRes, 0.5, 0.75) : null;
|
this.shieldKinRes = shieldKinRes ? 1 - this.diminishingReturns(shieldKinRes, shieldKinDREnd, shieldKinDRStart) : null;
|
||||||
this.shieldThermRes = shieldThermRes ? 1 - this.diminishingReturns(1 - shieldThermRes, 0.5, 0.75) : null;
|
this.shieldThermRes = shieldThermRes ? 1 - this.diminishingReturns(shieldThermRes, shieldThermDREnd, shieldThermDRStart) : null;
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -1141,11 +1232,19 @@ export default class Ship {
|
|||||||
// Armour from bulkheads
|
// Armour from bulkheads
|
||||||
let bulkhead = this.bulkheads.m;
|
let bulkhead = this.bulkheads.m;
|
||||||
let armour = this.baseArmour + (this.baseArmour * bulkhead.getHullBoost());
|
let armour = this.baseArmour + (this.baseArmour * bulkhead.getHullBoost());
|
||||||
|
let modulearmour = 0;
|
||||||
|
let moduleprotection = 1;
|
||||||
let hullExplRes = 1 - bulkhead.getExplosiveResistance();
|
let hullExplRes = 1 - bulkhead.getExplosiveResistance();
|
||||||
|
const hullExplResDRStart = hullExplRes * 0.7;
|
||||||
|
const hullExplResDREnd = hullExplRes * 0; // Currently don't know where this is
|
||||||
let hullKinRes = 1 - bulkhead.getKineticResistance();
|
let hullKinRes = 1 - bulkhead.getKineticResistance();
|
||||||
|
const hullKinResDRStart = hullKinRes * 0.7;
|
||||||
|
const hullKinResDREnd = hullKinRes * 0; // Currently don't know where this is
|
||||||
let hullThermRes = 1 - bulkhead.getThermalResistance();
|
let hullThermRes = 1 - bulkhead.getThermalResistance();
|
||||||
|
const hullThermResDRStart = hullThermRes * 0.7;
|
||||||
|
const hullThermResDREnd = hullThermRes * 0; // Currently don't know where this is
|
||||||
|
|
||||||
// Armour from HRPs
|
// Armour from HRPs and module armour from MRPs
|
||||||
for (let slot of this.internal) {
|
for (let slot of this.internal) {
|
||||||
if (slot.m && slot.m.grp == 'hr') {
|
if (slot.m && slot.m.grp == 'hr') {
|
||||||
armour += slot.m.getHullReinforcement();
|
armour += slot.m.getHullReinforcement();
|
||||||
@@ -1156,12 +1255,19 @@ export default class Ship {
|
|||||||
hullKinRes *= (1 - slot.m.getKineticResistance());
|
hullKinRes *= (1 - slot.m.getKineticResistance());
|
||||||
hullThermRes *= (1 - slot.m.getThermalResistance());
|
hullThermRes *= (1 - slot.m.getThermalResistance());
|
||||||
}
|
}
|
||||||
|
if (slot.m && slot.m.grp == 'mrp') {
|
||||||
|
modulearmour += slot.m.getIntegrity();
|
||||||
|
moduleprotection = moduleprotection * (1 - slot.m.getProtection());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
moduleprotection = 1 - moduleprotection;
|
||||||
|
|
||||||
this.armour = armour;
|
this.armour = armour;
|
||||||
this.hullExplRes = 1 - this.diminishingReturns(1 - hullExplRes, 0.5, 0.75);
|
this.modulearmour = modulearmour;
|
||||||
this.hullKinRes = 1 - this.diminishingReturns(1 - hullKinRes, 0.5, 0.75);
|
this.moduleprotection = moduleprotection;
|
||||||
this.hullThermRes = 1 - this.diminishingReturns(1 - hullThermRes, 0.5, 0.75);
|
this.hullExplRes = 1 - this.diminishingReturns(hullExplRes, hullExplResDREnd, hullExplResDRStart);
|
||||||
|
this.hullKinRes = 1 - this.diminishingReturns(hullKinRes, hullKinResDREnd, hullKinResDRStart);
|
||||||
|
this.hullThermRes = 1 - this.diminishingReturns(hullThermRes, hullThermResDREnd, hullThermResDRStart);
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -1224,52 +1330,6 @@ export default class Ship {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the modifications string in a human-readable format
|
|
||||||
* @return {this} The ship instance (for chaining operations)
|
|
||||||
*/
|
|
||||||
debugupdateModificationsString() {
|
|
||||||
let allMods = new Array();
|
|
||||||
|
|
||||||
let bulkheadMods = new Array();
|
|
||||||
if (this.bulkheads.m && this.bulkheads.m.mods) {
|
|
||||||
for (let modKey in this.bulkheads.m.mods) {
|
|
||||||
bulkheadMods.push(Modifications.modifications.indexOf(modKey) + ':' + this.bulkheads.m.getModValue(modKey));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
allMods.push(bulkheadMods.join(';'));
|
|
||||||
|
|
||||||
for (let slot of this.standard) {
|
|
||||||
let slotMods = new Array();
|
|
||||||
if (slot.m && slot.m.mods) {
|
|
||||||
for (let modKey in slot.m.mods) {
|
|
||||||
slotMods.push(Modifications.modifications.indexOf(modKey) + ':' + slot.m.getModValue(modKey));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
allMods.push(slotMods.join(';'));
|
|
||||||
}
|
|
||||||
for (let slot of this.hardpoints) {
|
|
||||||
let slotMods = new Array();
|
|
||||||
if (slot.m && slot.m.mods) {
|
|
||||||
for (let modKey in slot.m.mods) {
|
|
||||||
slotMods.push(Modifications.modifications.indexOf(modKey) + ':' + slot.m.getModValue(modKey));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
allMods.push(slotMods.join(';'));
|
|
||||||
}
|
|
||||||
for (let slot of this.internal) {
|
|
||||||
let slotMods = new Array();
|
|
||||||
if (slot.m && slot.m.mods) {
|
|
||||||
for (let modKey in slot.m.mods) {
|
|
||||||
slotMods.push(Modifications.modifications.indexOf(modKey) + ':' + slot.m.getModValue(modKey));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
allMods.push(slotMods.join(';'));
|
|
||||||
}
|
|
||||||
this.serialized.modifications = LZString.compressToBase64(allMods.join(',').replace(/,+$/, ''));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Populate the modifications array with modification values from the code
|
* Populate the modifications array with modification values from the code
|
||||||
* @param {String} code Serialized modification code
|
* @param {String} code Serialized modification code
|
||||||
@@ -1284,7 +1344,8 @@ export default class Ship {
|
|||||||
for (let j = 0; j < mods.length; j++) {
|
for (let j = 0; j < mods.length; j++) {
|
||||||
let modElements = mods[j].split(':');
|
let modElements = mods[j].split(':');
|
||||||
if (modElements[0].match('[0-9]+')) {
|
if (modElements[0].match('[0-9]+')) {
|
||||||
arr[i][Modifications.modifications[modElements[0]]] = Number(modElements[1]);
|
const modification = _.find(Modifications.modifications, function(o) { return o.id === modElements[0]; });
|
||||||
|
if (modification != null) arr[i][modification.name] = Number(modElements[1]);
|
||||||
} else {
|
} else {
|
||||||
arr[i][modElements[0]] = Number(modElements[1]);
|
arr[i][modElements[0]] = Number(modElements[1]);
|
||||||
}
|
}
|
||||||
@@ -1305,29 +1366,38 @@ export default class Ship {
|
|||||||
updateModificationsString() {
|
updateModificationsString() {
|
||||||
// Start off by gathering the information that we need
|
// Start off by gathering the information that we need
|
||||||
let slots = new Array();
|
let slots = new Array();
|
||||||
|
let blueprints = new Array();
|
||||||
|
let specials = new Array();
|
||||||
|
|
||||||
let bulkheadMods = new Array();
|
let bulkheadMods = new Array();
|
||||||
|
let bulkheadBlueprint = null;
|
||||||
|
let bulkheadBlueprintGrade = null;
|
||||||
if (this.bulkheads.m && this.bulkheads.m.mods) {
|
if (this.bulkheads.m && this.bulkheads.m.mods) {
|
||||||
for (let modKey in this.bulkheads.m.mods) {
|
for (let modKey in this.bulkheads.m.mods) {
|
||||||
// Filter out invalid modifications
|
// Filter out invalid modifications
|
||||||
if (Modifications.validity['bh'] && Modifications.validity['bh'].indexOf(modKey) != -1) {
|
if (Modifications.modules['bh'] && Modifications.modules['bh'].modifications.indexOf(modKey) != -1) {
|
||||||
bulkheadMods.push({ id: Modifications.modifications.indexOf(modKey), value: this.bulkheads.m.getModValue(modKey) });
|
bulkheadMods.push({ id: Modifications.modifications[modKey].id, value: this.bulkheads.m.getModValue(modKey) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
bulkheadBlueprint = this.bulkheads.m.blueprint;
|
||||||
}
|
}
|
||||||
slots.push(bulkheadMods);
|
slots.push(bulkheadMods);
|
||||||
|
blueprints.push(bulkheadBlueprint);
|
||||||
|
specials.push(bulkheadBlueprint ? bulkheadBlueprint.special : null);
|
||||||
|
|
||||||
for (let slot of this.standard) {
|
for (let slot of this.standard) {
|
||||||
let slotMods = new Array();
|
let slotMods = new Array();
|
||||||
if (slot.m && slot.m.mods) {
|
if (slot.m && slot.m.mods) {
|
||||||
for (let modKey in slot.m.mods) {
|
for (let modKey in slot.m.mods) {
|
||||||
// Filter out invalid modifications
|
// Filter out invalid modifications
|
||||||
if (Modifications.validity[slot.m.grp] && Modifications.validity[slot.m.grp].indexOf(modKey) != -1) {
|
if (Modifications.modules[slot.m.grp] && Modifications.modules[slot.m.grp].modifications.indexOf(modKey) != -1) {
|
||||||
slotMods.push({ id: Modifications.modifications.indexOf(modKey), value: slot.m.getModValue(modKey) });
|
slotMods.push({ id: Modifications.modifications[modKey].id, value: slot.m.getModValue(modKey) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
slots.push(slotMods);
|
slots.push(slotMods);
|
||||||
|
blueprints.push(slot.m ? slot.m.blueprint : null);
|
||||||
|
specials.push(slot.m && slot.m.blueprint ? slot.m.blueprint.special : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let slot of this.hardpoints) {
|
for (let slot of this.hardpoints) {
|
||||||
@@ -1335,12 +1405,14 @@ export default class Ship {
|
|||||||
if (slot.m && slot.m.mods) {
|
if (slot.m && slot.m.mods) {
|
||||||
for (let modKey in slot.m.mods) {
|
for (let modKey in slot.m.mods) {
|
||||||
// Filter out invalid modifications
|
// Filter out invalid modifications
|
||||||
if (Modifications.validity[slot.m.grp] && Modifications.validity[slot.m.grp].indexOf(modKey) != -1) {
|
if (Modifications.modules[slot.m.grp] && Modifications.modules[slot.m.grp].modifications.indexOf(modKey) != -1) {
|
||||||
slotMods.push({ id: Modifications.modifications.indexOf(modKey), value: slot.m.getModValue(modKey) });
|
slotMods.push({ id: Modifications.modifications[modKey].id, value: slot.m.getModValue(modKey) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
slots.push(slotMods);
|
slots.push(slotMods);
|
||||||
|
blueprints.push(slot.m ? slot.m.blueprint : null);
|
||||||
|
specials.push(slot.m && slot.m.blueprint ? slot.m.blueprint.special : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let slot of this.internal) {
|
for (let slot of this.internal) {
|
||||||
@@ -1348,19 +1420,28 @@ export default class Ship {
|
|||||||
if (slot.m && slot.m.mods) {
|
if (slot.m && slot.m.mods) {
|
||||||
for (let modKey in slot.m.mods) {
|
for (let modKey in slot.m.mods) {
|
||||||
// Filter out invalid modifications
|
// Filter out invalid modifications
|
||||||
if (Modifications.validity[slot.m.grp] && Modifications.validity[slot.m.grp].indexOf(modKey) != -1) {
|
if (Modifications.modules[slot.m.grp] && Modifications.modules[slot.m.grp].modifications.indexOf(modKey) != -1) {
|
||||||
slotMods.push({ id: Modifications.modifications.indexOf(modKey), value: slot.m.getModValue(modKey) });
|
slotMods.push({ id: Modifications.modifications[modKey].id, value: slot.m.getModValue(modKey) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
slots.push(slotMods);
|
slots.push(slotMods);
|
||||||
|
blueprints.push(slot.m ? slot.m.blueprint : null);
|
||||||
|
specials.push(slot.m && slot.m.blueprint ? slot.m.blueprint.special : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now work out the size of the binary buffer from our modifications array
|
// Now work out the size of the binary buffer from our modifications array
|
||||||
let bufsize = 0;
|
let bufsize = 0;
|
||||||
for (let slot of slots) {
|
for (let slot of slots) {
|
||||||
if (slot.length > 0) {
|
if (slot.length > 0) {
|
||||||
bufsize = bufsize + 1 + (5 * slot.length) + 1;
|
// Length is 1 for the slot ID, 10 for the blueprint name and grade, 5 for each modification, and 1 for the end marker
|
||||||
|
bufsize = bufsize + 1 + 10 + (5 * slot.length) + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let special of specials) {
|
||||||
|
if (special) {
|
||||||
|
// Length is 5 for each special
|
||||||
|
bufsize += 5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1373,18 +1454,37 @@ export default class Ship {
|
|||||||
for (let slot of slots) {
|
for (let slot of slots) {
|
||||||
if (slot.length > 0) {
|
if (slot.length > 0) {
|
||||||
buffer.writeInt8(i, curpos++);
|
buffer.writeInt8(i, curpos++);
|
||||||
for (let slotMod of slot) {
|
if (blueprints[i] && blueprints[i].id) {
|
||||||
buffer.writeInt8(slotMod.id, curpos++);
|
buffer.writeInt8(MODIFICATION_ID_BLUEPRINT, curpos++);
|
||||||
buffer.writeInt32LE(slotMod.value, curpos);
|
buffer.writeInt32LE(blueprints[i].id, curpos);
|
||||||
// console.log('ENCODE Slot ' + i + ': ' + Modifications.modifications[slotMod.id] + ' = ' + slotMod.value);
|
curpos += 4;
|
||||||
|
buffer.writeInt8(MODIFICATION_ID_GRADE, curpos++);
|
||||||
|
buffer.writeInt32LE(blueprints[i].grade, curpos);
|
||||||
curpos += 4;
|
curpos += 4;
|
||||||
}
|
}
|
||||||
buffer.writeInt8(-1, curpos++);
|
if (specials[i]) {
|
||||||
|
buffer.writeInt8(MODIFICATION_ID_SPECIAL, curpos++);
|
||||||
|
buffer.writeInt32LE(specials[i].id, curpos);
|
||||||
|
curpos += 4;
|
||||||
|
}
|
||||||
|
for (let slotMod of slot) {
|
||||||
|
buffer.writeInt8(slotMod.id, curpos++);
|
||||||
|
if (isNaN(slotMod.value)) {
|
||||||
|
// Need to write the string with exactly four characters, so pad with whitespace
|
||||||
|
buffer.write((' ' + slotMod.value).slice(-4), curpos, 4);
|
||||||
|
} else {
|
||||||
|
buffer.writeInt32LE(slotMod.value, curpos);
|
||||||
|
}
|
||||||
|
// const modification = _.find(Modifications.modifications, function(o) { return o.id === slotMod.id; });
|
||||||
|
// console.log('ENCODE Slot ' + i + ': ' + modification.name + ' = ' + slotMod.value);
|
||||||
|
curpos += 4;
|
||||||
|
}
|
||||||
|
buffer.writeInt8(MODIFICATION_ID_DONE, curpos++);
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
if (curpos > 0) {
|
if (curpos > 0) {
|
||||||
buffer.writeInt8(-1, curpos++);
|
buffer.writeInt8(SLOT_ID_DONE, curpos++);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.serialized.modifications = zlib.gzipSync(buffer).toString('base64');
|
this.serialized.modifications = zlib.gzipSync(buffer).toString('base64');
|
||||||
@@ -1397,23 +1497,48 @@ export default class Ship {
|
|||||||
/**
|
/**
|
||||||
* Populate the modifications array with modification values from the code.
|
* Populate the modifications array with modification values from the code.
|
||||||
* See updateModificationsString() for details of the structure.
|
* See updateModificationsString() for details of the structure.
|
||||||
* @param {String} buffer Buffer holding modification info
|
* @param {String} buffer Buffer holding modification info
|
||||||
* @param {Array} arr Modification array
|
* @param {Array} modArr Modification array
|
||||||
|
* @param {Array} blueprintArr Blueprint array
|
||||||
*/
|
*/
|
||||||
decodeModificationsStruct(buffer, arr) {
|
decodeModificationsStruct(buffer, modArr, blueprintArr) {
|
||||||
let curpos = 0;
|
let curpos = 0;
|
||||||
let slot = buffer.readInt8(curpos++);
|
let slot = buffer.readInt8(curpos++);
|
||||||
while (slot != -1) {
|
while (slot != SLOT_ID_DONE) {
|
||||||
let modifications = {};
|
let modifications = {};
|
||||||
|
let blueprint = {};
|
||||||
let modificationId = buffer.readInt8(curpos++);
|
let modificationId = buffer.readInt8(curpos++);
|
||||||
while (modificationId != -1) {
|
while (modificationId != MODIFICATION_ID_DONE) {
|
||||||
let modificationValue = buffer.readInt32LE(curpos);
|
let modificationValue;
|
||||||
|
if (modificationId === 40) {
|
||||||
|
// Type is special, in that it's a character string
|
||||||
|
modificationValue = buffer.toString('utf8', curpos, curpos + 4).trim();
|
||||||
|
} else {
|
||||||
|
modificationValue = buffer.readInt32LE(curpos);
|
||||||
|
}
|
||||||
curpos += 4;
|
curpos += 4;
|
||||||
// console.log('DECODE Slot ' + slot + ': ' + Modifications.modifications[modificationId] + ' = ' + modificationValue);
|
// There are a number of 'special' modification IDs, check for them here
|
||||||
modifications[Modifications.modifications[modificationId]] = modificationValue;
|
if (modificationId === MODIFICATION_ID_BLUEPRINT) {
|
||||||
|
if (modificationValue !== 0) {
|
||||||
|
blueprint = Object.assign(blueprint, _.find(Modifications.blueprints, function(o) { return o.id === modificationValue; }));
|
||||||
|
}
|
||||||
|
} else if (modificationId === MODIFICATION_ID_GRADE) {
|
||||||
|
if (modificationValue !== 0) {
|
||||||
|
blueprint.grade = modificationValue;
|
||||||
|
}
|
||||||
|
} else if (modificationId === MODIFICATION_ID_SPECIAL) {
|
||||||
|
blueprint.special = _.find(Modifications.specials, function(o) { return o.id === modificationValue; });
|
||||||
|
} else {
|
||||||
|
const modification = _.find(Modifications.modifications, function(o) { return o.id === modificationId; });
|
||||||
|
// console.log('DECODE Slot ' + slot + ': ' + modification.name + ' = ' + modificationValue);
|
||||||
|
modifications[modification.name] = modificationValue;
|
||||||
|
}
|
||||||
modificationId = buffer.readInt8(curpos++);
|
modificationId = buffer.readInt8(curpos++);
|
||||||
}
|
}
|
||||||
arr[slot] = modifications;
|
modArr[slot] = modifications;
|
||||||
|
if (blueprint.id) {
|
||||||
|
blueprintArr[slot] = blueprint;
|
||||||
|
}
|
||||||
slot = buffer.readInt8(curpos++);
|
slot = buffer.readInt8(curpos++);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1581,4 +1706,37 @@ export default class Ship {
|
|||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upgrade information about internals with version changes
|
||||||
|
* @param {array} internals the internals from the ship code
|
||||||
|
* @param {int} offset the offset of the internals information in the priorities etc. arrays
|
||||||
|
* @param {array} priorities the existing priorities arrray
|
||||||
|
* @param {array} enableds the existing enableds arrray
|
||||||
|
* @param {array} modifications the existing modifications arrray
|
||||||
|
* @param {array} blueprints the existing blueprints arrray
|
||||||
|
* @param {int} version the version of the information
|
||||||
|
*/
|
||||||
|
upgradeInternals(internals, offset, priorities, enableds, modifications, blueprints, version) {
|
||||||
|
if (version == 1) {
|
||||||
|
// Version 2 reflects the addition of military slots. this means that we need to juggle the internals and their
|
||||||
|
// associated information around to make holes in the appropriate places
|
||||||
|
for (let slotId = 0; slotId < this.internal.length; slotId++) {
|
||||||
|
if (this.internal[slotId].eligible && this.internal[slotId].eligible.mrp) {
|
||||||
|
// Found a restricted military slot - push all of the existing items down one to compensate for the fact that they didn't exist before now
|
||||||
|
internals.push.apply(internals, [0].concat(internals.splice(slotId).slice(0, -1)));
|
||||||
|
|
||||||
|
const offsetSlotId = offset + slotId;
|
||||||
|
|
||||||
|
// Same for priorities etc.
|
||||||
|
if (priorities) { priorities.push.apply(priorities, [0].concat(priorities.splice(offsetSlotId))); }
|
||||||
|
if (enableds) { enableds.push.apply(enableds, [1].concat(enableds.splice(offsetSlotId))); }
|
||||||
|
if (modifications) { modifications.push.apply(modifications, [null].concat(modifications.splice(offsetSlotId).slice(0, -1))); }
|
||||||
|
if (blueprints) { blueprints.push.apply(blueprints, [null].concat(blueprints.splice(offsetSlotId).slice(0, -1))); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Ensure that all items are the correct length
|
||||||
|
internals.splice(Ships[this.id].slots.internal.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,9 @@ export function trader(ship, shielded, standardOpts) {
|
|||||||
ship.use(slot, sg);
|
ship.use(slot, sg);
|
||||||
sg = null;
|
sg = null;
|
||||||
} else {
|
} else {
|
||||||
ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E'));
|
if (canMount(ship, slot, 'cr')) {
|
||||||
|
ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ const SHIP_FD_NAME_TO_CORIOLIS_NAME = {
|
|||||||
'CobraMkIII': 'cobra_mk_iii',
|
'CobraMkIII': 'cobra_mk_iii',
|
||||||
'CobraMkIV': 'cobra_mk_iv',
|
'CobraMkIV': 'cobra_mk_iv',
|
||||||
'Cutter': 'imperial_cutter',
|
'Cutter': 'imperial_cutter',
|
||||||
'DiamondBack': 'diamondback_explorer',
|
'DiamondBackXL': 'diamondback_explorer',
|
||||||
'DiamondBackXL': 'diamondback',
|
'DiamondBack': 'diamondback',
|
||||||
'Eagle': 'eagle',
|
'Eagle': 'eagle',
|
||||||
'Empire_Courier': 'imperial_courier',
|
'Empire_Courier': 'imperial_courier',
|
||||||
'Empire_Eagle': 'imperial_eagle',
|
'Empire_Eagle': 'imperial_eagle',
|
||||||
@@ -33,7 +33,7 @@ const SHIP_FD_NAME_TO_CORIOLIS_NAME = {
|
|||||||
'Type7': 'type_7_transport',
|
'Type7': 'type_7_transport',
|
||||||
'Type9': 'type_9_heavy',
|
'Type9': 'type_9_heavy',
|
||||||
'Viper': 'viper',
|
'Viper': 'viper',
|
||||||
'Viper_MKIV': 'viper_mk_iv',
|
'Viper_MkIV': 'viper_mk_iv',
|
||||||
'Vulture': 'vulture'
|
'Vulture': 'vulture'
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -149,13 +149,13 @@ export function shipFromJson(json) {
|
|||||||
throw 'Unknown bulkheads "' + armourJson.name + '"';
|
throw 'Unknown bulkheads "' + armourJson.name + '"';
|
||||||
}
|
}
|
||||||
ship.bulkheads.enabled = true;
|
ship.bulkheads.enabled = true;
|
||||||
if (armourJson.modifiers) _addModifications(ship.bulkheads.m, armourJson.modifiers);
|
if (armourJson.modifiers) _addModifications(ship.bulkheads.m, armourJson.modifiers, armourJson.recipeName, armourJson.recipeLevel);
|
||||||
|
|
||||||
// Add the standard modules
|
// Add the standard modules
|
||||||
// Power plant
|
// Power plant
|
||||||
const powerplantJson = json.modules.PowerPlant.module;
|
const powerplantJson = json.modules.PowerPlant.module;
|
||||||
const powerplant = _moduleFromEdId(powerplantJson.id);
|
const powerplant = _moduleFromEdId(powerplantJson.id);
|
||||||
if (powerplantJson.modifiers) _addModifications(powerplant, powerplantJson.modifiers);
|
if (powerplantJson.modifiers) _addModifications(powerplant, powerplantJson.modifiers, powerplantJson.recipeName, powerplantJson.recipeLevel);
|
||||||
ship.use(ship.standard[0], powerplant, true);
|
ship.use(ship.standard[0], powerplant, true);
|
||||||
ship.standard[0].enabled = powerplantJson.on === true;
|
ship.standard[0].enabled = powerplantJson.on === true;
|
||||||
ship.standard[0].priority = powerplantJson.priority;
|
ship.standard[0].priority = powerplantJson.priority;
|
||||||
@@ -163,7 +163,7 @@ export function shipFromJson(json) {
|
|||||||
// Thrusters
|
// Thrusters
|
||||||
const thrustersJson = json.modules.MainEngines.module;
|
const thrustersJson = json.modules.MainEngines.module;
|
||||||
const thrusters = _moduleFromEdId(thrustersJson.id);
|
const thrusters = _moduleFromEdId(thrustersJson.id);
|
||||||
if (thrustersJson.modifiers) _addModifications(thrusters, thrustersJson.modifiers);
|
if (thrustersJson.modifiers) _addModifications(thrusters, thrustersJson.modifiers, thrustersJson.recipeName, thrustersJson.recipeLevel);
|
||||||
ship.use(ship.standard[1], thrusters, true);
|
ship.use(ship.standard[1], thrusters, true);
|
||||||
ship.standard[1].enabled = thrustersJson.on === true;
|
ship.standard[1].enabled = thrustersJson.on === true;
|
||||||
ship.standard[1].priority = thrustersJson.priority;
|
ship.standard[1].priority = thrustersJson.priority;
|
||||||
@@ -171,7 +171,7 @@ export function shipFromJson(json) {
|
|||||||
// FSD
|
// FSD
|
||||||
const frameshiftdriveJson = json.modules.FrameShiftDrive.module;
|
const frameshiftdriveJson = json.modules.FrameShiftDrive.module;
|
||||||
const frameshiftdrive = _moduleFromEdId(frameshiftdriveJson.id);
|
const frameshiftdrive = _moduleFromEdId(frameshiftdriveJson.id);
|
||||||
if (frameshiftdriveJson.modifiers) _addModifications(frameshiftdrive, frameshiftdriveJson.modifiers);
|
if (frameshiftdriveJson.modifiers) _addModifications(frameshiftdrive, frameshiftdriveJson.modifiers, frameshiftdriveJson.recipeName, frameshiftdriveJson.recipeLevel);
|
||||||
ship.use(ship.standard[2], frameshiftdrive, true);
|
ship.use(ship.standard[2], frameshiftdrive, true);
|
||||||
ship.standard[2].enabled = frameshiftdriveJson.on === true;
|
ship.standard[2].enabled = frameshiftdriveJson.on === true;
|
||||||
ship.standard[2].priority = frameshiftdriveJson.priority;
|
ship.standard[2].priority = frameshiftdriveJson.priority;
|
||||||
@@ -179,7 +179,7 @@ export function shipFromJson(json) {
|
|||||||
// Life support
|
// Life support
|
||||||
const lifesupportJson = json.modules.LifeSupport.module;
|
const lifesupportJson = json.modules.LifeSupport.module;
|
||||||
const lifesupport = _moduleFromEdId(lifesupportJson.id);
|
const lifesupport = _moduleFromEdId(lifesupportJson.id);
|
||||||
if (lifesupportJson.modifiers)_addModifications(lifesupport, lifesupportJson.modifiers);
|
if (lifesupportJson.modifiers)_addModifications(lifesupport, lifesupportJson.modifiers, lifesupportJson.recipeName, lifesupportJson.recipeLevel);
|
||||||
ship.use(ship.standard[3], lifesupport, true);
|
ship.use(ship.standard[3], lifesupport, true);
|
||||||
ship.standard[3].enabled = lifesupportJson.on === true;
|
ship.standard[3].enabled = lifesupportJson.on === true;
|
||||||
ship.standard[3].priority = lifesupportJson.priority;
|
ship.standard[3].priority = lifesupportJson.priority;
|
||||||
@@ -187,7 +187,7 @@ export function shipFromJson(json) {
|
|||||||
// Power distributor
|
// Power distributor
|
||||||
const powerdistributorJson = json.modules.PowerDistributor.module;
|
const powerdistributorJson = json.modules.PowerDistributor.module;
|
||||||
const powerdistributor = _moduleFromEdId(powerdistributorJson.id);
|
const powerdistributor = _moduleFromEdId(powerdistributorJson.id);
|
||||||
if (powerdistributorJson.modifiers) _addModifications(powerdistributor, powerdistributorJson.modifiers);
|
if (powerdistributorJson.modifiers) _addModifications(powerdistributor, powerdistributorJson.modifiers, powerdistributorJson.recipeName, powerdistributorJson.recipeLevel);
|
||||||
ship.use(ship.standard[4], powerdistributor, true);
|
ship.use(ship.standard[4], powerdistributor, true);
|
||||||
ship.standard[4].enabled = powerdistributorJson.on === true;
|
ship.standard[4].enabled = powerdistributorJson.on === true;
|
||||||
ship.standard[4].priority = powerdistributorJson.priority;
|
ship.standard[4].priority = powerdistributorJson.priority;
|
||||||
@@ -195,7 +195,7 @@ export function shipFromJson(json) {
|
|||||||
// Sensors
|
// Sensors
|
||||||
const sensorsJson = json.modules.Radar.module;
|
const sensorsJson = json.modules.Radar.module;
|
||||||
const sensors = _moduleFromEdId(sensorsJson.id);
|
const sensors = _moduleFromEdId(sensorsJson.id);
|
||||||
if (sensorsJson.modifiers) _addModifications(sensors, sensorsJson.modifiers);
|
if (sensorsJson.modifiers) _addModifications(sensors, sensorsJson.modifiers, sensorsJson.recipeName, sensorsJson.recipeLevel);
|
||||||
ship.use(ship.standard[5], sensors, true);
|
ship.use(ship.standard[5], sensors, true);
|
||||||
ship.standard[5].enabled = sensorsJson.on === true;
|
ship.standard[5].enabled = sensorsJson.on === true;
|
||||||
ship.standard[5].priority = sensorsJson.priority;
|
ship.standard[5].priority = sensorsJson.priority;
|
||||||
@@ -224,12 +224,14 @@ export function shipFromJson(json) {
|
|||||||
// Now that we know what we're looking for, find it
|
// Now that we know what we're looking for, find it
|
||||||
const hardpointName = HARDPOINT_NUM_TO_CLASS[hardpointClassNum] + 'Hardpoint' + hardpointSlotNum;
|
const hardpointName = HARDPOINT_NUM_TO_CLASS[hardpointClassNum] + 'Hardpoint' + hardpointSlotNum;
|
||||||
const hardpointSlot = json.modules[hardpointName];
|
const hardpointSlot = json.modules[hardpointName];
|
||||||
if (!hardpointSlot.module) {
|
if (!hardpointSlot) {
|
||||||
|
// This can happen with old imports that don't contain new hardpoints
|
||||||
|
} else if (!hardpointSlot.module) {
|
||||||
// No module
|
// No module
|
||||||
} else {
|
} else {
|
||||||
const hardpointJson = hardpointSlot.module;
|
const hardpointJson = hardpointSlot.module;
|
||||||
const hardpoint = _moduleFromEdId(hardpointJson.id);
|
const hardpoint = _moduleFromEdId(hardpointJson.id);
|
||||||
if (hardpointJson.modifiers) _addModifications(hardpoint, hardpointJson.modifiers);
|
if (hardpointJson.modifiers) _addModifications(hardpoint, hardpointJson.modifiers, hardpointJson.recipeName, hardpointJson.recipeLevel);
|
||||||
ship.use(ship.hardpoints[hardpointArrayNum], hardpoint, true);
|
ship.use(ship.hardpoints[hardpointArrayNum], hardpoint, true);
|
||||||
ship.hardpoints[hardpointArrayNum].enabled = hardpointJson.on === true;
|
ship.hardpoints[hardpointArrayNum].enabled = hardpointJson.on === true;
|
||||||
ship.hardpoints[hardpointArrayNum].priority = hardpointJson.priority;
|
ship.hardpoints[hardpointArrayNum].priority = hardpointJson.priority;
|
||||||
@@ -239,24 +241,36 @@ export function shipFromJson(json) {
|
|||||||
|
|
||||||
// Add internal compartments
|
// Add internal compartments
|
||||||
let internalSlotNum = 1;
|
let internalSlotNum = 1;
|
||||||
|
let militarySlotNum = 1;
|
||||||
for (let i in shipTemplate.slots.internal) {
|
for (let i in shipTemplate.slots.internal) {
|
||||||
const internalClassNum = shipTemplate.slots.internal[i];
|
const internalClassNum = isNaN(shipTemplate.slots.internal[i]) ? shipTemplate.slots.internal[i].class : shipTemplate.slots.internal[i];
|
||||||
|
const isMilitary = isNaN(shipTemplate.slots.internal[i]) ? shipTemplate.slots.internal[i].name == 'Military' : false;
|
||||||
|
|
||||||
|
// The internal slot might be a standard or a military slot. Military slots have a different naming system
|
||||||
let internalSlot = null;
|
let internalSlot = null;
|
||||||
while (internalSlot === null && internalSlotNum < 99) {
|
if (isMilitary) {
|
||||||
// Slot numbers are not contiguous so handle skips
|
const internalName = 'Military0' + militarySlotNum;
|
||||||
const internalName = 'Slot' + (internalSlotNum <= 9 ? '0' : '') + internalSlotNum + '_Size' + internalClassNum;
|
internalSlot = json.modules[internalName];
|
||||||
if (json.modules[internalName]) {
|
militarySlotNum++;
|
||||||
internalSlot = json.modules[internalName];
|
} else {
|
||||||
|
while (internalSlot === null && internalSlotNum < 99) {
|
||||||
|
// Slot numbers are not contiguous so handle skips
|
||||||
|
const internalName = 'Slot' + (internalSlotNum <= 9 ? '0' : '') + internalSlotNum + '_Size' + internalClassNum;
|
||||||
|
if (json.modules[internalName]) {
|
||||||
|
internalSlot = json.modules[internalName];
|
||||||
|
}
|
||||||
|
internalSlotNum++;
|
||||||
}
|
}
|
||||||
internalSlotNum++;
|
|
||||||
}
|
}
|
||||||
if (!internalSlot.module) {
|
|
||||||
|
if (!internalSlot) {
|
||||||
|
// This can happen with old imports that don't contain new slots
|
||||||
|
} else if (!internalSlot.module) {
|
||||||
// No module
|
// No module
|
||||||
} else {
|
} else {
|
||||||
const internalJson = internalSlot.module;
|
const internalJson = internalSlot.module;
|
||||||
const internal = _moduleFromEdId(internalJson.id);
|
const internal = _moduleFromEdId(internalJson.id);
|
||||||
if (internalJson.modifiers) _addModifications(internal, internalJson.modifiers);
|
if (internalJson.modifiers) _addModifications(internal, internalJson.modifiers, internalJson.recipeName, internalJson.recipeLevel);
|
||||||
ship.use(ship.internal[i], internal, true);
|
ship.use(ship.internal[i], internal, true);
|
||||||
ship.internal[i].enabled = internalJson.on === true;
|
ship.internal[i].enabled = internalJson.on === true;
|
||||||
ship.internal[i].priority = internalJson.priority;
|
ship.internal[i].priority = internalJson.priority;
|
||||||
@@ -271,23 +285,63 @@ export function shipFromJson(json) {
|
|||||||
* Add the modifications for a module
|
* Add the modifications for a module
|
||||||
* @param {Module} module the module
|
* @param {Module} module the module
|
||||||
* @param {Object} modifiers the modifiers
|
* @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) {
|
function _addModifications(module, modifiers, blueprint, grade) {
|
||||||
if (!modifiers || !modifiers.modifiers) return;
|
if (!modifiers || !modifiers.modifiers) return;
|
||||||
|
|
||||||
|
let special;
|
||||||
for (const i in modifiers.modifiers) {
|
for (const i in modifiers.modifiers) {
|
||||||
// Look up the modifiers to find what we need to do
|
// Some special modifications
|
||||||
const modifierActions = Modifications.modifierActions[modifiers.modifiers[i].name];
|
if (modifiers.modifiers[i].name === 'mod_weapon_clip_size_override') {
|
||||||
const value = modifiers.modifiers[i].value;
|
// 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') {
|
||||||
|
// This is an absolute number that acts as an override
|
||||||
|
module.setModValue('burstrof', modifiers.modifiers[i].value * 100);
|
||||||
|
} else if (modifiers.modifiers[i].name === 'mod_weapon_falloffrange_from_range') {
|
||||||
|
// Obtain the falloff value directly from the range
|
||||||
|
module.setModValue('fallofffromrange', 1);
|
||||||
|
} 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
|
// Carry out the required changes
|
||||||
for (const action in modifierActions) {
|
for (const action in modifierActions) {
|
||||||
const actionValue = modifierActions[action] * value;
|
if (isNaN(modifierActions[action])) {
|
||||||
let mod = module.getModValue(action) / 10000;
|
module.setModValue(action, modifierActions[action]);
|
||||||
if (!mod) {
|
} else {
|
||||||
mod = 0;
|
const actionValue = modifierActions[action] * value;
|
||||||
|
let mod = module.getModValue(action) / 10000;
|
||||||
|
if (!mod) {
|
||||||
|
mod = 0;
|
||||||
|
}
|
||||||
|
module.setModValue(action, ((1 + mod) * (1 + actionValue) - 1) * 10000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
module.setModValue(action, ((1 + mod) * (1 + actionValue) - 1) * 10000);
|
}
|
||||||
|
|
||||||
|
// Note the special if present
|
||||||
|
if (modifiers.modifiers[i].name && modifiers.modifiers[i].name.startsWith('special_')) {
|
||||||
|
special = Modifications.specials[modifiers.modifiers[i].name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -355,6 +409,12 @@ function _addModifications(module, modifiers) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
// Jitter is an absolute number, so we need to divide it by 100
|
||||||
if (module.getModValue('jitter')) {
|
if (module.getModValue('jitter')) {
|
||||||
module.setModValue('jitter', module.getModValue('jitter') / 100);
|
module.setModValue('jitter', module.getModValue('jitter') / 100);
|
||||||
|
|||||||
@@ -1,17 +1,27 @@
|
|||||||
import request from 'superagent';
|
import request from 'superagent';
|
||||||
|
|
||||||
const SHORTEN_API = 'https://www.googleapis.com/urlshortener/v1/url?key=';
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shorten a URL
|
||||||
|
* @param {string} url The URL to shorten
|
||||||
|
* @param {function} success Success callback
|
||||||
|
* @param {function} error Failure/Error callback
|
||||||
|
*/
|
||||||
|
export default function shorternUrl(url, success, error) {
|
||||||
|
shortenUrlEddp(url, success, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const SHORTEN_API_GOOGLE = 'https://www.googleapis.com/urlshortener/v1/url?key=';
|
||||||
/**
|
/**
|
||||||
* Shorten a URL using Google's URL shortener API
|
* Shorten a URL using Google's URL shortener API
|
||||||
* @param {string} url The URL to shorten
|
* @param {string} url The URL to shorten
|
||||||
* @param {function} success Success callback
|
* @param {function} success Success callback
|
||||||
* @param {function} error Failure/Error callback
|
* @param {function} error Failure/Error callback
|
||||||
*/
|
*/
|
||||||
export default function shortenUrl(url, success, error) {
|
function shortenUrlGoogle(url, success, error) {
|
||||||
if (window.navigator.onLine) {
|
if (window.navigator.onLine) {
|
||||||
try {
|
try {
|
||||||
request.post(SHORTEN_API + window.CORIOLIS_GAPI_KEY)
|
request.post(SHORTEN_API_GOOGLE + window.CORIOLIS_GAPI_KEY)
|
||||||
.send({ longUrl: url })
|
.send({ longUrl: url })
|
||||||
.end(function(err, response) {
|
.end(function(err, response) {
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -27,3 +37,30 @@ export default function shortenUrl(url, success, error) {
|
|||||||
error('Not Online');
|
error('Not Online');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SHORTEN_API_EDDP = 'https://eddp.co/u';
|
||||||
|
/**
|
||||||
|
* Shorten a URL using EDDP's URL shortener API
|
||||||
|
* @param {string} url The URL to shorten
|
||||||
|
* @param {function} success Success callback
|
||||||
|
* @param {function} error Failure/Error callback
|
||||||
|
*/
|
||||||
|
function shortenUrlEddp(url, success, error) {
|
||||||
|
if (window.navigator.onLine) {
|
||||||
|
try {
|
||||||
|
request.post(SHORTEN_API_EDDP)
|
||||||
|
.send(url)
|
||||||
|
.end(function(err, response) {
|
||||||
|
if (err) {
|
||||||
|
error('Bad Request');
|
||||||
|
} else {
|
||||||
|
success(response.header['location']);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
error(e.message ? e.message : e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error('Not Online');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ export function diffDetails(language, m, mm) {
|
|||||||
|
|
||||||
let mPowerGeneration = m.pgen || 0;
|
let mPowerGeneration = m.pgen || 0;
|
||||||
let mmPowerGeneration = mm ? mm.getPowerGeneration() : 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>);
|
if (mPowerGeneration != mmPowerGeneration) propDiffs.push(<div key='pgen'>{translate('pgen')}: <span className={diffClass(mPowerGeneration, mmPowerGeneration)}>{diff(formats.round, mPowerGeneration, mmPowerGeneration)}{units.MJ}</span></div>);
|
||||||
|
|
||||||
let mPowerUsage = m.power || 0;
|
let mPowerUsage = m.power || 0;
|
||||||
let mmPowerUsage = mm ? mm.getPowerUsage() : 0;
|
let mmPowerUsage = mm ? mm.getPowerUsage() : 0;
|
||||||
|
|||||||
@@ -71,3 +71,15 @@ export function shallowEqual(objA, objB) {
|
|||||||
export function fromUrlSafe(data) {
|
export function fromUrlSafe(data) {
|
||||||
return data ? data.replace(/-/g, '/').replace(/_/g, '+') : null;
|
return data ? data.replace(/-/g, '/').replace(/_/g, '+') : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if an object is empty
|
||||||
|
* @param {object} obj the object
|
||||||
|
* @return {bool} true if the object is empty, otherwise false
|
||||||
|
*/
|
||||||
|
export function isEmpty(obj) {
|
||||||
|
for (let key in obj) {
|
||||||
|
if (obj.hasOwnProperty(key)) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
@import 'tooltip';
|
@import 'tooltip';
|
||||||
@import 'buttons';
|
@import 'buttons';
|
||||||
@import 'error';
|
@import 'error';
|
||||||
|
@import 'shipselector';
|
||||||
@import 'sortable';
|
@import 'sortable';
|
||||||
@import 'loader';
|
@import 'loader';
|
||||||
|
|
||||||
@@ -41,8 +42,10 @@ div, a, li {
|
|||||||
#coriolis {
|
#coriolis {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
padding-top: 48px;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
-webkit-overflow-scrolling: touch;
|
-webkit-overflow-scrolling: touch;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page {
|
.page {
|
||||||
|
|||||||
@@ -20,8 +20,12 @@ header {
|
|||||||
line-height: 3em;
|
line-height: 3em;
|
||||||
font-family: @fTitle;
|
font-family: @fTitle;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
position: relative;
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
left: 0px;
|
||||||
|
width: 100%;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
|
box-sizing: border-box;
|
||||||
.user-select-none();
|
.user-select-none();
|
||||||
|
|
||||||
.menu {
|
.menu {
|
||||||
@@ -186,6 +190,7 @@ header {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.no-wrap {
|
.no-wrap {
|
||||||
|
overflow-x: auto;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,8 +34,10 @@
|
|||||||
width: 1.1em;
|
width: 1.1em;
|
||||||
height: 1em;
|
height: 1em;
|
||||||
stoke: @fg;
|
stoke: @fg;
|
||||||
|
stroke-width: 20;
|
||||||
fill: transparent;
|
fill: transparent;
|
||||||
|
|
||||||
|
|
||||||
&.sm {
|
&.sm {
|
||||||
width: 0.8em;
|
width: 0.8em;
|
||||||
height: 0.75em;
|
height: 0.75em;
|
||||||
|
|||||||
199
src/less/shipselector.less
Executable file
199
src/less/shipselector.less
Executable file
@@ -0,0 +1,199 @@
|
|||||||
|
.shipselector {
|
||||||
|
background-color: @bgBlack;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 0 0 1em;
|
||||||
|
height: 3em;
|
||||||
|
line-height: 3em;
|
||||||
|
font-family: @fTitle;
|
||||||
|
vertical-align: middle;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.user-select-none();
|
||||||
|
|
||||||
|
.menu {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
cursor: default;
|
||||||
|
|
||||||
|
&.r {
|
||||||
|
.menu-list {
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.smallTablet({
|
||||||
|
position: static;
|
||||||
|
position: initial;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-header {
|
||||||
|
padding : 0 1em;
|
||||||
|
cursor: pointer;
|
||||||
|
color: @warning;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-list {
|
||||||
|
font-family: @fStandard;
|
||||||
|
position: absolute;
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
box-sizing: border-box;
|
||||||
|
min-width: 100%;
|
||||||
|
overflow-x: hidden;
|
||||||
|
background-color: @bgBlack;
|
||||||
|
font-size: 0.9em;
|
||||||
|
overflow-y: auto;
|
||||||
|
z-index: 0;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
max-height: 500px;
|
||||||
|
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-track {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
background-color: @warning-disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
border: none;
|
||||||
|
background-color: transparent;
|
||||||
|
text-align: right;
|
||||||
|
font-size: 1em;
|
||||||
|
font-family: @fStandard;
|
||||||
|
}
|
||||||
|
|
||||||
|
.smallTablet({
|
||||||
|
max-height: 400px;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
border-bottom: 1px solid @bg;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
.tablet({
|
||||||
|
li, a {
|
||||||
|
padding: 0.3em 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
.dbl {
|
||||||
|
-webkit-column-count: 2; /* Chrome, Safari, Opera */
|
||||||
|
-moz-column-count: 2; /* Firefox */
|
||||||
|
column-count: 2;
|
||||||
|
ul {
|
||||||
|
min-width: 10em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.smallTablet({
|
||||||
|
-webkit-column-count: 3; /* Chrome, Safari, Opera */
|
||||||
|
-moz-column-count: 3; /* Firefox */
|
||||||
|
column-count: 3;
|
||||||
|
|
||||||
|
ul {
|
||||||
|
min-width: 20em;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
.largePhone({
|
||||||
|
-webkit-column-count: 2; /* Chrome, Safari, Opera */
|
||||||
|
-moz-column-count: 2; /* Firefox */
|
||||||
|
column-count: 2;
|
||||||
|
});
|
||||||
|
|
||||||
|
.smallPhone({
|
||||||
|
-webkit-column-count: 1; /* Chrome, Safari, Opera */
|
||||||
|
-moz-column-count: 1; /* Firefox */
|
||||||
|
column-count: 1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
.quad {
|
||||||
|
-webkit-column-count: 4; /* Chrome, Safari, Opera */
|
||||||
|
-moz-column-count: 4; /* Firefox */
|
||||||
|
column-count: 4;
|
||||||
|
ul {
|
||||||
|
min-width: 10em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.smallTablet({
|
||||||
|
-webkit-column-count: 3; /* Chrome, Safari, Opera */
|
||||||
|
-moz-column-count: 3; /* Firefox */
|
||||||
|
column-count: 3;
|
||||||
|
|
||||||
|
ul {
|
||||||
|
min-width: 20em;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
.largePhone({
|
||||||
|
-webkit-column-count: 2; /* Chrome, Safari, Opera */
|
||||||
|
-moz-column-count: 2; /* Firefox */
|
||||||
|
column-count: 2;
|
||||||
|
});
|
||||||
|
|
||||||
|
.smallPhone({
|
||||||
|
-webkit-column-count: 1; /* Chrome, Safari, Opera */
|
||||||
|
-moz-column-count: 1; /* Firefox */
|
||||||
|
column-count: 1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
display: inline-block;
|
||||||
|
white-space: nowrap;
|
||||||
|
margin: 0 0 0.5em;
|
||||||
|
padding: 0;
|
||||||
|
line-height: 1.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
white-space: normal;
|
||||||
|
list-style: none;
|
||||||
|
margin-left: 1em;
|
||||||
|
line-height: 1.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
vertical-align: middle;
|
||||||
|
color: @warning;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
&:visited {
|
||||||
|
color: @warning;
|
||||||
|
}
|
||||||
|
.no-touch &:hover {
|
||||||
|
color: teal;
|
||||||
|
}
|
||||||
|
&.active {
|
||||||
|
color: @primary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
border: none;
|
||||||
|
border-top: 1px solid @disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-wrap {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block {
|
||||||
|
display: block;
|
||||||
|
line-height: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 1.3em;
|
||||||
|
display: inline-block;
|
||||||
|
margin:0px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -68,6 +68,7 @@
|
|||||||
"rating": { "$ref": "#/definitions/standardRatings" },
|
"rating": { "$ref": "#/definitions/standardRatings" },
|
||||||
"enabled": { "type": "boolean" },
|
"enabled": { "type": "boolean" },
|
||||||
"priority": { "type": "integer", "minimum": 1, "maximum": 5 },
|
"priority": { "type": "integer", "minimum": 1, "maximum": 5 },
|
||||||
|
"blueprint": { "type": "object" },
|
||||||
"modifications": { "type": "object" }
|
"modifications": { "type": "object" }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -82,6 +83,7 @@
|
|||||||
"description": "The name identifing the thrusters (if applicable), e.g. 'Enhanced Performance'",
|
"description": "The name identifing the thrusters (if applicable), e.g. 'Enhanced Performance'",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"blueprint": { "type": "object" },
|
||||||
"modifications": { "type": "object" }
|
"modifications": { "type": "object" }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -92,6 +94,7 @@
|
|||||||
"rating": { "$ref": "#/definitions/standardRatings" },
|
"rating": { "$ref": "#/definitions/standardRatings" },
|
||||||
"enabled": { "type": "boolean" },
|
"enabled": { "type": "boolean" },
|
||||||
"priority": { "type": "integer", "minimum": 1, "maximum": 5 },
|
"priority": { "type": "integer", "minimum": 1, "maximum": 5 },
|
||||||
|
"blueprint": { "type": "object" },
|
||||||
"modifications": { "type": "object" }
|
"modifications": { "type": "object" }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -102,6 +105,7 @@
|
|||||||
"rating": { "$ref": "#/definitions/standardRatings" },
|
"rating": { "$ref": "#/definitions/standardRatings" },
|
||||||
"enabled": { "type": "boolean" },
|
"enabled": { "type": "boolean" },
|
||||||
"priority": { "type": "integer", "minimum": 1, "maximum": 5 },
|
"priority": { "type": "integer", "minimum": 1, "maximum": 5 },
|
||||||
|
"blueprint": { "type": "object" },
|
||||||
"modifications": { "type": "object" }
|
"modifications": { "type": "object" }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -112,6 +116,7 @@
|
|||||||
"rating": { "$ref": "#/definitions/standardRatings" },
|
"rating": { "$ref": "#/definitions/standardRatings" },
|
||||||
"enabled": { "type": "boolean" },
|
"enabled": { "type": "boolean" },
|
||||||
"priority": { "type": "integer", "minimum": 1, "maximum": 5 },
|
"priority": { "type": "integer", "minimum": 1, "maximum": 5 },
|
||||||
|
"blueprint": { "type": "object" },
|
||||||
"modifications": { "type": "object" }
|
"modifications": { "type": "object" }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -122,6 +127,7 @@
|
|||||||
"rating": { "$ref": "#/definitions/standardRatings" },
|
"rating": { "$ref": "#/definitions/standardRatings" },
|
||||||
"enabled": { "type": "boolean" },
|
"enabled": { "type": "boolean" },
|
||||||
"priority": { "type": "integer", "minimum": 1, "maximum": 5 },
|
"priority": { "type": "integer", "minimum": 1, "maximum": 5 },
|
||||||
|
"blueprint": { "type": "object" },
|
||||||
"modifications": { "type": "object" }
|
"modifications": { "type": "object" }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -132,6 +138,7 @@
|
|||||||
"rating": { "$ref": "#/definitions/standardRatings" },
|
"rating": { "$ref": "#/definitions/standardRatings" },
|
||||||
"enabled": { "type": "boolean" },
|
"enabled": { "type": "boolean" },
|
||||||
"priority": { "type": "integer", "minimum": 1, "maximum": 5 },
|
"priority": { "type": "integer", "minimum": 1, "maximum": 5 },
|
||||||
|
"blueprint": { "type": "object" },
|
||||||
"modifications": { "type": "object" }
|
"modifications": { "type": "object" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -155,6 +162,7 @@
|
|||||||
"description": "The name identifying the component (if applicable), e.g. 'Advance Discovery Scanner', or 'Detailed Surface Scanner'",
|
"description": "The name identifying the component (if applicable), e.g. 'Advance Discovery Scanner', or 'Detailed Surface Scanner'",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"blueprint": { "type": "object" },
|
||||||
"modifications": { "type": "object" }
|
"modifications": { "type": "object" }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -179,6 +187,7 @@
|
|||||||
"description": "The name identifing the component (if applicable), e.g. 'Retributor', or 'Mining Lance'",
|
"description": "The name identifing the component (if applicable), e.g. 'Retributor', or 'Mining Lance'",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"blueprint": { "type": "object" },
|
||||||
"modifications": { "type": "object" }
|
"modifications": { "type": "object" }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -202,6 +211,7 @@
|
|||||||
"description": "The name identifing the component (if applicable), e.g. 'Point Defence', or 'Electronic Countermeasure'",
|
"description": "The name identifing the component (if applicable), e.g. 'Point Defence', or 'Electronic Countermeasure'",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"blueprint": { "type": "object" },
|
||||||
"modifications": { "type": "object" }
|
"modifications": { "type": "object" }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user