Compare commits

...

47 Commits

Author SHA1 Message Date
Colin McLeod
b49eb06101 Use IDs for standard components instead of [class][rating]. Update tests accordingly 2016-06-06 00:07:21 -07:00
Colin McLeod
eeb4a86a13 Allow 'named' standard module (special case for bulkheads) 2016-06-05 00:56:09 -07:00
Colin McLeod
3a265b2804 Update version to 2.0.0. Update react to v15 2016-06-05 00:54:35 -07:00
Colin McLeod
5ebabd6e6e Tweak string interpolation based on React v15 changes 2016-06-05 00:54:02 -07:00
Colin McLeod
6e6c337bbb Use table to align settings. Fixes #163 2016-06-05 00:52:23 -07:00
Colin McLeod
31b56354e0 Update French translations 2016-06-05 00:51:41 -07:00
Colin McLeod
fd1adbe55c Update German translations 2016-06-05 00:51:19 -07:00
Colin McLeod
3d0259c304 Fix lint issues 2016-04-04 13:02:26 -07:00
Colin McLeod
6f1b86118e Bump beta version 2016-04-04 12:52:31 -07:00
Colin McLeod
b3fcfa7808 Improve scrolling overview of shipyard page 2016-04-04 12:52:05 -07:00
Colin McLeod
4ead80a37c Improve touch detection 2016-04-04 12:51:46 -07:00
Colin McLeod
72dc73e090 Include discounts on comparison page 2016-04-04 12:31:54 -07:00
Colin McLeod
bb19dc25c0 Autofill sensor bug fix 2016-04-04 12:31:27 -07:00
Colin McLeod
1729636657 Make boost speed 0 when unable to boost 2016-04-04 12:30:18 -07:00
Colin McLeod
92809d1d24 Fix scrolling and hover issues on tablet 2016-04-04 12:29:59 -07:00
Colin McLeod
4e81c828df Fix JEST mock issue 2016-03-20 00:36:21 -07:00
Colin McLeod
f5bd7d2ecb Lint fix 2016-03-20 00:35:40 -07:00
Colin McLeod
a989bd5b5d Include migrate.html with prod build 2016-03-19 23:28:02 -07:00
Colin McLeod
1bedbb1909 Take discount into consideration for module tool tip 2016-03-19 23:27:43 -07:00
Colin McLeod
9775e1c742 Refactor footer, UI tweaks 2016-03-19 23:27:26 -07:00
Colin McLeod
19228a9c56 Change hold alt messaging 2016-03-19 23:25:42 -07:00
Colin McLeod
363bf7fd2a Shipyard page changes - in progress 2016-03-10 12:29:19 -08:00
Colin McLeod
15748066c9 Bulkhead refactoring 2016-03-10 12:29:02 -08:00
Colin McLeod
c3fe0a0cef UI Tweaks 2016-03-10 12:28:35 -08:00
Colin McLeod
1544deb350 Update tests for eagle changes 2016-03-07 09:14:48 -08:00
Colin McLeod
f060eb1b62 Fix standalone router bugs 2016-03-06 22:39:33 -08:00
Colin McLeod
32795ea678 Hide ALT text for mobile 2016-03-06 22:39:17 -08:00
Colin McLeod
d744d15132 Tweak touch module select tooltip 2016-03-06 21:33:25 -08:00
Colin McLeod
bfcef18c02 Fix Cost section discount label 2016-03-06 20:24:48 -08:00
Colin McLeod
b04c2ed2f5 Fix thruster warning bug 2016-03-06 20:24:33 -08:00
Colin McLeod
f51998b1c2 Update German, French, and Russian translations 2016-03-06 17:22:42 -08:00
Colin McLeod
d7000bfebf Update test to work with Jest 0.9.0 2016-03-06 17:22:26 -08:00
Colin McLeod
3e2b3e33fb Add localstorage migration for iframe 2016-03-04 11:15:51 -08:00
Colin McLeod
87dd52c043 Tweak and refactor build/role preset functions 2016-03-03 19:58:47 -08:00
Colin McLeod
05bc8ebb93 Fix find lightest shield generator min mass bug 2016-03-03 19:58:08 -08:00
Colin McLeod
09b945d29e tweak comments 2016-03-03 19:57:41 -08:00
Colin McLeod
67b5d749df Add can mount 2016-03-03 19:55:52 -08:00
Colin McLeod
a19fd69d0b Tweak internal slot labels 2016-03-03 19:55:04 -08:00
Colin McLeod
cfb65396ab fix max speed/boost, use lightest FSD and fuel tank 2016-03-02 10:08:16 -08:00
Colin McLeod
a4a0f96502 Add fuel tank to optimize 2016-03-02 10:07:49 -08:00
Colin McLeod
1390339024 Hull reinforcement will always clobber, fix 2016-03-02 10:07:05 -08:00
Colin McLeod
43d19f1dbb Fix phrase case 2016-03-02 10:06:41 -08:00
Colin McLeod
c20439264a Tweak GA tracking 2016-03-02 10:06:28 -08:00
Colin McLeod
61c3941618 Tweak reload on update message positioning 2016-02-29 09:35:55 -08:00
Colin McLeod
da277e4eaa Bump Beta version 2 2016-02-29 09:28:50 -08:00
Colin McLeod
51d24a1105 Fix Planetary Vehicle Hangar typo. Fixes #153 2016-02-26 09:04:47 -08:00
Colin McLeod
138931c0cf fix deploy script 2016-02-24 13:23:30 -08:00
45 changed files with 855 additions and 803 deletions

View File

@@ -6,7 +6,8 @@
{ {
"name": "Coriolis.io", "name": "Coriolis.io",
"url": "http://localhost:3300/outfit/anaconda/48A6A6A5A8A8A5C2c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.AwRj4zNKqA==.CwBhCYzBGW9qCTSqs5xA?bn=Test%20My%20Ship", "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" "shipId": "anaconda"
} }
], ],

View File

@@ -2,31 +2,31 @@
{ {
"shipId": "anaconda", "shipId": "anaconda",
"buildName": "Imported 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" "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", "shipId": "anaconda",
"buildName": "Imported 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" "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", "shipId": "cobra_mk_iii",
"buildName": "Imported 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" "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", "shipId": "type_9_heavy",
"buildName": "Imported 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" "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", "shipId": "vulture",
"buildName": "Imported 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)" "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)"
} }
] ]

View File

@@ -1,50 +1,50 @@
{ {
"type_6_transporter": { "type_6_transporter": {
"Cargo": "02A4D4A2D2D2D4C-----04040303430101.Iw1-kA==.Aw1-kA==", "Cargo": "0p0tdFal8d8s8f4-----04040303430101.Iw1-kA==.Aw1-kA==",
"Miner": "03A4D4A2D2D2D4C2l2l---040403451q0101.Iw1-kA==.Aw1-kA==", "Miner": "0p5tdFal8d8s8f42l2l---040403451q0101.Iw1-kA==.Aw1-kA==",
"Hopper": "02A4D4A2D1A2D4C1717---030302024300-.Iw1-kA==.Aw1-kA==" "Hopper": "0p0tdFal8d0s8f41717---030302024300-.Iw1-kA==.Aw1-kA=="
}, },
"type_7_transport": { "type_7_transport": {
"Cargo": "02A5D5A4D3D3D5C--------0505040403480101.Iw18aQ==.Aw18aQ==", "Cargo": "0p0tiFfliddsdf5--------0505040403480101.Iw18aQ==.Aw18aQ==",
"Miner": "04D5D5A4D2D3D5C--2l2l----0505041v03450000.Iw18aQ==.Aw18aQ==" "Miner": "0pdtiFflid8sdf5--2l2l----0505041v03450000.Iw18aQ==.Aw18aQ=="
}, },
"federal_dropship": { "federal_dropship": {
"Cargo": "04D5D5A5D3D4D4C-1717------05040448020201.Iw18aQ==.Aw18aQ==" "Cargo": "0pdtiFflnddsif4-1717------05040448020201.Iw18aQ==.Aw18aQ=="
}, },
"asp": { "asp": {
"Miner": "25A5A5A4D4A5A5C0s0s24242l2l---04054a1q02022o27.Iw18WQ==.Aw18WQ==" "Miner": "2pftfFflidfskf50s0s24242l2l---04054a1q02022o27.Iw18WQ==.Aw18WQ=="
}, },
"imperial_clipper": { "imperial_clipper": {
"Cargo": "03A5D5A5D4D5D4C--0s0s----0605450302020101.Iw18aQ==.Aw18aQ==", "Cargo": "0p5tiFflndisnf4--0s0s----0605450302020101.Iw18aQ==.Aw18aQ==",
"Dream": "26A6A5A5D6A5A4C0v0v0s0s0404040n4k5n5d2b29292o-.Iw18aQ==.Aw18aQ==", "Dream": "2pktkFflndpskf40v0v0s0s0404040n4k5n5d2b29292o-.Iw18aQ==.Aw18aQ==",
"Current": "04A6A5A5D4A5A4C----------------.Iw18aQ==.Aw18aQ==" "Current": "0patkFflndfskf4----------------.Iw18aQ==.Aw18aQ=="
}, },
"type_9_heavy": { "type_9_heavy": {
"Current": "04A7D6A5D5D4D6C---------0706054a0303020224.Iw18eQ==.Aw18eQ==" "Current": "0patsFklndnsif6---------0706054a0303020224.Iw18eQ==.Aw18eQ=="
}, },
"python": { "python": {
"Cargo": "04A6D5A4D6D6D5C---------050505040448020201.Iw18eQ==.Aw18eQ==", "Cargo": "0patnFflidsssf5---------050505040448020201.Iw18eQ==.Aw18eQ==",
"Miner": "06A6A5A4D6A6A5C0v0v0v2m2m0404--050505Ce4a1v02022o.Iw18eQ==.Aw18eQ==", "Miner": "0pktkFflidpspf50v0v0v2m2m0404--050505Ce4a1v02022o.Iw18eQ==.Aw18eQ==",
"Dream": "27A6A5A4D7A6A5C0v0v0v27270404040m5n5n4f2d2d032t0201.Iw18eQ==.Aw18eQ==", "Dream": "2pptkFfliduspf50v0v0v27270404040m5n5n4f2d2d032t0201.Iw18eQ==.Aw18eQ==",
"Missile": "07E6E5E4E7E6E5C2f2g2d2ePh----04044j03---002h.Iw18eQ==.Aw18eQ==" "Missile": "0pttoFjljdystf52f2g2d2ePh----04044j03---002h.Iw18eQ==.Aw18eQ=="
}, },
"anaconda": { "anaconda": {
"Dream": "48A7A6A5D8A8A5C2c0o0o0o1m1m0q0q0404040l0b0100004k5n5n112d2d040303326b.Iw18ZlA=.Aw18ZlA=", "Dream": "4putpFklndzsuf52c0o0o0o1m1m0q0q0404040l0b0100004k5n5n112d2d040303326b.Iw18ZlA=.Aw18ZlA=",
"Cargo": "04A6D6A5D5D8D5C----------------0605050504040445030301.Iw18ZlA=.Aw18ZlA=", "Cargo": "0patnFklndnsxf5----------------0605050504040445030301.Iw18ZlA=.Aw18ZlA=",
"Current": "04A6D6A5D5A8D5C----------------0605050504040403034524.Iw18ZlA=.Aw18ZlA=", "Current": "0patnFklndksxf5----------------0605050504040403034524.Iw18ZlA=.Aw18ZlA=",
"Explorer": "04A6D6A5D5A8D5C--------0202------f7050505040s372f2i4524.Iw18ZlA=.Aw18ZlA=", "Explorer": "0patnFklndksxf5--------0202------f7050505040s372f2i4524.Iw18ZlA=.Aw18ZlA=",
"Test": "48A6A6A5A8A8A5C2c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.Iw18ZlA=.Aw18ZlA=" "Test": "4putkFklkdzsuf52c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.Iw18ZlA=.Aw18ZlA="
}, },
"diamondback_explorer": { "diamondback_explorer": {
"Explorer": "02A4D5A3D3D3D5C---0202--320p432i2f.Iw1-kA==.Aw1-kA==" "Explorer": "0p0tdFfldddsdf5---0202--320p432i2f.Iw1-kA==.Aw1-kA=="
}, },
"vulture": { "vulture": {
"Bounty Hunter": "34A4C4A3D5A4A3C1e1e0404-0l4a5d27662j.Iw19kA==.Aw19kA==" "Bounty Hunter": "3patcFalddksff31e1e0404-0l4a5d27662j.Iw19kA==.Aw19kA=="
}, },
"fer_de_lance": { "fer_de_lance": {
"Attack": "25A5C4A4D6A4A3C1r0s0s0s0s000404-04-4a-5d27-.Iw18aQ==.Aw18aQ==" "Attack": "2pfthFalidpsff31r0s0s0s0s000404-04-4a-5d27-.Iw18aQ==.Aw18aQ=="
}, },
"eagle": { "eagle": {
"Figther": "42A3A3A1D2A2A2C0p0p24-40532j.Iw19A===.Aw19A===" "Figther": "4p0t5F5l3d5s5f20p0p24-40532j-.Iw1-EA==.Aw1-EA=="
} }
} }

