mirror of
https://github.com/EDCD/coriolis.git
synced 2025-12-09 14:45:35 +00:00
Compare commits
65 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fac279d9dd | ||
|
|
f4e5254832 | ||
|
|
2fb3ee8cd8 | ||
|
|
cc2f3fd1fe | ||
|
|
87146b2cf3 | ||
|
|
bec3ae3f89 | ||
|
|
af2e0cbed3 | ||
|
|
4bf30c0cd5 | ||
|
|
a9fdf73d86 | ||
|
|
fe691d12c7 | ||
|
|
a5df542aa2 | ||
|
|
069959dabb | ||
|
|
a0e8f19683 | ||
|
|
342ca7af05 | ||
|
|
d00c0c3904 | ||
|
|
8e0f1ca977 | ||
|
|
33360fd6cf | ||
|
|
78ad34b082 | ||
|
|
ba98fe49a9 | ||
|
|
11f5c04efa | ||
|
|
ee9f65052a | ||
|
|
a25dde8d2d | ||
|
|
8d813688f7 | ||
|
|
99fc55ba6c | ||
|
|
402a4c1939 | ||
|
|
d1e16470b8 | ||
|
|
76027b8537 | ||
|
|
fd5ff3b6a8 | ||
|
|
d2d8f084d2 | ||
|
|
ea3d57399c | ||
|
|
77d3053ff8 | ||
|
|
aea3e43e1c | ||
|
|
a949bd6738 | ||
|
|
4981ffb908 | ||
|
|
4859138053 | ||
|
|
1f3c66d9ba | ||
|
|
f2af463d00 | ||
|
|
3858712613 | ||
|
|
d7941e0a8a | ||
|
|
178d38f28d | ||
|
|
e42a0d1210 | ||
|
|
ddb89c47e7 | ||
|
|
8b9aae342b | ||
|
|
ab1d73a6ea | ||
|
|
d73a3cc2b4 | ||
|
|
230351b959 | ||
|
|
4ee5c03cd1 | ||
|
|
d8e9733170 | ||
|
|
aba2abe507 | ||
|
|
45e6b71ec9 | ||
|
|
15a14dc280 | ||
|
|
cbac650b9e | ||
|
|
f011f1f4d5 | ||
|
|
d467ad5f7c | ||
|
|
b56ac177d9 | ||
|
|
5b13d64a1d | ||
|
|
2620935745 | ||
|
|
45852db507 | ||
|
|
7fbcbb75ed | ||
|
|
abf65ee436 | ||
|
|
24849cee08 | ||
|
|
9e5efe50dc | ||
|
|
588cfc3990 | ||
|
|
bc31be5884 | ||
|
|
55e4c51d77 |
47
ChangeLog.md
47
ChangeLog.md
@@ -1,3 +1,50 @@
|
|||||||
|
#2.2.19
|
||||||
|
* Power management panel now displays modules in descending order of power usage by default
|
||||||
|
* Shot speed can no longer be modified directly. Its value is derived from the range modifier for Long Range and Focused modifications
|
||||||
|
* Ensure that jump range chart updates when fuel slider is changed
|
||||||
|
* Add 'Engine profile' and 'FSD profile' charts. These show how your maximum speed/jump range will alter as you alter the mass of your build
|
||||||
|
* Use coriolis-data 2.2.19:
|
||||||
|
* Remove shot speed modification - it is directly tied to range
|
||||||
|
* Fix incorrect minimal mass for 3C bi-weave shield generator
|
||||||
|
|
||||||
|
#2.2.18
|
||||||
|
* Change methodology for calculating explorer role; can result in lighter builds
|
||||||
|
* Tidy up layout for module selection and lay everything out in a consistent best-to-worst for both class and grade
|
||||||
|
* Make integrity for module reinforcement packages visible
|
||||||
|
* Clean up breakpoints for modules in available modules list; stops 7- or 8- module long lines
|
||||||
|
* Add damager/range graphs to damage dealt
|
||||||
|
* Reorder panels
|
||||||
|
* Use coriolis-data 2.2.18:
|
||||||
|
* Correct lower efficiency value to be better, not worse
|
||||||
|
|
||||||
|
#2.2.17
|
||||||
|
* Use in-game terminology for shield generator optmul and optmass items
|
||||||
|
* Add crew to shipyard and outfitting page information
|
||||||
|
* Use coriolis-data 2.2.17:
|
||||||
|
* Add mass as potential SCB modification
|
||||||
|
* Fix mining laser statistics
|
||||||
|
* Remove non-existent grade 4 and 5 wake scanner modifications
|
||||||
|
* Add number of crew for each ship
|
||||||
|
|
||||||
|
#2.2.16
|
||||||
|
* Fix 'Extreme' blueprint roll where some incorrect ranges were chosen
|
||||||
|
* Use coriolis-data 2.2.16:
|
||||||
|
* Fix incorrect thermal load modifiers for dirty drives
|
||||||
|
* Provide explicit information about if values are higher numeric value == better or not
|
||||||
|
|
||||||
|
#2.2.15
|
||||||
|
* Ensure that standard slots are repainted when any component changes
|
||||||
|
* Reload page if Safari throws a security error
|
||||||
|
* Handle import of ships with incorrectly-sized slots
|
||||||
|
* Add 'Extreme' blueprint roll: best beneficial and worst detrimental outcome (in place of 'Average' roll)
|
||||||
|
* Display information about Microsoft browser issues when an import fails
|
||||||
|
* Add 'purchase this build' icon link to EDDB
|
||||||
|
* Add 'miner' and 'shielded miner' ship roles
|
||||||
|
* Use coriolis-data 2.2.15:
|
||||||
|
* Fix location of initial cargo rack for Vulture
|
||||||
|
* Fix broken regeneration rate for 6B shield generators
|
||||||
|
* Tidy up breach damage values
|
||||||
|
|
||||||
#2.2.14
|
#2.2.14
|
||||||
* Ensure that jitter is shown correctly when the result of a special effect
|
* Ensure that jitter is shown correctly when the result of a special effect
|
||||||
* Use restyled blueprint information
|
* Use restyled blueprint information
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ Chat to us on [Discord](https://discord.gg/0uwCh6R62aPRjk9w)!
|
|||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
See the [Developer's Guide](https://github.com/cmmcleod/coriolis/wiki/Developer's-Guide) in the wiki.
|
See the [Developer's Guide](https://github.com/EDCD/coriolis/wiki/Developing-for-Coriolis) in the wiki.
|
||||||
|
|
||||||
|
|
||||||
### Ship and Module Database
|
### Ship and Module Database
|
||||||
|
|||||||
@@ -320,6 +320,7 @@
|
|||||||
"shieldExplRes": 0.5,
|
"shieldExplRes": 0.5,
|
||||||
"shieldKinRes": 0.4,
|
"shieldKinRes": 0.4,
|
||||||
"shieldThermRes": -0.2,
|
"shieldThermRes": -0.2,
|
||||||
"timeToDrain": 7.04
|
"timeToDrain": 7.04,
|
||||||
|
"crew": 3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
314
__tests__/fixtures/companion-api-import-3.json
Normal file
314
__tests__/fixtures/companion-api-import-3.json
Normal file
@@ -0,0 +1,314 @@
|
|||||||
|
{
|
||||||
|
"cargo": {
|
||||||
|
"capacity": 264
|
||||||
|
},
|
||||||
|
"free": false,
|
||||||
|
"fuel": {
|
||||||
|
"main": {
|
||||||
|
"capacity": 32
|
||||||
|
},
|
||||||
|
"reserve": {
|
||||||
|
"capacity": 0.52
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": 4,
|
||||||
|
"modules": {
|
||||||
|
"Armour": {
|
||||||
|
"module": {
|
||||||
|
"free": false,
|
||||||
|
"id": 128049298,
|
||||||
|
"name": "Type7_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": 128667746,
|
||||||
|
"name": "Decal_Trade_Dealer",
|
||||||
|
"on": true,
|
||||||
|
"priority": 1,
|
||||||
|
"unloaned": 0,
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Decal2": {
|
||||||
|
"module": {
|
||||||
|
"free": false,
|
||||||
|
"id": 128667738,
|
||||||
|
"name": "Decal_Combat_Competent",
|
||||||
|
"on": true,
|
||||||
|
"priority": 1,
|
||||||
|
"unloaned": 0,
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Decal3": {
|
||||||
|
"module": {
|
||||||
|
"free": false,
|
||||||
|
"id": 128667753,
|
||||||
|
"name": "Decal_Explorer_Scout",
|
||||||
|
"on": true,
|
||||||
|
"priority": 1,
|
||||||
|
"unloaned": 0,
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"EngineColour": [],
|
||||||
|
"FrameShiftDrive": {
|
||||||
|
"module": {
|
||||||
|
"free": false,
|
||||||
|
"id": 128064122,
|
||||||
|
"name": "Int_Hyperdrive_Size5_Class5",
|
||||||
|
"on": true,
|
||||||
|
"priority": 0,
|
||||||
|
"unloaned": 0,
|
||||||
|
"value": 5103953
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"FuelTank": {
|
||||||
|
"module": {
|
||||||
|
"free": false,
|
||||||
|
"id": 128064350,
|
||||||
|
"name": "Int_FuelTank_Size5_Class3",
|
||||||
|
"on": true,
|
||||||
|
"priority": 1,
|
||||||
|
"unloaned": 97754,
|
||||||
|
"value": 97754
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"LifeSupport": {
|
||||||
|
"module": {
|
||||||
|
"free": false,
|
||||||
|
"id": 128064154,
|
||||||
|
"name": "Int_LifeSupport_Size4_Class2",
|
||||||
|
"on": true,
|
||||||
|
"priority": 0,
|
||||||
|
"unloaned": 0,
|
||||||
|
"value": 28373
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"MainEngines": {
|
||||||
|
"module": {
|
||||||
|
"free": false,
|
||||||
|
"id": 128064087,
|
||||||
|
"name": "Int_Engine_Size5_Class5",
|
||||||
|
"on": true,
|
||||||
|
"priority": 0,
|
||||||
|
"unloaned": 0,
|
||||||
|
"value": 5103953
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"PaintJob": {
|
||||||
|
"module": {
|
||||||
|
"free": false,
|
||||||
|
"id": 128671422,
|
||||||
|
"name": "PaintJob_Type7_Tactical_White",
|
||||||
|
"on": true,
|
||||||
|
"priority": 1,
|
||||||
|
"unloaned": 0,
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"PlanetaryApproachSuite": {
|
||||||
|
"module": {
|
||||||
|
"free": false,
|
||||||
|
"id": 128672317,
|
||||||
|
"name": "Int_PlanetApproachSuite",
|
||||||
|
"on": true,
|
||||||
|
"priority": 1,
|
||||||
|
"unloaned": 500,
|
||||||
|
"value": 500
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"PowerDistributor": {
|
||||||
|
"module": {
|
||||||
|
"free": false,
|
||||||
|
"id": 128064192,
|
||||||
|
"name": "Int_PowerDistributor_Size3_Class5",
|
||||||
|
"on": true,
|
||||||
|
"priority": 0,
|
||||||
|
"unloaned": 0,
|
||||||
|
"value": 158331
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"PowerPlant": {
|
||||||
|
"module": {
|
||||||
|
"free": false,
|
||||||
|
"id": 128064047,
|
||||||
|
"name": "Int_Powerplant_Size4_Class5",
|
||||||
|
"on": true,
|
||||||
|
"priority": 1,
|
||||||
|
"unloaned": 0,
|
||||||
|
"value": 1610080
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Radar": {
|
||||||
|
"module": {
|
||||||
|
"free": false,
|
||||||
|
"id": 128064229,
|
||||||
|
"name": "Int_Sensors_Size3_Class2",
|
||||||
|
"on": true,
|
||||||
|
"priority": 0,
|
||||||
|
"unloaned": 0,
|
||||||
|
"value": 10133
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Slot01_Size6": {
|
||||||
|
"module": {
|
||||||
|
"free": false,
|
||||||
|
"id": 128064343,
|
||||||
|
"name": "Int_CargoRack_Size6_Class1",
|
||||||
|
"on": true,
|
||||||
|
"priority": 1,
|
||||||
|
"unloaned": 0,
|
||||||
|
"value": 362591
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Slot02_Size6": {
|
||||||
|
"module": {
|
||||||
|
"free": false,
|
||||||
|
"id": 128064343,
|
||||||
|
"name": "Int_CargoRack_Size6_Class1",
|
||||||
|
"on": true,
|
||||||
|
"priority": 1,
|
||||||
|
"unloaned": 0,
|
||||||
|
"value": 362591
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Slot03_Size5": {
|
||||||
|
"module": {
|
||||||
|
"free": false,
|
||||||
|
"id": 128064343,
|
||||||
|
"name": "Int_CargoRack_Size6_Class1",
|
||||||
|
"on": true,
|
||||||
|
"priority": 1,
|
||||||
|
"unloaned": 0,
|
||||||
|
"value": 362591
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Slot04_Size5": {
|
||||||
|
"module": {
|
||||||
|
"free": false,
|
||||||
|
"id": 128064342,
|
||||||
|
"name": "Int_CargoRack_Size5_Class1",
|
||||||
|
"on": true,
|
||||||
|
"priority": 1,
|
||||||
|
"unloaned": 111566,
|
||||||
|
"value": 111566
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Slot05_Size4": {
|
||||||
|
"module": {
|
||||||
|
"free": false,
|
||||||
|
"id": 128064342,
|
||||||
|
"name": "Int_CargoRack_Size5_Class1",
|
||||||
|
"on": true,
|
||||||
|
"priority": 1,
|
||||||
|
"unloaned": 111566,
|
||||||
|
"value": 111566
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Slot06_Size4": {
|
||||||
|
"module": {
|
||||||
|
"free": false,
|
||||||
|
"id": 128064279,
|
||||||
|
"name": "Int_ShieldGenerator_Size5_Class2",
|
||||||
|
"on": true,
|
||||||
|
"priority": 0,
|
||||||
|
"unloaned": 0,
|
||||||
|
"value": 189035
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Slot07_Size2": {
|
||||||
|
"module": {
|
||||||
|
"free": false,
|
||||||
|
"id": 128049549,
|
||||||
|
"name": "Int_DockingComputer_Standard",
|
||||||
|
"on": true,
|
||||||
|
"priority": 0,
|
||||||
|
"unloaned": 0,
|
||||||
|
"value": 4500
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Slot08_Size2": {
|
||||||
|
"module": {
|
||||||
|
"free": false,
|
||||||
|
"id": 128064340,
|
||||||
|
"name": "Int_CargoRack_Size3_Class1",
|
||||||
|
"on": true,
|
||||||
|
"priority": 1,
|
||||||
|
"unloaned": 0,
|
||||||
|
"value": 10563
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"SmallHardpoint1": [],
|
||||||
|
"SmallHardpoint2": [],
|
||||||
|
"SmallHardpoint3": [],
|
||||||
|
"SmallHardpoint4": [],
|
||||||
|
"TinyHardpoint1": {
|
||||||
|
"module": {
|
||||||
|
"free": false,
|
||||||
|
"id": 128668536,
|
||||||
|
"name": "Hpt_ShieldBooster_Size0_Class5",
|
||||||
|
"on": true,
|
||||||
|
"priority": 0,
|
||||||
|
"unloaned": 0,
|
||||||
|
"value": 281000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"TinyHardpoint2": {
|
||||||
|
"module": {
|
||||||
|
"free": false,
|
||||||
|
"id": 128668536,
|
||||||
|
"name": "Hpt_ShieldBooster_Size0_Class5",
|
||||||
|
"on": true,
|
||||||
|
"priority": 0,
|
||||||
|
"unloaned": 0,
|
||||||
|
"value": 281000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"TinyHardpoint3": {
|
||||||
|
"module": {
|
||||||
|
"free": false,
|
||||||
|
"id": 128668536,
|
||||||
|
"name": "Hpt_ShieldBooster_Size0_Class5",
|
||||||
|
"on": true,
|
||||||
|
"priority": 0,
|
||||||
|
"unloaned": 0,
|
||||||
|
"value": 281000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"TinyHardpoint4": {
|
||||||
|
"module": {
|
||||||
|
"free": false,
|
||||||
|
"id": 128049513,
|
||||||
|
"name": "Hpt_ChaffLauncher_Tiny",
|
||||||
|
"on": true,
|
||||||
|
"priority": 0,
|
||||||
|
"unloaned": 0,
|
||||||
|
"value": 8500
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"WeaponColour": []
|
||||||
|
},
|
||||||
|
"name": "Type7",
|
||||||
|
"value": {
|
||||||
|
"hull": 16780009,
|
||||||
|
"modules": 14479580,
|
||||||
|
"unloaned": 321386
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -229,7 +229,7 @@ describe('Import Modal', function() {
|
|||||||
|
|
||||||
beforeEach(reset);
|
beforeEach(reset);
|
||||||
|
|
||||||
it('imports a valid v4 build', function() {
|
it('imports a valid companion API build', function() {
|
||||||
const importData = require('./fixtures/companion-api-import-1');
|
const importData = require('./fixtures/companion-api-import-1');
|
||||||
pasteText(JSON.stringify(importData));
|
pasteText(JSON.stringify(importData));
|
||||||
|
|
||||||
@@ -241,7 +241,7 @@ describe('Import Modal', function() {
|
|||||||
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/federal_corvette?code=A2putsFklndzsxf50x0x7l28281919040404040402020l06p05sf63c5ifr--v66g2f.AwRj4zNaqA%3D%3D.CwRgDBldUExuBiIlUA%3D%3D.H4sIAAAAAAAAA02SPy9DURjG3%2F65vW1v47TXVbeqqF7EQtIIBomRJswsYmISH8BgkFhqFZ9AwlALMYitkXQyEF2k4SMYJNK0dV7PK7nc5ck55%2Fm9z%2FnznpBeJqLvECQbM4hUjZnjO5hyWGfFikAGGjGiku0QuddhQCNdZmdWM9snsDmih4REOdlnNvz9DrPrJIicPdSwoZf8pAnTIpq8x7DYADS%2Bi5DERY85%2BYqpmkc6x%2FWGf6beKCR3YBIZFZCxCgrtczjuOmo4qTf94F4KYuxhz5jjEhXmUJNexFrpIUo02ALN1j9u1JMgD%2FMga1GfbMNRd9iHUwGy%2BpspZF3IBSGvMFJluS%2FuR24FJ2KlV%2Fxju6sQq4lhRsQTUVUJTgegLtS6EUjEE1HPAmUC0KdAjwKJeCKqD8zoURx72gHyDW9nvQhJGHkyUscS1x%2BAZnAlqwU%2FI%2BKJKEvextXrf93eQrR1KUlS5HWwGC61mfOn0oN3IM4OHoBzuuIHj33hS5jT8KeamIYa0sjhgH%2BLfplP4kcwD5Xl3xR1wfeHtqWzBHHX8I9SH9Je%2FgGvXxeungIAAA%3D%3D&bn=Imported%20Federal%20Corvette');
|
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/federal_corvette?code=A2putsFklndzsxf50x0x7l28281919040404040402020l06p05sf63c5ifr--v66g2f.AwRj4zNaqA%3D%3D.CwRgDBldUExuBiIlUA%3D%3D.H4sIAAAAAAAAA02SPy9DURjG3%2F65vW1v47TXVbeqqF7EQtIIBomRJswsYmISH8BgkFhqFZ9AwlALMYitkXQyEF2k4SMYJNK0dV7PK7nc5ck55%2Fm9z%2FnznpBeJqLvECQbM4hUjZnjO5hyWGfFikAGGjGiku0QuddhQCNdZmdWM9snsDmih4REOdlnNvz9DrPrJIicPdSwoZf8pAnTIpq8x7DYADS%2Bi5DERY85%2BYqpmkc6x%2FWGf6beKCR3YBIZFZCxCgrtczjuOmo4qTf94F4KYuxhz5jjEhXmUJNexFrpIUo02ALN1j9u1JMgD%2FMga1GfbMNRd9iHUwGy%2BpspZF3IBSGvMFJluS%2FuR24FJ2KlV%2Fxju6sQq4lhRsQTUVUJTgegLtS6EUjEE1HPAmUC0KdAjwKJeCKqD8zoURx72gHyDW9nvQhJGHkyUscS1x%2BAZnAlqwU%2FI%2BKJKEvextXrf93eQrR1KUlS5HWwGC61mfOn0oN3IM4OHoBzuuIHj33hS5jT8KeamIYa0sjhgH%2BLfplP4kcwD5Xl3xR1wfeHtqWzBHHX8I9SH9Je%2FgGvXxeungIAAA%3D%3D&bn=Imported%20Federal%20Corvette');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('imports a valid v4 build', function() {
|
it('imports a valid companion API build', function() {
|
||||||
const importData = require('./fixtures/companion-api-import-2');
|
const importData = require('./fixtures/companion-api-import-2');
|
||||||
pasteText(JSON.stringify(importData));
|
pasteText(JSON.stringify(importData));
|
||||||
|
|
||||||
@@ -252,6 +252,18 @@ describe('Import Modal', function() {
|
|||||||
expect(MockRouter.go.mock.calls.length).toBe(1);
|
expect(MockRouter.go.mock.calls.length).toBe(1);
|
||||||
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/beluga?code=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');
|
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');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('imports a valid companion API build', function() {
|
||||||
|
const importData = require('./fixtures/companion-api-import-3');
|
||||||
|
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/type_7_transport?code=A0patfFflidasdf5----0404040005050504044d2402.AwRj4yrI.CwRgDBlVK7EiA%3D%3D%3D.&bn=Imported%20Type-7%20Transporter');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Import E:D Shipyard Builds', function() {
|
describe('Import E:D Shipyard Builds', function() {
|
||||||
|
|||||||
11
d3-funcs.js
vendored
Normal file
11
d3-funcs.js
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
export {
|
||||||
|
axisBottom,
|
||||||
|
axisLeft,
|
||||||
|
axisTop,
|
||||||
|
formatLocale,
|
||||||
|
line,
|
||||||
|
scaleBand,
|
||||||
|
scaleLinear,
|
||||||
|
scaleOrdinal,
|
||||||
|
select
|
||||||
|
} from 'd3';
|
||||||
4
d3.min.js
vendored
Normal file
4
d3.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
20
package.json
20
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "coriolis_shipyard",
|
"name": "coriolis_shipyard",
|
||||||
"version": "2.2.14",
|
"version": "2.2.19",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/EDCD/coriolis"
|
"url": "https://github.com/EDCD/coriolis"
|
||||||
@@ -11,6 +11,7 @@
|
|||||||
"engine": "node >= 4.0.0",
|
"engine": "node >= 4.0.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"prepublish": "rollup -c && uglifyjs d3.js -c -m -o d3.min.js",
|
||||||
"extract-translations": "grep -hroE \"(translate\\('[^']+'\\))|(tip.bind\\(null, '[^']+')\" src/* | grep -oE \"'[^']+'\" | grep -oE \"[^']+\" | sort -u -f",
|
"extract-translations": "grep -hroE \"(translate\\('[^']+'\\))|(tip.bind\\(null, '[^']+')\" src/* | grep -oE \"'[^']+'\" | grep -oE \"[^']+\" | sort -u -f",
|
||||||
"clean": "rimraf build",
|
"clean": "rimraf build",
|
||||||
"start": "node devServer.js",
|
"start": "node devServer.js",
|
||||||
@@ -18,7 +19,7 @@
|
|||||||
"test": "jest",
|
"test": "jest",
|
||||||
"prod-serve": "nginx -p $(pwd) -c nginx.conf",
|
"prod-serve": "nginx -p $(pwd) -c nginx.conf",
|
||||||
"prod-stop": "kill -QUIT $(cat nginx.pid)",
|
"prod-stop": "kill -QUIT $(cat nginx.pid)",
|
||||||
"build": "npm run clean && NODE_ENV=production webpack -d -p --config webpack.config.prod.js",
|
"build": "npm run clean && NODE_ENV=production webpack -p --config webpack.config.prod.js",
|
||||||
"rsync": "rsync -ae \"ssh -i $CORIOLIS_PEM\" --delete build/ $CORIOLIS_USER@$CORIOLIS_HOST:~/wwws",
|
"rsync": "rsync -ae \"ssh -i $CORIOLIS_PEM\" --delete build/ $CORIOLIS_USER@$CORIOLIS_HOST:~/wwws",
|
||||||
"deploy": "npm run lint && npm test && npm run build && npm run rsync"
|
"deploy": "npm run lint && npm test && npm run build && npm run rsync"
|
||||||
},
|
},
|
||||||
@@ -53,7 +54,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"appcache-webpack-plugin": "^1.2.1",
|
"appcache-webpack-plugin": "^1.3.0",
|
||||||
"babel-core": "*",
|
"babel-core": "*",
|
||||||
"babel-eslint": "*",
|
"babel-eslint": "*",
|
||||||
"babel-jest": "*",
|
"babel-jest": "*",
|
||||||
@@ -62,13 +63,14 @@
|
|||||||
"babel-preset-react": "*",
|
"babel-preset-react": "*",
|
||||||
"babel-preset-stage-0": "*",
|
"babel-preset-stage-0": "*",
|
||||||
"css-loader": "^0.23.0",
|
"css-loader": "^0.23.0",
|
||||||
|
"d3-selection": "1",
|
||||||
"eslint": "2.2.0",
|
"eslint": "2.2.0",
|
||||||
"eslint-plugin-react": "^4.0.0",
|
"eslint-plugin-react": "^4.0.0",
|
||||||
"expose-loader": "^0.7.1",
|
"expose-loader": "^0.7.1",
|
||||||
"express": "^4.13.3",
|
"express": "^4.13.3",
|
||||||
"extract-text-webpack-plugin": "^0.9.1",
|
"extract-text-webpack-plugin": "2.0.0",
|
||||||
"file-loader": "^0.8.4",
|
"file-loader": "^0.8.4",
|
||||||
"html-webpack-plugin": "^1.7.0",
|
"html-webpack-plugin": "^2.28.0",
|
||||||
"jest-cli": "^16.0.1",
|
"jest-cli": "^16.0.1",
|
||||||
"jsen": "^0.6.0",
|
"jsen": "^0.6.0",
|
||||||
"json-loader": "^0.5.3",
|
"json-loader": "^0.5.3",
|
||||||
@@ -77,17 +79,19 @@
|
|||||||
"react-addons-test-utils": "^15.0.1",
|
"react-addons-test-utils": "^15.0.1",
|
||||||
"react-testutils-additions": "^15.1.0",
|
"react-testutils-additions": "^15.1.0",
|
||||||
"rimraf": "^2.4.3",
|
"rimraf": "^2.4.3",
|
||||||
|
"rollup": "0.36",
|
||||||
|
"rollup-plugin-node-resolve": "2",
|
||||||
"style-loader": "^0.13.0",
|
"style-loader": "^0.13.0",
|
||||||
"url-loader": "^0.5.6",
|
"url-loader": "^0.5.6",
|
||||||
"webpack": "^1.9.6",
|
"webpack": "^2.2.1",
|
||||||
"webpack-dev-server": "^1.14.0"
|
"webpack-dev-server": "^2.4.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"babel-polyfill": "*",
|
"babel-polyfill": "*",
|
||||||
"classnames": "^2.2.0",
|
"classnames": "^2.2.0",
|
||||||
"browserify-zlib": "ipfs/browserify-zlib",
|
"browserify-zlib": "ipfs/browserify-zlib",
|
||||||
"coriolis-data": "EDCD/coriolis-data",
|
"coriolis-data": "EDCD/coriolis-data",
|
||||||
"d3": "3.5.16",
|
"d3": "4.6.0",
|
||||||
"fbemitter": "^2.0.0",
|
"fbemitter": "^2.0.0",
|
||||||
"lodash": "^4.15.0",
|
"lodash": "^4.15.0",
|
||||||
"lz-string": "^1.4.4",
|
"lz-string": "^1.4.4",
|
||||||
|
|||||||
9
rollup.config.js
Normal file
9
rollup.config.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import nodeResolve from "rollup-plugin-node-resolve";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
entry: "d3-funcs.js",
|
||||||
|
format: "umd",
|
||||||
|
moduleName: "d3",
|
||||||
|
plugins: [nodeResolve({jsnext: true})],
|
||||||
|
dest: "d3.js"
|
||||||
|
};
|
||||||
@@ -11,7 +11,6 @@ import ModalHelp from './components/ModalHelp';
|
|||||||
import ModalImport from './components/ModalImport';
|
import ModalImport from './components/ModalImport';
|
||||||
import ModalPermalink from './components/ModalPermalink';
|
import ModalPermalink from './components/ModalPermalink';
|
||||||
import * as CompanionApiUtils from './utils/CompanionApiUtils';
|
import * as CompanionApiUtils from './utils/CompanionApiUtils';
|
||||||
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';
|
||||||
|
|||||||
@@ -76,7 +76,16 @@ Router.go = function(path, state) {
|
|||||||
if (isStandAlone()) {
|
if (isStandAlone()) {
|
||||||
Persist.setState(ctx);
|
Persist.setState(ctx);
|
||||||
}
|
}
|
||||||
history.pushState(ctx.state, ctx.title, ctx.canonicalPath);
|
try {
|
||||||
|
history.pushState(ctx.state, ctx.title, ctx.canonicalPath);
|
||||||
|
} catch (ex) {
|
||||||
|
sessionStorage.setItem('__safari_history_fix', JSON.stringify({
|
||||||
|
state: ctx.state,
|
||||||
|
title: ctx.title,
|
||||||
|
path: ctx.canonicalPath
|
||||||
|
}));
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return ctx;
|
return ctx;
|
||||||
};
|
};
|
||||||
@@ -99,7 +108,16 @@ Router.replace = function(path, state, dispatch) {
|
|||||||
if (isStandAlone()) {
|
if (isStandAlone()) {
|
||||||
Persist.setState(ctx);
|
Persist.setState(ctx);
|
||||||
}
|
}
|
||||||
history.replaceState(ctx.state, ctx.title, ctx.canonicalPath);
|
try {
|
||||||
|
history.replaceState(ctx.state, ctx.title, ctx.canonicalPath);
|
||||||
|
} catch (ex) {
|
||||||
|
sessionStorage.setItem('__safari_history_fix', JSON.stringify({
|
||||||
|
state: ctx.state,
|
||||||
|
title: ctx.title,
|
||||||
|
path: ctx.canonicalPath
|
||||||
|
}));
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
return ctx;
|
return ctx;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,74 @@ import { MountFixed, MountGimballed, MountTurret } from './SvgIcons';
|
|||||||
|
|
||||||
const PRESS_THRESHOLD = 500; // mouse/touch down threshold
|
const PRESS_THRESHOLD = 500; // mouse/touch down threshold
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Categorisation of module groups
|
||||||
|
*/
|
||||||
|
const GRPCAT = {
|
||||||
|
'sg': 'shields',
|
||||||
|
'bsg': 'shields',
|
||||||
|
'psg': 'shields',
|
||||||
|
'scb': 'shields',
|
||||||
|
'cc': 'limpet controllers',
|
||||||
|
'fx': 'limpet controllers',
|
||||||
|
'hb': 'limpet controllers',
|
||||||
|
'pc': 'limpet controllers',
|
||||||
|
'pce': 'passenger cabins',
|
||||||
|
'pci': 'passenger cabins',
|
||||||
|
'pcm': 'passenger cabins',
|
||||||
|
'pcq': 'passenger cabins',
|
||||||
|
'fh': 'hangars',
|
||||||
|
'pv': 'hangars',
|
||||||
|
'fs': 'fuel',
|
||||||
|
'ft': 'fuel',
|
||||||
|
'hr': 'structural reinforcement',
|
||||||
|
'mrp': 'structural reinforcement',
|
||||||
|
'bl': 'lasers',
|
||||||
|
'pl': 'lasers',
|
||||||
|
'ul': 'lasers',
|
||||||
|
'ml': 'lasers',
|
||||||
|
'c': 'projectiles',
|
||||||
|
'mc': 'projectiles',
|
||||||
|
'fc': 'projectiles',
|
||||||
|
'pa': 'projectiles',
|
||||||
|
'rg': 'projectiles',
|
||||||
|
'mr': 'ordnance',
|
||||||
|
'tp': 'ordnance',
|
||||||
|
'nl': 'ordnance',
|
||||||
|
// Utilities
|
||||||
|
'cs': 'scanners',
|
||||||
|
'kw': 'scanners',
|
||||||
|
'ws': 'scanners',
|
||||||
|
'ch': 'defence',
|
||||||
|
'po': 'defence',
|
||||||
|
'ec': 'defence',
|
||||||
|
};
|
||||||
|
// Order here is the order in which items will be shown in the modules menu
|
||||||
|
const CATEGORIES = {
|
||||||
|
// Internals
|
||||||
|
'am': ['am'],
|
||||||
|
'cr': ['cr'],
|
||||||
|
'fi': ['fi'],
|
||||||
|
'fuel': ['ft', 'fs'],
|
||||||
|
'hangars': ['fh', 'pv'],
|
||||||
|
'limpet controllers': ['cc', 'fx', 'hb', 'pc'],
|
||||||
|
'passenger cabins': ['pce', 'pci', 'pcm', 'pcq'],
|
||||||
|
'rf': ['rf'],
|
||||||
|
'sc': ['sc'],
|
||||||
|
'shields': ['sg', 'bsg', 'psg', 'scb'],
|
||||||
|
'structural reinforcement': ['hr', 'mrp'],
|
||||||
|
'dc': ['dc'],
|
||||||
|
// Hardpoints
|
||||||
|
'lasers': ['pl', 'ul', 'bl', 'ml'],
|
||||||
|
'projectiles': ['mc', 'c', 'fc', 'pa', 'rg'],
|
||||||
|
'ordnance': ['mr', 'tp', 'nl'],
|
||||||
|
// Utilities
|
||||||
|
'sb': ['sb'],
|
||||||
|
'hs': ['hs'],
|
||||||
|
'defence': ['ch', 'po', 'ec'],
|
||||||
|
'scanners': ['cs', 'kw', 'ws'],
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Available modules menu
|
* Available modules menu
|
||||||
*/
|
*/
|
||||||
@@ -63,15 +131,55 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
|||||||
} else {
|
} else {
|
||||||
list = [];
|
list = [];
|
||||||
// At present time slots with grouped options (Hardpoints and Internal) can be empty
|
// At present time slots with grouped options (Hardpoints and Internal) can be empty
|
||||||
list.push(<div className='empty-c upp' key='empty' onClick={onSelect.bind(null, null)} >{translate('empty')}</div>);
|
if (m) {
|
||||||
for (let g in modules) {
|
list.push(<div className='empty-c upp' key='empty' onClick={onSelect.bind(null, null)} >{translate('empty')}</div>);
|
||||||
if (m && g == m.grp) {
|
}
|
||||||
list.push(<div ref={(elem) => this.groupElem = elem} key={g} className={'select-group cap'}>{translate(g)}</div>);
|
|
||||||
} else {
|
|
||||||
list.push(<div key={g} className={'select-group cap'}>{translate(g)}</div>);
|
|
||||||
}
|
|
||||||
|
|
||||||
list.push(buildGroup(g, modules[g]));
|
// Need to regroup the modules by our own categorisation
|
||||||
|
let catmodules = {};
|
||||||
|
// Pre-create to preserve ordering
|
||||||
|
for (let cat in CATEGORIES) {
|
||||||
|
catmodules[cat] = [];
|
||||||
|
}
|
||||||
|
for (let g in modules) {
|
||||||
|
const moduleCategory = GRPCAT[g] || g;
|
||||||
|
const existing = catmodules[moduleCategory] || [];
|
||||||
|
catmodules[moduleCategory] = existing.concat(modules[g]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let category in catmodules) {
|
||||||
|
let categoryHeader = false;
|
||||||
|
// Order through CATEGORIES if present
|
||||||
|
const categories = CATEGORIES[category] || [category];
|
||||||
|
if (categories && categories.length) {
|
||||||
|
for (let n in categories) {
|
||||||
|
const grp = categories[n];
|
||||||
|
// We now have the group and the category. We might not have any modules, though...
|
||||||
|
if (modules[grp]) {
|
||||||
|
// Decide if we need a category header as well as a group header
|
||||||
|
if (categories.length === 1) {
|
||||||
|
// Show category header instead of group header
|
||||||
|
if (m && grp == m.grp) {
|
||||||
|
list.push(<div ref={(elem) => this.groupElem = elem} key={category} className={'select-category upp'}>{translate(category)}</div>);
|
||||||
|
} else {
|
||||||
|
list.push(<div key={category} className={'select-category upp'}>{translate(category)}</div>);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Show category header as well as group header
|
||||||
|
if (!categoryHeader) {
|
||||||
|
list.push(<div key={category} className={'select-category upp'}>{translate(category)}</div>);
|
||||||
|
categoryHeader = true;
|
||||||
|
}
|
||||||
|
if (m && grp == m.grp) {
|
||||||
|
list.push(<div ref={(elem) => this.groupElem = elem} key={grp} className={'select-group cap'}>{translate(grp)}</div>);
|
||||||
|
} else {
|
||||||
|
list.push(<div key={grp} className={'select-group cap'}>{translate(grp)}</div>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
list.push(buildGroup(grp, modules[grp]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,6 +203,12 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
|||||||
|
|
||||||
const sortedModules = modules.sort(this._moduleOrder);
|
const sortedModules = modules.sort(this._moduleOrder);
|
||||||
|
|
||||||
|
// Calculate the number of items per class. Used so we don't have long lists with only a few items in each row
|
||||||
|
const tmp = sortedModules.map((v, i) => v['class']).reduce((count, cls) => { count[cls] = ++count[cls] || 1; return count; }, {});
|
||||||
|
const itemsPerClass = Math.max.apply(null, Object.keys(tmp).map(key => tmp[key]));
|
||||||
|
|
||||||
|
let itemsOnThisRow = 0;
|
||||||
|
|
||||||
for (let i = 0; i < sortedModules.length; i++) {
|
for (let i = 0; i < sortedModules.length; i++) {
|
||||||
let m = sortedModules[i];
|
let m = sortedModules[i];
|
||||||
let mount = null;
|
let mount = null;
|
||||||
@@ -128,8 +242,9 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
|||||||
case 'T': mount = <MountTurret className={'lg'}/>; break;
|
case 'T': mount = <MountTurret className={'lg'}/>; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i > 0 && sortedModules.length > 3 && m.class != prevClass && (m.rating != prevRating || m.mount) && m.grp != 'pa') {
|
if (itemsOnThisRow == 6 || i > 0 && sortedModules.length > 3 && itemsPerClass > 2 && m.class != prevClass && (m.rating != prevRating || m.mount)) {
|
||||||
elems.push(<br key={'b' + m.grp + i} />);
|
elems.push(<br key={'b' + m.grp + i} />);
|
||||||
|
itemsOnThisRow = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
elems.push(
|
elems.push(
|
||||||
@@ -138,6 +253,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
|||||||
{(mount ? ' ' : '') + m.class + m.rating + (m.missile ? '/' + m.missile : '') + (m.name ? ' ' + translate(m.name) : '')}
|
{(mount ? ' ' : '') + m.class + m.rating + (m.missile ? '/' + m.missile : '') + (m.name ? ' ' + translate(m.name) : '')}
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
|
itemsOnThisRow++;
|
||||||
prevClass = m.class;
|
prevClass = m.class;
|
||||||
prevRating = m.rating;
|
prevRating = m.rating;
|
||||||
}
|
}
|
||||||
@@ -232,12 +348,12 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Rating ordered from lowest (E) to highest (A)
|
// Rating ordered from highest (A) to lowest (E)
|
||||||
if (a.rating < b.rating) {
|
if (a.rating < b.rating) {
|
||||||
return 1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (a.rating > b.rating) {
|
if (a.rating > b.rating) {
|
||||||
return -1;
|
return 1;
|
||||||
}
|
}
|
||||||
// Do not attempt to order by name at this point, as that mucks up the order of armour
|
// Do not attempt to order by name at this point, as that mucks up the order of armour
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import d3 from 'd3';
|
import * as d3 from 'd3';
|
||||||
import TranslatedComponent from './TranslatedComponent';
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
|
|
||||||
const MARGIN = { top: 15, right: 20, bottom: 40, left: 150 };
|
const MARGIN = { top: 15, right: 20, bottom: 40, left: 150 };
|
||||||
@@ -68,13 +68,13 @@ export default class BarChart extends TranslatedComponent {
|
|||||||
this._updateDimensions = this._updateDimensions.bind(this);
|
this._updateDimensions = this._updateDimensions.bind(this);
|
||||||
this._hideTip = this._hideTip.bind(this);
|
this._hideTip = this._hideTip.bind(this);
|
||||||
|
|
||||||
let scale = d3.scale.linear();
|
let scale = d3.scaleLinear();
|
||||||
let y0 = d3.scale.ordinal();
|
let y0 = d3.scaleBand();
|
||||||
let y1 = d3.scale.ordinal();
|
let y1 = d3.scaleBand();
|
||||||
|
|
||||||
this.xAxis = d3.svg.axis().scale(scale).ticks(5).outerTickSize(0).orient('bottom').tickFormat(context.language.formats.s2);
|
this.xAxis = d3.axisBottom(scale).ticks(5).tickSizeOuter(0).tickFormat(context.language.formats.s2);
|
||||||
this.yAxis = d3.svg.axis().scale(y0).outerTickSize(0).orient('left');
|
this.yAxis = d3.axisLeft(y0).tickSizeOuter(0);
|
||||||
this.state = { scale, y0, y1, color: d3.scale.ordinal().range(props.colors) };
|
this.state = { scale, y0, y1, color: d3.scaleOrdinal().range(props.colors) };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -131,8 +131,8 @@ export default class BarChart extends TranslatedComponent {
|
|||||||
let max = data.reduce((max, build) => (properties.reduce(((m, p) => (m > build[p] ? m : build[p])), max)), 0);
|
let max = data.reduce((max, build) => (properties.reduce(((m, p) => (m > build[p] ? m : build[p])), max)), 0);
|
||||||
|
|
||||||
this.state.scale.range([0, innerWidth]).domain([0, max]);
|
this.state.scale.range([0, innerWidth]).domain([0, max]);
|
||||||
this.state.y0.domain(data.map(bName)).rangeRoundBands([0, innerHeight], 0.3);
|
this.state.y0.domain(data.map(bName)).range([0, innerHeight], 0.3).padding(0.4);
|
||||||
this.state.y1.domain(properties).rangeRoundBands([0, this.state.y0.rangeBand()]);
|
this.state.y1.domain(properties).range([0, this.state.y0.bandwidth()]).padding(0.1);
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
barHeight,
|
barHeight,
|
||||||
@@ -192,7 +192,7 @@ export default class BarChart extends TranslatedComponent {
|
|||||||
x={0}
|
x={0}
|
||||||
y={y1(p)}
|
y={y1(p)}
|
||||||
width={scale(build[p])}
|
width={scale(build[p])}
|
||||||
height={y1.rangeBand()}
|
height={y1.bandwidth()}
|
||||||
fill={color(p)}
|
fill={color(p)}
|
||||||
onMouseOver={this._showTip.bind(this, build, p, propIndex)}
|
onMouseOver={this._showTip.bind(this, build, p, propIndex)}
|
||||||
onMouseOut={this._hideTip}
|
onMouseOut={this._hideTip}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import TranslatedComponent from './TranslatedComponent';
|
|||||||
import Link from './Link';
|
import Link from './Link';
|
||||||
import cn from 'classnames';
|
import cn from 'classnames';
|
||||||
import { outfitURL } from '../utils/UrlGenerators';
|
import { outfitURL } from '../utils/UrlGenerators';
|
||||||
import { SizeMap } from '../shipyard/Constants';
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -4,7 +4,12 @@ import { Ships } from 'coriolis-data/dist';
|
|||||||
import ShipSelector from './ShipSelector';
|
import ShipSelector from './ShipSelector';
|
||||||
import { nameComparator } from '../utils/SlotFunctions';
|
import { nameComparator } from '../utils/SlotFunctions';
|
||||||
import { CollapseSection, ExpandSection, MountFixed, MountGimballed, MountTurret } from './SvgIcons';
|
import { CollapseSection, ExpandSection, MountFixed, MountGimballed, MountTurret } from './SvgIcons';
|
||||||
|
import LineChart from '../components/LineChart';
|
||||||
import Slider from '../components/Slider';
|
import Slider from '../components/Slider';
|
||||||
|
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
||||||
|
import Module from '../shipyard/Module';
|
||||||
|
|
||||||
|
const DAMAGE_DEALT_COLORS = ['#FFFFFF', '#FF0000', '#00FF00', '#7777FF', '#FFFF00', '#FF00FF', '#00FFFF', '#777777'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates an internationalization friendly weapon comparator that will
|
* Generates an internationalization friendly weapon comparator that will
|
||||||
@@ -47,6 +52,7 @@ export function weaponComparator(translate, propComparator, desc) {
|
|||||||
export default class DamageDealt extends TranslatedComponent {
|
export default class DamageDealt extends TranslatedComponent {
|
||||||
static PropTypes = {
|
static PropTypes = {
|
||||||
ship: React.PropTypes.object.isRequired,
|
ship: React.PropTypes.object.isRequired,
|
||||||
|
chartWidth: React.PropTypes.number.isRequired,
|
||||||
code: React.PropTypes.string.isRequired
|
code: React.PropTypes.string.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -55,21 +61,33 @@ export default class DamageDealt extends TranslatedComponent {
|
|||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
* @param {Object} props React Component properties
|
* @param {Object} props React Component properties
|
||||||
|
* @param {Object} context React Component context
|
||||||
*/
|
*/
|
||||||
constructor(props) {
|
constructor(props, context) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this._sort = this._sort.bind(this);
|
this._sort = this._sort.bind(this);
|
||||||
this._onShipChange = this._onShipChange.bind(this);
|
this._onShipChange = this._onShipChange.bind(this);
|
||||||
this._onCollapseExpand = this._onCollapseExpand.bind(this);
|
this._onCollapseExpand = this._onCollapseExpand.bind(this);
|
||||||
|
|
||||||
|
const ship = this.props.ship;
|
||||||
|
const against = DamageDealt.DEFAULT_AGAINST;
|
||||||
|
const maxRange = this._calcMaxRange(ship);
|
||||||
|
const range = 1000 / maxRange;
|
||||||
|
const maxDps = this._calcMaxSDps(ship, against);
|
||||||
|
const weaponNames = this._weaponNames(ship, context);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
predicate: 'n',
|
predicate: 'n',
|
||||||
desc: true,
|
desc: true,
|
||||||
against: DamageDealt.DEFAULT_AGAINST,
|
against,
|
||||||
expanded: false,
|
expanded: false,
|
||||||
range: 0.1667,
|
range,
|
||||||
maxRange: 6000
|
maxRange,
|
||||||
|
maxDps,
|
||||||
|
weaponNames,
|
||||||
|
calcHullDpsFunc: this._calcDps.bind(this, context, ship, weaponNames, against, true),
|
||||||
|
calcShieldsDpsFunc: this._calcDps.bind(this, context, ship, weaponNames, against, false)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,7 +95,7 @@ export default class DamageDealt extends TranslatedComponent {
|
|||||||
* Set the initial weapons state
|
* Set the initial weapons state
|
||||||
*/
|
*/
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
const data = this._calcWeapons(this.props.ship, this.state.against, this.state.range * this.state.maxRange);
|
const data = this._calcWeaponsDps(this.props.ship, this.state.against, this.state.range * this.state.maxRange, true);
|
||||||
this.setState({ weapons: data.weapons, totals: data.totals });
|
this.setState({ weapons: data.weapons, totals: data.totals });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,20 +107,165 @@ export default class DamageDealt extends TranslatedComponent {
|
|||||||
*/
|
*/
|
||||||
componentWillReceiveProps(nextProps, nextContext) {
|
componentWillReceiveProps(nextProps, nextContext) {
|
||||||
if (nextProps.code != this.props.code) {
|
if (nextProps.code != this.props.code) {
|
||||||
const data = this._calcWeapons(this.props.ship, this.state.against, this.state.range * this.state.maxRange);
|
const data = this._calcWeaponsDps(nextProps.ship, this.state.against, this.state.range * this.state.maxRange, this.props.hull);
|
||||||
this.setState({ weapons: data.weapons, totals: data.totals });
|
const weaponNames = this._weaponNames(nextProps.ship, nextContext);
|
||||||
|
const maxRange = this._calcMaxRange(nextProps.ship);
|
||||||
|
const maxDps = this._calcMaxSDps(nextProps.ship, this.state.against);
|
||||||
|
this.setState({ weapons: data.weapons,
|
||||||
|
totals: data.totals,
|
||||||
|
weaponNames,
|
||||||
|
maxRange,
|
||||||
|
maxDps,
|
||||||
|
calcHullDpsFunc: this._calcDps.bind(this, nextContext, nextProps.ship, weaponNames, this.state.against, true),
|
||||||
|
calcShieldsDpsFunc: this._calcDps.bind(this, nextContext, nextProps.ship, weaponNames, this.state.against, false) });
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the maximum sustained single-weapon DPS for this ship against another ship
|
||||||
|
* @param {Object} ship The ship
|
||||||
|
* @param {Object} against The target
|
||||||
|
* @return {number} The maximum sustained single-weapon DPS
|
||||||
|
*/
|
||||||
|
_calcMaxSDps(ship, against) {
|
||||||
|
let maxSDps = 0;
|
||||||
|
for (let i = 0; i < ship.hardpoints.length; i++) {
|
||||||
|
if (ship.hardpoints[i].m && ship.hardpoints[i].enabled) {
|
||||||
|
const m = ship.hardpoints[i].m;
|
||||||
|
const thisSDps = m.getClip() ? (m.getClip() * m.getDps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload()) : m.getDps();
|
||||||
|
if (thisSDps > maxSDps) {
|
||||||
|
maxSDps = thisSDps;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return maxSDps;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the per-weapon DPS for this ship against another ship at a given range
|
||||||
|
* @param {Object} context The context
|
||||||
|
* @param {Object} ship The ship
|
||||||
|
* @param {Object} weaponNames The names of the weapons for which to calculate DPS
|
||||||
|
* @param {Object} against The target
|
||||||
|
* @param {bool} hull true if to calculate against hull, false if to calculate against shields
|
||||||
|
* @param {Object} range The engagement range
|
||||||
|
* @return {array} The array of weapon DPS
|
||||||
|
*/
|
||||||
|
_calcDps(context, ship, weaponNames, against, hull, range) {
|
||||||
|
let results = {};
|
||||||
|
let weaponNum = 0;
|
||||||
|
for (let i = 0; i < ship.hardpoints.length; i++) {
|
||||||
|
if (ship.hardpoints[i].m && ship.hardpoints[i].enabled) {
|
||||||
|
const m = ship.hardpoints[i].m;
|
||||||
|
results[weaponNames[weaponNum++]] = this._calcWeaponDps(context, m, against, hull, range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the maximum range of a ship's weapons
|
||||||
|
* @param {Object} ship The ship
|
||||||
|
* @returns {int} The maximum range, in metres
|
||||||
|
*/
|
||||||
|
_calcMaxRange(ship) {
|
||||||
|
let maxRange = 1000;
|
||||||
|
for (let i = 0; i < ship.hardpoints.length; i++) {
|
||||||
|
if (ship.hardpoints[i].maxClass > 0 && ship.hardpoints[i].m && ship.hardpoints[i].enabled) {
|
||||||
|
const thisRange = ship.hardpoints[i].m.getRange();
|
||||||
|
if (thisRange > maxRange) {
|
||||||
|
maxRange = thisRange;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain the weapon names for this ship
|
||||||
|
* @param {Object} ship The ship
|
||||||
|
* @param {Object} context The context
|
||||||
|
* @return {array} The weapon names
|
||||||
|
*/
|
||||||
|
_weaponNames(ship, context) {
|
||||||
|
const translate = context.language.translate;
|
||||||
|
let names = [];
|
||||||
|
let num = 1;
|
||||||
|
for (let i = 0; i < ship.hardpoints.length; i++) {
|
||||||
|
if (ship.hardpoints[i].maxClass > 0 && ship.hardpoints[i].m && ship.hardpoints[i].enabled) {
|
||||||
|
const m = ship.hardpoints[i].m;
|
||||||
|
let name = '' + num++ + ': ' + m.class + m.rating + (m.missile ? '/' + m.missile : '') + ' ' + translate(m.name || m.grp);
|
||||||
|
let engineering;
|
||||||
|
if (m.blueprint && m.blueprint.name) {
|
||||||
|
engineering = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
|
||||||
|
if (m.blueprint.special && m.blueprint.special.id) {
|
||||||
|
engineering += ', ' + translate(m.blueprint.special.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (engineering) {
|
||||||
|
name = name + ' (' + engineering + ')';
|
||||||
|
}
|
||||||
|
names.push(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate a specific weapon DPS for this ship against another ship at a given range
|
||||||
|
* @param {Object} context The context
|
||||||
|
* @param {Object} m The weapon
|
||||||
|
* @param {Object} against The target
|
||||||
|
* @param {bool} hull true if to calculate against hull, false if to calculate against shields
|
||||||
|
* @param {Object} range The engagement range
|
||||||
|
* @return {number} The weapon DPS
|
||||||
|
*/
|
||||||
|
_calcWeaponDps(context, m, against, hull, range) {
|
||||||
|
const translate = context.language.translate;
|
||||||
|
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 : ''}`;
|
||||||
|
let engineering;
|
||||||
|
if (m.blueprint && m.blueprint.name) {
|
||||||
|
engineering = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
|
||||||
|
if (m.blueprint.special && m.blueprint.special.id) {
|
||||||
|
engineering += ', ' + translate(m.blueprint.special.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const effectivenessShields = dropoff;
|
||||||
|
const effectiveDpsShields = m.getDps() * effectivenessShields;
|
||||||
|
const effectiveSDpsShields = (m.getClip() ? (m.getClip() * m.getDps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload()) * effectivenessShields : effectiveDpsShields);
|
||||||
|
const effectivenessHull = (m.getPiercing() >= against.properties.hardness ? 1 : m.getPiercing() / against.properties.hardness) * dropoff;
|
||||||
|
const effectiveDpsHull = m.getDps() * effectivenessHull;
|
||||||
|
const effectiveSDpsHull = (m.getClip() ? (m.getClip() * m.getDps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload()) * effectivenessHull : effectiveDpsHull);
|
||||||
|
|
||||||
|
return hull ? effectiveSDpsHull : effectiveSDpsShields;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate the damage dealt by a ship
|
* Calculate the damage dealt by a ship
|
||||||
* @param {Object} ship The ship which will deal the damage
|
* @param {Object} ship The ship which will deal the damage
|
||||||
* @param {Object} against The ship against which damage will be dealt
|
* @param {Object} against The ship against which damage will be dealt
|
||||||
* @param {Object} range The engagement range
|
* @param {Object} range The engagement range
|
||||||
* @return {boolean} Returns the per-weapon damage
|
* @return {object} Returns the per-weapon damage
|
||||||
*/
|
*/
|
||||||
_calcWeapons(ship, against, range) {
|
_calcWeaponsDps(ship, against, range) {
|
||||||
const translate = this.context.language.translate;
|
const translate = this.context.language.translate;
|
||||||
|
|
||||||
// Tidy up the range so that it's to 4 decimal places
|
// Tidy up the range so that it's to 4 decimal places
|
||||||
@@ -120,7 +283,7 @@ export default class DamageDealt extends TranslatedComponent {
|
|||||||
|
|
||||||
let weapons = [];
|
let weapons = [];
|
||||||
for (let i = 0; i < ship.hardpoints.length; i++) {
|
for (let i = 0; i < ship.hardpoints.length; i++) {
|
||||||
if (ship.hardpoints[i].m && ship.hardpoints[i].enabled) {
|
if (ship.hardpoints[i].maxClass > 0 && ship.hardpoints[i].m && ship.hardpoints[i].enabled) {
|
||||||
const m = ship.hardpoints[i].m;
|
const m = ship.hardpoints[i].m;
|
||||||
if (m.getDamage() && m.grp !== 'po') {
|
if (m.getDamage() && m.grp !== 'po') {
|
||||||
let dropoff = 1;
|
let dropoff = 1;
|
||||||
@@ -146,10 +309,42 @@ export default class DamageDealt extends TranslatedComponent {
|
|||||||
engineering += ', ' + translate(m.blueprint.special.name);
|
engineering += ', ' + translate(m.blueprint.special.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const effectivenessShields = dropoff;
|
|
||||||
|
// Alter effectiveness as per standard shields (all have the same resistances)
|
||||||
|
const sg = ModuleUtils.findModule('sg', '3v');
|
||||||
|
let effectivenessShields = 0;
|
||||||
|
if (m.getDamageDist().E) {
|
||||||
|
effectivenessShields += m.getDamageDist().E * (1 - sg.getExplosiveResistance());
|
||||||
|
}
|
||||||
|
if (m.getDamageDist().K) {
|
||||||
|
effectivenessShields += m.getDamageDist().K * (1 - sg.getKineticResistance());
|
||||||
|
}
|
||||||
|
if (m.getDamageDist().T) {
|
||||||
|
effectivenessShields += m.getDamageDist().T * (1 - sg.getThermalResistance());
|
||||||
|
}
|
||||||
|
if (m.getDamageDist().A) {
|
||||||
|
effectivenessShields += m.getDamageDist().A;
|
||||||
|
}
|
||||||
|
effectivenessShields *= dropoff;
|
||||||
const effectiveDpsShields = m.getDps() * effectivenessShields;
|
const effectiveDpsShields = m.getDps() * effectivenessShields;
|
||||||
const effectiveSDpsShields = (m.getClip() ? (m.getClip() * m.getDps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload()) * effectivenessShields : effectiveDpsShields);
|
const effectiveSDpsShields = (m.getClip() ? (m.getClip() * m.getDps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload()) * effectivenessShields : effectiveDpsShields);
|
||||||
const effectivenessHull = (m.getPiercing() >= against.properties.hardness ? 1 : m.getPiercing() / against.properties.hardness) * dropoff;
|
|
||||||
|
// Alter effectiveness as per standard hull
|
||||||
|
const bulkheads = new Module({ template: against.bulkheads });
|
||||||
|
let effectivenessHull = 0;
|
||||||
|
if (m.getDamageDist().E) {
|
||||||
|
effectivenessHull += m.getDamageDist().E * (1 - bulkheads.getExplosiveResistance());
|
||||||
|
}
|
||||||
|
if (m.getDamageDist().K) {
|
||||||
|
effectivenessHull += m.getDamageDist().K * (1 - bulkheads.getKineticResistance());
|
||||||
|
}
|
||||||
|
if (m.getDamageDist().T) {
|
||||||
|
effectivenessHull += m.getDamageDist().T * (1 - bulkheads.getThermalResistance());
|
||||||
|
}
|
||||||
|
if (m.getDamageDist().A) {
|
||||||
|
effectivenessHull += m.getDamageDist().A;
|
||||||
|
}
|
||||||
|
effectivenessHull *= Math.min(m.getPiercing() / against.properties.hardness, 1) * dropoff;
|
||||||
const effectiveDpsHull = m.getDps() * effectivenessHull;
|
const effectiveDpsHull = m.getDps() * effectivenessHull;
|
||||||
const effectiveSDpsHull = (m.getClip() ? (m.getClip() * m.getDps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload()) * effectivenessHull : effectiveDpsHull);
|
const effectiveSDpsHull = (m.getClip() ? (m.getClip() * m.getDps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload()) * effectivenessHull : effectiveDpsHull);
|
||||||
totals.effectiveDpsShields += effectiveDpsShields;
|
totals.effectiveDpsShields += effectiveDpsShields;
|
||||||
@@ -158,7 +353,6 @@ export default class DamageDealt extends TranslatedComponent {
|
|||||||
totals.effectiveSDpsHull += effectiveSDpsHull;
|
totals.effectiveSDpsHull += effectiveSDpsHull;
|
||||||
totalDps += m.getDps();
|
totalDps += m.getDps();
|
||||||
|
|
||||||
|
|
||||||
weapons.push({ id: i,
|
weapons.push({ id: i,
|
||||||
mount: m.mount,
|
mount: m.mount,
|
||||||
name: m.name || m.grp,
|
name: m.name || m.grp,
|
||||||
@@ -192,8 +386,12 @@ export default class DamageDealt extends TranslatedComponent {
|
|||||||
*/
|
*/
|
||||||
_onShipChange(s) {
|
_onShipChange(s) {
|
||||||
const against = Ships[s];
|
const against = Ships[s];
|
||||||
const data = this._calcWeapons(this.props.ship, against, this.state.range * this.state.maxRange);
|
const data = this._calcWeaponsDps(this.props.ship, against, this.state.range * this.state.maxRange);
|
||||||
this.setState({ against, weapons: data.weapons, totals: data.totals });
|
this.setState({ against,
|
||||||
|
weapons: data.weapons,
|
||||||
|
totals: data.totals,
|
||||||
|
calcHullDpsFunc: this._calcDps.bind(this, this.context, this.props.ship, this.state.weaponNames, against, true),
|
||||||
|
calcShieldsDpsFunc: this._calcDps.bind(this, this.context, this.props.ship, this.state.weaponNames, against, false) });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -259,11 +457,11 @@ export default class DamageDealt extends TranslatedComponent {
|
|||||||
{weapon.classRating} {translate(weapon.name)}
|
{weapon.classRating} {translate(weapon.name)}
|
||||||
{weapon.engineering ? ' (' + weapon.engineering + ')' : null }
|
{weapon.engineering ? ' (' + weapon.engineering + ')' : null }
|
||||||
</td>
|
</td>
|
||||||
<td className='ri'>{formats.round1(weapon.effectiveDpsShields)}</td>
|
<td className='ri'>{formats.f1(weapon.effectiveDpsShields)}</td>
|
||||||
<td className='ri'>{formats.round1(weapon.effectiveSDpsShields)}</td>
|
<td className='ri'>{formats.f1(weapon.effectiveSDpsShields)}</td>
|
||||||
<td className='ri'>{formats.pct(weapon.effectivenessShields)}</td>
|
<td className='ri'>{formats.pct(weapon.effectivenessShields)}</td>
|
||||||
<td className='ri'>{formats.round1(weapon.effectiveDpsHull)}</td>
|
<td className='ri'>{formats.f1(weapon.effectiveDpsHull)}</td>
|
||||||
<td className='ri'>{formats.round1(weapon.effectiveSDpsHull)}</td>
|
<td className='ri'>{formats.f1(weapon.effectiveSDpsHull)}</td>
|
||||||
<td className='ri'>{formats.pct(weapon.effectivenessHull)}</td>
|
<td className='ri'>{formats.pct(weapon.effectivenessHull)}</td>
|
||||||
</tr>);
|
</tr>);
|
||||||
}
|
}
|
||||||
@@ -277,8 +475,10 @@ export default class DamageDealt extends TranslatedComponent {
|
|||||||
* @param {number} range Range 0-1
|
* @param {number} range Range 0-1
|
||||||
*/
|
*/
|
||||||
_rangeChange(range) {
|
_rangeChange(range) {
|
||||||
const data = this._calcWeapons(this.props.ship, this.state.against, this.state.range * this.state.maxRange);
|
const data = this._calcWeaponsDps(this.props.ship, this.state.against, this.state.range * this.state.maxRange);
|
||||||
this.setState({ range, weapons: data.weapons, totals: data.totals });
|
this.setState({ range,
|
||||||
|
weapons: data.weapons,
|
||||||
|
totals: data.totals });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -288,22 +488,25 @@ export default class DamageDealt extends TranslatedComponent {
|
|||||||
render() {
|
render() {
|
||||||
const { language, onWindowResize, sizeRatio, tooltip, termtip } = this.context;
|
const { language, onWindowResize, sizeRatio, tooltip, termtip } = this.context;
|
||||||
const { formats, translate, units } = language;
|
const { formats, translate, units } = language;
|
||||||
const { expanded, maxRange, range, totals } = this.state;
|
const { against, expanded, maxRange, range, totals } = this.state;
|
||||||
|
const { ship } = this.props;
|
||||||
|
|
||||||
const sortOrder = this._sortOrder;
|
const sortOrder = this._sortOrder;
|
||||||
const onCollapseExpand = this._onCollapseExpand;
|
const onCollapseExpand = this._onCollapseExpand;
|
||||||
|
|
||||||
|
const code = ship.getHardpointsString() + '.' + ship.getModificationsString() + '.' + ship.getPowerEnabledString() + '.' + against.properties.name;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
<h1>{translate('damage dealt against')} {expanded ? <span onClick={onCollapseExpand}><CollapseSection className='summary'/></span> : <span onClick={onCollapseExpand}><ExpandSection className='summary'/></span>}</h1>
|
<h1>{translate('damage dealt to')} {expanded ? <span onClick={onCollapseExpand}><CollapseSection className='summary'/></span> : <span onClick={onCollapseExpand}><ExpandSection className='summary'/></span>}</h1>
|
||||||
{expanded ? <span>
|
{expanded ? <span>
|
||||||
<ShipSelector initial={this.state.against} currentMenu={this.props.currentMenu} onChange={this._onShipChange} />
|
<ShipSelector initial={against} currentMenu={this.props.currentMenu} onChange={this._onShipChange} />
|
||||||
<table className='summary' style={{ width: '100%' }}>
|
<table className='summary' style={{ width: '100%' }}>
|
||||||
<thead>
|
<thead>
|
||||||
<tr className='main'>
|
<tr className='main'>
|
||||||
<th rowSpan='2' className='sortable' onClick={sortOrder.bind(this, 'n')}>{translate('weapon')}</th>
|
<th rowSpan='2' className='sortable' onClick={sortOrder.bind(this, 'n')}>{translate('weapon')}</th>
|
||||||
<th colSpan='3'>{translate('shields')}</th>
|
<th colSpan='3'>{translate('standard shields')}</th>
|
||||||
<th colSpan='3'>{translate('armour')}</th>
|
<th colSpan='3'>{translate('standard armour')}</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th className='lft sortable' onClick={sortOrder.bind(this, 'edpss')}>{translate('effective dps')}</th>
|
<th className='lft sortable' onClick={sortOrder.bind(this, 'edpss')}>{translate('effective dps')}</th>
|
||||||
@@ -320,11 +523,11 @@ export default class DamageDealt extends TranslatedComponent {
|
|||||||
<tfoot>
|
<tfoot>
|
||||||
<tr className='main'>
|
<tr className='main'>
|
||||||
<td className='ri'><i>{translate('total')}</i></td>
|
<td className='ri'><i>{translate('total')}</i></td>
|
||||||
<td className='ri'><i>{formats.round1(totals.effectiveDpsShields)}</i></td>
|
<td className='ri'><i>{formats.f1(totals.effectiveDpsShields)}</i></td>
|
||||||
<td className='ri'><i>{formats.round1(totals.effectiveSDpsShields)}</i></td>
|
<td className='ri'><i>{formats.f1(totals.effectiveSDpsShields)}</i></td>
|
||||||
<td className='ri'><i>{formats.pct(totals.effectivenessShields)}</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.f1(totals.effectiveDpsHull)}</i></td>
|
||||||
<td className='ri'><i>{formats.round1(totals.effectiveSDpsHull)}</i></td>
|
<td className='ri'><i>{formats.f1(totals.effectiveSDpsHull)}</i></td>
|
||||||
<td className='ri'><i>{formats.pct(totals.effectivenessHull)}</i></td>
|
<td className='ri'><i>{formats.pct(totals.effectivenessHull)}</i></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tfoot>
|
</tfoot>
|
||||||
@@ -349,7 +552,40 @@ export default class DamageDealt extends TranslatedComponent {
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table></span> : null }
|
</table>
|
||||||
|
<div className='group half'>
|
||||||
|
<h1>{translate('sustained dps against standard shields')}</h1>
|
||||||
|
<LineChart
|
||||||
|
width={this.props.chartWidth}
|
||||||
|
xMax={maxRange}
|
||||||
|
yMax={this.state.maxDps}
|
||||||
|
xLabel={translate('range')}
|
||||||
|
xUnit={translate('m')}
|
||||||
|
yLabel={translate('sdps')}
|
||||||
|
series={this.state.weaponNames}
|
||||||
|
colors={DAMAGE_DEALT_COLORS}
|
||||||
|
func={this.state.calcShieldsDpsFunc}
|
||||||
|
points={200}
|
||||||
|
code={code}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className='group half'>
|
||||||
|
<h1>{translate('sustained dps against standard armour')}</h1>
|
||||||
|
<LineChart
|
||||||
|
width={this.props.chartWidth}
|
||||||
|
xMax={maxRange}
|
||||||
|
yMax={this.state.maxDps}
|
||||||
|
xLabel={translate('range')}
|
||||||
|
xUnit={translate('m')}
|
||||||
|
yLabel={translate('sdps')}
|
||||||
|
series={this.state.weaponNames}
|
||||||
|
colors={DAMAGE_DEALT_COLORS}
|
||||||
|
func={this.state.calcHullDpsFunc}
|
||||||
|
points={200}
|
||||||
|
code={code}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</span> : null }
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -278,7 +278,7 @@ export default class DamageReceived extends TranslatedComponent {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
<h1>{translate('damage received by')} {expanded ? <span onClick={onCollapseExpand}><CollapseSection className='summary'/></span> : <span onClick={onCollapseExpand}><ExpandSection className='summary'/></span>}</h1>
|
<h1>{translate('damage received from')} {expanded ? <span onClick={onCollapseExpand}><CollapseSection className='summary'/></span> : <span onClick={onCollapseExpand}><ExpandSection className='summary'/></span>}</h1>
|
||||||
{expanded ? <span>
|
{expanded ? <span>
|
||||||
<table className='summary' style={{ width: '100%' }}>
|
<table className='summary' style={{ width: '100%' }}>
|
||||||
<thead>
|
<thead>
|
||||||
|
|||||||
150
src/app/components/EngineProfile.jsx
Normal file
150
src/app/components/EngineProfile.jsx
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
|
import { Ships } from 'coriolis-data/dist';
|
||||||
|
import ShipSelector from './ShipSelector';
|
||||||
|
import { nameComparator } from '../utils/SlotFunctions';
|
||||||
|
import LineChart from '../components/LineChart';
|
||||||
|
import Slider from '../components/Slider';
|
||||||
|
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
||||||
|
import Module from '../shipyard/Module';
|
||||||
|
import * as Calc from '../shipyard/Calculations';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Engine profile for a given ship
|
||||||
|
*/
|
||||||
|
export default class EngineProfile extends TranslatedComponent {
|
||||||
|
static PropTypes = {
|
||||||
|
ship: React.PropTypes.object.isRequired,
|
||||||
|
chartWidth: React.PropTypes.number.isRequired,
|
||||||
|
code: React.PropTypes.string.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param {Object} props React Component properties
|
||||||
|
* @param {Object} context React Component context
|
||||||
|
*/
|
||||||
|
constructor(props, context) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
const ship = this.props.ship;
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
cargo: ship.cargoCapacity,
|
||||||
|
calcMaxSpeedFunc: this._calcMaxSpeed.bind(this, ship)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the 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) {
|
||||||
|
this.setState({ cargo: nextProps.ship.cargoCapacity, calcMaxSpeedFunc: this._calcMaxSpeed.bind(this, nextProps.ship) });
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the maximum speed for this ship across its applicable mass
|
||||||
|
* @param {Object} ship The ship
|
||||||
|
* @param {Object} mass The mass at which to calculate the top speed
|
||||||
|
* @return {number} The maximum speed
|
||||||
|
*/
|
||||||
|
_calcMaxSpeed(ship, mass) {
|
||||||
|
// Obtain the thrusters for this ship
|
||||||
|
const thrusters = ship.standard[1].m;
|
||||||
|
|
||||||
|
// Obtain the top speed
|
||||||
|
return Calc.speed(mass, ship.speed, thrusters, ship.engpip)[4];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update cargo level
|
||||||
|
* @param {number} cargoLevel Cargo level 0 - 1
|
||||||
|
*/
|
||||||
|
_cargoChange(cargoLevel) {
|
||||||
|
let ship = this.props.ship;
|
||||||
|
let cargo = Math.round(ship.cargoCapacity * cargoLevel);
|
||||||
|
this.setState({
|
||||||
|
cargo
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render engine profile
|
||||||
|
* @return {React.Component} contents
|
||||||
|
*/
|
||||||
|
render() {
|
||||||
|
const { language, onWindowResize, sizeRatio, tooltip, termtip } = this.context;
|
||||||
|
const { formats, translate, units } = language;
|
||||||
|
const { ship } = this.props;
|
||||||
|
const { cargo } = this.state;
|
||||||
|
|
||||||
|
// Calculate bounds for our line chart
|
||||||
|
const thrusters = ship.standard[1].m;
|
||||||
|
const minMass = thrusters.getMinMass();
|
||||||
|
const maxMass = thrusters.getMaxMass();
|
||||||
|
const minSpeed = Calc.speed(maxMass, ship.speed, thrusters, ship.engpip)[4];
|
||||||
|
const maxSpeed = Calc.speed(minMass, ship.speed, thrusters, ship.engpip)[4];
|
||||||
|
let mass = ship.unladenMass + ship.fuelCapacity + cargo;
|
||||||
|
let mark;
|
||||||
|
if (mass < minMass) {
|
||||||
|
mark = minMass;
|
||||||
|
} else if (mass > maxMass) {
|
||||||
|
mark = maxMass;
|
||||||
|
} else {
|
||||||
|
mark = mass;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cargoPercent = cargo / ship.cargoCapacity;
|
||||||
|
|
||||||
|
const code = ship.toString() + '.' + ship.getModificationsString() + '.' + ship.getPowerEnabledString();
|
||||||
|
|
||||||
|
// This graph has a precipitous fall-off so we use lots of points to make it look a little smoother
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
<h1>{translate('engine profile')}</h1>
|
||||||
|
<LineChart
|
||||||
|
width={this.props.chartWidth}
|
||||||
|
xMin={minMass}
|
||||||
|
xMax={maxMass}
|
||||||
|
yMin={minSpeed}
|
||||||
|
yMax={maxSpeed}
|
||||||
|
xMark={mark}
|
||||||
|
xLabel={translate('mass')}
|
||||||
|
xUnit={translate('T')}
|
||||||
|
yLabel={translate('maximum speed')}
|
||||||
|
yUnit={translate('m/s')}
|
||||||
|
func={this.state.calcMaxSpeedFunc}
|
||||||
|
points={1000}
|
||||||
|
code={code}
|
||||||
|
/>
|
||||||
|
{ship.cargoCapacity ?
|
||||||
|
<span>
|
||||||
|
<h3>{translate('cargo carried')}: {formats.int(cargo)}{units.T}</h3>
|
||||||
|
<table style={{ width: '100%', lineHeight: '1em', backgroundColor: 'transparent' }}>
|
||||||
|
<tbody >
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<Slider
|
||||||
|
axis={true}
|
||||||
|
onChange={this._cargoChange.bind(this)}
|
||||||
|
axisUnit={translate('T')}
|
||||||
|
percent={cargoPercent}
|
||||||
|
max={ship.cargoCapacity}
|
||||||
|
scale={sizeRatio}
|
||||||
|
onResize={onWindowResize}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</span> : '' }
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
151
src/app/components/FSDProfile.jsx
Normal file
151
src/app/components/FSDProfile.jsx
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
|
import { Ships } from 'coriolis-data/dist';
|
||||||
|
import ShipSelector from './ShipSelector';
|
||||||
|
import { nameComparator } from '../utils/SlotFunctions';
|
||||||
|
import LineChart from '../components/LineChart';
|
||||||
|
import Slider from '../components/Slider';
|
||||||
|
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
||||||
|
import Module from '../shipyard/Module';
|
||||||
|
import * as Calc from '../shipyard/Calculations';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FSD profile for a given ship
|
||||||
|
*/
|
||||||
|
export default class FSDProfile extends TranslatedComponent {
|
||||||
|
static PropTypes = {
|
||||||
|
ship: React.PropTypes.object.isRequired,
|
||||||
|
chartWidth: React.PropTypes.number.isRequired,
|
||||||
|
code: React.PropTypes.string.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param {Object} props React Component properties
|
||||||
|
* @param {Object} context React Component context
|
||||||
|
*/
|
||||||
|
constructor(props, context) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
const ship = this.props.ship;
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
cargo: ship.cargoCapacity,
|
||||||
|
calcMaxRangeFunc: this._calcMaxRange.bind(this, ship)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the 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) {
|
||||||
|
this.setState({ cargo: nextProps.ship.cargoCapacity, calcMaxRangeFunc: this._calcMaxRange.bind(this, nextProps.ship) });
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the maximum range for this ship across its applicable mass
|
||||||
|
* @param {Object} ship The ship
|
||||||
|
* @param {Object} mass The mass at which to calculate the maximum range
|
||||||
|
* @return {number} The maximum range
|
||||||
|
*/
|
||||||
|
_calcMaxRange(ship, mass) {
|
||||||
|
// Obtain the FSD for this ship
|
||||||
|
const fsd = ship.standard[2].m;
|
||||||
|
|
||||||
|
// Obtain the maximum range
|
||||||
|
return Calc.jumpRange(mass, fsd, fsd.getMaxFuelPerJump());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update cargo level
|
||||||
|
* @param {number} cargoLevel Cargo level 0 - 1
|
||||||
|
*/
|
||||||
|
_cargoChange(cargoLevel) {
|
||||||
|
let ship = this.props.ship;
|
||||||
|
let cargo = Math.round(ship.cargoCapacity * cargoLevel);
|
||||||
|
this.setState({
|
||||||
|
cargo
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render engine profile
|
||||||
|
* @return {React.Component} contents
|
||||||
|
*/
|
||||||
|
render() {
|
||||||
|
const { language, onWindowResize, sizeRatio, tooltip, termtip } = this.context;
|
||||||
|
const { formats, translate, units } = language;
|
||||||
|
const { ship } = this.props;
|
||||||
|
const { cargo } = this.state;
|
||||||
|
|
||||||
|
|
||||||
|
// Calculate bounds for our line chart - use thruster info for X
|
||||||
|
const thrusters = ship.standard[1].m;
|
||||||
|
const fsd = ship.standard[2].m;
|
||||||
|
const minMass = thrusters.getMinMass();
|
||||||
|
const maxMass = thrusters.getMaxMass();
|
||||||
|
const minRange = 0;
|
||||||
|
const maxRange = Calc.jumpRange(minMass + fsd.getMaxFuelPerJump(), fsd, fsd.getMaxFuelPerJump());
|
||||||
|
let mass = ship.unladenMass + fsd.getMaxFuelPerJump() + cargo;
|
||||||
|
let mark;
|
||||||
|
if (mass < minMass) {
|
||||||
|
mark = minMass;
|
||||||
|
} else if (mass > maxMass) {
|
||||||
|
mark = maxMass;
|
||||||
|
} else {
|
||||||
|
mark = mass;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cargoPercent = cargo / ship.cargoCapacity;
|
||||||
|
|
||||||
|
const code = ship.name + ship.toString() + '.' + ship.getModificationsString() + '.' + ship.getPowerEnabledString();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
<h1>{translate('fsd profile')}</h1>
|
||||||
|
<LineChart
|
||||||
|
width={this.props.chartWidth}
|
||||||
|
xMin={minMass}
|
||||||
|
xMax={maxMass}
|
||||||
|
yMin={minRange}
|
||||||
|
yMax={maxRange}
|
||||||
|
xMark={mark}
|
||||||
|
xLabel={translate('mass')}
|
||||||
|
xUnit={translate('T')}
|
||||||
|
yLabel={translate('maximum range')}
|
||||||
|
yUnit={translate('LY')}
|
||||||
|
func={this.state.calcMaxRangeFunc}
|
||||||
|
points={200}
|
||||||
|
code={code}
|
||||||
|
/>
|
||||||
|
{ship.cargoCapacity ?
|
||||||
|
<span>
|
||||||
|
<h3>{translate('cargo carried')}: {formats.int(cargo)}{units.T}</h3>
|
||||||
|
<table style={{ width: '100%', lineHeight: '1em', backgroundColor: 'transparent' }}>
|
||||||
|
<tbody >
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<Slider
|
||||||
|
axis={true}
|
||||||
|
onChange={this._cargoChange.bind(this)}
|
||||||
|
axisUnit={translate('T')}
|
||||||
|
percent={cargoPercent}
|
||||||
|
max={ship.cargoCapacity}
|
||||||
|
scale={sizeRatio}
|
||||||
|
onResize={onWindowResize}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</span> : '' }
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,8 +39,8 @@ export default class InternalSlot extends Slot {
|
|||||||
<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'}>
|
||||||
{ m.getOptMass() ? <div className={'l'}>{translate('optimal mass')}: {formats.int(m.getOptMass())}{u.T}</div> : null }
|
{ m.getOptMass() ? <div className={'l'}>{translate('optmass', 'sg')}: {formats.int(m.getOptMass())}{u.T}</div> : null }
|
||||||
{ m.getMaxMass() ? <div className={'l'}>{translate('max mass')}: {formats.int(m.getMaxMass())}{u.T}</div> : null }
|
{ m.getMaxMass() ? <div className={'l'}>{translate('maxmass', 'sg')}: {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.rebuildsperbay ? <div className={'l'}>{translate('rebuildsperbay')}: {m.rebuildsperbay}</div> : null }
|
||||||
@@ -60,6 +60,7 @@ export default class InternalSlot extends Slot {
|
|||||||
{ 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.getProtection() ? <div className={'l'}>{formats.rPct(m.getProtection())} <u className='cap'>{translate('protection')}</u></div> : null }
|
||||||
|
{ m.getIntegrity() && m.grp === 'mrp' ? <div className={'l'}>{formats.int(m.getIntegrity())} <u className='cap'>{translate('integrity')}</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 }
|
||||||
|
|||||||
126
src/app/components/JumpRange.jsx
Normal file
126
src/app/components/JumpRange.jsx
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
|
import { Ships } from 'coriolis-data/dist';
|
||||||
|
import ShipSelector from './ShipSelector';
|
||||||
|
import { nameComparator } from '../utils/SlotFunctions';
|
||||||
|
import LineChart from '../components/LineChart';
|
||||||
|
import Slider from '../components/Slider';
|
||||||
|
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
||||||
|
import Module from '../shipyard/Module';
|
||||||
|
import * as Calc from '../shipyard/Calculations';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Jump range for a given ship
|
||||||
|
*/
|
||||||
|
export default class JumpRange extends TranslatedComponent {
|
||||||
|
static PropTypes = {
|
||||||
|
ship: React.PropTypes.object.isRequired,
|
||||||
|
chartWidth: React.PropTypes.number.isRequired,
|
||||||
|
code: React.PropTypes.string.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param {Object} props React Component properties
|
||||||
|
* @param {Object} context React Component context
|
||||||
|
*/
|
||||||
|
constructor(props, context) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
const ship = this.props.ship;
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
fuelLevel: 1,
|
||||||
|
calcJumpRangeFunc: this._calcJumpRange.bind(this, ship)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the 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) {
|
||||||
|
this.setState({ fuelLevel: 1,
|
||||||
|
calcJumpRangeFunc: this._calcJumpRange.bind(this, nextProps.ship) });
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the jump range this ship at a given cargo
|
||||||
|
* @param {Object} ship The ship
|
||||||
|
* @param {Object} cargo The cargo
|
||||||
|
* @return {number} The jump range
|
||||||
|
*/
|
||||||
|
_calcJumpRange(ship, cargo) {
|
||||||
|
// Obtain the FSD for this ship
|
||||||
|
const fsd = ship.standard[2].m;
|
||||||
|
|
||||||
|
const fuel = this.state.fuelLevel * ship.fuelCapacity;
|
||||||
|
|
||||||
|
// Obtain the jump range
|
||||||
|
return Calc.jumpRange(ship.unladenMass + fuel + cargo, fsd, fuel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update fuel level
|
||||||
|
* @param {number} fuelLevel Fuel level 0 - 1
|
||||||
|
*/
|
||||||
|
_fuelChange(fuelLevel) {
|
||||||
|
this.setState({
|
||||||
|
fuelLevel,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render engine profile
|
||||||
|
* @return {React.Component} contents
|
||||||
|
*/
|
||||||
|
render() {
|
||||||
|
const { language, onWindowResize, sizeRatio, tooltip, termtip } = this.context;
|
||||||
|
const { formats, translate, units } = language;
|
||||||
|
const { ship } = this.props;
|
||||||
|
const { fuelLevel } = this.state;
|
||||||
|
|
||||||
|
const code = ship.toString() + '.' + ship.getModificationsString() + '.' + fuelLevel;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
<h1>{translate('jump range')}</h1>
|
||||||
|
<LineChart
|
||||||
|
width={this.props.chartWidth}
|
||||||
|
xMax={ship.cargoCapacity}
|
||||||
|
yMax={ship.unladenRange}
|
||||||
|
xLabel={translate('cargo')}
|
||||||
|
xUnit={translate('T')}
|
||||||
|
yLabel={translate('jump range')}
|
||||||
|
yUnit={translate('LY')}
|
||||||
|
func={this.state.calcJumpRangeFunc}
|
||||||
|
points={200}
|
||||||
|
code={code}
|
||||||
|
/>
|
||||||
|
<h3>{translate('fuel carried')}: {formats.f2(fuelLevel * ship.fuelCapacity)}{units.T}</h3>
|
||||||
|
<table style={{ width: '100%', lineHeight: '1em', backgroundColor: 'transparent' }}>
|
||||||
|
<tbody >
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<Slider
|
||||||
|
axis={true}
|
||||||
|
onChange={this._fuelChange.bind(this)}
|
||||||
|
axisUnit={translate('T')}
|
||||||
|
percent={fuelLevel}
|
||||||
|
max={ship.fuelCapacity}
|
||||||
|
scale={sizeRatio}
|
||||||
|
onResize={onWindowResize}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import d3 from 'd3';
|
import * as d3 from 'd3';
|
||||||
import TranslatedComponent from './TranslatedComponent';
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
|
|
||||||
const RENDER_POINTS = 20; // Only render 20 points on the graph
|
|
||||||
const MARGIN = { top: 15, right: 20, bottom: 35, left: 60 };
|
const MARGIN = { top: 15, right: 20, bottom: 35, left: 60 };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -11,8 +10,10 @@ const MARGIN = { top: 15, right: 20, bottom: 35, left: 60 };
|
|||||||
export default class LineChart extends TranslatedComponent {
|
export default class LineChart extends TranslatedComponent {
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
|
code: '',
|
||||||
xMin: 0,
|
xMin: 0,
|
||||||
yMin: 0,
|
yMin: 0,
|
||||||
|
points: 20,
|
||||||
colors: ['#ff8c0d']
|
colors: ['#ff8c0d']
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -23,12 +24,15 @@ export default class LineChart extends TranslatedComponent {
|
|||||||
xMin: React.PropTypes.number,
|
xMin: React.PropTypes.number,
|
||||||
xMax: React.PropTypes.number.isRequired,
|
xMax: React.PropTypes.number.isRequired,
|
||||||
xUnit: React.PropTypes.string.isRequired,
|
xUnit: React.PropTypes.string.isRequired,
|
||||||
|
xMark: React.PropTypes.number,
|
||||||
yLabel: React.PropTypes.string.isRequired,
|
yLabel: React.PropTypes.string.isRequired,
|
||||||
yMin: React.PropTypes.number,
|
yMin: React.PropTypes.number,
|
||||||
yMax: React.PropTypes.number.isRequired,
|
yMax: React.PropTypes.number.isRequired,
|
||||||
yUnit: React.PropTypes.string.isRequired,
|
yUnit: React.PropTypes.string,
|
||||||
series: React.PropTypes.array,
|
series: React.PropTypes.array,
|
||||||
colors: React.PropTypes.array,
|
colors: React.PropTypes.array,
|
||||||
|
points: React.PropTypes.number,
|
||||||
|
code: React.PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -40,37 +44,25 @@ export default class LineChart extends TranslatedComponent {
|
|||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this._updateDimensions = this._updateDimensions.bind(this);
|
this._updateDimensions = this._updateDimensions.bind(this);
|
||||||
this._updateSeriesData = this._updateSeriesData.bind(this);
|
this._updateSeries = this._updateSeries.bind(this);
|
||||||
this._tooltip = this._tooltip.bind(this);
|
this._tooltip = this._tooltip.bind(this);
|
||||||
this._showTip = this._showTip.bind(this);
|
this._showTip = this._showTip.bind(this);
|
||||||
this._hideTip = this._hideTip.bind(this);
|
this._hideTip = this._hideTip.bind(this);
|
||||||
this._moveTip = this._moveTip.bind(this);
|
this._moveTip = this._moveTip.bind(this);
|
||||||
|
|
||||||
let markerElems = [];
|
const series = props.series;
|
||||||
let detailElems = [<text key='lbl' className='text-tip x' y='1.25em'/>];
|
|
||||||
let xScale = d3.scale.linear();
|
|
||||||
let xAxisScale = d3.scale.linear();
|
|
||||||
let yScale = d3.scale.linear();
|
|
||||||
let series = props.series;
|
|
||||||
let seriesLines = [];
|
|
||||||
|
|
||||||
this.xAxis = d3.svg.axis().scale(xAxisScale).outerTickSize(0).orient('bottom');
|
let xScale = d3.scaleLinear();
|
||||||
this.yAxis = d3.svg.axis().scale(yScale).ticks(6).outerTickSize(0).orient('left');
|
let yScale = d3.scaleLinear();
|
||||||
|
let xAxisScale = d3.scaleLinear();
|
||||||
|
|
||||||
for(let i = 0, l = series ? series.length : 1; i < l; i++) {
|
this.xAxis = d3.axisBottom(xAxisScale).tickSizeOuter(0);
|
||||||
let yAccessor = series ? function(d) { return yScale(d[1][this]); }.bind(series[i]) : (d) => yScale(d[1]);
|
this.yAxis = d3.axisLeft(yScale).ticks(6).tickSizeOuter(0);
|
||||||
seriesLines.push(d3.svg.line().x((d) => xScale(d[0])).y(yAccessor));
|
|
||||||
detailElems.push(<text key={i} className='text-tip y' y={1.25 * (i + 2) + 'em'}/>);
|
|
||||||
markerElems.push(<circle key={i} className='marker' r='4' />);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
xScale,
|
xScale,
|
||||||
xAxisScale,
|
xAxisScale,
|
||||||
yScale,
|
yScale,
|
||||||
seriesLines,
|
|
||||||
detailElems,
|
|
||||||
markerElems,
|
|
||||||
tipHeight: 2 + (1.2 * (series ? series.length : 0.8))
|
tipHeight: 2 + (1.2 * (series ? series.length : 0.8))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -98,7 +90,7 @@ export default class LineChart extends TranslatedComponent {
|
|||||||
let yVal = series ? y0[series[i]] : y0;
|
let yVal = series ? y0[series[i]] : y0;
|
||||||
yTotal += yVal;
|
yTotal += yVal;
|
||||||
return (series ? translate(series[i]) : '') + ' ' + formats.f2(yVal);
|
return (series ? translate(series[i]) : '') + ' ' + formats.f2(yVal);
|
||||||
}).append('tspan').attr('class', 'metric').text(' ' + yUnit);
|
}).append('tspan').attr('class', 'metric').text(yUnit ? ' ' + yUnit : '');
|
||||||
|
|
||||||
tips.selectAll('text').each(function() {
|
tips.selectAll('text').each(function() {
|
||||||
if (this.getBBox().width > tipWidth) {
|
if (this.getBBox().width > tipWidth) {
|
||||||
@@ -129,7 +121,7 @@ export default class LineChart extends TranslatedComponent {
|
|||||||
|
|
||||||
this.state.xScale.range([0, innerWidth]).domain([xMin, xMax || 1]).clamp(true);
|
this.state.xScale.range([0, innerWidth]).domain([xMin, xMax || 1]).clamp(true);
|
||||||
this.state.xAxisScale.range([0, innerWidth]).domain([xMin, xMax]).clamp(true);
|
this.state.xAxisScale.range([0, innerWidth]).domain([xMin, xMax]).clamp(true);
|
||||||
this.state.yScale.range([innerHeight, 0]).domain([yMin, yMax]);
|
this.state.yScale.range([innerHeight, 0]).domain([yMin, yMax + (yMax - yMin) * 0.1]); // 10% higher than maximum value for tooltip visibility
|
||||||
this.setState({ innerWidth, outerHeight, innerHeight });
|
this.setState({ innerWidth, outerHeight, innerHeight });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,27 +156,40 @@ export default class LineChart extends TranslatedComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update series data generated from props
|
* Update series generated from props
|
||||||
* @param {Object} props React Component properties
|
* @param {Object} props React Component properties
|
||||||
|
* @param {Object} state React Component state
|
||||||
*/
|
*/
|
||||||
_updateSeriesData(props) {
|
_updateSeries(props, state) {
|
||||||
let { func, xMin, xMax, series } = props;
|
let { func, xMin, xMax, series, points } = props;
|
||||||
let delta = (xMax - xMin) / RENDER_POINTS;
|
let delta = (xMax - xMin) / points;
|
||||||
let seriesData = new Array(RENDER_POINTS);
|
let seriesData = new Array(points);
|
||||||
|
|
||||||
if (delta) {
|
if (delta) {
|
||||||
seriesData = new Array(RENDER_POINTS);
|
seriesData = new Array(points);
|
||||||
for (let i = 0, x = xMin; i < RENDER_POINTS; i++) {
|
for (let i = 0, x = xMin; i < points; i++) {
|
||||||
seriesData[i] = [x, func(x)];
|
seriesData[i] = [x, func(x)];
|
||||||
x += delta;
|
x += delta;
|
||||||
}
|
}
|
||||||
seriesData[RENDER_POINTS - 1] = [xMax, func(xMax)];
|
seriesData[points - 1] = [xMax, func(xMax)];
|
||||||
} else {
|
} else {
|
||||||
let yVal = func(xMin);
|
let yVal = func(xMin);
|
||||||
seriesData = [[0, yVal], [1, yVal]];
|
seriesData = [[0, yVal], [1, yVal]];
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({ seriesData });
|
const markerElems = [];
|
||||||
|
const detailElems = [<text key='lbl' className='text-tip x' y='1.25em'/>];
|
||||||
|
const seriesLines = [];
|
||||||
|
for (let i = 0, l = series ? series.length : 1; i < l; i++) {
|
||||||
|
const yAccessor = series ? function(d) { return state.yScale(d[1][this]); }.bind(series[i]) : (d) => state.yScale(d[1]);
|
||||||
|
seriesLines.push(d3.line().x((d, i) => this.state.xScale(d[0])).y(yAccessor));
|
||||||
|
detailElems.push(<text key={i} className='text-tip y' stroke={props.colors[i]} y={1.25 * (i + 2) + 'em'}/>);
|
||||||
|
markerElems.push(<circle key={i} className='marker' r='4' />);
|
||||||
|
}
|
||||||
|
|
||||||
|
const tipHeight = 2 + (1.2 * (seriesLines ? seriesLines.length : 0.8));
|
||||||
|
|
||||||
|
this.setState({ markerElems, detailElems, seriesLines, seriesData, tipHeight });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -192,7 +197,7 @@ export default class LineChart extends TranslatedComponent {
|
|||||||
*/
|
*/
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
this._updateDimensions(this.props, this.context.sizeRatio);
|
this._updateDimensions(this.props, this.context.sizeRatio);
|
||||||
this._updateSeriesData(this.props);
|
this._updateSeries(this.props, this.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -210,8 +215,8 @@ export default class LineChart extends TranslatedComponent {
|
|||||||
this._updateDimensions(nextProps, nextContext.sizeRatio);
|
this._updateDimensions(nextProps, nextContext.sizeRatio);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (domainChanged) {
|
if (props.code != nextProps.code) {
|
||||||
this._updateSeriesData(nextProps);
|
this._updateSeries(nextProps, this.state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,13 +229,17 @@ export default class LineChart extends TranslatedComponent {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
let { xLabel, yLabel, xUnit, yUnit, colors } = this.props;
|
let { xMin, xMax, xLabel, yLabel, xUnit, yUnit, xMark, colors } = this.props;
|
||||||
let { innerWidth, outerHeight, innerHeight, tipHeight, detailElems, markerElems, seriesData, seriesLines } = this.state;
|
let { innerWidth, outerHeight, innerHeight, tipHeight, detailElems, markerElems, seriesData, seriesLines } = this.state;
|
||||||
let line = this.line;
|
let line = this.line;
|
||||||
let lines = seriesLines.map((line, i) => <path key={i} className='line' stroke={colors[i]} strokeWidth='2' d={line(seriesData)} />);
|
let lines = seriesLines.map((line, i) => <path key={i} className='line' fill='none' stroke={colors[i]} strokeWidth='1' d={line(seriesData)} />).reverse();
|
||||||
|
|
||||||
|
const markX = xMark ? innerWidth * (xMark - xMin) / (xMax - xMin) : 0;
|
||||||
|
const xmark = xMark ? <path key={'mark'} className='line' fill='none' strokeDasharray='5,5' stroke={colors[0]} strokeWidth='1' d={'M ' + markX + ' ' + innerHeight + ' L ' + markX + ' 0'} /> : '';
|
||||||
|
|
||||||
return <svg style={{ width: '100%', height: outerHeight }}>
|
return <svg style={{ width: '100%', height: outerHeight }}>
|
||||||
<g transform={`translate(${MARGIN.left},${MARGIN.top})`}>
|
<g transform={`translate(${MARGIN.left},${MARGIN.top})`}>
|
||||||
|
<g>{xmark}</g>
|
||||||
<g>{lines}</g>
|
<g>{lines}</g>
|
||||||
<g className='x axis' ref={(elem) => d3.select(elem).call(this.xAxis)} transform={`translate(0,${innerHeight})`}>
|
<g className='x axis' ref={(elem) => d3.select(elem).call(this.xAxis)} transform={`translate(0,${innerHeight})`}>
|
||||||
<text className='cap' y='30' dy='.1em' x={innerWidth / 2} style={{ textAnchor: 'middle' }}>
|
<text className='cap' y='30' dy='.1em' x={innerWidth / 2} style={{ textAnchor: 'middle' }}>
|
||||||
@@ -241,7 +250,7 @@ export default class LineChart extends TranslatedComponent {
|
|||||||
<g className='y axis' ref={(elem) => d3.select(elem).call(this.yAxis)}>
|
<g className='y axis' ref={(elem) => d3.select(elem).call(this.yAxis)}>
|
||||||
<text className='cap' transform='rotate(-90)' y='-50' dy='.1em' x={innerHeight / -2} style={{ textAnchor: 'middle' }}>
|
<text className='cap' transform='rotate(-90)' y='-50' dy='.1em' x={innerHeight / -2} style={{ textAnchor: 'middle' }}>
|
||||||
<tspan>{yLabel}</tspan>
|
<tspan>{yLabel}</tspan>
|
||||||
<tspan className='metric'> ({yUnit})</tspan>
|
{ yUnit && <tspan className='metric'> ({yUnit})</tspan> }
|
||||||
</text>
|
</text>
|
||||||
</g>
|
</g>
|
||||||
<g ref={(g) => this.tipContainer = d3.select(g)} style={{ display: 'none' }}>
|
<g ref={(g) => this.tipContainer = d3.select(g)} style={{ display: 'none' }}>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { findDOMNode } from 'react-dom';
|
|
||||||
import TranslatedComponent from './TranslatedComponent';
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
import cn from 'classnames';
|
import cn from 'classnames';
|
||||||
import NumberEditor from 'react-number-editor';
|
import NumberEditor from 'react-number-editor';
|
||||||
@@ -81,7 +80,7 @@ export default class Modification extends TranslatedComponent {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={'cb'} key={name}>
|
<div className={'cb'} key={name}>
|
||||||
<div className={'cb'}>{translate(name)}{symbol}</div>
|
<div className={'cb'}>{translate(name, m.grp)}{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,10 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import { findDOMNode } from 'react-dom';
|
|
||||||
import TranslatedComponent from './TranslatedComponent';
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
import { isEmpty, 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 { Modifications } from 'coriolis-data/dist';
|
import { Modifications } from 'coriolis-data/dist';
|
||||||
import Modification from './Modification';
|
import Modification from './Modification';
|
||||||
|
|
||||||
@@ -32,8 +30,8 @@ export default class ModificationsMenu extends TranslatedComponent {
|
|||||||
this._toggleSpecialsMenu = this._toggleSpecialsMenu.bind(this);
|
this._toggleSpecialsMenu = this._toggleSpecialsMenu.bind(this);
|
||||||
this._rollWorst = this._rollWorst.bind(this);
|
this._rollWorst = this._rollWorst.bind(this);
|
||||||
this._rollRandom = this._rollRandom.bind(this);
|
this._rollRandom = this._rollRandom.bind(this);
|
||||||
this._rollAverage = this._rollAverage.bind(this);
|
|
||||||
this._rollBest = this._rollBest.bind(this);
|
this._rollBest = this._rollBest.bind(this);
|
||||||
|
this._rollExtreme = this._rollExtreme.bind(this);
|
||||||
this._reset = this._reset.bind(this);
|
this._reset = this._reset.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,8 +42,8 @@ export default class ModificationsMenu extends TranslatedComponent {
|
|||||||
* @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 } = props;
|
||||||
const { language, tooltip, termtip } = context;
|
const { language } = context;
|
||||||
const translate = language.translate;
|
const translate = language.translate;
|
||||||
|
|
||||||
// Set up the blueprints
|
// Set up the blueprints
|
||||||
@@ -185,20 +183,6 @@ export default class ModificationsMenu extends TranslatedComponent {
|
|||||||
this.props.onChange();
|
this.props.onChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Provide an 'average' roll within the information we have
|
|
||||||
*/
|
|
||||||
_rollAverage() {
|
|
||||||
const { m, ship } = this.props;
|
|
||||||
const features = m.blueprint.grades[m.blueprint.grade].features;
|
|
||||||
for (const featureName in features) {
|
|
||||||
let value = (features[featureName][0] + features[featureName][1]) / 2;
|
|
||||||
this._setRollResult(ship, m, featureName, value);
|
|
||||||
}
|
|
||||||
this.setState({ modifications: this._setModifications(this.props) });
|
|
||||||
this.props.onChange();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provide a random roll within the information we have
|
* Provide a random roll within the information we have
|
||||||
*/
|
*/
|
||||||
@@ -227,6 +211,36 @@ export default class ModificationsMenu extends TranslatedComponent {
|
|||||||
this.props.onChange();
|
this.props.onChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide an 'extreme' roll within the information we have
|
||||||
|
*/
|
||||||
|
_rollExtreme() {
|
||||||
|
const { m, ship } = this.props;
|
||||||
|
const features = m.blueprint.grades[m.blueprint.grade].features;
|
||||||
|
for (const featureName in features) {
|
||||||
|
let value;
|
||||||
|
if (Modifications.modifications[featureName].higherbetter) {
|
||||||
|
// Higher is better, but is this making it better or worse?
|
||||||
|
if (features[featureName][0] < 0 || (features[featureName][0] === 0 && features[featureName][1] < 0)) {
|
||||||
|
value = features[featureName][0];
|
||||||
|
} else {
|
||||||
|
value = features[featureName][1];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Higher is worse, but is this making it better or worse?
|
||||||
|
if (features[featureName][0] < 0 || (features[featureName][0] === 0 && features[featureName][1] < 0)) {
|
||||||
|
value = features[featureName][1];
|
||||||
|
} else {
|
||||||
|
value = features[featureName][0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._setRollResult(ship, m, featureName, value);
|
||||||
|
}
|
||||||
|
this.setState({ modifications: this._setModifications(this.props) });
|
||||||
|
this.props.onChange();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset modification information
|
* Reset modification information
|
||||||
*/
|
*/
|
||||||
@@ -252,8 +266,8 @@ export default class ModificationsMenu extends TranslatedComponent {
|
|||||||
const _toggleBlueprintsMenu = this._toggleBlueprintsMenu;
|
const _toggleBlueprintsMenu = this._toggleBlueprintsMenu;
|
||||||
const _toggleSpecialsMenu = this._toggleSpecialsMenu;
|
const _toggleSpecialsMenu = this._toggleSpecialsMenu;
|
||||||
const _rollBest = this._rollBest;
|
const _rollBest = this._rollBest;
|
||||||
|
const _rollExtreme = this._rollExtreme;
|
||||||
const _rollWorst = this._rollWorst;
|
const _rollWorst = this._rollWorst;
|
||||||
const _rollAverage = this._rollAverage;
|
|
||||||
const _rollRandom = this._rollRandom;
|
const _rollRandom = this._rollRandom;
|
||||||
const _reset = this._reset;
|
const _reset = this._reset;
|
||||||
|
|
||||||
@@ -267,7 +281,6 @@ export default class ModificationsMenu extends TranslatedComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let specialLabel;
|
let specialLabel;
|
||||||
let haveSpecial = false;
|
|
||||||
if (m.blueprint && m.blueprint.special) {
|
if (m.blueprint && m.blueprint.special) {
|
||||||
specialLabel = m.blueprint.special.name;
|
specialLabel = m.blueprint.special.name;
|
||||||
} else {
|
} else {
|
||||||
@@ -278,6 +291,7 @@ export default class ModificationsMenu extends TranslatedComponent {
|
|||||||
const showSpecial = haveBlueprint && this.state.specials.length > 0;
|
const showSpecial = haveBlueprint && this.state.specials.length > 0;
|
||||||
const showSpecialsMenu = specialMenuOpened;
|
const showSpecialsMenu = specialMenuOpened;
|
||||||
const showRolls = haveBlueprint && !blueprintMenuOpened && !specialMenuOpened;
|
const showRolls = haveBlueprint && !blueprintMenuOpened && !specialMenuOpened;
|
||||||
|
const showReset = !blueprintMenuOpened && !specialMenuOpened;
|
||||||
const showMods = !blueprintMenuOpened && !specialMenuOpened;
|
const showMods = !blueprintMenuOpened && !specialMenuOpened;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -287,26 +301,30 @@ export default class ModificationsMenu extends TranslatedComponent {
|
|||||||
onContextMenu={stopCtxPropagation}
|
onContextMenu={stopCtxPropagation}
|
||||||
>
|
>
|
||||||
<div className={ cn('section-menu', { selected: blueprintMenuOpened })} style={{ cursor: 'pointer' }} onClick={_toggleBlueprintsMenu}>{blueprintLabel}</div>
|
<div className={ cn('section-menu', { selected: blueprintMenuOpened })} style={{ cursor: 'pointer' }} onClick={_toggleBlueprintsMenu}>{blueprintLabel}</div>
|
||||||
{ showBlueprintsMenu ? this.state.blueprints : '' }
|
{ showBlueprintsMenu ? this.state.blueprints : null }
|
||||||
{ showSpecial ? <div className={ cn('section-menu', { selected: specialMenuOpened })} style={{ cursor: 'pointer' }} onClick={_toggleSpecialsMenu}>{specialLabel}</div> : '' }
|
{ showSpecial ? <div className={ cn('section-menu', { selected: specialMenuOpened })} style={{ cursor: 'pointer' }} onClick={_toggleSpecialsMenu}>{specialLabel}</div> : null }
|
||||||
{ showSpecialsMenu ? this.state.specials : '' }
|
{ showSpecialsMenu ? this.state.specials : null }
|
||||||
{ showRolls ?
|
{ showRolls || showReset ?
|
||||||
<table style={{ width: '100%', backgroundColor: 'transparent' }}>
|
<table style={{ width: '100%', backgroundColor: 'transparent' }}>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
{ showRolls ?
|
||||||
<tr>
|
<tr>
|
||||||
<td> { translate('roll') }: </td>
|
<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={_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={_rollBest}onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_BEST')} onMouseOut={tooltip.bind(null, null)}> { translate('best') } </td>
|
||||||
|
<td style={{ cursor: 'pointer' }} onClick={_rollExtreme}onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_EXTREME')} onMouseOut={tooltip.bind(null, null)}> { translate('extreme') } </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={_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> : null }
|
||||||
</tr>
|
{ showReset ?
|
||||||
|
<tr>
|
||||||
|
<td colSpan={'5'} style={{ cursor: 'pointer' }} onClick={_reset}onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_RESET')} onMouseOut={tooltip.bind(null, null)}> { translate('reset') } </td>
|
||||||
|
</tr> : null }
|
||||||
</tbody>
|
</tbody>
|
||||||
</table> : '' }
|
</table> : null }
|
||||||
{ showMods ?
|
{ showMods ?
|
||||||
<span onMouseOver={termtip.bind(null, 'HELP_MODIFICATIONS_MENU')} onMouseOut={tooltip.bind(null, null)} >
|
<span onMouseOver={termtip.bind(null, 'HELP_MODIFICATIONS_MENU')} onMouseOut={tooltip.bind(null, null)} >
|
||||||
{ this.state.modifications }
|
{ this.state.modifications }
|
||||||
</span> : '' }
|
</span> : null }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ export default class MovementSummary extends TranslatedComponent {
|
|||||||
let ship = this.props.ship;
|
let ship = this.props.ship;
|
||||||
let { language, tooltip, termtip } = this.context;
|
let { language, tooltip, termtip } = this.context;
|
||||||
let { formats, translate, units } = language;
|
let { formats, translate, units } = language;
|
||||||
let hide = tooltip.bind(null, null);
|
|
||||||
let boostMultiplier = ship.topBoost / ship.topSpeed;
|
let boostMultiplier = ship.topBoost / ship.topSpeed;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import d3 from 'd3';
|
import * as d3 from 'd3';
|
||||||
import cn from 'classnames';
|
import cn from 'classnames';
|
||||||
import TranslatedComponent from './TranslatedComponent';
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
import { wrapCtxMenu } from '../utils/UtilityFunctions';
|
import { wrapCtxMenu } from '../utils/UtilityFunctions';
|
||||||
@@ -46,10 +46,10 @@ export default class PowerBands extends TranslatedComponent {
|
|||||||
*/
|
*/
|
||||||
constructor(props, context) {
|
constructor(props, context) {
|
||||||
super(props);
|
super(props);
|
||||||
this.wattScale = d3.scale.linear();
|
this.wattScale = d3.scaleLinear();
|
||||||
this.pctScale = d3.scale.linear().domain([0, 1]);
|
this.pctScale = d3.scaleLinear().domain([0, 1]);
|
||||||
this.wattAxis = d3.svg.axis().scale(this.wattScale).outerTickSize(0).orient('top').tickFormat(context.language.formats.r2);
|
this.wattAxis = d3.axisTop(this.wattScale).tickSizeOuter(0).tickFormat(context.language.formats.r2);
|
||||||
this.pctAxis = d3.svg.axis().scale(this.pctScale).outerTickSize(0).orient('bottom').tickFormat(context.language.formats.rPct);
|
this.pctAxis = d3.axisBottom(this.pctScale).tickSizeOuter(0).tickFormat(context.language.formats.rPct);
|
||||||
|
|
||||||
this._updateDimensions = this._updateDimensions.bind(this);
|
this._updateDimensions = this._updateDimensions.bind(this);
|
||||||
this._updateScales = this._updateScales.bind(this);
|
this._updateScales = this._updateScales.bind(this);
|
||||||
@@ -186,9 +186,9 @@ export default class PowerBands extends TranslatedComponent {
|
|||||||
|
|
||||||
let { wattScale, pctScale, context, props, state } = this;
|
let { wattScale, pctScale, context, props, state } = this;
|
||||||
let { translate, formats } = context.language;
|
let { translate, formats } = context.language;
|
||||||
let { f2, pct1, rPct, r2 } = formats; // wattFmt, pctFmt, pctAxis, wattAxis
|
let { f2, pct1 } = formats; // wattFmt, pctFmt
|
||||||
let { available, bands, width } = props;
|
let { available, bands } = props;
|
||||||
let { innerWidth, maxPwr, ret, dep } = state;
|
let { innerWidth, ret, dep } = state;
|
||||||
let pwrWarningClass = cn('threshold', { exceeded: bands[0].retractedSum * 2 >= available });
|
let pwrWarningClass = cn('threshold', { exceeded: bands[0].retractedSum * 2 >= available });
|
||||||
let deployed = [];
|
let deployed = [];
|
||||||
let retracted = [];
|
let retracted = [];
|
||||||
|
|||||||
@@ -34,8 +34,8 @@ export default class PowerManagement extends TranslatedComponent {
|
|||||||
this._sort = this._sort.bind(this);
|
this._sort = this._sort.bind(this);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
predicate: 'n',
|
predicate: 'pwr',
|
||||||
desc: true,
|
desc: false,
|
||||||
width: 0
|
width: 0
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ export default class ShipSelector extends TranslatedComponent {
|
|||||||
*/
|
*/
|
||||||
_getShipsMenu() {
|
_getShipsMenu() {
|
||||||
const _selectShip = this._selectShip;
|
const _selectShip = this._selectShip;
|
||||||
const _openMenu = this._openMenu;
|
|
||||||
|
|
||||||
let shipList = [];
|
let shipList = [];
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import TranslatedComponent from './TranslatedComponent';
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
import cn from 'classnames';
|
import cn from 'classnames';
|
||||||
import { SizeMap } from '../shipyard/Constants';
|
|
||||||
import { Warning } from './SvgIcons';
|
import { Warning } from './SvgIcons';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -23,7 +22,7 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
|||||||
let translate = language.translate;
|
let translate = language.translate;
|
||||||
let u = language.units;
|
let u = language.units;
|
||||||
let formats = language.formats;
|
let formats = language.formats;
|
||||||
let { time, int, round, f1, f2, pct } = formats;
|
let { time, int, round, f1, f2 } = formats;
|
||||||
let sgClassNames = cn({ warning: ship.findInternalByGroup('sg') && !ship.shield, muted: !ship.findInternalByGroup('sg') });
|
let sgClassNames = cn({ warning: ship.findInternalByGroup('sg') && !ship.shield, muted: !ship.findInternalByGroup('sg') });
|
||||||
let sgRecover = '-';
|
let sgRecover = '-';
|
||||||
let sgRecharge = '-';
|
let sgRecharge = '-';
|
||||||
@@ -52,6 +51,7 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
|||||||
<th rowSpan={2}>{translate('fuel')}</th>
|
<th rowSpan={2}>{translate('fuel')}</th>
|
||||||
<th colSpan={3}>{translate('jump range')}</th>
|
<th colSpan={3}>{translate('jump range')}</th>
|
||||||
<th onMouseEnter={termtip.bind(null, 'PHRASE_FASTEST_RANGE')} onMouseLeave={hide} colSpan={3}>{translate('fastest range')}</th>
|
<th onMouseEnter={termtip.bind(null, 'PHRASE_FASTEST_RANGE')} onMouseLeave={hide} colSpan={3}>{translate('fastest range')}</th>
|
||||||
|
<th rowSpan={2}>{translate('crew')}</th>
|
||||||
<th onMouseEnter={termtip.bind(null, 'mass lock factor')} onMouseLeave={hide} rowSpan={2}>{translate('MLF')}</th>
|
<th onMouseEnter={termtip.bind(null, 'mass lock factor')} onMouseLeave={hide} rowSpan={2}>{translate('MLF')}</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -88,6 +88,7 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
|||||||
<td>{int(ship.maxJumpCount)}</td>
|
<td>{int(ship.maxJumpCount)}</td>
|
||||||
<td>{f2(ship.unladenFastestRange)} {u.LY}</td>
|
<td>{f2(ship.unladenFastestRange)} {u.LY}</td>
|
||||||
<td>{f2(ship.ladenFastestRange)} {u.LY}</td>
|
<td>{f2(ship.ladenFastestRange)} {u.LY}</td>
|
||||||
|
<td>{ship.crew}</td>
|
||||||
<td>{ship.masslock}</td>
|
<td>{ship.masslock}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ import TranslatedComponent from './TranslatedComponent';
|
|||||||
import cn from 'classnames';
|
import cn from 'classnames';
|
||||||
import AvailableModulesMenu from './AvailableModulesMenu';
|
import AvailableModulesMenu from './AvailableModulesMenu';
|
||||||
import ModificationsMenu from './ModificationsMenu';
|
import ModificationsMenu from './ModificationsMenu';
|
||||||
import { Modifications } from 'coriolis-data/dist';
|
|
||||||
import { ListModifications } from './SvgIcons';
|
|
||||||
import { diffDetails } from '../utils/SlotFunctions';
|
import { diffDetails } from '../utils/SlotFunctions';
|
||||||
import { wrapCtxMenu } from '../utils/UtilityFunctions';
|
import { wrapCtxMenu } from '../utils/UtilityFunctions';
|
||||||
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ export default class SlotSection extends TranslatedComponent {
|
|||||||
* @param {Object} m Selected module
|
* @param {Object} m Selected module
|
||||||
*/
|
*/
|
||||||
_selectModule(slot, m) {
|
_selectModule(slot, m) {
|
||||||
this.props.ship.use(slot, m);
|
this.props.ship.use(slot, m, false);
|
||||||
this.props.onChange();
|
this.props.onChange();
|
||||||
this._close();
|
this._close();
|
||||||
}
|
}
|
||||||
@@ -123,7 +123,7 @@ export default class SlotSection extends TranslatedComponent {
|
|||||||
// We want to copy the module in to the target slot
|
// We want to copy the module in to the target slot
|
||||||
if (targetSlot && canMount(this.props.ship, targetSlot, m.grp, m.class)) {
|
if (targetSlot && canMount(this.props.ship, targetSlot, m.grp, m.class)) {
|
||||||
const mCopy = m.clone();
|
const mCopy = m.clone();
|
||||||
this.props.ship.use(targetSlot, mCopy);
|
this.props.ship.use(targetSlot, mCopy, false);
|
||||||
this.props.onChange();
|
this.props.onChange();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import React from 'react';
|
|||||||
import cn from 'classnames';
|
import cn from 'classnames';
|
||||||
import Persist from '../stores/Persist';
|
import Persist from '../stores/Persist';
|
||||||
import TranslatedComponent from './TranslatedComponent';
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
import { jumpRange } from '../shipyard/Calculations';
|
|
||||||
import { diffDetails } from '../utils/SlotFunctions';
|
import { diffDetails } from '../utils/SlotFunctions';
|
||||||
import AvailableModulesMenu from './AvailableModulesMenu';
|
import AvailableModulesMenu from './AvailableModulesMenu';
|
||||||
import ModificationsMenu from './ModificationsMenu';
|
import ModificationsMenu from './ModificationsMenu';
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import cn from 'classnames';
|
|||||||
import SlotSection from './SlotSection';
|
import SlotSection from './SlotSection';
|
||||||
import StandardSlot from './StandardSlot';
|
import StandardSlot from './StandardSlot';
|
||||||
import Module from '../shipyard/Module';
|
import Module from '../shipyard/Module';
|
||||||
import { diffDetails } from '../utils/SlotFunctions';
|
|
||||||
import * as ShipRoles from '../shipyard/ShipRoles';
|
import * as ShipRoles from '../shipyard/ShipRoles';
|
||||||
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
||||||
|
|
||||||
@@ -53,6 +52,16 @@ export default class StandardSlotSection extends SlotSection {
|
|||||||
this._close();
|
this._close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Miner Build
|
||||||
|
* @param {Boolean} shielded True if shield generator should be included
|
||||||
|
*/
|
||||||
|
_optimizeMiner(shielded) {
|
||||||
|
ShipRoles.miner(this.props.ship, shielded);
|
||||||
|
this.props.onChange();
|
||||||
|
this._close();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Explorer role
|
* Explorer role
|
||||||
* @param {Boolean} planetary True if Planetary Vehicle Hangar (PVH) should be included
|
* @param {Boolean} planetary True if Planetary Vehicle Hangar (PVH) should be included
|
||||||
@@ -209,6 +218,8 @@ export default class StandardSlotSection extends SlotSection {
|
|||||||
<li className='lc' onClick={this._optimizeCargo.bind(this, true)}>{translate('Shielded Trader')}</li>
|
<li className='lc' onClick={this._optimizeCargo.bind(this, true)}>{translate('Shielded Trader')}</li>
|
||||||
<li className='lc' onClick={this._optimizeExplorer.bind(this, false)}>{translate('Explorer')}</li>
|
<li className='lc' onClick={this._optimizeExplorer.bind(this, false)}>{translate('Explorer')}</li>
|
||||||
<li className={cn('lc', { disabled: planetaryDisabled })} onClick={!planetaryDisabled && this._optimizeExplorer.bind(this, true)}>{translate('Planetary Explorer')}</li>
|
<li className={cn('lc', { disabled: planetaryDisabled })} onClick={!planetaryDisabled && this._optimizeExplorer.bind(this, true)}>{translate('Planetary Explorer')}</li>
|
||||||
|
<li className='lc' onClick={this._optimizeMiner.bind(this, false)}>{translate('Miner')}</li>
|
||||||
|
<li className='lc' onClick={this._optimizeMiner.bind(this, true)}>{translate('Shielded Miner')}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -227,6 +227,26 @@ export class LinkIcon extends SvgIcon {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shopping icon (dollar sign)
|
||||||
|
*/
|
||||||
|
export class ShoppingIcon 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='M94 188v-17c-9-1-16-3-21-6-6-3-11-7-15-14-4-6-6-14-6-23l17-3c2 9 4 16 7 21 5 6 11 9 18 10v-56c-7-1-14-4-22-8-6-3-10-8-13-13-3-6-4-12-4-19 0-13 4-23 13-31 6-5 15-8 26-9v-8h11v8c10 1 18 4 24 9 8 6 12 15 14 26l-18 3c-1-7-4-12-7-16s-8-6-13-7v50l17 6c6 2 10 5 13 8 4 4 7 8 8 13 2 4 3 10 3 15 0 12-4 22-11 31-8 8-18 12-30 13v17H94zm0-153c-7 1-12 3-16 8-4 4-6 9-6 15s2 11 5 16c4 4 9 7 17 9V35zm11 121a28 28 0 0 0 24-28c-1-6-2-11-6-15-3-4-9-7-18-10v53z'/>
|
||||||
|
</g>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* No Power - Lightning bolt + no entry
|
* No Power - Lightning bolt + no entry
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -99,11 +99,11 @@ export default class UtilitySlotSection extends SlotSection {
|
|||||||
</ul>
|
</ul>
|
||||||
<div className='select-group cap'>{translate('sb')}</div>
|
<div className='select-group cap'>{translate('sb')}</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li className='c' onClick={_use.bind(this, 'sb', 'E', null)}>E</li>
|
|
||||||
<li className='c' onClick={_use.bind(this, 'sb', 'D', null)}>D</li>
|
|
||||||
<li className='c' onClick={_use.bind(this, 'sb', 'C', null)}>C</li>
|
|
||||||
<li className='c' onClick={_use.bind(this, 'sb', 'B', null)}>B</li>
|
|
||||||
<li className='c' onClick={_use.bind(this, 'sb', 'A', null)}>A</li>
|
<li className='c' onClick={_use.bind(this, 'sb', 'A', null)}>A</li>
|
||||||
|
<li className='c' onClick={_use.bind(this, 'sb', 'B', null)}>B</li>
|
||||||
|
<li className='c' onClick={_use.bind(this, 'sb', 'C', null)}>C</li>
|
||||||
|
<li className='c' onClick={_use.bind(this, 'sb', 'D', null)}>D</li>
|
||||||
|
<li className='c' onClick={_use.bind(this, 'sb', 'E', null)}>E</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div className='select-group cap'>{translate('hs')}</div>
|
<div className='select-group cap'>{translate('hs')}</div>
|
||||||
<ul>
|
<ul>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import * as FR from './fr';
|
|||||||
import * as IT from './it';
|
import * as IT from './it';
|
||||||
import * as RU from './ru';
|
import * as RU from './ru';
|
||||||
import * as PL from './pl';
|
import * as PL from './pl';
|
||||||
import d3 from 'd3';
|
import * as d3 from 'd3';
|
||||||
|
|
||||||
let fallbackTerms = EN.terms;
|
let fallbackTerms = EN.terms;
|
||||||
|
|
||||||
@@ -30,29 +30,30 @@ export function getLanguage(langCode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let currentTerms = lang.terms;
|
let currentTerms = lang.terms;
|
||||||
let d3Locale = d3.locale(lang.formats);
|
let d3Locale = d3.formatLocale(lang.formats);
|
||||||
let gen = d3Locale.numberFormat('n');
|
let gen = d3Locale.format('');
|
||||||
|
const round = function(x, n) { const ten_n = Math.pow(10,n); return Math.round(x * ten_n) / ten_n; };
|
||||||
|
|
||||||
if(lang === EN) {
|
if(lang === EN) {
|
||||||
translate = (t) => { return currentTerms[t] || t; };
|
translate = (t, x) => { return currentTerms[t + '_' + x] || currentTerms[t] || t; };
|
||||||
} else {
|
} else {
|
||||||
translate = (t) => { return currentTerms[t] || fallbackTerms[t] || t; };
|
translate = (t, x) => { return currentTerms[t + '_' + x] || currentTerms[t] || fallbackTerms[t + '_' + x] || fallbackTerms[t] || t; };
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
formats: {
|
formats: {
|
||||||
gen, // General number format (.e.g 1,001,001.1234)
|
gen, // General number format (.e.g 1,001,001.1234)
|
||||||
int: d3Locale.numberFormat(',.0f'), // Fixed to 0 decimal places (.e.g 1,001)
|
int: d3Locale.format(',.0f'), // Fixed to 0 decimal places (.e.g 1,001)
|
||||||
f1: d3Locale.numberFormat(',.1f'), // Fixed to 1 decimal place (.e.g 1,001.1)
|
f1: d3Locale.format(',.1f'), // Fixed to 1 decimal place (.e.g 1,001.1)
|
||||||
f2: d3Locale.numberFormat(',.2f'), // Fixed to 2 decimal places (.e.g 1,001.10)
|
f2: d3Locale.format(',.2f'), // Fixed to 2 decimal places (.e.g 1,001.10)
|
||||||
s2: d3Locale.numberFormat('.2s'), // SI Format to 2 decimal places (.e.g 1.1k)
|
s2: d3Locale.format('.2s'), // SI Format to 2 decimal places (.e.g 1.1k)
|
||||||
pct: d3Locale.numberFormat('.2%'), // % to 2 decimal places (.e.g 5.40%)
|
pct: d3Locale.format('.2%'), // % to 2 decimal places (.e.g 5.40%)
|
||||||
pct1: d3Locale.numberFormat('.1%'), // % to 1 decimal places (.e.g 5.4%)
|
pct1: d3Locale.format('.1%'), // % to 1 decimal places (.e.g 5.4%)
|
||||||
r1: d3Locale.numberFormat('.1r'), // Rounded to 1 significant number (.e.g 512 => 500, 4.122 => 4)
|
r1: d3Locale.format('.1r'), // Rounded to 1 significant number (.e.g 512 => 500, 4.122 => 4)
|
||||||
r2: d3Locale.numberFormat('.2r'), // Rounded to 2 significant numbers (.e.g 512 => 510, 4.122 => 4.1)
|
r2: d3Locale.format('.2r'), // Rounded to 2 significant numbers (.e.g 512 => 510, 4.122 => 4.1)
|
||||||
rPct: d3.format('%'), // % to 0 decimal places (.e.g 5%)
|
rPct: d3Locale.format('.0%'), // % to 0 decimal places (.e.g 5%)
|
||||||
round1: (d) => gen(d3.round(d, 1)), // Rounded to 0-1 decimal places (.e.g 5.1, 4)
|
round1: (d) => gen(round(d, 1)), // Round to 0-1 decimal places (e.g. 5.1, 4)
|
||||||
round: (d) => gen(d3.round(d, 2)), // Rounded to 0-2 decimal places (.e.g 5.12, 4.1)
|
round: (d) => gen(round(d, 2)), // Rounded to 0-2 decimal places (.e.g 5.12, 4.1)
|
||||||
time: (d) => (d < 0 ? '-' : '') + Math.floor(Math.abs(d) / 60) + ':' + ('00' + Math.floor(Math.abs(d) % 60)).substr(-2, 2)
|
time: (d) => (d < 0 ? '-' : '') + Math.floor(Math.abs(d) / 60) + ':' + ('00' + Math.floor(Math.abs(d) % 60)).substr(-2, 2)
|
||||||
},
|
},
|
||||||
translate,
|
translate,
|
||||||
|
|||||||
@@ -31,12 +31,13 @@ export const terms = {
|
|||||||
PHRASE_ENGAGEMENT_RANGE: 'The distance between your ship and its target',
|
PHRASE_ENGAGEMENT_RANGE: 'The distance between your ship and its target',
|
||||||
PHRASE_SELECT_BLUEPRINT: 'Click to select a blueprint',
|
PHRASE_SELECT_BLUEPRINT: 'Click to select a blueprint',
|
||||||
PHRASE_BLUEPRINT_WORST: 'Worst primary values for this 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_RANDOM: 'Random selection between worst and best primary values for this blueprint',
|
||||||
PHRASE_BLUEPRINT_BEST: 'Best primary values for this blueprint',
|
PHRASE_BLUEPRINT_BEST: 'Best primary values for this blueprint',
|
||||||
|
PHRASE_BLUEPRINT_EXTREME: 'Best beneficial and worst detrimental primary values for this blueprint',
|
||||||
PHRASE_BLUEPRINT_RESET: 'Remove all modifications and blueprint',
|
PHRASE_BLUEPRINT_RESET: 'Remove all modifications and blueprint',
|
||||||
PHRASE_SELECT_SPECIAL: 'Click to select an experimental effect',
|
PHRASE_SELECT_SPECIAL: 'Click to select an experimental effect',
|
||||||
PHRASE_NO_SPECIAL: 'No experimental effect',
|
PHRASE_NO_SPECIAL: 'No experimental effect',
|
||||||
|
PHRASE_SHOPPING_LIST: 'Stations that sell this build',
|
||||||
|
|
||||||
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',
|
||||||
|
|
||||||
@@ -98,8 +99,8 @@ export const terms = {
|
|||||||
// Items on the outfitting page
|
// Items on the outfitting page
|
||||||
// Notification of restricted slot
|
// Notification of restricted slot
|
||||||
emptyrestricted: 'empty (restricted)',
|
emptyrestricted: 'empty (restricted)',
|
||||||
'damage dealt against': 'Damage dealt against',
|
'damage dealt to': 'Damage dealt to',
|
||||||
'damage received by': 'Damage received by',
|
'damage received from': 'Damage received from',
|
||||||
'against shields': 'Against shields',
|
'against shields': 'Against shields',
|
||||||
'against hull': 'Against hull',
|
'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
|
||||||
@@ -115,6 +116,7 @@ export const terms = {
|
|||||||
average: 'Average',
|
average: 'Average',
|
||||||
random: 'Random',
|
random: 'Random',
|
||||||
best: 'Best',
|
best: 'Best',
|
||||||
|
extreme: 'Extreme',
|
||||||
reset: 'Reset',
|
reset: 'Reset',
|
||||||
|
|
||||||
// Weapon, offence, defence and movement
|
// Weapon, offence, defence and movement
|
||||||
@@ -187,6 +189,14 @@ export const terms = {
|
|||||||
wepcap: 'Weapons capacity',
|
wepcap: 'Weapons capacity',
|
||||||
weprate: 'Weapons recharge rate',
|
weprate: 'Weapons recharge rate',
|
||||||
|
|
||||||
|
// Shield generators use a different terminology
|
||||||
|
minmass_sg: 'Minimum hull mass',
|
||||||
|
optmass_sg: 'Optimal hull mass',
|
||||||
|
maxmass_sg: 'Maximum hull mass',
|
||||||
|
minmul_sg: 'Minimum strength',
|
||||||
|
optmul_sg: 'Optimal strength',
|
||||||
|
maxmul_sg: 'Minimum strength',
|
||||||
|
|
||||||
// Help text
|
// Help text
|
||||||
HELP_TEXT: `
|
HELP_TEXT: `
|
||||||
<h1>Introduction</h1>
|
<h1>Introduction</h1>
|
||||||
@@ -202,6 +212,8 @@ Once you have a working companion API connection go to the 'Shipyard'
|
|||||||
|
|
||||||
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>
|
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>
|
||||||
|
|
||||||
|
Also, the imported information does not provide any data on the power priority or enabled status of your cargo hatch. Coriolis sets this item to have a power priority of "5" and to be disabled by default. You can change this after import in the Power Management section. </p>
|
||||||
|
|
||||||
<h2>Importing Your Ship From EDMC</h2>
|
<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>
|
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>
|
||||||
|
|
||||||
@@ -226,15 +238,7 @@ 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>
|
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>
|
Clicking on the headings for each set of modules gives you the ability to either select an overall role for your ship (when clicking the core internal header) or a specific module with which you want to fill all applicable slots (when clicking the other headers). </p>
|
||||||
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>
|
<h2>Offence Summary</h2>
|
||||||
The offence summary panel provides information about the damage that you deal with your weapons.</p>
|
The offence summary panel provides information about the damage that you deal with your weapons.</p>
|
||||||
@@ -267,15 +271,31 @@ Along the top of this panel are the number of pips you put in to your ENG capaci
|
|||||||
<dt>Yaw</dt><dd>The fastest the ship can turn its nose left or right, 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>
|
</dl>
|
||||||
|
|
||||||
|
<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>Engine Profile</h2>
|
||||||
|
The engine profile panel provides information about the capabilities of your current thrusters. The graph shows you how the maximum speed (with 4 pips to engines) alters with the overall mass of your build. The slider can be altered to change the amount of cargo you have on-board. Your engine profile can be altered by obtaining different thrusters or engineering your existing thrusters.</p>
|
||||||
|
|
||||||
|
<h2>FSD Profile</h2>
|
||||||
|
The FSD profile panel provides information about the capabilities of your current frame shift drive. The graph shows you how the maximum jump range alters with the overall mass of your build. The slider can be altered to change the amount of cargo you have on-board. Your FSD profile can be altered by obtaining a different FSD or engineering your existing FSD.</p>
|
||||||
|
|
||||||
<h2>Jump Range</h2>
|
<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.
|
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.</p>
|
||||||
|
|
||||||
<h2>Damage Dealt</h2>
|
<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 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 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>
|
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, and assumes standard shield resistances. Effectiveness against hull takes in to account the weapon and, its engagement range and the target's hardness, and assumes military grade armour resistances.</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>
|
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>
|
||||||
|
|
||||||
@@ -283,7 +303,9 @@ Total effective DPS, SDPS and effectiveness against both shields and hull are pr
|
|||||||
|
|
||||||
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.
|
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.
|
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.</p>
|
||||||
|
|
||||||
|
At the bottom of this panel are two graphs showing how your sustained DPS changes with engagement range. This shows at a glance how effective each weapon is at different distances.</p>
|
||||||
|
|
||||||
<h2>Damage Received</h2>
|
<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>
|
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>
|
||||||
|
|||||||
@@ -39,10 +39,16 @@ export default class ErrorDetails extends React.Component {
|
|||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const importerror = ed && ed.scriptUrl && ed.scriptUrl.indexOf('/import') != -1;
|
||||||
|
|
||||||
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>
|
||||||
|
<br/>
|
||||||
|
{importerror ? <div>If you are attempting to import a ship from EDDI or EDMC and are seeing a 'Z_BUF_ERROR' it means that the URL has not been provided correctly. This is a common problem when using Microsoft Internet Explorer or Microsoft Edge, and you should use another browser instead.</div> : null }
|
||||||
|
<br/>
|
||||||
<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>
|
<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>
|
||||||
|
<br/>
|
||||||
{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, LinkIcon } from '../components/SvgIcons';
|
import { FloppyDisk, Bin, Switch, Download, Reload, LinkIcon, ShoppingIcon } 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';
|
||||||
@@ -17,18 +17,17 @@ 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 MovementSummary from '../components/MovementSummary';
|
||||||
|
import EngineProfile from '../components/EngineProfile';
|
||||||
|
import FSDProfile from '../components/FSDProfile';
|
||||||
|
import JumpRange from '../components/JumpRange';
|
||||||
import DamageDealt from '../components/DamageDealt';
|
import DamageDealt from '../components/DamageDealt';
|
||||||
import DamageReceived from '../components/DamageReceived';
|
import DamageReceived from '../components/DamageReceived';
|
||||||
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 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_COLORS = ['#0088d2', '#ff8c0d', '#D26D00', '#c06400'];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Document Title Generator
|
* Document Title Generator
|
||||||
* @param {String} shipName Ship Name
|
* @param {String} shipName Ship Name
|
||||||
@@ -81,7 +80,6 @@ export default class OutfittingPage extends Page {
|
|||||||
ship.buildWith(data.defaults); // Populate with default components
|
ship.buildWith(data.defaults); // Populate with default components
|
||||||
}
|
}
|
||||||
|
|
||||||
let fuelCapacity = ship.fuelCapacity;
|
|
||||||
this._getTitle = getTitle.bind(this, data.properties.name);
|
this._getTitle = getTitle.bind(this, data.properties.name);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -93,12 +91,7 @@ export default class OutfittingPage extends Page {
|
|||||||
shipId,
|
shipId,
|
||||||
ship,
|
ship,
|
||||||
code,
|
code,
|
||||||
savedCode,
|
savedCode
|
||||||
fuelCapacity,
|
|
||||||
fuelLevel: 1,
|
|
||||||
jumpRangeChartFunc: ship.calcJumpRangeWith.bind(ship, fuelCapacity),
|
|
||||||
fastestRangeChartFunc: ship.calcFastestRangeWith.bind(ship, fuelCapacity),
|
|
||||||
speedChartFunc: ship.calcSpeedsWith.bind(ship, fuelCapacity)
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,13 +186,9 @@ export default class OutfittingPage extends Page {
|
|||||||
* Trigger render on ship model change
|
* Trigger render on ship model change
|
||||||
*/
|
*/
|
||||||
_shipUpdated() {
|
_shipUpdated() {
|
||||||
let { shipId, buildName, ship, fuelCapacity } = this.state;
|
let { shipId, buildName, ship } = this.state;
|
||||||
let code = ship.toString();
|
let code = ship.toString();
|
||||||
|
|
||||||
if (fuelCapacity != ship.fuelCapacity) {
|
|
||||||
this._fuelChange(this.state.fuelLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._updateRoute(shipId, buildName, code);
|
this._updateRoute(shipId, buildName, code);
|
||||||
this.setState({ code });
|
this.setState({ code });
|
||||||
}
|
}
|
||||||
@@ -214,23 +203,6 @@ export default class OutfittingPage extends Page {
|
|||||||
Router.replace(outfitURL(shipId, code, buildName));
|
Router.replace(outfitURL(shipId, code, buildName));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Update current fuel level
|
|
||||||
* @param {number} fuelLevel Fuel leval 0 - 1
|
|
||||||
*/
|
|
||||||
_fuelChange(fuelLevel) {
|
|
||||||
let ship = this.state.ship;
|
|
||||||
let fuelCapacity = ship.fuelCapacity;
|
|
||||||
let fuel = fuelCapacity * fuelLevel;
|
|
||||||
this.setState({
|
|
||||||
fuelLevel,
|
|
||||||
fuelCapacity,
|
|
||||||
jumpRangeChartFunc: ship.calcJumpRangeWith.bind(ship, fuel),
|
|
||||||
fastestRangeChartFunc: ship.calcFastestRangeWith.bind(ship, fuel),
|
|
||||||
speedChartFunc: ship.calcSpeedsWith.bind(ship, fuel)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update dimenions from rendered DOM
|
* Update dimenions from rendered DOM
|
||||||
*/
|
*/
|
||||||
@@ -239,7 +211,8 @@ export default class OutfittingPage extends Page {
|
|||||||
|
|
||||||
if (elem) {
|
if (elem) {
|
||||||
this.setState({
|
this.setState({
|
||||||
chartWidth: findDOMNode(this.refs.chartThird).offsetWidth
|
thirdChartWidth: findDOMNode(this.refs.chartThird).offsetWidth,
|
||||||
|
halfChartWidth: findDOMNode(this.refs.chartThird).offsetWidth * 3 / 2
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -284,6 +257,20 @@ export default class OutfittingPage extends Page {
|
|||||||
this.context.showModal(<ModalPermalink url={window.location.href}/>);
|
this.context.showModal(<ModalPermalink url={window.location.href}/>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open up a window for EDDB with a shopping list of our components
|
||||||
|
*/
|
||||||
|
_eddbShoppingList() {
|
||||||
|
const ship = this.state.ship;
|
||||||
|
|
||||||
|
const shipId = Ships[ship.id].eddbID;
|
||||||
|
// Provide unique list of non-PP module EDDB IDs
|
||||||
|
const modIds = ship.internal.concat(ship.bulkheads, ship.standard, ship.hardpoints).filter(slot => slot !== null && slot.m !== null && !slot.m.pp).map(slot => slot.m.eddbID).filter((v, i, a) => a.indexOf(v) === i);
|
||||||
|
|
||||||
|
// Open up the relevant URL
|
||||||
|
window.open('https://eddb.io/station?s=' + shipId + '&m=' + modIds.join(','));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle Key Down
|
* Handle Key Down
|
||||||
* @param {Event} e Keyboard Event
|
* @param {Event} e Keyboard Event
|
||||||
@@ -308,7 +295,7 @@ export default class OutfittingPage extends Page {
|
|||||||
let state = this.state,
|
let state = this.state,
|
||||||
{ language, termtip, tooltip, sizeRatio, onWindowResize } = this.context,
|
{ language, termtip, tooltip, sizeRatio, onWindowResize } = this.context,
|
||||||
{ translate, units, formats } = language,
|
{ translate, units, formats } = language,
|
||||||
{ ship, code, savedCode, buildName, newBuildName, chartWidth, fuelCapacity, fuelLevel } = state,
|
{ ship, code, savedCode, buildName, newBuildName, halfChartWidth, thirdChartWidth } = state,
|
||||||
hide = tooltip.bind(null, null),
|
hide = tooltip.bind(null, null),
|
||||||
menu = this.props.currentMenu,
|
menu = this.props.currentMenu,
|
||||||
shipUpdated = this._shipUpdated,
|
shipUpdated = this._shipUpdated,
|
||||||
@@ -316,9 +303,11 @@ export default class OutfittingPage extends Page {
|
|||||||
canRename = buildName && newBuildName && buildName != newBuildName,
|
canRename = buildName && newBuildName && buildName != newBuildName,
|
||||||
canReload = savedCode && canSave,
|
canReload = savedCode && canSave,
|
||||||
hStr = ship.getHardpointsString() + '.' + ship.getModificationsString(),
|
hStr = ship.getHardpointsString() + '.' + ship.getModificationsString(),
|
||||||
sStr = ship.getStandardString() + '.' + ship.getModificationsString(),
|
|
||||||
iStr = ship.getInternalString() + '.' + ship.getModificationsString();
|
iStr = ship.getInternalString() + '.' + ship.getModificationsString();
|
||||||
|
|
||||||
|
// Code can be blank for a default loadout. Prefix it with the ship name to ensure that changes in default ships is picked up
|
||||||
|
code = ship.name + (code || '');
|
||||||
|
|
||||||
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'>
|
||||||
@@ -343,6 +332,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._eddbShoppingList} onMouseOver={termtip.bind(null, 'PHRASE_SHOPPING_LIST')} onMouseOut={hide}>
|
||||||
|
<ShoppingIcon className='lg' />
|
||||||
|
</button>
|
||||||
<button onClick={this._genShortlink} onMouseOver={termtip.bind(null, 'shortlink')} onMouseOut={hide}>
|
<button onClick={this._genShortlink} onMouseOver={termtip.bind(null, 'shortlink')} onMouseOut={hide}>
|
||||||
<LinkIcon className='lg' />
|
<LinkIcon className='lg' />
|
||||||
</button>
|
</button>
|
||||||
@@ -350,64 +342,38 @@ export default class OutfittingPage extends Page {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ShipSummaryTable ship={ship} code={code} />
|
<ShipSummaryTable ship={ship} code={code} />
|
||||||
<StandardSlotSection ship={ship} code={sStr} onChange={shipUpdated} currentMenu={menu} />
|
<StandardSlotSection ship={ship} code={code} onChange={shipUpdated} currentMenu={menu} />
|
||||||
<InternalSlotSection ship={ship} code={iStr} onChange={shipUpdated} currentMenu={menu} />
|
<InternalSlotSection ship={ship} code={iStr} onChange={shipUpdated} currentMenu={menu} />
|
||||||
<HardpointsSlotSection ship={ship} code={hStr} onChange={shipUpdated} currentMenu={menu} />
|
<HardpointsSlotSection ship={ship} code={hStr || ''} onChange={shipUpdated} currentMenu={menu} />
|
||||||
<UtilitySlotSection ship={ship} code={hStr} onChange={shipUpdated} currentMenu={menu} />
|
<UtilitySlotSection ship={ship} code={hStr || ''} onChange={shipUpdated} currentMenu={menu} />
|
||||||
<PowerManagement ship={ship} code={code} onChange={shipUpdated} />
|
|
||||||
<CostSection ship={ship} buildName={buildName} code={sStr + hStr + iStr} />
|
|
||||||
|
|
||||||
<div className='group third'>
|
<div ref='chartThird' className='group third'>
|
||||||
<OffenceSummary ship={ship} code={code}/>
|
<OffenceSummary ship={ship} code={code}/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='group third'>
|
<div className='group third'>
|
||||||
<DefenceSummary ship={ship} code={code}/>
|
<DefenceSummary ship={ship} code={code}/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='group third'>
|
<div className='group third'>
|
||||||
<MovementSummary ship={ship} code={code}/>
|
<MovementSummary ship={ship} code={code}/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div ref='chartThird' className='group third'>
|
<PowerManagement ship={ship} code={code} onChange={shipUpdated} />
|
||||||
<h1>{translate('jump range')}</h1>
|
<CostSection ship={ship} buildName={buildName} code={code} />
|
||||||
<LineChart
|
|
||||||
width={chartWidth}
|
<div className='group third'>
|
||||||
xMax={ship.cargoCapacity}
|
<EngineProfile ship={ship} code={code} chartWidth={thirdChartWidth} />
|
||||||
yMax={ship.unladenRange}
|
</div>
|
||||||
xUnit={translate('T')}
|
|
||||||
yUnit={translate('LY')}
|
<div className='group third'>
|
||||||
yLabel={translate('jump range')}
|
<FSDProfile ship={ship} code={code} chartWidth={thirdChartWidth} />
|
||||||
xLabel={translate('cargo')}
|
</div>
|
||||||
func={state.jumpRangeChartFunc}
|
|
||||||
/>
|
<div className='group third'>
|
||||||
<table style={{ width: '100%', lineHeight: '1em', backgroundColor: 'transparent' }}>
|
<JumpRange ship={ship} code={code} chartWidth={thirdChartWidth} />
|
||||||
<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>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<DamageDealt ship={ship} code={code} currentMenu={menu}/>
|
<DamageDealt ship={ship} code={code} chartWidth={halfChartWidth} currentMenu={menu}/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@@ -418,56 +384,3 @@ export default class OutfittingPage extends Page {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// <div ref='chartThird' className='group third'>
|
|
||||||
// <h1>{translate('jump range')}</h1>
|
|
||||||
// <LineChart
|
|
||||||
// width={chartWidth}
|
|
||||||
// xMax={ship.cargoCapacity}
|
|
||||||
// yMax={ship.unladenRange}
|
|
||||||
// xUnit={translate('T')}
|
|
||||||
// yUnit={translate('LY')}
|
|
||||||
// yLabel={translate('jump range')}
|
|
||||||
// xLabel={translate('cargo')}
|
|
||||||
// func={state.jumpRangeChartFunc}
|
|
||||||
// />
|
|
||||||
// </div>
|
|
||||||
// <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>
|
|
||||||
|
|||||||
@@ -39,6 +39,14 @@ export default class Page extends React.Component {
|
|||||||
this[prop] = this[prop].bind(this);
|
this[prop] = this[prop].bind(this);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let fix = sessionStorage.getItem('__safari_history_fix');
|
||||||
|
sessionStorage.removeItem('__safari_history_fix');
|
||||||
|
if (fix) {
|
||||||
|
fix = JSON.parse(fix);
|
||||||
|
history.replaceState(history.state, document.title, location.href);
|
||||||
|
history.pushState(fix.state, fix.title, fix.path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -82,4 +90,4 @@ export default class Page extends React.Component {
|
|||||||
return this.renderPage();
|
return this.renderPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,6 +143,7 @@ export default class ShipyardPage extends Page {
|
|||||||
<td className='cap'>{translate(SizeMap[s.class])}</td>
|
<td className='cap'>{translate(SizeMap[s.class])}</td>
|
||||||
<td className='ri'>{fInt(s.agility)}</td>
|
<td className='ri'>{fInt(s.agility)}</td>
|
||||||
<td className='ri'>{fInt(s.hardness)}</td>
|
<td className='ri'>{fInt(s.hardness)}</td>
|
||||||
|
<td className='ri'>{fInt(s.crew)}</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>
|
||||||
@@ -270,6 +271,7 @@ export default class ShipyardPage extends Page {
|
|||||||
<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' onClick={sortShips('agility')}>{translate('agility')}</th>
|
<th rowSpan={2} className='sortable' onClick={sortShips('agility')}>{translate('agility')}</th>
|
||||||
<th rowSpan={2} className='sortable' onClick={sortShips('hardness')}>{translate('hardness')}</th>
|
<th rowSpan={2} className='sortable' onClick={sortShips('hardness')}>{translate('hardness')}</th>
|
||||||
|
<th rowSpan={2} className='sortable' onClick={sortShips('crew')}>{translate('crew')}</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={6}>{translate('core module classes')}</th>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import * as ModuleUtils from './ModuleUtils';
|
import * as ModuleUtils from './ModuleUtils';
|
||||||
import * as _ from 'lodash';
|
|
||||||
import { Modifications } from 'coriolis-data/dist';
|
import { Modifications } from 'coriolis-data/dist';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -702,6 +701,16 @@ export default class Module {
|
|||||||
* @return {string} the shot speed for this module
|
* @return {string} the shot speed for this module
|
||||||
*/
|
*/
|
||||||
getShotSpeed() {
|
getShotSpeed() {
|
||||||
|
if (this.blueprint && (this.blueprint.name === 'Focused' || this.blueprintname === 'Long Range')) {
|
||||||
|
// If the modification is focused or long range then the shot speed
|
||||||
|
// uses the range modifier
|
||||||
|
const rangemod = this.getModValue('range') / 10000;
|
||||||
|
let result = this['shotspeed'];
|
||||||
|
if (!result) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return result * (1 + rangemod);
|
||||||
|
}
|
||||||
return this._getModifiedValue('shotspeed');
|
return this._getModifiedValue('shotspeed');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,10 @@
|
|||||||
import { ModuleGroupToName, MountMap, BulkheadNames } from './Constants';
|
import { ModuleGroupToName, MountMap, BulkheadNames } from './Constants';
|
||||||
import { Ships } from 'coriolis-data/dist';
|
import { Ships } from 'coriolis-data/dist';
|
||||||
import Ship from './Ship';
|
import Ship from './Ship';
|
||||||
import * as ModuleUtils from './ModuleUtils';
|
|
||||||
import * as Utils from '../utils/UtilityFunctions';
|
import * as Utils from '../utils/UtilityFunctions';
|
||||||
import LZString from 'lz-string';
|
import LZString from 'lz-string';
|
||||||
import { outfitURL } from '../utils/UrlGenerators';
|
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
|
* Generates ship-loadout JSON Schema standard object
|
||||||
* @param {Object} standard model
|
* @param {Object} standard model
|
||||||
|
|||||||
@@ -835,7 +835,6 @@ export default class Ship {
|
|||||||
*/
|
*/
|
||||||
setSlotPriority(slot, newPriority) {
|
setSlotPriority(slot, newPriority) {
|
||||||
if (newPriority >= 0 && newPriority < this.priorityBands.length) {
|
if (newPriority >= 0 && newPriority < this.priorityBands.length) {
|
||||||
let oldPriority = slot.priority;
|
|
||||||
slot.priority = newPriority;
|
slot.priority = newPriority;
|
||||||
this.updatePowerPrioritesString();
|
this.updatePowerPrioritesString();
|
||||||
|
|
||||||
@@ -1437,7 +1436,6 @@ export default class Ship {
|
|||||||
|
|
||||||
let bulkheadMods = new Array();
|
let bulkheadMods = new Array();
|
||||||
let bulkheadBlueprint = null;
|
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
|
||||||
|
|||||||
@@ -31,20 +31,38 @@ export function multiPurpose(ship, shielded, bulkheadIndex) {
|
|||||||
* @param {Object} standardOpts [Optional] Standard module optional overrides
|
* @param {Object} standardOpts [Optional] Standard module optional overrides
|
||||||
*/
|
*/
|
||||||
export function trader(ship, shielded, standardOpts) {
|
export function trader(ship, shielded, standardOpts) {
|
||||||
let sg = shielded ? ship.getAvailableModules().lightestShieldGenerator(ship.hullMass) : null;
|
let usedSlots = [],
|
||||||
|
sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass);
|
||||||
for (let i = ship.internal.length; i--;) {
|
|
||||||
let slot = ship.internal[i];
|
// Shield generator if required
|
||||||
if (sg && canMount(ship, slot, 'sg', sg.class)) {
|
if (shielded) {
|
||||||
ship.use(slot, sg);
|
const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8];
|
||||||
sg = null;
|
const shieldInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
||||||
} else {
|
.filter(a => (!a.eligible) || a.eligible.sg)
|
||||||
if (canMount(ship, slot, 'cr')) {
|
.filter(a => a.maxClass >= sg.class)
|
||||||
ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E'));
|
.sort((a,b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass));
|
||||||
|
for (let i = 0; i < shieldInternals.length; i++) {
|
||||||
|
if (canMount(ship, shieldInternals[i], 'sg')) {
|
||||||
|
ship.use(shieldInternals[i], sg);
|
||||||
|
usedSlots.push(shieldInternals[i]);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fill the empty internals with cargo racks
|
||||||
|
for (let i = ship.internal.length; i--;) {
|
||||||
|
let slot = ship.internal[i];
|
||||||
|
if (usedSlots.indexOf(slot) == -1 && canMount(ship, slot, 'cr')) {
|
||||||
|
ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty the hardpoints
|
||||||
|
for (let s of ship.hardpoints) {
|
||||||
|
ship.use(s, null);
|
||||||
|
}
|
||||||
|
|
||||||
ship.useLightestStandard(standardOpts);
|
ship.useLightestStandard(standardOpts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,53 +73,102 @@ export function trader(ship, shielded, standardOpts) {
|
|||||||
*/
|
*/
|
||||||
export function explorer(ship, planetary) {
|
export function explorer(ship, planetary) {
|
||||||
let standardOpts = { ppRating: 'A' },
|
let standardOpts = { ppRating: 'A' },
|
||||||
intLength = ship.internal.length,
|
|
||||||
heatSinkCount = 2, // Fit 2 heat sinks if possible
|
heatSinkCount = 2, // Fit 2 heat sinks if possible
|
||||||
afmUnitCount = 2, // Fit 2 AFM Units if possible
|
usedSlots = [],
|
||||||
shieldNext = planetary,
|
|
||||||
sgSlot,
|
sgSlot,
|
||||||
fuelScoopSlot,
|
fuelScoopSlot,
|
||||||
pvhSlot,
|
|
||||||
sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass);
|
sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass);
|
||||||
|
|
||||||
if (!planetary) { // Non-planetary explorers don't really need to boost
|
if (!planetary) { // Non-planetary explorers don't really need to boost
|
||||||
standardOpts.pd = '1D';
|
standardOpts.pd = '1D';
|
||||||
}
|
}
|
||||||
|
|
||||||
ship.setSlotEnabled(ship.cargoHatch, false)
|
// Cargo hatch can be disabled
|
||||||
.use(ship.internal[--intLength], ModuleUtils.internal('2f')); // Advanced Discovery Scanner
|
ship.setSlotEnabled(ship.cargoHatch, false);
|
||||||
|
|
||||||
if (!planetary || intLength > 3) { // Don't mount a DDS on planetary explorer ships too small for both a PVH and DDS
|
// Advanced Discovery Scanner - class 1 or higher
|
||||||
ship.use(ship.internal[--intLength], ModuleUtils.internal('2i')); // Detailed Surface Scanner
|
const adsOrder = [1, 2, 3, 4, 5, 6, 7, 8];
|
||||||
|
const adsInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
||||||
|
.filter(a => (!a.eligible) || a.eligible.sc)
|
||||||
|
.sort((a,b) => adsOrder.indexOf(a.maxClass) - adsOrder.indexOf(b.maxClass));
|
||||||
|
for (let i = 0; i < adsInternals.length; i++) {
|
||||||
|
if (canMount(ship, adsInternals[i], 'sc')) {
|
||||||
|
ship.use(adsInternals[i], ModuleUtils.internal('2f'));
|
||||||
|
usedSlots.push(adsInternals[i]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < intLength; i++) {
|
if (planetary) {
|
||||||
let slot = ship.internal[i];
|
// Planetary Vehicle Hangar - class 2 or higher
|
||||||
let nextSlot = (i + 1) < intLength ? ship.internal[i + 1] : null;
|
const pvhOrder = [2, 3, 4, 5, 6, 7, 8, 1];
|
||||||
// Fit best possible Fuel Scoop
|
const pvhInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
||||||
if (!fuelScoopSlot && canMount(ship, slot, 'fs')) {
|
.filter(a => (!a.eligible) || a.eligible.pv)
|
||||||
fuelScoopSlot = slot;
|
.sort((a,b) => pvhOrder.indexOf(a.maxClass) - pvhOrder.indexOf(b.maxClass));
|
||||||
ship.use(slot, ModuleUtils.findInternal('fs', slot.maxClass, 'A'));
|
for (let i = 0; i < pvhInternals.length; i++) {
|
||||||
ship.setSlotEnabled(slot, true);
|
if (canMount(ship, pvhInternals[i], 'pv')) {
|
||||||
// Mount a Shield generator if possible AND an AFM Unit has been mounted already (Guarantees at least 1 AFM Unit)
|
// Planetary Vehical Hangar only has even classes
|
||||||
} else if (!sgSlot && shieldNext && canMount(ship, slot, 'sg', sg.class) && !canMount(ship, nextSlot, 'sg', sg.class)) {
|
const pvhClass = pvhInternals[i].maxClass % 2 === 1 ? pvhInternals[i].maxClass - 1 : pvhInternals[i].maxClass;
|
||||||
sgSlot = slot;
|
ship.use(pvhInternals[i], ModuleUtils.findInternal('pv', pvhClass, 'G')); // G is lower mass
|
||||||
shieldNext = false;
|
ship.setSlotEnabled(pvhInternals[i], false); // Disable power for Planetary Vehical Hangar
|
||||||
ship.use(slot, sg);
|
usedSlots.push(pvhInternals[i]);
|
||||||
ship.setSlotEnabled(slot, true);
|
break;
|
||||||
// if planetary explorer and the next slot cannot mount a PVH or the next modul to mount is a SG
|
}
|
||||||
} else if (planetary && !pvhSlot && canMount(ship, slot, 'pv') && (shieldNext || !canMount(ship, nextSlot, 'pv', 2))) {
|
}
|
||||||
pvhSlot = slot;
|
}
|
||||||
ship.use(slot, ModuleUtils.findInternal('pv', Math.min(Math.floor(pvhSlot.maxClass / 2) * 2, 6), 'G'));
|
|
||||||
ship.setSlotEnabled(slot, false); // Disabled power for PVH
|
// Shield generator
|
||||||
shieldNext = !sgSlot;
|
const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8];
|
||||||
} else if (afmUnitCount > 0 && canMount(ship, slot, 'am')) {
|
const shieldInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
||||||
afmUnitCount--;
|
.filter(a => (!a.eligible) || a.eligible.sg)
|
||||||
ship.use(slot, ModuleUtils.findInternal('am', slot.maxClass, 'A'));
|
.filter(a => a.maxClass >= sg.class)
|
||||||
ship.setSlotEnabled(slot, false); // Disabled power for AFM Unit
|
.sort((a,b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass));
|
||||||
shieldNext = !sgSlot;
|
for (let i = 0; i < shieldInternals.length; i++) {
|
||||||
} else {
|
if (canMount(ship, shieldInternals[i], 'sg')) {
|
||||||
ship.use(slot, null);
|
ship.use(shieldInternals[i], sg);
|
||||||
|
usedSlots.push(shieldInternals[i]);
|
||||||
|
sgSlot = shieldInternals[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detailed Surface Scanner
|
||||||
|
const dssOrder = [1, 2, 3, 4, 5, 6, 7, 8];
|
||||||
|
const dssInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
||||||
|
.filter(a => (!a.eligible) || a.eligible.sc)
|
||||||
|
.sort((a,b) => dssOrder.indexOf(a.maxClass) - dssOrder.indexOf(b.maxClass));
|
||||||
|
for (let i = 0; i < dssInternals.length; i++) {
|
||||||
|
if (canMount(ship, dssInternals[i], 'sc')) {
|
||||||
|
ship.use(dssInternals[i], ModuleUtils.internal('2i'));
|
||||||
|
usedSlots.push(dssInternals[i]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fuel scoop - best possible
|
||||||
|
const fuelScoopOrder = [8, 7, 6, 5, 4, 3, 2, 1];
|
||||||
|
const fuelScoopInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
||||||
|
.filter(a => (!a.eligible) || a.eligible.fs)
|
||||||
|
.sort((a,b) => fuelScoopOrder.indexOf(a.maxClass) - fuelScoopOrder.indexOf(b.maxClass));
|
||||||
|
for (let i = 0; i < fuelScoopInternals.length; i++) {
|
||||||
|
if (canMount(ship, fuelScoopInternals[i], 'fs')) {
|
||||||
|
ship.use(fuelScoopInternals[i], ModuleUtils.findInternal('fs', fuelScoopInternals[i].maxClass, 'A'));
|
||||||
|
usedSlots.push(fuelScoopInternals[i]);
|
||||||
|
fuelScoopSlot = fuelScoopInternals[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AFMUs - fill as they are 0-weight
|
||||||
|
const afmuOrder = [8, 7, 6, 5, 4, 3, 2, 1];
|
||||||
|
const afmuInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
||||||
|
.filter(a => (!a.eligible) || a.eligible.pc)
|
||||||
|
.sort((a,b) => afmuOrder.indexOf(a.maxClass) - afmuOrder.indexOf(b.maxClass));
|
||||||
|
for (let i = 0; i < afmuInternals.length; i++) {
|
||||||
|
if (canMount(ship, afmuInternals[i], 'am')) {
|
||||||
|
ship.use(afmuInternals[i], ModuleUtils.findInternal('am', afmuInternals[i].maxClass, 'A'));
|
||||||
|
usedSlots.push(afmuInternals[i]);
|
||||||
|
ship.setSlotEnabled(afmuInternals[i], false); // Disable power for AFM Unit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,7 +182,7 @@ export function explorer(ship, planetary) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sgSlot) {
|
if (sgSlot && fuelScoopSlot) {
|
||||||
// The SG and Fuel scoop to not need to be powered at the same time
|
// The SG and Fuel scoop to not need to be powered at the same time
|
||||||
if (sgSlot.m.getPowerUsage() > fuelScoopSlot.m.getPowerUsage()) { // The Shield generator uses the most power
|
if (sgSlot.m.getPowerUsage() > fuelScoopSlot.m.getPowerUsage()) { // The Shield generator uses the most power
|
||||||
ship.setSlotEnabled(fuelScoopSlot, false);
|
ship.setSlotEnabled(fuelScoopSlot, false);
|
||||||
@@ -126,3 +193,104 @@ export function explorer(ship, planetary) {
|
|||||||
|
|
||||||
ship.useLightestStandard(standardOpts);
|
ship.useLightestStandard(standardOpts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Miner Role
|
||||||
|
* @param {Ship} ship Ship instance
|
||||||
|
* @param {Boolean} shielded True if shield generator should be included
|
||||||
|
*/
|
||||||
|
export function miner(ship, shielded) {
|
||||||
|
let standardOpts = { ppRating: 'A' },
|
||||||
|
miningLaserCount = 2,
|
||||||
|
usedSlots = [],
|
||||||
|
sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass);
|
||||||
|
|
||||||
|
// Cargo hatch should be enabled
|
||||||
|
ship.setSlotEnabled(ship.cargoHatch, true);
|
||||||
|
|
||||||
|
// 4A or largest possible refinery
|
||||||
|
const refineryOrder = [4, 5, 6, 7, 8, 3, 2, 1];
|
||||||
|
const refineryInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
||||||
|
.filter(a => (!a.eligible) || a.eligible.rf)
|
||||||
|
.sort((a,b) => refineryOrder.indexOf(a.maxClass) - refineryOrder.indexOf(b.maxClass));
|
||||||
|
for (let i = 0; i < refineryInternals.length; i++) {
|
||||||
|
if (canMount(ship, refineryInternals[i], 'rf')) {
|
||||||
|
ship.use(refineryInternals[i], ModuleUtils.findInternal('rf', refineryInternals[i].maxClass, 'A'));
|
||||||
|
usedSlots.push(refineryInternals[i]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prospector limpet controller - 3A if possible
|
||||||
|
const prospectorOrder = [3, 4, 5, 6, 7, 8, 2, 1];
|
||||||
|
const prospectorInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
||||||
|
.filter(a => (!a.eligible) || a.eligible.pc)
|
||||||
|
.sort((a,b) => prospectorOrder.indexOf(a.maxClass) - prospectorOrder.indexOf(b.maxClass));
|
||||||
|
for (let i = 0; i < prospectorInternals.length; i++) {
|
||||||
|
if (canMount(ship, prospectorInternals[i], 'pc')) {
|
||||||
|
// Prospector only has odd classes
|
||||||
|
const prospectorClass = prospectorInternals[i].maxClass % 2 === 0 ? prospectorInternals[i].maxClass - 1 : prospectorInternals[i].maxClass;
|
||||||
|
ship.use(prospectorInternals[i], ModuleUtils.findInternal('pc', prospectorClass, 'A'));
|
||||||
|
usedSlots.push(prospectorInternals[i]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shield generator if required
|
||||||
|
if (shielded) {
|
||||||
|
const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8];
|
||||||
|
const shieldInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
||||||
|
.filter(a => (!a.eligible) || a.eligible.sg)
|
||||||
|
.filter(a => a.maxClass >= sg.class)
|
||||||
|
.sort((a,b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass));
|
||||||
|
for (let i = 0; i < shieldInternals.length; i++) {
|
||||||
|
if (canMount(ship, shieldInternals[i], 'sg')) {
|
||||||
|
ship.use(shieldInternals[i], sg);
|
||||||
|
usedSlots.push(shieldInternals[i]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collector limpet controller if there are enough internals left
|
||||||
|
let collectorLimpetsRequired = Math.max(ship.internal.filter(a => (!a.eligible) || a.eligible.cr).length - 6, 0);
|
||||||
|
if (collectorLimpetsRequired > 0) {
|
||||||
|
const collectorOrder = [1, 2, 3, 4, 5, 6, 7, 8];
|
||||||
|
const collectorInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
||||||
|
.filter(a => (!a.eligible) || a.eligible.cc)
|
||||||
|
.sort((a,b) => collectorOrder.indexOf(a.maxClass) - collectorOrder.indexOf(b.maxClass));
|
||||||
|
for (let i = 0; i < collectorInternals.length && collectorLimpetsRequired > 0; i++) {
|
||||||
|
if (canMount(ship, collectorInternals[i], 'cc')) {
|
||||||
|
// Collector only has odd classes
|
||||||
|
const collectorClass = collectorInternals[i].maxClass % 2 === 0 ? collectorInternals[i].maxClass - 1 : collectorInternals[i].maxClass;
|
||||||
|
ship.use(collectorInternals[i], ModuleUtils.findInternal('cc', collectorClass, 'A'));
|
||||||
|
usedSlots.push(collectorInternals[i]);
|
||||||
|
collectorLimpetsRequired -= collectorInternals[i].m.maximum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dual mining lasers of highest possible class; remove anything else
|
||||||
|
const miningLaserOrder = [2, 3, 4, 1, 0];
|
||||||
|
const miningLaserHardpoints = ship.hardpoints.concat().sort(function(a,b) {
|
||||||
|
return miningLaserOrder.indexOf(a.maxClass) - miningLaserOrder.indexOf(b.maxClass);
|
||||||
|
});
|
||||||
|
for (let s of miningLaserHardpoints) {
|
||||||
|
if (s.maxClass >= 1 && miningLaserCount) {
|
||||||
|
ship.use(s, ModuleUtils.hardpoints(s.maxClass >= 2 ? '2m' : '2l'));
|
||||||
|
miningLaserCount--;
|
||||||
|
} else {
|
||||||
|
ship.use(s, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill the empty internals with cargo racks
|
||||||
|
for (let i = ship.internal.length; i--;) {
|
||||||
|
let slot = ship.internal[i];
|
||||||
|
if (usedSlots.indexOf(slot) == -1 && canMount(ship, slot, 'cr')) {
|
||||||
|
ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ship.useLightestStandard(standardOpts);
|
||||||
|
}
|
||||||
|
|||||||
@@ -243,7 +243,6 @@ export function shipFromJson(json) {
|
|||||||
let internalSlotNum = 1;
|
let internalSlotNum = 1;
|
||||||
let militarySlotNum = 1;
|
let militarySlotNum = 1;
|
||||||
for (let i in shipTemplate.slots.internal) {
|
for (let i in shipTemplate.slots.internal) {
|
||||||
const internalClassNum = isNaN(shipTemplate.slots.internal[i]) ? shipTemplate.slots.internal[i].class : shipTemplate.slots.internal[i];
|
|
||||||
const isMilitary = isNaN(shipTemplate.slots.internal[i]) ? shipTemplate.slots.internal[i].name == 'Military' : false;
|
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
|
// The internal slot might be a standard or a military slot. Military slots have a different naming system
|
||||||
@@ -253,11 +252,15 @@ export function shipFromJson(json) {
|
|||||||
internalSlot = json.modules[internalName];
|
internalSlot = json.modules[internalName];
|
||||||
militarySlotNum++;
|
militarySlotNum++;
|
||||||
} else {
|
} else {
|
||||||
|
// Slot numbers are not contiguous so handle skips.
|
||||||
while (internalSlot === null && internalSlotNum < 99) {
|
while (internalSlot === null && internalSlotNum < 99) {
|
||||||
// Slot numbers are not contiguous so handle skips
|
// Slot sizes have no relationship to the actual size, either, so check all possibilities
|
||||||
const internalName = 'Slot' + (internalSlotNum <= 9 ? '0' : '') + internalSlotNum + '_Size' + internalClassNum;
|
for (let slotsize = 0; slotsize < 9; slotsize++) {
|
||||||
if (json.modules[internalName]) {
|
const internalName = 'Slot' + (internalSlotNum <= 9 ? '0' : '') + internalSlotNum + '_Size' + slotsize;
|
||||||
internalSlot = json.modules[internalName];
|
if (json.modules[internalName]) {
|
||||||
|
internalSlot = json.modules[internalName];
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
internalSlotNum++;
|
internalSlotNum++;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,13 +143,7 @@ export function diffDetails(language, m, mm) {
|
|||||||
let { formats, translate, units } = language;
|
let { formats, translate, units } = language;
|
||||||
let propDiffs = [];
|
let propDiffs = [];
|
||||||
|
|
||||||
let mCost = m.cost || 0;
|
// Module-specific items
|
||||||
let mmCost = mm ? mm.cost : 0;
|
|
||||||
if (mCost != mmCost) propDiffs.push(<div key='cost'>{translate('cost')}: <span className={diffClass(mCost, mmCost, true) }>{mCost ? Math.round(mCost * (1 - Persist.getModuleDiscount())) : 0}{units.CR}</span></div>);
|
|
||||||
|
|
||||||
let mMass = m.mass || 0;
|
|
||||||
let mmMass = mm ? mm.getMass() : 0;
|
|
||||||
if (mMass != mmMass) propDiffs.push(<div key='mass'>{translate('mass')}: <span className={diffClass(mMass, mmMass, true)}>{diff(formats.round, mMass, mmMass)}{units.T}</span></div>);
|
|
||||||
|
|
||||||
if (m.grp === 'pp') {
|
if (m.grp === 'pp') {
|
||||||
let mPowerGeneration = m.pgen || 0;
|
let mPowerGeneration = m.pgen || 0;
|
||||||
@@ -157,7 +151,7 @@ export function diffDetails(language, m, mm) {
|
|||||||
if (mPowerGeneration != mmPowerGeneration) propDiffs.push(<div key='pgen'>{translate('pgen')}: <span className={diffClass(mPowerGeneration, mmPowerGeneration)}>{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>);
|
||||||
} else {
|
} else {
|
||||||
let mPowerUsage = m.power || 0;
|
let mPowerUsage = m.power || 0;
|
||||||
let mmPowerUsage = mm ? mm.getPowerUsage() : 0;
|
let mmPowerUsage = mm ? mm.getPowerUsage() || 0 : 0;
|
||||||
if (mPowerUsage != mmPowerUsage) propDiffs.push(<div key='power'>{translate('power')}: <span className={diffClass(mPowerUsage, mmPowerUsage, true)}>{diff(formats.round, mPowerUsage, mmPowerUsage)}{units.MJ}</span></div>);
|
if (mPowerUsage != mmPowerUsage) propDiffs.push(<div key='power'>{translate('power')}: <span className={diffClass(mPowerUsage, mmPowerUsage, true)}>{diff(formats.round, mPowerUsage, mmPowerUsage)}{units.MJ}</span></div>);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,6 +181,20 @@ export function diffDetails(language, m, mm) {
|
|||||||
propDiffs.push(<div key='shields'>{translate('shields')}: <span className={sgDiffClass}>{diff(formats.int, newShield, shield)}{units.MJ}</span></div>);
|
propDiffs.push(<div key='shields'>{translate('shields')}: <span className={sgDiffClass}>{diff(formats.int, newShield, shield)}{units.MJ}</span></div>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m.grp === 'mrp') {
|
||||||
|
let mProtection = m.protection;
|
||||||
|
let mmProtection = mm ? mm.getProtection() || 0 : 0;
|
||||||
|
if (mProtection != mmProtection) {
|
||||||
|
propDiffs.push(<div key='protection'>{translate('protection')}: <span className={diffClass(mmProtection, mProtection, true)}>{diff(formats.pct, mProtection, mmProtection)}</span></div>);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mIntegrity = m.integrity;
|
||||||
|
let mmIntegrity = mm ? mm.getIntegrity() || 0 : 0;
|
||||||
|
if (mIntegrity != mmIntegrity) {
|
||||||
|
propDiffs.push(<div key='integrity'>{translate('integrity')}: <span className={diffClass(mmIntegrity, mIntegrity, true)}>{diff(formats.round, mIntegrity, mmIntegrity)}</span></div>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (m.grp == 'pd') {
|
if (m.grp == 'pd') {
|
||||||
propDiffs.push(<div key='wep'>
|
propDiffs.push(<div key='wep'>
|
||||||
{`${translate('WEP')}: `}
|
{`${translate('WEP')}: `}
|
||||||
@@ -208,6 +216,16 @@ export function diffDetails(language, m, mm) {
|
|||||||
</div>);
|
</div>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Common items
|
||||||
|
|
||||||
|
let mCost = m.cost || 0;
|
||||||
|
let mmCost = mm ? mm.cost : 0;
|
||||||
|
if (mCost != mmCost) propDiffs.push(<div key='cost'>{translate('cost')}: <span className={diffClass(mCost, mmCost, true) }>{mCost ? Math.round(mCost * (1 - Persist.getModuleDiscount())) : 0}{units.CR}</span></div>);
|
||||||
|
|
||||||
|
let mMass = m.mass || 0;
|
||||||
|
let mmMass = mm ? mm.getMass() : 0;
|
||||||
|
if (mMass != mmMass) propDiffs.push(<div key='mass'>{translate('mass')}: <span className={diffClass(mMass, mmMass, true)}>{diff(formats.round, mMass, mmMass)}{units.T}</span></div>);
|
||||||
|
|
||||||
let massDiff = mMass - mmMass;
|
let massDiff = mMass - mmMass;
|
||||||
let mCap = m.fuel || m.cargo || 0;
|
let mCap = m.fuel || m.cargo || 0;
|
||||||
let mmCap = mm ? mm.fuel || mm.cargo || 0 : 0;
|
let mmCap = mm ? mm.fuel || mm.cargo || 0 : 0;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html {%= o.htmlWebpackPlugin.options.appCache ? 'manifest=/' + o.htmlWebpackPlugin.options.appCache : '' %} >
|
<html <%= htmlWebpackPlugin.options.appCache ? 'manifest=/' + htmlWebpackPlugin.options.appCache : '' %> >
|
||||||
<head>
|
<head>
|
||||||
<title>Coriolis EDCD Edition</title>
|
<title>Coriolis EDCD Edition</title>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<link rel="stylesheet" href="{%= o.htmlWebpackPlugin.files.css[0] %}">
|
<link rel="stylesheet" href="<%= htmlWebpackPlugin.files.css[0] %>">
|
||||||
<!-- Standard headers -->
|
<!-- Standard headers -->
|
||||||
<meta name="description" content="A ship builder, outfitting and comparison tool for Elite Dangerous">
|
<meta name="description" content="A ship builder, outfitting and comparison tool for Elite Dangerous">
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
@@ -22,24 +22,24 @@
|
|||||||
<meta name="msapplication-TileImage" content="/mstile-144x144.png">
|
<meta name="msapplication-TileImage" content="/mstile-144x144.png">
|
||||||
<meta name="msapplication-config" content="/browserconfig.xml">
|
<meta name="msapplication-config" content="/browserconfig.xml">
|
||||||
<meta name="theme-color" content="#000000">
|
<meta name="theme-color" content="#000000">
|
||||||
|
|
||||||
|
<script>
|
||||||
|
window.CORIOLIS_GAPI_KEY = '<%- htmlWebpackPlugin.options.gapiKey %>';
|
||||||
|
window.CORIOLIS_VERSION = '<%- htmlWebpackPlugin.options.version %>';
|
||||||
|
window.CORIOLIS_DATE = '<%- new Date().toISOString().slice(0, 10) %>';
|
||||||
|
</script>
|
||||||
|
<% if (htmlWebpackPlugin.options.uaTracking) { %>
|
||||||
|
<script>
|
||||||
|
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
|
||||||
|
ga('create', '<%- htmlWebpackPlugin.options.uaTracking %>', 'auto');
|
||||||
|
ga('send', 'pageview');
|
||||||
|
</script>
|
||||||
|
<script async src='https://www.google-analytics.com/analytics.js'></script>
|
||||||
|
<% } %>
|
||||||
</head>
|
</head>
|
||||||
<body style="background-color:#000;">
|
<body style="background-color:#000;">
|
||||||
<section id="coriolis"></section>
|
<section id="coriolis"></section>
|
||||||
<script>
|
<script src="<%= htmlWebpackPlugin.files.chunks.lib.entry %>" charset="utf-8" crossorigin="anonymous"></script>
|
||||||
window.CORIOLIS_GAPI_KEY = '{%= o.htmlWebpackPlugin.options.gapiKey %}';
|
<script src="<%= htmlWebpackPlugin.files.chunks.app.entry %>" charset="utf-8" crossorigin="anonymous"></script>
|
||||||
window.CORIOLIS_VERSION = '{%= o.htmlWebpackPlugin.options.version %}';
|
|
||||||
window.CORIOLIS_DATE = '{%= new Date().toISOString().slice(0, 10) %}';
|
|
||||||
</script>
|
|
||||||
<script src="{%= o.htmlWebpackPlugin.files.chunks.lib.entry %}" charset="utf-8" crossorigin="anonymous"></script>
|
|
||||||
<script src="{%= o.htmlWebpackPlugin.files.chunks.app.entry %}" charset="utf-8" crossorigin="anonymous"></script>
|
|
||||||
<script>
|
|
||||||
{% if (o.htmlWebpackPlugin.options.uaTracking) { %}
|
|
||||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
|
||||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
|
||||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
|
||||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
|
||||||
ga('create', '{%= o.htmlWebpackPlugin.options.uaTracking %}', 'auto');
|
|
||||||
{% } %}
|
|
||||||
</script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -35,7 +35,8 @@ svg {
|
|||||||
}
|
}
|
||||||
|
|
||||||
text {
|
text {
|
||||||
font-size: 0.8em;
|
font-size: 1.2em;
|
||||||
|
font-family: @fStandard;
|
||||||
fill: @primary-disabled;
|
fill: @primary-disabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +56,7 @@ svg {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.label {
|
.label {
|
||||||
font-size: 0.75em;
|
font-size: 1.1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-tip {
|
.text-tip {
|
||||||
|
|||||||
@@ -55,10 +55,23 @@ select {
|
|||||||
background-color: @primary-disabled;
|
background-color: @primary-disabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.select-category {
|
||||||
|
white-space: nowrap;
|
||||||
|
line-height: 2em;
|
||||||
|
font-size: 1.2em;
|
||||||
|
text-align: center;
|
||||||
|
margin: 0.5em 0;
|
||||||
|
padding-left: 5px;
|
||||||
|
border-top: 3px solid @primary-disabled;
|
||||||
|
border-bottom: 3px solid @primary-disabled;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
.select-group {
|
.select-group {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
line-height: 1.5em;
|
line-height: 1.5em;
|
||||||
text-align: left;
|
text-align: center;
|
||||||
margin: 0.5em 0;
|
margin: 0.5em 0;
|
||||||
padding-left: 5px;
|
padding-left: 5px;
|
||||||
border-top: 1px solid @primary-disabled;
|
border-top: 1px solid @primary-disabled;
|
||||||
@@ -145,15 +158,6 @@ select {
|
|||||||
width: 4.5em;
|
width: 4.5em;
|
||||||
padding: 0.1em 0.2em;
|
padding: 0.1em 0.2em;
|
||||||
}
|
}
|
||||||
ul {
|
|
||||||
width: 17em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.standard {
|
|
||||||
ul {
|
|
||||||
width: 16.25em;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,79 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>Coriolis: Migrate to HTTPS</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<script>
|
|
||||||
|
|
||||||
function fromJsonToObject(str) {
|
|
||||||
try {
|
|
||||||
var o = JSON.parse(str);
|
|
||||||
if (o instanceof Object && !(o instanceof Array)) {
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
} catch (e) { }
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
function listener(event) {
|
|
||||||
var origin = event.origin || event.originalEvent.origin;
|
|
||||||
var source = event.source;
|
|
||||||
var data = event.message;
|
|
||||||
|
|
||||||
if (window.location.href.indexOf('coriolis') != -1 && origin !== "http://coriolis.io") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
var builds = fromJsonToObject(localStorage.getItem('builds'));
|
|
||||||
var comparisons = fromJsonToObject(localStorage.getItem('comparisons'));
|
|
||||||
|
|
||||||
// merge builds
|
|
||||||
if (typeof data.builds == 'object') {
|
|
||||||
|
|
||||||
for (var bName in data.builds) {
|
|
||||||
// Build existing in http and does not existing in HTTPS
|
|
||||||
if (data.builds.hasOwnProperty(bName) && !builds[bName]) {
|
|
||||||
build[bName] = data.builds[bName];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
localStorage.setItem('builds', JSON.stringify(builds));
|
|
||||||
}
|
|
||||||
// merge comparisons
|
|
||||||
if (typeof data.comparisons == 'object') {
|
|
||||||
|
|
||||||
for (var comp in data.comparisons) {
|
|
||||||
// Comparison existing in http and does not existing in HTTPS
|
|
||||||
if (data.comparisons.hasOwnProperty(comp) && !data.comparisons[comp]) {
|
|
||||||
comparisons[comp] = data.comparisons[comp];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
localStorage.setItem('comparisons', JSON.stringify(comparisons));
|
|
||||||
}
|
|
||||||
|
|
||||||
source.postMessage({
|
|
||||||
success: true,
|
|
||||||
buildCount: Object.keys(builds).length,
|
|
||||||
comparisonCount: Object.keys(comparisons).length
|
|
||||||
}, origin);
|
|
||||||
|
|
||||||
} catch (e) {
|
|
||||||
source.postMessage({ success: false, error: e }, origin);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (window.addEventListener){
|
|
||||||
window.addEventListener("message", listener, false);
|
|
||||||
} else {
|
|
||||||
window.attachEvent("onmessage", listener);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
// When requiring, you don't need to add these extensions
|
// When requiring, you don't need to add these extensions
|
||||||
extensions: ['', '.js', '.jsx', '.json', '.less']
|
extensions: ['.js', '.jsx', '.json', '.less']
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
path: path.join(__dirname, 'build'),
|
path: path.join(__dirname, 'build'),
|
||||||
@@ -36,30 +36,34 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
new CopyDirPlugin(path.join(__dirname, 'src/.htaccess'), ''),
|
new CopyDirPlugin(path.join(__dirname, 'src/.htaccess'), ''),
|
||||||
new webpack.optimize.CommonsChunkPlugin('lib', 'lib.js'),
|
new webpack.optimize.CommonsChunkPlugin({
|
||||||
|
name: 'lib',
|
||||||
|
filename: 'lib.js'
|
||||||
|
}),
|
||||||
new HtmlWebpackPlugin({
|
new HtmlWebpackPlugin({
|
||||||
inject: false,
|
inject: false,
|
||||||
template: path.join(__dirname, "src/index.html"),
|
template: path.join(__dirname, "src/index.ejs"),
|
||||||
version: pkgJson.version,
|
version: pkgJson.version,
|
||||||
gapiKey: process.env.CORIOLIS_GAPI_KEY || '',
|
gapiKey: process.env.CORIOLIS_GAPI_KEY || '',
|
||||||
}),
|
}),
|
||||||
new ExtractTextPlugin('app.css', {
|
new ExtractTextPlugin({
|
||||||
|
filename: 'app.css',
|
||||||
|
disable: false,
|
||||||
allChunks: true
|
allChunks: true
|
||||||
}),
|
}),
|
||||||
new webpack.HotModuleReplacementPlugin(),
|
new webpack.HotModuleReplacementPlugin(),
|
||||||
new webpack.NoErrorsPlugin()
|
new webpack.NoEmitOnErrorsPlugin()
|
||||||
],
|
],
|
||||||
module: {
|
module: {
|
||||||
loaders: [
|
rules: [
|
||||||
{ test: /\.css$/, loader: ExtractTextPlugin.extract('style-loader','css-loader') },
|
{ test: /\.css$/, loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader'}) },
|
||||||
{ test: /\.less$/, loader: ExtractTextPlugin.extract('style-loader','css-loader!less-loader') },
|
{ test: /\.less$/, loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader!less-loader'}) },
|
||||||
{ test: /\.(js|jsx)$/, loaders: [ 'babel' ], include: path.join(__dirname, 'src') },
|
{ test: /\.(js|jsx)$/, loaders: [ 'babel-loader' ], include: path.join(__dirname, 'src') },
|
||||||
{ test: /\.json$/, loader: 'json-loader' },
|
{ test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/font-woff' },
|
||||||
{ test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/font-woff' },
|
{ test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/font-woff' },
|
||||||
{ test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/font-woff' },
|
{ test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/octet-stream' },
|
||||||
{ test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/octet-stream' },
|
{ test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: 'file-loader' },
|
||||||
{ test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: 'file' },
|
{ test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=image/svg+xml' }
|
||||||
{ test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=image/svg+xml' }
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,12 +6,6 @@ var HtmlWebpackPlugin = require("html-webpack-plugin");
|
|||||||
var ExtractTextPlugin = require("extract-text-webpack-plugin");
|
var ExtractTextPlugin = require("extract-text-webpack-plugin");
|
||||||
var AppCachePlugin = require('appcache-webpack-plugin');
|
var AppCachePlugin = require('appcache-webpack-plugin');
|
||||||
|
|
||||||
var node_modules_dir = path.resolve(__dirname, 'node_modules');
|
|
||||||
var d3Path = path.resolve(node_modules_dir, 'd3/d3.min.js');
|
|
||||||
var reactPath = path.resolve(node_modules_dir, 'react/dist/react.min.js');
|
|
||||||
var reactDomPath = path.resolve(node_modules_dir, 'react-dom/dist/react-dom.min.js');
|
|
||||||
var lzStringPath = path.resolve(node_modules_dir, 'lz-string/libs/lz-string.min.js');
|
|
||||||
|
|
||||||
function CopyDirPlugin(source, destination) {
|
function CopyDirPlugin(source, destination) {
|
||||||
this.source = source;
|
this.source = source;
|
||||||
this.destination = destination;
|
this.destination = destination;
|
||||||
@@ -26,16 +20,10 @@ CopyDirPlugin.prototype.apply = function(compiler) {
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
entry: {
|
entry: {
|
||||||
app: ['babel-polyfill', path.resolve(__dirname, 'src/app/index')],
|
app: ['babel-polyfill', path.resolve(__dirname, 'src/app/index')],
|
||||||
lib: ['babel-polyfill', 'd3', 'react', 'react-dom', 'classnames', 'fbemitter', 'lz-string']
|
lib: ['d3', 'react', 'react-dom', 'classnames', 'fbemitter', 'lz-string']
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
extensions: ['', '.js', '.jsx', '.json', '.less'],
|
extensions: ['.js', '.jsx', '.json', '.less']
|
||||||
alias: {
|
|
||||||
'd3': d3Path,
|
|
||||||
'react': reactPath,
|
|
||||||
'react-dom': reactDomPath,
|
|
||||||
'lz-string': lzStringPath
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
path: path.join(__dirname, 'build'),
|
path: path.join(__dirname, 'build'),
|
||||||
@@ -45,12 +33,12 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
new webpack.optimize.UglifyJsPlugin({
|
new webpack.optimize.UglifyJsPlugin({
|
||||||
mangle: {
|
|
||||||
except: []
|
|
||||||
},
|
|
||||||
'screw-ie8': true
|
'screw-ie8': true
|
||||||
}),
|
}),
|
||||||
new webpack.optimize.CommonsChunkPlugin('lib', 'lib.[chunkhash:6].js'),
|
//new webpack.optimize.CommonsChunkPlugin({
|
||||||
|
// name: 'lib',
|
||||||
|
// filename: 'lib.[chunkhash:6].js'
|
||||||
|
//}),
|
||||||
new HtmlWebpackPlugin({
|
new HtmlWebpackPlugin({
|
||||||
inject: false,
|
inject: false,
|
||||||
appCache: 'coriolis.appcache',
|
appCache: 'coriolis.appcache',
|
||||||
@@ -64,17 +52,18 @@ module.exports = {
|
|||||||
removeScriptTypeAttributes: true,
|
removeScriptTypeAttributes: true,
|
||||||
removeStyleLinkTypeAttributes: true
|
removeStyleLinkTypeAttributes: true
|
||||||
},
|
},
|
||||||
template: path.join(__dirname, "src/index.html"),
|
template: path.join(__dirname, "src/index.ejs"),
|
||||||
uaTracking: process.env.CORIOLIS_UA_TRACKING || '',
|
uaTracking: process.env.CORIOLIS_UA_TRACKING || '',
|
||||||
gapiKey: process.env.CORIOLIS_GAPI_KEY || '',
|
gapiKey: process.env.CORIOLIS_GAPI_KEY || '',
|
||||||
version: pkgJson.version
|
version: pkgJson.version
|
||||||
}),
|
}),
|
||||||
new ExtractTextPlugin('[contenthash:6].css', {
|
new ExtractTextPlugin({
|
||||||
|
filename: '[contenthash:6].css',
|
||||||
|
disable: false,
|
||||||
allChunks: true
|
allChunks: true
|
||||||
}),
|
}),
|
||||||
new CopyDirPlugin(path.join(__dirname, 'src/schemas'), 'schemas'),
|
new CopyDirPlugin(path.join(__dirname, 'src/schemas'), 'schemas'),
|
||||||
new CopyDirPlugin(path.join(__dirname, 'src/images/logo/*'), ''),
|
new CopyDirPlugin(path.join(__dirname, 'src/images/logo/*'), ''),
|
||||||
new CopyDirPlugin(path.join(__dirname, 'src/migrate.html'), ''),
|
|
||||||
new CopyDirPlugin(path.join(__dirname, 'src/.htaccess'), ''),
|
new CopyDirPlugin(path.join(__dirname, 'src/.htaccess'), ''),
|
||||||
new AppCachePlugin({
|
new AppCachePlugin({
|
||||||
network: ['*'],
|
network: ['*'],
|
||||||
@@ -84,22 +73,15 @@ module.exports = {
|
|||||||
})
|
})
|
||||||
],
|
],
|
||||||
module: {
|
module: {
|
||||||
noParse: [d3Path, reactPath, lzStringPath],
|
rules: [
|
||||||
loaders: [
|
{ test: /\.css$/, loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader'}) },
|
||||||
// Expose non-parsed globally scoped libs
|
{ test: /\.less$/, loader: ExtractTextPlugin.extract({ fallback: 'style-loader',use: 'css-loader!less-loader'}) },
|
||||||
{ test: reactPath, loader: "expose?React" },
|
{ test: /\.(js|jsx)$/, loaders: [ 'babel-loader' ], include: path.join(__dirname, 'src') },
|
||||||
{ test: d3Path, loader: "expose?d3" },
|
{ test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/font-woff' },
|
||||||
{ test: lzStringPath, loader: "expose?LZString" },
|
{ test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/font-woff' },
|
||||||
|
{ test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/octet-stream' },
|
||||||
{ test: /\.css$/, loader: ExtractTextPlugin.extract('style-loader','css-loader') },
|
{ test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: 'file-loader' },
|
||||||
{ test: /\.less$/, loader: ExtractTextPlugin.extract('style-loader','css-loader!less-loader') },
|
{ test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=image/svg+xml' }
|
||||||
{ test: /\.(js|jsx)$/, loaders: [ 'babel' ], include: path.join(__dirname, 'src') },
|
|
||||||
{ test: /\.json$/, loader: 'json-loader' },
|
|
||||||
{ test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/font-woff' },
|
|
||||||
{ test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/font-woff' },
|
|
||||||
{ test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/octet-stream' },
|
|
||||||
{ test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: 'file' },
|
|
||||||
{ test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=image/svg+xml' }
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user