mirror of
https://github.com/EDCD/coriolis.git
synced 2025-12-09 14:45:35 +00:00
Compare commits
678 Commits
2.3.7
...
special-ke
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
934593fe2a | ||
|
|
e4a826592f | ||
|
|
cee4c32551 | ||
|
|
081d8fb86a | ||
|
|
3dfd563d90 | ||
|
|
fd08cd219c | ||
|
|
6a15326d31 | ||
|
|
608ecc51b7 | ||
|
|
fcef26ebbb | ||
|
|
ba6d758ed5 | ||
|
|
43aa3e4e79 | ||
|
|
18f0e060a7 | ||
|
|
c7547e8baf | ||
|
|
ffff242abe | ||
|
|
b44c66b986 | ||
|
|
ae77ec6256 | ||
|
|
4f1e32b154 | ||
|
|
af37c2bfc5 | ||
|
|
5d4ab6f2ad | ||
|
|
0c9db53057 | ||
|
|
b689605ac2 | ||
|
|
baab91e371 | ||
|
|
70e69c7099 | ||
|
|
f4534fd3eb | ||
|
|
93594e1a65 | ||
|
|
b5e449ea54 | ||
|
|
0ff4b849aa | ||
|
|
b99e38043f | ||
|
|
28e3a59473 | ||
|
|
b20290fb60 | ||
|
|
2734beb6f8 | ||
|
|
345eec528c | ||
|
|
7a17e18a76 | ||
|
|
4697677457 | ||
|
|
7d8a5a1368 | ||
|
|
dd7402bd0e | ||
|
|
65592b0fc6 | ||
|
|
0ab66023a6 | ||
|
|
d6fad098ee | ||
|
|
1b5730d337 | ||
|
|
439b615b1b | ||
|
|
a8b30594dc | ||
|
|
9b6b1d328c | ||
|
|
ac2e2e4d69 | ||
|
|
3a5fb31860 | ||
|
|
c610eb8627 | ||
|
|
94980270c4 | ||
|
|
c685e002e3 | ||
|
|
1f665eed9e | ||
|
|
0c4fc1fd9a | ||
|
|
0fc033363e | ||
|
|
fb6e9538bc | ||
|
|
95b98fc4ed | ||
|
|
1840dafed0 | ||
|
|
1ad82b116c | ||
|
|
7bdd17504b | ||
|
|
2d820bb5d5 | ||
|
|
2e14512ed8 | ||
|
|
48ed583c6d | ||
|
|
dd444a17f3 | ||
|
|
2ea63c711e | ||
|
|
6d6d31db25 | ||
|
|
e9273dcb9b | ||
|
|
2bdc4562c6 | ||
|
|
9e8a5323e9 | ||
|
|
8e001063b3 | ||
|
|
dc88fab4c5 | ||
|
|
dfca917e50 | ||
|
|
ef7dfd6ca1 | ||
|
|
435c1b6d45 | ||
|
|
c5c9abe588 | ||
|
|
363735d36b | ||
|
|
2741e7701b | ||
|
|
3be442ea60 | ||
|
|
34cbeca201 | ||
|
|
b37e73ead6 | ||
|
|
ee775521d6 | ||
|
|
5f84aaef1b | ||
|
|
99ac58d999 | ||
|
|
f128a1e87d | ||
|
|
8c0768b451 | ||
|
|
319307136c | ||
|
|
a498452943 | ||
|
|
4b854b8305 | ||
|
|
b400db8216 | ||
|
|
fb811faf5e | ||
|
|
deeb525433 | ||
|
|
1cb88115f6 | ||
|
|
a181791500 | ||
|
|
94eec120da | ||
|
|
48092d4395 | ||
|
|
2457c30b94 | ||
|
|
593f069806 | ||
|
|
a073692632 | ||
|
|
7752d5c9db | ||
|
|
544e5acaef | ||
|
|
9ab35bbaf9 | ||
|
|
98782da200 | ||
|
|
2936364934 | ||
|
|
01e1609a9f | ||
|
|
f85a03a9ae | ||
|
|
2703c2aa23 | ||
|
|
954921c231 | ||
|
|
8bed35a8ba | ||
|
|
9f4ae60577 | ||
|
|
ee3c50e27d | ||
|
|
03020743b3 | ||
|
|
001fed67b7 | ||
|
|
3894915740 | ||
|
|
68fd13e8dc | ||
|
|
fdf16cd959 | ||
|
|
d916c67fe0 | ||
|
|
d8a8e224f4 | ||
|
|
e1c115747c | ||
|
|
e9b6d71606 | ||
|
|
e03e249d2f | ||
|
|
0cfb0b6878 | ||
|
|
600df162aa | ||
|
|
94141aa3c5 | ||
|
|
aca90d7077 | ||
|
|
a66fa8e83f | ||
|
|
194db07057 | ||
|
|
307886d4ae | ||
|
|
bbba048129 | ||
|
|
222173b388 | ||
|
|
ec0d05e081 | ||
|
|
b3be0bd639 | ||
|
|
529d80682c | ||
|
|
934de01803 | ||
|
|
3367580d78 | ||
|
|
fbf59219d0 | ||
|
|
77401a3b3f | ||
|
|
7c587c29aa | ||
|
|
2295dccd82 | ||
|
|
cc4ad6d132 | ||
|
|
8a2d27290a | ||
|
|
eda61a8e06 | ||
|
|
3987c4e681 | ||
|
|
71b90eb6f4 | ||
|
|
4e891f382c | ||
|
|
3d3f9e44b5 | ||
|
|
dd7a133caa | ||
|
|
ae247c4812 | ||
|
|
6c932f96a6 | ||
|
|
0ea25692d3 | ||
|
|
e129e1da39 | ||
|
|
8acd32b0fc | ||
|
|
8e5dd9fb8d | ||
|
|
97ce2828e0 | ||
|
|
f8f99a5aaa | ||
|
|
70cfa58896 | ||
|
|
6e79ced51e | ||
|
|
56571f9c1f | ||
|
|
0b10cac85c | ||
|
|
34c04a6354 | ||
|
|
4e337c4ca1 | ||
|
|
5048b7e094 | ||
|
|
27fbc1ad66 | ||
|
|
4ab376d9ed | ||
|
|
dfffc3a268 | ||
|
|
b59fa15e00 | ||
|
|
12bca4c44e | ||
|
|
2858ef3e93 | ||
|
|
7d99471f89 | ||
|
|
a2ab708ac9 | ||
|
|
a34a9c355f | ||
|
|
3215b3942d | ||
|
|
557c0afd9b | ||
|
|
d52365a204 | ||
|
|
14b2a14e58 | ||
|
|
7f24904f77 | ||
|
|
da07790594 | ||
|
|
5008c7cd74 | ||
|
|
a778b1b6e1 | ||
|
|
bd9771f9ba | ||
|
|
600c244f9b | ||
|
|
a599b1a076 | ||
|
|
3e0a5e22b1 | ||
|
|
3a6ac818c2 | ||
|
|
6f077d4c41 | ||
|
|
9c767c928c | ||
|
|
515f4ad3da | ||
|
|
4fcf074595 | ||
|
|
e5f8153a34 | ||
|
|
571854a11c | ||
|
|
1f22f249a1 | ||
|
|
718ac0a514 | ||
|
|
8f089cb1ee | ||
|
|
d19a7276dd | ||
|
|
10fffe67fc | ||
|
|
f0bf8e8ce2 | ||
|
|
598cf8d677 | ||
|
|
90f03de3fe | ||
|
|
e0766f4424 | ||
|
|
28a90768e4 | ||
|
|
f3d917ccbe | ||
|
|
7e5d52385d | ||
|
|
4368015dc0 | ||
|
|
1201da1811 | ||
|
|
d195b568b0 | ||
|
|
c9866c146b | ||
|
|
5d52809d0d | ||
|
|
8f0cca4fd9 | ||
|
|
e46bb425fe | ||
|
|
06dc110025 | ||
|
|
e9c34c636a | ||
|
|
59d38cbd33 | ||
|
|
51f5188efc | ||
|
|
be8934da80 | ||
|
|
18d78b3089 | ||
|
|
b1ff4e84f7 | ||
|
|
bed2ede701 | ||
|
|
124bd62d2c | ||
|
|
975846f4ab | ||
|
|
3f73f9be10 | ||
|
|
d218101708 | ||
|
|
608ce12156 | ||
|
|
80d653483a | ||
|
|
f9a1e5afd9 | ||
|
|
7f5181a9c7 | ||
|
|
93d8f15f26 | ||
|
|
06d8930777 | ||
|
|
23f4024e09 | ||
|
|
a5e9a71037 | ||
|
|
423d42bc8a | ||
|
|
4075bab3d0 | ||
|
|
469f7a0a48 | ||
|
|
25d9f8ec61 | ||
|
|
1151bd1614 | ||
|
|
2456ce330b | ||
|
|
f86ce62c27 | ||
|
|
c295a9f4e4 | ||
|
|
c4186faa4a | ||
|
|
00f4889c93 | ||
|
|
b6c83f4aba | ||
|
|
3fa040c210 | ||
|
|
8c4957c21e | ||
|
|
a178c59aa3 | ||
|
|
0d9fcd97d7 | ||
|
|
b02ca117be | ||
|
|
ca2e2a22dd | ||
|
|
035e217124 | ||
|
|
6424ab283a | ||
|
|
f30f10c904 | ||
|
|
8ce09a607f | ||
|
|
5b48e30798 | ||
|
|
0d7e6af2e6 | ||
|
|
be3ca23aee | ||
|
|
28f464ba5a | ||
|
|
8cedd1b8bc | ||
|
|
56cbc49d04 | ||
|
|
5d1ccd2a20 | ||
|
|
f898fbc55e | ||
|
|
63acad3aeb | ||
|
|
5b4b0b8dff | ||
|
|
8492dd74f9 | ||
|
|
77e652cd34 | ||
|
|
5069d7e464 | ||
|
|
8b3e9c0f63 | ||
|
|
99e76e480b | ||
|
|
c161ed2298 | ||
|
|
464770f096 | ||
|
|
9fba1be814 | ||
|
|
b419b8b104 | ||
|
|
0cae955ca2 | ||
|
|
ec70ad5d29 | ||
|
|
8a386c4ece | ||
|
|
013460ada4 | ||
|
|
d46ad89dc5 | ||
|
|
86c53c8e46 | ||
|
|
df14786e79 | ||
|
|
24f206ad82 | ||
|
|
242c3efe45 | ||
|
|
07a848b906 | ||
|
|
1b5402fd2d | ||
|
|
903d791549 | ||
|
|
f03c164f1c | ||
|
|
107bab0192 | ||
|
|
41f5ebb2f1 | ||
|
|
266090dc2a | ||
|
|
83f29f3d62 | ||
|
|
3e6137ca96 | ||
|
|
58510bbd22 | ||
|
|
2f7be75bcf | ||
|
|
0aecbbf892 | ||
|
|
9ef2f4179b | ||
|
|
afbfe3ea12 | ||
|
|
b145a2da85 | ||
|
|
a679f7dd98 | ||
|
|
f343366ea1 | ||
|
|
1bbea7dda0 | ||
|
|
da097e0955 | ||
|
|
1307474755 | ||
|
|
80f5a95297 | ||
|
|
e619966679 | ||
|
|
3f94382925 | ||
|
|
ea7a6f8872 | ||
|
|
4e87b3a0b8 | ||
|
|
8463dd46f7 | ||
|
|
3febe465f6 | ||
|
|
45b834c424 | ||
|
|
1ca041097b | ||
|
|
587f7acd5b | ||
|
|
9e64e4a26b | ||
|
|
96e09ab36c | ||
|
|
4268d3f07b | ||
|
|
1767a2aed5 | ||
|
|
9f061506bb | ||
|
|
0eb9a2048d | ||
|
|
faeafb2402 | ||
|
|
2a0acfa6cb | ||
|
|
0ac44ac267 | ||
|
|
b9ae3a4d5a | ||
|
|
b1aefb0003 | ||
|
|
f6a41ec55c | ||
|
|
39650cc584 | ||
|
|
d837287da7 | ||
|
|
6e718a39d1 | ||
|
|
2e7db02238 | ||
|
|
dbfd108819 | ||
|
|
82c0f6fc0f | ||
|
|
9e6bbaa67d | ||
|
|
012a203c4a | ||
|
|
c7ba129ed7 | ||
|
|
fbe57d00db | ||
|
|
dc4fdf215c | ||
|
|
2166ac1584 | ||
|
|
504756de09 | ||
|
|
90f04dba94 | ||
|
|
bfd7645fb7 | ||
|
|
c37c6983c3 | ||
|
|
736c700aa0 | ||
|
|
de1cb901fc | ||
|
|
3d9d6397f6 | ||
|
|
dbe836729f | ||
|
|
82fa93e676 | ||
|
|
90a3392b80 | ||
|
|
b7f715bd5e | ||
|
|
5f88f46770 | ||
|
|
2244c91a64 | ||
|
|
78134404c3 | ||
|
|
c9cd6b175d | ||
|
|
5f70d283e0 | ||
|
|
cd1e27fd11 | ||
|
|
0fd1c19514 | ||
|
|
a82dffd77d | ||
|
|
d3eed87077 | ||
|
|
d99f8ad7e7 | ||
|
|
e933305407 | ||
|
|
690bc5a64a | ||
|
|
d2380a5c9c | ||
|
|
c2d0a6e9e0 | ||
|
|
a8c44fddca | ||
|
|
0e6b306bdc | ||
|
|
9b2a47ba0c | ||
|
|
3038ebc1c7 | ||
|
|
5a48d5b400 | ||
|
|
f36edfe98d | ||
|
|
3e80d6e13b | ||
|
|
c8f0a1ccc6 | ||
|
|
79325a3129 | ||
|
|
1ca0fffe5e | ||
|
|
f5b9842b2e | ||
|
|
72ea3f125e | ||
|
|
f7a2408e44 | ||
|
|
c05de9a085 | ||
|
|
fc657f3b2d | ||
|
|
e388de0364 | ||
|
|
26e55afb29 | ||
|
|
9b8e1039f6 | ||
|
|
082d14ba50 | ||
|
|
87ead595c6 | ||
|
|
fb073acdc3 | ||
|
|
3dda4e24bf | ||
|
|
c966bd08ed | ||
|
|
692c73a6d0 | ||
|
|
849b484b4d | ||
|
|
9489baccda | ||
|
|
62cac15222 | ||
|
|
0ee9ce2958 | ||
|
|
a83007fc6c | ||
|
|
901f266dad | ||
|
|
6009426f04 | ||
|
|
6fb89df4cd | ||
|
|
48b99425a3 | ||
|
|
9569f9b09b | ||
|
|
7421896902 | ||
|
|
b837c8d66c | ||
|
|
e180f96eab | ||
|
|
de1f1f790e | ||
|
|
fefa74ce0f | ||
|
|
a2ceb70aa4 | ||
|
|
634103f144 | ||
|
|
f3379de81e | ||
|
|
064ee74c1a | ||
|
|
0e0c15fe3b | ||
|
|
3c855c608d | ||
|
|
414516289c | ||
|
|
d79fa71337 | ||
|
|
39c2ca94c8 | ||
|
|
acf1c5ce04 | ||
|
|
fd7923155f | ||
|
|
2aa6a6daa2 | ||
|
|
9662621980 | ||
|
|
37d007d9ab | ||
|
|
a4e48c359a | ||
|
|
8ef0101a6e | ||
|
|
e21f109026 | ||
|
|
737837eebd | ||
|
|
ba09b54409 | ||
|
|
417091c648 | ||
|
|
5a02368298 | ||
|
|
da7c739497 | ||
|
|
0842281466 | ||
|
|
b480f879b1 | ||
|
|
f8a215d790 | ||
|
|
da69f3b2c8 | ||
|
|
fc442c1a42 | ||
|
|
cf59a6b9fd | ||
|
|
34afcd511a | ||
|
|
e2444a2e4e | ||
|
|
692516de9b | ||
|
|
d51009c823 | ||
|
|
2f121bef5e | ||
|
|
61f7d376d2 | ||
|
|
9e253012e6 | ||
|
|
e4e5b1327b | ||
|
|
6ef3b227b8 | ||
|
|
bf657a0945 | ||
|
|
c3dd1886c9 | ||
|
|
32498bb8a7 | ||
|
|
69bb90a0e4 | ||
|
|
8e2988edf0 | ||
|
|
876a352cfd | ||
|
|
84e44cabfa | ||
|
|
36a838d565 | ||
|
|
9ee8693f40 | ||
|
|
6f02965149 | ||
|
|
27ce82de3b | ||
|
|
3d5a9ef220 | ||
|
|
9b81f6efd2 | ||
|
|
3e77e23d71 | ||
|
|
120c032c82 | ||
|
|
46e15b8ecd | ||
|
|
d71d87041b | ||
|
|
124bd58b9f | ||
|
|
257b9b0562 | ||
|
|
b8e15f691d | ||
|
|
2255e3bfc4 | ||
|
|
8797d84605 | ||
|
|
719179a923 | ||
|
|
1d544099f6 | ||
|
|
9b131a762a | ||
|
|
08c5d2256a | ||
|
|
ed6ee4341f | ||
|
|
157c1148fb | ||
|
|
507ea9e09e | ||
|
|
af68cba7be | ||
|
|
224fbe0e8f | ||
|
|
49a6c5f2c4 | ||
|
|
07c936897c | ||
|
|
3786fb907c | ||
|
|
752d9f0c68 | ||
|
|
baf59aafcb | ||
|
|
8787303d2a | ||
|
|
298eaa8b4b | ||
|
|
118c80af27 | ||
|
|
7b87038a8c | ||
|
|
d103939e45 | ||
|
|
fdc9171c69 | ||
|
|
2a6ade3cab | ||
|
|
c8c42689f9 | ||
|
|
da2f472f4d | ||
|
|
3ba878237b | ||
|
|
7577fb53a2 | ||
|
|
f2509f89ee | ||
|
|
9dd1f78330 | ||
|
|
ebb6c2c420 | ||
|
|
9d23dc1763 | ||
|
|
5fa3f8703e | ||
|
|
797885faea | ||
|
|
68e7e9f5b7 | ||
|
|
d1d165ad51 | ||
|
|
f43bd100e6 | ||
|
|
0ebb247666 | ||
|
|
aa73bc2809 | ||
|
|
c3fcdb918f | ||
|
|
5e3722bcfd | ||
|
|
6a4fca2eb1 | ||
|
|
fbba0e3ea5 | ||
|
|
6922cfd047 | ||
|
|
d93fc1d2d0 | ||
|
|
b15695128f | ||
|
|
48290b2e75 | ||
|
|
b62abef618 | ||
|
|
e0c0778d82 | ||
|
|
b4a82ae7c2 | ||
|
|
fc73102b30 | ||
|
|
b14e7473f3 | ||
|
|
f4cc9fc722 | ||
|
|
8d19ef7783 | ||
|
|
32be186ec5 | ||
|
|
abb0c7f90d | ||
|
|
71ddbdfe75 | ||
|
|
38463ad9a6 | ||
|
|
0870b90443 | ||
|
|
70375f94c8 | ||
|
|
2d4336116a | ||
|
|
f52880765e | ||
|
|
dbfe68decb | ||
|
|
659f337de9 | ||
|
|
1d36d41da1 | ||
|
|
0f90efaa54 | ||
|
|
48ccab152b | ||
|
|
4442930a82 | ||
|
|
912a775088 | ||
|
|
24a229d818 | ||
|
|
66afb61494 | ||
|
|
e7511cc05b | ||
|
|
926f19a936 | ||
|
|
9e6f86b963 | ||
|
|
059de43a9a | ||
|
|
e246b737b2 | ||
|
|
f3bc900f16 | ||
|
|
38842417b0 | ||
|
|
82d485a98e | ||
|
|
fac71feea7 | ||
|
|
ec148847a9 | ||
|
|
445c63878b | ||
|
|
0474af912a | ||
|
|
7df5953824 | ||
|
|
e391b563fb | ||
|
|
53f62f96d0 | ||
|
|
18745979a0 | ||
|
|
c1966a38ff | ||
|
|
d86973f3b1 | ||
|
|
56b8d19649 | ||
|
|
7cb037e0bc | ||
|
|
56e1b3f9e9 | ||
|
|
c792323a8a | ||
|
|
4d2865de13 | ||
|
|
0d360bc367 | ||
|
|
91c9b46b91 | ||
|
|
7e67bd80dd | ||
|
|
2f775ea09b | ||
|
|
0603c55089 | ||
|
|
41f25a44e9 | ||
|
|
35440b7273 | ||
|
|
7d68b91018 | ||
|
|
deaa61b848 | ||
|
|
2d00cbc41b | ||
|
|
57c1e83c67 | ||
|
|
b7079dbd4e | ||
|
|
be642a5373 | ||
|
|
ffc691c1a2 | ||
|
|
6122d99369 | ||
|
|
b73a8bcdab | ||
|
|
dd624537b7 | ||
|
|
50a67f73fd | ||
|
|
2262a980d4 | ||
|
|
84964ceb5f | ||
|
|
c4bdb7a66e | ||
|
|
21309e129f | ||
|
|
928e02c718 | ||
|
|
7f494dd200 | ||
|
|
f38e743e59 | ||
|
|
58b55eb3da | ||
|
|
8375ad95b3 | ||
|
|
506d027a2d | ||
|
|
fbeb6237cf | ||
|
|
bd95e2c5a5 | ||
|
|
f3276e557a | ||
|
|
8b813e0e7f | ||
|
|
d660d2959f | ||
|
|
014ebda7d2 | ||
|
|
607398d364 | ||
|
|
97d141ce2b | ||
|
|
9f492db9c6 | ||
|
|
6ed82b366c | ||
|
|
a7ca037f48 | ||
|
|
6e21d0e74a | ||
|
|
e6c75bf2af | ||
|
|
270c2f386e | ||
|
|
83f7880c58 | ||
|
|
51d29aee38 | ||
|
|
c7754c0365 | ||
|
|
94037cea38 | ||
|
|
5c1a9d9eea | ||
|
|
3835c73ffd | ||
|
|
b8c1effecb | ||
|
|
dd07241dd9 | ||
|
|
5ad828a613 | ||
|
|
9856df5527 | ||
|
|
7b249900ec | ||
|
|
e3b9267c3f | ||
|
|
a996b8135a | ||
|
|
5e1237390b | ||
|
|
83571b4bef | ||
|
|
c2a0dad9a8 | ||
|
|
f6f057689c | ||
|
|
4378f0020e | ||
|
|
2e9e7c4fc6 | ||
|
|
b7b8ee5580 | ||
|
|
c630dbbed0 | ||
|
|
fa6703a3b8 | ||
|
|
bdcb64c9d1 | ||
|
|
a1afc869a7 | ||
|
|
d8ce26c7cf | ||
|
|
95c474dc05 | ||
|
|
56ca73b4ad | ||
|
|
d82cd6a89e | ||
|
|
bf20d32364 | ||
|
|
e968e62fca | ||
|
|
b9c9ca9fa1 | ||
|
|
6e965e2e98 | ||
|
|
7d569f9036 | ||
|
|
df5a77199d | ||
|
|
3c8dfebfdc | ||
|
|
3cc422596f | ||
|
|
4ed167de22 | ||
|
|
b420647501 | ||
|
|
0c318b5e68 | ||
|
|
1c627297b8 | ||
|
|
f41e2d0552 | ||
|
|
1a1d539c60 | ||
|
|
ba2e46f88f | ||
|
|
c2f1fa81af | ||
|
|
752e03fa0f | ||
|
|
1da69664d7 | ||
|
|
9aa986a133 | ||
|
|
bdbfb28c4a | ||
|
|
162156bb2b | ||
|
|
64c5d542e9 | ||
|
|
31dc789f6e | ||
|
|
b0e2cfd7db | ||
|
|
54ddb0d014 | ||
|
|
e19688e96f | ||
|
|
4f53d75999 | ||
|
|
7277460060 | ||
|
|
93c4f6f3c0 | ||
|
|
c6919a7518 | ||
|
|
ca428e67dc | ||
|
|
c6726cf020 | ||
|
|
56ae1378da | ||
|
|
e982ab1a3b | ||
|
|
0d6aa87e89 | ||
|
|
94d06e4025 | ||
|
|
cee5b297ac | ||
|
|
c549213ce0 | ||
|
|
f3f9112767 | ||
|
|
fb325ea3e2 | ||
|
|
3773f6f7ec | ||
|
|
b90ab6fe48 | ||
|
|
cc2e56dc8a | ||
|
|
ae65af7bbf | ||
|
|
c8fb513cd1 | ||
|
|
7fc3855af4 | ||
|
|
bcebb26d4a | ||
|
|
1f23e4cfcc | ||
|
|
65998778fe | ||
|
|
e53c04a07f | ||
|
|
e5a8e106c1 | ||
|
|
11d1d80a53 | ||
|
|
c8a3d86a45 | ||
|
|
4484ca226a | ||
|
|
496c9ba35c | ||
|
|
dae2fc9192 | ||
|
|
07b00e6230 | ||
|
|
36526f0824 | ||
|
|
f4691939ba | ||
|
|
534f735b63 | ||
|
|
bc2c8406a2 | ||
|
|
032c44f39a | ||
|
|
2b43c8e91a | ||
|
|
e91b3df31a | ||
|
|
3e0597023a | ||
|
|
1dd2edf742 | ||
|
|
4ee92b1f3e |
35
.babelrc
35
.babelrc
@@ -1,3 +1,34 @@
|
|||||||
{
|
{
|
||||||
"presets": ["es2015", "react", "stage-0"]
|
"presets": [
|
||||||
}
|
["@babel/preset-env", {"modules": "commonjs"}],
|
||||||
|
"@babel/preset-react"
|
||||||
|
],
|
||||||
|
"plugins": [
|
||||||
|
"@babel/plugin-syntax-dynamic-import",
|
||||||
|
"@babel/plugin-syntax-import-meta",
|
||||||
|
["@babel/plugin-proposal-class-properties", { "loose": true }],
|
||||||
|
"@babel/plugin-proposal-json-strings",
|
||||||
|
[
|
||||||
|
"@babel/plugin-proposal-decorators",
|
||||||
|
{
|
||||||
|
"legacy": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@babel/plugin-proposal-function-sent",
|
||||||
|
"@babel/plugin-proposal-export-namespace-from",
|
||||||
|
"@babel/plugin-proposal-numeric-separator",
|
||||||
|
"@babel/plugin-proposal-throw-expressions",
|
||||||
|
"@babel/plugin-proposal-export-default-from",
|
||||||
|
"@babel/plugin-proposal-logical-assignment-operators",
|
||||||
|
"@babel/plugin-proposal-optional-chaining",
|
||||||
|
[
|
||||||
|
"@babel/plugin-proposal-pipeline-operator",
|
||||||
|
{
|
||||||
|
"proposal": "minimal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@babel/plugin-proposal-nullish-coalescing-operator",
|
||||||
|
"@babel/plugin-proposal-do-expressions",
|
||||||
|
"@babel/plugin-proposal-function-bind"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|||||||
77
.dockerignore
Normal file
77
.dockerignore
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
node_modules
|
||||||
|
npm-debug.log
|
||||||
|
### Node template
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# TypeScript v1 declaration files
|
||||||
|
typings/
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variables file
|
||||||
|
.env
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# next.js build output
|
||||||
|
.next
|
||||||
|
|
||||||
|
# nuxt.js build output
|
||||||
|
.nuxt
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
.serverless
|
||||||
|
|
||||||
21
.editorconfig
Normal file
21
.editorconfig
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
|
||||||
|
[*]
|
||||||
|
|
||||||
|
# change these settings to your own preference
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
# we recommend you to keep these unchanged
|
||||||
|
end_of_line = lf
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
[{package,bower}.json]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
103
.esdoc.json
Normal file
103
.esdoc.json
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
{
|
||||||
|
"source": "./src/app",
|
||||||
|
"includes": ["\\.js$", "\\.jsx$"],
|
||||||
|
"destination": "./docs",
|
||||||
|
"index": "./README.md",
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"name": "esdoc-standard-plugin",
|
||||||
|
"option": {
|
||||||
|
"lint": {
|
||||||
|
"enable": false
|
||||||
|
},
|
||||||
|
"coverage": {
|
||||||
|
"enable": false
|
||||||
|
},
|
||||||
|
"accessor": {
|
||||||
|
"access": [
|
||||||
|
"public",
|
||||||
|
"protected",
|
||||||
|
"private"
|
||||||
|
],
|
||||||
|
"autoPrivate": true
|
||||||
|
},
|
||||||
|
"undocumentIdentifier": {
|
||||||
|
"enable": true
|
||||||
|
},
|
||||||
|
"unexportedIdentifier": {
|
||||||
|
"enable": false
|
||||||
|
},
|
||||||
|
"typeInference": {
|
||||||
|
"enable": true
|
||||||
|
},
|
||||||
|
"brand": {
|
||||||
|
"logo": "./src/images/logo/192x192.png",
|
||||||
|
"title": "Coriolis",
|
||||||
|
"description": "Coriolis Shipyard for Elite Dangerous",
|
||||||
|
"repository": "https://github.com/EDCD/coriolis",
|
||||||
|
"site": "https://coriolis.io",
|
||||||
|
"author": "https://github.com/edcd",
|
||||||
|
"image": "./src/images/logo/192x192.png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "esdoc-ecmascript-proposal-plugin",
|
||||||
|
"option": {
|
||||||
|
"all": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "esdoc-react-plugin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "esdoc-standard-plugin",
|
||||||
|
"option": {
|
||||||
|
"lint": {
|
||||||
|
"enable": false
|
||||||
|
},
|
||||||
|
"coverage": {
|
||||||
|
"enable": false
|
||||||
|
},
|
||||||
|
"accessor": {
|
||||||
|
"access": [
|
||||||
|
"public",
|
||||||
|
"protected",
|
||||||
|
"private"
|
||||||
|
],
|
||||||
|
"autoPrivate": true
|
||||||
|
},
|
||||||
|
"undocumentIdentifier": {
|
||||||
|
"enable": true
|
||||||
|
},
|
||||||
|
"unexportedIdentifier": {
|
||||||
|
"enable": false
|
||||||
|
},
|
||||||
|
"typeInference": {
|
||||||
|
"enable": true
|
||||||
|
},
|
||||||
|
"brand": {
|
||||||
|
"logo": "./src/images/logo/192x192.png",
|
||||||
|
"title": "Coriolis",
|
||||||
|
"description": "Coriolis Shipyard for Elite Dangerous",
|
||||||
|
"repository": "https://github.com/EDCD/coriolis",
|
||||||
|
"site": "https://coriolis.io",
|
||||||
|
"author": "https://github.com/edcd",
|
||||||
|
"image": "./src/images/logo/192x192.png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "esdoc-jsx-plugin",
|
||||||
|
"option": {
|
||||||
|
"enable": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "esdoc-publish-html-plugin",
|
||||||
|
"option": {
|
||||||
|
"template": "./node_modules/esdoc-custom-theme/template"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -5,11 +5,12 @@
|
|||||||
"jsx": true,
|
"jsx": true,
|
||||||
"classes": true,
|
"classes": true,
|
||||||
"modules": true
|
"modules": true
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
"env": {
|
"env": {
|
||||||
"browser": true,
|
"browser": true,
|
||||||
"node": true
|
"node": true,
|
||||||
|
"es6": true
|
||||||
},
|
},
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"react"
|
"react"
|
||||||
@@ -33,7 +34,6 @@
|
|||||||
"ClassDeclaration": true
|
"ClassDeclaration": true
|
||||||
}
|
}
|
||||||
}],
|
}],
|
||||||
"no-console": 2,
|
|
||||||
"brace-style": [2, "1tbs", { "allowSingleLine": true }],
|
"brace-style": [2, "1tbs", { "allowSingleLine": true }],
|
||||||
"comma-style": [2, "last"],
|
"comma-style": [2, "last"],
|
||||||
"indent": [2, 2, { "SwitchCase": 1, "VariableDeclarator": 2 }],
|
"indent": [2, 2, { "SwitchCase": 1, "VariableDeclarator": 2 }],
|
||||||
|
|||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -7,3 +7,7 @@ nginx.pid
|
|||||||
/bin
|
/bin
|
||||||
env
|
env
|
||||||
*.swp
|
*.swp
|
||||||
|
.project
|
||||||
|
.vscode/
|
||||||
|
docs/
|
||||||
|
package-lock.json
|
||||||
|
|||||||
13
.gitlab-ci.yml
Normal file
13
.gitlab-ci.yml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
image: docker:stable
|
||||||
|
services:
|
||||||
|
- docker:dind
|
||||||
|
|
||||||
|
stages:
|
||||||
|
- Build image
|
||||||
|
|
||||||
|
docker build:
|
||||||
|
stage: Build image
|
||||||
|
script:
|
||||||
|
- img build --build-arg branch=$CI_COMMIT_REF_NAME -t edcd/coriolis:$CI_COMMIT_REF_NAME .
|
||||||
|
- echo "$REGISTRY_PASSWORD" | img login --username "$REGISTRY_USER" --password-stdin
|
||||||
|
- img push edcd/coriolis:$CI_COMMIT_REF_NAME
|
||||||
@@ -8,7 +8,8 @@ cache:
|
|||||||
directories:
|
directories:
|
||||||
- node_modules
|
- node_modules
|
||||||
|
|
||||||
before_script:
|
before_install:
|
||||||
|
- git clone https://github.com/EDCD/coriolis-data.git ../coriolis-data
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- npm run lint
|
- npm run lint
|
||||||
|
|||||||
26
ChangeLog.md
26
ChangeLog.md
@@ -1,3 +1,29 @@
|
|||||||
|
#2.5.1
|
||||||
|
* Passenger count on main page
|
||||||
|
* AX Modules
|
||||||
|
* Engineering fixes
|
||||||
|
* Use coriolis-data 2.5.1
|
||||||
|
|
||||||
|
#2.5.0
|
||||||
|
* willyb321 and myself have conquered engineering. Mainly him though...
|
||||||
|
* Use coriolis-data 2.5.0
|
||||||
|
|
||||||
|
#2.4.2
|
||||||
|
Lots of kind people have helped out for this release! Check out the PR history!
|
||||||
|
* Uses coriolis-data update:
|
||||||
|
* Fixes issues with repair limpets
|
||||||
|
* Adds requirement data
|
||||||
|
* Adds requirements panel
|
||||||
|
* Adds comma formatting to tooltip numbers
|
||||||
|
|
||||||
|
#2.4.1
|
||||||
|
* Small patches and changes
|
||||||
|
|
||||||
|
#2.4.0
|
||||||
|
* Changed compression library to Pako
|
||||||
|
* Use coriolis-data 2.4.0
|
||||||
|
* Repair Limpets added
|
||||||
|
|
||||||
#2.3.7
|
#2.3.7
|
||||||
* Fixed Travis test issues
|
* Fixed Travis test issues
|
||||||
* Bumped NodeJS version to provide better compatability and support
|
* Bumped NodeJS version to provide better compatability and support
|
||||||
|
|||||||
35
Dockerfile
Normal file
35
Dockerfile
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
### STAGE 1: Build ###
|
||||||
|
FROM node:9.11.1-alpine as builder
|
||||||
|
ARG branch=develop
|
||||||
|
ENV BRANCH=$branch
|
||||||
|
WORKDIR /src/app
|
||||||
|
RUN mkdir -p /src/app/coriolis
|
||||||
|
RUN mkdir -p /src/app/coriolis-data
|
||||||
|
|
||||||
|
RUN apk add --update git
|
||||||
|
|
||||||
|
COPY . /src/app/coriolis
|
||||||
|
|
||||||
|
RUN npm i -g npm
|
||||||
|
|
||||||
|
# Set up coriolis-data
|
||||||
|
WORKDIR /src/app/coriolis-data
|
||||||
|
RUN git clone https://github.com/EDCD/coriolis-data.git .
|
||||||
|
RUN git checkout ${BRANCH}
|
||||||
|
RUN npm install --no-package-lock
|
||||||
|
RUN npm start
|
||||||
|
|
||||||
|
# Set up coriolis
|
||||||
|
WORKDIR /src/app/coriolis
|
||||||
|
RUN git checkout ${BRANCH}
|
||||||
|
RUN npm install --no-package-lock
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
|
||||||
|
### STAGE 2: Production Environment ###
|
||||||
|
FROM fholzer/nginx-brotli as web
|
||||||
|
COPY nginx.conf /etc/nginx/nginx.conf
|
||||||
|
COPY --from=builder /src/app/coriolis/build /usr/share/nginx/html
|
||||||
|
WORKDIR /usr/share/nginx/html
|
||||||
|
EXPOSE 80
|
||||||
|
CMD ["nginx", "-c", "/etc/nginx/nginx.conf", "-g", "daemon off;"]
|
||||||
24
LICENSE.md
Normal file
24
LICENSE.md
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
All Data and [associated JSON](https://github.com/EDCD/coriolis-data) files are intellectual property and copyright of Frontier Developments plc ('Frontier', 'Frontier Developments') and are subject to their
|
||||||
|
[terms and conditions](https://www.frontierstore.net/terms-and-conditions/).
|
||||||
|
|
||||||
|
The code (Javascript, CSS, HTML, and SVG files only) specificially for Coriolis.io is released under the MIT License.
|
||||||
|
|
||||||
|
Copyright (c) 2015 Coriolis.io, Colin McLeod
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software (Javascript, CSS, HTML, and SVG files only), and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
@@ -10,6 +10,11 @@ Coriolis was created using assets and imagery from Elite: Dangerous, with the pe
|
|||||||
|
|
||||||
Please [submit issues](https://github.com/EDCD/coriolis/issues), or better yet [pull requests](https://github.com/EDCD/coriolis/pulls) for any corrections or additions to the database or the code.
|
Please [submit issues](https://github.com/EDCD/coriolis/issues), or better yet [pull requests](https://github.com/EDCD/coriolis/pulls) for any corrections or additions to the database or the code.
|
||||||
|
|
||||||
|
### Translations
|
||||||
|
|
||||||
|
Please use the OneSky translation site to suggest new translations: http://edcd-coriolis.oneskyapp.com
|
||||||
|
These will be merged regularly by the project manager.
|
||||||
|
|
||||||
### Feature Requests, Suggestions & Bugs
|
### Feature Requests, Suggestions & Bugs
|
||||||
|
|
||||||
Chat to us on [Discord](https://discord.gg/0uwCh6R62aPRjk9w)!
|
Chat to us on [Discord](https://discord.gg/0uwCh6R62aPRjk9w)!
|
||||||
@@ -18,11 +23,14 @@ Chat to us on [Discord](https://discord.gg/0uwCh6R62aPRjk9w)!
|
|||||||
|
|
||||||
See the [Developer's Guide](https://github.com/EDCD/coriolis/wiki/Developing-for-Coriolis) in the wiki.
|
See the [Developer's Guide](https://github.com/EDCD/coriolis/wiki/Developing-for-Coriolis) in the wiki.
|
||||||
|
|
||||||
|
Also see [the documentation site.](https://coriolis.willb.info/)
|
||||||
|
|
||||||
### Ship and Module Database
|
### Ship and Module Database
|
||||||
|
|
||||||
See the [Data wiki](https://github.com/cmmcleod/coriolis-data/wiki) for details on structure, etc.
|
See the [Data wiki](https://github.com/cmmcleod/coriolis-data/wiki) for details on structure, etc.
|
||||||
|
|
||||||
|
You can find hosted and compiled versions of these data-jsons under https://coriolis.io/data/ and https://beta.coriolis.io/data/.
|
||||||
|
You might want to load these as depedency instead of reyling on the npm-dependency.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
11
d3-funcs.js
vendored
11
d3-funcs.js
vendored
@@ -1,11 +0,0 @@
|
|||||||
export {
|
|
||||||
axisBottom,
|
|
||||||
axisLeft,
|
|
||||||
axisTop,
|
|
||||||
formatLocale,
|
|
||||||
line,
|
|
||||||
scaleBand,
|
|
||||||
scaleLinear,
|
|
||||||
scaleOrdinal,
|
|
||||||
select
|
|
||||||
} from 'd3';
|
|
||||||
1
devServer.js
Normal file → Executable file
1
devServer.js
Normal file → Executable file
@@ -5,6 +5,7 @@ var config = require('./webpack.config.dev');
|
|||||||
new WebpackDevServer(webpack(config), {
|
new WebpackDevServer(webpack(config), {
|
||||||
publicPath: config.output.publicPath,
|
publicPath: config.output.publicPath,
|
||||||
hot: true,
|
hot: true,
|
||||||
|
disableHostCheck: true,
|
||||||
headers: { "Access-Control-Allow-Origin": "*" },
|
headers: { "Access-Control-Allow-Origin": "*" },
|
||||||
historyApiFallback: {
|
historyApiFallback: {
|
||||||
rewrites: [
|
rewrites: [
|
||||||
|
|||||||
56
docker-compose.yml
Normal file
56
docker-compose.yml
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
version: '2.2'
|
||||||
|
|
||||||
|
services:
|
||||||
|
coriolis_prod:
|
||||||
|
image: edcd/coriolis:master
|
||||||
|
build:
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
args:
|
||||||
|
branch: master
|
||||||
|
restart: always
|
||||||
|
volumes:
|
||||||
|
- ./nginx.conf:/etc/nginx/nginx.conf
|
||||||
|
networks:
|
||||||
|
- web
|
||||||
|
labels:
|
||||||
|
- "traefik.docker.network=web"
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.basic.frontend.rule=Host:coriolis.io,coriolis.edcd.io"
|
||||||
|
- "traefik.basic.port=80"
|
||||||
|
- "traefik.basic.protocol=http"
|
||||||
|
|
||||||
|
coriolis_dev:
|
||||||
|
image: edcd/coriolis:develop
|
||||||
|
build:
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
args:
|
||||||
|
branch: develop
|
||||||
|
restart: always
|
||||||
|
volumes:
|
||||||
|
- ./nginx.conf:/etc/nginx/nginx.conf
|
||||||
|
networks:
|
||||||
|
- web
|
||||||
|
labels:
|
||||||
|
- "traefik.docker.network=web"
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.basic.frontend.rule=Host:beta.coriolis.io,beta.coriolis.edcd.io"
|
||||||
|
- "traefik.basic.port=80"
|
||||||
|
- "traefik.basic.protocol=http"
|
||||||
|
|
||||||
|
coriolis_dw2:
|
||||||
|
image: edcd/coriolis:dw2
|
||||||
|
restart: always
|
||||||
|
volumes:
|
||||||
|
- ./nginx.conf:/etc/nginx/nginx.conf
|
||||||
|
networks:
|
||||||
|
- web
|
||||||
|
labels:
|
||||||
|
- "traefik.docker.network=web"
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.basic.frontend.rule=Host:dw2.coriolis.io"
|
||||||
|
- "traefik.basic.port=80"
|
||||||
|
- "traefik.basic.protocol=http"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
web:
|
||||||
|
external: true
|
||||||
127
nginx.conf
127
nginx.conf
@@ -1,59 +1,96 @@
|
|||||||
worker_processes 2;
|
worker_processes 1;
|
||||||
error_log ./nginx.error.log;
|
user nobody nobody;
|
||||||
worker_rlimit_nofile 8192;
|
error_log /tmp/error.log;
|
||||||
pid nginx.pid;
|
pid /tmp/nginx.pid;
|
||||||
|
|
||||||
events {
|
events {
|
||||||
worker_connections 1024;
|
|
||||||
multi_accept on;
|
worker_connections 1024;
|
||||||
}
|
}
|
||||||
|
|
||||||
http {
|
http {
|
||||||
|
|
||||||
access_log off;
|
include /etc/nginx/mime.types;
|
||||||
charset UTF-8;
|
default_type application/octet-stream;
|
||||||
|
sendfile on;
|
||||||
|
client_body_temp_path /tmp/client_body;
|
||||||
|
fastcgi_temp_path /tmp/fastcgi_temp;
|
||||||
|
proxy_temp_path /tmp/proxy_temp;
|
||||||
|
scgi_temp_path /tmp/scgi_temp;
|
||||||
|
uwsgi_temp_path /tmp/uwsgi_temp;
|
||||||
|
access_log /tmp/access.log;
|
||||||
|
error_log /tmp/error.log;
|
||||||
|
|
||||||
types {
|
# https://nginx.org/en/docs/http/ngx_http_gzip_module.html
|
||||||
text/html html htm shtml;
|
# Enable gzip compression.
|
||||||
text/css css;
|
# Default: off
|
||||||
text/xml xml rss;
|
gzip off;
|
||||||
image/gif gif;
|
|
||||||
image/jpeg jpeg jpg;
|
|
||||||
application/x-javascript js;
|
|
||||||
text/plain txt;
|
|
||||||
image/png png;
|
|
||||||
image/svg+xml svg;
|
|
||||||
image/x-icon ico;
|
|
||||||
application/pdf pdf;
|
|
||||||
text/cache-manifest appcache;
|
|
||||||
}
|
|
||||||
|
|
||||||
gzip on;
|
# Compression level (1-9).
|
||||||
gzip_vary on;
|
# 5 is a perfect compromise between size and CPU usage, offering about
|
||||||
gzip_proxied any;
|
# 75% reduction for most ASCII files (almost identical to level 9).
|
||||||
gzip_comp_level 6;
|
# Default: 1
|
||||||
gzip_buffers 16 8k;
|
gzip_comp_level 5;
|
||||||
gzip_http_version 1.1;
|
|
||||||
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
|
|
||||||
|
|
||||||
server {
|
# Don't compress anything that's already small and unlikely to shrink much
|
||||||
listen 3301;
|
# if at all (the default is 20 bytes, which is bad as that usually leads to
|
||||||
server_name localhost;
|
# larger files after gzipping).
|
||||||
root ./build/;
|
# Default: 20
|
||||||
index index.html;
|
gzip_min_length 256;
|
||||||
|
|
||||||
location ~* \.(?:manifest|appcache|html?|xml|json|css|js|map|jpg|jpeg|gif|png|ico|svg|eot|ttf|woff|woff2)$ {
|
# Compress data even for clients that are connecting to us via proxies,
|
||||||
expires -1;
|
# identified by the "Via" header (required for CloudFront).
|
||||||
add_header Access-Control-Allow-Origin *;
|
# Default: off
|
||||||
|
gzip_proxied any;
|
||||||
|
|
||||||
|
# Tell proxies to cache both the gzipped and regular version of a resource
|
||||||
|
# whenever the client's Accept-Encoding capabilities header varies;
|
||||||
|
# Avoids the issue where a non-gzip capable client (which is extremely rare
|
||||||
|
# today) would display gibberish if their proxy gave them the gzipped version.
|
||||||
|
# Default: off
|
||||||
|
gzip_vary on;
|
||||||
|
|
||||||
|
# Compress all output labeled with one of the following MIME-types.
|
||||||
|
# text/html is always compressed by gzip module.
|
||||||
|
# Default: text/html
|
||||||
|
gzip_types *;
|
||||||
|
brotli on;
|
||||||
|
# brotli_static on;
|
||||||
|
brotli_types *;
|
||||||
|
# This should be turned on if you are going to have pre-compressed copies (.gz) of
|
||||||
|
# static files available. If not it should be left off as it will cause extra I/O
|
||||||
|
# for the check. It is best if you enable this in a location{} block for
|
||||||
|
# a specific directory, or on an individual server{} level.
|
||||||
|
# gzip_static on;
|
||||||
|
keepalive_timeout 3000;
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
listen [::]:80;
|
||||||
|
index index.html;
|
||||||
|
server_name localhost;
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
autoindex on;
|
||||||
|
|
||||||
|
location ~* \.(?:manifest|appcache|html?|xml|json|css|js|map|jpg|jpeg|gif|png|ico|svg|eot|ttf|woff|woff2)$ {
|
||||||
|
add_header Access-Control-Allow-Origin *;
|
||||||
|
add_header Access-Control-Allow-Credentials true;
|
||||||
|
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
|
||||||
|
add_header Access-Control-Allow-Headers 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
|
||||||
|
access_log off;
|
||||||
|
}
|
||||||
|
location /service-worker.js {
|
||||||
|
expires -1;
|
||||||
|
add_header Access-Control-Allow-Origin *;
|
||||||
add_header Access-Control-Allow-Credentials true;
|
add_header Access-Control-Allow-Credentials true;
|
||||||
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
|
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
|
||||||
add_header Access-Control-Allow-Headers 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
|
add_header Access-Control-Allow-Headers 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
|
||||||
access_log off;
|
access_log off;
|
||||||
}
|
}
|
||||||
|
location / {
|
||||||
location / {
|
try_files $uri $uri/ /index.html =404;
|
||||||
try_files $uri $uri/ /index.html =404;
|
}
|
||||||
}
|
location /iframe.html {
|
||||||
}
|
try_files $uri $uri/ /iframe.html =404;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
125
package.json
125
package.json
@@ -1,17 +1,16 @@
|
|||||||
{
|
{
|
||||||
"name": "coriolis_shipyard",
|
"name": "coriolis_shipyard",
|
||||||
"version": "2.3.7",
|
"version": "3.0.0",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/EDCD/coriolis"
|
"url": "https://github.com/EDCD/coriolis"
|
||||||
},
|
},
|
||||||
"homepage": "https://coriolis.edcd.io",
|
"homepage": "https://coriolis.io",
|
||||||
"bugs": "https://github.com/EDCD/coriolis/issues",
|
"bugs": "https://github.com/EDCD/coriolis/issues",
|
||||||
"private": true,
|
"private": true,
|
||||||
"engine": "node >= 4.8.1",
|
"engine": "node >= 4.8.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepublish": "rollup -c && uglifyjs d3.js -c -m -o d3.min.js",
|
|
||||||
"extract-translations": "grep -hroE \"(translate\\('[^']+'\\))|(tip.bind\\(null, '[^']+')\" src/* | grep -oE \"'[^']+'\" | grep -oE \"[^']+\" | sort -u -f",
|
"extract-translations": "grep -hroE \"(translate\\('[^']+'\\))|(tip.bind\\(null, '[^']+')\" src/* | grep -oE \"'[^']+'\" | grep -oE \"[^']+\" | sort -u -f",
|
||||||
"clean": "rimraf build",
|
"clean": "rimraf build",
|
||||||
"start": "node devServer.js",
|
"start": "node devServer.js",
|
||||||
@@ -19,12 +18,14 @@
|
|||||||
"test": "jest",
|
"test": "jest",
|
||||||
"prod-serve": "nginx -p $(pwd) -c nginx.conf",
|
"prod-serve": "nginx -p $(pwd) -c nginx.conf",
|
||||||
"prod-stop": "kill -QUIT $(cat nginx.pid)",
|
"prod-stop": "kill -QUIT $(cat nginx.pid)",
|
||||||
"build": "npm run clean && NODE_ENV=production webpack -p --config webpack.config.prod.js",
|
"build": "npm run clean && cross-env NODE_ENV=production webpack -p --config webpack.config.prod.js",
|
||||||
"rsync": "rsync -ae \"ssh -i $CORIOLIS_PEM\" --delete build/ $CORIOLIS_USER@$CORIOLIS_HOST:~/wwws",
|
"rsync": "rsync -ae \"ssh -i $CORIOLIS_PEM\" --delete build/ $CORIOLIS_USER@$CORIOLIS_HOST:~/wwws",
|
||||||
"deploy": "npm run lint && npm test && npm run build && npm run rsync"
|
"deploy": "npm run lint && npm test && npm run build && npm run rsync"
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"transform": {".*": "<rootDir>/node_modules/babel-jest"},
|
"transform": {
|
||||||
|
".*": "<rootDir>/node_modules/babel-jest"
|
||||||
|
},
|
||||||
"testRegex": "(/__tests__/test-.*|\\.(test|spec))\\.js$",
|
"testRegex": "(/__tests__/test-.*|\\.(test|spec))\\.js$",
|
||||||
"moduleFileExtensions": [
|
"moduleFileExtensions": [
|
||||||
"js",
|
"js",
|
||||||
@@ -37,6 +38,7 @@
|
|||||||
"<rootDir>/node_modules/lodash",
|
"<rootDir>/node_modules/lodash",
|
||||||
"<rootDir>/node_modules/react",
|
"<rootDir>/node_modules/react",
|
||||||
"<rootDir>/node_modules/react-dom",
|
"<rootDir>/node_modules/react-dom",
|
||||||
|
"<rootDir>/node_modules/react-transition-group",
|
||||||
"<rootDir>/node_modules/react-testutils-additions",
|
"<rootDir>/node_modules/react-testutils-additions",
|
||||||
"<rootDir>/node_modules/fbjs",
|
"<rootDir>/node_modules/fbjs",
|
||||||
"<rootDir>/node_modules/fbemitter",
|
"<rootDir>/node_modules/fbemitter",
|
||||||
@@ -53,55 +55,92 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"appcache-webpack-plugin": "^1.3.0",
|
"@babel/core": "^7.0.0",
|
||||||
"babel-core": "*",
|
"@babel/plugin-proposal-class-properties": "^7.0.0",
|
||||||
"babel-eslint": "*",
|
"@babel/plugin-proposal-decorators": "^7.0.0",
|
||||||
"babel-jest": "*",
|
"@babel/plugin-proposal-do-expressions": "^7.0.0",
|
||||||
"babel-loader": "*",
|
"@babel/plugin-proposal-export-default-from": "^7.0.0",
|
||||||
"babel-preset-es2015": "*",
|
"@babel/plugin-proposal-export-namespace-from": "^7.0.0",
|
||||||
"babel-preset-react": "*",
|
"@babel/plugin-proposal-function-bind": "^7.0.0",
|
||||||
"babel-preset-stage-0": "*",
|
"@babel/plugin-proposal-function-sent": "^7.0.0",
|
||||||
"css-loader": "^0.28.0",
|
"@babel/plugin-proposal-json-strings": "^7.0.0",
|
||||||
"d3-selection": "1",
|
"@babel/plugin-proposal-logical-assignment-operators": "^7.0.0",
|
||||||
"eslint": "3.19.0",
|
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.0.0",
|
||||||
"eslint-plugin-react": "^6.10.3",
|
"@babel/plugin-proposal-numeric-separator": "^7.0.0",
|
||||||
"expose-loader": "^0.7.3",
|
"@babel/plugin-proposal-optional-chaining": "^7.0.0",
|
||||||
"express": "^4.15.2",
|
"@babel/plugin-proposal-pipeline-operator": "^7.0.0",
|
||||||
"extract-text-webpack-plugin": "2.1.0",
|
"@babel/plugin-proposal-throw-expressions": "^7.0.0",
|
||||||
"file-loader": "^0.11.1",
|
"@babel/plugin-syntax-dynamic-import": "^7.0.0",
|
||||||
"html-webpack-plugin": "^2.28.0",
|
"@babel/plugin-syntax-import-meta": "^7.0.0",
|
||||||
"jest-cli": "^19.0.2",
|
"@babel/preset-env": "^7.0.0",
|
||||||
|
"@babel/preset-react": "^7.0.0",
|
||||||
|
"appcache-webpack-plugin": "^1.4.0",
|
||||||
|
"babel-core": "^7.0.0-bridge.0",
|
||||||
|
"babel-eslint": "^10.0.1",
|
||||||
|
"babel-jest": "^23.6.0",
|
||||||
|
"babel-loader": "^8.0.0",
|
||||||
|
"copy-webpack-plugin": "^4.5.2",
|
||||||
|
"create-react-class": "^15.6.3",
|
||||||
|
"cross-env": "^5.2.0",
|
||||||
|
"css-loader": "^1.0.0",
|
||||||
|
"d3-selection": "^1.3.2",
|
||||||
|
"esdoc": "^1.1.0",
|
||||||
|
"esdoc-custom-theme": "^1.4.2",
|
||||||
|
"esdoc-ecmascript-proposal-plugin": "^1.0.0",
|
||||||
|
"esdoc-jsx-plugin": "^1.0.0",
|
||||||
|
"esdoc-publish-html-plugin": "^1.1.2",
|
||||||
|
"esdoc-react-plugin": "^1.0.1",
|
||||||
|
"esdoc-standard-plugin": "^1.0.0",
|
||||||
|
"eslint": "^5.6.0",
|
||||||
|
"eslint-plugin-react": "^7.11.1",
|
||||||
|
"expose-loader": "^0.7.5",
|
||||||
|
"express": "^4.16.3",
|
||||||
|
"extract-text-webpack-plugin": "^4.0.0-beta.0",
|
||||||
|
"file-loader": "^2.0.0",
|
||||||
|
"html-webpack-plugin": "^3.0.7",
|
||||||
|
"jest-cli": "^23.6.0",
|
||||||
"jsen": "^0.6.4",
|
"jsen": "^0.6.4",
|
||||||
"json-loader": "^0.5.4",
|
"json-loader": "^0.5.4",
|
||||||
"less": "^2.7.2",
|
"less": "^3.8.1",
|
||||||
"less-loader": "^4.0.3",
|
"less-loader": "^4.1.0",
|
||||||
"react-addons-perf": "^15.4.2",
|
"react-addons-perf": "^15.4.2",
|
||||||
"react-measure": "^1.4.7",
|
"react-container-dimensions": "^1.4.1",
|
||||||
"react-testutils-additions": "^15.2.0",
|
"react-testutils-additions": "^16.0.0",
|
||||||
|
"react-transition-group": "^2.5.0",
|
||||||
"rimraf": "^2.6.1",
|
"rimraf": "^2.6.1",
|
||||||
"rollup": "0.41",
|
"rollup": "^0.66.2",
|
||||||
"rollup-plugin-node-resolve": "3",
|
"rollup-plugin-node-resolve": "^3.4.0",
|
||||||
"style-loader": "^0.16.1",
|
"style-loader": "^0.23.0",
|
||||||
"url-loader": "^0.5.8",
|
"uglify-js": "^3.4.9",
|
||||||
"webpack": "^2.4.1",
|
"url-loader": "^1.1.1",
|
||||||
"webpack-dev-server": "^2.4.4",
|
"webpack": "^4.20.2",
|
||||||
"uglify-js": "^2.4.11"
|
"webpack-bugsnag-plugins": "^1.2.2",
|
||||||
|
"webpack-cli": "^3.1.1",
|
||||||
|
"webpack-dev-server": "^3.1.9",
|
||||||
|
"webpack-notifier": "^1.6.0",
|
||||||
|
"workbox-webpack-plugin": "^3.6.1"
|
||||||
},
|
},
|
||||||
|
"sideEffects": false,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"babel-polyfill": "*",
|
"@babel/polyfill": "^7.0.0",
|
||||||
"browserify-zlib-next": "^1.0.1",
|
"browserify-zlib-next": "^1.0.1",
|
||||||
"classnames": "^2.2.5",
|
"classnames": "^2.2.6",
|
||||||
"coriolis-data": "EDCD/coriolis-data",
|
"coriolis-data": "../coriolis-data",
|
||||||
"d3": "4.8.0",
|
"d3": "^5.7.0",
|
||||||
"detect-browser": "^1.7.0",
|
"detect-browser": "^3.0.1",
|
||||||
"fbemitter": "^2.1.1",
|
"fbemitter": "^2.1.1",
|
||||||
"lodash": "^4.17.4",
|
"lodash": "^4.17.11",
|
||||||
"lz-string": "^1.4.4",
|
"lz-string": "^1.4.4",
|
||||||
"prop-types": "^15.5.8",
|
"pako": "^1.0.6",
|
||||||
|
"prop-types": "^15.6.2",
|
||||||
"react": "^15.5.4",
|
"react": "^15.5.4",
|
||||||
"react-dom": "^15.5.4",
|
"react-dom": "^15.5.4",
|
||||||
|
"react-extras": "^0.7.1",
|
||||||
|
"react-fuzzy": "^0.5.2",
|
||||||
|
"react-ga": "^2.5.3",
|
||||||
"react-number-editor": "Athanasius/react-number-editor.git#miggy",
|
"react-number-editor": "Athanasius/react-number-editor.git#miggy",
|
||||||
"recharts": "^0.22.3",
|
"recharts": "^1.2.0",
|
||||||
"superagent": "^3.5.2"
|
"register-service-worker": "^1.5.2",
|
||||||
|
"superagent": "^3.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import Router from './Router';
|
import Router from './Router';
|
||||||
|
import { register } from 'register-service-worker';
|
||||||
import { EventEmitter } from 'fbemitter';
|
import { EventEmitter } from 'fbemitter';
|
||||||
import { getLanguage } from './i18n/Language';
|
import { getLanguage } from './i18n/Language';
|
||||||
import Persist from './stores/Persist';
|
import Persist from './stores/Persist';
|
||||||
|
|
||||||
|
import Announcement from './components/Announcement';
|
||||||
import Header from './components/Header';
|
import Header from './components/Header';
|
||||||
import Tooltip from './components/Tooltip';
|
import Tooltip from './components/Tooltip';
|
||||||
import ModalExport from './components/ModalExport';
|
import ModalExport from './components/ModalExport';
|
||||||
@@ -12,7 +14,7 @@ import ModalHelp from './components/ModalHelp';
|
|||||||
import ModalImport from './components/ModalImport';
|
import ModalImport from './components/ModalImport';
|
||||||
import ModalPermalink from './components/ModalPermalink';
|
import ModalPermalink from './components/ModalPermalink';
|
||||||
import * as CompanionApiUtils from './utils/CompanionApiUtils';
|
import * as CompanionApiUtils from './utils/CompanionApiUtils';
|
||||||
|
import * as JournalUtils from './utils/JournalUtils';
|
||||||
import AboutPage from './pages/AboutPage';
|
import AboutPage from './pages/AboutPage';
|
||||||
import NotFoundPage from './pages/NotFoundPage';
|
import NotFoundPage from './pages/NotFoundPage';
|
||||||
import OutfittingPage from './pages/OutfittingPage';
|
import OutfittingPage from './pages/OutfittingPage';
|
||||||
@@ -20,13 +22,13 @@ import ComparisonPage from './pages/ComparisonPage';
|
|||||||
import ShipyardPage from './pages/ShipyardPage';
|
import ShipyardPage from './pages/ShipyardPage';
|
||||||
import ErrorDetails from './pages/ErrorDetails';
|
import ErrorDetails from './pages/ErrorDetails';
|
||||||
|
|
||||||
const zlib = require('zlib');
|
const zlib = require('pako');
|
||||||
|
const request = require('superagent');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Coriolis App
|
* Coriolis App
|
||||||
*/
|
*/
|
||||||
export default class Coriolis extends React.Component {
|
export default class Coriolis extends React.Component {
|
||||||
|
|
||||||
static childContextTypes = {
|
static childContextTypes = {
|
||||||
closeMenu: PropTypes.func.isRequired,
|
closeMenu: PropTypes.func.isRequired,
|
||||||
hideModal: PropTypes.func.isRequired,
|
hideModal: PropTypes.func.isRequired,
|
||||||
@@ -65,11 +67,12 @@ export default class Coriolis extends React.Component {
|
|||||||
this.state = {
|
this.state = {
|
||||||
noTouch: !('ontouchstart' in window || navigator.msMaxTouchPoints || navigator.maxTouchPoints),
|
noTouch: !('ontouchstart' in window || navigator.msMaxTouchPoints || navigator.maxTouchPoints),
|
||||||
page: null,
|
page: null,
|
||||||
|
announcements: [],
|
||||||
language: getLanguage(Persist.getLangCode()),
|
language: getLanguage(Persist.getLangCode()),
|
||||||
route: {},
|
route: {},
|
||||||
sizeRatio: Persist.getSizeRatio()
|
sizeRatio: Persist.getSizeRatio()
|
||||||
};
|
};
|
||||||
|
this._getAnnouncements();
|
||||||
Router('', (r) => this._setPage(ShipyardPage, r));
|
Router('', (r) => this._setPage(ShipyardPage, r));
|
||||||
Router('/import?', (r) => this._importBuild(r));
|
Router('/import?', (r) => this._importBuild(r));
|
||||||
Router('/import/:data', (r) => this._importBuild(r));
|
Router('/import/:data', (r) => this._importBuild(r));
|
||||||
@@ -90,17 +93,34 @@ export default class Coriolis extends React.Component {
|
|||||||
_importBuild(r) {
|
_importBuild(r) {
|
||||||
try {
|
try {
|
||||||
// Need to decode and gunzip the data, then build the ship
|
// Need to decode and gunzip the data, then build the ship
|
||||||
const data = zlib.gunzipSync(new Buffer(r.params.data, 'base64'));
|
const data = zlib.inflate(new Buffer(r.params.data, 'base64'), { to: 'string' });
|
||||||
const json = JSON.parse(data);
|
const json = JSON.parse(data);
|
||||||
const ship = CompanionApiUtils.shipFromJson(json);
|
console.info('Ship import data: ');
|
||||||
|
console.info(json);
|
||||||
|
let ship;
|
||||||
|
if (json && json.modules) {
|
||||||
|
ship = CompanionApiUtils.shipFromJson(json);
|
||||||
|
} else if (json && json.Modules) {
|
||||||
|
ship = JournalUtils.shipFromLoadoutJSON(json);
|
||||||
|
}
|
||||||
r.params.ship = ship.id;
|
r.params.ship = ship.id;
|
||||||
r.params.code = ship.toString();
|
r.params.code = ship.toString();
|
||||||
this._setPage(OutfittingPage, r);
|
this._setPage(OutfittingPage, r)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this._onError('Failed to import ship', r.path, 0, 0, err);
|
this._onError('Failed to import ship', r.path, 0, 0, err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async _getAnnouncements() {
|
||||||
|
try {
|
||||||
|
const announces = await request.get('https://orbis.zone/api/announcement')
|
||||||
|
.query({ showInCoriolis: true });
|
||||||
|
this.setState({ announcements: announces.body });
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates / Sets the page and route context
|
* Updates / Sets the page and route context
|
||||||
* @param {[type]} page The page to be shown
|
* @param {[type]} page The page to be shown
|
||||||
@@ -124,6 +144,13 @@ export default class Coriolis extends React.Component {
|
|||||||
*/
|
*/
|
||||||
_onError(msg, scriptUrl, line, col, errObj) {
|
_onError(msg, scriptUrl, line, col, errObj) {
|
||||||
console && console.error && console.error(arguments); // eslint-disable-line no-console
|
console && console.error && console.error(arguments); // eslint-disable-line no-console
|
||||||
|
if (errObj) {
|
||||||
|
if (errObj instanceof Error) {
|
||||||
|
bugsnagClient.notify(errObj); // eslint-disable-line
|
||||||
|
} else if (errObj instanceof String) {
|
||||||
|
bugsnagClient.notify(msg, errObj); // eslint-disable-line
|
||||||
|
}
|
||||||
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
error: <ErrorDetails error={{ message: msg, details: { scriptUrl, line, col, error: JSON.stringify(errObj) } }}/>,
|
error: <ErrorDetails error={{ message: msg, details: { scriptUrl, line, col, error: JSON.stringify(errObj) } }}/>,
|
||||||
page: null,
|
page: null,
|
||||||
@@ -165,13 +192,13 @@ export default class Coriolis extends React.Component {
|
|||||||
case 72: // 'h'
|
case 72: // 'h'
|
||||||
if (e.ctrlKey || e.metaKey) { // CTRL/CMD + h
|
if (e.ctrlKey || e.metaKey) { // CTRL/CMD + h
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this._showModal(<ModalHelp />);
|
this._showModal(<ModalHelp/>);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 73: // 'i'
|
case 73: // 'i'
|
||||||
if (e.ctrlKey || e.metaKey) { // CTRL/CMD + i
|
if (e.ctrlKey || e.metaKey) { // CTRL/CMD + i
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this._showModal(<ModalImport />);
|
this._showModal(<ModalImport/>);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 79: // 'o'
|
case 79: // 'o'
|
||||||
@@ -193,7 +220,7 @@ export default class Coriolis extends React.Component {
|
|||||||
* @param {React.Component} content Modal Content
|
* @param {React.Component} content Modal Content
|
||||||
*/
|
*/
|
||||||
_showModal(content) {
|
_showModal(content) {
|
||||||
let modal = <div className='modal-bg' onClick={(e) => this._hideModal() }>{content}</div>;
|
let modal = <div className='modal-bg' onClick={(e) => this._hideModal()}>{content}</div>;
|
||||||
this.setState({ modal });
|
this.setState({ modal });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,7 +298,7 @@ export default class Coriolis extends React.Component {
|
|||||||
return this.emitter.addListener('windowResize', listener);
|
return this.emitter.addListener('windowResize', listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a listener to global commands such as save,
|
* Add a listener to global commands such as save,
|
||||||
* @param {Function} listener Listener callback
|
* @param {Function} listener Listener callback
|
||||||
* @return {Object} Subscription token
|
* @return {Object} Subscription token
|
||||||
@@ -307,14 +334,50 @@ export default class Coriolis extends React.Component {
|
|||||||
*/
|
*/
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
// Listen for appcache updated event, present refresh to update view
|
// Listen for appcache updated event, present refresh to update view
|
||||||
if (window.applicationCache) {
|
// Check that service workers are registered
|
||||||
window.applicationCache.addEventListener('updateready', () => {
|
if (navigator.storage && navigator.storage.persist) {
|
||||||
if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
|
window.addEventListener('load', () => {
|
||||||
this.setState({ appCacheUpdate: true }); // Browser downloaded a new app cache.
|
navigator.storage.persist().then(granted => {
|
||||||
}
|
if (granted)
|
||||||
|
console.log('Storage will not be cleared except by explicit user action');
|
||||||
|
else
|
||||||
|
console.log('Storage may be cleared by the UA under storage pressure.');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if ('serviceWorker' in navigator) {
|
||||||
|
// Your service-worker.js *must* be located at the top-level directory relative to your site.
|
||||||
|
// It won't be able to control pages unless it's located at the same level or higher than them.
|
||||||
|
// *Don't* register service worker file in, e.g., a scripts/ sub-directory!
|
||||||
|
// See https://github.com/slightlyoff/ServiceWorker/issues/468
|
||||||
|
const self = this;
|
||||||
|
if (process.env.NODE_ENV === 'production') {
|
||||||
|
register('/service-worker.js', {
|
||||||
|
ready(registration) {
|
||||||
|
console.log('Service worker is active.');
|
||||||
|
},
|
||||||
|
registered(registration) {
|
||||||
|
console.log('Service worker has been registered.');
|
||||||
|
},
|
||||||
|
cached(registration) {
|
||||||
|
console.log('Content has been cached for offline use.');
|
||||||
|
},
|
||||||
|
updatefound(registration) {
|
||||||
|
console.log('New content is downloading.');
|
||||||
|
},
|
||||||
|
updated(registration) {
|
||||||
|
self.setState({ appCacheUpdate: true });
|
||||||
|
console.log('New content is available; please refresh.');
|
||||||
|
},
|
||||||
|
offline() {
|
||||||
|
console.log('No internet connection found. App is running in offline mode.');
|
||||||
|
},
|
||||||
|
error(error) {
|
||||||
|
console.error('Error during service worker registration:', error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
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.getElementById('coriolis').addEventListener('scroll', () => this._tooltip());
|
document.getElementById('coriolis').addEventListener('scroll', () => this._tooltip());
|
||||||
@@ -332,14 +395,26 @@ export default class Coriolis extends React.Component {
|
|||||||
render() {
|
render() {
|
||||||
let currentMenu = this.state.currentMenu;
|
let currentMenu = this.state.currentMenu;
|
||||||
|
|
||||||
return <div style={{ minHeight: '100%' }} onClick={this._closeMenu} className={ this.state.noTouch ? 'no-touch' : null }>
|
return <div style={{ minHeight: '100%' }} onClick={this._closeMenu}
|
||||||
<Header appCacheUpdate={this.state.appCacheUpdate} currentMenu={currentMenu} />
|
className={this.state.noTouch ? 'no-touch' : null}>
|
||||||
{ this.state.error ? this.state.error : this.state.page ? React.createElement(this.state.page, { currentMenu }) : <NotFoundPage/> }
|
<Header announcements={this.state.announcements} appCacheUpdate={this.state.appCacheUpdate}
|
||||||
{ this.state.modal }
|
currentMenu={currentMenu}/>
|
||||||
{ this.state.tooltip }
|
<div className="announcement-container">{this.state.announcements.map(a => <Announcement
|
||||||
|
text={a.message}/>)}</div>
|
||||||
|
{this.state.error ? this.state.error : this.state.page ? React.createElement(this.state.page, { currentMenu }) :
|
||||||
|
<NotFoundPage/>}
|
||||||
|
{this.state.modal}
|
||||||
|
{this.state.tooltip}
|
||||||
<footer>
|
<footer>
|
||||||
<div className="right cap">
|
<div className="right cap">
|
||||||
<a href="https://github.com/EDCD/coriolis" target="_blank" title="Coriolis Github Project">{window.CORIOLIS_VERSION} - {window.CORIOLIS_DATE}</a>
|
<a href="https://github.com/EDCD/coriolis" target="_blank" rel="noopener noreferrer"
|
||||||
|
title="Coriolis Github Project">{window.CORIOLIS_VERSION} - {window.CORIOLIS_DATE}</a>
|
||||||
|
<br/>
|
||||||
|
<a
|
||||||
|
href={'https://github.com/EDCD/coriolis/compare/edcd:develop@{' + window.CORIOLIS_DATE + '}...edcd:develop'}
|
||||||
|
target="_blank" rel="noopener noreferrer" title={'Coriolis Commits since' + window.CORIOLIS_DATE}>Commits
|
||||||
|
since last release
|
||||||
|
({window.CORIOLIS_DATE})</a>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</div>;
|
</div>;
|
||||||
|
|||||||
@@ -257,9 +257,8 @@ Route.prototype.match = function(path, params) {
|
|||||||
* @param {string} path Path to track
|
* @param {string} path Path to track
|
||||||
*/
|
*/
|
||||||
function gaTrack(path) {
|
function gaTrack(path) {
|
||||||
if (window.ga) {
|
const _paq = window._paq || [];
|
||||||
window.ga('send', 'pageview', path);
|
_paq.push(['trackPageView']);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ function isActive(href) {
|
|||||||
* Active Link - Highlighted when URL matches window location
|
* Active Link - Highlighted when URL matches window location
|
||||||
*/
|
*/
|
||||||
export default class ActiveLink extends Link {
|
export default class ActiveLink extends Link {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders the component
|
* Renders the component
|
||||||
* @return {React.Component} The active link
|
* @return {React.Component} The active link
|
||||||
@@ -29,5 +28,4 @@ export default class ActiveLink extends Link {
|
|||||||
|
|
||||||
return <a {...this.props} className={className} onClick={this.handler}>{this.props.children}</a>;
|
return <a {...this.props} className={className} onClick={this.handler}>{this.props.children}</a>;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|||||||
31
src/app/components/Announcement.jsx
Normal file
31
src/app/components/Announcement.jsx
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { autoBind } from 'react-extras';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Announcement component
|
||||||
|
*/
|
||||||
|
export default class Announcement extends React.Component {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
text: PropTypes.string
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param {Object} props React Component properties
|
||||||
|
*/
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
autoBind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the announcement
|
||||||
|
* @return {React.Component} A href element
|
||||||
|
*/
|
||||||
|
render() {
|
||||||
|
return <div className="announcement" >{this.props.text}</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import TranslatedComponent from './TranslatedComponent';
|
|||||||
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
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';
|
||||||
|
import FuzzySearch from 'react-fuzzy';
|
||||||
|
|
||||||
const PRESS_THRESHOLD = 500; // mouse/touch down threshold
|
const PRESS_THRESHOLD = 500; // mouse/touch down threshold
|
||||||
|
|
||||||
@@ -20,6 +21,7 @@ const GRPCAT = {
|
|||||||
'fx': 'limpet controllers',
|
'fx': 'limpet controllers',
|
||||||
'hb': 'limpet controllers',
|
'hb': 'limpet controllers',
|
||||||
'pc': 'limpet controllers',
|
'pc': 'limpet controllers',
|
||||||
|
'rpl': 'limpet controllers',
|
||||||
'pce': 'passenger cabins',
|
'pce': 'passenger cabins',
|
||||||
'pci': 'passenger cabins',
|
'pci': 'passenger cabins',
|
||||||
'pcm': 'passenger cabins',
|
'pcm': 'passenger cabins',
|
||||||
@@ -36,10 +38,20 @@ const GRPCAT = {
|
|||||||
'ml': 'lasers',
|
'ml': 'lasers',
|
||||||
'c': 'projectiles',
|
'c': 'projectiles',
|
||||||
'mc': 'projectiles',
|
'mc': 'projectiles',
|
||||||
|
'axmc': 'experimental',
|
||||||
'fc': 'projectiles',
|
'fc': 'projectiles',
|
||||||
|
'rfl': 'experimental',
|
||||||
'pa': 'projectiles',
|
'pa': 'projectiles',
|
||||||
'rg': 'projectiles',
|
'rg': 'projectiles',
|
||||||
'mr': 'ordnance',
|
'mr': 'ordnance',
|
||||||
|
'axmr': 'experimental',
|
||||||
|
'rcpl': 'experimental',
|
||||||
|
'dtl': 'experimental',
|
||||||
|
'tbsc': 'experimental',
|
||||||
|
'tbem': 'experimental',
|
||||||
|
'tbrfl': 'experimental',
|
||||||
|
'mahr': 'experimental',
|
||||||
|
'rsl': 'experimental',
|
||||||
'tp': 'ordnance',
|
'tp': 'ordnance',
|
||||||
'nl': 'ordnance',
|
'nl': 'ordnance',
|
||||||
'sc': 'scanners',
|
'sc': 'scanners',
|
||||||
@@ -48,9 +60,29 @@ const GRPCAT = {
|
|||||||
'cs': 'scanners',
|
'cs': 'scanners',
|
||||||
'kw': 'scanners',
|
'kw': 'scanners',
|
||||||
'ws': 'scanners',
|
'ws': 'scanners',
|
||||||
|
'xs': 'scanners',
|
||||||
'ch': 'defence',
|
'ch': 'defence',
|
||||||
'po': 'defence',
|
'po': 'defence',
|
||||||
'ec': 'defence',
|
'ec': 'defence',
|
||||||
|
'sfn': 'defence',
|
||||||
|
// Guardian
|
||||||
|
'gpp': 'guardian',
|
||||||
|
'gpc': 'guardian',
|
||||||
|
'gsrp': 'guardian',
|
||||||
|
'ggc': 'guardian',
|
||||||
|
'gfsb': 'guardian',
|
||||||
|
'gmrp': 'guardian',
|
||||||
|
'gsc': 'guardian',
|
||||||
|
'ghrp': 'guardian',
|
||||||
|
|
||||||
|
// Mining
|
||||||
|
'scl': 'mining',
|
||||||
|
'pwa': 'mining',
|
||||||
|
'sdm': 'mining',
|
||||||
|
|
||||||
|
// Assists
|
||||||
|
'dc': 'flight assists',
|
||||||
|
'sua': 'flight assists',
|
||||||
};
|
};
|
||||||
// Order here is the order in which items will be shown in the modules menu
|
// Order here is the order in which items will be shown in the modules menu
|
||||||
const CATEGORIES = {
|
const CATEGORIES = {
|
||||||
@@ -60,14 +92,15 @@ const CATEGORIES = {
|
|||||||
'fi': ['fi'],
|
'fi': ['fi'],
|
||||||
'fuel': ['ft', 'fs'],
|
'fuel': ['ft', 'fs'],
|
||||||
'hangars': ['fh', 'pv'],
|
'hangars': ['fh', 'pv'],
|
||||||
'limpet controllers': ['cc', 'fx', 'hb', 'pc'],
|
'limpet controllers': ['cc', 'fx', 'hb', 'pc', 'rpl'],
|
||||||
'passenger cabins': ['pce', 'pci', 'pcm', 'pcq'],
|
'passenger cabins': ['pce', 'pci', 'pcm', 'pcq'],
|
||||||
'rf': ['rf'],
|
'rf': ['rf'],
|
||||||
'shields': ['sg', 'bsg', 'psg', 'scb'],
|
'shields': ['sg', 'bsg', 'psg', 'scb'],
|
||||||
'structural reinforcement': ['hr', 'mrp'],
|
'structural reinforcement': ['hr', 'mrp'],
|
||||||
'dc': ['dc'],
|
'flight assists': ['dc', 'sua'],
|
||||||
|
|
||||||
// Hardpoints
|
// Hardpoints
|
||||||
'lasers': ['pl', 'ul', 'bl', 'ml'],
|
'lasers': ['pl', 'ul', 'bl'],
|
||||||
'projectiles': ['mc', 'c', 'fc', 'pa', 'rg'],
|
'projectiles': ['mc', 'c', 'fc', 'pa', 'rg'],
|
||||||
'ordnance': ['mr', 'tp', 'nl'],
|
'ordnance': ['mr', 'tp', 'nl'],
|
||||||
// Utilities
|
// Utilities
|
||||||
@@ -75,24 +108,30 @@ const CATEGORIES = {
|
|||||||
'hs': ['hs'],
|
'hs': ['hs'],
|
||||||
'defence': ['ch', 'po', 'ec'],
|
'defence': ['ch', 'po', 'ec'],
|
||||||
'scanners': ['sc', 'ss', 'cs', 'kw', 'ws'], // Overloaded with internal scanners
|
'scanners': ['sc', 'ss', 'cs', 'kw', 'ws'], // Overloaded with internal scanners
|
||||||
|
// Experimental
|
||||||
|
'experimental': ['axmc', 'axmr', 'rfl', 'tbrfl', 'tbsc', 'tbem', 'xs', 'sfn', 'rcpl', 'dtl', 'rsl', 'mahr',],
|
||||||
|
|
||||||
|
// Guardian
|
||||||
|
'guardian': ['gpp', 'gpd', 'gpc', 'ggc', 'gsrp', 'gfsb', 'ghrp', 'gmrp', 'gsc'],
|
||||||
|
|
||||||
|
'mining': ['ml', 'scl', 'pwa', 'sdm', 'abl'],
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Available modules menu
|
* Available modules menu
|
||||||
*/
|
*/
|
||||||
export default class AvailableModulesMenu extends TranslatedComponent {
|
export default class AvailableModulesMenu extends TranslatedComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
modules: PropTypes.oneOfType([PropTypes.object, PropTypes.array]).isRequired,
|
modules: PropTypes.oneOfType([PropTypes.object, PropTypes.array]).isRequired,
|
||||||
onSelect: PropTypes.func.isRequired,
|
onSelect: PropTypes.func.isRequired,
|
||||||
diffDetails: PropTypes.func,
|
diffDetails: PropTypes.func,
|
||||||
m: PropTypes.object,
|
m: PropTypes.object,
|
||||||
shipMass: PropTypes.number,
|
ship: PropTypes.object.isRequired,
|
||||||
warning: PropTypes.func
|
warning: PropTypes.func,
|
||||||
};
|
firstSlotId: PropTypes.string,
|
||||||
|
lastSlotId: PropTypes.string,
|
||||||
static defaultProps = {
|
activeSlotId: PropTypes.string,
|
||||||
shipMass: 0
|
slotDiv: PropTypes.object
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -103,7 +142,9 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
|||||||
constructor(props, context) {
|
constructor(props, context) {
|
||||||
super(props);
|
super(props);
|
||||||
this._hideDiff = this._hideDiff.bind(this);
|
this._hideDiff = this._hideDiff.bind(this);
|
||||||
|
this._showSearch = this._showSearch.bind(this);
|
||||||
this.state = this._initState(props, context);
|
this.state = this._initState(props, context);
|
||||||
|
this.slotItems = [];// Array to hold <li> refs.
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -114,27 +155,33 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
|||||||
*/
|
*/
|
||||||
_initState(props, context) {
|
_initState(props, context) {
|
||||||
let translate = context.language.translate;
|
let translate = context.language.translate;
|
||||||
let { m, warning, shipMass, onSelect, modules } = props;
|
let { m, warning, onSelect, modules, ship } = props;
|
||||||
let list, currentGroup;
|
let list, currentGroup;
|
||||||
|
|
||||||
let buildGroup = this._buildGroup.bind(
|
let buildGroup = this._buildGroup.bind(
|
||||||
this,
|
this,
|
||||||
|
ship,
|
||||||
translate,
|
translate,
|
||||||
m,
|
m,
|
||||||
warning,
|
warning,
|
||||||
shipMass - (m && m.mass ? m.mass : 0),
|
|
||||||
(m, event) => {
|
(m, event) => {
|
||||||
this._hideDiff(event);
|
this._hideDiff(event);
|
||||||
onSelect(m);
|
onSelect(m);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
let fuzzy = [];
|
||||||
if (modules instanceof Array) {
|
if (modules instanceof Array) {
|
||||||
list = buildGroup(modules[0].grp, modules);
|
list = buildGroup(modules[0].grp, modules);
|
||||||
} else {
|
} else {
|
||||||
list = [];
|
list = [];
|
||||||
// At present time slots with grouped options (Hardpoints and Internal) can be empty
|
// At present time slots with grouped options (Hardpoints and Internal) can be empty
|
||||||
if (m) {
|
if (m) {
|
||||||
list.push(<div className='empty-c upp' key='empty' onClick={onSelect.bind(null, null)} >{translate('empty')}</div>);
|
let emptyId = 'empty';
|
||||||
|
if (this.firstSlotId == null) this.firstSlotId = emptyId;
|
||||||
|
let keyDown = this._keyDown.bind(this, onSelect);
|
||||||
|
list.push(<div className='empty-c upp' key={emptyId} data-id={emptyId} onClick={onSelect.bind(null, null)}
|
||||||
|
onKeyDown={keyDown} tabIndex="0"
|
||||||
|
ref={slotItem => this.slotItems[emptyId] = slotItem}>{translate('empty')}</div>);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Need to regroup the modules by our own categorisation
|
// Need to regroup the modules by our own categorisation
|
||||||
@@ -148,7 +195,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
|||||||
const existing = catmodules[moduleCategory] || [];
|
const existing = catmodules[moduleCategory] || [];
|
||||||
catmodules[moduleCategory] = existing.concat(modules[g]);
|
catmodules[moduleCategory] = existing.concat(modules[g]);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let category in catmodules) {
|
for (let category in catmodules) {
|
||||||
let categoryHeader = false;
|
let categoryHeader = false;
|
||||||
// Order through CATEGORIES if present
|
// Order through CATEGORIES if present
|
||||||
@@ -162,7 +209,8 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
|||||||
if (categories.length === 1) {
|
if (categories.length === 1) {
|
||||||
// Show category header instead of group header
|
// Show category header instead of group header
|
||||||
if (m && grp == m.grp) {
|
if (m && grp == m.grp) {
|
||||||
list.push(<div ref={(elem) => this.groupElem = elem} key={category} className={'select-category upp'}>{translate(category)}</div>);
|
list.push(<div ref={(elem) => this.groupElem = elem} key={category}
|
||||||
|
className={'select-category upp'}>{translate(category)}</div>);
|
||||||
} else {
|
} else {
|
||||||
list.push(<div key={category} className={'select-category upp'}>{translate(category)}</div>);
|
list.push(<div key={category} className={'select-category upp'}>{translate(category)}</div>);
|
||||||
}
|
}
|
||||||
@@ -173,54 +221,71 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
|||||||
categoryHeader = true;
|
categoryHeader = true;
|
||||||
}
|
}
|
||||||
if (m && grp == m.grp) {
|
if (m && grp == m.grp) {
|
||||||
list.push(<div ref={(elem) => this.groupElem = elem} key={grp} className={'select-group cap'}>{translate(grp)}</div>);
|
list.push(<div ref={(elem) => this.groupElem = elem} key={grp}
|
||||||
|
className={'select-group cap'}>{translate(grp)}</div>);
|
||||||
} else {
|
} else {
|
||||||
list.push(<div key={grp} className={'select-group cap'}>{translate(grp)}</div>);
|
list.push(<div key={grp} className={'select-group cap'}>{translate(grp)}</div>);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
list.push(buildGroup(grp, modules[grp]));
|
list.push(buildGroup(grp, modules[grp]));
|
||||||
|
for (const i of modules[grp]) {
|
||||||
|
let mount = '';
|
||||||
|
if (i.mount === 'F') {
|
||||||
|
mount = 'Fixed';
|
||||||
|
} else if (i.mount === 'G') {
|
||||||
|
mount = 'Gimballed';
|
||||||
|
} else if (i.mount === 'T') {
|
||||||
|
mount = 'Turreted';
|
||||||
|
}
|
||||||
|
const fuzz = { grp, m: i, name: `${i.class}${i.rating}${mount ? ' ' + mount : ''} ${translate(grp)}` };
|
||||||
|
fuzzy.push(fuzz);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let trackingFocus = false;
|
||||||
return { list, currentGroup };
|
return { list, currentGroup, fuzzy, trackingFocus };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate React Components for Module Group
|
* Generate React Components for Module Group
|
||||||
|
* @param {Ship} ship Ship the selection is for
|
||||||
* @param {Function} translate Translate function
|
* @param {Function} translate Translate function
|
||||||
* @param {Objecy} mountedModule Mounted Module
|
* @param {Object} mountedModule Mounted Module
|
||||||
* @param {Funciton} warningFunc Warning function
|
* @param {Function} warningFunc Warning function
|
||||||
* @param {number} mass Mass
|
|
||||||
* @param {function} onSelect Select/Mount callback
|
* @param {function} onSelect Select/Mount callback
|
||||||
* @param {string} grp Group name
|
* @param {string} grp Group name
|
||||||
* @param {Array} modules Available modules
|
* @param {Array} modules Available modules
|
||||||
* @return {React.Component} Available Module Group contents
|
* @return {React.Component} Available Module Group contents
|
||||||
*/
|
*/
|
||||||
_buildGroup(translate, mountedModule, warningFunc, mass, onSelect, grp, modules) {
|
_buildGroup(ship, translate, mountedModule, warningFunc, onSelect, grp, modules) {
|
||||||
let prevClass = null, prevRating = null;
|
let prevClass = null, prevRating = null, prevName;
|
||||||
let elems = [];
|
let elems = [];
|
||||||
|
|
||||||
const sortedModules = modules.sort(this._moduleOrder);
|
const sortedModules = modules.sort(this._moduleOrder);
|
||||||
|
|
||||||
// Calculate the number of items per class. Used so we don't have long lists with only a few items in each row
|
// Calculate the number of items per class. Used so we don't have long lists with only a few items in each row
|
||||||
const tmp = sortedModules.map((v, i) => v['class']).reduce((count, cls) => { count[cls] = ++count[cls] || 1; return count; }, {});
|
const tmp = sortedModules.map((v, i) => v['class']).reduce((count, cls) => {
|
||||||
|
count[cls] = ++count[cls] || 1;
|
||||||
|
return count;
|
||||||
|
}, {});
|
||||||
const itemsPerClass = Math.max.apply(null, Object.keys(tmp).map(key => tmp[key]));
|
const itemsPerClass = Math.max.apply(null, Object.keys(tmp).map(key => tmp[key]));
|
||||||
|
|
||||||
let itemsOnThisRow = 0;
|
let itemsOnThisRow = 0;
|
||||||
|
|
||||||
for (let i = 0; i < sortedModules.length; i++) {
|
for (let i = 0; i < sortedModules.length; i++) {
|
||||||
let m = sortedModules[i];
|
let m = sortedModules[i];
|
||||||
let mount = null;
|
let mount = null;
|
||||||
let disabled = false;
|
let disabled = false;
|
||||||
|
prevName = m.name;
|
||||||
if (ModuleUtils.isShieldGenerator(m.grp)) {
|
if (ModuleUtils.isShieldGenerator(m.grp)) {
|
||||||
// Shield generators care about maximum hull mass
|
// Shield generators care about maximum hull mass
|
||||||
disabled = mass > m.maxmass;
|
disabled = ship.hullMass > m.maxmass;
|
||||||
} else if (m.maxmass) {
|
// If the mounted module is experimental as well, we can replace it so
|
||||||
// Thrusters care about total mass
|
// the maximum does not apply
|
||||||
disabled = mass + m.mass > m.maxmass;
|
} else if (m.experimental && (!mountedModule || !mountedModule.experimental)) {
|
||||||
|
disabled = 4 <= ship.hardpoints.filter(o => o.m && o.m.experimental).length;
|
||||||
}
|
}
|
||||||
let active = mountedModule && mountedModule.id === m.id;
|
let active = mountedModule && mountedModule.id === m.id;
|
||||||
let classes = cn(m.name ? 'lc' : 'c', {
|
let classes = cn(m.name ? 'lc' : 'c', {
|
||||||
@@ -230,9 +295,21 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
|||||||
});
|
});
|
||||||
let eventHandlers;
|
let eventHandlers;
|
||||||
|
|
||||||
if (disabled || active) {
|
if (disabled) {
|
||||||
eventHandlers = {};
|
eventHandlers = {
|
||||||
|
onKeyDown: this._keyDown.bind(this, null),
|
||||||
|
onKeyUp: this._keyUp.bind(this, null)
|
||||||
|
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
|
/**
|
||||||
|
* Get the ids of the first and last <li> elements in the <ul> that are focusable (i.e. are not active or disabled)
|
||||||
|
* Will be used to keep focus inside the <ul> on Tab and Shift-Tab while it is visible
|
||||||
|
*/
|
||||||
|
if (this.firstSlotId == null) this.firstSlotId = sortedModules[i].id;
|
||||||
|
if (active) this.activeSlotId = sortedModules[i].id;
|
||||||
|
this.lastSlotId = sortedModules[i].id;
|
||||||
|
|
||||||
let showDiff = this._showDiff.bind(this, mountedModule, m);
|
let showDiff = this._showDiff.bind(this, mountedModule, m);
|
||||||
let select = onSelect.bind(null, m);
|
let select = onSelect.bind(null, m);
|
||||||
|
|
||||||
@@ -241,33 +318,46 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
|||||||
onTouchStart: this._touchStart.bind(this, showDiff),
|
onTouchStart: this._touchStart.bind(this, showDiff),
|
||||||
onTouchEnd: this._touchEnd.bind(this, select),
|
onTouchEnd: this._touchEnd.bind(this, select),
|
||||||
onMouseLeave: this._hideDiff,
|
onMouseLeave: this._hideDiff,
|
||||||
onClick: select
|
onClick: select,
|
||||||
|
onKeyDown: this._keyDown.bind(this, select),
|
||||||
|
onKeyUp: this._keyUp.bind(this, select)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(m.mount) {
|
switch (m.mount) {
|
||||||
case 'F': mount = <MountFixed className={'lg'} />; break;
|
case 'F':
|
||||||
case 'G': mount = <MountGimballed className={'lg'}/>; break;
|
mount = <MountFixed className={'lg'}/>;
|
||||||
case 'T': mount = <MountTurret className={'lg'}/>; break;
|
break;
|
||||||
|
case 'G':
|
||||||
|
mount = <MountGimballed className={'lg'}/>;
|
||||||
|
break;
|
||||||
|
case 'T':
|
||||||
|
mount = <MountTurret className={'lg'}/>;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
if (m.name && m.name === prevName) {
|
||||||
if (itemsOnThisRow == 6 || i > 0 && sortedModules.length > 3 && itemsPerClass > 2 && m.class != prevClass && (m.rating != prevRating || m.mount)) {
|
// elems.push(<br key={'b' + m.grp + i} />);
|
||||||
elems.push(<br key={'b' + m.grp + i} />);
|
|
||||||
itemsOnThisRow = 0;
|
itemsOnThisRow = 0;
|
||||||
}
|
}
|
||||||
|
if (itemsOnThisRow == 6 || i > 0 && sortedModules.length > 3 && itemsPerClass > 2 && m.class != prevClass && (m.rating != prevRating || m.mount)) {
|
||||||
|
elems.push(<br key={'b' + m.grp + i}/>);
|
||||||
|
itemsOnThisRow = 0;
|
||||||
|
}
|
||||||
|
let tbIdx = (classes.indexOf('disabled') < 0) ? 0 : undefined;
|
||||||
elems.push(
|
elems.push(
|
||||||
<li key={m.id} className={classes} {...eventHandlers}>
|
<li key={m.id} data-id={m.id} className={classes} {...eventHandlers} tabIndex={tbIdx}
|
||||||
|
ref={slotItem => this.slotItems[m.id] = slotItem}>
|
||||||
{mount}
|
{mount}
|
||||||
{(mount ? ' ' : '') + m.class + m.rating + (m.missile ? '/' + m.missile : '') + (m.name ? ' ' + translate(m.name) : '')}
|
{(mount ? ' ' : '') + m.class + m.rating + (m.missile ? '/' + m.missile : '') + (m.name ? ' ' + translate(m.name) : '')}
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
|
|
||||||
itemsOnThisRow++;
|
itemsOnThisRow++;
|
||||||
prevClass = m.class;
|
prevClass = m.class;
|
||||||
prevRating = m.rating;
|
prevRating = m.rating;
|
||||||
|
prevName = m.name;
|
||||||
}
|
}
|
||||||
|
return <ul key={'modules' + grp}>{elems}</ul>;
|
||||||
return <ul key={'modules' + grp} >{elems}</ul>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -284,6 +374,40 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate tooltip content for the difference between the
|
||||||
|
* mounted module and the hovered modules
|
||||||
|
*/
|
||||||
|
_showSearch() {
|
||||||
|
if (this.props.modules instanceof Array) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<FuzzySearch
|
||||||
|
list={this.state.fuzzy}
|
||||||
|
keys={['grp', 'name']}
|
||||||
|
tokenize={true}
|
||||||
|
className={'input'}
|
||||||
|
width={'100%'}
|
||||||
|
style={{ padding: 0 }}
|
||||||
|
onSelect={e => this.props.onSelect.bind(null, e.m)()}
|
||||||
|
resultsTemplate={(props, state, styles, clickHandler) => {
|
||||||
|
return state.results.map((val, i) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
className={'lc'}
|
||||||
|
onClick={() => clickHandler(i)}
|
||||||
|
>
|
||||||
|
{val.name}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mouse over diff handler
|
* Mouse over diff handler
|
||||||
* @param {Function} showDiff diff tooltip callback
|
* @param {Function} showDiff diff tooltip callback
|
||||||
@@ -318,6 +442,41 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
|||||||
this._hideDiff();
|
this._hideDiff();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Key down - select module on Enter key, move to next/previous module on Tab/Shift-Tab, close on Esc
|
||||||
|
* @param {Function} select Select module callback
|
||||||
|
* @param {SyntheticEvent} event Event
|
||||||
|
*/
|
||||||
|
_keyDown(select, event) {
|
||||||
|
let className = event.currentTarget.attributes['class'].value;
|
||||||
|
if (event.key == 'Enter' && className.indexOf('disabled') < 0 && className.indexOf('active') < 0) {
|
||||||
|
select();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let elemId = event.currentTarget.attributes['data-id'].value;
|
||||||
|
if (className.indexOf('disabled') < 0 && event.key == 'Tab') {
|
||||||
|
if (event.shiftKey && elemId == this.firstSlotId) {
|
||||||
|
event.preventDefault();
|
||||||
|
this.slotItems[this.lastSlotId].focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!event.shiftKey && elemId == this.lastSlotId) {
|
||||||
|
event.preventDefault();
|
||||||
|
this.slotItems[this.firstSlotId].focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Key Up
|
||||||
|
* @param {Function} select Select module callback
|
||||||
|
* @param {SytheticEvent} event Event
|
||||||
|
*/
|
||||||
|
_keyUp(select, event) {
|
||||||
|
// nothing here yet
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hide diff tooltip
|
* Hide diff tooltip
|
||||||
* @param {SyntheticEvent} event Event
|
* @param {SyntheticEvent} event Event
|
||||||
@@ -375,6 +534,24 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
|||||||
if (this.groupElem) { // Scroll to currently selected group
|
if (this.groupElem) { // Scroll to currently selected group
|
||||||
this.node.scrollTop = this.groupElem.offsetTop;
|
this.node.scrollTop = this.groupElem.offsetTop;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Set focus on active or first slot element, if applicable.
|
||||||
|
*/
|
||||||
|
if (this.slotItems[this.activeSlotId]) {
|
||||||
|
this.slotItems[this.activeSlotId].focus();
|
||||||
|
} else if (this.slotItems[this.firstSlotId]) {
|
||||||
|
this.slotItems[this.firstSlotId].focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle focus if the component updates
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
componentWillUnmount() {
|
||||||
|
if (this.props.slotDiv) {
|
||||||
|
this.props.slotDiv.focus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -393,14 +570,14 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div ref={node => this.node = node}
|
<div ref={node => this.node = node}
|
||||||
className={cn('select', this.props.className)}
|
className={cn('select', this.props.className)}
|
||||||
onScroll={this._hideDiff}
|
onScroll={this._hideDiff}
|
||||||
onClick={(e) => e.stopPropagation() }
|
onClick={(e) => e.stopPropagation()}
|
||||||
onContextMenu={stopCtxPropagation}
|
onContextMenu={stopCtxPropagation}
|
||||||
>
|
>
|
||||||
|
{this._showSearch()}
|
||||||
{this.state.list}
|
{this.state.list}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ function insertLinebreaks(d) {
|
|||||||
* Bar Chart
|
* Bar Chart
|
||||||
*/
|
*/
|
||||||
export default class BarChart extends TranslatedComponent {
|
export default class BarChart extends TranslatedComponent {
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
colors: ['#7b6888', '#6b486b', '#3182bd', '#a05d56', '#d0743c'],
|
colors: ['#7b6888', '#6b486b', '#3182bd', '#a05d56', '#d0743c'],
|
||||||
labels: null,
|
labels: null,
|
||||||
|
|||||||
@@ -1,13 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import TranslatedComponent from './TranslatedComponent';
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
import { Ships } from 'coriolis-data/dist';
|
|
||||||
import { nameComparator } from '../utils/SlotFunctions';
|
|
||||||
import { Pip } from './SvgIcons';
|
|
||||||
import LineChart from '../components/LineChart';
|
|
||||||
import Slider from '../components/Slider';
|
|
||||||
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
|
||||||
import Module from '../shipyard/Module';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Boost displays a boost button that toggles bosot
|
* Boost displays a boost button that toggles bosot
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import TranslatedComponent from './TranslatedComponent';
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
import { Ships } from 'coriolis-data/dist';
|
|
||||||
import Slider from '../components/Slider';
|
import Slider from '../components/Slider';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import { outfitURL } from '../utils/UrlGenerators';
|
|||||||
* Comparison Table
|
* Comparison Table
|
||||||
*/
|
*/
|
||||||
export default class ComparisonTable extends TranslatedComponent {
|
export default class ComparisonTable extends TranslatedComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
facets: PropTypes.array.isRequired,
|
facets: PropTypes.array.isRequired,
|
||||||
builds: PropTypes.array.isRequired,
|
builds: PropTypes.array.isRequired,
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import { ShoppingIcon } from '../components/SvgIcons';
|
|||||||
* Cost Section
|
* Cost Section
|
||||||
*/
|
*/
|
||||||
export default class CostSection extends TranslatedComponent {
|
export default class CostSection extends TranslatedComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
ship: PropTypes.object.isRequired,
|
ship: PropTypes.object.isRequired,
|
||||||
code: PropTypes.string.isRequired,
|
code: PropTypes.string.isRequired,
|
||||||
@@ -361,11 +360,11 @@ export default class CostSection extends TranslatedComponent {
|
|||||||
for (let i = 0, l = retrofitCosts.length; i < l; i++) {
|
for (let i = 0, l = retrofitCosts.length; i < l; i++) {
|
||||||
let item = retrofitCosts[i];
|
let item = retrofitCosts[i];
|
||||||
rows.push(<tr key={i} className={cn('highlight', { disabled: !item.retroItem.incCost })} onClick={this._toggleRetrofitCost.bind(this, item)}>
|
rows.push(<tr key={i} className={cn('highlight', { disabled: !item.retroItem.incCost })} onClick={this._toggleRetrofitCost.bind(this, item)}>
|
||||||
<td className='ptr' style={{ width: '1em' }}>{item.sellClassRating}</td>
|
<td className='ptr' style={{ width: '1em' }}>{item.sellClassRating}</td>
|
||||||
<td className='le ptr shorten cap'>{translate(item.sellName)}</td>
|
<td className='le ptr shorten cap'>{translate(item.sellName)}</td>
|
||||||
<td className='ptr' style={{ width: '1em' }}>{item.buyClassRating}</td>
|
<td className='ptr' style={{ width: '1em' }}>{item.buyClassRating}</td>
|
||||||
<td className='le ptr shorten cap'>{translate(item.buyName)}</td>
|
<td className='le ptr shorten cap'>{translate(item.buyName)}</td>
|
||||||
<td colSpan='2' className={cn('ri ptr', item.retroItem.incCost ? item.netCost > 0 ? 'warning' : 'secondary-disabled' : 'disabled')}>{int(item.netCost)}{units.CR}</td>
|
<td colSpan='2' className={cn('ri ptr', item.retroItem.incCost ? item.netCost > 0 ? 'warning' : 'secondary-disabled' : 'disabled')}>{int(item.netCost)}{units.CR}</td>
|
||||||
</tr>);
|
</tr>);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -52,12 +52,12 @@ export default class Defence extends TranslatedComponent {
|
|||||||
* @return {React.Component} contents
|
* @return {React.Component} contents
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
const { ship, sys, opponentWep } = this.props;
|
const { opponent, sys, opponentWep } = this.props;
|
||||||
const { language, tooltip, termtip } = this.context;
|
const { language, tooltip, termtip } = this.context;
|
||||||
const { formats, translate, units } = language;
|
const { formats, translate, units } = language;
|
||||||
const { shield, armour, shielddamage, armourdamage } = this.state;
|
const { shield, armour, shielddamage, armourdamage } = this.state;
|
||||||
|
|
||||||
const pd = ship.standard[4].m;
|
const pd = opponent.standard[4].m;
|
||||||
|
|
||||||
const shieldSourcesData = [];
|
const shieldSourcesData = [];
|
||||||
const effectiveShieldData = [];
|
const effectiveShieldData = [];
|
||||||
@@ -76,6 +76,7 @@ export default class Defence extends TranslatedComponent {
|
|||||||
shieldSourcesData.push({ value: Math.round(shield.generator), label: translate('generator') });
|
shieldSourcesData.push({ value: Math.round(shield.generator), label: translate('generator') });
|
||||||
shieldSourcesData.push({ value: Math.round(shield.boosters), label: translate('boosters') });
|
shieldSourcesData.push({ value: Math.round(shield.boosters), label: translate('boosters') });
|
||||||
shieldSourcesData.push({ value: Math.round(shield.cells), label: translate('cells') });
|
shieldSourcesData.push({ value: Math.round(shield.cells), label: translate('cells') });
|
||||||
|
shieldSourcesData.push({ value: Math.round(shield.addition), label: translate('shield addition') });
|
||||||
|
|
||||||
if (shield.generator > 0) {
|
if (shield.generator > 0) {
|
||||||
shieldSourcesTt.push(<div key='generator'>{translate('generator') + ' ' + formats.int(shield.generator)}{units.MJ}</div>);
|
shieldSourcesTt.push(<div key='generator'>{translate('generator') + ' ' + formats.int(shield.generator)}{units.MJ}</div>);
|
||||||
@@ -101,19 +102,19 @@ export default class Defence extends TranslatedComponent {
|
|||||||
|
|
||||||
// Add effective shield from resistances
|
// Add effective shield from resistances
|
||||||
const rawMj = shield.generator + shield.boosters + shield.cells;
|
const rawMj = shield.generator + shield.boosters + shield.cells;
|
||||||
const explosiveMj = rawMj / (shield.explosive.generator * shield.explosive.boosters) - rawMj;
|
const explosiveMj = rawMj / (shield.explosive.base) - rawMj;
|
||||||
if (explosiveMj != 0) effectiveShieldExplosiveTt.push(<div key='resistance'>{translate('resistance') + ' ' + formats.int(explosiveMj)}{units.MJ}</div>);
|
if (explosiveMj != 0) effectiveShieldExplosiveTt.push(<div key='resistance'>{translate('resistance') + ' ' + formats.int(explosiveMj)}{units.MJ}</div>);
|
||||||
const kineticMj = rawMj / (shield.kinetic.generator * shield.kinetic.boosters) - rawMj;
|
const kineticMj = rawMj / (shield.kinetic.base) - rawMj;
|
||||||
if (kineticMj != 0) effectiveShieldKineticTt.push(<div key='resistance'>{translate('resistance') + ' ' + formats.int(kineticMj)}{units.MJ}</div>);
|
if (kineticMj != 0) effectiveShieldKineticTt.push(<div key='resistance'>{translate('resistance') + ' ' + formats.int(kineticMj)}{units.MJ}</div>);
|
||||||
const thermalMj = rawMj / (shield.thermal.generator * shield.thermal.boosters) - rawMj;
|
const thermalMj = rawMj / (shield.thermal.base) - rawMj;
|
||||||
if (thermalMj != 0) effectiveShieldThermalTt.push(<div key='resistance'>{translate('resistance') + ' ' + formats.int(thermalMj)}{units.MJ}</div>);
|
if (thermalMj != 0) effectiveShieldThermalTt.push(<div key='resistance'>{translate('resistance') + ' ' + formats.int(thermalMj)}{units.MJ}</div>);
|
||||||
|
|
||||||
// Add effective shield from power distributor SYS pips
|
// Add effective shield from power distributor SYS pips
|
||||||
if (shield.absolute.sys != 1) {
|
if (shield.absolute.sys != 1) {
|
||||||
effectiveShieldAbsoluteTt.push(<div key='power distributor'>{translate('power distributor') + ' ' + formats.int(rawMj / shield.absolute.sys - rawMj)}{units.MJ}</div>);
|
effectiveShieldAbsoluteTt.push(<div key='power distributor'>{translate('power distributor') + ' ' + formats.int(rawMj / shield.absolute.total - rawMj)}{units.MJ}</div>);
|
||||||
effectiveShieldExplosiveTt.push(<div key='power distributor'>{translate('power distributor') + ' ' + formats.int(rawMj / shield.explosive.sys - rawMj)}{units.MJ}</div>);
|
effectiveShieldExplosiveTt.push(<div key='power distributor'>{translate('power distributor') + ' ' + formats.int(rawMj / shield.explosive.total - rawMj / shield.explosive.base)}{units.MJ}</div>);
|
||||||
effectiveShieldKineticTt.push(<div key='power distributor'>{translate('power distributor') + ' ' + formats.int(rawMj / shield.kinetic.sys - rawMj)}{units.MJ}</div>);
|
effectiveShieldKineticTt.push(<div key='power distributor'>{translate('power distributor') + ' ' + formats.int(rawMj / shield.kinetic.total - rawMj / shield.kinetic.base)}{units.MJ}</div>);
|
||||||
effectiveShieldThermalTt.push(<div key='power distributor'>{translate('power distributor') + ' ' + formats.int(rawMj / shield.thermal.sys - rawMj)}{units.MJ}</div>);
|
effectiveShieldThermalTt.push(<div key='power distributor'>{translate('power distributor') + ' ' + formats.int(rawMj / shield.thermal.total - rawMj / shield.thermal.base)}{units.MJ}</div>);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,18 +160,21 @@ export default class Defence extends TranslatedComponent {
|
|||||||
const effectiveArmourExplosiveTt = [];
|
const effectiveArmourExplosiveTt = [];
|
||||||
const effectiveArmourKineticTt = [];
|
const effectiveArmourKineticTt = [];
|
||||||
const effectiveArmourThermalTt = [];
|
const effectiveArmourThermalTt = [];
|
||||||
|
const effectiveArmourCausticTt = [];
|
||||||
if (armour.bulkheads > 0) {
|
if (armour.bulkheads > 0) {
|
||||||
armourSourcesTt.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.int(armour.bulkheads)}</div>);
|
armourSourcesTt.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.int(armour.bulkheads)}</div>);
|
||||||
effectiveArmourAbsoluteTt.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.int(armour.bulkheads)}</div>);
|
effectiveArmourAbsoluteTt.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.int(armour.bulkheads)}</div>);
|
||||||
effectiveArmourExplosiveTt.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.int(armour.bulkheads)}</div>);
|
effectiveArmourExplosiveTt.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.int(armour.bulkheads)}</div>);
|
||||||
effectiveArmourKineticTt.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.int(armour.bulkheads)}</div>);
|
effectiveArmourKineticTt.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.int(armour.bulkheads)}</div>);
|
||||||
effectiveArmourThermalTt.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.int(armour.bulkheads)}</div>);
|
effectiveArmourThermalTt.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.int(armour.bulkheads)}</div>);
|
||||||
|
effectiveArmourCausticTt.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.int(armour.bulkheads)}</div>);
|
||||||
if (armour.reinforcement > 0) {
|
if (armour.reinforcement > 0) {
|
||||||
armourSourcesTt.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.int(armour.reinforcement)}</div>);
|
armourSourcesTt.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.int(armour.reinforcement)}</div>);
|
||||||
effectiveArmourAbsoluteTt.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.int(armour.reinforcement)}</div>);
|
effectiveArmourAbsoluteTt.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.int(armour.reinforcement)}</div>);
|
||||||
effectiveArmourExplosiveTt.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.int(armour.reinforcement)}</div>);
|
effectiveArmourExplosiveTt.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.int(armour.reinforcement)}</div>);
|
||||||
effectiveArmourKineticTt.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.int(armour.reinforcement)}</div>);
|
effectiveArmourKineticTt.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.int(armour.reinforcement)}</div>);
|
||||||
effectiveArmourThermalTt.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.int(armour.reinforcement)}</div>);
|
effectiveArmourThermalTt.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.int(armour.reinforcement)}</div>);
|
||||||
|
effectiveArmourCausticTt.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.int(armour.reinforcement)}</div>);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,17 +187,22 @@ export default class Defence extends TranslatedComponent {
|
|||||||
const armourDamageTakenExplosiveTt = [];
|
const armourDamageTakenExplosiveTt = [];
|
||||||
armourDamageTakenExplosiveTt.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.pct1(armour.explosive.bulkheads)}</div>);
|
armourDamageTakenExplosiveTt.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.pct1(armour.explosive.bulkheads)}</div>);
|
||||||
armourDamageTakenExplosiveTt.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.pct1(armour.explosive.reinforcement)}</div>);
|
armourDamageTakenExplosiveTt.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.pct1(armour.explosive.reinforcement)}</div>);
|
||||||
if (armour.explosive.bulkheads * armour.explosive.reinforcement != 1) effectiveArmourExplosiveTt.push(<div key='resistance'>{translate('resistance') + ' ' + formats.int(rawArmour / (armour.explosive.bulkheads * armour.explosive.reinforcement) - rawArmour)}</div>);
|
if (armour.explosive.total != 1) effectiveArmourExplosiveTt.push(<div key='resistance'>{translate('resistance') + ' ' + formats.int(rawArmour / armour.explosive.total - rawArmour)}</div>);
|
||||||
|
|
||||||
const armourDamageTakenKineticTt = [];
|
const armourDamageTakenKineticTt = [];
|
||||||
armourDamageTakenKineticTt.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.pct1(armour.kinetic.bulkheads)}</div>);
|
armourDamageTakenKineticTt.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.pct1(armour.kinetic.bulkheads)}</div>);
|
||||||
armourDamageTakenKineticTt.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.pct1(armour.kinetic.reinforcement)}</div>);
|
armourDamageTakenKineticTt.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.pct1(armour.kinetic.reinforcement)}</div>);
|
||||||
if (armour.kinetic.bulkheads * armour.kinetic.reinforcement != 1) effectiveArmourKineticTt.push(<div key='resistance'>{translate('resistance') + ' ' + formats.int(rawArmour / (armour.kinetic.bulkheads * armour.kinetic.reinforcement) - rawArmour)}</div>);
|
if (armour.kinetic.total != 1) effectiveArmourKineticTt.push(<div key='resistance'>{translate('resistance') + ' ' + formats.int(rawArmour / armour.kinetic.total - rawArmour)}</div>);
|
||||||
|
|
||||||
const armourDamageTakenThermalTt = [];
|
const armourDamageTakenThermalTt = [];
|
||||||
armourDamageTakenThermalTt.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.pct1(armour.thermal.bulkheads)}</div>);
|
armourDamageTakenThermalTt.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.pct1(armour.thermal.bulkheads)}</div>);
|
||||||
armourDamageTakenThermalTt.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.pct1(armour.thermal.reinforcement)}</div>);
|
armourDamageTakenThermalTt.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.pct1(armour.thermal.reinforcement)}</div>);
|
||||||
if (armour.thermal.bulkheads * armour.thermal.reinforcement != 1) effectiveArmourThermalTt.push(<div key='resistance'>{translate('resistance') + ' ' + formats.int(rawArmour / (armour.thermal.bulkheads * armour.thermal.reinforcement) - rawArmour)}</div>);
|
if (armour.thermal.total != 1) effectiveArmourThermalTt.push(<div key='resistance'>{translate('resistance') + ' ' + formats.int(rawArmour / armour.thermal.total - rawArmour)}</div>);
|
||||||
|
|
||||||
|
const armourDamageTakenCausticTt = [];
|
||||||
|
armourDamageTakenCausticTt.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.pct1(armour.caustic.bulkheads)}</div>);
|
||||||
|
armourDamageTakenCausticTt.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.pct1(armour.caustic.reinforcement)}</div>);
|
||||||
|
if (armour.thermal.total != 1) effectiveArmourCausticTt.push(<div key='resistance'>{translate('resistance') + ' ' + formats.int(rawArmour / armour.caustic.total - rawArmour)}</div>);
|
||||||
|
|
||||||
const effectiveArmourData = [];
|
const effectiveArmourData = [];
|
||||||
const effectiveAbsoluteArmour = armour.total / armour.absolute.total;
|
const effectiveAbsoluteArmour = armour.total / armour.absolute.total;
|
||||||
@@ -204,36 +213,39 @@ export default class Defence extends TranslatedComponent {
|
|||||||
effectiveArmourData.push({ value: Math.round(effectiveKineticArmour), label: translate('kinetic'), tooltip: effectiveArmourKineticTt });
|
effectiveArmourData.push({ value: Math.round(effectiveKineticArmour), label: translate('kinetic'), tooltip: effectiveArmourKineticTt });
|
||||||
const effectiveThermalArmour = armour.total / armour.thermal.total;
|
const effectiveThermalArmour = armour.total / armour.thermal.total;
|
||||||
effectiveArmourData.push({ value: Math.round(effectiveThermalArmour), label: translate('thermal'), tooltip: effectiveArmourThermalTt });
|
effectiveArmourData.push({ value: Math.round(effectiveThermalArmour), label: translate('thermal'), tooltip: effectiveArmourThermalTt });
|
||||||
|
const effectiveCausticArmour = armour.total / armour.caustic.total;
|
||||||
|
effectiveArmourData.push({ value: Math.round(effectiveCausticArmour), label: translate('caustic'), tooltip: effectiveArmourCausticTt });
|
||||||
|
|
||||||
const armourDamageTakenData = [];
|
const armourDamageTakenData = [];
|
||||||
armourDamageTakenData.push({ value: Math.round(armour.absolute.total * 100), label: translate('absolute'), tooltip: armourDamageTakenTt });
|
armourDamageTakenData.push({ value: Math.round(armour.absolute.total * 100), label: translate('absolute'), tooltip: armourDamageTakenTt });
|
||||||
armourDamageTakenData.push({ value: Math.round(armour.explosive.total * 100), label: translate('explosive'), tooltip: armourDamageTakenExplosiveTt });
|
armourDamageTakenData.push({ value: Math.round(armour.explosive.total * 100), label: translate('explosive'), tooltip: armourDamageTakenExplosiveTt });
|
||||||
armourDamageTakenData.push({ value: Math.round(armour.kinetic.total * 100), label: translate('kinetic'), tooltip: armourDamageTakenKineticTt });
|
armourDamageTakenData.push({ value: Math.round(armour.kinetic.total * 100), label: translate('kinetic'), tooltip: armourDamageTakenKineticTt });
|
||||||
armourDamageTakenData.push({ value: Math.round(armour.thermal.total * 100), label: translate('thermal'), tooltip: armourDamageTakenThermalTt });
|
armourDamageTakenData.push({ value: Math.round(armour.thermal.total * 100), label: translate('thermal'), tooltip: armourDamageTakenThermalTt });
|
||||||
|
armourDamageTakenData.push({ value: Math.round(armour.caustic.total * 100), label: translate('caustic'), tooltip: armourDamageTakenCausticTt });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span id='defence'>
|
<span id='defence'>
|
||||||
{shield.total ? <span>
|
{shield.total ? <span>
|
||||||
<div className='group quarter'>
|
<div className='group quarter'>
|
||||||
<h2>{translate('shield metrics')}</h2>
|
<h2>{translate('shield metrics')}</h2>
|
||||||
<br/>
|
<br/>
|
||||||
<h2 onMouseOver={termtip.bind(null, <div>{shieldSourcesTt}</div>)} onMouseOut={tooltip.bind(null, null)} className='summary'>{translate('raw shield strength')}<br/>{formats.int(shield.total)}{units.MJ}</h2>
|
<h2 onMouseOver={termtip.bind(null, <div>{shieldSourcesTt}</div>)} onMouseOut={tooltip.bind(null, null)} className='summary'>{translate('raw shield strength')}<br/>{formats.int(shield.total)}{units.MJ}</h2>
|
||||||
<h2 onMouseOver={termtip.bind(null, translate('TT_TIME_TO_LOSE_SHIELDS'))} onMouseOut={tooltip.bind(null, null)}>{translate('PHRASE_TIME_TO_LOSE_SHIELDS')}<br/>{shielddamage.totalsdps == 0 ? translate('ever') : formats.time(Calc.timeToDeplete(shield.total, shielddamage.totalsdps, shielddamage.totalseps, pd.getWeaponsCapacity(), pd.getWeaponsRechargeRate() * opponentWep / 4))}</h2>
|
<h2 onMouseOver={termtip.bind(null, translate('TT_TIME_TO_LOSE_SHIELDS'))} onMouseOut={tooltip.bind(null, null)}>{translate('PHRASE_TIME_TO_LOSE_SHIELDS')}<br/>{shielddamage.totalsdps == 0 ? translate('ever') : formats.time(Calc.timeToDeplete(shield.total, shielddamage.totalsdps, shielddamage.totalseps, pd.getWeaponsCapacity(), pd.getWeaponsRechargeRate() * opponentWep / 4))}</h2>
|
||||||
<h2 onMouseOver={termtip.bind(null, translate('PHRASE_SG_RECOVER'))} onMouseOut={tooltip.bind(null, null)}>{translate('PHRASE_TIME_TO_RECOVER_SHIELDS')}<br/>{shield.recover === Math.Inf ? translate('never') : formats.time(shield.recover)}</h2>
|
<h2 onMouseOver={termtip.bind(null, translate('PHRASE_SG_RECOVER'))} onMouseOut={tooltip.bind(null, null)}>{translate('PHRASE_TIME_TO_RECOVER_SHIELDS')}<br/>{shield.recover === Math.Inf ? translate('never') : formats.time(shield.recover)}</h2>
|
||||||
<h2 onMouseOver={termtip.bind(null, translate('PHRASE_SG_RECHARGE'))} onMouseOut={tooltip.bind(null, null)}>{translate('PHRASE_TIME_TO_RECHARGE_SHIELDS')}<br/>{shield.recharge === Math.Inf ? translate('never') : formats.time(shield.recharge)}</h2>
|
<h2 onMouseOver={termtip.bind(null, translate('PHRASE_SG_RECHARGE'))} onMouseOut={tooltip.bind(null, null)}>{translate('PHRASE_TIME_TO_RECHARGE_SHIELDS')}<br/>{shield.recharge === Math.Inf ? translate('never') : formats.time(shield.recharge)}</h2>
|
||||||
</div>
|
</div>
|
||||||
<div className='group quarter'>
|
<div className='group quarter'>
|
||||||
<h2 onMouseOver={termtip.bind(null, translate('PHRASE_SHIELD_SOURCES'))} onMouseOut={tooltip.bind(null, null)}>{translate('shield sources')}</h2>
|
<h2 onMouseOver={termtip.bind(null, translate('PHRASE_SHIELD_SOURCES'))} onMouseOut={tooltip.bind(null, null)}>{translate('shield sources')}</h2>
|
||||||
<PieChart data={shieldSourcesData} />
|
<PieChart data={shieldSourcesData} />
|
||||||
</div>
|
</div>
|
||||||
<div className='group quarter'>
|
<div className='group quarter'>
|
||||||
<h2 onMouseOver={termtip.bind(null, translate('PHRASE_DAMAGE_TAKEN'))} onMouseOut={tooltip.bind(null, null)}>{translate('damage taken')}(%)</h2>
|
<h2 onMouseOver={termtip.bind(null, translate('PHRASE_DAMAGE_TAKEN'))} onMouseOut={tooltip.bind(null, null)}>{translate('damage taken')}(%)</h2>
|
||||||
<VerticalBarChart data={shieldDamageTakenData} yMax={140} />
|
<VerticalBarChart data={shieldDamageTakenData} yMax={140} />
|
||||||
</div>
|
</div>
|
||||||
<div className='group quarter'>
|
<div className='group quarter'>
|
||||||
<h2 onMouseOver={termtip.bind(null, translate('PHRASE_EFFECTIVE_SHIELD'))} onMouseOut={tooltip.bind(null, null)}>{translate('effective shield')}(MJ)</h2>
|
<h2 onMouseOver={termtip.bind(null, translate('PHRASE_EFFECTIVE_SHIELD'))} onMouseOut={tooltip.bind(null, null)}>{translate('effective shield')}(MJ)</h2>
|
||||||
<VerticalBarChart data={effectiveShieldData} yMax={maxEffectiveShield}/>
|
<VerticalBarChart data={effectiveShieldData} yMax={maxEffectiveShield}/>
|
||||||
</div>
|
</div>
|
||||||
</span> : null }
|
</span> : null }
|
||||||
|
|
||||||
<div className='group quarter'>
|
<div className='group quarter'>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import TranslatedComponent from './TranslatedComponent';
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
import { Ships } from 'coriolis-data/dist';
|
|
||||||
import Slider from '../components/Slider';
|
import Slider from '../components/Slider';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -25,7 +24,7 @@ export default class EngagementRange extends TranslatedComponent {
|
|||||||
|
|
||||||
const { ship } = props;
|
const { ship } = props;
|
||||||
|
|
||||||
const maxRange = this._calcMaxRange(ship);
|
const maxRange = Math.round(this._calcMaxRange(ship));
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
maxRange
|
maxRange
|
||||||
|
|||||||
@@ -1,12 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import TranslatedComponent from './TranslatedComponent';
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
import { Ships } from 'coriolis-data/dist';
|
|
||||||
import { nameComparator } from '../utils/SlotFunctions';
|
|
||||||
import LineChart from '../components/LineChart';
|
import LineChart from '../components/LineChart';
|
||||||
import Slider from '../components/Slider';
|
|
||||||
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
|
||||||
import Module from '../shipyard/Module';
|
|
||||||
import * as Calc from '../shipyard/Calculations';
|
import * as Calc from '../shipyard/Calculations';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,12 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import TranslatedComponent from './TranslatedComponent';
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
import { Ships } from 'coriolis-data/dist';
|
|
||||||
import { nameComparator } from '../utils/SlotFunctions';
|
|
||||||
import LineChart from '../components/LineChart';
|
import LineChart from '../components/LineChart';
|
||||||
import Slider from '../components/Slider';
|
|
||||||
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
|
||||||
import Module from '../shipyard/Module';
|
|
||||||
import * as Calc from '../shipyard/Calculations';
|
import * as Calc from '../shipyard/Calculations';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -57,7 +52,7 @@ export default class FSDProfile extends TranslatedComponent {
|
|||||||
*/
|
*/
|
||||||
_calcMaxRange(ship, fuel, mass) {
|
_calcMaxRange(ship, fuel, mass) {
|
||||||
// Obtain the maximum range
|
// Obtain the maximum range
|
||||||
return Calc.jumpRange(mass, ship.standard[2].m, Math.min(fuel, ship.standard[2].m.getMaxFuelPerJump()));
|
return Calc.jumpRange(mass, ship.standard[2].m, Math.min(fuel, ship.standard[2].m.getMaxFuelPerJump()), ship);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -69,7 +64,7 @@ export default class FSDProfile extends TranslatedComponent {
|
|||||||
const { formats, translate, units } = language;
|
const { formats, translate, units } = language;
|
||||||
const { ship, cargo, fuel } = this.props;
|
const { ship, cargo, fuel } = this.props;
|
||||||
|
|
||||||
|
|
||||||
// Calculate bounds for our line chart - use thruster info for X
|
// Calculate bounds for our line chart - use thruster info for X
|
||||||
const thrusters = ship.standard[1].m;
|
const thrusters = ship.standard[1].m;
|
||||||
const fsd = ship.standard[2].m;
|
const fsd = ship.standard[2].m;
|
||||||
@@ -77,7 +72,7 @@ export default class FSDProfile extends TranslatedComponent {
|
|||||||
const maxMass = thrusters.getMaxMass();
|
const maxMass = thrusters.getMaxMass();
|
||||||
const mass = ship.unladenMass + fuel + cargo;
|
const mass = ship.unladenMass + fuel + cargo;
|
||||||
const minRange = 0;
|
const minRange = 0;
|
||||||
const maxRange = Calc.jumpRange(minMass + fsd.getMaxFuelPerJump(), fsd, fsd.getMaxFuelPerJump());
|
const maxRange = Calc.jumpRange(minMass + fsd.getMaxFuelPerJump(), fsd, fsd.getMaxFuelPerJump(), ship);
|
||||||
// Add a mark at our current mass
|
// Add a mark at our current mass
|
||||||
const mark = Math.min(mass, maxMass);
|
const mark = Math.min(mass, maxMass);
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import TranslatedComponent from './TranslatedComponent';
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
import { Ships } from 'coriolis-data/dist';
|
|
||||||
import Slider from '../components/Slider';
|
import Slider from '../components/Slider';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2,12 +2,21 @@ import React from 'react';
|
|||||||
import cn from 'classnames';
|
import cn from 'classnames';
|
||||||
import Slot from './Slot';
|
import Slot from './Slot';
|
||||||
import Persist from '../stores/Persist';
|
import Persist from '../stores/Persist';
|
||||||
import { DamageAbsolute, DamageKinetic, DamageThermal, DamageExplosive, MountFixed, MountGimballed, MountTurret, ListModifications, Modified } from './SvgIcons';
|
import {
|
||||||
|
DamageAbsolute,
|
||||||
|
DamageKinetic,
|
||||||
|
DamageThermal,
|
||||||
|
DamageExplosive,
|
||||||
|
MountFixed,
|
||||||
|
MountGimballed,
|
||||||
|
MountTurret,
|
||||||
|
ListModifications,
|
||||||
|
Modified
|
||||||
|
} from './SvgIcons';
|
||||||
import { Modifications } from 'coriolis-data/dist';
|
import { Modifications } from 'coriolis-data/dist';
|
||||||
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
||||||
import { blueprintTooltip } from '../utils/BlueprintFunctions';
|
import { blueprintTooltip } from '../utils/BlueprintFunctions';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hardpoint / Utility Slot
|
* Hardpoint / Utility Slot
|
||||||
*/
|
*/
|
||||||
@@ -27,7 +36,7 @@ export default class HardpointSlot extends Slot {
|
|||||||
* @return {string} Label
|
* @return {string} Label
|
||||||
*/
|
*/
|
||||||
_getMaxClassLabel(translate) {
|
_getMaxClassLabel(translate) {
|
||||||
return translate(['U','S','M','L','H'][this.props.maxClass]);
|
return translate(['U', 'S', 'M', 'L', 'H'][this.props.maxClass]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -66,42 +75,77 @@ export default class HardpointSlot extends Slot {
|
|||||||
return <div className={className} draggable='true' onDragStart={drag} onDragEnd={drop}>
|
return <div className={className} draggable='true' onDragStart={drag} onDragEnd={drop}>
|
||||||
<div className={'cb'}>
|
<div className={'cb'}>
|
||||||
<div className={'l'}>
|
<div className={'l'}>
|
||||||
{m.mount && m.mount == 'F' ? <span onMouseOver={termtip.bind(null, 'fixed')} onMouseOut={tooltip.bind(null, null)}><MountFixed /></span> : ''}
|
{m.mount && m.mount == 'F' ? <span onMouseOver={termtip.bind(null, 'fixed')}
|
||||||
{m.mount && m.mount == 'G' ? <span onMouseOver={termtip.bind(null, 'gimballed')} onMouseOut={tooltip.bind(null, null)}><MountGimballed /></span> : ''}
|
onMouseOut={tooltip.bind(null, null)}><MountFixed/></span> : ''}
|
||||||
{m.mount && m.mount == 'T' ? <span onMouseOver={termtip.bind(null, 'turreted')} onMouseOut={tooltip.bind(null, null)}><MountTurret /></span> : ''}
|
{m.mount && m.mount == 'G' ? <span onMouseOver={termtip.bind(null, 'gimballed')}
|
||||||
{m.getDamageDist() && m.getDamageDist().K ? <span onMouseOver={termtip.bind(null, 'kinetic')} onMouseOut={tooltip.bind(null, null)}><DamageKinetic /></span> : ''}
|
onMouseOut={tooltip.bind(null, null)}><MountGimballed/></span> : ''}
|
||||||
{m.getDamageDist() && m.getDamageDist().T ? <span onMouseOver={termtip.bind(null, 'thermal')} onMouseOut={tooltip.bind(null, null)}><DamageThermal /></span> : ''}
|
{m.mount && m.mount == 'T' ? <span onMouseOver={termtip.bind(null, 'turreted')}
|
||||||
{m.getDamageDist() && m.getDamageDist().E ? <span onMouseOver={termtip.bind(null, 'explosive')} onMouseOut={tooltip.bind(null, null)}><DamageExplosive /></span> : ''}
|
onMouseOut={tooltip.bind(null, null)}><MountTurret/></span> : ''}
|
||||||
{m.getDamageDist() && m.getDamageDist().A ? <span onMouseOver={termtip.bind(null, 'absolute')} onMouseOut={tooltip.bind(null, null)}><DamageAbsolute /></span> : ''}
|
{m.getDamageDist() && m.getDamageDist().K ? <span onMouseOver={termtip.bind(null, 'kinetic')}
|
||||||
{classRating} {translate(m.name || m.grp)}{ m.mods && Object.keys(m.mods).length > 0 ? <span className='r' onMouseOver={termtip.bind(null, modTT)} onMouseOut={tooltip.bind(null, null)}><Modified /></span> : null }
|
onMouseOut={tooltip.bind(null, null)}><DamageKinetic/></span> : ''}
|
||||||
|
{m.getDamageDist() && m.getDamageDist().T ? <span onMouseOver={termtip.bind(null, 'thermal')}
|
||||||
|
onMouseOut={tooltip.bind(null, null)}><DamageThermal/></span> : ''}
|
||||||
|
{m.getDamageDist() && m.getDamageDist().E ? <span onMouseOver={termtip.bind(null, 'explosive')}
|
||||||
|
onMouseOut={tooltip.bind(null, null)}><DamageExplosive/></span> : ''}
|
||||||
|
{m.getDamageDist() && m.getDamageDist().A ? <span onMouseOver={termtip.bind(null, 'absolute')}
|
||||||
|
onMouseOut={tooltip.bind(null, null)}><DamageAbsolute/></span> : ''}
|
||||||
|
{classRating} {translate(m.name || m.grp)}{m.mods && Object.keys(m.mods).length > 0 ? <span className='r'
|
||||||
|
onMouseOver={termtip.bind(null, modTT)}
|
||||||
|
onMouseOut={tooltip.bind(null, null)}><Modified/></span> : null}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={'r'}>{formats.round(m.getMass())}{u.T}</div>
|
<div className={'r'}>{formats.round(m.getMass())}{u.T}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={'cb'}>
|
<div className={'cb'}>
|
||||||
{ m.getDps() ? <div className={'l'} onMouseOver={termtip.bind(null, m.getClip() ? 'dpssdps' : 'dps')} onMouseOut={tooltip.bind(null, null)}>{translate('DPS')}: {formats.round1(m.getDps())} { m.getClip() ? <span>({formats.round1((m.getClip() * m.getDps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload())) })</span> : null }</div> : null }
|
{m.getDps() ? <div className={'l'} onMouseOver={termtip.bind(null, m.getClip() ? 'dpssdps' : 'dps')}
|
||||||
{ m.getEps() ? <div className={'l'} onMouseOver={termtip.bind(null, m.getClip() ? 'epsseps' : 'eps')} onMouseOut={tooltip.bind(null, null)}>{translate('EPS')}: {formats.round1(m.getEps())}{u.MW} { m.getClip() ? <span>({formats.round1((m.getClip() * m.getEps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload())) }{u.MW})</span> : null }</div> : null }
|
onMouseOut={tooltip.bind(null, null)}>{translate('DPS')}: {formats.round1(m.getDps())} {m.getClip() ?
|
||||||
{ m.getHps() ? <div className={'l'} onMouseOver={termtip.bind(null, m.getClip() ? 'hpsshps' : 'hps')} onMouseOut={tooltip.bind(null, null)}>{translate('HPS')}: {formats.round1(m.getHps())} { m.getClip() ? <span>({formats.round1((m.getClip() * m.getHps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload())) })</span> : null }</div> : null }
|
<span>({formats.round1(m.getSDps())})</span> : null}</div> : null}
|
||||||
{ m.getDps() && m.getEps() ? <div className={'l'} onMouseOver={termtip.bind(null, 'dpe')} onMouseOut={tooltip.bind(null, null)}>{translate('DPE')}: {formats.f1(m.getDps() / m.getEps())}</div> : null }
|
{m.getDamage() ? <div className={'l'} onMouseOver={termtip.bind(null, m.getDamage() ? 'shotdmg' : 'shotdmg')}
|
||||||
{ m.getRoF() ? <div className={'l'} onMouseOver={termtip.bind(null, 'rof')} onMouseOut={tooltip.bind(null, null)}>{translate('ROF')}: {formats.f1(m.getRoF())}{u.ps}</div> : null }
|
onMouseOut={tooltip.bind(null, null)}>{translate('shotdmg')}: {formats.round1(m.getDamage())}</div> : null}
|
||||||
{ m.getRange() ? <div className={'l'}>{translate('range', m.grp)} {formats.f1(m.getRange() / 1000)}{u.km}</div> : null }
|
{m.getEps() ? <div className={'l'} onMouseOver={termtip.bind(null, m.getClip() ? 'epsseps' : 'eps')}
|
||||||
{ m.getScanTime() ? <div className={'l'}>{translate('scantime')} {formats.f1(m.getScanTime())}{u.s}</div> : null }
|
onMouseOut={tooltip.bind(null, null)}>{translate('EPS')}: {formats.round1(m.getEps())}{u.MW} {m.getClip() ?
|
||||||
{ m.getFalloff() ? <div className={'l'}>{translate('falloff')} {formats.round(m.getFalloff() / 1000)}{u.km}</div> : null }
|
<span>({formats.round1(m.getEps() * m.getSustainedFactor())}{u.MW})</span> : null}</div> : null}
|
||||||
{ m.getShieldBoost() ? <div className={'l'}>+{formats.pct1(m.getShieldBoost())}</div> : null }
|
{m.getHps() ? <div className={'l'} onMouseOver={termtip.bind(null, m.getClip() ? 'hpsshps' : 'hps')}
|
||||||
{ m.getAmmo() ? <div className={'l'}>{translate('ammunition')}: {formats.int(m.getClip())}/{formats.int(m.getAmmo())}</div> : null }
|
onMouseOut={tooltip.bind(null, null)}>{translate('HPS')}: {formats.round1(m.getHps())} {m.getClip() ?
|
||||||
{ m.getReload() ? <div className={'l'}>{translate('reload')}: {formats.round(m.getReload())}{u.s}</div> : null }
|
<span>({formats.round1(m.getHps() * m.getSustainedFactor())})</span> : null}</div> : null}
|
||||||
{ m.getShotSpeed() ? <div className={'l'}>{translate('shotspeed')}: {formats.int(m.getShotSpeed())}{u.mps}</div> : null }
|
{m.getDps() && m.getEps() ? <div className={'l'} onMouseOver={termtip.bind(null, 'dpe')}
|
||||||
{ m.getPiercing() ? <div className={'l'}>{translate('piercing')}: {formats.int(m.getPiercing())}</div> : null }
|
onMouseOut={tooltip.bind(null, null)}>{translate('DPE')}: {formats.f1(m.getDps() / m.getEps())}</div> : null}
|
||||||
{ m.getJitter() ? <div className={'l'}>{translate('jitter')}: {formats.f2(m.getJitter())}°</div> : null }
|
{m.getRoF() ? <div className={'l'} onMouseOver={termtip.bind(null, 'rof')}
|
||||||
{ showModuleResistances && m.getExplosiveResistance() ? <div className='l'>{translate('explres')}: {formats.pct(m.getExplosiveResistance())}</div> : null }
|
onMouseOut={tooltip.bind(null, null)}>{translate('ROF')}: {formats.f1(m.getRoF())}{u.ps}</div> : null}
|
||||||
{ showModuleResistances && m.getKineticResistance() ? <div className='l'>{translate('kinres')}: {formats.pct(m.getKineticResistance())}</div> : null }
|
{m.getRange() ? <div
|
||||||
{ showModuleResistances && m.getThermalResistance() ? <div className='l'>{translate('thermres')}: {formats.pct(m.getThermalResistance())}</div> : null }
|
className={'l'}>{translate('range', m.grp)} {formats.f1(m.getRange() / 1000)}{u.km}</div> : null}
|
||||||
{ m.getIntegrity() ? <div className='l'>{translate('integrity')}: {formats.int(m.getIntegrity())}</div> : null }
|
{m.getScanTime() ? <div
|
||||||
{ m && validMods.length > 0 ? <div className='r' ><button onClick={this._toggleModifications.bind(this)} onContextMenu={stopCtxPropagation} onMouseOver={termtip.bind(null, 'modifications')} onMouseOut={tooltip.bind(null, null)}><ListModifications /></button></div> : null }
|
className={'l'}>{translate('scantime')} {formats.f1(m.getScanTime())}{u.s}</div> : null}
|
||||||
|
{m.getFalloff() ? <div
|
||||||
|
className={'l'}>{translate('falloff')} {formats.round(m.getFalloff() / 1000)}{u.km}</div> : null}
|
||||||
|
{m.getShieldBoost() ? <div className={'l'}>+{formats.pct1(m.getShieldBoost())}</div> : null}
|
||||||
|
{m.getAmmo() ? <div
|
||||||
|
className={'l'}>{translate('ammunition')}: {formats.int(m.getClip())}/{formats.int(m.getAmmo())}</div> : null}
|
||||||
|
{m.getReload() ? <div className={'l'}>{translate('wep_reload')}: {formats.round(m.getReload())}{u.s}</div> : null}
|
||||||
|
{m.getShotSpeed() ? <div
|
||||||
|
className={'l'}>{translate('shotspeed')}: {formats.int(m.getShotSpeed())}{u.mps}</div> : null}
|
||||||
|
{m.getPiercing() ? <div className={'l'}>{translate('piercing')}: {formats.int(m.getPiercing())}</div> : null}
|
||||||
|
{m.getJitter() ? <div className={'l'}>{translate('jitter')}: {formats.f2(m.getJitter())}°</div> : null}
|
||||||
|
{m.getScanAngle() ? <div className={'l'}>{translate('scan angle')}: {formats.f2(m.getScanAngle())}°</div> : null}
|
||||||
|
{m.getScanRange() ? <div className={'l'}>{translate('scan range')}: {formats.int(m.getScanRange())}{u.m}</div> : null}
|
||||||
|
{m.getMaxAngle() ? <div className={'l'}>{translate('max angle')}: {formats.f2(m.getMaxAngle())}°</div> : null}
|
||||||
|
{showModuleResistances && m.getExplosiveResistance() ? <div
|
||||||
|
className='l'>{translate('explres')}: {formats.pct(m.getExplosiveResistance())}</div> : null}
|
||||||
|
{showModuleResistances && m.getKineticResistance() ? <div
|
||||||
|
className='l'>{translate('kinres')}: {formats.pct(m.getKineticResistance())}</div> : null}
|
||||||
|
{showModuleResistances && m.getThermalResistance() ? <div
|
||||||
|
className='l'>{translate('thermres')}: {formats.pct(m.getThermalResistance())}</div> : null}
|
||||||
|
{m.getIntegrity() ? <div className='l'>{translate('integrity')}: {formats.int(m.getIntegrity())}</div> : null}
|
||||||
|
{m && validMods.length > 0 ? <div className='r' tabIndex="0" ref={modButton => this.modButton = modButton}>
|
||||||
|
<button tabIndex="-1" onClick={this._toggleModifications.bind(this)} onContextMenu={stopCtxPropagation}
|
||||||
|
onMouseOver={termtip.bind(null, 'modifications')} onMouseOut={tooltip.bind(null, null)}>
|
||||||
|
<ListModifications/></button>
|
||||||
|
</div> : null}
|
||||||
</div>
|
</div>
|
||||||
</div>;
|
</div>;
|
||||||
} else {
|
} else {
|
||||||
return <div className={'empty'}>{translate('empty')}</div>;
|
return <div className={'empty'}>{translate('empty')}</div>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import SlotSection from './SlotSection';
|
import SlotSection from './SlotSection';
|
||||||
import HardpointSlot from './HardpointSlot';
|
import HardpointSlot from './HardpointSlot';
|
||||||
import cn from 'classnames';
|
|
||||||
import { MountFixed, MountGimballed, MountTurret } from '../components/SvgIcons';
|
import { MountFixed, MountGimballed, MountTurret } from '../components/SvgIcons';
|
||||||
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
||||||
|
|
||||||
@@ -17,14 +16,25 @@ export default class HardpointSlotSection extends SlotSection {
|
|||||||
*/
|
*/
|
||||||
constructor(props, context) {
|
constructor(props, context) {
|
||||||
super(props, context, 'hardpoints', 'hardpoints');
|
super(props, context, 'hardpoints', 'hardpoints');
|
||||||
|
|
||||||
this._empty = this._empty.bind(this);
|
this._empty = this._empty.bind(this);
|
||||||
|
this.selectedRefId = null;
|
||||||
|
this.firstRefId = 'emptyall';
|
||||||
|
this.lastRefId = 'nl-F';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle focus when component updates
|
||||||
|
* @param {Object} prevProps React Component properties
|
||||||
|
*/
|
||||||
|
componentDidUpdate(prevProps) {
|
||||||
|
this._handleSectionFocus(prevProps,this.firstRefId, this.lastRefId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Empty all slots
|
* Empty all slots
|
||||||
*/
|
*/
|
||||||
_empty() {
|
_empty() {
|
||||||
|
this.selectedRefId = 'emptyall';
|
||||||
this.props.ship.emptyWeapons();
|
this.props.ship.emptyWeapons();
|
||||||
this.props.onChange();
|
this.props.onChange();
|
||||||
this._close();
|
this._close();
|
||||||
@@ -37,6 +47,7 @@ export default class HardpointSlotSection extends SlotSection {
|
|||||||
* @param {SyntheticEvent} event Event
|
* @param {SyntheticEvent} event Event
|
||||||
*/
|
*/
|
||||||
_fill(group, mount, event) {
|
_fill(group, mount, event) {
|
||||||
|
this.selectedRefId = group + '-' + mount;
|
||||||
this.props.ship.useWeapon(group, mount, null, event.getModifierState('Alt'));
|
this.props.ship.useWeapon(group, mount, null, event.getModifierState('Alt'));
|
||||||
this.props.onChange();
|
this.props.onChange();
|
||||||
this._close();
|
this._close();
|
||||||
@@ -95,52 +106,61 @@ export default class HardpointSlotSection 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' tabIndex='0' onClick={this._empty} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['emptyall'] = smRef}>{translate('empty all')}</li>
|
||||||
<li className='optional-hide' style={{ textAlign: 'center', marginTop: '1em' }}>{translate('PHRASE_ALT_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>
|
||||||
<li className='c' onClick={_fill.bind(this, 'pl', 'F')}><MountFixed className='lg'/></li>
|
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'pl', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['pl-F'] = smRef}><MountFixed className='lg'/></li>
|
||||||
<li className='c' onClick={_fill.bind(this, 'pl', 'G')}><MountGimballed className='lg'/></li>
|
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'pl', 'G')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['pl-G'] = smRef}><MountGimballed className='lg'/></li>
|
||||||
<li className='c' onClick={_fill.bind(this, 'pl', 'T')}><MountTurret className='lg'/></li>
|
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'pl', 'T')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['pl-T'] = smRef}><MountTurret className='lg'/></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div className='select-group cap'>{translate('ul')}</div>
|
<div className='select-group cap'>{translate('ul')}</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li className='c' onClick={_fill.bind(this, 'ul', 'F')}><MountFixed className='lg'/></li>
|
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'ul', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['ul-F'] = smRef}><MountFixed className='lg'/></li>
|
||||||
<li className='c' onClick={_fill.bind(this, 'ul', 'G')}><MountGimballed className='lg'/></li>
|
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'ul', 'G')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['ul-G'] = smRef}><MountGimballed className='lg'/></li>
|
||||||
<li className='c' onClick={_fill.bind(this, 'ul', 'T')}><MountTurret className='lg'/></li>
|
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'ul', 'T')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['ul-T'] = smRef}><MountTurret className='lg'/></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div className='select-group cap'>{translate('bl')}</div>
|
<div className='select-group cap'>{translate('bl')}</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li className='c' onClick={_fill.bind(this, 'bl', 'F')}><MountFixed className='lg'/></li>
|
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'bl', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['bl-F'] = smRef}><MountFixed className='lg'/></li>
|
||||||
<li className='c' onClick={_fill.bind(this, 'bl', 'G')}><MountGimballed className='lg'/></li>
|
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'bl', 'G')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['bl-G'] = smRef}><MountGimballed className='lg'/></li>
|
||||||
<li className='c' onClick={_fill.bind(this, 'bl', 'T')}><MountTurret className='lg'/></li>
|
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'bl', 'T')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['bl-T'] = smRef}><MountTurret className='lg'/></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div className='select-group cap'>{translate('mc')}</div>
|
<div className='select-group cap'>{translate('mc')}</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li className='c' onClick={_fill.bind(this, 'mc', 'F')}><MountFixed className='lg'/></li>
|
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'mc', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['mc-F'] = smRef}><MountFixed className='lg'/></li>
|
||||||
<li className='c' onClick={_fill.bind(this, 'mc', 'G')}><MountGimballed className='lg'/></li>
|
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'mc', 'G')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['mc-G'] = smRef}><MountGimballed className='lg'/></li>
|
||||||
<li className='c' onClick={_fill.bind(this, 'mc', 'T')}><MountTurret className='lg'/></li>
|
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'mc', 'T')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['mc-T'] = smRef}><MountTurret className='lg'/></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div className='select-group cap'>{translate('c')}</div>
|
<div className='select-group cap'>{translate('c')}</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li className='c' onClick={_fill.bind(this, 'c', 'F')}><MountFixed className='lg'/></li>
|
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'c', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['c-F'] = smRef}><MountFixed className='lg'/></li>
|
||||||
<li className='c' onClick={_fill.bind(this, 'c', 'G')}><MountGimballed className='lg'/></li>
|
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'c', 'G')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['c-G'] = smRef}><MountGimballed className='lg'/></li>
|
||||||
<li className='c' onClick={_fill.bind(this, 'c', 'T')}><MountTurret className='lg'/></li>
|
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'c', 'T')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['c-T'] = smRef}><MountTurret className='lg'/></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div className='select-group cap'>{translate('fc')}</div>
|
<div className='select-group cap'>{translate('fc')}</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li className='c' onClick={_fill.bind(this, 'fc', 'F')}><MountFixed className='lg'/></li>
|
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'fc', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['fc-F'] = smRef}><MountFixed className='lg'/></li>
|
||||||
<li className='c' onClick={_fill.bind(this, 'fc', 'G')}><MountGimballed className='lg'/></li>
|
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'fc', 'G')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['fc-G'] = smRef}><MountGimballed className='lg'/></li>
|
||||||
<li className='c' onClick={_fill.bind(this, 'fc', 'T')}><MountTurret className='lg'/></li>
|
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'fc', 'T')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['fc-T'] = smRef}><MountTurret className='lg'/></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div className='select-group cap'>{translate('pa')}</div>
|
<div className='select-group cap'>{translate('pa')}</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li className='lc' onClick={_fill.bind(this, 'pa', 'F')}>{translate('pa')}</li>
|
<li className='lc' tabIndex='0' onClick={_fill.bind(this, 'pa', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['pa-F'] = smRef}>{translate('pa')}</li>
|
||||||
|
</ul>
|
||||||
|
<div className='select-group cap'>{translate('rg')}</div>
|
||||||
|
<ul>
|
||||||
|
<li className='lc' tabIndex='0' onClick={_fill.bind(this, 'rg', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['rg-F'] = smRef}>{translate('rg')}</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div className='select-group cap'>{translate('nl')}</div>
|
<div className='select-group cap'>{translate('nl')}</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li className='lc' onClick={_fill.bind(this, 'nl', 'F')}>{translate('nl')}</li>
|
<li className='lc' tabIndex='0' onClick={_fill.bind(this, 'nl', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['nl-F'] = smRef}>{translate('nl')}</li>
|
||||||
|
</ul>
|
||||||
|
<div className='select-group cap'>{translate('rfl')}</div>
|
||||||
|
<ul>
|
||||||
|
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'rfl', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['rfl-F'] = smRef}><MountFixed className='lg'/></li>
|
||||||
|
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'rfl', 'T')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['rfl-T'] = smRef}><MountTurret className='lg'/></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,11 +9,14 @@ import { Cogs, CoriolisLogo, Hammer, Help, Rocket, StatsBars } from './SvgIcons'
|
|||||||
import { Ships } from 'coriolis-data/dist';
|
import { Ships } from 'coriolis-data/dist';
|
||||||
import Persist from '../stores/Persist';
|
import Persist from '../stores/Persist';
|
||||||
import { toDetailedExport } from '../shipyard/Serializer';
|
import { toDetailedExport } from '../shipyard/Serializer';
|
||||||
|
import Ship from '../shipyard/Ship';
|
||||||
|
import ModalBatchOrbis from './ModalBatchOrbis';
|
||||||
import ModalDeleteAll from './ModalDeleteAll';
|
import ModalDeleteAll from './ModalDeleteAll';
|
||||||
import ModalExport from './ModalExport';
|
import ModalExport from './ModalExport';
|
||||||
import ModalHelp from './ModalHelp';
|
import ModalHelp from './ModalHelp';
|
||||||
import ModalImport from './ModalImport';
|
import ModalImport from './ModalImport';
|
||||||
import Slider from './Slider';
|
import Slider from './Slider';
|
||||||
|
import Announcement from './Announcement';
|
||||||
import { outfitURL } from '../utils/UrlGenerators';
|
import { outfitURL } from '../utils/UrlGenerators';
|
||||||
|
|
||||||
const SIZE_MIN = 0.65;
|
const SIZE_MIN = 0.65;
|
||||||
@@ -74,8 +77,11 @@ export default class Header extends TranslatedComponent {
|
|||||||
this._openShips = this._openMenu.bind(this, 's');
|
this._openShips = this._openMenu.bind(this, 's');
|
||||||
this._openBuilds = this._openMenu.bind(this, 'b');
|
this._openBuilds = this._openMenu.bind(this, 'b');
|
||||||
this._openComp = this._openMenu.bind(this, 'comp');
|
this._openComp = this._openMenu.bind(this, 'comp');
|
||||||
|
this._openAnnounce = this._openMenu.bind(this, 'announce');
|
||||||
|
this._getAnnouncementsMenu = this._getAnnouncementsMenu.bind(this);
|
||||||
this._openSettings = this._openMenu.bind(this, 'settings');
|
this._openSettings = this._openMenu.bind(this, 'settings');
|
||||||
this._showHelp = this._showHelp.bind(this);
|
this._showHelp = this._showHelp.bind(this);
|
||||||
|
this.update = this.update.bind(this);
|
||||||
this.languageOptions = [];
|
this.languageOptions = [];
|
||||||
this.insuranceOptions = [];
|
this.insuranceOptions = [];
|
||||||
this.state = {
|
this.state = {
|
||||||
@@ -235,6 +241,43 @@ export default class Header extends TranslatedComponent {
|
|||||||
/>);
|
/>);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uploads all ship-builds to orbis
|
||||||
|
* @param {e} e Event
|
||||||
|
*/
|
||||||
|
_uploadAllBuildsToOrbis(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const data = Persist.getBuilds();
|
||||||
|
let postObject = [];
|
||||||
|
for (const ship in data) {
|
||||||
|
for (const code in data[ship]) {
|
||||||
|
const shipModel = ship;
|
||||||
|
if (!shipModel) {
|
||||||
|
throw 'No such ship found: "' + ship + '"';
|
||||||
|
}
|
||||||
|
const shipTemplate = Ships[shipModel];
|
||||||
|
const shipPostObject = {};
|
||||||
|
let shipInstance = new Ship(shipModel, shipTemplate.properties, shipTemplate.slots);
|
||||||
|
shipInstance.buildWith(null);
|
||||||
|
shipInstance.buildFrom(data[ship][code]);
|
||||||
|
shipPostObject.coriolisId = shipInstance.id;
|
||||||
|
shipPostObject.coriolisShip = shipInstance;
|
||||||
|
|
||||||
|
shipPostObject.coriolisShip.url = window.location.origin + outfitURL(shipModel, data[ship][code], code);
|
||||||
|
shipPostObject.title = code || shipInstance.id;
|
||||||
|
shipPostObject.description = code || shipInstance.id;
|
||||||
|
shipPostObject.ShipName = shipInstance.id;
|
||||||
|
shipPostObject.Ship = shipInstance.id;
|
||||||
|
postObject.push(shipPostObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(postObject);
|
||||||
|
|
||||||
|
this.context.showModal(<ModalBatchOrbis
|
||||||
|
ships={postObject}
|
||||||
|
/>);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show export modal with detailed export
|
* Show export modal with detailed export
|
||||||
* @param {SyntheticEvent} e Event
|
* @param {SyntheticEvent} e Event
|
||||||
@@ -306,7 +349,7 @@ export default class Header extends TranslatedComponent {
|
|||||||
_getShipsMenu() {
|
_getShipsMenu() {
|
||||||
let shipList = [];
|
let shipList = [];
|
||||||
|
|
||||||
for (let s in Ships) {
|
for (let s of this.shipOrder) {
|
||||||
shipList.push(<ActiveLink key={s} href={outfitURL(s)} className='block'>{Ships[s].properties.name}</ActiveLink>);
|
shipList.push(<ActiveLink key={s} href={outfitURL(s)} className='block'>{Ships[s].properties.name}</ActiveLink>);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -372,6 +415,29 @@ export default class Header extends TranslatedComponent {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate the announcement menu
|
||||||
|
* @return {React.Component} Menu
|
||||||
|
*/
|
||||||
|
_getAnnouncementsMenu() {
|
||||||
|
let announcements;
|
||||||
|
let translate = this.context.language.translate;
|
||||||
|
|
||||||
|
if (this.props.announcements) {
|
||||||
|
announcements = [];
|
||||||
|
for (let announce of this.props.announcements) {
|
||||||
|
announcements.push(<Announcement text={announce.message} />);
|
||||||
|
announcements.push(<hr/>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className='menu-list' onClick={ (e) => e.stopPropagation() } style={{ whiteSpace: 'nowrap' }}>
|
||||||
|
{announcements}
|
||||||
|
<hr />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate the settings menu
|
* Generate the settings menu
|
||||||
* @return {React.Component} Menu
|
* @return {React.Component} Menu
|
||||||
@@ -430,6 +496,7 @@ export default class Header extends TranslatedComponent {
|
|||||||
{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._uploadAllBuildsToOrbis.bind(this)}>{translate('upload all builds to orbis')}</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="#" className='block' 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>
|
||||||
@@ -494,6 +561,15 @@ export default class Header extends TranslatedComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async update() {
|
||||||
|
const reg = await navigator.serviceWorker.getRegistration();
|
||||||
|
if (!reg || !reg.waiting) {
|
||||||
|
return window.location.reload();
|
||||||
|
}
|
||||||
|
reg.waiting.postMessage('skipWaiting');
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render the header
|
* Render the header
|
||||||
* @return {React.Component} Header
|
* @return {React.Component} Header
|
||||||
@@ -504,7 +580,10 @@ export default class Header extends TranslatedComponent {
|
|||||||
let hasBuilds = Persist.hasBuilds();
|
let hasBuilds = Persist.hasBuilds();
|
||||||
return (
|
return (
|
||||||
<header>
|
<header>
|
||||||
{this.props.appCacheUpdate && <div id="app-update" onClick={() => window.location.reload() }>{translate('PHRASE_UPDATE_RDY')}</div>}
|
{this.props.appCacheUpdate && <div id="app-update" onClick={this.update}>{translate('PHRASE_UPDATE_RDY')}</div>}
|
||||||
|
{this.props.appCacheUpdate ? <a className={'view-changes'} href={'https://github.com/EDCD/coriolis/compare/edcd:develop@{' + window.CORIOLIS_DATE + '}...edcd:develop'} target="_blank">
|
||||||
|
{'View Release Changes'}
|
||||||
|
</a> : null}
|
||||||
<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'>
|
||||||
@@ -528,6 +607,23 @@ export default class Header extends TranslatedComponent {
|
|||||||
{openedMenu == 'comp' ? this._getComparisonsMenu() : null}
|
{openedMenu == 'comp' ? this._getComparisonsMenu() : null}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className='l menu'>
|
||||||
|
<div className={cn('menu-header', { selected: openedMenu == 'announce', disabled: this.props.announcements.length === 0})} onClick={this.props.announcements.length !== 0 && this._openAnnounce}>
|
||||||
|
<span className='menu-item-label'>{translate('announcements')}</span>
|
||||||
|
</div>
|
||||||
|
{openedMenu == 'announce' ? this._getAnnouncementsMenu() : null}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{window.location.origin.search('.edcd.io') >= 0 ?
|
||||||
|
<div className='l menu'>
|
||||||
|
<a href="https://youtu.be/4SvnLcefhtI" target="_blank">
|
||||||
|
<div className={cn('menu-header')}>
|
||||||
|
<Rocket className='warning'/><span className='menu-item-label'>{translate('please migrate to coriolis.io')}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div> : null
|
||||||
|
}
|
||||||
|
|
||||||
<div className='r menu'>
|
<div className='r menu'>
|
||||||
<div className={cn('menu-header', { selected: openedMenu == 'settings' })} onClick={this._openSettings}>
|
<div className={cn('menu-header', { selected: openedMenu == 'settings' })} onClick={this._openSettings}>
|
||||||
<Cogs className='xl warning'/><span className='menu-item-label'>{translate('settings')}</span>
|
<Cogs className='xl warning'/><span className='menu-item-label'>{translate('settings')}</span>
|
||||||
|
|||||||
@@ -26,13 +26,16 @@ export default class InternalSlot extends Slot {
|
|||||||
let classRating = m.class + m.rating;
|
let classRating = m.class + m.rating;
|
||||||
let { drag, drop, ship } = this.props;
|
let { drag, drop, ship } = this.props;
|
||||||
let { termtip, tooltip } = this.context;
|
let { termtip, tooltip } = this.context;
|
||||||
let validMods = Modifications.modules[m.grp].modifications || [];
|
let validMods = (Modifications.modules[m.grp] ? Modifications.modules[m.grp].modifications : []);
|
||||||
let showModuleResistances = Persist.showModuleResistances();
|
let showModuleResistances = Persist.showModuleResistances();
|
||||||
|
|
||||||
// Modifications tooltip shows blueprint and grade, if available
|
// Modifications tooltip shows blueprint and grade, if available
|
||||||
let modTT = translate('modified');
|
let modTT = translate('modified');
|
||||||
if (m && m.blueprint && m.blueprint.name) {
|
if (m && m.blueprint && m.blueprint.name) {
|
||||||
modTT = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
|
modTT = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
|
||||||
|
if (m.blueprint.special && m.blueprint.special.id >= 0) {
|
||||||
|
modTT += ', ' + translate(m.blueprint.special.name);
|
||||||
|
}
|
||||||
modTT = (
|
modTT = (
|
||||||
<div>
|
<div>
|
||||||
<div>{modTT}</div>
|
<div>{modTT}</div>
|
||||||
@@ -60,6 +63,9 @@ export default class InternalSlot extends Slot {
|
|||||||
{ m.getSpinup() ? <div className={'l'}>{translate('spinup')}: {formats.f1(m.getSpinup())}{u.s}</div> : null }
|
{ m.getSpinup() ? <div className={'l'}>{translate('spinup')}: {formats.f1(m.getSpinup())}{u.s}</div> : null }
|
||||||
{ m.getDuration() ? <div className={'l'}>{translate('duration')}: {formats.f1(m.getDuration())}{u.s}</div> : null }
|
{ m.getDuration() ? <div className={'l'}>{translate('duration')}: {formats.f1(m.getDuration())}{u.s}</div> : null }
|
||||||
{ m.grp === 'scb' ? <div className={'l'}>{translate('cells')}: {formats.int(m.getAmmo() + 1)}</div> : null }
|
{ m.grp === 'scb' ? <div className={'l'}>{translate('cells')}: {formats.int(m.getAmmo() + 1)}</div> : null }
|
||||||
|
{ m.grp === 'gsrp' ? <div className={'l'}>{translate('shield addition')}: {formats.f1(m.getShieldAddition())}{u.MJ}</div> : null }
|
||||||
|
{ m.grp === 'gfsb' ? <div className={'l'}>{translate('jump addition')}: {formats.f1(m.getJumpBoost())}{u.LY}</div> : null }
|
||||||
|
{ m.grp === 'gs' ? <div className={'l'}>{translate('shield addition')}: {formats.f1(m.getShieldAddition())}{u.MJ}</div> : null }
|
||||||
{ m.getShieldReinforcement() ? <div className={'l'}>{translate('shieldreinforcement')}: {formats.f1(m.getDuration() * m.getShieldReinforcement())}{u.MJ}</div> : null }
|
{ m.getShieldReinforcement() ? <div className={'l'}>{translate('shieldreinforcement')}: {formats.f1(m.getDuration() * m.getShieldReinforcement())}{u.MJ}</div> : null }
|
||||||
{ m.getShieldReinforcement() ? <div className={'l'}>{translate('total')}: {formats.int((m.getAmmo() + 1) * (m.getDuration() * m.getShieldReinforcement()))}{u.MJ}</div> : null }
|
{ m.getShieldReinforcement() ? <div className={'l'}>{translate('total')}: {formats.int((m.getAmmo() + 1) * (m.getDuration() * m.getShieldReinforcement()))}{u.MJ}</div> : null }
|
||||||
{ m.repair ? <div className={'l'}>{translate('repair')}: {m.repair}</div> : null }
|
{ m.repair ? <div className={'l'}>{translate('repair')}: {m.repair}</div> : null }
|
||||||
@@ -67,21 +73,22 @@ export default class InternalSlot extends Slot {
|
|||||||
{ m.getRange() ? <div className={'l'}>{translate('range')} {formats.f2(m.getRange())}{u.km}</div> : null }
|
{ m.getRange() ? <div className={'l'}>{translate('range')} {formats.f2(m.getRange())}{u.km}</div> : null }
|
||||||
{ m.getRangeT() ? <div className={'l'}>{translate('ranget')} {formats.f1(m.getRangeT())}{u.s}</div> : null }
|
{ m.getRangeT() ? <div className={'l'}>{translate('ranget')} {formats.f1(m.getRangeT())}{u.s}</div> : null }
|
||||||
{ m.getTime() ? <div className={'l'}>{translate('time')}: {formats.time(m.getTime())}</div> : null }
|
{ m.getTime() ? <div className={'l'}>{translate('time')}: {formats.time(m.getTime())}</div> : null }
|
||||||
|
{ m.getHackTime() ? <div className={'l'}>{translate('hacktime')}: {formats.time(m.getHackTime())}</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'}>{translate('range')}: {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'}>∞{u.Ls}</div> : null }
|
{ m.rangeLS === null ? <div className={'l'}>∞{u.Ls}</div> : null }
|
||||||
{ m.rangeRating ? <div className={'l'}>{translate('range')}: {m.rangeRating}</div> : null }
|
{ m.rangeRating ? <div className={'l'}>{translate('range')}: {m.rangeRating}</div> : null }
|
||||||
{ m.maximum ? <div className={'l'}>{translate('max')}: {(m.maximum)}</div> : null }
|
|
||||||
{ m.passengers ? <div className={'l'}>{translate('passengers')}: {m.passengers}</div> : null }
|
{ m.passengers ? <div className={'l'}>{translate('passengers')}: {m.passengers}</div> : null }
|
||||||
{ m.getRegenerationRate() ? <div className='l'>{translate('regen')}: {formats.round1(m.getRegenerationRate())}{u.ps}</div> : null }
|
{ m.getRegenerationRate() ? <div className='l'>{translate('regen')}: {formats.round1(m.getRegenerationRate())}{u.ps}</div> : null }
|
||||||
{ m.getBrokenRegenerationRate() ? <div className='l'>{translate('brokenregen')}: {formats.round1(m.getBrokenRegenerationRate())}{u.ps}</div> : null }
|
{ m.getBrokenRegenerationRate() ? <div className='l'>{translate('brokenregen')}: {formats.round1(m.getBrokenRegenerationRate())}{u.ps}</div> : null }
|
||||||
{ showModuleResistances && m.getExplosiveResistance() ? <div className='l'>{translate('explres')}: {formats.pct(m.getExplosiveResistance())}</div> : null }
|
{ showModuleResistances && m.getExplosiveResistance() ? <div className='l'>{translate('explres')}: {formats.pct(m.getExplosiveResistance())}</div> : null }
|
||||||
{ showModuleResistances && m.getKineticResistance() ? <div className='l'>{translate('kinres')}: {formats.pct(m.getKineticResistance())}</div> : null }
|
{ showModuleResistances && m.getKineticResistance() ? <div className='l'>{translate('kinres')}: {formats.pct(m.getKineticResistance())}</div> : null }
|
||||||
{ showModuleResistances && m.getThermalResistance() ? <div className='l'>{translate('thermres')}: {formats.pct(m.getThermalResistance())}</div> : null }
|
{ showModuleResistances && m.getThermalResistance() ? <div className='l'>{translate('thermres')}: {formats.pct(m.getThermalResistance())}</div> : null }
|
||||||
|
{ showModuleResistances && m.getCausticResistance() ? <div className='l'>{translate('causres')}: {formats.pct(m.getCausticResistance())}</div> : null }
|
||||||
{ m.getHullReinforcement() ? <div className='l'>{translate('armour')}: {formats.int(m.getHullReinforcement() + ship.baseArmour * m.getModValue('hullboost') / 10000)}</div> : null }
|
{ m.getHullReinforcement() ? <div className='l'>{translate('armour')}: {formats.int(m.getHullReinforcement() + ship.baseArmour * m.getModValue('hullboost') / 10000)}</div> : null }
|
||||||
{ m.getProtection() ? <div className='l'>{translate('protection')}: {formats.rPct(m.getProtection())}</div> : null }
|
{ m.getProtection() ? <div className='l'>{translate('protection')}: {formats.rPct(m.getProtection())}</div> : null }
|
||||||
{ m.getIntegrity() ? <div className='l'>{translate('integrity')}: {formats.int(m.getIntegrity())}</div> : null }
|
{ m.getIntegrity() ? <div className='l'>{translate('integrity')}: {formats.int(m.getIntegrity())}</div> : null }
|
||||||
{ m && validMods.length > 0 ? <div className='r' ><button onClick={this._toggleModifications.bind(this)} onContextMenu={stopCtxPropagation} onMouseOver={termtip.bind(null, 'modifications')} onMouseOut={tooltip.bind(null, null)}><ListModifications /></button></div> : null }
|
{ m && validMods.length > 0 ? <div className='r' tabIndex="0" ref={ modButton => this.modButton = modButton }><button tabIndex="-1" onClick={this._toggleModifications.bind(this)} onContextMenu={stopCtxPropagation} onMouseOver={termtip.bind(null, 'modifications')} onMouseOut={tooltip.bind(null, null)}><ListModifications /></button></div> : null }
|
||||||
</div>
|
</div>
|
||||||
</div>;
|
</div>;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import cn from 'classnames';
|
|
||||||
import SlotSection from './SlotSection';
|
import SlotSection from './SlotSection';
|
||||||
import InternalSlot from './InternalSlot';
|
import InternalSlot from './InternalSlot';
|
||||||
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
||||||
@@ -18,7 +17,6 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
*/
|
*/
|
||||||
constructor(props, context) {
|
constructor(props, context) {
|
||||||
super(props, context, 'internal', 'optional internal');
|
super(props, context, 'internal', 'optional internal');
|
||||||
|
|
||||||
this._empty = this._empty.bind(this);
|
this._empty = this._empty.bind(this);
|
||||||
this._fillWithCargo = this._fillWithCargo.bind(this);
|
this._fillWithCargo = this._fillWithCargo.bind(this);
|
||||||
this._fillWithCells = this._fillWithCells.bind(this);
|
this._fillWithCells = this._fillWithCells.bind(this);
|
||||||
@@ -29,12 +27,24 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
this._fillWithFirstClassCabins = this._fillWithFirstClassCabins.bind(this);
|
this._fillWithFirstClassCabins = this._fillWithFirstClassCabins.bind(this);
|
||||||
this._fillWithBusinessClassCabins = this._fillWithBusinessClassCabins.bind(this);
|
this._fillWithBusinessClassCabins = this._fillWithBusinessClassCabins.bind(this);
|
||||||
this._fillWithEconomyClassCabins = this._fillWithEconomyClassCabins.bind(this);
|
this._fillWithEconomyClassCabins = this._fillWithEconomyClassCabins.bind(this);
|
||||||
|
this.selectedRefId = null;
|
||||||
|
this.firstRefId = 'emptyall';
|
||||||
|
this.lastRefId = this.sectionRefArr['pcq'] ? 'pcq' : 'pcm';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle focus when component updates
|
||||||
|
* @param {Object} prevProps React Component properties
|
||||||
|
*/
|
||||||
|
componentDidUpdate(prevProps) {
|
||||||
|
this._handleSectionFocus(prevProps,this.firstRefId, this.lastRefId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Empty all slots
|
* Empty all slots
|
||||||
*/
|
*/
|
||||||
_empty() {
|
_empty() {
|
||||||
|
this.selectedRefId = 'emptyall';
|
||||||
this.props.ship.emptyInternal();
|
this.props.ship.emptyInternal();
|
||||||
this.props.onChange();
|
this.props.onChange();
|
||||||
this._close();
|
this._close();
|
||||||
@@ -45,6 +55,7 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
* @param {SyntheticEvent} event Event
|
* @param {SyntheticEvent} event Event
|
||||||
*/
|
*/
|
||||||
_fillWithCargo(event) {
|
_fillWithCargo(event) {
|
||||||
|
this.selectedRefId = 'cargo';
|
||||||
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) => {
|
||||||
@@ -61,6 +72,7 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
* @param {SyntheticEvent} event Event
|
* @param {SyntheticEvent} event Event
|
||||||
*/
|
*/
|
||||||
_fillWithFuelTanks(event) {
|
_fillWithFuelTanks(event) {
|
||||||
|
this.selectedRefId = 'ft';
|
||||||
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) => {
|
||||||
@@ -77,6 +89,7 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
* @param {SyntheticEvent} event Event
|
* @param {SyntheticEvent} event Event
|
||||||
*/
|
*/
|
||||||
_fillWithLuxuryCabins(event) {
|
_fillWithLuxuryCabins(event) {
|
||||||
|
this.selectedRefId = 'pcq';
|
||||||
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) => {
|
||||||
@@ -93,6 +106,7 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
* @param {SyntheticEvent} event Event
|
* @param {SyntheticEvent} event Event
|
||||||
*/
|
*/
|
||||||
_fillWithFirstClassCabins(event) {
|
_fillWithFirstClassCabins(event) {
|
||||||
|
this.selectedRefId = 'pcm';
|
||||||
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) => {
|
||||||
@@ -109,6 +123,7 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
* @param {SyntheticEvent} event Event
|
* @param {SyntheticEvent} event Event
|
||||||
*/
|
*/
|
||||||
_fillWithBusinessClassCabins(event) {
|
_fillWithBusinessClassCabins(event) {
|
||||||
|
this.selectedRefId = 'pci';
|
||||||
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) => {
|
||||||
@@ -125,6 +140,7 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
* @param {SyntheticEvent} event Event
|
* @param {SyntheticEvent} event Event
|
||||||
*/
|
*/
|
||||||
_fillWithEconomyClassCabins(event) {
|
_fillWithEconomyClassCabins(event) {
|
||||||
|
this.selectedRefId = 'pce';
|
||||||
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) => {
|
||||||
@@ -141,6 +157,7 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
* @param {SyntheticEvent} event Event
|
* @param {SyntheticEvent} event Event
|
||||||
*/
|
*/
|
||||||
_fillWithCells(event) {
|
_fillWithCells(event) {
|
||||||
|
this.selectedRefId = 'scb';
|
||||||
let clobber = event.getModifierState('Alt');
|
let clobber = event.getModifierState('Alt');
|
||||||
let ship = this.props.ship;
|
let ship = this.props.ship;
|
||||||
let chargeCap = 0; // Capacity of single activation
|
let chargeCap = 0; // Capacity of single activation
|
||||||
@@ -160,6 +177,7 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
* @param {SyntheticEvent} event Event
|
* @param {SyntheticEvent} event Event
|
||||||
*/
|
*/
|
||||||
_fillWithArmor(event) {
|
_fillWithArmor(event) {
|
||||||
|
this.selectedRefId = 'hr';
|
||||||
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) => {
|
||||||
@@ -176,6 +194,7 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
* @param {SyntheticEvent} event Event
|
* @param {SyntheticEvent} event Event
|
||||||
*/
|
*/
|
||||||
_fillWithModuleReinforcementPackages(event) {
|
_fillWithModuleReinforcementPackages(event) {
|
||||||
|
this.selectedRefId = 'mrp';
|
||||||
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) => {
|
||||||
@@ -240,16 +259,16 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
_getSectionMenu(translate, ship) {
|
_getSectionMenu(translate, ship) {
|
||||||
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' tabIndex='0' onClick={this._empty} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['emptyall'] = smRef}>{translate('empty all')}</li>
|
||||||
<li className='lc' onClick={this._fillWithCargo}>{translate('cargo')}</li>
|
<li className='lc' tabIndex='0' onClick={this._fillWithCargo} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['cargo'] = smRef}>{translate('cargo')}</li>
|
||||||
<li className='lc' onClick={this._fillWithCells}>{translate('scb')}</li>
|
<li className='lc' tabIndex='0' onClick={this._fillWithCells} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['scb'] = smRef}>{translate('scb')}</li>
|
||||||
<li className='lc' onClick={this._fillWithArmor}>{translate('hr')}</li>
|
<li className='lc' tabIndex='0' onClick={this._fillWithArmor} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['hr'] = smRef}>{translate('hr')}</li>
|
||||||
<li className='lc' onClick={this._fillWithModuleReinforcementPackages}>{translate('mrp')}</li>
|
<li className='lc' tabIndex='0' onClick={this._fillWithModuleReinforcementPackages} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['mrp'] = smRef}>{translate('mrp')}</li>
|
||||||
<li className='lc' onClick={this._fillWithFuelTanks}>{translate('ft')}</li>
|
<li className='lc' tabIndex='0' onClick={this._fillWithFuelTanks} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['ft'] = smRef}>{translate('ft')}</li>
|
||||||
<li className='lc' onClick={this._fillWithEconomyClassCabins}>{translate('pce')}</li>
|
<li className='lc' tabIndex='0' onClick={this._fillWithEconomyClassCabins} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['pce'] = smRef}>{translate('pce')}</li>
|
||||||
<li className='lc' onClick={this._fillWithBusinessClassCabins}>{translate('pci')}</li>
|
<li className='lc' tabIndex='0' onClick={this._fillWithBusinessClassCabins} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['pci'] = smRef}>{translate('pci')}</li>
|
||||||
<li className='lc' onClick={this._fillWithFirstClassCabins}>{translate('pcm')}</li>
|
<li className='lc' tabIndex='0' onClick={this._fillWithFirstClassCabins} onKeyDown={ship.luxuryCabins ? '' : this._keyDown} ref={smRef => this.sectionRefArr['pcm'] = smRef}>{translate('pcm')}</li>
|
||||||
{ ship.luxuryCabins ? <li className='lc' onClick={this._fillWithLuxuryCabins}>{translate('pcq')}</li> : ''}
|
{ ship.luxuryCabins ? <li className='lc' tabIndex='0' onClick={this._fillWithLuxuryCabins} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['pcq'] = smRef}>{translate('pcq')}</li> : ''}
|
||||||
<li className='optional-hide' style={{ textAlign: 'center', marginTop: '1em' }}>{translate('PHRASE_ALT_ALL')}</li>
|
<li className='optional-hide' style={{ textAlign: 'center', marginTop: '1em' }}>{translate('PHRASE_ALT_ALL')}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>;
|
</div>;
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import TranslatedComponent from './TranslatedComponent';
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
import { Ships } from 'coriolis-data/dist';
|
|
||||||
import { nameComparator } from '../utils/SlotFunctions';
|
|
||||||
import LineChart from '../components/LineChart';
|
import LineChart from '../components/LineChart';
|
||||||
import Slider from '../components/Slider';
|
import Slider from '../components/Slider';
|
||||||
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
|
||||||
import Module from '../shipyard/Module';
|
|
||||||
import * as Calc from '../shipyard/Calculations';
|
import * as Calc from '../shipyard/Calculations';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -61,7 +57,7 @@ export default class JumpRange extends TranslatedComponent {
|
|||||||
const fuel = this.state.fuelLevel * ship.fuelCapacity;
|
const fuel = this.state.fuelLevel * ship.fuelCapacity;
|
||||||
|
|
||||||
// Obtain the jump range
|
// Obtain the jump range
|
||||||
return Calc.jumpRange(ship.unladenMass + fuel + cargo, fsd, fuel);
|
return Calc.jumpRange(ship.unladenMass + fuel + cargo, fsd, fuel, ship);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,283 +1,281 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import Measure from 'react-measure';
|
import ContainerDimensions from 'react-container-dimensions';
|
||||||
import * as d3 from 'd3';
|
import * as d3 from 'd3';
|
||||||
import TranslatedComponent from './TranslatedComponent';
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
|
|
||||||
const MARGIN = { top: 15, right: 20, bottom: 35, left: 60 };
|
const MARGIN = { top: 15, right: 20, bottom: 35, left: 60 };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Line Chart
|
* Line Chart
|
||||||
*/
|
*/
|
||||||
export default class LineChart extends TranslatedComponent {
|
export default class LineChart extends TranslatedComponent {
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
code: '',
|
code: '',
|
||||||
xMin: 0,
|
xMin: 0,
|
||||||
yMin: 0,
|
yMin: 0,
|
||||||
points: 20,
|
points: 20,
|
||||||
colors: ['#ff8c0d'],
|
colors: ['#ff8c0d'],
|
||||||
aspect: 0.5
|
aspect: 0.5
|
||||||
};
|
};
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
func: PropTypes.func.isRequired,
|
func: PropTypes.func.isRequired,
|
||||||
xLabel: PropTypes.string.isRequired,
|
xLabel: PropTypes.string.isRequired,
|
||||||
xMin: PropTypes.number,
|
xMin: PropTypes.number,
|
||||||
xMax: PropTypes.number.isRequired,
|
xMax: PropTypes.number.isRequired,
|
||||||
xUnit: PropTypes.string.isRequired,
|
xUnit: PropTypes.string.isRequired,
|
||||||
xMark: PropTypes.number,
|
xMark: PropTypes.number,
|
||||||
yLabel: PropTypes.string.isRequired,
|
yLabel: PropTypes.string.isRequired,
|
||||||
yMin: PropTypes.number,
|
yMin: PropTypes.number,
|
||||||
yMax: PropTypes.number.isRequired,
|
yMax: PropTypes.number.isRequired,
|
||||||
yUnit: PropTypes.string,
|
yUnit: PropTypes.string,
|
||||||
series: PropTypes.array,
|
series: PropTypes.array,
|
||||||
colors: PropTypes.array,
|
colors: PropTypes.array,
|
||||||
points: PropTypes.number,
|
points: PropTypes.number,
|
||||||
aspect: PropTypes.number,
|
aspect: PropTypes.number,
|
||||||
code: PropTypes.string,
|
code: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
* @param {Object} props React Component properties
|
* @param {Object} props React Component properties
|
||||||
* @param {Object} context React Component context
|
* @param {Object} context React Component context
|
||||||
*/
|
*/
|
||||||
constructor(props, context) {
|
constructor(props, context) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this._updateDimensions = this._updateDimensions.bind(this);
|
this._updateDimensions = this._updateDimensions.bind(this);
|
||||||
this._updateSeries = this._updateSeries.bind(this);
|
this._updateSeries = this._updateSeries.bind(this);
|
||||||
this._tooltip = this._tooltip.bind(this);
|
this._tooltip = this._tooltip.bind(this);
|
||||||
this._showTip = this._showTip.bind(this);
|
this._showTip = this._showTip.bind(this);
|
||||||
this._hideTip = this._hideTip.bind(this);
|
this._hideTip = this._hideTip.bind(this);
|
||||||
this._moveTip = this._moveTip.bind(this);
|
this._moveTip = this._moveTip.bind(this);
|
||||||
|
|
||||||
const series = props.series;
|
const series = props.series;
|
||||||
|
|
||||||
let xScale = d3.scaleLinear();
|
let xScale = d3.scaleLinear();
|
||||||
let yScale = d3.scaleLinear();
|
let yScale = d3.scaleLinear();
|
||||||
let xAxisScale = d3.scaleLinear();
|
let xAxisScale = d3.scaleLinear();
|
||||||
|
|
||||||
this.xAxis = d3.axisBottom(xAxisScale).tickSizeOuter(0);
|
this.xAxis = d3.axisBottom(xAxisScale).tickSizeOuter(0);
|
||||||
this.yAxis = d3.axisLeft(yScale).ticks(6).tickSizeOuter(0);
|
this.yAxis = d3.axisLeft(yScale).ticks(6).tickSizeOuter(0);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
xScale,
|
xScale,
|
||||||
xAxisScale,
|
xAxisScale,
|
||||||
yScale,
|
yScale,
|
||||||
tipHeight: 2 + (1.2 * (series ? series.length : 0.8)),
|
tipHeight: 2 + (1.2 * (series ? series.length : 0.8)),
|
||||||
dimensions: {
|
};
|
||||||
width: 100,
|
}
|
||||||
height: 100
|
|
||||||
}
|
/**
|
||||||
};
|
* Update tooltip content
|
||||||
}
|
* @param {number} xPos x coordinate
|
||||||
|
* @param {number} width current container width
|
||||||
/**
|
*/
|
||||||
* Update tooltip content
|
_tooltip(xPos, width) {
|
||||||
* @param {number} xPos x coordinate
|
let { xLabel, yLabel, xUnit, yUnit, func, series } = this.props;
|
||||||
*/
|
let { xScale, yScale } = this.state;
|
||||||
_tooltip(xPos) {
|
let { formats, translate } = this.context.language;
|
||||||
let { xLabel, yLabel, xUnit, yUnit, func, series } = this.props;
|
let x0 = xScale.invert(xPos),
|
||||||
let { xScale, yScale } = this.state;
|
y0 = func(x0),
|
||||||
let { width } = this.state.dimensions;
|
tips = this.tipContainer,
|
||||||
let { formats, translate } = this.context.language;
|
yTotal = 0,
|
||||||
let x0 = xScale.invert(xPos),
|
flip = (xPos / width > 0.50),
|
||||||
y0 = func(x0),
|
tipWidth = 0,
|
||||||
tips = this.tipContainer,
|
tipHeightPx = tips.selectAll('rect').node().getBoundingClientRect().height;
|
||||||
yTotal = 0,
|
|
||||||
flip = (xPos / width > 0.50),
|
|
||||||
tipWidth = 0,
|
xPos = xScale(x0); // Clamp xPos
|
||||||
tipHeightPx = tips.selectAll('rect').node().getBoundingClientRect().height;
|
|
||||||
|
tips.selectAll('text.text-tip.y').text(function(d, i) {
|
||||||
|
let yVal = series ? y0[series[i]] : y0;
|
||||||
xPos = xScale(x0); // Clamp xPos
|
yTotal += yVal;
|
||||||
|
return (series ? translate(series[i]) : '') + ' ' + formats.f2(yVal);
|
||||||
tips.selectAll('text.text-tip.y').text(function(d, i) {
|
}).append('tspan').attr('class', 'metric').text(yUnit ? ' ' + yUnit : '');
|
||||||
let yVal = series ? y0[series[i]] : y0;
|
|
||||||
yTotal += yVal;
|
tips.selectAll('text').each(function() {
|
||||||
return (series ? translate(series[i]) : '') + ' ' + formats.f2(yVal);
|
if (this.getBBox().width > tipWidth) {
|
||||||
}).append('tspan').attr('class', 'metric').text(yUnit ? ' ' + yUnit : '');
|
tipWidth = Math.ceil(this.getBBox().width);
|
||||||
|
}
|
||||||
tips.selectAll('text').each(function() {
|
});
|
||||||
if (this.getBBox().width > tipWidth) {
|
|
||||||
tipWidth = Math.ceil(this.getBBox().width);
|
let tipY = Math.floor(yScale(yTotal / (series ? series.length : 1)) - (tipHeightPx / 2));
|
||||||
}
|
|
||||||
});
|
tipWidth += 8;
|
||||||
|
tips.attr('transform', 'translate(' + xPos + ',' + tipY + ')');
|
||||||
let tipY = Math.floor(yScale(yTotal / (series ? series.length : 1)) - (tipHeightPx / 2));
|
tips.selectAll('text.text-tip').attr('x', flip ? -12 : 12).style('text-anchor', flip ? 'end' : 'start');
|
||||||
|
tips.selectAll('text.text-tip.x').text(formats.f2(x0)).append('tspan').attr('class', 'metric').text(' ' + xUnit);
|
||||||
tipWidth += 8;
|
tips.selectAll('rect').attr('width', tipWidth + 4).attr('x', flip ? -tipWidth - 12 : 8).attr('y', 0).style('text-anchor', flip ? 'end' : 'start');
|
||||||
tips.attr('transform', 'translate(' + xPos + ',' + tipY + ')');
|
this.markersContainer.selectAll('circle').attr('cx', xPos).attr('cy', (d, i) => yScale(series ? y0[series[i]] : y0));
|
||||||
tips.selectAll('text.text-tip').attr('x', flip ? -12 : 12).style('text-anchor', flip ? 'end' : 'start');
|
}
|
||||||
tips.selectAll('text.text-tip.x').text(formats.f2(x0)).append('tspan').attr('class', 'metric').text(' ' + xUnit);
|
|
||||||
tips.selectAll('rect').attr('width', tipWidth + 4).attr('x', flip ? -tipWidth - 12 : 8).attr('y', 0).style('text-anchor', flip ? 'end' : 'start');
|
/**
|
||||||
this.markersContainer.selectAll('circle').attr('cx', xPos).attr('cy', (d, i) => yScale(series ? y0[series[i]] : y0));
|
* Update dimensions based on properties and scale
|
||||||
}
|
* @param {Object} props React Component properties
|
||||||
|
* @param {number} scale size ratio / scale
|
||||||
/**
|
* @param {number} width current width of the container
|
||||||
* Update dimensions based on properties and scale
|
* @returns {Object} calculated dimensions
|
||||||
* @param {Object} props React Component properties
|
*/
|
||||||
* @param {number} scale size ratio / scale
|
_updateDimensions(props, scale, width) {
|
||||||
* @returns {Object} calculated dimensions
|
const { xMax, xMin, yMin, yMax } = props;
|
||||||
*/
|
const innerWidth = width - MARGIN.left - MARGIN.right;
|
||||||
_updateDimensions(props, scale) {
|
const outerHeight = Math.round(width * props.aspect);
|
||||||
const { xMax, xMin, yMin, yMax } = props;
|
const innerHeight = outerHeight - MARGIN.top - MARGIN.bottom;
|
||||||
const { width, height } = this.state.dimensions;
|
|
||||||
const innerWidth = width - MARGIN.left - MARGIN.right;
|
this.state.xScale.range([0, innerWidth]).domain([xMin, xMax || 1]).clamp(true);
|
||||||
const outerHeight = Math.round(width * props.aspect);
|
this.state.xAxisScale.range([0, innerWidth]).domain([xMin, xMax]).clamp(true);
|
||||||
const innerHeight = outerHeight - MARGIN.top - MARGIN.bottom;
|
this.state.yScale.range([innerHeight, 0]).domain([yMin, yMax + (yMax - yMin) * 0.1]); // 10% higher than maximum value for tooltip visibility
|
||||||
|
return { innerWidth, outerHeight, innerHeight };
|
||||||
this.state.xScale.range([0, innerWidth]).domain([xMin, xMax || 1]).clamp(true);
|
}
|
||||||
this.state.xAxisScale.range([0, innerWidth]).domain([xMin, xMax]).clamp(true);
|
|
||||||
this.state.yScale.range([innerHeight, 0]).domain([yMin, yMax + (yMax - yMin) * 0.1]); // 10% higher than maximum value for tooltip visibility
|
/**
|
||||||
return { innerWidth, outerHeight, innerHeight };
|
* Show tooltip
|
||||||
}
|
* @param {SyntheticEvent} e Event
|
||||||
|
*/
|
||||||
/**
|
_showTip(e) {
|
||||||
* Show tooltip
|
e.preventDefault();
|
||||||
* @param {SyntheticEvent} e Event
|
this.tipContainer.style('display', null);
|
||||||
*/
|
this.markersContainer.style('display', null);
|
||||||
_showTip(e) {
|
this._moveTip(e);
|
||||||
e.preventDefault();
|
}
|
||||||
this.tipContainer.style('display', null);
|
|
||||||
this.markersContainer.style('display', null);
|
/**
|
||||||
this._moveTip(e);
|
* Move and update tooltip
|
||||||
}
|
* @param {SyntheticEvent} e Event
|
||||||
|
* @param {number} width current container width
|
||||||
/**
|
*/
|
||||||
* Move and update tooltip
|
_moveTip(e, width) {
|
||||||
* @param {SyntheticEvent} e Event
|
let clientX = e.touches ? e.touches[0].clientX : e.clientX;
|
||||||
*/
|
this._tooltip(Math.round(clientX - e.currentTarget.getBoundingClientRect().left), width);
|
||||||
_moveTip(e) {
|
}
|
||||||
let clientX = e.touches ? e.touches[0].clientX : e.clientX;
|
|
||||||
this._tooltip(Math.round(clientX - e.currentTarget.getBoundingClientRect().left));
|
/**
|
||||||
}
|
* Hide tooltip
|
||||||
|
* @param {SyntheticEvent} e Event
|
||||||
/**
|
*/
|
||||||
* Hide tooltip
|
_hideTip(e) {
|
||||||
* @param {SyntheticEvent} e Event
|
e.preventDefault();
|
||||||
*/
|
this.tipContainer.style('display', 'none');
|
||||||
_hideTip(e) {
|
this.markersContainer.style('display', 'none');
|
||||||
e.preventDefault();
|
}
|
||||||
this.tipContainer.style('display', 'none');
|
|
||||||
this.markersContainer.style('display', 'none');
|
/**
|
||||||
}
|
* Update series generated from props
|
||||||
|
* @param {Object} props React Component properties
|
||||||
/**
|
* @param {Object} state React Component state
|
||||||
* Update series generated from props
|
*/
|
||||||
* @param {Object} props React Component properties
|
_updateSeries(props, state) {
|
||||||
* @param {Object} state React Component state
|
let { func, xMin, xMax, series, points } = props;
|
||||||
*/
|
let delta = (xMax - xMin) / points;
|
||||||
_updateSeries(props, state) {
|
let seriesData = new Array(points);
|
||||||
let { func, xMin, xMax, series, points } = props;
|
|
||||||
let delta = (xMax - xMin) / points;
|
if (delta) {
|
||||||
let seriesData = new Array(points);
|
seriesData = new Array(points);
|
||||||
|
for (let i = 0, x = xMin; i < points; i++) {
|
||||||
if (delta) {
|
seriesData[i] = [x, func(x)];
|
||||||
seriesData = new Array(points);
|
x += delta;
|
||||||
for (let i = 0, x = xMin; i < points; i++) {
|
}
|
||||||
seriesData[i] = [x, func(x)];
|
seriesData[points - 1] = [xMax, func(xMax)];
|
||||||
x += delta;
|
} else {
|
||||||
}
|
let yVal = func(xMin);
|
||||||
seriesData[points - 1] = [xMax, func(xMax)];
|
seriesData = [[0, yVal], [1, yVal]];
|
||||||
} else {
|
}
|
||||||
let yVal = func(xMin);
|
|
||||||
seriesData = [[0, yVal], [1, yVal]];
|
const markerElems = [];
|
||||||
}
|
const detailElems = [<text key='lbl' className='text-tip x' y='1.25em'/>];
|
||||||
|
const seriesLines = [];
|
||||||
const markerElems = [];
|
for (let i = 0, l = series ? series.length : 1; i < l; i++) {
|
||||||
const detailElems = [<text key='lbl' className='text-tip x' y='1.25em'/>];
|
const yAccessor = series ? function(d) { return state.yScale(d[1][this]); }.bind(series[i]) : (d) => state.yScale(d[1]);
|
||||||
const seriesLines = [];
|
seriesLines.push(d3.line().x((d, i) => this.state.xScale(d[0])).y(yAccessor));
|
||||||
for (let i = 0, l = series ? series.length : 1; i < l; i++) {
|
detailElems.push(<text key={i} className='text-tip y' strokeWidth={0} fill={props.colors[i]} y={1.25 * (i + 2) + 'em'}/>);
|
||||||
const yAccessor = series ? function(d) { return state.yScale(d[1][this]); }.bind(series[i]) : (d) => state.yScale(d[1]);
|
markerElems.push(<circle key={i} className='marker' r='4' />);
|
||||||
seriesLines.push(d3.line().x((d, i) => this.state.xScale(d[0])).y(yAccessor));
|
}
|
||||||
detailElems.push(<text key={i} className='text-tip y' strokeWidth={0} fill={props.colors[i]} y={1.25 * (i + 2) + 'em'}/>);
|
|
||||||
markerElems.push(<circle key={i} className='marker' r='4' />);
|
const tipHeight = 2 + (1.2 * (seriesLines ? seriesLines.length : 0.8));
|
||||||
}
|
|
||||||
|
this.setState({ markerElems, detailElems, seriesLines, seriesData, tipHeight });
|
||||||
const tipHeight = 2 + (1.2 * (seriesLines ? seriesLines.length : 0.8));
|
}
|
||||||
|
|
||||||
this.setState({ markerElems, detailElems, seriesLines, seriesData, tipHeight });
|
/**
|
||||||
}
|
* Update dimensions and series data based on props and context.
|
||||||
|
*/
|
||||||
/**
|
componentWillMount() {
|
||||||
* Update dimensions and series data based on props and context.
|
this._updateSeries(this.props, this.state);
|
||||||
*/
|
}
|
||||||
componentWillMount() {
|
|
||||||
this._updateSeries(this.props, this.state);
|
/**
|
||||||
}
|
* Update state based on property and context changes
|
||||||
|
* @param {Object} nextProps Incoming/Next properties
|
||||||
/**
|
* @param {Object} nextContext Incoming/Next conext
|
||||||
* Update state based on property and context changes
|
*/
|
||||||
* @param {Object} nextProps Incoming/Next properties
|
componentWillReceiveProps(nextProps, nextContext) {
|
||||||
* @param {Object} nextContext Incoming/Next conext
|
const props = this.props;
|
||||||
*/
|
|
||||||
componentWillReceiveProps(nextProps, nextContext) {
|
if (props.code != nextProps.code) {
|
||||||
const props = this.props;
|
this._updateSeries(nextProps, this.state);
|
||||||
|
}
|
||||||
if (props.code != nextProps.code) {
|
}
|
||||||
this._updateSeries(nextProps, this.state);
|
|
||||||
}
|
/**
|
||||||
}
|
* Render the chart
|
||||||
|
* @return {React.Component} Chart SVG
|
||||||
/**
|
*/
|
||||||
* Render the chart
|
render() {
|
||||||
* @return {React.Component} Chart SVG
|
return (
|
||||||
*/
|
<ContainerDimensions>
|
||||||
render() {
|
{ ({ width, height }) => {
|
||||||
const { innerWidth, outerHeight, innerHeight } = this._updateDimensions(this.props, this.context.sizeRatio);
|
const { innerWidth, outerHeight, innerHeight } = this._updateDimensions(this.props, this.context.sizeRatio, width, height);
|
||||||
const { width, height } = this.state.dimensions;
|
const { xMin, xMax, xLabel, yLabel, xUnit, yUnit, xMark, colors } = this.props;
|
||||||
const { xMin, xMax, xLabel, yLabel, xUnit, yUnit, xMark, colors } = this.props;
|
const { tipHeight, detailElems, markerElems, seriesData, seriesLines } = this.state;
|
||||||
const { tipHeight, detailElems, markerElems, seriesData, seriesLines } = this.state;
|
const lines = seriesLines.map((line, i) => <path key={i} className='line' fill='none' stroke={colors[i]} strokeWidth='1' d={line(seriesData)} />).reverse();
|
||||||
const line = this.line;
|
|
||||||
const lines = seriesLines.map((line, i) => <path key={i} className='line' fill='none' stroke={colors[i]} strokeWidth='1' d={line(seriesData)} />).reverse();
|
const markX = xMark ? innerWidth * (xMark - xMin) / (xMax - xMin) : 0;
|
||||||
|
const xmark = xMark ? <path key={'mark'} className='line' fill='none' strokeDasharray='5,5' stroke={'#ff8c0d'} strokeWidth='1' d={'M ' + markX + ' ' + innerHeight + ' L ' + markX + ' 0'} /> : '';
|
||||||
const markX = xMark ? innerWidth * (xMark - xMin) / (xMax - xMin) : 0;
|
return (
|
||||||
const xmark = xMark ? <path key={'mark'} className='line' fill='none' strokeDasharray='5,5' stroke={'#ff8c0d'} strokeWidth='1' d={'M ' + markX + ' ' + innerHeight + ' L ' + markX + ' 0'} /> : '';
|
<div width={width} height={height}>
|
||||||
|
<svg style={{ width: '100%', height: outerHeight }}>
|
||||||
return (
|
<g transform={`translate(${MARGIN.left},${MARGIN.top})`}>
|
||||||
<Measure width='100%' whitelist={['width', 'top']} onMeasure={ (dimensions) => { this.setState({ dimensions }); }}>
|
<g>{xmark}</g>
|
||||||
<div width={width} height={height}>
|
<g>{lines}</g>
|
||||||
<svg style={{ width: '100%', height: outerHeight }}>
|
<g className='x axis' ref={(elem) => d3.select(elem).call(this.xAxis)} transform={`translate(0,${innerHeight})`}>
|
||||||
<g transform={`translate(${MARGIN.left},${MARGIN.top})`}>
|
<text className='cap' y='30' dy='.1em' x={innerWidth / 2} style={{ textAnchor: 'middle' }}>
|
||||||
<g>{xmark}</g>
|
<tspan>{xLabel}</tspan>
|
||||||
<g>{lines}</g>
|
<tspan className='metric'> ({xUnit})</tspan>
|
||||||
<g className='x axis' ref={(elem) => d3.select(elem).call(this.xAxis)} transform={`translate(0,${innerHeight})`}>
|
</text>
|
||||||
<text className='cap' y='30' dy='.1em' x={innerWidth / 2} style={{ textAnchor: 'middle' }}>
|
</g>
|
||||||
<tspan>{xLabel}</tspan>
|
<g className='y axis' ref={(elem) => d3.select(elem).call(this.yAxis)}>
|
||||||
<tspan className='metric'> ({xUnit})</tspan>
|
<text className='cap' transform='rotate(-90)' y='-50' dy='.1em' x={innerHeight / -2} style={{ textAnchor: 'middle' }}>
|
||||||
</text>
|
<tspan>{yLabel}</tspan>
|
||||||
</g>
|
{ yUnit && <tspan className='metric'> ({yUnit})</tspan> }
|
||||||
<g className='y axis' ref={(elem) => d3.select(elem).call(this.yAxis)}>
|
</text>
|
||||||
<text className='cap' transform='rotate(-90)' y='-50' dy='.1em' x={innerHeight / -2} style={{ textAnchor: 'middle' }}>
|
</g>
|
||||||
<tspan>{yLabel}</tspan>
|
<g ref={(g) => this.tipContainer = d3.select(g)} style={{ display: 'none' }}>
|
||||||
{ yUnit && <tspan className='metric'> ({yUnit})</tspan> }
|
<rect className='tooltip' height={tipHeight + 'em'}></rect>
|
||||||
</text>
|
{detailElems}
|
||||||
</g>
|
</g>
|
||||||
<g ref={(g) => this.tipContainer = d3.select(g)} style={{ display: 'none' }}>
|
<g ref={(g) => this.markersContainer = d3.select(g)} style={{ display: 'none' }}>
|
||||||
<rect className='tooltip' height={tipHeight + 'em'}></rect>
|
{markerElems}
|
||||||
{detailElems}
|
</g>
|
||||||
</g>
|
<rect
|
||||||
<g ref={(g) => this.markersContainer = d3.select(g)} style={{ display: 'none' }}>
|
fillOpacity='0'
|
||||||
{markerElems}
|
height={innerHeight}
|
||||||
</g>
|
width={innerWidth + 1}
|
||||||
<rect
|
onMouseEnter={this._showTip}
|
||||||
fillOpacity='0'
|
onTouchStart={this._showTip}
|
||||||
height={innerHeight}
|
onMouseLeave={this._hideTip}
|
||||||
width={innerWidth + 1}
|
onTouchEnd={this._hideTip}
|
||||||
onMouseEnter={this._showTip}
|
onMouseMove={e => this._moveTip(e, width)}
|
||||||
onTouchStart={this._showTip}
|
onTouchMove={e => this._moveTip(e, width)}
|
||||||
onMouseLeave={this._hideTip}
|
/>
|
||||||
onTouchEnd={this._hideTip}
|
</g>
|
||||||
onMouseMove={this._moveTip}
|
</svg>
|
||||||
onTouchMove={this._moveTip}
|
</div>
|
||||||
/>
|
);
|
||||||
</g>
|
}}
|
||||||
</svg>
|
</ContainerDimensions>
|
||||||
</div>
|
);
|
||||||
</Measure>
|
}
|
||||||
);
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
93
src/app/components/ModalBatchOrbis.jsx
Normal file
93
src/app/components/ModalBatchOrbis.jsx
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import request from 'superagent';
|
||||||
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
|
import { orbisUpload } from '../utils/ShortenUrl';
|
||||||
|
import Persist from '../stores/Persist';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permalink modal
|
||||||
|
*/
|
||||||
|
export default class ModalBatchOrbis extends TranslatedComponent {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
ships: PropTypes.any.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param {Object} props React Component properties
|
||||||
|
*/
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
orbisCreds: Persist.getOrbisCreds(),
|
||||||
|
resp: ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send ship to Orbis.zone
|
||||||
|
* @param {SyntheticEvent} e React Event
|
||||||
|
* @return {Promise} Promise sending post request to orbis
|
||||||
|
*/
|
||||||
|
sendToOrbis(e) {
|
||||||
|
let agent;
|
||||||
|
try {
|
||||||
|
agent = request.agent(); // apparently this crashes somehow
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
if (!agent) {
|
||||||
|
agent = request;
|
||||||
|
}
|
||||||
|
const API_ORBIS = 'https://orbis.zone/api/builds/add/batch';
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
agent
|
||||||
|
.post(API_ORBIS)
|
||||||
|
.withCredentials()
|
||||||
|
.redirects(0)
|
||||||
|
.set('Content-Type', 'application/json')
|
||||||
|
.send(this.props.ships)
|
||||||
|
.end((err, response) => {
|
||||||
|
console.log(response);
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
this.setState({ resp: response.text });
|
||||||
|
reject('Bad Request');
|
||||||
|
} else {
|
||||||
|
this.setState({ resp: 'All builds uploaded. Check https://orbis.zone' });
|
||||||
|
resolve('All builds uploaded. Check https://orbis.zone');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
reject(e.message ? e.message : e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the modal
|
||||||
|
* @return {React.Component} Modal Content
|
||||||
|
*/
|
||||||
|
render() {
|
||||||
|
let translate = this.context.language.translate;
|
||||||
|
this.sendToOrbis = this.sendToOrbis.bind(this);
|
||||||
|
|
||||||
|
return <div className='modal' onClick={ (e) => e.stopPropagation() }>
|
||||||
|
<h2>{translate('permalink')}</h2>
|
||||||
|
<br/>
|
||||||
|
<a className='button' href="https://orbis.zone/api/auth">Log in / signup to Orbis</a>
|
||||||
|
<br/><br/>
|
||||||
|
<h3 >{translate('success')}</h3>
|
||||||
|
<input value={this.state.resp} readOnly size={25} onFocus={ (e) => e.target.select() }/>
|
||||||
|
<br/><br/>
|
||||||
|
<p>Orbis.zone is currently in a trial period, and may be wiped at any time as development progresses. Some elements are also still placeholders.</p>
|
||||||
|
<button className={'l cb dismiss cap'} disabled={!!this.state.failed} onClick={this.sendToOrbis}>{translate('PHASE_UPLOAD_ORBIS')}</button>
|
||||||
|
<button className={'r dismiss cap'} onClick={this.context.hideModal}>{translate('close')}</button>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,7 +6,6 @@ import Persist from '../stores/Persist';
|
|||||||
* Delete All saved data modal
|
* Delete All saved data modal
|
||||||
*/
|
*/
|
||||||
export default class ModalDeleteAll extends TranslatedComponent {
|
export default class ModalDeleteAll extends TranslatedComponent {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete everything and hide the modal
|
* Delete everything and hide the modal
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import { Download } from './SvgIcons';
|
|||||||
import { outfitURL } from '../utils/UrlGenerators';
|
import { outfitURL } from '../utils/UrlGenerators';
|
||||||
import * as CompanionApiUtils from '../utils/CompanionApiUtils';
|
import * as CompanionApiUtils from '../utils/CompanionApiUtils';
|
||||||
|
|
||||||
|
const zlib = require('pako');
|
||||||
|
|
||||||
const textBuildRegex = new RegExp('^\\[([\\w \\-]+)\\]\n');
|
const textBuildRegex = new RegExp('^\\[([\\w \\-]+)\\]\n');
|
||||||
const lineRegex = new RegExp('^([\\dA-Z]{1,2}): (\\d)([A-I])[/]?([FGT])?([SD])? ([\\w\\- ]+)');
|
const lineRegex = new RegExp('^([\\dA-Z]{1,2}): (\\d)([A-I])[/]?([FGT])?([SD])? ([\\w\\- ]+)');
|
||||||
const mountMap = { 'H': 4, 'L': 3, 'M': 2, 'S': 1, 'U': 0 };
|
const mountMap = { 'H': 4, 'L': 3, 'M': 2, 'S': 1, 'U': 0 };
|
||||||
@@ -99,6 +101,7 @@ export default class ModalImport extends TranslatedComponent {
|
|||||||
this.state = {
|
this.state = {
|
||||||
builds: props.builds,
|
builds: props.builds,
|
||||||
canEdit: !props.builds,
|
canEdit: !props.builds,
|
||||||
|
loadoutEvent: null,
|
||||||
comparisons: null,
|
comparisons: null,
|
||||||
shipDiscount: null,
|
shipDiscount: null,
|
||||||
moduleDiscount: null,
|
moduleDiscount: null,
|
||||||
@@ -111,12 +114,28 @@ export default class ModalImport extends TranslatedComponent {
|
|||||||
this._process = this._process.bind(this);
|
this._process = this._process.bind(this);
|
||||||
this._import = this._import.bind(this);
|
this._import = this._import.bind(this);
|
||||||
this._importBackup = this._importBackup.bind(this);
|
this._importBackup = this._importBackup.bind(this);
|
||||||
|
this._importLoadout = this._importLoadout.bind(this);
|
||||||
this._importDetailedArray = this._importDetailedArray.bind(this);
|
this._importDetailedArray = this._importDetailedArray.bind(this);
|
||||||
this._importTextBuild = this._importTextBuild.bind(this);
|
this._importTextBuild = this._importTextBuild.bind(this);
|
||||||
this._importCompanionApiBuild = this._importCompanionApiBuild.bind(this);
|
this._importCompanionApiBuild = this._importCompanionApiBuild.bind(this);
|
||||||
this._validateImport = this._validateImport.bind(this);
|
this._validateImport = this._validateImport.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import a Loadout event from Elite: Dangerous journal files
|
||||||
|
* @param {Object} data Loadout event
|
||||||
|
* @throws {string} If import fails
|
||||||
|
*/
|
||||||
|
_importLoadout(data) {
|
||||||
|
if (data && data.Ship && data.Modules) {
|
||||||
|
const deflated = zlib.deflate(JSON.stringify(data), { to: 'string' });
|
||||||
|
let compressed = btoa(deflated);
|
||||||
|
this.setState({loadoutEvent: compressed});
|
||||||
|
} else {
|
||||||
|
throw 'Loadout event must contain Ship and Modules';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Import a Coriolis backup
|
* Import a Coriolis backup
|
||||||
* @param {Object} importData Backup Data
|
* @param {Object} importData Backup Data
|
||||||
@@ -126,7 +145,11 @@ export default class ModalImport extends TranslatedComponent {
|
|||||||
if (importData.builds && typeof importData.builds == 'object') {
|
if (importData.builds && typeof importData.builds == 'object') {
|
||||||
for (let shipId in importData.builds) {
|
for (let shipId in importData.builds) {
|
||||||
for (let buildName in importData.builds[shipId]) {
|
for (let buildName in importData.builds[shipId]) {
|
||||||
validateBuild(shipId, importData.builds[shipId][buildName], buildName);
|
try {
|
||||||
|
validateBuild(shipId, importData.builds[shipId][buildName], buildName);
|
||||||
|
} catch (err) {
|
||||||
|
delete importData.builds[shipId][buildName];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.setState({ builds: importData.builds });
|
this.setState({ builds: importData.builds });
|
||||||
@@ -155,7 +178,7 @@ export default class ModalImport extends TranslatedComponent {
|
|||||||
}
|
}
|
||||||
// Check for module discount
|
// Check for module discount
|
||||||
if (!isNaN(importData.moduleDiscount)) {
|
if (!isNaN(importData.moduleDiscount)) {
|
||||||
this.setState({ shipDiscount: importData.moduleDiscount * 1 });
|
this.setState({ moduleDiscount: importData.moduleDiscount * 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof importData.insurance == 'string') {
|
if (typeof importData.insurance == 'string') {
|
||||||
@@ -341,12 +364,14 @@ export default class ModalImport extends TranslatedComponent {
|
|||||||
} else if (importData.ship && typeof importData.name !== undefined) { // Using JSON from a single ship build export
|
} else if (importData.ship && typeof importData.name !== undefined) { // Using JSON from a single ship build export
|
||||||
this._importDetailedArray([importData]); // Convert to array with singleobject
|
this._importDetailedArray([importData]); // Convert to array with singleobject
|
||||||
this.setState({ singleBuild: true });
|
this.setState({ singleBuild: true });
|
||||||
|
} else if (importData.Modules != null && importData.Modules[0] != null) {
|
||||||
|
this._importLoadout(importData);
|
||||||
} else { // Using Backup JSON
|
} else { // Using Backup JSON
|
||||||
this._importBackup(importData);
|
this._importBackup(importData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// console.log(e.stack);
|
console.log(e);
|
||||||
this.setState({ errorMsg: (typeof e == 'string') ? e : 'Cannot Parse the data!' });
|
this.setState({ errorMsg: (typeof e == 'string') ? e : 'Cannot Parse the data!' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -360,6 +385,10 @@ export default class ModalImport extends TranslatedComponent {
|
|||||||
_process() {
|
_process() {
|
||||||
let builds = null, comparisons = null;
|
let builds = null, comparisons = null;
|
||||||
|
|
||||||
|
if (this.state.loadoutEvent) {
|
||||||
|
return Router.go(`/import?data=${this.state.loadoutEvent}`);
|
||||||
|
}
|
||||||
|
|
||||||
// If only importing a single build go straight to the outfitting page
|
// If only importing a single build go straight to the outfitting page
|
||||||
if (this.state.singleBuild) {
|
if (this.state.singleBuild) {
|
||||||
builds = this.state.builds;
|
builds = this.state.builds;
|
||||||
@@ -476,7 +505,7 @@ export default class ModalImport extends TranslatedComponent {
|
|||||||
if (!state.processed) {
|
if (!state.processed) {
|
||||||
importStage = (
|
importStage = (
|
||||||
<div>
|
<div>
|
||||||
<textarea className='cb json' ref={node => this.importField = node} onChange={this._validateImport} defaultValue={this.state.importString} placeholder={translate('PHRASE_IMPORT')} />
|
<textarea spellCheck={false} className='cb json' ref={node => this.importField = node} onChange={this._validateImport} defaultValue={this.state.importString} placeholder={translate('PHRASE_IMPORT')} />
|
||||||
<button id='proceed' className='l cap' onClick={this._process} disabled={!state.importValid} >{translate('proceed')}</button>
|
<button id='proceed' className='l cap' onClick={this._process} disabled={!state.importValid} >{translate('proceed')}</button>
|
||||||
<div className='l warning' style={{ marginLeft:'3em' }}>{state.errorMsg}</div>
|
<div className='l warning' style={{ marginLeft:'3em' }}>{state.errorMsg}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
141
src/app/components/ModalOrbis.jsx
Normal file
141
src/app/components/ModalOrbis.jsx
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
|
import { orbisUpload } from '../utils/ShortenUrl';
|
||||||
|
import Persist from '../stores/Persist';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permalink modal
|
||||||
|
*/
|
||||||
|
export default class ModalOrbis extends TranslatedComponent {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
ship: PropTypes.any.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param {Object} props React Component properties
|
||||||
|
*/
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
orbisCreds: Persist.getOrbisCreds(),
|
||||||
|
orbisUrl: '...',
|
||||||
|
ship: this.props.ship,
|
||||||
|
authenticatedStatus: 'Checking...'
|
||||||
|
};
|
||||||
|
this.orbisCategory = this.orbisCategory.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send ship to Orbis.zone
|
||||||
|
* @param {SyntheticEvent} e React Event
|
||||||
|
*/
|
||||||
|
sendToOrbis(e) {
|
||||||
|
const target = e.target;
|
||||||
|
target.disabled = true;
|
||||||
|
this.setState({ orbisUrl: 'Sending...' }, () => {
|
||||||
|
orbisUpload(this.props.ship, this.state.orbisCreds)
|
||||||
|
.then(orbisUrl => {
|
||||||
|
target.disabled = false;
|
||||||
|
this.setState({ orbisUrl });
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
target.disabled = false;
|
||||||
|
this.setState({ orbisUrl: 'Error - ' + err });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Orbis.zone auth status
|
||||||
|
* @returns {Object} auth status
|
||||||
|
*/
|
||||||
|
getOrbisAuthStatus() {
|
||||||
|
return fetch('https://orbis.zone/api/checkauth', {
|
||||||
|
credentials: 'include',
|
||||||
|
mode: 'cors'
|
||||||
|
})
|
||||||
|
.then(data => data.json())
|
||||||
|
.then(res => {
|
||||||
|
this.setState({ authenticatedStatus: res.status || res.error });
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
this.setState({ authenticatedStatus: err.message });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for changing cmdr name
|
||||||
|
* @param {SyntheticEvent} e React Event
|
||||||
|
*/
|
||||||
|
orbisPasswordHandler(e) {
|
||||||
|
let password = e.target.value;
|
||||||
|
this.setState({ orbisCreds: { email: this.state.orbisCreds.email, password } }, () => {
|
||||||
|
Persist.setOrbisCreds(this.state.orbisCreds);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for changing cmdr name
|
||||||
|
* @param {SyntheticEvent} e React Event
|
||||||
|
*/
|
||||||
|
orbisUsername(e) {
|
||||||
|
let orbisUsername = e.target.value;
|
||||||
|
this.setState({ orbisCreds: { email: orbisUsername, password: this.state.orbisCreds.password } }, () => {
|
||||||
|
Persist.setOrbisCreds(this.state.orbisCreds);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for changing category
|
||||||
|
* @param {SyntheticEvent} e React Event
|
||||||
|
*/
|
||||||
|
orbisCategory(e) {
|
||||||
|
let ship = this.state.ship;
|
||||||
|
let cat = e.target.value;
|
||||||
|
ship.category = cat;
|
||||||
|
this.setState({ship});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the modal
|
||||||
|
* @return {React.Component} Modal Content
|
||||||
|
*/
|
||||||
|
render() {
|
||||||
|
let translate = this.context.language.translate;
|
||||||
|
this.orbisPasswordHandler = this.orbisPasswordHandler.bind(this);
|
||||||
|
this.orbisUsername = this.orbisUsername.bind(this);
|
||||||
|
this.sendToOrbis = this.sendToOrbis.bind(this);
|
||||||
|
this.getOrbisAuthStatus();
|
||||||
|
return <div className='modal' onClick={ (e) => e.stopPropagation() }>
|
||||||
|
<h2>{translate('upload to orbis')}</h2>
|
||||||
|
<br/>
|
||||||
|
<label>Orbis auth status: </label>
|
||||||
|
<input value={this.state.authenticatedStatus} readOnly size={25} onFocus={ (e) => e.target.select() }/>
|
||||||
|
<br/><br/>
|
||||||
|
<a className='button' href="https://orbis.zone/api/auth">Log in / signup to Orbis</a>
|
||||||
|
<br/><br/>
|
||||||
|
<h3>Category</h3>
|
||||||
|
<select onChange={this.orbisCategory}>
|
||||||
|
<option value="">No Category</option>
|
||||||
|
<option>Combat</option>
|
||||||
|
<option>Mining</option>
|
||||||
|
<option>Trading</option>
|
||||||
|
<option>Exploration</option>
|
||||||
|
<option>Passenger Liner</option>
|
||||||
|
<option>PvP</option>
|
||||||
|
</select>
|
||||||
|
<br/><br/>
|
||||||
|
<h3 >{translate('Orbis link')}</h3>
|
||||||
|
<input value={this.state.orbisUrl} readOnly size={25} onFocus={ (e) => e.target.select() }/>
|
||||||
|
<br/><br/>
|
||||||
|
<p>Orbis.zone is currently in a trial period, and may be wiped at any time as development progresses. Some elements are also still placeholders.</p>
|
||||||
|
<button className={'l cb dismiss cap'} disabled={!!this.state.failed} onClick={this.sendToOrbis}>{translate('PHASE_UPLOAD_ORBIS')}</button>
|
||||||
|
<button className={'r dismiss cap'} onClick={this.context.hideModal}>{translate('close')}</button>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -50,6 +50,7 @@ export default class ModalPermalink extends TranslatedComponent {
|
|||||||
<h3 >{translate('shortened')}</h3>
|
<h3 >{translate('shortened')}</h3>
|
||||||
<input value={this.state.shortenedUrl} readOnly size={25} onFocus={ (e) => e.target.select() }/>
|
<input value={this.state.shortenedUrl} readOnly size={25} onFocus={ (e) => e.target.select() }/>
|
||||||
<br/><br/>
|
<br/><br/>
|
||||||
|
<p>s.orbis.zone is the new URL shortener domain, old eddp.co urls are considered end of life and could go down at any moment. Sorry for any inconvenience.</p>
|
||||||
<button className={'r dismiss cap'} onClick={this.context.hideModal}>{translate('close')}</button>
|
<button className={'r dismiss cap'} onClick={this.context.hideModal}>{translate('close')}</button>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|||||||
263
src/app/components/ModalShoppingList.jsx
Normal file
263
src/app/components/ModalShoppingList.jsx
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
|
import request from 'superagent';
|
||||||
|
import Persist from '../stores/Persist';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permalink modal
|
||||||
|
*/
|
||||||
|
export default class ModalShoppingList extends TranslatedComponent {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
ship: PropTypes.object.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param {Object} props React Component properties
|
||||||
|
*/
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
matsList: '',
|
||||||
|
mats: {},
|
||||||
|
failed: false,
|
||||||
|
cmdrName: Persist.getCmdr().selected,
|
||||||
|
cmdrs: Persist.getCmdr().cmdrs,
|
||||||
|
matsPerGrade: Persist.getRolls(),
|
||||||
|
blueprints: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* React component did mount
|
||||||
|
*/
|
||||||
|
componentDidMount() {
|
||||||
|
this.renderMats();
|
||||||
|
if (this.checkBrowserIsCompatible()) {
|
||||||
|
this.getCommanders();
|
||||||
|
this.registerBPs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find all blueprints needed to make a build.
|
||||||
|
*/
|
||||||
|
registerBPs() {
|
||||||
|
const ship = this.props.ship;
|
||||||
|
let blueprints = [];
|
||||||
|
for (const module of ship.costList) {
|
||||||
|
if (module.type === 'SHIP') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (module.m && module.m.blueprint) {
|
||||||
|
if (!module.m.blueprint.grade || !module.m.blueprint.grades) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (module.m.blueprint.special) {
|
||||||
|
console.log(module.m.blueprint.special);
|
||||||
|
blueprints.push({ uuid: module.m.blueprint.special.uuid, number: 1 });
|
||||||
|
}
|
||||||
|
for (const g in module.m.blueprint.grades) {
|
||||||
|
if (!module.m.blueprint.grades.hasOwnProperty(g)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (g > module.m.blueprint.grade) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
blueprints.push({ uuid: module.m.blueprint.grades[g].uuid, number: this.state.matsPerGrade[g] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.setState({ blueprints });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check browser isn't firefox.
|
||||||
|
* @return {boolean} true if compatible, false if not.
|
||||||
|
*/
|
||||||
|
checkBrowserIsCompatible() {
|
||||||
|
// Firefox 1.0+
|
||||||
|
return typeof InstallTrigger === 'undefined';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a list of commanders from EDEngineer.
|
||||||
|
*/
|
||||||
|
getCommanders() {
|
||||||
|
request
|
||||||
|
.get('http://localhost:44405/commanders')
|
||||||
|
.end((err, res) => {
|
||||||
|
if (err) {
|
||||||
|
console.log(err);
|
||||||
|
return this.setState({ failed: true });
|
||||||
|
}
|
||||||
|
const cmdrs = JSON.parse(res.text);
|
||||||
|
if (!this.state.cmdrName) {
|
||||||
|
this.setState({ cmdrName: cmdrs[0] });
|
||||||
|
}
|
||||||
|
this.setState({ cmdrs }, () => {
|
||||||
|
Persist.setCmdr({ selected: this.state.cmdrName, cmdrs });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send all blueprints to ED Engineer
|
||||||
|
* @param {Event} event React event
|
||||||
|
*/
|
||||||
|
sendToEDEng(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
let translate = this.context.language.translate;
|
||||||
|
const target = event.target;
|
||||||
|
target.disabled = this.state.blueprints.length > 0;
|
||||||
|
if (this.state.blueprints.length === 0) {
|
||||||
|
target.innerText = translate('No modded components.');
|
||||||
|
target.disabled = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
target.innerText = translate('Send to EDEngineer');
|
||||||
|
target.disabled = false;
|
||||||
|
}, 3000);
|
||||||
|
} else {
|
||||||
|
target.innerText = translate('Sending...');
|
||||||
|
}
|
||||||
|
let countSent = 0;
|
||||||
|
let countTotal = this.state.blueprints.length;
|
||||||
|
|
||||||
|
for (const i of this.state.blueprints) {
|
||||||
|
request
|
||||||
|
.patch(`http://localhost:44405/${this.state.cmdrName}/shopping-list`)
|
||||||
|
.field('uuid', i.uuid)
|
||||||
|
.field('size', i.number)
|
||||||
|
.end(err => {
|
||||||
|
if (err) {
|
||||||
|
console.log(err);
|
||||||
|
if (err.message !== 'Bad Request') {
|
||||||
|
this.setState({ failed: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
countSent++;
|
||||||
|
if (countSent === countTotal) {
|
||||||
|
target.disabled = false;
|
||||||
|
target.innerText = translate('Send to EDEngineer');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert mats object to string
|
||||||
|
*/
|
||||||
|
renderMats() {
|
||||||
|
const ship = this.props.ship;
|
||||||
|
let mats = {};
|
||||||
|
for (const module of ship.costList) {
|
||||||
|
if (module.type === 'SHIP') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (module.m && module.m.blueprint) {
|
||||||
|
if (!module.m.blueprint.grade || !module.m.blueprint.grades) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (const g in module.m.blueprint.grades) {
|
||||||
|
if (!module.m.blueprint.grades.hasOwnProperty(g)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (g > module.m.blueprint.grade) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (const i in module.m.blueprint.grades[g].components) {
|
||||||
|
if (!module.m.blueprint.grades[g].components.hasOwnProperty(i)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (mats[i]) {
|
||||||
|
mats[i] += module.m.blueprint.grades[g].components[i] * this.state.matsPerGrade[g];
|
||||||
|
} else {
|
||||||
|
mats[i] = module.m.blueprint.grades[g].components[i] * this.state.matsPerGrade[g];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let matsString = '';
|
||||||
|
for (const i in mats) {
|
||||||
|
if (!mats.hasOwnProperty(i)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (mats[i] === 0) {
|
||||||
|
delete mats[i];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
matsString += `${i}: ${mats[i]}\n`;
|
||||||
|
}
|
||||||
|
this.setState({ matsList: matsString, mats });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for changing roll amounts
|
||||||
|
* @param {SyntheticEvent} e React Event
|
||||||
|
*/
|
||||||
|
changeHandler(e) {
|
||||||
|
let grade = e.target.id;
|
||||||
|
let newState = this.state.matsPerGrade;
|
||||||
|
newState[grade] = parseInt(e.target.value);
|
||||||
|
this.setState({ matsPerGrade: newState });
|
||||||
|
Persist.setRolls(newState);
|
||||||
|
this.renderMats();
|
||||||
|
this.registerBPs();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for changing cmdr name
|
||||||
|
* @param {SyntheticEvent} e React Event
|
||||||
|
*/
|
||||||
|
cmdrChangeHandler(e) {
|
||||||
|
let cmdrName = e.target.value;
|
||||||
|
this.setState({ cmdrName }, () => {
|
||||||
|
Persist.setCmdr({ selected: this.state.cmdrName, cmdrs: this.state.cmdrs });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the modal
|
||||||
|
* @return {React.Component} Modal Content
|
||||||
|
*/
|
||||||
|
render() {
|
||||||
|
let translate = this.context.language.translate;
|
||||||
|
this.changeHandler = this.changeHandler.bind(this);
|
||||||
|
const compatible = this.checkBrowserIsCompatible();
|
||||||
|
this.cmdrChangeHandler = this.cmdrChangeHandler.bind(this);
|
||||||
|
this.sendToEDEng = this.sendToEDEng.bind(this);
|
||||||
|
return <div className='modal' onClick={ (e) => e.stopPropagation() }>
|
||||||
|
<h2>{translate('PHRASE_SHOPPING_MATS')}</h2>
|
||||||
|
<label>{translate('Grade 1 rolls ')}</label>
|
||||||
|
<input id={1} type={'number'} min={0} defaultValue={this.state.matsPerGrade[1]} onChange={this.changeHandler} />
|
||||||
|
<br/>
|
||||||
|
<label>{translate('Grade 2 rolls ')}</label>
|
||||||
|
<input id={2} type={'number'} min={0} defaultValue={this.state.matsPerGrade[2]} onChange={this.changeHandler} />
|
||||||
|
<br/>
|
||||||
|
<label>{translate('Grade 3 rolls ')}</label>
|
||||||
|
<input id={3} type={'number'} min={0} value={this.state.matsPerGrade[3]} onChange={this.changeHandler} />
|
||||||
|
<br/>
|
||||||
|
<label>{translate('Grade 4 rolls ')}</label>
|
||||||
|
<input id={4} type={'number'} min={0} value={this.state.matsPerGrade[4]} onChange={this.changeHandler} />
|
||||||
|
<br/>
|
||||||
|
<label>{translate('Grade 5 rolls ')}</label>
|
||||||
|
<input id={5} type={'number'} min={0} value={this.state.matsPerGrade[5]} onChange={this.changeHandler} />
|
||||||
|
<div>
|
||||||
|
<textarea className='cb json' readOnly value={this.state.matsList} />
|
||||||
|
</div>
|
||||||
|
<label hidden={!compatible} className={'l cap'}>{translate('CMDR Name')}</label>
|
||||||
|
<br/>
|
||||||
|
<select hidden={!compatible} className={'cmdr-select l cap'} onChange={this.cmdrChangeHandler} defaultValue={this.state.cmdrName}>
|
||||||
|
{this.state.cmdrs.map(e => <option key={e}>{e}</option>)}
|
||||||
|
</select>
|
||||||
|
<br/>
|
||||||
|
<p hidden={!this.state.failed} id={'failed'} className={'l'}>{translate('PHRASE_FAIL_EDENGINEER')}</p>
|
||||||
|
<p hidden={compatible} id={'browserbad'} className={'l'}>{translate('PHRASE_FIREFOX_EDENGINEER')}</p>
|
||||||
|
<button className={'l cb dismiss cap'} disabled={!!this.state.failed || !compatible} onClick={this.sendToEDEng}>{translate('Send to EDEngineer')}</button>
|
||||||
|
<button className={'r dismiss cap'} onClick={this.context.hideModal}>{translate('close')}</button>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,18 +3,22 @@ import PropTypes from 'prop-types';
|
|||||||
import TranslatedComponent from './TranslatedComponent';
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
import cn from 'classnames';
|
import cn from 'classnames';
|
||||||
import NumberEditor from 'react-number-editor';
|
import NumberEditor from 'react-number-editor';
|
||||||
|
import { isChangeValueBeneficial } from '../utils/BlueprintFunctions';
|
||||||
|
import { Modifications } from 'coriolis-data/dist';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modification
|
* Modification
|
||||||
*/
|
*/
|
||||||
export default class Modification extends TranslatedComponent {
|
export default class Modification extends TranslatedComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
ship: PropTypes.object.isRequired,
|
ship: PropTypes.object.isRequired,
|
||||||
m: PropTypes.object.isRequired,
|
m: PropTypes.object.isRequired,
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
value: PropTypes.number.isRequired,
|
value: PropTypes.number.isRequired,
|
||||||
onChange: PropTypes.func.isRequired
|
onChange: PropTypes.func.isRequired,
|
||||||
|
onKeyDown: PropTypes.func.isRequired,
|
||||||
|
modItems: PropTypes.array.isRequired,
|
||||||
|
handleModChange: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -35,31 +39,37 @@ export default class Modification extends TranslatedComponent {
|
|||||||
* in a value by hand
|
* in a value by hand
|
||||||
*/
|
*/
|
||||||
_updateValue(value) {
|
_updateValue(value) {
|
||||||
const name = this.props.name;
|
|
||||||
|
|
||||||
let scaledValue = Math.round(Number(value) * 100);
|
|
||||||
// Limit to +1000% / -99.99%
|
|
||||||
if (scaledValue > 100000) {
|
|
||||||
scaledValue = 100000;
|
|
||||||
value = 1000;
|
|
||||||
}
|
|
||||||
if (scaledValue < -9999) {
|
|
||||||
scaledValue = -9999;
|
|
||||||
value = -99.99;
|
|
||||||
}
|
|
||||||
|
|
||||||
let m = this.props.m;
|
|
||||||
let ship = this.props.ship;
|
|
||||||
ship.setModification(m, name, scaledValue, true);
|
|
||||||
|
|
||||||
this.setState({ value });
|
this.setState({ value });
|
||||||
|
let reCast = String(Number(value));
|
||||||
|
if (reCast.endsWith(value) || reCast.startsWith(value)) {
|
||||||
|
let { m, name, ship } = this.props;
|
||||||
|
value = Math.max(Math.min(value, 50000), -50000);
|
||||||
|
ship.setModification(m, name, value, true, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggered when a key is pressed down with focus on the number editor.
|
||||||
|
* @param {SyntheticEvent} event Key down event
|
||||||
|
*/
|
||||||
|
_keyDown(event) {
|
||||||
|
if (event.key == 'Enter') {
|
||||||
|
this._updateFinished();
|
||||||
|
}
|
||||||
|
this.props.onKeyDown(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Triggered when an update to slider value is finished i.e. when losing focus
|
* Triggered when an update to slider value is finished i.e. when losing focus
|
||||||
|
*
|
||||||
|
* pnellesen (24/05/2018): added value check below - this should prevent experimental effects from being recalculated
|
||||||
|
* with each onBlur event, even when no change has actually been made to the field.
|
||||||
*/
|
*/
|
||||||
_updateFinished() {
|
_updateFinished() {
|
||||||
this.props.onChange();
|
if (this.props.value != this.state.value) {
|
||||||
|
this.props.handleModChange(true);
|
||||||
|
this.props.onChange();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -67,28 +77,56 @@ export default class Modification extends TranslatedComponent {
|
|||||||
* @return {React.Component} modification
|
* @return {React.Component} modification
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
let translate = this.context.language.translate;
|
let { translate, formats, units } = this.context.language;
|
||||||
let { m, name } = this.props;
|
let { m, name } = this.props;
|
||||||
|
let modValue = m.getChange(name);
|
||||||
|
let isOverwrite = Modifications.modifications[name].method === 'overwrite';
|
||||||
|
|
||||||
if (name === 'damagedist') {
|
if (name === 'damagedist') {
|
||||||
// We don't show damage distribution
|
// We don't show damage distribution
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
let symbol;
|
let inputClassNames = {
|
||||||
if (name === 'jitter') {
|
'cb': true,
|
||||||
symbol = '°';
|
'greyed-out': !this.props.highlight
|
||||||
} else if (name !== 'burst' && name != 'burstrof') {
|
};
|
||||||
symbol = '%';
|
|
||||||
}
|
|
||||||
if (symbol) {
|
|
||||||
symbol = ' (' + symbol + ')';
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div onBlur={this._updateFinished.bind(this)} className={'cb'} key={name}>
|
<div onBlur={this._updateFinished.bind(this)} key={name}
|
||||||
<div className={'cb'}>{translate(name, m.grp)}{symbol}</div>
|
className={cn('cb', 'modification-container')}
|
||||||
<NumberEditor className={'cb'} style={{ width: '90%', textAlign: 'center' }} step={0.01} stepModifier={1} decimals={2} value={this.state.value} onValueChange={this._updateValue.bind(this)} />
|
ref={ modItem => this.props.modItems[name] = modItem }>
|
||||||
|
<span className={'cb'}>{translate(name, m.grp)}</span>
|
||||||
|
<span className={'header-adjuster'}></span>
|
||||||
|
<table style={{ width: '100%' }}>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td className={'input-container'}>
|
||||||
|
<span>
|
||||||
|
{this.props.editable ?
|
||||||
|
<NumberEditor className={cn(inputClassNames)} value={this.state.value}
|
||||||
|
decimals={2} style={{ textAlign: 'right' }} step={0.01}
|
||||||
|
stepModifier={1} onKeyDown={this._keyDown.bind(this)}
|
||||||
|
onValueChange={this._updateValue.bind(this)} /> :
|
||||||
|
<input type="text" value={formats.f2(this.state.value)}
|
||||||
|
disabled className={cn('number-editor', 'greyed-out')}
|
||||||
|
style={{ textAlign: 'right', cursor: 'inherit' }}/>
|
||||||
|
}
|
||||||
|
<span className={'unit-container'}>
|
||||||
|
{units[m.getStoredUnitFor(name)]}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td style={{ textAlign: 'center' }} className={
|
||||||
|
modValue ?
|
||||||
|
isChangeValueBeneficial(name, modValue) ? 'secondary' : 'warning' :
|
||||||
|
''
|
||||||
|
}>
|
||||||
|
{formats.f2(modValue / 100) || 0}{isOverwrite ? '' : '%'}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,22 +2,33 @@ import React from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import TranslatedComponent from './TranslatedComponent';
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
import { isEmpty, stopCtxPropagation } from '../utils/UtilityFunctions';
|
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
||||||
import cn from 'classnames';
|
import cn from 'classnames';
|
||||||
import { Modifications } from 'coriolis-data/dist';
|
import { Modifications } from 'coriolis-data/dist';
|
||||||
import Modification from './Modification';
|
import Modification from './Modification';
|
||||||
import { getBlueprint, blueprintTooltip, setWorst, setBest, setExtreme, setRandom } from '../utils/BlueprintFunctions';
|
import {
|
||||||
|
getBlueprint,
|
||||||
|
blueprintTooltip,
|
||||||
|
setPercent,
|
||||||
|
getPercent,
|
||||||
|
setRandom,
|
||||||
|
specialToolTip
|
||||||
|
} from '../utils/BlueprintFunctions';
|
||||||
|
|
||||||
|
const MODIFICATIONS_COMPARATOR = (mod1, mod2) => {
|
||||||
|
return mod1.props.name.localeCompare(mod2.props.name);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modifications menu
|
* Modifications menu
|
||||||
*/
|
*/
|
||||||
export default class ModificationsMenu extends TranslatedComponent {
|
export default class ModificationsMenu extends TranslatedComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
ship: PropTypes.object.isRequired,
|
ship: PropTypes.object.isRequired,
|
||||||
m: PropTypes.object.isRequired,
|
m: PropTypes.object.isRequired,
|
||||||
marker: PropTypes.string.isRequired,
|
marker: PropTypes.string.isRequired,
|
||||||
onChange: PropTypes.func.isRequired
|
onChange: PropTypes.func.isRequired,
|
||||||
|
modButton:PropTypes.object
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -30,14 +41,24 @@ export default class ModificationsMenu extends TranslatedComponent {
|
|||||||
|
|
||||||
this._toggleBlueprintsMenu = this._toggleBlueprintsMenu.bind(this);
|
this._toggleBlueprintsMenu = this._toggleBlueprintsMenu.bind(this);
|
||||||
this._toggleSpecialsMenu = this._toggleSpecialsMenu.bind(this);
|
this._toggleSpecialsMenu = this._toggleSpecialsMenu.bind(this);
|
||||||
this._rollWorst = this._rollWorst.bind(this);
|
this._rollFifty = this._rollFifty.bind(this);
|
||||||
this._rollRandom = this._rollRandom.bind(this);
|
this._rollRandom = this._rollRandom.bind(this);
|
||||||
this._rollBest = this._rollBest.bind(this);
|
this._rollBest = this._rollBest.bind(this);
|
||||||
this._rollExtreme = this._rollExtreme.bind(this);
|
this._rollWorst = this._rollWorst.bind(this);
|
||||||
this._reset = this._reset.bind(this);
|
this._reset = this._reset.bind(this);
|
||||||
|
this._keyDown = this._keyDown.bind(this);
|
||||||
|
this.modItems = [];// Array to hold various element refs (<li>, <div>, <ul>, etc.)
|
||||||
|
this.firstModId = null;
|
||||||
|
this.firstBPLabel = null;// First item in mod menu
|
||||||
|
this.lastModId = null;
|
||||||
|
this.selectedModId = null;
|
||||||
|
this.selectedSpecialId = null;
|
||||||
|
this.lastNeId = null;// Last number editor id. Used to set focus to last number editor when shift-tab pressed on first element in mod menu.
|
||||||
|
this.modValDidChange = false; // used to determine if component update was caused by change in modification value.
|
||||||
|
this._handleModChange = this._handleModChange.bind(this);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
blueprintMenuOpened: false,
|
blueprintMenuOpened: !(props.m.blueprint && props.m.blueprint.name),
|
||||||
specialMenuOpened: false
|
specialMenuOpened: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -52,7 +73,6 @@ export default class ModificationsMenu extends TranslatedComponent {
|
|||||||
const { m } = props;
|
const { m } = props;
|
||||||
const { language, tooltip, termtip } = context;
|
const { language, tooltip, termtip } = context;
|
||||||
const translate = language.translate;
|
const translate = language.translate;
|
||||||
|
|
||||||
const blueprints = [];
|
const blueprints = [];
|
||||||
for (const blueprintName in Modifications.modules[m.grp].blueprints) {
|
for (const blueprintName in Modifications.modules[m.grp].blueprints) {
|
||||||
const blueprint = getBlueprint(blueprintName, m);
|
const blueprint = getBlueprint(blueprintName, m);
|
||||||
@@ -61,14 +81,18 @@ export default class ModificationsMenu extends TranslatedComponent {
|
|||||||
// Grade is a string in the JSON so make it a number
|
// Grade is a string in the JSON so make it a number
|
||||||
grade = Number(grade);
|
grade = Number(grade);
|
||||||
const classes = cn('c', {
|
const classes = cn('c', {
|
||||||
active: m.blueprint && blueprint.id === m.blueprint.id && grade === m.blueprint.grade
|
active: m.blueprint && blueprint.id === m.blueprint.id && grade === m.blueprint.grade
|
||||||
});
|
});
|
||||||
const close = this._blueprintSelected.bind(this, blueprintName, grade);
|
const close = this._blueprintSelected.bind(this, blueprintName, grade);
|
||||||
const key = blueprintName + ':' + grade;
|
const key = blueprintName + ':' + grade;
|
||||||
const tooltipContent = blueprintTooltip(translate, blueprint.grades[grade], Modifications.modules[m.grp].blueprints[blueprintName].grades[grade].engineers, m.grp);
|
const tooltipContent = blueprintTooltip(translate, blueprint.grades[grade], Modifications.modules[m.grp].blueprints[blueprintName].grades[grade].engineers, m.grp);
|
||||||
blueprintGrades.unshift(<li key={key} className={classes} style={{ width: '2em' }} onMouseOver={termtip.bind(null, tooltipContent)} onMouseOut={tooltip.bind(null, null)} onClick={close}>{grade}</li>);
|
if (classes.indexOf('active') >= 0) this.selectedModId = key;
|
||||||
|
blueprintGrades.unshift(<li key={key} tabIndex="0" data-id={key} className={classes} style={{ width: '2em' }} onMouseOver={termtip.bind(null, tooltipContent)} onMouseOut={tooltip.bind(null, null)} onClick={close} onKeyDown={this._keyDown} ref={modItem => this.modItems[key] = modItem}>{grade}</li>);
|
||||||
}
|
}
|
||||||
if (blueprintGrades) {
|
if (blueprintGrades) {
|
||||||
|
const thisLen = blueprintGrades.length;
|
||||||
|
if (this.firstModId == null) this.firstModId = blueprintGrades[0].key;
|
||||||
|
this.lastModId = blueprintGrades[thisLen - 1].key;
|
||||||
blueprints.push(<div key={blueprint.name} className={'select-group cap'}>{translate(blueprint.name)}</div>);
|
blueprints.push(<div key={blueprint.name} className={'select-group cap'}>{translate(blueprint.name)}</div>);
|
||||||
blueprints.push(<ul key={blueprintName}>{blueprintGrades}</ul>);
|
blueprints.push(<ul key={blueprintName}>{blueprintGrades}</ul>);
|
||||||
}
|
}
|
||||||
@@ -76,6 +100,64 @@ export default class ModificationsMenu extends TranslatedComponent {
|
|||||||
return blueprints;
|
return blueprints;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Key down - select module on Enter key, move to next/previous module on Tab/Shift-Tab, close on Esc
|
||||||
|
* @param {SyntheticEvent} event Event
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
_keyDown(event) {
|
||||||
|
let className = null;
|
||||||
|
let elemId = null;
|
||||||
|
if (event.currentTarget.attributes['class']) className = event.currentTarget.attributes['class'].value;
|
||||||
|
if (event.currentTarget.attributes['data-id']) elemId = event.currentTarget.attributes['data-id'].value;
|
||||||
|
|
||||||
|
if (event.key == 'Enter' && className.indexOf('disabled') < 0 && className.indexOf('active') < 0) {
|
||||||
|
event.stopPropagation();
|
||||||
|
if (elemId != null) {
|
||||||
|
this.modItems[elemId].click();
|
||||||
|
} else {
|
||||||
|
event.currentTarget.click();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.key == 'Tab') {
|
||||||
|
// Shift-Tab
|
||||||
|
if(event.shiftKey) {
|
||||||
|
if (elemId == this.firstModId && elemId != null) {
|
||||||
|
// Initial modification menu
|
||||||
|
event.preventDefault();
|
||||||
|
this.modItems[this.lastModId].focus();
|
||||||
|
return;
|
||||||
|
} else if (event.currentTarget.className.indexOf('button-inline-menu') >= 0 && event.currentTarget.previousElementSibling == null && this.lastNeId != null && this.modItems[this.lastNeId] != null) {
|
||||||
|
// shift-tab on first element in modifications menu. set focus to last number editor field if open
|
||||||
|
event.preventDefault();
|
||||||
|
this.modItems[this.lastNeId].lastChild.focus();
|
||||||
|
return;
|
||||||
|
} else if (event.currentTarget.className.indexOf('button-inline-menu') >= 0 && event.currentTarget.previousElementSibling == null) {
|
||||||
|
// shift-tab on button-inline-menu with no number editor
|
||||||
|
event.preventDefault();
|
||||||
|
event.currentTarget.parentElement.lastElementChild.focus();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (elemId == this.lastModId && elemId != null) {
|
||||||
|
// Initial modification menu
|
||||||
|
event.preventDefault();
|
||||||
|
this.modItems[this.firstModId].focus();
|
||||||
|
return;
|
||||||
|
} else if (event.currentTarget.className.indexOf('button-inline-menu') >= 0 && event.currentTarget.nextSibling == null && event.currentTarget.nodeName != 'TD') {
|
||||||
|
// Experimental menu
|
||||||
|
event.preventDefault();
|
||||||
|
event.currentTarget.parentElement.firstElementChild.focus();
|
||||||
|
return;
|
||||||
|
} else if (event.currentTarget.className == 'cb' && event.currentTarget.parentElement.nextSibling == null) {
|
||||||
|
event.preventDefault();
|
||||||
|
this.modItems[this.firstBPLabel].focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render the specials
|
* Render the specials
|
||||||
* @param {Object} props React component properties
|
* @param {Object} props React component properties
|
||||||
@@ -86,15 +168,34 @@ export default class ModificationsMenu extends TranslatedComponent {
|
|||||||
const { m } = props;
|
const { m } = props;
|
||||||
const { language, tooltip, termtip } = context;
|
const { language, tooltip, termtip } = context;
|
||||||
const translate = language.translate;
|
const translate = language.translate;
|
||||||
|
|
||||||
const specials = [];
|
const specials = [];
|
||||||
const specialsId = m.missile && Modifications.modules[m.grp]['specials_' + m.missile] ? 'specials_' + m.missile : 'specials';
|
const specialsId = m.missile && Modifications.modules[m.grp]['specials_' + m.missile] ? 'specials_' + m.missile : 'specials';
|
||||||
if (Modifications.modules[m.grp][specialsId] && Modifications.modules[m.grp][specialsId].length > 0) {
|
if (Modifications.modules[m.grp][specialsId] && Modifications.modules[m.grp][specialsId].length > 0) {
|
||||||
const close = this._specialSelected.bind(this, null);
|
const close = this._specialSelected.bind(this, null);
|
||||||
specials.push(<div style={{ cursor: 'pointer' }} key={ 'none' } onClick={ close }>{translate('PHRASE_NO_SPECIAL')}</div>);
|
specials.push(<div tabIndex="0" style={{ cursor: 'pointer', fontWeight: 'bold' }} className={ 'button-inline-menu warning' } key={ 'none' } data-id={ 'none' } onClick={ close } onKeyDown={this._keyDown} ref={modItem => this.modItems['none'] = modItem}>{translate('PHRASE_NO_SPECIAL')}</div>);
|
||||||
for (const specialName of Modifications.modules[m.grp][specialsId]) {
|
for (const specialName of Modifications.modules[m.grp][specialsId]) {
|
||||||
|
if (Modifications.specials[specialName].name.search('Legacy') >= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const classes = cn('button-inline-menu', {
|
||||||
|
active: m.blueprint && m.blueprint.special && m.blueprint.special.key == specialName
|
||||||
|
});
|
||||||
|
if (classes.indexOf('active') >= 0) this.selectedSpecialId = specialName;
|
||||||
const close = this._specialSelected.bind(this, specialName);
|
const close = this._specialSelected.bind(this, specialName);
|
||||||
specials.push(<div style={{ cursor: 'pointer' }} key={ specialName } onClick={ close }>{translate(Modifications.specials[specialName].name)}</div>);
|
if (m.blueprint && m.blueprint.name) {
|
||||||
|
let tmp = {};
|
||||||
|
if (m.blueprint.special) {
|
||||||
|
tmp = m.blueprint.special;
|
||||||
|
} else {
|
||||||
|
tmp = undefined;
|
||||||
|
}
|
||||||
|
m.blueprint.special = Modifications.specials[specialName];
|
||||||
|
let specialTt = specialToolTip(translate, m.blueprint.grades[m.blueprint.grade], m.grp, m, specialName);
|
||||||
|
m.blueprint.special = tmp;
|
||||||
|
specials.push(<div tabIndex="0" style={{ cursor: 'pointer' }} className={classes} key={ specialName } data-id={ specialName } onMouseOver={termtip.bind(null, specialTt)} onMouseOut={tooltip.bind(null, null)} onClick={ close } onKeyDown={this._keyDown} ref={modItem => this.modItems[specialName] = modItem}>{translate(Modifications.specials[specialName].name)}</div>);
|
||||||
|
} else {
|
||||||
|
specials.push(<div tabIndex="0" style={{ cursor: 'pointer' }} className={classes} key={ specialName } data-id={ specialName }onClick={ close } onKeyDown={this._keyDown} ref={modItem => this.modItems[specialName] = modItem}>{translate(Modifications.specials[specialName].name)}</div>);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return specials;
|
return specials;
|
||||||
@@ -107,14 +208,26 @@ export default class ModificationsMenu extends TranslatedComponent {
|
|||||||
*/
|
*/
|
||||||
_renderModifications(props) {
|
_renderModifications(props) {
|
||||||
const { m, onChange, ship } = props;
|
const { m, onChange, ship } = props;
|
||||||
|
const modifiableModifications = [];
|
||||||
const modifications = [];
|
const modifications = [];
|
||||||
for (const modName of Modifications.modules[m.grp].modifications) {
|
for (const modName of Modifications.modules[m.grp].modifications) {
|
||||||
if (!Modifications.modifications[modName].hidden) {
|
if (!Modifications.modifications[modName].hidden) {
|
||||||
const key = modName + (m.getModValue(modName) / 100 || 0);
|
const key = modName + (m.getModValue(modName) / 100 || 0);
|
||||||
modifications.push(<Modification key={ key } ship={ ship } m={ m } name={ modName } value={ m.getModValue(modName) / 100 || 0 } onChange={ onChange }/>);
|
const editable = modName !== 'fallofffromrange';
|
||||||
|
const highlight = m.blueprint.grades[m.blueprint.grade].features[modName];
|
||||||
|
this.lastNeId = modName;
|
||||||
|
(editable && highlight ? modifiableModifications : modifications).push(
|
||||||
|
<Modification key={ key } ship={ ship } m={ m } highlight={highlight}
|
||||||
|
value={m.getPretty(modName) || 0} modItems={this.modItems}
|
||||||
|
onChange={onChange} onKeyDown={this._keyDown} name={modName}
|
||||||
|
editable={editable} handleModChange = {this._handleModChange} />
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return modifications;
|
|
||||||
|
modifiableModifications.sort(MODIFICATIONS_COMPARATOR);
|
||||||
|
modifications.sort(MODIFICATIONS_COMPARATOR);
|
||||||
|
return modifiableModifications.concat(modifications);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -136,8 +249,9 @@ export default class ModificationsMenu extends TranslatedComponent {
|
|||||||
const blueprint = getBlueprint(fdname, m);
|
const blueprint = getBlueprint(fdname, m);
|
||||||
blueprint.grade = grade;
|
blueprint.grade = grade;
|
||||||
ship.setModuleBlueprint(m, blueprint);
|
ship.setModuleBlueprint(m, blueprint);
|
||||||
|
setPercent(ship, m, 100);
|
||||||
|
|
||||||
this.setState({ blueprintMenuOpened: false });
|
this.setState({ blueprintMenuOpened: false, specialMenuOpened: true });
|
||||||
this.props.onChange();
|
this.props.onChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,11 +282,15 @@ export default class ModificationsMenu extends TranslatedComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provide a 'worst' roll within the information we have
|
* Provide a '50%' roll within the information we have
|
||||||
*/
|
*/
|
||||||
_rollWorst() {
|
_rollFifty() {
|
||||||
const { m, ship } = this.props;
|
const { m, ship } = this.props;
|
||||||
setWorst(ship, m);
|
setPercent(ship, m, 50);
|
||||||
|
|
||||||
|
// this will change the values in the modifications. Set modDidChange to true to prevent focus change when component updates
|
||||||
|
this._handleModChange(true);
|
||||||
|
|
||||||
this.props.onChange();
|
this.props.onChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,6 +300,10 @@ export default class ModificationsMenu extends TranslatedComponent {
|
|||||||
_rollRandom() {
|
_rollRandom() {
|
||||||
const { m, ship } = this.props;
|
const { m, ship } = this.props;
|
||||||
setRandom(ship, m);
|
setRandom(ship, m);
|
||||||
|
|
||||||
|
// this will change the values in the modifications. Set modDidChange to true to prevent focus change when component updates
|
||||||
|
this._handleModChange(true);
|
||||||
|
|
||||||
this.props.onChange();
|
this.props.onChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,16 +312,22 @@ export default class ModificationsMenu extends TranslatedComponent {
|
|||||||
*/
|
*/
|
||||||
_rollBest() {
|
_rollBest() {
|
||||||
const { m, ship } = this.props;
|
const { m, ship } = this.props;
|
||||||
setBest(ship, m);
|
setPercent(ship, m, 100);
|
||||||
|
|
||||||
|
// this will change the values in the modifications. Set modDidChange to true to prevent focus change when component updates
|
||||||
|
this._handleModChange(true);
|
||||||
|
|
||||||
this.props.onChange();
|
this.props.onChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provide an 'extreme' roll within the information we have
|
* Provide a 'worst' roll within the information we have
|
||||||
*/
|
*/
|
||||||
_rollExtreme() {
|
_rollWorst() {
|
||||||
const { m, ship } = this.props;
|
const { m, ship } = this.props;
|
||||||
setExtreme(ship, m);
|
setPercent(ship, m, 0);
|
||||||
|
// this will change the values in the modifications. Set modDidChange to true to prevent focus change when component updates
|
||||||
|
this._handleModChange(true);
|
||||||
this.props.onChange();
|
this.props.onChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,10 +338,67 @@ export default class ModificationsMenu extends TranslatedComponent {
|
|||||||
const { m, ship } = this.props;
|
const { m, ship } = this.props;
|
||||||
ship.clearModifications(m);
|
ship.clearModifications(m);
|
||||||
ship.clearModuleBlueprint(m);
|
ship.clearModuleBlueprint(m);
|
||||||
|
this.selectedModId = null;
|
||||||
|
this.selectedSpecialId = null;
|
||||||
this.props.onChange();
|
this.props.onChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set mod did change boolean
|
||||||
|
* @param {boolean} b Boolean to determine if a change has been made to a module
|
||||||
|
*/
|
||||||
|
_handleModChange(b) {
|
||||||
|
this.modValDidChange = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set focus on first element in modifications menu
|
||||||
|
* after it first mounts
|
||||||
|
*/
|
||||||
|
componentDidMount() {
|
||||||
|
let firstEleCn = this.modItems['modMainDiv'].children.length > 0 ? this.modItems['modMainDiv'].children[0].className : null;
|
||||||
|
if (firstEleCn.indexOf('select-group cap') >= 0) {
|
||||||
|
this.modItems['modMainDiv'].children[1].firstElementChild.focus();
|
||||||
|
} else {
|
||||||
|
this.modItems['modMainDiv'].firstElementChild.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set focus on first element in modifications menu
|
||||||
|
* if component updates, unless update is due to value change
|
||||||
|
* in a modification
|
||||||
|
*/
|
||||||
|
componentDidUpdate() {
|
||||||
|
if (!this.modValDidChange) {
|
||||||
|
if (this.modItems['modMainDiv'].children.length > 0) {
|
||||||
|
if (this.modItems[this.selectedModId]) {
|
||||||
|
this.modItems[this.selectedModId].focus();
|
||||||
|
return;
|
||||||
|
} else if (this.modItems[this.selectedSpecialId]) {
|
||||||
|
this.modItems[this.selectedSpecialId].focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let firstEleCn = this.modItems['modMainDiv'].children[0].className;
|
||||||
|
if (firstEleCn.indexOf('button-inline-menu') >= 0) {
|
||||||
|
this.modItems['modMainDiv'].firstElementChild.focus();
|
||||||
|
} else if (firstEleCn.indexOf('select-group cap') >= 0) {
|
||||||
|
this.modItems['modMainDiv'].children[1].firstElementChild.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this._handleModChange(false);// Need to reset if component update due to value change
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* set focus to the modification menu icon after mod menu is unmounted.
|
||||||
|
*/
|
||||||
|
componentWillUnmount() {
|
||||||
|
if (this.props.modButton) {
|
||||||
|
this.props.modButton.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render the list
|
* Render the list
|
||||||
* @return {React.Component} List
|
* @return {React.Component} List
|
||||||
@@ -226,66 +411,82 @@ export default class ModificationsMenu extends TranslatedComponent {
|
|||||||
|
|
||||||
const _toggleBlueprintsMenu = this._toggleBlueprintsMenu;
|
const _toggleBlueprintsMenu = this._toggleBlueprintsMenu;
|
||||||
const _toggleSpecialsMenu = this._toggleSpecialsMenu;
|
const _toggleSpecialsMenu = this._toggleSpecialsMenu;
|
||||||
const _rollBest = this._rollBest;
|
const _rollFull = this._rollBest;
|
||||||
const _rollExtreme = this._rollExtreme;
|
|
||||||
const _rollWorst = this._rollWorst;
|
const _rollWorst = this._rollWorst;
|
||||||
|
const _rollFifty = this._rollFifty;
|
||||||
const _rollRandom = this._rollRandom;
|
const _rollRandom = this._rollRandom;
|
||||||
const _reset = this._reset;
|
const _reset = this._reset;
|
||||||
|
|
||||||
let blueprintLabel;
|
let blueprintLabel;
|
||||||
let haveBlueprint = false;
|
let haveBlueprint = false;
|
||||||
let blueprintTt;
|
let blueprintTt;
|
||||||
if (m.blueprint && m.blueprint.name) {
|
let blueprintCv;
|
||||||
|
// TODO: Fix this to actually find the correct blueprint.
|
||||||
|
if (!m.blueprint || !m.blueprint.name || !m.blueprint.fdname || !Modifications.modules[m.grp].blueprints || !Modifications.modules[m.grp].blueprints[m.blueprint.fdname]) {
|
||||||
|
this.props.ship.clearModuleBlueprint(m);
|
||||||
|
this.props.ship.clearModuleSpecial(m);
|
||||||
|
}
|
||||||
|
if (m.blueprint && m.blueprint.name && Modifications.modules[m.grp].blueprints[m.blueprint.fdname].grades[m.blueprint.grade]) {
|
||||||
blueprintLabel = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
|
blueprintLabel = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
|
||||||
haveBlueprint = true;
|
haveBlueprint = true;
|
||||||
blueprintTt = blueprintTooltip(translate, m.blueprint.grades[m.blueprint.grade], Modifications.modules[m.grp].blueprints[m.blueprint.fdname].grades[m.blueprint.grade].engineers, m.grp);
|
blueprintTt = blueprintTooltip(translate, m.blueprint.grades[m.blueprint.grade], Modifications.modules[m.grp].blueprints[m.blueprint.fdname].grades[m.blueprint.grade].engineers, m.grp);
|
||||||
|
blueprintCv = getPercent(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
let specialLabel;
|
let specialLabel;
|
||||||
|
let specialTt;
|
||||||
if (m.blueprint && m.blueprint.special) {
|
if (m.blueprint && m.blueprint.special) {
|
||||||
specialLabel = m.blueprint.special.name;
|
specialLabel = m.blueprint.special.name;
|
||||||
|
specialTt = specialToolTip(translate, m.blueprint.grades[m.blueprint.grade], m.grp, m, m.blueprint.special.key);
|
||||||
} else {
|
} else {
|
||||||
specialLabel = translate('PHRASE_SELECT_SPECIAL');
|
specialLabel = translate('PHRASE_SELECT_SPECIAL');
|
||||||
}
|
}
|
||||||
|
|
||||||
const specials = this._renderSpecials(this.props, this.context);
|
const specials = this._renderSpecials(this.props, this.context);
|
||||||
|
/**
|
||||||
|
* pnellesen - 05/28/2018 - added additional checks for specials.length below to ensure menus
|
||||||
|
* display correctly in cases where there are no specials (ex: AFMUs.)
|
||||||
|
*/
|
||||||
const showBlueprintsMenu = blueprintMenuOpened;
|
const showBlueprintsMenu = blueprintMenuOpened;
|
||||||
const showSpecial = haveBlueprint && specials.length && !blueprintMenuOpened;
|
const showSpecial = haveBlueprint && specials.length && !blueprintMenuOpened;
|
||||||
const showSpecialsMenu = specialMenuOpened;
|
const showSpecialsMenu = specialMenuOpened && specials.length;
|
||||||
const showRolls = haveBlueprint && !blueprintMenuOpened && !specialMenuOpened;
|
const showRolls = haveBlueprint && !blueprintMenuOpened && (!specialMenuOpened || !specials.length);
|
||||||
const showReset = !blueprintMenuOpened && !specialMenuOpened;
|
const showReset = !blueprintMenuOpened && (!specialMenuOpened || !specials.length) && haveBlueprint;
|
||||||
const showMods = !blueprintMenuOpened && !specialMenuOpened;
|
const showMods = !blueprintMenuOpened && (!specialMenuOpened || !specials.length) && haveBlueprint;
|
||||||
|
if (haveBlueprint) {
|
||||||
|
this.firstBPLabel = blueprintLabel;
|
||||||
|
} else {
|
||||||
|
this.firstBPLabel = 'selectBP';
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn('select', this.props.className)}
|
className={cn('select', this.props.className)}
|
||||||
onClick={(e) => e.stopPropagation() }
|
onClick={(e) => e.stopPropagation() }
|
||||||
onContextMenu={stopCtxPropagation}
|
onContextMenu={stopCtxPropagation}
|
||||||
|
ref={modItem => this.modItems['modMainDiv'] = modItem}
|
||||||
>
|
>
|
||||||
{ showBlueprintsMenu ? '' : haveBlueprint ?
|
{ showBlueprintsMenu | showSpecialsMenu ? '' : haveBlueprint ?
|
||||||
<div className={ cn('section-menu', { selected: blueprintMenuOpened })} style={{ cursor: 'pointer' }} onMouseOver={termtip.bind(null, blueprintTt)} onMouseOut={tooltip.bind(null, null)} onClick={_toggleBlueprintsMenu}>{blueprintLabel}</div> :
|
<div tabIndex="0" className={ cn('section-menu button-inline-menu', { selected: blueprintMenuOpened })} style={{ cursor: 'pointer' }} onMouseOver={termtip.bind(null, blueprintTt)} onMouseOut={tooltip.bind(null, null)} onClick={_toggleBlueprintsMenu} onKeyDown={ this._keyDown } ref={modItems => this.modItems[this.firstBPLabel] = modItems}>{blueprintLabel}</div> :
|
||||||
<div className={ cn('section-menu', { selected: blueprintMenuOpened })} style={{ cursor: 'pointer' }} onClick={_toggleBlueprintsMenu}>{translate('PHRASE_SELECT_BLUEPRINT')}</div> }
|
<div tabIndex="0" className={ cn('section-menu button-inline-menu', { selected: blueprintMenuOpened })} style={{ cursor: 'pointer' }} onClick={_toggleBlueprintsMenu} onKeyDown={ this._keyDown } ref={modItems => this.modItems[this.firstBPLabel] = modItems}>{translate('PHRASE_SELECT_BLUEPRINT')}</div> }
|
||||||
{ showBlueprintsMenu ? this._renderBlueprints(this.props, this.context) : null }
|
{ showBlueprintsMenu ? this._renderBlueprints(this.props, this.context) : null }
|
||||||
{ showSpecial ? <div className={ cn('section-menu', { selected: specialMenuOpened })} style={{ cursor: 'pointer' }} onClick={_toggleSpecialsMenu}>{specialLabel}</div> : null }
|
{ showSpecial & !showSpecialsMenu ? <div tabIndex="0" className={ cn('section-menu button-inline-menu', { selected: specialMenuOpened })} style={{ cursor: 'pointer' }} onMouseOver={specialTt ? termtip.bind(null, specialTt) : null} onMouseOut={specialTt ? tooltip.bind(null, null) : null} onClick={_toggleSpecialsMenu} onKeyDown={ this._keyDown }>{specialLabel}</div> : null }
|
||||||
{ showSpecialsMenu ? specials : null }
|
{ showSpecialsMenu ? specials : null }
|
||||||
{ showRolls || showReset ?
|
{ showReset ? <div tabIndex="0" className={'section-menu button-inline-menu warning'} style={{ cursor: 'pointer' }} onClick={_reset} onKeyDown={ this._keyDown } onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_RESET')} onMouseOut={tooltip.bind(null, null)}> { translate('reset') } </div> : null }
|
||||||
<table style={{ width: '100%', backgroundColor: 'transparent' }}>
|
{ showRolls ?
|
||||||
<tbody>
|
|
||||||
{ showRolls ?
|
<table style={{ width: '100%', backgroundColor: 'transparent' }}>
|
||||||
|
<tbody>
|
||||||
|
{ showRolls ?
|
||||||
<tr>
|
<tr>
|
||||||
<td> { translate('roll') }: </td>
|
<td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: false }) }> { translate('mroll') }: </td>
|
||||||
<td style={{ cursor: 'pointer' }} onClick={_rollWorst} onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_WORST')} onMouseOut={tooltip.bind(null, null)}> { translate('worst') } </td>
|
<td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: blueprintCv === 0 }) } style={{ cursor: 'pointer' }} onClick={_rollWorst} onKeyDown={ this._keyDown } onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_WORST')} onMouseOut={tooltip.bind(null, null)}> { translate('0%') } </td>
|
||||||
<td style={{ cursor: 'pointer' }} onClick={_rollBest}onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_BEST')} onMouseOut={tooltip.bind(null, null)}> { translate('best') } </td>
|
<td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: blueprintCv === 50 })} style={{ cursor: 'pointer' }} onClick={_rollFifty} onKeyDown={ this._keyDown } onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_FIFTY')} onMouseOut={tooltip.bind(null, null)}> { translate('50%') } </td>
|
||||||
<td style={{ cursor: 'pointer' }} onClick={_rollExtreme}onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_EXTREME')} onMouseOut={tooltip.bind(null, null)}> { translate('extreme') } </td>
|
<td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: blueprintCv === 100 })} style={{ cursor: 'pointer' }} onClick={_rollFull} onKeyDown={ this._keyDown } onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_BEST')} onMouseOut={tooltip.bind(null, null)}> { translate('100%') } </td>
|
||||||
<td style={{ cursor: 'pointer' }} onClick={_rollRandom} onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_RANDOM')} onMouseOut={tooltip.bind(null, null)}> { translate('random') } </td>
|
<td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: blueprintCv === null || blueprintCv % 50 != 0 })} style={{ cursor: 'pointer' }} onClick={_rollRandom} onKeyDown={ this._keyDown } onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_RANDOM')} onMouseOut={tooltip.bind(null, null)}> { translate('random') } </td>
|
||||||
</tr> : null }
|
</tr> : null }
|
||||||
{ showReset ?
|
</tbody>
|
||||||
<tr>
|
|
||||||
<td colSpan={'5'} style={{ cursor: 'pointer' }} onClick={_reset}onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_RESET')} onMouseOut={tooltip.bind(null, null)}> { translate('reset') } </td>
|
|
||||||
</tr> : null }
|
|
||||||
</tbody>
|
|
||||||
</table> : null }
|
</table> : null }
|
||||||
|
{ showMods ? <hr /> : null }
|
||||||
{ showMods ?
|
{ showMods ?
|
||||||
<span onMouseOver={termtip.bind(null, 'HELP_MODIFICATIONS_MENU')} onMouseOut={tooltip.bind(null, null)} >
|
<span onMouseOver={termtip.bind(null, 'HELP_MODIFICATIONS_MENU')} onMouseOut={tooltip.bind(null, null)} >
|
||||||
{ this._renderModifications(this.props) }
|
{ this._renderModifications(this.props) }
|
||||||
|
|||||||
@@ -35,13 +35,13 @@ export default class Movement extends TranslatedComponent {
|
|||||||
return (
|
return (
|
||||||
<span id='movement'>
|
<span id='movement'>
|
||||||
<svg viewBox='0 0 600 600' fillRule="evenodd" clipRule="evenodd">
|
<svg viewBox='0 0 600 600' fillRule="evenodd" clipRule="evenodd">
|
||||||
// Axes
|
{/* Axes */}
|
||||||
<path d="M150 250v300" strokeWidth='1'/>
|
<path d="M150 250v300" strokeWidth='1'/>
|
||||||
<path d="M150 250l236 236" strokeWidth='1'/>
|
<path d="M150 250l236 236" strokeWidth='1'/>
|
||||||
<path d="M150 250l350 -200" strokeWidth='1'/>
|
<path d="M150 250l350 -200" strokeWidth='1'/>
|
||||||
// End Arrow
|
{/* End Arrow */}
|
||||||
<path d="M508 43.3L487 67l-10-17.3 31-6.4z"/>
|
<path d="M508 43.3L487 67l-10-17.3 31-6.4z"/>
|
||||||
// Axes arcs and arrows
|
{/* Axes arcs and arrows */}
|
||||||
<path d="M71.7 251.7C64.2 259.2 60 269.4 60 280c0 22 18 40 40 40s40-18 40-40c0-10.6-4.2-20.8-11.7-28.3 7.5 7.5 11.7 17.7 11.7 28.3 0 22-18 40-40 40s-40-18-40-40c0-10.6 4.2-20.8 11.7-28.3z" strokeWidth='4' transform="matrix(.6 0 0 .3 87.5 376.3)"/>
|
<path d="M71.7 251.7C64.2 259.2 60 269.4 60 280c0 22 18 40 40 40s40-18 40-40c0-10.6-4.2-20.8-11.7-28.3 7.5 7.5 11.7 17.7 11.7 28.3 0 22-18 40-40 40s-40-18-40-40c0-10.6 4.2-20.8 11.7-28.3z" strokeWidth='4' transform="matrix(.6 0 0 .3 87.5 376.3)"/>
|
||||||
<path d="M142.8 453l-13.2 8.7-2.6-9.7 15.8 1z"/>
|
<path d="M142.8 453l-13.2 8.7-2.6-9.7 15.8 1z"/>
|
||||||
<path d="M144.7 451.6l.5 1.6-16.2 10.6h-.4l-3.5-13 .7-.4 19.3 1.2zm-14.2 7.7l7.7-5-9.2-.7 1.5 5.7zm25.7-6.3l15.8-1-2.6 9.7-13.2-8.8z"/>
|
<path d="M144.7 451.6l.5 1.6-16.2 10.6h-.4l-3.5-13 .7-.4 19.3 1.2zm-14.2 7.7l7.7-5-9.2-.7 1.5 5.7zm25.7-6.3l15.8-1-2.6 9.7-13.2-8.8z"/>
|
||||||
@@ -57,13 +57,13 @@ export default class Movement extends TranslatedComponent {
|
|||||||
<path d="M359.5 422.4l-1.2 19.3-1.6.4-10.7-16 .2-.2 13-3.4.3.4zm-9 5l5.2 7.8.6-9.3-5.7 1.2zm-10.5 24l-13.2 8.6-2.6-9.7 15.8 1z"/>
|
<path d="M359.5 422.4l-1.2 19.3-1.6.4-10.7-16 .2-.2 13-3.4.3.4zm-9 5l5.2 7.8.6-9.3-5.7 1.2zm-10.5 24l-13.2 8.6-2.6-9.7 15.8 1z"/>
|
||||||
<path d="M342 450l.4 1.5-16.2 10.7-.4-.2-3.5-13 .3-.3L342 450zm-14.3 7.6l7.7-5-9.2-.6 1.5 5.6z"/>
|
<path d="M342 450l.4 1.5-16.2 10.7-.4-.2-3.5-13 .3-.3L342 450zm-14.3 7.6l7.7-5-9.2-.6 1.5 5.6z"/>
|
||||||
|
|
||||||
// Speed
|
{/* Speed */}
|
||||||
<text x="470" y="30" strokeWidth='0'>{ship.canThrust(cargo, fuel) ? formats.int(ship.calcSpeed(eng, fuel, cargo, boost)) + 'm/s' : '-'}</text>
|
<text x="470" y="30" strokeWidth='0'>{ship.canThrust(cargo, fuel) ? formats.int(ship.calcSpeed(eng, fuel, cargo, boost)) + 'm/s' : '-'}</text>
|
||||||
// Pitch
|
{/* Pitch */}
|
||||||
<text x="355" y="410" strokeWidth='0'>{ship.canThrust(cargo, fuel) ? formats.int(ship.calcPitch(eng, fuel, cargo, boost)) + '°/s' : '-'}</text>
|
<text x="355" y="410" strokeWidth='0'>{ship.canThrust(cargo, fuel) ? formats.int(ship.calcPitch(eng, fuel, cargo, boost)) + '°/s' : '-'}</text>
|
||||||
// Roll
|
{/* Roll */}
|
||||||
<text x="450" y="110" strokeWidth='0'>{ship.canThrust(cargo, fuel) ? formats.int(ship.calcRoll(eng, fuel, cargo, boost)) + '°/s' : '-'}</text>
|
<text x="450" y="110" strokeWidth='0'>{ship.canThrust(cargo, fuel) ? formats.int(ship.calcRoll(eng, fuel, cargo, boost)) + '°/s' : '-'}</text>
|
||||||
// Yaw
|
{/* Yaw */}
|
||||||
<text x="160" y="430" strokeWidth='0'>{ship.canThrust(cargo, fuel) ? formats.int(ship.calcYaw(eng, fuel, cargo, boost)) + '°/s' : '-'}</text>
|
<text x="160" y="430" strokeWidth='0'>{ship.canThrust(cargo, fuel) ? formats.int(ship.calcYaw(eng, fuel, cargo, boost)) + '°/s' : '-'}</text>
|
||||||
</svg>
|
</svg>
|
||||||
</span>);
|
</span>);
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import * as Calc from '../shipyard/Calculations';
|
|||||||
import PieChart from './PieChart';
|
import PieChart from './PieChart';
|
||||||
import { nameComparator } from '../utils/SlotFunctions';
|
import { nameComparator } from '../utils/SlotFunctions';
|
||||||
import { MountFixed, MountGimballed, MountTurret } from './SvgIcons';
|
import { MountFixed, MountGimballed, MountTurret } from './SvgIcons';
|
||||||
import VerticalBarChart from './VerticalBarChart';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates an internationalization friendly weapon comparator that will
|
* Generates an internationalization friendly weapon comparator that will
|
||||||
@@ -42,6 +41,63 @@ export function weaponComparator(translate, propComparator, desc) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a tooltip that shows damage by type.
|
||||||
|
* @param {function} translate Translation function
|
||||||
|
* @param {Object} formats Object that holds format functions
|
||||||
|
* @param {Calc.SDps} sdpsObject Object that holds sdps split up by type
|
||||||
|
* @returns {Array} Tooltip
|
||||||
|
*/
|
||||||
|
function getSDpsTooltip(translate, formats, sdpsObject) {
|
||||||
|
return Object.keys(sdpsObject).filter(key => sdpsObject[key])
|
||||||
|
.map(key => {
|
||||||
|
return (
|
||||||
|
<div key={key}>
|
||||||
|
{translate(key) + ' ' + formats.f1(sdpsObject[key])}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a data object used by {@link PieChart} that shows damage by type.
|
||||||
|
* @param {function} translate Translation function
|
||||||
|
* @param {Calc.SDps} sdpsObject Object that holds sdps split up by type
|
||||||
|
* @returns {Object} Data object
|
||||||
|
*/
|
||||||
|
function getSDpsData(translate, sdpsObject) {
|
||||||
|
return Object.keys(sdpsObject).map(key => {
|
||||||
|
return {
|
||||||
|
value: Math.round(sdpsObject[key]),
|
||||||
|
label: translate(key)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds all damage of `add` onto `addOn`.
|
||||||
|
* @param {Calc.SDps} addOn Object that holds sdps split up by type (will be mutated)
|
||||||
|
* @param {Calc.SDps} add Object that holds sdps split up by type
|
||||||
|
*/
|
||||||
|
function addSDps(addOn, add) {
|
||||||
|
Object.keys(addOn).map(k => addOn[k] += (add[k] ? add[k] : 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the overall sdps of an sdps object.
|
||||||
|
* @param {Calc.SDps} sdpsObject Object that holds sdps spluit up by type
|
||||||
|
*/
|
||||||
|
function sumSDps(sdpsObject) {
|
||||||
|
if (sdpsObject.total) {
|
||||||
|
return sdpsObject.total;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.keys(sdpsObject).reduce(
|
||||||
|
(acc, k) => acc + (sdpsObject[k] ? sdpsObject[k] : 0),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Offence information
|
* Offence information
|
||||||
* Offence information consists of four panels:
|
* Offence information consists of four panels:
|
||||||
@@ -70,7 +126,7 @@ export default class Offence extends TranslatedComponent {
|
|||||||
this._sort = this._sort.bind(this);
|
this._sort = this._sort.bind(this);
|
||||||
|
|
||||||
const damage = Calc.offenceMetrics(props.ship, props.opponent, props.wep, props.opponentSys, props.engagementrange);
|
const damage = Calc.offenceMetrics(props.ship, props.opponent, props.wep, props.opponentSys, props.engagementrange);
|
||||||
this.state = {
|
this.state = {
|
||||||
predicate: 'n',
|
predicate: 'n',
|
||||||
desc: true,
|
desc: true,
|
||||||
damage
|
damage
|
||||||
@@ -144,51 +200,36 @@ export default class Offence extends TranslatedComponent {
|
|||||||
|
|
||||||
const timeToDrain = Calc.timeToDrainWep(ship, wep);
|
const timeToDrain = Calc.timeToDrainWep(ship, wep);
|
||||||
|
|
||||||
let absoluteShieldsSDps = 0;
|
|
||||||
let explosiveShieldsSDps = 0;
|
|
||||||
let kineticShieldsSDps = 0;
|
|
||||||
let thermalShieldsSDps = 0;
|
|
||||||
let absoluteArmourSDps = 0;
|
|
||||||
let explosiveArmourSDps = 0;
|
|
||||||
let kineticArmourSDps = 0;
|
|
||||||
let thermalArmourSDps = 0;
|
|
||||||
|
|
||||||
let totalSEps = 0;
|
let totalSEps = 0;
|
||||||
|
let totalSDpsObject = { 'absolute': 0, 'explosive': 0, 'kinetic': 0, 'thermal': 0 };
|
||||||
|
let shieldsSDpsObject = { 'absolute': 0, 'explosive': 0, 'kinetic': 0, 'thermal': 0 };
|
||||||
|
let armourSDpsObject = { 'absolute': 0, 'explosive': 0, 'kinetic': 0, 'thermal': 0 };
|
||||||
|
|
||||||
const rows = [];
|
const rows = [];
|
||||||
for (let i = 0; i < damage.length; i++) {
|
for (let i = 0; i < damage.length; i++) {
|
||||||
const weapon = damage[i];
|
const weapon = damage[i];
|
||||||
|
|
||||||
totalSEps += weapon.seps;
|
totalSEps += weapon.seps;
|
||||||
absoluteShieldsSDps += weapon.sdps.shields.absolute;
|
addSDps(totalSDpsObject, weapon.sdps.base);
|
||||||
explosiveShieldsSDps += weapon.sdps.shields.explosive;
|
addSDps(shieldsSDpsObject, weapon.sdps.shields);
|
||||||
kineticShieldsSDps += weapon.sdps.shields.kinetic;
|
addSDps(armourSDpsObject, weapon.sdps.armour);
|
||||||
thermalShieldsSDps += weapon.sdps.shields.thermal;
|
|
||||||
absoluteArmourSDps += weapon.sdps.armour.absolute;
|
const baseSDpsTooltipDetails = getSDpsTooltip(translate, formats, weapon.sdps.base);
|
||||||
explosiveArmourSDps += weapon.sdps.armour.explosive;
|
|
||||||
kineticArmourSDps += weapon.sdps.armour.kinetic;
|
|
||||||
thermalArmourSDps += weapon.sdps.armour.thermal;
|
|
||||||
|
|
||||||
const effectivenessShieldsTooltipDetails = [];
|
const effectivenessShieldsTooltipDetails = [];
|
||||||
effectivenessShieldsTooltipDetails.push(<div key='range'>{translate('range') + ' ' + formats.pct1(weapon.effectiveness.shields.range)}</div>);
|
effectivenessShieldsTooltipDetails.push(<div key='range'>{translate('range') + ' ' + formats.pct1(weapon.effectiveness.shields.range)}</div>);
|
||||||
effectivenessShieldsTooltipDetails.push(<div key='resistance'>{translate('resistance') + ' ' + formats.pct1(weapon.effectiveness.shields.resistance)}</div>);
|
effectivenessShieldsTooltipDetails.push(<div key='resistance'>{translate('resistance') + ' ' + formats.pct1(weapon.effectiveness.shields.resistance)}</div>);
|
||||||
effectivenessShieldsTooltipDetails.push(<div key='power distributor'>{translate('power distributor') + ' ' + formats.pct1(weapon.effectiveness.shields.sys)}</div>);
|
effectivenessShieldsTooltipDetails.push(<div key='power distributor'>{translate('power distributor') + ' ' + formats.pct1(weapon.effectiveness.shields.sys)}</div>);
|
||||||
|
|
||||||
const effectiveShieldsSDpsTooltipDetails = [];
|
const effectiveShieldsSDpsTooltipDetails = getSDpsTooltip(translate, formats, weapon.sdps.armour);
|
||||||
if (weapon.sdps.shields.absolute) effectiveShieldsSDpsTooltipDetails.push(<div key='absolute'>{translate('absolute') + ' ' + formats.f1(weapon.sdps.shields.absolute)}</div>);
|
|
||||||
if (weapon.sdps.shields.explosive) effectiveShieldsSDpsTooltipDetails.push(<div key='explosive'>{translate('explosive') + ' ' + formats.f1(weapon.sdps.shields.explosive)}</div>);
|
|
||||||
if (weapon.sdps.shields.kinetic) effectiveShieldsSDpsTooltipDetails.push(<div key='kinetic'>{translate('kinetic') + ' ' + formats.f1(weapon.sdps.shields.kinetic)}</div>);
|
|
||||||
if (weapon.sdps.shields.thermal) effectiveShieldsSDpsTooltipDetails.push(<div key='thermal'>{translate('thermal') + ' ' + formats.f1(weapon.sdps.shields.thermal)}</div>);
|
|
||||||
|
|
||||||
const effectivenessArmourTooltipDetails = [];
|
const effectivenessArmourTooltipDetails = [];
|
||||||
effectivenessArmourTooltipDetails.push(<div key='range'>{translate('range') + ' ' + formats.pct1(weapon.effectiveness.armour.range)}</div>);
|
effectivenessArmourTooltipDetails.push(<div key='range'>{translate('range') + ' ' + formats.pct1(weapon.effectiveness.armour.range)}</div>);
|
||||||
effectivenessArmourTooltipDetails.push(<div key='resistance'>{translate('resistance') + ' ' + formats.pct1(weapon.effectiveness.armour.resistance)}</div>);
|
effectivenessArmourTooltipDetails.push(<div key='resistance'>{translate('resistance') + ' ' + formats.pct1(weapon.effectiveness.armour.resistance)}</div>);
|
||||||
effectivenessArmourTooltipDetails.push(<div key='hardness'>{translate('hardness') + ' ' + formats.pct1(weapon.effectiveness.armour.hardness)}</div>);
|
effectivenessArmourTooltipDetails.push(<div key='hardness'>{translate('hardness') + ' ' + formats.pct1(weapon.effectiveness.armour.hardness)}</div>);
|
||||||
const effectiveArmourSDpsTooltipDetails = [];
|
|
||||||
if (weapon.sdps.armour.absolute) effectiveArmourSDpsTooltipDetails.push(<div key='absolute'>{translate('absolute') + ' ' + formats.f1(weapon.sdps.armour.absolute)}</div>);
|
const effectiveArmourSDpsTooltipDetails = getSDpsTooltip(translate, formats, weapon.sdps.armour);
|
||||||
if (weapon.sdps.armour.explosive) effectiveArmourSDpsTooltipDetails.push(<div key='explosive'>{translate('explosive') + ' ' + formats.f1(weapon.sdps.armour.explosive)}</div>);
|
|
||||||
if (weapon.sdps.armour.kinetic) effectiveArmourSDpsTooltipDetails.push(<div key='kinetic'>{translate('kinetic') + ' ' + formats.f1(weapon.sdps.armour.kinetic)}</div>);
|
|
||||||
if (weapon.sdps.armour.thermal) effectiveArmourSDpsTooltipDetails.push(<div key='thermal'>{translate('thermal') + ' ' + formats.f1(weapon.sdps.armour.thermal)}</div>);
|
|
||||||
|
|
||||||
rows.push(
|
rows.push(
|
||||||
<tr key={weapon.id}>
|
<tr key={weapon.id}>
|
||||||
@@ -199,27 +240,25 @@ export default class Offence extends TranslatedComponent {
|
|||||||
{weapon.classRating} {translate(weapon.name)}
|
{weapon.classRating} {translate(weapon.name)}
|
||||||
{weapon.engineering ? ' (' + weapon.engineering + ')' : null }
|
{weapon.engineering ? ' (' + weapon.engineering + ')' : null }
|
||||||
</td>
|
</td>
|
||||||
|
<td className='ri'><span onMouseOver={termtip.bind(null, baseSDpsTooltipDetails)} onMouseOut={tooltip.bind(null, null)}>{formats.f1(weapon.sdps.base.total)}</span></td>
|
||||||
<td className='ri'><span onMouseOver={termtip.bind(null, effectiveShieldsSDpsTooltipDetails)} onMouseOut={tooltip.bind(null, null)}>{formats.f1(weapon.sdps.shields.total)}</span></td>
|
<td className='ri'><span onMouseOver={termtip.bind(null, effectiveShieldsSDpsTooltipDetails)} onMouseOut={tooltip.bind(null, null)}>{formats.f1(weapon.sdps.shields.total)}</span></td>
|
||||||
<td className='ri'><span onMouseOver={termtip.bind(null, effectivenessShieldsTooltipDetails)} onMouseOut={tooltip.bind(null, null)}>{formats.pct1(weapon.effectiveness.shields.total)}</span></td>
|
<td className='ri'><span onMouseOver={termtip.bind(null, effectivenessShieldsTooltipDetails)} onMouseOut={tooltip.bind(null, null)}>{formats.pct1(weapon.effectiveness.shields.total)}</span></td>
|
||||||
<td className='ri'><span onMouseOver={termtip.bind(null, effectiveArmourSDpsTooltipDetails)} onMouseOut={tooltip.bind(null, null)}>{formats.f1(weapon.sdps.armour.total)}</span></td>
|
<td className='ri'><span onMouseOver={termtip.bind(null, effectiveArmourSDpsTooltipDetails)} onMouseOut={tooltip.bind(null, null)}>{formats.f1(weapon.sdps.armour.total)}</span></td>
|
||||||
<td className='ri'><span onMouseOver={termtip.bind(null, effectivenessArmourTooltipDetails)} onMouseOut={tooltip.bind(null, null)}>{formats.pct1(weapon.effectiveness.armour.total)}</span></td>
|
<td className='ri'><span onMouseOver={termtip.bind(null, effectivenessArmourTooltipDetails)} onMouseOut={tooltip.bind(null, null)}>{formats.pct1(weapon.effectiveness.armour.total)}</span></td>
|
||||||
</tr>);
|
</tr>);
|
||||||
}
|
}
|
||||||
|
|
||||||
const totalShieldsSDps = absoluteShieldsSDps + explosiveShieldsSDps + kineticShieldsSDps + thermalShieldsSDps;
|
const totalSDps = sumSDps(totalSDpsObject);
|
||||||
const totalArmourSDps = absoluteArmourSDps + explosiveArmourSDps + kineticArmourSDps + thermalArmourSDps;
|
const totalSDpsTooltipDetails = getSDpsTooltip(translate, formats, totalSDpsObject);
|
||||||
|
const totalSDpsData = getSDpsData(translate, totalSDpsObject);
|
||||||
|
|
||||||
const shieldsSDpsData = [];
|
const totalShieldsSDps = sumSDps(shieldsSDpsObject);
|
||||||
shieldsSDpsData.push({ value: Math.round(absoluteShieldsSDps), label: translate('absolute') });
|
const totalShieldsSDpsTooltipDetails = getSDpsTooltip(translate, formats, shieldsSDpsObject);
|
||||||
shieldsSDpsData.push({ value: Math.round(explosiveShieldsSDps), label: translate('explosive') });
|
const shieldsSDpsData = getSDpsData(translate, shieldsSDpsObject);
|
||||||
shieldsSDpsData.push({ value: Math.round(kineticShieldsSDps), label: translate('kinetic') });
|
|
||||||
shieldsSDpsData.push({ value: Math.round(thermalShieldsSDps), label: translate('thermal') });
|
|
||||||
|
|
||||||
const armourSDpsData = [];
|
const totalArmourSDps = sumSDps(armourSDpsObject);
|
||||||
armourSDpsData.push({ value: Math.round(absoluteArmourSDps), label: translate('absolute') });
|
const totalArmourSDpsTooltipDetails = getSDpsTooltip(translate, formats, armourSDpsObject);
|
||||||
armourSDpsData.push({ value: Math.round(explosiveArmourSDps), label: translate('explosive') });
|
const armourSDpsData = getSDpsData(translate, armourSDpsObject);
|
||||||
armourSDpsData.push({ value: Math.round(kineticArmourSDps), label: translate('kinetic') });
|
|
||||||
armourSDpsData.push({ value: Math.round(thermalArmourSDps), label: translate('thermal') });
|
|
||||||
|
|
||||||
const timeToDepleteShields = Calc.timeToDeplete(opponentShields.total, totalShieldsSDps, totalSEps, pd.getWeaponsCapacity(), pd.getWeaponsRechargeRate() * (wep / 4));
|
const timeToDepleteShields = Calc.timeToDeplete(opponentShields.total, totalShieldsSDps, totalSEps, pd.getWeaponsCapacity(), pd.getWeaponsRechargeRate() * (wep / 4));
|
||||||
const timeToDepleteArmour = Calc.timeToDeplete(opponentArmour.total, totalArmourSDps, totalSEps, pd.getWeaponsCapacity(), pd.getWeaponsRechargeRate() * (wep / 4));
|
const timeToDepleteArmour = Calc.timeToDeplete(opponentArmour.total, totalArmourSDps, totalSEps, pd.getWeaponsCapacity(), pd.getWeaponsRechargeRate() * (wep / 4));
|
||||||
@@ -227,24 +266,36 @@ export default class Offence extends TranslatedComponent {
|
|||||||
return (
|
return (
|
||||||
<span id='offence'>
|
<span id='offence'>
|
||||||
<div className='group full'>
|
<div className='group full'>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr className='main'>
|
<tr className='main'>
|
||||||
<th rowSpan='2' className='sortable' onClick={sortOrder.bind(this, 'n')}>{translate('weapon')}</th>
|
<th rowSpan='2' className='sortable' onClick={sortOrder.bind(this, 'n')}>{translate('weapon')}</th>
|
||||||
<th colSpan='2'>{translate('opponent\'s shields')}</th>
|
<th colSpan='1'>{translate('overall')}</th>
|
||||||
<th colSpan='2'>{translate('opponent\'s armour')}</th>
|
<th colSpan='2'>{translate('opponent\'s shields')}</th>
|
||||||
</tr>
|
<th colSpan='2'>{translate('opponent\'s armour')}</th>
|
||||||
<tr>
|
</tr>
|
||||||
<th className='lft sortable' onMouseOver={termtip.bind(null, 'TT_EFFECTIVE_SDPS_SHIELDS')} onMouseOut={tooltip.bind(null, null)} onClick={sortOrder.bind(this, 'esdpss')}>{'sdps'}</th>
|
<tr>
|
||||||
<th className='sortable' onMouseOver={termtip.bind(null, 'TT_EFFECTIVENESS_SHIELDS')} onMouseOut={tooltip.bind(null, null)}onClick={sortOrder.bind(this, 'es')}>{'eft'}</th>
|
<th className='lft sortable' onMouseOver={termtip.bind(null, 'TT_EFFECTIVE_SDPS_SHIELDS')} onMouseOut={tooltip.bind(null, null)} onClick={sortOrder.bind(this, 'esdpss')}>{'sdps'}</th>
|
||||||
<th className='lft sortable' onMouseOver={termtip.bind(null, 'TT_EFFECTIVE_SDPS_ARMOUR')} onMouseOut={tooltip.bind(null, null)}onClick={sortOrder.bind(this, 'esdpsh')}>{'sdps'}</th>
|
<th className='lft sortable' onMouseOver={termtip.bind(null, 'TT_EFFECTIVE_SDPS_SHIELDS')} onMouseOut={tooltip.bind(null, null)} onClick={sortOrder.bind(this, 'esdpss')}>{'sdps'}</th>
|
||||||
<th className='sortable' onMouseOver={termtip.bind(null, 'TT_EFFECTIVENESS_ARMOUR')} onMouseOut={tooltip.bind(null, null)}onClick={sortOrder.bind(this, 'eh')}>{'eft'}</th>
|
<th className='sortable' onMouseOver={termtip.bind(null, 'TT_EFFECTIVENESS_SHIELDS')} onMouseOut={tooltip.bind(null, null)}onClick={sortOrder.bind(this, 'es')}>{'eft'}</th>
|
||||||
</tr>
|
<th className='lft sortable' onMouseOver={termtip.bind(null, 'TT_EFFECTIVE_SDPS_ARMOUR')} onMouseOut={tooltip.bind(null, null)}onClick={sortOrder.bind(this, 'esdpsh')}>{'sdps'}</th>
|
||||||
</thead>
|
<th className='sortable' onMouseOver={termtip.bind(null, 'TT_EFFECTIVENESS_ARMOUR')} onMouseOut={tooltip.bind(null, null)}onClick={sortOrder.bind(this, 'eh')}>{'eft'}</th>
|
||||||
<tbody>
|
</tr>
|
||||||
{rows}
|
</thead>
|
||||||
</tbody>
|
<tbody>
|
||||||
</table>
|
{rows}
|
||||||
|
{rows.length > 0 &&
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td className='ri'><span onMouseOver={termtip.bind(null, totalSDpsTooltipDetails)} onMouseOut={tooltip.bind(null, null)}>={formats.f1(totalSDps)}</span></td>
|
||||||
|
<td className='ri'><span onMouseOver={termtip.bind(null, totalShieldsSDpsTooltipDetails)} onMouseOut={tooltip.bind(null, null)}>={formats.f1(totalShieldsSDps)}</span></td>
|
||||||
|
<td></td>
|
||||||
|
<td className='ri'><span onMouseOver={termtip.bind(null, totalArmourSDpsTooltipDetails)} onMouseOut={tooltip.bind(null, null)}>={formats.f1(totalArmourSDps)}</span></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div className='group quarter'>
|
<div className='group quarter'>
|
||||||
<h2>{translate('offence metrics')}</h2>
|
<h2>{translate('offence metrics')}</h2>
|
||||||
@@ -254,6 +305,10 @@ export default class Offence extends TranslatedComponent {
|
|||||||
<h2 onMouseOver={termtip.bind(null, translate('TT_EFFECTIVE_SDPS_ARMOUR'))} onMouseOut={tooltip.bind(null, null)}>{translate('PHRASE_EFFECTIVE_SDPS_ARMOUR')}<br/>{formats.f1(totalArmourSDps)}</h2>
|
<h2 onMouseOver={termtip.bind(null, translate('TT_EFFECTIVE_SDPS_ARMOUR'))} onMouseOut={tooltip.bind(null, null)}>{translate('PHRASE_EFFECTIVE_SDPS_ARMOUR')}<br/>{formats.f1(totalArmourSDps)}</h2>
|
||||||
<h2 onMouseOver={termtip.bind(null, translate('TT_TIME_TO_REMOVE_ARMOUR'))} onMouseOut={tooltip.bind(null, null)}>{translate('PHRASE_TIME_TO_REMOVE_ARMOUR')}<br/>{timeToDepleteArmour === Infinity ? translate('never') : formats.time(timeToDepleteArmour)}</h2>
|
<h2 onMouseOver={termtip.bind(null, translate('TT_TIME_TO_REMOVE_ARMOUR'))} onMouseOut={tooltip.bind(null, null)}>{translate('PHRASE_TIME_TO_REMOVE_ARMOUR')}<br/>{timeToDepleteArmour === Infinity ? translate('never') : formats.time(timeToDepleteArmour)}</h2>
|
||||||
</div>
|
</div>
|
||||||
|
<div className='group quarter'>
|
||||||
|
<h2 onMouseOver={termtip.bind(null, translate('PHRASE_OVERALL_DAMAGE'))} onMouseOut={tooltip.bind(null, null)}>{translate('overall damage')}</h2>
|
||||||
|
<PieChart data={totalSDpsData} />
|
||||||
|
</div>
|
||||||
<div className='group quarter'>
|
<div className='group quarter'>
|
||||||
<h2 onMouseOver={termtip.bind(null, translate('PHRASE_SHIELD_DAMAGE'))} onMouseOut={tooltip.bind(null, null)}>{translate('shield damage sources')}</h2>
|
<h2 onMouseOver={termtip.bind(null, translate('PHRASE_SHIELD_DAMAGE'))} onMouseOut={tooltip.bind(null, null)}>{translate('shield damage sources')}</h2>
|
||||||
<PieChart data={shieldsSDpsData} />
|
<PieChart data={shieldsSDpsData} />
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import cn from 'classnames';
|
import cn from 'classnames';
|
||||||
import { Ships } from 'coriolis-data/dist';
|
|
||||||
import Ship from '../shipyard/Ship';
|
|
||||||
import Persist from '../stores/Persist';
|
import Persist from '../stores/Persist';
|
||||||
import TranslatedComponent from './TranslatedComponent';
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
import PowerManagement from './PowerManagement';
|
import PowerManagement from './PowerManagement';
|
||||||
@@ -176,7 +174,7 @@ export default class OutfittingSubpages extends TranslatedComponent {
|
|||||||
<th style={{ width:'25%' }} className={cn({ active: tab == 'power' })} onClick={this._showTab.bind(this, 'power')} >{translate('power and costs')}</th>
|
<th style={{ width:'25%' }} className={cn({ active: tab == 'power' })} onClick={this._showTab.bind(this, 'power')} >{translate('power and costs')}</th>
|
||||||
<th style={{ width:'25%' }} className={cn({ active: tab == 'profiles' })} onClick={this._showTab.bind(this, 'profiles')} >{translate('profiles')}</th>
|
<th style={{ width:'25%' }} className={cn({ active: tab == 'profiles' })} onClick={this._showTab.bind(this, 'profiles')} >{translate('profiles')}</th>
|
||||||
<th style={{ width:'25%' }} className={cn({ active: tab == 'offence' })} onClick={this._showTab.bind(this, 'offence')} >{translate('offence')}</th>
|
<th style={{ width:'25%' }} className={cn({ active: tab == 'offence' })} onClick={this._showTab.bind(this, 'offence')} >{translate('offence')}</th>
|
||||||
<th style={{ width:'25%' }} className={cn({ active: tab == 'defence' })} onClick={this._showTab.bind(this, 'defence')} >{translate('defence')}</th>
|
<th style={{ width:'25%' }} className={cn({ active: tab == 'defence' })} onClick={this._showTab.bind(this, 'defence')} >{translate('tab_defence')}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -1,97 +1,92 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import Measure from 'react-measure';
|
import ContainerDimensions from 'react-container-dimensions';
|
||||||
import * as d3 from 'd3';
|
import * as d3 from 'd3';
|
||||||
|
|
||||||
const CORIOLIS_COLOURS = ['#FF8C0D', '#1FB0FF', '#71A052', '#D5D54D'];
|
const CORIOLIS_COLOURS = ['#FF8C0D', '#1FB0FF', '#71A052', '#D5D54D'];
|
||||||
const LABEL_COLOUR = '#000000';
|
const LABEL_COLOUR = '#000000';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A pie chart
|
* A pie chart
|
||||||
*/
|
*/
|
||||||
export default class PieChart extends Component {
|
export default class PieChart extends Component {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
data : PropTypes.array.isRequired
|
data : PropTypes.array.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
* @param {Object} props React Component properties
|
* @param {Object} props React Component properties
|
||||||
* @param {Object} context React Component context
|
* @param {Object} context React Component context
|
||||||
*/
|
*/
|
||||||
constructor(props, context) {
|
constructor(props, context) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.pie = d3.pie().value((d) => d.value);
|
this.pie = d3.pie().value((d) => d.value);
|
||||||
this.colors = CORIOLIS_COLOURS;
|
this.colors = CORIOLIS_COLOURS;
|
||||||
this.arc = d3.arc();
|
this.arc = d3.arc();
|
||||||
this.arc.innerRadius(0);
|
this.arc.innerRadius(0);
|
||||||
|
}
|
||||||
this.state = {
|
|
||||||
dimensions: {
|
|
||||||
width: 100,
|
/**
|
||||||
height: 100
|
* Generate a slice of the pie chart
|
||||||
}
|
* @param {Object} d the data for this slice
|
||||||
};
|
* @param {number} i the index of this slice
|
||||||
}
|
* @param {number} width the current width of the parent container
|
||||||
|
* @returns {Object} the SVG for the slice
|
||||||
|
*/
|
||||||
/**
|
sliceGenerator(d, i, width) {
|
||||||
* Generate a slice of the pie chart
|
if (!d || d.value == 0) {
|
||||||
* @param {Object} d the data for this slice
|
// Ignore 0 values
|
||||||
* @param {number} i the index of this slice
|
return null;
|
||||||
* @returns {Object} the SVG for the slice
|
}
|
||||||
*/
|
|
||||||
sliceGenerator(d, i) {
|
const { data } = this.props;
|
||||||
if (!d || d.value == 0) {
|
|
||||||
// Ignore 0 values
|
// Push the labels further out from the centre of the slice
|
||||||
return null;
|
let [labelX, labelY] = this.arc.centroid(d);
|
||||||
}
|
const labelTranslate = `translate(${labelX * 1.5}, ${labelY * 1.5})`;
|
||||||
|
|
||||||
const { width, height } = this.state.dimensions;
|
// Put the keys in a line with equal spacing
|
||||||
const { data } = this.props;
|
const nonZeroItems = data.filter(d => d.value != 0).length;
|
||||||
|
const thisItemIndex = data.slice(0, i + 1).filter(d => d.value != 0).length - 1;
|
||||||
// Push the labels further out from the centre of the slice
|
const keyX = -width / 2 + (width / nonZeroItems) * (thisItemIndex + 0.5);
|
||||||
let [labelX, labelY] = this.arc.centroid(d);
|
const keyTranslate = `translate(${keyX}, ${width * 0.45})`;
|
||||||
const labelTranslate = `translate(${labelX * 1.5}, ${labelY * 1.5})`;
|
|
||||||
|
return (
|
||||||
// Put the keys in a line with equal spacing
|
<g key={`group-${i}`}>
|
||||||
const nonZeroItems = data.filter(d => d.value != 0).length;
|
<path key={`arc-${i}`} d={this.arc(d)} style={{ fill: this.colors[i] }} />
|
||||||
const thisItemIndex = data.slice(0, i + 1).filter(d => d.value != 0).length - 1;
|
<text key={`label-${i}`} transform={labelTranslate} style={{ strokeWidth: '0px', fill: LABEL_COLOUR }} textAnchor='middle'>{d.value}</text>
|
||||||
const keyX = -width / 2 + (width / nonZeroItems) * (thisItemIndex + 0.5);
|
<text key={`key-${i}`} transform={keyTranslate} style={{ strokeWidth:'0px', fill: this.colors[i] }} textAnchor='middle'>{d.data.label}</text>
|
||||||
const keyTranslate = `translate(${keyX}, ${width * 0.45})`;
|
</g>
|
||||||
|
);
|
||||||
return (
|
}
|
||||||
<g key={`group-${i}`}>
|
|
||||||
<path key={`arc-${i}`} d={this.arc(d)} style={{ fill: this.colors[i] }} />
|
/**
|
||||||
<text key={`label-${i}`} transform={labelTranslate} style={{ strokeWidth: '0px', fill: LABEL_COLOUR }} textAnchor='middle'>{d.value}</text>
|
* Render the component
|
||||||
<text key={`key-${i}`} transform={keyTranslate} style={{ strokeWidth:'0px', fill: this.colors[i] }} textAnchor='middle'>{d.data.label}</text>
|
* @returns {object} Markup
|
||||||
</g>
|
*/
|
||||||
);
|
render() {
|
||||||
}
|
return (
|
||||||
|
<ContainerDimensions>
|
||||||
/**
|
{ ({ width }) => {
|
||||||
* Render the component
|
const pie = this.pie(this.props.data),
|
||||||
* @returns {object} Markup
|
translate = `translate(${width / 2}, ${width * 0.4})`;
|
||||||
*/
|
|
||||||
render() {
|
this.arc.outerRadius(width * 0.4);
|
||||||
const { width, height } = this.state.dimensions;
|
return (
|
||||||
const pie = this.pie(this.props.data),
|
<div width={width} height={width}>
|
||||||
translate = `translate(${width / 2}, ${width * 0.4})`;
|
<svg style={{ stroke: 'None' }} width={width} height={width * 0.9}>
|
||||||
|
<g transform={translate}>
|
||||||
this.arc.outerRadius(width * 0.4);
|
{pie.map((d, i) => this.sliceGenerator(d, i, width))}
|
||||||
|
</g>
|
||||||
return (
|
</svg>
|
||||||
<Measure width='100%' whitelist={['width', 'top']} onMeasure={ (dimensions) => { this.setState({ dimensions }); }}>
|
</div>
|
||||||
<div width={width} height={width}>
|
);
|
||||||
<svg style={{ stroke: 'None' }} width={width} height={width * 0.9}>
|
}}
|
||||||
<g transform={translate}>
|
</ContainerDimensions>
|
||||||
{pie.map((d, i) => this.sliceGenerator(d, i))}
|
);
|
||||||
</g>
|
}
|
||||||
</svg>
|
}
|
||||||
</div>
|
|
||||||
</Measure>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,13 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import TranslatedComponent from './TranslatedComponent';
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
import { Ships } from 'coriolis-data/dist';
|
|
||||||
import { nameComparator } from '../utils/SlotFunctions';
|
|
||||||
import { Pip } from './SvgIcons';
|
import { Pip } from './SvgIcons';
|
||||||
import LineChart from '../components/LineChart';
|
import { autoBind } from 'react-extras';
|
||||||
import Slider from '../components/Slider';
|
|
||||||
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
|
||||||
import Module from '../shipyard/Module';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pips displays SYS/ENG/WEP pips and allows users to change them with key presses by clicking on the relevant area.
|
* Pips displays SYS/ENG/WEP pips and allows users to change them with key presses by clicking on the relevant area.
|
||||||
@@ -18,6 +13,9 @@ export default class Pips extends TranslatedComponent {
|
|||||||
sys: PropTypes.number.isRequired,
|
sys: PropTypes.number.isRequired,
|
||||||
eng: PropTypes.number.isRequired,
|
eng: PropTypes.number.isRequired,
|
||||||
wep: PropTypes.number.isRequired,
|
wep: PropTypes.number.isRequired,
|
||||||
|
mcSys: PropTypes.number.isRequired,
|
||||||
|
mcEng: PropTypes.number.isRequired,
|
||||||
|
mcWep: PropTypes.number.isRequired,
|
||||||
onChange: PropTypes.func.isRequired
|
onChange: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -28,9 +26,7 @@ export default class Pips extends TranslatedComponent {
|
|||||||
*/
|
*/
|
||||||
constructor(props, context) {
|
constructor(props, context) {
|
||||||
super(props);
|
super(props);
|
||||||
const { sys, eng, wep } = props;
|
autoBind(this);
|
||||||
|
|
||||||
this._keyDown = this._keyDown.bind(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -74,30 +70,21 @@ export default class Pips extends TranslatedComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle a click
|
|
||||||
* @param {string} which Which item was clicked
|
|
||||||
*/
|
|
||||||
onClick(which) {
|
|
||||||
if (which == 'SYS') {
|
|
||||||
this._incSys();
|
|
||||||
} else if (which == 'ENG') {
|
|
||||||
this._incEng();
|
|
||||||
} else if (which == 'WEP') {
|
|
||||||
this._incWep();
|
|
||||||
} else if (which == 'RST') {
|
|
||||||
this._reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset the capacitor
|
* Reset the capacitor
|
||||||
*/
|
*/
|
||||||
_reset() {
|
_reset(isMc) {
|
||||||
let { sys, eng, wep } = this.props;
|
let { sys, eng, wep, mcSys, mcEng, mcWep } = this.props;
|
||||||
if (sys != 2 || eng != 2 || wep != 2) {
|
if (isMc) {
|
||||||
|
if (mcSys || mcEng || mcWep) {
|
||||||
|
sys -= mcSys;
|
||||||
|
eng -= mcEng;
|
||||||
|
wep -= mcWep;
|
||||||
|
this.props.onChange(sys, eng, wep, 0, 0, 0);
|
||||||
|
}
|
||||||
|
} else if (sys != 2 || eng != 2 || wep != 2) {
|
||||||
sys = eng = wep = 2;
|
sys = eng = wep = 2;
|
||||||
this.props.onChange(sys, eng, wep);
|
this.props.onChange(sys + mcSys, eng + mcEng, wep + mcWep, mcSys, mcEng, mcWep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,151 +92,133 @@ export default class Pips extends TranslatedComponent {
|
|||||||
* Increment the SYS capacitor
|
* Increment the SYS capacitor
|
||||||
*/
|
*/
|
||||||
_incSys() {
|
_incSys() {
|
||||||
let { sys, eng, wep } = this.props;
|
this._inc('sys', false);
|
||||||
|
|
||||||
const required = Math.min(1, 4 - sys);
|
|
||||||
if (required > 0) {
|
|
||||||
if (required == 0.5) {
|
|
||||||
// Take from whichever is larger
|
|
||||||
if (eng > wep) {
|
|
||||||
eng -= 0.5;
|
|
||||||
sys += 0.5;
|
|
||||||
} else {
|
|
||||||
wep -= 0.5;
|
|
||||||
sys += 0.5;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Required is 1 - take from both if possible
|
|
||||||
if (eng == 0) {
|
|
||||||
wep -= 1;
|
|
||||||
sys += 1;
|
|
||||||
} else if (wep == 0) {
|
|
||||||
eng -= 1;
|
|
||||||
sys += 1;
|
|
||||||
} else {
|
|
||||||
eng -= 0.5;
|
|
||||||
wep -= 0.5;
|
|
||||||
sys += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.props.onChange(sys, eng, wep);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Increment the ENG capacitor
|
* Increment the ENG capacitor
|
||||||
*/
|
*/
|
||||||
_incEng() {
|
_incEng() {
|
||||||
let { sys, eng, wep } = this.props;
|
this._inc('eng', false);
|
||||||
|
|
||||||
const required = Math.min(1, 4 - eng);
|
|
||||||
if (required > 0) {
|
|
||||||
if (required == 0.5) {
|
|
||||||
// Take from whichever is larger
|
|
||||||
if (sys > wep) {
|
|
||||||
sys -= 0.5;
|
|
||||||
eng += 0.5;
|
|
||||||
} else {
|
|
||||||
wep -= 0.5;
|
|
||||||
eng += 0.5;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Required is 1 - take from both if possible
|
|
||||||
if (sys == 0) {
|
|
||||||
wep -= 1;
|
|
||||||
eng += 1;
|
|
||||||
} else if (wep == 0) {
|
|
||||||
sys -= 1;
|
|
||||||
eng += 1;
|
|
||||||
} else {
|
|
||||||
sys -= 0.5;
|
|
||||||
wep -= 0.5;
|
|
||||||
eng += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.props.onChange(sys, eng, wep);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Increment the WEP capacitor
|
* Increment the WEP capacitor
|
||||||
*/
|
*/
|
||||||
_incWep() {
|
_incWep() {
|
||||||
let { sys, eng, wep } = this.props;
|
this._inc('wep', false);
|
||||||
|
}
|
||||||
|
|
||||||
const required = Math.min(1, 4 - wep);
|
_wrapMcClick(key) {
|
||||||
if (required > 0) {
|
return (event) => {
|
||||||
if (required == 0.5) {
|
event.stopPropagation();
|
||||||
// Take from whichever is larger
|
event.preventDefault();
|
||||||
if (sys > eng) {
|
if (key == 'rst') {
|
||||||
sys -= 0.5;
|
this._reset(true);
|
||||||
wep += 0.5;
|
|
||||||
} else {
|
|
||||||
eng -= 0.5;
|
|
||||||
wep += 0.5;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Required is 1 - take from both if possible
|
this._inc(key, true);
|
||||||
if (sys == 0) {
|
}
|
||||||
eng -= 1;
|
};
|
||||||
wep += 1;
|
}
|
||||||
} else if (eng == 0) {
|
|
||||||
sys -= 1;
|
/**
|
||||||
wep += 1;
|
* Increases a given capacitor
|
||||||
|
* @param {String} key Pip name to increase (one of 'sys', 'eng', 'wep')
|
||||||
|
* @param {Boolean} isMc True when increase is by multi crew
|
||||||
|
*/
|
||||||
|
_inc(key, isMc) {
|
||||||
|
if (!['sys', 'eng', 'wep'].includes(key)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let { sys, eng, wep, mcSys, mcEng, mcWep } = this.props;
|
||||||
|
let mc = key == 'sys' ? mcSys : (key == 'eng' ? mcEng : mcWep);
|
||||||
|
let pips = this.props[key] - mc;
|
||||||
|
let other1 = key == 'sys' ? eng - mcEng : sys - mcSys;
|
||||||
|
let other2 = key == 'wep' ? eng - mcEng : wep - mcWep;
|
||||||
|
|
||||||
|
const required = Math.min(1, 4 - mc - pips);
|
||||||
|
if (isMc) {
|
||||||
|
// We can only set full pips in multi-crew also we can only set two pips
|
||||||
|
if (required > 0.5 && mcSys + mcEng + mcWep < 2) {
|
||||||
|
if (key == 'sys') {
|
||||||
|
mcSys += 1;
|
||||||
|
} else if (key == 'eng') {
|
||||||
|
mcEng += 1;
|
||||||
} else {
|
} else {
|
||||||
sys -= 0.5;
|
mcWep += 1;
|
||||||
eng -= 0.5;
|
|
||||||
wep += 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.props.onChange(sys, eng, wep);
|
} else if (required > 0) {
|
||||||
|
if (required == 0.5) {
|
||||||
|
// Take from whichever is larger
|
||||||
|
if (other1 > other2) {
|
||||||
|
other1 -= 0.5;
|
||||||
|
} else {
|
||||||
|
other2 -= 0.5;
|
||||||
|
}
|
||||||
|
pips += 0.5;
|
||||||
|
} else {
|
||||||
|
// Required is 1 - take from both if possible
|
||||||
|
if (other1 == 0) {
|
||||||
|
other2 -= 1;
|
||||||
|
} else if (other2 == 0) {
|
||||||
|
other1 -= 1;
|
||||||
|
} else {
|
||||||
|
other1 -= 0.5;
|
||||||
|
other2 -= 0.5;
|
||||||
|
}
|
||||||
|
pips += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sys = mcSys + (key == 'sys' ? pips : other1);
|
||||||
|
eng = mcEng + (key == 'eng' ? pips : (key == 'sys' ? other1 : other2));
|
||||||
|
wep = mcWep + (key == 'wep' ? pips : other2);
|
||||||
|
this.props.onChange(sys, eng, wep, mcSys, mcEng, mcWep);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set up the rendering for pips
|
* Set up the rendering for pips
|
||||||
* @param {int} sys the SYS pips
|
* @param {Number} sys the SYS pips
|
||||||
* @param {int} eng the ENG pips
|
* @param {Number} eng the ENG pips
|
||||||
* @param {int} wep the WEP pips
|
* @param {Number} wep the WEP pips
|
||||||
|
* @param {Number} mcSys SYS pips from multi-crew
|
||||||
|
* @param {Number} mcEng ENG pips from multi-crew
|
||||||
|
* @param {Number} mcWep WEP pips from multi-crew
|
||||||
* @returns {Object} Object containing the rendering for the pips
|
* @returns {Object} Object containing the rendering for the pips
|
||||||
*/
|
*/
|
||||||
_renderPips(sys, eng, wep) {
|
_renderPips(sys, eng, wep, mcSys, mcEng, mcWep) {
|
||||||
const pipsSvg = {};
|
const pipsSvg = {
|
||||||
|
SYS: [],
|
||||||
|
ENG: [],
|
||||||
|
WEP: [],
|
||||||
|
};
|
||||||
|
|
||||||
// SYS
|
// Multi-crew pipsSettings actually are included in the overall pip count therefore
|
||||||
pipsSvg['SYS'] = [];
|
// we can consider [0, sys - mcSys] as normal pipsSettings whilst [sys - mcSys, sys]
|
||||||
for (let i = 0; i < Math.floor(sys); i++) {
|
// are the multi-crew pipsSettings in what follows.
|
||||||
pipsSvg['SYS'].push(<Pip className='full' key={i} />);
|
|
||||||
}
|
|
||||||
if (sys > Math.floor(sys)) {
|
|
||||||
pipsSvg['SYS'].push(<Pip className='half' key={'half'} />);
|
|
||||||
}
|
|
||||||
for (let i = Math.floor(sys + 0.5); i < 4; i++) {
|
|
||||||
pipsSvg['SYS'].push(<Pip className='empty' key={i} />);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ENG
|
let pipsSettings = {
|
||||||
pipsSvg['ENG'] = [];
|
SYS: [sys, mcSys],
|
||||||
for (let i = 0; i < Math.floor(eng); i++) {
|
ENG: [eng, mcEng],
|
||||||
pipsSvg['ENG'].push(<Pip className='full' key={i} />);
|
WEP: [wep, mcWep],
|
||||||
}
|
};
|
||||||
if (eng > Math.floor(eng)) {
|
|
||||||
pipsSvg['ENG'].push(<Pip className='half' key={'half'} />);
|
|
||||||
}
|
|
||||||
for (let i = Math.floor(eng + 0.5); i < 4; i++) {
|
|
||||||
pipsSvg['ENG'].push(<Pip className='empty' key={i} />);
|
|
||||||
}
|
|
||||||
|
|
||||||
// WEP
|
for (let pipName in pipsSettings) {
|
||||||
pipsSvg['WEP'] = [];
|
let [pips, mcPips] = pipsSettings[pipName];
|
||||||
for (let i = 0; i < Math.floor(wep); i++) {
|
for (let i = 0; i < Math.floor(pips - mcPips); i++) {
|
||||||
pipsSvg['WEP'].push(<Pip className='full' key={i} />);
|
pipsSvg[pipName].push(<Pip key={i} className='full' />);
|
||||||
}
|
}
|
||||||
if (wep > Math.floor(wep)) {
|
if (pips > Math.floor(pips)) {
|
||||||
pipsSvg['WEP'].push(<Pip className='half' key={'half'} />);
|
pipsSvg[pipName].push(<Pip className='half' key={'half'} />);
|
||||||
}
|
}
|
||||||
for (let i = Math.floor(wep + 0.5); i < 4; i++) {
|
for (let i = pips - mcPips; i < Math.floor(pips); i++) {
|
||||||
pipsSvg['WEP'].push(<Pip className='empty' key={i} />);
|
pipsSvg[pipName].push(<Pip key={i} className='mc' />);
|
||||||
|
}
|
||||||
|
for (let i = Math.floor(pips + 0.5); i < 4; i++) {
|
||||||
|
pipsSvg[pipName].push(<Pip className='empty' key={i} />);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return pipsSvg;
|
return pipsSvg;
|
||||||
@@ -260,15 +229,11 @@ export default class Pips extends TranslatedComponent {
|
|||||||
* @return {React.Component} contents
|
* @return {React.Component} contents
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
|
const { tooltip, termtip } = this.context;
|
||||||
const { formats, translate, units } = this.context.language;
|
const { formats, translate, units } = this.context.language;
|
||||||
const { sys, eng, wep } = this.props;
|
const { sys, eng, wep, mcSys, mcEng, mcWep } = this.props;
|
||||||
|
|
||||||
const onSysClicked = this.onClick.bind(this, 'SYS');
|
const pipsSvg = this._renderPips(sys, eng, wep, mcSys, mcEng, mcWep);
|
||||||
const onEngClicked = this.onClick.bind(this, 'ENG');
|
|
||||||
const onWepClicked = this.onClick.bind(this, 'WEP');
|
|
||||||
const onRstClicked = this.onClick.bind(this, 'RST');
|
|
||||||
|
|
||||||
const pipsSvg = this._renderPips(sys, eng, wep);
|
|
||||||
return (
|
return (
|
||||||
<span id='pips'>
|
<span id='pips'>
|
||||||
<table>
|
<table>
|
||||||
@@ -276,20 +241,38 @@ export default class Pips extends TranslatedComponent {
|
|||||||
<tr>
|
<tr>
|
||||||
<td> </td>
|
<td> </td>
|
||||||
<td> </td>
|
<td> </td>
|
||||||
<td className='clickable' onClick={onEngClicked}>{pipsSvg['ENG']}</td>
|
<td className='clickable' onClick={() => this._inc('eng')}
|
||||||
|
onContextMenu={this._wrapMcClick('eng')}>{pipsSvg['ENG']}</td>
|
||||||
<td> </td>
|
<td> </td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td> </td>
|
<td> </td>
|
||||||
<td className='clickable' onClick={onSysClicked}>{pipsSvg['SYS']}</td>
|
<td className='clickable' onClick={this._incSys}
|
||||||
<td className='clickable' onClick={onEngClicked}>{translate('ENG')}</td>
|
onContextMenu={this._wrapMcClick('sys')}>{pipsSvg['SYS']}</td>
|
||||||
<td className='clickable' onClick={onWepClicked}>{pipsSvg['WEP']}</td>
|
<td className='clickable' onClick={this._incEng}
|
||||||
|
onContextMenu={this._wrapMcClick('eng')}>{translate('ENG')}</td>
|
||||||
|
<td className='clickable' onClick={this._incWep}
|
||||||
|
onContextMenu={this._wrapMcClick('wep')}>{pipsSvg['WEP']}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td> </td>
|
<td> </td>
|
||||||
<td className='clickable' onClick={onSysClicked}>{translate('SYS')}</td>
|
<td className='clickable' onClick={this._incSys}
|
||||||
<td className='clickable' onClick={onRstClicked}>{translate('RST')}</td>
|
onContextMenu={this._wrapMcClick('sys')}>{translate('SYS')}</td>
|
||||||
<td className='clickable' onClick={onWepClicked}>{translate('WEP')}</td>
|
<td className='clickable' onClick={this._reset.bind(this, false)}>
|
||||||
|
{translate('RST')}
|
||||||
|
</td>
|
||||||
|
<td className='clickable' onClick={this._incWep}
|
||||||
|
onContextMenu={this._wrapMcClick('wep')}>{translate('WEP')}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td> </td>
|
||||||
|
<td> </td>
|
||||||
|
<td className='clickable secondary' onClick={this._wrapMcClick('rst')}
|
||||||
|
onMouseEnter={termtip.bind(null, 'PHRASE_MULTI_CREW_CAPACITOR_POINTS')}
|
||||||
|
onMouseLeave={tooltip.bind(null, null)}>
|
||||||
|
{translate('RST')}
|
||||||
|
</td>
|
||||||
|
<td> </td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ function bandText(val, index, wattScale) {
|
|||||||
* Renders the SVG to simulate in-game power bands
|
* Renders the SVG to simulate in-game power bands
|
||||||
*/
|
*/
|
||||||
export default class PowerBands extends TranslatedComponent {
|
export default class PowerBands extends TranslatedComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
bands: PropTypes.array.isRequired,
|
bands: PropTypes.array.isRequired,
|
||||||
available: PropTypes.number.isRequired,
|
available: PropTypes.number.isRequired,
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import TranslatedComponent from './TranslatedComponent';
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
import Ship from '../shipyard/Ship';
|
|
||||||
import { Ships } from 'coriolis-data/dist';
|
import { Ships } from 'coriolis-data/dist';
|
||||||
import { Rocket } from './SvgIcons';
|
import { Rocket } from './SvgIcons';
|
||||||
import Persist from '../stores/Persist';
|
import Persist from '../stores/Persist';
|
||||||
|
|||||||
@@ -15,22 +15,34 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
|||||||
cargo: PropTypes.number.isRequired,
|
cargo: PropTypes.number.isRequired,
|
||||||
fuel: PropTypes.number.isRequired,
|
fuel: PropTypes.number.isRequired,
|
||||||
marker: PropTypes.string.isRequired,
|
marker: PropTypes.string.isRequired,
|
||||||
|
pips: PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ShipSummaryTable constructor
|
||||||
|
* @param {Object} props The props
|
||||||
|
*/
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.didContextChange = this.didContextChange.bind(this);
|
||||||
|
this.state = {
|
||||||
|
shieldColour: 'blue'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render the table
|
* Render the table
|
||||||
* @return {React.Component} Summary table
|
* @return {React.Component} Summary table
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
const { ship, cargo, fuel } = this.props;
|
const { ship, cargo, fuel, pips } = this.props;
|
||||||
let { language, tooltip, termtip } = this.context;
|
let { language, tooltip, termtip } = this.context;
|
||||||
let translate = language.translate;
|
let translate = language.translate;
|
||||||
let u = language.units;
|
let u = language.units;
|
||||||
let formats = language.formats;
|
let formats = language.formats;
|
||||||
let { time, int, round, f1, f2 } = formats;
|
let { time, int, round, f1, f2 } = formats;
|
||||||
let hide = tooltip.bind(null, null);
|
let hide = tooltip.bind(null, null);
|
||||||
|
const shieldGenerator = ship.findInternalByGroup('sg') || ship.findInternalByGroup('psg');
|
||||||
const shieldGenerator = ship.findInternalByGroup('sg');
|
|
||||||
const sgClassNames = cn({ warning: shieldGenerator && !ship.shield, muted: !shieldGenerator });
|
const sgClassNames = cn({ warning: shieldGenerator && !ship.shield, muted: !shieldGenerator });
|
||||||
const sgTooltip = shieldGenerator ? 'TT_SUMMARY_SHIELDS' : 'TT_SUMMARY_SHIELDS_NONFUNCTIONAL';
|
const sgTooltip = shieldGenerator ? 'TT_SUMMARY_SHIELDS' : 'TT_SUMMARY_SHIELDS_NONFUNCTIONAL';
|
||||||
const timeToDrain = Calc.timeToDrainWep(ship, 4);
|
const timeToDrain = Calc.timeToDrainWep(ship, 4);
|
||||||
@@ -38,64 +50,167 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
|||||||
const speedTooltip = canThrust ? 'TT_SUMMARY_SPEED' : 'TT_SUMMARY_SPEED_NONFUNCTIONAL';
|
const speedTooltip = canThrust ? 'TT_SUMMARY_SPEED' : 'TT_SUMMARY_SPEED_NONFUNCTIONAL';
|
||||||
const canBoost = ship.canBoost(cargo, ship.fuelCapacity);
|
const canBoost = ship.canBoost(cargo, ship.fuelCapacity);
|
||||||
const boostTooltip = canBoost ? 'TT_SUMMARY_BOOST' : canThrust ? 'TT_SUMMARY_BOOST_NONFUNCTIONAL' : 'TT_SUMMARY_SPEED_NONFUNCTIONAL';
|
const boostTooltip = canBoost ? 'TT_SUMMARY_BOOST' : canThrust ? 'TT_SUMMARY_BOOST_NONFUNCTIONAL' : 'TT_SUMMARY_SPEED_NONFUNCTIONAL';
|
||||||
|
const canJump = ship.getSlotStatus(ship.standard[2]) == 3;
|
||||||
|
const sgMetrics = Calc.shieldMetrics(ship, pips.sys);
|
||||||
|
const shipBoost = canBoost ? Calc.calcBoost(ship) : 'No Boost';
|
||||||
|
const restingHeat = Math.sqrt(((ship.standard[0].m.pgen * ship.standard[0].m.eff) / ship.heatCapacity) / 0.2);
|
||||||
|
const armourMetrics = Calc.armourMetrics(ship);
|
||||||
|
let shieldColour = 'blue';
|
||||||
|
if (shieldGenerator && shieldGenerator.m.grp === 'psg') {
|
||||||
|
shieldColour = 'green';
|
||||||
|
} else if (shieldGenerator && shieldGenerator.m.grp === 'bsg') {
|
||||||
|
shieldColour = 'purple';
|
||||||
|
}
|
||||||
|
this.state = {
|
||||||
|
shieldColour
|
||||||
|
};
|
||||||
return <div id='summary'>
|
return <div id='summary'>
|
||||||
<table id='summaryTable'>
|
<div style={{display: "table", width: "100%"}}>
|
||||||
<thead>
|
<div style={{display: "table-row"}}>
|
||||||
<tr className='main'>
|
<table className={'summaryTable'}>
|
||||||
<th rowSpan={2} className={ cn({ 'bg-warning-disabled': !canThrust }) }>{translate('speed')}</th>
|
<thead>
|
||||||
<th rowSpan={2} className={ cn({ 'bg-warning-disabled': !canBoost }) }>{translate('boost')}</th>
|
<tr className='main'>
|
||||||
<th colSpan={5}>{translate('jump range')}</th>
|
<th rowSpan={2} className={ cn({ 'bg-warning-disabled': !canThrust }) }>{translate('speed')}</th>
|
||||||
<th rowSpan={2}>{translate('shield')}</th>
|
<th rowSpan={2} className={ cn({ 'bg-warning-disabled': !canBoost }) }>{translate('boost')}</th>
|
||||||
<th rowSpan={2}>{translate('integrity')}</th>
|
<th colSpan={5} className={ cn({ 'bg-warning-disabled': !canJump }) }>{translate('jump range')}</th>
|
||||||
<th rowSpan={2}>{translate('DPS')}</th>
|
<th rowSpan={2}>{translate('shield')}</th>
|
||||||
<th rowSpan={2}>{translate('EPS')}</th>
|
<th rowSpan={2}>{translate('integrity')}</th>
|
||||||
<th rowSpan={2}>{translate('TTD')}</th>
|
<th rowSpan={2}>{translate('DPS')}</th>
|
||||||
{/* <th onMouseEnter={termtip.bind(null, 'heat per second')} onMouseLeave={hide} rowSpan={2}>{translate('HPS')}</th> */}
|
<th rowSpan={2}>{translate('EPS')}</th>
|
||||||
<th rowSpan={2}>{translate('cargo')}</th>
|
<th rowSpan={2}>{translate('TTD')}</th>
|
||||||
<th rowSpan={2}>{translate('fuel')}</th>
|
{/* <th onMouseEnter={termtip.bind(null, 'heat per second')} onMouseLeave={hide} rowSpan={2}>{translate('HPS')}</th> */}
|
||||||
<th colSpan={3}>{translate('mass')}</th>
|
<th rowSpan={2}>{translate('cargo')}</th>
|
||||||
<th onMouseEnter={termtip.bind(null, 'hull hardness', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('hrd')}</th>
|
<th rowSpan={2} onMouseEnter={termtip.bind(null, 'passenger capacity', { cap: 0 })} onMouseLeave={hide}>{translate('pax')}</th>
|
||||||
<th rowSpan={2}>{translate('crew')}</th>
|
<th rowSpan={2}>{translate('fuel')}</th>
|
||||||
<th onMouseEnter={termtip.bind(null, 'mass lock factor', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('MLF')}</th>
|
<th colSpan={3}>{translate('mass')}</th>
|
||||||
</tr>
|
<th onMouseEnter={termtip.bind(null, 'hull hardness', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('hrd')}</th>
|
||||||
<tr>
|
<th rowSpan={2}>{translate('crew')}</th>
|
||||||
<th className='lft'>{translate('max')}</th>
|
<th onMouseEnter={termtip.bind(null, 'mass lock factor', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('MLF')}</th>
|
||||||
<th>{translate('unladen')}</th>
|
<th onMouseEnter={termtip.bind(null, 'TT_SUMMARY_BOOST_INTERVAL', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('boost interval')}</th>
|
||||||
<th>{translate('laden')}</th>
|
<th rowSpan={2}>{translate('resting heat (Beta)')}</th>
|
||||||
<th>{translate('total unladen')}</th>
|
</tr>
|
||||||
<th>{translate('total laden')}</th>
|
<tr>
|
||||||
<th className='lft'>{translate('hull')}</th>
|
<th className={ cn({ 'lft': true, 'bg-warning-disabled': !canJump }) }>{translate('max')}</th>
|
||||||
<th>{translate('unladen')}</th>
|
<th className={ cn({ 'bg-warning-disabled': !canJump }) }>{translate('unladen')}</th>
|
||||||
<th>{translate('laden')}</th>
|
<th className={ cn({ 'bg-warning-disabled': !canJump }) }>{translate('laden')}</th>
|
||||||
</tr>
|
<th className={ cn({ 'bg-warning-disabled': !canJump }) }>{translate('total unladen')}</th>
|
||||||
</thead>
|
<th className={ cn({ 'bg-warning-disabled': !canJump }) }>{translate('total laden')}</th>
|
||||||
<tbody>
|
<th className='lft'>{translate('hull')}</th>
|
||||||
<tr>
|
<th>{translate('unladen')}</th>
|
||||||
<td onMouseEnter={termtip.bind(null, speedTooltip, { cap: 0 })} onMouseLeave={hide}>{ canThrust ? <span>{int(ship.calcSpeed(4, ship.fuelCapacity, 0, false))}{u['m/s']}</span> : <span className='warning'>0 <Warning/></span> }</td>
|
<th>{translate('laden')}</th>
|
||||||
<td onMouseEnter={termtip.bind(null, boostTooltip, { cap: 0 })} onMouseLeave={hide}>{ canBoost ? <span>{int(ship.calcSpeed(4, ship.fuelCapacity, 0, true))}{u['m/s']}</span> : <span className='warning'>0 <Warning/></span> }</td>
|
</tr>
|
||||||
<td><span onMouseEnter={termtip.bind(null, 'TT_SUMMARY_MAX_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{f2(Calc.jumpRange(ship.unladenMass + ship.standard[2].m.getMaxFuelPerJump(), ship.standard[2].m, ship.standard[2].m.getMaxFuelPerJump()))}{u.LY}</span></td>
|
</thead>
|
||||||
<td><span onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{f2(Calc.jumpRange(ship.unladenMass + ship.fuelCapacity, ship.standard[2].m, ship.fuelCapacity))}{u.LY}</span></td>
|
<tbody>
|
||||||
<td><span onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{f2(Calc.jumpRange(ship.unladenMass + ship.fuelCapacity + ship.cargoCapacity, ship.standard[2].m, ship.fuelCapacity))}{u.LY}</span></td>
|
<tr>
|
||||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_TOTAL_JUMP', { cap: 0 })} onMouseLeave={hide}>{f2(Calc.totalJumpRange(ship.unladenMass + ship.fuelCapacity, ship.standard[2].m, ship.fuelCapacity))}{u.LY}</td>
|
<td onMouseEnter={termtip.bind(null, speedTooltip, { cap: 0 })} onMouseLeave={hide}>{ canThrust ? <span>{int(ship.calcSpeed(4, ship.fuelCapacity, 0, false))}{u['m/s']}</span> : <span className='warning'>0 <Warning/></span> }</td>
|
||||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_TOTAL_JUMP', { cap: 0 })} onMouseLeave={hide}>{f2(Calc.totalJumpRange(ship.unladenMass + ship.fuelCapacity + ship.cargoCapacity, ship.standard[2].m, ship.fuelCapacity))}{u.LY}</td>
|
<td onMouseEnter={termtip.bind(null, boostTooltip, { cap: 0 })} onMouseLeave={hide}>{ canBoost ? <span>{int(ship.calcSpeed(4, ship.fuelCapacity, 0, true))}{u['m/s']}</span> : <span className='warning'>0 <Warning/></span> }</td>
|
||||||
<td className={sgClassNames} onMouseEnter={termtip.bind(null, sgTooltip, { cap: 0 })} onMouseLeave={hide}>{int(ship.shield)}{u.MJ}</td>
|
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_MAX_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{ canJump ? <span>{ f2(Calc.jumpRange(ship.unladenMass + ship.standard[2].m.getMaxFuelPerJump(), ship.standard[2].m, ship.standard[2].m.getMaxFuelPerJump(), ship))}{u.LY}</span> : <span className='warning'>0 <Warning/></span> }</td>
|
||||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_INTEGRITY', { cap: 0 })} onMouseLeave={hide}>{int(ship.armour)}</td>
|
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{ canJump ? <span>{f2(Calc.jumpRange(ship.unladenMass + ship.fuelCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</span> : <span className='warning'>0 <Warning/></span> }</td>
|
||||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_DPS', { cap: 0 })} onMouseLeave={hide}>{f1(ship.totalDps)}</td>
|
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{ canJump ? <span>{f2(Calc.jumpRange(ship.unladenMass + ship.fuelCapacity + ship.cargoCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</span> : <span className='warning'>0 <Warning/></span> }</td>
|
||||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_EPS', { cap: 0 })} onMouseLeave={hide}>{f1(ship.totalEps)}</td>
|
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_TOTAL_JUMP', { cap: 0 })} onMouseLeave={hide}>{ canJump ? <span>{f2(Calc.totalJumpRange(ship.unladenMass + ship.fuelCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</span> : <span className='warning'>0 <Warning/></span> }</td>
|
||||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_TTD', { cap: 0 })} onMouseLeave={hide}>{timeToDrain === Infinity ? '∞' : time(timeToDrain)}</td>
|
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_TOTAL_JUMP', { cap: 0 })} onMouseLeave={hide}>{ canJump ? <span>{f2(Calc.totalJumpRange(ship.unladenMass + ship.fuelCapacity + ship.cargoCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</span> : <span className='warning'>0 <Warning/></span> }</td>
|
||||||
{/* <td>{f1(ship.totalHps)}</td> */}
|
<td className={sgClassNames} onMouseEnter={termtip.bind(null, sgTooltip, { cap: 0 })} onMouseLeave={hide}>{int(ship.shield)}{u.MJ}</td>
|
||||||
<td>{round(ship.cargoCapacity)}{u.T}</td>
|
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_INTEGRITY', { cap: 0 })} onMouseLeave={hide}>{int(ship.armour)}</td>
|
||||||
<td>{round(ship.fuelCapacity)}{u.T}</td>
|
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_DPS', { cap: 0 })} onMouseLeave={hide}>{f1(ship.totalDps)}</td>
|
||||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_HULL_MASS', { cap: 0 })} onMouseLeave={hide}>{ship.hullMass}{u.T}</td>
|
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_EPS', { cap: 0 })} onMouseLeave={hide}>{f1(ship.totalEps)}</td>
|
||||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_MASS', { cap: 0 })} onMouseLeave={hide}>{int(ship.unladenMass)}{u.T}</td>
|
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_TTD', { cap: 0 })} onMouseLeave={hide}>{timeToDrain === Infinity ? '∞' : time(timeToDrain)}</td>
|
||||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_MASS', { cap: 0 })} onMouseLeave={hide}>{int(ship.ladenMass)}{u.T}</td>
|
{/* <td>{f1(ship.totalHps)}</td> */}
|
||||||
<td>{int(ship.hardness)}</td>
|
<td>{round(ship.cargoCapacity)}{u.T}</td>
|
||||||
<td>{ship.crew}</td>
|
<td>{ship.passengerCapacity}</td>
|
||||||
<td>{ship.masslock}</td>
|
<td>{round(ship.fuelCapacity)}{u.T}</td>
|
||||||
</tr>
|
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_HULL_MASS', { cap: 0 })} onMouseLeave={hide}>{ship.hullMass}{u.T}</td>
|
||||||
</tbody>
|
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_MASS', { cap: 0 })} onMouseLeave={hide}>{int(ship.unladenMass)}{u.T}</td>
|
||||||
</table>
|
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_MASS', { cap: 0 })} onMouseLeave={hide}>{int(ship.ladenMass)}{u.T}</td>
|
||||||
|
<td>{int(ship.hardness)}</td>
|
||||||
|
<td>{ship.crew}</td>
|
||||||
|
<td>{ship.masslock}</td>
|
||||||
|
<td>{shipBoost !== 'No Boost' ? formats.time(shipBoost) : 'No Boost'}</td>
|
||||||
|
<td>{formats.pct(restingHeat)}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<table className={'summaryTable'}>
|
||||||
|
<thead className={this.state.shieldColour}>
|
||||||
|
<tr>
|
||||||
|
<th rowSpan={2} onMouseEnter={termtip.bind(null, 'shield', { cap: 0 })} onMouseLeave={hide} className='lft'>{translate('shield')}</th>
|
||||||
|
<th colSpan={4} className='lft'>{translate('resistance')}</th>
|
||||||
|
|
||||||
|
<th colSpan={5} onMouseEnter={termtip.bind(null, 'TT_SUMMARY_SHIELDS_SCB', { cap: 0 })} onMouseLeave={hide} className='lft'>{`${translate('HP')}`}</th>
|
||||||
|
<th rowSpan={2} onMouseEnter={termtip.bind(null, 'PHRASE_SG_RECOVER', { cap: 0 })} onMouseLeave={hide} className='lft'>{translate('recovery')}</th>
|
||||||
|
<th rowSpan={2} onMouseEnter={termtip.bind(null, 'PHRASE_SG_RECHARGE', { cap: 0 })} onMouseLeave={hide} className='lft'>{translate('recharge')}</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>{`${translate('explosive')}`}</th>
|
||||||
|
<th>{`${translate('kinetic')}`}</th>
|
||||||
|
<th>{`${translate('thermal')}`}</th>
|
||||||
|
<th></th>
|
||||||
|
|
||||||
|
<th className={'bordered'}>{`${translate('absolute')}`}</th>
|
||||||
|
<th>{`${translate('explosive')}`}</th>
|
||||||
|
<th>{`${translate('kinetic')}`}</th>
|
||||||
|
<th>{`${translate('thermal')}`}</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>{translate(shieldGenerator && shieldGenerator.m.grp || 'No Shield')}</td>
|
||||||
|
<td>{formats.pct1(ship.shieldExplRes)}</td>
|
||||||
|
<td>{formats.pct1(ship.shieldKinRes)}</td>
|
||||||
|
<td>{formats.pct1(ship.shieldThermRes)}</td>
|
||||||
|
<td></td>
|
||||||
|
|
||||||
|
<td>{int(ship && sgMetrics.summary > 0 ? sgMetrics.summary : 0)}{u.MJ}</td>
|
||||||
|
<td>{int(ship && sgMetrics.summary > 0 ? sgMetrics.summary / sgMetrics.explosive.base : 0)}{u.MJ}</td>
|
||||||
|
<td>{int(ship && sgMetrics.summary ? sgMetrics.summary / sgMetrics.kinetic.base : 0)}{u.MJ}</td>
|
||||||
|
<td>{int(ship && sgMetrics.summary ? sgMetrics.summary / sgMetrics.thermal.base : 0)}{u.MJ}</td>
|
||||||
|
<td></td>
|
||||||
|
<td>{sgMetrics && sgMetrics.recover === Math.Inf ? translate('Never') : formats.time(sgMetrics.recover)}</td>
|
||||||
|
<td>{sgMetrics && sgMetrics.recharge === Math.Inf ? translate('Never') : formats.time(sgMetrics.recharge)}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th rowSpan={2} onMouseEnter={termtip.bind(null, 'armour', { cap: 0 })} onMouseLeave={hide} className='lft'>{translate('armour')}</th>
|
||||||
|
<th colSpan={4} className='lft'>{translate('resistance')}</th>
|
||||||
|
|
||||||
|
<th colSpan={5} onMouseEnter={termtip.bind(null, 'PHRASE_EFFECTIVE_ARMOUR', { cap: 0 })} onMouseLeave={hide} className='lft'>{`${translate('HP')}`}</th>
|
||||||
|
<th rowSpan={2} onMouseEnter={termtip.bind(null, 'TT_MODULE_ARMOUR', { cap: 0 })} onMouseLeave={hide} className='lft'>{translate('raw module armour')}</th>
|
||||||
|
<th rowSpan={2} onMouseEnter={termtip.bind(null, 'TT_MODULE_PROTECTION_INTERNAL', { cap: 0 })} onMouseLeave={hide} className='lft'>{translate('internal protection')}</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>{`${translate('explosive')}`}</th>
|
||||||
|
<th>{`${translate('kinetic')}`}</th>
|
||||||
|
<th>{`${translate('thermal')}`}</th>
|
||||||
|
<th>{`${translate('caustic')}`}</th>
|
||||||
|
|
||||||
|
<th className={'bordered'}>{`${translate('absolute')}`}</th>
|
||||||
|
<th>{`${translate('explosive')}`}</th>
|
||||||
|
<th>{`${translate('kinetic')}`}</th>
|
||||||
|
<th>{`${translate('thermal')}`}</th>
|
||||||
|
<th>{`${translate('caustic')}`}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>{translate(ship && ship.bulkheads && ship.bulkheads.m && ship.bulkheads.m.name || 'No Armour')}</td>
|
||||||
|
<td>{formats.pct1(ship.hullExplRes)}</td>
|
||||||
|
<td>{formats.pct1(ship.hullKinRes)}</td>
|
||||||
|
<td>{formats.pct1(ship.hullThermRes)}</td>
|
||||||
|
<td>{formats.pct1(ship.hullCausRes)}</td>
|
||||||
|
<td>{int(armourMetrics.total)}</td>
|
||||||
|
<td>{int(armourMetrics.total / armourMetrics.explosive.total)}</td>
|
||||||
|
<td>{int(armourMetrics.total/ armourMetrics.kinetic.total)}</td>
|
||||||
|
<td>{int(armourMetrics.total / armourMetrics.thermal.total)}</td>
|
||||||
|
<td>{int(armourMetrics.total/ armourMetrics.caustic.total)}</td>
|
||||||
|
<td>{int(armourMetrics.modulearmour)}</td>
|
||||||
|
<td>{int(armourMetrics.moduleprotection * 100) + '%'}</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,166 +1,386 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
const MARGIN_LR = 8; // Left/ Right margin
|
const MARGIN_LR = 8; // Left/ Right margin
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Horizontal Slider
|
* Horizontal Slider
|
||||||
*/
|
*/
|
||||||
export default class Slider extends React.Component {
|
export default class Slider extends React.Component {
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
axis: false,
|
axis: false,
|
||||||
min: 0,
|
min: 0,
|
||||||
max: 1,
|
max: 1,
|
||||||
scale: 1 // SVG render scale
|
scale: 1 // SVG render scale
|
||||||
};
|
};
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
axis: PropTypes.bool,
|
axis: PropTypes.bool,
|
||||||
axisUnit: PropTypes.string,
|
axisUnit: PropTypes.string,// units (T, M, etc.)
|
||||||
max: PropTypes.number,
|
max: PropTypes.number,
|
||||||
min: PropTypes.number,
|
min: PropTypes.number,
|
||||||
onChange: PropTypes.func.isRequired,
|
onChange: PropTypes.func.isRequired,// function which determins percent value
|
||||||
onResize: PropTypes.func,
|
onResize: PropTypes.func,
|
||||||
percent: PropTypes.number.isRequired,
|
percent: PropTypes.number.isRequired,// value of slider
|
||||||
scale: PropTypes.number
|
scale: PropTypes.number
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
* @param {Object} props React Component properties
|
* @param {Object} props React Component properties
|
||||||
*/
|
*/
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this._down = this._down.bind(this);
|
this._down = this._down.bind(this);
|
||||||
this._move = this._move.bind(this);
|
this._move = this._move.bind(this);
|
||||||
this._up = this._up.bind(this);
|
this._up = this._up.bind(this);
|
||||||
this._updatePercent = this._updatePercent.bind(this);
|
this._keyup = this._keyup.bind(this);
|
||||||
this._updateDimensions = this._updateDimensions.bind(this);
|
this._keydown = this._keydown.bind(this);
|
||||||
|
this._touchstart = this._touchstart.bind(this);
|
||||||
this.state = { width: 0 };
|
this._touchend = this._touchend.bind(this);
|
||||||
}
|
|
||||||
|
this._updatePercent = this._updatePercent.bind(this);
|
||||||
/**
|
this._updateDimensions = this._updateDimensions.bind(this);
|
||||||
* On Mouse/Touch down handler
|
|
||||||
* @param {SyntheticEvent} event Event
|
this.state = { width: 0 };
|
||||||
*/
|
}
|
||||||
_down(event) {
|
|
||||||
let rect = event.currentTarget.getBoundingClientRect();
|
/**
|
||||||
this.left = rect.left;
|
* On Mouse/Touch down handler
|
||||||
this.width = rect.width;
|
* @param {SyntheticEvent} event Event
|
||||||
this._move(event);
|
*/
|
||||||
}
|
_down(event) {
|
||||||
|
let rect = event.currentTarget.getBoundingClientRect();
|
||||||
/**
|
this.left = rect.left;
|
||||||
* Update the slider percentage on move
|
this.width = rect.width;
|
||||||
* @param {SyntheticEvent} event Event
|
this._move(event);
|
||||||
*/
|
this.touchStartTimer = setTimeout(() => this.sliderInputBox._setDisplay('block'), 1500);
|
||||||
_move(event) {
|
}
|
||||||
if(this.width !== null && this.left != null) {
|
|
||||||
let clientX = event.touches ? event.touches[0].clientX : event.clientX;
|
/**
|
||||||
event.preventDefault();
|
* Update the slider percentage on move
|
||||||
this._updatePercent(clientX - this.left, this.width);
|
* @param {SyntheticEvent} event Event
|
||||||
}
|
*/
|
||||||
}
|
_move(event) {
|
||||||
|
if(this.width !== null && this.left != null) {
|
||||||
/**
|
let clientX = event.touches ? event.touches[0].clientX : event.clientX;
|
||||||
* On Mouse/Touch up handler
|
event.preventDefault();
|
||||||
* @param {Event} event DOM Event
|
this._updatePercent(clientX - this.left, this.width);
|
||||||
*/
|
}
|
||||||
_up(event) {
|
}
|
||||||
event.preventDefault();
|
|
||||||
this.left = null;
|
/**
|
||||||
this.width = null;
|
* On Mouse/Touch up handler
|
||||||
}
|
* @param {Event} event DOM Event
|
||||||
|
*/
|
||||||
/**
|
_up(event) {
|
||||||
* Determine if the user is still dragging
|
this.sliderInputBox.sliderVal.focus();
|
||||||
* @param {SyntheticEvent} event Event
|
clearTimeout(this.touchStartTimer);
|
||||||
*/
|
event.preventDefault();
|
||||||
_enter(event) {
|
this.left = null;
|
||||||
if(event.buttons !== 1) {
|
this.width = null;
|
||||||
this.left = null;
|
}
|
||||||
this.width = null;
|
|
||||||
}
|
|
||||||
}
|
/**
|
||||||
|
* Key up handler for keyboard.
|
||||||
/**
|
* display the number field then set focus to it
|
||||||
* Update the slider percentage
|
* when "Enter" key is pressed
|
||||||
* @param {number} pos Slider drag position
|
* @param {Event} event Keyboard event
|
||||||
* @param {number} width Slider width
|
*/
|
||||||
* @param {Event} event DOM Event
|
_keyup(event) {
|
||||||
*/
|
switch (event.key) {
|
||||||
_updatePercent(pos, width) {
|
case 'Enter':
|
||||||
this.props.onChange(Math.min(Math.max(pos / width, 0), 1));
|
event.preventDefault();
|
||||||
}
|
this.sliderInputBox._setDisplay('block');
|
||||||
|
return;
|
||||||
/**
|
default:
|
||||||
* Update dimenions from rendered DOM
|
return;
|
||||||
*/
|
}
|
||||||
_updateDimensions() {
|
}
|
||||||
this.setState({
|
/**
|
||||||
outerWidth: this.node.getBoundingClientRect().width
|
* Key down handler
|
||||||
});
|
* increment slider position by +/- 1 when right/left arrow key is pressed or held
|
||||||
}
|
* @param {Event} event Keyboard even
|
||||||
|
*/
|
||||||
/**
|
_keydown(event) {
|
||||||
* Add listeners when about to mount
|
let newVal = this.props.percent * this.props.max;
|
||||||
*/
|
switch (event.key) {
|
||||||
componentWillMount() {
|
case 'ArrowRight':
|
||||||
if (this.props.onResize) {
|
newVal += 1;
|
||||||
this.resizeListener = this.props.onResize(this._updateDimensions);
|
if (newVal <= this.props.max) this.props.onChange(newVal / this.props.max);
|
||||||
}
|
return;
|
||||||
}
|
case 'ArrowLeft':
|
||||||
|
newVal -= 1;
|
||||||
/**
|
if (newVal >= 0) this.props.onChange(newVal / this.props.max);
|
||||||
* Trigger DOM updates on mount
|
return;
|
||||||
*/
|
default:
|
||||||
componentDidMount() {
|
return;
|
||||||
this._updateDimensions();
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove listeners on unmount
|
* Touch start handler
|
||||||
*/
|
* @param {Event} event DOM Event
|
||||||
componentWillUnmount() {
|
*
|
||||||
if (this.resizeListener) {
|
*/
|
||||||
this.resizeListener.remove();
|
_touchstart(event) {
|
||||||
}
|
this.touchStartTimer = setTimeout(() => this.sliderInputBox._setDisplay('block'), 1500);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render the slider
|
* Touch end handler
|
||||||
* @return {React.Component} The slider
|
* @param {Event} event DOM Event
|
||||||
*/
|
*
|
||||||
render() {
|
*/
|
||||||
let outerWidth = this.state.outerWidth;
|
_touchend(event) {
|
||||||
let { axis, axisUnit, min, max, scale } = this.props;
|
this.sliderInputBox.sliderVal.focus();
|
||||||
|
clearTimeout(this.touchStartTimer);
|
||||||
let style = {
|
}
|
||||||
width: '100%',
|
|
||||||
height: axis ? '2.5em' : '1.5em',
|
/**
|
||||||
boxSizing: 'border-box'
|
* Determine if the user is still dragging
|
||||||
};
|
* @param {SyntheticEvent} event Event
|
||||||
|
*/
|
||||||
if (!outerWidth) {
|
_enter(event) {
|
||||||
return <svg style={style} ref={node => this.node = node} />;
|
if(event.buttons !== 1) {
|
||||||
}
|
this.left = null;
|
||||||
|
this.width = null;
|
||||||
let margin = MARGIN_LR * scale;
|
}
|
||||||
let width = outerWidth - (margin * 2);
|
}
|
||||||
let pctPos = width * this.props.percent;
|
|
||||||
|
/**
|
||||||
return <svg onMouseUp={this._up} onMouseEnter={this._enter.bind(this)} onMouseMove={this._move} onTouchEnd={this._up} style={style} ref={node => this.node = node}>
|
* Update the slider percentage
|
||||||
<rect className='primary' style={{ opacity: 0.3 }} x={margin} y='0.25em' rx='0.3em' ry='0.3em' width={width} height='0.7em' />
|
* @param {number} pos Slider drag position
|
||||||
<rect className='primary-disabled' x={margin} y='0.45em' rx='0.15em' ry='0.15em' width={pctPos} height='0.3em' />
|
* @param {number} width Slider width
|
||||||
<circle className='primary' r={margin} cy='0.6em' cx={pctPos + margin} />
|
* @param {Event} event DOM Event
|
||||||
<rect x={margin} width={width} height='100%' fillOpacity='0' style={{ cursor: 'col-resize' }} onMouseDown={this._down} onTouchMove={this._move} onTouchStart={this._down} />
|
*/
|
||||||
{axis && <g style={{ fontSize: '.7em' }}>
|
_updatePercent(pos, width) {
|
||||||
<text className='primary-disabled' y='3em' x={margin} style={{ textAnchor: 'middle' }}>{min + axisUnit}</text>
|
this.props.onChange(Math.min(Math.max(pos / width, 0), 1));
|
||||||
<text className='primary-disabled' y='3em' x='50%' style={{ textAnchor: 'middle' }}>{(min + max / 2) + axisUnit}</text>
|
}
|
||||||
<text className='primary-disabled' y='3em' x='100%' style={{ textAnchor: 'end' }}>{max + axisUnit}</text>
|
|
||||||
</g>}
|
/**
|
||||||
</svg>;
|
* Update dimenions from rendered DOM
|
||||||
}
|
*/
|
||||||
}
|
_updateDimensions() {
|
||||||
|
this.setState({
|
||||||
|
outerWidth: this.node.getBoundingClientRect().width
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add listeners when about to mount
|
||||||
|
*/
|
||||||
|
componentWillMount() {
|
||||||
|
if (this.props.onResize) {
|
||||||
|
this.resizeListener = this.props.onResize(this._updateDimensions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trigger DOM updates on mount
|
||||||
|
*/
|
||||||
|
componentDidMount() {
|
||||||
|
this._updateDimensions();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove listeners on unmount
|
||||||
|
*/
|
||||||
|
componentWillUnmount() {
|
||||||
|
if (this.resizeListener) {
|
||||||
|
this.resizeListener.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the slider
|
||||||
|
* @return {React.Component} The slider
|
||||||
|
*/
|
||||||
|
render() {
|
||||||
|
let outerWidth = this.state.outerWidth;
|
||||||
|
let { axis, axisUnit, min, max, scale } = this.props;
|
||||||
|
let style = {
|
||||||
|
width: '100%',
|
||||||
|
height: axis ? '2.5em' : '1.5em',
|
||||||
|
boxSizing: 'border-box'
|
||||||
|
};
|
||||||
|
if (!outerWidth) {
|
||||||
|
return <svg style={style} ref={node => this.node = node} />;
|
||||||
|
}
|
||||||
|
let margin = MARGIN_LR * scale;
|
||||||
|
let width = outerWidth - (margin * 2);
|
||||||
|
let pctPos = width * this.props.percent;
|
||||||
|
return <div><svg
|
||||||
|
onMouseUp={this._up} onMouseEnter={this._enter.bind(this)} onMouseMove={this._move} onKeyUp={this._keyup} onKeyDown={this._keydown} style={style} ref={node => this.node = node} tabIndex="0">
|
||||||
|
<rect className='primary' style={{ opacity: 0.3 }} x={margin} y='0.25em' rx='0.3em' ry='0.3em' width={width} height='0.7em' />
|
||||||
|
<rect className='primary-disabled' x={margin} y='0.45em' rx='0.15em' ry='0.15em' width={pctPos} height='0.3em' />
|
||||||
|
<circle className='primary' r={margin} cy='0.6em' cx={pctPos + margin} />
|
||||||
|
<rect x={margin} width={width} height='100%' fillOpacity='0' style={{ cursor: 'col-resize' }} onMouseDown={this._down} onTouchMove={this._move} onTouchStart={this._down} onTouchEnd={this._touchend} />
|
||||||
|
{axis && <g style={{ fontSize: '.7em' }}>
|
||||||
|
<text className='primary-disabled' y='3em' x={margin} style={{ textAnchor: 'middle' }}>{min + axisUnit}</text>
|
||||||
|
<text className='primary-disabled' y='3em' x='50%' style={{ textAnchor: 'middle' }}>{(min + max / 2) + axisUnit}</text>
|
||||||
|
<text className='primary-disabled' y='3em' x='100%' style={{ textAnchor: 'end' }}>{max + axisUnit}</text>
|
||||||
|
</g>}
|
||||||
|
</svg>
|
||||||
|
<TextInputBox ref={(tb) => this.sliderInputBox = tb}
|
||||||
|
onChange={this.props.onChange}
|
||||||
|
percent={this.props.percent}
|
||||||
|
axisUnit={this.props.axisUnit}
|
||||||
|
scale={this.props.scale}
|
||||||
|
max={this.props.max}
|
||||||
|
/>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* New component to add keyboard support for sliders - works on all devices (desktop, iOS, Android)
|
||||||
|
**/
|
||||||
|
class TextInputBox extends React.Component {
|
||||||
|
static propTypes = {
|
||||||
|
axisUnit: PropTypes.string,// units (T, M, etc.)
|
||||||
|
max: PropTypes.number,
|
||||||
|
onChange: PropTypes.func.isRequired,// function which determins percent value
|
||||||
|
percent: PropTypes.number.isRequired,// value of slider
|
||||||
|
scale: PropTypes.number
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Determine if the user is still dragging
|
||||||
|
* @param {Object} props React Component properties
|
||||||
|
*/
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this._handleFocus = this._handleFocus.bind(this);
|
||||||
|
this._handleBlur = this._handleBlur.bind(this);
|
||||||
|
this._handleChange = this._handleChange.bind(this);
|
||||||
|
this._keyup = this._keyup.bind(this);
|
||||||
|
this.state = this._getInitialState();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Update input value if slider changes will change props/state
|
||||||
|
* @param {Object} nextProps React Component properites
|
||||||
|
* @param {Object} nextState React Component state values
|
||||||
|
*/
|
||||||
|
componentWillReceiveProps(nextProps, nextState) {
|
||||||
|
let nextValue = nextProps.percent * nextProps.max;
|
||||||
|
// See https://stackoverflow.com/questions/32414308/updating-state-on-props-change-in-react-form
|
||||||
|
if (nextValue !== this.state.inputValue && nextValue <= nextProps.max) {
|
||||||
|
this.setState({ inputValue: nextValue });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Update slider textbox visibility/values if changes are made to slider
|
||||||
|
* @param {Object} prevProps React Component properites
|
||||||
|
* @param {Object} prevState React Component state values
|
||||||
|
*/
|
||||||
|
componentDidUpdate(prevProps, prevState) {
|
||||||
|
if (prevState.divStyle.display == 'none' && this.state.divStyle.display == 'block') {
|
||||||
|
this.enterTimer = setTimeout(() => this.sliderVal.focus(), 10);
|
||||||
|
}
|
||||||
|
if (prevProps.max !== this.props.max && this.state.inputValue > this.props.max) {
|
||||||
|
// they chose a different module
|
||||||
|
this.setState({ inputValue: this.props.max });
|
||||||
|
}
|
||||||
|
if (this.state.inputValue != prevState.inputValue && prevProps.max == this.props.max) {
|
||||||
|
this.props.onChange(this.state.inputValue / this.props.max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Set initial state for the textbox.
|
||||||
|
* We may want to rethink this to
|
||||||
|
* try and make it a stateless component
|
||||||
|
* @returns {object} React state object with initial values set
|
||||||
|
*/
|
||||||
|
_getInitialState() {
|
||||||
|
return {
|
||||||
|
divStyle: { display:'none' },
|
||||||
|
inputStyle: { width:'4em' },
|
||||||
|
labelStyle: { marginLeft: '.1em' },
|
||||||
|
maxLength:5,
|
||||||
|
size:5,
|
||||||
|
min:0,
|
||||||
|
tabIndex:-1,
|
||||||
|
type:'number',
|
||||||
|
readOnly: true,
|
||||||
|
inputValue: this.props.percent * this.props.max
|
||||||
|
};
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {string} val block or none
|
||||||
|
*/
|
||||||
|
_setDisplay(val) {
|
||||||
|
this.setState({
|
||||||
|
divStyle: { display:val }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Update the input value
|
||||||
|
* when textbox gets focus
|
||||||
|
*/
|
||||||
|
_handleFocus() {
|
||||||
|
this.setState({
|
||||||
|
inputValue:this._getValue()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Update inputValue when textbox loses focus
|
||||||
|
*/
|
||||||
|
_handleBlur() {
|
||||||
|
this._setDisplay('none');
|
||||||
|
if (this.state.inputValue !== '') {
|
||||||
|
this.props.onChange(this.state.inputValue / this.props.max);
|
||||||
|
} else {
|
||||||
|
this.setState({
|
||||||
|
inputValue: this.props.percent * this.props.max
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get the value in the text box
|
||||||
|
* @returns {number} inputValue Value of the input box
|
||||||
|
*/
|
||||||
|
_getValue() {
|
||||||
|
return this.state.inputValue;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Update and set limits on input box
|
||||||
|
* values depending on what user
|
||||||
|
* has selected
|
||||||
|
*
|
||||||
|
* @param {SyntheticEvent} event ReactJs onChange event
|
||||||
|
*/
|
||||||
|
_handleChange(event) {
|
||||||
|
if (event.target.value < 0) {
|
||||||
|
this.setState({ inputValue: 0 });
|
||||||
|
} else if (event.target.value <= this.props.max) {
|
||||||
|
this.setState({ inputValue: event.target.value });
|
||||||
|
} else {
|
||||||
|
this.setState({ inputValue: this.props.max });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Key up handler for input field.
|
||||||
|
* If user hits Enter key, blur/close the input field
|
||||||
|
* @param {Event} event Keyboard event
|
||||||
|
*/
|
||||||
|
_keyup(event) {
|
||||||
|
switch (event.key) {
|
||||||
|
case 'Enter':
|
||||||
|
this.sliderVal.blur();
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get the value in the text box
|
||||||
|
* @return {React.Component} Text Input component for Slider
|
||||||
|
*/
|
||||||
|
render() {
|
||||||
|
let { axisUnit, onChange, percent, scale } = this.props;
|
||||||
|
return <div style={this.state.divStyle}><input style={this.state.inputStyle} value={this._getValue()} min={this.state.min} max={this.props.max} onChange={this._handleChange} onKeyUp={this._keyup} tabIndex={this.state.tabIndex} maxLength={this.state.maxLength} size={this.state.size} onBlur={() => {this._handleBlur();}} onFocus={() => {this._handleFocus();}} type={this.state.type} ref={(ip) => this.sliderVal = ip}/><text className="primary upp" style={this.state.labelStyle}>{this.props.axisUnit}</text></div>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import AvailableModulesMenu from './AvailableModulesMenu';
|
|||||||
import ModificationsMenu from './ModificationsMenu';
|
import ModificationsMenu from './ModificationsMenu';
|
||||||
import { diffDetails } from '../utils/SlotFunctions';
|
import { diffDetails } from '../utils/SlotFunctions';
|
||||||
import { wrapCtxMenu } from '../utils/UtilityFunctions';
|
import { wrapCtxMenu } from '../utils/UtilityFunctions';
|
||||||
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract Slot
|
* Abstract Slot
|
||||||
@@ -40,6 +39,8 @@ export default class Slot extends TranslatedComponent {
|
|||||||
|
|
||||||
this._contextMenu = wrapCtxMenu(this._contextMenu.bind(this));
|
this._contextMenu = wrapCtxMenu(this._contextMenu.bind(this));
|
||||||
this._getMaxClassLabel = this._getMaxClassLabel.bind(this);
|
this._getMaxClassLabel = this._getMaxClassLabel.bind(this);
|
||||||
|
this._keyDown = this._keyDown.bind(this);
|
||||||
|
this.slotDiv = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Must be implemented by subclasses:
|
// Must be implemented by subclasses:
|
||||||
@@ -73,6 +74,22 @@ export default class Slot extends TranslatedComponent {
|
|||||||
this.props.onSelect(null,null);
|
this.props.onSelect(null,null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Key Down handler
|
||||||
|
* @param {SyntheticEvent} event Event
|
||||||
|
* ToDo: see if this can be moved up
|
||||||
|
* we do more or less the same thing
|
||||||
|
* in every section when Enter key is pressed
|
||||||
|
* on a focusable item
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
_keyDown(event) {
|
||||||
|
if (event.key == 'Enter') {
|
||||||
|
if(event.target.className == 'r') {
|
||||||
|
this._toggleModifications();
|
||||||
|
}
|
||||||
|
this.props.onOpen(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Render the slot
|
* Render the slot
|
||||||
* @return {React.Component} The slot
|
* @return {React.Component} The slot
|
||||||
@@ -104,16 +121,18 @@ export default class Slot extends TranslatedComponent {
|
|||||||
ship={ship}
|
ship={ship}
|
||||||
m={m}
|
m={m}
|
||||||
marker={modificationsMarker}
|
marker={modificationsMarker}
|
||||||
|
modButton = {this.modButton}
|
||||||
/>;
|
/>;
|
||||||
} else {
|
} else {
|
||||||
menu = <AvailableModulesMenu
|
menu = <AvailableModulesMenu
|
||||||
className={this._getClassNames()}
|
className={this._getClassNames()}
|
||||||
modules={availableModules()}
|
modules={availableModules()}
|
||||||
shipMass={ship.hullMass}
|
|
||||||
m={m}
|
m={m}
|
||||||
|
ship={ship}
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
warning={warning}
|
warning={warning}
|
||||||
diffDetails={diffDetails.bind(ship, this.context.language)}
|
diffDetails={diffDetails.bind(ship, this.context.language)}
|
||||||
|
slotDiv = {this.slotDiv}
|
||||||
/>;
|
/>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -121,7 +140,7 @@ export default class Slot extends TranslatedComponent {
|
|||||||
// TODO: implement touch dragging
|
// TODO: implement touch dragging
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn('slot', dropClass, { selected })} onClick={onOpen} onContextMenu={this._contextMenu} onDragOver={dragOver}>
|
<div className={cn('slot', dropClass, { selected })} onClick={onOpen} onKeyDown={this._keyDown} onContextMenu={this._contextMenu} onDragOver={dragOver} tabIndex="0" ref={slotDiv => this.slotDiv = slotDiv}>
|
||||||
<div className='details-container'>
|
<div className='details-container'>
|
||||||
<div className='sz'>{this._getMaxClassLabel(translate)}</div>
|
<div className='sz'>{this._getMaxClassLabel(translate)}</div>
|
||||||
{slotDetails}
|
{slotDetails}
|
||||||
@@ -131,6 +150,7 @@ export default class Slot extends TranslatedComponent {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggle the modifications flag when selecting the modifications icon
|
* Toggle the modifications flag when selecting the modifications icon
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ export default class SlotSection extends TranslatedComponent {
|
|||||||
onCargoChange: PropTypes.func.isRequired,
|
onCargoChange: PropTypes.func.isRequired,
|
||||||
onFuelChange: PropTypes.func.isRequired,
|
onFuelChange: PropTypes.func.isRequired,
|
||||||
code: PropTypes.string.isRequired,
|
code: PropTypes.string.isRequired,
|
||||||
togglePwr: PropTypes.func
|
togglePwr: PropTypes.func,
|
||||||
|
sectionMenuRefs: PropTypes.object
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -32,7 +33,10 @@ export default class SlotSection extends TranslatedComponent {
|
|||||||
super(props);
|
super(props);
|
||||||
this.sectionId = sectionId;
|
this.sectionId = sectionId;
|
||||||
this.sectionName = sectionName;
|
this.sectionName = sectionName;
|
||||||
|
this.ssHeadRef = null;
|
||||||
|
|
||||||
|
this.sectionRefArr = this.props.sectionMenuRefs[this.sectionId] = [];
|
||||||
|
this.sectionRefArr['selectedRef'] = null;
|
||||||
this._getSlots = this._getSlots.bind(this);
|
this._getSlots = this._getSlots.bind(this);
|
||||||
this._selectModule = this._selectModule.bind(this);
|
this._selectModule = this._selectModule.bind(this);
|
||||||
this._getSectionMenu = this._getSectionMenu.bind(this);
|
this._getSectionMenu = this._getSectionMenu.bind(this);
|
||||||
@@ -40,6 +44,8 @@ export default class SlotSection extends TranslatedComponent {
|
|||||||
this._drop = this._drop.bind(this);
|
this._drop = this._drop.bind(this);
|
||||||
this._dragOverNone = this._dragOverNone.bind(this);
|
this._dragOverNone = this._dragOverNone.bind(this);
|
||||||
this._close = this._close.bind(this);
|
this._close = this._close.bind(this);
|
||||||
|
this._keyDown = this._keyDown.bind(this);
|
||||||
|
this._handleSectionFocus = this._handleSectionFocus.bind(this);
|
||||||
this.state = {};
|
this.state = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,7 +53,59 @@ export default class SlotSection extends TranslatedComponent {
|
|||||||
// _getSlots()
|
// _getSlots()
|
||||||
// _getSectionMenu()
|
// _getSectionMenu()
|
||||||
// _contextMenu()
|
// _contextMenu()
|
||||||
|
// componentDidUpdate(prevProps)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: May either need to send the function to be triggered when Enter key is pressed, or else
|
||||||
|
* may need a separate keyDown handler for each subclass (StandardSlotSection, HardpointSlotSection, etc.)
|
||||||
|
* ex: _keyDown(_keyDownfn, event)
|
||||||
|
*
|
||||||
|
* @param {SyntheticEvent} event KeyDown event
|
||||||
|
*/
|
||||||
|
_keyDown(event) {
|
||||||
|
if (event.key == 'Enter') {
|
||||||
|
event.stopPropagation();
|
||||||
|
if (event.currentTarget.nodeName === 'H1') {
|
||||||
|
this._openMenu(this.sectionName, event);
|
||||||
|
} else {
|
||||||
|
event.currentTarget.click();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.key == 'Tab') {
|
||||||
|
if (event.shiftKey) {
|
||||||
|
if ((event.currentTarget === this.sectionRefArr[this.firstRefId]) && this.sectionRefArr[this.lastRefId]) {
|
||||||
|
event.preventDefault();
|
||||||
|
this.sectionRefArr[this.lastRefId].focus();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ((event.currentTarget === this.sectionRefArr[this.lastRefId]) && this.sectionRefArr[this.firstRefId]) {
|
||||||
|
event.preventDefault();
|
||||||
|
this.sectionRefArr[this.firstRefId].focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set focus on appropriate Slot Section Menu element
|
||||||
|
* @param {Object} focusPrevProps prevProps for componentDidUpdate() from ...SlotSection.jsx
|
||||||
|
* @param {String} firstRef id of the first ref in ...SlotSection.jsx
|
||||||
|
* @param {String} lastRef id of the last ref in ...SlotSection.jsx
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
_handleSectionFocus(focusPrevProps, firstRef, lastRef) {
|
||||||
|
if (this.selectedRefId !== null && this.sectionRefArr[this.selectedRefId]) {
|
||||||
|
// set focus on the previously selected option for the currently open section menu
|
||||||
|
this.sectionRefArr[this.selectedRefId].focus();
|
||||||
|
} else if (this.sectionRefArr[firstRef] && this.sectionRefArr[firstRef] != null) {
|
||||||
|
// set focus on the first option in the currently open section menu if none have been selected previously
|
||||||
|
this.sectionRefArr[firstRef].focus();
|
||||||
|
} else if (this.props.currentMenu == null && focusPrevProps.currentMenu == this.sectionName && this.sectionRefArr['ssHeadRef']) {
|
||||||
|
// set focus on the section menu header when section menu is closed
|
||||||
|
this.sectionRefArr['ssHeadRef'].focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Open a menu
|
* Open a menu
|
||||||
* @param {string} menu Menu name
|
* @param {string} menu Menu name
|
||||||
@@ -137,6 +195,15 @@ export default class SlotSection extends TranslatedComponent {
|
|||||||
if (targetSlot && canMount(this.props.ship, targetSlot, m.grp, m.class)) {
|
if (targetSlot && canMount(this.props.ship, targetSlot, m.grp, m.class)) {
|
||||||
const mCopy = m.clone();
|
const mCopy = m.clone();
|
||||||
this.props.ship.use(targetSlot, mCopy, false);
|
this.props.ship.use(targetSlot, mCopy, false);
|
||||||
|
let experimentalNum = this.props.ship.hardpoints
|
||||||
|
.filter(s => s.m && s.m.experimental).length;
|
||||||
|
// Remove the module on the last slot if we now exceed the number of
|
||||||
|
// experimentals allowed
|
||||||
|
if (m.experimental && 4 < experimentalNum) {
|
||||||
|
this.props.ship.updateStats(originSlot, null, originSlot.m);
|
||||||
|
originSlot.m = null; // Empty the slot
|
||||||
|
originSlot.discountedCost = 0;
|
||||||
|
}
|
||||||
// Copy power info
|
// Copy power info
|
||||||
targetSlot.enabled = originSlot.enabled;
|
targetSlot.enabled = originSlot.enabled;
|
||||||
targetSlot.priority = originSlot.priority;
|
targetSlot.priority = originSlot.priority;
|
||||||
@@ -170,6 +237,18 @@ export default class SlotSection extends TranslatedComponent {
|
|||||||
targetSlot.priority = targetPriority;
|
targetSlot.priority = targetPriority;
|
||||||
}
|
}
|
||||||
this.props.onChange();
|
this.props.onChange();
|
||||||
|
this.props.ship
|
||||||
|
.updatePowerGenerated()
|
||||||
|
.updatePowerUsed()
|
||||||
|
.recalculateMass()
|
||||||
|
.updateJumpStats()
|
||||||
|
.recalculateShield()
|
||||||
|
.recalculateShieldCells()
|
||||||
|
.recalculateArmour()
|
||||||
|
.recalculateDps()
|
||||||
|
.recalculateEps()
|
||||||
|
.recalculateHps()
|
||||||
|
.updateMovement();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -225,7 +304,7 @@ export default class SlotSection extends TranslatedComponent {
|
|||||||
return (
|
return (
|
||||||
<div id={this.sectionId} className={'group'} onDragLeave={this._dragOverNone}>
|
<div id={this.sectionId} className={'group'} onDragLeave={this._dragOverNone}>
|
||||||
<div className={cn('section-menu', { selected: sectionMenuOpened })} onClick={open} onContextMenu={ctx}>
|
<div className={cn('section-menu', { selected: sectionMenuOpened })} onClick={open} onContextMenu={ctx}>
|
||||||
<h1>{translate(this.sectionName)} <Equalizer/></h1>
|
<h1 tabIndex="0" onKeyDown={this._keyDown} ref={ssHead => this.sectionRefArr['ssHeadRef'] = ssHead}>{translate(this.sectionName)} <Equalizer/></h1>
|
||||||
{sectionMenuOpened ? this._getSectionMenu(translate, this.props.ship) : null }
|
{sectionMenuOpened ? this._getSectionMenu(translate, this.props.ship) : null }
|
||||||
</div>
|
</div>
|
||||||
{this._getSlots()}
|
{this._getSlots()}
|
||||||
|
|||||||
@@ -35,6 +35,21 @@ export default class StandardSlot extends TranslatedComponent {
|
|||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this._modificationsSelected = false;
|
this._modificationsSelected = false;
|
||||||
|
this._keyDown = this._keyDown.bind(this);
|
||||||
|
this.modButton = null;
|
||||||
|
this.slotDiv = null;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Handle Enter key
|
||||||
|
* @param {SyntheticEvent} event KeyDown event
|
||||||
|
*/
|
||||||
|
_keyDown(event) {
|
||||||
|
if (event.key == 'Enter') {
|
||||||
|
if(event.target.className == 'r') {
|
||||||
|
this._toggleModifications();
|
||||||
|
}
|
||||||
|
this.props.onOpen(event);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,7 +63,13 @@ export default class StandardSlot extends TranslatedComponent {
|
|||||||
let m = slot.m;
|
let m = slot.m;
|
||||||
let classRating = m.class + m.rating;
|
let classRating = m.class + m.rating;
|
||||||
let menu;
|
let menu;
|
||||||
let validMods = m == null ? [] : (Modifications.modules[m.grp].modifications || []);
|
let validMods = m == null || !Modifications.modules[m.grp] ? [] : (Modifications.modules[m.grp].modifications || []);
|
||||||
|
if (m && m.name && m.name === 'Guardian Hybrid Power Plant') {
|
||||||
|
validMods = [];
|
||||||
|
}
|
||||||
|
if (m && m.name && m.name === 'Guardian Power Distributor') {
|
||||||
|
validMods = [];
|
||||||
|
}
|
||||||
let showModuleResistances = Persist.showModuleResistances();
|
let showModuleResistances = Persist.showModuleResistances();
|
||||||
let mass = m.getMass() || m.cargo || m.fuel || 0;
|
let mass = m.getMass() || m.cargo || m.fuel || 0;
|
||||||
|
|
||||||
@@ -56,6 +77,9 @@ export default class StandardSlot extends TranslatedComponent {
|
|||||||
let modTT = translate('modified');
|
let modTT = translate('modified');
|
||||||
if (m && m.blueprint && m.blueprint.name) {
|
if (m && m.blueprint && m.blueprint.name) {
|
||||||
modTT = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
|
modTT = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
|
||||||
|
if (m.blueprint.special && m.blueprint.special.id >= 0) {
|
||||||
|
modTT += ', ' + translate(m.blueprint.special.name);
|
||||||
|
}
|
||||||
modTT = (
|
modTT = (
|
||||||
<div>
|
<div>
|
||||||
<div>{modTT}</div>
|
<div>{modTT}</div>
|
||||||
@@ -79,24 +103,26 @@ export default class StandardSlot extends TranslatedComponent {
|
|||||||
ship={ship}
|
ship={ship}
|
||||||
m={m}
|
m={m}
|
||||||
marker={modificationsMarker}
|
marker={modificationsMarker}
|
||||||
|
modButton = {this.modButton}
|
||||||
/>;
|
/>;
|
||||||
} else {
|
} else {
|
||||||
menu = <AvailableModulesMenu
|
menu = <AvailableModulesMenu
|
||||||
className='standard'
|
className='standard'
|
||||||
modules={modules}
|
modules={modules}
|
||||||
shipMass={ModuleUtils.isShieldGenerator(m.grp) ? ship.hullMass : ship.unladenMass}
|
|
||||||
m={m}
|
m={m}
|
||||||
|
ship={ship}
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
warning={warning}
|
warning={warning}
|
||||||
diffDetails={diffDetails.bind(ship, this.context.language)}
|
diffDetails={diffDetails.bind(ship, this.context.language)}
|
||||||
|
slotDiv = {this.slotDiv}
|
||||||
/>;
|
/>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn('slot', { selected: this.props.selected })} onClick={this.props.onOpen} onContextMenu={stopCtxPropagation}>
|
<div className={cn('slot', { selected: this.props.selected })} onClick={this.props.onOpen} onKeyDown={this._keyDown} onContextMenu={stopCtxPropagation} tabIndex="0" ref={ slotDiv => this.slotDiv = slotDiv }>
|
||||||
<div className={cn('details-container', { warning: warning && warning(slot.m), disabled: m.grp !== 'bh' && !slot.enabled })}>
|
<div className={cn('details-container', { warning: warning && warning(slot.m), disabled: m.grp !== 'bh' && !slot.enabled })}>
|
||||||
<div className={'sz'}>{slot.maxClass}</div>
|
<div className={'sz'}>{m.grp == 'bh' ? m.name.charAt(0) : slot.maxClass}</div>
|
||||||
<div>
|
<div>
|
||||||
<div className={'l'}>{classRating} {translate(m.name || m.grp)}{m.mods && Object.keys(m.mods).length > 0 ? <span className='r' onMouseOver={termtip.bind(null, modTT)} onMouseOut={tooltip.bind(null, null)}><Modified /></span> : null }</div>
|
<div className={'l'}>{classRating} {translate(m.name || m.grp)}{m.mods && Object.keys(m.mods).length > 0 ? <span className='r' onMouseOver={termtip.bind(null, modTT)} onMouseOut={tooltip.bind(null, null)}><Modified /></span> : null }</div>
|
||||||
<div className={'r'}>{formats.round(mass)}{units.T}</div>
|
<div className={'r'}>{formats.round(mass)}{units.T}</div>
|
||||||
@@ -118,7 +144,7 @@ export default class StandardSlot extends TranslatedComponent {
|
|||||||
{ showModuleResistances && m.getKineticResistance() ? <div className='l'>{translate('kinres')}: {formats.pct(m.getKineticResistance())}</div> : null }
|
{ showModuleResistances && m.getKineticResistance() ? <div className='l'>{translate('kinres')}: {formats.pct(m.getKineticResistance())}</div> : null }
|
||||||
{ showModuleResistances && m.getThermalResistance() ? <div className='l'>{translate('thermres')}: {formats.pct(m.getThermalResistance())}</div> : null }
|
{ showModuleResistances && m.getThermalResistance() ? <div className='l'>{translate('thermres')}: {formats.pct(m.getThermalResistance())}</div> : null }
|
||||||
{ m.getIntegrity() ? <div className='l'>{translate('integrity')}: {formats.int(m.getIntegrity())}</div> : null }
|
{ m.getIntegrity() ? <div className='l'>{translate('integrity')}: {formats.int(m.getIntegrity())}</div> : null }
|
||||||
{ validMods.length > 0 ? <div className='r' ><button onClick={this._toggleModifications.bind(this)} onContextMenu={stopCtxPropagation} onMouseOver={termtip.bind(null, 'modifications')} onMouseOut={tooltip.bind(null, null)}><ListModifications /></button></div> : null }
|
{ validMods.length > 0 ? <div className='r' tabIndex="0" ref={ modButton => this.modButton = modButton }><button tabIndex="-1" onClick={this._toggleModifications.bind(this)} onContextMenu={stopCtxPropagation} onMouseOver={termtip.bind(null, 'modifications')} onMouseOut={tooltip.bind(null, null)}><ListModifications /></button></div> : null }
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -20,12 +20,23 @@ export default class StandardSlotSection extends SlotSection {
|
|||||||
super(props, context, 'standard', 'core internal');
|
super(props, context, 'standard', 'core internal');
|
||||||
this._optimizeStandard = this._optimizeStandard.bind(this);
|
this._optimizeStandard = this._optimizeStandard.bind(this);
|
||||||
this._selectBulkhead = this._selectBulkhead.bind(this);
|
this._selectBulkhead = this._selectBulkhead.bind(this);
|
||||||
|
this.selectedRefId = null;
|
||||||
|
this.firstRefId = 'maxjump';
|
||||||
|
this.lastRefId = 'racer';
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Handle focus if the component updates
|
||||||
|
* @param {Object} prevProps React Component properties
|
||||||
|
*/
|
||||||
|
componentDidUpdate(prevProps) {
|
||||||
|
this._handleSectionFocus(prevProps,this.firstRefId, this.lastRefId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use the lightest/optimal available standard modules
|
* Use the lightest/optimal available standard modules
|
||||||
*/
|
*/
|
||||||
_optimizeStandard() {
|
_optimizeStandard() {
|
||||||
|
this.selectedRefId = 'maxjump';
|
||||||
this.props.ship.useLightestStandard();
|
this.props.ship.useLightestStandard();
|
||||||
this.props.onChange();
|
this.props.onChange();
|
||||||
this.props.onCargoChange(this.props.ship.cargoCapacity);
|
this.props.onCargoChange(this.props.ship.cargoCapacity);
|
||||||
@@ -39,6 +50,8 @@ export default class StandardSlotSection extends SlotSection {
|
|||||||
* @param {integer} bulkheadIndex Bulkhead to use see Constants.BulkheadNames
|
* @param {integer} bulkheadIndex Bulkhead to use see Constants.BulkheadNames
|
||||||
*/
|
*/
|
||||||
_multiPurpose(shielded, bulkheadIndex) {
|
_multiPurpose(shielded, bulkheadIndex) {
|
||||||
|
this.selectedRefId = 'multipurpose';
|
||||||
|
if (bulkheadIndex === 2) this.selectedRefId = 'combat';
|
||||||
ShipRoles.multiPurpose(this.props.ship, shielded, bulkheadIndex);
|
ShipRoles.multiPurpose(this.props.ship, shielded, bulkheadIndex);
|
||||||
this.props.onChange();
|
this.props.onChange();
|
||||||
this.props.onCargoChange(this.props.ship.cargoCapacity);
|
this.props.onCargoChange(this.props.ship.cargoCapacity);
|
||||||
@@ -51,6 +64,7 @@ export default class StandardSlotSection extends SlotSection {
|
|||||||
* @param {Boolean} shielded True if shield generator should be included
|
* @param {Boolean} shielded True if shield generator should be included
|
||||||
*/
|
*/
|
||||||
_optimizeCargo(shielded) {
|
_optimizeCargo(shielded) {
|
||||||
|
this.selectedRefId = 'trader';
|
||||||
ShipRoles.trader(this.props.ship, shielded);
|
ShipRoles.trader(this.props.ship, shielded);
|
||||||
this.props.onChange();
|
this.props.onChange();
|
||||||
this.props.onCargoChange(this.props.ship.cargoCapacity);
|
this.props.onCargoChange(this.props.ship.cargoCapacity);
|
||||||
@@ -63,6 +77,7 @@ export default class StandardSlotSection extends SlotSection {
|
|||||||
* @param {Boolean} shielded True if shield generator should be included
|
* @param {Boolean} shielded True if shield generator should be included
|
||||||
*/
|
*/
|
||||||
_optimizeMiner(shielded) {
|
_optimizeMiner(shielded) {
|
||||||
|
this.selectedRefId = 'miner';
|
||||||
ShipRoles.miner(this.props.ship, shielded);
|
ShipRoles.miner(this.props.ship, shielded);
|
||||||
this.props.onChange();
|
this.props.onChange();
|
||||||
this.props.onCargoChange(this.props.ship.cargoCapacity);
|
this.props.onCargoChange(this.props.ship.cargoCapacity);
|
||||||
@@ -75,6 +90,8 @@ export default class StandardSlotSection extends SlotSection {
|
|||||||
* @param {Boolean} planetary True if Planetary Vehicle Hangar (PVH) should be included
|
* @param {Boolean} planetary True if Planetary Vehicle Hangar (PVH) should be included
|
||||||
*/
|
*/
|
||||||
_optimizeExplorer(planetary) {
|
_optimizeExplorer(planetary) {
|
||||||
|
this.selectedRefId = 'explorer';
|
||||||
|
if (planetary) this.selectedRefId = 'planetary';
|
||||||
ShipRoles.explorer(this.props.ship, planetary);
|
ShipRoles.explorer(this.props.ship, planetary);
|
||||||
this.props.onChange();
|
this.props.onChange();
|
||||||
this.props.onCargoChange(this.props.ship.cargoCapacity);
|
this.props.onCargoChange(this.props.ship.cargoCapacity);
|
||||||
@@ -86,6 +103,7 @@ export default class StandardSlotSection extends SlotSection {
|
|||||||
* Racer role
|
* Racer role
|
||||||
*/
|
*/
|
||||||
_optimizeRacer() {
|
_optimizeRacer() {
|
||||||
|
this.selectedRefId = 'racer';
|
||||||
ShipRoles.racer(this.props.ship);
|
ShipRoles.racer(this.props.ship);
|
||||||
this.props.onChange();
|
this.props.onChange();
|
||||||
this.props.onCargoChange(this.props.ship.cargoCapacity);
|
this.props.onCargoChange(this.props.ship.cargoCapacity);
|
||||||
@@ -229,19 +247,17 @@ export default class StandardSlotSection extends SlotSection {
|
|||||||
let planetaryDisabled = this.props.ship.internal.length < 4;
|
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('Maximize Jump Range')}</li>
|
<li className='lc' tabIndex="0" onClick={this._optimizeStandard} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['maxjump'] = smRef}>{translate('Maximize Jump Range')}</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._multiPurpose.bind(this, false, 0)}>{translate('Multi-purpose')}</li>
|
<li className='lc' tabIndex="0" onClick={this._multiPurpose.bind(this, false, 0)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['multipurpose'] = smRef}>{translate('Multi-purpose')}</li>
|
||||||
<li className='lc' onClick={this._multiPurpose.bind(this, true, 2)}>{translate('Combat')}</li>
|
<li className='lc' tabIndex="0" onClick={this._multiPurpose.bind(this, true, 2)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['combat'] = smRef}>{translate('Combat')}</li>
|
||||||
<li className='lc' onClick={this._optimizeCargo.bind(this, false)}>{translate('Trader')}</li>
|
<li className='lc' tabIndex="0" onClick={this._optimizeCargo.bind(this, true)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['trader'] = smRef}>{translate('Trader')}</li>
|
||||||
<li className='lc' onClick={this._optimizeCargo.bind(this, true)}>{translate('Shielded Trader')}</li>
|
<li className='lc' tabIndex="0" onClick={this._optimizeExplorer.bind(this, false)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['explorer'] = smRef}>{translate('Explorer')}</li>
|
||||||
<li className='lc' onClick={this._optimizeExplorer.bind(this, false)}>{translate('Explorer')}</li>
|
<li className={cn('lc', { disabled: planetaryDisabled })} tabIndex={planetaryDisabled ? '' : '0'} onClick={!planetaryDisabled && this._optimizeExplorer.bind(this, true)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['planetary'] = smRef}>{translate('Planetary Explorer')}</li>
|
||||||
<li className={cn('lc', { disabled: planetaryDisabled })} onClick={!planetaryDisabled && this._optimizeExplorer.bind(this, true)}>{translate('Planetary Explorer')}</li>
|
<li className='lc' tabIndex="0" onClick={this._optimizeMiner.bind(this, true)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['miner'] = smRef}>{translate('Miner')}</li>
|
||||||
<li className='lc' onClick={this._optimizeMiner.bind(this, false)}>{translate('Miner')}</li>
|
<li className='lc' tabIndex="0" onClick={this._optimizeRacer.bind(this)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['racer'] = smRef}>{translate('Racer')}</li>
|
||||||
<li className='lc' onClick={this._optimizeMiner.bind(this, true)}>{translate('Shielded Miner')}</li>
|
|
||||||
<li className='lc' onClick={this._optimizeRacer.bind(this)}>{translate('Racer')}</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -228,6 +228,96 @@ export class LinkIcon extends SvgIcon {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Link / Permalink / Chain
|
||||||
|
*/
|
||||||
|
export class OrbisIcon extends SvgIcon {
|
||||||
|
/**
|
||||||
|
* Generate the SVG
|
||||||
|
* @return {React.Component} SVG Contents
|
||||||
|
*/
|
||||||
|
svg() {
|
||||||
|
return (
|
||||||
|
<g transform="scale(.037296)">
|
||||||
|
<path d="m429 319c60.75 0 110 49.248 110 110 0 60.75-49.247 110-110 110s-110-49.247-110-110c0-60.749 49.248-110 110-110m0-20c-34.724 0-67.369 13.522-91.922 38.075s-38.075 57.198-38.075 91.922 13.522 67.369 38.075 91.922c24.553 24.554 57.198 38.075 91.922 38.075s67.369-13.521 91.922-38.075c24.554-24.553 38.075-57.198 38.075-91.922s-13.521-67.369-38.075-91.922c-24.553-24.553-57.198-38.075-91.922-38.075z" />
|
||||||
|
<path d="m429 235c107.14 0 194 86.855 194 194s-86.855 194-194 194-194-86.855-194-194 86.855-194 194-194m0-20c-28.881 0-56.908 5.661-83.304 16.825-25.485 10.779-48.368 26.207-68.016 45.853-19.646 19.647-35.074 42.53-45.853 68.015-11.163 26.396-16.824 54.423-16.824 83.304s5.661 56.908 16.825 83.304c10.779 25.484 26.207 48.368 45.853 68.016 19.647 19.646 42.53 35.073 68.015 45.854 26.396 11.164 54.423 16.825 83.304 16.825s56.908-5.661 83.304-16.825c25.484-10.779 48.368-26.206 68.016-45.854 19.646-19.646 35.073-42.53 45.854-68.016 11.164-26.396 16.825-54.423 16.825-83.304s-5.661-56.908-16.825-83.304c-10.779-25.485-26.206-48.368-45.854-68.015-19.646-19.646-42.53-35.074-68.016-45.853-26.396-11.164-54.423-16.825-83.304-16.825z" />
|
||||||
|
<path d="m429 63c202.14 0 366 163.86 366 366s-163.86 366-366 366-366-163.86-366-366 163.86-366 366-366m0-20c-52.101 0-102.65 10.208-150.25 30.342-45.966 19.442-87.244 47.271-122.69 82.714s-63.272 76.721-82.714 122.69c-20.134 47.601-30.342 98.153-30.342 150.25s10.208 102.65 30.342 150.25c19.442 45.967 47.271 87.244 82.714 122.69 35.443 35.442 76.721 63.271 122.69 82.715 47.601 20.133 98.153 30.342 150.25 30.342s102.65-10.209 150.25-30.342c45.967-19.442 87.244-47.271 122.69-82.715 35.441-35.442 63.271-76.721 82.714-122.69 20.133-47.601 30.342-98.153 30.342-150.25s-10.209-102.65-30.342-150.25c-19.442-45.966-47.271-87.244-82.714-122.69s-76.722-63.272-122.69-82.714c-47.601-20.134-98.153-30.342-150.25-30.342z"/>
|
||||||
|
<path d="m429 63c202.14 0 366 163.86 366 366s-163.86 366-366 366-366-163.86-366-366 163.86-366 366-366m0-20c-52.101 0-102.65 10.208-150.25 30.342-45.966 19.442-87.244 47.271-122.69 82.714s-63.272 76.721-82.714 122.69c-20.134 47.601-30.342 98.153-30.342 150.25s10.208 102.65 30.342 150.25c19.442 45.967 47.271 87.244 82.714 122.69 35.443 35.442 76.721 63.271 122.69 82.715 47.601 20.133 98.153 30.342 150.25 30.342s102.65-10.209 150.25-30.342c45.967-19.442 87.244-47.271 122.69-82.715 35.441-35.442 63.271-76.721 82.714-122.69 20.133-47.601 30.342-98.153 30.342-150.25s-10.209-102.65-30.342-150.25c-19.442-45.966-47.271-87.244-82.714-122.69s-76.722-63.272-122.69-82.714c-47.601-20.134-98.153-30.342-150.25-30.342z" />
|
||||||
|
<path d="m429 20c225.88 0 409 183.11 409 409s-183.11 409-409 409-409-183.11-409-409 183.11-409 409-409m0-20c-57.905 0-114.09 11.345-166.99 33.721-51.087 21.608-96.963 52.538-136.36 91.93s-70.321 85.269-91.93 136.36c-22.376 52.902-33.721 109.09-33.721 166.99s11.345 114.09 33.721 166.99c21.608 51.087 52.538 96.964 91.93 136.35 39.392 39.392 85.269 70.321 136.36 91.931 52.902 22.375 109.09 33.721 166.99 33.721s114.09-11.346 166.99-33.721c51.087-21.608 96.964-52.538 136.35-91.931 39.392-39.392 70.321-85.269 91.931-136.35 22.375-52.902 33.721-109.09 33.721-166.99s-11.346-114.09-33.721-166.99c-21.608-51.087-52.538-96.963-91.931-136.36-39.392-39.392-85.269-70.321-136.35-91.93-52.902-22.376-109.09-33.721-166.99-33.721z"/>
|
||||||
|
<path d="m155.34 679.12 173.25-190.21-15.626-13.721-170.9 190.4zm31.01 31.714 202.41-169.1-16.418-14.417-198.76 170.43z"/>
|
||||||
|
<path d="m702.66 178.87-173.25 190.21 15.625 13.721 170.9-190.4zm-31.01-31.714-202.41 169.1 16.418 14.417 198.76-170.43z" />
|
||||||
|
<rect transform="matrix(-.7071 -.7071 .7071 -.7071 429.34 1036.2)" x="387.09" y="420.77" width="84.379" height="16.859" />
|
||||||
|
</g>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Material
|
||||||
|
*/
|
||||||
|
export class MatIcon extends SvgIcon {
|
||||||
|
/**
|
||||||
|
* Generate the SVG
|
||||||
|
* @return {React.Component} SVG Contents
|
||||||
|
*/
|
||||||
|
svg() {
|
||||||
|
return<g xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#FF7100" d="M 24.86,4.18
|
||||||
|
C 24.86,4.18 17.17,7.82 17.17,7.82
|
||||||
|
17.17,7.82 15.35,14.55 15.35,14.55
|
||||||
|
15.35,14.55 24.70,9.75 24.70,9.75
|
||||||
|
24.70,9.75 24.86,4.18 24.86,4.18 Z
|
||||||
|
M 32.21,17.45
|
||||||
|
C 32.21,17.45 26.41,11.18 26.41,11.18
|
||||||
|
26.41,11.18 19.51,11.51 19.51,11.51
|
||||||
|
19.51,11.51 26.92,19.01 26.92,19.01
|
||||||
|
26.92,19.01 32.21,17.45 32.21,17.45 Z
|
||||||
|
M 21.99,28.62
|
||||||
|
C 21.99,28.62 26.10,21.10 26.10,21.10
|
||||||
|
26.10,21.10 23.66,14.57 23.66,14.57
|
||||||
|
23.66,14.57 18.89,24.01 18.89,24.01
|
||||||
|
18.89,24.01 21.99,28.62 21.99,28.62 Z
|
||||||
|
M 8.33,22.24
|
||||||
|
C 8.33,22.24 16.67,23.87 16.67,23.87
|
||||||
|
16.67,23.87 22.06,19.51 22.06,19.51
|
||||||
|
22.06,19.51 11.70,17.84 11.70,17.84
|
||||||
|
11.70,17.84 8.33,22.24 8.33,22.24 Z
|
||||||
|
M 10.11,7.14
|
||||||
|
C 10.11,7.14 11.15,15.66 11.15,15.66
|
||||||
|
11.15,15.66 16.92,19.49 16.92,19.49
|
||||||
|
16.92,19.49 15.29,9.02 15.29,9.02
|
||||||
|
15.29,9.02 10.11,7.14 10.11,7.14 Z
|
||||||
|
M 27.69,2.67
|
||||||
|
C 27.69,2.67 35.89,16.00 35.89,16.00
|
||||||
|
35.89,16.00 27.69,29.33 27.69,29.33
|
||||||
|
27.69,29.33 11.31,29.33 11.31,29.33
|
||||||
|
11.31,29.33 3.11,16.00 3.11,16.00
|
||||||
|
3.11,16.00 11.31,2.67 11.31,2.67
|
||||||
|
11.31,2.67 27.67,2.67 27.67,2.67M 29.16,0.00
|
||||||
|
C 29.16,0.00 27.69,0.00 27.69,0.00
|
||||||
|
27.69,0.00 11.31,0.00 11.31,0.00
|
||||||
|
11.31,0.00 9.84,0.00 9.84,0.00
|
||||||
|
9.84,0.00 9.06,1.25 9.06,1.25
|
||||||
|
9.06,1.25 0.87,14.57 0.87,14.57
|
||||||
|
0.87,14.57 0.00,15.98 0.00,15.98
|
||||||
|
0.00,15.98 0.87,17.39 0.87,17.39
|
||||||
|
0.87,17.39 9.06,30.73 9.06,30.73
|
||||||
|
9.06,30.73 9.84,32.00 9.84,32.00
|
||||||
|
9.84,32.00 11.31,32.00 11.31,32.00
|
||||||
|
11.31,32.00 27.69,32.00 27.69,32.00
|
||||||
|
27.69,32.00 29.16,32.00 29.16,32.00
|
||||||
|
29.16,32.00 29.94,30.73 29.94,30.73
|
||||||
|
29.94,30.73 38.13,17.39 38.13,17.39
|
||||||
|
38.13,17.39 39.00,15.98 39.00,15.98
|
||||||
|
39.00,15.98 38.13,14.57 38.13,14.57
|
||||||
|
38.13,14.57 29.94,1.25 29.94,1.25
|
||||||
|
29.94,1.25 29.16,0.00 29.16,0.00
|
||||||
|
29.16,0.00 29.16,0.00 29.16,0.00 Z" />
|
||||||
|
</g>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shopping icon (dollar sign)
|
* Shopping icon (dollar sign)
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import cn from 'classnames';
|
|
||||||
import SlotSection from './SlotSection';
|
import SlotSection from './SlotSection';
|
||||||
import HardpointSlot from './HardpointSlot';
|
import HardpointSlot from './HardpointSlot';
|
||||||
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
||||||
@@ -8,7 +7,6 @@ import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
|||||||
* Utility Slot Section
|
* Utility Slot Section
|
||||||
*/
|
*/
|
||||||
export default class UtilitySlotSection extends SlotSection {
|
export default class UtilitySlotSection extends SlotSection {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
* @param {Object} props React Component properties
|
* @param {Object} props React Component properties
|
||||||
@@ -17,12 +15,23 @@ export default class UtilitySlotSection extends SlotSection {
|
|||||||
constructor(props, context) {
|
constructor(props, context) {
|
||||||
super(props, context, 'utility', 'utility mounts');
|
super(props, context, 'utility', 'utility mounts');
|
||||||
this._empty = this._empty.bind(this);
|
this._empty = this._empty.bind(this);
|
||||||
|
this.selectedRefId = null;
|
||||||
|
this.firstRefId = 'emptyall';
|
||||||
|
this.lastRefId = 'po';
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Handle focus if the component updates
|
||||||
|
* @param {Object} prevProps React Component properties
|
||||||
|
*/
|
||||||
|
componentDidUpdate(prevProps) {
|
||||||
|
this._handleSectionFocus(prevProps,this.firstRefId, this.lastRefId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Empty all utility slots and close the menu
|
* Empty all utility slots and close the menu
|
||||||
*/
|
*/
|
||||||
_empty() {
|
_empty() {
|
||||||
|
this.selectedRefId = this.firstRefId;
|
||||||
this.props.ship.emptyUtility();
|
this.props.ship.emptyUtility();
|
||||||
this.props.onChange();
|
this.props.onChange();
|
||||||
this._close();
|
this._close();
|
||||||
@@ -36,6 +45,9 @@ export default class UtilitySlotSection extends SlotSection {
|
|||||||
* @param {Synthetic} event Event
|
* @param {Synthetic} event Event
|
||||||
*/
|
*/
|
||||||
_use(group, rating, name, event) {
|
_use(group, rating, name, event) {
|
||||||
|
this.selectedRefId = group;
|
||||||
|
if (rating !== null) this.selectedRefId += '-' + rating;
|
||||||
|
|
||||||
this.props.ship.useUtility(group, rating, name, event.getModifierState('Alt'));
|
this.props.ship.useUtility(group, rating, name, event.getModifierState('Alt'));
|
||||||
this.props.onChange();
|
this.props.onChange();
|
||||||
this._close();
|
this._close();
|
||||||
@@ -94,30 +106,29 @@ 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' tabIndex='0' onClick={this._empty} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['emptyall'] = smRef}>{translate('empty all')}</li>
|
||||||
<li className='optional-hide' style={{ textAlign: 'center', marginTop: '1em' }}>{translate('PHRASE_ALT_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>
|
||||||
<li className='c' onClick={_use.bind(this, 'sb', 'A', null)}>A</li>
|
<li className='c' tabIndex='0' onClick={_use.bind(this, 'sb', 'A', null)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['sb-A'] = smRef}>A</li>
|
||||||
<li className='c' onClick={_use.bind(this, 'sb', 'B', null)}>B</li>
|
<li className='c' tabIndex='0' onClick={_use.bind(this, 'sb', 'B', null)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['sb-B'] = smRef}>B</li>
|
||||||
<li className='c' onClick={_use.bind(this, 'sb', 'C', null)}>C</li>
|
<li className='c' tabIndex='0' onClick={_use.bind(this, 'sb', 'C', null)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['sb-C'] = smRef}>C</li>
|
||||||
<li className='c' onClick={_use.bind(this, 'sb', 'D', null)}>D</li>
|
<li className='c' tabIndex='0' onClick={_use.bind(this, 'sb', 'D', null)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['sb-D'] = smRef}>D</li>
|
||||||
<li className='c' onClick={_use.bind(this, 'sb', 'E', null)}>E</li>
|
<li className='c' tabIndex='0' onClick={_use.bind(this, 'sb', 'E', null)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['sb-E'] = smRef}>E</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div className='select-group cap'>{translate('hs')}</div>
|
<div className='select-group cap'>{translate('hs')}</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li className='lc' onClick={_use.bind(this, 'hs', null, 'Heat Sink Launcher')}>{translate('Heat Sink Launcher')}</li>
|
<li className='lc' tabIndex='0' onClick={_use.bind(this, 'hs', null, 'Heat Sink Launcher')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['hs'] = smRef}>{translate('Heat Sink Launcher')}</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div className='select-group cap'>{translate('ch')}</div>
|
<div className='select-group cap'>{translate('ch')}</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li className='lc' onClick={_use.bind(this, 'ch', null, 'Chaff Launcher')}>{translate('Chaff Launcher')}</li>
|
<li className='lc' tabIndex='0' onClick={_use.bind(this, 'ch', null, 'Chaff Launcher')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['ch'] = smRef}>{translate('Chaff Launcher')}</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div className='select-group cap'>{translate('po')}</div>
|
<div className='select-group cap'>{translate('po')}</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li className='lc' onClick={_use.bind(this, 'po', null, 'Point Defence')}>{translate('Point Defence')}</li>
|
<li className='lc' tabIndex='0' onClick={_use.bind(this, 'po', null, 'Point Defence')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['po'] = smRef}>{translate('Point Defence')}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,111 +1,80 @@
|
|||||||
import TranslatedComponent from './TranslatedComponent';
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
import Measure from 'react-measure';
|
import ContainerDimensions from 'react-container-dimensions';
|
||||||
import { BarChart, Bar, XAxis, YAxis } from 'recharts';
|
import { BarChart, Bar, XAxis, YAxis, LabelList } from 'recharts';
|
||||||
|
|
||||||
const CORIOLIS_COLOURS = ['#FF8C0D', '#1FB0FF', '#71A052', '#D5D54D'];
|
const CORIOLIS_COLOURS = ['#FF8C0D', '#1FB0FF', '#71A052', '#D5D54D'];
|
||||||
const LABEL_COLOUR = '#000000';
|
const LABEL_COLOUR = '#000000';
|
||||||
const AXIS_COLOUR = '#C06400';
|
const AXIS_COLOUR = '#C06400';
|
||||||
|
|
||||||
const ASPECT = 1;
|
const ASPECT = 1;
|
||||||
|
|
||||||
const merge = function(one, two) {
|
const merge = function(one, two) {
|
||||||
return Object.assign({}, one, two);
|
return Object.assign({}, one, two);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A vertical bar chart
|
* A vertical bar chart
|
||||||
*/
|
*/
|
||||||
export default class VerticalBarChart extends TranslatedComponent {
|
export default class VerticalBarChart extends TranslatedComponent {
|
||||||
|
static propTypes = {
|
||||||
static propTypes = {
|
data : PropTypes.array.isRequired,
|
||||||
data : PropTypes.array.isRequired,
|
yMax : PropTypes.number
|
||||||
yMax : PropTypes.number
|
};
|
||||||
};
|
|
||||||
|
/**
|
||||||
/**
|
* Constructor
|
||||||
* Constructor
|
* @param {Object} props React Component properties
|
||||||
* @param {Object} props React Component properties
|
* @param {Object} context React Component context
|
||||||
* @param {Object} context React Component context
|
*/
|
||||||
*/
|
constructor(props, context) {
|
||||||
constructor(props, context) {
|
super(props);
|
||||||
super(props);
|
|
||||||
|
this._termtip = this._termtip.bind(this);
|
||||||
this._termtip = this._termtip.bind(this);
|
}
|
||||||
|
|
||||||
this.state = {
|
/**
|
||||||
dimensions: {
|
* Render the bar chart
|
||||||
width: 300,
|
* @returns {Object} the markup
|
||||||
height: 300
|
*/
|
||||||
}
|
render() {
|
||||||
};
|
const { tooltip, termtip } = this.context;
|
||||||
}
|
|
||||||
|
// Calculate maximum for Y
|
||||||
/**
|
let dataMax = Math.max(...this.props.data.map(d => d.value));
|
||||||
* Render the bar chart
|
if (dataMax == -Infinity) dataMax = 0;
|
||||||
* @returns {Object} the markup
|
let yMax = this.props.yMax ? Math.round(this.props.yMax) : 0;
|
||||||
*/
|
const localMax = Math.max(dataMax, yMax);
|
||||||
render() {
|
|
||||||
const { width, height } = this.state.dimensions;
|
return (
|
||||||
const { tooltip, termtip } = this.context;
|
<ContainerDimensions>
|
||||||
|
{ ({ width }) => (
|
||||||
// Calculate maximum for Y
|
<div width='100%'>
|
||||||
let dataMax = Math.max(...this.props.data.map(d => d.value));
|
<BarChart width={width} height={width * ASPECT} data={this.props.data} margin={{ top: 5, right: 5, left: 5, bottom: 5 }}>
|
||||||
if (dataMax == -Infinity) dataMax = 0;
|
<XAxis interval={0} fontSize='0.8em' stroke={AXIS_COLOUR} dataKey='label' />
|
||||||
let yMax = this.props.yMax ? Math.round(this.props.yMax) : 0;
|
<YAxis interval={'preserveStart'} tickCount={11} fontSize='0.8em' stroke={AXIS_COLOUR} type='number' domain={[0, localMax]}/>
|
||||||
const localMax = Math.max(dataMax, yMax);
|
<Bar dataKey='value' fill={CORIOLIS_COLOURS[0]} isAnimationActive={false} onMouseOver={this._termtip} onMouseOut={tooltip.bind(null, null)}>
|
||||||
|
<LabelList dataKey='value' position='insideTop'/>
|
||||||
return (
|
</Bar>
|
||||||
<Measure whitelist={['width', 'top']} onMeasure={ (dimensions) => this.setState({ dimensions }) }>
|
</BarChart>
|
||||||
<div width='100%'>
|
</div>
|
||||||
<BarChart width={width} height={width * ASPECT} data={this.props.data} margin={{ top: 5, right: 5, left: 5, bottom: 5 }}>
|
)}
|
||||||
<XAxis interval={0} fontSize='0.8em' stroke={AXIS_COLOUR} dataKey='label' />
|
</ContainerDimensions>
|
||||||
<YAxis interval={'preserveStart'} tickCount={11} fontSize='0.8em' stroke={AXIS_COLOUR} type='number' domain={[0, localMax]}/>
|
);
|
||||||
<Bar dataKey='value' label={<ValueLabel />} fill={CORIOLIS_COLOURS[0]} isAnimationActive={false} onMouseOver={this._termtip} onMouseOut={tooltip.bind(null, null)}/>
|
}
|
||||||
</BarChart>
|
|
||||||
</div>
|
/**
|
||||||
</Measure>
|
* Generate a term tip
|
||||||
);
|
* @param {Object} d the data
|
||||||
}
|
* @param {number} i the index
|
||||||
|
* @param {Object} e the event
|
||||||
/**
|
* @returns {Object} termtip markup
|
||||||
* Generate a term tip
|
*/
|
||||||
* @param {Object} d the data
|
_termtip(d, i, e) {
|
||||||
* @param {number} i the index
|
if (this.props.data[i].tooltip) {
|
||||||
* @param {Object} e the event
|
return this.context.termtip(this.props.data[i].tooltip, e);
|
||||||
* @returns {Object} termtip markup
|
} else {
|
||||||
*/
|
return null;
|
||||||
_termtip(d, i, e) {
|
}
|
||||||
if (this.props.data[i].tooltip) {
|
}
|
||||||
return this.context.termtip(this.props.data[i].tooltip, e);
|
}
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A label that displays the value within the bar of the chart
|
|
||||||
*/
|
|
||||||
class ValueLabel extends React.Component {
|
|
||||||
static propTypes = {
|
|
||||||
x: PropTypes.number,
|
|
||||||
y: PropTypes.number,
|
|
||||||
payload: PropTypes.object,
|
|
||||||
value: PropTypes.number
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Render offence
|
|
||||||
* @return {React.Component} contents
|
|
||||||
*/
|
|
||||||
render() {
|
|
||||||
const { x, y, payload, value } = this.props;
|
|
||||||
|
|
||||||
const em = value < 1000 ? '1em' : value < 1000 ? '0.8em' : '0.7em';
|
|
||||||
|
|
||||||
return (
|
|
||||||
<text x={x} y={y} fill="#000000" textAnchor="middle" dy={20} style={{ fontSize: em }}>{value}</text>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -1,13 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import TranslatedComponent from './TranslatedComponent';
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
import { Ships } from 'coriolis-data/dist';
|
|
||||||
import { nameComparator } from '../utils/SlotFunctions';
|
|
||||||
import { CollapseSection, ExpandSection, MountFixed, MountGimballed, MountTurret } from './SvgIcons';
|
|
||||||
import LineChart from '../components/LineChart';
|
import LineChart from '../components/LineChart';
|
||||||
import Slider from '../components/Slider';
|
|
||||||
import * as Calc from '../shipyard/Calculations';
|
import * as Calc from '../shipyard/Calculations';
|
||||||
import Module from '../shipyard/Module';
|
|
||||||
|
|
||||||
const DAMAGE_DEALT_COLORS = ['#FFFFFF', '#FF0000', '#00FF00', '#7777FF', '#FFFF00', '#FF00FF', '#00FFFF', '#777777'];
|
const DAMAGE_DEALT_COLORS = ['#FFFFFF', '#FF0000', '#00FF00', '#7777FF', '#FFFF00', '#FF00FF', '#00FFFF', '#777777'];
|
||||||
|
|
||||||
@@ -74,7 +69,7 @@ export default class WeaponDamageChart extends TranslatedComponent {
|
|||||||
* Calculate the maximum range of a ship's weapons
|
* Calculate the maximum range of a ship's weapons
|
||||||
* @param {Object} ship The ship
|
* @param {Object} ship The ship
|
||||||
* @returns {int} The maximum range, in metres
|
* @returns {int} The maximum range, in metres
|
||||||
*/
|
*/
|
||||||
_calcMaxRange(ship) {
|
_calcMaxRange(ship) {
|
||||||
let maxRange = 1000; // Minimum
|
let maxRange = 1000; // Minimum
|
||||||
for (let i = 0; i < ship.hardpoints.length; i++) {
|
for (let i = 0; i < ship.hardpoints.length; i++) {
|
||||||
@@ -184,7 +179,7 @@ export default class WeaponDamageChart extends TranslatedComponent {
|
|||||||
const code = `${ship.toString()}:${opponent.toString()}`;
|
const code = `${ship.toString()}:${opponent.toString()}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span>
|
<div>
|
||||||
<LineChart
|
<LineChart
|
||||||
xMax={maxRange}
|
xMax={maxRange}
|
||||||
yMax={this.state.maxDps}
|
yMax={this.state.maxDps}
|
||||||
@@ -198,7 +193,7 @@ export default class WeaponDamageChart extends TranslatedComponent {
|
|||||||
points={200}
|
points={200}
|
||||||
code={code}
|
code={code}
|
||||||
/>
|
/>
|
||||||
</span>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import * as FR from './fr';
|
|||||||
import * as IT from './it';
|
import * as IT from './it';
|
||||||
import * as RU from './ru';
|
import * as RU from './ru';
|
||||||
import * as PL from './pl';
|
import * as PL from './pl';
|
||||||
|
import * as PT from './pt';
|
||||||
|
import * as CN from './cn';
|
||||||
import * as d3 from 'd3';
|
import * as d3 from 'd3';
|
||||||
|
|
||||||
let fallbackTerms = EN.terms;
|
let fallbackTerms = EN.terms;
|
||||||
@@ -25,6 +27,8 @@ export function getLanguage(langCode) {
|
|||||||
case 'it': lang = IT; break;
|
case 'it': lang = IT; break;
|
||||||
case 'ru': lang = RU; break;
|
case 'ru': lang = RU; break;
|
||||||
case 'pl': lang = PL; break;
|
case 'pl': lang = PL; break;
|
||||||
|
case 'pt': lang = PT; break;
|
||||||
|
case 'cn': lang = CN; break;
|
||||||
default:
|
default:
|
||||||
lang = EN;
|
lang = EN;
|
||||||
}
|
}
|
||||||
@@ -58,17 +62,20 @@ export function getLanguage(langCode) {
|
|||||||
},
|
},
|
||||||
translate,
|
translate,
|
||||||
units: {
|
units: {
|
||||||
|
ang: '°', // Angle
|
||||||
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
|
||||||
|
m: <u>{translate('m')}</u>, // Meters
|
||||||
MJ: <u>{translate('MJ')}</u>, // Mega Joules
|
MJ: <u>{translate('MJ')}</u>, // Mega Joules
|
||||||
'm/s': <u>{translate('m/s')}</u>, // Meters per second
|
'm/s': <u>{translate('m/s')}</u>, // Meters per second
|
||||||
'°/s': <u>{translate('°/s')}</u>, // Degrees per second
|
'°/s': <u>{translate('°/s')}</u>, // Degrees per second
|
||||||
MW: <u>{translate('MW')}</u>, // Mega Watts (same as Mega Joules per second)
|
MW: <u>{translate('MW')}</u>, // Mega Watts (same as Mega Joules per second)
|
||||||
mps: <u>{translate('m/s')}</u>, // Metres per second
|
mps: <u>{translate('m/s')}</u>, // Metres per second
|
||||||
|
pct: '%', // Percent
|
||||||
ps: <u>{translate('/s')}</u>, // per second
|
ps: <u>{translate('/s')}</u>, // per second
|
||||||
pm: <u>{translate('/min')}</u>, // per minute
|
pm: <u>{translate('/min')}</u>, // per minute
|
||||||
s: <u>{translate('secs')}</u>, // Seconds
|
s: <u>{translate('secs')}</u>, // Seconds
|
||||||
@@ -88,5 +95,7 @@ export const Languages = {
|
|||||||
es: 'Español',
|
es: 'Español',
|
||||||
fr: 'Français',
|
fr: 'Français',
|
||||||
ru: 'ру́сский',
|
ru: 'ру́сский',
|
||||||
pl: 'polski'
|
pl: 'polski',
|
||||||
|
pt: 'português',
|
||||||
|
cn: '中文'
|
||||||
};
|
};
|
||||||
|
|||||||
16
src/app/i18n/cn.js
Normal file
16
src/app/i18n/cn.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
export const formats = {
|
||||||
|
decimal: '.',
|
||||||
|
thousands: ',',
|
||||||
|
grouping: [3],
|
||||||
|
currency: ['¥', ''],
|
||||||
|
dateTime: '%a %b %e %X %Y',
|
||||||
|
date: '%Y年%m月%d日',
|
||||||
|
time: '%H:%M:%S',
|
||||||
|
periods: ['AM', 'PM'],
|
||||||
|
days: ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'],
|
||||||
|
shortDays: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
|
||||||
|
months: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
|
||||||
|
shortMonths: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月']
|
||||||
|
};
|
||||||
|
|
||||||
|
export { default as terms } from './cn.json';
|
||||||
405
src/app/i18n/cn.json
Normal file
405
src/app/i18n/cn.json
Normal file
File diff suppressed because one or more lines are too long
@@ -13,607 +13,4 @@ export const formats = {
|
|||||||
shortMonths: ['Jan', 'Feb', 'Mrz', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez']
|
shortMonths: ['Jan', 'Feb', 'Mrz', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez']
|
||||||
};
|
};
|
||||||
|
|
||||||
export const terms = {
|
export { default as terms } from './de.json';
|
||||||
PHRASE_ALT_ALL: 'Alt + Klick um alle Plätze zu füllen',
|
|
||||||
PHRASE_BACKUP_DESC: 'Export aller Coriolis-Daten, um sie zu sichern oder um sie zu einem anderen Browser/Gerät zu übertragen.',
|
|
||||||
PHRASE_CONFIRMATION: 'Sind Sie sicher?',
|
|
||||||
PHRASE_EXPORT_DESC: 'Ein detaillierter JSON-Export Ihrer Konfiguration für die Verwendung in anderen Websites und Tools',
|
|
||||||
PHRASE_FASTEST_RANGE: 'aufeinanderfolgende maximale Reichweite/Sprünge',
|
|
||||||
PHRASE_IMPORT: 'JSON hier einfügen oder importieren',
|
|
||||||
PHRASE_LADEN: 'Schiffsmasse + Treibstoff + Fracht',
|
|
||||||
PHRASE_NO_BUILDS: 'Keine Konfigurationen zum Vergleich ausgewählt!',
|
|
||||||
PHRASE_NO_RETROCH: 'Keine Umrüständerungen',
|
|
||||||
PHRASE_SELECT_BUILDS: 'Ausstattung zum Vergleich auswählen',
|
|
||||||
PHRASE_SG_RECHARGE: 'Zeit von 50% bis 100% der Ladung bei vollem SYS Kondensator',
|
|
||||||
PHRASE_SG_RECOVER: 'Erneuerung (zu 50%) nach Zusammenbruch bei vollem SYS Kondensator',
|
|
||||||
PHRASE_UNLADEN: 'Schiffsmasse ohne Treibstoff und Fracht',
|
|
||||||
PHRASE_UPDATE_RDY: 'Update verfügbar! Klicken zum Aktualisieren',
|
|
||||||
PHRASE_ENGAGEMENT_RANGE: 'Die Distanz zwischen deinem Schiff und seinem Ziel',
|
|
||||||
PHRASE_SELECT_BLUEPRINT: 'Klicken um eine Bauplan auszuwählen',
|
|
||||||
PHRASE_BLUEPRINT_WORST: 'Schlechteste Primärwerte für diesen Bauplan',
|
|
||||||
PHRASE_BLUEPRINT_RANDOM: 'Zufällige Auswahl an Primärwerten für diesen Bauplan (Schlechteste <> Beste)',
|
|
||||||
PHRASE_BLUEPRINT_BEST: 'Beste Primärwerte für diesen Bauplan',
|
|
||||||
PHRASE_BLUEPRINT_EXTREME: 'Beste Positive Werte und schlechteste Negative Werte für diesen Bauplan',
|
|
||||||
PHRASE_BLUEPRINT_RESET: 'Entferne alle Modifikationen und experimentelle Effekte',
|
|
||||||
PHRASE_SELECT_SPECIAL: 'Klicken um einen experimentellen Effekt auszuwählen',
|
|
||||||
PHRASE_NO_SPECIAL: 'Keine Experimentellen Effekte',
|
|
||||||
PHRASE_SHOPPING_LIST: 'Stationen die diese Schiffskonfiguration verkaufen',
|
|
||||||
PHRASE_REFIT_SHOPPING_LIST: 'Stationen die die benötigten Module verkaufen',
|
|
||||||
PHRASE_TOTAL_EFFECTIVE_SHIELD: 'Gesamtschaden der von jeder Schadensart absorbiert werden kann (Bei Nutzung aller Schildzellen)',
|
|
||||||
PHRASE_TIME_TO_LOSE_SHIELDS: 'Schilde werden halten für',
|
|
||||||
PHRASE_TIME_TO_RECOVER_SHIELDS: 'Schilde werden sich erholen in',
|
|
||||||
PHRASE_TIME_TO_RECHARGE_SHIELDS: 'Schilde werden sich wieder aufgeladen haben in',
|
|
||||||
PHRASE_SHIELD_SOURCES: 'Aufschlüsselung der Schildenergiezusammensetzung',
|
|
||||||
PHRASE_EFFECTIVE_SHIELD: 'Effektive Schildstärke gegen die Unterschiedlichen Schadensarten',
|
|
||||||
PHRASE_ARMOUR_SOURCES: 'Aufschlüsselung der Hüllenpanzerungszusammensetzung',
|
|
||||||
PHRASE_EFFECTIVE_ARMOUR: 'Effektive Hüllenpanzerungsstärke gegen die unterschiedlichen Schadensarten',
|
|
||||||
PHRASE_DAMAGE_TAKEN: '% des rohen Schadens der unterschiedlichen Schadensarten',
|
|
||||||
PHRASE_TIME_TO_LOSE_ARMOUR: 'Hüllenpanzerung wird halten für',
|
|
||||||
PHRASE_MODULE_PROTECTION_EXTERNAL: 'Modulpanzerung der Waffenaufhängung',
|
|
||||||
PHRASE_MODULE_PROTECTION_INTERNAL: 'Modulpanzerung für alle anderen Module',
|
|
||||||
PHRASE_SHIELD_DAMAGE: 'Aufschlüsselung des kontinuierlichen SPS gegen Schilde',
|
|
||||||
PHRASE_ARMOUR_DAMAGE: 'Aufschlüsselung des kontinuierlichen SPS gegen Hüllenpanzerung',
|
|
||||||
|
|
||||||
PHRASE_TIME_TO_REMOVE_SHIELDS: 'Schilde werden zusammenbrechen in',
|
|
||||||
TT_TIME_TO_REMOVE_SHIELDS: 'Mit andauerndem Beschuss durch alle Waffen',
|
|
||||||
PHRASE_TIME_TO_REMOVE_ARMOUR: 'Hüllenpanzerung wird brechen in',
|
|
||||||
TT_TIME_TO_REMOVE_ARMOUR: 'Mit andauerndem Beschuss durch alle Waffen',
|
|
||||||
PHRASE_TIME_TO_DRAIN_WEP: 'Leert WAF Energie in',
|
|
||||||
TT_TIME_TO_DRAIN_WEP: 'Dauer um WAF Energie aufzubrauchen wenn alle Waffen gefeuert werden',
|
|
||||||
TT_TIME_TO_LOSE_SHIELDS: 'Gegen andauernden Beschuss durch alle Waffen des Gegners',
|
|
||||||
TT_TIME_TO_LOSE_ARMOUR: 'Gegen andauernden Beschuss durch alle Waffen des Gegners',
|
|
||||||
TT_MODULE_ARMOUR: 'ModulPanzerung für den Schutz interner Subsysteme (Module)',
|
|
||||||
TT_MODULE_PROTECTION_EXTERNAL: 'Prozensatz des Schadens der von den Waffenaufhängungen zu den Modulverstärkungen umgeleitet wird',
|
|
||||||
TT_MODULE_PROTECTION_INTERNAL: 'Prozensatz des Schadens der von den Subsystem (außer Waffen) zu den Modulverstärkungen umgeleitet wird',
|
|
||||||
|
|
||||||
TT_EFFECTIVE_SDPS_SHIELDS: 'Effektiver SPS solange die Waffenenergie nicht aufgebraucht wurde',
|
|
||||||
TT_EFFECTIVENESS_SHIELDS: 'Effektivität im Vergleich zu einem Ziel ohne Widerstände mit 0 PIPS in SYS bei 0m',
|
|
||||||
TT_EFFECTIVE_SDPS_ARMOUR: 'Effektiver kontinuierlicher Schaden solange der WAF Kondensator nicht aufgebraucht wurde',
|
|
||||||
TT_EFFECTIVENESS_ARMOUR: 'Effektivität im Vergleich zu einem Ziel ohne Widerstände bei 0m',
|
|
||||||
|
|
||||||
PHRASE_EFFECTIVE_SDPS_SHIELDS: 'Effektiver kontinuierlicher SPS gegen Schilde',
|
|
||||||
PHRASE_EFFECTIVE_SDPS_ARMOUR: 'Effektiver kontinuierlicher SPS gegen Hüllenpanzerung',
|
|
||||||
|
|
||||||
TT_SUMMARY_SPEED: 'Mit vollem Tank und 4 PIPS in WAF',
|
|
||||||
TT_SUMMARY_SPEED_NONFUNCTIONAL: 'Schubdüsen deaktiviert oder maximale Masse überschritten',
|
|
||||||
TT_SUMMARY_BOOST: 'Mit vollem Tank und 4 PIPS in ANT',
|
|
||||||
TT_SUMMARY_BOOST_NONFUNCTIONAL: 'Energieverteiler kann nicht genügend Energie für den Boost liefern',
|
|
||||||
TT_SUMMARY_SHIELDS: 'Rohe Schildstärke, inklusive Schildverstärker',
|
|
||||||
TT_SUMMARY_SHIELDS_NONFUNCTIONAL: 'Keine Schildgenerator oder Schilde deaktiviert',
|
|
||||||
TT_SUMMARY_INTEGRITY: 'Schiffsintegrität, einschließlich Hüllenpanzerung und Rumpfhüllenverstärkung',
|
|
||||||
TT_SUMMARY_HULL_MASS: 'Hüllenmasse, bevor jegliche Module installiert wurde',
|
|
||||||
TT_SUMMARY_UNLADEN_MASS: 'Hüllenmasse ohne Ladung und Treibstoff',
|
|
||||||
TT_SUMMARY_LADEN_MASS: 'Hüllenmasse, einschließlich Treibstoff und Ladung',
|
|
||||||
TT_SUMMARY_DPS: 'Schaden pro Sekunde wenn alle Waffen feuern',
|
|
||||||
TT_SUMMARY_EPS: 'WAF Kondensator Verbrauch pro Sekunde wenn alle Waffen feuern',
|
|
||||||
TT_SUMMARY_TTD: 'Zeit um den WAF Kondensator aufzubrauchen wenn alle Waffen feuern und 4 PIPS auf dem WAF Kondesator',
|
|
||||||
TT_SUMMARY_MAX_SINGLE_JUMP: 'Weitest mögliche Sprungreichweite ohne Ladung und nur genügend Treibstoff für den Sprung selbst',
|
|
||||||
TT_SUMMARY_UNLADEN_SINGLE_JUMP: 'Weitest mögliche Sprungreichweite ohne Ladung und einem vollen Tank',
|
|
||||||
TT_SUMMARY_LADEN_SINGLE_JUMP: 'Weitest mögliche Sprungreichweite mit voller Ladung und einem vollen Tank',
|
|
||||||
TT_SUMMARY_UNLADEN_TOTAL_JUMP: 'Weitest mögliche Sprungreichweite ohne Ladung, einem vollen Tank und der weitest möglichen Sprungreichweite bei jedem Sprung',
|
|
||||||
TT_SUMMARY_LADEN_TOTAL_JUMP: 'Weitest mögliche Sprungreichweite mit maximaler Ladung, einem vollen Tank und der weitest möglichen Sprungreichweite bei jedem Sprung',
|
|
||||||
|
|
||||||
HELP_MODIFICATIONS_MENU: 'Klicke auf eine Zahl um einen neuen Wert einzutragen oder bewege den Regler',
|
|
||||||
|
|
||||||
// Other languages fallback to these values
|
|
||||||
// Only Translate to other languages if the name is different in-game
|
|
||||||
am: 'Automatische Feldwartung',
|
|
||||||
bh: 'Hüllenpanzerung',
|
|
||||||
bl: 'Strahlenlaser',
|
|
||||||
bsg: 'Bizellengenerator',
|
|
||||||
c: 'Kanone',
|
|
||||||
cc: 'Sammeldrohnensteuerung',
|
|
||||||
ch: 'Düppelwerfer',
|
|
||||||
cr: 'Laderaum',
|
|
||||||
cs: 'Ladungssensor',
|
|
||||||
dc: 'Landecomputer',
|
|
||||||
ec: 'Elektronische Gegenmaßnahme',
|
|
||||||
fc: ' Fragmentkanone',
|
|
||||||
fh: 'Jägerhangar',
|
|
||||||
fi: 'Frameshift Unterbrecher',
|
|
||||||
fs: 'Treibstoffsammler',
|
|
||||||
fsd: 'Frameshiftantrieb',
|
|
||||||
ft: 'Treibstofftank',
|
|
||||||
fx: 'Treibstoffdrohnencontroller',
|
|
||||||
hb: 'Ladelukenbrecherdrohnencontroller',
|
|
||||||
hr: 'Rumpfhüllenverstärkung',
|
|
||||||
hs: 'Kühlkörperwerfer',
|
|
||||||
kw: 'Tötungsbefehlscanner',
|
|
||||||
ls: 'Lebenserhaltung',
|
|
||||||
mc: 'Mehrzweckgeschütz ',
|
|
||||||
ml: 'Erzabbaulaser',
|
|
||||||
mr: 'Raketengestell',
|
|
||||||
mrp: 'Modulverstärkung',
|
|
||||||
nl: 'Minenwerfer',
|
|
||||||
pa: 'Plasmabeschleuniger',
|
|
||||||
pas: 'Planetare Annäherungseinheit',
|
|
||||||
pc: 'Erzsuchersteuerung',
|
|
||||||
pce: 'Touristen Passagierkabine',
|
|
||||||
pci: 'Business Klasse Passagierkabine',
|
|
||||||
pcm: 'Erste Klasse Passagierkabine',
|
|
||||||
pcq: 'Luxus Passagierkabine',
|
|
||||||
pd: 'Energieverteiler',
|
|
||||||
pl: 'Pulslaser',
|
|
||||||
po: 'Punktverteidigung',
|
|
||||||
pp: 'Kraftwerk',
|
|
||||||
psg: 'Prismatischer Schildgenerator',
|
|
||||||
pv: 'Planetarer Fahrzeughangar',
|
|
||||||
rf: 'Raffinerie',
|
|
||||||
rg: 'Schienenkanone',
|
|
||||||
s: 'Sensoren',
|
|
||||||
sb: 'Schildverstärker',
|
|
||||||
sc: 'Himmelskörperscanner',
|
|
||||||
scb: 'Schildzellenbatterie',
|
|
||||||
sg: 'Schildgenerator',
|
|
||||||
ss: 'Oberflächensensor',
|
|
||||||
t: 'Schubdüsen',
|
|
||||||
tp: 'Torpedopylone',
|
|
||||||
ul: 'Salvenlaser',
|
|
||||||
ws: 'Frameshiftwolkenscanner',
|
|
||||||
hrd: 'Hüllenhärte',
|
|
||||||
|
|
||||||
// Items on the outfitting page
|
|
||||||
// Notification of restricted slot
|
|
||||||
emptyrestricted: 'leer (eingeschränkt)',
|
|
||||||
'damage dealt to': 'Schaden gegen',
|
|
||||||
'damage received from': 'Schaden durch',
|
|
||||||
'against shields': 'Gegen Schilde',
|
|
||||||
'against hull': 'Gegen Hülle',
|
|
||||||
'total effective shield': 'Effektiver Schild (kombiniert)',
|
|
||||||
|
|
||||||
// 'ammo' was overloaded for outfitting page and modul info, so changed to ammunition for outfitting page
|
|
||||||
ammunition: 'Munition',
|
|
||||||
|
|
||||||
// Unit for seconds
|
|
||||||
secs: 's',
|
|
||||||
|
|
||||||
rebuildsperbay: 'Jäger pro Stellpaltz',
|
|
||||||
|
|
||||||
// Blueprint rolls
|
|
||||||
worst: 'Schlecht',
|
|
||||||
average: 'Durchschnitt',
|
|
||||||
random: 'Zufall',
|
|
||||||
best: 'Sehr gut',
|
|
||||||
extreme: 'Extrem',
|
|
||||||
reset: 'Zurücksetzen',
|
|
||||||
|
|
||||||
// Weapon, offence, defence and movement
|
|
||||||
dpe: 'Damage per MJ of energy',
|
|
||||||
dps: 'Schaden pro Sekunde',
|
|
||||||
sdps: 'Kontinuierlicher Schaden pro Sekunde',
|
|
||||||
dpssdps: 'Schaden pro Sekunde (kontinuierlicher Schaden pro Sekunde)',
|
|
||||||
eps: 'Energie pro Sekunde',
|
|
||||||
epsseps: 'Energie pro Sekunde (kontinuierliche Energie pro Sekunde)',
|
|
||||||
hps: 'Hitze pro Sekunde',
|
|
||||||
hpsshps: 'Hitze pro Sekunde (kontinuierliche Hitze pro Sekunde)',
|
|
||||||
'damage by': 'Schaden von',
|
|
||||||
'damage from': 'Schaden von',
|
|
||||||
'shield cells': 'Schildbatterien',
|
|
||||||
'recovery': 'Erholung',
|
|
||||||
'recharge': 'Auflaung',
|
|
||||||
'engine pips': 'Schubpriorität',
|
|
||||||
'4b': '4 PIPS und Boost',
|
|
||||||
'speed': 'Tempo',
|
|
||||||
'pitch': 'Kippen',
|
|
||||||
'roll': 'Rollen',
|
|
||||||
'yaw': 'Gieren',
|
|
||||||
'internal protection': 'Interner Schutz',
|
|
||||||
'external protection': 'Externer Schutz',
|
|
||||||
'engagement range': 'Gefechtsreichweite',
|
|
||||||
'total': 'Insg.',
|
|
||||||
|
|
||||||
// Modifications
|
|
||||||
ammo: 'Maximale Munition',
|
|
||||||
boot: 'Startzeit',
|
|
||||||
brokenregen: 'Regenrationsrate (Gebrochene Schilde)',
|
|
||||||
burst: 'Salve',
|
|
||||||
burstrof: 'Salven Feuerrate',
|
|
||||||
clip: 'Munnitionsmagazin',
|
|
||||||
damage: 'Schaden',
|
|
||||||
distdraw: 'Energieverteilerverbrauch',
|
|
||||||
duration: 'Dauer',
|
|
||||||
eff: 'Effizienz',
|
|
||||||
engcap: 'Antriebskapazität',
|
|
||||||
engrate: 'Antrieb Ladungsrate',
|
|
||||||
explres: 'Explosionswiderstand',
|
|
||||||
facinglimit: 'Facing limit',
|
|
||||||
hullboost: 'Hüllenboost',
|
|
||||||
hullreinforcement: 'Hüllenverstärkung',
|
|
||||||
integrity: 'Integrität',
|
|
||||||
jitter: 'Schwankungsbreite',
|
|
||||||
kinres: 'Kinetischer Widerstand',
|
|
||||||
maxfuel: 'Maximaler Treibstoff pro Sprung',
|
|
||||||
mass: 'Masse',
|
|
||||||
optmass: 'Optimale Masse',
|
|
||||||
optmul: 'Optimalmultiplikator',
|
|
||||||
pgen: 'Energiegewinnung',
|
|
||||||
piercing: 'Durchdringung',
|
|
||||||
power: 'Energieverbrauch',
|
|
||||||
protection: 'Schutz',
|
|
||||||
range: 'Reichweite',
|
|
||||||
ranget: 'Reichweite/s', // Range in time (for FSD interdictor)
|
|
||||||
regen: 'Wiederaufladungsrate',
|
|
||||||
reload: 'Wiederaufladung',
|
|
||||||
rof: 'Feuerrate',
|
|
||||||
angle: 'Abtastwinkel',
|
|
||||||
scanrate: 'Abtastrate',
|
|
||||||
scantime: 'Abtastzeit',
|
|
||||||
shield: 'Schild',
|
|
||||||
shieldboost: 'Schildverstärkung',
|
|
||||||
shieldreinforcement: 'Schildverstärkung',
|
|
||||||
shotspeed: 'Schussgeschwidndigkeit',
|
|
||||||
spinup: 'Aufwärmphase',
|
|
||||||
syscap: 'Systemkapazität',
|
|
||||||
sysrate: 'System Ladungsrate',
|
|
||||||
thermload: 'Thermische Last',
|
|
||||||
thermres: 'Thermischer Widerstand',
|
|
||||||
wepcap: 'Waffenkapazität',
|
|
||||||
weprate: 'Waffen Ladungsrate',
|
|
||||||
|
|
||||||
// Shield generators use a different terminology
|
|
||||||
minmass_sg: 'Minimale Hüllenmasse',
|
|
||||||
optmass_sg: 'Optimale Hüllenmasse',
|
|
||||||
maxmass_sg: 'Maximum Hüllenmasse',
|
|
||||||
minmul_sg: 'Minimale Stärke',
|
|
||||||
optmul_sg: 'Optimale Stärke',
|
|
||||||
maxmul_sg: 'Maximale Stärke',
|
|
||||||
minmass_psg: 'Minimale Hüllenmasse',
|
|
||||||
optmass_psg: 'Optimale Hüllenmasse',
|
|
||||||
maxmass_psg: 'Maximum Hüllenmasse',
|
|
||||||
minmul_psg: 'Minimale Stärke',
|
|
||||||
optmul_psg: 'Optimale Stärke',
|
|
||||||
maxmul_psg: 'Maximale Stärke',
|
|
||||||
minmass_bsg: 'Minimale Hüllenmasse',
|
|
||||||
optmass_bsg: 'Optimale Hüllenmasse',
|
|
||||||
maxmass_bsg: 'Maximum Hüllenmasse',
|
|
||||||
minmul_bsg: 'Minimale Stärke',
|
|
||||||
optmul_bsg: 'Optimale Stärke',
|
|
||||||
maxmul_bsg: 'Maximale Stärke',
|
|
||||||
|
|
||||||
range_s: 'Typische Emissionsreichweite',
|
|
||||||
|
|
||||||
// Damage types
|
|
||||||
absolute: 'Insgesamt',
|
|
||||||
explosive: 'Explosiv',
|
|
||||||
kinetic: 'Kinetisch',
|
|
||||||
thermal: 'Thermisch',
|
|
||||||
|
|
||||||
// Shield sources
|
|
||||||
generator: 'Generator',
|
|
||||||
boosters: 'Verstärker',
|
|
||||||
cells: 'Batterien',
|
|
||||||
|
|
||||||
// Armour sources
|
|
||||||
bulkheads: 'Hüllenpanzerung',
|
|
||||||
reinforcement: 'Hüllenverstärkung',
|
|
||||||
|
|
||||||
// Optional module groups (only these, that are not in the list with the short terms)
|
|
||||||
'hangars': 'Hangars',
|
|
||||||
'limpet controllers': 'Drohnensteuerung',
|
|
||||||
'passenger cabins': 'Passagierkabinen',
|
|
||||||
'structural reinforcement': 'Strukturverstärkungen',
|
|
||||||
|
|
||||||
// Hardpoint module groups
|
|
||||||
'lasers': 'Laser',
|
|
||||||
'projectiles': 'Projektilwaffen',
|
|
||||||
'ordnance': 'Artillerie',
|
|
||||||
|
|
||||||
// Armour modules
|
|
||||||
'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',
|
|
||||||
|
|
||||||
// Scanner modules
|
|
||||||
'scanners': 'Scanner',
|
|
||||||
'Basic Discovery Scanner': 'Aufklärungsscanner (einf.)',
|
|
||||||
'Advanced Discovery Scanner': 'Aufklärungsscanner (fortgeschr.)',
|
|
||||||
'Detailed Surface Scanner': 'Detail-Oberflächenscanner',
|
|
||||||
'Intermedia Discovery Scanner': 'Intermedia Discovery Scanner',
|
|
||||||
|
|
||||||
// Docking modules
|
|
||||||
'Standard Docking Computer': 'Standard-Landecomputer',
|
|
||||||
|
|
||||||
// Point defence modules
|
|
||||||
'Point Defence': 'Punktverteidigung',
|
|
||||||
|
|
||||||
// Chaff launcher modules
|
|
||||||
'Chaff Launcher': 'Düppel-Werfer',
|
|
||||||
|
|
||||||
// Heat sink launcher modules
|
|
||||||
'Heat Sink Launcher': 'Kühlkörperwerfer',
|
|
||||||
|
|
||||||
// Panel headings and subheadings
|
|
||||||
'power and costs': 'Energie und Kosten',
|
|
||||||
'costs': 'Kosten',
|
|
||||||
'retrofit costs': 'Umrüstkosten',
|
|
||||||
'reload costs': 'Nachladekosten',
|
|
||||||
'profiles': 'Profile',
|
|
||||||
'engine profile': 'Antriebsprofil',
|
|
||||||
'fsd profile': 'Frameshit Antriebsprofil',
|
|
||||||
'movement profile': 'Bewegungsprofil',
|
|
||||||
'damage to opponent\'s shields': 'Gegnerischer Schildschaden',
|
|
||||||
'damage to opponent\'s hull': 'Gegnerischer Hüllenschaden',
|
|
||||||
'offence': 'Offensiv',
|
|
||||||
'defence': 'Defensiv',
|
|
||||||
'shield metrics': 'Schildwerte',
|
|
||||||
'raw shield strength': 'Pure Schildstärke',
|
|
||||||
'shield sources': 'Schildzusammensetzung',
|
|
||||||
'damage taken': 'Erhaltener Schaden',
|
|
||||||
'effective shield': 'Effektiver Schildwert',
|
|
||||||
'armour metrics': 'Panzerungswerte',
|
|
||||||
'raw armour strength': 'Pure Panzerungsstärke',
|
|
||||||
'armour sources': 'Panzerungszusammensetzung',
|
|
||||||
'raw module armour': 'Pure Modulpanzerung',
|
|
||||||
'effective armour': 'Effektive Panzerung',
|
|
||||||
'offence metrics': 'Offensivwerte',
|
|
||||||
'defence metrics': 'Defensivwerte',
|
|
||||||
|
|
||||||
// internal module panel header
|
|
||||||
'Maximize Jump Range': 'Sprungreichweite maximieren',
|
|
||||||
'roles': 'Rollen',
|
|
||||||
'Multi-purpose': 'Allrounder',
|
|
||||||
'Combat': 'Kampf',
|
|
||||||
'Trader': 'Handel',
|
|
||||||
'Shielded Trader': 'Handel (mit Schild)',
|
|
||||||
'Explorer': 'Entdecker',
|
|
||||||
'Planetary Explorer': 'Planetenentdecker',
|
|
||||||
'Miner': 'Erzabbau',
|
|
||||||
'Shielded Miner': 'Erzabbau (mit Schild)',
|
|
||||||
'Racer': 'Geschwindigkeit',
|
|
||||||
|
|
||||||
// Misc items
|
|
||||||
'fuel carried': 'geladener Treibstoff',
|
|
||||||
'cargo carried': 'geladene Fracht',
|
|
||||||
'ship control': 'Energieverteilung',
|
|
||||||
'opponent': 'Gegner',
|
|
||||||
'opponent\'s shields': 'Gegnerische Schilde',
|
|
||||||
'opponent\'s armour': 'Gegenerische Panzerung',
|
|
||||||
'shield damage sources': 'Schadensquellen (Schild)',
|
|
||||||
'armour damage sources': 'Schadensquellen (Panzerung)',
|
|
||||||
'never': 'Niemals',
|
|
||||||
'stock': 'Standard',
|
|
||||||
'boost': 'Boost',
|
|
||||||
'ship': 'Schiff',
|
|
||||||
'laden': 'Beladen',
|
|
||||||
'unladen': 'Leer',
|
|
||||||
'jump range': 'Sprungreichweite',
|
|
||||||
'total laden': 'gesamt beladen',
|
|
||||||
'total unladen': 'gesamt leer',
|
|
||||||
'cargo': 'Fracht',
|
|
||||||
'hull': 'Hülle',
|
|
||||||
'Enter Name': 'Name eingeben',
|
|
||||||
'fuel': 'Tank',
|
|
||||||
|
|
||||||
// Items on the ship list page
|
|
||||||
// Ship list
|
|
||||||
'manufacturer': 'Hersteller',
|
|
||||||
'cost': 'Preis',
|
|
||||||
'size': 'Größe',
|
|
||||||
'small': 'Klein',
|
|
||||||
'medium': 'Mittel',
|
|
||||||
'large': 'Groß',
|
|
||||||
'agility': 'MNV',
|
|
||||||
'base': 'Basis',
|
|
||||||
'armour': 'Panzerung',
|
|
||||||
'shields': 'Schilde',
|
|
||||||
'jump': 'Sprung',
|
|
||||||
'core module classes': 'Basismodulklassen',
|
|
||||||
'Power Plant': 'Kraftwerk',
|
|
||||||
th: 'Schubdüsen',
|
|
||||||
fsd: 'Frameshiftantrieb',
|
|
||||||
ls: 'Lebenserhaltung',
|
|
||||||
pd: 'Energieverteiler',
|
|
||||||
s: 'Sensoren',
|
|
||||||
'hardpoints': 'Aufhängungen',
|
|
||||||
'internal compartments': 'Optionale Modulklassen',
|
|
||||||
|
|
||||||
// Menu items
|
|
||||||
'ships': 'Schiffe',
|
|
||||||
'builds': 'eigene builds',
|
|
||||||
'compare': 'Vergleiche',
|
|
||||||
'compare all': 'Alle vergleichen',
|
|
||||||
'create new': 'Neu anlegen',
|
|
||||||
'none created': 'Keine angelegt',
|
|
||||||
'insurance': 'Versicherung',
|
|
||||||
'discount': 'Nachlass',
|
|
||||||
'settings': 'Einstellungen',
|
|
||||||
'module resistances': 'Modulwiderstände',
|
|
||||||
'comparisons': 'Vergleiche',
|
|
||||||
'backup': 'Sichern',
|
|
||||||
'detailed export': 'Detaillierter Export',
|
|
||||||
'import': 'Importieren',
|
|
||||||
'delete all': 'Alles löschen',
|
|
||||||
'about': 'Über',
|
|
||||||
|
|
||||||
|
|
||||||
// Items on the outfitting page
|
|
||||||
'core internal': 'Intern (basis)',
|
|
||||||
'optional internal': 'Intern (optional)',
|
|
||||||
'utility mounts': 'Werkzeug-Steckplätze',
|
|
||||||
'empty': 'leer',
|
|
||||||
'empty all': 'Alles leeren',
|
|
||||||
|
|
||||||
// Help text
|
|
||||||
HELP_TEXT: `
|
|
||||||
<h1>Einführung</h1>
|
|
||||||
Coriolis ist ein Schiffskonfigurator für Elite:Dangerous. Diese Hilfedatei versorgt dich mit allen Informationen die du benötigst um Coriolis zu benutzen.
|
|
||||||
|
|
||||||
<h1>Importiere dein Schiff nach Coriolis</h1>
|
|
||||||
Often, you will want to start with your existing ship in Coriolis and see how particular changes might affect it, for example upgrading your FSD. There are a number of tools that can be used to import your ship without you having to create it manually. This has the added benefit of copying over any engineering modifications that have taken place as well. </p>
|
|
||||||
|
|
||||||
<h2>Importiere dein Schiff von EDDI</h2>
|
|
||||||
To import your ship from EDDI first ensure that your connection to the Frontier servers' companion API is working. To do this check the 'Companion App' tab where you should see "Your connection to the companion app is operational". If not then follow the instructions in the companion app tab in EDDI to connect to the Frontier servers.</p>
|
|
||||||
|
|
||||||
Once you have a working companion API connection go to the 'Shipyard' tab. At the right-hand side of each ship is an 'Export to Coriolis' button that will open your default web browser in Coriolis with the ship's build. </p>
|
|
||||||
|
|
||||||
Note that Internet Explorer and Edge might not import correctly, due to their internal restrictions on URL length. If you find that this is the case then please change your default browser to Chrome. </p>
|
|
||||||
|
|
||||||
Also, the imported information does not provide any data on the power priority or enabled status of your cargo hatch. Coriolis sets this item to have a power priority of "5" and to be disabled by default. You can change this after import in the Power Management section. </p>
|
|
||||||
|
|
||||||
<h2>Importiere dein Schiff von EDMC</h2>
|
|
||||||
To import your ship from EDMC once your connection to the Frontier servers' companion API is working go to 'Settings ->Configuration' and set the 'Preferred Shipyard' to 'Coriolis'. Once this is set up clicking on your ship in the main window will open your default web browser in Coriolis with the ship's build.</p>
|
|
||||||
|
|
||||||
Note that Internet Explorer and Edge might not import correctly, due to their internal restrictions on URL length. If you find that this is the case then please change your default browser to Chrome. </p>
|
|
||||||
|
|
||||||
<h1>Understanding And Using The Outfitting Panels</h1>
|
|
||||||
The outfitting page is where you will spend most of your time, and contains the information for your ship. Information on each of the panels is provided below. </p>
|
|
||||||
|
|
||||||
<h2>Schlüsselwerte</h2>
|
|
||||||
Along the top of the screen are some of the key values for your build. This is a handy reference for the values, but more information is provided for the values in the further panels. </p>
|
|
||||||
|
|
||||||
Here, along with most places in Coriolis, acronyms will have tooltips explaining what they mean. Hover over the acronym to obtain more detail, or look in the glossary at the end of this help.</p>
|
|
||||||
|
|
||||||
All values are the highest possible, assuming that you an optimal setup for that particular value (maximum pips in ENG for speed, minimum fuel for jump range, etc.). This means that these values will not be affected by changes to pip settings. Details of the specific setup for each value are listed in the associated tootip.</p>
|
|
||||||
|
|
||||||
<h2>Module</h2>
|
|
||||||
The next set of panels laid out horizontally across the screen contain the modules you have put in your build. From left to right these are the core modules, the internal modules, the hardpoints and the utility mounts. These represent the available slots in your ship and cannot be altered. Each slot has a class, or size, and in general any module up to a given size can fit in a given slot (exceptions being bulkheads, life support and sensors in core modules and restricted internal slots, which can only take a subset of module depending on their restrictions). </p>
|
|
||||||
|
|
||||||
To add a module to a slot left-click on the slot and select the required module. Only the modules capable of fitting in the selected slot will be shown. </p>
|
|
||||||
|
|
||||||
To remove a module from a slot right-click on the module. </p>
|
|
||||||
|
|
||||||
To move a module from one slot to another drag it. If you instead want to copy the module drag it whilst holding down the 'Alt' key. </p>
|
|
||||||
|
|
||||||
Clicking on the headings for each set of modules gives you the ability to either select an overall role for your ship (when clicking the core internal header) or a specific module with which you want to fill all applicable slots (when clicking the other headers). </p>
|
|
||||||
|
|
||||||
<h2>Schiffskontrolle</h2>
|
|
||||||
The ship controls allow you to set your pips, boost, and amount of fuel and cargo that your build carries. The changes made here will effect the information supplied in the subsequent panels, giving you a clearer view of what effect different changing these items will have. </p>
|
|
||||||
|
|
||||||
Ship control settings are saved as part of a build. </p>
|
|
||||||
|
|
||||||
<h2>Gegner</h2>
|
|
||||||
The opponet selection allows you to choose your opponent. The opponent can be either a stock build of a ship or one of your own saved builds. You can also set the engagement range between you and your opponent. Your selection here will effect the information supplied in the subsequent panels, specifically the Offence and Defence panels. </p>
|
|
||||||
|
|
||||||
Opponent settings are saved as part of a build. </p>
|
|
||||||
|
|
||||||
<h2>Energieverbrauch und Kosten Untermenü</h2>
|
|
||||||
<h3>Energie</h3>
|
|
||||||
The power management panel provides information about power usage and priorities. It allows you to enable and disable individual modules, as well as set power priorities for each module. Disabled modules will not be included in the build's statistics, with the exception of Shield Cell Banks as they are usually disabled when not in use and only enabled when required. </p>
|
|
||||||
|
|
||||||
<h3>Kosten</h3>
|
|
||||||
The costs panel provides information about the costs for each of your modules, and the total cost and insurance for your build. By default Coriolis uses the standard costs, however discounts for your ship, modules and insurance can be altered in the 'Settings' at the top-right of the page.</p>
|
|
||||||
|
|
||||||
The retrofit costs provides information about the costs of changing the base build for your ship, or your saved build, to the current build.</p>
|
|
||||||
|
|
||||||
The reload costs provides information about the costs of reloading your current build.</p>
|
|
||||||
|
|
||||||
<h2>Profile</h2>
|
|
||||||
Profiles provide graphs that show the general performance of modules in your build
|
|
||||||
|
|
||||||
<h3>Antriebsprofil</h3>
|
|
||||||
The engine profile panel provides information about the capabilities of your current thrusters. The graph shows you how the maximum speed alters with the overall mass of your build. The vertical dashed line on the graph shows your current mass. Your engine profile can be altered by obtaining different thrusters or engineering your existing thrusters, and you can increase your maximum speed by adding pips to the ENG capacitor as well as reducing the amount of fuel and cargo you are carrying as well as reducing the overall weight of the build. You can also temporarily increase your speed by hitting the boost button. </p>
|
|
||||||
|
|
||||||
<h3>FSA Profil</h3>
|
|
||||||
The FSD profile panel provides information about the capabilities of your current frame shift drive. The graph shows you how the maximum jump range alters with the overall mass of your build. The vertical dashed line on the graph shows your current maximum single jump range. Your FSD profile can be altered by obtaining a different FSD or engineering your existing FSD, and you can increase your maximum jump range by reducing the amount of fuel and cargo you are carrying as well as reducing the overall weight of the build, </p>
|
|
||||||
|
|
||||||
<h3>Bewegungsprofil</h3>
|
|
||||||
The movement profile panel provides information about the capabilities of your current thrusters with your current overall mass and ENG pips settings. The diagram shows your ability to move and rotate in the different axes:
|
|
||||||
|
|
||||||
<dl>
|
|
||||||
<dt>Geschwindigkeit</dt><dd>The fastest the ship can move, in metres per second</dd>
|
|
||||||
<dt>Kippen</dt><dd>The fastest the ship can raise or lower its nose, in degrees per second</dd>
|
|
||||||
<dt>Rollen</dt><dd>The fastest the ship can roll its body, in degrees per second</dd>
|
|
||||||
<dt>Gieren</dt><dd>The fastest the ship can turn its nose left or right, in degrees per second</dd>
|
|
||||||
</dl>
|
|
||||||
|
|
||||||
Your movement profile can be altered by obtaining different thrusters or engineering your existing thrusters, and you can increase your movement values by adding pips to the ENG capacitor as well as reducing the amount of fuel and cargo you are carrying as well as reducing the overall weight of the build. You can also temporarily increase your movement profile by hitting the boost button. </p>
|
|
||||||
|
|
||||||
<h3>Schadensprofil</h3>
|
|
||||||
The damage profile provides two graphs showing how the the build's damage to the opponent's shields and hull change with engagement range. The vertical dashed line on the graph shows your current engagement range. This combines information about the build's weapons with the opponent's shields and hull to provide an accurate picture of sustained damage that can be inflicted on the opponent. </p>
|
|
||||||
|
|
||||||
<h2>Offensive</h2>
|
|
||||||
<h3>Zusammenfassung</h3>
|
|
||||||
The offence summary provides per-weapon information about sustained damage per second inflicted to shields and hull, along with a measure of effectiveness of that weapon. The effectiveness value has a tooltip that provides a breakdown of the effectiveness, and can include reductions or increases due to range, resistance, and either power distributor (for shields) or hardness (for hull). The final effectiveness value is calculated by multiplying these percentages together. </p>
|
|
||||||
|
|
||||||
<h3>Offensivwerte</h3>
|
|
||||||
The offence metrics panel provides information about your offence. </p>
|
|
||||||
|
|
||||||
Time to drain is a measure of how quickly your WEP capacitor will drain when firing all weapons. It is affected by the number of pips you have in your WEP capacitor, with more pips resulting in a higher WEP recharge rate and hence a longer time to drain. </p>
|
|
||||||
|
|
||||||
The next value is the time it will take you to remove your opponent's shields. This assumes that you have 100% time on target and that your engagement range stays constant. Note that if your time to remove shields is longer than your time to drain this assumes that you continue firing throughout, inflicting lower damage due to the reduced energy in your WEP capacitor. </p>
|
|
||||||
|
|
||||||
The next value is the time it will take you to remove your opponent's armour. This follows the same logic as the time to remove shields. </p>
|
|
||||||
|
|
||||||
<h3>Schadensquellen (Schilde)</h3>
|
|
||||||
The shield damage sources provides information about the sources of damage to your opponent by damage type. For each applicable type of damage (absolute explosive, kinetic, thermal) a sustained damage per second value is provided. </p>
|
|
||||||
|
|
||||||
<h3>Schadensquelle (Hülle)</h3>
|
|
||||||
The hull damage sources provides information about the sources of damage to your opponent by damage type. For each applicable type of damage (absolute explosive, kinetic, thermal) a sustained damage per second value is provided. </p>
|
|
||||||
|
|
||||||
<h2>Defensive</h2>
|
|
||||||
<h3>Schildwerte</h3>
|
|
||||||
The shield metrics provides information about your shield defence. </p>
|
|
||||||
|
|
||||||
Raw shield strength is the sum of the shield from your generator, boosters and shield cell banks. A tooltip provides a breakdown of these values. </p>
|
|
||||||
|
|
||||||
The time the shields will hold for is the time it will take your opponent' to remove your shields. This assumes that they have 100% time on target and that the engagement range stays constant. It also assumes that you fire all of your shield cell banks prior to your shields being lost. </p>
|
|
||||||
|
|
||||||
The time the shields will recover in is the time it will take your shields to go from collapsed (0%) to recovered (50%). This is affected by the number of pips you have in your SYS capacitor. </p>
|
|
||||||
|
|
||||||
The time the shields will recharge in is the time it will take your shields to go from recovered (50%) to full (100%). This is affected by the number of pips you have in your SYS capacitor. </p>
|
|
||||||
|
|
||||||
</h3>Schildstärke (Zusammensetzung)</h3>
|
|
||||||
This chart provides information about the sources of your shields. For each applicable source of shields (generator, boosters, shield cell banks) a value is provided. </p>
|
|
||||||
|
|
||||||
</h3>Schadensauswirkung</h3>
|
|
||||||
This graph shows how the initial damage from the weapons of each type are reduced before their damage is applied to the shields. For each type of damage (absolute, explosive, kinetic, thermal) a percentage of the initial damage is provided. A tooltip provides a breakdown of these values. </p>
|
|
||||||
|
|
||||||
</h3>Effektiver Schildwert</h3>
|
|
||||||
This graph shows the effective shield for each damage type, found by dividing the raw shield value by the damage taken for that type. </p>
|
|
||||||
|
|
||||||
<h3>Panzerungswerte</h3>
|
|
||||||
The armour metrics provides information about your armour defence. </p>
|
|
||||||
|
|
||||||
Raw armour strength is the sum of the armour from your bulkheads and hull reinforcement packages. A tooltip provides a breakdown of these values. </p>
|
|
||||||
|
|
||||||
The time the armour will hold for is the time it will take your opponent' to take your armour to 0. This assumes that they have 100% time on target, the engagement range stays constant, and that all damage is dealt to the armour rather than modules. </p>
|
|
||||||
|
|
||||||
Raw module armour is the sum of the protection from your module reinforcement packages. </p>
|
|
||||||
|
|
||||||
Protection for hardpoints is the amount of protection that your module reinforcement packages provide to hardpoints. This percentage of damage to the hardpoints will be diverted to the module reinforcement packages. </p>
|
|
||||||
|
|
||||||
Protection for all other modules is the amount of protection that your module reinforcement packages provide to everything other than hardpoints. This percentage of damage to the modules will be diverted to the module reinforcement packages. </p>
|
|
||||||
|
|
||||||
</h3>Hüllenpanzerung (Zusammensetzung)</h3>
|
|
||||||
This chart provides information about the sources of your armour. For each applicable source of shields (bulkheads, hull reinforcement packages) a value is provided. </p>
|
|
||||||
|
|
||||||
</h3>Schadensauswirkung</h3>
|
|
||||||
This graph shows how the initial damage from the weapons of each type are reduced before their damage is applied to the armour. For each type of damage (absolute, explosive, kinetic, thermal) a percentage of the initial damage is provided. A tooltip provides a breakdown of these values. </p>
|
|
||||||
|
|
||||||
</h3>Effektive Hüllenpanzerung</h3>
|
|
||||||
This graph shows the effective armour for each damage type, found by dividing the raw armour value by the damage taken for that type. </p>
|
|
||||||
|
|
||||||
<h1>Tastaturkommandos</h1>
|
|
||||||
<dl>
|
|
||||||
<dt>Ctrl-b</dt><dd>Boost an-/auschalten</dd>
|
|
||||||
<dt>Ctrl-e</dt><dd>open Export Dialog (ausschließlich Ausstattungsseite)</dd>
|
|
||||||
<dt>Ctrl-h</dt><dd>Öffne den Hilfedialog</dd>
|
|
||||||
<dt>Ctrl-i</dt><dd>Öffne den Importdialog</dd>
|
|
||||||
<dt>Ctrl-o</dt><dd>Öffne den Kurzlinkdialog</dd>
|
|
||||||
<dt>Ctrl-left-arrow</dt><dd>Erhöhe den SYS Kondensator</dd>
|
|
||||||
<dt>Ctrl-up-arrow</dt><dd>Erhöhe den ANT Kondensator</dd>
|
|
||||||
<dt>Ctrl-right-arrow</dt><dd>Erhöhe den WAF Kondensator</dd>
|
|
||||||
<dt>Ctrl-down-arrow</dt><dd>Setze den Energieverteiler zurück</dd>
|
|
||||||
<dt>Esc</dt><dd>Schließe jeden offenen Dialog</dd>
|
|
||||||
</dl>
|
|
||||||
<h1>Glossar</h1>
|
|
||||||
<dl>
|
|
||||||
<dt>Absoluter Schaden</dt><dd>A type of damage, without any protection. Absolute damage is always dealt at 100% regardless of if the damage is to shields, hull or modules, and irrespective of resistances</dd>
|
|
||||||
<dt>SPS</dt><dd>Schaden pro Sekunde; the amount of damage that a weapon or a ship can deal per second to a target under optimum conditions</dd>
|
|
||||||
<dt>EPS</dt><dd>Energie pro Sekunde; the amount of energy that a weapon or a ship drains from the weapons capacitor per second when firing</dd>
|
|
||||||
<dt>HPS</dt><dd>Hitze pro Sekunde; the amount of heat that a weapon or a ship generates per second when firing</dd>
|
|
||||||
<dt>Effektivität</dt><dd>A comparison of the maximum DPS of a given weapon to the actual DPS of the given weapon in a specific situation. DPS can be reduced by range to the target, the target's hull and shield resistances, and the target's hardness</dd>
|
|
||||||
<dt>Explosiver Schaden</dt><dd>A type of damage, protected against by explosive resistance</dd>
|
|
||||||
<dt>Hüllenhärte</dt><dd>The inherent resistance to damage of a ship's hull. Hardness is defined on a per-ship basis and there is currently nothing that can be done to change it. Hardness of a ship's hull is compared to the piercing of weapons: if piercing is higher than hardness the weapon does 100% damage, otherwise it does a fraction of its damage calculated as piercing/hardness</dd>
|
|
||||||
<dt>Schadensabfall</dt><dd>The distance at which a weapons starts to do less damage than its stated DPS</dd>
|
|
||||||
<dt>Kinetischer Schaden</dt><dd>A type of damage, protected against by kinetic resistance</dd>
|
|
||||||
<dt>KSPS</dt><dd>Kontinuierlicher Schaden pro Sekunde; the amount of damage that a weapon or a ship can deal per second to a target, taking in to account ammunition reload</dd>
|
|
||||||
<dt>KEPS</dt><dd>Kontinuierliche Energie pro Sekunde; the amount of energy that a weapon or a ship drains from the weapons capacitor per second when firing, taking in to account ammunition reload</dd>
|
|
||||||
<dt>KHPS</dt><dd>Kontinuierliche Hitze pro Sekunde; the amount of heat that a weapon or a ship generates per second when firing, taking in to account ammunition reload</dd>
|
|
||||||
<dt>Thermischer Schaden</dt><dd>A type of damage, protected against by thermal resistance</dd>
|
|
||||||
</dl>
|
|
||||||
|
|
||||||
`,
|
|
||||||
};
|
|
||||||
450
src/app/i18n/de.json
Normal file
450
src/app/i18n/de.json
Normal file
File diff suppressed because one or more lines are too long
@@ -13,496 +13,4 @@ export const formats = {
|
|||||||
shortMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
|
shortMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
|
||||||
};
|
};
|
||||||
|
|
||||||
export const terms = {
|
export { default as terms } from './en.json';
|
||||||
PHRASE_ALT_ALL: 'Alt + Click to fill all slots',
|
|
||||||
PHRASE_BACKUP_DESC: 'Backup of all Coriolis data to save or transfer to another browser/device',
|
|
||||||
PHRASE_CONFIRMATION: 'Are you sure?',
|
|
||||||
PHRASE_EXPORT_DESC: 'A detailed JSON export of your build for use in other sites and tools',
|
|
||||||
PHRASE_FASTEST_RANGE: 'Consecutive max range jumps',
|
|
||||||
PHRASE_IMPORT: 'Paste JSON or import here',
|
|
||||||
PHRASE_LADEN: 'Ship mass + fuel + cargo',
|
|
||||||
PHRASE_NO_BUILDS: 'No builds added to comparison!',
|
|
||||||
PHRASE_NO_RETROCH: 'No Retrofitting changes',
|
|
||||||
PHRASE_SELECT_BUILDS: 'Select builds to compare',
|
|
||||||
PHRASE_SG_RECHARGE: 'Time from 50% to 100% charge, assuming full SYS capacitor to start with',
|
|
||||||
PHRASE_SG_RECOVER: 'Time from 0% to 50% charge, assuming full SYS capacitor to start with',
|
|
||||||
PHRASE_UNLADEN: 'Ship mass excluding fuel and cargo',
|
|
||||||
PHRASE_UPDATE_RDY: 'Update Available! Click to refresh',
|
|
||||||
PHRASE_ENGAGEMENT_RANGE: 'The distance between your ship and its target',
|
|
||||||
PHRASE_SELECT_BLUEPRINT: 'Click to select a blueprint',
|
|
||||||
PHRASE_BLUEPRINT_WORST: 'Worst primary values for this blueprint',
|
|
||||||
PHRASE_BLUEPRINT_RANDOM: 'Random selection between worst and best primary values for this blueprint',
|
|
||||||
PHRASE_BLUEPRINT_BEST: 'Best primary values for this blueprint',
|
|
||||||
PHRASE_BLUEPRINT_EXTREME: 'Best beneficial and worst detrimental primary values for this blueprint',
|
|
||||||
PHRASE_BLUEPRINT_RESET: 'Remove all modifications and blueprint',
|
|
||||||
PHRASE_SELECT_SPECIAL: 'Click to select an experimental effect',
|
|
||||||
PHRASE_NO_SPECIAL: 'No experimental effect',
|
|
||||||
PHRASE_SHOPPING_LIST: 'Stations that sell this build',
|
|
||||||
PHRASE_REFIT_SHOPPING_LIST: 'Stations that sell required modules',
|
|
||||||
PHRASE_TOTAL_EFFECTIVE_SHIELD: 'Total amount of damage that can be taken from each damage type, if using all shield cells',
|
|
||||||
PHRASE_TIME_TO_LOSE_SHIELDS: 'Shields will hold for',
|
|
||||||
PHRASE_TIME_TO_RECOVER_SHIELDS: 'Shields will recover in',
|
|
||||||
PHRASE_TIME_TO_RECHARGE_SHIELDS: 'Shields will recharge in',
|
|
||||||
PHRASE_SHIELD_SOURCES: 'Breakdown of the supply of shield energy',
|
|
||||||
PHRASE_EFFECTIVE_SHIELD: 'Effective shield strength against different damage types',
|
|
||||||
PHRASE_ARMOUR_SOURCES: 'Breakdown of the supply of armour',
|
|
||||||
PHRASE_EFFECTIVE_ARMOUR: 'Effective armour strength against different damage types',
|
|
||||||
PHRASE_DAMAGE_TAKEN: '% of raw damage taken for different damage types',
|
|
||||||
PHRASE_TIME_TO_LOSE_ARMOUR: 'Armour will hold for',
|
|
||||||
PHRASE_MODULE_PROTECTION_EXTERNAL: 'Protection for hardpoints',
|
|
||||||
PHRASE_MODULE_PROTECTION_INTERNAL: 'Protection for all other modules',
|
|
||||||
PHRASE_SHIELD_DAMAGE: 'Breakdown of sources for sustained DPS against shields',
|
|
||||||
PHRASE_ARMOUR_DAMAGE: 'Breakdown of sources for sustained DPS against armour',
|
|
||||||
|
|
||||||
PHRASE_TIME_TO_REMOVE_SHIELDS: 'Will remove shields in',
|
|
||||||
TT_TIME_TO_REMOVE_SHIELDS: 'With sustained fire by all weapons',
|
|
||||||
PHRASE_TIME_TO_REMOVE_ARMOUR: 'Will remove armour in',
|
|
||||||
TT_TIME_TO_REMOVE_ARMOUR: 'With sustained fire by all weapons',
|
|
||||||
PHRASE_TIME_TO_DRAIN_WEP: 'Will drain WEP in',
|
|
||||||
TT_TIME_TO_DRAIN_WEP: 'Time to drain WEP capacitor with all weapons firing',
|
|
||||||
TT_TIME_TO_LOSE_SHIELDS: 'Against sustained fire from all opponent\'s weapons',
|
|
||||||
TT_TIME_TO_LOSE_ARMOUR: 'Against sustained fire from all opponent\'s weapons',
|
|
||||||
TT_MODULE_ARMOUR: 'Armour protecting against module damage',
|
|
||||||
TT_MODULE_PROTECTION_EXTERNAL: 'Percentage of damage diverted from hardpoints to module reinforcement packages',
|
|
||||||
TT_MODULE_PROTECTION_INTERNAL: 'Percentage of damage diverted from non-hardpoint modules to module reinforcement packages',
|
|
||||||
|
|
||||||
TT_EFFECTIVE_SDPS_SHIELDS: 'Actual sustained DPS whilst WEP capacitor is not empty',
|
|
||||||
TT_EFFECTIVENESS_SHIELDS: 'Effectivness compared to hitting a 0-resistance target with 0 pips to SYS at 0m',
|
|
||||||
TT_EFFECTIVE_SDPS_ARMOUR: 'Actual sustained DPS whilst WEP capacitor is not empty',
|
|
||||||
TT_EFFECTIVENESS_ARMOUR: 'Effectivness compared to hitting a 0-resistance target at 0m',
|
|
||||||
|
|
||||||
PHRASE_EFFECTIVE_SDPS_SHIELDS: 'SDPS against shields',
|
|
||||||
PHRASE_EFFECTIVE_SDPS_ARMOUR: 'SDPS against armour',
|
|
||||||
|
|
||||||
TT_SUMMARY_SPEED: 'With full fuel tank and 4 pips to ENG',
|
|
||||||
TT_SUMMARY_SPEED_NONFUNCTIONAL: 'Thrusters powered off or over maximum mass with full fuel and cargo loads',
|
|
||||||
TT_SUMMARY_BOOST: 'With full fuel tank and 4 pips to ENG',
|
|
||||||
TT_SUMMARY_BOOST_NONFUNCTIONAL: 'Power distributor not able to supply enough power to boost',
|
|
||||||
TT_SUMMARY_SHIELDS: 'Raw shield strength, including boosters',
|
|
||||||
TT_SUMMARY_SHIELDS_NONFUNCTIONAL: 'No shield generator or shield generator powered off',
|
|
||||||
TT_SUMMARY_INTEGRITY: 'Ship integrity, including bulkheads and hull reinforcement packages',
|
|
||||||
TT_SUMMARY_HULL_MASS: 'Mass of the hull prior to any modules being installed',
|
|
||||||
TT_SUMMARY_UNLADEN_MASS: 'Mass of the hull and modules prior to any fuel or cargo',
|
|
||||||
TT_SUMMARY_LADEN_MASS: 'Mass of the hull and modules with full fuel and cargo',
|
|
||||||
TT_SUMMARY_DPS: 'Damage per second with all weapons firing',
|
|
||||||
TT_SUMMARY_EPS: 'WEP capacitor consumed per second with all weapons firing',
|
|
||||||
TT_SUMMARY_TTD: 'Time to drain WEP capacitor with all weapons firing and 4 pips to WEP',
|
|
||||||
TT_SUMMARY_MAX_SINGLE_JUMP: 'Farthest possible jump range with no cargo and only enough fuel for the jump itself',
|
|
||||||
TT_SUMMARY_UNLADEN_SINGLE_JUMP: 'Farthest possible jump range with no cargo and a full fuel tank',
|
|
||||||
TT_SUMMARY_LADEN_SINGLE_JUMP: 'Farthest possible jump range with full cargo and a full fuel tank',
|
|
||||||
TT_SUMMARY_UNLADEN_TOTAL_JUMP: 'Farthest possible range with no cargo, a full fuel tank, and jumping as far as possible each time',
|
|
||||||
TT_SUMMARY_LADEN_TOTAL_JUMP: 'Farthest possible range with full cargo, a full fuel tank, and jumping as far as possible each time',
|
|
||||||
|
|
||||||
HELP_MODIFICATIONS_MENU: 'Click on a number to enter a new value, or drag along the bar for small changes',
|
|
||||||
|
|
||||||
// Other languages fallback to these values
|
|
||||||
// Only Translate to other languages if the name is different in-game
|
|
||||||
am: 'Auto Field-Maintenance Unit',
|
|
||||||
bh: 'Bulkheads',
|
|
||||||
bl: 'Beam Laser',
|
|
||||||
bsg: 'Bi-Weave Shield Generator',
|
|
||||||
c: 'Cannon',
|
|
||||||
cc: 'Collector Limpet Controller',
|
|
||||||
ch: 'Chaff Launcher',
|
|
||||||
cr: 'Cargo Rack',
|
|
||||||
cs: 'Manifest Scanner',
|
|
||||||
dc: 'Docking Computer',
|
|
||||||
ec: 'Electronic Countermeasure',
|
|
||||||
fc: 'Fragment Cannon',
|
|
||||||
fh: 'Fighter Hangar',
|
|
||||||
fi: 'FSD Interdictor',
|
|
||||||
fs: 'Fuel Scoop',
|
|
||||||
fsd: 'Frame Shift Drive',
|
|
||||||
ft: 'Fuel Tank',
|
|
||||||
fx: 'Fuel Transfer Limpet Controller',
|
|
||||||
hb: 'Hatch Breaker Limpet Controller',
|
|
||||||
hr: 'Hull Reinforcement Package',
|
|
||||||
hs: 'Heat Sink Launcher',
|
|
||||||
kw: 'Kill Warrant Scanner',
|
|
||||||
ls: 'Life Support',
|
|
||||||
mc: 'Multi-cannon',
|
|
||||||
ml: 'Mining Laser',
|
|
||||||
mr: 'Missile Rack',
|
|
||||||
mrp: 'Module Reinforcement Package',
|
|
||||||
nl: 'Mine Launcher',
|
|
||||||
pa: 'Plasma Accelerator',
|
|
||||||
pas: 'Planetary Approach Suite',
|
|
||||||
pc: 'Prospector Limpet Controller',
|
|
||||||
pce: 'Economy Class Passenger Cabin',
|
|
||||||
pci: 'Business Class Passenger Cabin',
|
|
||||||
pcm: 'First Class Passenger Cabin',
|
|
||||||
pcq: 'Luxury Passenger Cabin',
|
|
||||||
pd: 'power distributor',
|
|
||||||
pl: 'Pulse Laser',
|
|
||||||
po: 'Point Defence',
|
|
||||||
pp: 'Power Plant',
|
|
||||||
psg: 'Prismatic Shield Generator',
|
|
||||||
pv: 'Planetary Vehicle Hangar',
|
|
||||||
rf: 'Refinery',
|
|
||||||
rg: 'Rail Gun',
|
|
||||||
s: 'Sensors',
|
|
||||||
sb: 'Shield Booster',
|
|
||||||
sc: 'Stellar Scanners',
|
|
||||||
scb: 'Shield Cell Bank',
|
|
||||||
sg: 'Shield Generator',
|
|
||||||
ss: 'Surface Scanners',
|
|
||||||
t: 'thrusters',
|
|
||||||
tp: 'Torpedo Pylon',
|
|
||||||
ul: 'Burst Laser',
|
|
||||||
ws: 'Frame Shift Wake Scanner',
|
|
||||||
|
|
||||||
// Items on the outfitting page
|
|
||||||
// Notification of restricted slot
|
|
||||||
emptyrestricted: 'empty (restricted)',
|
|
||||||
'damage dealt to': 'Damage dealt to',
|
|
||||||
'damage received from': 'Damage received from',
|
|
||||||
'against shields': 'Against shields',
|
|
||||||
'against hull': 'Against hull',
|
|
||||||
'total effective shield': 'Total effective shield',
|
|
||||||
|
|
||||||
// 'ammo' was overloaded for outfitting page and modul info, so changed to ammunition for outfitting page
|
|
||||||
ammunition: 'Ammo',
|
|
||||||
|
|
||||||
// Unit for seconds
|
|
||||||
secs: 's',
|
|
||||||
|
|
||||||
rebuildsperbay: 'Rebuilds per bay',
|
|
||||||
|
|
||||||
// Blueprint rolls
|
|
||||||
worst: 'Worst',
|
|
||||||
average: 'Average',
|
|
||||||
random: 'Random',
|
|
||||||
best: 'Best',
|
|
||||||
extreme: 'Extreme',
|
|
||||||
reset: 'Reset',
|
|
||||||
|
|
||||||
// Weapon, offence, defence and movement
|
|
||||||
dpe: 'Damage per MJ of energy',
|
|
||||||
dps: 'Damage per second',
|
|
||||||
sdps: 'Sustained damage per second',
|
|
||||||
dpssdps: 'Damage per second (sustained damage per second)',
|
|
||||||
eps: 'Energy per second',
|
|
||||||
epsseps: 'Energy per second (sustained energy per second)',
|
|
||||||
hps: 'Heat per second',
|
|
||||||
hpsshps: 'Heat per second (sustained heat per second)',
|
|
||||||
'damage by': 'Damage by',
|
|
||||||
'damage from': 'Damage from',
|
|
||||||
'shield cells': 'Shield cells',
|
|
||||||
'recovery': 'Recovery',
|
|
||||||
'recharge': 'Recharge',
|
|
||||||
'engine pips': 'Engine Pips',
|
|
||||||
'4b': '4 pips and boost',
|
|
||||||
'speed': 'Speed',
|
|
||||||
'pitch': 'Pitch',
|
|
||||||
'roll': 'Roll',
|
|
||||||
'yaw': 'Yaw',
|
|
||||||
'internal protection': 'Internal protection',
|
|
||||||
'external protection': 'External protection',
|
|
||||||
'engagement range': 'Engagement range',
|
|
||||||
'total': 'Total',
|
|
||||||
|
|
||||||
// Modifications
|
|
||||||
ammo: 'Ammunition maximum',
|
|
||||||
boot: 'Boot time',
|
|
||||||
brokenregen: 'Broken regeneration rate',
|
|
||||||
burst: 'Burst',
|
|
||||||
burstrof: 'Burst rate of fire',
|
|
||||||
clip: 'Ammunition clip',
|
|
||||||
damage: 'Damage',
|
|
||||||
distdraw: 'Distributor draw',
|
|
||||||
duration: 'Duration',
|
|
||||||
eff: 'Efficiency',
|
|
||||||
engcap: 'Engines capacity',
|
|
||||||
engrate: 'Engines recharge rate',
|
|
||||||
explres: 'Explosive resistance',
|
|
||||||
facinglimit: 'Facing limit',
|
|
||||||
hullboost: 'Hull boost',
|
|
||||||
hullreinforcement: 'Hull reinforcement',
|
|
||||||
integrity: 'Integrity',
|
|
||||||
jitter: 'Jitter',
|
|
||||||
kinres: 'Kinetic resistance',
|
|
||||||
maxfuel: 'Maximum fuel per jump',
|
|
||||||
mass: 'Mass',
|
|
||||||
optmass: 'Optimal mass',
|
|
||||||
optmul: 'Optimal multiplier',
|
|
||||||
pgen: 'Power generation',
|
|
||||||
piercing: 'Piercing',
|
|
||||||
power: 'Power draw',
|
|
||||||
protection: 'Protection',
|
|
||||||
range: 'Range',
|
|
||||||
ranget: 'Range', // Range in time (for FSD interdictor)
|
|
||||||
regen: 'Regeneration rate',
|
|
||||||
reload: 'Reload',
|
|
||||||
rof: 'Rate of fire',
|
|
||||||
angle: 'Scan angle',
|
|
||||||
scanrate: 'Scan rate',
|
|
||||||
scantime: 'Scan time',
|
|
||||||
shield: 'Shield',
|
|
||||||
shieldboost: 'Shield boost',
|
|
||||||
shieldreinforcement: 'Shield reinforcement',
|
|
||||||
shotspeed: 'Shot speed',
|
|
||||||
spinup: 'Spin up time',
|
|
||||||
syscap: 'Systems capacity',
|
|
||||||
sysrate: 'Systems recharge rate',
|
|
||||||
thermload: 'Thermal load',
|
|
||||||
thermres: 'Thermal resistance',
|
|
||||||
wepcap: 'Weapons capacity',
|
|
||||||
weprate: 'Weapons recharge rate',
|
|
||||||
|
|
||||||
// Shield generators use a different terminology
|
|
||||||
minmass_sg: 'Minimum hull mass',
|
|
||||||
optmass_sg: 'Optimal hull mass',
|
|
||||||
maxmass_sg: 'Maximum hull mass',
|
|
||||||
minmul_sg: 'Minimum strength',
|
|
||||||
optmul_sg: 'Optimal strength',
|
|
||||||
maxmul_sg: 'Minimum strength',
|
|
||||||
minmass_psg: 'Minimum hull mass',
|
|
||||||
optmass_psg: 'Optimal hull mass',
|
|
||||||
maxmass_psg: 'Maximum hull mass',
|
|
||||||
minmul_psg: 'Minimum strength',
|
|
||||||
optmul_psg: 'Optimal strength',
|
|
||||||
maxmul_psg: 'Minimum strength',
|
|
||||||
minmass_bsg: 'Minimum hull mass',
|
|
||||||
optmass_bsg: 'Optimal hull mass',
|
|
||||||
maxmass_bsg: 'Maximum hull mass',
|
|
||||||
minmul_bsg: 'Minimum strength',
|
|
||||||
optmul_bsg: 'Optimal strength',
|
|
||||||
maxmul_bsg: 'Minimum strength',
|
|
||||||
|
|
||||||
range_s: 'Typical emission range',
|
|
||||||
|
|
||||||
// Damage types
|
|
||||||
absolute: 'Absolute',
|
|
||||||
explosive: 'Explosive',
|
|
||||||
kinetic: 'Kinetic',
|
|
||||||
thermal: 'Thermal',
|
|
||||||
|
|
||||||
// Shield sources
|
|
||||||
generator: 'Generator',
|
|
||||||
boosters: 'Boosters',
|
|
||||||
cells: 'Cells',
|
|
||||||
|
|
||||||
// Armour sources
|
|
||||||
bulkheads: 'Bulkheads',
|
|
||||||
reinforcement: 'Reinforcement',
|
|
||||||
|
|
||||||
// Panel headings and subheadings
|
|
||||||
'power and costs': 'power and costs',
|
|
||||||
'costs': 'costs',
|
|
||||||
'retrofit costs': 'retrofit costs',
|
|
||||||
'reload costs': 'reload costs',
|
|
||||||
'profiles': 'profiles',
|
|
||||||
'engine profile': 'engine profile',
|
|
||||||
'fsd profile': 'fsd profile',
|
|
||||||
'movement profile': 'movement profile',
|
|
||||||
'damage to opponent\'s shields': 'damage to opponent\'s shields',
|
|
||||||
'damage to opponent\'s hull': 'damage to opponent\'s hull',
|
|
||||||
'offence': 'offence',
|
|
||||||
'defence': 'defence',
|
|
||||||
'shield metrics': 'shield metrics',
|
|
||||||
'raw shield strength': 'raw shield strength',
|
|
||||||
'shield sources': 'shield sources',
|
|
||||||
'damage taken': 'damage taken',
|
|
||||||
'effective shield': 'effective shield',
|
|
||||||
'armour metrics': 'armour metrics',
|
|
||||||
'raw armour strength': 'raw armour strength',
|
|
||||||
'armour sources': 'armour sources',
|
|
||||||
'raw module armour': 'raw module armour',
|
|
||||||
'effective armour': 'effective armour',
|
|
||||||
'offence metrics': 'offence metrics',
|
|
||||||
'defence metrics': 'defence metrics',
|
|
||||||
// Misc items
|
|
||||||
'fuel carried': 'fuel carried',
|
|
||||||
'cargo carried': 'cargo carried',
|
|
||||||
'ship control': 'ship control',
|
|
||||||
'opponent': 'opponent',
|
|
||||||
'opponent\'s shields': 'opponent\'s shields',
|
|
||||||
'opponent\'s armour': 'opponent\'s armour',
|
|
||||||
'shield damage sources': 'shield damage sources',
|
|
||||||
'armour damage sources': 'armour damage sources',
|
|
||||||
'never': 'never',
|
|
||||||
'stock': 'stock',
|
|
||||||
'boost': 'boost',
|
|
||||||
|
|
||||||
// Help text
|
|
||||||
HELP_TEXT: `
|
|
||||||
<h1>Introduction</h1>
|
|
||||||
Coriolis is a ship builder for Elite: Dangerous. This help file provides you with the information you need to use Coriolis.
|
|
||||||
|
|
||||||
<h1>Importing Your Ship Into Coriolis</h1>
|
|
||||||
Often, you will want to start with your existing ship in Coriolis and see how particular changes might affect it, for example upgrading your FSD. There are a number of tools that can be used to import your ship without you having to create it manually. This has the added benefit of copying over any engineering modifications that have taken place as well. </p>
|
|
||||||
|
|
||||||
<h2>Importing Your Ship From EDDI</h2>
|
|
||||||
To import your ship from EDDI first ensure that your connection to the Frontier servers' companion API is working. To do this check the 'Companion App' tab where you should see "Your connection to the companion app is operational". If not then follow the instructions in the companion app tab in EDDI to connect to the Frontier servers.</p>
|
|
||||||
|
|
||||||
Once you have a working companion API connection go to the 'Shipyard' tab. At the right-hand side of each ship is an 'Export to Coriolis' button that will open your default web browser in Coriolis with the ship's build. </p>
|
|
||||||
|
|
||||||
Note that Internet Explorer and Edge might not import correctly, due to their internal restrictions on URL length. If you find that this is the case then please change your default browser to Chrome. </p>
|
|
||||||
|
|
||||||
Also, the imported information does not provide any data on the power priority or enabled status of your cargo hatch. Coriolis sets this item to have a power priority of "5" and to be disabled by default. You can change this after import in the Power Management section. </p>
|
|
||||||
|
|
||||||
<h2>Importing Your Ship From EDMC</h2>
|
|
||||||
To import your ship from EDMC once your connection to the Frontier servers' companion API is working go to 'Settings ->Configuration' and set the 'Preferred Shipyard' to 'Coriolis'. Once this is set up clicking on your ship in the main window will open your default web browser in Coriolis with the ship's build.</p>
|
|
||||||
|
|
||||||
Note that Internet Explorer and Edge might not import correctly, due to their internal restrictions on URL length. If you find that this is the case then please change your default browser to Chrome. </p>
|
|
||||||
|
|
||||||
<h1>Understanding And Using The Outfitting Panels</h1>
|
|
||||||
The outfitting page is where you will spend most of your time, and contains the information for your ship. Information on each of the panels is provided below. </p>
|
|
||||||
|
|
||||||
<h2>Key Values</h2>
|
|
||||||
Along the top of the screen are some of the key values for your build. This is a handy reference for the values, but more information is provided for the values in the further panels. </p>
|
|
||||||
|
|
||||||
Here, along with most places in Coriolis, acronyms will have tooltips explaining what they mean. Hover over the acronym to obtain more detail, or look in the glossary at the end of this help.</p>
|
|
||||||
|
|
||||||
All values are the highest possible, assuming that you an optimal setup for that particular value (maximum pips in ENG for speed, minimum fuel for jump range, etc.). This means that these values will not be affected by changes to pip settings. Details of the specific setup for each value are listed in the associated tootip.</p>
|
|
||||||
|
|
||||||
<h2>Modules</h2>
|
|
||||||
The next set of panels laid out horizontally across the screen contain the modules you have put in your build. From left to right these are the core modules, the internal modules, the hardpoints and the utility mounts. These represent the available slots in your ship and cannot be altered. Each slot has a class, or size, and in general any module up to a given size can fit in a given slot (exceptions being bulkheads, life support and sensors in core modules and restricted internal slots, which can only take a subset of module depending on their restrictions). </p>
|
|
||||||
|
|
||||||
To add a module to a slot left-click on the slot and select the required module. Only the modules capable of fitting in the selected slot will be shown. </p>
|
|
||||||
|
|
||||||
To remove a module from a slot right-click on the module. </p>
|
|
||||||
|
|
||||||
To move a module from one slot to another drag it. If you instead want to copy the module drag it whilst holding down the 'Alt' key. </p>
|
|
||||||
|
|
||||||
Clicking on the headings for each set of modules gives you the ability to either select an overall role for your ship (when clicking the core internal header) or a specific module with which you want to fill all applicable slots (when clicking the other headers). </p>
|
|
||||||
|
|
||||||
<h2>Ship Controls</h2>
|
|
||||||
The ship controls allow you to set your pips, boost, and amount of fuel and cargo that your build carries. The changes made here will effect the information supplied in the subsequent panels, giving you a clearer view of what effect different changing these items will have. </p>
|
|
||||||
|
|
||||||
Ship control settings are saved as part of a build. </p>
|
|
||||||
|
|
||||||
<h2>Opponent</h2>
|
|
||||||
The opponet selection allows you to choose your opponent. The opponent can be either a stock build of a ship or one of your own saved builds. You can also set the engagement range between you and your opponent. Your selection here will effect the information supplied in the subsequent panels, specifically the Offence and Defence panels. </p>
|
|
||||||
|
|
||||||
Opponent settings are saved as part of a build. </p>
|
|
||||||
|
|
||||||
<h2>Power and Costs Sub-panels</h2>
|
|
||||||
<h3>Power</h3>
|
|
||||||
The power management panel provides information about power usage and priorities. It allows you to enable and disable individual modules, as well as set power priorities for each module. Disabled modules will not be included in the build's statistics, with the exception of Shield Cell Banks as they are usually disabled when not in use and only enabled when required. </p>
|
|
||||||
|
|
||||||
<h3>Costs</h3>
|
|
||||||
The costs panel provides information about the costs for each of your modules, and the total cost and insurance for your build. By default Coriolis uses the standard costs, however discounts for your ship, modules and insurance can be altered in the 'Settings' at the top-right of the page.</p>
|
|
||||||
|
|
||||||
The retrofit costs provides information about the costs of changing the base build for your ship, or your saved build, to the current build.</p>
|
|
||||||
|
|
||||||
The reload costs provides information about the costs of reloading your current build.</p>
|
|
||||||
|
|
||||||
<h2>Profiles</h2>
|
|
||||||
Profiles provide graphs that show the general performance of modules in your build
|
|
||||||
|
|
||||||
<h3>Engine Profile</h3>
|
|
||||||
The engine profile panel provides information about the capabilities of your current thrusters. The graph shows you how the maximum speed alters with the overall mass of your build. The vertical dashed line on the graph shows your current mass. Your engine profile can be altered by obtaining different thrusters or engineering your existing thrusters, and you can increase your maximum speed by adding pips to the ENG capacitor as well as reducing the amount of fuel and cargo you are carrying as well as reducing the overall weight of the build. You can also temporarily increase your speed by hitting the boost button. </p>
|
|
||||||
|
|
||||||
<h3>FSD Profile</h3>
|
|
||||||
The FSD profile panel provides information about the capabilities of your current frame shift drive. The graph shows you how the maximum jump range alters with the overall mass of your build. The vertical dashed line on the graph shows your current maximum single jump range. Your FSD profile can be altered by obtaining a different FSD or engineering your existing FSD, and you can increase your maximum jump range by reducing the amount of fuel and cargo you are carrying as well as reducing the overall weight of the build, </p>
|
|
||||||
|
|
||||||
<h3>Movement Profile</h3>
|
|
||||||
The movement profile panel provides information about the capabilities of your current thrusters with your current overall mass and ENG pips settings. The diagram shows your ability to move and rotate in the different axes:
|
|
||||||
|
|
||||||
<dl>
|
|
||||||
<dt>Speed</dt><dd>The fastest the ship can move, in metres per second</dd>
|
|
||||||
<dt>Pitch</dt><dd>The fastest the ship can raise or lower its nose, in degrees per second</dd>
|
|
||||||
<dt>Roll</dt><dd>The fastest the ship can roll its body, in degrees per second</dd>
|
|
||||||
<dt>Yaw</dt><dd>The fastest the ship can turn its nose left or right, in degrees per second</dd>
|
|
||||||
</dl>
|
|
||||||
|
|
||||||
Your movement profile can be altered by obtaining different thrusters or engineering your existing thrusters, and you can increase your movement values by adding pips to the ENG capacitor as well as reducing the amount of fuel and cargo you are carrying as well as reducing the overall weight of the build. You can also temporarily increase your movement profile by hitting the boost button. </p>
|
|
||||||
|
|
||||||
<h3>Damage Profile</h3>
|
|
||||||
The damage profile provides two graphs showing how the the build's damage to the opponent's shields and hull change with engagement range. The vertical dashed line on the graph shows your current engagement range. This combines information about the build's weapons with the opponent's shields and hull to provide an accurate picture of sustained damage that can be inflicted on the opponent. </p>
|
|
||||||
|
|
||||||
<h2>Offence</h2>
|
|
||||||
<h3>Summary</h3>
|
|
||||||
The offence summary provides per-weapon information about sustained damage per second inflicted to shields and hull, along with a measure of effectiveness of that weapon. The effectiveness value has a tooltip that provides a breakdown of the effectiveness, and can include reductions or increases due to range, resistance, and either power distributor (for shields) or hardness (for hull). The final effectiveness value is calculated by multiplying these percentages together. </p>
|
|
||||||
|
|
||||||
<h3>Offence Metrics</h3>
|
|
||||||
The offence metrics panel provides information about your offence. </p>
|
|
||||||
|
|
||||||
Time to drain is a measure of how quickly your WEP capacitor will drain when firing all weapons. It is affected by the number of pips you have in your WEP capacitor, with more pips resulting in a higher WEP recharge rate and hence a longer time to drain. </p>
|
|
||||||
|
|
||||||
The next value is the time it will take you to remove your opponent's shields. This assumes that you have 100% time on target and that your engagement range stays constant. Note that if your time to remove shields is longer than your time to drain this assumes that you continue firing throughout, inflicting lower damage due to the reduced energy in your WEP capacitor. </p>
|
|
||||||
|
|
||||||
The next value is the time it will take you to remove your opponent's armour. This follows the same logic as the time to remove shields. </p>
|
|
||||||
|
|
||||||
<h3>Shield Damage Sources</h3>
|
|
||||||
The shield damage sources provides information about the sources of damage to your opponent by damage type. For each applicable type of damage (absolute explosive, kinetic, thermal) a sustained damage per second value is provided. </p>
|
|
||||||
|
|
||||||
<h3>Hull Damage Sources</h3>
|
|
||||||
The hull damage sources provides information about the sources of damage to your opponent by damage type. For each applicable type of damage (absolute explosive, kinetic, thermal) a sustained damage per second value is provided. </p>
|
|
||||||
|
|
||||||
<h2>Defence</h2>
|
|
||||||
<h3>Shield Metrics</h3>
|
|
||||||
The shield metrics provides information about your shield defence. </p>
|
|
||||||
|
|
||||||
Raw shield strength is the sum of the shield from your generator, boosters and shield cell banks. A tooltip provides a breakdown of these values. </p>
|
|
||||||
|
|
||||||
The time the shields will hold for is the time it will take your opponent' to remove your shields. This assumes that they have 100% time on target and that the engagement range stays constant. It also assumes that you fire all of your shield cell banks prior to your shields being lost. </p>
|
|
||||||
|
|
||||||
The time the shields will recover in is the time it will take your shields to go from collapsed (0%) to recovered (50%). This is affected by the number of pips you have in your SYS capacitor. </p>
|
|
||||||
|
|
||||||
The time the shields will recharge in is the time it will take your shields to go from recovered (50%) to full (100%). This is affected by the number of pips you have in your SYS capacitor. </p>
|
|
||||||
|
|
||||||
</h3>Shield Sources</h3>
|
|
||||||
This chart provides information about the sources of your shields. For each applicable source of shields (generator, boosters, shield cell banks) a value is provided. </p>
|
|
||||||
|
|
||||||
</h3>Damage Taken</h3>
|
|
||||||
This graph shows how the initial damage from the weapons of each type are reduced before their damage is applied to the shields. For each type of damage (absolute, explosive, kinetic, thermal) a percentage of the initial damage is provided. A tooltip provides a breakdown of these values. </p>
|
|
||||||
|
|
||||||
</h3>Effective Shield</h3>
|
|
||||||
This graph shows the effective shield for each damage type, found by dividing the raw shield value by the damage taken for that type. </p>
|
|
||||||
|
|
||||||
<h3>Amour Metrics</h3>
|
|
||||||
The armour metrics provides information about your armour defence. </p>
|
|
||||||
|
|
||||||
Raw armour strength is the sum of the armour from your bulkheads and hull reinforcement packages. A tooltip provides a breakdown of these values. </p>
|
|
||||||
|
|
||||||
The time the armour will hold for is the time it will take your opponent' to take your armour to 0. This assumes that they have 100% time on target, the engagement range stays constant, and that all damage is dealt to the armour rather than modules. </p>
|
|
||||||
|
|
||||||
Raw module armour is the sum of the protection from your module reinforcement packages. </p>
|
|
||||||
|
|
||||||
Protection for hardpoints is the amount of protection that your module reinforcement packages provide to hardpoints. This percentage of damage to the hardpoints will be diverted to the module reinforcement packages. </p>
|
|
||||||
|
|
||||||
Protection for all other modules is the amount of protection that your module reinforcement packages provide to everything other than hardpoints. This percentage of damage to the modules will be diverted to the module reinforcement packages. </p>
|
|
||||||
|
|
||||||
</h3>Armour Sources</h3>
|
|
||||||
This chart provides information about the sources of your armour. For each applicable source of shields (bulkheads, hull reinforcement packages) a value is provided. </p>
|
|
||||||
|
|
||||||
</h3>Damage Taken</h3>
|
|
||||||
This graph shows how the initial damage from the weapons of each type are reduced before their damage is applied to the armour. For each type of damage (absolute, explosive, kinetic, thermal) a percentage of the initial damage is provided. A tooltip provides a breakdown of these values. </p>
|
|
||||||
|
|
||||||
</h3>Effective Armour</h3>
|
|
||||||
This graph shows the effective armour for each damage type, found by dividing the raw armour value by the damage taken for that type. </p>
|
|
||||||
|
|
||||||
<h1>Keyboard Shortcuts</h1>
|
|
||||||
<dl>
|
|
||||||
<dt>Ctrl-b</dt><dd>toggle boost</dd>
|
|
||||||
<dt>Ctrl-e</dt><dd>open export dialogue (outfitting page only)</dd>
|
|
||||||
<dt>Ctrl-h</dt><dd>open help dialogue</dd>
|
|
||||||
<dt>Ctrl-i</dt><dd>open import dialogue</dd>
|
|
||||||
<dt>Ctrl-o</dt><dd>open shortlink dialogue</dd>
|
|
||||||
<dt>Ctrl-left-arrow</dt><dd>increase SYS capacitor</dd>
|
|
||||||
<dt>Ctrl-up-arrow</dt><dd>increase ENG capacitor</dd>
|
|
||||||
<dt>Ctrl-right-arrow</dt><dd>increase WEP capacitor</dd>
|
|
||||||
<dt>Ctrl-down-arrow</dt><dd>reset power distributor</dd>
|
|
||||||
<dt>Esc</dt><dd>close any open dialogue</dd>
|
|
||||||
</dl>
|
|
||||||
<h1>Glossary</h1>
|
|
||||||
<dl>
|
|
||||||
<dt>Absolute damage</dt><dd>A type of damage, without any protection. Absolute damage is always dealt at 100% regardless of if the damage is to shields, hull or modules, and irrespective of resistances</dd>
|
|
||||||
<dt>DPS</dt><dd>Damage per second; the amount of damage that a weapon or a ship can deal per second to a target under optimum conditions</dd>
|
|
||||||
<dt>EPS</dt><dd>Energy per second; the amount of energy that a weapon or a ship drains from the weapons capacitor per second when firing</dd>
|
|
||||||
<dt>HPS</dt><dd>Heat per second; the amount of heat that a weapon or a ship generates per second when firing</dd>
|
|
||||||
<dt>Effectivness</dt><dd>A comparison of the maximum DPS of a given weapon to the actual DPS of the given weapon in a specific situation. DPS can be reduced by range to the target, the target's hull and shield resistances, and the target's hardness</dd>
|
|
||||||
<dt>Explosive damage</dt><dd>A type of damage, protected against by explosive resistance</dd>
|
|
||||||
<dt>Hardness</dt><dd>The inherent resistance to damage of a ship's hull. Hardness is defined on a per-ship basis and there is currently nothing that can be done to change it. Hardness of a ship's hull is compared to the piercing of weapons: if piercing is higher than hardness the weapon does 100% damage, otherwise it does a fraction of its damage calculated as piercing/hardness</dd>
|
|
||||||
<dt>Falloff</dt><dd>The distance at which a weapons starts to do less damage than its stated DPS</dd>
|
|
||||||
<dt>Kinetic damage</dt><dd>A type of damage, protected against by kinetic resistance</dd>
|
|
||||||
<dt>SDPS</dt><dd>Sustained damage per second; the amount of damage that a weapon or a ship can deal per second to a target, taking in to account ammunition reload</dd>
|
|
||||||
<dt>SEPS</dt><dd>Sustained energy per second; the amount of energy that a weapon or a ship drains from the weapons capacitor per second when firing, taking in to account ammunition reload</dd>
|
|
||||||
<dt>SHPS</dt><dd>Sustained heat per second; the amount of heat that a weapon or a ship generates per second when firing, taking in to account ammunition reload</dd>
|
|
||||||
<dt>Thermal damage</dt><dd>A type of damage, protected against by thermal resistance</dd>
|
|
||||||
</dl>
|
|
||||||
|
|
||||||
`,
|
|
||||||
};
|
|
||||||
366
src/app/i18n/en.json
Normal file
366
src/app/i18n/en.json
Normal file
File diff suppressed because one or more lines are too long
@@ -13,196 +13,4 @@ export const formats = {
|
|||||||
shortMonths: ['ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul', 'ago', 'sep', 'oct', 'nov', 'dic']
|
shortMonths: ['ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul', 'ago', 'sep', 'oct', 'nov', 'dic']
|
||||||
};
|
};
|
||||||
|
|
||||||
export const terms = {
|
export { default as terms } from './es.json';
|
||||||
'PHRASE_EXPORT_DESC': 'Una detallada exportaci\u00f3n JSON de tu construcci\u00f3n para usarlo en otros sitios web y herramientas',
|
|
||||||
'A-Rated': 'Calidad-A',
|
|
||||||
'about': 'Acerca',
|
|
||||||
'action': 'Acci\u00f3n',
|
|
||||||
'added': 'A\u00f1adido',
|
|
||||||
'Advanced Discovery Scanner': 'Esc\u00e1ner de exploraci\u00f3n avanzado',
|
|
||||||
maneuverability: 'Maniobrabilidad',
|
|
||||||
'alpha': 'Alfa',
|
|
||||||
'ammo': 'Munici\u00f3n',
|
|
||||||
'PHRASE_CONFIRMATION': '\u00bfEst\u00e1s seguro?',
|
|
||||||
'armour': 'Blindaje',
|
|
||||||
'am': 'Unidad de auto-reparaciones',
|
|
||||||
'available': 'Disponible',
|
|
||||||
'backup': 'Reserva',
|
|
||||||
'Basic Discovery Scanner': 'Esc\u00e1ner de exploraci\u00f3n b\u00e1sico',
|
|
||||||
'bl': 'L\u00e1ser de haz',
|
|
||||||
'bins': 'contenedores',
|
|
||||||
'boost': 'incrementar',
|
|
||||||
'build': 'Construcci\u00f3n',
|
|
||||||
'build name': 'Nombre de la construcci\u00f3n',
|
|
||||||
'builds': 'Construcciones',
|
|
||||||
'bh': 'mamparos',
|
|
||||||
'ul': 'Laser de r\u00e1fagas',
|
|
||||||
'buy': 'Comprar',
|
|
||||||
'cancel': 'Cancelar',
|
|
||||||
'c': 'Ca\u00f1\u00f3n',
|
|
||||||
'capital': 'capital',
|
|
||||||
'cargo': 'Carga',
|
|
||||||
'Cargo Hatch': 'Compuerta de carga',
|
|
||||||
'cr': 'Compartimento de carga',
|
|
||||||
'cs': 'Esc\u00e1ner de carga',
|
|
||||||
'cells': 'celdas',
|
|
||||||
'Chaff Launcher': 'Lanzador de virutas',
|
|
||||||
'close': 'Cerrar',
|
|
||||||
'cc': 'Controlador de Drones de Recogida',
|
|
||||||
'compare': 'Comparar',
|
|
||||||
'compare all': 'comparar todas',
|
|
||||||
'comparison': 'Comparativa',
|
|
||||||
'comparisons': 'Comparativas',
|
|
||||||
'component': 'Componente',
|
|
||||||
'cost': 'Coste',
|
|
||||||
'costs': 'Costes',
|
|
||||||
'cm': 'Contramedidas',
|
|
||||||
'create': 'Crear',
|
|
||||||
'create new': 'Crear nuevo',
|
|
||||||
'credits': 'Cr\u00e9ditos',
|
|
||||||
'damage': 'Da\u00f1o',
|
|
||||||
'delete': 'Borrar',
|
|
||||||
'delete all': 'Borrar todo',
|
|
||||||
'dep': 'desp',
|
|
||||||
'deployed': 'Desplegado',
|
|
||||||
'detailed export': 'Exportacion detallada',
|
|
||||||
'Detailed Surface Scanner': 'Escaner de exploraci\u00f3n detallada',
|
|
||||||
'disabled': 'Desactivado',
|
|
||||||
'discount': 'Descuento',
|
|
||||||
'dc': 'Ordenador de aterrizaje',
|
|
||||||
'done': 'Hecho',
|
|
||||||
'DPS': 'DPS (Da\u00f1o Por Segundo)',
|
|
||||||
'edit data': 'Editar datos',
|
|
||||||
'efficiency': 'Eficiencia',
|
|
||||||
'Electronic Countermeasure': 'Contramedidas electr\u00f3nicas',
|
|
||||||
'empty': 'Vac\u00edo',
|
|
||||||
'ENG': 'MOT',
|
|
||||||
'enter name': 'Introduce el nombre',
|
|
||||||
'export': 'exportar',
|
|
||||||
'fixed': 'fijo',
|
|
||||||
'forum': 'Foro',
|
|
||||||
'fc': 'Ca\u00f1\u00f3n de fragmentaci\u00f3n',
|
|
||||||
'fsd': 'Motor de salto',
|
|
||||||
'ws': 'Esc\u00e1ner de Salto',
|
|
||||||
'fi': 'Interdictor FSD',
|
|
||||||
'fuel': 'Combustible',
|
|
||||||
'fs': 'Recolector de Combustible',
|
|
||||||
'ft': 'Tanque de combustible',
|
|
||||||
'fx': 'Sistema de Transferencia de Combustilble',
|
|
||||||
'full tank': 'Tanque lleno',
|
|
||||||
'Gimballed': 'Card\u00e1n',
|
|
||||||
'H': 'E',
|
|
||||||
'hardpoints': 'Montura de armas',
|
|
||||||
'hb': 'Controlador de Apertura de Bah\u00eda de Carga',
|
|
||||||
'Heat Sink Launcher': 'Eyector de Acumulador de Calor',
|
|
||||||
'huge': 'enorme',
|
|
||||||
'hull': 'Casco',
|
|
||||||
'hr': 'Sistema de Casco Reforzado',
|
|
||||||
'import': 'Importar',
|
|
||||||
'import all': 'Importar todo',
|
|
||||||
'insurance': 'Seguro',
|
|
||||||
'Intermediate Discovery Scanner': 'Esc\u00e1ner de exploraci\u00f3n media',
|
|
||||||
'internal compartments': 'Compartimentos internos',
|
|
||||||
'jump range': 'Rango de salto',
|
|
||||||
'jumps': 'Saltos',
|
|
||||||
'kw': 'Esc\u00e1ner Detector de Recompensas',
|
|
||||||
'L': 'G',
|
|
||||||
'laden': 'Cargada',
|
|
||||||
'language': 'Idioma',
|
|
||||||
'large': 'Grande',
|
|
||||||
'ls': 'Soporte vital',
|
|
||||||
'Lightweight Alloy': 'Aleaci\u00f3n ligera',
|
|
||||||
'limpets': 'Drones',
|
|
||||||
'lock factor': 'factor de bloqueo',
|
|
||||||
'mass': 'Masa',
|
|
||||||
'max': 'm\u00e1x',
|
|
||||||
'max mass': 'Masa m\u00e1xima',
|
|
||||||
'medium': 'medio',
|
|
||||||
'Military Grade Composite': 'Blindaje Militar',
|
|
||||||
'nl': 'Lanzaminas',
|
|
||||||
'Mining Lance': 'Lanza de miner\u00eda',
|
|
||||||
'ml': 'L\u00e1ser de miner\u00eda',
|
|
||||||
'Mirrored Surface Composite': 'Blindaje Reflectante',
|
|
||||||
'mr': 'Bah\u00eda de Misiles',
|
|
||||||
'mc': 'Ca\u00f1\u00f3n m\u00faltiple',
|
|
||||||
'net cost': 'Coste neto',
|
|
||||||
'PHRASE_NO_BUILDS': '\u00a1No se a\u00f1adieron plantillas para comparaci\u00f3n!',
|
|
||||||
'PHRASE_NO_RETROCH': 'No hay cambios en los ajutes',
|
|
||||||
'none': 'Nada',
|
|
||||||
'none created': 'Nada creado',
|
|
||||||
'off': 'apagado',
|
|
||||||
'on': 'encendido',
|
|
||||||
'optimal': '\u00f3ptimo',
|
|
||||||
'optimal mass': 'masa \u00f3ptima',
|
|
||||||
'optimize mass': 'optimizar masa',
|
|
||||||
'overwrite': 'Sobreescribir',
|
|
||||||
'PHRASE_IMPORT': 'Pega el JSON o imp\u00f3rtalo aqu\u00ed',
|
|
||||||
'penetration': 'penetraci\u00f3n',
|
|
||||||
'permalink': 'enlace permanente',
|
|
||||||
'pa': 'Acelerador de Plasma',
|
|
||||||
'Point Defence': 'Punto de Defensa',
|
|
||||||
'power': 'energ\u00eda',
|
|
||||||
'pd': 'distribuidor de energ\u00eda',
|
|
||||||
'pp': 'Planta de Energ\u00eda',
|
|
||||||
'priority': 'prioridad',
|
|
||||||
'proceed': 'Proceder',
|
|
||||||
'pc': 'Controlador de drones de prospecci\u00f3n',
|
|
||||||
'pl': 'L\u00e1ser de Pulso',
|
|
||||||
'PWR': 'POT',
|
|
||||||
'rg': 'Ca\u00f1\u00f3n de Riel',
|
|
||||||
'range': 'rango',
|
|
||||||
'rate': 'ratio',
|
|
||||||
'Reactive Surface Composite': 'Blindaje Reactivo',
|
|
||||||
'recharge': 'recargar',
|
|
||||||
'rf': 'Refineria',
|
|
||||||
'refuel time': 'Tiempo para repostar',
|
|
||||||
'Reinforced Alloy': 'Armadura reforzada',
|
|
||||||
'reload': 'Recargar',
|
|
||||||
'rename': 'Renombrar',
|
|
||||||
'repair': 'Reparar',
|
|
||||||
'reset': 'Reiniciar',
|
|
||||||
'ret': 'PLE',
|
|
||||||
'retracted': 'plegadas',
|
|
||||||
'retrofit costs': 'costes de equipamiento',
|
|
||||||
'retrofit from': 'equipamiento desde',
|
|
||||||
'ROF': 'RDF',
|
|
||||||
'S': 'P',
|
|
||||||
'save': 'guardar',
|
|
||||||
'sc': 'sc\u00e1ner',
|
|
||||||
'PHRASE_SELECT_BUILDS': 'Selecciona equipamientos para comparar',
|
|
||||||
'sell': 'Vender',
|
|
||||||
's': 'Sensores',
|
|
||||||
'settings': 'Configuraci\u00f3n',
|
|
||||||
'sb': 'Potenciador de Escudos',
|
|
||||||
'scb': 'C\u00e9lula de Energ\u00eda de Escudos',
|
|
||||||
'sg': 'Generador de escudos',
|
|
||||||
'shields': 'Escudos',
|
|
||||||
'ship': 'Nave ',
|
|
||||||
'ships': 'Naves',
|
|
||||||
'shortened': 'Abreviado',
|
|
||||||
'size': 'Tama\u00f1o',
|
|
||||||
'skip': 'omitir',
|
|
||||||
'small': 'Peque\u00f1o',
|
|
||||||
'speed': 'velocidad',
|
|
||||||
'standard': 'est\u00e1ndar',
|
|
||||||
'Standard Docking Computer': 'Computador de Atraque Est\u00e1ndar',
|
|
||||||
'Stock': 'De serie',
|
|
||||||
'SYS': 'SIS',
|
|
||||||
'T_LOAD': 'c-t\u00e9rmica',
|
|
||||||
't': 'Propulsores',
|
|
||||||
'time': 'Tiempo',
|
|
||||||
'tp': 'Anclaje de torpedo',
|
|
||||||
'total': 'Total',
|
|
||||||
'total range': 'Rango total',
|
|
||||||
'turret': 'torreta',
|
|
||||||
'type': 'Tipo',
|
|
||||||
'unladen': 'Sin carga',
|
|
||||||
'PHRASE_UPDATE_RDY': 'Actualizacion disponible! Haz click para recargar',
|
|
||||||
'URL': 'Enlace',
|
|
||||||
'utility': 'utilidad',
|
|
||||||
'utility mounts': 'monturas de utilidad',
|
|
||||||
'version': 'Versi\u00f3n',
|
|
||||||
'WEP': 'ARM',
|
|
||||||
'yes': 'si',
|
|
||||||
'PHRASE_BACKUP_DESC': 'Copia de seguridad de todos los datos de Coriolis para guardarlos o transferirlos a otro navegador\/dispositivo'
|
|
||||||
};
|
|
||||||
|
|||||||
193
src/app/i18n/es.json
Normal file
193
src/app/i18n/es.json
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
{
|
||||||
|
"PHRASE_EXPORT_DESC": "Una detallada exportación JSON de tu construcción para usarlo en otros sitios web y herramientas",
|
||||||
|
"A-Rated": "Calidad-A",
|
||||||
|
"about": "Acerca",
|
||||||
|
"action": "Acción",
|
||||||
|
"added": "Añadido",
|
||||||
|
"Advanced Discovery Scanner": "Escáner de exploración avanzado",
|
||||||
|
"maneuverability": "Maniobrabilidad",
|
||||||
|
"alpha": "Alfa",
|
||||||
|
"ammo": "Munición",
|
||||||
|
"PHRASE_CONFIRMATION": "¿Estás seguro?",
|
||||||
|
"armour": "Blindaje",
|
||||||
|
"am": "Unidad de auto-reparaciones",
|
||||||
|
"available": "Disponible",
|
||||||
|
"backup": "Reserva",
|
||||||
|
"Basic Discovery Scanner": "Escáner de exploración básico",
|
||||||
|
"bl": "Láser de haz",
|
||||||
|
"bins": "contenedores",
|
||||||
|
"boost": "incrementar",
|
||||||
|
"build": "Construcción",
|
||||||
|
"build name": "Nombre de la construcción",
|
||||||
|
"builds": "Construcciones",
|
||||||
|
"bh": "mamparos",
|
||||||
|
"ul": "Laser de ráfagas",
|
||||||
|
"buy": "Comprar",
|
||||||
|
"cancel": "Cancelar",
|
||||||
|
"c": "Cañón",
|
||||||
|
"capital": "capital",
|
||||||
|
"cargo": "Carga",
|
||||||
|
"Cargo Hatch": "Compuerta de carga",
|
||||||
|
"cr": "Compartimento de carga",
|
||||||
|
"cs": "Escáner de carga",
|
||||||
|
"cells": "celdas",
|
||||||
|
"Chaff Launcher": "Lanzador de virutas",
|
||||||
|
"close": "Cerrar",
|
||||||
|
"cc": "Controlador de Drones de Recogida",
|
||||||
|
"compare": "Comparar",
|
||||||
|
"compare all": "comparar todas",
|
||||||
|
"comparison": "Comparativa",
|
||||||
|
"comparisons": "Comparativas",
|
||||||
|
"component": "Componente",
|
||||||
|
"cost": "Coste",
|
||||||
|
"costs": "Costes",
|
||||||
|
"cm": "Contramedidas",
|
||||||
|
"create": "Crear",
|
||||||
|
"create new": "Crear nuevo",
|
||||||
|
"credits": "Créditos",
|
||||||
|
"damage": "Daño",
|
||||||
|
"delete": "Borrar",
|
||||||
|
"delete all": "Borrar todo",
|
||||||
|
"dep": "desp",
|
||||||
|
"deployed": "Desplegado",
|
||||||
|
"detailed export": "Exportacion detallada",
|
||||||
|
"Detailed Surface Scanner": "Escaner de exploración detallada",
|
||||||
|
"disabled": "Desactivado",
|
||||||
|
"discount": "Descuento",
|
||||||
|
"dc": "Ordenador de aterrizaje",
|
||||||
|
"done": "Hecho",
|
||||||
|
"DPS": "DPS (Daño Por Segundo)",
|
||||||
|
"edit data": "Editar datos",
|
||||||
|
"efficiency": "Eficiencia",
|
||||||
|
"Electronic Countermeasure": "Contramedidas electrónicas",
|
||||||
|
"empty": "Vacío",
|
||||||
|
"ENG": "MOT",
|
||||||
|
"enter name": "Introduce el nombre",
|
||||||
|
"export": "exportar",
|
||||||
|
"fixed": "fijo",
|
||||||
|
"forum": "Foro",
|
||||||
|
"fc": "Cañón de fragmentación",
|
||||||
|
"fsd": "Motor de salto",
|
||||||
|
"ws": "Escáner de Salto",
|
||||||
|
"fi": "Interdictor FSD",
|
||||||
|
"fuel": "Combustible",
|
||||||
|
"fs": "Recolector de Combustible",
|
||||||
|
"ft": "Tanque de combustible",
|
||||||
|
"fx": "Sistema de Transferencia de Combustilble",
|
||||||
|
"full tank": "Tanque lleno",
|
||||||
|
"Gimballed": "Cardán",
|
||||||
|
"H": "E",
|
||||||
|
"hardpoints": "Montura de armas",
|
||||||
|
"hb": "Controlador de Apertura de Bahía de Carga",
|
||||||
|
"Heat Sink Launcher": "Eyector de Acumulador de Calor",
|
||||||
|
"huge": "enorme",
|
||||||
|
"hull": "Casco",
|
||||||
|
"hr": "Sistema de Casco Reforzado",
|
||||||
|
"import": "Importar",
|
||||||
|
"import all": "Importar todo",
|
||||||
|
"insurance": "Seguro",
|
||||||
|
"Intermediate Discovery Scanner": "Escáner de exploración media",
|
||||||
|
"internal compartments": "Compartimentos internos",
|
||||||
|
"jump range": "Rango de salto",
|
||||||
|
"jumps": "Saltos",
|
||||||
|
"kw": "Escáner Detector de Recompensas",
|
||||||
|
"L": "G",
|
||||||
|
"laden": "Cargada",
|
||||||
|
"language": "Idioma",
|
||||||
|
"large": "Grande",
|
||||||
|
"ls": "Soporte vital",
|
||||||
|
"Lightweight Alloy": "Aleación ligera",
|
||||||
|
"limpets": "Drones",
|
||||||
|
"lock factor": "factor de bloqueo",
|
||||||
|
"mass": "Masa",
|
||||||
|
"max": "máx",
|
||||||
|
"max mass": "Masa máxima",
|
||||||
|
"medium": "medio",
|
||||||
|
"Military Grade Composite": "Blindaje Militar",
|
||||||
|
"nl": "Lanzaminas",
|
||||||
|
"Mining Lance": "Lanza de minería",
|
||||||
|
"ml": "Láser de minería",
|
||||||
|
"Mirrored Surface Composite": "Blindaje Reflectante",
|
||||||
|
"mr": "Bahía de Misiles",
|
||||||
|
"mc": "Cañón múltiple",
|
||||||
|
"net cost": "Coste neto",
|
||||||
|
"PHRASE_NO_BUILDS": "¡No se añadieron plantillas para comparación!",
|
||||||
|
"PHRASE_NO_RETROCH": "No hay cambios en los ajutes",
|
||||||
|
"none": "Nada",
|
||||||
|
"none created": "Nada creado",
|
||||||
|
"off": "apagado",
|
||||||
|
"on": "encendido",
|
||||||
|
"optimal": "óptimo",
|
||||||
|
"optimal mass": "masa óptima",
|
||||||
|
"optimize mass": "optimizar masa",
|
||||||
|
"overwrite": "Sobreescribir",
|
||||||
|
"PHRASE_IMPORT": "Pega el JSON o impórtalo aquí",
|
||||||
|
"penetration": "penetración",
|
||||||
|
"permalink": "enlace permanente",
|
||||||
|
"pa": "Acelerador de Plasma",
|
||||||
|
"Point Defence": "Punto de Defensa",
|
||||||
|
"power": "energía",
|
||||||
|
"pd": "distribuidor de energía",
|
||||||
|
"pp": "Planta de Energía",
|
||||||
|
"priority": "prioridad",
|
||||||
|
"proceed": "Proceder",
|
||||||
|
"pc": "Controlador de drones de prospección",
|
||||||
|
"pl": "Láser de Pulso",
|
||||||
|
"PWR": "POT",
|
||||||
|
"rg": "Cañón de Riel",
|
||||||
|
"range": "rango",
|
||||||
|
"rate": "ratio",
|
||||||
|
"Reactive Surface Composite": "Blindaje Reactivo",
|
||||||
|
"recharge": "recargar",
|
||||||
|
"rf": "Refineria",
|
||||||
|
"refuel time": "Tiempo para repostar",
|
||||||
|
"Reinforced Alloy": "Armadura reforzada",
|
||||||
|
"reload": "Recargar",
|
||||||
|
"rename": "Renombrar",
|
||||||
|
"repair": "Reparar",
|
||||||
|
"reset": "Reiniciar",
|
||||||
|
"ret": "PLE",
|
||||||
|
"retracted": "plegadas",
|
||||||
|
"retrofit costs": "costes de equipamiento",
|
||||||
|
"retrofit from": "equipamiento desde",
|
||||||
|
"ROF": "RDF",
|
||||||
|
"S": "P",
|
||||||
|
"save": "guardar",
|
||||||
|
"sc": "scáner",
|
||||||
|
"PHRASE_SELECT_BUILDS": "Selecciona equipamientos para comparar",
|
||||||
|
"sell": "Vender",
|
||||||
|
"s": "Sensores",
|
||||||
|
"settings": "Configuración",
|
||||||
|
"sb": "Potenciador de Escudos",
|
||||||
|
"scb": "Célula de Energía de Escudos",
|
||||||
|
"sg": "Generador de escudos",
|
||||||
|
"shields": "Escudos",
|
||||||
|
"ship": "Nave ",
|
||||||
|
"ships": "Naves",
|
||||||
|
"shortened": "Abreviado",
|
||||||
|
"size": "Tamaño",
|
||||||
|
"skip": "omitir",
|
||||||
|
"small": "Pequeño",
|
||||||
|
"speed": "velocidad",
|
||||||
|
"standard": "estándar",
|
||||||
|
"Standard Docking Computer": "Computador de Atraque Estándar",
|
||||||
|
"Stock": "De serie",
|
||||||
|
"SYS": "SIS",
|
||||||
|
"T_LOAD": "c-térmica",
|
||||||
|
"t": "Propulsores",
|
||||||
|
"time": "Tiempo",
|
||||||
|
"tp": "Anclaje de torpedo",
|
||||||
|
"total": "Total",
|
||||||
|
"total range": "Rango total",
|
||||||
|
"turret": "torreta",
|
||||||
|
"type": "Tipo",
|
||||||
|
"unladen": "Sin carga",
|
||||||
|
"PHRASE_UPDATE_RDY": "Actualizacion disponible! Haz click para recargar",
|
||||||
|
"URL": "Enlace",
|
||||||
|
"utility": "utilidad",
|
||||||
|
"utility mounts": "monturas de utilidad",
|
||||||
|
"version": "Versión",
|
||||||
|
"WEP": "ARM",
|
||||||
|
"yes": "si",
|
||||||
|
"PHRASE_BACKUP_DESC": "Copia de seguridad de todos los datos de Coriolis para guardarlos o transferirlos a otro navegador/dispositivo"
|
||||||
|
}
|
||||||
@@ -13,143 +13,4 @@ export const formats = {
|
|||||||
shortMonths: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.', 'août', 'sept.', 'oct.', 'nov.', 'déc.']
|
shortMonths: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.', 'août', 'sept.', 'oct.', 'nov.', 'déc.']
|
||||||
};
|
};
|
||||||
|
|
||||||
export const terms = {
|
export { default as terms } from './fr.json';
|
||||||
// Phrases
|
|
||||||
PHRASE_BACKUP_DESC: 'Copie des données Coriolis pour l\'utilisation dans d\'autres sites et outils', // Backup of all Coriolis data to save or transfer to another browser/device
|
|
||||||
PHRASE_CONFIRMATION: 'Êtes-vous sûr?', // Are You Sure?
|
|
||||||
PHRASE_EXPORT_DESC: 'Un export détaillé en JSON de votre configuration pour l\'utilisation dans d\'autres sites et outils', // A detailed JSON export of your build for use in other sites and tools
|
|
||||||
PHRASE_FASTEST_RANGE: 'Portée maximale en cas de sauts successifs', // Consecutive max range jumps
|
|
||||||
PHRASE_IMPORT: 'Coller JSON ou importer ici', // Paste JSON or import here
|
|
||||||
PHRASE_LADEN: 'Masse du Vaisseau + Carburant + Cargo', // Ship Mass + Fuel + Cargo
|
|
||||||
PHRASE_NO_BUILDS: 'Aucune configuration ajoutée pour la comparaison', // No builds added to comparison!
|
|
||||||
PHRASE_NO_RETROCH: 'Aucun changement de configuration', // No Retrofitting changes
|
|
||||||
PHRASE_SELECT_BUILDS: 'Sélectionner les configurations à comparer', // Select Builds to Compare
|
|
||||||
PHRASE_SG_RECHARGE: 'Temps de charge de 50% à 100 %', // Time from 50% to 100% Charge
|
|
||||||
PHRASE_SG_RECOVER: 'Temps de redémarrage après perte du bouclier', // Recovery (to 50%) after collapse
|
|
||||||
PHRASE_UNLADEN: 'Masse du Vaisseau hors Carburant et Cargo', // Ship Mass excluding Fuel and Cargo
|
|
||||||
PHRASE_UPDATE_RDY: 'Mise à jour disponible ! Cliquez pour rafraichir', // Update Available! Click to Refresh
|
|
||||||
|
|
||||||
// Units / Metrics
|
|
||||||
Ls: 'SL', // Light seconds
|
|
||||||
LY: 'AL', // Light Years
|
|
||||||
|
|
||||||
// Sizes
|
|
||||||
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
|
|
||||||
about: 'à propos', // Link to about page / about Coriolis.io
|
|
||||||
added: 'ajouté',
|
|
||||||
ammo: 'munition', // Ammunition
|
|
||||||
armour: 'Armure',
|
|
||||||
available: 'Disponibilité', // Available options
|
|
||||||
backup: 'sauvegarde',
|
|
||||||
bays: 'baies',
|
|
||||||
bins: 'bacs', // Number of Mining Refinery bins
|
|
||||||
build: 'Configuration', // Shorthand for the build/configuration/design name
|
|
||||||
'build name': 'Nom de la configuration', // Ship build/configuration/design name
|
|
||||||
builds: 'Configurations', // Ship build/configuration/design names
|
|
||||||
buy: 'Acheter',
|
|
||||||
cancel: 'Annuler',
|
|
||||||
cargo: 'Soute',
|
|
||||||
cells: 'Cellule', // Number of cells in a shield cell bank
|
|
||||||
close: 'fermer',
|
|
||||||
compare: 'comparer',
|
|
||||||
'compare all': 'tout comparer',
|
|
||||||
comparison: 'comparaison',
|
|
||||||
comparisons: 'comparaisons',
|
|
||||||
cost: 'coût', // Cost / price of a module or price of a ship
|
|
||||||
costs: 'coûts', // Costs / prices of a modules or prices of ships
|
|
||||||
create: 'Créer',
|
|
||||||
'create new': 'Créer nouveau',
|
|
||||||
credits: 'crédits',
|
|
||||||
damage: 'dégât',
|
|
||||||
'damage per second': 'dégât par seconde',
|
|
||||||
delete: 'supprimer',
|
|
||||||
'delete all': 'tout supprimer',
|
|
||||||
dep: 'depl', // Weapons/Hardpoints Deployed abbreviation
|
|
||||||
deployed: 'déployé', // Weapons/Hardpoints Deployed
|
|
||||||
'detailed export': 'export détaillé',
|
|
||||||
disabled: 'désactivé',
|
|
||||||
discount: 'ristourne',
|
|
||||||
'edit data': 'Editer donnée',
|
|
||||||
efficiency: 'rendement', // Power Plant efficiency
|
|
||||||
empty: 'Vide',
|
|
||||||
'empty all': 'vide tout',
|
|
||||||
'Enter Name': 'Entrer nom',
|
|
||||||
Explorer: 'explorateur',
|
|
||||||
'fastest range': 'gamme la plus rapide', // Fastet totaljump range - sum of succesive jumps
|
|
||||||
fuel: 'carburant',
|
|
||||||
'fuel level': 'niveau de carburant', // Percent of fuel (T) in the tank
|
|
||||||
'full tank': 'Réservoir plein',
|
|
||||||
hardpoints: 'Points d\'emport',
|
|
||||||
hull: 'Coque', // Ships hull
|
|
||||||
import: 'Importer',
|
|
||||||
insurance: 'Assurance',
|
|
||||||
'internal compartments': 'compartiments internes',
|
|
||||||
jump: 'saut', // Single jump range
|
|
||||||
'jump range': 'Distance de saut',
|
|
||||||
jumps: 'Sauts',
|
|
||||||
laden: 'chargé',
|
|
||||||
language: 'Langage',
|
|
||||||
maneuverability: 'maniabilité',
|
|
||||||
manufacturer: 'fabricant',
|
|
||||||
mass: 'Masse',
|
|
||||||
'mass lock factor': 'facteur inhibition de masse',
|
|
||||||
'max mass': 'masse max',
|
|
||||||
MLF: 'FIM', // Mass Lock Factor Abbreviation
|
|
||||||
'net cost': 'coûts nets',
|
|
||||||
no: 'non',
|
|
||||||
'none created': 'Rien de créé',
|
|
||||||
ok: 'D\'accord',
|
|
||||||
'optimal mass': 'masse optimale', // Lowest weight / best weight for jump distance, etc
|
|
||||||
optimize: 'optimiser',
|
|
||||||
pen: 'pén.', // Armour peneration abbreviation
|
|
||||||
permalink: 'lien durable',
|
|
||||||
power: 'énergie', // Power = Energy / second. Power generated by the power plant, or power consumed (MW / Mega Watts). Used in the power plant section
|
|
||||||
proceed: 'continuer',
|
|
||||||
PWR: 'P', // Power Abbreviation. See Power
|
|
||||||
qty: 'quantité', // Quantity abbreviation
|
|
||||||
range: 'portée',
|
|
||||||
rate: 'cadence',
|
|
||||||
recharge: 'recharger', // Shield Recharge time from 50% -> 100%
|
|
||||||
recovery: 'récupération', // Shield recovery time (after losing shields/turning on -> 50%)
|
|
||||||
'refuel time': 'Temps de remplissage', // Time to refuel the tank when scooping
|
|
||||||
reload: 'recharger', // Reload weapon/hardpoint
|
|
||||||
'reload costs': 'recharger coûts',
|
|
||||||
rename: 'renommer',
|
|
||||||
repair: 'réparer',
|
|
||||||
reset: 'Réinitialisation',
|
|
||||||
ret: 'esc', // Retracted abbreviation
|
|
||||||
retracted: 'escamoté', // Weapons/Hardpoints retracted
|
|
||||||
'retrofit costs': 'Valeur de rachat', // The cost difference when upgrading / downgrading a component
|
|
||||||
'retrofit from': 'Racheter de', // Retrofit from Build A against build B
|
|
||||||
ROF: 'cadence', // Rate of Fire abbreviation
|
|
||||||
roles: 'rôles', // Commander/Ship build roles - e.g. Trader, Bounty-Hunter, Explorer, etc
|
|
||||||
save: 'sauvegarder',
|
|
||||||
sell: 'vendre',
|
|
||||||
settings: 'paramètres', // Coriolis application settings
|
|
||||||
shields: 'boucliers',
|
|
||||||
ship: 'vaisseau',
|
|
||||||
ships: 'vaisseaux',
|
|
||||||
shortened: 'raccourci', // Standard/Stock build of a ship when purchased new
|
|
||||||
size: 'taille',
|
|
||||||
skip: 'Suivant', // Skip past something / ignore it
|
|
||||||
speed: 'vitesse',
|
|
||||||
Stock: 'de base', // Thermal-load abbreviation
|
|
||||||
strength: 'force', // Strength in reference to Shield Strength
|
|
||||||
subtotal: 'Sous-Total',
|
|
||||||
'T-Load': 'degrés', // Thermal load abbreviation
|
|
||||||
time: 'temps', // time it takes to complete something
|
|
||||||
tooltips: 'infobulles', // Tooltips setting - show/hide
|
|
||||||
'total range': 'plage totale',
|
|
||||||
Trader: 'commerçant', // Trader role
|
|
||||||
'unit cost': 'coût unitaire',
|
|
||||||
unladen: 'Non chargé', // No cargo or fuel
|
|
||||||
'utility mounts': 'Support utilitaire',
|
|
||||||
WEP: 'ARM', // Abbreviation - Weapon recharge rate for power distributor
|
|
||||||
yes: 'oui'
|
|
||||||
};
|
|
||||||
|
|||||||
133
src/app/i18n/fr.json
Normal file
133
src/app/i18n/fr.json
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
{
|
||||||
|
"PHRASE_BACKUP_DESC": "Copie des données Coriolis pour l'utilisation dans d'autres sites et outils",
|
||||||
|
"PHRASE_CONFIRMATION": "Êtes-vous sûr?",
|
||||||
|
"PHRASE_EXPORT_DESC": "Un export détaillé en JSON de votre configuration pour l'utilisation dans d'autres sites et outils",
|
||||||
|
"PHRASE_FASTEST_RANGE": "Portée maximale en cas de sauts successifs",
|
||||||
|
"PHRASE_IMPORT": "Coller JSON ou importer ici",
|
||||||
|
"PHRASE_LADEN": "Masse du Vaisseau + Carburant + Cargo",
|
||||||
|
"PHRASE_NO_BUILDS": "Aucune configuration ajoutée pour la comparaison",
|
||||||
|
"PHRASE_NO_RETROCH": "Aucun changement de configuration",
|
||||||
|
"PHRASE_SELECT_BUILDS": "Sélectionner les configurations à comparer",
|
||||||
|
"PHRASE_SG_RECHARGE": "Temps de charge de 50% à 100 %",
|
||||||
|
"PHRASE_SG_RECOVER": "Temps de redémarrage après perte du bouclier",
|
||||||
|
"PHRASE_UNLADEN": "Masse du Vaisseau hors Carburant et Cargo",
|
||||||
|
"PHRASE_UPDATE_RDY": "Mise à jour disponible ! Cliquez pour rafraichir",
|
||||||
|
"Ls": "SL",
|
||||||
|
"LY": "AL",
|
||||||
|
"L": "G",
|
||||||
|
"large": "grand",
|
||||||
|
"medium": "moyen",
|
||||||
|
"S": "P",
|
||||||
|
"small": "petit",
|
||||||
|
"about": "à propos",
|
||||||
|
"added": "ajouté",
|
||||||
|
"ammo": "munition",
|
||||||
|
"armour": "Armure",
|
||||||
|
"available": "Disponibilité",
|
||||||
|
"backup": "sauvegarde",
|
||||||
|
"bays": "baies",
|
||||||
|
"bins": "bacs",
|
||||||
|
"build": "Configuration",
|
||||||
|
"build name": "Nom de la configuration",
|
||||||
|
"builds": "Configurations",
|
||||||
|
"buy": "Acheter",
|
||||||
|
"cancel": "Annuler",
|
||||||
|
"cargo": "Soute",
|
||||||
|
"cells": "Cellule",
|
||||||
|
"close": "fermer",
|
||||||
|
"compare": "comparer",
|
||||||
|
"compare all": "tout comparer",
|
||||||
|
"comparison": "comparaison",
|
||||||
|
"comparisons": "comparaisons",
|
||||||
|
"cost": "coût",
|
||||||
|
"costs": "coûts",
|
||||||
|
"create": "Créer",
|
||||||
|
"create new": "Créer nouveau",
|
||||||
|
"credits": "crédits",
|
||||||
|
"damage": "dégât",
|
||||||
|
"damage per second": "dégât par seconde",
|
||||||
|
"delete": "supprimer",
|
||||||
|
"delete all": "tout supprimer",
|
||||||
|
"dep": "depl",
|
||||||
|
"deployed": "déployé",
|
||||||
|
"detailed export": "export détaillé",
|
||||||
|
"disabled": "désactivé",
|
||||||
|
"discount": "ristourne",
|
||||||
|
"edit data": "Editer donnée",
|
||||||
|
"efficiency": "rendement",
|
||||||
|
"empty": "Vide",
|
||||||
|
"empty all": "vide tout",
|
||||||
|
"Enter Name": "Entrer nom",
|
||||||
|
"Explorer": "explorateur",
|
||||||
|
"farthest range": "gamme la plus rapide",
|
||||||
|
"fuel": "carburant",
|
||||||
|
"fuel level": "niveau de carburant",
|
||||||
|
"full tank": "Réservoir plein",
|
||||||
|
"hardpoints": "Points d'emport",
|
||||||
|
"hull": "Coque",
|
||||||
|
"import": "Importer",
|
||||||
|
"insurance": "Assurance",
|
||||||
|
"internal compartments": "compartiments internes",
|
||||||
|
"jump": "saut",
|
||||||
|
"jump range": "Distance de saut",
|
||||||
|
"jumps": "Sauts",
|
||||||
|
"laden": "chargé",
|
||||||
|
"language": "Langage",
|
||||||
|
"maneuverability": "maniabilité",
|
||||||
|
"manufacturer": "fabricant",
|
||||||
|
"mass": "Masse",
|
||||||
|
"mass lock factor": "facteur inhibition de masse",
|
||||||
|
"max mass": "masse max",
|
||||||
|
"MLF": "FIM",
|
||||||
|
"net cost": "coûts nets",
|
||||||
|
"no": "non",
|
||||||
|
"none created": "Rien de créé",
|
||||||
|
"ok": "D'accord",
|
||||||
|
"optimal mass": "masse optimale",
|
||||||
|
"optimize": "optimiser",
|
||||||
|
"pen": "pén.",
|
||||||
|
"permalink": "lien durable",
|
||||||
|
"power": "énergie",
|
||||||
|
"proceed": "continuer",
|
||||||
|
"PWR": "P",
|
||||||
|
"qty": "quantité",
|
||||||
|
"range": "portée",
|
||||||
|
"rate": "cadence",
|
||||||
|
"recharge": "recharger",
|
||||||
|
"recovery": "récupération",
|
||||||
|
"refuel time": "Temps de remplissage",
|
||||||
|
"reload": "recharger",
|
||||||
|
"reload costs": "recharger coûts",
|
||||||
|
"rename": "renommer",
|
||||||
|
"repair": "réparer",
|
||||||
|
"reset": "Réinitialisation",
|
||||||
|
"ret": "esc",
|
||||||
|
"retracted": "escamoté",
|
||||||
|
"retrofit costs": "Valeur de rachat",
|
||||||
|
"retrofit from": "Racheter de",
|
||||||
|
"ROF": "cadence",
|
||||||
|
"roles": "rôles",
|
||||||
|
"save": "sauvegarder",
|
||||||
|
"sell": "vendre",
|
||||||
|
"settings": "paramètres",
|
||||||
|
"shields": "boucliers",
|
||||||
|
"ship": "vaisseau",
|
||||||
|
"ships": "vaisseaux",
|
||||||
|
"shortened": "raccourci",
|
||||||
|
"size": "taille",
|
||||||
|
"skip": "Suivant",
|
||||||
|
"speed": "vitesse",
|
||||||
|
"Stock": "de base",
|
||||||
|
"strength": "force",
|
||||||
|
"subtotal": "Sous-Total",
|
||||||
|
"T-Load": "degrés",
|
||||||
|
"time": "temps",
|
||||||
|
"tooltips": "infobulles",
|
||||||
|
"total range": "plage totale",
|
||||||
|
"Trader": "commerçant",
|
||||||
|
"unit cost": "coût unitaire",
|
||||||
|
"unladen": "Non chargé",
|
||||||
|
"utility mounts": "Support utilitaire",
|
||||||
|
"WEP": "ARM",
|
||||||
|
"yes": "oui"
|
||||||
|
}
|
||||||
@@ -13,117 +13,4 @@ export const formats = {
|
|||||||
shortMonths: ['Gen', 'Feb', 'Mar', 'Apr', 'Mag', 'Giu', 'Lug', 'Ago', 'Set', 'Ott', 'Nov', 'Dic']
|
shortMonths: ['Gen', 'Feb', 'Mar', 'Apr', 'Mag', 'Giu', 'Lug', 'Ago', 'Set', 'Ott', 'Nov', 'Dic']
|
||||||
};
|
};
|
||||||
|
|
||||||
export const terms = {
|
export { default as terms } from './it.json';
|
||||||
PHRASE_EXPORT_DESC: 'Un export dettagliato in formato JSON della tua configurazione per essere usato in altri siti o tools',
|
|
||||||
'A-Rated': 'Classe A',
|
|
||||||
about: 'Info su Coriolis',
|
|
||||||
action: 'azione',
|
|
||||||
added: 'aggiunto',
|
|
||||||
Advanced: 'Avanzato',
|
|
||||||
maneuverability: 'agilità',
|
|
||||||
ammo: 'munizioni',
|
|
||||||
PHRASE_CONFIRMATION: 'Sei sicuro ?',
|
|
||||||
armour: 'armatura',
|
|
||||||
available: 'disponibile',
|
|
||||||
bins: 'contenitore',
|
|
||||||
build: 'configurazione',
|
|
||||||
'build name': 'Nome Configurazione',
|
|
||||||
builds: 'configurazioni',
|
|
||||||
buy: 'compra',
|
|
||||||
cancel: 'cancella',
|
|
||||||
cells: 'celle',
|
|
||||||
close: 'chiudi',
|
|
||||||
compare: 'confronta',
|
|
||||||
'compare all': 'confronta tutti',
|
|
||||||
comparison: 'comparazione',
|
|
||||||
comparisons: 'comparazioni',
|
|
||||||
component: 'componente',
|
|
||||||
cost: 'costo',
|
|
||||||
costs: 'costi',
|
|
||||||
cm: 'Contromisure',
|
|
||||||
create: 'crea',
|
|
||||||
'create new': 'crea nuovo',
|
|
||||||
credits: 'crediti',
|
|
||||||
damage: 'danno',
|
|
||||||
delete: 'elimina',
|
|
||||||
'delete all': 'elimina tutto',
|
|
||||||
dep: 'dep',
|
|
||||||
deployed: 'deployed',
|
|
||||||
'detailed export': 'esportazione dettagliata',
|
|
||||||
disabled: 'disabilita',
|
|
||||||
discount: 'sconto',
|
|
||||||
done: 'fatto',
|
|
||||||
'edit data': 'modifica i dati',
|
|
||||||
efficiency: 'efficenza',
|
|
||||||
empty: 'vuoto',
|
|
||||||
Enforcer: 'Rinforzatore',
|
|
||||||
'enter name': 'Inserisci un nome',
|
|
||||||
export: 'esporta',
|
|
||||||
fixed: 'fissi',
|
|
||||||
fuel: 'carburante',
|
|
||||||
'full tank': 'Serbatoio Pieno',
|
|
||||||
huge: 'enorme',
|
|
||||||
hull: 'corazza',
|
|
||||||
import: 'importa',
|
|
||||||
'import all': 'importa tutto',
|
|
||||||
insurance: 'assicurazione',
|
|
||||||
'internal compartments': 'compartimenti interni',
|
|
||||||
'jump range': 'distanza di salto',
|
|
||||||
jumps: 'salti',
|
|
||||||
laden: 'carico',
|
|
||||||
language: 'lingua',
|
|
||||||
large: 'largo',
|
|
||||||
mass: 'massa',
|
|
||||||
max: 'massimo',
|
|
||||||
'max mass': 'massa massimale',
|
|
||||||
medium: 'medio',
|
|
||||||
'net cost': 'costo netto',
|
|
||||||
PHRASE_NO_BUILDS: 'nessuna configurazione è stata aggiunta per la comparazione!',
|
|
||||||
PHRASE_NO_RETROCH: 'Nessun cambiamento di Retrofitting',
|
|
||||||
none: 'nessuno',
|
|
||||||
'none created': 'nessuno creato',
|
|
||||||
optimal: 'ottimale',
|
|
||||||
'optimal mass': 'massa ottimale',
|
|
||||||
'optimize mass': 'ottimizza la massa',
|
|
||||||
overwrite: 'sovrasscrivi',
|
|
||||||
PHRASE_IMPORT: 'Incolla un JSON o importalo qua',
|
|
||||||
penetration: 'penetrazione',
|
|
||||||
power: 'potenza',
|
|
||||||
priority: 'priorità',
|
|
||||||
proceed: 'procedi',
|
|
||||||
range: 'distanza',
|
|
||||||
rate: 'rateo',
|
|
||||||
recharge: 'ricarica',
|
|
||||||
reload: 'ricarica',
|
|
||||||
rename: 'rinomina',
|
|
||||||
repair: 'ripara',
|
|
||||||
reset: 'resetta',
|
|
||||||
retracted: 'retratti',
|
|
||||||
'retrofit costs': 'costi di retrofit',
|
|
||||||
'retrofit from': 'retrofit da',
|
|
||||||
save: 'salva',
|
|
||||||
sell: 'vendi',
|
|
||||||
settings: 'impostazioni',
|
|
||||||
shields: 'scudi',
|
|
||||||
ship: 'nave',
|
|
||||||
ships: 'navi',
|
|
||||||
shortened: 'accorciato',
|
|
||||||
size: 'grandezza',
|
|
||||||
skip: 'salta',
|
|
||||||
small: 'piccolo',
|
|
||||||
speed: 'velocità',
|
|
||||||
Stock: 'appena comprata',
|
|
||||||
t: 'thrusters',
|
|
||||||
time: 'tempo',
|
|
||||||
total: 'totale',
|
|
||||||
'total range': 'distanza totale',
|
|
||||||
turret: 'torrette',
|
|
||||||
type: 'tipo',
|
|
||||||
unladen: 'scarico',
|
|
||||||
PHRASE_UPDATE_RDY: 'Aggiornamenti disponibili ! Clicca per Aggiornare',
|
|
||||||
utility: 'supporti',
|
|
||||||
'utility mounts': 'supporti di utilità',
|
|
||||||
version: 'versione',
|
|
||||||
yes: 'sì',
|
|
||||||
PHRASE_BACKUP_DESC: 'Esportazione di tutti i dati su Coriolis per salvarli o trasferirli in un altro Browser/dispositivo'
|
|
||||||
};
|
|
||||||
|
|||||||
114
src/app/i18n/it.json
Normal file
114
src/app/i18n/it.json
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
{
|
||||||
|
"PHRASE_EXPORT_DESC": "Un export dettagliato in formato JSON della tua configurazione per essere usato in altri siti o tools",
|
||||||
|
"A-Rated": "Classe A",
|
||||||
|
"about": "Info su Coriolis",
|
||||||
|
"action": "azione",
|
||||||
|
"added": "aggiunto",
|
||||||
|
"Advanced": "Avanzato",
|
||||||
|
"maneuverability": "agilità",
|
||||||
|
"ammo": "munizioni",
|
||||||
|
"PHRASE_CONFIRMATION": "Sei sicuro ?",
|
||||||
|
"armour": "armatura",
|
||||||
|
"available": "disponibile",
|
||||||
|
"bins": "contenitore",
|
||||||
|
"build": "configurazione",
|
||||||
|
"build name": "Nome Configurazione",
|
||||||
|
"builds": "configurazioni",
|
||||||
|
"buy": "compra",
|
||||||
|
"cancel": "cancella",
|
||||||
|
"cells": "celle",
|
||||||
|
"close": "chiudi",
|
||||||
|
"compare": "confronta",
|
||||||
|
"compare all": "confronta tutti",
|
||||||
|
"comparison": "comparazione",
|
||||||
|
"comparisons": "comparazioni",
|
||||||
|
"component": "componente",
|
||||||
|
"cost": "costo",
|
||||||
|
"costs": "costi",
|
||||||
|
"cm": "Contromisure",
|
||||||
|
"create": "crea",
|
||||||
|
"create new": "crea nuovo",
|
||||||
|
"credits": "crediti",
|
||||||
|
"damage": "danno",
|
||||||
|
"delete": "elimina",
|
||||||
|
"delete all": "elimina tutto",
|
||||||
|
"dep": "dep",
|
||||||
|
"deployed": "deployed",
|
||||||
|
"detailed export": "esportazione dettagliata",
|
||||||
|
"disabled": "disabilita",
|
||||||
|
"discount": "sconto",
|
||||||
|
"done": "fatto",
|
||||||
|
"edit data": "modifica i dati",
|
||||||
|
"efficiency": "efficenza",
|
||||||
|
"empty": "vuoto",
|
||||||
|
"Enforcer": "Rinforzatore",
|
||||||
|
"enter name": "Inserisci un nome",
|
||||||
|
"export": "esporta",
|
||||||
|
"fixed": "fissi",
|
||||||
|
"fuel": "carburante",
|
||||||
|
"full tank": "Serbatoio Pieno",
|
||||||
|
"huge": "enorme",
|
||||||
|
"hull": "corazza",
|
||||||
|
"import": "importa",
|
||||||
|
"import all": "importa tutto",
|
||||||
|
"insurance": "assicurazione",
|
||||||
|
"internal compartments": "compartimenti interni",
|
||||||
|
"jump range": "distanza di salto",
|
||||||
|
"jumps": "salti",
|
||||||
|
"laden": "carico",
|
||||||
|
"language": "lingua",
|
||||||
|
"large": "largo",
|
||||||
|
"mass": "massa",
|
||||||
|
"max": "massimo",
|
||||||
|
"max mass": "massa massimale",
|
||||||
|
"medium": "medio",
|
||||||
|
"net cost": "costo netto",
|
||||||
|
"PHRASE_NO_BUILDS": "nessuna configurazione è stata aggiunta per la comparazione!",
|
||||||
|
"PHRASE_NO_RETROCH": "Nessun cambiamento di Retrofitting",
|
||||||
|
"none": "nessuno",
|
||||||
|
"none created": "nessuno creato",
|
||||||
|
"optimal": "ottimale",
|
||||||
|
"optimal mass": "massa ottimale",
|
||||||
|
"optimize mass": "ottimizza la massa",
|
||||||
|
"overwrite": "sovrasscrivi",
|
||||||
|
"PHRASE_IMPORT": "Incolla un JSON o importalo qua",
|
||||||
|
"penetration": "penetrazione",
|
||||||
|
"power": "potenza",
|
||||||
|
"priority": "priorità",
|
||||||
|
"proceed": "procedi",
|
||||||
|
"range": "distanza",
|
||||||
|
"rate": "rateo",
|
||||||
|
"recharge": "ricarica",
|
||||||
|
"reload": "ricarica",
|
||||||
|
"rename": "rinomina",
|
||||||
|
"repair": "ripara",
|
||||||
|
"reset": "resetta",
|
||||||
|
"retracted": "retratti",
|
||||||
|
"retrofit costs": "costi di retrofit",
|
||||||
|
"retrofit from": "retrofit da",
|
||||||
|
"save": "salva",
|
||||||
|
"sell": "vendi",
|
||||||
|
"settings": "impostazioni",
|
||||||
|
"shields": "scudi",
|
||||||
|
"ship": "nave",
|
||||||
|
"ships": "navi",
|
||||||
|
"shortened": "accorciato",
|
||||||
|
"size": "grandezza",
|
||||||
|
"skip": "salta",
|
||||||
|
"small": "piccolo",
|
||||||
|
"speed": "velocità",
|
||||||
|
"Stock": "appena comprata",
|
||||||
|
"t": "thrusters",
|
||||||
|
"time": "tempo",
|
||||||
|
"total": "totale",
|
||||||
|
"total range": "distanza totale",
|
||||||
|
"turret": "torrette",
|
||||||
|
"type": "tipo",
|
||||||
|
"unladen": "scarico",
|
||||||
|
"PHRASE_UPDATE_RDY": "Aggiornamenti disponibili ! Clicca per Aggiornare",
|
||||||
|
"utility": "supporti",
|
||||||
|
"utility mounts": "supporti di utilità",
|
||||||
|
"version": "versione",
|
||||||
|
"yes": "sì",
|
||||||
|
"PHRASE_BACKUP_DESC": "Esportazione di tutti i dati su Coriolis per salvarli o trasferirli in un altro Browser/dispositivo"
|
||||||
|
}
|
||||||
@@ -13,65 +13,4 @@ export const formats = {
|
|||||||
shortMonths: ['Sty', 'Lut', 'Mar', 'Kwi', 'Maj', 'Cze', 'Lip', 'Sie', 'Wrz', 'Paź', 'Lis', 'Gru']
|
shortMonths: ['Sty', 'Lut', 'Mar', 'Kwi', 'Maj', 'Cze', 'Lip', 'Sie', 'Wrz', 'Paź', 'Lis', 'Gru']
|
||||||
};
|
};
|
||||||
|
|
||||||
export const terms = {
|
export { default as terms } from './pl.json';
|
||||||
PHRASE_ALT_ALL: 'Alt + kliknięcie by wypełnić wszystkie sloty',
|
|
||||||
PHRASE_BACKUP_DESC: 'Kopia zapasowa wszystkich danych Coriolis w celu zapisu lub przeniesienia na inne urządzenie/przeglądarkę',
|
|
||||||
PHRASE_CONFIRMATION: 'Czy jesteś pewien?',
|
|
||||||
PHRASE_EXPORT_DESC: 'Szczegółowy eksport schematu w formacie JSON w celu użycia na innych stronach i narzędziach',
|
|
||||||
PHRASE_FASTEST_RANGE: 'Maksymalna ilość skoków na najwyższym zasięgu',
|
|
||||||
PHRASE_IMPORT: 'Wklej tu JSON lub importuj',
|
|
||||||
PHRASE_LADEN: 'Masa statku + paliwo + ładunek',
|
|
||||||
PHRASE_NO_BUILDS: 'Nie dodano schematu do porównania!',
|
|
||||||
PHRASE_NO_RETROCH: 'Brak zmian retrofit',
|
|
||||||
PHRASE_SELECT_BUILDS: 'Wybierz schematy do porównania',
|
|
||||||
PHRASE_SG_RECHARGE: 'Czas od 50% do 100% naładowania',
|
|
||||||
PHRASE_SG_RECOVER: 'Odnowienie (do 50%) po upadku',
|
|
||||||
PHRASE_UNLADEN: 'Masa statku z wyłączeniem paliwa i ładunku',
|
|
||||||
PHRASE_UPDATE_RDY: 'Dostępna aktualizacja! Naciśnij by odświeżyć',
|
|
||||||
|
|
||||||
// Other languages fallback to these values
|
|
||||||
// Only Translate to other languages if the name is different in-game
|
|
||||||
am: 'Auto Field-Maintenance Unit',
|
|
||||||
bh: 'Bulkheads',
|
|
||||||
bl: 'Beam Laser',
|
|
||||||
bsg: '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 Interdictor',
|
|
||||||
fs: 'Fuel Scoop',
|
|
||||||
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: 'Planetary Approach Suite',
|
|
||||||
pc: 'Prospector Limpet Controller',
|
|
||||||
pd: 'power distributor',
|
|
||||||
pl: 'Pulse Laser',
|
|
||||||
pp: 'Power Plant',
|
|
||||||
psg: 'Prismatic Shield Generator',
|
|
||||||
pv: 'Planetary Vehicle Hangar',
|
|
||||||
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: 'Frame Shift Wake Scanner'
|
|
||||||
};
|
|
||||||
|
|||||||
59
src/app/i18n/pl.json
Normal file
59
src/app/i18n/pl.json
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
{
|
||||||
|
"PHRASE_ALT_ALL": "Alt + kliknięcie by wypełnić wszystkie sloty",
|
||||||
|
"PHRASE_BACKUP_DESC": "Kopia zapasowa wszystkich danych Coriolis w celu zapisu lub przeniesienia na inne urządzenie/przeglądarkę",
|
||||||
|
"PHRASE_CONFIRMATION": "Czy jesteś pewien?",
|
||||||
|
"PHRASE_EXPORT_DESC": "Szczegółowy eksport schematu w formacie JSON w celu użycia na innych stronach i narzędziach",
|
||||||
|
"PHRASE_FASTEST_RANGE": "Maksymalna ilość skoków na najwyższym zasięgu",
|
||||||
|
"PHRASE_IMPORT": "Wklej tu JSON lub importuj",
|
||||||
|
"PHRASE_LADEN": "Masa statku + paliwo + ładunek",
|
||||||
|
"PHRASE_NO_BUILDS": "Nie dodano schematu do porównania!",
|
||||||
|
"PHRASE_NO_RETROCH": "Brak zmian retrofit",
|
||||||
|
"PHRASE_SELECT_BUILDS": "Wybierz schematy do porównania",
|
||||||
|
"PHRASE_SG_RECHARGE": "Czas od 50% do 100% naładowania",
|
||||||
|
"PHRASE_SG_RECOVER": "Odnowienie (do 50%) po upadku",
|
||||||
|
"PHRASE_UNLADEN": "Masa statku z wyłączeniem paliwa i ładunku",
|
||||||
|
"PHRASE_UPDATE_RDY": "Dostępna aktualizacja! Naciśnij by odświeżyć",
|
||||||
|
"am": "Auto Field-Maintenance Unit",
|
||||||
|
"bh": "Bulkheads",
|
||||||
|
"bl": "Beam Laser",
|
||||||
|
"bsg": "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 Interdictor",
|
||||||
|
"fs": "Fuel Scoop",
|
||||||
|
"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": "Planetary Approach Suite",
|
||||||
|
"pc": "Prospector Limpet Controller",
|
||||||
|
"pd": "power distributor",
|
||||||
|
"pl": "Pulse Laser",
|
||||||
|
"pp": "Power Plant",
|
||||||
|
"psg": "Prismatic Shield Generator",
|
||||||
|
"pv": "Planetary Vehicle Hangar",
|
||||||
|
"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": "Frame Shift Wake Scanner"
|
||||||
|
}
|
||||||
16
src/app/i18n/pt.js
Normal file
16
src/app/i18n/pt.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
export const formats = {
|
||||||
|
decimal: ',',
|
||||||
|
thousands: '.',
|
||||||
|
grouping: [3],
|
||||||
|
currency: ['', ' €'],
|
||||||
|
dateTime: '%A, %e de %B de %Y, %X',
|
||||||
|
date: '%d/%m/%Y',
|
||||||
|
time: '%H:%M:%S',
|
||||||
|
periods: ['AM', 'PM'],
|
||||||
|
days: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes', 'sábado'],
|
||||||
|
shortDays: ['dom', 'lun', 'mar', 'mié', 'jue', 'vie', 'sáb'],
|
||||||
|
months: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio', 'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre'],
|
||||||
|
shortMonths: ['ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul', 'ago', 'sep', 'oct', 'nov', 'dic']
|
||||||
|
};
|
||||||
|
|
||||||
|
export { default as terms } from './pt.json';
|
||||||
278
src/app/i18n/pt.json
Normal file
278
src/app/i18n/pt.json
Normal file
File diff suppressed because one or more lines are too long
@@ -13,447 +13,4 @@ export const formats = {
|
|||||||
shortMonths: ['янв', 'фев', 'мар', 'апр', 'май', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек']
|
shortMonths: ['янв', 'фев', 'мар', 'апр', 'май', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек']
|
||||||
};
|
};
|
||||||
|
|
||||||
export const terms = {
|
export { default as terms } from './ru.json';
|
||||||
PHRASE_ALT_ALL: 'Alt + Нажатие для заполнения всех слотов',
|
|
||||||
PHRASE_BACKUP_DESC: 'Сохраните все данные перед переносом в другой браузер или устройство',
|
|
||||||
PHRASE_CONFIRMATION: 'Вы уверены?',
|
|
||||||
PHRASE_EXPORT_DESC: 'Детальный JSON-экспорт вашей сборки для использования в других местах и инструментах',
|
|
||||||
PHRASE_FASTEST_RANGE: 'Последовательные прыжки максимальной дальности',
|
|
||||||
PHRASE_IMPORT: 'Для импорта вставьте код в эту форму',
|
|
||||||
PHRASE_LADEN: 'Масса корабля с учётом топлива и грузов',
|
|
||||||
PHRASE_NO_BUILDS: 'Нечего сравнивать',
|
|
||||||
PHRASE_NO_RETROCH: 'Нет ранних версий сборки',
|
|
||||||
PHRASE_SELECT_BUILDS: 'Выберите конфигурацию для сравнения',
|
|
||||||
PHRASE_SG_RECHARGE: 'Восстановление с 50% до 100% объема щита, учитывая полный аккумулятор СИС в начале',
|
|
||||||
PHRASE_SG_RECOVER: 'Восстановление с 0% до 50% объема щита, учитывая полный аккумулятор СИС в начале',
|
|
||||||
PHRASE_UNLADEN: 'Масса корабля без учета топлива и грузов',
|
|
||||||
PHRASE_UPDATE_RDY: 'Доступна новая версия. Нажмите для обновления.',
|
|
||||||
PHRASE_ENGAGEMENT_RANGE: 'Дистанция между кораблём и целью',
|
|
||||||
PHRASE_SELECT_BLUEPRINT: 'Нажмите чтобы выбрать чертёж',
|
|
||||||
PHRASE_BLUEPRINT_WORST: 'Худшие основные значения для чертежа',
|
|
||||||
PHRASE_BLUEPRINT_RANDOM: 'Случайный выбор между худшими и лучшими значениями для этого чертежа',
|
|
||||||
PHRASE_BLUEPRINT_BEST: 'Лучшие основные значения для чертежа',
|
|
||||||
PHRASE_BLUEPRINT_EXTREME: 'Лучшие положительные и худшие отрицательные основные значения для чертежа',
|
|
||||||
PHRASE_BLUEPRINT_RESET: 'Убрать все изменения и чертёж',
|
|
||||||
PHRASE_SELECT_SPECIAL: 'Нажмите чтобы выбрать экспериментальный эффект',
|
|
||||||
PHRASE_NO_SPECIAL: 'Без экспериментального эффекта',
|
|
||||||
PHRASE_SHOPPING_LIST: 'Станции что продают эту сборку',
|
|
||||||
PHRASE_REFIT_SHOPPING_LIST: 'Станции что продают необходимые модули',
|
|
||||||
PHRASE_TOTAL_EFFECTIVE_SHIELD: 'Общий урон что может быть нанесён в каждым типе, если используются все щитонакопители',
|
|
||||||
PHRASE_TIME_TO_LOSE_SHIELDS: 'Щиты продержатся',
|
|
||||||
PHRASE_TIME_TO_RECOVER_SHIELDS: 'Щиты восстановятся за',
|
|
||||||
PHRASE_TIME_TO_RECHARGE_SHIELDS: 'Щиты будут заряжены за',
|
|
||||||
PHRASE_SHIELD_SOURCES: 'Подробности энергии щита',
|
|
||||||
PHRASE_EFFECTIVE_SHIELD: 'Эффективная сила щита против разных типов урона',
|
|
||||||
PHRASE_ARMOUR_SOURCES: 'Подробности состава брони',
|
|
||||||
PHRASE_EFFECTIVE_ARMOUR: 'Эффективная сила брони против разных типов урона',
|
|
||||||
PHRASE_DAMAGE_TAKEN: '% общих повреждений полученных в разных типах урона',
|
|
||||||
PHRASE_TIME_TO_LOSE_ARMOUR: 'Броня продержится',
|
|
||||||
PHRASE_MODULE_PROTECTION_EXTERNAL: 'Защита гнёзд',
|
|
||||||
PHRASE_MODULE_PROTECTION_INTERNAL: 'Защита всех остальных модулей',
|
|
||||||
PHRASE_SHIELD_DAMAGE: 'Подробности источников поддерживаемого ДПС против щитов',
|
|
||||||
PHRASE_ARMOUR_DAMAGE: 'Подробности источников поддерживаемого ДПС против брони',
|
|
||||||
|
|
||||||
PHRASE_TIME_TO_REMOVE_SHIELDS: 'Снимет щиты за',
|
|
||||||
TT_TIME_TO_REMOVE_SHIELDS: 'Непрерывным огнём из всех орудий',
|
|
||||||
PHRASE_TIME_TO_REMOVE_ARMOUR: 'Снимет броню за',
|
|
||||||
TT_TIME_TO_REMOVE_ARMOUR: 'Непрерывным огнём из всех орудий',
|
|
||||||
PHRASE_TIME_TO_DRAIN_WEP: 'Опустошит ОРУЖ за',
|
|
||||||
TT_TIME_TO_DRAIN_WEP: 'Время за которое опустошится аккумулятор ОРУЖ при стрельбе из всех орудий',
|
|
||||||
TT_TIME_TO_LOSE_SHIELDS: 'Против поддерживаемой стрельбы из всех орудий противника',
|
|
||||||
TT_TIME_TO_LOSE_ARMOUR: 'Против поддерживаемой стрельбы из всех орудий противника',
|
|
||||||
TT_MODULE_ARMOUR: 'Броня защищаюшае модули от урона',
|
|
||||||
TT_MODULE_PROTECTION_EXTERNAL: 'Процент урона перенаправленного от гнёзд на наборы для усиления модулей',
|
|
||||||
TT_MODULE_PROTECTION_INTERNAL: 'Процент урона перенаправленного от модулей вне гнёзд на наборы для усиления модулей',
|
|
||||||
|
|
||||||
TT_EFFECTIVE_SDPS_SHIELDS: 'Реальный поддерживаемый ДПС пока аккумулятор ОРУЖ не пуст',
|
|
||||||
TT_EFFECTIVENESS_SHIELDS: 'Эффективность в сравнении с попаданием по цели с 0-сопротивляемостью без пунктов в СИС на 0 метрах',
|
|
||||||
TT_EFFECTIVE_SDPS_ARMOUR: 'Реальный поддерживаемый ДПС пока аккумулятор ОРУЖ не пуст',
|
|
||||||
TT_EFFECTIVENESS_ARMOUR: 'Эффективность в сравнении с попаданием по цели с 0-сопротивляемостью на 0 метрах',
|
|
||||||
|
|
||||||
PHRASE_EFFECTIVE_SDPS_SHIELDS: 'ПДПС против щитов',
|
|
||||||
PHRASE_EFFECTIVE_SDPS_ARMOUR: 'ПДПС против брони',
|
|
||||||
|
|
||||||
TT_SUMMARY_SPEED: 'С полным топливным баком и 4 пунктами в ДВИ',
|
|
||||||
TT_SUMMARY_SPEED_NONFUNCTIONAL: 'маневровые двигатели выключены или превышена максимальная масса с топливом и грузом',
|
|
||||||
TT_SUMMARY_BOOST: 'С полным топливным баком и 4 пунктами в ДВИ',
|
|
||||||
TT_SUMMARY_BOOST_NONFUNCTIONAL: 'Распределитель питания не может обеспечить достаточно энергии для форсажа',
|
|
||||||
TT_SUMMARY_SHIELDS: 'Чистая сила щита, включая усилители',
|
|
||||||
TT_SUMMARY_SHIELDS_NONFUNCTIONAL: 'Шитогенератор отсутствует или выключен',
|
|
||||||
TT_SUMMARY_INTEGRITY: 'Целостность корабля, включая переборки и наборы для усиления корпуса',
|
|
||||||
TT_SUMMARY_HULL_MASS: 'Масса корпуса без каких-либо модулей',
|
|
||||||
TT_SUMMARY_UNLADEN_MASS: 'Масса корпуса и модулей без топлива и груза',
|
|
||||||
TT_SUMMARY_LADEN_MASS: 'Масса корпуса и модулей с топливом и грузом',
|
|
||||||
TT_SUMMARY_DPS: 'Урон в секунду при стрельбе из всех орудий',
|
|
||||||
TT_SUMMARY_EPS: 'Расход аккумулятора ОРУЖ в секунду при стрельбе из всех орудий',
|
|
||||||
TT_SUMMARY_TTD: 'Время расхода аккумулятора ОРУЖ при стрельбе из всех орудий и с 4 пунктами в ОРУЖ',
|
|
||||||
TT_SUMMARY_MAX_SINGLE_JUMP: 'Самый дальний возможный прыжок без груза и с топливом достаточным только на сам прыжок',
|
|
||||||
TT_SUMMARY_UNLADEN_SINGLE_JUMP: 'Самый дальний возможный прыжок без груза и с полным топливным баком',
|
|
||||||
TT_SUMMARY_LADEN_SINGLE_JUMP: 'Самый дальний возможный прыжок с полным грузовым отсеком и с полным топливным баком',
|
|
||||||
TT_SUMMARY_UNLADEN_TOTAL_JUMP: 'Самая дальняя общая дистанция без груза, с полным топливным баком и при прыжках на максимальное расстояние',
|
|
||||||
TT_SUMMARY_LADEN_TOTAL_JUMP: 'Самая дальняя общая дистанция с полным грузовым отсеком, с полным топливным баком и при прыжках на максимальное расстояние',
|
|
||||||
|
|
||||||
HELP_MODIFICATIONS_MENU: 'Ткните на номер чтобы ввести новое значение, или потяните вдоль полосы для малых изменений',
|
|
||||||
|
|
||||||
// Other languages fallback to these values
|
|
||||||
// Only Translate to other languages if the name is different in-game
|
|
||||||
am: 'Блок Автом. Полевого Ремонта',
|
|
||||||
bh: 'Переборки',
|
|
||||||
bl: 'Пучковый Лазер',
|
|
||||||
bsg: 'Двухпоточный Щитогенератор',
|
|
||||||
c: 'Орудие',
|
|
||||||
cc: 'Контроллер магнитного снаряда для сбора',
|
|
||||||
ch: 'Разбрасыватель дипольных отражателей',
|
|
||||||
cr: 'Грузовой стеллаж',
|
|
||||||
cs: 'Сканер содержимого',
|
|
||||||
dc: 'Стыковочный компьютер',
|
|
||||||
ec: 'Электр. противодействие',
|
|
||||||
fc: 'Залповое орудие',
|
|
||||||
fh: 'Ангар для истребителя',
|
|
||||||
fi: 'FSD-перехватчик',
|
|
||||||
fs: 'Топливозаборник',
|
|
||||||
fsd: 'Рамочно Сместительный двигатель',
|
|
||||||
ft: 'Топливный бак',
|
|
||||||
fx: 'Контроллер магнитного снаряда для топлива',
|
|
||||||
hb: 'Контроллер магнитного снаряда для взлома трюма',
|
|
||||||
hr: 'Набор для усиления корпуса',
|
|
||||||
hs: 'Теплоотводная катапульта',
|
|
||||||
kw: 'Сканер преступников',
|
|
||||||
ls: 'Система жизнеобеспечения',
|
|
||||||
mc: 'Многоствольное орудие',
|
|
||||||
ml: 'Проходочный лазер',
|
|
||||||
mr: 'Ракетный лоток',
|
|
||||||
mrp: 'Набор для усиления модуля',
|
|
||||||
nl: 'Мины',
|
|
||||||
pa: 'Ускоритель плазмы',
|
|
||||||
pas: 'Комплект для сближения с планетой',
|
|
||||||
pc: 'Контроллер магнитного снаряда для геологоразведки',
|
|
||||||
pce: 'Каюта пассажира эконом-класса',
|
|
||||||
pci: 'Каюта пассажира бизнес-класса',
|
|
||||||
pcm: 'Каюта пассажира первого класса',
|
|
||||||
pcq: 'Каюта пассажира класса люкс',
|
|
||||||
pd: 'Распределитель питания',
|
|
||||||
pl: 'Ипмульсный лазер',
|
|
||||||
po: 'Точечная оборона',
|
|
||||||
pp: 'Силовая установка',
|
|
||||||
psg: 'Призматический щитогенератор',
|
|
||||||
pv: 'Гараж для планетарного транспорта',
|
|
||||||
rf: 'Устройство переработки',
|
|
||||||
rg: 'Электромагнитная пушка',
|
|
||||||
s: 'Сенсоры',
|
|
||||||
sb: 'Усилитель щита',
|
|
||||||
sc: 'Сканер обнаружения',
|
|
||||||
scb: 'Щитонакопитель',
|
|
||||||
sg: 'Щитогенератор',
|
|
||||||
ss: 'Сканер Поверхностей',
|
|
||||||
t: 'Маневровые двигатели',
|
|
||||||
tp: 'Торпедная стойка',
|
|
||||||
ul: 'Пульсирующие лазеры',
|
|
||||||
ws: 'Сканер следа FSD',
|
|
||||||
|
|
||||||
// Items on the outfitting page
|
|
||||||
// Notification of restricted slot
|
|
||||||
emptyrestricted: 'пусто (ограниченно)',
|
|
||||||
'damage dealt to': 'Урон нанесён',
|
|
||||||
'damage received from': 'Урон получен от',
|
|
||||||
'against shields': 'Против шитов',
|
|
||||||
'against hull': 'Против корпуса',
|
|
||||||
'total effective shield': 'Общие эффективные щиты',
|
|
||||||
|
|
||||||
// 'ammo' was overloaded for outfitting page and modul info, so changed to ammunition for outfitting page
|
|
||||||
ammunition: 'Припасы',
|
|
||||||
|
|
||||||
// Unit for seconds
|
|
||||||
secs: 'с',
|
|
||||||
|
|
||||||
rebuildsperbay: 'Построек за полосу',
|
|
||||||
|
|
||||||
// Blueprint rolls
|
|
||||||
worst: 'Худшее',
|
|
||||||
average: 'Среднее',
|
|
||||||
random: 'Случайное',
|
|
||||||
best: 'Лучшее',
|
|
||||||
extreme: 'Экстремальное',
|
|
||||||
reset: 'Обнулить',
|
|
||||||
|
|
||||||
// Weapon, offence, defence and movement
|
|
||||||
dpe: 'Урон на МДж энергии',
|
|
||||||
dps: 'Урон в Секунду',
|
|
||||||
sdps: 'Поддерживаемый урон в секунду',
|
|
||||||
dpssdps: 'Урон в секунду (поддерживаемый урон в секунду)',
|
|
||||||
eps: 'Энергия в секунду',
|
|
||||||
epsseps: 'Энергия в секунду (поддерживаемая энергия в секунду)',
|
|
||||||
hps: 'Нагрев в секунду',
|
|
||||||
hpsshps: 'Heat per second (sustained heat per second)',
|
|
||||||
'damage by': 'Урон',
|
|
||||||
'damage from': 'Урон от',
|
|
||||||
'shield cells': 'Щитонакопители',
|
|
||||||
'recovery': 'Восстановление',
|
|
||||||
'recharge': 'Перезарядка',
|
|
||||||
'engine pips': 'Пункты в двигателе',
|
|
||||||
'4b': '4 пункта и Форсаж',
|
|
||||||
'speed': 'Скорость',
|
|
||||||
'pitch': 'Тангаж',
|
|
||||||
'roll': 'Крен',
|
|
||||||
'yaw': 'Рыскание',
|
|
||||||
'internal protection': 'Внутренняя защита',
|
|
||||||
'external protection': 'Внешняя защита',
|
|
||||||
'engagement range': 'Боевое расстояние',
|
|
||||||
'total': 'Общее',
|
|
||||||
|
|
||||||
// Modifications
|
|
||||||
ammo: 'Макс. боекомплект',
|
|
||||||
boot: 'Время загрузки',
|
|
||||||
brokenregen: 'Скорость восстановления при пробое',
|
|
||||||
burst: 'Длина очереди',
|
|
||||||
burstrof: 'Скорострельность очереди',
|
|
||||||
clip: 'Боекомплект',
|
|
||||||
damage: 'Урон',
|
|
||||||
distdraw: 'Тяга распределителя',
|
|
||||||
duration: 'Продолжительность',
|
|
||||||
eff: 'Эффективность',
|
|
||||||
engcap: 'Ресурс двигателей',
|
|
||||||
engrate: 'Перезарядка двигателей',
|
|
||||||
explres: 'Сопротивление взрывам',
|
|
||||||
facinglimit: 'Ограничение по направлению',
|
|
||||||
hullboost: 'Увеличение корпуса',
|
|
||||||
hullreinforcement: 'Укрепление корпуса',
|
|
||||||
integrity: 'Целостность',
|
|
||||||
jitter: 'Дрожание',
|
|
||||||
kinres: 'Сопротивление китетическому урону',
|
|
||||||
maxfuel: 'Макс. топлива на прыжок',
|
|
||||||
mass: 'Масса',
|
|
||||||
optmass: 'Оптимизированная масса',
|
|
||||||
optmul: 'Оптимальный усилитель',
|
|
||||||
pgen: 'Мощность',
|
|
||||||
piercing: 'Бронебойность',
|
|
||||||
power: 'Энергопотребление',
|
|
||||||
protection: 'Защита от повреждений',
|
|
||||||
range: 'Дальность',
|
|
||||||
ranget: 'Дальность', // Range in time (for FSD interdictor)
|
|
||||||
regen: 'Скорость восстановления',
|
|
||||||
reload: 'Время перезарядки',
|
|
||||||
rof: 'Скорострельность',
|
|
||||||
angle: 'Угол сканера',
|
|
||||||
scanrate: 'Скорость сканера',
|
|
||||||
scantime: 'Время сканирования',
|
|
||||||
shield: 'Щит',
|
|
||||||
shieldboost: 'Усиление щитов',
|
|
||||||
shieldreinforcement: 'Усилитель щита',
|
|
||||||
shotspeed: 'Скорость выстрела',
|
|
||||||
spinup: 'Время раскрутки',
|
|
||||||
syscap: 'Ресурс систем',
|
|
||||||
sysrate: 'Перезарядка систем',
|
|
||||||
thermload: 'Тепловая нагрузка',
|
|
||||||
thermres: 'Сопротивление термическому урону',
|
|
||||||
wepcap: 'Орудийный ресурс',
|
|
||||||
weprate: 'Перезарядка оружия',
|
|
||||||
|
|
||||||
// Shield generators use a different terminology
|
|
||||||
minmass_sg: 'Мин. масса корпуса',
|
|
||||||
optmass_sg: 'Опт. масса корпуса',
|
|
||||||
maxmass_sg: 'Макс. масса корпуса',
|
|
||||||
minmul_sg: 'Минимальная прочность',
|
|
||||||
optmul_sg: 'Оптимальная прочность',
|
|
||||||
maxmul_sg: 'Максимальная прочность',
|
|
||||||
minmass_psg: 'Мин. масса корпуса',
|
|
||||||
optmass_psg: 'Опт. масса корпуса',
|
|
||||||
maxmass_psg: 'Макс. масса корпуса',
|
|
||||||
minmul_psg: 'Минимальная прочность',
|
|
||||||
optmul_psg: 'Оптимальная прочность',
|
|
||||||
maxmul_psg: 'Максимальная прочность',
|
|
||||||
minmass_bsg: 'Мин. масса корпуса',
|
|
||||||
optmass_bsg: 'Опт. масса корпуса',
|
|
||||||
maxmass_bsg: 'Макс. масса корпуса',
|
|
||||||
minmul_bsg: 'Минимальная прочность',
|
|
||||||
optmul_bsg: 'Оптимальная прочность',
|
|
||||||
maxmul_bsg: 'Максимальная прочность',
|
|
||||||
|
|
||||||
range_s: 'Типовой диапозон выброса',
|
|
||||||
|
|
||||||
// Damage types
|
|
||||||
absolute: 'Общий',
|
|
||||||
explosive: 'Взрывч.',
|
|
||||||
kinetic: 'Механич.',
|
|
||||||
thermal: 'Тепл.',
|
|
||||||
|
|
||||||
// Shield sources
|
|
||||||
generator: 'Генератор',
|
|
||||||
boosters: 'Усилители',
|
|
||||||
cells: 'Накопители',
|
|
||||||
|
|
||||||
// Armour sources
|
|
||||||
bulkheads: 'Переборки',
|
|
||||||
reinforcement: 'Усилители',
|
|
||||||
|
|
||||||
// Panel headings and subheadings
|
|
||||||
'power and costs': 'Энергия и стоимость',
|
|
||||||
'costs': 'Цены',
|
|
||||||
'retrofit costs': 'Стоимость модификации',
|
|
||||||
'reload costs': 'Стоимость перезарядки',
|
|
||||||
'profiles': 'Графики',
|
|
||||||
'engine profile': 'Двигатели',
|
|
||||||
'fsd profile': 'FSD',
|
|
||||||
'movement profile': 'Движение',
|
|
||||||
'damage to opponent\'s shields': 'Урон щиту противника',
|
|
||||||
'damage to opponent\'s hull': 'Урон корпусу противника',
|
|
||||||
'offence': 'Нападение',
|
|
||||||
'defence': 'Оборона',
|
|
||||||
'shield metrics': 'Данные щита',
|
|
||||||
'raw shield strength': 'Чистая мощность щита',
|
|
||||||
'shield sources': 'Ресурсы щита',
|
|
||||||
'damage taken': 'Полученный урон',
|
|
||||||
'effective shield': 'Эффективный щит',
|
|
||||||
'armour metrics': 'Данные брони',
|
|
||||||
'raw armour strength': 'Чистая мощность брони',
|
|
||||||
'armour sources': 'Ресурсы брони',
|
|
||||||
'raw module armour': 'Чистая броня модулей',
|
|
||||||
'effective armour': 'Эффективная броня',
|
|
||||||
'offence metrics': 'Данные нападения',
|
|
||||||
'defence metrics': 'Данные обороны',
|
|
||||||
// Misc items
|
|
||||||
'fuel carried': 'Топливо на борту',
|
|
||||||
'cargo carried': 'Груз на борту',
|
|
||||||
'ship control': 'Управление кораблём',
|
|
||||||
'opponent': 'Противник',
|
|
||||||
'opponent\'s shields': 'Щит противника',
|
|
||||||
'opponent\'s armour': 'Броня противника',
|
|
||||||
'shield damage sources': 'источники урона по щиту',
|
|
||||||
'armour damage sources': 'источники урона по броне',
|
|
||||||
'never': 'Никогда',
|
|
||||||
'stock': 'базовый',
|
|
||||||
'boost': 'Форсаж',
|
|
||||||
|
|
||||||
// Units / Metrics
|
|
||||||
'/s': '/с', // Per second
|
|
||||||
'm/s': 'м/с', // Meters / Second
|
|
||||||
Ls: 'Св.сек', // Light seconds
|
|
||||||
LY: 'Св.лет', // Light Years
|
|
||||||
CR: 'кр.', // Credits abbreviation
|
|
||||||
|
|
||||||
// Sizes
|
|
||||||
S: 'M', // Small Hardpoint (single Character)
|
|
||||||
M: 'С', // Medium Hardpoint size (single character)
|
|
||||||
L: 'б', // Large Hardpoint size (single character)
|
|
||||||
H: 'O', // Huge Hardpoint size (single character)
|
|
||||||
U: 'B', // Utility Hardpoint size (single character) - Kill warrant scanner, etc
|
|
||||||
small: 'Малый', // Small ship size
|
|
||||||
medium: 'Средний', // Medium ship size
|
|
||||||
large: 'большой', // Large Ship Size
|
|
||||||
// Insurance
|
|
||||||
alpha: 'Альфа', // Alpha backer insurance level
|
|
||||||
beta: 'Бета', // Beta back insurance level
|
|
||||||
standard: 'Стандартный', // Standard insurance level
|
|
||||||
// Terms
|
|
||||||
'build name': 'название сборки', // Ship build/configuration/design name
|
|
||||||
'compare all': 'сравнить все',
|
|
||||||
'create new': 'Создать новый',
|
|
||||||
'damage per second': 'урон в секунду',
|
|
||||||
'delete all': 'Удалить все',
|
|
||||||
'detailed export': 'Подробный экспорт',
|
|
||||||
'edit data': 'Редактирование',
|
|
||||||
'empty all': 'пусто все',
|
|
||||||
'Enter Name': 'Введите имя',
|
|
||||||
'fastest range': 'быстрый диапазон', // Fastet totaljump range - sum of succesive jumps
|
|
||||||
'fuel level': 'уровень топлива', // Percent of fuel (T) in the tank
|
|
||||||
'full tank': 'Полный бак',
|
|
||||||
'internal compartments': 'внутренние отсеки',
|
|
||||||
'jump range': 'Дальность прыжка',
|
|
||||||
'mass lock factor': 'Масс. блок',
|
|
||||||
'max mass': 'Максимальная масса',
|
|
||||||
'net cost': 'разница в цене',
|
|
||||||
'none created': 'не создано',
|
|
||||||
'refuel time': 'Время дозаправки', // Time to refuel the tank when scooping
|
|
||||||
'retrofit costs': 'цена модификации', // The cost difference when upgrading / downgrading a component
|
|
||||||
'retrofit from': 'модификация от', // Retrofit from Build A against build B
|
|
||||||
'T-Load': 'Тепл.', // Thermal load abbreviation
|
|
||||||
'utility mounts': 'Вспомогательное оборудование',
|
|
||||||
about: 'О ...', // Link to about page / about Coriolis.io
|
|
||||||
action: 'Действие',
|
|
||||||
added: 'Добавлено',
|
|
||||||
ammo: 'Боекомплект', // Ammunition
|
|
||||||
armour: 'Броня',
|
|
||||||
available: 'доступно', // Available options
|
|
||||||
backup: 'Резервная копия',
|
|
||||||
bins: 'контейнеры', // Number of Mining Refinery bins
|
|
||||||
boost: 'форсаж',
|
|
||||||
build: 'cборка', // Shorthand for the build/configuration/design name
|
|
||||||
builds: 'cборки', // Ship build/configuration/design names
|
|
||||||
buy: 'купить',
|
|
||||||
cancel: 'отменить',
|
|
||||||
cargo: 'Груз',
|
|
||||||
cells: 'Ячейки', // Number of cells in a shield cell bank
|
|
||||||
close: 'закрыть',
|
|
||||||
compare: 'сравнить ',
|
|
||||||
comparison: 'сравнение',
|
|
||||||
comparisons: 'сравнения',
|
|
||||||
cost: 'Стоимость', // Cost / price of a module or price of a ship
|
|
||||||
costs: 'Расходы', // Costs / prices of a modules or prices of ships
|
|
||||||
create: 'создать',
|
|
||||||
credits: 'Кредиты',
|
|
||||||
damage: 'Урон',
|
|
||||||
delete: 'Удалить',
|
|
||||||
dep: 'Вып', // Weapons/Hardpoints Deployed abbreviation
|
|
||||||
deployed: 'Открыты', // Weapons/Hardpoints Deployed
|
|
||||||
disabled: 'Отключено',
|
|
||||||
discount: 'Скидка',
|
|
||||||
DPS: 'УВС', // Damage per second abbreviation
|
|
||||||
efficiency: 'Эффективность', // Power Plant efficiency
|
|
||||||
empty: 'пусто',
|
|
||||||
ENG: 'ДВИ', // Abbreviation - Engine recharge rate for power distributor
|
|
||||||
export: 'Экспорт',
|
|
||||||
forum: 'Форум',
|
|
||||||
fuel: 'Топливо',
|
|
||||||
hardpoints: 'Орудийные порты',
|
|
||||||
hull: 'Корпус', // Ships hull
|
|
||||||
import: 'импортировать ',
|
|
||||||
insurance: 'Страховка',
|
|
||||||
|
|
||||||
jumps: 'Прыжков',
|
|
||||||
laden: 'Груженый',
|
|
||||||
language: 'Язык',
|
|
||||||
maneuverability: 'Маневренность',
|
|
||||||
|
|
||||||
mass: 'Масса',
|
|
||||||
max: 'Макс',
|
|
||||||
|
|
||||||
no: 'Нет',
|
|
||||||
pen: 'ПБ', // Armour peneration abbreviation
|
|
||||||
permalink: 'Постоянная ссылка',
|
|
||||||
power: 'Мощность', // Power = Energy / second. Power generated by the power plant, or power consumed (MW / Mega Watts). Used in the power plant section
|
|
||||||
pri: 'Осн', // Priority abbreviation for power management
|
|
||||||
proceed: 'продолжить',
|
|
||||||
PWR: 'Эн', // Power Abbreviation. See Power
|
|
||||||
range: 'Дальность',
|
|
||||||
rate: 'скорость',
|
|
||||||
recharge: 'перезарядка', // Shield Recharge time from 50% -> 100%
|
|
||||||
recovery: 'включение', // Shield recovery time (after losing shields/turning on -> 50%)
|
|
||||||
reload: 'Перезагрузить', // Reload weapon/hardpoint
|
|
||||||
rename: 'Переименовать',
|
|
||||||
repair: 'Починка',
|
|
||||||
reset: 'Сброс',
|
|
||||||
ret: 'Убр.', // Retracted abbreviation
|
|
||||||
retracted: 'Убрано', // Weapons/Hardpoints retracted
|
|
||||||
ROF: 'В/сек', // Rate of Fire abbreviation
|
|
||||||
|
|
||||||
save: 'Сохранить',
|
|
||||||
sell: 'Продать',
|
|
||||||
settings: 'Настройки', // Coriolis application settings
|
|
||||||
shields: 'Щиты',
|
|
||||||
ship: 'Корабль',
|
|
||||||
ships: 'Корабли',
|
|
||||||
shortened: 'Укороченный', // Standard/Stock build of a ship when purchased new
|
|
||||||
size: 'размер',
|
|
||||||
skip: 'пропустить', // Skip past something / ignore it
|
|
||||||
speed: 'скорость',
|
|
||||||
standard: 'Стандартный', // Standard / Common modules (FSD, power plant, life support, etc)
|
|
||||||
Stock: 'Стандартная комплектация', // Thermal-load abbreviation
|
|
||||||
SYS: 'СИС', // Abbreviation - System recharge rate for power distributor
|
|
||||||
time: 'Время', // time it takes to complete something
|
|
||||||
total: 'Всего',
|
|
||||||
type: 'Тип',
|
|
||||||
unladen: 'Пустой', // No cargo or fuel
|
|
||||||
URL: 'Ссылка', // Link, Uniform Resource Locator
|
|
||||||
WEP: 'ОРУЖ', // Abbreviation - Weapon recharge rate for power distributor
|
|
||||||
yes: 'Да'
|
|
||||||
};
|
|
||||||
384
src/app/i18n/ru.json
Normal file
384
src/app/i18n/ru.json
Normal file
@@ -0,0 +1,384 @@
|
|||||||
|
{
|
||||||
|
"PHRASE_ALT_ALL": "Alt + Нажатие для заполнения всех слотов",
|
||||||
|
"PHRASE_BACKUP_DESC": "Сохраните все данные перед переносом в другой браузер или устройство",
|
||||||
|
"PHRASE_CONFIRMATION": "Вы уверены?",
|
||||||
|
"PHRASE_EXPORT_DESC": "Детальный JSON-экспорт вашей сборки для использования в других местах и инструментах",
|
||||||
|
"PHRASE_FASTEST_RANGE": "Последовательные прыжки максимальной дальности",
|
||||||
|
"PHRASE_IMPORT": "Для импорта вставьте код в эту форму",
|
||||||
|
"PHRASE_LADEN": "Масса корабля с учётом топлива и грузов",
|
||||||
|
"PHRASE_NO_BUILDS": "Нечего сравнивать",
|
||||||
|
"PHRASE_NO_RETROCH": "Нет ранних версий сборки",
|
||||||
|
"PHRASE_SELECT_BUILDS": "Выберите конфигурацию для сравнения",
|
||||||
|
"PHRASE_SG_RECHARGE": "Восстановление с 50% до 100% объема щита, учитывая полный аккумулятор СИС в начале",
|
||||||
|
"PHRASE_SG_RECOVER": "Восстановление с 0% до 50% объема щита, учитывая полный аккумулятор СИС в начале",
|
||||||
|
"PHRASE_UNLADEN": "Масса корабля без учета топлива и грузов",
|
||||||
|
"PHRASE_UPDATE_RDY": "Доступна новая версия. Нажмите для обновления.",
|
||||||
|
"PHRASE_ENGAGEMENT_RANGE": "Дистанция между кораблём и целью",
|
||||||
|
"PHRASE_SELECT_BLUEPRINT": "Нажмите чтобы выбрать чертёж",
|
||||||
|
"PHRASE_BLUEPRINT_WORST": "Худшие основные значения для чертежа",
|
||||||
|
"PHRASE_BLUEPRINT_RANDOM": "Случайный выбор между худшими и лучшими значениями для этого чертежа",
|
||||||
|
"PHRASE_BLUEPRINT_BEST": "Лучшие основные значения для чертежа",
|
||||||
|
"PHRASE_BLUEPRINT_EXTREME": "Лучшие положительные и худшие отрицательные основные значения для чертежа",
|
||||||
|
"PHRASE_BLUEPRINT_RESET": "Убрать все изменения и чертёж",
|
||||||
|
"PHRASE_SELECT_SPECIAL": "Нажмите, чтобы выбрать экспериментальный эффект",
|
||||||
|
"PHRASE_NO_SPECIAL": "Без экспериментального эффекта",
|
||||||
|
"PHRASE_SHOPPING_LIST": "Станции, что продают эту сборку",
|
||||||
|
"PHRASE_REFIT_SHOPPING_LIST": "Станции, что продают необходимые модули",
|
||||||
|
"PHRASE_TOTAL_EFFECTIVE_SHIELD": "Общий урон, что может быть нанесён в каждым типе, если используются все щитонакопители",
|
||||||
|
"PHRASE_TIME_TO_LOSE_SHIELDS": "Щиты продержатся",
|
||||||
|
"PHRASE_TIME_TO_RECOVER_SHIELDS": "Щиты восстановятся за",
|
||||||
|
"PHRASE_TIME_TO_RECHARGE_SHIELDS": "Щиты будут заряжены за",
|
||||||
|
"PHRASE_SHIELD_SOURCES": "Подробности энергии щита",
|
||||||
|
"PHRASE_EFFECTIVE_SHIELD": "Эффективная сила щита против разных типов урона",
|
||||||
|
"PHRASE_ARMOUR_SOURCES": "Подробности состава брони",
|
||||||
|
"PHRASE_EFFECTIVE_ARMOUR": "Эффективная сила брони против разных типов урона",
|
||||||
|
"PHRASE_DAMAGE_TAKEN": "% общих повреждений полученных в разных типах урона",
|
||||||
|
"PHRASE_TIME_TO_LOSE_ARMOUR": "Броня продержится",
|
||||||
|
"PHRASE_MODULE_PROTECTION_EXTERNAL": "Защита гнёзд",
|
||||||
|
"PHRASE_MODULE_PROTECTION_INTERNAL": "Защита всех остальных модулей",
|
||||||
|
"PHRASE_SHIELD_DAMAGE": "Подробности источников поддерживаемого ДПС против щитов",
|
||||||
|
"PHRASE_ARMOUR_DAMAGE": "Подробности источников поддерживаемого ДПС против брони",
|
||||||
|
"PHRASE_TIME_TO_REMOVE_SHIELDS": "Снимет щиты за",
|
||||||
|
"TT_TIME_TO_REMOVE_SHIELDS": "Непрерывным огнём из всех орудий",
|
||||||
|
"PHRASE_TIME_TO_REMOVE_ARMOUR": "Снимет броню за",
|
||||||
|
"TT_TIME_TO_REMOVE_ARMOUR": "Непрерывным огнём из всех орудий",
|
||||||
|
"PHRASE_TIME_TO_DRAIN_WEP": "Опустошит ОРУЖ за",
|
||||||
|
"TT_TIME_TO_DRAIN_WEP": "Время, за которое опустошится аккумулятор ОРУЖ при стрельбе из всех орудий",
|
||||||
|
"TT_TIME_TO_LOSE_SHIELDS": "Против поддерживаемой стрельбы из всех орудий противника",
|
||||||
|
"TT_TIME_TO_LOSE_ARMOUR": "Против поддерживаемой стрельбы из всех орудий противника",
|
||||||
|
"TT_MODULE_ARMOUR": "Броня, защищающая модули от урона",
|
||||||
|
"TT_MODULE_PROTECTION_EXTERNAL": "Процент урона, перенаправленного от гнёзд на наборы для усиления модулей",
|
||||||
|
"TT_MODULE_PROTECTION_INTERNAL": "Процент урона, перенаправленного от модулей вне гнёзд на наборы для усиления модулей",
|
||||||
|
"TT_EFFECTIVE_SDPS_SHIELDS": "Реальный поддерживаемый ДПС пока аккумулятор ОРУЖ не пуст",
|
||||||
|
"TT_EFFECTIVENESS_SHIELDS": "Эффективность в сравнении с попаданием по цели с 0-сопротивляемостью без пунктов в СИС на 0 метрах",
|
||||||
|
"TT_EFFECTIVE_SDPS_ARMOUR": "Реальный поддерживаемый ДПС пока аккумулятор ОРУЖ не пуст",
|
||||||
|
"TT_EFFECTIVENESS_ARMOUR": "Эффективность в сравнении с попаданием по цели с 0-сопротивляемостью на 0 метрах",
|
||||||
|
"PHRASE_EFFECTIVE_SDPS_SHIELDS": "ПДПС против щитов",
|
||||||
|
"PHRASE_EFFECTIVE_SDPS_ARMOUR": "ПДПС против брони",
|
||||||
|
"TT_SUMMARY_SPEED": "С полным топливным баком и 4 пунктами в ДВИ",
|
||||||
|
"TT_SUMMARY_SPEED_NONFUNCTIONAL": "Маневровые двигатели выключены или превышена максимальная масса с топливом и грузом",
|
||||||
|
"TT_SUMMARY_BOOST": "С полным топливным баком и 4 пунктами в ДВИ",
|
||||||
|
"TT_SUMMARY_BOOST_NONFUNCTIONAL": "Распределитель питания не может обеспечить достаточно энергии для форсажа",
|
||||||
|
"TT_SUMMARY_SHIELDS": "Чистая сила щита, включая усилители",
|
||||||
|
"TT_SUMMARY_SHIELDS_NONFUNCTIONAL": "Шитогенератор отсутствует или выключен",
|
||||||
|
"TT_SUMMARY_INTEGRITY": "Целостность корабля, включая переборки и наборы для усиления корпуса",
|
||||||
|
"TT_SUMMARY_HULL_MASS": "Масса корпуса без каких-либо модулей",
|
||||||
|
"TT_SUMMARY_UNLADEN_MASS": "Масса корпуса и модулей без топлива и груза",
|
||||||
|
"TT_SUMMARY_LADEN_MASS": "Масса корпуса и модулей с топливом и грузом",
|
||||||
|
"TT_SUMMARY_DPS": "Урон в секунду при стрельбе из всех орудий",
|
||||||
|
"TT_SUMMARY_EPS": "Расход аккумулятора ОРУЖ в секунду при стрельбе из всех орудий",
|
||||||
|
"TT_SUMMARY_TTD": "Время расхода аккумулятора ОРУЖ при стрельбе из всех орудий и с 4 пунктами в ОРУЖ",
|
||||||
|
"TT_SUMMARY_MAX_SINGLE_JUMP": "Самый дальний возможный прыжок без груза и с топливом, достаточным только на сам прыжок",
|
||||||
|
"TT_SUMMARY_UNLADEN_SINGLE_JUMP": "Самый дальний возможный прыжок без груза и с полным топливным баком",
|
||||||
|
"TT_SUMMARY_LADEN_SINGLE_JUMP": "Самый дальний возможный прыжок с полным грузовым отсеком и с полным топливным баком",
|
||||||
|
"TT_SUMMARY_UNLADEN_TOTAL_JUMP": "Самая дальняя общая дистанция без груза, с полным топливным баком и при прыжках на максимальное расстояние",
|
||||||
|
"TT_SUMMARY_LADEN_TOTAL_JUMP": "Самая дальняя общая дистанция с полным грузовым отсеком, с полным топливным баком и при прыжках на максимальное расстояние",
|
||||||
|
"HELP_MODIFICATIONS_MENU": "Нажмите на номер, чтобы ввести новое значение, или потяните вдоль полосы для малых изменений",
|
||||||
|
"am": "Блок Автом. Полевого Ремонта",
|
||||||
|
"bh": "Переборки",
|
||||||
|
"bl": "Пучковый лазер",
|
||||||
|
"bsg": "Двухпоточный щитогенератор",
|
||||||
|
"c": "Орудие",
|
||||||
|
"cc": "Контроллер магнитного снаряда для сбора",
|
||||||
|
"ch": "Разбрасыватель дипольных отражателей",
|
||||||
|
"cr": "Грузовой стеллаж",
|
||||||
|
"cs": "Сканер содержимого",
|
||||||
|
"dc": "Стыковочный компьютер",
|
||||||
|
"ec": "Электр. противодействие",
|
||||||
|
"fc": "Залповое орудие",
|
||||||
|
"fh": "Ангар для истребителя",
|
||||||
|
"fi": "FSD-перехватчик",
|
||||||
|
"fs": "Топливозаборник",
|
||||||
|
"fsd": "Рамочно-сместительный двигатель",
|
||||||
|
"ft": "Топливный бак",
|
||||||
|
"fx": "Контроллер магнитного снаряда для топлива",
|
||||||
|
"hb": "Контроллер магнитного снаряда для взлома трюма",
|
||||||
|
"hr": "Набор для усиления корпуса",
|
||||||
|
"hs": "Теплоотводная катапульта",
|
||||||
|
"kw": "Сканер преступников",
|
||||||
|
"ls": "Система жизнеобеспечения",
|
||||||
|
"mc": "Многоствольное орудие",
|
||||||
|
"ml": "Проходочный лазер",
|
||||||
|
"mr": "Ракетный лоток",
|
||||||
|
"mrp": "Набор для усиления модуля",
|
||||||
|
"nl": "Мины",
|
||||||
|
"pa": "Ускоритель плазмы",
|
||||||
|
"pas": "Комплект для сближения с планетой",
|
||||||
|
"pc": "Контроллер магнитного снаряда для геологоразведки",
|
||||||
|
"pce": "Каюта пассажира эконом-класса",
|
||||||
|
"pci": "Каюта пассажира бизнес-класса",
|
||||||
|
"pcm": "Каюта пассажира первого класса",
|
||||||
|
"pcq": "Каюта пассажира класса люкс",
|
||||||
|
"pd": "Распределитель питания",
|
||||||
|
"pl": "Импульсный лазер",
|
||||||
|
"po": "Точечная оборона",
|
||||||
|
"pp": "Силовая установка",
|
||||||
|
"psg": "Призматический щитогенератор",
|
||||||
|
"pv": "Гараж для планетарного транспорта",
|
||||||
|
"rf": "Устройство переработки",
|
||||||
|
"rg": "Электромагнитная пушка",
|
||||||
|
"s": "Сенсоры",
|
||||||
|
"sb": "Усилитель щита",
|
||||||
|
"sc": "Сканер обнаружения",
|
||||||
|
"scb": "Щитонакопитель",
|
||||||
|
"sg": "Щитогенератор",
|
||||||
|
"ss": "Сканер поверхностей",
|
||||||
|
"t": "Маневровые двигатели",
|
||||||
|
"tp": "Торпедная стойка",
|
||||||
|
"ul": "Пульсирующие лазеры",
|
||||||
|
"ws": "Сканер следа FSD",
|
||||||
|
"emptyrestricted": "пусто (ограниченно)",
|
||||||
|
"damage dealt to": "Урон нанесён",
|
||||||
|
"damage received from": "Урон получен от",
|
||||||
|
"against shields": "Против щитов",
|
||||||
|
"against hull": "Против корпуса",
|
||||||
|
"total effective shield": "Общие эффективные щиты",
|
||||||
|
"ammunition": "Припасы",
|
||||||
|
"secs": "с",
|
||||||
|
"rebuildsperbay": "Построек за полосу",
|
||||||
|
"worst": "Худшее",
|
||||||
|
"average": "Среднее",
|
||||||
|
"random": "Случайное",
|
||||||
|
"best": "Лучшее",
|
||||||
|
"extreme": "Экстремальное",
|
||||||
|
"reset": "Сброс",
|
||||||
|
"dpe": "Урон на МДж энергии",
|
||||||
|
"dps": "Урон в Секунду",
|
||||||
|
"sdps": "Поддерживаемый урон в секунду",
|
||||||
|
"dpssdps": "Урон в секунду (поддерживаемый урон в секунду)",
|
||||||
|
"eps": "Энергия в секунду",
|
||||||
|
"epsseps": "Энергия в секунду (поддерживаемая энергия в секунду)",
|
||||||
|
"hps": "Нагрев в секунду",
|
||||||
|
"hpsshps": "Нагрев в секунду (поддерживаемый нагрев в секунду)",
|
||||||
|
"damage by": "Урон",
|
||||||
|
"damage from": "Урон от",
|
||||||
|
"shield cells": "Щитонакопители",
|
||||||
|
"recovery": "включение",
|
||||||
|
"recharge": "перезарядка",
|
||||||
|
"engine pips": "Пункты в двигателе",
|
||||||
|
"4b": "4 пункта и Форсаж",
|
||||||
|
"speed": "скорость",
|
||||||
|
"pitch": "Тангаж",
|
||||||
|
"roll": "Крен",
|
||||||
|
"yaw": "Рыскание",
|
||||||
|
"internal protection": "Внутренняя защита",
|
||||||
|
"external protection": "Внешняя защита",
|
||||||
|
"engagement range": "Боевое расстояние",
|
||||||
|
"total": "Всего",
|
||||||
|
"ammo": "Боекомплект",
|
||||||
|
"boot": "Время загрузки",
|
||||||
|
"brokenregen": "Скорость восстановления при пробое",
|
||||||
|
"burst": "Длина очереди",
|
||||||
|
"burstrof": "Скорострельность очереди",
|
||||||
|
"clip": "Боекомплект",
|
||||||
|
"damage": "Урон",
|
||||||
|
"distdraw": "Тяга распределителя",
|
||||||
|
"duration": "Продолжительность",
|
||||||
|
"eff": "Эффективность",
|
||||||
|
"engcap": "Ресурс двигателей",
|
||||||
|
"engrate": "Перезарядка двигателей",
|
||||||
|
"explres": "Сопротивление взрывам",
|
||||||
|
"facinglimit": "Ограничение по направлению",
|
||||||
|
"hullboost": "Увеличение корпуса",
|
||||||
|
"hullreinforcement": "Укрепление корпуса",
|
||||||
|
"integrity": "Целостность",
|
||||||
|
"jitter": "Дрожание",
|
||||||
|
"kinres": "Сопротивление кинетическому урону",
|
||||||
|
"maxfuel": "Макс. топлива на прыжок",
|
||||||
|
"mass": "Масса",
|
||||||
|
"optmass": "Оптимизированная масса",
|
||||||
|
"optmul": "Оптимальный усилитель",
|
||||||
|
"pgen": "Мощность",
|
||||||
|
"piercing": "Бронебойность",
|
||||||
|
"power": "Мощность",
|
||||||
|
"protection": "Защита от повреждений",
|
||||||
|
"range": "Дальность",
|
||||||
|
"ranget": "Дальность",
|
||||||
|
"regen": "Скорость восстановления",
|
||||||
|
"reload": "Перезагрузить",
|
||||||
|
"rof": "Скорострельность",
|
||||||
|
"angle": "Угол сканера",
|
||||||
|
"scanrate": "Скорость сканера",
|
||||||
|
"scantime": "Время сканирования",
|
||||||
|
"shield": "Щит",
|
||||||
|
"shieldboost": "Усиление щитов",
|
||||||
|
"shieldreinforcement": "Усилитель щита",
|
||||||
|
"shotspeed": "Скорость выстрела",
|
||||||
|
"spinup": "Время раскрутки",
|
||||||
|
"syscap": "Ресурс систем",
|
||||||
|
"sysrate": "Перезарядка систем",
|
||||||
|
"thermload": "Тепловая нагрузка",
|
||||||
|
"thermres": "Сопротивление термическому урону",
|
||||||
|
"wepcap": "Орудийный ресурс",
|
||||||
|
"weprate": "Перезарядка оружия",
|
||||||
|
"minmass_sg": "Мин. масса корпуса",
|
||||||
|
"optmass_sg": "Опт. масса корпуса",
|
||||||
|
"maxmass_sg": "Макс. масса корпуса",
|
||||||
|
"minmul_sg": "Минимальная прочность",
|
||||||
|
"optmul_sg": "Оптимальная прочность",
|
||||||
|
"maxmul_sg": "Максимальная прочность",
|
||||||
|
"minmass_psg": "Мин. масса корпуса",
|
||||||
|
"optmass_psg": "Опт. масса корпуса",
|
||||||
|
"maxmass_psg": "Макс. масса корпуса",
|
||||||
|
"minmul_psg": "Минимальная прочность",
|
||||||
|
"optmul_psg": "Оптимальная прочность",
|
||||||
|
"maxmul_psg": "Максимальная прочность",
|
||||||
|
"minmass_bsg": "Мин. масса корпуса",
|
||||||
|
"optmass_bsg": "Опт. масса корпуса",
|
||||||
|
"maxmass_bsg": "Макс. масса корпуса",
|
||||||
|
"minmul_bsg": "Минимальная прочность",
|
||||||
|
"optmul_bsg": "Оптимальная прочность",
|
||||||
|
"maxmul_bsg": "Максимальная прочность",
|
||||||
|
"range_s": "Типовой диапозон выброса",
|
||||||
|
"absolute": "Общий",
|
||||||
|
"explosive": "Взрывч.",
|
||||||
|
"kinetic": "Механич.",
|
||||||
|
"thermal": "Тепл.",
|
||||||
|
"generator": "Генератор",
|
||||||
|
"boosters": "Усилители",
|
||||||
|
"cells": "Ячейки",
|
||||||
|
"bulkheads": "Переборки",
|
||||||
|
"reinforcement": "Усилители",
|
||||||
|
"power and costs": "Энергия и стоимость",
|
||||||
|
"costs": "Расходы",
|
||||||
|
"retrofit costs": "цена модификации",
|
||||||
|
"reload costs": "Стоимость перезарядки",
|
||||||
|
"profiles": "Графики",
|
||||||
|
"engine profile": "Двигатели",
|
||||||
|
"fsd profile": "FSD",
|
||||||
|
"movement profile": "Движение",
|
||||||
|
"damage to opponent's shields": "Урон щиту противника",
|
||||||
|
"damage to opponent's hull": "Урон корпусу противника",
|
||||||
|
"offence": "Нападение",
|
||||||
|
"defence": "Оборона",
|
||||||
|
"shield metrics": "Данные щита",
|
||||||
|
"raw shield strength": "Чистая мощность щита",
|
||||||
|
"shield sources": "Ресурсы щита",
|
||||||
|
"damage taken": "Полученный урон",
|
||||||
|
"effective shield": "Эффективный щит",
|
||||||
|
"armour metrics": "Данные брони",
|
||||||
|
"raw armour strength": "Чистая мощность брони",
|
||||||
|
"armour sources": "Ресурсы брони",
|
||||||
|
"raw module armour": "Чистая броня модулей",
|
||||||
|
"effective armour": "Эффективная броня",
|
||||||
|
"offence metrics": "Данные нападения",
|
||||||
|
"defence metrics": "Данные обороны",
|
||||||
|
"fuel carried": "Топливо на борту",
|
||||||
|
"cargo carried": "Груз на борту",
|
||||||
|
"ship control": "Управление кораблём",
|
||||||
|
"opponent": "Противник",
|
||||||
|
"opponent's shields": "Щит противника",
|
||||||
|
"opponent's armour": "Броня противника",
|
||||||
|
"shield damage sources": "источники урона по щиту",
|
||||||
|
"armour damage sources": "источники урона по броне",
|
||||||
|
"never": "Никогда",
|
||||||
|
"stock": "базовый",
|
||||||
|
"boost": "форсаж",
|
||||||
|
"/s": "/с",
|
||||||
|
"m/s": "м/с",
|
||||||
|
"Ls": "Св.сек",
|
||||||
|
"LY": "Св.лет",
|
||||||
|
"CR": "кр.",
|
||||||
|
"S": "M",
|
||||||
|
"M": "С",
|
||||||
|
"L": "б",
|
||||||
|
"H": "O",
|
||||||
|
"U": "B",
|
||||||
|
"small": "Малый",
|
||||||
|
"medium": "Средний",
|
||||||
|
"large": "большой",
|
||||||
|
"alpha": "Альфа",
|
||||||
|
"beta": "Бета",
|
||||||
|
"standard": "Стандартный",
|
||||||
|
"build name": "название сборки",
|
||||||
|
"compare all": "сравнить все",
|
||||||
|
"create new": "Создать новый",
|
||||||
|
"damage per second": "урон в секунду",
|
||||||
|
"delete all": "Удалить все",
|
||||||
|
"detailed export": "Подробный экспорт",
|
||||||
|
"edit data": "Редактирование",
|
||||||
|
"empty all": "пусто все",
|
||||||
|
"Enter Name": "Введите имя",
|
||||||
|
"farthest range": "быстрый диапазон",
|
||||||
|
"fuel level": "уровень топлива",
|
||||||
|
"full tank": "Полный бак",
|
||||||
|
"internal compartments": "внутренние отсеки",
|
||||||
|
"jump range": "Дальность прыжка",
|
||||||
|
"mass lock factor": "Масс. блок",
|
||||||
|
"max mass": "Максимальная масса",
|
||||||
|
"net cost": "разница в цене",
|
||||||
|
"none created": "не создано",
|
||||||
|
"refuel time": "Время дозаправки",
|
||||||
|
"retrofit from": "модификация от",
|
||||||
|
"T-Load": "Тепл.",
|
||||||
|
"utility mounts": "Вспомогательное оборудование",
|
||||||
|
"about": "О ...",
|
||||||
|
"action": "Действие",
|
||||||
|
"added": "Добавлено",
|
||||||
|
"armour": "Броня",
|
||||||
|
"available": "доступно",
|
||||||
|
"backup": "Резервная копия",
|
||||||
|
"bins": "контейнеры",
|
||||||
|
"build": "cборка",
|
||||||
|
"builds": "cборки",
|
||||||
|
"buy": "купить",
|
||||||
|
"cancel": "отменить",
|
||||||
|
"cargo": "Груз",
|
||||||
|
"close": "закрыть",
|
||||||
|
"compare": "сравнить ",
|
||||||
|
"comparison": "сравнение",
|
||||||
|
"comparisons": "сравнения",
|
||||||
|
"cost": "Стоимость",
|
||||||
|
"create": "создать",
|
||||||
|
"credits": "Кредиты",
|
||||||
|
"delete": "Удалить",
|
||||||
|
"dep": "Вып",
|
||||||
|
"deployed": "Открыты",
|
||||||
|
"disabled": "Отключено",
|
||||||
|
"discount": "Скидка",
|
||||||
|
"DPS": "УВС",
|
||||||
|
"efficiency": "Эффективность",
|
||||||
|
"empty": "пусто",
|
||||||
|
"ENG": "ДВИ",
|
||||||
|
"export": "Экспорт",
|
||||||
|
"forum": "Форум",
|
||||||
|
"fuel": "Топливо",
|
||||||
|
"hardpoints": "Орудийные порты",
|
||||||
|
"hull": "Корпус",
|
||||||
|
"import": "импортировать ",
|
||||||
|
"insurance": "Страховка",
|
||||||
|
"jumps": "Прыжков",
|
||||||
|
"laden": "Груженый",
|
||||||
|
"language": "Язык",
|
||||||
|
"maneuverability": "Маневренность",
|
||||||
|
"max": "Макс",
|
||||||
|
"no": "Нет",
|
||||||
|
"pen": "ПБ",
|
||||||
|
"permalink": "Постоянная ссылка",
|
||||||
|
"pri": "Осн",
|
||||||
|
"proceed": "продолжить",
|
||||||
|
"PWR": "Эн",
|
||||||
|
"rate": "скорость",
|
||||||
|
"rename": "Переименовать",
|
||||||
|
"repair": "Починка",
|
||||||
|
"ret": "Убр.",
|
||||||
|
"retracted": "Убрано",
|
||||||
|
"ROF": "В/сек",
|
||||||
|
"save": "Сохранить",
|
||||||
|
"sell": "Продать",
|
||||||
|
"settings": "Настройки",
|
||||||
|
"shields": "Щиты",
|
||||||
|
"ship": "Корабль",
|
||||||
|
"ships": "Корабли",
|
||||||
|
"shortened": "Укороченный",
|
||||||
|
"size": "размер",
|
||||||
|
"skip": "пропустить",
|
||||||
|
"Stock": "Стандартная комплектация",
|
||||||
|
"SYS": "СИС",
|
||||||
|
"time": "Время",
|
||||||
|
"type": "Тип",
|
||||||
|
"unladen": "Пустой",
|
||||||
|
"URL": "Ссылка",
|
||||||
|
"WEP": "ОРУЖ",
|
||||||
|
"yes": "Да"
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import 'babel-polyfill';
|
import '@babel/polyfill';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { render } from 'react-dom';
|
import { render } from 'react-dom';
|
||||||
import '../less/app.less';
|
import '../less/app.less';
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import { CoriolisLogo, GitHub } from '../components/SvgIcons';
|
|||||||
* About Page
|
* About Page
|
||||||
*/
|
*/
|
||||||
export default class AboutPage extends Page {
|
export default class AboutPage extends Page {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
* @param {Object} props React Component properties
|
* @param {Object} props React Component properties
|
||||||
@@ -23,24 +22,112 @@ export default class AboutPage extends Page {
|
|||||||
* @return {React.Component} The page contents
|
* @return {React.Component} The page contents
|
||||||
*/
|
*/
|
||||||
renderPage() {
|
renderPage() {
|
||||||
return <div className={'page'} style={{ textAlign: 'left', maxWidth: 800, margin: '0 auto' }}>
|
return (
|
||||||
<h1><CoriolisLogo style={{ marginRight: '0.4em' }} className='xl'/><span className='warning'>Coriolis EDCD Edition</span></h1>
|
<div
|
||||||
|
className={'page'}
|
||||||
|
style={{ textAlign: 'left', maxWidth: 800, margin: '0 auto' }}
|
||||||
|
>
|
||||||
|
<h1>
|
||||||
|
<CoriolisLogo style={{ marginRight: '0.4em' }} className="xl" />
|
||||||
|
<span className="warning">Coriolis EDCD Edition</span>
|
||||||
|
</h1>
|
||||||
|
|
||||||
<p>This is a clone of the Coriolis project, whose original author is currently unable to maintain it. This clone is maintained by the <a href="http://edcd.github.io/">EDCD community</a>.</p>
|
<p>
|
||||||
<p>To recover your builds, go to <a href='https://coriolis.io/' target='_blank'>https://coriolis.io/</a>, backup your builds (Settings / Backup), copy the text, return here and import (Settings / Import).</p>
|
This is a clone of the Coriolis project, whose original author is
|
||||||
<p>The Coriolis project was inspired by <a href='http://www.edshipyard.com/' target='_blank'>E:D Shipyard</a> and, of course, <a href='http://www.elitedangerous.com' target='_blank'>Elite Dangerous</a>. The ultimate goal of Coriolis is to provide rich features to support in-game play and planning while engaging the E:D community to support its development.</p>
|
currently unable to maintain it. This clone is maintained by the{' '}
|
||||||
<p>Coriolis was created using assets and imagery from Elite: Dangerous, with the permission of Frontier Developments plc, for non-commercial purposes. It is not endorsed by nor reflects the views or opinions of Frontier Developments. A number of assets were sourced from <a href='http://edassets.org' target='_blank'>ED Assets</a></p>
|
<a href="http://edcd.github.io/">EDCD community</a>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
To recover your builds, go to{' '}
|
||||||
|
<a href="https://coriolis.io/" target="_blank">
|
||||||
|
https://coriolis.io/
|
||||||
|
</a>
|
||||||
|
, backup your builds (Settings / Backup), copy the text, return here
|
||||||
|
and import (Settings / Import).
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The Coriolis project was inspired by{' '}
|
||||||
|
<a href="http://www.edshipyard.com/" target="_blank">
|
||||||
|
E:D Shipyard
|
||||||
|
</a>{' '}
|
||||||
|
and, of course,{' '}
|
||||||
|
<a href="http://www.elitedangerous.com" target="_blank">
|
||||||
|
Elite Dangerous
|
||||||
|
</a>
|
||||||
|
. The ultimate goal of Coriolis is to provide rich features to support
|
||||||
|
in-game play and planning while engaging the E:D community to support
|
||||||
|
its development.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Coriolis was created using assets and imagery from Elite: Dangerous,
|
||||||
|
with the permission of Frontier Developments plc, for non-commercial
|
||||||
|
purposes. It is not endorsed by nor reflects the views or opinions of
|
||||||
|
Frontier Developments. A number of assets were sourced from{' '}
|
||||||
|
<a href="http://edassets.org" target="_blank">
|
||||||
|
ED Assets
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
<a style={{ display: 'block', textDecoration: 'none' }} href='https://github.com/EDCD/coriolis' target='_blank' title='Coriolis Github Project'>
|
<a
|
||||||
<GitHub style={{ margin: '0.4em' }} className='l fg xl'/>
|
style={{ display: 'block', textDecoration: 'none' }}
|
||||||
<h2 style={{ margin: 0, textDecoration: 'none' }}>Github</h2>
|
href="https://github.com/EDCD/coriolis"
|
||||||
github.com/EDCD/coriolis
|
target="_blank"
|
||||||
</a>
|
title="Coriolis Github Project"
|
||||||
|
>
|
||||||
|
<GitHub style={{ margin: '0.4em' }} className="l fg xl" />
|
||||||
|
<h2 style={{ margin: 0, textDecoration: 'none' }}>Github</h2>
|
||||||
|
github.com/EDCD/coriolis
|
||||||
|
</a>
|
||||||
|
|
||||||
<p>Coriolis is an open source project. Checkout the list of upcoming features and to-do list on github. Any and all contributions and feedback are welcome. If you encounter any bugs please report them and provide as much detail as possible.</p>
|
<p>
|
||||||
|
Coriolis is an open source project. Checkout the list of upcoming
|
||||||
|
features and to-do list on github. Any and all contributions and
|
||||||
|
feedback are welcome. If you encounter any bugs please report them and
|
||||||
|
provide as much detail as possible.
|
||||||
|
</p>
|
||||||
|
|
||||||
<h3>Chat</h3>
|
<h3>Chat</h3>
|
||||||
<p>You can chat to us on our <a href='https://discord.gg/0uwCh6R62aPRjk9w' target='_blank'>EDCD Discord server</a>.</p>
|
<p>
|
||||||
</div>;
|
You can chat to us on our{' '}
|
||||||
|
<a href="https://discord.gg/0uwCh6R62aPRjk9w" target="_blank">
|
||||||
|
EDCD Discord server
|
||||||
|
</a>
|
||||||
|
.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Supporting Coriolis</h3>
|
||||||
|
<p>
|
||||||
|
Coriolis is an open source project, and I work on it in my free time.
|
||||||
|
I have set up a patreon at{' '}
|
||||||
|
<a href="https://www.patreon.com/coriolis_elite">
|
||||||
|
patreon.com/coriolis_elite
|
||||||
|
</a>
|
||||||
|
, which will be used to keep Coriolis up to date and the servers
|
||||||
|
running.
|
||||||
|
</p>
|
||||||
|
<form
|
||||||
|
action="https://www.paypal.com/cgi-bin/webscr"
|
||||||
|
method="post"
|
||||||
|
target="_top"
|
||||||
|
>
|
||||||
|
<input type="hidden" name="cmd" value="_s-xclick" />
|
||||||
|
<input type="hidden" name="hosted_button_id" value="SJBKT2SWEEU68" />
|
||||||
|
<input
|
||||||
|
type="image"
|
||||||
|
src="https://www.paypalobjects.com/en_AU/i/btn/btn_donate_SM.gif"
|
||||||
|
border="0"
|
||||||
|
name="submit"
|
||||||
|
alt="PayPal – The safer, easier way to pay online!"
|
||||||
|
/>
|
||||||
|
<img
|
||||||
|
alt=""
|
||||||
|
border="0"
|
||||||
|
src="https://www.paypalobjects.com/en_AU/i/scr/pixel.gif"
|
||||||
|
width="1"
|
||||||
|
height="1"
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,14 @@ import ModalCompare from '../components/ModalCompare';
|
|||||||
import ModalExport from '../components/ModalExport';
|
import ModalExport from '../components/ModalExport';
|
||||||
import ModalPermalink from '../components/ModalPermalink';
|
import ModalPermalink from '../components/ModalPermalink';
|
||||||
import ModalImport from '../components/ModalImport';
|
import ModalImport from '../components/ModalImport';
|
||||||
import { FloppyDisk, Bin, Download, Embed, Rocket, LinkIcon } from '../components/SvgIcons';
|
import {
|
||||||
|
FloppyDisk,
|
||||||
|
Bin,
|
||||||
|
Download,
|
||||||
|
Embed,
|
||||||
|
Rocket,
|
||||||
|
LinkIcon
|
||||||
|
} from '../components/SvgIcons';
|
||||||
import ShortenUrl from '../utils/ShortenUrl';
|
import ShortenUrl from '../utils/ShortenUrl';
|
||||||
import { comparisonBBCode } from '../utils/BBCode';
|
import { comparisonBBCode } from '../utils/BBCode';
|
||||||
const browser = require('detect-browser');
|
const browser = require('detect-browser');
|
||||||
@@ -42,7 +49,6 @@ function sortBy(predicate) {
|
|||||||
* Comparison Page
|
* Comparison Page
|
||||||
*/
|
*/
|
||||||
export default class ComparisonPage extends Page {
|
export default class ComparisonPage extends Page {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
* @param {Object} props React Component properties
|
* @param {Object} props React Component properties
|
||||||
@@ -81,7 +87,13 @@ export default class ComparisonPage extends Page {
|
|||||||
for (let shipId in allBuilds) {
|
for (let shipId in allBuilds) {
|
||||||
for (let buildName in allBuilds[shipId]) {
|
for (let buildName in allBuilds[shipId]) {
|
||||||
if (buildName && allBuilds[shipId][buildName]) {
|
if (buildName && allBuilds[shipId][buildName]) {
|
||||||
builds.push(this._createBuild(shipId, buildName, allBuilds[shipId][buildName]));
|
builds.push(
|
||||||
|
this._createBuild(
|
||||||
|
shipId,
|
||||||
|
buildName,
|
||||||
|
allBuilds[shipId][buildName]
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -89,7 +101,9 @@ export default class ComparisonPage extends Page {
|
|||||||
let comparisonData = Persist.getComparison(name);
|
let comparisonData = Persist.getComparison(name);
|
||||||
if (comparisonData) {
|
if (comparisonData) {
|
||||||
defaultFacets = comparisonData.facets;
|
defaultFacets = comparisonData.facets;
|
||||||
comparisonData.builds.forEach((b) => builds.push(this._createBuild(b.shipId, b.buildName)));
|
comparisonData.builds.forEach(b =>
|
||||||
|
builds.push(this._createBuild(b.shipId, b.buildName))
|
||||||
|
);
|
||||||
saved = true;
|
saved = true;
|
||||||
newName = name;
|
newName = name;
|
||||||
}
|
}
|
||||||
@@ -101,7 +115,7 @@ export default class ComparisonPage extends Page {
|
|||||||
newName = name = comparisonData.n;
|
newName = name = comparisonData.n;
|
||||||
predicate = comparisonData.p;
|
predicate = comparisonData.p;
|
||||||
desc = comparisonData.d;
|
desc = comparisonData.d;
|
||||||
comparisonData.b.forEach((build) => {
|
comparisonData.b.forEach(build => {
|
||||||
builds.push(this._createBuild(build.s, build.n, build.c));
|
builds.push(this._createBuild(build.s, build.n, build.c));
|
||||||
if (!importObj[build.s]) {
|
if (!importObj[build.s]) {
|
||||||
importObj[build.s] = {};
|
importObj[build.s] = {};
|
||||||
@@ -118,9 +132,9 @@ export default class ComparisonPage extends Page {
|
|||||||
let selectedFacets = new Array(selectedLength);
|
let selectedFacets = new Array(selectedLength);
|
||||||
|
|
||||||
for (let i = 0; i < ShipFacets.length; i++) {
|
for (let i = 0; i < ShipFacets.length; i++) {
|
||||||
let facet = Object.assign({ }, ShipFacets[i]);
|
let facet = Object.assign({}, ShipFacets[i]);
|
||||||
let defaultIndex = defaultFacets.indexOf(facet.i);
|
let defaultIndex = defaultFacets.indexOf(facet.i);
|
||||||
if(defaultIndex == -1) {
|
if (defaultIndex == -1) {
|
||||||
facets.push(facet);
|
facets.push(facet);
|
||||||
} else {
|
} else {
|
||||||
facet.active = true;
|
facet.active = true;
|
||||||
@@ -155,17 +169,18 @@ export default class ComparisonPage extends Page {
|
|||||||
_createBuild(id, name, code) {
|
_createBuild(id, name, code) {
|
||||||
code = code ? code : Persist.getBuild(id, name); // Retrieve build code if not passed
|
code = code ? code : Persist.getBuild(id, name); // Retrieve build code if not passed
|
||||||
|
|
||||||
if (!code) { // No build found
|
if (!code) {
|
||||||
|
// No build found
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = Ships[id]; // Get ship properties
|
let data = Ships[id]; // Get ship properties
|
||||||
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());
|
b.applyDiscounts(Persist.getShipDiscount(), Persist.getModuleDiscount());
|
||||||
return b;
|
return b;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update state with the specified sort predicates
|
* Update state with the specified sort predicates
|
||||||
@@ -184,13 +199,18 @@ export default class ComparisonPage extends Page {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.setState({ predicate, desc });
|
this.setState({ predicate, desc });
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show selected builds modal
|
* Show selected builds modal
|
||||||
*/
|
*/
|
||||||
_selectBuilds() {
|
_selectBuilds() {
|
||||||
this.context.showModal(<ModalCompare onSelect={this._buildsSelected} builds={this.state.builds} />);
|
this.context.showModal(
|
||||||
|
<ModalCompare
|
||||||
|
onSelect={this._buildsSelected}
|
||||||
|
builds={this.state.builds}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -224,7 +244,7 @@ export default class ComparisonPage extends Page {
|
|||||||
_facetDrag(e) {
|
_facetDrag(e) {
|
||||||
this.nodeAfter = false;
|
this.nodeAfter = false;
|
||||||
this.dragged = e.currentTarget;
|
this.dragged = e.currentTarget;
|
||||||
let placeholder = this.placeholder = document.createElement('li');
|
let placeholder = (this.placeholder = document.createElement('li'));
|
||||||
placeholder.style.width = Math.round(this.dragged.offsetWidth) + 'px';
|
placeholder.style.width = Math.round(this.dragged.offsetWidth) + 'px';
|
||||||
placeholder.className = 'facet-placeholder';
|
placeholder.className = 'facet-placeholder';
|
||||||
if (!browser || (browser.name !== 'edge' && browser.name !== 'ie')) {
|
if (!browser || (browser.name !== 'edge' && browser.name !== 'ie')) {
|
||||||
@@ -262,7 +282,7 @@ export default class ComparisonPage extends Page {
|
|||||||
_facetDragOver(e) {
|
_facetDragOver(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
if(e.target.className == 'facet-placeholder') {
|
if (e.target.className == 'facet-placeholder') {
|
||||||
return;
|
return;
|
||||||
} else if (e.target != e.currentTarget) {
|
} else if (e.target != e.currentTarget) {
|
||||||
this.over = e.target;
|
this.over = e.target;
|
||||||
@@ -272,7 +292,7 @@ export default class ComparisonPage extends Page {
|
|||||||
let parent = e.target.parentNode;
|
let parent = e.target.parentNode;
|
||||||
|
|
||||||
if (parent == e.currentTarget) {
|
if (parent == e.currentTarget) {
|
||||||
if(relX > width && this.dragged != e.target) {
|
if (relX > width && this.dragged != e.target) {
|
||||||
this.nodeAfter = true;
|
this.nodeAfter = true;
|
||||||
parent.insertBefore(this.placeholder, e.target.nextElementSibling);
|
parent.insertBefore(this.placeholder, e.target.nextElementSibling);
|
||||||
} else {
|
} else {
|
||||||
@@ -321,7 +341,7 @@ export default class ComparisonPage extends Page {
|
|||||||
let { newName, builds, facets } = this.state;
|
let { newName, builds, facets } = this.state;
|
||||||
let selectedFacets = [];
|
let selectedFacets = [];
|
||||||
|
|
||||||
facets.forEach((f) => {
|
facets.forEach(f => {
|
||||||
if (f.active) {
|
if (f.active) {
|
||||||
selectedFacets.unshift(f.i);
|
selectedFacets.unshift(f.i);
|
||||||
}
|
}
|
||||||
@@ -348,14 +368,20 @@ export default class ComparisonPage extends Page {
|
|||||||
|
|
||||||
let code = fromComparison(name, builds, selectedFacets, predicate, desc);
|
let code = fromComparison(name, builds, selectedFacets, predicate, desc);
|
||||||
let loc = window.location;
|
let loc = window.location;
|
||||||
return loc.protocol + '//' + loc.host + '/comparison?code=' + encodeURIComponent(code);
|
return (
|
||||||
|
loc.protocol +
|
||||||
|
'//' +
|
||||||
|
loc.host +
|
||||||
|
'/comparison?code=' +
|
||||||
|
encodeURIComponent(code)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the long permalink URL
|
* Generates the long permalink URL
|
||||||
*/
|
*/
|
||||||
_genPermalink() {
|
_genPermalink() {
|
||||||
this.context.showModal(<ModalPermalink url={this._buildUrl()}/>);
|
this.context.showModal(<ModalPermalink url={this._buildUrl()} />);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -365,18 +391,25 @@ export default class ComparisonPage extends Page {
|
|||||||
let { translate, formats } = this.context.language;
|
let { translate, formats } = this.context.language;
|
||||||
let { facets, builds } = this.state;
|
let { facets, builds } = this.state;
|
||||||
|
|
||||||
let generator = (callback) => {
|
let generator = callback => {
|
||||||
let url = this._buildUrl();
|
let url = this._buildUrl();
|
||||||
ShortenUrl(url,
|
ShortenUrl(
|
||||||
(shortenedUrl) => callback(comparisonBBCode(translate, formats, facets, builds, shortenedUrl)),
|
url,
|
||||||
(error) => callback(comparisonBBCode(translate, formats, facets, builds, url))
|
shortenedUrl =>
|
||||||
|
callback(
|
||||||
|
comparisonBBCode(translate, formats, facets, builds, shortenedUrl)
|
||||||
|
),
|
||||||
|
error =>
|
||||||
|
callback(comparisonBBCode(translate, formats, facets, builds, url))
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.context.showModal(<ModalExport
|
this.context.showModal(
|
||||||
title={translate('forum') + ' BBCode'}
|
<ModalExport
|
||||||
generator={generator}
|
title={translate('forum') + ' BBCode'}
|
||||||
/>);
|
generator={generator}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -409,7 +442,8 @@ export default class ComparisonPage extends Page {
|
|||||||
* @param {Object} nextContext Incoming/Next conext
|
* @param {Object} nextContext Incoming/Next conext
|
||||||
*/
|
*/
|
||||||
componentWillReceiveProps(nextProps, nextContext) {
|
componentWillReceiveProps(nextProps, nextContext) {
|
||||||
if (this.context.route !== nextContext.route) { // Only reinit state if the route has changed
|
if (this.context.route !== nextContext.route) {
|
||||||
|
// Only reinit state if the route has changed
|
||||||
this.setState(this._initState(nextContext));
|
this.setState(this._initState(nextContext));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -419,7 +453,10 @@ 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);
|
this.persistListener = Persist.addListener(
|
||||||
|
'discounts',
|
||||||
|
this._updateDiscounts
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -444,65 +481,132 @@ export default class ComparisonPage extends Page {
|
|||||||
renderPage() {
|
renderPage() {
|
||||||
let translate = this.context.language.translate;
|
let translate = this.context.language.translate;
|
||||||
let compareHeader;
|
let compareHeader;
|
||||||
let { newName, name, saved, builds, facets, predicate, desc, chartWidth } = this.state;
|
let {
|
||||||
|
newName,
|
||||||
|
name,
|
||||||
|
saved,
|
||||||
|
builds,
|
||||||
|
facets,
|
||||||
|
predicate,
|
||||||
|
desc,
|
||||||
|
chartWidth
|
||||||
|
} = this.state;
|
||||||
|
|
||||||
if (this.state.compareMode) {
|
if (this.state.compareMode) {
|
||||||
compareHeader = <tr>
|
compareHeader = (
|
||||||
<td className='head'>{translate('comparison')}</td>
|
<tr>
|
||||||
<td>
|
<td className="head">{translate('comparison')}</td>
|
||||||
<input value={newName} onChange={this._onNameChange} placeholder={translate('Enter Name')} maxLength='50' />
|
<td>
|
||||||
<button onClick={this._save} disabled={!newName || newName == 'all' || saved}>
|
<input
|
||||||
<FloppyDisk className='lg'/><span className='button-lbl'>{translate('save')}</span>
|
value={newName}
|
||||||
</button>
|
onChange={this._onNameChange}
|
||||||
<button onClick={this._delete} disabled={name == 'all' || !saved}><Bin className='lg warning'/></button>
|
placeholder={translate('Enter Name')}
|
||||||
<button onClick={this._selectBuilds}>
|
maxLength="50"
|
||||||
<Rocket className='lg'/><span className='button-lbl'>{translate('builds')}</span>
|
/>
|
||||||
</button>
|
<button
|
||||||
<button className='r' onClick={this._genPermalink} disabled={builds.length == 0}>
|
onClick={this._save}
|
||||||
<LinkIcon className='lg'/><span className='button-lbl'>{translate('permalink')}</span>
|
disabled={!newName || newName == 'all' || saved}
|
||||||
</button>
|
>
|
||||||
<button className='r' onClick={this._genBBcode} disabled={builds.length == 0}>
|
<FloppyDisk className="lg" />
|
||||||
<Embed className='lg'/><span className='button-lbl'>{translate('forum')}</span>
|
<span className="button-lbl">{translate('save')}</span>
|
||||||
</button>
|
</button>
|
||||||
</td>
|
<button onClick={this._delete} disabled={name == 'all' || !saved}>
|
||||||
</tr>;
|
<Bin className="lg warning" />
|
||||||
|
</button>
|
||||||
|
<button onClick={this._selectBuilds}>
|
||||||
|
<Rocket className="lg" />
|
||||||
|
<span className="button-lbl">{translate('builds')}</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="r"
|
||||||
|
onClick={this._genPermalink}
|
||||||
|
disabled={builds.length == 0}
|
||||||
|
>
|
||||||
|
<LinkIcon className="lg" />
|
||||||
|
<span className="button-lbl">{translate('permalink')}</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="r"
|
||||||
|
onClick={this._genBBcode}
|
||||||
|
disabled={builds.length == 0}
|
||||||
|
>
|
||||||
|
<Embed className="lg" />
|
||||||
|
<span className="button-lbl">{translate('forum')}</span>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
compareHeader = <tr>
|
compareHeader = (
|
||||||
<td className='head'>{translate('comparison')}</td>
|
<tr>
|
||||||
<td>
|
<td className="head">{translate('comparison')}</td>
|
||||||
<h3>{name}</h3>
|
<td>
|
||||||
<button className='r' onClick={this._import}><Download className='lg'/>{translate('import')}</button>
|
<h3>{name}</h3>
|
||||||
</td>
|
<button className="r" onClick={this._import}>
|
||||||
</tr>;
|
<Download className="lg" />
|
||||||
|
{translate('import')}
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={'page'} style={{ fontSize: this.context.sizeRatio + 'em' }}>
|
<div
|
||||||
<table id='comparison'>
|
className={'page'}
|
||||||
|
style={{ fontSize: this.context.sizeRatio + 'em' }}
|
||||||
|
>
|
||||||
|
<table id="comparison">
|
||||||
<tbody>
|
<tbody>
|
||||||
{compareHeader}
|
{compareHeader}
|
||||||
<tr key='facets'>
|
<tr key="facets">
|
||||||
<td className='head'>{translate('compare')}</td>
|
<td className="head">{translate('compare')}</td>
|
||||||
<td>
|
<td>
|
||||||
<ul id='facet-container' onDragOver={this._facetDragOver}>
|
<ul id="facet-container" onDragOver={this._facetDragOver}>
|
||||||
{facets.map((f, i) =>
|
{facets.map((f, i) => (
|
||||||
<li key={f.title} data-i={i} draggable='true' onDragStart={this._facetDrag} onDragEnd={this._facetDrop} className={cn('facet', { active: f.active })} onClick={this._toggleFacet.bind(this, f)}>
|
<li
|
||||||
|
key={f.title}
|
||||||
|
data-i={i}
|
||||||
|
draggable="true"
|
||||||
|
onDragStart={this._facetDrag}
|
||||||
|
onDragEnd={this._facetDrop}
|
||||||
|
className={cn('facet', { active: f.active })}
|
||||||
|
onClick={this._toggleFacet.bind(this, f)}
|
||||||
|
>
|
||||||
{'↔ ' + translate(f.title)}
|
{'↔ ' + translate(f.title)}
|
||||||
</li>
|
</li>
|
||||||
)}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<ComparisonTable builds={builds} facets={facets} onSort={this._sortShips} predicate={predicate} desc={desc} />
|
<ComparisonTable
|
||||||
|
builds={builds}
|
||||||
|
facets={facets}
|
||||||
|
onSort={this._sortShips}
|
||||||
|
predicate={predicate}
|
||||||
|
desc={desc}
|
||||||
|
/>
|
||||||
|
|
||||||
{!builds.length ?
|
{!builds.length ? (
|
||||||
<div className='chart' ref={node => this.chartRef = node}>{translate('PHRASE_NO_BUILDS')}</div> :
|
<div className="chart" ref={node => (this.chartRef = node)}>
|
||||||
facets.filter((f) => f.active).map((f, i) =>
|
{translate('PHRASE_NO_BUILDS')}
|
||||||
<div key={f.title} className='chart' ref={ i == 0 ? node => this.chartRef = node : null}>
|
</div>
|
||||||
<h3 className='ptr' onClick={this._sortShips.bind(this, f.props[0])}>{translate(f.title)}</h3>
|
) : (
|
||||||
|
facets.filter(f => f.active).map((f, i) => (
|
||||||
|
<div
|
||||||
|
key={f.title}
|
||||||
|
className="chart"
|
||||||
|
ref={i == 0 ? node => (this.chartRef = node) : null}
|
||||||
|
>
|
||||||
|
<h3
|
||||||
|
className="ptr"
|
||||||
|
onClick={this._sortShips.bind(this, f.props[0])}
|
||||||
|
>
|
||||||
|
{translate(f.title)}
|
||||||
|
</h3>
|
||||||
<BarChart
|
<BarChart
|
||||||
width={chartWidth}
|
width={chartWidth}
|
||||||
data={builds}
|
data={builds}
|
||||||
@@ -515,8 +619,8 @@ export default class ComparisonPage extends Page {
|
|||||||
desc={desc}
|
desc={desc}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
))
|
||||||
)}
|
)}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import Page from './Page';
|
|||||||
* 404 Page
|
* 404 Page
|
||||||
*/
|
*/
|
||||||
export default class NotFoundPage extends Page {
|
export default class NotFoundPage extends Page {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
* @param {Object} props React Component properties
|
* @param {Object} props React Component properties
|
||||||
@@ -22,6 +21,10 @@ export default class NotFoundPage extends Page {
|
|||||||
* @return {React.Component} The page contents
|
* @return {React.Component} The page contents
|
||||||
*/
|
*/
|
||||||
renderPage() {
|
renderPage() {
|
||||||
return <div className='page' style={{ marginTop: 30 }}>Page <small>{this.context.route.path}</small> Not Found</div>;
|
return (
|
||||||
|
<div className="page" style={{ marginTop: 30 }}>
|
||||||
|
Page <small>{this.context.route.path}</small> Not Found
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,20 @@ import Router from '../Router';
|
|||||||
import Persist from '../stores/Persist';
|
import Persist from '../stores/Persist';
|
||||||
import * as Utils from '../utils/UtilityFunctions';
|
import * as Utils from '../utils/UtilityFunctions';
|
||||||
import Ship from '../shipyard/Ship';
|
import Ship from '../shipyard/Ship';
|
||||||
|
import * as _ from 'lodash';
|
||||||
import { toDetailedBuild } from '../shipyard/Serializer';
|
import { toDetailedBuild } from '../shipyard/Serializer';
|
||||||
import { outfitURL } from '../utils/UrlGenerators';
|
import { outfitURL } from '../utils/UrlGenerators';
|
||||||
import { FloppyDisk, Bin, Switch, Download, Reload, LinkIcon, ShoppingIcon } from '../components/SvgIcons';
|
import {
|
||||||
|
FloppyDisk,
|
||||||
|
Bin,
|
||||||
|
Switch,
|
||||||
|
Download,
|
||||||
|
Reload,
|
||||||
|
LinkIcon,
|
||||||
|
ShoppingIcon,
|
||||||
|
MatIcon,
|
||||||
|
OrbisIcon
|
||||||
|
} from '../components/SvgIcons';
|
||||||
import LZString from 'lz-string';
|
import LZString from 'lz-string';
|
||||||
import ShipSummaryTable from '../components/ShipSummaryTable';
|
import ShipSummaryTable from '../components/ShipSummaryTable';
|
||||||
import StandardSlotSection from '../components/StandardSlotSection';
|
import StandardSlotSection from '../components/StandardSlotSection';
|
||||||
@@ -25,6 +36,8 @@ import EngagementRange from '../components/EngagementRange';
|
|||||||
import OutfittingSubpages from '../components/OutfittingSubpages';
|
import OutfittingSubpages from '../components/OutfittingSubpages';
|
||||||
import ModalExport from '../components/ModalExport';
|
import ModalExport from '../components/ModalExport';
|
||||||
import ModalPermalink from '../components/ModalPermalink';
|
import ModalPermalink from '../components/ModalPermalink';
|
||||||
|
import ModalShoppingList from '../components/ModalShoppingList';
|
||||||
|
import ModalOrbis from '../components/ModalOrbis';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Document Title Generator
|
* Document Title Generator
|
||||||
@@ -40,7 +53,6 @@ function getTitle(shipName, buildName) {
|
|||||||
* The Outfitting Page
|
* The Outfitting Page
|
||||||
*/
|
*/
|
||||||
export default class OutfittingPage extends Page {
|
export default class OutfittingPage extends Page {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
* @param {Object} props React Component properties
|
* @param {Object} props React Component properties
|
||||||
@@ -58,6 +70,7 @@ export default class OutfittingPage extends Page {
|
|||||||
this._fuelUpdated = this._fuelUpdated.bind(this);
|
this._fuelUpdated = this._fuelUpdated.bind(this);
|
||||||
this._opponentUpdated = this._opponentUpdated.bind(this);
|
this._opponentUpdated = this._opponentUpdated.bind(this);
|
||||||
this._engagementRangeUpdated = this._engagementRangeUpdated.bind(this);
|
this._engagementRangeUpdated = this._engagementRangeUpdated.bind(this);
|
||||||
|
this._sectionMenuRefs = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -71,25 +84,38 @@ export default class OutfittingPage extends Page {
|
|||||||
let shipId = params.ship;
|
let shipId = params.ship;
|
||||||
let code = params.code;
|
let code = params.code;
|
||||||
let buildName = params.bn;
|
let buildName = params.bn;
|
||||||
let data = Ships[shipId]; // Retrieve the basic ship properties, slots and defaults
|
let data = Ships[shipId]; // Retrieve the basic ship properties, slots and defaults
|
||||||
let savedCode = Persist.getBuild(shipId, buildName);
|
let savedCode = Persist.getBuild(shipId, buildName);
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return { error: { message: 'Ship not found: ' + shipId } };
|
return { error: { message: 'Ship not found: ' + shipId } };
|
||||||
}
|
}
|
||||||
|
let ship = new Ship(shipId, data.properties, data.slots); // Create a new Ship instance
|
||||||
let ship = new Ship(shipId, data.properties, data.slots); // Create a new Ship instance
|
|
||||||
|
|
||||||
if (code) {
|
if (code) {
|
||||||
ship.buildFrom(code); // Populate modules from serialized 'code' URL param
|
ship.buildFrom(code); // Populate modules from serialized 'code' URL param
|
||||||
} else {
|
} else {
|
||||||
ship.buildWith(data.defaults); // Populate with default components
|
ship.buildWith(data.defaults); // Populate with default components
|
||||||
}
|
}
|
||||||
|
|
||||||
this._getTitle = getTitle.bind(this, data.properties.name);
|
this._getTitle = getTitle.bind(this, data.properties.name);
|
||||||
|
|
||||||
// Obtain ship control from code
|
// Obtain ship control from code
|
||||||
const { sys, eng, wep, boost, fuel, cargo, opponent, opponentBuild, opponentSys, opponentEng, opponentWep, engagementRange } = this._obtainControlFromCode(ship, code);
|
const {
|
||||||
|
sys,
|
||||||
|
eng,
|
||||||
|
wep,
|
||||||
|
mcSys,
|
||||||
|
mcEng,
|
||||||
|
mcWep,
|
||||||
|
boost,
|
||||||
|
fuel,
|
||||||
|
cargo,
|
||||||
|
opponent,
|
||||||
|
opponentBuild,
|
||||||
|
opponentSys,
|
||||||
|
opponentEng,
|
||||||
|
opponentWep,
|
||||||
|
engagementRange
|
||||||
|
} = this._obtainControlFromCode(ship, code);
|
||||||
return {
|
return {
|
||||||
error: null,
|
error: null,
|
||||||
title: this._getTitle(buildName),
|
title: this._getTitle(buildName),
|
||||||
@@ -103,6 +129,9 @@ export default class OutfittingPage extends Page {
|
|||||||
sys,
|
sys,
|
||||||
eng,
|
eng,
|
||||||
wep,
|
wep,
|
||||||
|
mcSys,
|
||||||
|
mcEng,
|
||||||
|
mcWep,
|
||||||
boost,
|
boost,
|
||||||
fuel,
|
fuel,
|
||||||
cargo,
|
cargo,
|
||||||
@@ -125,7 +154,10 @@ export default class OutfittingPage extends Page {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (Persist.hasBuild(this.state.shipId, stateChanges.newBuildName)) {
|
if (Persist.hasBuild(this.state.shipId, stateChanges.newBuildName)) {
|
||||||
stateChanges.savedCode = Persist.getBuild(this.state.shipId, stateChanges.newBuildName);
|
stateChanges.savedCode = Persist.getBuild(
|
||||||
|
this.state.shipId,
|
||||||
|
stateChanges.newBuildName
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
stateChanges.savedCode = null;
|
stateChanges.savedCode = null;
|
||||||
}
|
}
|
||||||
@@ -151,7 +183,9 @@ export default class OutfittingPage extends Page {
|
|||||||
* @returns {string} the code for this ship
|
* @returns {string} the code for this ship
|
||||||
*/
|
*/
|
||||||
_fullCode(ship, fuel, cargo) {
|
_fullCode(ship, fuel, cargo) {
|
||||||
return `${ship.toString()}.${LZString.compressToBase64(this._controlCode(fuel, cargo))}`;
|
return `${ship.toString()}.${LZString.compressToBase64(
|
||||||
|
this._controlCode(fuel, cargo)
|
||||||
|
)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -165,10 +199,17 @@ export default class OutfittingPage extends Page {
|
|||||||
let sys = 2;
|
let sys = 2;
|
||||||
let eng = 2;
|
let eng = 2;
|
||||||
let wep = 2;
|
let wep = 2;
|
||||||
|
let mcSys = 0;
|
||||||
|
let mcEng = 0;
|
||||||
|
let mcWep = 0;
|
||||||
let boost = false;
|
let boost = false;
|
||||||
let fuel = ship.fuelCapacity;
|
let fuel = ship.fuelCapacity;
|
||||||
let cargo = ship.cargoCapacity;
|
let cargo = ship.cargoCapacity;
|
||||||
let opponent = new Ship('eagle', Ships['eagle'].properties, Ships['eagle'].slots).buildWith(Ships['eagle'].defaults);
|
let opponent = new Ship(
|
||||||
|
'eagle',
|
||||||
|
Ships['eagle'].properties,
|
||||||
|
Ships['eagle'].slots
|
||||||
|
).buildWith(Ships['eagle'].defaults);
|
||||||
let opponentSys = 2;
|
let opponentSys = 2;
|
||||||
let opponentEng = 2;
|
let opponentEng = 2;
|
||||||
let opponentWep = 2;
|
let opponentWep = 2;
|
||||||
@@ -180,16 +221,25 @@ export default class OutfittingPage extends Page {
|
|||||||
const parts = code.split('.');
|
const parts = code.split('.');
|
||||||
if (parts.length >= 5) {
|
if (parts.length >= 5) {
|
||||||
// We have control information in the code
|
// We have control information in the code
|
||||||
const control = LZString.decompressFromBase64(Utils.fromUrlSafe(parts[4])).split('/');
|
const control = LZString.decompressFromBase64(
|
||||||
|
Utils.fromUrlSafe(parts[4])
|
||||||
|
).split('/');
|
||||||
sys = parseFloat(control[0]);
|
sys = parseFloat(control[0]);
|
||||||
eng = parseFloat(control[1]);
|
eng = parseFloat(control[1]);
|
||||||
wep = parseFloat(control[2]);
|
wep = parseFloat(control[2]);
|
||||||
|
if (sys + eng + wep > 6) {
|
||||||
|
sys = eng = wep = 2;
|
||||||
|
}
|
||||||
boost = control[3] == 1 ? true : false;
|
boost = control[3] == 1 ? true : false;
|
||||||
fuel = parseFloat(control[4]);
|
fuel = parseFloat(control[4]) || fuel;
|
||||||
cargo = parseInt(control[5]);
|
cargo = parseInt(control[5]) || cargo;
|
||||||
if (control[6]) {
|
if (control[6]) {
|
||||||
const shipId = control[6];
|
const shipId = control[6];
|
||||||
opponent = new Ship(shipId, Ships[shipId].properties, Ships[shipId].slots);
|
opponent = new Ship(
|
||||||
|
shipId,
|
||||||
|
Ships[shipId].properties,
|
||||||
|
Ships[shipId].slots
|
||||||
|
);
|
||||||
if (control[7] && Persist.getBuild(shipId, control[7])) {
|
if (control[7] && Persist.getBuild(shipId, control[7])) {
|
||||||
// Ship is a particular build
|
// Ship is a particular build
|
||||||
const opponentCode = Persist.getBuild(shipId, control[7]);
|
const opponentCode = Persist.getBuild(shipId, control[7]);
|
||||||
@@ -199,10 +249,12 @@ export default class OutfittingPage extends Page {
|
|||||||
// Obtain opponent's sys/eng/wep pips from their code
|
// Obtain opponent's sys/eng/wep pips from their code
|
||||||
const opponentParts = opponentCode.split('.');
|
const opponentParts = opponentCode.split('.');
|
||||||
if (opponentParts.length >= 5) {
|
if (opponentParts.length >= 5) {
|
||||||
const opponentControl = LZString.decompressFromBase64(Utils.fromUrlSafe(opponentParts[4])).split('/');
|
const opponentControl = LZString.decompressFromBase64(
|
||||||
opponentSys = parseFloat(opponentControl[0]);
|
Utils.fromUrlSafe(opponentParts[4])
|
||||||
opponentEng = parseFloat(opponentControl[1]);
|
).split('/');
|
||||||
opponentWep = parseFloat(opponentControl[2]);
|
opponentSys = parseFloat(opponentControl[0]) || opponentSys;
|
||||||
|
opponentEng = parseFloat(opponentControl[1]) || opponentEng;
|
||||||
|
opponentWep = parseFloat(opponentControl[2]) || opponentWep;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -210,21 +262,50 @@ export default class OutfittingPage extends Page {
|
|||||||
opponent.buildWith(Ships[shipId].defaults);
|
opponent.buildWith(Ships[shipId].defaults);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
engagementRange = parseInt(control[8]);
|
engagementRange = parseInt(control[8]) || engagementRange;
|
||||||
|
|
||||||
|
// Multi-crew pips were introduced later on so assign default values
|
||||||
|
// because those values might not be present.
|
||||||
|
mcSys = parseInt(control[9]) || mcSys;
|
||||||
|
mcEng = parseInt(control[10]) || mcEng;
|
||||||
|
mcWep = parseInt(control[11]) || mcWep;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { sys, eng, wep, boost, fuel, cargo, opponent, opponentBuild, opponentSys, opponentEng, opponentWep, engagementRange };
|
return {
|
||||||
|
sys,
|
||||||
|
eng,
|
||||||
|
wep,
|
||||||
|
mcSys,
|
||||||
|
mcEng,
|
||||||
|
mcWep,
|
||||||
|
boost,
|
||||||
|
fuel,
|
||||||
|
cargo,
|
||||||
|
opponent,
|
||||||
|
opponentBuild,
|
||||||
|
opponentSys,
|
||||||
|
opponentEng,
|
||||||
|
opponentWep,
|
||||||
|
engagementRange
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Triggered when pips have been updated
|
* Triggered when pips have been updated. Multi-crew pips are already included
|
||||||
|
* in sys, eng and wep but mcSys, mcEng and mcWep make clear where each pip
|
||||||
|
* comes from.
|
||||||
* @param {number} sys SYS pips
|
* @param {number} sys SYS pips
|
||||||
* @param {number} eng ENG pips
|
* @param {number} eng ENG pips
|
||||||
* @param {number} wep WEP pips
|
* @param {number} wep WEP pips
|
||||||
|
* @param {number} mcSys SYS pips from multi-crew
|
||||||
|
* @param {number} mcEng ENG pips from multi-crew
|
||||||
|
* @param {number} mcWep WEP pips from multi-crew
|
||||||
*/
|
*/
|
||||||
_pipsUpdated(sys, eng, wep) {
|
_pipsUpdated(sys, eng, wep, mcSys, mcEng, mcWep) {
|
||||||
this.setState({ sys, eng, wep }, () => this._updateRouteOnControlChange());
|
this.setState({ sys, eng, wep, mcSys, mcEng, mcWep }, () =>
|
||||||
|
this._updateRouteOnControlChange()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -232,7 +313,7 @@ export default class OutfittingPage extends Page {
|
|||||||
* @param {boolean} boost true if boosting
|
* @param {boolean} boost true if boosting
|
||||||
*/
|
*/
|
||||||
_boostUpdated(boost) {
|
_boostUpdated(boost) {
|
||||||
this.setState({ boost }, () => this._updateRouteOnControlChange());
|
this.setState({ boost }, () => this._updateRouteOnControlChange());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -240,7 +321,7 @@ export default class OutfittingPage extends Page {
|
|||||||
* @param {number} fuel the amount of fuel, in T
|
* @param {number} fuel the amount of fuel, in T
|
||||||
*/
|
*/
|
||||||
_fuelUpdated(fuel) {
|
_fuelUpdated(fuel) {
|
||||||
this.setState({ fuel }, () => this._updateRouteOnControlChange());
|
this.setState({ fuel }, () => this._updateRouteOnControlChange());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -248,7 +329,7 @@ export default class OutfittingPage extends Page {
|
|||||||
* @param {number} cargo the amount of cargo, in T
|
* @param {number} cargo the amount of cargo, in T
|
||||||
*/
|
*/
|
||||||
_cargoUpdated(cargo) {
|
_cargoUpdated(cargo) {
|
||||||
this.setState({ cargo }, () => this._updateRouteOnControlChange());
|
this.setState({ cargo }, () => this._updateRouteOnControlChange());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -256,7 +337,9 @@ export default class OutfittingPage extends Page {
|
|||||||
* @param {number} engagementRange the engagement range, in m
|
* @param {number} engagementRange the engagement range, in m
|
||||||
*/
|
*/
|
||||||
_engagementRangeUpdated(engagementRange) {
|
_engagementRangeUpdated(engagementRange) {
|
||||||
this.setState({ engagementRange }, () => this._updateRouteOnControlChange());
|
this.setState({ engagementRange }, () =>
|
||||||
|
this._updateRouteOnControlChange()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -265,7 +348,11 @@ export default class OutfittingPage extends Page {
|
|||||||
* @param {string} opponentBuild the name of the opponent's build
|
* @param {string} opponentBuild the name of the opponent's build
|
||||||
*/
|
*/
|
||||||
_opponentUpdated(opponent, opponentBuild) {
|
_opponentUpdated(opponent, opponentBuild) {
|
||||||
const opponentShip = new Ship(opponent, Ships[opponent].properties, Ships[opponent].slots);
|
const opponentShip = new Ship(
|
||||||
|
opponent,
|
||||||
|
Ships[opponent].properties,
|
||||||
|
Ships[opponent].slots
|
||||||
|
);
|
||||||
let opponentSys = this.state.opponentSys;
|
let opponentSys = this.state.opponentSys;
|
||||||
let opponentEng = this.state.opponentEng;
|
let opponentEng = this.state.opponentEng;
|
||||||
let opponentWep = this.state.opponentWep;
|
let opponentWep = this.state.opponentWep;
|
||||||
@@ -273,9 +360,13 @@ export default class OutfittingPage extends Page {
|
|||||||
// Ship is a particular build
|
// Ship is a particular build
|
||||||
opponentShip.buildFrom(Persist.getBuild(opponent, opponentBuild));
|
opponentShip.buildFrom(Persist.getBuild(opponent, opponentBuild));
|
||||||
// Set pips for opponent
|
// Set pips for opponent
|
||||||
const opponentParts = Persist.getBuild(opponent, opponentBuild).split('.');
|
const opponentParts = Persist.getBuild(opponent, opponentBuild).split(
|
||||||
|
'.'
|
||||||
|
);
|
||||||
if (opponentParts.length >= 5) {
|
if (opponentParts.length >= 5) {
|
||||||
const opponentControl = LZString.decompressFromBase64(Utils.fromUrlSafe(opponentParts[4])).split('/');
|
const opponentControl = LZString.decompressFromBase64(
|
||||||
|
Utils.fromUrlSafe(opponentParts[4])
|
||||||
|
).split('/');
|
||||||
opponentSys = parseFloat(opponentControl[0]);
|
opponentSys = parseFloat(opponentControl[0]);
|
||||||
opponentEng = parseFloat(opponentControl[1]);
|
opponentEng = parseFloat(opponentControl[1]);
|
||||||
opponentWep = parseFloat(opponentControl[2]);
|
opponentWep = parseFloat(opponentControl[2]);
|
||||||
@@ -288,7 +379,16 @@ export default class OutfittingPage extends Page {
|
|||||||
opponentWep = 2;
|
opponentWep = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({ opponent: opponentShip, opponentBuild, opponentSys, opponentEng, opponentWep }, () => this._updateRouteOnControlChange());
|
this.setState(
|
||||||
|
{
|
||||||
|
opponent: opponentShip,
|
||||||
|
opponentBuild,
|
||||||
|
opponentSys,
|
||||||
|
opponentEng,
|
||||||
|
opponentWep
|
||||||
|
},
|
||||||
|
() => this._updateRouteOnControlChange()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -298,8 +398,22 @@ export default class OutfittingPage extends Page {
|
|||||||
* @returns {string} The control code
|
* @returns {string} The control code
|
||||||
*/
|
*/
|
||||||
_controlCode(fuel, cargo) {
|
_controlCode(fuel, cargo) {
|
||||||
const { sys, eng, wep, boost, opponent, opponentBuild, engagementRange } = this.state;
|
const {
|
||||||
const code = `${sys}/${eng}/${wep}/${boost ? 1 : 0}/${fuel || this.state.fuel}/${cargo || this.state.cargo}/${opponent.id}/${opponentBuild ? opponentBuild : ''}/${engagementRange}`;
|
sys,
|
||||||
|
eng,
|
||||||
|
wep,
|
||||||
|
mcSys,
|
||||||
|
mcEng,
|
||||||
|
mcWep,
|
||||||
|
boost,
|
||||||
|
opponent,
|
||||||
|
opponentBuild,
|
||||||
|
engagementRange
|
||||||
|
} = this.state;
|
||||||
|
const code = `${sys}/${eng}/${wep}/${boost ? 1 : 0}/${fuel ||
|
||||||
|
this.state.fuel}/${cargo || this.state.cargo}/${opponent.id}/${
|
||||||
|
opponentBuild ? opponentBuild : ''
|
||||||
|
}/${engagementRange}/${mcSys}/${mcEng}/${mcWep}`;
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -310,27 +424,44 @@ export default class OutfittingPage extends Page {
|
|||||||
const { ship, buildName, newBuildName, shipId } = this.state;
|
const { ship, buildName, newBuildName, shipId } = this.state;
|
||||||
|
|
||||||
// If this is a stock ship the code won't be set, so ensure that we have it
|
// If this is a stock ship the code won't be set, so ensure that we have it
|
||||||
const code = this.state.code || ship.toString();
|
const code = this.state.code || ship.toString();
|
||||||
|
|
||||||
Persist.saveBuild(shipId, newBuildName, code);
|
Persist.saveBuild(shipId, newBuildName, code);
|
||||||
this._updateRoute(shipId, newBuildName, code);
|
this._updateRoute(shipId, newBuildName, code);
|
||||||
|
|
||||||
let opponent, opponentBuild, opponentSys, opponentEng, opponentWep;
|
let opponent, opponentBuild, opponentSys, opponentEng, opponentWep;
|
||||||
if (shipId === this.state.opponent.id && buildName === this.state.opponentBuild) {
|
if (
|
||||||
|
shipId === this.state.opponent.id &&
|
||||||
|
buildName === this.state.opponentBuild
|
||||||
|
) {
|
||||||
// This is a save of our current opponent build; update it
|
// This is a save of our current opponent build; update it
|
||||||
opponentBuild = newBuildName;
|
opponentBuild = newBuildName;
|
||||||
opponent = new Ship(shipId, Ships[shipId].properties, Ships[shipId].slots).buildFrom(code);
|
opponent = new Ship(
|
||||||
|
shipId,
|
||||||
|
Ships[shipId].properties,
|
||||||
|
Ships[shipId].slots
|
||||||
|
).buildFrom(code);
|
||||||
opponentSys = this.state.sys;
|
opponentSys = this.state.sys;
|
||||||
opponentEng = this.state.eng;
|
opponentEng = this.state.eng;
|
||||||
opponentWep = this.state.wep;
|
opponentWep = this.state.wep;
|
||||||
} else {
|
} else {
|
||||||
opponentBuild = this.state.opponentBuild;
|
opponentBuild = this.state.opponentBuild;
|
||||||
opponent = this.state.opponent;
|
opponent = this.state.opponent;
|
||||||
opponentSys = this.state.opponentSys;
|
opponentSys = this.state.opponentSys;
|
||||||
opponentEng = this.state.opponentEng;
|
opponentEng = this.state.opponentEng;
|
||||||
opponentWep = this.state.opponentWep;
|
opponentWep = this.state.opponentWep;
|
||||||
}
|
}
|
||||||
this.setState({ buildName: newBuildName, code, savedCode: code, opponent, opponentBuild, opponentSys, opponentEng, opponentWep, title: this._getTitle(newBuildName) });
|
this.setState({
|
||||||
|
buildName: newBuildName,
|
||||||
|
code,
|
||||||
|
savedCode: code,
|
||||||
|
opponent,
|
||||||
|
opponentBuild,
|
||||||
|
opponentSys,
|
||||||
|
opponentEng,
|
||||||
|
opponentWep,
|
||||||
|
title: this._getTitle(newBuildName)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -342,7 +473,12 @@ export default class OutfittingPage extends Page {
|
|||||||
Persist.deleteBuild(shipId, buildName);
|
Persist.deleteBuild(shipId, buildName);
|
||||||
Persist.saveBuild(shipId, newBuildName, code);
|
Persist.saveBuild(shipId, newBuildName, code);
|
||||||
this._updateRoute(shipId, newBuildName, code);
|
this._updateRoute(shipId, newBuildName, code);
|
||||||
this.setState({ buildName: newBuildName, code, savedCode: code, opponentBuild: newBuildName });
|
this.setState({
|
||||||
|
buildName: newBuildName,
|
||||||
|
code,
|
||||||
|
savedCode: code,
|
||||||
|
opponentBuild: newBuildName
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -362,19 +498,38 @@ export default class OutfittingPage extends Page {
|
|||||||
ship.buildWith(Ships[shipId].defaults);
|
ship.buildWith(Ships[shipId].defaults);
|
||||||
// Reset controls
|
// Reset controls
|
||||||
const code = ship.toString();
|
const code = ship.toString();
|
||||||
const { sys, eng, wep, boost, fuel, cargo, opponent, opponentBuild, engagementRange } = this._obtainControlFromCode(ship, code);
|
const {
|
||||||
// Update state, and refresh the ship
|
|
||||||
this.setState({
|
|
||||||
sys,
|
sys,
|
||||||
eng,
|
eng,
|
||||||
wep,
|
wep,
|
||||||
|
mcSys,
|
||||||
|
mcEng,
|
||||||
|
mcWep,
|
||||||
boost,
|
boost,
|
||||||
fuel,
|
fuel,
|
||||||
cargo,
|
cargo,
|
||||||
opponent,
|
opponent,
|
||||||
opponentBuild,
|
opponentBuild,
|
||||||
engagementRange
|
engagementRange
|
||||||
}, () => this._updateRoute(shipId, buildName, code));
|
} = this._obtainControlFromCode(ship, code);
|
||||||
|
// Update state, and refresh the ship
|
||||||
|
this.setState(
|
||||||
|
{
|
||||||
|
sys,
|
||||||
|
eng,
|
||||||
|
wep,
|
||||||
|
mcSys,
|
||||||
|
mcEng,
|
||||||
|
mcWep,
|
||||||
|
boost,
|
||||||
|
fuel,
|
||||||
|
cargo,
|
||||||
|
opponent,
|
||||||
|
opponentBuild,
|
||||||
|
engagementRange
|
||||||
|
},
|
||||||
|
() => this._updateRoute(shipId, buildName, code)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -385,7 +540,10 @@ export default class OutfittingPage extends Page {
|
|||||||
Persist.deleteBuild(shipId, buildName);
|
Persist.deleteBuild(shipId, buildName);
|
||||||
|
|
||||||
let opponentBuild;
|
let opponentBuild;
|
||||||
if (shipId === this.state.opponent.id && buildName === this.state.opponentBuild) {
|
if (
|
||||||
|
shipId === this.state.opponent.id &&
|
||||||
|
buildName === this.state.opponentBuild
|
||||||
|
) {
|
||||||
// Our current opponent has been deleted; revert to stock
|
// Our current opponent has been deleted; revert to stock
|
||||||
opponentBuild = null;
|
opponentBuild = null;
|
||||||
} else {
|
} else {
|
||||||
@@ -402,11 +560,13 @@ export default class OutfittingPage extends Page {
|
|||||||
_exportBuild() {
|
_exportBuild() {
|
||||||
let translate = this.context.language.translate;
|
let translate = this.context.language.translate;
|
||||||
let { buildName, ship } = this.state;
|
let { buildName, ship } = this.state;
|
||||||
this.context.showModal(<ModalExport
|
this.context.showModal(
|
||||||
title={(buildName || ship.name) + ' ' + translate('export')}
|
<ModalExport
|
||||||
description={translate('PHRASE_EXPORT_DESC')}
|
title={(buildName || ship.name) + ' ' + translate('export')}
|
||||||
data={toDetailedBuild(buildName, ship, ship.toString())}
|
description={translate('PHRASE_EXPORT_DESC')}
|
||||||
/>);
|
data={toDetailedBuild(buildName, ship, ship.toString())}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -419,19 +579,38 @@ export default class OutfittingPage extends Page {
|
|||||||
this.state.ship.buildFrom(code);
|
this.state.ship.buildFrom(code);
|
||||||
|
|
||||||
// Obtain controls from the code
|
// Obtain controls from the code
|
||||||
const { sys, eng, wep, boost, fuel, cargo, opponent, opponentBuild, engagementRange } = this._obtainControlFromCode(ship, code);
|
const {
|
||||||
// Update state, and refresh the route when complete
|
|
||||||
this.setState({
|
|
||||||
sys,
|
sys,
|
||||||
eng,
|
eng,
|
||||||
wep,
|
wep,
|
||||||
|
mcSys,
|
||||||
|
mcEng,
|
||||||
|
mcWep,
|
||||||
boost,
|
boost,
|
||||||
fuel,
|
fuel,
|
||||||
cargo,
|
cargo,
|
||||||
opponent,
|
opponent,
|
||||||
opponentBuild,
|
opponentBuild,
|
||||||
engagementRange
|
engagementRange
|
||||||
}, () => this._updateRoute(shipId, buildName, code));
|
} = this._obtainControlFromCode(ship, code);
|
||||||
|
// Update state, and refresh the route when complete
|
||||||
|
this.setState(
|
||||||
|
{
|
||||||
|
sys,
|
||||||
|
eng,
|
||||||
|
wep,
|
||||||
|
mcSys,
|
||||||
|
mcEng,
|
||||||
|
mcWep,
|
||||||
|
boost,
|
||||||
|
fuel,
|
||||||
|
cargo,
|
||||||
|
opponent,
|
||||||
|
opponentBuild,
|
||||||
|
engagementRange
|
||||||
|
},
|
||||||
|
() => this._updateRoute(shipId, buildName, code)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -447,8 +626,14 @@ export default class OutfittingPage extends Page {
|
|||||||
}
|
}
|
||||||
const code = this._fullCode(ship, fuel, cargo);
|
const code = this._fullCode(ship, fuel, cargo);
|
||||||
// Only update the state if this really has been updated
|
// Only update the state if this really has been updated
|
||||||
if (this.state.code != code || this.state.cargo != cargo || this.state.fuel != fuel) {
|
if (
|
||||||
this.setState({ code, cargo, fuel }, () => this._updateRoute(shipId, buildName, code));
|
this.state.code != code ||
|
||||||
|
this.state.cargo != cargo ||
|
||||||
|
this.state.fuel != fuel
|
||||||
|
) {
|
||||||
|
this.setState({ code, cargo, fuel }, () =>
|
||||||
|
this._updateRoute(shipId, buildName, code)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -468,7 +653,8 @@ export default class OutfittingPage extends Page {
|
|||||||
* @param {Object} nextContext Incoming/Next conext
|
* @param {Object} nextContext Incoming/Next conext
|
||||||
*/
|
*/
|
||||||
componentWillReceiveProps(nextProps, nextContext) {
|
componentWillReceiveProps(nextProps, nextContext) {
|
||||||
if (this.context.route !== nextContext.route) { // Only reinit state if the route has changed
|
if (this.context.route !== nextContext.route) {
|
||||||
|
// Only reinit state if the route has changed
|
||||||
this.setState(this._initState(nextProps, nextContext));
|
this.setState(this._initState(nextProps, nextContext));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -491,7 +677,24 @@ export default class OutfittingPage extends Page {
|
|||||||
* Generates the short URL
|
* Generates the short URL
|
||||||
*/
|
*/
|
||||||
_genShortlink() {
|
_genShortlink() {
|
||||||
this.context.showModal(<ModalPermalink url={window.location.href}/>);
|
this.context.showModal(<ModalPermalink url={window.location.href} />);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate Orbis link
|
||||||
|
*/
|
||||||
|
_genOrbis() {
|
||||||
|
const data = {};
|
||||||
|
const ship = this.state.ship;
|
||||||
|
ship.coriolisId = ship.id;
|
||||||
|
data.coriolisShip = ship;
|
||||||
|
data.url = window.location.href;
|
||||||
|
data.title = this.state.buildName || ship.id;
|
||||||
|
data.description = this.state.buildName || ship.id;
|
||||||
|
data.ShipName = ship.id;
|
||||||
|
data.Ship = ship.id;
|
||||||
|
console.log(data);
|
||||||
|
this.context.showModal(<ModalOrbis ship={data} />);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -502,10 +705,23 @@ export default class OutfittingPage extends Page {
|
|||||||
|
|
||||||
const shipId = Ships[ship.id].eddbID;
|
const shipId = Ships[ship.id].eddbID;
|
||||||
// Provide unique list of non-PP module EDDB IDs
|
// Provide unique list of non-PP module EDDB IDs
|
||||||
const modIds = ship.internal.concat(ship.bulkheads, ship.standard, ship.hardpoints).filter(slot => slot !== null && slot.m !== null && !slot.m.pp).map(slot => slot.m.eddbID).filter((v, i, a) => a.indexOf(v) === i);
|
const modIds = ship.internal
|
||||||
|
.concat(ship.bulkheads, ship.standard, ship.hardpoints)
|
||||||
|
.filter(slot => slot !== null && slot.m !== null && !slot.m.pp)
|
||||||
|
.map(slot => slot.m.eddbID)
|
||||||
|
.filter((v, i, a) => a.indexOf(v) === i);
|
||||||
|
|
||||||
// Open up the relevant URL
|
// Open up the relevant URL
|
||||||
window.open('https://eddb.io/station?s=' + shipId + '&m=' + modIds.join(','));
|
window.open(
|
||||||
|
'https://eddb.io/station?s=' + shipId + '&m=' + modIds.join(',')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the shopping list
|
||||||
|
*/
|
||||||
|
_genShoppingList() {
|
||||||
|
this.context.showModal(<ModalShoppingList ship={this.state.ship} />);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -515,8 +731,9 @@ export default class OutfittingPage extends Page {
|
|||||||
_keyDown(e) {
|
_keyDown(e) {
|
||||||
// .keyCode will eventually be replaced with .key
|
// .keyCode will eventually be replaced with .key
|
||||||
switch (e.keyCode) {
|
switch (e.keyCode) {
|
||||||
case 69: // 'e'
|
case 69: // 'e'
|
||||||
if (e.ctrlKey || e.metaKey) { // CTRL/CMD + e
|
if (e.ctrlKey || e.metaKey) {
|
||||||
|
// CTRL/CMD + e
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this._exportBuild();
|
this._exportBuild();
|
||||||
}
|
}
|
||||||
@@ -532,7 +749,28 @@ export default class OutfittingPage extends Page {
|
|||||||
let state = this.state,
|
let state = this.state,
|
||||||
{ language, termtip, tooltip, sizeRatio, onWindowResize } = this.context,
|
{ language, termtip, tooltip, sizeRatio, onWindowResize } = this.context,
|
||||||
{ translate, units, formats } = language,
|
{ translate, units, formats } = language,
|
||||||
{ ship, code, savedCode, buildName, newBuildName, sys, eng, wep, boost, fuel, cargo, opponent, opponentBuild, opponentSys, opponentEng, opponentWep, engagementRange } = state,
|
{
|
||||||
|
ship,
|
||||||
|
code,
|
||||||
|
savedCode,
|
||||||
|
buildName,
|
||||||
|
newBuildName,
|
||||||
|
sys,
|
||||||
|
eng,
|
||||||
|
wep,
|
||||||
|
mcSys,
|
||||||
|
mcEng,
|
||||||
|
mcWep,
|
||||||
|
boost,
|
||||||
|
fuel,
|
||||||
|
cargo,
|
||||||
|
opponent,
|
||||||
|
opponentBuild,
|
||||||
|
opponentSys,
|
||||||
|
opponentEng,
|
||||||
|
opponentWep,
|
||||||
|
engagementRange
|
||||||
|
} = state,
|
||||||
hide = tooltip.bind(null, null),
|
hide = tooltip.bind(null, null),
|
||||||
menu = this.props.currentMenu,
|
menu = this.props.currentMenu,
|
||||||
shipUpdated = this._shipUpdated,
|
shipUpdated = this._shipUpdated,
|
||||||
@@ -550,80 +788,295 @@ export default class OutfittingPage extends Page {
|
|||||||
const _pStr = `${ship.getPowerEnabledString()}${ship.getPowerPrioritiesString()}`;
|
const _pStr = `${ship.getPowerEnabledString()}${ship.getPowerPrioritiesString()}`;
|
||||||
const _mStr = ship.getModificationsString();
|
const _mStr = ship.getModificationsString();
|
||||||
|
|
||||||
const standardSlotMarker = `${ship.name}${_sStr}${_pStr}${_mStr}${ship.ladenMass}${cargo}${fuel}`;
|
const standardSlotMarker = `${ship.name}${_sStr}${_pStr}${_mStr}${
|
||||||
|
ship.ladenMass
|
||||||
|
}${cargo}${fuel}`;
|
||||||
const internalSlotMarker = `${ship.name}${_iStr}${_pStr}${_mStr}`;
|
const internalSlotMarker = `${ship.name}${_iStr}${_pStr}${_mStr}`;
|
||||||
const hardpointsSlotMarker = `${ship.name}${_hStr}${_pStr}${_mStr}`;
|
const hardpointsSlotMarker = `${ship.name}${_hStr}${_pStr}${_mStr}`;
|
||||||
const boostMarker = `${ship.canBoost(cargo, fuel)}`;
|
const boostMarker = `${ship.canBoost(cargo, fuel)}`;
|
||||||
const shipSummaryMarker = `${ship.name}${_sStr}${_iStr}${_hStr}${_pStr}${_mStr}${ship.ladenMass}${cargo}${fuel}`;
|
const shipSummaryMarker = `${
|
||||||
|
ship.name
|
||||||
|
}${_sStr}${_iStr}${_hStr}${_pStr}${_mStr}${ship.ladenMass}${cargo}${fuel}`;
|
||||||
|
|
||||||
|
const requirements = Ships[ship.id].requirements;
|
||||||
|
let requirementElements = [];
|
||||||
|
/**
|
||||||
|
* Render the requirements for a ship / etc
|
||||||
|
* @param {string} className Class names
|
||||||
|
* @param {string} textKey The key for translating
|
||||||
|
* @param {String} tooltipTextKey Tooltip key
|
||||||
|
*/
|
||||||
|
function renderRequirement(className, textKey, tooltipTextKey) {
|
||||||
|
if (textKey.startsWith('empire') || textKey.startsWith('federation')) {
|
||||||
|
requirementElements.push(
|
||||||
|
<div
|
||||||
|
key={textKey}
|
||||||
|
className={className}
|
||||||
|
onMouseEnter={termtip.bind(null, tooltipTextKey)}
|
||||||
|
onMouseLeave={hide}
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
href={
|
||||||
|
textKey.startsWith('empire') ?
|
||||||
|
'http://elite-dangerous.wikia.com/wiki/Empire/Ranks' :
|
||||||
|
'http://elite-dangerous.wikia.com/wiki/Federation/Ranks'
|
||||||
|
}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
>
|
||||||
|
{translate(textKey)}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
requirementElements.push(
|
||||||
|
<div
|
||||||
|
key={textKey}
|
||||||
|
className={className}
|
||||||
|
onMouseEnter={termtip.bind(null, tooltipTextKey)}
|
||||||
|
onMouseLeave={hide}
|
||||||
|
>
|
||||||
|
{translate(textKey)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requirements) {
|
||||||
|
requirements.federationRank &&
|
||||||
|
renderRequirement(
|
||||||
|
'federation',
|
||||||
|
'federation rank ' + requirements.federationRank,
|
||||||
|
'federation rank required'
|
||||||
|
);
|
||||||
|
requirements.empireRank &&
|
||||||
|
renderRequirement(
|
||||||
|
'empire',
|
||||||
|
'empire rank ' + requirements.empireRank,
|
||||||
|
'empire rank required'
|
||||||
|
);
|
||||||
|
requirements.horizons &&
|
||||||
|
renderRequirement('horizons', 'horizons', 'horizons required');
|
||||||
|
requirements.horizonsEarlyAdoption &&
|
||||||
|
renderRequirement(
|
||||||
|
'horizons',
|
||||||
|
'horizons early adoption',
|
||||||
|
'horizons early adoption required'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id='outfit' className={'page'} style={{ fontSize: (sizeRatio * 0.9) + 'em' }}>
|
<div
|
||||||
<div id='overview'>
|
id="outfit"
|
||||||
|
className={'page'}
|
||||||
|
style={{ fontSize: sizeRatio * 0.9 + 'em' }}
|
||||||
|
>
|
||||||
|
<div id="overview">
|
||||||
<h1>{ship.name}</h1>
|
<h1>{ship.name}</h1>
|
||||||
<div id='build'>
|
<div id="requirements">{requirementElements}</div>
|
||||||
<input value={newBuildName || ''} onChange={this._buildNameChange} placeholder={translate('Enter Name')} maxLength={50} />
|
<div id="build">
|
||||||
<button onClick={canSave && this._saveBuild} disabled={!canSave} onMouseOver={termtip.bind(null, 'save')} onMouseOut={hide}>
|
<input
|
||||||
<FloppyDisk className='lg' />
|
value={newBuildName || ''}
|
||||||
|
onChange={this._buildNameChange}
|
||||||
|
placeholder={translate('Enter Name')}
|
||||||
|
maxLength={50}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
onClick={canSave && this._saveBuild}
|
||||||
|
disabled={!canSave}
|
||||||
|
onMouseOver={termtip.bind(null, 'save')}
|
||||||
|
onMouseOut={hide}
|
||||||
|
>
|
||||||
|
<FloppyDisk className="lg" />
|
||||||
</button>
|
</button>
|
||||||
<button onClick={canRename && this._renameBuild} disabled={!canRename} onMouseOver={termtip.bind(null, 'rename')} onMouseOut={hide}>
|
<button
|
||||||
<span style={{ textTransform: 'none', fontSize: '1.8em' }}>a|</span>
|
onClick={canRename && this._renameBuild}
|
||||||
|
disabled={!canRename}
|
||||||
|
onMouseOver={termtip.bind(null, 'rename')}
|
||||||
|
onMouseOut={hide}
|
||||||
|
>
|
||||||
|
<span style={{ textTransform: 'none', fontSize: '1.8em' }}>
|
||||||
|
a|
|
||||||
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<button onClick={canReload && this._reloadBuild} disabled={!canReload} onMouseOver={termtip.bind(null, 'reload')} onMouseOut={hide}>
|
<button
|
||||||
<Reload className='lg'/>
|
onClick={canReload && this._reloadBuild}
|
||||||
|
disabled={!canReload}
|
||||||
|
onMouseOver={termtip.bind(null, 'reload')}
|
||||||
|
onMouseOut={hide}
|
||||||
|
>
|
||||||
|
<Reload className="lg" />
|
||||||
</button>
|
</button>
|
||||||
<button className={'danger'} onClick={savedCode && this._deleteBuild} disabled={!savedCode} onMouseOver={termtip.bind(null, 'delete')} onMouseOut={hide}>
|
<button
|
||||||
<Bin className='lg'/>
|
className={'danger'}
|
||||||
|
onClick={savedCode && this._deleteBuild}
|
||||||
|
disabled={!savedCode}
|
||||||
|
onMouseOver={termtip.bind(null, 'delete')}
|
||||||
|
onMouseOut={hide}
|
||||||
|
>
|
||||||
|
<Bin className="lg" />
|
||||||
</button>
|
</button>
|
||||||
<button onClick={code && this._resetBuild} disabled={!code} onMouseOver={termtip.bind(null, 'reset')} onMouseOut={hide}>
|
<button
|
||||||
<Switch className='lg'/>
|
onClick={code && this._resetBuild}
|
||||||
|
disabled={!code}
|
||||||
|
onMouseOver={termtip.bind(null, 'reset')}
|
||||||
|
onMouseOut={hide}
|
||||||
|
>
|
||||||
|
<Switch className="lg" />
|
||||||
</button>
|
</button>
|
||||||
<button onClick={buildName && this._exportBuild} disabled={!buildName} onMouseOver={termtip.bind(null, 'export')} onMouseOut={hide}>
|
<button
|
||||||
<Download className='lg'/>
|
onClick={buildName && this._exportBuild}
|
||||||
|
disabled={!buildName}
|
||||||
|
onMouseOver={termtip.bind(null, 'export')}
|
||||||
|
onMouseOut={hide}
|
||||||
|
>
|
||||||
|
<Download className="lg" />
|
||||||
</button>
|
</button>
|
||||||
<button onClick={this._eddbShoppingList} onMouseOver={termtip.bind(null, 'PHRASE_SHOPPING_LIST')} onMouseOut={hide}>
|
<button
|
||||||
<ShoppingIcon className='lg' />
|
onClick={this._eddbShoppingList}
|
||||||
|
onMouseOver={termtip.bind(null, 'PHRASE_SHOPPING_LIST')}
|
||||||
|
onMouseOut={hide}
|
||||||
|
>
|
||||||
|
<ShoppingIcon className="lg" />
|
||||||
</button>
|
</button>
|
||||||
<button onClick={this._genShortlink} onMouseOver={termtip.bind(null, 'shortlink')} onMouseOut={hide}>
|
<button
|
||||||
<LinkIcon className='lg' />
|
onClick={this._genShortlink}
|
||||||
|
onMouseOver={termtip.bind(null, 'shortlink')}
|
||||||
|
onMouseOut={hide}
|
||||||
|
>
|
||||||
|
<LinkIcon className="lg" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={this._genOrbis}
|
||||||
|
onMouseOver={termtip.bind(null, 'PHASE_UPLOAD_ORBIS')}
|
||||||
|
onMouseOut={hide}
|
||||||
|
>
|
||||||
|
<OrbisIcon className="lg" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={this._genShoppingList}
|
||||||
|
onMouseOver={termtip.bind(null, 'PHRASE_SHOPPING_MATS')}
|
||||||
|
onMouseOut={hide}
|
||||||
|
>
|
||||||
|
<MatIcon className="lg" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Main tables */}
|
{/* Main tables */}
|
||||||
<ShipSummaryTable ship={ship} fuel={fuel} cargo={cargo} marker={shipSummaryMarker} />
|
<ShipSummaryTable
|
||||||
<StandardSlotSection ship={ship} fuel={fuel} cargo={cargo} code={standardSlotMarker} onChange={shipUpdated} onCargoChange={this._cargoUpdated} onFuelChange={this._fuelUpdated} currentMenu={menu} />
|
ship={ship}
|
||||||
<InternalSlotSection ship={ship} code={internalSlotMarker} onChange={shipUpdated} onCargoChange={this._cargoUpdated} onFuelChange={this._fuelUpdated} currentMenu={menu} />
|
fuel={fuel}
|
||||||
<HardpointSlotSection ship={ship} code={hardpointsSlotMarker} onChange={shipUpdated} onCargoChange={this._cargoUpdated} onFuelChange={this._fuelUpdated} currentMenu={menu} />
|
cargo={cargo}
|
||||||
<UtilitySlotSection ship={ship} code={hardpointsSlotMarker} onChange={shipUpdated} onCargoChange={this._cargoUpdated} onFuelChange={this._fuelUpdated} currentMenu={menu} />
|
marker={shipSummaryMarker}
|
||||||
|
pips={{
|
||||||
|
sys: this.state.sys,
|
||||||
|
wep: this.state.wep,
|
||||||
|
eng: this.state.eng
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<StandardSlotSection
|
||||||
|
ship={ship}
|
||||||
|
fuel={fuel}
|
||||||
|
cargo={cargo}
|
||||||
|
code={standardSlotMarker}
|
||||||
|
onChange={shipUpdated}
|
||||||
|
onCargoChange={this._cargoUpdated}
|
||||||
|
onFuelChange={this._fuelUpdated}
|
||||||
|
currentMenu={menu}
|
||||||
|
sectionMenuRefs={this._sectionMenuRefs}
|
||||||
|
/>
|
||||||
|
<InternalSlotSection
|
||||||
|
ship={ship}
|
||||||
|
code={internalSlotMarker}
|
||||||
|
onChange={shipUpdated}
|
||||||
|
onCargoChange={this._cargoUpdated}
|
||||||
|
onFuelChange={this._fuelUpdated}
|
||||||
|
currentMenu={menu}
|
||||||
|
sectionMenuRefs={this._sectionMenuRefs}
|
||||||
|
/>
|
||||||
|
<HardpointSlotSection
|
||||||
|
ship={ship}
|
||||||
|
code={hardpointsSlotMarker}
|
||||||
|
onChange={shipUpdated}
|
||||||
|
onCargoChange={this._cargoUpdated}
|
||||||
|
onFuelChange={this._fuelUpdated}
|
||||||
|
currentMenu={menu}
|
||||||
|
sectionMenuRefs={this._sectionMenuRefs}
|
||||||
|
/>
|
||||||
|
<UtilitySlotSection
|
||||||
|
ship={ship}
|
||||||
|
code={hardpointsSlotMarker}
|
||||||
|
onChange={shipUpdated}
|
||||||
|
onCargoChange={this._cargoUpdated}
|
||||||
|
onFuelChange={this._fuelUpdated}
|
||||||
|
currentMenu={menu}
|
||||||
|
sectionMenuRefs={this._sectionMenuRefs}
|
||||||
|
/>
|
||||||
|
|
||||||
{/* Control of ship and opponent */}
|
{/* Control of ship and opponent */}
|
||||||
<div className='group quarter'>
|
<div className="group quarter">
|
||||||
<div className='group half'>
|
<div className="group half">
|
||||||
<h2 style={{ verticalAlign: 'middle', textAlign: 'left' }}>{translate('ship control')}</h2>
|
<h2 style={{ verticalAlign: 'middle', textAlign: 'left' }}>
|
||||||
|
{translate('ship control')}
|
||||||
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<div className='group half'>
|
<div className="group half">
|
||||||
<Boost marker={boostMarker} ship={ship} boost={boost} onChange={this._boostUpdated} />
|
<Boost
|
||||||
|
marker={boostMarker}
|
||||||
|
ship={ship}
|
||||||
|
boost={boost}
|
||||||
|
onChange={this._boostUpdated}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='group quarter'>
|
<div className="group quarter">
|
||||||
<Pips sys={sys} eng={eng} wep={wep} onChange={this._pipsUpdated} />
|
<Pips
|
||||||
|
sys={sys}
|
||||||
|
eng={eng}
|
||||||
|
wep={wep}
|
||||||
|
mcSys={mcSys}
|
||||||
|
mcEng={mcEng}
|
||||||
|
mcWep={mcWep}
|
||||||
|
onChange={this._pipsUpdated}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='group quarter'>
|
<div className="group quarter">
|
||||||
<Fuel fuelCapacity={ship.fuelCapacity} fuel={fuel} onChange={this._fuelUpdated}/>
|
<Fuel
|
||||||
|
fuelCapacity={ship.fuelCapacity}
|
||||||
|
fuel={fuel}
|
||||||
|
onChange={this._fuelUpdated}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='group quarter'>
|
<div className="group quarter">
|
||||||
{ ship.cargoCapacity > 0 ? <Cargo cargoCapacity={ship.cargoCapacity} cargo={cargo} onChange={this._cargoUpdated}/> : null }
|
{ship.cargoCapacity > 0 ? (
|
||||||
|
<Cargo
|
||||||
|
cargoCapacity={ship.cargoCapacity}
|
||||||
|
cargo={cargo}
|
||||||
|
onChange={this._cargoUpdated}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
<div className='group half'>
|
<div className="group half">
|
||||||
<div className='group quarter'>
|
<div className="group quarter">
|
||||||
<h2 style={{ verticalAlign: 'middle', textAlign: 'left' }}>{translate('opponent')}</h2>
|
<h2 style={{ verticalAlign: 'middle', textAlign: 'left' }}>
|
||||||
|
{translate('opponent')}
|
||||||
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<div className='group threequarters'>
|
<div className="group threequarters">
|
||||||
<ShipPicker ship={opponent.id} build={opponentBuild} onChange={this._opponentUpdated}/>
|
<ShipPicker
|
||||||
|
ship={opponent.id}
|
||||||
|
build={opponentBuild}
|
||||||
|
onChange={this._opponentUpdated}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='group half'>
|
<div className="group half">
|
||||||
<EngagementRange ship={ship} engagementRange={engagementRange} onChange={this._engagementRangeUpdated}/>
|
<EngagementRange
|
||||||
|
ship={ship}
|
||||||
|
engagementRange={engagementRange}
|
||||||
|
onChange={this._engagementRangeUpdated}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Tabbed subpages */}
|
{/* Tabbed subpages */}
|
||||||
|
|||||||
@@ -51,22 +51,16 @@ export default class Page extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pages are 'pure' components that only render when props, state, or context changes.
|
* Update the window title upon mount
|
||||||
* This method performs a shallow comparison to determine change.
|
|
||||||
*
|
|
||||||
* @param {Object} np Next/Incoming properties
|
|
||||||
* @param {Object} ns Next/Incoming state
|
|
||||||
* @param {Object} nc Next/Incoming context
|
|
||||||
* @return {Boolean} True if props, state, or context has changed
|
|
||||||
*/
|
*/
|
||||||
shouldComponentUpdate(np, ns, nc) {
|
componentWillMount() {
|
||||||
return !shallowEqual(this.props, np) || !shallowEqual(this.state, ns) || !shallowEqual(this.context, nc);
|
document.title = this.state.title || 'Coriolis';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the window title upon mount
|
* Update the window title upon mount
|
||||||
*/
|
*/
|
||||||
componentWillMount() {
|
componentDidMount() {
|
||||||
document.title = this.state.title || 'Coriolis';
|
document.title = this.state.title || 'Coriolis';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,9 +22,32 @@ function countHp(slot) {
|
|||||||
*/
|
*/
|
||||||
function countInt(slot) {
|
function countInt(slot) {
|
||||||
let crEligible = !slot.eligible || slot.eligible.cr;
|
let crEligible = !slot.eligible || slot.eligible.cr;
|
||||||
this.int[slot.maxClass - 1]++; // Subtract 1 since there is no Class 0 Internal compartment
|
this.int[slot.maxClass - 1]++; // Subtract 1 since there is no Class 0 Internal compartment
|
||||||
this.intCount++;
|
this.intCount++;
|
||||||
this.maxCargo += crEligible ? ModuleUtils.findInternal('cr', slot.maxClass, 'E').cargo : 0;
|
this.maxCargo += crEligible ?
|
||||||
|
ModuleUtils.findInternal('cr', slot.maxClass, 'E').cargo :
|
||||||
|
0;
|
||||||
|
|
||||||
|
// if no eligiblity, then assume pce
|
||||||
|
let passSlotType = null;
|
||||||
|
let passSlotRating = null;
|
||||||
|
if (!slot.eligible || slot.eligible.pce) {
|
||||||
|
passSlotType = 'pce';
|
||||||
|
passSlotRating = 'E';
|
||||||
|
} else if (slot.eligible.pci) {
|
||||||
|
passSlotType = 'pci';
|
||||||
|
passSlotRating = 'D';
|
||||||
|
} else if (slot.eligible.pcm) {
|
||||||
|
passSlotType = 'pcm';
|
||||||
|
passSlotRating = 'C';
|
||||||
|
} else if (slot.eligible.pcq) {
|
||||||
|
passSlotType = 'pcq';
|
||||||
|
passSlotRating = 'B';
|
||||||
|
}
|
||||||
|
let passengerBay = passSlotType ?
|
||||||
|
ModuleUtils.findMaxInternal(passSlotType, slot.maxClass, passSlotRating) :
|
||||||
|
null;
|
||||||
|
this.maxPassengers += passengerBay ? passengerBay.passengers : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,22 +61,27 @@ function shipSummary(shipId, shipData) {
|
|||||||
id: shipId,
|
id: shipId,
|
||||||
hpCount: 0,
|
hpCount: 0,
|
||||||
intCount: 0,
|
intCount: 0,
|
||||||
|
beta: shipData.beta,
|
||||||
maxCargo: 0,
|
maxCargo: 0,
|
||||||
|
maxPassengers: 0,
|
||||||
hp: [0, 0, 0, 0, 0], // Utility, Small, Medium, Large, Huge
|
hp: [0, 0, 0, 0, 0], // Utility, Small, Medium, Large, Huge
|
||||||
int: [0, 0, 0, 0, 0, 0, 0, 0], // Sizes 1 - 8
|
int: [0, 0, 0, 0, 0, 0, 0, 0], // Sizes 1 - 8
|
||||||
standard: shipData.slots.standard,
|
standard: shipData.slots.standard,
|
||||||
agility: shipData.properties.pitch + shipData.properties.yaw + shipData.properties.roll
|
agility:
|
||||||
|
shipData.properties.pitch +
|
||||||
|
shipData.properties.yaw +
|
||||||
|
shipData.properties.roll
|
||||||
};
|
};
|
||||||
Object.assign(summary, shipData.properties);
|
Object.assign(summary, shipData.properties);
|
||||||
let ship = new Ship(shipId, shipData.properties, shipData.slots);
|
let ship = new Ship(shipId, shipData.properties, shipData.slots);
|
||||||
|
|
||||||
// Build Ship
|
// Build Ship
|
||||||
ship.buildWith(shipData.defaults); // Populate with stock/default components
|
ship.buildWith(shipData.defaults); // Populate with stock/default components
|
||||||
ship.hardpoints.forEach(countHp.bind(summary)); // Count Hardpoints by class
|
ship.hardpoints.forEach(countHp.bind(summary)); // Count Hardpoints by class
|
||||||
ship.internal.forEach(countInt.bind(summary)); // Count Internal Compartments by class
|
ship.internal.forEach(countInt.bind(summary)); // Count Internal Compartments by class
|
||||||
summary.retailCost = ship.totalCost; // Record Stock/Default/retail cost
|
summary.retailCost = ship.totalCost; // Record Stock/Default/retail cost
|
||||||
ship.optimizeMass({ pd: '1D' }); // Optimize Mass with 1D PD for maximum possible jump range
|
ship.optimizeMass({ pd: '1D' }); // Optimize Mass with 1D PD for maximum possible jump range
|
||||||
summary.maxJumpRange = ship.unladenRange; // Record Jump Range
|
summary.maxJumpRange = ship.unladenRange; // Record Jump Range
|
||||||
|
|
||||||
// Best thrusters
|
// Best thrusters
|
||||||
let th;
|
let th;
|
||||||
@@ -77,7 +105,6 @@ function shipSummary(shipId, shipData) {
|
|||||||
* The Shipyard summary page
|
* The Shipyard summary page
|
||||||
*/
|
*/
|
||||||
export default class ShipyardPage extends Page {
|
export default class ShipyardPage extends Page {
|
||||||
|
|
||||||
static cachedShipSummaries = null;
|
static cachedShipSummaries = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -99,12 +126,14 @@ export default class ShipyardPage extends Page {
|
|||||||
title: 'Coriolis EDCD Edition - Shipyard',
|
title: 'Coriolis EDCD Edition - Shipyard',
|
||||||
shipPredicate: 'name',
|
shipPredicate: 'name',
|
||||||
shipDesc: true,
|
shipDesc: true,
|
||||||
shipSummaries: ShipyardPage.cachedShipSummaries
|
shipSummaries: ShipyardPage.cachedShipSummaries,
|
||||||
|
compare: {},
|
||||||
|
groupCompared: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Higlight the current ship in the table
|
* Higlight the current ship in the table on mouse over
|
||||||
* @param {String} shipId Ship Id
|
* @param {String} shipId Ship Id
|
||||||
* @param {SyntheticEvent} event Event
|
* @param {SyntheticEvent} event Event
|
||||||
*/
|
*/
|
||||||
@@ -113,6 +142,24 @@ export default class ShipyardPage extends Page {
|
|||||||
this.setState({ shipId });
|
this.setState({ shipId });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle compare highlighting for ships in the table
|
||||||
|
* @param {String} shipId Ship Id
|
||||||
|
*/
|
||||||
|
_toggleCompare(shipId) {
|
||||||
|
let compare = this.state.compare;
|
||||||
|
compare[shipId] = !compare[shipId];
|
||||||
|
this.setState({ compare });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle grouping of compared ships in the table
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_toggleGroupCompared() {
|
||||||
|
this.setState({groupCompared: !this.state.groupCompared})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update state with the specified sort predicates
|
* Update state with the specified sort predicates
|
||||||
* @param {String} shipPredicate Sort predicate - property name
|
* @param {String} shipPredicate Sort predicate - property name
|
||||||
@@ -125,12 +172,15 @@ export default class ShipyardPage extends Page {
|
|||||||
shipPredicateIndex = undefined;
|
shipPredicateIndex = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.shipPredicate == shipPredicate && this.state.shipPredicateIndex == shipPredicateIndex) {
|
if (
|
||||||
|
this.state.shipPredicate == shipPredicate &&
|
||||||
|
this.state.shipPredicateIndex == shipPredicateIndex
|
||||||
|
) {
|
||||||
shipDesc = !shipDesc;
|
shipDesc = !shipDesc;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({ shipPredicate, shipDesc, shipPredicateIndex });
|
this.setState({ shipPredicate, shipDesc, shipPredicateIndex });
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate the table row summary for the ship
|
* Generate the table row summary for the ship
|
||||||
@@ -144,48 +194,56 @@ export default class ShipyardPage extends Page {
|
|||||||
_shipRowElement(s, translate, u, fInt, fRound) {
|
_shipRowElement(s, translate, u, fInt, fRound) {
|
||||||
let noTouch = this.context.noTouch;
|
let noTouch = this.context.noTouch;
|
||||||
|
|
||||||
return <tr
|
return (
|
||||||
|
<tr
|
||||||
key={s.id}
|
key={s.id}
|
||||||
style={{ height: '1.5em' }}
|
style={{ height: '1.5em' }}
|
||||||
className={cn({ highlighted: noTouch && this.state.shipId === s.id })}
|
className={cn({
|
||||||
|
highlighted: noTouch && this.state.shipId === s.id,
|
||||||
|
comparehighlight: this.state.compare[s.id],
|
||||||
|
})}
|
||||||
onMouseEnter={noTouch && this._highlightShip.bind(this, s.id)}
|
onMouseEnter={noTouch && this._highlightShip.bind(this, s.id)}
|
||||||
|
onClick={() => this._toggleCompare(s.id)}
|
||||||
>
|
>
|
||||||
<td className='ri'>{s.manufacturer}</td>
|
<td className="ri">{s.manufacturer}</td>
|
||||||
<td className='ri'>{fInt(s.retailCost)}</td>
|
<td className="ri">{fInt(s.retailCost)}</td>
|
||||||
<td className='ri cap'>{translate(SizeMap[s.class])}</td>
|
<td className="ri cap">{translate(SizeMap[s.class])}</td>
|
||||||
<td className='ri'>{fInt(s.crew)}</td>
|
<td className="ri">{fInt(s.crew)}</td>
|
||||||
<td className='ri'>{s.masslock}</td>
|
<td className="ri">{s.masslock}</td>
|
||||||
<td className='ri'>{fInt(s.agility)}</td>
|
<td className="ri">{fInt(s.agility)}</td>
|
||||||
<td className='ri'>{fInt(s.hardness)}</td>
|
<td className="ri">{fInt(s.hardness)}</td>
|
||||||
<td className='ri'>{fInt(s.hullMass)}</td>
|
<td className="ri">{fInt(s.hullMass)}</td>
|
||||||
<td className='ri'>{fInt(s.speed)}</td>
|
<td className="ri">{fInt(s.speed)}</td>
|
||||||
<td className='ri'>{fInt(s.boost)}</td>
|
<td className="ri">{fInt(s.boost)}</td>
|
||||||
<td className='ri'>{fInt(s.baseArmour)}</td>
|
<td className="ri">{fInt(s.baseArmour)}</td>
|
||||||
<td className='ri'>{fInt(s.baseShieldStrength)}</td>
|
<td className="ri">{fInt(s.baseShieldStrength)}</td>
|
||||||
<td className='ri'>{fInt(s.topSpeed)}</td>
|
<td className="ri">{fInt(s.topSpeed)}</td>
|
||||||
<td className='ri'>{fInt(s.topBoost)}</td>
|
<td className="ri">{fInt(s.topBoost)}</td>
|
||||||
<td className='ri'>{fRound(s.maxJumpRange)}</td>
|
<td className="ri">{fRound(s.maxJumpRange)}</td>
|
||||||
<td className='ri'>{fInt(s.maxCargo)}</td>
|
<td className="ri">{fInt(s.maxCargo)}</td>
|
||||||
<td className='cn'>{s.standard[0]}</td>
|
<td className="ri">{fInt(s.maxPassengers)}</td>
|
||||||
<td className='cn'>{s.standard[1]}</td>
|
<td className="cn">{s.standard[0]}</td>
|
||||||
<td className='cn'>{s.standard[2]}</td>
|
<td className="cn">{s.standard[1]}</td>
|
||||||
<td className='cn'>{s.standard[3]}</td>
|
<td className="cn">{s.standard[2]}</td>
|
||||||
<td className='cn'>{s.standard[4]}</td>
|
<td className="cn">{s.standard[3]}</td>
|
||||||
<td className='cn'>{s.standard[5]}</td>
|
<td className="cn">{s.standard[4]}</td>
|
||||||
<td className={cn({ disabled: !s.hp[1] })}>{s.hp[1]}</td>
|
<td className="cn">{s.standard[5]}</td>
|
||||||
<td className={cn({ disabled: !s.hp[2] })}>{s.hp[2]}</td>
|
<td className="cn">{s.standard[6]}</td>
|
||||||
<td className={cn({ disabled: !s.hp[3] })}>{s.hp[3]}</td>
|
<td className={cn({ disabled: !s.hp[1] })}>{s.hp[1]}</td>
|
||||||
<td className={cn({ disabled: !s.hp[4] })}>{s.hp[4]}</td>
|
<td className={cn({ disabled: !s.hp[2] })}>{s.hp[2]}</td>
|
||||||
<td className={cn({ disabled: !s.hp[0] })}>{s.hp[0]}</td>
|
<td className={cn({ disabled: !s.hp[3] })}>{s.hp[3]}</td>
|
||||||
<td className={cn({ disabled: !s.int[0] })}>{s.int[0]}</td>
|
<td className={cn({ disabled: !s.hp[4] })}>{s.hp[4]}</td>
|
||||||
<td className={cn({ disabled: !s.int[1] })}>{s.int[1]}</td>
|
<td className={cn({ disabled: !s.hp[0] })}>{s.hp[0]}</td>
|
||||||
<td className={cn({ disabled: !s.int[2] })}>{s.int[2]}</td>
|
<td className={cn({ disabled: !s.int[0] })}>{s.int[0]}</td>
|
||||||
<td className={cn({ disabled: !s.int[3] })}>{s.int[3]}</td>
|
<td className={cn({ disabled: !s.int[1] })}>{s.int[1]}</td>
|
||||||
<td className={cn({ disabled: !s.int[4] })}>{s.int[4]}</td>
|
<td className={cn({ disabled: !s.int[2] })}>{s.int[2]}</td>
|
||||||
<td className={cn({ disabled: !s.int[5] })}>{s.int[5]}</td>
|
<td className={cn({ disabled: !s.int[3] })}>{s.int[3]}</td>
|
||||||
<td className={cn({ disabled: !s.int[6] })}>{s.int[6]}</td>
|
<td className={cn({ disabled: !s.int[4] })}>{s.int[4]}</td>
|
||||||
<td className={cn({ disabled: !s.int[7] })}>{s.int[7]}</td>
|
<td className={cn({ disabled: !s.int[5] })}>{s.int[5]}</td>
|
||||||
</tr>;
|
<td className={cn({ disabled: !s.int[6] })}>{s.int[6]}</td>
|
||||||
|
<td className={cn({ disabled: !s.int[7] })}>{s.int[7]}</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -198,8 +256,9 @@ export default class ShipyardPage extends Page {
|
|||||||
let hide = this.context.tooltip.bind(null, null);
|
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, shipPredicate, shipPredicateIndex } = this.state;
|
let { shipSummaries, shipPredicate, shipPredicateIndex, compare, groupCompared } = this.state;
|
||||||
let sortShips = (predicate, index) => this._sortShips.bind(this, predicate, index);
|
let sortShips = (predicate, index) =>
|
||||||
|
this._sortShips.bind(this, predicate, index);
|
||||||
|
|
||||||
let filters = {
|
let filters = {
|
||||||
// 'class': { 1: 1, 2: 1}
|
// 'class': { 1: 1, 2: 1}
|
||||||
@@ -216,7 +275,8 @@ export default class ShipyardPage extends Page {
|
|||||||
|
|
||||||
// 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];
|
||||||
|
|
||||||
if (shipPredicateIndex != undefined) {
|
if (shipPredicateIndex != undefined) {
|
||||||
valA = valA[shipPredicateIndex];
|
valA = valA[shipPredicateIndex];
|
||||||
@@ -229,7 +289,16 @@ export default class ShipyardPage extends Page {
|
|||||||
valB = val;
|
valB = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(valA == valB) {
|
if (groupCompared) {
|
||||||
|
if (compare[a.id] && !compare[b.id]) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!compare[a.id] && compare[b.id]) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valA == valB) {
|
||||||
if (a.name > b.name) {
|
if (a.name > b.name) {
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
@@ -247,114 +316,316 @@ export default class ShipyardPage extends Page {
|
|||||||
let detailRows = new Array(shipSummaries.length);
|
let detailRows = new Array(shipSummaries.length);
|
||||||
|
|
||||||
for (let s of shipSummaries) {
|
for (let s of shipSummaries) {
|
||||||
detailRows[i] = this._shipRowElement(s, translate, units, fInt, formats.f1);
|
detailRows[i] = this._shipRowElement(
|
||||||
|
s,
|
||||||
|
translate,
|
||||||
|
units,
|
||||||
|
fInt,
|
||||||
|
formats.f1,
|
||||||
|
);
|
||||||
shipRows[i] = (
|
shipRows[i] = (
|
||||||
<tr
|
<tr
|
||||||
key={i}
|
key={i}
|
||||||
style={{ height: '1.5em' }}
|
style={{ height: '1.5em' }}
|
||||||
className={cn({ highlighted: noTouch && this.state.shipId === s.id })}
|
className={cn({
|
||||||
|
highlighted: noTouch && this.state.shipId === s.id,
|
||||||
|
comparehighlight: this.state.compare[s.id],
|
||||||
|
})}
|
||||||
onMouseEnter={noTouch && this._highlightShip.bind(this, s.id)}
|
onMouseEnter={noTouch && this._highlightShip.bind(this, s.id)}
|
||||||
|
onClick={() => this._toggleCompare(s.id)}
|
||||||
>
|
>
|
||||||
<td className='le'><Link href={'/outfit/' + s.id}>{s.name}</Link></td>
|
<td className="le">
|
||||||
|
<Link href={'/outfit/' + s.id}>{s.name} {s.beta === true ? '(Beta)' : null}</Link>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='page' style={{ fontSize: sizeRatio + 'em' }}>
|
<div className="page" style={{fontSize: sizeRatio + 'em'}}>
|
||||||
<div style={{ whiteSpace: 'nowrap', margin: '0 auto', fontSize: '0.8em', position: 'relative', display: 'inline-block', maxWidth: '100%' }}>
|
<div className="content-wrapper">
|
||||||
<table style={{ width: '12em', position: 'absolute', zIndex: 1 }}>
|
<div className="shipyard-table-wrapper">
|
||||||
|
<table style={{width: '12em', position: 'absolute', zIndex: 1}} className="shipyard-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th className='le rgt'> </th>
|
<th className="le rgt"> </th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr className='main'>
|
<tr className="main">
|
||||||
<th className='sortable le rgt' onClick={sortShips('name')}>{translate('ship')}</th>
|
<th className="sortable le rgt" onClick={sortShips('name')}>
|
||||||
|
{translate('ship')}
|
||||||
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th className='le rgt invisible'>{units['m/s']}</th>
|
<th className="le rgt invisible">{units['m/s']}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody onMouseLeave={this._highlightShip.bind(this, null)}>
|
<tbody onMouseLeave={this._highlightShip.bind(this, null)}>
|
||||||
{shipRows}
|
{shipRows}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div style={{ overflowX: 'scroll', maxWidth: '100%' }}>
|
<div style={{ overflowX: 'auto', maxWidth: '100%' }}>
|
||||||
<table style={{ marginLeft: 'calc(12em - 1px)', zIndex: 0 }}>
|
<table style={{ marginLeft: 'calc(12em - 1px)', zIndex: 0 }} className="shipyard-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr className='main'>
|
<tr className="main">
|
||||||
<th rowSpan={3} className='sortable' onClick={sortShips('manufacturer')}>{translate('manufacturer')}</th>
|
<th
|
||||||
<th> </th>
|
rowSpan={3}
|
||||||
<th rowSpan={3} className='sortable' onClick={sortShips('class')}>{translate('size')}</th>
|
className="sortable"
|
||||||
<th rowSpan={3} className='sortable' onClick={sortShips('crew')}>{translate('crew')}</th>
|
onClick={sortShips('manufacturer')}
|
||||||
<th rowSpan={3} className='sortable' onMouseEnter={termtip.bind(null, 'mass lock factor')} onMouseLeave={hide} onClick={sortShips('masslock')} >{translate('MLF')}</th>
|
>
|
||||||
<th rowSpan={3} className='sortable' onClick={sortShips('agility')}>{translate('agility')}</th>
|
{translate('manufacturer')}
|
||||||
<th rowSpan={3} className='sortable' onMouseEnter={termtip.bind(null, 'hardness')} onMouseLeave={hide} onClick={sortShips('hardness')}>{translate('hrd')}</th>
|
</th>
|
||||||
<th> </th>
|
<th> </th>
|
||||||
<th colSpan={4}>{translate('base')}</th>
|
<th
|
||||||
<th colSpan={4}>{translate('max')}</th>
|
rowSpan={3}
|
||||||
<th className='lft' colSpan={6}></th>
|
className="sortable"
|
||||||
<th className='lft' colSpan={5}></th>
|
onClick={sortShips('class')}
|
||||||
<th className='lft' colSpan={8}></th>
|
>
|
||||||
</tr>
|
{translate('size')}
|
||||||
<tr>
|
</th>
|
||||||
<th className='sortable lft' onClick={sortShips('retailCost')}>{translate('cost')}</th>
|
<th
|
||||||
<th className='sortable lft' onClick={sortShips('hullMass')}>{translate('hull')}</th>
|
rowSpan={3}
|
||||||
<th className='sortable lft' onClick={sortShips('speed')}>{translate('speed')}</th>
|
className="sortable"
|
||||||
<th className='sortable' onClick={sortShips('boost')}>{translate('boost')}</th>
|
onClick={sortShips('crew')}
|
||||||
<th className='sortable' onClick={sortShips('baseArmour')}>{translate('armour')}</th>
|
>
|
||||||
<th className='sortable' onClick={sortShips('baseShieldStrength')}>{translate('shields')}</th>
|
{translate('crew')}
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
rowSpan={3}
|
||||||
|
className="sortable"
|
||||||
|
onMouseEnter={termtip.bind(null, 'mass lock factor')}
|
||||||
|
onMouseLeave={hide}
|
||||||
|
onClick={sortShips('masslock')}
|
||||||
|
>
|
||||||
|
{translate('MLF')}
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
rowSpan={3}
|
||||||
|
className="sortable"
|
||||||
|
onClick={sortShips('agility')}
|
||||||
|
>
|
||||||
|
{translate('agility')}
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
rowSpan={3}
|
||||||
|
className="sortable"
|
||||||
|
onMouseEnter={termtip.bind(null, 'hardness')}
|
||||||
|
onMouseLeave={hide}
|
||||||
|
onClick={sortShips('hardness')}
|
||||||
|
>
|
||||||
|
{translate('hrd')}
|
||||||
|
</th>
|
||||||
|
<th> </th>
|
||||||
|
<th colSpan={4}>{translate('base')}</th>
|
||||||
|
<th colSpan={5}>{translate('max')}</th>
|
||||||
|
<th className="lft" colSpan={7} />
|
||||||
|
<th className="lft" colSpan={5} />
|
||||||
|
<th className="lft" colSpan={8} />
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th
|
||||||
|
className="sortable lft"
|
||||||
|
onClick={sortShips('retailCost')}
|
||||||
|
>
|
||||||
|
{translate('cost')}
|
||||||
|
</th>
|
||||||
|
<th className="sortable lft" onClick={sortShips('hullMass')}>
|
||||||
|
{translate('hull')}
|
||||||
|
</th>
|
||||||
|
<th className="sortable lft" onClick={sortShips('speed')}>
|
||||||
|
{translate('speed')}
|
||||||
|
</th>
|
||||||
|
<th className="sortable" onClick={sortShips('boost')}>
|
||||||
|
{translate('boost')}
|
||||||
|
</th>
|
||||||
|
<th className="sortable" onClick={sortShips('baseArmour')}>
|
||||||
|
{translate('armour')}
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
className="sortable"
|
||||||
|
onClick={sortShips('baseShieldStrength')}
|
||||||
|
>
|
||||||
|
{translate('shields')}
|
||||||
|
</th>
|
||||||
|
|
||||||
<th className='sortable lft' onClick={sortShips('topSpeed')}>{translate('speed')}</th>
|
<th className="sortable lft" onClick={sortShips('topSpeed')}>
|
||||||
<th className='sortable' onClick={sortShips('topBoost')}>{translate('boost')}</th>
|
{translate('speed')}
|
||||||
<th className='sortable' onClick={sortShips('maxJumpRange')}>{translate('jump')}</th>
|
</th>
|
||||||
<th className='sortable' onClick={sortShips('maxCargo')}>{translate('cargo')}</th>
|
<th className="sortable" onClick={sortShips('topBoost')}>
|
||||||
|
{translate('boost')}
|
||||||
|
</th>
|
||||||
|
<th className="sortable" onClick={sortShips('maxJumpRange')}>
|
||||||
|
{translate('jump')}
|
||||||
|
</th>
|
||||||
|
<th className="sortable" onClick={sortShips('maxCargo')}>
|
||||||
|
{translate('cargo')}
|
||||||
|
</th>
|
||||||
|
<th className="sortable" onClick={sortShips('maxPassengers')} onMouseEnter={termtip.bind(null, 'passenger capacity')}
|
||||||
|
onMouseLeave={hide}>
|
||||||
|
{translate('pax')}
|
||||||
|
</th>
|
||||||
|
<th className="lft" colSpan={7}>
|
||||||
|
{translate('core module classes')}
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
colSpan={5}
|
||||||
|
className="sortable lft"
|
||||||
|
onClick={sortShips('hpCount')}
|
||||||
|
>
|
||||||
|
{translate('hardpoints')}
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
colSpan={8}
|
||||||
|
className="sortable lft"
|
||||||
|
onClick={sortShips('intCount')}
|
||||||
|
>
|
||||||
|
{translate('internal compartments')}
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th
|
||||||
|
className="sortable lft"
|
||||||
|
onClick={sortShips('retailCost')}
|
||||||
|
>
|
||||||
|
{units.CR}
|
||||||
|
</th>
|
||||||
|
<th className="sortable lft" onClick={sortShips('hullMass')}>
|
||||||
|
{units.T}
|
||||||
|
</th>
|
||||||
|
<th className="sortable lft" onClick={sortShips('speed')}>
|
||||||
|
{units['m/s']}
|
||||||
|
</th>
|
||||||
|
<th className="sortable" onClick={sortShips('boost')}>
|
||||||
|
{units['m/s']}
|
||||||
|
</th>
|
||||||
|
<th> </th>
|
||||||
|
<th
|
||||||
|
className="sortable"
|
||||||
|
onClick={sortShips('baseShieldStrength')}
|
||||||
|
>
|
||||||
|
{units.MJ}
|
||||||
|
</th>
|
||||||
|
<th className="sortable lft" onClick={sortShips('topSpeed')}>
|
||||||
|
{units['m/s']}
|
||||||
|
</th>
|
||||||
|
<th className="sortable" onClick={sortShips('topBoost')}>
|
||||||
|
{units['m/s']}
|
||||||
|
</th>
|
||||||
|
<th className="sortable" onClick={sortShips('maxJumpRange')}>
|
||||||
|
{units.LY}
|
||||||
|
</th>
|
||||||
|
<th className="sortable" onClick={sortShips('maxCargo')}>
|
||||||
|
{units.T}
|
||||||
|
</th>
|
||||||
|
<th> </th>
|
||||||
|
<th
|
||||||
|
className="sortable lft"
|
||||||
|
onMouseEnter={termtip.bind(null, 'power plant')}
|
||||||
|
onMouseLeave={hide}
|
||||||
|
onClick={sortShips('standard', 0)}
|
||||||
|
>
|
||||||
|
{'pp'}
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
className="sortable"
|
||||||
|
onMouseEnter={termtip.bind(null, 'thrusters')}
|
||||||
|
onMouseLeave={hide}
|
||||||
|
onClick={sortShips('standard', 1)}
|
||||||
|
>
|
||||||
|
{'th'}
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
className="sortable"
|
||||||
|
onMouseEnter={termtip.bind(null, 'frame shift drive')}
|
||||||
|
onMouseLeave={hide}
|
||||||
|
onClick={sortShips('standard', 2)}
|
||||||
|
>
|
||||||
|
{'fsd'}
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
className="sortable"
|
||||||
|
onMouseEnter={termtip.bind(null, 'life support')}
|
||||||
|
onMouseLeave={hide}
|
||||||
|
onClick={sortShips('standard', 3)}
|
||||||
|
>
|
||||||
|
{'ls'}
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
className="sortable"
|
||||||
|
onMouseEnter={termtip.bind(null, 'power distriubtor')}
|
||||||
|
onMouseLeave={hide}
|
||||||
|
onClick={sortShips('standard', 4)}
|
||||||
|
>
|
||||||
|
{'pd'}
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
className="sortable"
|
||||||
|
onMouseEnter={termtip.bind(null, 'sensors')}
|
||||||
|
onMouseLeave={hide}
|
||||||
|
onClick={sortShips('standard', 5)}
|
||||||
|
>
|
||||||
|
{'s'}
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
className="sortable"
|
||||||
|
onMouseEnter={termtip.bind(null, 'fuel tank')}
|
||||||
|
onMouseLeave={hide}
|
||||||
|
onClick={sortShips('standard', 6)}
|
||||||
|
>
|
||||||
|
{'ft'}
|
||||||
|
</th>
|
||||||
|
<th className="sortable lft" onClick={sortShips('hp', 1)}>
|
||||||
|
{translate('S')}
|
||||||
|
</th>
|
||||||
|
<th className="sortable" onClick={sortShips('hp', 2)}>
|
||||||
|
{translate('M')}
|
||||||
|
</th>
|
||||||
|
<th className="sortable" onClick={sortShips('hp', 3)}>
|
||||||
|
{translate('L')}
|
||||||
|
</th>
|
||||||
|
<th className="sortable" onClick={sortShips('hp', 4)}>
|
||||||
|
{translate('H')}
|
||||||
|
</th>
|
||||||
|
<th className="sortable" onClick={sortShips('hp', 0)}>
|
||||||
|
{translate('U')}
|
||||||
|
</th>
|
||||||
|
|
||||||
<th className='lft' colSpan={6}>{translate('core module classes')}</th>
|
<th className="sortable lft" onClick={sortShips('int', 0)}>
|
||||||
<th colSpan={5} className='sortable lft' onClick={sortShips('hpCount')}>{translate('hardpoints')}</th>
|
1
|
||||||
<th colSpan={8} className='sortable lft' onClick={sortShips('intCount')}>{translate('internal compartments')}</th>
|
</th>
|
||||||
</tr>
|
<th className="sortable" onClick={sortShips('int', 1)}>
|
||||||
<tr>
|
2
|
||||||
<th className='sortable lft' onClick={sortShips('retailCost')}>{units.CR}</th>
|
</th>
|
||||||
<th className='sortable lft' onClick={sortShips('hullMass')}>{units.T}</th>
|
<th className="sortable" onClick={sortShips('int', 2)}>
|
||||||
<th className='sortable lft' onClick={sortShips('speed')}>{units['m/s']}</th>
|
3
|
||||||
<th className='sortable' onClick={sortShips('boost')}>{units['m/s']}</th>
|
</th>
|
||||||
<th> </th>
|
<th className="sortable" onClick={sortShips('int', 3)}>
|
||||||
<th className='sortable' onClick={sortShips('baseShieldStrength')}>{units.MJ}</th>
|
4
|
||||||
<th className='sortable lft' onClick={sortShips('topSpeed')}>{units['m/s']}</th>
|
</th>
|
||||||
<th className='sortable' onClick={sortShips('topBoost')}>{units['m/s']}</th>
|
<th className="sortable" onClick={sortShips('int', 4)}>
|
||||||
<th className='sortable' onClick={sortShips('maxJumpRange')}>{units.LY}</th>
|
5
|
||||||
<th className='sortable' onClick={sortShips('maxCargo')}>{units.T}</th>
|
</th>
|
||||||
<th className='sortable lft' onMouseEnter={termtip.bind(null, 'power plant')} onMouseLeave={hide} onClick={sortShips('standard', 0)}>{'pp'}</th>
|
<th className="sortable" onClick={sortShips('int', 5)}>
|
||||||
<th className='sortable' onMouseEnter={termtip.bind(null, 'thrusters')} onMouseLeave={hide} onClick={sortShips('standard', 1)}>{'th'}</th>
|
6
|
||||||
<th className='sortable' onMouseEnter={termtip.bind(null, 'frame shift drive')} onMouseLeave={hide} onClick={sortShips('standard', 2)}>{'fsd'}</th>
|
</th>
|
||||||
<th className='sortable' onMouseEnter={termtip.bind(null, 'life support')} onMouseLeave={hide} onClick={sortShips('standard', 3)}>{'ls'}</th>
|
<th className="sortable" onClick={sortShips('int', 6)}>
|
||||||
<th className='sortable' onMouseEnter={termtip.bind(null, 'power distriubtor')} onMouseLeave={hide} onClick={sortShips('standard', 4)}>{'pd'}</th>
|
7
|
||||||
<th className='sortable' onMouseEnter={termtip.bind(null, 'sensors')} onMouseLeave={hide} onClick={sortShips('standard', 5)}>{'s'}</th>
|
</th>
|
||||||
|
<th className="sortable" onClick={sortShips('int', 7)}>
|
||||||
<th className='sortable lft' onClick={sortShips('hp',1)}>{translate('S')}</th>
|
8
|
||||||
<th className='sortable' onClick={sortShips('hp', 2)}>{translate('M')}</th>
|
</th>
|
||||||
<th className='sortable' onClick={sortShips('hp', 3)}>{translate('L')}</th>
|
</tr>
|
||||||
<th className='sortable' onClick={sortShips('hp', 4)}>{translate('H')}</th>
|
</thead>
|
||||||
<th className='sortable' onClick={sortShips('hp', 0)}>{translate('U')}</th>
|
<tbody onMouseLeave={this._highlightShip.bind(this, null)}>
|
||||||
|
{detailRows}
|
||||||
<th className='sortable lft' onClick={sortShips('int', 0)} >1</th>
|
</tbody>
|
||||||
<th className='sortable' onClick={sortShips('int', 1)} >2</th>
|
</table>
|
||||||
<th className='sortable' onClick={sortShips('int', 2)} >3</th>
|
|
||||||
<th className='sortable' onClick={sortShips('int', 3)} >4</th>
|
|
||||||
<th className='sortable' onClick={sortShips('int', 4)} >5</th>
|
|
||||||
<th className='sortable' onClick={sortShips('int', 5)} >6</th>
|
|
||||||
<th className='sortable' onClick={sortShips('int', 6)} >7</th>
|
|
||||||
<th className='sortable' onClick={sortShips('int', 7)} >8</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody onMouseLeave={this._highlightShip.bind(this, null)}>
|
|
||||||
{detailRows}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="table-tools" >
|
||||||
|
<label><input type="checkbox" checked={this.state.groupCompared} onClick={() => this._toggleGroupCompared()}/>Group highlighted ships</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,11 +7,20 @@ import Module from './Module';
|
|||||||
* @param {object} fsd The FDS object/component with maxfuel, fuelmul, fuelpower, optmass
|
* @param {object} fsd The FDS object/component with maxfuel, fuelmul, fuelpower, optmass
|
||||||
* @param {number} fuel Optional - The fuel consumed during the jump
|
* @param {number} fuel Optional - The fuel consumed during the jump
|
||||||
* @return {number} Distance in Light Years
|
* @return {number} Distance in Light Years
|
||||||
|
* @param {object} ship Ship instance
|
||||||
*/
|
*/
|
||||||
export function jumpRange(mass, fsd, fuel) {
|
export function jumpRange(mass, fsd, fuel, ship) {
|
||||||
const fsdMaxFuelPerJump = fsd instanceof Module ? fsd.getMaxFuelPerJump() : fsd.maxfuel;
|
const fsdMaxFuelPerJump = fsd instanceof Module ? fsd.getMaxFuelPerJump() : fsd.maxfuel;
|
||||||
const fsdOptimalMass = fsd instanceof Module ? fsd.getOptMass() : fsd.optmass;
|
const fsdOptimalMass = fsd instanceof Module ? fsd.getOptMass() : fsd.optmass;
|
||||||
return Math.pow(Math.min(fuel === undefined ? fsdMaxFuelPerJump : fuel, fsdMaxFuelPerJump) / fsd.fuelmul, 1 / fsd.fuelpower) * fsdOptimalMass / mass;
|
let jumpAddition = 0;
|
||||||
|
if (ship) {
|
||||||
|
for (const module of ship.internal) {
|
||||||
|
if (module && module.m && module.m.grp === 'gfsb' && ship.getSlotStatus(module) == 3) {
|
||||||
|
jumpAddition += module.m.getJumpBoost();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (Math.pow(Math.min(fuel === undefined ? fsdMaxFuelPerJump : fuel, fsdMaxFuelPerJump) / fsd.fuelmul, 1 / fsd.fuelpower) * fsdOptimalMass / mass) + jumpAddition;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -21,8 +30,9 @@ export function jumpRange(mass, fsd, fuel) {
|
|||||||
* @param {object} fsd The FDS object/component with maxfuel, fuelmul, fuelpower, optmass
|
* @param {object} fsd The FDS object/component with maxfuel, fuelmul, fuelpower, optmass
|
||||||
* @param {number} fuel The total fuel available
|
* @param {number} fuel The total fuel available
|
||||||
* @return {number} Distance in Light Years
|
* @return {number} Distance in Light Years
|
||||||
|
* @param {object} ship Ship instance
|
||||||
*/
|
*/
|
||||||
export function totalJumpRange(mass, fsd, fuel) {
|
export function totalJumpRange(mass, fsd, fuel, ship) {
|
||||||
const fsdMaxFuelPerJump = fsd instanceof Module ? fsd.getMaxFuelPerJump() : fsd.maxfuel;
|
const fsdMaxFuelPerJump = fsd instanceof Module ? fsd.getMaxFuelPerJump() : fsd.maxfuel;
|
||||||
const fsdOptimalMass = fsd instanceof Module ? fsd.getOptMass() : fsd.optmass;
|
const fsdOptimalMass = fsd instanceof Module ? fsd.getOptMass() : fsd.optmass;
|
||||||
|
|
||||||
@@ -30,7 +40,7 @@ export function totalJumpRange(mass, fsd, fuel) {
|
|||||||
let totalRange = 0;
|
let totalRange = 0;
|
||||||
while (fuelRemaining > 0) {
|
while (fuelRemaining > 0) {
|
||||||
const fuelForThisJump = Math.min(fuelRemaining, fsdMaxFuelPerJump);
|
const fuelForThisJump = Math.min(fuelRemaining, fsdMaxFuelPerJump);
|
||||||
totalRange += this.jumpRange(mass, fsd, fuelForThisJump);
|
totalRange += this.jumpRange(mass, fsd, fuelForThisJump, ship);
|
||||||
// Mass is reduced
|
// Mass is reduced
|
||||||
mass -= fuelForThisJump;
|
mass -= fuelForThisJump;
|
||||||
fuelRemaining -= fuelForThisJump;
|
fuelRemaining -= fuelForThisJump;
|
||||||
@@ -45,7 +55,7 @@ export function totalJumpRange(mass, fsd, fuel) {
|
|||||||
* @param {number} baseShield Base Shield strength MJ for ship
|
* @param {number} baseShield Base Shield strength MJ for ship
|
||||||
* @param {object} sg The shield generator used
|
* @param {object} sg The shield generator used
|
||||||
* @param {number} multiplier Shield multiplier for ship (1 + shield boosters if any)
|
* @param {number} multiplier Shield multiplier for ship (1 + shield boosters if any)
|
||||||
* @return {number} Approximate shield strengh in MJ
|
* @return {number} Approximate shield strengh in MJ
|
||||||
*/
|
*/
|
||||||
export function shieldStrength(mass, baseShield, sg, multiplier) {
|
export function shieldStrength(mass, baseShield, sg, multiplier) {
|
||||||
// sg might be a module or a template; handle either here
|
// sg might be a module or a template; handle either here
|
||||||
@@ -55,13 +65,12 @@ export function shieldStrength(mass, baseShield, sg, multiplier) {
|
|||||||
let minMul = sg instanceof Module ? sg.getMinMul() : sg.minmul;
|
let minMul = sg instanceof Module ? sg.getMinMul() : sg.minmul;
|
||||||
let optMul = sg instanceof Module ? sg.getOptMul() : sg.optmul;
|
let optMul = sg instanceof Module ? sg.getOptMul() : sg.optmul;
|
||||||
let maxMul = sg instanceof Module ? sg.getMaxMul() : sg.maxmul;
|
let maxMul = sg instanceof Module ? sg.getMaxMul() : sg.maxmul;
|
||||||
|
|
||||||
let xnorm = Math.min(1, (maxMass - mass) / (maxMass - minMass));
|
let xnorm = Math.min(1, (maxMass - mass) / (maxMass - minMass));
|
||||||
let exponent = Math.log((optMul - minMul) / (maxMul - minMul)) / Math.log(Math.min(1, (maxMass - optMass) / (maxMass - minMass)));
|
let exponent = Math.log((optMul - minMul) / (maxMul - minMul)) / Math.log(Math.min(1, (maxMass - optMass) / (maxMass - minMass)));
|
||||||
let ynorm = Math.pow(xnorm, exponent);
|
let ynorm = Math.pow(xnorm, exponent);
|
||||||
let mul = minMul + ynorm * (maxMul - minMul);
|
let mul = minMul + ynorm * (maxMul - minMul);
|
||||||
|
|
||||||
return baseShield * mul * multiplier;
|
return (baseShield * mul * multiplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -87,6 +96,16 @@ export function speed(mass, baseSpeed, thrusters, engpip) {
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate pip multiplier for speed.
|
||||||
|
* @param {number} baseSpeed The base speed of ship in data
|
||||||
|
* @param {number} topSpeed The top speed of ship in data
|
||||||
|
* @return {number} The multiplier that pips affect speed.
|
||||||
|
*/
|
||||||
|
export function calcPipSpeed(baseSpeed, topSpeed) {
|
||||||
|
return (topSpeed - baseSpeed) / (4 * topSpeed);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate pitch of a ship based on mass and thrusters
|
* Calculate pitch of a ship based on mass and thrusters
|
||||||
* @param {number} mass the mass of the ship
|
* @param {number} mass the mass of the ship
|
||||||
@@ -202,7 +221,7 @@ function calcValue(minMass, optMass, maxMass, minMul, optMul, maxMul, mass, base
|
|||||||
* Calculate speed for a given setup
|
* Calculate speed for a given setup
|
||||||
* @param {number} mass the mass of the ship
|
* @param {number} mass the mass of the ship
|
||||||
* @param {number} baseSpeed the base speed of the ship
|
* @param {number} baseSpeed the base speed of the ship
|
||||||
* @param {ojbect} thrusters the thrusters of the ship
|
* @param {object} thrusters the thrusters of the ship
|
||||||
* @param {number} engpip the multiplier per pip to engines
|
* @param {number} engpip the multiplier per pip to engines
|
||||||
* @param {number} eng the pips to engines
|
* @param {number} eng the pips to engines
|
||||||
* @param {number} boostFactor the boost factor for ths ship
|
* @param {number} boostFactor the boost factor for ths ship
|
||||||
@@ -230,7 +249,7 @@ export function calcSpeed(mass, baseSpeed, thrusters, engpip, eng, boostFactor,
|
|||||||
* Calculate pitch for a given setup
|
* Calculate pitch for a given setup
|
||||||
* @param {number} mass the mass of the ship
|
* @param {number} mass the mass of the ship
|
||||||
* @param {number} basePitch the base pitch of the ship
|
* @param {number} basePitch the base pitch of the ship
|
||||||
* @param {ojbect} thrusters the thrusters of the ship
|
* @param {object} thrusters the thrusters of the ship
|
||||||
* @param {number} engpip the multiplier per pip to engines
|
* @param {number} engpip the multiplier per pip to engines
|
||||||
* @param {number} eng the pips to engines
|
* @param {number} eng the pips to engines
|
||||||
* @param {number} boostFactor the boost factor for ths ship
|
* @param {number} boostFactor the boost factor for ths ship
|
||||||
@@ -321,41 +340,76 @@ export function shieldMetrics(ship, sys) {
|
|||||||
const maxSysResistance = this.sysResistance(4);
|
const maxSysResistance = this.sysResistance(4);
|
||||||
|
|
||||||
let shield = {};
|
let shield = {};
|
||||||
|
const dimReturnLine = (res) => 1 - (1 - res) * 0.7;
|
||||||
|
|
||||||
const shieldGeneratorSlot = ship.findInternalByGroup('sg');
|
const shieldGeneratorSlot = ship.findInternalByGroup('sg');
|
||||||
if (shieldGeneratorSlot && shieldGeneratorSlot.enabled && shieldGeneratorSlot.m) {
|
if (shieldGeneratorSlot && shieldGeneratorSlot.enabled && shieldGeneratorSlot.m) {
|
||||||
const shieldGenerator = shieldGeneratorSlot.m;
|
const shieldGenerator = shieldGeneratorSlot.m;
|
||||||
|
let res = {
|
||||||
|
kin: shieldGenerator.kinres,
|
||||||
|
therm: shieldGenerator.thermres,
|
||||||
|
expl: shieldGenerator.explres
|
||||||
|
};
|
||||||
// Boosters
|
// Boosters
|
||||||
let boost = 1;
|
let boost = 1;
|
||||||
let boosterExplDmg = 1;
|
let boosterExplDmg = 1;
|
||||||
let boosterKinDmg = 1;
|
let boosterKinDmg = 1;
|
||||||
let boosterThermDmg = 1;
|
let boosterThermDmg = 1;
|
||||||
|
// const explDim = dimReturnLine(shieldGenerator.explres);
|
||||||
|
// const thermDim = dimReturnLine(shieldGenerator.thermres);
|
||||||
|
// const kinDim = dimReturnLine(shieldGenerator.kinres);
|
||||||
for (let slot of ship.hardpoints) {
|
for (let slot of ship.hardpoints) {
|
||||||
if (slot.enabled && slot.m && slot.m.grp == 'sb') {
|
if (slot.enabled && slot.m && slot.m.grp == 'sb') {
|
||||||
boost += slot.m.getShieldBoost();
|
boost += slot.m.getShieldBoost();
|
||||||
|
res.expl += slot.m.getExplosiveResistance();
|
||||||
|
res.kin += slot.m.getKineticResistance();
|
||||||
|
res.therm += slot.m.getThermalResistance();
|
||||||
boosterExplDmg = boosterExplDmg * (1 - slot.m.getExplosiveResistance());
|
boosterExplDmg = boosterExplDmg * (1 - slot.m.getExplosiveResistance());
|
||||||
boosterKinDmg = boosterKinDmg * (1 - slot.m.getKineticResistance());
|
boosterKinDmg = boosterKinDmg * (1 - slot.m.getKineticResistance());
|
||||||
boosterThermDmg = boosterThermDmg * (1 - slot.m.getThermalResistance());
|
boosterThermDmg = boosterThermDmg * (1 - slot.m.getThermalResistance());
|
||||||
}
|
}
|
||||||
}
|
if (slot.m && slot.m.grp == 'gsrp') {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
// Calculate diminishing returns for boosters
|
// Calculate diminishing returns for boosters
|
||||||
// Diminishing returns not currently in-game
|
// Diminishing returns not currently in-game
|
||||||
// boost = Math.min(boost, (1 - Math.pow(Math.E, -0.7 * boost)) * 2.5);
|
// boost = Math.min(boost, (1 - Math.pow(Math.E, -0.7 * boost)) * 2.5);
|
||||||
|
|
||||||
|
|
||||||
// Remove base shield generator strength
|
// Remove base shield generator strength
|
||||||
boost -= 1;
|
boost -= 1;
|
||||||
// Apply diminishing returns
|
|
||||||
boosterExplDmg = boosterExplDmg > 0.7 ? boosterExplDmg : 0.7 - (0.7 - boosterExplDmg) / 2;
|
|
||||||
boosterKinDmg = boosterKinDmg > 0.7 ? boosterKinDmg : 0.7 - (0.7 - boosterKinDmg) / 2;
|
|
||||||
boosterThermDmg = boosterThermDmg > 0.7 ? boosterThermDmg : 0.7 - (0.7 - boosterThermDmg) / 2;
|
|
||||||
|
|
||||||
const generatorStrength = this.shieldStrength(ship.hullMass, ship.baseShieldStrength, shieldGenerator, 1);
|
// if (res.expl > explDim) {
|
||||||
|
// const overage = (res.expl - explDim) * 0.5;
|
||||||
|
// res.expl = explDim + overage;
|
||||||
|
// boosterExplDmg = explDim + overage;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (res.therm > thermDim) {
|
||||||
|
// const overage = (res.therm - thermDim) * 0.5;
|
||||||
|
// res.therm = thermDim + overage;
|
||||||
|
// boosterThermDmg = thermDim + overage;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (res.kin > kinDim) {
|
||||||
|
// const overage = (res.kin - kinDim) * 0.5;
|
||||||
|
// res.kin = kinDim + overage;
|
||||||
|
// boosterKinDmg = kinDim + overage;
|
||||||
|
// }
|
||||||
|
let shieldAddition = 0;
|
||||||
|
if (ship) {
|
||||||
|
for (const module of ship.internal) {
|
||||||
|
if (module && module.m && module.m.grp === 'gsrp' && module.enabled) {
|
||||||
|
shieldAddition += module.m.getShieldAddition();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let generatorStrength = this.shieldStrength(ship.hullMass, ship.baseShieldStrength, shieldGenerator, 1);
|
||||||
const boostersStrength = generatorStrength * boost;
|
const boostersStrength = generatorStrength * boost;
|
||||||
|
|
||||||
// Recover time is the time taken to go from 0 to 50%. It includes a 16-second wait before shields start to recover
|
// Recover time is the time taken to go from 0 to 50%. It includes a 16-second wait before shields start to recover
|
||||||
const shieldToRecover = (generatorStrength + boostersStrength) / 2;
|
const shieldToRecover = (generatorStrength + boostersStrength + shieldAddition) / 2;
|
||||||
const powerDistributor = ship.standard[4].m;
|
const powerDistributor = ship.standard[4].m;
|
||||||
const sysRechargeRate = this.sysRechargeRate(powerDistributor, sys);
|
const sysRechargeRate = this.sysRechargeRate(powerDistributor, sys);
|
||||||
|
|
||||||
@@ -374,7 +428,7 @@ export function shieldMetrics(ship, sys) {
|
|||||||
const remainingShieldToRecover = shieldToRecover - capacitorLifetime * shieldGenerator.getBrokenRegenerationRate();
|
const remainingShieldToRecover = shieldToRecover - capacitorLifetime * shieldGenerator.getBrokenRegenerationRate();
|
||||||
if (sys === 0) {
|
if (sys === 0) {
|
||||||
// No system pips so will never recover shields
|
// No system pips so will never recover shields
|
||||||
recover = Math.Inf;
|
recover = Math.Infinity;
|
||||||
} else {
|
} else {
|
||||||
// Recover remaining shields at the rate of the power distributor's recharge
|
// Recover remaining shields at the rate of the power distributor's recharge
|
||||||
recover += remainingShieldToRecover / (sysRechargeRate / 0.6);
|
recover += remainingShieldToRecover / (sysRechargeRate / 0.6);
|
||||||
@@ -382,7 +436,7 @@ export function shieldMetrics(ship, sys) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Recharge time is the time taken to go from 50% to 100%
|
// Recharge time is the time taken to go from 50% to 100%
|
||||||
const shieldToRecharge = (generatorStrength + boostersStrength) / 2;
|
const shieldToRecharge = (generatorStrength + boostersStrength + shieldAddition) / 2;
|
||||||
|
|
||||||
// Our initial regeneration comes from the SYS capacitor store, which is replenished as it goes
|
// Our initial regeneration comes from the SYS capacitor store, which is replenished as it goes
|
||||||
// 0.6 is a magic number from FD: each 0.6 MW of energy from the power distributor recharges 1 MJ/s of regeneration
|
// 0.6 is a magic number from FD: each 0.6 MW of energy from the power distributor recharges 1 MJ/s of regeneration
|
||||||
@@ -409,8 +463,10 @@ export function shieldMetrics(ship, sys) {
|
|||||||
shield = {
|
shield = {
|
||||||
generator: generatorStrength,
|
generator: generatorStrength,
|
||||||
boosters: boostersStrength,
|
boosters: boostersStrength,
|
||||||
|
addition: shieldAddition,
|
||||||
cells: ship.shieldCells,
|
cells: ship.shieldCells,
|
||||||
total: generatorStrength + boostersStrength + ship.shieldCells,
|
summary: generatorStrength + boostersStrength + shieldAddition,
|
||||||
|
total: generatorStrength + boostersStrength + ship.shieldCells + shieldAddition,
|
||||||
recover,
|
recover,
|
||||||
recharge,
|
recharge,
|
||||||
};
|
};
|
||||||
@@ -425,34 +481,74 @@ export function shieldMetrics(ship, sys) {
|
|||||||
max: 1 - maxSysResistance
|
max: 1 - maxSysResistance
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object that stores a selection of difference damage multipliers that
|
||||||
|
* deal with a ship's shield strength.
|
||||||
|
* @typedef {Object} ShieldDamageMults
|
||||||
|
* @property {number} generator Base damage multiplier of the shield
|
||||||
|
* contributing it's base resistance.
|
||||||
|
* @property {number} boosters Damage multiplier contributed by all
|
||||||
|
* boosters, i.e. `rawMj / (generator * boosters)` equals shield strength
|
||||||
|
* with 0 pips to sys.
|
||||||
|
* @property {number} sys Damage multiplier contributed by pips to sys.
|
||||||
|
* @property {number} base Damage multiplier with 0 pips to sys; just
|
||||||
|
* boosters and shield generator. Equals `generator * boosters`.
|
||||||
|
* @property {number} total Damage multiplier with current pip settings.
|
||||||
|
* @property {number} max Damage multiplier with 4 pips to sys.
|
||||||
|
*/
|
||||||
|
|
||||||
|
let sgExplosiveDmg = 1 - shieldGenerator.getExplosiveResistance();
|
||||||
|
let sgSbExplosiveDmg = diminishDamageMult(sgExplosiveDmg * 0.7, (1 - shieldGenerator.getExplosiveResistance()) * boosterExplDmg);
|
||||||
|
/** @type {ShieldDamageMults} */
|
||||||
shield.explosive = {
|
shield.explosive = {
|
||||||
generator: 1 - shieldGenerator.getExplosiveResistance(),
|
generator: sgExplosiveDmg,
|
||||||
boosters: boosterExplDmg,
|
boosters: sgSbExplosiveDmg / sgExplosiveDmg,
|
||||||
sys: (1 - sysResistance),
|
sys: (1 - sysResistance),
|
||||||
total: (1 - shieldGenerator.getExplosiveResistance()) * boosterExplDmg * (1 - sysResistance),
|
base: sgSbExplosiveDmg,
|
||||||
max: (1 - shieldGenerator.getExplosiveResistance()) * boosterExplDmg * (1 - maxSysResistance)
|
total: sgSbExplosiveDmg * (1 - sysResistance),
|
||||||
|
max: sgSbExplosiveDmg * (1 - maxSysResistance),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let sgKineticDmg = 1 - shieldGenerator.getKineticResistance();
|
||||||
|
let sgSbKineticDmg = diminishDamageMult(sgKineticDmg * 0.7, (1 - shieldGenerator.getKineticResistance()) * boosterKinDmg);
|
||||||
|
/** @type {ShieldDamageMults} */
|
||||||
shield.kinetic = {
|
shield.kinetic = {
|
||||||
generator: 1 - shieldGenerator.getKineticResistance(),
|
generator: sgKineticDmg,
|
||||||
boosters: boosterKinDmg,
|
boosters: sgSbKineticDmg / sgKineticDmg,
|
||||||
sys: (1 - sysResistance),
|
sys: (1 - sysResistance),
|
||||||
total: (1 - shieldGenerator.getKineticResistance()) * boosterKinDmg * (1 - sysResistance),
|
base: sgSbKineticDmg,
|
||||||
max: (1 - shieldGenerator.getKineticResistance()) * boosterKinDmg * (1 - maxSysResistance)
|
total: sgSbKineticDmg * (1 - sysResistance),
|
||||||
|
max: sgSbKineticDmg * (1 - maxSysResistance),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let sgThermalDmg = 1 - shieldGenerator.getThermalResistance();
|
||||||
|
let sgSbThermalDmg = diminishDamageMult(sgThermalDmg * 0.7, (1 - shieldGenerator.getThermalResistance()) * boosterThermDmg);
|
||||||
|
/** @type {ShieldDamageMults} */
|
||||||
shield.thermal = {
|
shield.thermal = {
|
||||||
generator: 1 - shieldGenerator.getThermalResistance(),
|
generator: sgThermalDmg,
|
||||||
boosters: boosterThermDmg,
|
boosters: sgSbThermalDmg / sgThermalDmg,
|
||||||
sys: (1 - sysResistance),
|
sys: (1 - sysResistance),
|
||||||
total: (1 - shieldGenerator.getThermalResistance()) * boosterThermDmg * (1 - sysResistance),
|
base: sgSbThermalDmg,
|
||||||
max: (1 - shieldGenerator.getThermalResistance()) * boosterThermDmg * (1 - maxSysResistance)
|
total: sgSbThermalDmg * (1 - sysResistance),
|
||||||
|
max: sgSbThermalDmg * (1 - maxSysResistance),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return shield;
|
return shield;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate time from one boost to another
|
||||||
|
* @return {number} Boost frequency in seconds
|
||||||
|
* @param {Ship} ship Ship object
|
||||||
|
*/
|
||||||
|
export function calcBoost(ship) {
|
||||||
|
if (!ship.boostEnergy || !ship.standard[4] || !ship.standard[4].m) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return ship.boostEnergy / ship.standard[4].m.getEnginesRechargeRate();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate armour metrics
|
* Calculate armour metrics
|
||||||
* @param {Object} ship The ship
|
* @param {Object} ship The ship
|
||||||
@@ -465,33 +561,58 @@ export function armourMetrics(ship) {
|
|||||||
|
|
||||||
let moduleArmour = 0;
|
let moduleArmour = 0;
|
||||||
let moduleProtection = 1;
|
let moduleProtection = 1;
|
||||||
|
const bulkheads = ship.bulkheads.m;
|
||||||
let hullExplDmg = 1;
|
let hullExplDmg = 1;
|
||||||
let hullKinDmg = 1;
|
let hullKinDmg = 1;
|
||||||
let hullThermDmg = 1;
|
let hullThermDmg = 1;
|
||||||
|
let hullCausDmg = 1;
|
||||||
|
// const dimReturnLine = (res) => 1 - (1 - res) * 0.7;
|
||||||
|
// let res = {
|
||||||
|
// kin: 0,
|
||||||
|
// therm: 0,
|
||||||
|
// expl: 0
|
||||||
|
// };
|
||||||
// Armour from HRPs and module armour from MRPs
|
// Armour from HRPs and module armour from MRPs
|
||||||
for (let slot of ship.internal) {
|
for (let slot of ship.internal) {
|
||||||
if (slot.m && slot.m.grp == 'hr') {
|
if (slot.m && slot.enabled && (slot.m.grp === 'hr' || slot.m.grp === 'ghrp' || slot.m.grp == 'mahr')) {
|
||||||
armourReinforcement += slot.m.getHullReinforcement();
|
armourReinforcement += slot.m.getHullReinforcement();
|
||||||
// Hull boost for HRPs is applied against the ship's base armour
|
// Hull boost for HRPs is applied against the ship's base armour
|
||||||
armourReinforcement += ship.baseArmour * slot.m.getModValue('hullboost') / 10000;
|
armourReinforcement += ship.baseArmour * slot.m.getModValue('hullboost') / 10000;
|
||||||
|
// res.expl += slot.m.getExplosiveResistance();
|
||||||
|
// res.kin += slot.m.getKineticResistance();
|
||||||
|
// res.therm += slot.m.getThermalResistance();
|
||||||
hullExplDmg = hullExplDmg * (1 - slot.m.getExplosiveResistance());
|
hullExplDmg = hullExplDmg * (1 - slot.m.getExplosiveResistance());
|
||||||
hullKinDmg = hullKinDmg * (1 - slot.m.getKineticResistance());
|
hullKinDmg = hullKinDmg * (1 - slot.m.getKineticResistance());
|
||||||
hullThermDmg = hullThermDmg * (1 - slot.m.getThermalResistance());
|
hullThermDmg = hullThermDmg * (1 - slot.m.getThermalResistance());
|
||||||
|
hullCausDmg = hullCausDmg * (1 - slot.m.getCausticResistance());
|
||||||
}
|
}
|
||||||
if (slot.m && slot.m.grp == 'mrp') {
|
if (slot.m && slot.enabled && (slot.m.grp == 'mrp' || slot.m.grp == 'gmrp')) {
|
||||||
moduleArmour += slot.m.getIntegrity();
|
moduleArmour += slot.m.getIntegrity();
|
||||||
moduleProtection = moduleProtection * (1 - slot.m.getProtection());
|
moduleProtection = moduleProtection * (1 - slot.m.getProtection());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
moduleProtection = 1 - moduleProtection;
|
moduleProtection = 1 - moduleProtection;
|
||||||
|
|
||||||
// Apply diminishing returns
|
// const explDim = dimReturnLine(bulkheads.explres);
|
||||||
hullExplDmg = hullExplDmg > 0.7 ? hullExplDmg : 0.7 - (0.7 - hullExplDmg) / 2;
|
// const thermDim = dimReturnLine(bulkheads.thermres);
|
||||||
hullKinDmg = hullKinDmg > 0.7 ? hullKinDmg : 0.7 - (0.7 - hullKinDmg) / 2;
|
// const kinDim = dimReturnLine(bulkheads.kinres);
|
||||||
hullThermDmg = hullThermDmg > 0.7 ? hullThermDmg : 0.7 - (0.7 - hullThermDmg) / 2;
|
// if (res.expl > explDim) {
|
||||||
|
// const overage = (res.expl - explDim) * 0.5;
|
||||||
|
// res.expl = explDim + overage;
|
||||||
|
// hullExplDmg = explDim + overage;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (res.therm > thermDim) {
|
||||||
|
// const overage = (res.therm - thermDim) * 0.5;
|
||||||
|
// res.therm = thermDim + overage;
|
||||||
|
// hullThermDmg = thermDim + overage;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (res.kin > kinDim) {
|
||||||
|
// const overage = (res.kin - kinDim) * 0.5;
|
||||||
|
// res.kin = kinDim + overage;
|
||||||
|
// hullKinDmg = kinDim + overage;
|
||||||
|
// }
|
||||||
|
|
||||||
const armour = {
|
const armour = {
|
||||||
bulkheads: armourBulkheads,
|
bulkheads: armourBulkheads,
|
||||||
@@ -509,24 +630,41 @@ export function armourMetrics(ship) {
|
|||||||
total: 1
|
total: 1
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let armourExplDmg = diminishDamageMult(0.7, 1 - ship.bulkheads.m.getExplosiveResistance());
|
||||||
|
let armourReinforcedExplDmg = diminishDamageMult(0.7, (1 - ship.bulkheads.m.getExplosiveResistance()) * hullExplDmg);
|
||||||
armour.explosive = {
|
armour.explosive = {
|
||||||
bulkheads: 1 - ship.bulkheads.m.getExplosiveResistance(),
|
bulkheads: armourExplDmg,
|
||||||
reinforcement: hullExplDmg,
|
reinforcement: armourReinforcedExplDmg / armourExplDmg,
|
||||||
total: (1 - ship.bulkheads.m.getExplosiveResistance()) * hullExplDmg
|
total: armourReinforcedExplDmg,
|
||||||
|
res: 1 - armourReinforcedExplDmg
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let armourKinDmg = diminishDamageMult(0.7, 1 - ship.bulkheads.m.getKineticResistance());
|
||||||
|
let armourReinforcedKinDmg = diminishDamageMult(0.7, (1 - ship.bulkheads.m.getKineticResistance()) * hullKinDmg);
|
||||||
armour.kinetic = {
|
armour.kinetic = {
|
||||||
bulkheads: 1 - ship.bulkheads.m.getKineticResistance(),
|
bulkheads: armourKinDmg,
|
||||||
reinforcement: hullKinDmg,
|
reinforcement: armourReinforcedKinDmg / armourKinDmg,
|
||||||
total: (1 - ship.bulkheads.m.getKineticResistance()) * hullKinDmg
|
total: armourReinforcedKinDmg,
|
||||||
|
res: 1 - armourReinforcedKinDmg
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let armourThermDmg = diminishDamageMult(0.7, 1 - ship.bulkheads.m.getThermalResistance());
|
||||||
|
let armourReinforcedThermDmg = diminishDamageMult(0.7, (1 - ship.bulkheads.m.getThermalResistance()) * hullThermDmg);
|
||||||
armour.thermal = {
|
armour.thermal = {
|
||||||
bulkheads: 1 - ship.bulkheads.m.getThermalResistance(),
|
bulkheads: armourThermDmg,
|
||||||
reinforcement: hullThermDmg,
|
reinforcement: armourReinforcedThermDmg / armourThermDmg,
|
||||||
total: (1 - ship.bulkheads.m.getThermalResistance()) * hullThermDmg
|
total: armourReinforcedThermDmg,
|
||||||
|
res: 1 - armourReinforcedThermDmg
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let armourCausDmg = diminishDamageMult(0.7, 1 - ship.bulkheads.m.getCausticResistance());
|
||||||
|
let armourReinforcedCausDmg = diminishDamageMult(0.7, (1 - ship.bulkheads.m.getCausticResistance()) * hullCausDmg);
|
||||||
|
armour.caustic = {
|
||||||
|
bulkheads: armourCausDmg,
|
||||||
|
reinforcement: armourReinforcedCausDmg / armourCausDmg,
|
||||||
|
total: armourReinforcedCausDmg,
|
||||||
|
res: 1 - armourReinforcedCausDmg,
|
||||||
|
};
|
||||||
return armour;
|
return armour;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -698,20 +836,55 @@ export function _sustainedDps(ship, opponent, opponentShields, opponentArmour, e
|
|||||||
return { shieldsdps, armoursdps, eps };
|
return { shieldsdps, armoursdps, eps };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores SDPS split up by type.
|
||||||
|
* @typedef {Object} SDps
|
||||||
|
* @property {number} absolute Damage of type absolute
|
||||||
|
* @property {number} explosive Damage of type explosive
|
||||||
|
* @property {number} kinetic Damage of type kinetic
|
||||||
|
* @property {number} thermal Damage of type thermal
|
||||||
|
* @property {number} [total] Sum of all damage types
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object that holds information about SDPS for a given weapon and opponent.
|
||||||
|
* @typedef {Object} WeaponDamage
|
||||||
|
* @property {number} eps Energy per second
|
||||||
|
* @property {Object} damage An object that stores damage inflicted by
|
||||||
|
* the weapon.
|
||||||
|
* @property {Object} effectiveness An object that stores the effectiveness of
|
||||||
|
* the weapon against the opponent given.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores overall SDPS and against a given opponent's shields and armour.
|
||||||
|
* @typedef {Object} WeaponDamage~damage
|
||||||
|
* @property {SDps} base Overall SDPS.
|
||||||
|
* @property {SDps} shields SDPS against the given opponent's shields.
|
||||||
|
* @property {SDps} armour SDPS against the given opponent's armour.
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate the sustained DPS for a weapon at a given range
|
* Calculate the sustained DPS for a weapon at a given range
|
||||||
* @param {Object} m The weapon
|
* @param {Object} m The weapon
|
||||||
* @param {Object} opponent The opponent ship
|
* @param {Object} opponent The opponent ship
|
||||||
* @param {Object} opponentShields The opponent's shield resistances
|
* @param {Object} opponentShields The opponent's shield resistances
|
||||||
* @param {Object} opponentArmour The opponent's armour resistances
|
* @param {Object} opponentArmour The opponent's armour resistances
|
||||||
* @param {int} engagementrange The range between the ship and opponent
|
* @param {int} engagementrange The range between the ship and opponent
|
||||||
* @returns {Object} Sustained DPS for shield and armour
|
* @returns {WeaponDamage} Sustained DPS for shield and armour
|
||||||
*/
|
*/
|
||||||
export function _weaponSustainedDps(m, opponent, opponentShields, opponentArmour, engagementrange) {
|
export function _weaponSustainedDps(m, opponent, opponentShields, opponentArmour, engagementrange) {
|
||||||
const opponentHasShields = opponentShields.generator ? true : false;
|
const opponentHasShields = opponentShields.generator ? true : false;
|
||||||
const weapon = {
|
const weapon = {
|
||||||
eps: 0,
|
eps: 0,
|
||||||
damage: {
|
damage: {
|
||||||
|
base: {
|
||||||
|
absolute: 0,
|
||||||
|
explosive: 0,
|
||||||
|
kinetic: 0,
|
||||||
|
thermal: 0,
|
||||||
|
total: 0,
|
||||||
|
},
|
||||||
shields: {
|
shields: {
|
||||||
absolute: 0,
|
absolute: 0,
|
||||||
explosive: 0,
|
explosive: 0,
|
||||||
@@ -745,7 +918,7 @@ export function _weaponSustainedDps(m, opponent, opponentShields, opponentArmour
|
|||||||
weapon.eps = m.getClip() ? (m.getClip() * m.getEps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload()) : m.getEps();
|
weapon.eps = m.getClip() ? (m.getClip() * m.getEps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload()) : m.getEps();
|
||||||
|
|
||||||
// Initial sustained DPS
|
// Initial sustained DPS
|
||||||
let sDps = m.getClip() ? (m.getClip() * m.getDps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload()) : m.getDps();
|
let sDps = m.getSDps();
|
||||||
|
|
||||||
// Take fall-off in to account
|
// Take fall-off in to account
|
||||||
const falloff = m.getFalloff();
|
const falloff = m.getFalloff();
|
||||||
@@ -756,6 +929,12 @@ export function _weaponSustainedDps(m, opponent, opponentShields, opponentArmour
|
|||||||
sDps *= dropoff;
|
sDps *= dropoff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
weapon.damage.base.absolute = sDps * m.getDamageDist().A;
|
||||||
|
weapon.damage.base.explosive = sDps * m.getDamageDist().E;
|
||||||
|
weapon.damage.base.kinetic = sDps * m.getDamageDist().K;
|
||||||
|
weapon.damage.base.thermal = sDps * m.getDamageDist().T;
|
||||||
|
weapon.damage.base.total = sDps;
|
||||||
|
|
||||||
// Piercing/hardness modifier (for armour only)
|
// Piercing/hardness modifier (for armour only)
|
||||||
const armourMultiple = m.getPiercing() >= opponent.hardness ? 1 : m.getPiercing() / opponent.hardness;
|
const armourMultiple = m.getPiercing() >= opponent.hardness ? 1 : m.getPiercing() / opponent.hardness;
|
||||||
weapon.effectiveness.armour.hardness = armourMultiple;
|
weapon.effectiveness.armour.hardness = armourMultiple;
|
||||||
@@ -836,7 +1015,10 @@ export function timeToDrainWep(ship, wep) {
|
|||||||
*/
|
*/
|
||||||
export function timeToDeplete(amount, dps, eps, capacity, recharge) {
|
export function timeToDeplete(amount, dps, eps, capacity, recharge) {
|
||||||
const drainPerSecond = eps - recharge;
|
const drainPerSecond = eps - recharge;
|
||||||
if (drainPerSecond <= 0) {
|
// If there is nothing to remove, we're don instantly
|
||||||
|
if (!amount) {
|
||||||
|
return 0;
|
||||||
|
} if (drainPerSecond <= 0) {
|
||||||
// Simple result
|
// Simple result
|
||||||
return amount / dps;
|
return amount / dps;
|
||||||
} else {
|
} else {
|
||||||
@@ -853,3 +1035,17 @@ export function timeToDeplete(amount, dps, eps, capacity, recharge) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies diminishing returns to resistances.
|
||||||
|
* @param {number} diminishFrom The base resistance up to which no diminishing returns are applied.
|
||||||
|
* @param {number} damageMult Resistance as damage multiplier
|
||||||
|
* @returns {number} Actual damage multiplier
|
||||||
|
*/
|
||||||
|
export function diminishDamageMult(diminishFrom, damageMult) {
|
||||||
|
if (damageMult > diminishFrom) {
|
||||||
|
return damageMult;
|
||||||
|
} else {
|
||||||
|
return (diminishFrom / 2) + 0.5 * damageMult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,12 +9,16 @@ export const StandardArray = [
|
|||||||
'pd', // Power Distributor
|
'pd', // Power Distributor
|
||||||
's', // Sensors
|
's', // Sensors
|
||||||
'ft', // Fuel Tank
|
'ft', // Fuel Tank
|
||||||
|
'gpp', // Guardian Hybrid Power Plant
|
||||||
|
'gpd' // Guardian Hybrid Power Distributor
|
||||||
];
|
];
|
||||||
|
|
||||||
// Map to lookup group labels/names for component grp, used for JSON Serialization
|
// Map to lookup group labels/names for component grp, used for JSON Serialization
|
||||||
export const ModuleGroupToName = {
|
export const ModuleGroupToName = {
|
||||||
// Standard
|
// Standard
|
||||||
pp: 'Power Plant',
|
pp: 'Power Plant',
|
||||||
|
gpp: 'Guardian Hybrid Power Plant',
|
||||||
|
gpd: 'Guardian Power Distributor',
|
||||||
t: 'Thrusters',
|
t: 'Thrusters',
|
||||||
fsd: 'Frame Shift Drive',
|
fsd: 'Frame Shift Drive',
|
||||||
ls: 'Life Support',
|
ls: 'Life Support',
|
||||||
@@ -48,6 +52,12 @@ export const ModuleGroupToName = {
|
|||||||
pcq: 'Luxury Passenger Cabin',
|
pcq: 'Luxury Passenger Cabin',
|
||||||
cc: 'Collector Limpet Controller',
|
cc: 'Collector Limpet Controller',
|
||||||
ss: 'Surface Scanner',
|
ss: 'Surface Scanner',
|
||||||
|
gsrp: 'Guardian Shield Reinforcement Packages',
|
||||||
|
gfsb: 'Guardian Frame Shift Drive Booster',
|
||||||
|
ghrp: 'Guardian Hull Reinforcement Package',
|
||||||
|
gmrp: 'Guardian Module Reinforcement Package',
|
||||||
|
mahr: 'Meta Alloy Hull Reinforcement Package',
|
||||||
|
sua: 'Supercruise Assist',
|
||||||
|
|
||||||
// Hard Points
|
// Hard Points
|
||||||
bl: 'Beam Laser',
|
bl: 'Beam Laser',
|
||||||
@@ -58,19 +68,37 @@ export const ModuleGroupToName = {
|
|||||||
cm: 'Countermeasure',
|
cm: 'Countermeasure',
|
||||||
ec: 'Electronic Countermeasure',
|
ec: 'Electronic Countermeasure',
|
||||||
fc: 'Fragment Cannon',
|
fc: 'Fragment Cannon',
|
||||||
|
rfl: 'Remote Release Flak Launcher',
|
||||||
hs: 'Heat Sink Launcher',
|
hs: 'Heat Sink Launcher',
|
||||||
ws: 'Frame Shift Wake Scanner',
|
ws: 'Frame Shift Wake Scanner',
|
||||||
kw: 'Kill Warrant Scanner',
|
kw: 'Kill Warrant Scanner',
|
||||||
nl: 'Mine Launcher',
|
nl: 'Mine Launcher',
|
||||||
ml: 'Mining Laser',
|
ml: 'Mining Laser',
|
||||||
mr: 'Missile Rack',
|
mr: 'Missile Rack',
|
||||||
|
axmr: 'AX Missile Rack',
|
||||||
pa: 'Plasma Accelerator',
|
pa: 'Plasma Accelerator',
|
||||||
po: 'Point Defence',
|
po: 'Point Defence',
|
||||||
mc: 'Multi-cannon',
|
mc: 'Multi-cannon',
|
||||||
|
axmc: 'AX Multi-cannon',
|
||||||
pl: 'Pulse Laser',
|
pl: 'Pulse Laser',
|
||||||
rg: 'Rail Gun',
|
rg: 'Rail Gun',
|
||||||
sb: 'Shield Booster',
|
sb: 'Shield Booster',
|
||||||
tp: 'Torpedo Pylon'
|
tp: 'Torpedo Pylon',
|
||||||
|
sfn: 'Shutdown Field Neutraliser',
|
||||||
|
xs: 'Xeno Scanner',
|
||||||
|
rcpl: 'Recon Limpet Controller',
|
||||||
|
rsl: 'Research Limpet Controller',
|
||||||
|
dtl: 'Decontamination Limpet Controller',
|
||||||
|
gpc: 'Guardian Plasma Charger',
|
||||||
|
ggc: 'Guardian Gauss Cannon',
|
||||||
|
tbsc: 'Shock Cannon',
|
||||||
|
gsc: 'Guardian Shard Cannon',
|
||||||
|
tbem: 'Enzyme Missile Rack',
|
||||||
|
tbrfl: 'Remote Release Flechette Launcher',
|
||||||
|
pwa: 'Pulse Wave Analyser',
|
||||||
|
abl: 'Abrasion Blaster',
|
||||||
|
scl: 'Seismic Charge Launcher',
|
||||||
|
sdm: 'Sub-Surface Displacement Missile',
|
||||||
};
|
};
|
||||||
|
|
||||||
let GrpNameToCodeMap = {};
|
let GrpNameToCodeMap = {};
|
||||||
@@ -179,7 +207,7 @@ export const ShipFacets = [
|
|||||||
i: 9
|
i: 9
|
||||||
},
|
},
|
||||||
{ // 10
|
{ // 10
|
||||||
title: 'fastest range',
|
title: 'farthest range',
|
||||||
props: ['unladenFastestRange', 'ladenFastestRange'],
|
props: ['unladenFastestRange', 'ladenFastestRange'],
|
||||||
lbls: ['unladen', 'laden'],
|
lbls: ['unladen', 'laden'],
|
||||||
unit: 'LY',
|
unit: 'LY',
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
* Modification - a modification and its value
|
* Modification - a modification and its value
|
||||||
*/
|
*/
|
||||||
export default class Modification {
|
export default class Modification {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {String} id Unique modification ID
|
* @param {String} id Unique modification ID
|
||||||
* @param {Number} value Value of the modification
|
* @param {Number} value Value of the modification
|
||||||
@@ -11,5 +10,4 @@ export default class Modification {
|
|||||||
this.id = id;
|
this.id = id;
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -17,7 +17,6 @@ function filter(arr, maxClass, minClass, mass) {
|
|||||||
* The available module set for a specific ship
|
* The available module set for a specific ship
|
||||||
*/
|
*/
|
||||||
export default class ModuleSet {
|
export default class ModuleSet {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiate the module set
|
* Instantiate the module set
|
||||||
* @param {Object} modules All Modules
|
* @param {Object} modules All Modules
|
||||||
|
|||||||
@@ -63,8 +63,10 @@ export function standard(type, id) {
|
|||||||
if (!isNaN(type)) {
|
if (!isNaN(type)) {
|
||||||
type = StandardArray[type];
|
type = StandardArray[type];
|
||||||
}
|
}
|
||||||
|
let s = Modules.standard[type].find(e => e.id === id);
|
||||||
let s = Modules.standard[type].find(e => e.id == id || (e.class == id.charAt(0) && e.rating == id.charAt(1)));
|
if (!s) {
|
||||||
|
s = Modules.standard[type].find(e => (e.class == id.charAt(0) && e.rating == id.charAt(1)));
|
||||||
|
}
|
||||||
if (s) {
|
if (s) {
|
||||||
s = new Module({ template: s });
|
s = new Module({ template: s });
|
||||||
}
|
}
|
||||||
@@ -196,6 +198,29 @@ export function findInternal(groupName, clss, rating, name) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds an internal module based on Class, Rating, Group and/or name.
|
||||||
|
* At least one of Group name or unique module name must be provided.
|
||||||
|
* will start searching at specified class and proceed lower until a
|
||||||
|
* module is found or 0 is hit
|
||||||
|
* Uses findInternal internally
|
||||||
|
*
|
||||||
|
* @param {String} groupName [Optional] Full name or abbreviated name for module group
|
||||||
|
* @param {integer} clss module Class
|
||||||
|
* @param {String} rating module Rating
|
||||||
|
* @param {String} name [Optional] Long/unique name for module -e.g. 'Advanced Discover Scanner'
|
||||||
|
* @return {Object} The module if found, null if not found
|
||||||
|
*/
|
||||||
|
export function findMaxInternal(groupName, clss, rating, name) {
|
||||||
|
let foundModule = null;
|
||||||
|
let currentClss = clss;
|
||||||
|
while (currentClss > 0 && foundModule == null) {
|
||||||
|
foundModule = findInternal(groupName, currentClss, rating, name);
|
||||||
|
currentClss = currentClss - 1;
|
||||||
|
}
|
||||||
|
return foundModule;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
|
|||||||
@@ -85,12 +85,12 @@ export function toDetailedBuild(buildName, ship) {
|
|||||||
code = ship.toString();
|
code = ship.toString();
|
||||||
|
|
||||||
let data = {
|
let data = {
|
||||||
$schema: 'https://coriolis.edcd.io/schemas/ship-loadout/4.json#',
|
$schema: 'https://coriolis.io/schemas/ship-loadout/4.json#',
|
||||||
name: buildName,
|
name: buildName,
|
||||||
ship: ship.name,
|
ship: ship.name,
|
||||||
references: [{
|
references: [{
|
||||||
name: 'Coriolis.io',
|
name: 'Coriolis.io',
|
||||||
url: 'https://coriolis.edcd.io' + outfitURL(ship.id, code, buildName),
|
url: 'https://coriolis.io' + outfitURL(ship.id, code, buildName),
|
||||||
code,
|
code,
|
||||||
shipId: ship.id
|
shipId: ship.id
|
||||||
}],
|
}],
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user