View File

@@ -3348,8 +3348,8 @@
"references": [ "references": [
{ {
"name": "Coriolis.io", "name": "Coriolis.io",
"url": "http://localhost:3300/outfit/eagle/42A3A3A1D2A2A2C0p0p24-40532j.Iw19A===.Aw19A===?bn=Figther", "url": "http://localhost:3300/outfit/eagle/42A3A3A1D2A2A2C0p0p24-40532j-.Iw1-EA==.Aw1-EA==?bn=Figther",
"code": "42A3A3A1D2A2A2C0p0p24-40532j.AwRj49iA.AwgsIkEZigmIA===", "code": "42A3A3A1D2A2A2C0p0p24-40532j-.Iw1-EA==.Aw1-EA==",
"shipId": "eagle" "shipId": "eagle"
} }
], ],

View File

@@ -10,7 +10,7 @@ import { getLanguage } from '../src/app/i18n/Language';
describe('Import Modal', function() { 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 Persist = require('../src/app/stores/Persist').default;
const ModalImport = require('../src/app/components/ModalImport').default; const ModalImport = require('../src/app/components/ModalImport').default;
const mockContext = { const mockContext = {
@@ -25,8 +25,6 @@ describe('Import Modal', function() {
onWindowResize: jest.genMockFunction() onWindowResize: jest.genMockFunction()
}; };
MockRouter.go = jest.genMockFunction();
let modal, render, ContextProvider = Utils.createContextProvider(mockContext); let modal, render, ContextProvider = Utils.createContextProvider(mockContext);
/** /**
@@ -140,9 +138,10 @@ describe('Import Modal', function() {
expect(modal.state.importValid).toBeTruthy(); expect(modal.state.importValid).toBeTruthy();
expect(modal.state.errorMsg).toEqual(null); expect(modal.state.errorMsg).toEqual(null);
expect(modal.state.singleBuild).toBe(true);
clickProceed(); clickProceed();
expect(MockRouter.go.mock.calls.length).toBe(1); expect(MockRouter.go.mock.calls.length).toBe(1);
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/anaconda/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() { it('catches an invalid build', function() {

View File

@@ -2,7 +2,7 @@ import Ship from '../src/app/shipyard/Ship';
import { Ships } from 'coriolis-data/dist'; import { Ships } from 'coriolis-data/dist';
import * as ModuleUtils from '../src/app/shipyard/ModuleUtils'; import * as ModuleUtils from '../src/app/shipyard/ModuleUtils';
describe("Ship Factory", function() { describe("Ship", function() {
it("can build all ships", function() { it("can build all ships", function() {
for (let s in Ships) { for (let s in Ships) {
@@ -115,8 +115,7 @@ describe("Ship Factory", function() {
anaconda.use(anaconda.internal[1], ModuleUtils.internal('4j')); // 6E Shield Generator 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 is empty');
expect(anaconda.internal[2].m).toEqual(null, 'Anaconda default shield generator slot id is null');
expect(anaconda.internal[1].m.id).toEqual('4j', 'Slot 1 should have SG 4j in it'); 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'); 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')); 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 is empty');
expect(anaconda.internal[4].m).toEqual(null, 'Anaconda original fuel scoop slot id is null');
expect(anaconda.internal[3].m.id).toEqual('32', 'Slot 1 should have FS 32 in it'); 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'); 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')); 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 is empty');
expect(anaconda.internal[4].m).toEqual(null, 'Anaconda original refinery slot id is null');
expect(anaconda.internal[3].m.id).toEqual('23', 'Slot 1 should have RF 23 in it'); 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'); expect(anaconda.internal[3].m.grp).toEqual('rf','Slot 1 should have RF 23 in it');
}); });

View File

@@ -1,6 +1,6 @@
{ {
"name": "coriolis_shipyard", "name": "coriolis_shipyard",
"version": "2.0.0-Beta-1", "version": "2.0.0",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/cmmcleod/coriolis" "url": "https://github.com/cmmcleod/coriolis"
@@ -20,7 +20,7 @@
"prod-stop": "kill -QUIT $(cat nginx.pid)", "prod-stop": "kill -QUIT $(cat nginx.pid)",
"build": "npm run clean && NODE_ENV=production webpack -d -p --config webpack.config.prod.js", "build": "npm run clean && NODE_ENV=production webpack -d -p --config webpack.config.prod.js",
"rsync": "rsync -ae \"ssh -i $CORIOLIS_PEM\" --delete build/ $CORIOLIS_USER@$CORIOLIS_HOST:~/wwws", "rsync": "rsync -ae \"ssh -i $CORIOLIS_PEM\" --delete build/ $CORIOLIS_USER@$CORIOLIS_HOST:~/wwws",
"deploy": "npm run lint && npm test && npm run build:prod && npm run rsync" "deploy": "npm run lint && npm test && npm run build && npm run rsync"
}, },
"jest": { "jest": {
"scriptPreprocessor": "<rootDir>/node_modules/babel-jest", "scriptPreprocessor": "<rootDir>/node_modules/babel-jest",
@@ -43,7 +43,7 @@
"<rootDir>/node_modules/d3", "<rootDir>/node_modules/d3",
"<rootDir>/node_modules/lz-string", "<rootDir>/node_modules/lz-string",
"<rootDir>/node_modules/jsen", "<rootDir>/node_modules/jsen",
"<rootDir>/node_modules/coriolis-data", "coriolis-data",
"<rootDir>/src/app/shipyard", "<rootDir>/src/app/shipyard",
"<rootDir>/src/app/i18n", "<rootDir>/src/app/i18n",
"<rootDir>/src/app/utils", "<rootDir>/src/app/utils",
@@ -68,13 +68,13 @@
"extract-text-webpack-plugin": "^0.9.1", "extract-text-webpack-plugin": "^0.9.1",
"file-loader": "^0.8.4", "file-loader": "^0.8.4",
"html-webpack-plugin": "^1.7.0", "html-webpack-plugin": "^1.7.0",
"jest-cli": "*", "jest-cli": "^0.9.2",
"jsen": "^0.6.0", "jsen": "^0.6.0",
"json-loader": "^0.5.3", "json-loader": "^0.5.3",
"less": "^2.5.3", "less": "^2.5.3",
"less-loader": "^2.2.1", "less-loader": "^2.2.1",
"react-addons-test-utils": "^0.14.7", "react-addons-test-utils": "^15.0.1",
"react-testutils-additions": "^0.16.0", "react-testutils-additions": "^15.1.0",
"rimraf": "^2.4.3", "rimraf": "^2.4.3",
"style-loader": "^0.13.0", "style-loader": "^0.13.0",
"url-loader": "^0.5.6", "url-loader": "^0.5.6",
@@ -85,11 +85,11 @@
"babel-polyfill": "*", "babel-polyfill": "*",
"classnames": "^2.2.0", "classnames": "^2.2.0",
"coriolis-data": "cmmcleod/coriolis-data", "coriolis-data": "cmmcleod/coriolis-data",
"d3": "^3.5.9", "d3": "3.5.16",
"fbemitter": "^2.0.0", "fbemitter": "^2.0.0",
"lz-string": "^1.4.4", "lz-string": "^1.4.4",
"react": "^0.14.7", "react": "^15.0.1",
"react-dom": "^0.14.7", "react-dom": "^15.0.1",
"superagent": "^1.4.0" "superagent": "^1.4.0"
} }
} }

View File

@@ -21,17 +21,18 @@ import ErrorDetails from './pages/ErrorDetails';
export default class Coriolis extends React.Component { export default class Coriolis extends React.Component {
static childContextTypes = { 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, closeMenu: React.PropTypes.func.isRequired,
showModal: React.PropTypes.func.isRequired,
hideModal: React.PropTypes.func.isRequired, hideModal: React.PropTypes.func.isRequired,
tooltip: React.PropTypes.func.isRequired, language: React.PropTypes.object.isRequired,
termtip: React.PropTypes.func.isRequired, noTouch: React.PropTypes.bool.isRequired,
onCommand: React.PropTypes.func.isRequired,
onWindowResize: 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.emitter = new EventEmitter();
this.state = { this.state = {
noTouch: !('ontouchstart' in window || navigator.msMaxTouchPoints || navigator.maxTouchPoints),
page: null, page: null,
language: getLanguage(Persist.getLangCode()), language: getLanguage(Persist.getLangCode()),
route: null, route: {},
sizeRatio: Persist.getSizeRatio() sizeRatio: Persist.getSizeRatio()
}; };
@@ -237,17 +239,18 @@ export default class Coriolis extends React.Component {
*/ */
getChildContext() { getChildContext() {
return { return {
language: this.state.language,
route: this.state.route,
sizeRatio: this.state.sizeRatio,
openMenu: this._openMenu,
closeMenu: this._closeMenu, closeMenu: this._closeMenu,
showModal: this._showModal,
hideModal: this._hideModal, hideModal: this._hideModal,
tooltip: this._tooltip, language: this.state.language,
termtip: this._termtip, noTouch: this.state.noTouch,
onCommand: this._onCommand,
onWindowResize: this._onWindowResize, 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.onerror = this._onError.bind(this);
window.addEventListener('resize', () => this.emitter.emit('windowResize')); 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); document.addEventListener('keydown', this._keyDown);
Persist.addListener('language', this._onLanguageChange); Persist.addListener('language', this._onLanguageChange);
Persist.addListener('sizeRatio', this._onSizeRatioChange); Persist.addListener('sizeRatio', this._onSizeRatioChange);
@@ -281,11 +284,16 @@ export default class Coriolis extends React.Component {
render() { render() {
let currentMenu = this.state.currentMenu; 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} /> <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.error ? this.state.error : this.state.page ? React.createElement(this.state.page, { currentMenu }) : <NotFoundPage/> }
{ this.state.modal } { this.state.modal }
{ this.state.tooltip } { this.state.tooltip }
<footer>
<div className="right cap">
<a href="https://github.com/cmmcleod/coriolis/releases/" target="_blank" title="Coriolis Github Project">{window.CORIOLIS_VERSION} - {window.CORIOLIS_DATE}</a>
</div>
</footer>
</div>; </div>;
} }
} }

View File

@@ -1,15 +1,20 @@
import Persist from './stores/Persist'; import Persist from './stores/Persist';
let standalone = undefined;
/** /**
* Determine if the app is running in mobile/tablet 'standalone' mode * Determine if the app is running in mobile/tablet 'standalone' mode
* @return {Boolean} True if the app is in standalone mode * @return {Boolean} True if the app is in standalone mode
*/ */
function isStandAlone() { function isStandAlone() {
try { if (standalone === undefined) {
return window.navigator.standalone || (window.external && window.external.msIsSiteMode && window.external.msIsSiteMode()); try {
} catch (ex) { standalone = window.navigator.standalone || (window.external && window.external.msIsSiteMode && window.external.msIsSiteMode());
return false; } catch (ex) {
standalone = false;
}
} }
return standalone;
} }
/** /**
@@ -44,14 +49,14 @@ Router.start = function() {
if (isStandAlone()) { if (isStandAlone()) {
let state = Persist.getState(); let state = Persist.getState();
// If a previous state has been stored, load that state // If a previous state has been stored, load that state
if (state && state.name && state.params) { if (state && state.path) {
Router(this.props.initialPath || '/'); Router.replace(state.path, null, true);
} else { } else {
Router('/'); Router.replace('/', null, true);
} }
} else { } else {
let url = location.pathname + location.search; 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); let ctx = new Context(path, state);
Router.dispatch(ctx); Router.dispatch(ctx);
if (!ctx.unhandled) { if (!ctx.unhandled) {
if (isStandAlone()) {
Persist.setState(ctx);
}
history.pushState(ctx.state, ctx.title, ctx.canonicalPath); history.pushState(ctx.state, ctx.title, ctx.canonicalPath);
} }
return ctx; return ctx;
@@ -85,7 +93,12 @@ Router.go = function(path, state) {
Router.replace = function(path, state, dispatch) { Router.replace = function(path, state, dispatch) {
gaTrack(path); gaTrack(path);
let ctx = new Context(path, state); 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); history.replaceState(ctx.state, ctx.title, ctx.canonicalPath);
return ctx; return ctx;
}; };
@@ -227,7 +240,7 @@ Route.prototype.match = function(path, params) {
*/ */
function gaTrack(path) { function gaTrack(path) {
if (window.ga) { if (window.ga) {
window.ga('send', 'pageview', { page: path }); window.ga('send', 'pageview', path);
} }
} }

View File

@@ -5,7 +5,7 @@ import { stopCtxPropagation } from '../utils/UtilityFunctions';
import cn from 'classnames'; import cn from 'classnames';
import { MountFixed, MountGimballed, MountTurret } from './SvgIcons'; 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 * Available modules menu
@@ -173,6 +173,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
* @param {SyntheticEvent} event Event * @param {SyntheticEvent} event Event
*/ */
_touchStart(showDiff, event) { _touchStart(showDiff, event) {
event.preventDefault();
let rect = event.currentTarget.getBoundingClientRect(); let rect = event.currentTarget.getBoundingClientRect();
this.touchTimeout = setTimeout(showDiff.bind(this, rect), PRESS_THRESHOLD); this.touchTimeout = setTimeout(showDiff.bind(this, rect), PRESS_THRESHOLD);
} }
@@ -183,10 +184,10 @@ export default class AvailableModulesMenu extends TranslatedComponent {
* @param {SyntheticEvent} event Event * @param {SyntheticEvent} event Event
*/ */
_touchEnd(select, event) { _touchEnd(select, event) {
event.preventDefault();
if (this.touchTimeout !== null) { // If timeout has not fired (been nulled out) yet if (this.touchTimeout !== null) { // If timeout has not fired (been nulled out) yet
select(); select();
} }
event.preventDefault();
this._hideDiff(); this._hideDiff();
} }

View File

@@ -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})`}> <g className='x axis' ref={(elem) => d3.select(elem).call(this.xAxis)} transform={`translate(0,${innerHeight})`}>
<text className='cap' y='30' dy='.1em' x={innerWidth / 2} style={{ textAnchor: 'middle' }}> <text className='cap' y='30' dy='.1em' x={innerWidth / 2} style={{ textAnchor: 'middle' }}>
<tspan>{title}</tspan> <tspan>{title}</tspan>
{ unit ? <tspan className='metric'>{` (${unit})`}</tspan> : null } { unit ? <tspan className='metric'> ({unit})</tspan> : null }
</text> </text>
</g> </g>
<g className='y axis' ref={(elem) => { let e = d3.select(elem); e.call(this.yAxis); e.selectAll('text').each(insertLinebreaks); }} /> <g className='y axis' ref={(elem) => { let e = d3.select(elem); e.call(this.yAxis); e.selectAll('text').each(insertLinebreaks); }} />

View File

@@ -304,8 +304,8 @@ export default class CostSection extends TranslatedComponent {
<tr className='main'> <tr className='main'>
<th colSpan='2' className='sortable le' onClick={this._sortCostBy.bind(this,'m')}> <th colSpan='2' className='sortable le' onClick={this._sortCostBy.bind(this,'m')}>
{translate('module')} {translate('module')}
{shipDiscount && <u className='cap optional-hide' style={{ marginLeft: '0.5em' }}>{`[${translate('ship')} -${formats.pct(shipDiscount)}]`}</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>} {moduleDiscount ? <u className='cap optional-hide' style={{ marginLeft: '0.5em' }}>{`[${translate('modules')} -${formats.pct(moduleDiscount)}]`}</u> : null}
</th> </th>
<th className='sortable le' onClick={this._sortCostBy.bind(this, 'cr')} >{translate('credits')}</th> <th className='sortable le' onClick={this._sortCostBy.bind(this, 'cr')} >{translate('credits')}</th>
</tr> </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, 'buyName')}>{translate('buy')}</th>
<th colSpan='2' className='sortable le' onClick={this._sortRetrofitBy.bind(this, 'cr')}> <th colSpan='2' className='sortable le' onClick={this._sortRetrofitBy.bind(this, 'cr')}>
{translate('net cost')} {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> </th>
</tr> </tr>
</thead> </thead>
@@ -400,7 +400,7 @@ export default class CostSection extends TranslatedComponent {
let retrofitCosts = []; let retrofitCosts = [];
let retrofitTotal = 0, i, l, item; let retrofitTotal = 0, i, l, item;
if (ship.bulkheads.index != retrofitShip.bulkheads.index) { if (ship.bulkheads.m.index != retrofitShip.bulkheads.m.index) {
item = { item = {
buyClassRating: ship.bulkheads.m.class + ship.bulkheads.m.rating, buyClassRating: ship.bulkheads.m.class + ship.bulkheads.m.rating,
buyName: ship.bulkheads.m.name, buyName: ship.bulkheads.m.name,

View File

@@ -38,7 +38,7 @@ export default class HardpointSlot extends Slot {
return <div className='details' draggable='true' onDragStart={drag} onDragEnd={drop}> return <div className='details' draggable='true' onDragStart={drag} onDragEnd={drop}>
<div className={'cb'}> <div className={'cb'}>
<div className={'l'}>{classRating + ' ' + translate(m.name || m.grp)}</div> <div className={'l'}>{classRating} {translate(m.name || m.grp)}</div>
<div className={'r'}>{m.mass}{u.T}</div> <div className={'r'}>{m.mass}{u.T}</div>
</div> </div>
<div className={'cb'}> <div className={'cb'}>

View File

@@ -94,6 +94,7 @@ export default class HardpointsSlotSection extends SlotSection {
return <div className='select hardpoint' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}> return <div className='select hardpoint' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
<ul> <ul>
<li className='lc' onClick={this._empty}>{translate('empty all')}</li> <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> </ul>
<div className='select-group cap'>{translate('pl')}</div> <div className='select-group cap'>{translate('pl')}</div>
<ul> <ul>

View File

@@ -362,37 +362,51 @@ export default class Header extends TranslatedComponent {
return ( return (
<div className='menu-list no-wrap cap' onClick={ (e) => e.stopPropagation() }> <div className='menu-list no-wrap cap' onClick={ (e) => e.stopPropagation() }>
<div style={{ lineHeight: '2em' }}> <table style={{ width: '100%', background: 'none', borderSpacing: '0 0.5em' }}>
{translate('language')} <tbody>
<select className='cap' value={Persist.getLangCode()} onChange={this._setLanguage}> <tr>
{this.languageOptions} <td>{translate('language')}</td>
</select> <td className='ri'>
<br/> <select className='cap' dir='rtl' value={Persist.getLangCode()} onChange={this._setLanguage}>
<span className='cap ptr' onClick={this._toggleTooltips} > {this.languageOptions}
{translate('tooltips')} </select>
<div className={cn({ disabled: !tips, 'primary-disabled': tips })} style={{ marginLeft: '0.5em', display: 'inline-block' }}>{(tips ? '✓' : '✗')}</div> </td>
</span> </tr>
<br/> <tr className='cap ptr' onClick={this._toggleTooltips} >
{translate('insurance')} <td>{translate('tooltips')}</td>
<select className='cap' value={Persist.getInsurance()} onChange={this._setInsurance}> <td className={cn('ri', { disabled: !tips, 'primary-disabled': tips })}>{(tips ? '✓' : '✗')}</td>
{this.insuranceOptions} </tr>
</select> <tr>
<br/> <td>{translate('insurance')}</td>
{translate('ship')} {translate('discount')} <td className='ri'>
<input type='text' size='10' value={this.state.shipDiscount} onChange={this._changeShipDiscount} onFocus={selectAll} onBlur={this._setShipDiscount} onKeyDown={this._kpShipDiscount}/> <select className='cap' dir='rtl' value={Persist.getInsurance()} onChange={this._setInsurance}>
<u className='primary-disabled'>%</u> {this.insuranceOptions}
<br/> </select>
{translate('module')} {translate('discount')} </td>
<input type='text' size='10' value={this.state.moduleDiscount} onChange={this._changeModuleDiscount} onFocus={selectAll} onBlur={this._setModuleDiscount} onKeyDown={this._kpModuleDiscount}/> </tr>
<u className='primary-disabled'>%</u> <tr>
</div> <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 /> <hr />
<ul> <ul style={{ width: '100%' }}>
{translate('builds')} & {translate('comparisons')} {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._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._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="#" 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> </ul>
<hr /> <hr />
<table style={{ width: 300, backgroundColor: 'transparent' }}> <table style={{ width: 300, backgroundColor: 'transparent' }}>
@@ -462,32 +476,28 @@ export default class Header extends TranslatedComponent {
let translate = this.context.language.translate; let translate = this.context.language.translate;
let openedMenu = this.props.currentMenu; let openedMenu = this.props.currentMenu;
let hasBuilds = Persist.hasBuilds(); let hasBuilds = Persist.hasBuilds();
if (this.props.appCacheUpdate) {
return <div id="app-update" onClick={() => window.location.reload() }>{translate('PHRASE_UPDATE_RDY')}</div>;
}
return ( return (
<header> <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> <Link className='l' href='/' style={{ marginRight: '1em' }} title='Home'><CoriolisLogo className='icon xl' /></Link>
<div className='l menu'> <div className='l menu'>
<div className={cn('menu-header', { selected: openedMenu == 's' })} onClick={this._openShips}> <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> </div>
{openedMenu == 's' ? this._getShipsMenu() : null} {openedMenu == 's' ? this._getShipsMenu() : null}
</div> </div>
<div className='l menu'> <div className='l menu'>
<div className={cn('menu-header', { selected: openedMenu == 'b', disabled: !hasBuilds })} onClick={hasBuilds && this._openBuilds}> <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> </div>
{openedMenu == 'b' ? this._getBuildsMenu() : null} {openedMenu == 'b' ? this._getBuildsMenu() : null}
</div> </div>
<div className='l menu'> <div className='l menu'>
<div className={cn('menu-header', { selected: openedMenu == 'comp', disabled: !hasBuilds })} onClick={hasBuilds && this._openComp}> <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> </div>
{openedMenu == 'comp' ? this._getComparisonsMenu() : null} {openedMenu == 'comp' ? this._getComparisonsMenu() : null}
</div> </div>

View File

@@ -22,14 +22,14 @@ export default class InternalSlot extends Slot {
return <div className='details' draggable='true' onDragStart={drag} onDragEnd={drop}> return <div className='details' draggable='true' onDragStart={drag} onDragEnd={drop}>
<div className={'cb'}> <div className={'cb'}>
<div className={'l'}>{classRating + ' ' + translate(m.name || m.grp)}</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 className={'r'}>{m.mass || m.cargo || m.fuel || 0}{u.T}</div>
</div> </div>
<div className={'cb'}> <div className={'cb'}>
{ m.optmass ? <div className={'l'}>{translate('optimal mass') + ': '}{m.optmass}{u.T}</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.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.bins ? <div className={'l'}>{m.bins} <u>{translate('bins')}</u></div> : null }
{ m.bays ? <div className={'l'}>{translate('bays') + ': ' + m.bays}</div> : null } { m.bays ? <div className={'l'}>{translate('bays')}: {m.bays}</div> : null }
{ m.rate ? <div className={'l'}>{translate('rate')}: {m.rate}{u.kgs}&nbsp;&nbsp;&nbsp;{translate('refuel time')}: {formats.time(this.props.fuel * 1000 / m.rate)}</div> : null } { m.rate ? <div className={'l'}>{translate('rate')}: {m.rate}{u.kgs}&nbsp;&nbsp;&nbsp;{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.ammo ? <div className={'l'}>{translate('ammo')}: {formats.gen(m.ammo)}</div> : null }
{ m.cells ? <div className={'l'}>{translate('cells')}: {m.cells}</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.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.time ? <div className={'l'}>{translate('time')}: {formats.time(m.time)}</div> : null }
{ m.maximum ? <div className={'l'}>{translate('max')}: {(m.maximum)}</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 ? <div className={'l'}>{translate('range')}: {m.rangeLS}{u.Ls}</div> : null }
{ m.rangeLS === null ? <div className={'l'}><Infinite/>{u.Ls}</div> : null } { m.rangeLS === null ? <div className={'l'}><Infinite/>{u.Ls}</div> : null }
{ m.rangeRating ? <div className={'l'}>{translate('range')}: {m.rangeRating}</div> : null } { m.rangeRating ? <div className={'l'}>{translate('range')}: {m.rangeRating}</div> : null }
{ m.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>
</div>; </div>;
} else { } else {

View File

@@ -76,7 +76,7 @@ export default class InternalSlotSection extends SlotSection {
let clobber = event.getModifierState('Alt'); let clobber = event.getModifierState('Alt');
let ship = this.props.ship; let ship = this.props.ship;
ship.internal.forEach((slot) => { ship.internal.forEach((slot) => {
if (clobber || !slot.c) { if (clobber || !slot.m) {
ship.use(slot, ModuleUtils.findInternal('hr', Math.min(slot.maxClass, 5), 'D')); // Hull reinforcements top out at 5D ship.use(slot, ModuleUtils.findInternal('hr', Math.min(slot.maxClass, 5), 'D')); // Hull reinforcements top out at 5D
} }
}); });
@@ -138,6 +138,7 @@ export default class InternalSlotSection extends SlotSection {
<li className='lc' onClick={this._fillWithCargo}>{translate('cargo')}</li> <li className='lc' onClick={this._fillWithCargo}>{translate('cargo')}</li>
<li className='lc' onClick={this._fillWithCells}>{translate('scb')}</li> <li className='lc' onClick={this._fillWithCells}>{translate('scb')}</li>
<li className='lc' onClick={this._fillWithArmor}>{translate('hr')}</li> <li className='lc' onClick={this._fillWithArmor}>{translate('hr')}</li>
<li className='optional-hide' style={{ textAlign: 'center', marginTop: '1em' }}>{translate('PHRASE_ALT_ALL')}</li>
</ul> </ul>
</div>; </div>;
} }

View File

@@ -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})`}> <g className='x axis' ref={(elem) => d3.select(elem).call(this.xAxis)} transform={`translate(0,${innerHeight})`}>
<text className='cap' y='30' dy='.1em' x={innerWidth / 2} style={{ textAnchor: 'middle' }}> <text className='cap' y='30' dy='.1em' x={innerWidth / 2} style={{ textAnchor: 'middle' }}>
<tspan>{xLabel}</tspan> <tspan>{xLabel}</tspan>
<tspan className='metric'>{` (${xUnit})`}</tspan> <tspan className='metric'> ({xUnit})</tspan>
</text> </text>
</g> </g>
<g className='y axis' ref={(elem) => d3.select(elem).call(this.yAxis)}> <g className='y axis' ref={(elem) => d3.select(elem).call(this.yAxis)}>
<text className='cap' transform='rotate(-90)' y='-50' dy='.1em' x={innerHeight / -2} style={{ textAnchor: 'middle' }}> <text className='cap' transform='rotate(-90)' y='-50' dy='.1em' x={innerHeight / -2} style={{ textAnchor: 'middle' }}>
<tspan>{yLabel}</tspan> <tspan>{yLabel}</tspan>
<tspan className='metric'>{` (${yUnit})`}</tspan> <tspan className='metric'> ({yUnit})</tspan>
</text> </text>
</g> </g>
<g ref={(g) => this.tipContainer = d3.select(g)} style={{ display: 'none' }}> <g ref={(g) => this.tipContainer = d3.select(g)} style={{ display: 'none' }}>

View File

@@ -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} /> <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.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='-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.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.depY} className={getClass(depSelected, depSum, available)}>{f2(Math.max(0, depSum))} ({pct1(Math.max(0, depSum / available))})</text>
</g> </g>
</svg> </svg>
); );

