Compare commits

...

43 Commits
2.2.2 ... 2.2.5

Author SHA1 Message Date
Cmdr McDonald
0a37b36ec2 Merge branch 'release/2.2.5' 2016-12-03 15:48:45 +00:00
Cmdr McDonald
85e6796e88 Bump version 2016-12-03 15:48:39 +00:00
Cmdr McDonald
fa1ef47b71 Add note to disable ghostery in error situations 2016-12-01 13:26:33 +00:00
Cmdr McDonald
f31e3c09f4 Merge branch 'feature/rebalance' into develop 2016-12-01 10:28:21 +00:00
Cmdr McDonald
e6ab536601 Merge branch 'feature/rebalance' of https://github.com/EDCD/coriolis into feature/rebalance 2016-12-01 10:28:08 +00:00
Cmdr McDonald
5bced9fe56 Bump version number 2016-11-30 15:09:25 +00:00
Cmdr McDonald
67742060d3 Merge branch 'feature/burst' into develop 2016-11-30 15:09:04 +00:00
Cmdr McDonald
ca2136544c Handle unmodifiable values 2016-11-30 15:08:32 +00:00
Cmdr McDonald
ee19e9af50 Lint 2016-11-30 14:56:33 +00:00
Cmdr McDonald
f457fd0bff Fixes for burst calculations 2016-11-30 14:51:19 +00:00
Cmdr McDonald
c1ce07e039 Calculate rate of fire for multi-burst weapons 2016-11-30 12:19:40 +00:00
Cmdr McDonald
c8d1536f77 Bump version number 2016-11-29 13:53:23 +00:00
Cmdr McDonald
231ad4af59 Merge branch 'release/2.2.4' into develop 2016-11-28 16:54:35 +00:00
Cmdr McDonald
d5f61d7ae8 Merge branch 'release/2.2.4' 2016-11-28 16:54:32 +00:00
Cmdr McDonald
8a5d4a36bf Bumped revision 2016-11-28 16:51:24 +00:00
Cmdr McDonald
2c9237626d Show specials; handle import of specials 2016-11-27 19:43:43 +00:00
Cmdr McDonald
37f889e317 Merge branch 'feature/incendiary' into develop 2016-11-26 23:24:37 +00:00
Cmdr McDonald
f86ba48295 Fix tests 2016-11-26 18:58:29 +00:00
Cmdr McDonald
aac35633a3 Merge branch 'feature/incendiary' of https://github.com/EDCD/coriolis into feature/incendiary 2016-11-26 18:56:57 +00:00
Cmdr McDonald
e73e0a305d Use new-style modification data 2016-11-26 18:54:22 +00:00
Cmdr McDonald
4b2b0efe37 Allow non-numeric modifiers 2016-11-26 18:54:22 +00:00
Cmdr McDonald
8fe20f6f65 Checkpoint - handle non-numeric modifiers 2016-11-26 18:54:22 +00:00
Cmdr McDonald
11af7f567a Move to method for damage type to allow for modifications 2016-11-26 18:54:22 +00:00
Cmdr McDonald
3b8444482f Add URL shortlink for outfitting page 2016-11-26 18:53:47 +00:00
Cmdr McDonald
c09e1b1b3e Merge branch 'feature/incendiary' of https://github.com/EDCD/coriolis into feature/incendiary 2016-11-26 13:16:38 +00:00
Cmdr McDonald
5770cf8d39 Use new-style modification data 2016-11-26 13:15:43 +00:00
Cmdr McDonald
6da09f2e5d Allow non-numeric modifiers 2016-11-26 13:15:43 +00:00
Cmdr McDonald
294fadf7cd Checkpoint - handle non-numeric modifiers 2016-11-26 13:15:43 +00:00
Cmdr McDonald
2a97678574 Move to method for damage type to allow for modifications 2016-11-26 13:15:43 +00:00
Cmdr McDonald
76b3bd34f5 Use new-style modification data 2016-11-26 12:54:22 +00:00
Cmdr McDonald
02bfecb92d Allow non-numeric modifiers 2016-11-24 22:23:14 +00:00
Cmdr McDonald
719759ad56 Merge branch 'release/2.2.3' into develop 2016-11-24 14:37:43 +00:00
Cmdr McDonald
fd446b29ba Merge branch 'release/2.2.3' 2016-11-24 14:37:39 +00:00
Cmdr McDonald
e5552d3e10 Updates ready for release 2016-11-24 13:49:37 +00:00
Cmdr McDonald
50946eeeb8 Fix misnamed diamondbacks - issue #36 2016-11-24 13:19:14 +00:00
Cmdr McDonald
faab41117c Checkpoint - handle non-numeric modifiers 2016-11-24 12:50:33 +00:00
Cmdr McDonald
0ab59c1f9a Handle import of restricted slots - fix for #35 2016-11-24 11:10:41 +00:00
Cmdr McDonald
1067dceaa3 Move to method for damage type to allow for modifications 2016-11-23 13:02:23 +00:00
Cmdr McDonald
9042de422a patch 2016-11-23 01:05:13 +00:00
Cmdr McDonald
f0547feb93 Merge branch 'feature/blueprints' into develop 2016-11-23 00:56:19 +00:00
Cmdr McDonald
f863daa347 Fix hull boost calculation. Partial fix for #29 2016-11-23 00:54:42 +00:00
Cmdr McDonald
fdb202e7d6 Add blueprints 2016-11-22 15:52:31 +00:00
Cmdr McDonald
c6bde19052 Merge branch 'release/2.2.2' into develop 2016-11-21 16:44:19 +00:00
23 changed files with 1630 additions and 163 deletions

View File

@@ -1,4 +1,34 @@
#2.2.x
#2.2.5
* Calculate rate of fire for multi-burst weapons
* Add note to disable ghostery in error situation
* Use coriolis-data 2.2.5:
* Fix incorrect ID for emissive munitions special
* Fix rate of fire for burst lasers
* Add fragment cannon modifications
* Fix internal name of dazzle shell
#2.2.4
* Add shortlink for outfitting page
* Use coriolis-data 2.2.4:
* Fix incorrect ID for class 5 luxury passenger cabin
* Add damage type modifier
* Change modifications from simple strings to objects, to allow more data-driven behaviour
* Add special effects
* Modification tooltip now shows special effect
#2.2.3
* Fix hull boost calculation - now shows correct % modifier and total armour
* Fix import of DiamondBack - can now be imported
* Fix import of Beluga - can now be imported
* Use coriolis-data 2.2.3:
* Fix mismatch between class 5 and class 7 fighter hangars - now shows correct module
* Add details for concordant sequence special effect - now shows correct damage
* Fix details for thermal shock special effect - now shows correct damage
* Add engineer blueprints
* Modification tooltip now shows name and grade of modifications for imported builds
* Retain import URL unless user changes the build - allows future updates of Coriolis to take advantage of additional build information
#2.2.2
* Update DPS/HPS/EPS in real-time as modifiers change
* Use coriolis-data 2.2.2:
* Add distributor draw modifier to shield generators

