mirror of
https://github.com/EDCD/coriolis.git
synced 2025-12-09 06:43:24 +00:00
Compare commits
72 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c347336055 | ||
|
|
cd87184169 | ||
|
|
09895852c3 | ||
|
|
32759d3e0e | ||
|
|
965134fc02 | ||
|
|
7da5360349 | ||
|
|
bf0701340c | ||
|
|
a3a490f442 | ||
|
|
33b22b8280 | ||
|
|
e6278858b9 | ||
|
|
ed7a9ef037 | ||
|
|
9c42179617 | ||
|
|
5f75c9d32b | ||
|
|
fe1d16415e | ||
|
|
ed8cbb403e | ||
|
|
58182bddce | ||
|
|
771ba7e692 | ||
|
|
ed0d4ddc48 | ||
|
|
266c63bc0c | ||
|
|
ebcc92f7d4 | ||
|
|
a1f280cf62 | ||
|
|
0306167e6e | ||
|
|
162136194a | ||
|
|
3600a4862f | ||
|
|
65fdad64b2 | ||
|
|
b49eb06101 | ||
|
|
eeb4a86a13 | ||
|
|
3a265b2804 | ||
|
|
5ebabd6e6e | ||
|
|
6e6c337bbb | ||
|
|
31b56354e0 | ||
|
|
fd1adbe55c | ||
|
|
3d0259c304 | ||
|
|
6f1b86118e | ||
|
|
b3fcfa7808 | ||
|
|
4ead80a37c | ||
|
|
72dc73e090 | ||
|
|
bb19dc25c0 | ||
|
|
1729636657 | ||
|
|
92809d1d24 | ||
|
|
4e81c828df | ||
|
|
f5bd7d2ecb | ||
|
|
a989bd5b5d | ||
|
|
1bedbb1909 | ||
|
|
9775e1c742 | ||
|
|
19228a9c56 | ||
|
|
363bf7fd2a | ||
|
|
15748066c9 | ||
|
|
c3fe0a0cef | ||
|
|
1544deb350 | ||
|
|
f060eb1b62 | ||
|
|
32795ea678 | ||
|
|
d744d15132 | ||
|
|
bfcef18c02 | ||
|
|
b04c2ed2f5 | ||
|
|
f51998b1c2 | ||
|
|
d7000bfebf | ||
|
|
3e2b3e33fb | ||
|
|
87dd52c043 | ||
|
|
05bc8ebb93 | ||
|
|
09b945d29e | ||
|
|
67b5d749df | ||
|
|
a19fd69d0b | ||
|
|
cfb65396ab | ||
|
|
a4a0f96502 | ||
|
|
1390339024 | ||
|
|
43d19f1dbb | ||
|
|
c20439264a | ||
|
|
61c3941618 | ||
|
|
da277e4eaa | ||
|
|
51d24a1105 | ||
|
|
138931c0cf |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -3,3 +3,5 @@ build
|
||||
.DS_Store
|
||||
*.log
|
||||
nginx.pid
|
||||
.idea
|
||||
/bin
|
||||
11
README.md
11
README.md
@@ -1,5 +1,4 @@
|
||||
 [](https://travis-ci.org/cmmcleod/coriolis) [](https://waffle.io/cmmcleod/coriolis) [](http://waffle.io/cmmcleod/coriolis) [](https://www.hipchat.com/gfYQiZcmy)
|
||||
|
||||
 [](https://travis-ci.org/EDCD/coriolis) [](https://discord.gg/0uwCh6R62aPRjk9w)
|
||||
|
||||
## About
|
||||
|
||||
@@ -9,13 +8,11 @@ Coriolis was created using assets and imagery from Elite: Dangerous, with the pe
|
||||
|
||||
## Contributing
|
||||
|
||||
Please [submit issues](https://github.com/cmmcleod/coriolis/issues), or better yet [pull requests](https://github.com/cmmcleod/coriolis/pulls) for any corrections or additions to the database or the code.
|
||||
Please [submit issues](https://github.com/EDCD/coriolis/issues), or better yet [pull requests](https://github.com/EDCD/coriolis/pulls) for any corrections or additions to the database or the code.
|
||||
|
||||
### Feature Requests, Suggestions & Bugs
|
||||
|
||||
Chat to us on [HipChat](https://www.hipchat.com/gfYQiZcmy)!
|
||||
|
||||
All such requests are managed and tracked through [issues](https://github.com/cmmcleod/coriolis/issues). An overview of these can be found [here](https://waffle.io/cmmcleod/coriolis).
|
||||
Chat to us on [Discord](https://discord.gg/0uwCh6R62aPRjk9w)!
|
||||
|
||||
## Development
|
||||
|
||||
@@ -29,7 +26,7 @@ See the [Data wiki](https://github.com/cmmcleod/coriolis-data/wiki) for details
|
||||
|
||||
## License
|
||||
|
||||
All Data and [associated JSON](https://github.com/cmmcleod/coriolis-data) files are intellectual property and copyright of Frontier Developments plc ('Frontier', 'Frontier Developments') and are subject to their
|
||||
All Data and [associated JSON](https://github.com/EDCD/coriolis-data) files are intellectual property and copyright of Frontier Developments plc ('Frontier', 'Frontier Developments') and are subject to their
|
||||
[terms and conditions](https://www.frontierstore.net/terms-and-conditions/).
|
||||
|
||||
The code (Javascript, CSS, HTML, and SVG files only) specificially for Coriolis.io is released under the MIT License.
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
{
|
||||
"name": "Coriolis.io",
|
||||
"url": "http://localhost:3300/outfit/anaconda/48A6A6A5A8A8A5C2c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.AwRj4zNKqA==.CwBhCYzBGW9qCTSqs5xA?bn=Test%20My%20Ship",
|
||||
"code": "48A6A6A5A8A8A5C2c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.AwRj4zNKqA==.CwBhCYzBGW9qCTSqs5xA",
|
||||
"old-code": "48A6A6A5A8A8A5C2c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.AwRj4zNKqA==.CwBhCYzBGW9qCTSqs5xA",
|
||||
"code": "4putkFklkdzsuf52c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.AwRj4zNKqA==.CwBhCYzBGW9qCTSqs5xA",
|
||||
"shipId": "anaconda"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -2,31 +2,31 @@
|
||||
{
|
||||
"shipId": "anaconda",
|
||||
"buildName": "Imported Anaconda",
|
||||
"buildCode": "08E7E6E5E8E8E5C------1717--------05044j-03--2h--00.Iw18ZlA=.Aw18ZlA=",
|
||||
"buildCode": "0pyttFolodDsyf5------1717--------05044j-03--2h--00.Iw18ZlA=.Aw18ZlA=",
|
||||
"buildText": "[Anaconda]\nS: 1F/F Pulse Laser\nS: 1F/F Pulse Laser\n\nBH: 1I Lightweight Alloy\nRB: 8E Power Plant\nTM: 7E Thrusters\nFH: 6E Frame Shift Drive\nEC: 5E Life Support\nPC: 8E Power Distributor\nSS: 8E Sensors\nFS: 5C Fuel Tank (Capacity: 32)\n\n7: 6E Cargo Rack (Capacity: 64)\n6: 5E Cargo Rack (Capacity: 32)\n6: 6E Shield Generator\n5: 4E Cargo Rack (Capacity: 16)\n4: 1E Basic Discovery Scanner\n2: 1E Cargo Rack (Capacity: 2)\n"
|
||||
},
|
||||
{
|
||||
"shipId": "anaconda",
|
||||
"buildName": "Imported Anaconda",
|
||||
"buildCode": "08E7E6E5E8E8E5C------1717--------05044j-03--2h--00.Iw18ZlA=.Aw18ZlA=",
|
||||
"buildCode": "0pyttFolodDsyf5------1717--------05044j-03--2h--00.Iw18ZlA=.Aw18ZlA=",
|
||||
"buildText": "\n\n \t[Anaconda]\nS: 1F/F Pulse Laser\nS: 1F/F Pulse Laser\n\nBH: 1I Lightweight Alloy\nRB: 8E Power Plant\nTM: 7E Thrusters\nFH: 6E Frame Shift Drive\nEC: 5E Life Support\nPC: 8E Power Distributor\nSS: 8E Sensors\nFS: 5C Fuel Tank (Capacity: 32)\n\n7: 6E Cargo Rack (Capacity: 64)\n6: 5E Cargo Rack (Capacity: 32)\n6: 6E Shield Generator\n5: 4E Cargo Rack (Capacity: 16)\n4: 1E Basic Discovery Scanner\n2: 1E Cargo Rack (Capacity: 2)\n"
|
||||
},
|
||||
{
|
||||
"shipId": "cobra_mk_iii",
|
||||
"buildName": "Imported Cobra Mk III",
|
||||
"buildCode": "04A4C4E3D2A3D4C1712222503040202490f242h.Iw1-kA==.Aw1-kA==",
|
||||
"buildCode": "0patcFeldd5sdf41712222503040202490f242h.Iw1-kA==.Aw1-kA==",
|
||||
"buildText": "[Cobra Mk III]\nM: 1F/F Pulse Laser\nM: 1G/G Burst Laser\nS: 1E/T Fragment Cannon\nS: 1G/T Multi-cannon\nU: 0I Point Defence\nU: 0A Shield Booster\n\nBH: 1I Lightweight Alloy\nRB: 4A Power Plant\nTM: 4C Thrusters\nFH: 4E Frame Shift Drive\nEC: 3D Life Support\nPC: 2A Power Distributor\nSS: 3D Sensors\nFS: 4C Fuel Tank (Capacity: 16)\n\n4: 3E Cargo Rack (Capacity: 8)\n4: 3E Cargo Rack (Capacity: 8)\n4: 4E Shield Generator\n2: 2C Auto Field-Maintenance Unit\n2: 1E Standard Docking Computer\n2: 1E Basic Discovery Scanner\n---\nShield: 112.29 MJ\nPower : 10.45 MW retracted (67%)\n 12.16 MW deployed (78%)\n 15.60 MW available\nCargo : 16 T\nFuel : 16 T\nMass : 235.5 T empty\n 267.5 T full\nRange : 10.69 LY unladen\n 10.05 LY laden\nPrice : 2,929,040 CR\nRe-Buy: 146,452 CR @ 95% insurance\n"
|
||||
},
|
||||
{
|
||||
"shipId": "type_9_heavy",
|
||||
"buildName": "Imported Type-9 Heavy",
|
||||
"buildCode": "35A7D6A5A4D4D5C7e2k2f2h110001020306054j03022f01242i.Iw18eQ==.Aw18eQ==",
|
||||
"buildCode": "3pftsFklkdisif57e2k2f2h110001020306054j03022f01242i.Iw18eQ==.Aw18eQ==",
|
||||
"buildText": "[Type-9 Heavy]\nM: 2D/G Fragment Cannon\nM: 2I/F Mine Launcher\nM: 2B/FD Missile Rack\nS: 1I/FS Torpedo Pylon\nS: 1F/F Burst Laser\nU: 0I Chaff Launcher\nU: 0F Electronic Countermeasure\nU: 0I Heat Sink Launcher\nU: 0I Point Defence\n\nBH: 1I Mirrored Surface Composite\nRB: 5A Power Plant\nTM: 7D Thrusters\nFH: 6A Frame Shift Drive\nEC: 5A Life Support\nPC: 4D Power Distributor\nSS: 4D Sensors\nFS: 5C Fuel Tank (Capacity: 32)\n\n8: 7E Cargo Rack (Capacity: 128)\n7: 6E Cargo Rack (Capacity: 64)\n6: 6E Shield Generator\n5: 4E Cargo Rack (Capacity: 16)\n4: 3E Cargo Rack (Capacity: 8)\n4: 1C Advanced Discovery Scanner\n3: 2E Cargo Rack (Capacity: 4)\n3: 1E Standard Docking Computer\n2: 1C Detailed Surface Scanner\n"
|
||||
},
|
||||
{
|
||||
"shipId": "vulture",
|
||||
"buildName": "Imported Vulture",
|
||||
"buildCode": "44A5A4A3D5A4D3C1e1e0e0j04044a0n532jf1.Iw19kA==.Aw19kA==",
|
||||
"buildCode": "4patfFalddksif31e1e0e0j04044a0n532jf1.Iw19kA==.Aw19kA==",
|
||||
"buildText": "[Vulture]\nL: 3E/G Pulse Laser\nL: 3E/G Pulse Laser\nU: 0A Frame Shift Wake Scanner\nU: 0A Kill Warrant Scanner\nU: 0A Shield Booster\nU: 0A Shield Booster\n\nBH: 1I Reactive Surface Composite\nRB: 4A Power Plant\nTM: 5A Thrusters\nFH: 4A Frame Shift Drive\nEC: 3D Life Support\nPC: 5A Power Distributor\nSS: 4D Sensors\nFS: 3C Fuel Tank (Capacity: 8)\n\n5: 5A Shield Generator\n4: 4A Auto Field-Maintenance Unit\n2: 2A Shield Cell Bank\n1: 1A Fuel Scoop\n1: 1C Fuel Tank (Capacity: 2)"
|
||||
}
|
||||
]
|
||||
@@ -1,50 +1,50 @@
|
||||
{
|
||||
"type_6_transporter": {
|
||||
"Cargo": "02A4D4A2D2D2D4C-----04040303430101.Iw1-kA==.Aw1-kA==",
|
||||
"Miner": "03A4D4A2D2D2D4C2l2l---040403451q0101.Iw1-kA==.Aw1-kA==",
|
||||
"Hopper": "02A4D4A2D1A2D4C1717---030302024300-.Iw1-kA==.Aw1-kA=="
|
||||
"Cargo": "0p0tdFal8d8s8f4-----04040303430101.Iw1-kA==.Aw1-kA==",
|
||||
"Miner": "0p5tdFal8d8s8f42l2l---040403451q0101.Iw1-kA==.Aw1-kA==",
|
||||
"Hopper": "0p0tdFal8d0s8f41717---030302024300-.Iw1-kA==.Aw1-kA=="
|
||||
},
|
||||
"type_7_transport": {
|
||||
"Cargo": "02A5D5A4D3D3D5C--------0505040403480101.Iw18aQ==.Aw18aQ==",
|
||||
"Miner": "04D5D5A4D2D3D5C--2l2l----0505041v03450000.Iw18aQ==.Aw18aQ=="
|
||||
"Cargo": "0p0tiFfliddsdf5--------0505040403480101.Iw18aQ==.Aw18aQ==",
|
||||
"Miner": "0pdtiFflid8sdf5--2l2l----0505041v03450000.Iw18aQ==.Aw18aQ=="
|
||||
},
|
||||
"federal_dropship": {
|
||||
"Cargo": "04D5D5A5D3D4D4C-1717------05040448020201.Iw18aQ==.Aw18aQ=="
|
||||
"Cargo": "0pdtiFflnddsif4-1717------05040448020201.Iw18aQ==.Aw18aQ=="
|
||||
},
|
||||
"asp": {
|
||||
"Miner": "25A5A5A4D4A5A5C0s0s24242l2l---04054a1q02022o27.Iw18WQ==.Aw18WQ=="
|
||||
"Miner": "2pftfFflidfskf50s0s24242l2l---04054a1q02022o27.Iw18WQ==.Aw18WQ=="
|
||||
},
|
||||
"imperial_clipper": {
|
||||
"Cargo": "03A5D5A5D4D5D4C--0s0s----0605450302020101.Iw18aQ==.Aw18aQ==",
|
||||
"Dream": "26A6A5A5D6A5A4C0v0v0s0s0404040n4k5n5d2b29292o-.Iw18aQ==.Aw18aQ==",
|
||||
"Current": "04A6A5A5D4A5A4C----------------.Iw18aQ==.Aw18aQ=="
|
||||
"Cargo": "0p5tiFflndisnf4--0s0s----0605450302020101.Iw18aQ==.Aw18aQ==",
|
||||
"Dream": "2pktkFflndpskf40v0v0s0s0404040n4k5n5d2b29292o-.Iw18aQ==.Aw18aQ==",
|
||||
"Current": "0patkFflndfskf4----------------.Iw18aQ==.Aw18aQ=="
|
||||
},
|
||||
"type_9_heavy": {
|
||||
"Current": "04A7D6A5D5D4D6C---------0706054a0303020224.Iw18eQ==.Aw18eQ=="
|
||||
"Current": "0patsFklndnsif6---------0706054a0303020224.Iw18eQ==.Aw18eQ=="
|
||||
},
|
||||
"python": {
|
||||
"Cargo": "04A6D5A4D6D6D5C---------050505040448020201.Iw18eQ==.Aw18eQ==",
|
||||
"Miner": "06A6A5A4D6A6A5C0v0v0v2m2m0404--050505Ce4a1v02022o.Iw18eQ==.Aw18eQ==",
|
||||
"Dream": "27A6A5A4D7A6A5C0v0v0v27270404040m5n5n4f2d2d032t0201.Iw18eQ==.Aw18eQ==",
|
||||
"Missile": "07E6E5E4E7E6E5C2f2g2d2ePh----04044j03---002h.Iw18eQ==.Aw18eQ=="
|
||||
"Cargo": "0patnFflidsssf5---------050505040448020201.Iw18eQ==.Aw18eQ==",
|
||||
"Miner": "0pktkFflidpspf50v0v0v2m2m0404--050505Ce4a1v02022o.Iw18eQ==.Aw18eQ==",
|
||||
"Dream": "2pptkFfliduspf50v0v0v27270404040m5n5n4f2d2d032t0201.Iw18eQ==.Aw18eQ==",
|
||||
"Missile": "0pttoFjljdystf52f2g2d2ePh----04044j03---002h.Iw18eQ==.Aw18eQ=="
|
||||
},
|
||||
"anaconda": {
|
||||
"Dream": "48A7A6A5D8A8A5C2c0o0o0o1m1m0q0q0404040l0b0100004k5n5n112d2d040303326b.Iw18ZlA=.Aw18ZlA=",
|
||||
"Cargo": "04A6D6A5D5D8D5C----------------0605050504040445030301.Iw18ZlA=.Aw18ZlA=",
|
||||
"Current": "04A6D6A5D5A8D5C----------------0605050504040403034524.Iw18ZlA=.Aw18ZlA=",
|
||||
"Explorer": "04A6D6A5D5A8D5C--------0202------f7050505040s372f2i4524.Iw18ZlA=.Aw18ZlA=",
|
||||
"Test": "48A6A6A5A8A8A5C2c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.Iw18ZlA=.Aw18ZlA="
|
||||
"Dream": "4putpFklndzsuf52c0o0o0o1m1m0q0q0404040l0b0100004k5n5n112d2d040303326b.Iw18ZlA=.Aw18ZlA=",
|
||||
"Cargo": "0patnFklndnsxf5----------------0605050504040445030301.Iw18ZlA=.Aw18ZlA=",
|
||||
"Current": "0patnFklndksxf5----------------0605050504040403034524.Iw18ZlA=.Aw18ZlA=",
|
||||
"Explorer": "0patnFklndksxf5--------0202------f7050505040s372f2i4524.Iw18ZlA=.Aw18ZlA=",
|
||||
"Test": "4putkFklkdzsuf52c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.Iw18ZlA=.Aw18ZlA="
|
||||
},
|
||||
"diamondback_explorer": {
|
||||
"Explorer": "02A4D5A3D3D3D5C---0202--320p432i2f.Iw1-kA==.Aw1-kA=="
|
||||
"Explorer": "0p0tdFfldddsdf5---0202--320p432i2f.Iw1-kA==.Aw1-kA=="
|
||||
},
|
||||
"vulture": {
|
||||
"Bounty Hunter": "34A4C4A3D5A4A3C1e1e0404-0l4a5d27662j.Iw19kA==.Aw19kA=="
|
||||
"Bounty Hunter": "3patcFalddksff31e1e0404-0l4a5d27662j.Iw19kA==.Aw19kA=="
|
||||
},
|
||||
"fer_de_lance": {
|
||||
"Attack": "25A5C4A4D6A4A3C1r0s0s0s0s000404-04-4a-5d27-.Iw18aQ==.Aw18aQ=="
|
||||
"Attack": "2pfthFalidpsff31r0s0s0s0s000404-04-4a-5d27-.Iw18aQ==.Aw18aQ=="
|
||||
},
|
||||
"eagle": {
|
||||
"Figther": "42A3A3A1D2A2A2C0p0p24-40532j.Iw19A===.Aw19A==="
|
||||
"Figther": "4p0t5F5l3d5s5f20p0p24-40532j-.Iw1-EA==.Aw1-EA=="
|
||||
}
|
||||
}
|
||||
@@ -3348,8 +3348,8 @@
|
||||
"references": [
|
||||
{
|
||||
"name": "Coriolis.io",
|
||||
"url": "http://localhost:3300/outfit/eagle/42A3A3A1D2A2A2C0p0p24-40532j.Iw19A===.Aw19A===?bn=Figther",
|
||||
"code": "42A3A3A1D2A2A2C0p0p24-40532j.AwRj49iA.AwgsIkEZigmIA===",
|
||||
"url": "http://localhost:3300/outfit/eagle/42A3A3A1D2A2A2C0p0p24-40532j-.Iw1-EA==.Aw1-EA==?bn=Figther",
|
||||
"code": "42A3A3A1D2A2A2C0p0p24-40532j-.Iw1-EA==.Aw1-EA==",
|
||||
"shipId": "eagle"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -10,7 +10,7 @@ import { getLanguage } from '../src/app/i18n/Language';
|
||||
|
||||
describe('Import Modal', function() {
|
||||
|
||||
let MockRouter = require('../src/app/Router');
|
||||
let MockRouter = require('../src/app/Router').default;
|
||||
const Persist = require('../src/app/stores/Persist').default;
|
||||
const ModalImport = require('../src/app/components/ModalImport').default;
|
||||
const mockContext = {
|
||||
@@ -25,8 +25,6 @@ describe('Import Modal', function() {
|
||||
onWindowResize: jest.genMockFunction()
|
||||
};
|
||||
|
||||
MockRouter.go = jest.genMockFunction();
|
||||
|
||||
let modal, render, ContextProvider = Utils.createContextProvider(mockContext);
|
||||
|
||||
/**
|
||||
@@ -110,7 +108,8 @@ describe('Import Modal', function() {
|
||||
it('catches an invalid backup', function() {
|
||||
const importData = require('./fixtures/valid-backup');
|
||||
let invalidImportData = Object.assign({}, importData);
|
||||
invalidImportData.builds.asp = null; // Remove Asp Miner build used in comparison
|
||||
//invalidImportData.builds.asp = null; // Remove Asp Miner build used in comparison
|
||||
delete(invalidImportData.builds.asp);
|
||||
|
||||
pasteText('"this is not valid"');
|
||||
expect(modal.state.importValid).toBeFalsy();
|
||||
@@ -140,9 +139,10 @@ describe('Import Modal', function() {
|
||||
|
||||
expect(modal.state.importValid).toBeTruthy();
|
||||
expect(modal.state.errorMsg).toEqual(null);
|
||||
expect(modal.state.singleBuild).toBe(true);
|
||||
clickProceed();
|
||||
expect(MockRouter.go.mock.calls.length).toBe(1);
|
||||
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/anaconda/48A6A6A5A8A8A5C2c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.AwRj4zNKqA==.CwBhCYzBGW9qCTSqs5xA?bn=Test%20My%20Ship');
|
||||
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/anaconda/4putkFklkdzsuf52c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.AwRj4zNKqA==.CwBhCYzBGW9qCTSqs5xA?bn=Test%20My%20Ship');
|
||||
});
|
||||
|
||||
it('catches an invalid build', function() {
|
||||
|
||||
@@ -2,7 +2,7 @@ import Ship from '../src/app/shipyard/Ship';
|
||||
import { Ships } from 'coriolis-data/dist';
|
||||
import * as ModuleUtils from '../src/app/shipyard/ModuleUtils';
|
||||
|
||||
describe("Ship Factory", function() {
|
||||
describe("Ship", function() {
|
||||
|
||||
it("can build all ships", function() {
|
||||
for (let s in Ships) {
|
||||
@@ -16,7 +16,7 @@ describe("Ship Factory", function() {
|
||||
ship.buildWith(shipData.defaults);
|
||||
|
||||
expect(ship.totalCost).toEqual(shipData.retailCost, s + ' retail cost does not match default build cost');
|
||||
expect(ship.cargoCapacity).toBeDefined(s + ' cargo');
|
||||
expect(ship.cargoCapacity).toBeDefined();
|
||||
expect(ship.priorityBands[0].retracted).toBeGreaterThan(0, s + ' priorityBands');
|
||||
expect(ship.powerAvailable).toBeGreaterThan(0, s + ' powerAvailable');
|
||||
expect(ship.unladenRange).toBeGreaterThan(0, s + ' unladenRange');
|
||||
@@ -115,8 +115,7 @@ describe("Ship Factory", function() {
|
||||
|
||||
anaconda.use(anaconda.internal[1], ModuleUtils.internal('4j')); // 6E Shield Generator
|
||||
|
||||
expect(anaconda.internal[2].c).toEqual(null, 'Anaconda default shield generator slot is empty');
|
||||
expect(anaconda.internal[2].m).toEqual(null, 'Anaconda default shield generator slot id is null');
|
||||
expect(anaconda.internal[2].m).toEqual(null, 'Anaconda default shield generator slot is empty');
|
||||
expect(anaconda.internal[1].m.id).toEqual('4j', 'Slot 1 should have SG 4j in it');
|
||||
expect(anaconda.internal[1].m.grp).toEqual('sg','Slot 1 should have SG 4j in it');
|
||||
|
||||
@@ -133,8 +132,7 @@ describe("Ship Factory", function() {
|
||||
|
||||
anaconda.use(anaconda.internal[3], ModuleUtils.internal('32'));
|
||||
|
||||
expect(anaconda.internal[4].c).toEqual(null, 'Anaconda original fuel scoop slot is empty');
|
||||
expect(anaconda.internal[4].m).toEqual(null, 'Anaconda original fuel scoop slot id is null');
|
||||
expect(anaconda.internal[4].m).toEqual(null, 'Anaconda original fuel scoop slot is empty');
|
||||
expect(anaconda.internal[3].m.id).toEqual('32', 'Slot 1 should have FS 32 in it');
|
||||
expect(anaconda.internal[3].m.grp).toEqual('fs','Slot 1 should have FS 32 in it');
|
||||
});
|
||||
@@ -150,8 +148,7 @@ describe("Ship Factory", function() {
|
||||
|
||||
anaconda.use(anaconda.internal[3], ModuleUtils.internal('23'));
|
||||
|
||||
expect(anaconda.internal[4].c).toEqual(null, 'Anaconda original refinery slot is empty');
|
||||
expect(anaconda.internal[4].m).toEqual(null, 'Anaconda original refinery slot id is null');
|
||||
expect(anaconda.internal[4].m).toEqual(null, 'Anaconda original refinery slot is empty');
|
||||
expect(anaconda.internal[3].m.id).toEqual('23', 'Slot 1 should have RF 23 in it');
|
||||
expect(anaconda.internal[3].m.grp).toEqual('rf','Slot 1 should have RF 23 in it');
|
||||
});
|
||||
|
||||
@@ -11,6 +11,15 @@ new WebpackDevServer(webpack(config), {
|
||||
// For some reason connect-history-api-fallback does not allow '.' in the URL for history fallback...
|
||||
{ from: /\/outfit\//, to: '/index.html' }
|
||||
]
|
||||
},
|
||||
stats: {
|
||||
assets: true,
|
||||
colors: true,
|
||||
version: false,
|
||||
hash: false,
|
||||
timings: true,
|
||||
chunks: false,
|
||||
chunkModules: false
|
||||
}
|
||||
}).listen(3300, "0.0.0.0", function (err, result) {
|
||||
if (err) {
|
||||
|
||||
31
package.json
31
package.json
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "coriolis_shipyard",
|
||||
"version": "2.0.0-Beta-1",
|
||||
"version": "2.2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/cmmcleod/coriolis"
|
||||
"url": "https://github.com/EDCD/coriolis"
|
||||
},
|
||||
"homepage": "https://coriolis.io",
|
||||
"bugs": "https://github.com/cmmcleod/coriolis/issues",
|
||||
"homepage": "https://coriolis.edcd.io",
|
||||
"bugs": "https://github.com/EDCD/coriolis/issues",
|
||||
"private": true,
|
||||
"engine": "node >= 4.0.0",
|
||||
"license": "MIT",
|
||||
@@ -20,18 +20,17 @@
|
||||
"prod-stop": "kill -QUIT $(cat nginx.pid)",
|
||||
"build": "npm run clean && NODE_ENV=production webpack -d -p --config webpack.config.prod.js",
|
||||
"rsync": "rsync -ae \"ssh -i $CORIOLIS_PEM\" --delete build/ $CORIOLIS_USER@$CORIOLIS_HOST:~/wwws",
|
||||
"deploy": "npm run lint && npm test && npm run build:prod && npm run rsync"
|
||||
"deploy": "npm run lint && npm test && npm run build && npm run rsync"
|
||||
},
|
||||
"jest": {
|
||||
"scriptPreprocessor": "<rootDir>/node_modules/babel-jest",
|
||||
"testFileExtensions": [
|
||||
"js"
|
||||
],
|
||||
"testRegex": "(/__tests__/test-.*|\\.(test|spec))\\.js$",
|
||||
"moduleFileExtensions": [
|
||||
"js",
|
||||
"json",
|
||||
"jsx"
|
||||
],
|
||||
"automock": true,
|
||||
"unmockedModulePathPatterns": [
|
||||
"<rootDir>/node_modules/react",
|
||||
"<rootDir>/node_modules/react-dom",
|
||||
@@ -43,7 +42,7 @@
|
||||
"<rootDir>/node_modules/d3",
|
||||
"<rootDir>/node_modules/lz-string",
|
||||
"<rootDir>/node_modules/jsen",
|
||||
"<rootDir>/node_modules/coriolis-data",
|
||||
"coriolis-data",
|
||||
"<rootDir>/src/app/shipyard",
|
||||
"<rootDir>/src/app/i18n",
|
||||
"<rootDir>/src/app/utils",
|
||||
@@ -68,13 +67,13 @@
|
||||
"extract-text-webpack-plugin": "^0.9.1",
|
||||
"file-loader": "^0.8.4",
|
||||
"html-webpack-plugin": "^1.7.0",
|
||||
"jest-cli": "*",
|
||||
"jest-cli": "^16.0.1",
|
||||
"jsen": "^0.6.0",
|
||||
"json-loader": "^0.5.3",
|
||||
"less": "^2.5.3",
|
||||
"less-loader": "^2.2.1",
|
||||
"react-addons-test-utils": "^0.14.7",
|
||||
"react-testutils-additions": "^0.16.0",
|
||||
"react-addons-test-utils": "^15.0.1",
|
||||
"react-testutils-additions": "^15.1.0",
|
||||
"rimraf": "^2.4.3",
|
||||
"style-loader": "^0.13.0",
|
||||
"url-loader": "^0.5.6",
|
||||
@@ -84,12 +83,12 @@
|
||||
"dependencies": {
|
||||
"babel-polyfill": "*",
|
||||
"classnames": "^2.2.0",
|
||||
"coriolis-data": "cmmcleod/coriolis-data",
|
||||
"d3": "^3.5.9",
|
||||
"coriolis-data": "EDCD/coriolis-data",
|
||||
"d3": "3.5.16",
|
||||
"fbemitter": "^2.0.0",
|
||||
"lz-string": "^1.4.4",
|
||||
"react": "^0.14.7",
|
||||
"react-dom": "^0.14.7",
|
||||
"react": "^15.0.1",
|
||||
"react-dom": "^15.0.1",
|
||||
"superagent": "^1.4.0"
|
||||
}
|
||||
}
|
||||
|
||||
906
src/.htaccess
Normal file
906
src/.htaccess
Normal file
@@ -0,0 +1,906 @@
|
||||
RewriteEngine on
|
||||
|
||||
# If a directory or a file exists, use the request directly
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
# Otherwise forward the request to index.php
|
||||
RewriteRule . index.html
|
||||
|
||||
# Apache Server Configs v2.10.0 | MIT License
|
||||
# https://github.com/h5bp/server-configs-apache
|
||||
|
||||
# (!) Using `.htaccess` files slows down Apache, therefore, if you have
|
||||
# access to the main server configuration file (which is usually called
|
||||
# `httpd.conf`), you should add this logic there.
|
||||
#
|
||||
# https://httpd.apache.org/docs/current/howto/htaccess.html.
|
||||
|
||||
# ######################################################################
|
||||
# # CROSS-ORIGIN #
|
||||
# ######################################################################
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# | Cross-origin requests |
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# Allow cross-origin requests.
|
||||
#
|
||||
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
|
||||
# http://enable-cors.org/
|
||||
# http://www.w3.org/TR/cors/
|
||||
|
||||
# <IfModule mod_headers.c>
|
||||
# Header set Access-Control-Allow-Origin "*"
|
||||
# </IfModule>
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# | Cross-origin images |
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# Send the CORS header for images when browsers request it.
|
||||
#
|
||||
# https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image
|
||||
# https://blog.chromium.org/2011/07/using-cross-domain-images-in-webgl-and.html
|
||||
|
||||
<IfModule mod_setenvif.c>
|
||||
<IfModule mod_headers.c>
|
||||
<FilesMatch "\.(bmp|cur|gif|ico|jpe?g|png|svgz?|webp)$">
|
||||
SetEnvIf Origin ":" IS_CORS
|
||||
Header set Access-Control-Allow-Origin "*" env=IS_CORS
|
||||
</FilesMatch>
|
||||
</IfModule>
|
||||
</IfModule>
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# | Cross-origin web fonts |
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# Allow cross-origin access to web fonts.
|
||||
|
||||
<IfModule mod_headers.c>
|
||||
<FilesMatch "\.(eot|otf|tt[cf]|woff2?)$">
|
||||
Header set Access-Control-Allow-Origin "*"
|
||||
</FilesMatch>
|
||||
</IfModule>
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# | Cross-origin resource timing |
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# Allow cross-origin access to the timing information for all resources.
|
||||
#
|
||||
# If a resource isn't served with a `Timing-Allow-Origin` header that
|
||||
# would allow its timing information to be shared with the document,
|
||||
# some of the attributes of the `PerformanceResourceTiming` object will
|
||||
# be set to zero.
|
||||
#
|
||||
# http://www.w3.org/TR/resource-timing/
|
||||
# http://www.stevesouders.com/blog/2014/08/21/resource-timing-practical-tips/
|
||||
|
||||
# <IfModule mod_headers.c>
|
||||
# Header set Timing-Allow-Origin: "*"
|
||||
# </IfModule>
|
||||
|
||||
|
||||
# ######################################################################
|
||||
# # ERRORS #
|
||||
# ######################################################################
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# | Custom error messages/pages |
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# Customize what Apache returns to the client in case of an error.
|
||||
# https://httpd.apache.org/docs/current/mod/core.html#errordocument
|
||||
|
||||
ErrorDocument 404 /404.html
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# | Error prevention |
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# Disable the pattern matching based on filenames.
|
||||
#
|
||||
# This setting prevents Apache from returning a 404 error as the result
|
||||
# of a rewrite when the directory with the same name does not exist.
|
||||
#
|
||||
# https://httpd.apache.org/docs/current/content-negotiation.html#multiviews
|
||||
|
||||
Options -MultiViews
|
||||
|
||||
|
||||
# ######################################################################
|
||||
# # INTERNET EXPLORER #
|
||||
# ######################################################################
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# | Document modes |
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# Force Internet Explorer 8/9/10 to render pages in the highest mode
|
||||
# available in the various cases when it may not.
|
||||
#
|
||||
# https://hsivonen.fi/doctype/#ie8
|
||||
#
|
||||
# (!) Starting with Internet Explorer 11, document modes are deprecated.
|
||||
# If your business still relies on older web apps and services that were
|
||||
# designed for older versions of Internet Explorer, you might want to
|
||||
# consider enabling `Enterprise Mode` throughout your company.
|
||||
#
|
||||
# http://msdn.microsoft.com/en-us/library/ie/bg182625.aspx#docmode
|
||||
# http://blogs.msdn.com/b/ie/archive/2014/04/02/stay-up-to-date-with-enterprise-mode-for-internet-explorer-11.aspx
|
||||
|
||||
<IfModule mod_headers.c>
|
||||
Header set X-UA-Compatible "IE=edge"
|
||||
# `mod_headers` cannot match based on the content-type, however,
|
||||
# the `X-UA-Compatible` response header should be send only for
|
||||
# HTML documents and not for the other resources.
|
||||
<FilesMatch "\.(appcache|atom|bbaw|bmp|crx|css|cur|eot|f4[abpv]|flv|geojson|gif|htc|ico|jpe?g|js|json(ld)?|m4[av]|manifest|map|mp4|oex|og[agv]|opus|otf|pdf|png|rdf|rss|safariextz|svgz?|swf|topojson|tt[cf]|txt|vcard|vcf|vtt|webapp|web[mp]|woff2?|xloc|xml|xpi)$">
|
||||
Header unset X-UA-Compatible
|
||||
</FilesMatch>
|
||||
</IfModule>
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# | Iframes cookies |
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# Allow cookies to be set from iframes in Internet Explorer.
|
||||
#
|
||||
# http://msdn.microsoft.com/en-us/library/ms537343.aspx
|
||||
# http://www.w3.org/TR/2000/CR-P3P-20001215/
|
||||
|
||||
# <IfModule mod_headers.c>
|
||||
# Header set P3P "policyref=\"/w3c/p3p.xml\", CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\""
|
||||
# </IfModule>
|
||||
|
||||
|
||||
# ######################################################################
|
||||
# # MEDIA TYPES AND CHARACTER ENCODINGS #
|
||||
# ######################################################################
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# | Media types |
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# Serve resources with the proper media types (f.k.a. MIME types).
|
||||
#
|
||||
# https://www.iana.org/assignments/media-types/media-types.xhtml
|
||||
# https://httpd.apache.org/docs/current/mod/mod_mime.html#addtype
|
||||
|
||||
<IfModule mod_mime.c>
|
||||
|
||||
# Data interchange
|
||||
AddType application/json json map topojson
|
||||
AddType application/ld+json jsonld
|
||||
AddType application/vnd.geo+json geojson
|
||||
AddType application/xml atom rdf rss xml
|
||||
|
||||
# JavaScript
|
||||
# Normalize to standard type.
|
||||
# https://tools.ietf.org/html/rfc4329#section-7.2
|
||||
AddType application/javascript js
|
||||
|
||||
# Manifest files
|
||||
|
||||
# If you are providing a web application manifest file (see
|
||||
# the specification: https://w3c.github.io/manifest/), it is
|
||||
# recommended that you serve it with the `application/manifest+json`
|
||||
# media type.
|
||||
#
|
||||
# Because the web application manifest file doesn't have its
|
||||
# own unique file extension, you can set its media type either
|
||||
# by matching:
|
||||
#
|
||||
# 1) the exact location of the file (this can be done using a
|
||||
# directive such as `<Location>`, but it will NOT work in
|
||||
# the `.htaccess` file, so you will have to do it in the main
|
||||
# server configuration file or inside of a `<VirtualHost>`
|
||||
# container)
|
||||
#
|
||||
# e.g.:
|
||||
#
|
||||
# <Location "/.well-known/manifest.json">
|
||||
# AddType application/manifest+json json
|
||||
# </Location>
|
||||
#
|
||||
# 2) the filename (this can be problematic as you will need to
|
||||
# ensure that you don't have any other file with the same name
|
||||
# as the one you gave to your web application manifest file)
|
||||
#
|
||||
# e.g.:
|
||||
#
|
||||
# <Files "manifest.json">
|
||||
# AddType application/manifest+json json
|
||||
# </Files>
|
||||
|
||||
AddType application/x-web-app-manifest+json webapp
|
||||
AddType text/cache-manifest appcache manifest
|
||||
|
||||
# Media files
|
||||
AddType audio/mp4 f4a f4b m4a
|
||||
AddType audio/ogg oga ogg opus
|
||||
AddType image/bmp bmp
|
||||
AddType image/webp webp
|
||||
AddType image/x-icon cur ico
|
||||
AddType video/mp4 f4v f4p m4v mp4
|
||||
AddType video/ogg ogv
|
||||
AddType video/webm webm
|
||||
AddType video/x-flv flv
|
||||
AddType image/svg+xml svg svgz
|
||||
|
||||
# Web fonts
|
||||
AddType application/font-woff woff
|
||||
AddType application/font-woff2 woff2
|
||||
AddType application/vnd.ms-fontobject eot
|
||||
|
||||
# Browsers usually ignore the font media types and simply sniff
|
||||
# the bytes to figure out the font type.
|
||||
# https://mimesniff.spec.whatwg.org/#matching-a-font-type-pattern
|
||||
#
|
||||
# However, Blink and WebKit based browsers will show a warning
|
||||
# in the console if the following font types are served with any
|
||||
# other media types.
|
||||
|
||||
AddType application/x-font-ttf ttc ttf
|
||||
AddType font/opentype otf
|
||||
|
||||
# Other
|
||||
AddType application/octet-stream safariextz
|
||||
AddType application/x-bb-appworld bbaw
|
||||
AddType application/x-chrome-extension crx
|
||||
AddType application/x-opera-extension oex
|
||||
AddType application/x-xpinstall xpi
|
||||
AddType text/vcard vcard vcf
|
||||
AddType text/vnd.rim.location.xloc xloc
|
||||
AddType text/vtt vtt
|
||||
AddType text/x-component htc
|
||||
|
||||
</IfModule>
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# | Character encodings |
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# Serve all resources labeled as `text/html` or `text/plain`
|
||||
# with the media type `charset` parameter set to `UTF-8`.
|
||||
#
|
||||
# https://httpd.apache.org/docs/current/mod/core.html#adddefaultcharset
|
||||
|
||||
AddDefaultCharset utf-8
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
# Serve the following file types with the media type `charset`
|
||||
# parameter set to `UTF-8`.
|
||||
#
|
||||
# https://httpd.apache.org/docs/current/mod/mod_mime.html#addcharset
|
||||
|
||||
<IfModule mod_mime.c>
|
||||
AddCharset utf-8 .atom \
|
||||
.bbaw \
|
||||
.css \
|
||||
.geojson \
|
||||
.js \
|
||||
.json \
|
||||
.jsonld \
|
||||
.rdf \
|
||||
.rss \
|
||||
.topojson \
|
||||
.vtt \
|
||||
.webapp \
|
||||
.xloc \
|
||||
.xml
|
||||
</IfModule>
|
||||
|
||||
|
||||
# ######################################################################
|
||||
# # REWRITES #
|
||||
# ######################################################################
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# | Rewrite engine |
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# (1) Turn on the rewrite engine (this is necessary in order for
|
||||
# the `RewriteRule` directives to work).
|
||||
#
|
||||
# https://httpd.apache.org/docs/current/mod/mod_rewrite.html#RewriteEngine
|
||||
#
|
||||
# (2) Enable the `FollowSymLinks` option if it isn't already.
|
||||
#
|
||||
# https://httpd.apache.org/docs/current/mod/core.html#options
|
||||
#
|
||||
# (3) If your web host doesn't allow the `FollowSymlinks` option,
|
||||
# you need to comment it out or remove it, and then uncomment
|
||||
# the `Options +SymLinksIfOwnerMatch` line (4), but be aware
|
||||
# of the performance impact.
|
||||
#
|
||||
# https://httpd.apache.org/docs/current/misc/perf-tuning.html#symlinks
|
||||
#
|
||||
# (4) Some cloud hosting services will require you set `RewriteBase`.
|
||||
#
|
||||
# http://www.rackspace.com/knowledge_center/frequently-asked-question/why-is-modrewrite-not-working-on-my-site
|
||||
# https://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewritebase
|
||||
#
|
||||
# (5) Depending on how your server is set up, you may also need to
|
||||
# use the `RewriteOptions` directive to enable some options for
|
||||
# the rewrite engine.
|
||||
#
|
||||
# https://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewriteoptions
|
||||
|
||||
<IfModule mod_rewrite.c>
|
||||
|
||||
# (1)
|
||||
RewriteEngine On
|
||||
|
||||
# (2)
|
||||
Options +FollowSymlinks
|
||||
|
||||
# (3)
|
||||
# Options +SymLinksIfOwnerMatch
|
||||
|
||||
# (4)
|
||||
# RewriteBase /
|
||||
|
||||
# (5)
|
||||
# RewriteOptions <options>
|
||||
|
||||
</IfModule>
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# | Forcing `https://` |
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# Redirect from the `http://` to the `https://` version of the URL.
|
||||
# https://wiki.apache.org/httpd/RewriteHTTPToHTTPS
|
||||
|
||||
# <IfModule mod_rewrite.c>
|
||||
# RewriteEngine On
|
||||
# RewriteCond %{HTTPS} !=on
|
||||
# RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]
|
||||
# </IfModule>
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# | Suppressing / Forcing the `www.` at the beginning of URLs |
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# The same content should never be available under two different
|
||||
# URLs, especially not with and without `www.` at the beginning.
|
||||
# This can cause SEO problems (duplicate content), and therefore,
|
||||
# you should choose one of the alternatives and redirect the other
|
||||
# one.
|
||||
#
|
||||
# By default `Option 1` (no `www.`) is activated.
|
||||
# http://no-www.org/faq.php?q=class_b
|
||||
#
|
||||
# If you would prefer to use `Option 2`, just comment out all the
|
||||
# lines from `Option 1` and uncomment the ones from `Option 2`.
|
||||
#
|
||||
# (!) NEVER USE BOTH RULES AT THE SAME TIME!
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
# Option 1: rewrite www.example.com → example.com
|
||||
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
RewriteCond %{HTTPS} !=on
|
||||
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
|
||||
RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L]
|
||||
</IfModule>
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
# Option 2: rewrite example.com → www.example.com
|
||||
#
|
||||
# Be aware that the following might not be a good idea if you use "real"
|
||||
# subdomains for certain parts of your website.
|
||||
|
||||
# <IfModule mod_rewrite.c>
|
||||
# RewriteEngine On
|
||||
# RewriteCond %{HTTPS} !=on
|
||||
# RewriteCond %{HTTP_HOST} !^www\. [NC]
|
||||
# RewriteCond %{SERVER_ADDR} !=127.0.0.1
|
||||
# RewriteCond %{SERVER_ADDR} !=::1
|
||||
# RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
|
||||
# </IfModule>
|
||||
|
||||
|
||||
# ######################################################################
|
||||
# # SECURITY #
|
||||
# ######################################################################
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# | Clickjacking |
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# Protect website against clickjacking.
|
||||
#
|
||||
# The example below sends the `X-Frame-Options` response header with
|
||||
# the value `DENY`, informing browsers not to display the content of
|
||||
# the web page in any frame.
|
||||
#
|
||||
# This might not be the best setting for everyone. You should read
|
||||
# about the other two possible values the `X-Frame-Options` header
|
||||
# field can have: `SAMEORIGIN` and `ALLOW-FROM`.
|
||||
# https://tools.ietf.org/html/rfc7034#section-2.1.
|
||||
#
|
||||
# Keep in mind that while you could send the `X-Frame-Options` header
|
||||
# for all of your website’s pages, this has the potential downside that
|
||||
# it forbids even non-malicious framing of your content (e.g.: when
|
||||
# users visit your website using a Google Image Search results page).
|
||||
#
|
||||
# Nonetheless, you should ensure that you send the `X-Frame-Options`
|
||||
# header for all pages that allow a user to make a state changing
|
||||
# operation (e.g: pages that contain one-click purchase links, checkout
|
||||
# or bank-transfer confirmation pages, pages that make permanent
|
||||
# configuration changes, etc.).
|
||||
#
|
||||
# Sending the `X-Frame-Options` header can also protect your website
|
||||
# against more than just clickjacking attacks:
|
||||
# https://cure53.de/xfo-clickjacking.pdf.
|
||||
#
|
||||
# https://tools.ietf.org/html/rfc7034
|
||||
# http://blogs.msdn.com/b/ieinternals/archive/2010/03/30/combating-clickjacking-with-x-frame-options.aspx
|
||||
# https://www.owasp.org/index.php/Clickjacking
|
||||
|
||||
# <IfModule mod_headers.c>
|
||||
# Header set X-Frame-Options "DENY"
|
||||
# # `mod_headers` cannot match based on the content-type, however,
|
||||
# # the `X-Frame-Options` response header should be send only for
|
||||
# # HTML documents and not for the other resources.
|
||||
# <FilesMatch "\.(appcache|atom|bbaw|bmp|crx|css|cur|eot|f4[abpv]|flv|geojson|gif|htc|ico|jpe?g|js|json(ld)?|m4[av]|manifest|map|mp4|oex|og[agv]|opus|otf|pdf|png|rdf|rss|safariextz|svgz?|swf|topojson|tt[cf]|txt|vcard|vcf|vtt|webapp|web[mp]|woff2?|xloc|xml|xpi)$">
|
||||
# Header unset X-Frame-Options
|
||||
# </FilesMatch>
|
||||
# </IfModule>
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# | Content Security Policy (CSP) |
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# Mitigate the risk of cross-site scripting and other content-injection
|
||||
# attacks.
|
||||
#
|
||||
# This can be done by setting a `Content Security Policy` which
|
||||
# whitelists trusted sources of content for your website.
|
||||
#
|
||||
# The example header below allows ONLY scripts that are loaded from the
|
||||
# current website's origin (no inline scripts, no CDN, etc). That almost
|
||||
# certainly won't work as-is for your website!
|
||||
#
|
||||
# For more details on how to craft a reasonable policy for your website,
|
||||
# read: http://www.html5rocks.com/en/tutorials/security/content-security-policy/
|
||||
# (or the specification: http://www.w3.org/TR/CSP11/). Also, to make
|
||||
# things easier, you can use an online CSP header generator such as:
|
||||
# http://cspisawesome.com/.
|
||||
|
||||
# <IfModule mod_headers.c>
|
||||
# Header set Content-Security-Policy "script-src 'self'; object-src 'self'"
|
||||
# # `mod_headers` cannot match based on the content-type, however,
|
||||
# # the `Content-Security-Policy` response header should be send
|
||||
# # only for HTML documents and not for the other resources.
|
||||
# <FilesMatch "\.(appcache|atom|bbaw|bmp|crx|css|cur|eot|f4[abpv]|flv|geojson|gif|htc|ico|jpe?g|js|json(ld)?|m4[av]|manifest|map|mp4|oex|og[agv]|opus|otf|pdf|png|rdf|rss|safariextz|svgz?|swf|topojson|tt[cf]|txt|vcard|vcf|vtt|webapp|web[mp]|woff2?|xloc|xml|xpi)$">
|
||||
# Header unset Content-Security-Policy
|
||||
# </FilesMatch>
|
||||
# </IfModule>
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# | File access |
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# Block access to directories without a default document.
|
||||
#
|
||||
# You should leave the following uncommented, as you shouldn't allow
|
||||
# anyone to surf through every directory on your server (which may
|
||||
# includes rather private places such as the CMS's directories).
|
||||
|
||||
<IfModule mod_autoindex.c>
|
||||
Options -Indexes
|
||||
</IfModule>
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
# Block access to all hidden files and directories with the exception of
|
||||
# the visible content from within the `/.well-known/` hidden directory.
|
||||
#
|
||||
# These types of files usually contain user preferences or the preserved
|
||||
# state of an utility, and can include rather private places like, for
|
||||
# example, the `.git` or `.svn` directories.
|
||||
#
|
||||
# The `/.well-known/` directory represents the standard (RFC 5785) path
|
||||
# prefix for "well-known locations" (e.g.: `/.well-known/manifest.json`,
|
||||
# `/.well-known/keybase.txt`), and therefore, access to its visible
|
||||
# content should not be blocked.
|
||||
#
|
||||
# https://www.mnot.net/blog/2010/04/07/well-known
|
||||
# https://tools.ietf.org/html/rfc5785
|
||||
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
RewriteCond %{REQUEST_URI} "!(^|/)\.well-known/([^./]+./?)+$" [NC]
|
||||
RewriteCond %{SCRIPT_FILENAME} -d [OR]
|
||||
RewriteCond %{SCRIPT_FILENAME} -f
|
||||
RewriteRule "(^|/)\." - [F]
|
||||
</IfModule>
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
# Block access to files that can expose sensitive information.
|
||||
#
|
||||
# By default, block access to backup and source files that may be
|
||||
# left by some text editors and can pose a security risk when anyone
|
||||
# has access to them.
|
||||
#
|
||||
# http://feross.org/cmsploit/
|
||||
#
|
||||
# (!) Update the `<FilesMatch>` regular expression from below to
|
||||
# include any files that might end up on your production server and
|
||||
# can expose sensitive information about your website. These files may
|
||||
# include: configuration files, files that contain metadata about the
|
||||
# project (e.g.: project dependencies), build scripts, etc..
|
||||
|
||||
<FilesMatch "(^#.*#|\.(bak|conf|dist|fla|in[ci]|log|psd|sh|sql|sw[op])|~)$">
|
||||
|
||||
# Apache < 2.3
|
||||
<IfModule !mod_authz_core.c>
|
||||
Order allow,deny
|
||||
Deny from all
|
||||
Satisfy All
|
||||
</IfModule>
|
||||
|
||||
# Apache ≥ 2.3
|
||||
<IfModule mod_authz_core.c>
|
||||
Require all denied
|
||||
</IfModule>
|
||||
|
||||
</FilesMatch>
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# | HTTP Strict Transport Security (HSTS) |
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# Force client-side SSL redirection.
|
||||
#
|
||||
# If a user types `example.com` in their browser, even if the server
|
||||
# redirects them to the secure version of the website, that still leaves
|
||||
# a window of opportunity (the initial HTTP connection) for an attacker
|
||||
# to downgrade or redirect the request.
|
||||
#
|
||||
# The following header ensures that browser will ONLY connect to your
|
||||
# server via HTTPS, regardless of what the users type in the browser's
|
||||
# address bar.
|
||||
#
|
||||
# (!) Remove the `includeSubDomains` optional directive if the website's
|
||||
# subdomains are not using HTTPS.
|
||||
#
|
||||
# http://www.html5rocks.com/en/tutorials/security/transport-layer-security/
|
||||
# https://tools.ietf.org/html/draft-ietf-websec-strict-transport-sec-14#section-6.1
|
||||
# http://blogs.msdn.com/b/ieinternals/archive/2014/08/18/hsts-strict-transport-security-attacks-mitigations-deployment-https.aspx
|
||||
|
||||
# <IfModule mod_headers.c>
|
||||
# Header set Strict-Transport-Security "max-age=16070400; includeSubDomains"
|
||||
# </IfModule>
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# | Reducing MIME type security risks |
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# Prevent some browsers from MIME-sniffing the response.
|
||||
#
|
||||
# This reduces exposure to drive-by download attacks and cross-origin
|
||||
# data leaks, and should be left uncommented, especially if the server
|
||||
# is serving user-uploaded content or content that could potentially be
|
||||
# treated as executable by the browser.
|
||||
#
|
||||
# http://www.slideshare.net/hasegawayosuke/owasp-hasegawa
|
||||
# http://blogs.msdn.com/b/ie/archive/2008/07/02/ie8-security-part-v-comprehensive-protection.aspx
|
||||
# http://msdn.microsoft.com/en-us/library/ie/gg622941.aspx
|
||||
# https://mimesniff.spec.whatwg.org/
|
||||
|
||||
<IfModule mod_headers.c>
|
||||
Header set X-Content-Type-Options "nosniff"
|
||||
</IfModule>
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# | Reflected Cross-Site Scripting (XSS) attacks |
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# (1) Try to re-enable the cross-site scripting (XSS) filter built
|
||||
# into most web browsers.
|
||||
#
|
||||
# The filter is usually enabled by default, but in some cases it
|
||||
# may be disabled by the user. However, in Internet Explorer for
|
||||
# example, it can be re-enabled just by sending the
|
||||
# `X-XSS-Protection` header with the value of `1`.
|
||||
#
|
||||
# (2) Prevent web browsers from rendering the web page if a potential
|
||||
# reflected (a.k.a non-persistent) XSS attack is detected by the
|
||||
# filter.
|
||||
#
|
||||
# By default, if the filter is enabled and browsers detect a
|
||||
# reflected XSS attack, they will attempt to block the attack
|
||||
# by making the smallest possible modifications to the returned
|
||||
# web page.
|
||||
#
|
||||
# Unfortunately, in some browsers (e.g.: Internet Explorer),
|
||||
# this default behavior may allow the XSS filter to be exploited,
|
||||
# thereby, it's better to inform browsers to prevent the rendering
|
||||
# of the page altogether, instead of attempting to modify it.
|
||||
#
|
||||
# http://hackademix.net/2009/11/21/ies-xss-filter-creates-xss-vulnerabilities
|
||||
#
|
||||
# (!) Do not rely on the XSS filter to prevent XSS attacks! Ensure that
|
||||
# you are taking all possible measures to prevent XSS attacks, the
|
||||
# most obvious being: validating and sanitizing your website's inputs.
|
||||
#
|
||||
# http://blogs.msdn.com/b/ie/archive/2008/07/02/ie8-security-part-iv-the-xss-filter.aspx
|
||||
# http://blogs.msdn.com/b/ieinternals/archive/2011/01/31/controlling-the-internet-explorer-xss-filter-with-the-x-xss-protection-http-header.aspx
|
||||
# https://www.owasp.org/index.php/Cross-site_Scripting_%28XSS%29
|
||||
|
||||
# <IfModule mod_headers.c>
|
||||
# # (1) (2)
|
||||
# Header set X-XSS-Protection "1; mode=block"
|
||||
# # `mod_headers` cannot match based on the content-type, however,
|
||||
# # the `X-XSS-Protection` response header should be send only for
|
||||
# # HTML documents and not for the other resources.
|
||||
# <FilesMatch "\.(appcache|atom|bbaw|bmp|crx|css|cur|eot|f4[abpv]|flv|geojson|gif|htc|ico|jpe?g|js|json(ld)?|m4[av]|manifest|map|mp4|oex|og[agv]|opus|otf|pdf|png|rdf|rss|safariextz|svgz?|swf|topojson|tt[cf]|txt|vcard|vcf|vtt|webapp|web[mp]|woff2?|xloc|xml|xpi)$">
|
||||
# Header unset X-XSS-Protection
|
||||
# </FilesMatch>
|
||||
# </IfModule>
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# | Server software information |
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# Prevent Apache from sending in the `Server` response header its
|
||||
# exact version number, the description of the generic OS-type or
|
||||
# information about its compiled-in modules.
|
||||
#
|
||||
# (!) The `ServerTokens` directive will only work in the main server
|
||||
# configuration file, so don't try to enable it in the `.htaccess` file!
|
||||
#
|
||||
# https://httpd.apache.org/docs/current/mod/core.html#servertokens
|
||||
|
||||
# ServerTokens Prod
|
||||
|
||||
|
||||
# ######################################################################
|
||||
# # WEB PERFORMANCE #
|
||||
# ######################################################################
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# | Compression |
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
<IfModule mod_deflate.c>
|
||||
|
||||
# Force compression for mangled `Accept-Encoding` request headers
|
||||
# https://developer.yahoo.com/blogs/ydn/pushing-beyond-gzipping-25601.html
|
||||
|
||||
<IfModule mod_setenvif.c>
|
||||
<IfModule mod_headers.c>
|
||||
SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding
|
||||
RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding
|
||||
</IfModule>
|
||||
</IfModule>
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
# Compress all output labeled with one of the following media types.
|
||||
#
|
||||
# (!) For Apache versions below version 2.3.7 you don't need to
|
||||
# enable `mod_filter` and can remove the `<IfModule mod_filter.c>`
|
||||
# and `</IfModule>` lines as `AddOutputFilterByType` is still in
|
||||
# the core directives.
|
||||
#
|
||||
# https://httpd.apache.org/docs/current/mod/mod_filter.html#addoutputfilterbytype
|
||||
|
||||
<IfModule mod_filter.c>
|
||||
AddOutputFilterByType DEFLATE "application/atom+xml" \
|
||||
"application/javascript" \
|
||||
"application/json" \
|
||||
"application/ld+json" \
|
||||
"application/manifest+json" \
|
||||
"application/rdf+xml" \
|
||||
"application/rss+xml" \
|
||||
"application/schema+json" \
|
||||
"application/vnd.geo+json" \
|
||||
"application/vnd.ms-fontobject" \
|
||||
"application/x-font-ttf" \
|
||||
"application/x-javascript" \
|
||||
"application/x-web-app-manifest+json" \
|
||||
"application/xhtml+xml" \
|
||||
"application/xml" \
|
||||
"font/opentype" \
|
||||
"image/bmp" \
|
||||
"image/svg+xml" \
|
||||
"image/x-icon" \
|
||||
"text/cache-manifest" \
|
||||
"text/css" \
|
||||
"text/html" \
|
||||
"text/javascript" \
|
||||
"text/plain" \
|
||||
"text/vcard" \
|
||||
"text/vnd.rim.location.xloc" \
|
||||
"text/vtt" \
|
||||
"text/x-component" \
|
||||
"text/xml"
|
||||
</IfModule>
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
# Map the following filename extensions to the specified
|
||||
# encoding type in order to make Apache serve the file types
|
||||
# with the appropriate `Content-Encoding` response header
|
||||
# (do note that this will NOT make Apache compress them!).
|
||||
#
|
||||
# If these files types would be served without an appropriate
|
||||
# `Content-Enable` response header, client applications (e.g.:
|
||||
# browsers) wouldn't know that they first need to uncompress
|
||||
# the response, and thus, wouldn't be able to understand the
|
||||
# content.
|
||||
#
|
||||
# https://httpd.apache.org/docs/current/mod/mod_mime.html#addencoding
|
||||
|
||||
<IfModule mod_mime.c>
|
||||
AddEncoding gzip svgz
|
||||
</IfModule>
|
||||
|
||||
</IfModule>
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# | Content transformation |
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# Prevent intermediate caches or proxies (e.g.: such as the ones
|
||||
# used by mobile network providers) from modifying the website's
|
||||
# content.
|
||||
#
|
||||
# https://tools.ietf.org/html/rfc2616#section-14.9.5
|
||||
#
|
||||
# (!) If you are using `mod_pagespeed`, please note that setting
|
||||
# the `Cache-Control: no-transform` response header will prevent
|
||||
# `PageSpeed` from rewriting `HTML` files, and, if the
|
||||
# `ModPagespeedDisableRewriteOnNoTransform` directive isn't set
|
||||
# to `off`, also from rewriting other resources.
|
||||
#
|
||||
# https://developers.google.com/speed/pagespeed/module/configuration#notransform
|
||||
|
||||
# <IfModule mod_headers.c>
|
||||
# Header merge Cache-Control "no-transform"
|
||||
# </IfModule>
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# | ETags |
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# Remove `ETags` as resources are sent with far-future expires headers.
|
||||
#
|
||||
# https://developer.yahoo.com/performance/rules.html#etags
|
||||
# https://tools.ietf.org/html/rfc7232#section-2.3
|
||||
|
||||
# `FileETag None` doesn't work in all cases.
|
||||
<IfModule mod_headers.c>
|
||||
Header unset ETag
|
||||
</IfModule>
|
||||
|
||||
FileETag None
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# | Expires headers |
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# Serve resources with far-future expires headers.
|
||||
#
|
||||
# (!) If you don't control versioning with filename-based
|
||||
# cache busting, you should consider lowering the cache times
|
||||
# to something like one week.
|
||||
#
|
||||
# https://httpd.apache.org/docs/current/mod/mod_expires.html
|
||||
|
||||
<IfModule mod_expires.c>
|
||||
|
||||
ExpiresActive on
|
||||
ExpiresDefault "access plus 1 month"
|
||||
|
||||
# CSS
|
||||
ExpiresByType text/css "access plus 1 year"
|
||||
|
||||
# Data interchange
|
||||
ExpiresByType application/atom+xml "access plus 1 hour"
|
||||
ExpiresByType application/rdf+xml "access plus 1 hour"
|
||||
ExpiresByType application/rss+xml "access plus 1 hour"
|
||||
|
||||
ExpiresByType application/json "access plus 0 seconds"
|
||||
ExpiresByType application/ld+json "access plus 0 seconds"
|
||||
ExpiresByType application/schema+json "access plus 0 seconds"
|
||||
ExpiresByType application/vnd.geo+json "access plus 0 seconds"
|
||||
ExpiresByType application/xml "access plus 0 seconds"
|
||||
ExpiresByType text/xml "access plus 0 seconds"
|
||||
|
||||
# Favicon (cannot be renamed!) and cursor images
|
||||
ExpiresByType image/x-icon "access plus 1 week"
|
||||
|
||||
# HTML
|
||||
ExpiresByType text/html "access plus 0 seconds"
|
||||
|
||||
# JavaScript
|
||||
ExpiresByType application/javascript "access plus 1 year"
|
||||
ExpiresByType application/x-javascript "access plus 1 year"
|
||||
ExpiresByType text/javascript "access plus 1 year"
|
||||
|
||||
# Manifest files
|
||||
ExpiresByType application/manifest+json "access plus 1 year"
|
||||
|
||||
ExpiresByType application/x-web-app-manifest+json "access plus 0 seconds"
|
||||
ExpiresByType text/cache-manifest "access plus 0 seconds"
|
||||
|
||||
# Media files
|
||||
ExpiresByType audio/ogg "access plus 1 month"
|
||||
ExpiresByType image/bmp "access plus 1 month"
|
||||
ExpiresByType image/gif "access plus 1 month"
|
||||
ExpiresByType image/jpeg "access plus 1 month"
|
||||
ExpiresByType image/png "access plus 1 month"
|
||||
ExpiresByType video/mp4 "access plus 1 month"
|
||||
ExpiresByType video/ogg "access plus 1 month"
|
||||
ExpiresByType video/webm "access plus 1 month"
|
||||
|
||||
# Web fonts
|
||||
ExpiresByType application/font-woff "access plus 1 month"
|
||||
ExpiresByType application/font-woff2 "access plus 1 month"
|
||||
ExpiresByType application/vnd.ms-fontobject "access plus 1 month"
|
||||
ExpiresByType application/x-font-ttf "access plus 1 month"
|
||||
ExpiresByType font/opentype "access plus 1 month"
|
||||
ExpiresByType image/svg+xml "access plus 1 month"
|
||||
|
||||
</IfModule>
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# | File concatenation |
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# Allow concatenation from within specific files.
|
||||
#
|
||||
# e.g.:
|
||||
#
|
||||
# If you have the following lines in a file called, for
|
||||
# example, `main.combined.js`:
|
||||
#
|
||||
# <!--#include file="js/jquery.js" -->
|
||||
# <!--#include file="js/jquery.timer.js" -->
|
||||
#
|
||||
# Apache will replace those lines with the content of the
|
||||
# specified files.
|
||||
|
||||
# <IfModule mod_include.c>
|
||||
# <FilesMatch "\.combined\.js$">
|
||||
# Options +Includes
|
||||
# AddOutputFilterByType INCLUDES application/javascript \
|
||||
# application/x-javascript \
|
||||
# text/javascript
|
||||
# SetOutputFilter INCLUDES
|
||||
# </FilesMatch>
|
||||
# <FilesMatch "\.combined\.css$">
|
||||
# Options +Includes
|
||||
# AddOutputFilterByType INCLUDES text/css
|
||||
# SetOutputFilter INCLUDES
|
||||
# </FilesMatch>
|
||||
# </IfModule>
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# | Filename-based cache busting |
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# If you're not using a build process to manage your filename version
|
||||
# revving, you might want to consider enabling the following directives
|
||||
# to route all requests such as `/style.12345.css` to `/style.css`.
|
||||
#
|
||||
# To understand why this is important and even a better solution than
|
||||
# using something like `*.css?v231`, please see:
|
||||
# http://www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/
|
||||
|
||||
# <IfModule mod_rewrite.c>
|
||||
# RewriteEngine On
|
||||
# RewriteCond %{REQUEST_FILENAME} !-f
|
||||
# RewriteRule ^(.+)\.(\d+)\.(bmp|css|cur|gif|ico|jpe?g|js|png|svgz?|webp)$ $1.$3 [L]
|
||||
# </IfModule>
|
||||
@@ -21,17 +21,18 @@ import ErrorDetails from './pages/ErrorDetails';
|
||||
export default class Coriolis extends React.Component {
|
||||
|
||||
static childContextTypes = {
|
||||
language: React.PropTypes.object.isRequired,
|
||||
sizeRatio: React.PropTypes.number.isRequired,
|
||||
route: React.PropTypes.object.isRequired,
|
||||
openMenu: React.PropTypes.func.isRequired,
|
||||
closeMenu: React.PropTypes.func.isRequired,
|
||||
showModal: React.PropTypes.func.isRequired,
|
||||
hideModal: React.PropTypes.func.isRequired,
|
||||
tooltip: React.PropTypes.func.isRequired,
|
||||
termtip: React.PropTypes.func.isRequired,
|
||||
language: React.PropTypes.object.isRequired,
|
||||
noTouch: React.PropTypes.bool.isRequired,
|
||||
onCommand: React.PropTypes.func.isRequired,
|
||||
onWindowResize: React.PropTypes.func.isRequired,
|
||||
onCommand: React.PropTypes.func.isRequired
|
||||
openMenu: React.PropTypes.func.isRequired,
|
||||
route: React.PropTypes.object.isRequired,
|
||||
showModal: React.PropTypes.func.isRequired,
|
||||
sizeRatio: React.PropTypes.number.isRequired,
|
||||
termtip: React.PropTypes.func.isRequired,
|
||||
tooltip: React.PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -54,9 +55,10 @@ export default class Coriolis extends React.Component {
|
||||
|
||||
this.emitter = new EventEmitter();
|
||||
this.state = {
|
||||
noTouch: !('ontouchstart' in window || navigator.msMaxTouchPoints || navigator.maxTouchPoints),
|
||||
page: null,
|
||||
language: getLanguage(Persist.getLangCode()),
|
||||
route: null,
|
||||
route: {},
|
||||
sizeRatio: Persist.getSizeRatio()
|
||||
};
|
||||
|
||||
@@ -237,17 +239,18 @@ export default class Coriolis extends React.Component {
|
||||
*/
|
||||
getChildContext() {
|
||||
return {
|
||||
language: this.state.language,
|
||||
route: this.state.route,
|
||||
sizeRatio: this.state.sizeRatio,
|
||||
openMenu: this._openMenu,
|
||||
closeMenu: this._closeMenu,
|
||||
showModal: this._showModal,
|
||||
hideModal: this._hideModal,
|
||||
tooltip: this._tooltip,
|
||||
termtip: this._termtip,
|
||||
language: this.state.language,
|
||||
noTouch: this.state.noTouch,
|
||||
onCommand: this._onCommand,
|
||||
onWindowResize: this._onWindowResize,
|
||||
onCommand: this._onCommand
|
||||
openMenu: this._openMenu,
|
||||
route: this.state.route,
|
||||
showModal: this._showModal,
|
||||
sizeRatio: this.state.sizeRatio,
|
||||
termtip: this._termtip,
|
||||
tooltip: this._tooltip
|
||||
};
|
||||
}
|
||||
|
||||
@@ -266,7 +269,7 @@ export default class Coriolis extends React.Component {
|
||||
|
||||
window.onerror = this._onError.bind(this);
|
||||
window.addEventListener('resize', () => this.emitter.emit('windowResize'));
|
||||
document.body.addEventListener('scroll', () => this._tooltip());
|
||||
document.getElementById('coriolis').addEventListener('scroll', () => this._tooltip());
|
||||
document.addEventListener('keydown', this._keyDown);
|
||||
Persist.addListener('language', this._onLanguageChange);
|
||||
Persist.addListener('sizeRatio', this._onSizeRatioChange);
|
||||
@@ -281,11 +284,16 @@ export default class Coriolis extends React.Component {
|
||||
render() {
|
||||
let currentMenu = this.state.currentMenu;
|
||||
|
||||
return <div onClick={this._closeMenu}>
|
||||
return <div style={{ minHeight: '100%' }} onClick={this._closeMenu} className={ this.state.noTouch ? 'no-touch' : null }>
|
||||
<Header appCacheUpdate={this.state.appCacheUpdate} currentMenu={currentMenu} />
|
||||
{ this.state.error ? this.state.error : this.state.page ? React.createElement(this.state.page, { currentMenu }) : <NotFoundPage/> }
|
||||
{ this.state.modal }
|
||||
{ this.state.tooltip }
|
||||
<footer>
|
||||
<div className="right cap">
|
||||
<a href="https://github.com/EDCD/coriolis" target="_blank" title="Coriolis Github Project">{window.CORIOLIS_VERSION} - {window.CORIOLIS_DATE}</a>
|
||||
</div>
|
||||
</footer>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
import Persist from './stores/Persist';
|
||||
|
||||
let standalone = undefined;
|
||||
|
||||
/**
|
||||
* Determine if the app is running in mobile/tablet 'standalone' mode
|
||||
* @return {Boolean} True if the app is in standalone mode
|
||||
*/
|
||||
function isStandAlone() {
|
||||
try {
|
||||
return window.navigator.standalone || (window.external && window.external.msIsSiteMode && window.external.msIsSiteMode());
|
||||
} catch (ex) {
|
||||
return false;
|
||||
if (standalone === undefined) {
|
||||
try {
|
||||
standalone = window.navigator.standalone || (window.external && window.external.msIsSiteMode && window.external.msIsSiteMode());
|
||||
} catch (ex) {
|
||||
standalone = false;
|
||||
}
|
||||
}
|
||||
return standalone;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -44,14 +49,14 @@ Router.start = function() {
|
||||
if (isStandAlone()) {
|
||||
let state = Persist.getState();
|
||||
// If a previous state has been stored, load that state
|
||||
if (state && state.name && state.params) {
|
||||
Router(this.props.initialPath || '/');
|
||||
if (state && state.path) {
|
||||
Router.replace(state.path, null, true);
|
||||
} else {
|
||||
Router('/');
|
||||
Router.replace('/', null, true);
|
||||
}
|
||||
} else {
|
||||
let url = location.pathname + location.search;
|
||||
Router.replace(url, null, true, true);
|
||||
Router.replace(url, null, true);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -68,6 +73,9 @@ Router.go = function(path, state) {
|
||||
let ctx = new Context(path, state);
|
||||
Router.dispatch(ctx);
|
||||
if (!ctx.unhandled) {
|
||||
if (isStandAlone()) {
|
||||
Persist.setState(ctx);
|
||||
}
|
||||
history.pushState(ctx.state, ctx.title, ctx.canonicalPath);
|
||||
}
|
||||
return ctx;
|
||||
@@ -85,7 +93,12 @@ Router.go = function(path, state) {
|
||||
Router.replace = function(path, state, dispatch) {
|
||||
gaTrack(path);
|
||||
let ctx = new Context(path, state);
|
||||
if (dispatch) Router.dispatch(ctx);
|
||||
if (dispatch) {
|
||||
Router.dispatch(ctx);
|
||||
}
|
||||
if (isStandAlone()) {
|
||||
Persist.setState(ctx);
|
||||
}
|
||||
history.replaceState(ctx.state, ctx.title, ctx.canonicalPath);
|
||||
return ctx;
|
||||
};
|
||||
@@ -227,7 +240,7 @@ Route.prototype.match = function(path, params) {
|
||||
*/
|
||||
function gaTrack(path) {
|
||||
if (window.ga) {
|
||||
window.ga('send', 'pageview', { page: path });
|
||||
window.ga('send', 'pageview', path);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
||||
import cn from 'classnames';
|
||||
import { MountFixed, MountGimballed, MountTurret } from './SvgIcons';
|
||||
|
||||
const PRESS_THRESHOLD = 5000; // mouse/touch down threshold
|
||||
const PRESS_THRESHOLD = 500; // mouse/touch down threshold
|
||||
|
||||
/**
|
||||
* Available modules menu
|
||||
@@ -173,6 +173,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
* @param {SyntheticEvent} event Event
|
||||
*/
|
||||
_touchStart(showDiff, event) {
|
||||
event.preventDefault();
|
||||
let rect = event.currentTarget.getBoundingClientRect();
|
||||
this.touchTimeout = setTimeout(showDiff.bind(this, rect), PRESS_THRESHOLD);
|
||||
}
|
||||
@@ -183,10 +184,10 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
* @param {SyntheticEvent} event Event
|
||||
*/
|
||||
_touchEnd(select, event) {
|
||||
event.preventDefault();
|
||||
if (this.touchTimeout !== null) { // If timeout has not fired (been nulled out) yet
|
||||
select();
|
||||
}
|
||||
event.preventDefault();
|
||||
this._hideDiff();
|
||||
}
|
||||
|
||||
|
||||
@@ -208,7 +208,7 @@ export default class BarChart extends TranslatedComponent {
|
||||
<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' }}>
|
||||
<tspan>{title}</tspan>
|
||||
{ unit ? <tspan className='metric'>{` (${unit})`}</tspan> : null }
|
||||
{ unit ? <tspan className='metric'> ({unit})</tspan> : null }
|
||||
</text>
|
||||
</g>
|
||||
<g className='y axis' ref={(elem) => { let e = d3.select(elem); e.call(this.yAxis); e.selectAll('text').each(insertLinebreaks); }} />
|
||||
|
||||
@@ -304,8 +304,8 @@ export default class CostSection extends TranslatedComponent {
|
||||
<tr className='main'>
|
||||
<th colSpan='2' className='sortable le' onClick={this._sortCostBy.bind(this,'m')}>
|
||||
{translate('module')}
|
||||
{shipDiscount && <u className='cap optional-hide' style={{ marginLeft: '0.5em' }}>{`[${translate('ship')} -${formats.pct(shipDiscount)}]`}</u>}
|
||||
{moduleDiscount && <u className='cap optional-hide' style={{ marginLeft: '0.5em' }}>{`[${translate('modules')} -${formats.pct(moduleDiscount)}]`}</u>}
|
||||
{shipDiscount ? <u className='cap optional-hide' style={{ marginLeft: '0.5em' }}>{`[${translate('ship')} -${formats.pct(shipDiscount)}]`}</u> : null}
|
||||
{moduleDiscount ? <u className='cap optional-hide' style={{ marginLeft: '0.5em' }}>{`[${translate('modules')} -${formats.pct(moduleDiscount)}]`}</u> : null}
|
||||
</th>
|
||||
<th className='sortable le' onClick={this._sortCostBy.bind(this, 'cr')} >{translate('credits')}</th>
|
||||
</tr>
|
||||
@@ -363,7 +363,7 @@ export default class CostSection extends TranslatedComponent {
|
||||
<th colSpan='2' className='sortable le' onClick={this._sortRetrofitBy.bind(this, 'buyName')}>{translate('buy')}</th>
|
||||
<th colSpan='2' className='sortable le' onClick={this._sortRetrofitBy.bind(this, 'cr')}>
|
||||
{translate('net cost')}
|
||||
{moduleDiscount < 1 && <u className='cap optional-hide' style={{ marginLeft: '0.5em' }}>{`[${translate('modules')} -${formats.pct(moduleDiscount)}]`}</u>}
|
||||
{moduleDiscount ? <u className='cap optional-hide' style={{ marginLeft: '0.5em' }}>{`[${translate('modules')} -${formats.pct(moduleDiscount)}]`}</u> : null}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -400,7 +400,7 @@ export default class CostSection extends TranslatedComponent {
|
||||
let retrofitCosts = [];
|
||||
let retrofitTotal = 0, i, l, item;
|
||||
|
||||
if (ship.bulkheads.index != retrofitShip.bulkheads.index) {
|
||||
if (ship.bulkheads.m.index != retrofitShip.bulkheads.m.index) {
|
||||
item = {
|
||||
buyClassRating: ship.bulkheads.m.class + ship.bulkheads.m.rating,
|
||||
buyName: ship.bulkheads.m.name,
|
||||
@@ -606,6 +606,7 @@ export default class CostSection extends TranslatedComponent {
|
||||
}
|
||||
|
||||
if (nextProps.ship != this.props.ship || nextProps.code != this.props.code) {
|
||||
nextProps.ship.applyDiscounts(Persist.getShipDiscount(), Persist.getModuleDiscount());
|
||||
this._updateAmmoCosts(nextProps.ship);
|
||||
this._updateRetrofit(nextProps.ship, retrofitShip);
|
||||
this._sortCost(nextProps.ship);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import Slot from './Slot';
|
||||
import { DamageKinetic, DamageThermal, DamageExplosive, MountFixed, MountGimballed, MountTurret } from './SvgIcons';
|
||||
|
||||
/**
|
||||
* Hardpoint / Utility Slot
|
||||
@@ -33,24 +34,31 @@ export default class HardpointSlot extends Slot {
|
||||
*/
|
||||
_getSlotDetails(m, translate, formats, u) {
|
||||
if (m) {
|
||||
let classRating = `${m.class}${m.rating}${m.mount ? '/' + m.mount : ''}${m.missile ? m.missile : ''}`;
|
||||
let classRating = `${m.class}${m.rating}${m.missile ? '/' + m.missile : ''}`;
|
||||
let { drag, drop } = this.props;
|
||||
|
||||
return <div className='details' draggable='true' onDragStart={drag} onDragEnd={drop}>
|
||||
<div className={'cb'}>
|
||||
<div className={'l'}>{classRating + ' ' + translate(m.name || m.grp)}</div>
|
||||
<div className={'l'}>
|
||||
{m.mount && m.mount == 'F' ? <MountFixed /> : ''}
|
||||
{m.mount && m.mount == 'G' ? <MountGimballed /> : ''}
|
||||
{m.mount && m.mount == 'T' ? <MountTurret /> : ''}
|
||||
{m.type && m.type == 'K' ? <DamageKinetic /> : ''}
|
||||
{m.type && m.type == 'T' ? <DamageThermal /> : ''}
|
||||
{m.type && m.type == 'KT' ? <span><DamageKinetic /><DamageThermal /></span> : ''}
|
||||
{m.type && m.type == 'E' ? <DamageExplosive /> : ''}
|
||||
{classRating} {translate(m.name || m.grp)}</div>
|
||||
<div className={'r'}>{m.mass}{u.T}</div>
|
||||
</div>
|
||||
<div className={'cb'}>
|
||||
{ m.damage ? <div className={'l'}>{translate('damage')}: {m.damage} { m.ssdam ? <span>({formats.int(m.ssdam)} {u.MJ})</span> : null }</div> : null }
|
||||
{ m.dps ? <div className={'l'}>{translate('DPS')}: {m.dps} { m.mjdps ? <span>({formats.int(m.mjdps)} {u.MJ})</span> : null }</div> : null }
|
||||
{ m.thermload ? <div className={'l'}>{translate('T-Load')}: {m.thermload}</div> : null }
|
||||
{ m.type ? <div className={'l'}>{translate('type')}: {m.type}</div> : null }
|
||||
{ m.dps ? <div className={'l'}>{translate('DPS')}: {formats.round1(m.dps)} { m.clip ? <span>({formats.round1((m.clip * m.dps / m.rof) / ((m.clip / m.rof) + m.reload)) })</span> : null }</div> : null }
|
||||
{ m.eps ? <div className={'l'}>{translate('EPS')}: {formats.round1(m.eps)} { m.clip ? <span>({formats.round1((m.clip * m.eps / m.rof) / ((m.clip / m.rof) + m.reload)) })</span> : null }</div> : null }
|
||||
{ m.hps ? <div className={'l'}>{translate('HPS')}: {formats.round1(m.hps)} { m.clip ? <span>({formats.round1((m.clip * m.hps / m.rof) / ((m.clip / m.rof) + m.reload)) })</span> : null }</div> : null }
|
||||
{ m.dps && m.eps ? <div className={'l'}>{translate('DPE')}: {formats.round1(m.dps / m.eps)}</div> : null }
|
||||
{ m.rof ? <div className={'l'}>{translate('ROF')}: {m.rof}{u.ps}</div> : null }
|
||||
{ m.armourpen ? <div className={'l'}>{translate('pen')}: {m.armourpen}</div> : null }
|
||||
{ m.range && !m.dps ? <div className={'l'}>{translate('Range')} : {formats.round(m.range / 1000)}{u.km}</div> : null }
|
||||
{ m.shieldmul ? <div className={'l'}>+{formats.rPct(m.shieldmul)}</div> : null }
|
||||
{ m.range ? <div className={'l'}>{m.range} <u>km</u></div> : null }
|
||||
{ m.ammo >= 0 ? <div className={'l'}>{translate('ammo')}: {formats.int(m.clip)}+{formats.int(m.ammo)}</div> : null }
|
||||
{ m.ammo >= 0 ? <div className={'l'}>{translate('ammo')}: {formats.int(m.clip)}/{formats.int(m.ammo)}</div> : null }
|
||||
</div>
|
||||
</div>;
|
||||
} else {
|
||||
|
||||
@@ -94,6 +94,7 @@ export default class HardpointsSlotSection extends SlotSection {
|
||||
return <div className='select hardpoint' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
|
||||
<ul>
|
||||
<li className='lc' onClick={this._empty}>{translate('empty all')}</li>
|
||||
<li className='optional-hide' style={{ textAlign: 'center', marginTop: '1em' }}>{translate('PHRASE_ALT_ALL')}</li>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('pl')}</div>
|
||||
<ul>
|
||||
|
||||
@@ -362,37 +362,51 @@ export default class Header extends TranslatedComponent {
|
||||
|
||||
return (
|
||||
<div className='menu-list no-wrap cap' onClick={ (e) => e.stopPropagation() }>
|
||||
<div style={{ lineHeight: '2em' }}>
|
||||
{translate('language')}
|
||||
<select className='cap' value={Persist.getLangCode()} onChange={this._setLanguage}>
|
||||
{this.languageOptions}
|
||||
</select>
|
||||
<br/>
|
||||
<span className='cap ptr' onClick={this._toggleTooltips} >
|
||||
{translate('tooltips')}
|
||||
<div className={cn({ disabled: !tips, 'primary-disabled': tips })} style={{ marginLeft: '0.5em', display: 'inline-block' }}>{(tips ? '✓' : '✗')}</div>
|
||||
</span>
|
||||
<br/>
|
||||
{translate('insurance')}
|
||||
<select className='cap' value={Persist.getInsurance()} onChange={this._setInsurance}>
|
||||
{this.insuranceOptions}
|
||||
</select>
|
||||
<br/>
|
||||
{translate('ship')} {translate('discount')}
|
||||
<input type='text' size='10' value={this.state.shipDiscount} onChange={this._changeShipDiscount} onFocus={selectAll} onBlur={this._setShipDiscount} onKeyDown={this._kpShipDiscount}/>
|
||||
<u className='primary-disabled'>%</u>
|
||||
<br/>
|
||||
{translate('module')} {translate('discount')}
|
||||
<input type='text' size='10' value={this.state.moduleDiscount} onChange={this._changeModuleDiscount} onFocus={selectAll} onBlur={this._setModuleDiscount} onKeyDown={this._kpModuleDiscount}/>
|
||||
<u className='primary-disabled'>%</u>
|
||||
</div>
|
||||
<table style={{ width: '100%', background: 'none', borderSpacing: '0 0.5em' }}>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{translate('language')}</td>
|
||||
<td className='ri'>
|
||||
<select className='cap' dir='rtl' value={Persist.getLangCode()} onChange={this._setLanguage}>
|
||||
{this.languageOptions}
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr className='cap ptr' onClick={this._toggleTooltips} >
|
||||
<td>{translate('tooltips')}</td>
|
||||
<td className={cn('ri', { disabled: !tips, 'primary-disabled': tips })}>{(tips ? '✓' : '✗')}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{translate('insurance')}</td>
|
||||
<td className='ri'>
|
||||
<select className='cap' dir='rtl' value={Persist.getInsurance()} onChange={this._setInsurance}>
|
||||
{this.insuranceOptions}
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{translate('ship')} {translate('discount')}</td>
|
||||
<td className='ri'>
|
||||
<input type='text' size='10' value={this.state.shipDiscount} onChange={this._changeShipDiscount} onFocus={selectAll} onBlur={this._setShipDiscount} onKeyDown={this._kpShipDiscount}/>
|
||||
<u className='primary-disabled'>%</u>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{translate('module')} {translate('discount')}</td>
|
||||
<td className='ri'>
|
||||
<input type='text' size='10' value={this.state.moduleDiscount} onChange={this._changeModuleDiscount} onFocus={selectAll} onBlur={this._setModuleDiscount} onKeyDown={this._kpModuleDiscount}/>
|
||||
<u className='primary-disabled'>%</u>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<hr />
|
||||
<ul>
|
||||
<ul style={{ width: '100%' }}>
|
||||
{translate('builds')} & {translate('comparisons')}
|
||||
<li><Link href="#" className='block' onClick={this._showBackup.bind(this)}>{translate('backup')}</Link></li>
|
||||
<li><Link href="#" className='block' onClick={this._showDetailedExport.bind(this)}>{translate('detailed export')}</Link></li>
|
||||
<li><Link href="#" className='block' onClick={this._showImport.bind(this)}>{translate('import')}</Link></li>
|
||||
<li><Link href="#" onClick={this._showDeleteAll.bind(this)}>{translate('delete all')}</Link></li>
|
||||
<li><Link href="#" className='block' onClick={this._showDeleteAll.bind(this)}>{translate('delete all')}</Link></li>
|
||||
</ul>
|
||||
<hr />
|
||||
<table style={{ width: 300, backgroundColor: 'transparent' }}>
|
||||
@@ -462,32 +476,28 @@ export default class Header extends TranslatedComponent {
|
||||
let translate = this.context.language.translate;
|
||||
let openedMenu = this.props.currentMenu;
|
||||
let hasBuilds = Persist.hasBuilds();
|
||||
|
||||
if (this.props.appCacheUpdate) {
|
||||
return <div id="app-update" onClick={() => window.location.reload() }>{translate('PHRASE_UPDATE_RDY')}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<header>
|
||||
{this.props.appCacheUpdate && <div id="app-update" onClick={() => window.location.reload() }>{translate('PHRASE_UPDATE_RDY')}</div>}
|
||||
<Link className='l' href='/' style={{ marginRight: '1em' }} title='Home'><CoriolisLogo className='icon xl' /></Link>
|
||||
|
||||
<div className='l menu'>
|
||||
<div className={cn('menu-header', { selected: openedMenu == 's' })} onClick={this._openShips}>
|
||||
<Rocket className='warning' /><span className='menu-item-label'>{' ' + translate('ships')}</span>
|
||||
<Rocket className='warning' /><span className='menu-item-label'>{translate('ships')}</span>
|
||||
</div>
|
||||
{openedMenu == 's' ? this._getShipsMenu() : null}
|
||||
</div>
|
||||
|
||||
<div className='l menu'>
|
||||
<div className={cn('menu-header', { selected: openedMenu == 'b', disabled: !hasBuilds })} onClick={hasBuilds && this._openBuilds}>
|
||||
<Hammer className={cn('warning', { 'warning-disabled': !hasBuilds })} /><span className='menu-item-label'>{' ' + translate('builds')}</span>
|
||||
<Hammer className={cn('warning', { 'warning-disabled': !hasBuilds })} /><span className='menu-item-label'>{translate('builds')}</span>
|
||||
</div>
|
||||
{openedMenu == 'b' ? this._getBuildsMenu() : null}
|
||||
</div>
|
||||
|
||||
<div className='l menu'>
|
||||
<div className={cn('menu-header', { selected: openedMenu == 'comp', disabled: !hasBuilds })} onClick={hasBuilds && this._openComp}>
|
||||
<StatsBars className={cn('warning', { 'warning-disabled': !hasBuilds })} /><span className='menu-item-label'>{' ' + translate('compare')}</span>
|
||||
<StatsBars className={cn('warning', { 'warning-disabled': !hasBuilds })} /><span className='menu-item-label'>{translate('compare')}</span>
|
||||
</div>
|
||||
{openedMenu == 'comp' ? this._getComparisonsMenu() : null}
|
||||
</div>
|
||||
|
||||
@@ -22,14 +22,14 @@ export default class InternalSlot extends Slot {
|
||||
|
||||
return <div className='details' draggable='true' onDragStart={drag} onDragEnd={drop}>
|
||||
<div className={'cb'}>
|
||||
<div className={'l'}>{classRating + ' ' + translate(m.name || m.grp)}</div>
|
||||
<div className={'l'}>{classRating} {translate(m.name || m.grp)}</div>
|
||||
<div className={'r'}>{m.mass || m.cargo || m.fuel || 0}{u.T}</div>
|
||||
</div>
|
||||
<div className={'cb'}>
|
||||
{ m.optmass ? <div className={'l'}>{translate('optimal mass') + ': '}{m.optmass}{u.T}</div> : null }
|
||||
{ m.maxmass ? <div className={'l'}>{translate('max mass') + ': '}{m.maxmass}{u.T}</div> : null }
|
||||
{ m.bins ? <div className={'l'}>{m.bins + ' '}<u>{translate('bins')}</u></div> : null }
|
||||
{ m.bays ? <div className={'l'}>{translate('bays') + ': ' + m.bays}</div> : null }
|
||||
{ m.optmass ? <div className={'l'}>{translate('optimal mass')}: {m.optmass}{u.T}</div> : null }
|
||||
{ m.maxmass ? <div className={'l'}>{translate('max mass')}: {m.maxmass}{u.T}</div> : null }
|
||||
{ m.bins ? <div className={'l'}>{m.bins} <u>{translate('bins')}</u></div> : null }
|
||||
{ m.bays ? <div className={'l'}>{translate('bays')}: {m.bays}</div> : null }
|
||||
{ m.rate ? <div className={'l'}>{translate('rate')}: {m.rate}{u.kgs} {translate('refuel time')}: {formats.time(this.props.fuel * 1000 / m.rate)}</div> : null }
|
||||
{ m.ammo ? <div className={'l'}>{translate('ammo')}: {formats.gen(m.ammo)}</div> : null }
|
||||
{ m.cells ? <div className={'l'}>{translate('cells')}: {m.cells}</div> : null }
|
||||
@@ -38,10 +38,10 @@ export default class InternalSlot extends Slot {
|
||||
{ m.range ? <div className={'l'}>{translate('range')} {m.range}{u.km}</div> : null }
|
||||
{ m.time ? <div className={'l'}>{translate('time')}: {formats.time(m.time)}</div> : null }
|
||||
{ m.maximum ? <div className={'l'}>{translate('max')}: {(m.maximum)}</div> : null }
|
||||
{ m.rangeLS ? <div className={'l'}>{m.rangeLS}{u.Ls}</div> : null }
|
||||
{ m.rangeLS === null ? <div className={'l'}><Infinite/>{u.Ls}</div> : null }
|
||||
{ m.rangeLS ? <div className={'l'}>{translate('range')}: {m.rangeLS}{u.Ls}</div> : null }
|
||||
{ m.rangeLS === null ? <div className={'l'}>∞{u.Ls}</div> : null }
|
||||
{ m.rangeRating ? <div className={'l'}>{translate('range')}: {m.rangeRating}</div> : null }
|
||||
{ m.armouradd ? <div className={'l'}>+{m.armouradd} <u>{translate('armour')}</u></div> : null }
|
||||
{ m.armouradd ? <div className={'l'}>+{m.armouradd} <u className='cap'>{translate('armour')}</u></div> : null }
|
||||
</div>
|
||||
</div>;
|
||||
} else {
|
||||
|
||||
@@ -58,7 +58,7 @@ export default class InternalSlotSection extends SlotSection {
|
||||
let ship = this.props.ship;
|
||||
let chargeCap = 0; // Capacity of single activation
|
||||
ship.internal.forEach(function(slot) {
|
||||
if ((!slot.m || (clobber && !ModuleUtils.isShieldGenerator(slot.m.grp))) && (!slot.eligible || slot.eligible.scb)) { // Check eligibility due to Orca special case
|
||||
if ((!slot.m || (clobber && !ModuleUtils.isShieldGenerator(slot.m.grp))) && (!slot.eligible || slot.eligible.scb)) { // Check eligibility due to passenger ships special case
|
||||
ship.use(slot, ModuleUtils.findInternal('scb', slot.maxClass, 'A'));
|
||||
ship.setSlotEnabled(slot, chargeCap <= ship.shieldStrength); // Don't waste cell capacity on overcharge
|
||||
chargeCap += slot.m.recharge;
|
||||
@@ -76,7 +76,7 @@ export default class InternalSlotSection extends SlotSection {
|
||||
let clobber = event.getModifierState('Alt');
|
||||
let ship = this.props.ship;
|
||||
ship.internal.forEach((slot) => {
|
||||
if (clobber || !slot.c) {
|
||||
if (clobber || !slot.m) {
|
||||
ship.use(slot, ModuleUtils.findInternal('hr', Math.min(slot.maxClass, 5), 'D')); // Hull reinforcements top out at 5D
|
||||
}
|
||||
});
|
||||
@@ -108,7 +108,7 @@ export default class InternalSlotSection extends SlotSection {
|
||||
slots.push(<InternalSlot
|
||||
key={i}
|
||||
maxClass={s.maxClass}
|
||||
availableModules={() => availableModules.getInts(s.maxClass, s.eligible)}
|
||||
availableModules={() => availableModules.getInts(ship, s.maxClass, s.eligible)}
|
||||
onOpen={this._openMenu.bind(this,s)}
|
||||
onSelect={this._selectModule.bind(this, s)}
|
||||
selected={currentMenu == s}
|
||||
@@ -138,6 +138,7 @@ export default class InternalSlotSection extends SlotSection {
|
||||
<li className='lc' onClick={this._fillWithCargo}>{translate('cargo')}</li>
|
||||
<li className='lc' onClick={this._fillWithCells}>{translate('scb')}</li>
|
||||
<li className='lc' onClick={this._fillWithArmor}>{translate('hr')}</li>
|
||||
<li className='optional-hide' style={{ textAlign: 'center', marginTop: '1em' }}>{translate('PHRASE_ALT_ALL')}</li>
|
||||
</ul>
|
||||
</div>;
|
||||
}
|
||||
|
||||
@@ -235,13 +235,13 @@ export default class LineChart extends TranslatedComponent {
|
||||
<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' }}>
|
||||
<tspan>{xLabel}</tspan>
|
||||
<tspan className='metric'>{` (${xUnit})`}</tspan>
|
||||
<tspan className='metric'> ({xUnit})</tspan>
|
||||
</text>
|
||||
</g>
|
||||
<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' }}>
|
||||
<tspan>{yLabel}</tspan>
|
||||
<tspan className='metric'>{` (${yUnit})`}</tspan>
|
||||
<tspan className='metric'> ({yUnit})</tspan>
|
||||
</text>
|
||||
</g>
|
||||
<g ref={(g) => this.tipContainer = d3.select(g)} style={{ display: 'none' }}>
|
||||
|
||||
@@ -36,7 +36,7 @@ export default class Link extends React.Component {
|
||||
* @param {SyntheticEvent} event Event
|
||||
*/
|
||||
handler(event) {
|
||||
if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey || event.button > 1) {
|
||||
if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey || event.button > 0) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
|
||||
@@ -271,8 +271,8 @@ export default class PowerBands extends TranslatedComponent {
|
||||
<line x1={pctScale(0.5)} x2={pctScale(0.5)} y1='0' y2={state.innerHeight} className={pwrWarningClass} />
|
||||
<text dy='0.5em' x='-3' y={state.retY} className='primary upp' textAnchor='end' onMouseOver={this.context.termtip.bind(null, 'retracted')} onMouseLeave={this._hidetip}>{translate('ret')}</text>
|
||||
<text dy='0.5em' x='-3' y={state.depY} className='primary upp' textAnchor='end' onMouseOver={this.context.termtip.bind(null, 'deployed', { orientation: 's', cap: 1 })} onMouseLeave={this._hidetip}>{translate('dep')}</text>
|
||||
<text dy='0.5em' x={innerWidth + 5} y={state.retY} className={getClass(retSelected, retSum, available)}>{f2(Math.max(0, retSum)) + ' (' + pct1(Math.max(0, retSum / available)) + ')'}</text>
|
||||
<text dy='0.5em' x={innerWidth + 5} y={state.depY} className={getClass(depSelected, depSum, available)}>{f2(Math.max(0, depSum)) + ' (' + pct1(Math.max(0, depSum / available)) + ')'}</text>
|
||||
<text dy='0.5em' x={innerWidth + 5} y={state.retY} className={getClass(retSelected, retSum, available)}>{f2(Math.max(0, retSum))} ({pct1(Math.max(0, retSum / available))})</text>
|
||||
<text dy='0.5em' x={innerWidth + 5} y={state.depY} className={getClass(depSelected, depSum, available)}>{f2(Math.max(0, depSum))} ({pct1(Math.max(0, depSum / available))})</text>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import { wrapCtxMenu } from '../utils/UtilityFunctions';
|
||||
import { canMount } from '../utils/SlotFunctions';
|
||||
import { Equalizer } from '../components/SvgIcons';
|
||||
import cn from 'classnames';
|
||||
|
||||
@@ -90,7 +91,7 @@ export default class SlotSection extends TranslatedComponent {
|
||||
e.stopPropagation();
|
||||
let os = this.state.originSlot;
|
||||
if (os) {
|
||||
e.dataTransfer.dropEffect = os != targetSlot && targetSlot.maxClass >= os.m.class ? 'copyMove' : 'none';
|
||||
e.dataTransfer.dropEffect = os != targetSlot && canMount(this.props.ship, targetSlot, os.m.grp, os.m.class) ? 'copyMove' : 'none';
|
||||
this.setState({ targetSlot });
|
||||
} else {
|
||||
e.dataTransfer.dropEffect = 'none';
|
||||
@@ -116,9 +117,9 @@ export default class SlotSection extends TranslatedComponent {
|
||||
let { originSlot, targetSlot } = this.state;
|
||||
let m = originSlot.m;
|
||||
|
||||
if (targetSlot && m && targetSlot.maxClass >= m.class) {
|
||||
if (targetSlot && m && canMount(this.props.ship, targetSlot, m.grp, m.class)) {
|
||||
// Swap modules if possible
|
||||
if (targetSlot.m && originSlot.maxClass >= targetSlot.m.class) {
|
||||
if (targetSlot.m && canMount(this.props.ship, originSlot, targetSlot.m.grp, targetSlot.m.class)) {
|
||||
this.props.ship.use(originSlot, targetSlot.m, true);
|
||||
} else { // Otherwise empty the origin slot
|
||||
this.props.ship.use(originSlot, null, true); // Empty but prevent summary update
|
||||
@@ -141,12 +142,12 @@ export default class SlotSection extends TranslatedComponent {
|
||||
return null;
|
||||
}
|
||||
if (slot === originSlot) {
|
||||
if (targetSlot && targetSlot.m && originSlot.maxClass < targetSlot.m.class) {
|
||||
if (targetSlot && targetSlot.m && !canMount(this.props.ship, originSlot, targetSlot.m.grp, targetSlot.m.class)) {
|
||||
return 'dropEmpty'; // Origin slot will be emptied
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (originSlot.m && slot.maxClass >= originSlot.m.class) { // Eligble drop slot
|
||||
if (originSlot.m && canMount(this.props.ship, slot, originSlot.m.grp, originSlot.m.class)) { // Eligble drop slot
|
||||
if (slot === targetSlot) {
|
||||
return 'drop'; // Can drop
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ export default class StandardSlot extends TranslatedComponent {
|
||||
|
||||
if (this.props.selected) {
|
||||
menu = <AvailableModulesMenu
|
||||
className='standard'
|
||||
modules={modules}
|
||||
shipMass={ship.ladenMass}
|
||||
m={m}
|
||||
@@ -47,16 +48,17 @@ export default class StandardSlot extends TranslatedComponent {
|
||||
<div className={cn('details-container', { warning: warning && warning(slot.m) })}>
|
||||
<div className={'sz'}>{slot.maxClass}</div>
|
||||
<div>
|
||||
<div className='l'>{classRating + ' ' + translate(m.grp)}</div>
|
||||
<div className={'r'}>{m.mass || m.fuel}{units.T}</div>
|
||||
<div className='l'>{classRating} {translate(m.grp == 'bh' ? m.grp : m.name || m.grp)}</div>
|
||||
<div className={'r'}>{m.mass || m.fuel || 0}{units.T}</div>
|
||||
<div className={'cb'}>
|
||||
{ m.optmass ? <div className='l'>{translate('optimal mass') + ': '}{m.optmass}{units.T}</div> : null }
|
||||
{ m.maxmass ? <div className='l'>{translate('max mass') + ': '}{m.maxmass}{units.T}</div> : null }
|
||||
{ m.grp == 'bh' && m.name ? <div className='l'>{translate(m.name)}</div> : null }
|
||||
{ m.optmass ? <div className='l'>{translate('optimal mass')}: {m.optmass}{units.T}</div> : null }
|
||||
{ m.maxmass ? <div className='l'>{translate('max mass')}: {m.maxmass}{units.T}</div> : null }
|
||||
{ m.range ? <div className='l'>{translate('range')}: {m.range}{units.km}</div> : null }
|
||||
{ m.time ? <div className='l'>{translate('time')}: {formats.time(m.time)}</div> : null }
|
||||
{ m.eff ? <div className='l'>{translate('efficiency')}: {m.eff}</div> : null }
|
||||
{ m.pGen ? <div className='l'>{translate('power')}: {m.pGen}{units.MW}</div> : null }
|
||||
{ m.maxfuel ? <div className='l'>{translate('max') + ' ' + translate('fuel') + ': '}{m.maxfuel}{units.T}</div> : null }
|
||||
{ m.maxfuel ? <div className='l'>{translate('max')} {translate('fuel')}: {m.maxfuel}{units.T}</div> : null }
|
||||
{ m.weaponcapacity ? <div className='l'>{translate('WEP')}: {m.weaponcapacity}{units.MJ} / {m.weaponrecharge}{units.MW}</div> : null }
|
||||
{ m.systemcapacity ? <div className='l'>{translate('SYS')}: {m.systemcapacity}{units.MJ} / {m.systemrecharge}{units.MW}</div> : null }
|
||||
{ m.enginecapacity ? <div className='l'>{translate('ENG')}: {m.enginecapacity}{units.MJ} / {m.enginerecharge}{units.MW}</div> : null }
|
||||
|
||||
@@ -4,6 +4,7 @@ import SlotSection from './SlotSection';
|
||||
import StandardSlot from './StandardSlot';
|
||||
import { diffDetails } from '../utils/SlotFunctions';
|
||||
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
||||
import * as ShipRoles from '../shipyard/ShipRoles';
|
||||
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
||||
|
||||
/**
|
||||
@@ -18,21 +19,8 @@ export default class StandardSlotSection extends SlotSection {
|
||||
*/
|
||||
constructor(props, context) {
|
||||
super(props, context, 'standard', 'standard');
|
||||
|
||||
this._optimizeStandard = this._optimizeStandard.bind(this);
|
||||
this._optimizeCargo = this._optimizeCargo.bind(this);
|
||||
this._optimizeExplorer = this._optimizeExplorer.bind(this);
|
||||
this._hideDiff = this._hideDiff.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill all standard slots with the specificed rating (using max class)
|
||||
* @param {String} rating [A-E]
|
||||
*/
|
||||
_fill(rating) {
|
||||
this.props.ship.useStandard(rating);
|
||||
this.props.onChange();
|
||||
this._close();
|
||||
this._selectBulkhead = this._selectBulkhead.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -45,85 +33,42 @@ export default class StandardSlotSection extends SlotSection {
|
||||
}
|
||||
|
||||
/**
|
||||
* Trader build
|
||||
* Fill all standard slots with the specificed rating (using max class)
|
||||
* @param {Boolean} shielded True if shield generator should be included
|
||||
* @param {integer} bulkheadIndex Bulkhead to use see Constants.BulkheadNames
|
||||
*/
|
||||
_optimizeCargo() {
|
||||
let ship = this.props.ship;
|
||||
ship.internal.forEach((slot) => ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E')));
|
||||
ship.useLightestStandard();
|
||||
_multiPurpose(shielded, bulkheadIndex) {
|
||||
ShipRoles.multiPurpose(this.props.ship, shielded, bulkheadIndex);
|
||||
this.props.onChange();
|
||||
this._close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Explorer build
|
||||
* Trader Build
|
||||
* @param {Boolean} shielded True if shield generator should be included
|
||||
*/
|
||||
_optimizeExplorer() {
|
||||
let ship = this.props.ship,
|
||||
intLength = ship.internal.length,
|
||||
heatSinkCount = 2, // Fit 2 heat sinks if possible
|
||||
afmUnitCount = 2, // Fit 2 AFM Units if possible
|
||||
sgSlot,
|
||||
fuelScoopSlot,
|
||||
sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass);
|
||||
_optimizeCargo(shielded) {
|
||||
ShipRoles.trader(this.props.ship, shielded);
|
||||
this.props.onChange();
|
||||
this._close();
|
||||
}
|
||||
|
||||
ship.setSlotEnabled(ship.cargoHatch, false)
|
||||
.use(ship.internal[--intLength], ModuleUtils.internal('2f')) // Advanced Discovery Scanner
|
||||
.use(ship.internal[--intLength], ModuleUtils.internal('2i')); // Detailed Surface Scanner
|
||||
|
||||
for (let i = 0; i < intLength; i++) {
|
||||
let slot = ship.internal[i];
|
||||
let nextSlot = (i + 1) < intLength ? ship.internal[i + 1] : null;
|
||||
if (!fuelScoopSlot && (!slot.eligible || slot.eligible.fs)) { // Fit best possible Fuel Scoop
|
||||
fuelScoopSlot = slot;
|
||||
ship.use(fuelScoopSlot, ModuleUtils.findInternal('fs', slot.maxClass, 'A'));
|
||||
ship.setSlotEnabled(fuelScoopSlot, true);
|
||||
|
||||
// Mount a Shield generator if possible AND an AFM Unit has been mounted already (Guarantees at least 1 AFM Unit)
|
||||
} else if (!sgSlot && afmUnitCount < 2 && sg.class <= slot.maxClass && (!slot.eligible || slot.eligible.sg) && (!nextSlot || nextSlot.maxClass < sg.class)) {
|
||||
sgSlot = slot;
|
||||
ship.use(sgSlot, sg);
|
||||
ship.setSlotEnabled(sgSlot, true);
|
||||
} else if (afmUnitCount > 0 && (!slot.eligible || slot.eligible.am)) {
|
||||
afmUnitCount--;
|
||||
let am = ModuleUtils.findInternal('am', slot.maxClass, afmUnitCount ? 'B' : 'A');
|
||||
ship.use(slot, am);
|
||||
ship.setSlotEnabled(slot, false); // Disabled power for AFM Unit
|
||||
} else {
|
||||
ship.use(slot, null);
|
||||
}
|
||||
}
|
||||
|
||||
ship.hardpoints.forEach((s) => {
|
||||
if (s.maxClass == 0 && heatSinkCount) { // Mount up to 2 heatsinks
|
||||
ship.use(s, ModuleUtils.hardpoints('02'));
|
||||
ship.setSlotEnabled(s, heatSinkCount == 2); // Only enable a single Heatsink
|
||||
heatSinkCount--;
|
||||
} else {
|
||||
ship.use(s, null);
|
||||
}
|
||||
});
|
||||
|
||||
if (sgSlot) {
|
||||
// The SG and Fuel scoop to not need to be powered at the same time
|
||||
if (sgSlot.m.power > fuelScoopSlot.m.power) { // The Shield generator uses the most power
|
||||
ship.setSlotEnabled(fuelScoopSlot, false);
|
||||
} else { // The Fuel scoop uses the most power
|
||||
ship.setSlotEnabled(sgSlot, false);
|
||||
}
|
||||
}
|
||||
|
||||
ship.useLightestStandard({ pd: '1D', ppRating: 'A' });
|
||||
/**
|
||||
* Explorer role
|
||||
* @param {Boolean} planetary True if Planetary Vehicle Hangar (PVH) should be included
|
||||
*/
|
||||
_optimizeExplorer(planetary) {
|
||||
ShipRoles.explorer(this.props.ship, planetary);
|
||||
this.props.onChange();
|
||||
this._close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the specified bulkhead
|
||||
* @param {number} bulkheadIndex 0 - 4
|
||||
* @param {Object} bulkhead Bulkhead module details
|
||||
*/
|
||||
_selectBulkhead(bulkheadIndex) {
|
||||
this.props.ship.useBulkhead(bulkheadIndex);
|
||||
_selectBulkhead(bulkhead) {
|
||||
this.props.ship.useBulkhead(bulkhead.index);
|
||||
this.context.tooltip();
|
||||
this.props.onChange();
|
||||
this._close();
|
||||
@@ -136,32 +81,12 @@ export default class StandardSlotSection extends SlotSection {
|
||||
this._optimizeStandard();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the bulkhead diff tooltip
|
||||
* @param {number} bhIndex Potential Bulkhead alternative
|
||||
* @param {SyntheticEvent} event Event
|
||||
*/
|
||||
_bhDiff(bhIndex, event) {
|
||||
let ship = this.props.ship;
|
||||
this.context.tooltip(
|
||||
diffDetails.call(ship, this.context.language, ModuleUtils.bulkheads(ship.id, bhIndex), ship.bulkheads.m),
|
||||
event.currentTarget.getBoundingClientRect()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the diff tooltip
|
||||
*/
|
||||
_hideDiff() {
|
||||
this.context.tooltip();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the slot React Components
|
||||
* @return {Array} Array of Slots
|
||||
*/
|
||||
_getSlots() {
|
||||
let { translate, units } = this.context.language;
|
||||
let { translate, units, formats } = this.context.language;
|
||||
let { ship, currentMenu } = this.props;
|
||||
let slots = new Array(8);
|
||||
let open = this._openMenu;
|
||||
@@ -171,41 +96,15 @@ export default class StandardSlotSection extends SlotSection {
|
||||
let avail = ship.getAvailableModules().standard;
|
||||
let bh = ship.bulkheads;
|
||||
|
||||
slots[0] = (
|
||||
<div key='bh' className={cn('slot', { selected: currentMenu === bh })} onClick={open.bind(this, bh)}>
|
||||
<div className={'details-container'}>
|
||||
<div className={'details'}>
|
||||
<div className={'sz'}>8</div>
|
||||
<div>
|
||||
<div className={'l'}>{translate('bh')}</div>
|
||||
<div className={'r'}>{bh.m.mass}{units.T}</div>
|
||||
<div className={'cl l'}>{translate(bh.m.name)}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{currentMenu === bh &&
|
||||
<div className='select' onClick={ e => e.stopPropagation() }>
|
||||
<ul>
|
||||
<li onClick={selBulkhead.bind(this, 0)} onMouseOver={this._bhDiff.bind(this, 0)} onMouseLeave={this._hideDiff} className={cn('lc', { active: bh.index == 0 })}>
|
||||
{translate('Lightweight Alloy')}
|
||||
</li>
|
||||
<li onClick={selBulkhead.bind(this, 1)} onMouseOver={this._bhDiff.bind(this, 1)} onMouseLeave={this._hideDiff} className={cn('lc', { active: bh.index == 1 })}>
|
||||
{translate('Reinforced Alloy')}
|
||||
</li>
|
||||
<li onClick={selBulkhead.bind(this, 2)} onMouseOver={this._bhDiff.bind(this, 2)} onMouseLeave={this._hideDiff} className={cn('lc', { active: bh.index == 2 })}>
|
||||
{translate('Military Grade Composite')}
|
||||
</li>
|
||||
<li onClick={selBulkhead.bind(this, 3)} onMouseOver={this._bhDiff.bind(this, 3)} onMouseLeave={this._hideDiff} className={cn('lc', { active: bh.index == 3 })}>
|
||||
{translate('Mirrored Surface Composite')}
|
||||
</li>
|
||||
<li onClick={selBulkhead.bind(this, 4)} onMouseOver={this._bhDiff.bind(this, 4)} onMouseLeave={this._hideDiff} className={cn('lc', { active: bh.index == 4 })}>
|
||||
{translate('Reactive Surface Composite')}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
slots[0] = <StandardSlot
|
||||
key='bh'
|
||||
slot={bh}
|
||||
modules={ship.getAvailableModules().bulkheads}
|
||||
onOpen={open.bind(this, bh)}
|
||||
onSelect={this._selectBulkhead}
|
||||
selected={currentMenu == bh}
|
||||
ship={ship}
|
||||
/>;
|
||||
|
||||
slots[1] = <StandardSlot
|
||||
key='pp'
|
||||
@@ -226,7 +125,7 @@ export default class StandardSlotSection extends SlotSection {
|
||||
onSelect={select.bind(this, st[1])}
|
||||
selected={currentMenu == st[1]}
|
||||
ship={ship}
|
||||
warning={m => m.maxmass < ship.ladenMass}
|
||||
warning={m => m.maxmass < (ship.ladenMass - st[1].mass + m.mass)}
|
||||
/>;
|
||||
|
||||
|
||||
@@ -292,21 +191,19 @@ export default class StandardSlotSection extends SlotSection {
|
||||
* @return {React.Component} Section menu
|
||||
*/
|
||||
_getSectionMenu(translate) {
|
||||
let _fill = this._fill;
|
||||
|
||||
let planetaryDisabled = this.props.ship.internal.length < 4;
|
||||
return <div className='select' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
|
||||
<ul>
|
||||
<li className='lc' onClick={this._optimizeStandard}>{translate('optimize')}</li>
|
||||
<li className='c' onClick={_fill.bind(this, 'E')}>E</li>
|
||||
<li className='c' onClick={_fill.bind(this, 'D')}>D</li>
|
||||
<li className='c' onClick={_fill.bind(this, 'C')}>C</li>
|
||||
<li className='c' onClick={_fill.bind(this, 'B')}>B</li>
|
||||
<li className='c' onClick={_fill.bind(this, 'A')}>A</li>
|
||||
<li className='lc' onClick={this._optimizeStandard}>{translate('Maximize Jump Range')}</li>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('roles')}</div>
|
||||
<ul>
|
||||
<li className='lc' onClick={this._optimizeCargo}>{translate('Trader')}</li>
|
||||
<li className='lc' onClick={this._optimizeExplorer}>{translate('Explorer')}</li>
|
||||
<li className='lc' onClick={this._multiPurpose.bind(this, false, 0)}>{translate('Multi-purpose')}</li>
|
||||
<li className='lc' onClick={this._multiPurpose.bind(this, true, 2)}>{translate('Combat')}</li>
|
||||
<li className='lc' onClick={this._optimizeCargo.bind(this, false)}>{translate('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={cn('lc', { disabled: planetaryDisabled })} onClick={!planetaryDisabled && this._optimizeExplorer.bind(this, true)}>{translate('Planetary Explorer')}</li>
|
||||
</ul>
|
||||
</div>;
|
||||
}
|
||||
|
||||
@@ -64,8 +64,8 @@ export class CoriolisLogo extends SvgIcon {
|
||||
*/
|
||||
svg() {
|
||||
return <g transform='translate(1,1)'>
|
||||
<path stroke='#ff3b00' transform='rotate(45 15 15)' d='m4,4 l 11,-4 l 11,4 l 4,11 l -4,11 l -11,4 l -11,-4 l -4,-11 l 4,-11 l 22,0 l 0,22 l -22,0 z' strokeWidth='1' fill='#000000'/>
|
||||
<rect height='3' width='10' y='13.5' x='10' strokeWidth='1' stroke='#ff3b00'/>
|
||||
<path stroke='#0a8bd6' transform='rotate(45 15 15)' d='m4,4 l 11,-4 l 11,4 l 4,11 l -4,11 l -11,4 l -11,-4 l -4,-11 l 4,-11 l 22,0 l 0,22 l -22,0 z' strokeWidth='1' fill='#000000'/>
|
||||
<rect height='3' width='10' y='13.5' x='10' strokeWidth='1' stroke='#0a8bd6'/>
|
||||
</g>;
|
||||
}
|
||||
}
|
||||
@@ -319,6 +319,90 @@ export class Warning extends SvgIcon {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thermal damage
|
||||
*/
|
||||
export class DamageThermal extends SvgIcon {
|
||||
/**
|
||||
* Overriden view box
|
||||
* @return {String} view box
|
||||
*/
|
||||
viewBox() { return '0 0 200 200'; }
|
||||
/**
|
||||
* Generate the SVG
|
||||
* @return {React.Component} SVG Contents
|
||||
*/
|
||||
svg() {
|
||||
return <g>
|
||||
<ellipse cx='100' cy='100' rx='90' ry='90' fillOpacity='0' />
|
||||
<ellipse cx='100' cy='100' rx='30' ry='30' fillOpacity='1' />
|
||||
<path d='M100 20v80' />
|
||||
</g>;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Kinetic damage
|
||||
*/
|
||||
export class DamageKinetic extends SvgIcon {
|
||||
/**
|
||||
* Overriden view box
|
||||
* @return {String} view box
|
||||
*/
|
||||
viewBox() { return '0 0 200 200'; }
|
||||
/**
|
||||
* Generate the SVG
|
||||
* @return {React.Component} SVG Contents
|
||||
*/
|
||||
svg() {
|
||||
return <g>
|
||||
<ellipse cx='100' cy='100' rx='90' ry='90' fillOpacity='0' />
|
||||
<ellipse cx='62' cy='67' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='62' cy='101' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='62' cy='135' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='100' cy='50' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='100' cy='84' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='100' cy='118' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='100' cy='152' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='138' cy='67' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='138' cy='101' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='138' cy='135' rx='5' ry='5' fillOpacity='1' />
|
||||
</g>;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Explosive damage
|
||||
*/
|
||||
export class DamageExplosive extends SvgIcon {
|
||||
/**
|
||||
* Overriden view box
|
||||
* @return {String} view box
|
||||
*/
|
||||
viewBox() { return '0 0 200 200'; }
|
||||
/**
|
||||
* Generate the SVG
|
||||
* @return {React.Component} SVG Contents
|
||||
*/
|
||||
svg() {
|
||||
return <g>
|
||||
<ellipse cx='100' cy='100' rx='50' ry='50' fillOpacity='0' />
|
||||
<ellipse cx='100' cy='20' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='156.57' cy='36.57' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='180' cy='100' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='156.57' cy='163.43' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='100' cy='180' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='43.43' cy='163.43' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='20' cy='100' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='43.43' cy='36.57' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='100' cy='75' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='125' cy='100' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='100' cy='125' rx='5' ry='5' fillOpacity='1' />
|
||||
<ellipse cx='75' cy='100' rx='5' ry='5' fillOpacity='1' />
|
||||
</g>;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixed mount hardpoint
|
||||
*/
|
||||
@@ -334,11 +418,11 @@ export class MountFixed extends SvgIcon {
|
||||
*/
|
||||
svg() {
|
||||
return <g>
|
||||
<circle fillOpacity='0' r='70' cy='100' cx='100' strokeWidth='5' />
|
||||
<line y2='60' x2='101' y1='0' x1='101' strokeWidth='5' />
|
||||
<line y2='101' x2='200' y1='101' x1='140' strokeWidth='5' />
|
||||
<line y2='101' x2='60' y1='101' x1='0' strokeWidth='5' />
|
||||
<line y2='200' x2='101' y1='140' x1='101' strokeWidth='5' />
|
||||
<circle cx='100' cy='100' r='76' fillOpacity='0' />
|
||||
<path d='M0 100h48' />
|
||||
<path d='M152 100h48' />
|
||||
<path d='M100 0v48' />
|
||||
<path d='M100 152v48' />
|
||||
</g>;
|
||||
}
|
||||
}
|
||||
@@ -358,8 +442,8 @@ export class MountGimballed extends SvgIcon {
|
||||
*/
|
||||
svg() {
|
||||
return <g>
|
||||
<ellipse ry='25' rx='95' cy='100' cx='100' fillOpacity='0' strokeWidth='5' />
|
||||
<ellipse ry='95' rx='25' cy='100' cx='100' fillOpacity='0' strokeWidth='5' />
|
||||
<ellipse cx='100' cy='100' rx='90' ry='25' fillOpacity='0' />
|
||||
<ellipse cx='100' cy='100' rx='20' ry='95' fillOpacity='0' />
|
||||
</g>;
|
||||
}
|
||||
}
|
||||
@@ -379,9 +463,14 @@ export class MountTurret extends SvgIcon {
|
||||
*/
|
||||
svg() {
|
||||
return <g>
|
||||
<line y2='170' x2='162' y1='170' x1='8' strokeWidth='6' />
|
||||
<path d='m13,138l144,0l0,-50l-27,-40l-90,0l-27,40l0,50z' id='svg_12' fillOpacity='0' strokeWidth='6' />
|
||||
<line y2='91' x2='200' y1='91' x1='159' strokeWidth='6' />
|
||||
<path d='M40 50 A 40 40 0 0 0 0 90' />
|
||||
<path d='M40 50h40' />
|
||||
<path d='M120 90 A 40 40 0 0 0 80 50' />
|
||||
<path d='M0 90v40' />
|
||||
<path d='M120 90v40' />
|
||||
<path d='M0 120h120' />
|
||||
<path d='M120 90h80' />
|
||||
<path d='M0 160h120' />
|
||||
</g>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +94,7 @@ export default class UtilitySlotSection extends SlotSection {
|
||||
return <div className='select' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
|
||||
<ul>
|
||||
<li className='lc' onClick={this._empty}>{translate('empty all')}</li>
|
||||
<li className='optional-hide' style={{ textAlign: 'center', marginTop: '1em' }}>{translate('PHRASE_ALT_ALL')}</li>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('sb')}</div>
|
||||
<ul>
|
||||
|
||||
@@ -41,29 +41,31 @@ export function getLanguage(langCode) {
|
||||
formats: {
|
||||
gen, // General number format (.e.g 1,001,001.1234)
|
||||
int: d3Locale.numberFormat(',.0f'), // Fixed to 0 decimal places (.e.g 1,001)
|
||||
f1: d3Locale.numberFormat(',.1f'), // Fixed to 1 decimal place (.e.g 1,001.1)
|
||||
f2: d3Locale.numberFormat(',.2f'), // Fixed to 2 decimal places (.e.g 1,001.10)
|
||||
s2: d3Locale.numberFormat('.2s'), // SI Format to 2 decimal places (.e.g 1.1k)
|
||||
pct: d3Locale.numberFormat('.2%'), // % to 2 decimal places (.e.g 5.40%)
|
||||
pct1: d3Locale.numberFormat('.1%'), // % to 1 decimal places (.e.g 5.4%)
|
||||
r2: d3Locale.numberFormat('.2r'), // Rounded to 2 significant numbers (.e.g 512 => 510, 4.122 => 4.1)
|
||||
rPct: d3.format('%'), // % to 0 decimal places (.e.g 5%)
|
||||
round1: (d) => gen(d3.round(d, 1)), // Rounded to 0-1 decimal places (.e.g 5.1, 4)
|
||||
round: (d) => gen(d3.round(d, 2)), // Rounded to 0-2 decimal places (.e.g 5.12, 4.1)
|
||||
time: (d) => (d < 0 ? '-' : '') + Math.floor(Math.abs(d) / 60) + ':' + ('00' + Math.floor(Math.abs(d) % 60)).substr(-2, 2)
|
||||
},
|
||||
translate,
|
||||
units: {
|
||||
CR: <u>{' ' + translate('CR')}</u>, // Credits
|
||||
kg: <u>{' ' + translate('kg')}</u>, // Kilograms
|
||||
kgs: <u>{' ' + translate('kg/s')}</u>, // Kilograms per second
|
||||
km: <u>{' ' + translate('km')}</u>, // Kilometers
|
||||
Ls: <u>{' ' + translate('Ls')}</u>, // Light Seconds
|
||||
LY: <u>{' ' + translate('LY')}</u>, // Light Years
|
||||
MJ: <u>{' ' + translate('MJ')}</u>, // Mega Joules
|
||||
'm/s': <u>{' ' + translate('m/s')}</u>, // Meters per second
|
||||
MW: <u>{' ' + translate('MW')}</u>, // Mega Watts (same as Mega Joules per second)
|
||||
CR: <u> {translate('CR')}</u>, // Credits
|
||||
kg: <u> {translate('kg')}</u>, // Kilograms
|
||||
kgs: <u> {translate('kg/s')}</u>, // Kilograms per second
|
||||
km: <u> {translate('km')}</u>, // Kilometers
|
||||
Ls: <u> {translate('Ls')}</u>, // Light Seconds
|
||||
LY: <u> {translate('LY')}</u>, // Light Years
|
||||
MJ: <u> {translate('MJ')}</u>, // Mega Joules
|
||||
'm/s': <u> {translate('m/s')}</u>, // Meters per second
|
||||
MW: <u> {translate('MW')}</u>, // Mega Watts (same as Mega Joules per second)
|
||||
ps: <u>{translate('/s')}</u>, // per second
|
||||
pm: <u>{translate('/min')}</u>, // per minute
|
||||
T: <u>{' ' + translate('T')}</u>, // Metric Tons
|
||||
T: <u> {translate('T')}</u>, // Metric Tons
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -15,76 +15,20 @@ export const formats = {
|
||||
|
||||
export const terms = {
|
||||
// Phrases
|
||||
PHRASE_BACKUP_DESC: 'Export aller Coriolis-Daten, um sie zu sichern oder oder um sie zu einem anderen Browser/Gerät zu übertragen.', // Backup of all Coriolis data to save or transfer to another browser/device
|
||||
PHRASE_BACKUP_DESC: 'Export aller Coriolis-Daten, um sie zu sichern oder um sie zu einem anderen Browser/Gerät zu übertragen.', // Backup of all Coriolis data to save or transfer to another browser/device
|
||||
PHRASE_CONFIRMATION: 'Sind Sie sicher?', // Are You Sure?
|
||||
PHRASE_EXPORT_DESC: 'Ein detaillierter JSON-Export Ihrer Konfiguration für die Verwendung in anderen Websites und Tools', // A detailed JSON export of your build for use in other sites and tools
|
||||
PHRASE_FASTEST_RANGE: null, // Consecutive max range jumps
|
||||
PHRASE_FASTEST_RANGE: 'aufeinanderfolgende maximale Reichweite/Sprünge', // Consecutive max range jumps
|
||||
PHRASE_IMPORT: 'JSON hier einfügen oder importieren', // Paste JSON or import here
|
||||
PHRASE_LADEN: null, // Ship Mass + Fuel + Cargo
|
||||
PHRASE_LADEN: 'Schiffsmasse + Treibstoff + Fracht', // Ship Mass + Fuel + Cargo
|
||||
PHRASE_NO_BUILDS: 'Keine Konfigurationen zum Vergleich ausgewählt!', // No builds added to comparison!
|
||||
PHRASE_NO_RETROCH: 'Keine Umrüständerungen', // No Retrofitting changes
|
||||
PHRASE_SELECT_BUILDS: 'Ausstattung zum Vergleich auswählen', // Select Builds to Compare
|
||||
PHRASE_SG_RECHARGE: null, // Time from 50% to 100% Charge
|
||||
PHRASE_SG_RECOVER: null, // Recovery (to 50%) after collapse
|
||||
PHRASE_UNLADEN: null, // Ship Mass excluding Fuel and Cargo
|
||||
PHRASE_SG_RECHARGE: 'Zeit von 50% bis 100% der Ladung', // Time from 50% to 100% Charge
|
||||
PHRASE_SG_RECOVER: 'Erneuerung (zu 50%) nach Zusammenbruch', // Recovery (to 50%) after collapse
|
||||
PHRASE_UNLADEN: 'Schiffsmasse ohne Treibstoff und Fracht', // Ship Mass excluding Fuel and Cargo
|
||||
PHRASE_UPDATE_RDY: 'Update verfügbar! Klicken zum Aktualisieren', // Update Available! Click to Refresh
|
||||
|
||||
// Modules / Module Types - These should match the in-game translation (if any)
|
||||
'Basic Discovery Scanner': 'einfacher Aufklärungsscanner', // Basic Discovery Scanner
|
||||
'Cargo Hatch': 'Frachtluke', // Cargo Hatch
|
||||
'Chaff Launcher': 'Düppel-Werfer', // Chaff Launcher
|
||||
'Detailed Surface Scanner': 'Detailoberflächenscanner', // Detailed Surface Scanner
|
||||
'Electronic Countermeasure': 'elektronische Gegenmaßnahme', // Electronic Countermeasure
|
||||
'Heat Sink Launcher': 'Kühlkörperwerfer', // Heat Sink Launcher
|
||||
'Intermediate Discovery Scanner': 'mittlerer Aufklärungsscanner', // Intermediate Discovery Scanner
|
||||
'Point Defence': 'Punktverteidigung',
|
||||
'Standard Docking Computer': 'Standard-Landecomputer', // Standard Docking Computer
|
||||
am: 'automatische Feldwartungs-Einheit', // Auto Field-Maintenance Unit
|
||||
bh: 'Rumpfhüllenverstärkung', // Bulkheads
|
||||
bl: 'Strahlenlaser', // Beam Laser
|
||||
c: 'Kanone', // Cannon
|
||||
cc: 'Krallensteuerung: Sammler', // Collector Limpet Controller
|
||||
cm: 'Gegenmaßnahme', // Countermeasure
|
||||
cr: 'Frachtgestell', // Cargo Rack
|
||||
cs: 'Frachtscanner', // Cargo Scanner
|
||||
dc: 'Standard-Landecomputer', // Docking Computer
|
||||
fc: 'Splitterkanone', // Fragment Cannon
|
||||
fi: 'FSA-Unterbrecher', // FSD Interdictor
|
||||
fs: 'Treibstoffsammler', // Fuel Scoop
|
||||
fsd: 'Frameshiftantrieb', // Frame Shift Drive
|
||||
ft: 'Treibstofftank', // Fuel Tank
|
||||
fx: 'Krallensteuerung Treibstoffstransfer', // Fuel Transfer Limpet Controller
|
||||
hb: 'Krallen-Steuereinheit (Ladelukenöffner)', // Hatch Breaker Limpet Controller
|
||||
hr: 'Rumpfhüllenverstärkung (Paket)', // Hull Reinforcement Package
|
||||
kw: 'Tötungsbefehl-Scanner', // Kill Warrant Scanner
|
||||
ls: 'Lebenserhaltung', // life support
|
||||
mc: 'Mehrfachgeschütz', // Multi-cannon
|
||||
ml: 'Abbaulaser', // Mining Laser
|
||||
mr: 'Raketenbatterie', // Missile Rack
|
||||
nl: 'Minenwerfer', // Mine Launcher
|
||||
pa: 'Plasmabeschleuniger', // Plasma Accelerator
|
||||
pc: 'Krallensteuerung: Erzsucher', // Prospector Limpet Controller
|
||||
pd: 'Energieverteiler', // power distributor
|
||||
pl: 'Impulslaser', // Pulse Laser
|
||||
pp: 'Kraftwerk', // Power Plant
|
||||
psg: 'Prismaschildgenerator', // Prismatic Shield Generator
|
||||
rf: 'Raffinerie', // Refinery
|
||||
rg: 'Schienenkanone', // Rail Gun
|
||||
s: 'Sensoren', // Sensors
|
||||
sb: 'Schild-Booster', // Shield Booster
|
||||
sc: 'Scanner', // Scanner
|
||||
scb: 'Schildzellenbank', // Shield Cell Bank
|
||||
sg: 'Schildgenerator', // Shield Generator
|
||||
t: 'Schubdüsen', // Thrusters
|
||||
tp: 'Torpedoaufhängung', // Torpedo Pylon
|
||||
ul: 'Salvenlaser', // Burst Laser
|
||||
ws: 'Frameshift-Sogwolkenscanner', // Frame Shift Wake Scanner
|
||||
// Bulkheads - These should match the in-game translation (if any)
|
||||
'Lightweight Alloy': 'leichte Legierung',
|
||||
'Reinforced Alloy': 'verstärkte Legierung',
|
||||
'Military Grade Composite': 'Militär-Komposit',
|
||||
'Mirrored Surface Composite': 'Gespiegelte-Oberfläche-Komposit',
|
||||
'Reactive Surface Composite': 'Reaktive-Oberfläche-Komposit',
|
||||
// Units / Metrics
|
||||
LY: 'Lj', // Light Years
|
||||
T: 't', // Tons (Metric Ton - 1000kg)
|
||||
@@ -94,109 +38,108 @@ export const terms = {
|
||||
L: 'G', // Large Hardpoint size (single character)
|
||||
H: 'R', // Huge Hardpoint size (single character)
|
||||
U: 'W', // Utility Hardpoint size (single character) - Kill warrant scanner, etc
|
||||
small: 'klein', // Small ship size
|
||||
medium: 'mittel', // Medium ship size
|
||||
large: 'groß', // Large Ship Size
|
||||
|
||||
// Terms
|
||||
'build name': 'Ausstattungsname', // Ship build/configuration/design name
|
||||
'compare all': 'Alles vergleichen',
|
||||
'create new': 'Neu erstellen',
|
||||
'damage per second': null,
|
||||
'delete all': 'Alles Löschen',
|
||||
'detailed export': 'detailierter Export',
|
||||
'edit data': 'bearbeiten',
|
||||
'empty all': 'leer alles',
|
||||
'Enter Name': 'Namen eingeben',
|
||||
'fastest range': null, // Fastet totaljump range - sum of succesive jumps
|
||||
'fuel level': null, // Percent of fuel (T) in the tank
|
||||
'full tank': 'Tank voll',
|
||||
'internal compartments': 'Innenbereich',
|
||||
'jump range': 'Sprungreichweite',
|
||||
'mass lock factor': 'Massensperrefaktor',
|
||||
'max mass': 'maximale Masse',
|
||||
'net cost': 'Nettokosten',
|
||||
'none created': 'Leer',
|
||||
'optimal mass': null, // Lowest weight / best weight for jump distance, etc
|
||||
'refuel time': 'Auftankzeit', // Time to refuel the tank when scooping
|
||||
'reload costs': null,
|
||||
'retrofit costs': 'Änderungskosten', // The cost difference when upgrading / downgrading a component
|
||||
'retrofit from': 'Nachrüsten von', // Retrofit from Build A against build B
|
||||
'T-Load': 'T-Lad', // Thermal load abbreviation
|
||||
'total range': null,
|
||||
'unit cost': null,
|
||||
'utility mounts': 'Werkzeug-Steckplätze',
|
||||
about: 'Über', // Link to about page / about Coriolis.io
|
||||
about: 'über', // Link to about page / about Coriolis.io
|
||||
action: 'Aktion',
|
||||
added: 'hinzugefügt',
|
||||
ammo: 'Munition', // Ammunition
|
||||
armour: 'Panzerung',
|
||||
available: 'verfügbar',
|
||||
available: 'verfügbar', // Available options
|
||||
backup: 'Sicherungsdatei',
|
||||
base: null,
|
||||
bays: null,
|
||||
base: 'Basis', // Base speed, boost, etc - Base ship stats
|
||||
bays: 'Lagerraum',
|
||||
bins: 'Behälter', // Number of Mining Refinery bins
|
||||
build: 'Ausstattung', // Shorthand for the build/configuration/design name
|
||||
'build name': 'Ausstattungsname', // Ship build/configuration/design name
|
||||
builds: 'Ausstattungen', // Ship build/configuration/design names
|
||||
buy: 'kaufen',
|
||||
cancel: 'Abbrechen',
|
||||
cancel: 'abbrechen',
|
||||
cargo: 'Fracht',
|
||||
cells: 'Zellen', // Number of cells in a shield cell bank
|
||||
close: 'Schließen',
|
||||
close: 'schließen',
|
||||
compare: 'vergleichen',
|
||||
'compare all': 'alles vergleichen',
|
||||
comparison: 'Vergleich',
|
||||
comparisons: 'Vergleiche',
|
||||
cost: 'Preis', // Cost / price of a module or price of a ship
|
||||
costs: 'Kosten', // Costs / prices of a modules or prices of ships
|
||||
create: 'erstellen',
|
||||
'create new': 'neu erstellen',
|
||||
credits: 'Credits',
|
||||
damage: 'Schaden',
|
||||
delete: 'Löschen',
|
||||
dep: 'Ausg', // Weapons/Hardpoints Deployed abbreviation
|
||||
deployed: 'Ausgefahren', // Weapons/Hardpoints Deployed
|
||||
disabled: 'Deaktiviert',
|
||||
'damage per second': 'Schaden pro Sekunde',
|
||||
delete: 'löschen',
|
||||
'delete all': 'alles löschen',
|
||||
dep: 'ausg', // Weapons/Hardpoints Deployed abbreviation
|
||||
deployed: 'ausgefahren', // Weapons/Hardpoints Deployed
|
||||
'detailed export': 'detailierter Export',
|
||||
disabled: 'deaktiviert',
|
||||
discount: 'Rabatt',
|
||||
'edit data': 'bearbeiten',
|
||||
efficiency: 'Effizienz', // Power Plant efficiency
|
||||
empty: 'leer',
|
||||
'empty all': 'alles entfernen',
|
||||
ENG: 'ANT', // Abbreviation - Engine recharge rate for power distributor
|
||||
'Enter Name': 'Namen eingeben',
|
||||
Explorer: 'Forscher',
|
||||
export: 'Export',
|
||||
'fastest range': 'maximale Reichweite', // Fastet totaljump range - sum of succesive jumps
|
||||
forum: 'Forum',
|
||||
fuel: 'Treibstoff',
|
||||
'fuel level': 'Tankfüllstand', // Percent of fuel (T) in the tank
|
||||
'full tank': 'Tank voll',
|
||||
hardpoints: 'Waffenaufhängungen',
|
||||
hull: 'Hülle', // Ships hull
|
||||
hull: 'Rumpf', // Ships hull
|
||||
import: 'importieren',
|
||||
insurance: 'Versicherung',
|
||||
jump: null, // Single jump range
|
||||
'internal compartments': 'Innenbereich',
|
||||
jump: 'Sprung', // Single jump range
|
||||
'jump range': 'Sprungreichweite',
|
||||
jumps: 'Sprünge',
|
||||
laden: 'beladen',
|
||||
language: 'Sprache',
|
||||
maneuverability: 'Manövrierbarkeit',
|
||||
manufacturer: null,
|
||||
manufacturer: 'Hersteller',
|
||||
mass: 'Masse',
|
||||
MLF: null, // Mass Lock Factor Abbreviation
|
||||
MNV: null, // Maneuverability abbreviation
|
||||
module: 'modul',
|
||||
modules: 'module',
|
||||
'mass lock factor': 'Massensperrefaktor',
|
||||
'max mass': 'maximale Masse',
|
||||
MLF: 'MSF', // Mass Lock Factor Abbreviation
|
||||
module: 'Modul',
|
||||
modules: 'Module',
|
||||
'net cost': 'Nettokosten',
|
||||
no: 'Nein',
|
||||
'none created': 'nicht erstellt',
|
||||
ok: 'OK',
|
||||
optimize: null,
|
||||
'optimal mass': 'optimale Masse', // Lowest weight / best weight for jump distance, etc
|
||||
optimize: 'optimieren',
|
||||
pen: 'Durchdr.', // Armour peneration abbreviation
|
||||
permalink: 'Permanentlink',
|
||||
power: 'Energie', // Power = Energy / second. Power generated by the power plant, or power consumed (MW / Mega Watts). Used in the power plant section
|
||||
pri: 'Prio', // Priority abbreviation for power management
|
||||
proceed: 'Fortfahren',
|
||||
PWR: 'En', // Power Abbreviation. See Power
|
||||
qty: null, // Quantity abbreviation
|
||||
proceed: 'fortfahren',
|
||||
qty: 'Menge', // Quantity abbreviation
|
||||
range: 'Reichweite',
|
||||
rate: 'Rate',
|
||||
recharge: null, // Shield Recharge time from 50% -> 100%
|
||||
recovery: null, // Shield recovery time (after losing shields/turning on -> 50%)
|
||||
recharge: 'aufladen', // Shield Recharge time from 50% -> 100%
|
||||
recovery: 'Erneuerung', // Shield recovery time (after losing shields/turning on -> 50%)
|
||||
'refuel time': 'Auftankzeit', // Time to refuel the tank when scooping
|
||||
reload: 'aktualisieren', // Reload weapon/hardpoint
|
||||
'reload costs': 'Nachladekosten',
|
||||
rename: 'umbenennen',
|
||||
repair: 'reparieren',
|
||||
reset: 'zurücksetzen',
|
||||
ret: 'Eing', // Retracted abbreviation
|
||||
retracted: 'Eingefahren', // Weapons/Hardpoints retracted
|
||||
ret: 'eing', // Retracted abbreviation
|
||||
retracted: 'eingefahren', // Weapons/Hardpoints retracted
|
||||
'retrofit costs': 'Änderungskosten', // The cost difference when upgrading / downgrading a component
|
||||
'retrofit from': 'nachrüsten von', // Retrofit from Build A against build B
|
||||
ROF: 'Kad', // Rate of Fire abbreviation
|
||||
roles: null, // Commander/Ship build roles - e.g. Trader, Bounty-Hunter, Explorer, etc
|
||||
save: 'Speichern',
|
||||
sell: 'Verkaufen',
|
||||
roles: 'Rollen', // Commander/Ship build roles - e.g. Trader, Bounty-Hunter, Explorer, etc
|
||||
save: 'speichern',
|
||||
sell: 'verkaufen',
|
||||
settings: 'Einstellungen', // Coriolis application settings
|
||||
shields: 'Schilde',
|
||||
ship: 'Schiff',
|
||||
@@ -205,16 +148,19 @@ export const terms = {
|
||||
size: 'Größe',
|
||||
skip: 'überspringen', // Skip past something / ignore it
|
||||
speed: 'Geschwindigkeit',
|
||||
standard: 'Standard', // Standard / Common modules (FSD, power plant, life support, etc)
|
||||
standard: 'Grundausstattung', // Standard / Common modules (FSD, power plant, life support, etc)
|
||||
Stock: 'Standard', // Thermal-load abbreviation
|
||||
strength: null, // Strength in reference to Shield Strength
|
||||
subtotal: null,
|
||||
strength: 'Stärke', // Strength in reference to Shield Strength
|
||||
subtotal: 'Zwischensumme',
|
||||
time: 'Dauer', // time it takes to complete something
|
||||
tooltips: null, // Tooltips setting - show/hide
|
||||
total: 'Gesamt',
|
||||
Trader: null, // Trader role
|
||||
tooltips: 'Tooltips', // Tooltips setting - show/hide
|
||||
total: 'gesamt',
|
||||
'total range': 'Gesamtbereich',
|
||||
Trader: 'Händler', // Trader role
|
||||
type: 'Typ',
|
||||
unladen: 'Unbeladen', // No cargo or fuel
|
||||
'unit cost': 'Kosten pro Einheit',
|
||||
unladen: 'unbeladen', // No cargo or fuel
|
||||
'utility mounts': 'Werkzeug-Steckplätze',
|
||||
WEP: 'WAF', // Abbreviation - Weapon recharge rate for power distributor
|
||||
yes: 'Ja'
|
||||
yes: 'ja'
|
||||
};
|
||||
|
||||
@@ -14,20 +14,22 @@ export const formats = {
|
||||
};
|
||||
|
||||
export const terms = {
|
||||
PHRASE_ALT_ALL: 'Alt + Click to fill all slots',
|
||||
PHRASE_BACKUP_DESC: 'Backup of all Coriolis data to save or transfer to another browser/device',
|
||||
PHRASE_CONFIRMATION: 'Are You Sure?',
|
||||
PHRASE_CONFIRMATION: 'Are you sure?',
|
||||
PHRASE_EXPORT_DESC: 'A detailed JSON export of your build for use in other sites and tools',
|
||||
PHRASE_FASTEST_RANGE: 'Consecutive max range jumps',
|
||||
PHRASE_IMPORT: 'Paste JSON or import here',
|
||||
PHRASE_LADEN: 'Ship Mass + Fuel + Cargo',
|
||||
PHRASE_LADEN: 'Ship mass + fuel + cargo',
|
||||
PHRASE_NO_BUILDS: 'No builds added to comparison!',
|
||||
PHRASE_NO_RETROCH: 'No Retrofitting changes',
|
||||
PHRASE_SELECT_BUILDS: 'Select Builds to Compare',
|
||||
PHRASE_SG_RECHARGE: 'Time from 50% to 100% Charge',
|
||||
PHRASE_SELECT_BUILDS: 'Select builds to compare',
|
||||
PHRASE_SG_RECHARGE: 'Time from 50% to 100% charge',
|
||||
PHRASE_SG_RECOVER: 'Recovery (to 50%) after collapse',
|
||||
PHRASE_UNLADEN: 'Ship Mass excluding Fuel and Cargo',
|
||||
PHRASE_UPDATE_RDY: 'Update Available! Click to Refresh',
|
||||
PHRASE_UNLADEN: 'Ship mass excluding fuel and cargo',
|
||||
PHRASE_UPDATE_RDY: 'Update Available! Click to refresh',
|
||||
|
||||
// Other languages fallback to these values
|
||||
// Only Translate to other languages if the name is different in-game
|
||||
am: 'Auto Field-Maintenance Unit',
|
||||
bh: 'Bulkheads',
|
||||
@@ -40,6 +42,7 @@ export const terms = {
|
||||
cs: 'Cargo Scanner',
|
||||
dc: 'Docking Computer',
|
||||
fc: 'Fragment Cannon',
|
||||
fh: 'Fighter Hangar',
|
||||
fi: 'FSD Interdictor',
|
||||
fs: 'Fuel Scoop',
|
||||
fsd: 'Frame Shift Drive',
|
||||
@@ -56,11 +59,15 @@ export const terms = {
|
||||
pa: 'Plasma Accelerator',
|
||||
pas: 'Planetary Approach Suite',
|
||||
pc: 'Prospector Limpet Controller',
|
||||
pce: 'Economy Class Passenger Cabin',
|
||||
pci: 'Business Class Passenger Cabin',
|
||||
pcm: 'First Class Passenger Cabin',
|
||||
pcq: 'Luxury Passenger Cabin',
|
||||
pd: 'power distributor',
|
||||
pl: 'Pulse Laser',
|
||||
pp: 'Power Plant',
|
||||
psg: 'Prismatic Shield Generator',
|
||||
pv: 'Planetary Vehicle Hanger',
|
||||
pv: 'Planetary Vehicle Hangar',
|
||||
rf: 'Refinery',
|
||||
rg: 'Rail Gun',
|
||||
s: 'Sensors',
|
||||
|
||||
@@ -15,119 +15,42 @@ export const formats = {
|
||||
|
||||
export const terms = {
|
||||
// Phrases
|
||||
PHRASE_BACKUP_DESC: 'Exportation détaillée des données Coriolis pour l\'utilisation dans d\'autres sites et outils', // Backup of all Coriolis data to save or transfer to another browser/device
|
||||
PHRASE_BACKUP_DESC: 'Copie des données Coriolis pour l\'utilisation dans d\'autres sites et outils', // Backup of all Coriolis data to save or transfer to another browser/device
|
||||
PHRASE_CONFIRMATION: 'Êtes-vous sûr?', // Are You Sure?
|
||||
PHRASE_EXPORT_DESC: 'Un export détaillé en JSON de votre configuration pour l\'utilisation dans d\'autres sites et outils', // A detailed JSON export of your build for use in other sites and tools
|
||||
PHRASE_FASTEST_RANGE: 'Portée maximale en cas de sauts successifs', // Consecutive max range jumps
|
||||
PHRASE_IMPORT: 'Coller JSON ou importer ici', // Paste JSON or import here
|
||||
PHRASE_NO_BUILDS: 'Défaut de configuration pour comparaison', // No builds added to comparison!
|
||||
PHRASE_NO_RETROCH: 'configuration non modifiée', // No Retrofitting changes
|
||||
PHRASE_SELECT_BUILDS: 'Sélectionner configurations à comparer', // Select Builds to Compare
|
||||
PHRASE_LADEN: 'Masse du Vaisseau + Carburant + Cargo', // Ship Mass + Fuel + Cargo
|
||||
PHRASE_NO_BUILDS: 'Aucune configuration ajoutée pour la comparaison', // No builds added to comparison!
|
||||
PHRASE_NO_RETROCH: 'Aucun changement de configuration', // No Retrofitting changes
|
||||
PHRASE_SELECT_BUILDS: 'Sélectionner les configurations à comparer', // Select Builds to Compare
|
||||
PHRASE_SG_RECHARGE: 'Temps de charge de 50% à 100 %', // Time from 50% to 100% Charge
|
||||
PHRASE_SG_RECOVER: 'Temps de redémarrage après perte du bouclier', // Recovery (to 50%) after collapse
|
||||
PHRASE_UNLADEN: 'Masse du Vaisseau hors Carburant et Cargo', // Ship Mass excluding Fuel and Cargo
|
||||
PHRASE_UPDATE_RDY: 'Mise à jour disponible ! Cliquez pour rafraichir', // Update Available! Click to Refresh
|
||||
|
||||
// Modules / Module Types - These should match the in-game translation (if any)
|
||||
am: 'Unité de maintenance de terrain auto', // Auto Field-Maintenance Unit
|
||||
'Basic Discovery Scanner': 'Détecteur de découverte simple', // Basic Discovery Scanner
|
||||
bh: 'Coque', // Bulkheads
|
||||
bl: 'Rayon Laser', // Beam Laser
|
||||
'Cargo Hatch': 'Ecoutille de soute', // Cargo Hatch
|
||||
c: 'Canon', // Cannon
|
||||
cc: 'Contrôleur de collecteurs', // Collector Limpet Controller
|
||||
cm: 'Contre-mesure', // Countermeasure
|
||||
'Chaff Launcher': 'Lanceur de paillettes', // Chaff Launcher
|
||||
cr: 'Compartiment de soute', // Cargo Rack
|
||||
cs: 'Scanner de soute', // Cargo Scanner
|
||||
dc: 'Ordinateur d\'appontage', // Docking Computer
|
||||
'Detailed Surface Scanner': 'Détecteur de surface détaillé', // Detailed Surface Scanner
|
||||
'Electronic Countermeasure': 'Contre mesure électronique', // Electronic Countermeasure
|
||||
fc: 'Canon à fragmentation', // Fragment Cannon
|
||||
fi: 'Intercepteur de réacteur FSD', // FSD Interdictor
|
||||
fs: 'Récupérateur de carburant', // Fuel Scoop
|
||||
fsd: 'Réacteur FSD', // Frame Shift Drive
|
||||
ft: 'Réservoir de carburant', // Fuel Tank
|
||||
fx: 'Drone de ravitaillement', // Fuel Transfer Limpet Controller
|
||||
'Heat Sink Launcher': 'Ejecteur de dissipateur thermique', // Heat Sink Launcher
|
||||
hb: 'Contrôle de patelle perce-soute', // Hatch Breaker Limpet Controller
|
||||
hr: 'Renfort de soute', // Hull Reinforcement Package
|
||||
'Intermediate Discovery Scanner': 'Détecteur de découverte intermédiaire', // Intermediate Discovery Scanner
|
||||
kw: 'Détecteur d\'avis de recherche', // Kill Warrant Scanner
|
||||
ls: 'Support vital', // life support
|
||||
mc: 'Canon multiple', // Multi-cannon
|
||||
ml: 'Laser minier', // Mining Laser
|
||||
mr: 'Lance missiles', // Missile Rack
|
||||
nl: 'Lance-mines', // Mine Launcher
|
||||
pa: 'accélérateur plasma', // Plasma Accelerator
|
||||
pc: 'Drône de minage', // Prospector Limpet Controller
|
||||
pd: 'distributeur d\'énérgie', // power distributor
|
||||
pl: 'Laser à impulsion', // Pulse Laser
|
||||
'Point Defence': 'Défense ponctuelle',
|
||||
pp: 'centrale d\'énergie', // Power Plant
|
||||
psg: 'générateur de bouclier prisme', // Prismatic Shield Generator
|
||||
rf: 'Raffinerie', // Refinery
|
||||
rg: 'Canon électromagnétique', // Rail Gun
|
||||
s: 'détecteurs', // Sensors
|
||||
sb: 'Survolteur de bouclier', // Shield Booster
|
||||
sc: 'scanner', // Scanner
|
||||
scb: 'Réserve de cellules d\'énergie', // Shield Cell Bank
|
||||
sg: 'Générateur de bouclier', // Shield Generator
|
||||
'Standard Docking Computer': 'ordinateur d\'appontage standard', // Standard Docking Computer
|
||||
t: 'propulseurs', // Thrusters
|
||||
tp: 'Tube lance-torpille', // Torpedo Pylon
|
||||
ul: 'Laser à rafale', // Burst Laser
|
||||
ws: 'Détecteur de sillage FSD', // Frame Shift Wake Scanner
|
||||
|
||||
// Bulkheads - These should match the in-game translation (if any)
|
||||
'Lightweight Alloy': 'alliage léger',
|
||||
'Reinforced Alloy': 'alliage renforcé',
|
||||
'Military Grade Composite': 'Composite militaire',
|
||||
'Mirrored Surface Composite': 'Composite à surface mirroir',
|
||||
'Reactive Surface Composite': 'Composite à surface réactive',
|
||||
|
||||
// Units / Metrics
|
||||
Ls: 'SL', // Light seconds
|
||||
LY: 'AL', // Light Years
|
||||
|
||||
// Sizes
|
||||
S: 'P', // Small Hardpoint (single Character)
|
||||
L: 'G', // Large Hardpoint size (single character)
|
||||
large: 'grand', // Large Ship Size
|
||||
medium: 'moyen', // Medium ship size
|
||||
S: 'P', // Small Hardpoint (single Character)
|
||||
small: 'petit', // Small ship size
|
||||
|
||||
// Terms
|
||||
'build name': 'Nom de la configuration', // Ship build/configuration/design name
|
||||
'compare all': 'tout comparer',
|
||||
'create new': 'Créer nouveau',
|
||||
'damage per second': null,
|
||||
'delete all': 'tout supprimer',
|
||||
'detailed export': 'export détaillé',
|
||||
'edit data': 'Editer donnée',
|
||||
'empty all': null,
|
||||
'Enter Name': 'Entrer nom',
|
||||
'fastest range': null, // Fastet totaljump range - sum of succesive jumps
|
||||
'fuel level': null, // Percent of fuel (T) in the tank
|
||||
'full tank': 'Réservoir plein',
|
||||
'internal compartments': 'compartiments internes',
|
||||
'jump range': 'Distance de saut',
|
||||
'mass lock factor': 'facteur inhibition de masse',
|
||||
'max mass': 'masse max',
|
||||
'net cost': 'coûts nets',
|
||||
'none created': 'Rien de créé',
|
||||
'optimal mass': null, // Lowest weight / best weight for jump distance, etc
|
||||
'refuel time': 'Temps de remplissage', // Time to refuel the tank when scooping
|
||||
'reload costs': null,
|
||||
'retrofit costs': 'Valeur de rachat', // The cost difference when upgrading / downgrading a component
|
||||
'retrofit from': 'Racheter de', // Retrofit from Build A against build B
|
||||
'T-Load': 'degrés', // Thermal load abbreviation
|
||||
'total range': null,
|
||||
'unit cost': null,
|
||||
'utility mounts': 'Support utilitaire',
|
||||
about: 'à propos', // Link to about page / about Coriolis.io
|
||||
added: 'ajouté',
|
||||
ammo: 'munition', // Ammunition
|
||||
armour: 'Armure',
|
||||
available: 'Disponibilité',
|
||||
available: 'Disponibilité', // Available options
|
||||
backup: 'sauvegarde',
|
||||
base: null,
|
||||
bays: null,
|
||||
bays: 'baies',
|
||||
bins: 'bacs', // Number of Mining Refinery bins
|
||||
build: 'Configuration', // Shorthand for the build/configuration/design name
|
||||
'build name': 'Nom de la configuration', // Ship build/configuration/design name
|
||||
builds: 'Configurations', // Ship build/configuration/design names
|
||||
buy: 'Acheter',
|
||||
cancel: 'Annuler',
|
||||
@@ -135,58 +58,77 @@ export const terms = {
|
||||
cells: 'Cellule', // Number of cells in a shield cell bank
|
||||
close: 'fermer',
|
||||
compare: 'comparer',
|
||||
'compare all': 'tout comparer',
|
||||
comparison: 'comparaison',
|
||||
comparisons: 'comparaisons',
|
||||
cost: 'coût', // Cost / price of a module or price of a ship
|
||||
costs: 'coûts', // Costs / prices of a modules or prices of ships
|
||||
create: 'Créer',
|
||||
'create new': 'Créer nouveau',
|
||||
credits: 'crédits',
|
||||
damage: 'Dégâts',
|
||||
damage: 'dégât',
|
||||
'damage per second': 'dégât par seconde',
|
||||
delete: 'supprimer',
|
||||
'delete all': 'tout supprimer',
|
||||
dep: 'depl', // Weapons/Hardpoints Deployed abbreviation
|
||||
deployed: 'déployé', // Weapons/Hardpoints Deployed
|
||||
'detailed export': 'export détaillé',
|
||||
disabled: 'désactivé',
|
||||
discount: 'ristourne',
|
||||
'edit data': 'Editer donnée',
|
||||
efficiency: 'rendement', // Power Plant efficiency
|
||||
empty: 'Vide',
|
||||
Explorer: null,
|
||||
'empty all': 'vide tout',
|
||||
'Enter Name': 'Entrer nom',
|
||||
Explorer: 'explorateur',
|
||||
'fastest range': 'gamme la plus rapide', // Fastet totaljump range - sum of succesive jumps
|
||||
fuel: 'carburant',
|
||||
'fuel level': 'niveau de carburant', // Percent of fuel (T) in the tank
|
||||
'full tank': 'Réservoir plein',
|
||||
hardpoints: 'Points d\'emport',
|
||||
hull: 'Coque', // Ships hull
|
||||
import: 'Importer',
|
||||
insurance: 'Assurance',
|
||||
jump: null, // Single jump range
|
||||
'internal compartments': 'compartiments internes',
|
||||
jump: 'saut', // Single jump range
|
||||
'jump range': 'Distance de saut',
|
||||
jumps: 'Sauts',
|
||||
laden: 'chargé',
|
||||
language: 'Langage',
|
||||
maneuverability: null,
|
||||
manufacturer: null,
|
||||
maneuverability: 'maniabilité',
|
||||
manufacturer: 'fabricant',
|
||||
mass: 'Masse',
|
||||
MLF: null, // Mass Lock Factor Abbreviation
|
||||
MNV: null, // Maneuverability abbreviation
|
||||
module: null,
|
||||
modules: null,
|
||||
'mass lock factor': 'facteur inhibition de masse',
|
||||
'max mass': 'masse max',
|
||||
MLF: 'FIM', // Mass Lock Factor Abbreviation
|
||||
'net cost': 'coûts nets',
|
||||
no: 'non',
|
||||
'none created': 'Rien de créé',
|
||||
ok: 'D\'accord',
|
||||
optimize: null,
|
||||
'optimal mass': 'masse optimale', // Lowest weight / best weight for jump distance, etc
|
||||
optimize: 'optimiser',
|
||||
pen: 'pén.', // Armour peneration abbreviation
|
||||
permalink: 'lien durable',
|
||||
power: 'énergie', // Power = Energy / second. Power generated by the power plant, or power consumed (MW / Mega Watts). Used in the power plant section
|
||||
proceed: 'continuer',
|
||||
PWR: 'P', // Power Abbreviation. See Power
|
||||
qty: null, // Quantity abbreviation
|
||||
qty: 'quantité', // Quantity abbreviation
|
||||
range: 'portée',
|
||||
rate: 'cadence',
|
||||
recharge: null, // Shield Recharge time from 50% -> 100%
|
||||
recovery: null, // Shield recovery time (after losing shields/turning on -> 50%)
|
||||
recharge: 'recharger', // Shield Recharge time from 50% -> 100%
|
||||
recovery: 'récupération', // Shield recovery time (after losing shields/turning on -> 50%)
|
||||
'refuel time': 'Temps de remplissage', // Time to refuel the tank when scooping
|
||||
reload: 'recharger', // Reload weapon/hardpoint
|
||||
'reload costs': 'recharger coûts',
|
||||
rename: 'renommer',
|
||||
repair: 'réparer',
|
||||
reset: 'Réinitialisation',
|
||||
ret: 'esc', // Retracted abbreviation
|
||||
retracted: 'escamoté', // Weapons/Hardpoints retracted
|
||||
'retrofit costs': 'Valeur de rachat', // The cost difference when upgrading / downgrading a component
|
||||
'retrofit from': 'Racheter de', // Retrofit from Build A against build B
|
||||
ROF: 'cadence', // Rate of Fire abbreviation
|
||||
roles: null, // Commander/Ship build roles - e.g. Trader, Bounty-Hunter, Explorer, etc
|
||||
roles: 'rôles', // Commander/Ship build roles - e.g. Trader, Bounty-Hunter, Explorer, etc
|
||||
save: 'sauvegarder',
|
||||
sell: 'vendre',
|
||||
settings: 'paramètres', // Coriolis application settings
|
||||
@@ -198,12 +140,16 @@ export const terms = {
|
||||
skip: 'Suivant', // Skip past something / ignore it
|
||||
speed: 'vitesse',
|
||||
Stock: 'de base', // Thermal-load abbreviation
|
||||
strength: null, // Strength in reference to Shield Strength
|
||||
subtotal: null,
|
||||
strength: 'force', // Strength in reference to Shield Strength
|
||||
subtotal: 'Sous-Total',
|
||||
'T-Load': 'degrés', // Thermal load abbreviation
|
||||
time: 'temps', // time it takes to complete something
|
||||
tooltips: null, // Tooltips setting - show/hide
|
||||
Trader: null, // Trader role
|
||||
tooltips: 'infobulles', // Tooltips setting - show/hide
|
||||
'total range': 'plage totale',
|
||||
Trader: 'commerçant', // Trader role
|
||||
'unit cost': 'coût unitaire',
|
||||
unladen: 'Non chargé', // No cargo or fuel
|
||||
'utility mounts': 'Support utilitaire',
|
||||
WEP: 'ARM', // Abbreviation - Weapon recharge rate for power distributor
|
||||
yes: 'oui'
|
||||
};
|
||||
|
||||
@@ -18,90 +18,22 @@ export const terms = {
|
||||
PHRASE_BACKUP_DESC: 'Сохраните все данные перед переносом в другой браузер или устройство', // Backup of all Coriolis data to save or transfer to another browser/device
|
||||
PHRASE_CONFIRMATION: 'Вы уверены?', // Are You Sure?
|
||||
PHRASE_EXPORT_DESC: 'Детальный JSON-экспорт вашей сборки для использования в других местах и инструментах', // A detailed JSON export of your build for use in other sites and tools
|
||||
PHRASE_FASTEST_RANGE: null, // Consecutive max range jumps
|
||||
PHRASE_FASTEST_RANGE: 'Последовательные прыжки максимальной дальности', // Consecutive max range jumps
|
||||
PHRASE_IMPORT: 'Для импорта вставьте код в эту форму', // Paste JSON or import here
|
||||
PHRASE_LADEN: null, // Ship Mass + Fuel + Cargo
|
||||
PHRASE_LADEN: 'Масса корабля с учётом топлива и грузов', // Ship Mass + Fuel + Cargo
|
||||
PHRASE_NO_BUILDS: 'Нечего сравнивать', // No builds added to comparison!
|
||||
PHRASE_NO_RETROCH: 'нет ранних версий сборкиконфигурации', // No Retrofitting changes
|
||||
PHRASE_NO_RETROCH: 'нет ранних версий сборки\\конфигурации', // No Retrofitting changes
|
||||
PHRASE_SELECT_BUILDS: 'Выберите конфигурацию для сравнения', // Select Builds to Compare
|
||||
PHRASE_SG_RECHARGE: null, // Time from 50% to 100% Charge
|
||||
PHRASE_SG_RECOVER: null, // Recovery (to 50%) after collapse
|
||||
PHRASE_UNLADEN: null, // Ship Mass excluding Fuel and Cargo
|
||||
PHRASE_SG_RECHARGE: 'восстановление с 60% до 100% объема щита', // Time from 50% to 100% Charge
|
||||
PHRASE_SG_RECOVER: 'восстановление [до 60%] после снятия щита', // Recovery (to 50%) after collapse
|
||||
PHRASE_UNLADEN: 'Масса корабля без учета топлива и грузов', // Ship Mass excluding Fuel and Cargo
|
||||
PHRASE_UPDATE_RDY: 'Доступно обновление. Нажмите для обновления.', // Update Available! Click to Refresh
|
||||
|
||||
// Modules / Module Types - These should match the in-game translation (if any)
|
||||
'Advanced Discovery Scanner': null, // Advanced Discovery Scanner
|
||||
'Basic Discovery Scanner': 'Стандартный исследовательский сканер', // Basic Discovery Scanner
|
||||
'Cargo Hatch': 'Грузовой люк', // Cargo Hatch
|
||||
'Chaff Launcher': 'Постановщик помех', // Chaff Launcher
|
||||
'Detailed Surface Scanner': 'Подробный сканер поверхности', // Detailed Surface Scanner
|
||||
'Electronic Countermeasure': 'Электронная противомера', // Electronic Countermeasure
|
||||
'Heat Sink Launcher': 'Теплоотводная ПУ', // Heat Sink Launcher
|
||||
'Intermediate Discovery Scanner': 'Средний исследовательский сканер', // Intermediate Discovery Scanner
|
||||
'Point Defence': 'Противоракетная защита',
|
||||
'Standard Docking Computer': 'Стандартный стыковочный компьютер', // Standard Docking Computer
|
||||
am: 'Ремонтный модуль', // Auto Field-Maintenance Unit
|
||||
bh: 'Корпус', // Bulkheads
|
||||
bl: 'Лучевой лазер', // Beam Laser
|
||||
bsg: null, // Bi-Weave Shield Generator
|
||||
c: 'Пушка', // Cannon
|
||||
cc: 'Контроллер "дрон-сборщик"', // Collector Limpet Controller
|
||||
cm: 'Контрмеры', // Countermeasure
|
||||
cr: 'Грузовой отсек', // Cargo Rack
|
||||
cs: 'Сканер груза', // Cargo Scanner
|
||||
dc: 'Стыковочный компьютер', // Docking Computer
|
||||
fc: 'Осколочное Орудие', // Fragment Cannon
|
||||
fi: 'Перехватчик FSD', // FSD Interdictor
|
||||
fs: 'Топливосборщик', // Fuel Scoop
|
||||
fsd: 'Двигатель FSD', // Frame Shift Drive
|
||||
ft: 'Топливный бак', // Fuel Tank
|
||||
fx: 'Контроллер "Дрон-заправщик"', // Fuel Transfer Limpet Controller
|
||||
hb: 'Контроллер "дрон-взломщик"', // Hatch Breaker Limpet Controller
|
||||
hr: 'Набор усиления корпуса', // Hull Reinforcement Package
|
||||
kw: 'Полицейский сканер', // Kill Warrant Scanner
|
||||
ls: 'Система жизнеобеспечения', // life support
|
||||
mc: 'Многоствольное орудие', // Multi-cannon
|
||||
ml: 'Бурильный лазер', // Mining Laser
|
||||
mr: 'Ракетная установка', // Missile Rack
|
||||
nl: 'Минноукладчик', // Mine Launcher
|
||||
pa: 'Ускоритель плазмы', // Plasma Accelerator
|
||||
pas: null, // Planetary Approach Suite
|
||||
pc: 'Контроллер "Дрон-исследователь"', // Prospector Limpet Controller
|
||||
pd: 'Распределитель энергии', // power distributor
|
||||
pl: 'Импульсный лазер', // Pulse Laser
|
||||
pp: 'Реактор', // Power Plant
|
||||
psg: 'Генератор призматического щита', // Prismatic Shield Generator
|
||||
pv: null, // Planetary Vehicle Hanger
|
||||
rf: 'Переработка', // Refinery
|
||||
rg: 'Рельсотрон', // Rail Gun
|
||||
s: 'Сенсоры', // Sensors
|
||||
sb: 'Усилитель щита', // Shield Booster
|
||||
sc: 'Сканер', // Scanner
|
||||
scb: 'Батареи перезарядки щита', // Shield Cell Bank
|
||||
sg: 'Генератор щита', // Shield Generator
|
||||
t: 'Двигатели', // Thrusters
|
||||
tp: 'Торпедный аппарат', // Torpedo Pylon
|
||||
ul: 'Мультиимпульсный лазер', // Burst Laser
|
||||
ws: 'FSD Сканнер', // Frame Shift Wake Scanner
|
||||
|
||||
// Bulkheads - These should match the in-game translation (if any)
|
||||
'Lightweight Alloy': 'Легкий сплав',
|
||||
'Reinforced Alloy': 'Усиленный сплав',
|
||||
'Military Grade Composite': 'Военный композит',
|
||||
'Mirrored Surface Composite': 'Зеркальный композит',
|
||||
'Reactive Surface Composite': 'Динамическая защита',
|
||||
|
||||
// Units / Metrics
|
||||
'/min': null, // Per minute
|
||||
'/s': null, // Per second
|
||||
kg: null, // Kilogram
|
||||
'kg/s': null, // Kilograms per second
|
||||
km: null, // Kilometer
|
||||
'/s': '/с', // Per second
|
||||
'm/s': 'м/с', // Meters / Second
|
||||
Ls: 'Св.сек', // Light seconds
|
||||
LY: 'Св.лет', // Light Years
|
||||
MJ: null, // Mega Joules
|
||||
MW: null, // Mega Watts
|
||||
CR: 'кр.', // Credits abbreviation
|
||||
|
||||
// Sizes
|
||||
@@ -110,24 +42,25 @@ export const terms = {
|
||||
L: 'б', // Large Hardpoint size (single character)
|
||||
H: 'O', // Huge Hardpoint size (single character)
|
||||
U: 'B', // Utility Hardpoint size (single character) - Kill warrant scanner, etc
|
||||
|
||||
small: 'Малый', // Small ship size
|
||||
medium: 'Средний', // Medium ship size
|
||||
large: 'большой', // Large Ship Size
|
||||
// Insurance
|
||||
alpha: 'Альфа', // Alpha backer insurance level
|
||||
beta: 'Бета', // Beta back insurance level
|
||||
standard: 'Стандартный', // Standard insurance level
|
||||
|
||||
// Terms
|
||||
'build name': 'название сборки', // Ship build/configuration/design name
|
||||
'compare all': 'сравнить все',
|
||||
'create new': 'Создать новый',
|
||||
'damage per second': null,
|
||||
'damage per second': 'урон в секунду',
|
||||
'delete all': 'Удалить все',
|
||||
'detailed export': 'Подробный экспорт',
|
||||
'edit data': 'Редактирование',
|
||||
'empty all': null,
|
||||
'empty all': 'пусто все',
|
||||
'Enter Name': 'Введите имя',
|
||||
'fastest range': null, // Fastet totaljump range - sum of succesive jumps
|
||||
'fuel level': null, // Percent of fuel (T) in the tank
|
||||
'fastest range': 'быстрый диапазон', // Fastet totaljump range - sum of succesive jumps
|
||||
'fuel level': 'уровень топлива', // Percent of fuel (T) in the tank
|
||||
'full tank': 'Полный бак',
|
||||
'internal compartments': 'внутренние отсеки',
|
||||
'jump range': 'Дальность прыжка',
|
||||
@@ -135,24 +68,18 @@ export const terms = {
|
||||
'max mass': 'Максимальная масса',
|
||||
'net cost': 'разница в цене',
|
||||
'none created': 'не создано',
|
||||
'optimal mass': null, // Lowest weight / best weight for jump distance, etc
|
||||
'refuel time': 'Время дозаправки', // Time to refuel the tank when scooping
|
||||
'reload costs': null,
|
||||
'retrofit costs': 'цена модификации', // The cost difference when upgrading / downgrading a component
|
||||
'retrofit from': 'модификация от', // Retrofit from Build A against build B
|
||||
'T-Load': 'Тепл.', // Thermal load abbreviation
|
||||
'total range': null,
|
||||
'unit cost': null,
|
||||
'utility mounts': 'Вспомогательное оборудование',
|
||||
about: 'О ...', // Link to about page / about Coriolis.io
|
||||
action: 'Действие',
|
||||
added: 'Добавлено',
|
||||
ammo: 'Боекомплект', // Ammunition
|
||||
armour: 'Броня',
|
||||
available: 'доступно',
|
||||
available: 'доступно', // Available options
|
||||
backup: 'Резервная копия',
|
||||
base: null,
|
||||
bays: null,
|
||||
bins: 'контейнеры', // Number of Mining Refinery bins
|
||||
boost: 'форсаж',
|
||||
build: 'cборка', // Shorthand for the build/configuration/design name
|
||||
@@ -179,7 +106,6 @@ export const terms = {
|
||||
efficiency: 'Эффективность', // Power Plant efficiency
|
||||
empty: 'пусто',
|
||||
ENG: 'ДВГ', // Abbreviation - Engine recharge rate for power distributor
|
||||
Explorer: null,
|
||||
export: 'Экспорт',
|
||||
forum: 'Форум',
|
||||
fuel: 'Топливо',
|
||||
@@ -187,32 +113,26 @@ export const terms = {
|
||||
hull: 'Корпус', // Ships hull
|
||||
import: 'импортировать ',
|
||||
insurance: 'Страховка',
|
||||
jump: null, // Single jump range
|
||||
|
||||
jumps: 'Прыжков',
|
||||
laden: 'Груженый',
|
||||
language: 'Язык',
|
||||
maneuverability: 'Моневренность',
|
||||
manufacturer: null,
|
||||
maneuverability: 'Маневренность',
|
||||
|
||||
mass: 'Масса',
|
||||
max: 'Макс',
|
||||
MLF: null, // Mass Lock Factor Abbreviation
|
||||
MNV: null, // Maneuverability abbreviation
|
||||
module: null,
|
||||
modules: null,
|
||||
|
||||
no: 'Нет',
|
||||
ok: null,
|
||||
optimize: null,
|
||||
pen: 'ПБ', // Armour peneration abbreviation
|
||||
permalink: 'Постоянная ссылка',
|
||||
power: 'Мощность', // Power = Energy / second. Power generated by the power plant, or power consumed (MW / Mega Watts). Used in the power plant section
|
||||
pri: 'Осн', // Priority abbreviation for power management
|
||||
proceed: 'продолжить',
|
||||
PWR: 'Эн', // Power Abbreviation. See Power
|
||||
qty: null, // Quantity abbreviation
|
||||
range: 'Дальность',
|
||||
rate: 'скорость',
|
||||
recharge: null, // Shield Recharge time from 50% -> 100%
|
||||
recovery: null, // Shield recovery time (after losing shields/turning on -> 50%)
|
||||
recharge: 'перезарядка', // Shield Recharge time from 50% -> 100%
|
||||
recovery: 'включение', // Shield recovery time (after losing shields/turning on -> 50%)
|
||||
reload: 'Перезагрузить', // Reload weapon/hardpoint
|
||||
rename: 'Переименовать',
|
||||
repair: 'Починка',
|
||||
@@ -220,7 +140,7 @@ export const terms = {
|
||||
ret: 'Убр.', // Retracted abbreviation
|
||||
retracted: 'Убрано', // Weapons/Hardpoints retracted
|
||||
ROF: 'В/сек', // Rate of Fire abbreviation
|
||||
roles: null, // Commander/Ship build roles - e.g. Trader, Bounty-Hunter, Explorer, etc
|
||||
|
||||
save: 'Сохранить',
|
||||
sell: 'Продать',
|
||||
settings: 'Настройки', // Coriolis application settings
|
||||
@@ -233,13 +153,9 @@ export const terms = {
|
||||
speed: 'скорость',
|
||||
standard: 'Стандартный', // Standard / Common modules (FSD, power plant, life support, etc)
|
||||
Stock: 'Стандартная комплектация', // Thermal-load abbreviation
|
||||
strength: null, // Strength in reference to Shield Strength
|
||||
subtotal: null,
|
||||
SYS: 'СИСТЕМЫ', // Abbreviation - System recharge rate for power distributor
|
||||
time: 'Время', // time it takes to complete something
|
||||
tooltips: null, // Tooltips setting - show/hide
|
||||
total: 'Всего',
|
||||
Trader: null, // Trader role
|
||||
type: 'Тип',
|
||||
unladen: 'Пустой', // No cargo or fuel
|
||||
URL: 'Ссылка', // Link, Uniform Resource Locator
|
||||
|
||||
@@ -24,15 +24,17 @@ export default class AboutPage extends Page {
|
||||
*/
|
||||
renderPage() {
|
||||
return <div className={'page'} style={{ textAlign: 'left', maxWidth: 800, margin: '0 auto' }}>
|
||||
<h1><CoriolisLogo style={{ marginRight: '0.4em' }} className='xl'/><span className='warning'>Coriolis</span></h1>
|
||||
<h1><CoriolisLogo style={{ marginRight: '0.4em' }} className='xl'/><span className='warning'>Coriolis EDCD Edition</span></h1>
|
||||
|
||||
<p>This is a clone of the Coriolis project, whose original author is currently unable to maintain it. This clone is maintained by the <a href="http://edcd.github.io/">EDCD community</a>.</p>
|
||||
<p>To recover your builds, go to <a href='https://coriolis.io/' target='_blank'>https://coriolis.io/</a>, backup your builds (Settings / Backup), copy the text, return here and import (Settings / Import).</p>
|
||||
<p>The Coriolis project was inspired by <a href='http://www.edshipyard.com/' target='_blank'>E:D Shipyard</a> and, of course, <a href='http://www.elitedangerous.com' target='_blank'>Elite Dangerous</a>. The ultimate goal of Coriolis is to provide rich features to support in-game play and planning while engaging the E:D community to support its development.</p>
|
||||
<p>Coriolis was created using assets and imagery from Elite: Dangerous, with the permission of Frontier Developments plc, for non-commercial purposes. It is not endorsed by nor reflects the views or opinions of Frontier Developments. A number of assets were sourced from <a href='http://edassets.org' target='_blank'>ED Assets</a></p>
|
||||
|
||||
<p>Coriolis was created using assets and imagery from Elite: Dangerous, with the permission of Frontier Developments plc, for non-commercial purposes. It is not endorsed by nor reflects the views or opinions of Frontier Developments.</p>
|
||||
|
||||
<a style={{ display: 'block', textDecoration: 'none' }} href='https://github.com/cmmcleod/coriolis' target='_blank' title='Coriolis Github Project'>
|
||||
<a style={{ display: 'block', textDecoration: 'none' }} href='https://github.com/EDCD/coriolis' target='_blank' title='Coriolis Github Project'>
|
||||
<GitHub style={{ margin: '0.4em' }} className='l fg xl'/>
|
||||
<h2 style={{ margin: 0, textDecoration: 'none' }}>Github</h2>
|
||||
github.com/cmmcleod/coriolis
|
||||
github.com/EDCD/coriolis
|
||||
</a>
|
||||
|
||||
<p>Coriolis is an open source project. Checkout the list of upcoming features and to-do list on github. Any and all contributions and feedback are welcome. If you encounter any bugs please report them and provide as much detail as possible.</p>
|
||||
@@ -47,7 +49,7 @@ export default class AboutPage extends Page {
|
||||
<p>Help keep the lights on! Donations will be used to cover costs of running and maintaining Coriolis. Thanks for helping!</p>
|
||||
|
||||
<h3>Chat</h3>
|
||||
<p>You can chat to us on <a href='https://www.hipchat.com/gfYQiZcmy' target='_blank'>HipChat</a> and sign up to chat on all E:D HipChat groups <a href='https://elite-dangerous.hipchat.com/invite/74670/a3af7ea57008362d83d05fada09bdf84' target='_blank'>here</a>.</p>
|
||||
<p>You can chat to us on our <a href='https://discord.gg/0uwCh6R62aPRjk9w' target='_blank'>EDCD Discord server</a>.</p>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ export default class ComparisonPage extends Page {
|
||||
super(props, context);
|
||||
this._sortShips = this._sortShips.bind(this);
|
||||
this._buildsSelected = this._buildsSelected.bind(this);
|
||||
this._updateDiscounts = this._updateDiscounts.bind(this);
|
||||
this.state = this._initState(context);
|
||||
}
|
||||
|
||||
@@ -130,7 +131,7 @@ export default class ComparisonPage extends Page {
|
||||
builds.sort(sortBy(predicate));
|
||||
|
||||
return {
|
||||
title: 'Coriolis - Compare',
|
||||
title: 'Coriolis EDCD Edition - Compare',
|
||||
predicate,
|
||||
desc,
|
||||
facets,
|
||||
@@ -161,6 +162,7 @@ export default class ComparisonPage extends Page {
|
||||
let b = new Ship(id, data.properties, data.slots); // Create a new Ship instance
|
||||
b.buildFrom(code); // Populate components from code
|
||||
b.buildName = name;
|
||||
b.applyDiscounts(Persist.getShipDiscount(), Persist.getModuleDiscount());
|
||||
return b;
|
||||
};
|
||||
|
||||
@@ -383,6 +385,21 @@ export default class ComparisonPage extends Page {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update all ship costs on disount change
|
||||
*/
|
||||
_updateDiscounts() {
|
||||
let shipDiscount = Persist.getShipDiscount();
|
||||
let moduleDiscount = Persist.getModuleDiscount();
|
||||
let builds = [];
|
||||
|
||||
for (let b of this.state.builds) {
|
||||
builds.push(b.applyDiscounts(shipDiscount, moduleDiscount));
|
||||
}
|
||||
|
||||
this.setState({ builds });
|
||||
}
|
||||
|
||||
/**
|
||||
* Update state based on context changes
|
||||
* @param {Object} nextProps Incoming/Next properties
|
||||
@@ -399,6 +416,7 @@ export default class ComparisonPage extends Page {
|
||||
*/
|
||||
componentWillMount() {
|
||||
this.resizeListener = this.context.onWindowResize(this._updateDimensions);
|
||||
this.persistListener = Persist.addListener('discounts', this._updateDiscounts);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -413,6 +431,7 @@ export default class ComparisonPage extends Page {
|
||||
*/
|
||||
componentWillUnmount() {
|
||||
this.resizeListener.remove();
|
||||
this.persistListener.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,6 +24,16 @@ 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
|
||||
* @param {String} shipName Ship Name
|
||||
* @param {String} buildName Build Name
|
||||
* @return {String} Document title
|
||||
*/
|
||||
function getTitle(shipName, buildName) {
|
||||
return `${shipName}${buildName ? ` - ${buildName}` : ''}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Outfitting Page
|
||||
*/
|
||||
@@ -65,10 +75,11 @@ export default class OutfittingPage extends Page {
|
||||
}
|
||||
|
||||
let fuelCapacity = ship.fuelCapacity;
|
||||
this._getTitle = getTitle.bind(this, data.properties.name);
|
||||
|
||||
return {
|
||||
error: null,
|
||||
title: 'Outfitting - ' + data.properties.name,
|
||||
title: this._getTitle(buildName),
|
||||
costTab: Persist.getCostTab() || 'costs',
|
||||
buildName,
|
||||
newBuildName: buildName,
|
||||
@@ -117,7 +128,7 @@ export default class OutfittingPage extends Page {
|
||||
this._updateRoute(shipId, newBuildName, code);
|
||||
}
|
||||
|
||||
this.setState({ buildName: newBuildName, code, savedCode: code });
|
||||
this.setState({ buildName: newBuildName, code, savedCode: code, title: this._getTitle(newBuildName) });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -282,7 +293,7 @@ export default class OutfittingPage extends Page {
|
||||
<div id='overview'>
|
||||
<h1>{ship.name}</h1>
|
||||
<div id='build'>
|
||||
<input value={newBuildName} onChange={this._buildNameChange} placeholder={translate('Enter Name')} maxsize={50} />
|
||||
<input value={newBuildName} onChange={this._buildNameChange} placeholder={translate('Enter Name')} maxLength={50} />
|
||||
<button onClick={canSave && this._saveBuild} disabled={!canSave} onMouseOver={termtip.bind(null, 'save')} onMouseOut={hide}>
|
||||
<FloppyDisk className='lg' />
|
||||
</button>
|
||||
|
||||
@@ -8,17 +8,18 @@ import { shallowEqual } from '../utils/UtilityFunctions';
|
||||
export default class Page extends React.Component {
|
||||
|
||||
static contextTypes = {
|
||||
route: React.PropTypes.object.isRequired,
|
||||
language: React.PropTypes.object.isRequired,
|
||||
sizeRatio: React.PropTypes.number.isRequired,
|
||||
openMenu: React.PropTypes.func.isRequired,
|
||||
closeMenu: React.PropTypes.func.isRequired,
|
||||
showModal: React.PropTypes.func.isRequired,
|
||||
hideModal: React.PropTypes.func.isRequired,
|
||||
tooltip: React.PropTypes.func.isRequired,
|
||||
termtip: React.PropTypes.func.isRequired,
|
||||
language: React.PropTypes.object.isRequired,
|
||||
noTouch: React.PropTypes.bool.isRequired,
|
||||
onCommand: React.PropTypes.func.isRequired,
|
||||
onWindowResize: React.PropTypes.func.isRequired,
|
||||
onCommand: React.PropTypes.func.isRequired
|
||||
openMenu: React.PropTypes.func.isRequired,
|
||||
route: React.PropTypes.object.isRequired,
|
||||
showModal: React.PropTypes.func.isRequired,
|
||||
sizeRatio: React.PropTypes.number.isRequired,
|
||||
termtip: React.PropTypes.func.isRequired,
|
||||
tooltip: React.PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
|
||||
167
src/app/pages/ShipyardPage.jsx
Executable file → Normal file
167
src/app/pages/ShipyardPage.jsx
Executable file → Normal file
@@ -27,6 +27,38 @@ function countInt(slot) {
|
||||
this.maxCargo += crEligible ? ModuleUtils.findInternal('cr', slot.maxClass, 'E').cargo : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate Ship summary and aggregated properties
|
||||
* @param {String} shipId Ship Id
|
||||
* @param {Object} shipData Ship Default Data
|
||||
* @return {Object} Ship summary and aggregated properties
|
||||
*/
|
||||
function shipSummary(shipId, shipData) {
|
||||
let summary = {
|
||||
id: shipId,
|
||||
hpCount: 0,
|
||||
intCount: 0,
|
||||
maxCargo: 0,
|
||||
hp: [0, 0, 0, 0, 0], // Utility, Small, Medium, Large, Huge
|
||||
int: [0, 0, 0, 0, 0, 0, 0, 0] // Sizes 1 - 8
|
||||
};
|
||||
Object.assign(summary, shipData.properties);
|
||||
let ship = new Ship(shipId, shipData.properties, shipData.slots);
|
||||
|
||||
// Build Ship
|
||||
ship.buildWith(shipData.defaults); // Populate with stock/default components
|
||||
ship.hardpoints.forEach(countHp.bind(summary)); // Count Hardpoints by class
|
||||
ship.internal.forEach(countInt.bind(summary)); // Count Internal Compartments by class
|
||||
summary.retailCost = ship.totalCost; // Record Stock/Default/retail cost
|
||||
ship.optimizeMass({ pd: '1D' }); // Optimize Mass with 1D PD for maximum possible jump range
|
||||
summary.maxJumpRange = ship.unladenRange; // Record Jump Range
|
||||
ship.optimizeMass({ th: ship.standard[1].maxClass + 'A', fsd: '2D', ft: '1C' }); // Optmize mass with Max Thrusters
|
||||
summary.topSpeed = ship.topSpeed;
|
||||
summary.topBoost = ship.topBoost;
|
||||
|
||||
return summary;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Shipyard summary page
|
||||
*/
|
||||
@@ -41,20 +73,30 @@ export default class ShipyardPage extends Page {
|
||||
*/
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.state = {
|
||||
title: 'Coriolis - Shipyard',
|
||||
shipPredicate: 'name',
|
||||
shipDesc: true
|
||||
};
|
||||
|
||||
if (!ShipyardPage.cachedShipSummaries) {
|
||||
ShipyardPage.cachedShipSummaries = [];
|
||||
for (let s in Ships) {
|
||||
ShipyardPage.cachedShipSummaries.push(this._shipSummary(s, Ships[s]));
|
||||
ShipyardPage.cachedShipSummaries.push(shipSummary(s, Ships[s]));
|
||||
}
|
||||
}
|
||||
|
||||
this.shipSummaries = ShipyardPage.cachedShipSummaries;
|
||||
this.state = {
|
||||
title: 'Coriolis EDCD Edition - Shipyard',
|
||||
shipPredicate: 'name',
|
||||
shipDesc: true,
|
||||
shipSummaries: ShipyardPage.cachedShipSummaries
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Higlight the current ship in the table
|
||||
* @param {String} shipId Ship Id
|
||||
* @param {SyntheticEvent} event Event
|
||||
*/
|
||||
_highlightShip(shipId, event) {
|
||||
event.stopPropagation();
|
||||
this.setState({ shipId });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,38 +118,6 @@ export default class ShipyardPage extends Page {
|
||||
this.setState({ shipPredicate, shipDesc, shipPredicateIndex });
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate Ship summary and aggregated properties
|
||||
* @param {String} shipId Ship Id
|
||||
* @param {Object} shipData Ship Default Data
|
||||
* @return {Object} Ship summary and aggregated properties
|
||||
*/
|
||||
_shipSummary(shipId, shipData) {
|
||||
let summary = {
|
||||
id: shipId,
|
||||
hpCount: 0,
|
||||
intCount: 0,
|
||||
maxCargo: 0,
|
||||
hp: [0, 0, 0, 0, 0], // Utility, Small, Medium, Large, Huge
|
||||
int: [0, 0, 0, 0, 0, 0, 0, 0] // Sizes 1 - 8
|
||||
};
|
||||
Object.assign(summary, shipData.properties);
|
||||
let ship = new Ship(shipId, shipData.properties, shipData.slots);
|
||||
|
||||
// Build Ship
|
||||
ship.buildWith(shipData.defaults); // Populate with stock/default components
|
||||
ship.hardpoints.forEach(countHp.bind(summary)); // Count Hardpoints by class
|
||||
ship.internal.forEach(countInt.bind(summary)); // Count Internal Compartments by class
|
||||
summary.retailCost = ship.totalCost; // Record Stock/Default/retail cost
|
||||
ship.optimizeMass({ pd: '1D' }); // Optimize Mass with 1D PD for maximum possible jump range
|
||||
summary.maxJumpRange = ship.unladenRange; // Record Jump Range
|
||||
ship.optimizeMass({ th: ship.standard[1].maxClass + 'A' }); // Optmize mass with Max Thrusters
|
||||
summary.topSpeed = ship.topSpeed;
|
||||
summary.topBoost = ship.topBoost;
|
||||
|
||||
return summary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the table row summary for the ship
|
||||
* @param {Object} s Ship summary
|
||||
@@ -118,8 +128,14 @@ export default class ShipyardPage extends Page {
|
||||
* @return {React.Component} Table Row
|
||||
*/
|
||||
_shipRowElement(s, translate, u, fInt, fRound) {
|
||||
return <tr key={s.id} className='highlight'>
|
||||
<td className='le'><Link href={'/outfit/' + s.id}>{s.name}</Link></td>
|
||||
let noTouch = this.context.noTouch;
|
||||
|
||||
return <tr
|
||||
key={s.id}
|
||||
style={{ height: '1.5em' }}
|
||||
className={cn({ highlighted: noTouch && this.state.shipId === s.id })}
|
||||
onMouseEnter={noTouch && this._highlightShip.bind(this, s.id)}
|
||||
>
|
||||
<td className='le'>{s.manufacturer}</td>
|
||||
<td className='cap'>{translate(SizeMap[s.class])}</td>
|
||||
<td>{s.agility}</td>
|
||||
@@ -155,17 +171,27 @@ export default class ShipyardPage extends Page {
|
||||
* @return {React.Component} The page contents
|
||||
*/
|
||||
renderPage() {
|
||||
let { translate, formats, units } = this.context.language;
|
||||
let { sizeRatio, language, termtip, noTouch } = this.context;
|
||||
let { translate, formats, units } = language;
|
||||
let hide = this.context.tooltip.bind(null, null);
|
||||
let fInt = formats.int;
|
||||
let fRound = formats.round;
|
||||
let shipSummaries = this.shipSummaries;
|
||||
let shipPredicate = this.state.shipPredicate;
|
||||
let shipPredicateIndex = this.state.shipPredicateIndex;
|
||||
let shipRows = [];
|
||||
let hide = this.context.tooltip.bind(null, null);
|
||||
let tip = this.context.termtip;
|
||||
let { shipSummaries, shipPredicate, shipPredicateIndex } = this.state;
|
||||
let sortShips = (predicate, index) => this._sortShips.bind(this, predicate, index);
|
||||
|
||||
let filters = {
|
||||
// 'class': { 1: 1, 2: 1}
|
||||
};
|
||||
|
||||
shipSummaries = shipSummaries.filter(s => {
|
||||
for (let prop in filters) {
|
||||
if (!(s[prop] in filters[prop])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
// Sort shipsOverview
|
||||
shipSummaries.sort((a, b) => {
|
||||
let valA = a[shipPredicate], valB = b[shipPredicate];
|
||||
@@ -194,26 +220,52 @@ export default class ShipyardPage extends Page {
|
||||
}
|
||||
});
|
||||
|
||||
let i = 0;
|
||||
let shipRows = new Array(shipSummaries.length);
|
||||
let detailRows = new Array(shipSummaries.length);
|
||||
|
||||
for (let s of shipSummaries) {
|
||||
shipRows.push(this._shipRowElement(s, translate, units, fInt, fRound));
|
||||
detailRows[i] = this._shipRowElement(s, translate, units, fInt, fRound);
|
||||
shipRows[i] = (
|
||||
<tr
|
||||
key={i}
|
||||
style={{ height: '1.5em' }}
|
||||
className={cn({ highlighted: noTouch && this.state.shipId === s.id })}
|
||||
onMouseEnter={noTouch && this._highlightShip.bind(this, s.id)}
|
||||
>
|
||||
<td className='le'><Link href={'/outfit/' + s.id}>{s.name}</Link></td>
|
||||
</tr>
|
||||
);
|
||||
i++;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='page' style={{ fontSize: this.context.sizeRatio + 'em' }}>
|
||||
<div className='scroll-x'>
|
||||
<table style={{ fontSize:'0.85em', whiteSpace:'nowrap', margin: '0 auto' }} align='center'>
|
||||
<div className='page' style={{ fontSize: sizeRatio + 'em' }}>
|
||||
<p style={{ textAlign: 'center' }}>This is <strong>Coriolis EDCD Edition</strong> - a temporary clone of <a href='https://coriolis.io/' target='_blank'>https://coriolis.io/</a> with added support for E:D 2.2. For more info see Settings / <Link href="/about" className='block'>About</Link></p>
|
||||
<div style={{ whiteSpace: 'nowrap', margin: '0 auto', fontSize: '0.8em', position: 'relative', display: 'inline-block', maxWidth: '100%' }}>
|
||||
<table style={{ width: '12em', position: 'absolute', zIndex: 1 }}>
|
||||
<thead>
|
||||
<tr className='main'>
|
||||
<th style={{ height: '2.6em', padding: '2px 0.4em 1px' }} className='sortable le rgt' onClick={sortShips('name')}>{translate('ship')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody onMouseLeave={this._highlightShip.bind(this, null)}>
|
||||
{shipRows}
|
||||
</tbody>
|
||||
</table>
|
||||
<div style={{ overflowX: 'scroll', maxWidth: '100%' }}>
|
||||
<table style={{ marginLeft: 'calc(12em - 1px)', zIndex: 0 }}>
|
||||
<thead>
|
||||
<tr className='main'>
|
||||
<th rowSpan={2} className='sortable le' onClick={sortShips('name')}>{translate('ship')}</th>
|
||||
<th rowSpan={2} className='sortable' onClick={sortShips('manufacturer')}>{translate('manufacturer')}</th>
|
||||
<th rowSpan={2} className='sortable' onClick={sortShips('class')}>{translate('size')}</th>
|
||||
<th rowSpan={2} className='sortable' onMouseEnter={tip.bind(null, 'maneuverability')} onMouseLeave={hide} onClick={sortShips('agility')}>{translate('mnv')}</th>
|
||||
<th rowSpan={2} className='sortable' onMouseEnter={termtip.bind(null, 'maneuverability')} onMouseLeave={hide} onClick={sortShips('agility')}>{translate('mnv')}</th>
|
||||
<th colSpan={4}>{translate('base')}</th>
|
||||
<th colSpan={4}>{translate('max')}</th>
|
||||
<th colSpan={5} className='sortable' onClick={sortShips('hpCount')}>{translate('hardpoints')}</th>
|
||||
<th colSpan={8} className='sortable' onClick={sortShips('intCount')}>{translate('internal compartments')}</th>
|
||||
<th rowSpan={2} className='sortable' onClick={sortShips('hullMass')}>{translate('hull')}</th>
|
||||
<th rowSpan={2} className='sortable' onMouseEnter={tip.bind(null, 'mass lock factor')} onMouseLeave={hide} onClick={sortShips('masslock')} >{translate('MLF')}</th>
|
||||
<th rowSpan={2} className='sortable' onMouseEnter={termtip.bind(null, 'mass lock factor')} onMouseLeave={hide} onClick={sortShips('masslock')} >{translate('MLF')}</th>
|
||||
<th rowSpan={2} className='sortable' onClick={sortShips('retailCost')}>{translate('cost')}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -243,10 +295,11 @@ export default class ShipyardPage extends Page {
|
||||
<th className='sortable' onClick={sortShips('int', 7)} >8</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{shipRows}
|
||||
<tbody onMouseLeave={this._highlightShip.bind(this, null)}>
|
||||
{detailRows}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -37,17 +37,22 @@ export const ModuleGroupToName = {
|
||||
am: 'Auto Field-Maintenance Unit',
|
||||
bsg: 'Bi-Weave Shield Generator',
|
||||
cr: 'Cargo Rack',
|
||||
fh: 'Fighter Hangar',
|
||||
fi: 'Frame Shift Drive Interdictor',
|
||||
hb: 'Hatch Breaker Limpet Controller',
|
||||
hr: 'Hull Reinforcement Package',
|
||||
rf: 'Refinery',
|
||||
scb: 'Shield Cell Bank',
|
||||
sg: 'Shield Generator',
|
||||
pv: 'Planetary Vehicle Hanger',
|
||||
pv: 'Planetary Vehicle Hangar',
|
||||
psg: 'Prismatic Shield Generator',
|
||||
dc: 'Docking Computer',
|
||||
fx: 'Fuel Transfer Limpet Controller',
|
||||
pc: 'Prospector Limpet Controller',
|
||||
pce: 'Economy Class Passenger Cabin',
|
||||
pci: 'Business Class Passenger Cabin',
|
||||
pcm: 'First Class Passenger Cabin',
|
||||
pcq: 'Luxury Passenger Cabin',
|
||||
cc: 'Collector Limpet Controller',
|
||||
|
||||
// Hard Points
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
|
||||
import { BulkheadNames } from './Constants';
|
||||
|
||||
/**
|
||||
* Filter eligble modules based on parameters
|
||||
* @param {Array} arr Available modules array
|
||||
@@ -19,12 +21,13 @@ export default class ModuleSet {
|
||||
/**
|
||||
* Instantiate the module set
|
||||
* @param {Object} modules All Modules
|
||||
* @param {number} mass Ship mass
|
||||
* @param {Array} maxStandardArr Array of standard slots classes/sizes
|
||||
* @param {Array} maxInternal Array of internal slots classes/sizes
|
||||
* @param {Array} maxHardPoint Array of hardpoint slots classes/sizes
|
||||
* @param {Object} shipData Ship Specifications Data (see coriolis-data/Ships)
|
||||
*/
|
||||
constructor(modules, mass, maxStandardArr, maxInternal, maxHardPoint) {
|
||||
constructor(modules, shipData) {
|
||||
let maxInternal = isNaN(shipData.slots.internal[0]) ? shipData.slots.internal[0].class : shipData.slots.internal[0];
|
||||
let mass = shipData.properties.hullMass + 6.5;
|
||||
let maxStandardArr = shipData.slots.standard;
|
||||
let maxHardPoint = shipData.slots.hardpoints[0];
|
||||
let stnd = modules.standard;
|
||||
this.mass = mass;
|
||||
this.standard = {};
|
||||
@@ -33,6 +36,10 @@ export default class ModuleSet {
|
||||
this.hpClass = {};
|
||||
this.intClass = {};
|
||||
|
||||
this.bulkheads = shipData.bulkheads.map((b, i) => {
|
||||
return Object.assign({ grp: 'bh', name: BulkheadNames[i], index: i, class: '', rating: '' }, b);
|
||||
});
|
||||
|
||||
this.standard[0] = filter(stnd.pp, maxStandardArr[0], 0, mass); // Power Plant
|
||||
this.standard[2] = filter(stnd.fsd, maxStandardArr[2], 0, mass); // FSD
|
||||
this.standard[4] = filter(stnd.pd, maxStandardArr[4], 0, mass); // Power Distributor
|
||||
@@ -53,18 +60,34 @@ export default class ModuleSet {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the specified bulkhead
|
||||
* @param {integer} index Bulkhead index
|
||||
* @return {Object} Bulkhead module details
|
||||
*/
|
||||
getBulkhead(index) {
|
||||
return this.bulkheads[index] || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the modules that areeligible for an internal slot
|
||||
* @param {Object} ship The ship
|
||||
* @param {integer} c The max class module that can be mounted in the slot
|
||||
* @param {Object} eligible) The map of eligible internal groups
|
||||
* @return {object} A map of all eligible modules by group
|
||||
*/
|
||||
getInts(c, eligible) {
|
||||
getInts(ship, c, eligible) {
|
||||
let o = {};
|
||||
for (let key in this.internal) {
|
||||
if (eligible && !eligible[key]) {
|
||||
continue;
|
||||
}
|
||||
if (key == 'pcq' && !(ship.luxuryCabins && ship.luxuryCabins === true)) {
|
||||
continue;
|
||||
}
|
||||
if (key == 'fh' && !(ship.fighterHangars && ship.fighterHangars === true)) {
|
||||
continue;
|
||||
}
|
||||
let data = filter(this.internal[key], c, 0, this.mass);
|
||||
if (data.length) { // If group is not empty
|
||||
o[key] = data;
|
||||
@@ -135,7 +158,7 @@ export default class ModuleSet {
|
||||
let sg = this.internal.sg[0];
|
||||
|
||||
for (let s of this.internal.sg) {
|
||||
if (s.mass < sg.mass && s.minmass <= hullMass && s.maxmass > hullMass) {
|
||||
if (s.mass < sg.mass && s.maxmass > hullMass) {
|
||||
sg = s;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ export function standard(type, id) {
|
||||
|
||||
/**
|
||||
* Finds the hardpoint with the specified ID
|
||||
* @param {string} id Hardpoint ID
|
||||
* @param {String} id Hardpoint ID
|
||||
* @return {Object} Hardpoint module or null
|
||||
*/
|
||||
export function hardpoints(id) {
|
||||
@@ -46,7 +46,7 @@ export function hardpoints(id) {
|
||||
|
||||
/**
|
||||
* Finds the internal module with the specified ID
|
||||
* @param {string} id Internal module ID
|
||||
* @param {String} id Internal module ID
|
||||
* @return {Object} Internal module or null
|
||||
*/
|
||||
export function internal(id) {
|
||||
@@ -65,11 +65,11 @@ export function internal(id) {
|
||||
* Finds an internal module based on Class, Rating, Group and/or name.
|
||||
* At least one ofGroup name or unique module name must be provided
|
||||
*
|
||||
* @param {string} groupName [Optional] Full name or abbreviated name for module group
|
||||
* @param {String} groupName [Optional] Full name or abbreviated name for module group
|
||||
* @param {integer} clss module Class
|
||||
* @param {string} rating module Rating
|
||||
* @param {string} name [Optional] Long/unique name for module -e.g. 'Advanced Discover Scanner'
|
||||
* @return {String} The id of the module if found, null if not found
|
||||
* @param {String} rating module Rating
|
||||
* @param {String} name [Optional] Long/unique name for module -e.g. 'Advanced Discover Scanner'
|
||||
* @return {Object} The module if found, null if not found
|
||||
*/
|
||||
export function findInternal(groupName, clss, rating, name) {
|
||||
let groups = {};
|
||||
@@ -103,10 +103,10 @@ export function findInternal(groupName, clss, rating, name) {
|
||||
* Finds an internal Module ID based on Class, Rating, Group and/or name.
|
||||
* At least one ofGroup name or unique module name must be provided
|
||||
*
|
||||
* @param {string} groupName [Optional] Full name or abbreviated name for module group
|
||||
* @param {String} groupName [Optional] Full name or abbreviated name for module group
|
||||
* @param {integer} clss module Class
|
||||
* @param {string} rating Module Rating
|
||||
* @param {string} name [Optional] Long/unique name for module -e.g. 'Advanced Discover Scanner'
|
||||
* @param {String} rating Module Rating
|
||||
* @param {String} name [Optional] Long/unique name for module -e.g. 'Advanced Discover Scanner'
|
||||
* @return {String} The id of the module if found, null if not found
|
||||
*/
|
||||
export function findInternalId(groupName, clss, rating, name) {
|
||||
@@ -118,12 +118,12 @@ export function findInternalId(groupName, clss, rating, name) {
|
||||
* Finds a hardpoint Module based on Class, Rating, Group and/or name.
|
||||
* At least one ofGroup name or unique module name must be provided
|
||||
*
|
||||
* @param {string} groupName [Optional] Full name or abbreviated name for module group
|
||||
* @param {String} groupName [Optional] Full name or abbreviated name for module group
|
||||
* @param {integer} clss Module Class
|
||||
* @param {string} rating [Optional] module Rating
|
||||
* @param {string} name [Optional] Long/unique name for module -e.g. 'Heat Sink Launcher'
|
||||
* @param {string} mount Mount type - [F]ixed, [G]imballed, [T]urret
|
||||
* @param {string} missile [Optional] Missile type - [D]umbfire, [S]eeker
|
||||
* @param {String} rating [Optional] module Rating
|
||||
* @param {String} name [Optional] Long/unique name for module -e.g. 'Heat Sink Launcher'
|
||||
* @param {String} mount Mount type - [F]ixed, [G]imballed, [T]urret
|
||||
* @param {String} missile [Optional] Missile type - [D]umbfire, [S]eeker
|
||||
* @return {String} The id of the module if found, null if not found
|
||||
*/
|
||||
export function findHardpoint(groupName, clss, rating, name, mount, missile) {
|
||||
@@ -158,12 +158,12 @@ export function findHardpoint(groupName, clss, rating, name, mount, missile) {
|
||||
* Finds a hardpoint module ID based on Class, Rating, Group and/or name.
|
||||
* At least one of Group name or unique module name must be provided
|
||||
*
|
||||
* @param {string} groupName [Optional] Full name or abbreviated name for module group
|
||||
* @param {String} groupName [Optional] Full name or abbreviated name for module group
|
||||
* @param {integer} clss module Class
|
||||
* @param {string} rating module Rating
|
||||
* @param {string} name [Optional] Long/unique name for module -e.g. 'Heat Sink Launcher'
|
||||
* @param {string} mount Mount type - [F]ixed, [G]imballed, [T]urret
|
||||
* @param {string} missile [Optional] Missile type - [D]umbfire, [S]eeker
|
||||
* @param {String} rating module Rating
|
||||
* @param {String} name [Optional] Long/unique name for module -e.g. 'Heat Sink Launcher'
|
||||
* @param {String} mount Mount type - [F]ixed, [G]imballed, [T]urret
|
||||
* @param {String} missile [Optional] Missile type - [D]umbfire, [S]eeker
|
||||
* @return {String} The id of the module if found, null if not found
|
||||
*/
|
||||
export function findHardpointId(groupName, clss, rating, name, mount, missile) {
|
||||
@@ -171,24 +171,9 @@ export function findHardpointId(groupName, clss, rating, name, mount, missile) {
|
||||
return h ? h.id : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up the bulkhead module for a specific ship and bulkhead
|
||||
* @param {string} shipId Unique ship Id/Key
|
||||
* @param {string|number} index Index for the specified bulkhead
|
||||
* @return {Object} The bulkhead module object
|
||||
*/
|
||||
export function bulkheads(shipId, index) {
|
||||
let bulkhead = Ships[shipId].bulkheads[index];
|
||||
bulkhead.class = 1;
|
||||
bulkhead.rating = 'I';
|
||||
bulkhead.name = BulkheadNames[index];
|
||||
|
||||
return bulkhead;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the bulkhead index for the given bulkhead name
|
||||
* @param {string} bulkheadName Bulkhead name in english
|
||||
* @param {String} bulkheadName Bulkhead name in english
|
||||
* @return {number} Bulkhead index
|
||||
*/
|
||||
export function bulkheadIndex(bulkheadName) {
|
||||
@@ -198,7 +183,7 @@ export function bulkheadIndex(bulkheadName) {
|
||||
|
||||
/**
|
||||
* Determine if a module group is a shield generator
|
||||
* @param {string} g Module Group name
|
||||
* @param {String} g Module Group name
|
||||
* @return {Boolean} True if the group is a shield generator
|
||||
*/
|
||||
export function isShieldGenerator(g) {
|
||||
@@ -211,11 +196,9 @@ export function isShieldGenerator(g) {
|
||||
*
|
||||
* 6.5 T is the lightest possible mass of standard components that any ship can use
|
||||
*
|
||||
* @param {string} shipId Unique ship Id/Key
|
||||
* @param {String} shipId Unique ship Id/Key
|
||||
* @return {ModuleSet} The set of modules the ship can install
|
||||
*/
|
||||
export function forShip(shipId) {
|
||||
let ship = Ships[shipId];
|
||||
let maxInternal = isNaN(ship.slots.internal[0]) ? ship.slots.internal[0].class : ship.slots.internal[0];
|
||||
return new ModuleSet(Modules, ship.properties.hullMass + 6.5, ship.slots.standard, maxInternal, ship.slots.hardpoints[0]);
|
||||
return new ModuleSet(Modules, Ships[shipId]);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,29 @@ import LZString from 'lz-string';
|
||||
|
||||
const STANDARD = ['powerPlant', 'thrusters', 'frameShiftDrive', 'lifeSupport', 'powerDistributor', 'sensors', 'fuelTank'];
|
||||
|
||||
/**
|
||||
* Generates ship-loadout JSON Schema standard object
|
||||
* @param {Object} standard model
|
||||
* @return {Object} JSON Schema
|
||||
*/
|
||||
function standardToSchema(standard) {
|
||||
if (standard.m) {
|
||||
let o = {
|
||||
class: standard.m.class,
|
||||
rating: standard.m.rating,
|
||||
enabled: Boolean(standard.enabled),
|
||||
priority: standard.priority + 1
|
||||
};
|
||||
|
||||
if (standard.m.name) {
|
||||
o.name = standard.m.name;
|
||||
}
|
||||
|
||||
return o;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates ship-loadout JSON Schema slot object
|
||||
* @param {Object} slot Slot model
|
||||
@@ -59,15 +82,15 @@ export function toDetailedBuild(buildName, ship) {
|
||||
}],
|
||||
components: {
|
||||
standard: {
|
||||
bulkheads: BulkheadNames[ship.bulkheads.index],
|
||||
bulkheads: BulkheadNames[ship.bulkheads.m.index],
|
||||
cargoHatch: { enabled: Boolean(ship.cargoHatch.enabled), priority: ship.cargoHatch.priority + 1 },
|
||||
powerPlant: { class: standard[0].m.class, rating: standard[0].m.rating, enabled: Boolean(standard[0].enabled), priority: standard[0].priority + 1 },
|
||||
thrusters: { class: standard[1].m.class, rating: standard[1].m.rating, enabled: Boolean(standard[1].enabled), priority: standard[1].priority + 1 },
|
||||
frameShiftDrive: { class: standard[2].m.class, rating: standard[2].m.rating, enabled: Boolean(standard[2].enabled), priority: standard[2].priority + 1 },
|
||||
lifeSupport: { class: standard[3].m.class, rating: standard[3].m.rating, enabled: Boolean(standard[3].enabled), priority: standard[3].priority + 1 },
|
||||
powerDistributor: { class: standard[4].m.class, rating: standard[4].m.rating, enabled: Boolean(standard[4].enabled), priority: standard[4].priority + 1 },
|
||||
sensors: { class: standard[5].m.class, rating: standard[5].m.rating, enabled: Boolean(standard[5].enabled), priority: standard[5].priority + 1 },
|
||||
fuelTank: { class: standard[6].m.class, rating: standard[6].m.rating, enabled: Boolean(standard[6].enabled), priority: standard[6].priority + 1 }
|
||||
powerPlant: standardToSchema(standard[0]),
|
||||
thrusters: standardToSchema(standard[1]),
|
||||
frameShiftDrive: standardToSchema(standard[2]),
|
||||
lifeSupport: standardToSchema(standard[3]),
|
||||
powerDistributor: standardToSchema(standard[4]),
|
||||
sensors: standardToSchema(standard[5]),
|
||||
fuelTank: standardToSchema(standard[6])
|
||||
},
|
||||
hardpoints: hardpoints.filter(slot => slot.maxClass > 0).map(slotToSchema),
|
||||
utility: hardpoints.filter(slot => slot.maxClass === 0).map(slotToSchema),
|
||||
|
||||
@@ -3,7 +3,7 @@ import * as Calc from './Calculations';
|
||||
import * as ModuleUtils from './ModuleUtils';
|
||||
import LZString from 'lz-string';
|
||||
|
||||
const UNIQUE_MODULES = ['psg', 'sg', 'bsg', 'rf', 'fs'];
|
||||
const UNIQUE_MODULES = ['psg', 'sg', 'bsg', 'rf', 'fs', 'fh'];
|
||||
|
||||
/**
|
||||
* Returns the power usage type of a slot and it's particular modul
|
||||
@@ -294,8 +294,8 @@ export default class Ship {
|
||||
*/
|
||||
getStandardString() {
|
||||
if(!this.serialized.standard) {
|
||||
this.serialized.standard = this.bulkheads.index + this.standard.reduce((arr, slot, i) => {
|
||||
arr[i] = slot.m ? slot.m.class + slot.m.rating : '-';
|
||||
this.serialized.standard = this.bulkheads.m.index + this.standard.reduce((arr, slot, i) => {
|
||||
arr[i] = slot.m ? slot.m.id : '-';
|
||||
return arr;
|
||||
}, new Array(this.standard.length)).join('');
|
||||
}
|
||||
@@ -739,7 +739,7 @@ export default class Ship {
|
||||
updateTopSpeed() {
|
||||
let speeds = Calc.speed(this.unladenMass + this.fuelCapacity, this.speed, this.boost, this.standard[1].m, this.pipSpeed);
|
||||
this.topSpeed = speeds['4 Pips'];
|
||||
this.topBoost = speeds.boost;
|
||||
this.topBoost = this.canBoost() ? speeds.boost : 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -855,8 +855,7 @@ export default class Ship {
|
||||
*/
|
||||
useBulkhead(index, preventUpdate) {
|
||||
let oldBulkhead = this.bulkheads.m;
|
||||
this.bulkheads.index = index;
|
||||
this.bulkheads.m = ModuleUtils.bulkheads(this.id, index);
|
||||
this.bulkheads.m = this.availCS.getBulkhead(index);
|
||||
this.bulkheads.discountedCost = this.bulkheads.m.cost * this.moduleCostMultiplier;
|
||||
this.armourMultiplier = ArmourMultiplier[index];
|
||||
this.updateStats(this.bulkheads, this.bulkheads.m, oldBulkhead, preventUpdate);
|
||||
@@ -880,7 +879,7 @@ export default class Ship {
|
||||
|
||||
/**
|
||||
* Use the lightest standard ModuleUtils unless otherwise specified
|
||||
* @param {Object} m Module overrides
|
||||
* @param {Object} m Module override set (standard type => module ID)
|
||||
* @return {this} The ship instance (for chaining operations)
|
||||
*/
|
||||
useLightestStandard(m) {
|
||||
@@ -889,16 +888,18 @@ export default class Ship {
|
||||
let standard = this.standard,
|
||||
// Find lightest Power Distributor that can still boost;
|
||||
pd = m.pd ? ModuleUtils.standard(4, m.pd) : this.availCS.lightestPowerDist(this.boostEnergy),
|
||||
fsd = m.fsd || standard[2].maxClass + 'A',
|
||||
ls = m.ls || standard[3].maxClass + 'D',
|
||||
s = m.s || standard[5].maxClass + 'D',
|
||||
fsd = ModuleUtils.standard(2, m.fsd || standard[2].maxClass + 'A'),
|
||||
ls = ModuleUtils.standard(3, m.ls || standard[3].maxClass + 'D'),
|
||||
s = ModuleUtils.standard(5, m.s || standard[5].maxClass + 'D'),
|
||||
ft = m.ft ? ModuleUtils.standard(6, m.ft) : standard[6].m, // Use existing fuel tank unless specified
|
||||
updated;
|
||||
|
||||
this.useBulkhead(0)
|
||||
.use(standard[2], ModuleUtils.standard(2, fsd)) // FSD
|
||||
.use(standard[3], ModuleUtils.standard(3, ls)) // Life Support
|
||||
.use(standard[5], ModuleUtils.standard(5, s)) // Sensors
|
||||
.use(standard[4], pd); // Power Distributor
|
||||
.use(standard[2], fsd) // FSD
|
||||
.use(standard[3], ls) // Life Support
|
||||
.use(standard[5], s) // Sensors
|
||||
.use(standard[4], pd) // Power Distributor
|
||||
.use(standard[6], ft); // Fuel Tank
|
||||
|
||||
// Thrusters and Powerplant must be determined after all other ModuleUtils are mounted
|
||||
// Loop at least once to determine absolute lightest PD and TH
|
||||
|
||||
126
src/app/shipyard/ShipRoles.js
Normal file
126
src/app/shipyard/ShipRoles.js
Normal file
@@ -0,0 +1,126 @@
|
||||
import * as ModuleUtils from './ModuleUtils';
|
||||
import { canMount } from '../utils/SlotFunctions';
|
||||
|
||||
/**
|
||||
* Standard / typical role for multi-purpose or combat (if shielded with better bulkheads)
|
||||
* @param {Ship} ship Ship instance
|
||||
* @param {Boolean} shielded True if shield generator should be included
|
||||
* @param {integer} bulkheadIndex Bulkhead to use see Constants.BulkheadNames
|
||||
*/
|
||||
export function multiPurpose(ship, shielded, bulkheadIndex) {
|
||||
ship.useStandard('A')
|
||||
.use(ship.standard[3], ModuleUtils.standard(3, ship.standard[3].maxClass + 'D')) // D Life Support
|
||||
.use(ship.standard[5], ModuleUtils.standard(5, ship.standard[5].maxClass + 'D')) // D Sensors
|
||||
.useBulkhead(bulkheadIndex);
|
||||
|
||||
if (shielded) {
|
||||
ship.internal.some(function(slot) {
|
||||
if (canMount(ship, slot, 'sg')) { // Assuming largest slot can hold an eligible shield
|
||||
ship.use(slot, ModuleUtils.findInternal('sg', slot.maxClass, 'A'));
|
||||
ship.setSlotEnabled(slot, true);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trader Role
|
||||
* @param {Ship} ship Ship instance
|
||||
* @param {Boolean} shielded True if shield generator should be included
|
||||
* @param {Object} standardOpts [Optional] Standard module optional overrides
|
||||
*/
|
||||
export function trader(ship, shielded, standardOpts) {
|
||||
let sg = shielded ? ship.getAvailableModules().lightestShieldGenerator(ship.hullMass) : null;
|
||||
|
||||
for (let i = ship.internal.length; i--;) {
|
||||
let slot = ship.internal[i];
|
||||
if (sg && canMount(ship, slot, 'sg', sg.class)) {
|
||||
ship.use(slot, sg);
|
||||
sg = null;
|
||||
} else {
|
||||
ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E'));
|
||||
}
|
||||
}
|
||||
|
||||
ship.useLightestStandard(standardOpts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Explorer Role
|
||||
* @param {Ship} ship Ship instance
|
||||
* @param {Boolean} planetary True if Planetary Vehicle Hangar (PVH) should be included
|
||||
*/
|
||||
export function explorer(ship, planetary) {
|
||||
let standardOpts = { ppRating: 'A' },
|
||||
intLength = ship.internal.length,
|
||||
heatSinkCount = 2, // Fit 2 heat sinks if possible
|
||||
afmUnitCount = 2, // Fit 2 AFM Units if possible
|
||||
shieldNext = planetary,
|
||||
sgSlot,
|
||||
fuelScoopSlot,
|
||||
pvhSlot,
|
||||
sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass);
|
||||
|
||||
if (!planetary) { // Non-planetary explorers don't really need to boost
|
||||
standardOpts.pd = '1D';
|
||||
}
|
||||
|
||||
ship.setSlotEnabled(ship.cargoHatch, false)
|
||||
.use(ship.internal[--intLength], ModuleUtils.internal('2f')); // Advanced Discovery Scanner
|
||||
|
||||
if (!planetary || intLength > 3) { // Don't mount a DDS on planetary explorer ships too small for both a PVH and DDS
|
||||
ship.use(ship.internal[--intLength], ModuleUtils.internal('2i')); // Detailed Surface Scanner
|
||||
}
|
||||
|
||||
for (let i = 0; i < intLength; i++) {
|
||||
let slot = ship.internal[i];
|
||||
let nextSlot = (i + 1) < intLength ? ship.internal[i + 1] : null;
|
||||
// Fit best possible Fuel Scoop
|
||||
if (!fuelScoopSlot && canMount(ship, slot, 'fs')) {
|
||||
fuelScoopSlot = slot;
|
||||
ship.use(slot, ModuleUtils.findInternal('fs', slot.maxClass, 'A'));
|
||||
ship.setSlotEnabled(slot, true);
|
||||
// Mount a Shield generator if possible AND an AFM Unit has been mounted already (Guarantees at least 1 AFM Unit)
|
||||
} else if (!sgSlot && shieldNext && canMount(ship, slot, 'sg', sg.class) && !canMount(ship, nextSlot, 'sg', sg.class)) {
|
||||
sgSlot = slot;
|
||||
shieldNext = false;
|
||||
ship.use(slot, sg);
|
||||
ship.setSlotEnabled(slot, true);
|
||||
// if planetary explorer and the next slot cannot mount a PVH or the next modul to mount is a SG
|
||||
} else if (planetary && !pvhSlot && canMount(ship, slot, 'pv') && (shieldNext || !canMount(ship, nextSlot, 'pv', 2))) {
|
||||
pvhSlot = slot;
|
||||
ship.use(slot, ModuleUtils.findInternal('pv', Math.min(Math.floor(pvhSlot.maxClass / 2) * 2, 6), 'G'));
|
||||
ship.setSlotEnabled(slot, false); // Disabled power for PVH
|
||||
shieldNext = !sgSlot;
|
||||
} else if (afmUnitCount > 0 && canMount(ship, slot, 'am')) {
|
||||
afmUnitCount--;
|
||||
ship.use(slot, ModuleUtils.findInternal('am', slot.maxClass, 'A'));
|
||||
ship.setSlotEnabled(slot, false); // Disabled power for AFM Unit
|
||||
shieldNext = !sgSlot;
|
||||
} else {
|
||||
ship.use(slot, null);
|
||||
}
|
||||
}
|
||||
|
||||
for (let s of ship.hardpoints) {
|
||||
if (s.maxClass == 0 && heatSinkCount) { // Mount up to 2 heatsinks
|
||||
ship.use(s, ModuleUtils.hardpoints('02'));
|
||||
ship.setSlotEnabled(s, heatSinkCount == 2); // Only enable a single Heatsink
|
||||
heatSinkCount--;
|
||||
} else {
|
||||
ship.use(s, null);
|
||||
}
|
||||
}
|
||||
|
||||
if (sgSlot) {
|
||||
// The SG and Fuel scoop to not need to be powered at the same time
|
||||
if (sgSlot.m.power > fuelScoopSlot.m.power) { // The Shield generator uses the most power
|
||||
ship.setSlotEnabled(fuelScoopSlot, false);
|
||||
} else { // The Fuel scoop uses the most power
|
||||
ship.setSlotEnabled(sgSlot, false);
|
||||
}
|
||||
}
|
||||
|
||||
ship.useLightestStandard(standardOpts);
|
||||
}
|
||||
@@ -2,6 +2,26 @@ import React from 'react';
|
||||
import cn from 'classnames';
|
||||
import { isShieldGenerator } from '../shipyard/ModuleUtils';
|
||||
import { Infinite } from '../components/SvgIcons';
|
||||
import Persist from '../stores/Persist';
|
||||
|
||||
/**
|
||||
* Determine if a slot on a ship can mount a module of a particular class and group
|
||||
* @param {Object} ship Ship object
|
||||
* @param {Object} slot Slot object
|
||||
* @param {String} group Module group/type abbrivation/code
|
||||
* @param {Integer} clazz [Optional] Module Class/Size
|
||||
* @return {Boolean} True if the slot can mount the module
|
||||
*/
|
||||
export function canMount(ship, slot, group, clazz) {
|
||||
if (slot &&
|
||||
(!slot.eligible || slot.eligible[group]) &&
|
||||
(group != 'pcq' || (ship.luxuryCabins && ship.luxuryCabins === true)) &&
|
||||
(group != 'fh' || (ship.fighterHangars && ship.fighterHangars === true)) &&
|
||||
(clazz === undefined || slot.maxClass >= clazz)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the translate name for the module mounted in the specified
|
||||
@@ -76,7 +96,9 @@ const PROP_BLACKLIST = {
|
||||
eddbID: 1,
|
||||
edID: 1,
|
||||
id: 1,
|
||||
index: 1,
|
||||
'class': 1,
|
||||
rating: 1,
|
||||
maxfuel: 1,
|
||||
fuelmul: 1,
|
||||
fuelpower: 1,
|
||||
@@ -105,7 +127,11 @@ const PROP_BLACKLIST = {
|
||||
enginecapacity: 1,
|
||||
enginerecharge: 1,
|
||||
systemcapacity: 1,
|
||||
systemrecharge: 1
|
||||
systemrecharge: 1,
|
||||
breachdps: 1,
|
||||
breachmin: 1,
|
||||
breachmax: 1,
|
||||
integrity: 1
|
||||
};
|
||||
|
||||
const TERM_LOOKUP = {
|
||||
@@ -158,10 +184,10 @@ function diffClass(a, b, negative) {
|
||||
*/
|
||||
function diff(format, mVal, mmVal) {
|
||||
if (mVal == Infinity) {
|
||||
return <Infinite/>;
|
||||
return '∞';
|
||||
} else {
|
||||
let diff = mVal - mmVal;
|
||||
if (!diff || !mVal || diff == mVal || Math.abs(diff) == Infinity) {
|
||||
if (!diff || mVal === undefined || diff == mVal || Math.abs(diff) == Infinity) {
|
||||
return format(mVal);
|
||||
}
|
||||
return `${format(mVal)} (${diff > 0 ? '+' : ''}${format(diff)})`;
|
||||
@@ -190,7 +216,7 @@ export function diffDetails(language, m, mm) {
|
||||
let mAffectsShield = isShieldGenerator(m.grp) || m.grp == 'sb';
|
||||
let mmAffectsShield = isShieldGenerator(mm.grp) || mm.grp == 'sb';
|
||||
|
||||
propDiffs.push(<div key='cost'>{translate('cost')}: <span className={diffClass(m.cost, mm.cost, true) }>{formats.int(m.cost || 0)}{units.CR}</span></div>);
|
||||
propDiffs.push(<div key='cost'>{translate('cost')}: <span className={diffClass(m.cost, mm.cost, true) }>{m.cost ? Math.round(m.cost * (1 - Persist.getModuleDiscount())) : 0}{units.CR}</span></div>);
|
||||
propDiffs.push(<div key='mass'>{translate('mass')}: <span className={diffClass(mMass, mm.mass, true)}>{diff(formats.round, mMass, mmMass)}{units.T}</span></div>);
|
||||
|
||||
for (let p in m) {
|
||||
@@ -218,7 +244,7 @@ export function diffDetails(language, m, mm) {
|
||||
}
|
||||
let sgDiffClass = Math.round((newShield - shield) * 100) / 100 == 0 ? 'muted' : (newShield > shield ? 'secondary' : 'warning');
|
||||
|
||||
propDiffs.push(<div key='shields'>{`${translate('shields')}: `}<span className={sgDiffClass}>{diff(formats.int, newShield, shield)}{units.MJ}</span></div>);
|
||||
propDiffs.push(<div key='shields'>{translate('shields')}: <span className={sgDiffClass}>{diff(formats.int, newShield, shield)}{units.MJ}</span></div>);
|
||||
}
|
||||
|
||||
if (m.grp == 'pd') {
|
||||
@@ -248,10 +274,10 @@ export function diffDetails(language, m, mm) {
|
||||
let ladenRange = this.calcLadenRange(massDiff + capDiff, m.fuel, fsd);
|
||||
|
||||
if (maxRange != this.unladenRange) {
|
||||
propDiffs.push(<div key='maxRange'>{`${translate('max')} ${translate('jump range')}: `}<span className={maxRange > this.unladenRange ? 'secondary' : 'warning'}>{formats.round(maxRange)}{units.LY}</span></div>);
|
||||
propDiffs.push(<div key='maxRange'>{translate('max')} {translate('jump range')}: <span className={maxRange > this.unladenRange ? 'secondary' : 'warning'}>{formats.round(maxRange)}{units.LY}</span></div>);
|
||||
}
|
||||
if (ladenRange != this.ladenRange) {
|
||||
propDiffs.push(<div key='unladenRange'>{`${translate('laden')} ${translate('jump range')}: `}<span className={ladenRange > this.ladenRange ? 'secondary' : 'warning'}>{formats.round(ladenRange)}{units.LY}</span></div>);
|
||||
propDiffs.push(<div key='unladenRange'>{translate('laden')} {translate('jump range')}: <span className={ladenRange > this.ladenRange ? 'secondary' : 'warning'}>{formats.round(ladenRange)}{units.LY}</span></div>);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
14
src/index.html
Executable file → Normal file
14
src/index.html
Executable file → Normal file
@@ -1,7 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html {%= o.htmlWebpackPlugin.options.appCache ? 'manifest=/' + o.htmlWebpackPlugin.options.appCache : '' %} >
|
||||
<head>
|
||||
<title>Coriolis</title>
|
||||
<title>Coriolis EDCD Edition</title>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="{%= o.htmlWebpackPlugin.files.css[0] %}">
|
||||
<!-- Standard headers -->
|
||||
@@ -25,12 +25,11 @@
|
||||
</head>
|
||||
<body style="background-color:#000;">
|
||||
<section id="coriolis"></section>
|
||||
<footer>
|
||||
<div class="right cap">
|
||||
<a href="https://github.com/cmmcleod/coriolis/releases/" target="_blank" title="Coriolis Github Project">{%= o.htmlWebpackPlugin.options.version %} - {%= new Date().toISOString().slice(0, 10) %}</a>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
window.CORIOLIS_GAPI_KEY = '{%= o.htmlWebpackPlugin.options.gapiKey %}';
|
||||
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>
|
||||
@@ -41,7 +40,6 @@
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
ga('create', '{%= o.htmlWebpackPlugin.options.uaTracking %}', 'auto');
|
||||
{% } %}
|
||||
window.CORIOLIS_GAPI_KEY = '{%= o.htmlWebpackPlugin.options.gapiKey %}';
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -23,9 +23,7 @@ html, body {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
text-rendering: optimizeLegibility;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
@@ -42,17 +40,23 @@ div, a, li {
|
||||
|
||||
#coriolis {
|
||||
width: 100%;
|
||||
min-height: 95%;
|
||||
height: 100%;
|
||||
overflow-y: scroll;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.page {
|
||||
margin: 0;
|
||||
padding: 0.5em 0;
|
||||
padding: 0.5em;
|
||||
width: 100%;
|
||||
min-height: 100%;
|
||||
clear: both;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
|
||||
.largePhone({
|
||||
padding: 0.5em 0.25em;
|
||||
});
|
||||
}
|
||||
|
||||
.l {
|
||||
|
||||
@@ -97,6 +97,7 @@ header {
|
||||
|
||||
input {
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
text-align: right;
|
||||
font-size: 1em;
|
||||
font-family: @fStandard;
|
||||
@@ -171,7 +172,7 @@ header {
|
||||
&:visited {
|
||||
color: @warning;
|
||||
}
|
||||
&:hover {
|
||||
.no-touch &:hover {
|
||||
color: teal;
|
||||
}
|
||||
&.active {
|
||||
|
||||
@@ -78,7 +78,7 @@ select {
|
||||
stroke-width: 0.5em;
|
||||
stroke: @primary-disabled;
|
||||
|
||||
&:hover {
|
||||
.no-touch &:hover {
|
||||
border-color: @primary;
|
||||
color: @primary;
|
||||
stroke: @primary;
|
||||
@@ -95,14 +95,14 @@ select {
|
||||
color: @warning-disabled;
|
||||
stroke: @warning-disabled;
|
||||
|
||||
&:hover {
|
||||
.no-touch &:hover {
|
||||
border-color: @warning;
|
||||
color: @warning;
|
||||
stroke: @warning;
|
||||
}
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
&.disabled, &.disabled:hover {
|
||||
cursor: not-allowed;
|
||||
border-color: @disabled;
|
||||
color: @disabled;
|
||||
@@ -150,4 +150,10 @@ select {
|
||||
}
|
||||
}
|
||||
|
||||
&.standard {
|
||||
ul {
|
||||
width: 16.25em;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
background-color: @primary-bg;
|
||||
border: 1px solid @primary-disabled;
|
||||
color: @fg;
|
||||
stroke: @fg;
|
||||
stroke-width: 20;
|
||||
fill: @fg;
|
||||
|
||||
.details-container {
|
||||
@@ -54,6 +56,8 @@
|
||||
font-size: 1.2em;
|
||||
width: 1.2em;
|
||||
color: @primary-disabled;
|
||||
stroke: @primary-disabled;
|
||||
stroke-width: 20;
|
||||
border-right: 1px solid @primary-disabled;
|
||||
box-sizing: border-box;
|
||||
padding-top: 0.2em;
|
||||
@@ -64,6 +68,8 @@
|
||||
text-transform: uppercase;
|
||||
font-size: 1.3em;
|
||||
color: lighten(@primary-bg, 12%);
|
||||
stroke: lighten(@primary-bg, 12%);
|
||||
stroke-width: 20;
|
||||
text-align: center;
|
||||
letter-spacing: 0.1em;
|
||||
line-height: 1.7em;
|
||||
@@ -71,12 +77,16 @@
|
||||
|
||||
&.selected {
|
||||
color: @primary-bg;
|
||||
stroke: @primary-bg;
|
||||
stroke-width: 20;
|
||||
fill: @primary-bg;
|
||||
background-color: @primary;
|
||||
border: 1px solid @primary;
|
||||
z-index: 1;
|
||||
.sz {
|
||||
color: @primary;
|
||||
stroke: @primary;
|
||||
stroke-width: 20;
|
||||
background-color: @primary-bg;
|
||||
border-right: 1px solid @primary;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
.user-select-none();
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
.no-touch &:hover {
|
||||
color: @primary;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,10 @@ thead {
|
||||
&.lft {
|
||||
border-left: 1px solid @primary-bg;
|
||||
}
|
||||
|
||||
&.rgt {
|
||||
border-right: 1px solid @primary-bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,23 +47,26 @@ tbody tr {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
td {
|
||||
line-height: 1.4em;
|
||||
padding: 0 0.3em;
|
||||
|
||||
&.val {
|
||||
border: 1px solid @primary-disabled;
|
||||
}
|
||||
&.lbl {
|
||||
border: 1px solid @primary-disabled;
|
||||
text-transform: uppercase;
|
||||
color: @primary-bg;
|
||||
background-color: @primary-disabled;
|
||||
}
|
||||
.no-touch &.highlight:hover, .no-touch &.highlighted {
|
||||
background-color: @warning-bg;
|
||||
}
|
||||
}
|
||||
|
||||
td {
|
||||
line-height: 1.4em;
|
||||
padding: 0 0.3em;
|
||||
|
||||
&.val {
|
||||
border: 1px solid @primary-disabled;
|
||||
}
|
||||
|
||||
&.lbl {
|
||||
border: 1px solid @primary-disabled;
|
||||
text-transform: uppercase;
|
||||
color: @primary-bg;
|
||||
background-color: @primary-disabled;
|
||||
}
|
||||
|
||||
&.tl {
|
||||
text-align: left;
|
||||
padding-left: 0.7em;
|
||||
|
||||
79
src/migrate.html
Normal file
79
src/migrate.html
Normal file
@@ -0,0 +1,79 @@
|
||||
<!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>
|
||||
|
||||
|
||||
@@ -76,7 +76,11 @@
|
||||
"class": { "type": "integer", "minimum": 2, "maximum": 8 },
|
||||
"rating": { "$ref": "#/definitions/standardRatings" },
|
||||
"enabled": { "type": "boolean" },
|
||||
"priority": { "type": "integer", "minimum": 1, "maximum": 5 }
|
||||
"priority": { "type": "integer", "minimum": 1, "maximum": 5 },
|
||||
"name": {
|
||||
"description": "The name identifing the thrusters (if applicable), e.g. 'Enhanced Performance'",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"frameShiftDrive": {
|
||||
|
||||
@@ -1,16 +1,28 @@
|
||||
var path = require('path');
|
||||
var exec = require('child_process').exec;
|
||||
var webpack = require('webpack');
|
||||
var pkgJson = require('./package');
|
||||
var HtmlWebpackPlugin = require("html-webpack-plugin");
|
||||
var ExtractTextPlugin = require("extract-text-webpack-plugin");
|
||||
|
||||
function CopyDirPlugin(source, destination) {
|
||||
this.source = source;
|
||||
this.destination = destination;
|
||||
}
|
||||
CopyDirPlugin.prototype.apply = function(compiler) {
|
||||
compiler.plugin('done', function() {
|
||||
console.log(compiler.outputPath, this.destination);
|
||||
exec('cp -r ' + this.source + ' ' + path.join(compiler.outputPath, this.destination));
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
devtool: 'eval',
|
||||
devServer: {
|
||||
headers: { "Access-Control-Allow-Origin": "*" }
|
||||
},
|
||||
entry: {
|
||||
app: [ 'webpack-dev-server/client?http://localhost:3300', 'webpack/hot/only-dev-server', path.join(__dirname, "src/app/index.js") ],
|
||||
app: [ 'webpack-dev-server/client?http://0.0.0.0:3300', 'webpack/hot/only-dev-server', path.join(__dirname, "src/app/index.js") ],
|
||||
lib: ['d3', 'react', 'react-dom', 'classnames', 'fbemitter', 'lz-string']
|
||||
},
|
||||
resolve: {
|
||||
@@ -23,6 +35,7 @@ module.exports = {
|
||||
publicPath: '/'
|
||||
},
|
||||
plugins: [
|
||||
new CopyDirPlugin(path.join(__dirname, 'src/.htaccess'), ''),
|
||||
new webpack.optimize.CommonsChunkPlugin('lib', 'lib.js'),
|
||||
new HtmlWebpackPlugin({
|
||||
inject: false,
|
||||
|
||||
@@ -74,6 +74,8 @@ module.exports = {
|
||||
}),
|
||||
new CopyDirPlugin(path.join(__dirname, 'src/schemas'), 'schemas'),
|
||||
new CopyDirPlugin(path.join(__dirname, 'src/images/logo/*'), ''),
|
||||
new CopyDirPlugin(path.join(__dirname, 'src/migrate.html'), ''),
|
||||
new CopyDirPlugin(path.join(__dirname, 'src/.htaccess'), ''),
|
||||
new AppCachePlugin({
|
||||
network: ['*'],
|
||||
settings: ['prefer-online'],
|
||||
|
||||
Reference in New Issue
Block a user