View File

@@ -33,6 +33,7 @@ export default class StandardSlot extends TranslatedComponent {
if (this.props.selected) { if (this.props.selected) {
menu = <AvailableModulesMenu menu = <AvailableModulesMenu
className='standard'
modules={modules} modules={modules}
shipMass={ship.ladenMass} shipMass={ship.ladenMass}
m={m} m={m}
@@ -47,16 +48,17 @@ export default class StandardSlot extends TranslatedComponent {
<div className={cn('details-container', { warning: warning && warning(slot.m) })}> <div className={cn('details-container', { warning: warning && warning(slot.m) })}>
<div className={'sz'}>{slot.maxClass}</div> <div className={'sz'}>{slot.maxClass}</div>
<div> <div>
<div className='l'>{classRating + ' ' + translate(m.grp)}</div> <div className='l'>{classRating} {translate(m.grp == 'bh' ? m.grp : m.name || m.grp)}</div>
<div className={'r'}>{m.mass || m.fuel}{units.T}</div> <div className={'r'}>{m.mass || m.fuel || 0}{units.T}</div>
<div className={'cb'}> <div className={'cb'}>
{ m.optmass ? <div className='l'>{translate('optimal mass') + ': '}{m.optmass}{units.T}</div> : null } { m.grp == 'bh' && m.name ? <div className='l'>{translate(m.name)}</div> : null }
{ m.maxmass ? <div className='l'>{translate('max mass') + ': '}{m.maxmass}{units.T}</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.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.time ? <div className='l'>{translate('time')}: {formats.time(m.time)}</div> : null }
{ m.eff ? <div className='l'>{translate('efficiency')}: {m.eff}</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.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.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.systemcapacity ? <div className='l'>{translate('SYS')}: {m.systemcapacity}{units.MJ} / {m.systemrecharge}{units.MW}</div> : null }
{ m.enginecapacity ? <div className='l'>{translate('ENG')}: {m.enginecapacity}{units.MJ} / {m.enginerecharge}{units.MW}</div> : null } { m.enginecapacity ? <div className='l'>{translate('ENG')}: {m.enginecapacity}{units.MJ} / {m.enginerecharge}{units.MW}</div> : null }

View File

@@ -4,6 +4,7 @@ import SlotSection from './SlotSection';
import StandardSlot from './StandardSlot'; import StandardSlot from './StandardSlot';
import { diffDetails } from '../utils/SlotFunctions'; import { diffDetails } from '../utils/SlotFunctions';
import * as ModuleUtils from '../shipyard/ModuleUtils'; import * as ModuleUtils from '../shipyard/ModuleUtils';
import * as ShipRoles from '../shipyard/ShipRoles';
import { stopCtxPropagation } from '../utils/UtilityFunctions'; import { stopCtxPropagation } from '../utils/UtilityFunctions';
/** /**
@@ -18,21 +19,8 @@ export default class StandardSlotSection extends SlotSection {
*/ */
constructor(props, context) { constructor(props, context) {
super(props, context, 'standard', 'standard'); super(props, context, 'standard', 'standard');
this._optimizeStandard = this._optimizeStandard.bind(this); this._optimizeStandard = this._optimizeStandard.bind(this);
this._optimizeCargo = this._optimizeCargo.bind(this); this._selectBulkhead = this._selectBulkhead.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();
} }
/** /**
@@ -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() { _multiPurpose(shielded, bulkheadIndex) {
let ship = this.props.ship; ShipRoles.multiPurpose(this.props.ship, shielded, bulkheadIndex);
ship.internal.forEach((slot) => ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E')));
ship.useLightestStandard();
this.props.onChange(); this.props.onChange();
this._close(); this._close();
} }
/** /**
* Explorer build * Trader Build
* @param {Boolean} shielded True if shield generator should be included
*/ */
_optimizeExplorer() { _optimizeCargo(shielded) {
let ship = this.props.ship, ShipRoles.trader(this.props.ship, shielded);
intLength = ship.internal.length, this.props.onChange();
heatSinkCount = 2, // Fit 2 heat sinks if possible this._close();
afmUnitCount = 2, // Fit 2 AFM Units if possible }
sgSlot,
fuelScoopSlot,
sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass);
ship.setSlotEnabled(ship.cargoHatch, false) /**
.use(ship.internal[--intLength], ModuleUtils.internal('2f')) // Advanced Discovery Scanner * Explorer role
.use(ship.internal[--intLength], ModuleUtils.internal('2i')); // Detailed Surface Scanner * @param {Boolean} planetary True if Planetary Vehicle Hangar (PVH) should be included
*/
for (let i = 0; i < intLength; i++) { _optimizeExplorer(planetary) {
let slot = ship.internal[i]; ShipRoles.explorer(this.props.ship, planetary);
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' });
this.props.onChange(); this.props.onChange();
this._close(); this._close();
} }
/** /**
* Use the specified bulkhead * Use the specified bulkhead
* @param {number} bulkheadIndex 0 - 4 * @param {Object} bulkhead Bulkhead module details
*/ */
_selectBulkhead(bulkheadIndex) { _selectBulkhead(bulkhead) {
this.props.ship.useBulkhead(bulkheadIndex); this.props.ship.useBulkhead(bulkhead.index);
this.context.tooltip(); this.context.tooltip();
this.props.onChange(); this.props.onChange();
this._close(); this._close();
@@ -136,32 +81,12 @@ export default class StandardSlotSection extends SlotSection {
this._optimizeStandard(); 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 * Generate the slot React Components
* @return {Array} Array of Slots * @return {Array} Array of Slots
*/ */
_getSlots() { _getSlots() {
let { translate, units } = this.context.language; let { translate, units, formats } = this.context.language;
let { ship, currentMenu } = this.props; let { ship, currentMenu } = this.props;
let slots = new Array(8); let slots = new Array(8);
let open = this._openMenu; let open = this._openMenu;
@@ -171,41 +96,15 @@ export default class StandardSlotSection extends SlotSection {
let avail = ship.getAvailableModules().standard; let avail = ship.getAvailableModules().standard;
let bh = ship.bulkheads; let bh = ship.bulkheads;
slots[0] = ( slots[0] = <StandardSlot
<div key='bh' className={cn('slot', { selected: currentMenu === bh })} onClick={open.bind(this, bh)}> key='bh'
<div className={'details-container'}> slot={bh}
<div className={'details'}> modules={ship.getAvailableModules().bulkheads}
<div className={'sz'}>8</div> onOpen={open.bind(this, bh)}
<div> onSelect={this._selectBulkhead}
<div className={'l'}>{translate('bh')}</div> selected={currentMenu == bh}
<div className={'r'}>{bh.m.mass}{units.T}</div> ship={ship}
<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[1] = <StandardSlot slots[1] = <StandardSlot
key='pp' key='pp'
@@ -226,7 +125,7 @@ export default class StandardSlotSection extends SlotSection {
onSelect={select.bind(this, st[1])} onSelect={select.bind(this, st[1])}
selected={currentMenu == st[1]} selected={currentMenu == st[1]}
ship={ship} 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 * @return {React.Component} Section menu
*/ */
_getSectionMenu(translate) { _getSectionMenu(translate) {
let _fill = this._fill; let planetaryDisabled = this.props.ship.internal.length < 4;
return <div className='select' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}> return <div className='select' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
<ul> <ul>
<li className='lc' onClick={this._optimizeStandard}>{translate('optimize')}</li> <li className='lc' onClick={this._optimizeStandard}>{translate('Maximize Jump Range')}</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>
</ul> </ul>
<div className='select-group cap'>{translate('roles')}</div> <div className='select-group cap'>{translate('roles')}</div>
<ul> <ul>
<li className='lc' onClick={this._optimizeCargo}>{translate('Trader')}</li> <li className='lc' onClick={this._multiPurpose.bind(this, false, 0)}>{translate('Multi-purpose')}</li>
<li className='lc' onClick={this._optimizeExplorer}>{translate('Explorer')}</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> </ul>
</div>; </div>;
} }

View File

@@ -94,6 +94,7 @@ export default class UtilitySlotSection extends SlotSection {
return <div className='select' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}> return <div className='select' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
<ul> <ul>
<li className='lc' onClick={this._empty}>{translate('empty all')}</li> <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> </ul>
<div className='select-group cap'>{translate('sb')}</div> <div className='select-group cap'>{translate('sb')}</div>
<ul> <ul>

View File

@@ -52,18 +52,18 @@ export function getLanguage(langCode) {
}, },
translate, translate,
units: { units: {
CR: <u>{' ' + translate('CR')}</u>, // Credits CR: <u> {translate('CR')}</u>, // Credits
kg: <u>{' ' + translate('kg')}</u>, // Kilograms kg: <u> {translate('kg')}</u>, // Kilograms
kgs: <u>{' ' + translate('kg/s')}</u>, // Kilograms per second kgs: <u> {translate('kg/s')}</u>, // Kilograms per second
km: <u>{' ' + translate('km')}</u>, // Kilometers km: <u> {translate('km')}</u>, // Kilometers
Ls: <u>{' ' + translate('Ls')}</u>, // Light Seconds Ls: <u> {translate('Ls')}</u>, // Light Seconds
LY: <u>{' ' + translate('LY')}</u>, // Light Years LY: <u> {translate('LY')}</u>, // Light Years
MJ: <u>{' ' + translate('MJ')}</u>, // Mega Joules MJ: <u> {translate('MJ')}</u>, // Mega Joules
'm/s': <u>{' ' + translate('m/s')}</u>, // Meters per second 'm/s': <u> {translate('m/s')}</u>, // Meters per second
MW: <u>{' ' + translate('MW')}</u>, // Mega Watts (same as Mega Joules per second) MW: <u> {translate('MW')}</u>, // Mega Watts (same as Mega Joules per second)
ps: <u>{translate('/s')}</u>, // per second ps: <u>{translate('/s')}</u>, // per second
pm: <u>{translate('/min')}</u>, // per minute pm: <u>{translate('/min')}</u>, // per minute
T: <u>{' ' + translate('T')}</u>, // Metric Tons T: <u> {translate('T')}</u>, // Metric Tons
} }
}; };
} }

View File

@@ -15,76 +15,20 @@ export const formats = {
export const terms = { export const terms = {
// Phrases // 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_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_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_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_BUILDS: 'Keine Konfigurationen zum Vergleich ausgewählt!', // No builds added to comparison!
PHRASE_NO_RETROCH: 'Keine Umrüständerungen', // No Retrofitting changes PHRASE_NO_RETROCH: 'Keine Umrüständerungen', // No Retrofitting changes
PHRASE_SELECT_BUILDS: 'Ausstattung zum Vergleich auswählen', // Select Builds to Compare PHRASE_SELECT_BUILDS: 'Ausstattung zum Vergleich auswählen', // Select Builds to Compare
PHRASE_SG_RECHARGE: null, // Time from 50% to 100% Charge PHRASE_SG_RECHARGE: 'Zeit von 50% bis 100% der Ladung', // Time from 50% to 100% Charge
PHRASE_SG_RECOVER: null, // Recovery (to 50%) after collapse PHRASE_SG_RECOVER: 'Erneuerung (zu 50%) nach Zusammenbruch', // Recovery (to 50%) after collapse
PHRASE_UNLADEN: null, // Ship Mass excluding Fuel and Cargo 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 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 // Units / Metrics
LY: 'Lj', // Light Years LY: 'Lj', // Light Years
T: 't', // Tons (Metric Ton - 1000kg) T: 't', // Tons (Metric Ton - 1000kg)
@@ -94,109 +38,108 @@ export const terms = {
L: 'G', // Large Hardpoint size (single character) L: 'G', // Large Hardpoint size (single character)
H: 'R', // Huge Hardpoint size (single character) H: 'R', // Huge Hardpoint size (single character)
U: 'W', // Utility Hardpoint size (single character) - Kill warrant scanner, etc 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 // Terms
'build name': 'Ausstattungsname', // Ship build/configuration/design name about: 'über', // Link to about page / about Coriolis.io
'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
action: 'Aktion', action: 'Aktion',
added: 'hinzugefügt', added: 'hinzugefügt',
ammo: 'Munition', // Ammunition ammo: 'Munition', // Ammunition
armour: 'Panzerung', armour: 'Panzerung',
available: 'verfügbar', available: 'verfügbar', // Available options
backup: 'Sicherungsdatei', backup: 'Sicherungsdatei',
base: null, base: 'Basis', // Base speed, boost, etc - Base ship stats
bays: null, bays: 'Lagerraum',
bins: 'Behälter', // Number of Mining Refinery bins bins: 'Behälter', // Number of Mining Refinery bins
build: 'Ausstattung', // Shorthand for the build/configuration/design name build: 'Ausstattung', // Shorthand for the build/configuration/design name
'build name': 'Ausstattungsname', // Ship build/configuration/design name
builds: 'Ausstattungen', // Ship build/configuration/design names builds: 'Ausstattungen', // Ship build/configuration/design names
buy: 'kaufen', buy: 'kaufen',
cancel: 'Abbrechen', cancel: 'abbrechen',
cargo: 'Fracht', cargo: 'Fracht',
cells: 'Zellen', // Number of cells in a shield cell bank cells: 'Zellen', // Number of cells in a shield cell bank
close: 'Schließen', close: 'schließen',
compare: 'vergleichen', compare: 'vergleichen',
'compare all': 'alles vergleichen',
comparison: 'Vergleich', comparison: 'Vergleich',
comparisons: 'Vergleiche', comparisons: 'Vergleiche',
cost: 'Preis', // Cost / price of a module or price of a ship cost: 'Preis', // Cost / price of a module or price of a ship
costs: 'Kosten', // Costs / prices of a modules or prices of ships costs: 'Kosten', // Costs / prices of a modules or prices of ships
create: 'erstellen', create: 'erstellen',
'create new': 'neu erstellen',
credits: 'Credits', credits: 'Credits',
damage: 'Schaden', damage: 'Schaden',
delete: 'Löschen', 'damage per second': 'Schaden pro Sekunde',
dep: 'Ausg', // Weapons/Hardpoints Deployed abbreviation delete: 'löschen',
deployed: 'Ausgefahren', // Weapons/Hardpoints Deployed 'delete all': 'alles löschen',
disabled: 'Deaktiviert', dep: 'ausg', // Weapons/Hardpoints Deployed abbreviation
deployed: 'ausgefahren', // Weapons/Hardpoints Deployed
'detailed export': 'detailierter Export',
disabled: 'deaktiviert',
discount: 'Rabatt', discount: 'Rabatt',
'edit data': 'bearbeiten',
efficiency: 'Effizienz', // Power Plant efficiency efficiency: 'Effizienz', // Power Plant efficiency
empty: 'leer', empty: 'leer',
'empty all': 'alles entfernen',
ENG: 'ANT', // Abbreviation - Engine recharge rate for power distributor ENG: 'ANT', // Abbreviation - Engine recharge rate for power distributor
'Enter Name': 'Namen eingeben',
Explorer: 'Forscher', Explorer: 'Forscher',
export: 'Export',
'fastest range': 'maximale Reichweite', // Fastet totaljump range - sum of succesive jumps
forum: 'Forum', forum: 'Forum',
fuel: 'Treibstoff', fuel: 'Treibstoff',
'fuel level': 'Tankfüllstand', // Percent of fuel (T) in the tank
'full tank': 'Tank voll',
hardpoints: 'Waffenaufhängungen', hardpoints: 'Waffenaufhängungen',
hull: 'Hülle', // Ships hull hull: 'Rumpf', // Ships hull
import: 'importieren', import: 'importieren',
insurance: 'Versicherung', insurance: 'Versicherung',
jump: null, // Single jump range 'internal compartments': 'Innenbereich',
jump: 'Sprung', // Single jump range
'jump range': 'Sprungreichweite',
jumps: 'Sprünge', jumps: 'Sprünge',
laden: 'beladen', laden: 'beladen',
language: 'Sprache', language: 'Sprache',
maneuverability: 'Manövrierbarkeit', maneuverability: 'Manövrierbarkeit',
manufacturer: null, manufacturer: 'Hersteller',
mass: 'Masse', mass: 'Masse',
MLF: null, // Mass Lock Factor Abbreviation 'mass lock factor': 'Massensperrefaktor',
MNV: null, // Maneuverability abbreviation 'max mass': 'maximale Masse',
module: 'modul', MLF: 'MSF', // Mass Lock Factor Abbreviation
modules: 'module', module: 'Modul',
modules: 'Module',
'net cost': 'Nettokosten',
no: 'Nein', no: 'Nein',
'none created': 'nicht erstellt',
ok: 'OK', ok: 'OK',
optimize: null, 'optimal mass': 'optimale Masse', // Lowest weight / best weight for jump distance, etc
optimize: 'optimieren',
pen: 'Durchdr.', // Armour peneration abbreviation 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 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 pri: 'Prio', // Priority abbreviation for power management
proceed: 'Fortfahren', proceed: 'fortfahren',
PWR: 'En', // Power Abbreviation. See Power qty: 'Menge', // Quantity abbreviation
qty: null, // Quantity abbreviation
range: 'Reichweite', range: 'Reichweite',
rate: 'Rate', rate: 'Rate',
recharge: null, // Shield Recharge time from 50% -> 100% recharge: 'aufladen', // Shield Recharge time from 50% -> 100%
recovery: null, // Shield recovery time (after losing shields/turning on -> 50%) 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: 'aktualisieren', // Reload weapon/hardpoint
'reload costs': 'Nachladekosten',
rename: 'umbenennen', rename: 'umbenennen',
repair: 'reparieren', repair: 'reparieren',
reset: 'zurücksetzen', reset: 'zurücksetzen',
ret: 'Eing', // Retracted abbreviation ret: 'eing', // Retracted abbreviation
retracted: 'Eingefahren', // Weapons/Hardpoints retracted 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 ROF: 'Kad', // Rate of Fire abbreviation
roles: null, // Commander/Ship build roles - e.g. Trader, Bounty-Hunter, Explorer, etc roles: 'Rollen', // Commander/Ship build roles - e.g. Trader, Bounty-Hunter, Explorer, etc
save: 'Speichern', save: 'speichern',
sell: 'Verkaufen', sell: 'verkaufen',
settings: 'Einstellungen', // Coriolis application settings settings: 'Einstellungen', // Coriolis application settings
shields: 'Schilde', shields: 'Schilde',
ship: 'Schiff', ship: 'Schiff',
@@ -205,16 +148,19 @@ export const terms = {
size: 'Größe', size: 'Größe',
skip: 'überspringen', // Skip past something / ignore it skip: 'überspringen', // Skip past something / ignore it
speed: 'Geschwindigkeit', 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 Stock: 'Standard', // Thermal-load abbreviation
strength: null, // Strength in reference to Shield Strength strength: 'Stärke', // Strength in reference to Shield Strength
subtotal: null, subtotal: 'Zwischensumme',
time: 'Dauer', // time it takes to complete something time: 'Dauer', // time it takes to complete something
tooltips: null, // Tooltips setting - show/hide tooltips: 'Tooltips', // Tooltips setting - show/hide
total: 'Gesamt', total: 'gesamt',
Trader: null, // Trader role 'total range': 'Gesamtbereich',
Trader: 'Händler', // Trader role
type: 'Typ', 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 WEP: 'WAF', // Abbreviation - Weapon recharge rate for power distributor
yes: 'Ja' yes: 'ja'
}; };

View File

@@ -14,20 +14,22 @@ export const formats = {
}; };
export const terms = { 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_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_EXPORT_DESC: 'A detailed JSON export of your build for use in other sites and tools',
PHRASE_FASTEST_RANGE: 'Consecutive max range jumps', PHRASE_FASTEST_RANGE: 'Consecutive max range jumps',
PHRASE_IMPORT: 'Paste JSON or import here', 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_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_SELECT_BUILDS: 'Select builds to compare',
PHRASE_SG_RECHARGE: 'Time from 50% to 100% Charge', PHRASE_SG_RECHARGE: 'Time from 50% to 100% charge',
PHRASE_SG_RECOVER: 'Recovery (to 50%) after collapse', PHRASE_SG_RECOVER: 'Recovery (to 50%) after collapse',
PHRASE_UNLADEN: 'Ship Mass excluding Fuel and Cargo', PHRASE_UNLADEN: 'Ship mass excluding fuel and cargo',
PHRASE_UPDATE_RDY: 'Update Available! Click to Refresh', PHRASE_UPDATE_RDY: 'Update Available! Click to refresh',
// Other languages fallback to these values
// Only Translate to other languages if the name is different in-game // Only Translate to other languages if the name is different in-game
am: 'Auto Field-Maintenance Unit', am: 'Auto Field-Maintenance Unit',
bh: 'Bulkheads', bh: 'Bulkheads',
@@ -60,7 +62,7 @@ export const terms = {
pl: 'Pulse Laser', pl: 'Pulse Laser',
pp: 'Power Plant', pp: 'Power Plant',
psg: 'Prismatic Shield Generator', psg: 'Prismatic Shield Generator',
pv: 'Planetary Vehicle Hanger', pv: 'Planetary Vehicle Hangar',
rf: 'Refinery', rf: 'Refinery',
rg: 'Rail Gun', rg: 'Rail Gun',
s: 'Sensors', s: 'Sensors',

View File

@@ -15,119 +15,42 @@ export const formats = {
export const terms = { export const terms = {
// Phrases // 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_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_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_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_LADEN: 'Masse du Vaisseau + Carburant + Cargo', // Ship Mass + Fuel + Cargo
PHRASE_NO_RETROCH: 'configuration non modifiée', // No Retrofitting changes PHRASE_NO_BUILDS: 'Aucune configuration ajoutée pour la comparaison', // No builds added to comparison!
PHRASE_SELECT_BUILDS: 'Sélectionner configurations à comparer', // Select Builds to Compare 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 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 // Units / Metrics
Ls: 'SL', // Light seconds Ls: 'SL', // Light seconds
LY: 'AL', // Light Years LY: 'AL', // Light Years
// Sizes // Sizes
S: 'P', // Small Hardpoint (single Character)
L: 'G', // Large Hardpoint size (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 // 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 about: 'à propos', // Link to about page / about Coriolis.io
added: 'ajouté', added: 'ajouté',
ammo: 'munition', // Ammunition ammo: 'munition', // Ammunition
armour: 'Armure', armour: 'Armure',
available: 'Disponibilité', available: 'Disponibilité', // Available options
backup: 'sauvegarde', backup: 'sauvegarde',
base: null, bays: 'baies',
bays: null,
bins: 'bacs', // Number of Mining Refinery bins bins: 'bacs', // Number of Mining Refinery bins
build: 'Configuration', // Shorthand for the build/configuration/design name 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 builds: 'Configurations', // Ship build/configuration/design names
buy: 'Acheter', buy: 'Acheter',
cancel: 'Annuler', cancel: 'Annuler',
@@ -135,58 +58,77 @@ export const terms = {
cells: 'Cellule', // Number of cells in a shield cell bank cells: 'Cellule', // Number of cells in a shield cell bank
close: 'fermer', close: 'fermer',
compare: 'comparer', compare: 'comparer',
'compare all': 'tout comparer',
comparison: 'comparaison', comparison: 'comparaison',
comparisons: 'comparaisons', comparisons: 'comparaisons',
cost: 'coût', // Cost / price of a module or price of a ship 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 costs: 'coûts', // Costs / prices of a modules or prices of ships
create: 'Créer', create: 'Créer',
'create new': 'Créer nouveau',
credits: 'crédits', credits: 'crédits',
damage: 'Dégâts', damage: 'dégât',
'damage per second': 'dégât par seconde',
delete: 'supprimer', delete: 'supprimer',
'delete all': 'tout supprimer',
dep: 'depl', // Weapons/Hardpoints Deployed abbreviation dep: 'depl', // Weapons/Hardpoints Deployed abbreviation
deployed: 'déployé', // Weapons/Hardpoints Deployed deployed: 'déployé', // Weapons/Hardpoints Deployed
'detailed export': 'export détaillé',
disabled: 'désactivé', disabled: 'désactivé',
discount: 'ristourne', discount: 'ristourne',
'edit data': 'Editer donnée',
efficiency: 'rendement', // Power Plant efficiency efficiency: 'rendement', // Power Plant efficiency
empty: 'Vide', 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: 'carburant',
'fuel level': 'niveau de carburant', // Percent of fuel (T) in the tank
'full tank': 'Réservoir plein',
hardpoints: 'Points d\'emport', hardpoints: 'Points d\'emport',
hull: 'Coque', // Ships hull hull: 'Coque', // Ships hull
import: 'Importer', import: 'Importer',
insurance: 'Assurance', insurance: 'Assurance',
jump: null, // Single jump range 'internal compartments': 'compartiments internes',
jump: 'saut', // Single jump range
'jump range': 'Distance de saut',
jumps: 'Sauts', jumps: 'Sauts',
laden: 'chargé', laden: 'chargé',
language: 'Langage', language: 'Langage',
maneuverability: null, maneuverability: 'maniabilité',
manufacturer: null, manufacturer: 'fabricant',
mass: 'Masse', mass: 'Masse',
MLF: null, // Mass Lock Factor Abbreviation 'mass lock factor': 'facteur inhibition de masse',
MNV: null, // Maneuverability abbreviation 'max mass': 'masse max',
module: null, MLF: 'FIM', // Mass Lock Factor Abbreviation
modules: null, 'net cost': 'coûts nets',
no: 'non', no: 'non',
'none created': 'Rien de créé',
ok: 'D\'accord', 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 pen: 'pén.', // Armour peneration abbreviation
permalink: 'lien durable', 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 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', proceed: 'continuer',
PWR: 'P', // Power Abbreviation. See Power PWR: 'P', // Power Abbreviation. See Power
qty: null, // Quantity abbreviation qty: 'quantité', // Quantity abbreviation
range: 'portée', range: 'portée',
rate: 'cadence', rate: 'cadence',
recharge: null, // Shield Recharge time from 50% -> 100% recharge: 'recharger', // Shield Recharge time from 50% -> 100%
recovery: null, // Shield recovery time (after losing shields/turning on -> 50%) 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: 'recharger', // Reload weapon/hardpoint
'reload costs': 'recharger coûts',
rename: 'renommer', rename: 'renommer',
repair: 'réparer', repair: 'réparer',
reset: 'Réinitialisation', reset: 'Réinitialisation',
ret: 'esc', // Retracted abbreviation ret: 'esc', // Retracted abbreviation
retracted: 'escamoté', // Weapons/Hardpoints retracted 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 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', save: 'sauvegarder',
sell: 'vendre', sell: 'vendre',
settings: 'paramètres', // Coriolis application settings settings: 'paramètres', // Coriolis application settings
@@ -198,12 +140,16 @@ export const terms = {
skip: 'Suivant', // Skip past something / ignore it skip: 'Suivant', // Skip past something / ignore it
speed: 'vitesse', speed: 'vitesse',
Stock: 'de base', // Thermal-load abbreviation Stock: 'de base', // Thermal-load abbreviation
strength: null, // Strength in reference to Shield Strength strength: 'force', // Strength in reference to Shield Strength
subtotal: null, subtotal: 'Sous-Total',
'T-Load': 'degrés', // Thermal load abbreviation
time: 'temps', // time it takes to complete something time: 'temps', // time it takes to complete something
tooltips: null, // Tooltips setting - show/hide tooltips: 'infobulles', // Tooltips setting - show/hide
Trader: null, // Trader role 'total range': 'plage totale',
Trader: 'commerçant', // Trader role
'unit cost': 'coût unitaire',
unladen: 'Non chargé', // No cargo or fuel unladen: 'Non chargé', // No cargo or fuel
'utility mounts': 'Support utilitaire',
WEP: 'ARM', // Abbreviation - Weapon recharge rate for power distributor WEP: 'ARM', // Abbreviation - Weapon recharge rate for power distributor
yes: 'oui' yes: 'oui'
}; };

View File

@@ -18,90 +18,22 @@ export const terms = {
PHRASE_BACKUP_DESC: 'Сохраните все данные перед переносом в другой браузер или устройство', // Backup of all Coriolis data to save or transfer to another browser/device 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: 'Детальный JSON-экспорт вашей сборки для использования в других местах и инструментах', // A detailed JSON export of your build for use in other sites and tools 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_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_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_SELECT_BUILDS: 'Выберите конфигурацию для сравнения', // Select Builds to Compare
PHRASE_SG_RECHARGE: null, // Time from 50% to 100% Charge PHRASE_SG_RECHARGE: 'восстановление с 60% до 100% объема щита', // Time from 50% to 100% Charge
PHRASE_SG_RECOVER: null, // Recovery (to 50%) after collapse PHRASE_SG_RECOVER: 'восстановление [до 60%] после падения\сбития', // Recovery (to 50%) after collapse
PHRASE_UNLADEN: null, // Ship Mass excluding Fuel and Cargo PHRASE_UNLADEN: 'Судно Масса без учета топлива и грузов', // Ship Mass excluding Fuel and Cargo
PHRASE_UPDATE_RDY: 'Доступно обновление. Нажмите для обновления.', // Update Available! Click to Refresh PHRASE_UPDATE_RDY: 'Доступно обновление. Нажмите для обновления.', // Update Available! Click to Refresh
// 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 // Units / Metrics
'/min': null, // Per minute '/s': '/с', // Per second
'/s': null, // Per second
kg: null, // Kilogram
'kg/s': null, // Kilograms per second
km: null, // Kilometer
'm/s': 'м/с', // Meters / Second 'm/s': 'м/с', // Meters / Second
Ls: 'Св.сек', // Light seconds Ls: 'Св.сек', // Light seconds
LY: 'Св.лет', // Light Years LY: 'Св.лет', // Light Years
MJ: null, // Mega Joules
MW: null, // Mega Watts
CR: 'кр.', // Credits abbreviation CR: 'кр.', // Credits abbreviation
// Sizes // Sizes
@@ -110,24 +42,25 @@ export const terms = {
L: 'б', // Large Hardpoint size (single character) L: 'б', // Large Hardpoint size (single character)
H: 'O', // Huge Hardpoint size (single character) H: 'O', // Huge Hardpoint size (single character)
U: 'B', // Utility Hardpoint size (single character) - Kill warrant scanner, etc U: 'B', // Utility Hardpoint size (single character) - Kill warrant scanner, etc
small: 'Малый', // Small ship size
medium: 'Средний', // Medium ship size
large: 'большой', // Large Ship Size
// Insurance // Insurance
alpha: 'Альфа', // Alpha backer insurance level alpha: 'Альфа', // Alpha backer insurance level
beta: 'Бета', // Beta back insurance level beta: 'Бета', // Beta back insurance level
standard: 'Стандартный', // Standard insurance level standard: 'Стандартный', // Standard insurance level
// Terms // Terms
'build name': 'название сборки', // Ship build/configuration/design name 'build name': 'название сборки', // Ship build/configuration/design name
'compare all': 'сравнить все', 'compare all': 'сравнить все',
'create new': 'Создать новый', 'create new': 'Создать новый',
'damage per second': null, 'damage per second': 'урон в секунду',
'delete all': 'Удалить все', 'delete all': 'Удалить все',
'detailed export': 'Подробный экспорт', 'detailed export': 'Подробный экспорт',
'edit data': 'Редактирование', 'edit data': 'Редактирование',
'empty all': null, 'empty all': 'пусто все',
'Enter Name': 'Введите имя', 'Enter Name': 'Введите имя',
'fastest range': null, // Fastet totaljump range - sum of succesive jumps 'fastest range': 'быстрый диапазон', // Fastet totaljump range - sum of succesive jumps
'fuel level': null, // Percent of fuel (T) in the tank 'fuel level': 'уровень топлива', // Percent of fuel (T) in the tank
'full tank': 'Полный бак', 'full tank': 'Полный бак',
'internal compartments': 'внутренние отсеки', 'internal compartments': 'внутренние отсеки',
'jump range': 'Дальность прыжка', 'jump range': 'Дальность прыжка',
@@ -135,24 +68,18 @@ export const terms = {
'max mass': 'Максимальная масса', 'max mass': 'Максимальная масса',
'net cost': 'разница в цене', 'net cost': 'разница в цене',
'none created': 'не создано', 'none created': 'не создано',
'optimal mass': null, // Lowest weight / best weight for jump distance, etc
'refuel time': 'Время дозаправки', // Time to refuel the tank when scooping 'refuel time': 'Время дозаправки', // Time to refuel the tank when scooping
'reload costs': null,
'retrofit costs': 'цена модификации', // The cost difference when upgrading / downgrading a component 'retrofit costs': 'цена модификации', // The cost difference when upgrading / downgrading a component
'retrofit from': 'модификация от', // Retrofit from Build A against build B 'retrofit from': 'модификация от', // Retrofit from Build A against build B
'T-Load': 'Тепл.', // Thermal load abbreviation 'T-Load': 'Тепл.', // Thermal load abbreviation
'total range': null,
'unit cost': null,
'utility mounts': 'Вспомогательное оборудование', 'utility mounts': 'Вспомогательное оборудование',
about: 'О ...', // Link to about page / about Coriolis.io about: 'О ...', // Link to about page / about Coriolis.io
action: 'Действие', action: 'Действие',
added: 'Добавлено', added: 'Добавлено',
ammo: 'Боекомплект', // Ammunition ammo: 'Боекомплект', // Ammunition
armour: 'Броня', armour: 'Броня',
available: 'доступно', available: 'доступно', // Available options
backup: 'Резервная копия', backup: 'Резервная копия',
base: null,
bays: null,
bins: 'контейнеры', // Number of Mining Refinery bins bins: 'контейнеры', // Number of Mining Refinery bins
boost: 'форсаж', boost: 'форсаж',
build: 'cборка', // Shorthand for the build/configuration/design name build: 'cборка', // Shorthand for the build/configuration/design name
@@ -179,7 +106,6 @@ export const terms = {
efficiency: 'Эффективность', // Power Plant efficiency efficiency: 'Эффективность', // Power Plant efficiency
empty: 'пусто', empty: 'пусто',
ENG: 'ДВГ', // Abbreviation - Engine recharge rate for power distributor ENG: 'ДВГ', // Abbreviation - Engine recharge rate for power distributor
Explorer: null,
export: 'Экспорт', export: 'Экспорт',
forum: 'Форум', forum: 'Форум',
fuel: 'Топливо', fuel: 'Топливо',
@@ -187,32 +113,26 @@ export const terms = {
hull: 'Корпус', // Ships hull hull: 'Корпус', // Ships hull
import: 'импортировать ', import: 'импортировать ',
insurance: 'Страховка', insurance: 'Страховка',
jump: null, // Single jump range
jumps: 'Прыжков', jumps: 'Прыжков',
laden: 'Груженый', laden: 'Груженый',
language: 'Язык', language: 'Язык',
maneuverability: 'Моневренность', maneuverability: 'Моневренность',
manufacturer: null,
mass: 'Масса', mass: 'Масса',
max: 'Макс', max: 'Макс',
MLF: null, // Mass Lock Factor Abbreviation
MNV: null, // Maneuverability abbreviation
module: null,
modules: null,
no: 'Нет', no: 'Нет',
ok: null,
optimize: null,
pen: 'ПБ', // Armour peneration abbreviation pen: 'ПБ', // Armour peneration abbreviation
permalink: 'Постоянная ссылка', permalink: 'Постоянная ссылка',
power: 'Мощность', // Power = Energy / second. Power generated by the power plant, or power consumed (MW / Mega Watts). Used in the power plant section 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 pri: 'Осн', // Priority abbreviation for power management
proceed: 'продолжить', proceed: 'продолжить',
PWR: 'Эн', // Power Abbreviation. See Power PWR: 'Эн', // Power Abbreviation. See Power
qty: null, // Quantity abbreviation
range: 'Дальность', range: 'Дальность',
rate: 'скорость', rate: 'скорость',
recharge: null, // Shield Recharge time from 50% -> 100% recharge: 'перезарядка', // Shield Recharge time from 50% -> 100%
recovery: null, // Shield recovery time (after losing shields/turning on -> 50%) recovery: 'включение', // Shield recovery time (after losing shields/turning on -> 50%)
reload: 'Перезагрузить', // Reload weapon/hardpoint reload: 'Перезагрузить', // Reload weapon/hardpoint
rename: 'Переименовать', rename: 'Переименовать',
repair: 'Починка', repair: 'Починка',
@@ -220,7 +140,7 @@ export const terms = {
ret: 'Убр.', // Retracted abbreviation ret: 'Убр.', // Retracted abbreviation
retracted: 'Убрано', // Weapons/Hardpoints retracted retracted: 'Убрано', // Weapons/Hardpoints retracted
ROF: 'В/сек', // Rate of Fire abbreviation ROF: 'В/сек', // Rate of Fire abbreviation
roles: null, // Commander/Ship build roles - e.g. Trader, Bounty-Hunter, Explorer, etc
save: 'Сохранить', save: 'Сохранить',
sell: 'Продать', sell: 'Продать',
settings: 'Настройки', // Coriolis application settings settings: 'Настройки', // Coriolis application settings
@@ -233,13 +153,9 @@ export const terms = {
speed: 'скорость', speed: 'скорость',
standard: 'Стандартный', // Standard / Common modules (FSD, power plant, life support, etc) standard: 'Стандартный', // Standard / Common modules (FSD, power plant, life support, etc)
Stock: 'Стандартная комплектация', // Thermal-load abbreviation Stock: 'Стандартная комплектация', // Thermal-load abbreviation
strength: null, // Strength in reference to Shield Strength
subtotal: null,
SYS: 'СИСТЕМЫ', // Abbreviation - System recharge rate for power distributor SYS: 'СИСТЕМЫ', // Abbreviation - System recharge rate for power distributor
time: 'Время', // time it takes to complete something time: 'Время', // time it takes to complete something
tooltips: null, // Tooltips setting - show/hide
total: 'Всего', total: 'Всего',
Trader: null, // Trader role
type: 'Тип', type: 'Тип',
unladen: 'Пустой', // No cargo or fuel unladen: 'Пустой', // No cargo or fuel
URL: 'Ссылка', // Link, Uniform Resource Locator URL: 'Ссылка', // Link, Uniform Resource Locator

View File

@@ -53,6 +53,7 @@ export default class ComparisonPage extends Page {
super(props, context); super(props, context);
this._sortShips = this._sortShips.bind(this); this._sortShips = this._sortShips.bind(this);
this._buildsSelected = this._buildsSelected.bind(this); this._buildsSelected = this._buildsSelected.bind(this);
this._updateDiscounts = this._updateDiscounts.bind(this);
this.state = this._initState(context); this.state = this._initState(context);
} }
@@ -161,6 +162,7 @@ export default class ComparisonPage extends Page {
let b = new Ship(id, data.properties, data.slots); // Create a new Ship instance let b = new Ship(id, data.properties, data.slots); // Create a new Ship instance
b.buildFrom(code); // Populate components from code b.buildFrom(code); // Populate components from code
b.buildName = name; b.buildName = name;
b.applyDiscounts(Persist.getShipDiscount(), Persist.getModuleDiscount());
return b; 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 * Update state based on context changes
* @param {Object} nextProps Incoming/Next properties * @param {Object} nextProps Incoming/Next properties
@@ -399,6 +416,7 @@ export default class ComparisonPage extends Page {
*/ */
componentWillMount() { componentWillMount() {
this.resizeListener = this.context.onWindowResize(this._updateDimensions); 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() { componentWillUnmount() {
this.resizeListener.remove(); this.resizeListener.remove();
this.persistListener.remove();
} }
/** /**

View File

@@ -8,17 +8,18 @@ import { shallowEqual } from '../utils/UtilityFunctions';
export default class Page extends React.Component { export default class Page extends React.Component {
static contextTypes = { 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, closeMenu: React.PropTypes.func.isRequired,
showModal: React.PropTypes.func.isRequired,
hideModal: React.PropTypes.func.isRequired, hideModal: React.PropTypes.func.isRequired,
tooltip: React.PropTypes.func.isRequired, language: React.PropTypes.object.isRequired,
termtip: React.PropTypes.func.isRequired, noTouch: React.PropTypes.bool.isRequired,
onCommand: React.PropTypes.func.isRequired,
onWindowResize: 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 = { static propTypes = {

View File

@@ -27,6 +27,38 @@ function countInt(slot) {
this.maxCargo += crEligible ? ModuleUtils.findInternal('cr', slot.maxClass, 'E').cargo : 0; 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 * The Shipyard summary page
*/ */
@@ -41,20 +73,30 @@ export default class ShipyardPage extends Page {
*/ */
constructor(props, context) { constructor(props, context) {
super(props, context); super(props, context);
this.state = {
title: 'Coriolis - Shipyard',
shipPredicate: 'name',
shipDesc: true
};
if (!ShipyardPage.cachedShipSummaries) { if (!ShipyardPage.cachedShipSummaries) {
ShipyardPage.cachedShipSummaries = []; ShipyardPage.cachedShipSummaries = [];
for (let s in Ships) { 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 - 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 }); 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 * Generate the table row summary for the ship
* @param {Object} s Ship summary * @param {Object} s Ship summary
@@ -118,8 +128,14 @@ export default class ShipyardPage extends Page {
* @return {React.Component} Table Row * @return {React.Component} Table Row
*/ */
_shipRowElement(s, translate, u, fInt, fRound) { _shipRowElement(s, translate, u, fInt, fRound) {
return <tr key={s.id} className='highlight'> let noTouch = this.context.noTouch;
<td className='le'><Link href={'/outfit/' + s.id}>{s.name}</Link></td>
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='le'>{s.manufacturer}</td>
<td className='cap'>{translate(SizeMap[s.class])}</td> <td className='cap'>{translate(SizeMap[s.class])}</td>
<td>{s.agility}</td> <td>{s.agility}</td>
@@ -155,17 +171,27 @@ export default class ShipyardPage extends Page {
* @return {React.Component} The page contents * @return {React.Component} The page contents
*/ */
renderPage() { 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 fInt = formats.int;
let fRound = formats.round; let fRound = formats.round;
let shipSummaries = this.shipSummaries; let { shipSummaries, shipPredicate, shipPredicateIndex } = this.state;
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 sortShips = (predicate, index) => this._sortShips.bind(this, predicate, index); 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 // Sort shipsOverview
shipSummaries.sort((a, b) => { shipSummaries.sort((a, b) => {
let valA = a[shipPredicate], valB = b[shipPredicate]; let valA = a[shipPredicate], valB = b[shipPredicate];
@@ -194,26 +220,51 @@ 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) { 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 ( return (
<div className='page' style={{ fontSize: this.context.sizeRatio + 'em' }}> <div className='page' style={{ fontSize: sizeRatio + 'em' }}>
<div className='scroll-x'> <div style={{ whiteSpace: 'nowrap', margin: '0 auto', fontSize: '0.8em', position: 'relative', display: 'inline-block', maxWidth: '100%' }}>
<table style={{ fontSize:'0.85em', whiteSpace:'nowrap', margin: '0 auto' }} align='center'> <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> <thead>
<tr className='main'> <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('manufacturer')}>{translate('manufacturer')}</th>
<th rowSpan={2} className='sortable' onClick={sortShips('class')}>{translate('size')}</th> <th rowSpan={2} className='sortable' onClick={sortShips('class')}>{translate('size')}</th>
<th rowSpan={2} className='sortable' onMouseEnter={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('base')}</th>
<th colSpan={4}>{translate('max')}</th> <th colSpan={4}>{translate('max')}</th>
<th colSpan={5} className='sortable' onClick={sortShips('hpCount')}>{translate('hardpoints')}</th> <th colSpan={5} className='sortable' onClick={sortShips('hpCount')}>{translate('hardpoints')}</th>
<th colSpan={8} className='sortable' onClick={sortShips('intCount')}>{translate('internal compartments')}</th> <th colSpan={8} className='sortable' onClick={sortShips('intCount')}>{translate('internal compartments')}</th>
<th rowSpan={2} className='sortable' onClick={sortShips('hullMass')}>{translate('hull')}</th> <th rowSpan={2} className='sortable' onClick={sortShips('hullMass')}>{translate('hull')}</th>
<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> <th rowSpan={2} className='sortable' onClick={sortShips('retailCost')}>{translate('cost')}</th>
</tr> </tr>
<tr> <tr>
@@ -243,10 +294,11 @@ export default class ShipyardPage extends Page {
<th className='sortable' onClick={sortShips('int', 7)} >8</th> <th className='sortable' onClick={sortShips('int', 7)} >8</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody onMouseLeave={this._highlightShip.bind(this, null)}>
{shipRows} {detailRows}
</tbody> </tbody>
</table> </table>
</div>
</div> </div>
</div> </div>
); );

View File

@@ -43,7 +43,7 @@ export const ModuleGroupToName = {
rf: 'Refinery', rf: 'Refinery',
scb: 'Shield Cell Bank', scb: 'Shield Cell Bank',
sg: 'Shield Generator', sg: 'Shield Generator',
pv: 'Planetary Vehicle Hanger', pv: 'Planetary Vehicle Hangar',
psg: 'Prismatic Shield Generator', psg: 'Prismatic Shield Generator',
dc: 'Docking Computer', dc: 'Docking Computer',
fx: 'Fuel Transfer Limpet Controller', fx: 'Fuel Transfer Limpet Controller',

View File

@@ -1,4 +1,6 @@
import { BulkheadNames } from './Constants';
/** /**
* Filter eligble modules based on parameters * Filter eligble modules based on parameters
* @param {Array} arr Available modules array * @param {Array} arr Available modules array
@@ -19,12 +21,13 @@ export default class ModuleSet {
/** /**
* Instantiate the module set * Instantiate the module set
* @param {Object} modules All Modules * @param {Object} modules All Modules
* @param {number} mass Ship mass * @param {Object} shipData Ship Specifications Data (see coriolis-data/Ships)
* @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
*/ */
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; let stnd = modules.standard;
this.mass = mass; this.mass = mass;
this.standard = {}; this.standard = {};
@@ -33,6 +36,10 @@ export default class ModuleSet {
this.hpClass = {}; this.hpClass = {};
this.intClass = {}; 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[0] = filter(stnd.pp, maxStandardArr[0], 0, mass); // Power Plant
this.standard[2] = filter(stnd.fsd, maxStandardArr[2], 0, mass); // FSD this.standard[2] = filter(stnd.fsd, maxStandardArr[2], 0, mass); // FSD
this.standard[4] = filter(stnd.pd, maxStandardArr[4], 0, mass); // Power Distributor this.standard[4] = filter(stnd.pd, maxStandardArr[4], 0, mass); // Power Distributor
@@ -53,6 +60,15 @@ 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 * Determine the modules that areeligible for an internal slot
* @param {integer} c The max class module that can be mounted in the slot * @param {integer} c The max class module that can be mounted in the slot
@@ -135,7 +151,7 @@ export default class ModuleSet {
let sg = this.internal.sg[0]; let sg = this.internal.sg[0];
for (let s of this.internal.sg) { 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; sg = s;
} }
} }

View File

@@ -29,7 +29,7 @@ export function standard(type, id) {
/** /**
* Finds the hardpoint with the specified ID * Finds the hardpoint with the specified ID
* @param {string} id Hardpoint ID * @param {String} id Hardpoint ID
* @return {Object} Hardpoint module or null * @return {Object} Hardpoint module or null
*/ */
export function hardpoints(id) { export function hardpoints(id) {
@@ -46,7 +46,7 @@ export function hardpoints(id) {
/** /**
* Finds the internal module with the specified 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 * @return {Object} Internal module or null
*/ */
export function internal(id) { export function internal(id) {
@@ -65,11 +65,11 @@ export function internal(id) {
* Finds an internal module based on Class, Rating, Group and/or name. * Finds an internal module based on Class, Rating, Group and/or name.
* At least one ofGroup name or unique module name must be provided * At least one ofGroup name or unique module name must be provided
* *
* @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 {integer} clss module Class
* @param {string} rating module Rating * @param {String} rating module Rating
* @param {string} name [Optional] Long/unique name for module -e.g. 'Advanced Discover Scanner' * @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 * @return {Object} The module if found, null if not found
*/ */
export function findInternal(groupName, clss, rating, name) { export function findInternal(groupName, clss, rating, name) {
let groups = {}; 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. * 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 * 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 {integer} clss module Class
* @param {string} rating Module Rating * @param {String} rating Module Rating
* @param {string} name [Optional] Long/unique name for module -e.g. 'Advanced Discover Scanner' * @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 * @return {String} The id of the module if found, null if not found
*/ */
export function findInternalId(groupName, clss, rating, name) { 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. * Finds a hardpoint Module based on Class, Rating, Group and/or name.
* At least one ofGroup name or unique module name must be provided * At least one ofGroup name or unique module name must be provided
* *
* @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 {integer} clss Module Class
* @param {string} rating [Optional] module Rating * @param {String} rating [Optional] module Rating
* @param {string} name [Optional] Long/unique name for module -e.g. 'Heat Sink Launcher' * @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} mount Mount type - [F]ixed, [G]imballed, [T]urret
* @param {string} missile [Optional] Missile type - [D]umbfire, [S]eeker * @param {String} missile [Optional] Missile type - [D]umbfire, [S]eeker
* @return {String} The id of the module if found, null if not found * @return {String} The id of the module if found, null if not found
*/ */
export function findHardpoint(groupName, clss, rating, name, mount, missile) { 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. * 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 * 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 {integer} clss module Class
* @param {string} rating module Rating * @param {String} rating module Rating
* @param {string} name [Optional] Long/unique name for module -e.g. 'Heat Sink Launcher' * @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} mount Mount type - [F]ixed, [G]imballed, [T]urret
* @param {string} missile [Optional] Missile type - [D]umbfire, [S]eeker * @param {String} missile [Optional] Missile type - [D]umbfire, [S]eeker
* @return {String} The id of the module if found, null if not found * @return {String} The id of the module if found, null if not found
*/ */
export function findHardpointId(groupName, clss, rating, name, mount, missile) { 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; 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 * 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 * @return {number} Bulkhead index
*/ */
export function bulkheadIndex(bulkheadName) { export function bulkheadIndex(bulkheadName) {
@@ -198,7 +183,7 @@ export function bulkheadIndex(bulkheadName) {
/** /**
* Determine if a module group is a shield generator * 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 * @return {Boolean} True if the group is a shield generator
*/ */
export function isShieldGenerator(g) { 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 * 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 * @return {ModuleSet} The set of modules the ship can install
*/ */
export function forShip(shipId) { export function forShip(shipId) {
let ship = Ships[shipId]; return new ModuleSet(Modules, 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]);
} }

View File

@@ -59,7 +59,7 @@ export function toDetailedBuild(buildName, ship) {
}], }],
components: { components: {
standard: { standard: {
bulkheads: BulkheadNames[ship.bulkheads.index], bulkheads: BulkheadNames[ship.bulkheads.m.index],
cargoHatch: { enabled: Boolean(ship.cargoHatch.enabled), priority: ship.cargoHatch.priority + 1 }, 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 }, 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 }, thrusters: { class: standard[1].m.class, rating: standard[1].m.rating, enabled: Boolean(standard[1].enabled), priority: standard[1].priority + 1 },

View File

@@ -294,8 +294,8 @@ export default class Ship {
*/ */
getStandardString() { getStandardString() {
if(!this.serialized.standard) { if(!this.serialized.standard) {
this.serialized.standard = this.bulkheads.index + this.standard.reduce((arr, slot, i) => { this.serialized.standard = this.bulkheads.m.index + this.standard.reduce((arr, slot, i) => {
arr[i] = slot.m ? slot.m.class + slot.m.rating : '-'; arr[i] = slot.m ? slot.m.id : '-';
return arr; return arr;
}, new Array(this.standard.length)).join(''); }, new Array(this.standard.length)).join('');
} }
@@ -739,7 +739,7 @@ export default class Ship {
updateTopSpeed() { updateTopSpeed() {
let speeds = Calc.speed(this.unladenMass + this.fuelCapacity, this.speed, this.boost, this.standard[1].m, this.pipSpeed); let speeds = Calc.speed(this.unladenMass + this.fuelCapacity, this.speed, this.boost, this.standard[1].m, this.pipSpeed);
this.topSpeed = speeds['4 Pips']; this.topSpeed = speeds['4 Pips'];
this.topBoost = speeds.boost; this.topBoost = this.canBoost() ? speeds.boost : 0;
return this; return this;
} }
@@ -855,8 +855,7 @@ export default class Ship {
*/ */
useBulkhead(index, preventUpdate) { useBulkhead(index, preventUpdate) {
let oldBulkhead = this.bulkheads.m; let oldBulkhead = this.bulkheads.m;
this.bulkheads.index = index; this.bulkheads.m = this.availCS.getBulkhead(index);
this.bulkheads.m = ModuleUtils.bulkheads(this.id, index);
this.bulkheads.discountedCost = this.bulkheads.m.cost * this.moduleCostMultiplier; this.bulkheads.discountedCost = this.bulkheads.m.cost * this.moduleCostMultiplier;
this.armourMultiplier = ArmourMultiplier[index]; this.armourMultiplier = ArmourMultiplier[index];
this.updateStats(this.bulkheads, this.bulkheads.m, oldBulkhead, preventUpdate); 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 * 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) * @return {this} The ship instance (for chaining operations)
*/ */
useLightestStandard(m) { useLightestStandard(m) {
@@ -889,16 +888,18 @@ export default class Ship {
let standard = this.standard, let standard = this.standard,
// Find lightest Power Distributor that can still boost; // Find lightest Power Distributor that can still boost;
pd = m.pd ? ModuleUtils.standard(4, m.pd) : this.availCS.lightestPowerDist(this.boostEnergy), pd = m.pd ? ModuleUtils.standard(4, m.pd) : this.availCS.lightestPowerDist(this.boostEnergy),
fsd = m.fsd || standard[2].maxClass + 'A', fsd = ModuleUtils.standard(2, m.fsd || standard[2].maxClass + 'A'),
ls = m.ls || standard[3].maxClass + 'D', ls = ModuleUtils.standard(3, m.ls || standard[3].maxClass + 'D'),
s = m.s || standard[5].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; updated;
this.useBulkhead(0) this.useBulkhead(0)
.use(standard[2], ModuleUtils.standard(2, fsd)) // FSD .use(standard[2], fsd) // FSD
.use(standard[3], ModuleUtils.standard(3, ls)) // Life Support .use(standard[3], ls) // Life Support
.use(standard[5], ModuleUtils.standard(5, s)) // Sensors .use(standard[5], s) // Sensors
.use(standard[4], pd); // Power Distributor .use(standard[4], pd) // Power Distributor
.use(standard[6], ft); // Fuel Tank
// Thrusters and Powerplant must be determined after all other ModuleUtils are mounted // Thrusters and Powerplant must be determined after all other ModuleUtils are mounted
// Loop at least once to determine absolute lightest PD and TH // Loop at least once to determine absolute lightest PD and TH

View 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(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(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(slot, 'fs')) {
fuelScoopSlot = slot;
ship.use(slot, ModuleUtils.findInternal('fs', slot.maxClass, 'A'));
ship.setSlotEnabled(slot, true);
// Mount a Shield generator if possible AND an AFM Unit has been mounted already (Guarantees at least 1 AFM Unit)
} else if (!sgSlot && shieldNext && canMount(slot, 'sg', sg.class) && !canMount(nextSlot, 'sg', sg.class)) {
sgSlot = slot;
shieldNext = false;
ship.use(slot, sg);
ship.setSlotEnabled(slot, true);
// if planetary explorer and the next slot cannot mount a PVH or the next modul to mount is a SG
} else if (planetary && !pvhSlot && canMount(slot, 'pv') && (shieldNext || !canMount(nextSlot, 'pv', 2))) {
pvhSlot = slot;
ship.use(slot, ModuleUtils.findInternal('pv', Math.min(Math.floor(pvhSlot.maxClass / 2) * 2, 6), 'G'));
ship.setSlotEnabled(slot, false); // Disabled power for PVH
shieldNext = !sgSlot;
} else if (afmUnitCount > 0 && canMount(slot, 'am')) {
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);
}

View File

@@ -2,6 +2,21 @@ import React from 'react';
import cn from 'classnames'; import cn from 'classnames';
import { isShieldGenerator } from '../shipyard/ModuleUtils'; import { isShieldGenerator } from '../shipyard/ModuleUtils';
import { Infinite } from '../components/SvgIcons'; import { Infinite } from '../components/SvgIcons';
import Persist from '../stores/Persist';
/**
* Determine if a slot can mount a module of a particular class and group
* @param {Object} slot Slot object
* @param {String} group Module group/type abbrivation/code
* @param {Integer} clazz [Optional] Module Class/Size
* @return {Boolean} True if the slot can mount the module
*/
export function canMount(slot, group, clazz) {
if (slot && (!slot.eligible || slot.eligible[group]) && (clazz === undefined || slot.maxClass >= clazz)) {
return true;
}
return false;
}
/** /**
* Returns the translate name for the module mounted in the specified * Returns the translate name for the module mounted in the specified
@@ -76,7 +91,9 @@ const PROP_BLACKLIST = {
eddbID: 1, eddbID: 1,
edID: 1, edID: 1,
id: 1, id: 1,
index: 1,
'class': 1, 'class': 1,
rating: 1,
maxfuel: 1, maxfuel: 1,
fuelmul: 1, fuelmul: 1,
fuelpower: 1, fuelpower: 1,
@@ -161,7 +178,7 @@ function diff(format, mVal, mmVal) {
return <Infinite/>; return <Infinite/>;
} else { } else {
let diff = mVal - mmVal; 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);
} }
return `${format(mVal)} (${diff > 0 ? '+' : ''}${format(diff)})`; return `${format(mVal)} (${diff > 0 ? '+' : ''}${format(diff)})`;
@@ -190,7 +207,7 @@ export function diffDetails(language, m, mm) {
let mAffectsShield = isShieldGenerator(m.grp) || m.grp == 'sb'; let mAffectsShield = isShieldGenerator(m.grp) || m.grp == 'sb';
let mmAffectsShield = isShieldGenerator(mm.grp) || mm.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>); 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) { for (let p in m) {
@@ -218,7 +235,7 @@ export function diffDetails(language, m, mm) {
} }
let sgDiffClass = Math.round((newShield - shield) * 100) / 100 == 0 ? 'muted' : (newShield > shield ? 'secondary' : 'warning'); 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') { if (m.grp == 'pd') {
@@ -248,10 +265,10 @@ export function diffDetails(language, m, mm) {
let ladenRange = this.calcLadenRange(massDiff + capDiff, m.fuel, fsd); let ladenRange = this.calcLadenRange(massDiff + capDiff, m.fuel, fsd);
if (maxRange != this.unladenRange) { 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) { 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>);
} }
} }

View File

@@ -25,12 +25,11 @@
</head> </head>
<body style="background-color:#000;"> <body style="background-color:#000;">
<section id="coriolis"></section> <section id="coriolis"></section>
<footer> <script>
<div class="right cap"> window.CORIOLIS_GAPI_KEY = '{%= o.htmlWebpackPlugin.options.gapiKey %}';
<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> window.CORIOLIS_VERSION = '{%= o.htmlWebpackPlugin.options.version %}';
</div> window.CORIOLIS_DATE = '{%= new Date().toISOString().slice(0, 10) %}';
</footer> </script>
<script src="{%= o.htmlWebpackPlugin.files.chunks.lib.entry %}" charset="utf-8" crossorigin="anonymous"></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 src="{%= o.htmlWebpackPlugin.files.chunks.app.entry %}" charset="utf-8" crossorigin="anonymous"></script>
<script> <script>
@@ -41,7 +40,6 @@
})(window,document,'script','//www.google-analytics.com/analytics.js','ga'); })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', '{%= o.htmlWebpackPlugin.options.uaTracking %}', 'auto'); ga('create', '{%= o.htmlWebpackPlugin.options.uaTracking %}', 'auto');
{% } %} {% } %}
window.CORIOLIS_GAPI_KEY = '{%= o.htmlWebpackPlugin.options.gapiKey %}';
</script> </script>
</body> </body>
</html> </html>

View File

@@ -23,9 +23,7 @@ html, body {
height: 100%; height: 100%;
width: 100%; width: 100%;
text-rendering: optimizeLegibility; text-rendering: optimizeLegibility;
overflow-x: hidden; overflow: hidden;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
} }
body { body {
@@ -42,17 +40,23 @@ div, a, li {
#coriolis { #coriolis {
width: 100%; width: 100%;
min-height: 95%; height: 100%;
overflow-y: scroll;
-webkit-overflow-scrolling: touch;
} }
.page { .page {
margin: 0; margin: 0;
padding: 0.5em 0; padding: 0.5em;
width: 100%; width: 100%;
min-height: 100%; min-height: 100%;
clear: both; clear: both;
text-align: center; text-align: center;
box-sizing: border-box; box-sizing: border-box;
.largePhone({
padding: 0.5em 0.25em;
});
} }
.l { .l {

View File

@@ -97,6 +97,7 @@ header {
input { input {
border: none; border: none;
background-color: transparent;
text-align: right; text-align: right;
font-size: 1em; font-size: 1em;
font-family: @fStandard; font-family: @fStandard;
@@ -171,7 +172,7 @@ header {
&:visited { &:visited {
color: @warning; color: @warning;
} }
&:hover { .no-touch &:hover {
color: teal; color: teal;
} }
&.active { &.active {

View File

@@ -78,7 +78,7 @@ select {
stroke-width: 0.5em; stroke-width: 0.5em;
stroke: @primary-disabled; stroke: @primary-disabled;
&:hover { .no-touch &:hover {
border-color: @primary; border-color: @primary;
color: @primary; color: @primary;
stroke: @primary; stroke: @primary;
@@ -95,14 +95,14 @@ select {
color: @warning-disabled; color: @warning-disabled;
stroke: @warning-disabled; stroke: @warning-disabled;
&:hover { .no-touch &:hover {
border-color: @warning; border-color: @warning;
color: @warning; color: @warning;
stroke: @warning; stroke: @warning;
} }
} }
&.disabled { &.disabled, &.disabled:hover {
cursor: not-allowed; cursor: not-allowed;
border-color: @disabled; border-color: @disabled;
color: @disabled; color: @disabled;
@@ -150,4 +150,10 @@ select {
} }
} }
&.standard {
ul {
width: 16.25em;
}
}
} }

View File

@@ -2,7 +2,7 @@
.user-select-none(); .user-select-none();
cursor: pointer; cursor: pointer;
&:hover { .no-touch &:hover {
color: @primary; color: @primary;
} }
} }

View File

@@ -33,6 +33,10 @@ thead {
&.lft { &.lft {
border-left: 1px solid @primary-bg; border-left: 1px solid @primary-bg;
} }
&.rgt {
border-right: 1px solid @primary-bg;
}
} }
} }
@@ -43,23 +47,26 @@ tbody tr {
text-align: right; text-align: right;
} }
td { .no-touch &.highlight:hover, .no-touch &.highlighted {
line-height: 1.4em; background-color: @warning-bg;
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;
}
} }
} }
td { 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 { &.tl {
text-align: left; text-align: left;
padding-left: 0.7em; padding-left: 0.7em;

79
src/migrate.html Normal file
View 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>

View File

@@ -74,6 +74,7 @@ module.exports = {
}), }),
new CopyDirPlugin(path.join(__dirname, 'src/schemas'), 'schemas'), new CopyDirPlugin(path.join(__dirname, 'src/schemas'), 'schemas'),
new CopyDirPlugin(path.join(__dirname, 'src/images/logo/*'), ''), new CopyDirPlugin(path.join(__dirname, 'src/images/logo/*'), ''),
new CopyDirPlugin(path.join(__dirname, 'src/migrate.html'), ''),
new AppCachePlugin({ new AppCachePlugin({
network: ['*'], network: ['*'],
settings: ['prefer-online'], settings: ['prefer-online'],