View File

@@ -271,7 +271,7 @@
"totalExplDpe": 0,
"totalExplDps": 0,
"totalExplSDps": 0,
"totalHps": 33.28,
"totalHps": 33.62,
"totalKinDpe": 103.97,
"totalKinDps": 28.92,
"totalKinSDps": 21.23,

View 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
}
}

View 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
}
}

View 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
}
}

View File

@@ -1,8 +1,8 @@
{
"type_6_transporter": {
"Cargo": "0p0tdFal8d8s8f4-----04040303430101.Iw1-kA==.Aw1-kA==.",
"Miner": "0p5tdFal8d8s8f42l2l---040403451q0101.Iw1-kA==.Aw1-kA==.",
"Hopper": "0p0tdFal8d0s8f41717---030302024300-.Iw1-kA==.Aw1-kA==."
"Cargo": "0p0tdFal8d8s8f4-----04040303430101.Iw1/kA==.Aw1/kA==.",
"Miner": "0p5tdFal8d8s8f42l2l---040403451q0101.Iw1/kA==.Aw1/kA==.",
"Hopper": "0p0tdFal8d0s8f41717---030302024300-.Iw1/kA==.Aw1/kA==."
},
"type_7_transport": {
"Cargo": "0p0tiFfliddsdf5--------0505040403480101.Iw18aQ==.Aw18aQ==.",
@@ -36,7 +36,7 @@
"Test": "4putkFklkdzsuf52c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.Iw18ZlA=.Aw18ZlA=."
},
"diamondback_explorer": {
"Explorer": "0p0tdFfldddsdf5---0202--320p432i2f.Iw1-kA==.Aw1-kA==."
"Explorer": "0p0tdFfldddsdf5---0202--320p432i2f.Iw1/kA==.Aw1/kA==."
},
"vulture": {
"Bounty Hunter": "3patcFalddksff31e1e0404-0l4a5d27662j.Iw19kA==.Aw19kA==."
@@ -45,6 +45,6 @@
"Attack": "2pfthFalidpsff31r0s0s0s0s000404-04-4a-5d27-.Iw18aQ==.Aw18aQ==."
},
"eagle": {
"Figther": "4p0t5F5l3d5s5f20p0p24-40532j-.Iw1-EA==.Aw1-EA==."
"Figther": "4p0t5F5l3d5s5f20p0p24-40532j-.Iw1/EA==.Aw1/EA==."
}
}

View File

@@ -142,7 +142,7 @@ describe('Import Modal', function() {
expect(modal.state.singleBuild).toBe(true);
clickProceed();
expect(MockRouter.go.mock.calls.length).toBe(1);
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/anaconda/4putkFklkdzsuf52c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.AwRj4zNKqA==.CwBhCYzBGW9qCTSqs5xA.?bn=Test%20My%20Ship');
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/anaconda?code=4putkFklkdzsuf52c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.AwRj4zNKqA%3D%3D.CwBhCYzBGW9qCTSqs5xA.&bn=Test%20My%20Ship');
});
it('catches an invalid build', function() {
@@ -167,7 +167,36 @@ describe('Import Modal', function() {
expect(modal.state.singleBuild).toBe(true);
clickProceed();
expect(MockRouter.go.mock.calls.length).toBe(1);
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/anaconda/4putkFklkdzsuf52c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.AwRj4zNKqA==.CwBhCYzBGW9qCTSqs5xA.H4sIAAAAAAAAA2MUe8HMwPD-PwDDhxeuCAAAAA==?bn=Test%20My%20Ship');
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/anaconda?code=4putkFklkdzsuf52c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.AwRj4zNKqA%3D%3D.CwBhCYzBGW9qCTSqs5xA.H4sIAAAAAAAAA2P8xwAEf0GE2AtmBob%2F%2FwFvM%2BjKEgAAAA%3D%3D&bn=Test%20My%20Ship');
});
});
describe('Import Detailed Engineered V4 Build', function() {
beforeEach(reset);
it('imports a valid v4 build', function() {
const importData = require('./fixtures/asp-test-detailed-export-v4');
pasteText(JSON.stringify(importData));
expect(modal.state.importValid).toBeTruthy();
expect(modal.state.errorMsg).toEqual(null);
expect(modal.state.singleBuild).toBe(true);
clickProceed();
expect(MockRouter.go.mock.calls.length).toBe(1);
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/asp?code=0pftiFflfddsnf5------020202033c044002v62f2i.AwRj4yvI.CwRgDBldHnJA.H4sIAAAAAAAAA2P858DAwPCXEUhwHPvx%2F78YG5AltB7I%2F8%2F0TwImJboDSPJ%2F%2B%2Ff%2Fv%2FKlX%2F%2F%2Fi3AwMTBIfARK%2FGf%2BJwVSxArStVAYqOjvz%2F%2F%2FJVo5GRhE2IBc4SKQSSz%2FDGEmCa398P8%2F%2F2%2BgTf%2F%2FAwDFxwtofAAAAA%3D%3D&bn=Multi-purpose%20Asp%20Explorer');
});
it('imports a valid v4 build with modifications', function() {
const importData = require('./fixtures/courier-test-detailed-export-v4');
pasteText(JSON.stringify(importData));
expect(modal.state.importValid).toBeTruthy();
expect(modal.state.errorMsg).toEqual(null);
expect(modal.state.singleBuild).toBe(true);
clickProceed();
expect(MockRouter.go.mock.calls.length).toBe(1);
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/imperial_courier?code=0patzF5l0das8f31a1a270202000e402t0101-2f.AwRj4zKA.CwRgDBldLiQ%3D.H4sIAAAAAAAAA12OP0tCYRjFj9fuVbvF1du9ekkT8s%2FkIg4NElyIBBd321yaGvwUQTS3N7UFfYygIT9EoyQUJA36ns47XJCWA%2B%2Fz%2Bz3Pe3ImBbDNKaqNPSBoGrL4ngfomKpFGiJ%2BLgHteR1IPjxJT5pF11uSeXNsJVcRfgdC92syWUuK0iMdKZqrjJ%2F0aoA71lJ5oKf38knWcCiptCPdhJIerdS00vlK0qktlqoj983UmqqHjQ33VsW8eazFmaTyULP2hQ4lX8LBme6g%2F6v0TTdbxJ2KhdEIaCw15MF%2FNB0L%2BS2hwEwyFM8KgP%2BqEpWWA3Qu9Z3z9kPWHzakt7Dt%2BAeD7ghSTgEAAA%3D%3D&bn=Multi-purpose%20Imperial%20Courier');
});
});
@@ -209,26 +238,38 @@ describe('Import Modal', function() {
expect(modal.state.singleBuild).toBe(true);
clickProceed();
expect(MockRouter.go.mock.calls.length).toBe(1);
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/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=2putsFklndzsxf50x0x7l28281919040404040402020l06p05sf63c5ifrv66g2f.AwRj4zNaKA%3D%3D.CwRgDBldUExuBiQqA%3D%3D%3D.H4sIAAAAAAAAA02Svy9DURTHT1vvtfoat30eXlvV0ufXQmLAIDHSRDcJAzHV1PgDDAaJpVbxF0gYKhFiEFuXTgbCIsKfYJCItHWP75E83vLNue%2F7Od977zs3pBeJ6DsE6TcNIlVn5lgFSw7rfrEikL6mSVS0HSL3MgxoqM3sTGtm%2BxA2R3RGSLSTfWzD32kxu043kVNFDxt6wU8ajVpEY7coh5uARrYR0n3aYY4%2FY6lmkc4xveafqZOHpHejRMb9J7NZQqN9Ascto4fjet0P7iQgRhV7mo5LlLtAUnIe34rVDaKBF9AThUJhla3%2FHqMRB76XBV7v8vEvOOoGx%2BJEgKz9BgvZEHJOyHNUakYujUuSW8KxWOkl%2F%2BzuMsR6QpkS8URUTYKTAagNta4EEvFE1INAqQD0IdCdQCKeiOoBk9%2BPYU87QL7i2tajkITKk0odSFxvAJrClawX%2BCkRT0RZYNjV5b%2BRbyLaOpMkafJa%2BBgufjFnjxBnvgFxKvgBnNYlP7jwiXcRnYQ%2F%2FoRlqCnTHAz41xha9F78CNahGXk8eZ3z%2FcyWjJcg7goeU%2BJdZsw%2FFW2pAaMCAAA%3D&bn=Imported%20Federal%20Corvette');
});
it('imports a valid v4 build', function() {
const importData = require('./fixtures/companion-api-import-2');
pasteText(JSON.stringify(importData));
expect(modal.state.importValid).toBeTruthy();
expect(modal.state.errorMsg).toEqual(null);
expect(modal.state.singleBuild).toBe(true);
clickProceed();
expect(MockRouter.go.mock.calls.length).toBe(1);
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/beluga?code=0pktsFplCdpsnf70t0t2727270004040404043c4fmimlmm04mc0iv62i2f.AwRj4yukg%3D%3D%3D.CwRgDBldHi8IUA%3D%3D.H4sIAAAAAAAAA2P8Z8%2FAwPCXEUiIKTMxMPCv%2F%2Ff%2FP8cFIPGf6Z8YTEr0GjMDg%2FJWICERBOTzn%2Fn7%2F7%2FIO5Ai5n9SIEWsQEIoSxAolfbt%2F3%2BJPk4GBhE7YQYGYVmgcuVnf4Aq%2FwMAIrEcGGsAAAA%3D&bn=Imported%20Beluga%20Liner');
});
});
describe('Import E:D Shipyard Builds', function() {
it('imports a valid builds', function() {
const imports = require('./fixtures/ed-shipyard-import-valid');
for (let i = 0; i < imports.length; i++ ) {
reset();
let fixture = imports[i];
pasteText(fixture.buildText);
expect(modal.state.importValid).toBeTruthy();
expect(modal.state.errorMsg).toEqual(null);
clickProceed();
expect(MockRouter.go.mock.calls.length).toBe(1);
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/' + fixture.shipId + '/' + fixture.buildCode + '?bn=' + encodeURIComponent(fixture.buildName));
}
});
// it('imports a valid build', function() {
// const imports = require('./fixtures/ed-shipyard-import-valid');
//
// for (let i = 0; i < imports.length; i++ ) {
// reset();
// let fixture = imports[i];
// pasteText(fixture.buildText);
// expect(modal.state.importValid).toBeTruthy();
// expect(modal.state.errorMsg).toEqual(null);
// clickProceed();
// expect(MockRouter.go.mock.calls.length).toBe(1);
// expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/' + fixture.shipId + '?code=' + encodeURIComponent(fixture.buildCode) + '&bn=' + encodeURIComponent(fixture.buildName));
// }
// });
it('catches invalid builds', function() {
const imports = require('./fixtures/ed-shipyard-import-invalid');

View File

@@ -1,6 +1,6 @@
{
"name": "coriolis_shipyard",
"version": "2.2.2",
"version": "2.2.5",
"repository": {
"type": "git",
"url": "https://github.com/EDCD/coriolis"

View File

@@ -8,7 +8,7 @@ import Header from './components/Header';
import Tooltip from './components/Tooltip';
import ModalImport from './components/ModalImport';
import * as CompanionApiUtils from './utils/CompanionApiUtils';
import { outfitURL } from './utils/UrlGenerators'
import { outfitURL } from './utils/UrlGenerators';
import AboutPage from './pages/AboutPage';
import NotFoundPage from './pages/NotFoundPage';

View File

@@ -72,7 +72,7 @@ export default class ComparisonTable extends TranslatedComponent {
* @return {React.Component} Table row
*/
_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 = [
<td key='s' className='tl'><Link href={url}>{build.name}</Link></td>,
<td key='bn' className='tl'><Link href={url}>{build.buildName}</Link></td>

View File

@@ -44,17 +44,26 @@ export default class HardpointSlot extends Slot {
let validMods = Modifications.validity[m.grp] || [];
let showModuleResistances = Persist.showModuleResistances();
// Modifications tooltip shows blueprint and grade, if available
let modTT = translate('modified');
if (m && m.blueprint) {
modTT = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
if (m.blueprint.special && m.blueprint.special.id) {
modTT += ', ' + translate(m.blueprint.special.name);
}
}
return <div className='details' draggable='true' onDragStart={drag} onDragEnd={drop}>
<div className={'cb'}>
<div className={'l'}>
{m.mount && m.mount == 'F' ? <span onMouseOver={termtip.bind(null, 'fixed')} onMouseOut={tooltip.bind(null, null)}><MountFixed /></span> : ''}
{m.mount && m.mount == 'G' ? <span onMouseOver={termtip.bind(null, 'gimballed')} onMouseOut={tooltip.bind(null, null)}><MountGimballed /></span> : ''}
{m.mount && m.mount == 'T' ? <span onMouseOver={termtip.bind(null, 'turreted')} onMouseOut={tooltip.bind(null, null)}><MountTurret /></span> : ''}
{m.type && m.type.match('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.type && m.type.match('E') ? <span onMouseOver={termtip.bind(null, 'explosive')} onMouseOut={tooltip.bind(null, null)}><DamageExplosive /></span> : ''}
{classRating} {translate(m.name || m.grp)}{ m.mods && Object.keys(m.mods).length > 0 ? <span className='r' onMouseOver={termtip.bind(null, 'modified')} onMouseOut={tooltip.bind(null, null)}><Modified /></span> : null }
</div>
{m.mount && m.mount == 'F' ? <span onMouseOver={termtip.bind(null, 'fixed')} onMouseOut={tooltip.bind(null, null)}><MountFixed /></span> : ''}
{m.mount && m.mount == 'G' ? <span onMouseOver={termtip.bind(null, 'gimballed')} onMouseOut={tooltip.bind(null, null)}><MountGimballed /></span> : ''}
{m.mount && m.mount == 'T' ? <span onMouseOver={termtip.bind(null, 'turreted')} onMouseOut={tooltip.bind(null, null)}><MountTurret /></span> : ''}
{m.getDamageType() && m.getDamageType().match('K') ? <span onMouseOver={termtip.bind(null, 'kinetic')} onMouseOut={tooltip.bind(null, null)}><DamageKinetic /></span> : ''}
{m.getDamageType() && m.getDamageType().match('T') ? <span onMouseOver={termtip.bind(null, 'thermal')} onMouseOut={tooltip.bind(null, null)}><DamageThermal /></span> : ''}
{m.getDamageType() && m.getDamageType().match('E') ? <span onMouseOver={termtip.bind(null, 'explosive')} onMouseOut={tooltip.bind(null, null)}><DamageExplosive /></span> : ''}
{classRating} {translate(m.name || m.grp)}{ m.mods && Object.keys(m.mods).length > 0 ? <span className='r' onMouseOver={termtip.bind(null, modTT)} onMouseOut={tooltip.bind(null, null)}><Modified /></span> : null }
</div>
<div className={'r'}>{formats.round(m.getMass())}{u.T}</div>
</div>

View File

@@ -26,10 +26,16 @@ export default class InternalSlot extends Slot {
let validMods = Modifications.validity[m.grp] || [];
let showModuleResistances = Persist.showModuleResistances();
// Modifications tooltip shows blueprint and grade, if available
let modTT = translate('modified');
if (m && m.blueprint) {
modTT = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
}
let mass = m.getMass() || m.cargo || m.fuel || 0;
return <div className='details' draggable='true' onDragStart={drag} onDragEnd={drop}>
<div className={'cb'}>
<div className={'l'}>{classRating} {translate(m.name || m.grp)}{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>
<div className={'cb'}>

View File

@@ -346,6 +346,7 @@ export default class ModalImport extends TranslatedComponent {
}
}
} catch (e) {
// console.log(e.stack);
this.setState({ errorMsg: (typeof e == 'string') ? e : 'Cannot Parse the data!' });
return;
}

View File

@@ -63,9 +63,24 @@ export default class Modification extends TranslatedComponent {
let translate = this.context.language.translate;
let name = this.props.name;
if (name === 'type') {
// We don't show type
return null;
}
let symbol;
if (name === 'jitter') {
symbol = '°';
} else if (name !== 'burst') {
symbol = '%';
}
if (symbol) {
symbol = ' (' + symbol + ')';
}
return (
<div className={'cb'} key={name}>
<div className={'cb'}>{translate(name)}{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)} />
</div>
);

View File

@@ -50,6 +50,12 @@ export default class StandardSlot extends TranslatedComponent {
let showModuleResistances = Persist.showModuleResistances();
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) {
modTT = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
}
if (!selected) {
// If not selected then sure that modifications flag is unset
this._modificationsSelected = false;
@@ -81,7 +87,7 @@ export default class StandardSlot extends TranslatedComponent {
<div className={cn('details-container', { warning: warning && warning(slot.m) })}>
<div className={'sz'}>{slot.maxClass}</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/>
<div className={'cb'}>

View File

@@ -42,6 +42,7 @@ export default class ErrorDetails extends React.Component {
return <div className='error'>
<h1>Jameson, we have a problem..</h1>
<h1><small>{error.message}</small></h1>
<div>Please note that this site uses Google Analytics to track performance and usage. If you are blocking cookies, for example using Ghostery, please disable blocking for this site and try again.</div>
{content}
</div>;
}

View File

@@ -8,7 +8,7 @@ import Persist from '../stores/Persist';
import Ship from '../shipyard/Ship';
import { toDetailedBuild } from '../shipyard/Serializer';
import { outfitURL } from '../utils/UrlGenerators';
import { FloppyDisk, Bin, Switch, Download, Reload, Fuel } from '../components/SvgIcons';
import { FloppyDisk, Bin, Switch, Download, Reload, Fuel, LinkIcon } from '../components/SvgIcons';
import ShipSummaryTable from '../components/ShipSummaryTable';
import StandardSlotSection from '../components/StandardSlotSection';
import HardpointsSlotSection from '../components/HardpointsSlotSection';
@@ -20,6 +20,7 @@ import LineChart from '../components/LineChart';
import PowerManagement from '../components/PowerManagement';
import CostSection from '../components/CostSection';
import ModalExport from '../components/ModalExport';
import ModalPermalink from '../components/ModalPermalink';
import Slider from '../components/Slider';
const SPEED_SERIES = ['boost', '4 Pips', '2 Pips', '0 Pips'];
@@ -270,6 +271,13 @@ export default class OutfittingPage extends Page {
this.resizeListener.remove();
}
/**
* Generates the short URL
*/
_genShortlink() {
this.context.showModal(<ModalPermalink url={window.location.href}/>);
}
/**
* Render the Page
* @return {React.Component} The page contents
@@ -289,8 +297,6 @@ export default class OutfittingPage extends Page {
sStr = ship.getStandardString() + '.' + ship.getModificationsString(),
iStr = ship.getInternalString() + '.' + ship.getModificationsString();
Router.replace(outfitURL(ship.id, code, buildName));
return (
<div id='outfit' className={'page'} style={{ fontSize: (sizeRatio * 0.9) + 'em' }}>
<div id='overview'>
@@ -315,6 +321,9 @@ export default class OutfittingPage extends Page {
<button onClick={buildName && this._exportBuild} disabled={!buildName} onMouseOver={termtip.bind(null, 'export')} onMouseOut={hide}>
<Download className='lg'/>
</button>
<button onClick={this._genShortlink} onMouseOver={termtip.bind(null, 'shortlink')} onMouseOut={hide}>
<LinkIcon className='lg' />
</button>
</div>
</div>

View File

@@ -1,5 +1,6 @@
import * as ModuleUtils from './ModuleUtils';
import * as _ from 'lodash';
import { Modifications } from 'coriolis-data/dist';
/**
* Module - active module in a ship's buildout
@@ -28,7 +29,7 @@ export default class Module {
/**
* Get a value for a given 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) {
return this.mods && this.mods[name] ? this.mods[name] : null;
@@ -37,7 +38,7 @@ export default class Module {
/**
* Set a value for a given modification ID
* @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) {
if (!this.mods) {
@@ -47,8 +48,12 @@ export default class Module {
if (value == null || value == 0) {
delete this.mods[name];
} else {
// Round just to be sure
this.mods[name] = Math.round(value);
if (isNaN(value)) {
this.mods[name] = value;
} else {
// Round just to be sure
this.mods[name] = Math.round(value);
}
}
}
@@ -61,8 +66,20 @@ export default class Module {
_getModifiedValue(name, additive) {
let result = this[name] || (additive ? 0 : null); // Additive NULL === 0
if (result != null) {
// Jitter is special, being the only non-percentage value (it is in fact degrees)
const modValue = name === 'jitter' ? this.getModValue(name) / 100 : this.getModValue(name) / 10000;
const modification = Modifications.modifications[name];
if (!modification) {
return result;
}
// We store percentages as decimals, so to get them back we need to divide by 10000. Otherwise
// we divide by 100. Both ways we end up with a value with two decimal places
let modValue;
if (modification.type === 'percentage') {
modValue = this.getModValue(name) / 10000;
} else if (modification.type === 'numeric') {
modValue = this.getModValue(name) / 100;
} else {
modValue = this.getModValue(name);
}
if (modValue) {
if (additive) {
result = result + modValue;
@@ -70,7 +87,18 @@ export default class Module {
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;
}
@@ -421,7 +449,7 @@ export default class Module {
getDps() {
// DPS is a synthetic value
let damage = this.getDamage();
let rpshot = this.getRoundsPerShot() || 1;
let rpshot = this.roundspershot || 1;
let rof = this.getRoF() || 1;
return damage * rpshot * rof;
@@ -434,7 +462,7 @@ export default class Module {
getEps() {
// EPS is a synthetic value
let distdraw = this.getDistDraw();
let rpshot = this.getRoundsPerShot() || 1;
let rpshot = this.roundspershot || 1;
let rof = this.getRoF() || 1;
return distdraw * rpshot * rof;
@@ -447,7 +475,7 @@ export default class Module {
getHps() {
// HPS is a synthetic value
let heat = this.getThermalLoad();
let rpshot = this.getRoundsPerShot() || 1;
let rpshot = this.roundspershot || 1;
let rof = this.getRoF() || 1;
return heat * rpshot * rof;
@@ -478,11 +506,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
*/
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);
}
/**
@@ -533,7 +585,6 @@ export default class Module {
return this._getModifiedValue('cells');
}
/**
* Get the jitter for this module, taking in to account modifications
* @return {Number} the jitter for this module
@@ -541,4 +592,12 @@ export default class Module {
getJitter() {
return this._getModifiedValue('jitter', true);
}
/**
* Get the damage type for this module, taking in to account modifications
* @return {string} the damage types for this module; any combination of E T and K
*/
getDamageType() {
return this.getModValue('type') || this.type;
}
}

View File

@@ -105,6 +105,59 @@ export function internal(id) {
return null;
};
/**
* Finds a standard module based on Class, Rating, Group and/or name.
* At least one of Group name or unique module name must be provided
*
* @param {String} groupName [Optional] Full name or abbreviated name for module group
* @param {integer} clss module Class
* @param {String} rating module Rating
* @param {String} name [Optional] Long/unique name for module -e.g. 'Advanced Discover Scanner'
* @return {Object} The module if found, null if not found
*/
export function findStandard(groupName, clss, rating, name) {
let groups = {};
if (groupName) {
if (Modules.standard[groupName]) {
groups[groupName] = Modules.standard[groupName];
} else {
let grpCode = ModuleNameToGroup[groupName.toLowerCase()];
if (grpCode && Modules.standard[grpCode]) {
groups[grpCode] = Modules.standard[grpCode];
}
}
} else if (name) {
groups = Modules.standard;
}
for (let g in groups) {
let group = groups[g];
for (let i = 0, l = group.length; i < l; i++) {
if (group[i].class == clss && group[i].rating == rating && ((!name && !group[i].name) || group[i].name == name)) {
return group[i];
}
}
}
return null;
}
/**
* Finds a standard Module ID based on Class, Rating, Group and/or name.
* At least one of Group name or unique module name must be provided
*
* @param {String} groupName [Optional] Full name or abbreviated name for module group
* @param {integer} clss module Class
* @param {String} rating Module Rating
* @param {String} name [Optional] Long/unique name for module -e.g. 'Advanced Discover Scanner'
* @return {String} The id of the module if found, null if not found
*/
export function findStandardId(groupName, clss, rating, name) {
let i = this.findStandard(groupName, clss, rating, name);
return i ? i.id : 0;
}
/**
* Finds an internal module based on Class, Rating, Group and/or name.
* At least one ofGroup name or unique module name must be provided

View File

@@ -8,6 +8,8 @@ import { outfitURL } from '../utils/UrlGenerators';
const STANDARD = ['powerPlant', 'thrusters', 'frameShiftDrive', 'lifeSupport', 'powerDistributor', 'sensors', 'fuelTank'];
const STANDARD_GROUPS = { 'powerPlant': 'pp', 'thrusters': 't', 'frameShiftDrive': 'fsd', 'lifeSupport': 'ls', 'powerDistributor': 'pd', 'sensors': 's', 'fuelTank': 'ft' };
/**
* Generates ship-loadout JSON Schema standard object
* @param {Object} standard model
@@ -30,6 +32,10 @@ function standardToSchema(standard) {
o.modifications = standard.m.mods;
}
if (standard.m.blueprint && Object.keys(standard.m.blueprint).length > 0) {
o.blueprint = standard.m.blueprint;
}
return o;
}
return null;
@@ -62,6 +68,10 @@ function slotToSchema(slot) {
if (slot.m.mods && Object.keys(slot.m.mods).length > 0) {
o.modifications = slot.m.mods;
}
if (slot.m.blueprint && Object.keys(slot.m.blueprint).length > 0) {
o.blueprint = slot.m.blueprint;
}
return o;
}
return null;
@@ -137,6 +147,7 @@ export function fromDetailedBuild(detailedBuild) {
let ship = new Ship(shipId, shipData.properties, shipData.slots);
let bulkheads = ModuleUtils.bulkheadIndex(stn.bulkheads);
let modifications = new Array(stn.bulkheads.modifications);
let blueprints = new Array(stn.bulkheads.blueprint);
if (bulkheads < 0) {
throw 'Invalid bulkheads: ' + stn.bulkheads;
@@ -149,13 +160,13 @@ export function fromDetailedBuild(detailedBuild) {
priorities.push(stn[c].priority === undefined ? 0 : stn[c].priority - 1);
enabled.push(stn[c].enabled === undefined ? true : stn[c].enabled);
modifications.push(stn[c].modifications);
return stn[c].class + stn[c].rating;
blueprints.push(stn[c].blueprint);
return ModuleUtils.findStandardId(STANDARD_GROUPS[c], stn[c].class, stn[c].rating, stn[c].name);
});
let internal = comps.internal.map(c => c ? ModuleUtils.findInternalId(c.group, c.class, c.rating, c.name) : 0);
let hardpoints = comps.hardpoints
.map(c => c ? ModuleUtils.findHardpointId(c.group, c.class, c.rating, c.name, MountMap[c.mount], c.missile) : 0)
let hardpoints = comps.hardpoints.map(c => c ? ModuleUtils.findHardpointId(c.group, c.class, c.rating, c.name, MountMap[c.mount], c.missile) : 0)
.concat(comps.utility.map(c => c ? ModuleUtils.findHardpointId(c.group, c.class, c.rating, c.name, MountMap[c.mount]) : 0));
// The ordering of these arrays must match the order in which they are read in Ship.buildWith
@@ -170,12 +181,17 @@ export function fromDetailedBuild(detailedBuild) {
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))
comps.hardpoints.map(c => (c ? c.modifications : null)),
comps.utility.map(c => (c ? c.modifications : null)),
comps.internal.map(c => (c ? c.modifications : null))
);
blueprints = blueprints.concat(
comps.hardpoints.map(c => (c ? c.blueprint : null)),
comps.utility.map(c => (c ? c.blueprint : null)),
comps.internal.map(c => (c ? c.blueprint : null))
);
ship.buildWith({ bulkheads, standard, hardpoints, internal }, priorities, enabled, modifications);
ship.buildWith({ bulkheads, standard, hardpoints, internal }, priorities, enabled, modifications, blueprints);
return ship;
};

View File

@@ -3,12 +3,20 @@ import * as ModuleUtils from './ModuleUtils';
import * as Utils from '../utils/UtilityFunctions';
import Module from './Module';
import LZString from 'lz-string';
import * as _ from 'lodash';
import isEqual from 'lodash/lang';
import { Modifications } from 'coriolis-data/dist';
const zlib = require('zlib');
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
* @param {Object} slot The Slot
@@ -450,7 +458,7 @@ export default class Ship {
} else if (name === 'shieldreinforcement') {
m.setModValue(name, value);
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);
this.recalculateDps();
this.recalculateHps();
@@ -472,9 +480,10 @@ export default class Ship {
* @param {array} priorities Slot priorities
* @param {Array} enabled Slot active/inactive
* @param {Array} mods Modifications
* @param {Array} blueprints Blueprints for modifications
* @return {this} The current ship instance for chaining
*/
buildWith(comps, priorities, enabled, mods) {
buildWith(comps, priorities, enabled, mods, blueprints) {
let internal = this.internal,
standard = this.standard,
hps = this.hardpoints,
@@ -514,6 +523,7 @@ export default class Ship {
this.bulkheads.m = null;
this.useBulkhead(comps && comps.bulkheads ? comps.bulkheads : 0, true);
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.enabled = enabled ? enabled[0] * 1 : true;
@@ -526,7 +536,10 @@ export default class Ship {
standard[i].discountedCost = 0;
if (comps) {
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);
}
}
@@ -545,7 +558,10 @@ export default class Ship {
if (comps && comps.hardpoints[i] !== 0) {
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);
}
}
@@ -562,7 +578,10 @@ export default class Ship {
if (comps && comps.internal[i] !== 0) {
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);
}
}
@@ -596,6 +615,7 @@ export default class Ship {
hardpoints = new Array(this.hardpoints.length),
internal = new Array(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('.'),
priorities = null,
enabled = null,
@@ -615,7 +635,7 @@ export default class Ship {
this.decodeModificationsString(modstr, modifications);
} else {
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) {
// Could be out-of-date URL; ignore
}
@@ -633,7 +653,8 @@ export default class Ship {
},
priorities,
enabled,
modifications
modifications,
blueprints,
);
};
@@ -909,22 +930,22 @@ export default class Ship {
totalDpe += dpe;
totalDps += dps;
totalSDps += sdps;
if (slot.m.type === 'E') {
if (slot.m.getDamageType() === 'E') {
totalExplDpe += dpe;
totalExplDps += dps;
totalExplSDps += sdps;
}
if (slot.m.type === 'K') {
if (slot.m.getDamageType() === 'K') {
totalKinDpe += dpe;
totalKinDps += dps;
totalKinSDps += sdps;
}
if (slot.m.type === 'T') {
if (slot.m.getDamageType() === 'T') {
totalThermDpe += dpe;
totalThermDps += dps;
totalThermSDps += sdps;
}
if (slot.m.type === 'EK') {
if (slot.m.getDamageType() === 'EK') {
totalExplDpe += dpe / 2;
totalKinDpe += dpe / 2;
totalExplDps += dps / 2;
@@ -932,7 +953,7 @@ export default class Ship {
totalExplSDps += sdps / 2;
totalKinSDps += sdps / 2;
}
if (slot.m.type === 'ET') {
if (slot.m.getDamageType() === 'ET') {
totalExplDpe += dpe / 2;
totalThermDpe += dpe / 2;
totalExplDps += dps / 2;
@@ -940,7 +961,7 @@ export default class Ship {
totalExplSDps += sdps / 2;
totalThermSDps += sdps / 2;
}
if (slot.m.type === 'KT') {
if (slot.m.getDamageType() === 'KT') {
totalKinDpe += dpe / 2;
totalThermDpe += dpe / 2;
totalKinDps += dps / 2;
@@ -1224,52 +1245,6 @@ export default class Ship {
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
* @param {String} code Serialized modification code
@@ -1284,7 +1259,8 @@ export default class Ship {
for (let j = 0; j < mods.length; j++) {
let modElements = mods[j].split(':');
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 {
arr[i][modElements[0]] = Number(modElements[1]);
}
@@ -1305,17 +1281,24 @@ export default class Ship {
updateModificationsString() {
// Start off by gathering the information that we need
let slots = new Array();
let blueprints = new Array();
let specials = new Array();
let bulkheadMods = new Array();
let bulkheadBlueprint = null;
let bulkheadBlueprintGrade = null;
if (this.bulkheads.m && this.bulkheads.m.mods) {
for (let modKey in this.bulkheads.m.mods) {
// Filter out invalid modifications
if (Modifications.validity['bh'] && Modifications.validity['bh'].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);
blueprints.push(bulkheadBlueprint);
specials.push(bulkheadBlueprint ? bulkheadBlueprint.special : null);
for (let slot of this.standard) {
let slotMods = new Array();
@@ -1323,11 +1306,13 @@ export default class Ship {
for (let modKey in slot.m.mods) {
// Filter out invalid modifications
if (Modifications.validity[slot.m.grp] && Modifications.validity[slot.m.grp].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);
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) {
@@ -1336,11 +1321,13 @@ export default class Ship {
for (let modKey in slot.m.mods) {
// Filter out invalid modifications
if (Modifications.validity[slot.m.grp] && Modifications.validity[slot.m.grp].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);
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) {
@@ -1349,18 +1336,27 @@ export default class Ship {
for (let modKey in slot.m.mods) {
// Filter out invalid modifications
if (Modifications.validity[slot.m.grp] && Modifications.validity[slot.m.grp].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);
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
let bufsize = 0;
for (let slot of slots) {
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 +1369,37 @@ export default class Ship {
for (let slot of slots) {
if (slot.length > 0) {
buffer.writeInt8(i, curpos++);
for (let slotMod of slot) {
buffer.writeInt8(slotMod.id, curpos++);
buffer.writeInt32LE(slotMod.value, curpos);
// console.log('ENCODE Slot ' + i + ': ' + Modifications.modifications[slotMod.id] + ' = ' + slotMod.value);
if (blueprints[i]) {
buffer.writeInt8(MODIFICATION_ID_BLUEPRINT, curpos++);
buffer.writeInt32LE(blueprints[i].id, curpos);
curpos += 4;
buffer.writeInt8(MODIFICATION_ID_GRADE, curpos++);
buffer.writeInt32LE(blueprints[i].grade, curpos);
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++;
}
if (curpos > 0) {
buffer.writeInt8(-1, curpos++);
buffer.writeInt8(SLOT_ID_DONE, curpos++);
}
this.serialized.modifications = zlib.gzipSync(buffer).toString('base64');
@@ -1397,23 +1412,42 @@ export default class Ship {
/**
* Populate the modifications array with modification values from the code.
* See updateModificationsString() for details of the structure.
* @param {String} buffer Buffer holding modification info
* @param {Array} arr Modification array
* @param {String} buffer Buffer holding modification info
* @param {Array} modArr Modification array
* @param {Array} blueprintArr Blueprint array
*/
decodeModificationsStruct(buffer, arr) {
decodeModificationsStruct(buffer, modArr, blueprintArr) {
let curpos = 0;
let slot = buffer.readInt8(curpos++);
while (slot != -1) {
while (slot != SLOT_ID_DONE) {
let modifications = {};
let blueprint = {};
let modificationId = buffer.readInt8(curpos++);
while (modificationId != -1) {
let modificationValue = buffer.readInt32LE(curpos);
while (modificationId != MODIFICATION_ID_DONE) {
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;
// console.log('DECODE Slot ' + slot + ': ' + Modifications.modifications[modificationId] + ' = ' + modificationValue);
modifications[Modifications.modifications[modificationId]] = modificationValue;
// There are a number of 'special' modification IDs, check for them here
if (modificationId === MODIFICATION_ID_BLUEPRINT) {
blueprint = Object.assign(blueprint, _.find(Modifications.blueprints, function(o) { return o.id === modificationValue; }));
} else if (modificationId === MODIFICATION_ID_GRADE) {
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++);
}
arr[slot] = modifications;
modArr[slot] = modifications;
blueprintArr[slot] = blueprint;
slot = buffer.readInt8(curpos++);
}
}

View File

@@ -13,8 +13,8 @@ const SHIP_FD_NAME_TO_CORIOLIS_NAME = {
'CobraMkIII': 'cobra_mk_iii',
'CobraMkIV': 'cobra_mk_iv',
'Cutter': 'imperial_cutter',
'DiamondBack': 'diamondback_explorer',
'DiamondBackXL': 'diamondback',
'DiamondBackXL': 'diamondback_explorer',
'DiamondBack': 'diamondback',
'Eagle': 'eagle',
'Empire_Courier': 'imperial_courier',
'Empire_Eagle': 'imperial_eagle',
@@ -149,13 +149,13 @@ export function shipFromJson(json) {
throw 'Unknown bulkheads "' + armourJson.name + '"';
}
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
// Power plant
const powerplantJson = json.modules.PowerPlant.module;
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.standard[0].enabled = powerplantJson.on === true;
ship.standard[0].priority = powerplantJson.priority;
@@ -163,7 +163,7 @@ export function shipFromJson(json) {
// Thrusters
const thrustersJson = json.modules.MainEngines.module;
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.standard[1].enabled = thrustersJson.on === true;
ship.standard[1].priority = thrustersJson.priority;
@@ -171,7 +171,7 @@ export function shipFromJson(json) {
// FSD
const frameshiftdriveJson = json.modules.FrameShiftDrive.module;
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.standard[2].enabled = frameshiftdriveJson.on === true;
ship.standard[2].priority = frameshiftdriveJson.priority;
@@ -179,7 +179,7 @@ export function shipFromJson(json) {
// Life support
const lifesupportJson = json.modules.LifeSupport.module;
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.standard[3].enabled = lifesupportJson.on === true;
ship.standard[3].priority = lifesupportJson.priority;
@@ -187,7 +187,7 @@ export function shipFromJson(json) {
// Power distributor
const powerdistributorJson = json.modules.PowerDistributor.module;
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.standard[4].enabled = powerdistributorJson.on === true;
ship.standard[4].priority = powerdistributorJson.priority;
@@ -195,7 +195,7 @@ export function shipFromJson(json) {
// Sensors
const sensorsJson = json.modules.Radar.module;
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.standard[5].enabled = sensorsJson.on === true;
ship.standard[5].priority = sensorsJson.priority;
@@ -229,7 +229,7 @@ export function shipFromJson(json) {
} else {
const hardpointJson = hardpointSlot.module;
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.hardpoints[hardpointArrayNum].enabled = hardpointJson.on === true;
ship.hardpoints[hardpointArrayNum].priority = hardpointJson.priority;
@@ -240,7 +240,7 @@ export function shipFromJson(json) {
// Add internal compartments
let internalSlotNum = 1;
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];
let internalSlot = null;
while (internalSlot === null && internalSlotNum < 99) {
@@ -256,7 +256,7 @@ export function shipFromJson(json) {
} else {
const internalJson = internalSlot.module;
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.internal[i].enabled = internalJson.on === true;
ship.internal[i].priority = internalJson.priority;
@@ -271,26 +271,63 @@ export function shipFromJson(json) {
* Add the modifications for a module
* @param {Module} module the module
* @param {Object} modifiers the modifiers
* @param {Object} blueprint the blueprint of the modification
* @param {Object} grade the grade of the modification
*/
function _addModifications(module, modifiers) {
function _addModifications(module, modifiers, blueprint, grade) {
if (!modifiers || !modifiers.modifiers) return;
let special;
for (const i in modifiers.modifiers) {
// 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;
// Some special modifications
if (modifiers.modifiers[i].name === 'mod_weapon_clip_size_override') {
// This is a numeric addition to the clip size, but we need to work it out in terms of being a percentage so
// that it works the same as other modifications
const origClip = module.clip || 1;
module.setModValue('clip', ((modifiers.modifiers[i].value - origClip) / origClip) * 10000);
} else if (modifiers.modifiers[i].name === 'mod_weapon_burst_size') {
// This is an absolute number that acts as an override
module.setModValue('burst', modifiers.modifiers[i].value * 100);
} else if (modifiers.modifiers[i].name === 'mod_weapon_burst_rof') {
// For some reason this is a non-normalised percentage (i.e. 12.23% is 12.23 value rather than 0.1223 as everywhere else), so fix that here
module.setModValue('burstrof', modifiers.modifiers[i].value * 100);
} else {
// Look up the modifiers to find what we need to do
const modifierActions = Modifications.modifierActions[modifiers.modifiers[i].name];
const value = modifiers.modifiers[i].value;
// Carry out the required changes
for (const action in modifierActions) {
const actionValue = modifierActions[action] * value;
let mod = module.getModValue(action) / 10000;
if (!mod) {
mod = 0;
// Carry out the required changes
for (const action in modifierActions) {
if (isNaN(modifierActions[action])) {
module.setModValue(action, modifierActions[action]);
} else {
const actionValue = modifierActions[action] * value;
let mod = module.getModValue(action) / 10000;
if (!mod) {
mod = 0;
}
module.setModValue(action, ((1 + mod) * (1 + actionValue) - 1) * 10000);
}
}
module.setModValue(action, ((1 + mod) * (1 + actionValue) - 1) * 10000);
}
// Note the special if present
if (modifiers.modifiers[i].name && modifiers.modifiers[i].name.startsWith('special_')) {
special = Modifications.specials[modifiers.modifiers[i].name];
}
}
// Add the blueprint ID, grade and special
if (blueprint) {
module.blueprint = Object.assign({}, Modifications.blueprints[blueprint]);
if (grade) {
module.blueprint.grade = Number(grade);
}
if (special) {
module.blueprint.special = special;
}
}
// Need to fix up a few items
// Shield boosters are treated internally as straight modifiers, so rather than (for example)
@@ -355,6 +392,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
if (module.getModValue('jitter')) {
module.setModValue('jitter', module.getModValue('jitter') / 100);

View File

@@ -68,6 +68,7 @@
"rating": { "$ref": "#/definitions/standardRatings" },
"enabled": { "type": "boolean" },
"priority": { "type": "integer", "minimum": 1, "maximum": 5 },
"blueprint": { "type": "object" },
"modifications": { "type": "object" }
}
},
@@ -82,6 +83,7 @@
"description": "The name identifing the thrusters (if applicable), e.g. 'Enhanced Performance'",
"type": "string"
},
"blueprint": { "type": "object" },
"modifications": { "type": "object" }
}
},
@@ -92,6 +94,7 @@
"rating": { "$ref": "#/definitions/standardRatings" },
"enabled": { "type": "boolean" },
"priority": { "type": "integer", "minimum": 1, "maximum": 5 },
"blueprint": { "type": "object" },
"modifications": { "type": "object" }
}
},
@@ -102,6 +105,7 @@
"rating": { "$ref": "#/definitions/standardRatings" },
"enabled": { "type": "boolean" },
"priority": { "type": "integer", "minimum": 1, "maximum": 5 },
"blueprint": { "type": "object" },
"modifications": { "type": "object" }
}
},
@@ -112,6 +116,7 @@
"rating": { "$ref": "#/definitions/standardRatings" },
"enabled": { "type": "boolean" },
"priority": { "type": "integer", "minimum": 1, "maximum": 5 },
"blueprint": { "type": "object" },
"modifications": { "type": "object" }
}
},
@@ -122,6 +127,7 @@
"rating": { "$ref": "#/definitions/standardRatings" },
"enabled": { "type": "boolean" },
"priority": { "type": "integer", "minimum": 1, "maximum": 5 },
"blueprint": { "type": "object" },
"modifications": { "type": "object" }
}
},
@@ -132,6 +138,7 @@
"rating": { "$ref": "#/definitions/standardRatings" },
"enabled": { "type": "boolean" },
"priority": { "type": "integer", "minimum": 1, "maximum": 5 },
"blueprint": { "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'",
"type": "string"
},
"blueprint": { "type": "object" },
"modifications": { "type": "object" }
}
},
@@ -179,6 +187,7 @@
"description": "The name identifing the component (if applicable), e.g. 'Retributor', or 'Mining Lance'",
"type": "string"
},
"blueprint": { "type": "object" },
"modifications": { "type": "object" }
}
},
@@ -202,6 +211,7 @@
"description": "The name identifing the component (if applicable), e.g. 'Point Defence', or 'Electronic Countermeasure'",
"type": "string"
},
"blueprint": { "type": "object" },
"modifications": { "type": "object" }
}
},