mirror of
https://github.com/EDCD/coriolis.git
synced 2025-12-09 14:45:35 +00:00
Compare commits
466 Commits
2.9.5
...
feature/ap
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
250d79162b | ||
|
|
9fc7e64499 | ||
|
|
6223908020 | ||
|
|
c73dd71953 | ||
|
|
c7062de5e6 | ||
|
|
82132d9141 | ||
|
|
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 |
33
.babelrc
33
.babelrc
@@ -1,3 +1,34 @@
|
|||||||
{
|
{
|
||||||
"presets": ["env", "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
|
||||||
|
|
||||||
@@ -5,7 +5,7 @@ root = true
|
|||||||
|
|
||||||
# change these settings to your own preference
|
# change these settings to your own preference
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 4
|
indent_size = 2
|
||||||
|
|
||||||
# we recommend you to keep these unchanged
|
# we recommend you to keep these unchanged
|
||||||
end_of_line = lf
|
end_of_line = lf
|
||||||
|
|||||||
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
|
||||||
|
|||||||
34
Dockerfile
Normal file
34
Dockerfile
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
### 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 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.
|
||||||
@@ -23,6 +23,7 @@ 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
|
||||||
|
|
||||||
|
|||||||
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';
|
|
||||||
52
docker-compose.yml
Normal file
52
docker-compose.yml
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
version: '2.2'
|
||||||
|
|
||||||
|
services:
|
||||||
|
coriolis_prod:
|
||||||
|
image: edcd/coriolis:master
|
||||||
|
restart: always
|
||||||
|
volumes:
|
||||||
|
- ./nginx.conf:/etc/nginx/nginx.conf
|
||||||
|
networks:
|
||||||
|
- web
|
||||||
|
labels:
|
||||||
|
- "traefik.docker.network=web"
|
||||||
|
- "traefik.frontend.redirect.regex=^https:\/\/coriolis\.edcd\.io\/(.*)"
|
||||||
|
- "traefik.frontend.redirect.replacement=https://coriolis.io/$1"
|
||||||
|
- "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
|
||||||
|
restart: always
|
||||||
|
volumes:
|
||||||
|
- ./nginx.conf:/etc/nginx/nginx.conf
|
||||||
|
networks:
|
||||||
|
- web
|
||||||
|
labels:
|
||||||
|
- "traefik.docker.network=web"
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.frontend.redirect.regex=^https:\/\/beta\.coriolis\.edcd\.io\/(.*)"
|
||||||
|
- "traefik.frontend.redirect.replacement=https://beta.coriolis.io/$1"
|
||||||
|
- "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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
12852
package-lock.json
generated
12852
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
118
package.json
118
package.json
@@ -1,17 +1,16 @@
|
|||||||
{
|
{
|
||||||
"name": "coriolis_shipyard",
|
"name": "coriolis_shipyard",
|
||||||
"version": "2.9.5",
|
"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",
|
||||||
@@ -56,61 +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-env": "*",
|
"@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",
|
||||||
"create-react-class": "^15.6.2",
|
"@babel/plugin-proposal-json-strings": "^7.0.0",
|
||||||
"css-loader": "^0.28.0",
|
"@babel/plugin-proposal-logical-assignment-operators": "^7.0.0",
|
||||||
"cross-env": "^5.1.4",
|
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.0.0",
|
||||||
"d3-selection": "1",
|
"@babel/plugin-proposal-numeric-separator": "^7.0.0",
|
||||||
"eslint": "3.19.0",
|
"@babel/plugin-proposal-optional-chaining": "^7.0.0",
|
||||||
"eslint-plugin-react": "^6.10.3",
|
"@babel/plugin-proposal-pipeline-operator": "^7.0.0",
|
||||||
"expose-loader": "^0.7.3",
|
"@babel/plugin-proposal-throw-expressions": "^7.0.0",
|
||||||
"express": "^4.15.2",
|
"@babel/plugin-syntax-dynamic-import": "^7.0.0",
|
||||||
"extract-text-webpack-plugin": "2.1.0",
|
"@babel/plugin-syntax-import-meta": "^7.0.0",
|
||||||
"file-loader": "^0.11.1",
|
"@babel/preset-env": "^7.0.0",
|
||||||
"html-webpack-plugin": "^2.28.0",
|
"@babel/preset-react": "^7.0.0",
|
||||||
"jest-cli": "^21.2.1",
|
"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": "^1.1.2",
|
"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",
|
||||||
"uglify-js": "^2.4.11",
|
"uglify-js": "^3.4.9",
|
||||||
"url-loader": "^0.5.8",
|
"url-loader": "^1.1.1",
|
||||||
"webpack": "^2.4.1",
|
"webpack": "^4.20.2",
|
||||||
"webpack-dev-server": "^2.4.4",
|
"webpack-bugsnag-plugins": "^1.2.2",
|
||||||
|
"webpack-cli": "^3.1.1",
|
||||||
|
"webpack-dev-server": "^3.1.9",
|
||||||
"webpack-notifier": "^1.6.0",
|
"webpack-notifier": "^1.6.0",
|
||||||
"webpack-bugsnag-plugins": "^1.1.1"
|
"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": "../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",
|
||||||
"pako": "^1.0.6",
|
"pako": "^1.0.6",
|
||||||
"prop-types": "^15.5.8",
|
"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';
|
||||||
@@ -13,7 +15,6 @@ 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 * 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';
|
||||||
@@ -22,12 +23,12 @@ import ShipyardPage from './pages/ShipyardPage';
|
|||||||
import ErrorDetails from './pages/ErrorDetails';
|
import ErrorDetails from './pages/ErrorDetails';
|
||||||
|
|
||||||
const zlib = require('pako');
|
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,
|
||||||
@@ -66,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));
|
||||||
@@ -93,8 +95,8 @@ export default class Coriolis extends React.Component {
|
|||||||
// 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.inflate(new Buffer(r.params.data, 'base64'), { to: 'string' });
|
const data = zlib.inflate(new Buffer(r.params.data, 'base64'), { to: 'string' });
|
||||||
const json = JSON.parse(data);
|
const json = JSON.parse(data);
|
||||||
console.log('Ship import data: ');
|
console.info('Ship import data: ');
|
||||||
console.log(json);
|
console.info(json);
|
||||||
let ship;
|
let ship;
|
||||||
if (json && json.modules) {
|
if (json && json.modules) {
|
||||||
ship = CompanionApiUtils.shipFromJson(json);
|
ship = CompanionApiUtils.shipFromJson(json);
|
||||||
@@ -103,12 +105,22 @@ export default class Coriolis extends React.Component {
|
|||||||
}
|
}
|
||||||
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
|
||||||
@@ -134,9 +146,9 @@ export default class Coriolis extends React.Component {
|
|||||||
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) {
|
||||||
if (errObj instanceof Error) {
|
if (errObj instanceof Error) {
|
||||||
bugsnagClient.notify(errObj) // eslint-disable-line
|
bugsnagClient.notify(errObj); // eslint-disable-line
|
||||||
} else if (errObj instanceof String) {
|
} else if (errObj instanceof String) {
|
||||||
bugsnagClient.notify(msg, errObj) // eslint-disable-line
|
bugsnagClient.notify(msg, errObj); // eslint-disable-line
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
@@ -180,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'
|
||||||
@@ -208,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 });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -286,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
|
||||||
@@ -322,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());
|
||||||
@@ -347,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>;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import Persist from './stores/Persist';
|
import Persist from './stores/Persist';
|
||||||
|
import ReactGA from 'react-ga';
|
||||||
|
|
||||||
|
ReactGA.initialize('UA-55840909-18');
|
||||||
let standalone = undefined;
|
let standalone = undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -257,9 +259,16 @@ 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 match = path.match(/\/outfit\/(.*)(\?code=.*)/);
|
||||||
window.ga('send', 'pageview', path);
|
if (match) {
|
||||||
|
if (match[1]) {
|
||||||
|
ReactGA.ga('set', 'contentGroup1', match[1]);
|
||||||
|
}
|
||||||
|
if (match[2]) {
|
||||||
|
ReactGA.ga('set', 'contentGroup2', match[2]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
ReactGA.pageview(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
@@ -39,11 +40,18 @@ const GRPCAT = {
|
|||||||
'mc': 'projectiles',
|
'mc': 'projectiles',
|
||||||
'axmc': 'experimental',
|
'axmc': 'experimental',
|
||||||
'fc': 'projectiles',
|
'fc': 'projectiles',
|
||||||
'rfl': 'experimental',
|
'rfl': 'experimental',
|
||||||
'pa': 'projectiles',
|
'pa': 'projectiles',
|
||||||
'rg': 'projectiles',
|
'rg': 'projectiles',
|
||||||
'mr': 'ordnance',
|
'mr': 'ordnance',
|
||||||
'axmr': 'experimental',
|
'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',
|
||||||
@@ -56,7 +64,21 @@ const GRPCAT = {
|
|||||||
'ch': 'defence',
|
'ch': 'defence',
|
||||||
'po': 'defence',
|
'po': 'defence',
|
||||||
'ec': 'defence',
|
'ec': 'defence',
|
||||||
'sfn': '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'
|
||||||
};
|
};
|
||||||
// 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 = {
|
||||||
@@ -73,7 +95,7 @@ const CATEGORIES = {
|
|||||||
'structural reinforcement': ['hr', 'mrp'],
|
'structural reinforcement': ['hr', 'mrp'],
|
||||||
'dc': ['dc'],
|
'dc': ['dc'],
|
||||||
// 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
|
||||||
@@ -82,21 +104,29 @@ const CATEGORIES = {
|
|||||||
'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
|
||||||
'experimental': ['axmc', 'axmr', 'rfl', 'xs', 'sfn']
|
'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,
|
shipMass: PropTypes.number,
|
||||||
warning: PropTypes.func
|
warning: PropTypes.func,
|
||||||
|
firstSlotId: PropTypes.string,
|
||||||
|
lastSlotId: PropTypes.string,
|
||||||
|
activeSlotId: PropTypes.string,
|
||||||
|
slotDiv: PropTypes.object
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
@@ -111,7 +141,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.
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -122,8 +154,9 @@ 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, shipMass, onSelect, modules, firstSlotId, lastSlotId } = props;
|
||||||
let list, currentGroup;
|
let list, currentGroup;
|
||||||
|
|
||||||
let buildGroup = this._buildGroup.bind(
|
let buildGroup = this._buildGroup.bind(
|
||||||
this,
|
this,
|
||||||
translate,
|
translate,
|
||||||
@@ -135,14 +168,19 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
|||||||
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
|
||||||
@@ -170,7 +208,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>);
|
||||||
}
|
}
|
||||||
@@ -181,48 +220,66 @@ 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 {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 {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
|
||||||
|
* @param {string} firstSlotId id of first slot item
|
||||||
|
* @param {string} lastSlotId id of last slot item
|
||||||
* @return {React.Component} Available Module Group contents
|
* @return {React.Component} Available Module Group contents
|
||||||
*/
|
*/
|
||||||
_buildGroup(translate, mountedModule, warningFunc, mass, onSelect, grp, modules) {
|
_buildGroup(translate, mountedModule, warningFunc, mass, onSelect, grp, modules, firstSlotId, lastSlotId) {
|
||||||
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 = mass > m.maxmass;
|
||||||
@@ -238,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);
|
||||||
|
|
||||||
@@ -249,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>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -292,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
|
||||||
@@ -326,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
|
||||||
@@ -383,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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -401,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';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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.getClip() * m.getEps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload()))}{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.getClip() * m.getHps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload()))})</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>
|
</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('ggc')}</div>
|
||||||
|
<ul>
|
||||||
|
<li className='lc' tabIndex='0' onClick={_fill.bind(this, 'ggc', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['ggc-F'] = smRef}>{translate('ggc')}</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>
|
||||||
|
|||||||
@@ -63,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 }
|
||||||
@@ -70,6 +73,7 @@ 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 }
|
||||||
@@ -81,10 +85,11 @@ export default class InternalSlot extends Slot {
|
|||||||
{ 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
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -126,7 +126,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 });
|
||||||
|
|||||||
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,21 @@ 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 { isValueBeneficial } from '../utils/BlueprintFunctions';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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 +38,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 +76,55 @@ 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);
|
||||||
|
|
||||||
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 ?
|
||||||
|
isValueBeneficial(name, modValue) ? 'secondary' : 'warning' :
|
||||||
|
''
|
||||||
|
}>
|
||||||
|
{formats.f2(modValue / 100) || 0}%
|
||||||
|
</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, setPercent, 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
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -33,11 +44,21 @@ export default class ModificationsMenu extends TranslatedComponent {
|
|||||||
this._rollFifty = this._rollFifty.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._rollSevenFive = this._rollSevenFive.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.edname == 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();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,6 +287,10 @@ export default class ModificationsMenu extends TranslatedComponent {
|
|||||||
_rollFifty() {
|
_rollFifty() {
|
||||||
const { m, ship } = this.props;
|
const { m, ship } = this.props;
|
||||||
setPercent(ship, m, 50);
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,15 +313,21 @@ export default class ModificationsMenu extends TranslatedComponent {
|
|||||||
_rollBest() {
|
_rollBest() {
|
||||||
const { m, ship } = this.props;
|
const { m, ship } = this.props;
|
||||||
setPercent(ship, m, 100);
|
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 '75%' roll within the information we have
|
* Provide a 'worst' roll within the information we have
|
||||||
*/
|
*/
|
||||||
_rollSevenFive() {
|
_rollWorst() {
|
||||||
const { m, ship } = this.props;
|
const { m, ship } = this.props;
|
||||||
setPercent(ship, m, 75);
|
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
|
||||||
@@ -227,7 +412,7 @@ export default class ModificationsMenu extends TranslatedComponent {
|
|||||||
const _toggleBlueprintsMenu = this._toggleBlueprintsMenu;
|
const _toggleBlueprintsMenu = this._toggleBlueprintsMenu;
|
||||||
const _toggleSpecialsMenu = this._toggleSpecialsMenu;
|
const _toggleSpecialsMenu = this._toggleSpecialsMenu;
|
||||||
const _rollFull = this._rollBest;
|
const _rollFull = this._rollBest;
|
||||||
const _rollSevenFive = this._rollSevenFive;
|
const _rollWorst = this._rollWorst;
|
||||||
const _rollFifty = this._rollFifty;
|
const _rollFifty = this._rollFifty;
|
||||||
const _rollRandom = this._rollRandom;
|
const _rollRandom = this._rollRandom;
|
||||||
const _reset = this._reset;
|
const _reset = this._reset;
|
||||||
@@ -235,57 +420,73 @@ export default class ModificationsMenu extends TranslatedComponent {
|
|||||||
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.edname);
|
||||||
} 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={_rollFifty} onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_FIFTY')} onMouseOut={tooltip.bind(null, null)}> { translate('50%') } </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={_rollSevenFive} onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_SEVEN_FIVE')} onMouseOut={tooltip.bind(null, null)}> { translate('75%') } </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={_rollFull} onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_BEST')} onMouseOut={tooltip.bind(null, null)}> { translate('100%') } </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,66 +50,166 @@ 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 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}>{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('pax')}</th>
|
{/* <th onMouseEnter={termtip.bind(null, 'heat per second')} onMouseLeave={hide} rowSpan={2}>{translate('HPS')}</th> */}
|
||||||
<th rowSpan={2}>{translate('fuel')}</th>
|
<th rowSpan={2}>{translate('cargo')}</th>
|
||||||
<th colSpan={3}>{translate('mass')}</th>
|
<th rowSpan={2} onMouseEnter={termtip.bind(null, 'passenger capacity', { cap: 0 })} onMouseLeave={hide}>{translate('pax')}</th>
|
||||||
<th onMouseEnter={termtip.bind(null, 'hull hardness', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('hrd')}</th>
|
<th rowSpan={2}>{translate('fuel')}</th>
|
||||||
<th rowSpan={2}>{translate('crew')}</th>
|
<th colSpan={3}>{translate('mass')}</th>
|
||||||
<th onMouseEnter={termtip.bind(null, 'mass lock factor', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('MLF')}</th>
|
<th onMouseEnter={termtip.bind(null, 'hull hardness', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('hrd')}</th>
|
||||||
</tr>
|
<th rowSpan={2}>{translate('crew')}</th>
|
||||||
<tr>
|
<th onMouseEnter={termtip.bind(null, 'mass lock factor', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('MLF')}</th>
|
||||||
<th className='lft'>{translate('max')}</th>
|
<th onMouseEnter={termtip.bind(null, 'TT_SUMMARY_BOOST_TIME', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('boost time')}</th>
|
||||||
<th>{translate('unladen')}</th>
|
<th rowSpan={2}>{translate('resting heat (Beta)')}</th>
|
||||||
<th>{translate('laden')}</th>
|
</tr>
|
||||||
<th>{translate('total unladen')}</th>
|
<tr>
|
||||||
<th>{translate('total laden')}</th>
|
<th className='lft'>{translate('max')}</th>
|
||||||
<th className='lft'>{translate('hull')}</th>
|
<th>{translate('unladen')}</th>
|
||||||
<th>{translate('unladen')}</th>
|
<th>{translate('laden')}</th>
|
||||||
<th>{translate('laden')}</th>
|
<th>{translate('total unladen')}</th>
|
||||||
</tr>
|
<th>{translate('total laden')}</th>
|
||||||
</thead>
|
<th className='lft'>{translate('hull')}</th>
|
||||||
<tbody>
|
<th>{translate('unladen')}</th>
|
||||||
<tr>
|
<th>{translate('laden')}</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>
|
</tr>
|
||||||
<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>
|
</thead>
|
||||||
<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>
|
<tbody>
|
||||||
<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>
|
<tr>
|
||||||
<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>
|
<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_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, 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 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><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(), ship))}{u.LY}</span></td>
|
||||||
<td className={sgClassNames} onMouseEnter={termtip.bind(null, sgTooltip, { cap: 0 })} onMouseLeave={hide}>{int(ship.shield)}{u.MJ}</td>
|
<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, ship))}{u.LY}</span></td>
|
||||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_INTEGRITY', { cap: 0 })} onMouseLeave={hide}>{int(ship.armour)}</td>
|
<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, ship))}{u.LY}</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_UNLADEN_TOTAL_JUMP', { cap: 0 })} onMouseLeave={hide}>{f2(Calc.totalJumpRange(ship.unladenMass + ship.fuelCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</td>
|
||||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_EPS', { cap: 0 })} onMouseLeave={hide}>{f1(ship.totalEps)}</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, ship))}{u.LY}</td>
|
||||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_TTD', { cap: 0 })} onMouseLeave={hide}>{timeToDrain === Infinity ? '∞' : time(timeToDrain)}</td>
|
<td className={sgClassNames} onMouseEnter={termtip.bind(null, sgTooltip, { cap: 0 })} onMouseLeave={hide}>{int(ship.shield)}{u.MJ}</td>
|
||||||
{/* <td>{f1(ship.totalHps)}</td> */}
|
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_INTEGRITY', { cap: 0 })} onMouseLeave={hide}>{int(ship.armour)}</td>
|
||||||
<td>{round(ship.cargoCapacity)}{u.T}</td>
|
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_DPS', { cap: 0 })} onMouseLeave={hide}>{f1(ship.totalDps)}</td>
|
||||||
<td>{ship.passengerCapacity}</td>
|
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_EPS', { cap: 0 })} onMouseLeave={hide}>{f1(ship.totalEps)}</td>
|
||||||
<td>{round(ship.fuelCapacity)}{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_HULL_MASS', { cap: 0 })} onMouseLeave={hide}>{ship.hullMass}{u.T}</td>
|
{/* <td>{f1(ship.totalHps)}</td> */}
|
||||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_MASS', { cap: 0 })} onMouseLeave={hide}>{int(ship.unladenMass)}{u.T}</td>
|
<td>{round(ship.cargoCapacity)}{u.T}</td>
|
||||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_MASS', { cap: 0 })} onMouseLeave={hide}>{int(ship.ladenMass)}{u.T}</td>
|
<td>{ship.passengerCapacity}</td>
|
||||||
<td>{int(ship.hardness)}</td>
|
<td>{round(ship.fuelCapacity)}{u.T}</td>
|
||||||
<td>{ship.crew}</td>
|
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_HULL_MASS', { cap: 0 })} onMouseLeave={hide}>{ship.hullMass}{u.T}</td>
|
||||||
<td>{ship.masslock}</td>
|
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_MASS', { cap: 0 })} onMouseLeave={hide}>{int(ship.unladenMass)}{u.T}</td>
|
||||||
</tr>
|
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_MASS', { cap: 0 })} onMouseLeave={hide}>{int(ship.ladenMass)}{u.T}</td>
|
||||||
</tbody>
|
<td>{int(ship.hardness)}</td>
|
||||||
</table>
|
<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 && ship.shield > 0 ? ship.shield : 0)}{u.MJ}</td>
|
||||||
|
<td>{int(ship && ship.shield > 0 ? ship.shield * ((1 / (1 - (ship.shieldExplRes)))) : 0)}{u.MJ}</td>
|
||||||
|
<td>{int(ship && ship.shield > 0 ? ship.shield * ((1 / (1 - (ship.shieldKinRes)))) : 0)}{u.MJ}</td>
|
||||||
|
<td>{int(ship && ship.shield > 0 ? ship.shield * ((1 / (1 - (ship.shieldThermRes)))) : 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(ship.armour)}</td>
|
||||||
|
<td>{int(ship.armour * ((1 / (1 - (ship.hullExplRes)))))}</td>
|
||||||
|
<td>{int(ship.armour * ((1 / (1 - (ship.hullKinRes)))))}</td>
|
||||||
|
<td>{int(ship.armour * ((1 / (1 - (ship.hullThermRes)))))}</td>
|
||||||
|
<td>{int(ship.armour * ((1 / (1 - (ship.hullCausRes)))))}</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,6 +121,7 @@ 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
|
||||||
@@ -114,6 +132,7 @@ export default class Slot extends TranslatedComponent {
|
|||||||
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
|
||||||
@@ -170,6 +228,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 +295,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;
|
||||||
|
|
||||||
@@ -82,6 +103,7 @@ 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
|
||||||
@@ -92,12 +114,13 @@ export default class StandardSlot extends TranslatedComponent {
|
|||||||
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'}>{m.grp == 'bh' ? m.name.charAt(0) : slot.maxClass}</div>
|
<div className={'sz'}>{m.grp == 'bh' ? m.name.charAt(0) : slot.maxClass}</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -121,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,17 +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, true)}>{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._optimizeExplorer.bind(this, false)}>{translate('Explorer')}</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={cn('lc', { disabled: planetaryDisabled })} onClick={!planetaryDisabled && this._optimizeExplorer.bind(this, true)}>{translate('Planetary 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='lc' onClick={this._optimizeMiner.bind(this, true)}>{translate('Miner')}</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._optimizeRacer.bind(this)}>{translate('Racer')}</li>
|
<li className='lc' tabIndex="0" onClick={this._optimizeRacer.bind(this)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['racer'] = smRef}>{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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,8 +7,12 @@ 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 PT from './pt';
|
||||||
|
import * as CN from './cn';
|
||||||
import * as d3 from 'd3';
|
import * as d3 from 'd3';
|
||||||
|
|
||||||
|
const owofy = require("owofy");
|
||||||
|
|
||||||
|
|
||||||
let fallbackTerms = EN.terms;
|
let fallbackTerms = EN.terms;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -27,6 +31,7 @@ export function getLanguage(langCode) {
|
|||||||
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 'pt': lang = PT; break;
|
||||||
|
case 'cn': lang = CN; break;
|
||||||
default:
|
default:
|
||||||
lang = EN;
|
lang = EN;
|
||||||
}
|
}
|
||||||
@@ -37,7 +42,14 @@ export function getLanguage(langCode) {
|
|||||||
const round = function(x, n) { const ten_n = Math.pow(10,n); return Math.round(x * ten_n) / ten_n; };
|
const round = function(x, n) { const ten_n = Math.pow(10,n); return Math.round(x * ten_n) / ten_n; };
|
||||||
|
|
||||||
if(lang === EN) {
|
if(lang === EN) {
|
||||||
translate = (t, x) => { return currentTerms[t + '_' + x] || currentTerms[t] || t; };
|
translate = (t, x) => {
|
||||||
|
if (currentTerms[t + '_' + x]) {
|
||||||
|
return owofy(currentTerms[t + '_' + x])
|
||||||
|
} else if (currentTerms[t]) {
|
||||||
|
return owofy(currentTerms[t])
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
translate = (t, x) => { return currentTerms[t + '_' + x] || currentTerms[t] || fallbackTerms[t + '_' + x] || fallbackTerms[t] || t; };
|
translate = (t, x) => { return currentTerms[t + '_' + x] || currentTerms[t] || fallbackTerms[t + '_' + x] || fallbackTerms[t] || t; };
|
||||||
}
|
}
|
||||||
@@ -60,17 +72,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
|
||||||
@@ -91,5 +106,6 @@ export const Languages = {
|
|||||||
fr: 'Français',
|
fr: 'Français',
|
||||||
ru: 'ру́сский',
|
ru: 'ру́сский',
|
||||||
pl: 'polski',
|
pl: 'polski',
|
||||||
pt: 'português'
|
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
File diff suppressed because one or more lines are too long
@@ -20,11 +20,11 @@
|
|||||||
"PHRASE_BLUEPRINT_BEST": "Лучшие основные значения для чертежа",
|
"PHRASE_BLUEPRINT_BEST": "Лучшие основные значения для чертежа",
|
||||||
"PHRASE_BLUEPRINT_EXTREME": "Лучшие положительные и худшие отрицательные основные значения для чертежа",
|
"PHRASE_BLUEPRINT_EXTREME": "Лучшие положительные и худшие отрицательные основные значения для чертежа",
|
||||||
"PHRASE_BLUEPRINT_RESET": "Убрать все изменения и чертёж",
|
"PHRASE_BLUEPRINT_RESET": "Убрать все изменения и чертёж",
|
||||||
"PHRASE_SELECT_SPECIAL": "Нажмите чтобы выбрать экспериментальный эффект",
|
"PHRASE_SELECT_SPECIAL": "Нажмите, чтобы выбрать экспериментальный эффект",
|
||||||
"PHRASE_NO_SPECIAL": "Без экспериментального эффекта",
|
"PHRASE_NO_SPECIAL": "Без экспериментального эффекта",
|
||||||
"PHRASE_SHOPPING_LIST": "Станции что продают эту сборку",
|
"PHRASE_SHOPPING_LIST": "Станции, что продают эту сборку",
|
||||||
"PHRASE_REFIT_SHOPPING_LIST": "Станции что продают необходимые модули",
|
"PHRASE_REFIT_SHOPPING_LIST": "Станции, что продают необходимые модули",
|
||||||
"PHRASE_TOTAL_EFFECTIVE_SHIELD": "Общий урон что может быть нанесён в каждым типе, если используются все щитонакопители",
|
"PHRASE_TOTAL_EFFECTIVE_SHIELD": "Общий урон, что может быть нанесён в каждым типе, если используются все щитонакопители",
|
||||||
"PHRASE_TIME_TO_LOSE_SHIELDS": "Щиты продержатся",
|
"PHRASE_TIME_TO_LOSE_SHIELDS": "Щиты продержатся",
|
||||||
"PHRASE_TIME_TO_RECOVER_SHIELDS": "Щиты восстановятся за",
|
"PHRASE_TIME_TO_RECOVER_SHIELDS": "Щиты восстановятся за",
|
||||||
"PHRASE_TIME_TO_RECHARGE_SHIELDS": "Щиты будут заряжены за",
|
"PHRASE_TIME_TO_RECHARGE_SHIELDS": "Щиты будут заряжены за",
|
||||||
@@ -43,12 +43,12 @@
|
|||||||
"PHRASE_TIME_TO_REMOVE_ARMOUR": "Снимет броню за",
|
"PHRASE_TIME_TO_REMOVE_ARMOUR": "Снимет броню за",
|
||||||
"TT_TIME_TO_REMOVE_ARMOUR": "Непрерывным огнём из всех орудий",
|
"TT_TIME_TO_REMOVE_ARMOUR": "Непрерывным огнём из всех орудий",
|
||||||
"PHRASE_TIME_TO_DRAIN_WEP": "Опустошит ОРУЖ за",
|
"PHRASE_TIME_TO_DRAIN_WEP": "Опустошит ОРУЖ за",
|
||||||
"TT_TIME_TO_DRAIN_WEP": "Время за которое опустошится аккумулятор ОРУЖ при стрельбе из всех орудий",
|
"TT_TIME_TO_DRAIN_WEP": "Время, за которое опустошится аккумулятор ОРУЖ при стрельбе из всех орудий",
|
||||||
"TT_TIME_TO_LOSE_SHIELDS": "Против поддерживаемой стрельбы из всех орудий противника",
|
"TT_TIME_TO_LOSE_SHIELDS": "Против поддерживаемой стрельбы из всех орудий противника",
|
||||||
"TT_TIME_TO_LOSE_ARMOUR": "Против поддерживаемой стрельбы из всех орудий противника",
|
"TT_TIME_TO_LOSE_ARMOUR": "Против поддерживаемой стрельбы из всех орудий противника",
|
||||||
"TT_MODULE_ARMOUR": "Броня защищаюшае модули от урона",
|
"TT_MODULE_ARMOUR": "Броня, защищающая модули от урона",
|
||||||
"TT_MODULE_PROTECTION_EXTERNAL": "Процент урона перенаправленного от гнёзд на наборы для усиления модулей",
|
"TT_MODULE_PROTECTION_EXTERNAL": "Процент урона, перенаправленного от гнёзд на наборы для усиления модулей",
|
||||||
"TT_MODULE_PROTECTION_INTERNAL": "Процент урона перенаправленного от модулей вне гнёзд на наборы для усиления модулей",
|
"TT_MODULE_PROTECTION_INTERNAL": "Процент урона, перенаправленного от модулей вне гнёзд на наборы для усиления модулей",
|
||||||
"TT_EFFECTIVE_SDPS_SHIELDS": "Реальный поддерживаемый ДПС пока аккумулятор ОРУЖ не пуст",
|
"TT_EFFECTIVE_SDPS_SHIELDS": "Реальный поддерживаемый ДПС пока аккумулятор ОРУЖ не пуст",
|
||||||
"TT_EFFECTIVENESS_SHIELDS": "Эффективность в сравнении с попаданием по цели с 0-сопротивляемостью без пунктов в СИС на 0 метрах",
|
"TT_EFFECTIVENESS_SHIELDS": "Эффективность в сравнении с попаданием по цели с 0-сопротивляемостью без пунктов в СИС на 0 метрах",
|
||||||
"TT_EFFECTIVE_SDPS_ARMOUR": "Реальный поддерживаемый ДПС пока аккумулятор ОРУЖ не пуст",
|
"TT_EFFECTIVE_SDPS_ARMOUR": "Реальный поддерживаемый ДПС пока аккумулятор ОРУЖ не пуст",
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
"PHRASE_EFFECTIVE_SDPS_SHIELDS": "ПДПС против щитов",
|
"PHRASE_EFFECTIVE_SDPS_SHIELDS": "ПДПС против щитов",
|
||||||
"PHRASE_EFFECTIVE_SDPS_ARMOUR": "ПДПС против брони",
|
"PHRASE_EFFECTIVE_SDPS_ARMOUR": "ПДПС против брони",
|
||||||
"TT_SUMMARY_SPEED": "С полным топливным баком и 4 пунктами в ДВИ",
|
"TT_SUMMARY_SPEED": "С полным топливным баком и 4 пунктами в ДВИ",
|
||||||
"TT_SUMMARY_SPEED_NONFUNCTIONAL": "маневровые двигатели выключены или превышена максимальная масса с топливом и грузом",
|
"TT_SUMMARY_SPEED_NONFUNCTIONAL": "Маневровые двигатели выключены или превышена максимальная масса с топливом и грузом",
|
||||||
"TT_SUMMARY_BOOST": "С полным топливным баком и 4 пунктами в ДВИ",
|
"TT_SUMMARY_BOOST": "С полным топливным баком и 4 пунктами в ДВИ",
|
||||||
"TT_SUMMARY_BOOST_NONFUNCTIONAL": "Распределитель питания не может обеспечить достаточно энергии для форсажа",
|
"TT_SUMMARY_BOOST_NONFUNCTIONAL": "Распределитель питания не может обеспечить достаточно энергии для форсажа",
|
||||||
"TT_SUMMARY_SHIELDS": "Чистая сила щита, включая усилители",
|
"TT_SUMMARY_SHIELDS": "Чистая сила щита, включая усилители",
|
||||||
@@ -68,16 +68,16 @@
|
|||||||
"TT_SUMMARY_DPS": "Урон в секунду при стрельбе из всех орудий",
|
"TT_SUMMARY_DPS": "Урон в секунду при стрельбе из всех орудий",
|
||||||
"TT_SUMMARY_EPS": "Расход аккумулятора ОРУЖ в секунду при стрельбе из всех орудий",
|
"TT_SUMMARY_EPS": "Расход аккумулятора ОРУЖ в секунду при стрельбе из всех орудий",
|
||||||
"TT_SUMMARY_TTD": "Время расхода аккумулятора ОРУЖ при стрельбе из всех орудий и с 4 пунктами в ОРУЖ",
|
"TT_SUMMARY_TTD": "Время расхода аккумулятора ОРУЖ при стрельбе из всех орудий и с 4 пунктами в ОРУЖ",
|
||||||
"TT_SUMMARY_MAX_SINGLE_JUMP": "Самый дальний возможный прыжок без груза и с топливом достаточным только на сам прыжок",
|
"TT_SUMMARY_MAX_SINGLE_JUMP": "Самый дальний возможный прыжок без груза и с топливом, достаточным только на сам прыжок",
|
||||||
"TT_SUMMARY_UNLADEN_SINGLE_JUMP": "Самый дальний возможный прыжок без груза и с полным топливным баком",
|
"TT_SUMMARY_UNLADEN_SINGLE_JUMP": "Самый дальний возможный прыжок без груза и с полным топливным баком",
|
||||||
"TT_SUMMARY_LADEN_SINGLE_JUMP": "Самый дальний возможный прыжок с полным грузовым отсеком и с полным топливным баком",
|
"TT_SUMMARY_LADEN_SINGLE_JUMP": "Самый дальний возможный прыжок с полным грузовым отсеком и с полным топливным баком",
|
||||||
"TT_SUMMARY_UNLADEN_TOTAL_JUMP": "Самая дальняя общая дистанция без груза, с полным топливным баком и при прыжках на максимальное расстояние",
|
"TT_SUMMARY_UNLADEN_TOTAL_JUMP": "Самая дальняя общая дистанция без груза, с полным топливным баком и при прыжках на максимальное расстояние",
|
||||||
"TT_SUMMARY_LADEN_TOTAL_JUMP": "Самая дальняя общая дистанция с полным грузовым отсеком, с полным топливным баком и при прыжках на максимальное расстояние",
|
"TT_SUMMARY_LADEN_TOTAL_JUMP": "Самая дальняя общая дистанция с полным грузовым отсеком, с полным топливным баком и при прыжках на максимальное расстояние",
|
||||||
"HELP_MODIFICATIONS_MENU": "Ткните на номер чтобы ввести новое значение, или потяните вдоль полосы для малых изменений",
|
"HELP_MODIFICATIONS_MENU": "Нажмите на номер, чтобы ввести новое значение, или потяните вдоль полосы для малых изменений",
|
||||||
"am": "Блок Автом. Полевого Ремонта",
|
"am": "Блок Автом. Полевого Ремонта",
|
||||||
"bh": "Переборки",
|
"bh": "Переборки",
|
||||||
"bl": "Пучковый Лазер",
|
"bl": "Пучковый лазер",
|
||||||
"bsg": "Двухпоточный Щитогенератор",
|
"bsg": "Двухпоточный щитогенератор",
|
||||||
"c": "Орудие",
|
"c": "Орудие",
|
||||||
"cc": "Контроллер магнитного снаряда для сбора",
|
"cc": "Контроллер магнитного снаряда для сбора",
|
||||||
"ch": "Разбрасыватель дипольных отражателей",
|
"ch": "Разбрасыватель дипольных отражателей",
|
||||||
@@ -89,7 +89,7 @@
|
|||||||
"fh": "Ангар для истребителя",
|
"fh": "Ангар для истребителя",
|
||||||
"fi": "FSD-перехватчик",
|
"fi": "FSD-перехватчик",
|
||||||
"fs": "Топливозаборник",
|
"fs": "Топливозаборник",
|
||||||
"fsd": "Рамочно Сместительный двигатель",
|
"fsd": "Рамочно-сместительный двигатель",
|
||||||
"ft": "Топливный бак",
|
"ft": "Топливный бак",
|
||||||
"fx": "Контроллер магнитного снаряда для топлива",
|
"fx": "Контроллер магнитного снаряда для топлива",
|
||||||
"hb": "Контроллер магнитного снаряда для взлома трюма",
|
"hb": "Контроллер магнитного снаряда для взлома трюма",
|
||||||
@@ -110,7 +110,7 @@
|
|||||||
"pcm": "Каюта пассажира первого класса",
|
"pcm": "Каюта пассажира первого класса",
|
||||||
"pcq": "Каюта пассажира класса люкс",
|
"pcq": "Каюта пассажира класса люкс",
|
||||||
"pd": "Распределитель питания",
|
"pd": "Распределитель питания",
|
||||||
"pl": "Ипмульсный лазер",
|
"pl": "Импульсный лазер",
|
||||||
"po": "Точечная оборона",
|
"po": "Точечная оборона",
|
||||||
"pp": "Силовая установка",
|
"pp": "Силовая установка",
|
||||||
"psg": "Призматический щитогенератор",
|
"psg": "Призматический щитогенератор",
|
||||||
@@ -122,7 +122,7 @@
|
|||||||
"sc": "Сканер обнаружения",
|
"sc": "Сканер обнаружения",
|
||||||
"scb": "Щитонакопитель",
|
"scb": "Щитонакопитель",
|
||||||
"sg": "Щитогенератор",
|
"sg": "Щитогенератор",
|
||||||
"ss": "Сканер Поверхностей",
|
"ss": "Сканер поверхностей",
|
||||||
"t": "Маневровые двигатели",
|
"t": "Маневровые двигатели",
|
||||||
"tp": "Торпедная стойка",
|
"tp": "Торпедная стойка",
|
||||||
"ul": "Пульсирующие лазеры",
|
"ul": "Пульсирующие лазеры",
|
||||||
@@ -130,7 +130,7 @@
|
|||||||
"emptyrestricted": "пусто (ограниченно)",
|
"emptyrestricted": "пусто (ограниченно)",
|
||||||
"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": "Общие эффективные щиты",
|
||||||
"ammunition": "Припасы",
|
"ammunition": "Припасы",
|
||||||
@@ -149,7 +149,7 @@
|
|||||||
"eps": "Энергия в секунду",
|
"eps": "Энергия в секунду",
|
||||||
"epsseps": "Энергия в секунду (поддерживаемая энергия в секунду)",
|
"epsseps": "Энергия в секунду (поддерживаемая энергия в секунду)",
|
||||||
"hps": "Нагрев в секунду",
|
"hps": "Нагрев в секунду",
|
||||||
"hpsshps": "Heat per second (sustained heat per second)",
|
"hpsshps": "Нагрев в секунду (поддерживаемый нагрев в секунду)",
|
||||||
"damage by": "Урон",
|
"damage by": "Урон",
|
||||||
"damage from": "Урон от",
|
"damage from": "Урон от",
|
||||||
"shield cells": "Щитонакопители",
|
"shield cells": "Щитонакопители",
|
||||||
@@ -183,7 +183,7 @@
|
|||||||
"hullreinforcement": "Укрепление корпуса",
|
"hullreinforcement": "Укрепление корпуса",
|
||||||
"integrity": "Целостность",
|
"integrity": "Целостность",
|
||||||
"jitter": "Дрожание",
|
"jitter": "Дрожание",
|
||||||
"kinres": "Сопротивление китетическому урону",
|
"kinres": "Сопротивление кинетическому урону",
|
||||||
"maxfuel": "Макс. топлива на прыжок",
|
"maxfuel": "Макс. топлива на прыжок",
|
||||||
"mass": "Масса",
|
"mass": "Масса",
|
||||||
"optmass": "Оптимизированная масса",
|
"optmass": "Оптимизированная масса",
|
||||||
@@ -381,4 +381,4 @@
|
|||||||
"URL": "Ссылка",
|
"URL": "Ссылка",
|
||||||
"WEP": "ОРУЖ",
|
"WEP": "ОРУЖ",
|
||||||
"yes": "Да"
|
"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,27 +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>
|
||||||
|
You can chat to us on our{' '}
|
||||||
|
<a href="https://discord.gg/0uwCh6R62aPRjk9w" target="_blank">
|
||||||
|
EDCD Discord server
|
||||||
|
</a>
|
||||||
|
.
|
||||||
|
</p>
|
||||||
|
|
||||||
<h3>Supporting Coriolis</h3>
|
<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>
|
<p>
|
||||||
</div>;
|
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,22 +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),
|
||||||
@@ -100,6 +129,9 @@ export default class OutfittingPage extends Page {
|
|||||||
sys,
|
sys,
|
||||||
eng,
|
eng,
|
||||||
wep,
|
wep,
|
||||||
|
mcSys,
|
||||||
|
mcEng,
|
||||||
|
mcWep,
|
||||||
boost,
|
boost,
|
||||||
fuel,
|
fuel,
|
||||||
cargo,
|
cargo,
|
||||||
@@ -122,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;
|
||||||
}
|
}
|
||||||
@@ -148,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)
|
||||||
|
)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -162,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;
|
||||||
@@ -177,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]);
|
||||||
@@ -196,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 {
|
||||||
@@ -207,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()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -229,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());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -237,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());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -245,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());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -253,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()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -262,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;
|
||||||
@@ -270,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]);
|
||||||
@@ -285,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()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -295,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,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)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -339,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
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,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)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -382,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 {
|
||||||
@@ -399,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())}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -416,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)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -444,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)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -465,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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -488,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} />);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -499,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} />);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -512,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();
|
||||||
}
|
}
|
||||||
@@ -529,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,
|
||||||
@@ -547,95 +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;
|
const requirements = Ships[ship.id].requirements;
|
||||||
var requirementElements = [];
|
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) {
|
function renderRequirement(className, textKey, tooltipTextKey) {
|
||||||
requirementElements.push(<div key={textKey} className={className} onMouseEnter={termtip.bind(null, tooltipTextKey)} onMouseLeave={hide}>{translate(textKey)}</div>);
|
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) {
|
if (requirements) {
|
||||||
requirements.federationRank && renderRequirement('federation', 'federation rank ' + requirements.federationRank, 'federation rank required');
|
requirements.federationRank &&
|
||||||
requirements.empireRank && renderRequirement('empire', 'empire rank ' + requirements.empireRank, 'empire rank required');
|
renderRequirement(
|
||||||
requirements.horizons && renderRequirement('horizons', 'horizons', 'horizons required');
|
'federation',
|
||||||
requirements.horizonsEarlyAdoption && renderRequirement('horizons', 'horizons early adoption', 'horizons early adoption required');
|
'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='requirements'>{requirementElements}</div>
|
<div id="requirements">{requirementElements}</div>
|
||||||
<div id='build'>
|
<div id="build">
|
||||||
<input value={newBuildName || ''} onChange={this._buildNameChange} placeholder={translate('Enter Name')} maxLength={50} />
|
<input
|
||||||
<button onClick={canSave && this._saveBuild} disabled={!canSave} onMouseOver={termtip.bind(null, 'save')} onMouseOut={hide}>
|
value={newBuildName || ''}
|
||||||
<FloppyDisk className='lg' />
|
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 */}
|
||||||
|
|||||||
@@ -70,6 +70,13 @@ export default class Page extends React.Component {
|
|||||||
document.title = this.state.title || 'Coriolis';
|
document.title = this.state.title || 'Coriolis';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the window title upon mount
|
||||||
|
*/
|
||||||
|
componentDidMount() {
|
||||||
|
document.title = this.state.title || 'Coriolis';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the title upon change
|
* Updates the title upon change
|
||||||
* @param {Object} newProps Incoming properties
|
* @param {Object} newProps Incoming properties
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import Ship from '../shipyard/Ship';
|
|||||||
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
||||||
import { SizeMap } from '../shipyard/Constants';
|
import { SizeMap } from '../shipyard/Constants';
|
||||||
import Link from '../components/Link';
|
import Link from '../components/Link';
|
||||||
|
const owofy = require("owofy");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Counts the hardpoints by class/size
|
* Counts the hardpoints by class/size
|
||||||
@@ -22,9 +23,11 @@ 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
|
// if no eligiblity, then assume pce
|
||||||
let passSlotType = null;
|
let passSlotType = null;
|
||||||
@@ -42,7 +45,9 @@ function countInt(slot) {
|
|||||||
passSlotType = 'pcq';
|
passSlotType = 'pcq';
|
||||||
passSlotRating = 'B';
|
passSlotRating = 'B';
|
||||||
}
|
}
|
||||||
let passengerBay = passSlotType ? ModuleUtils.findMaxInternal(passSlotType, slot.maxClass, passSlotRating) : null;
|
let passengerBay = passSlotType ?
|
||||||
|
ModuleUtils.findMaxInternal(passSlotType, slot.maxClass, passSlotRating) :
|
||||||
|
null;
|
||||||
this.maxPassengers += passengerBay ? passengerBay.passengers : 0;
|
this.maxPassengers += passengerBay ? passengerBay.passengers : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,23 +62,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,
|
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;
|
||||||
@@ -97,7 +106,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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -145,12 +153,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
|
||||||
@@ -159,56 +170,59 @@ export default class ShipyardPage extends Page {
|
|||||||
* @param {Object} u Localized unit map
|
* @param {Object} u Localized unit map
|
||||||
* @param {Function} fInt Localized integer formatter
|
* @param {Function} fInt Localized integer formatter
|
||||||
* @param {Function} fRound Localized round formatter
|
* @param {Function} fRound Localized round formatter
|
||||||
* @param {Boolean} highlight Should this row be highlighted
|
|
||||||
* @return {React.Component} Table Row
|
* @return {React.Component} Table Row
|
||||||
*/
|
*/
|
||||||
_shipRowElement(s, translate, u, fInt, fRound, highlight) {
|
_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, alt: highlight })}
|
className={cn({
|
||||||
|
highlighted: noTouch && this.state.shipId === s.id
|
||||||
|
})}
|
||||||
onMouseEnter={noTouch && this._highlightShip.bind(this, s.id)}
|
onMouseEnter={noTouch && this._highlightShip.bind(this, s.id)}
|
||||||
>
|
>
|
||||||
<td className='ri'>{s.manufacturer}</td>
|
<td className="ri">{owofy(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='ri'>{fInt(s.maxPassengers)}</td>
|
<td className="ri">{fInt(s.maxPassengers)}</td>
|
||||||
<td className='cn'>{s.standard[0]}</td>
|
<td className="cn">{s.standard[0]}</td>
|
||||||
<td className='cn'>{s.standard[1]}</td>
|
<td className="cn">{s.standard[1]}</td>
|
||||||
<td className='cn'>{s.standard[2]}</td>
|
<td className="cn">{s.standard[2]}</td>
|
||||||
<td className='cn'>{s.standard[3]}</td>
|
<td className="cn">{s.standard[3]}</td>
|
||||||
<td className='cn'>{s.standard[4]}</td>
|
<td className="cn">{s.standard[4]}</td>
|
||||||
<td className='cn'>{s.standard[5]}</td>
|
<td className="cn">{s.standard[5]}</td>
|
||||||
<td className='cn'>{s.standard[6]}</td>
|
<td className="cn">{s.standard[6]}</td>
|
||||||
<td className={cn({ disabled: !s.hp[1] })}>{s.hp[1]}</td>
|
<td className={cn({ disabled: !s.hp[1] })}>{s.hp[1]}</td>
|
||||||
<td className={cn({ disabled: !s.hp[2] })}>{s.hp[2]}</td>
|
<td className={cn({ disabled: !s.hp[2] })}>{s.hp[2]}</td>
|
||||||
<td className={cn({ disabled: !s.hp[3] })}>{s.hp[3]}</td>
|
<td className={cn({ disabled: !s.hp[3] })}>{s.hp[3]}</td>
|
||||||
<td className={cn({ disabled: !s.hp[4] })}>{s.hp[4]}</td>
|
<td className={cn({ disabled: !s.hp[4] })}>{s.hp[4]}</td>
|
||||||
<td className={cn({ disabled: !s.hp[0] })}>{s.hp[0]}</td>
|
<td className={cn({ disabled: !s.hp[0] })}>{s.hp[0]}</td>
|
||||||
<td className={cn({ disabled: !s.int[0] })}>{s.int[0]}</td>
|
<td className={cn({ disabled: !s.int[0] })}>{s.int[0]}</td>
|
||||||
<td className={cn({ disabled: !s.int[1] })}>{s.int[1]}</td>
|
<td className={cn({ disabled: !s.int[1] })}>{s.int[1]}</td>
|
||||||
<td className={cn({ disabled: !s.int[2] })}>{s.int[2]}</td>
|
<td className={cn({ disabled: !s.int[2] })}>{s.int[2]}</td>
|
||||||
<td className={cn({ disabled: !s.int[3] })}>{s.int[3]}</td>
|
<td className={cn({ disabled: !s.int[3] })}>{s.int[3]}</td>
|
||||||
<td className={cn({ disabled: !s.int[4] })}>{s.int[4]}</td>
|
<td className={cn({ disabled: !s.int[4] })}>{s.int[4]}</td>
|
||||||
<td className={cn({ disabled: !s.int[5] })}>{s.int[5]}</td>
|
<td className={cn({ disabled: !s.int[5] })}>{s.int[5]}</td>
|
||||||
<td className={cn({ disabled: !s.int[6] })}>{s.int[6]}</td>
|
<td className={cn({ disabled: !s.int[6] })}>{s.int[6]}</td>
|
||||||
<td className={cn({ disabled: !s.int[7] })}>{s.int[7]}</td>
|
<td className={cn({ disabled: !s.int[7] })}>{s.int[7]}</td>
|
||||||
</tr>;
|
</tr>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -222,7 +236,8 @@ export default class ShipyardPage extends Page {
|
|||||||
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 } = 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}
|
||||||
@@ -239,7 +254,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];
|
||||||
@@ -252,7 +268,7 @@ export default class ShipyardPage extends Page {
|
|||||||
valB = val;
|
valB = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(valA == valB) {
|
if (valA == valB) {
|
||||||
if (a.name > b.name) {
|
if (a.name > b.name) {
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
@@ -269,47 +285,54 @@ export default class ShipyardPage extends Page {
|
|||||||
let shipRows = new Array(shipSummaries.length);
|
let shipRows = new Array(shipSummaries.length);
|
||||||
let detailRows = new Array(shipSummaries.length);
|
let detailRows = new Array(shipSummaries.length);
|
||||||
|
|
||||||
let lastShipSortValue = null;
|
|
||||||
let backgroundHighlight = false;
|
|
||||||
|
|
||||||
for (let s of shipSummaries) {
|
for (let s of shipSummaries) {
|
||||||
let shipSortValue = s[shipPredicate];
|
detailRows[i] = this._shipRowElement(
|
||||||
if( shipPredicateIndex != undefined ) {
|
s,
|
||||||
shipSortValue = shipSortValue[shipPredicateIndex];
|
translate,
|
||||||
}
|
units,
|
||||||
|
fInt,
|
||||||
if( shipSortValue != lastShipSortValue ) {
|
formats.f1,
|
||||||
backgroundHighlight = !backgroundHighlight;
|
);
|
||||||
lastShipSortValue = shipSortValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
detailRows[i] = this._shipRowElement(s, translate, units, fInt, formats.f1, backgroundHighlight);
|
|
||||||
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, alt: backgroundHighlight })}
|
className={cn({
|
||||||
|
highlighted: noTouch && this.state.shipId === s.id,
|
||||||
|
})}
|
||||||
onMouseEnter={noTouch && this._highlightShip.bind(this, s.id)}
|
onMouseEnter={noTouch && this._highlightShip.bind(this, s.id)}
|
||||||
>
|
>
|
||||||
<td className='le'><Link href={'/outfit/' + s.id}>{s.name}</Link></td>
|
<td className="le">
|
||||||
|
<Link href={'/outfit/' + s.id}>{owofy(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
|
||||||
|
style={{
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
margin: '0 auto',
|
||||||
|
fontSize: '0.8em',
|
||||||
|
position: 'relative',
|
||||||
|
display: 'inline-block',
|
||||||
|
maxWidth: '100%'
|
||||||
|
}}
|
||||||
|
>
|
||||||
<table style={{ width: '12em', position: 'absolute', zIndex: 1 }}>
|
<table style={{ width: '12em', position: 'absolute', zIndex: 1 }}>
|
||||||
<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)}>
|
||||||
@@ -317,83 +340,265 @@ export default class ShipyardPage extends Page {
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div style={{ overflowX: 'scroll', maxWidth: '100%' }}>
|
<div style={{ overflowX: 'scroll', maxWidth: '100%' }}>
|
||||||
<table style={{ marginLeft: 'calc(12em - 1px)', zIndex: 0 }}>
|
<table style={{ marginLeft: 'calc(12em - 1px)', zIndex: 0 }}>
|
||||||
<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={5}>{translate('max')}</th>
|
rowSpan={3}
|
||||||
<th className='lft' colSpan={7}></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')}>
|
||||||
<th className='sortable' onClick={sortShips('maxPassengers')}>{translate('pax')}</th>
|
{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={7}>{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> </th>
|
<th className="sortable" onClick={sortShips('int', 5)}>
|
||||||
<th className='sortable lft' onMouseEnter={termtip.bind(null, 'power plant')} onMouseLeave={hide} onClick={sortShips('standard', 0)}>{'pp'}</th>
|
6
|
||||||
<th className='sortable' onMouseEnter={termtip.bind(null, 'thrusters')} onMouseLeave={hide} onClick={sortShips('standard', 1)}>{'th'}</th>
|
</th>
|
||||||
<th className='sortable' onMouseEnter={termtip.bind(null, 'frame shift drive')} onMouseLeave={hide} onClick={sortShips('standard', 2)}>{'fsd'}</th>
|
<th className="sortable" onClick={sortShips('int', 6)}>
|
||||||
<th className='sortable' onMouseEnter={termtip.bind(null, 'life support')} onMouseLeave={hide} onClick={sortShips('standard', 3)}>{'ls'}</th>
|
7
|
||||||
<th className='sortable' onMouseEnter={termtip.bind(null, 'power distriubtor')} onMouseLeave={hide} onClick={sortShips('standard', 4)}>{'pd'}</th>
|
</th>
|
||||||
<th className='sortable' onMouseEnter={termtip.bind(null, 'sensors')} onMouseLeave={hide} onClick={sortShips('standard', 5)}>{'s'}</th>
|
<th className="sortable" onClick={sortShips('int', 7)}>
|
||||||
<th className='sortable' onMouseEnter={termtip.bind(null, 'fuel tank')} onMouseLeave={hide} onClick={sortShips('standard', 6)}>{'ft'}</th>
|
8
|
||||||
<th className='sortable lft' onClick={sortShips('hp',1)}>{translate('S')}</th>
|
</th>
|
||||||
<th className='sortable' onClick={sortShips('hp', 2)}>{translate('M')}</th>
|
</tr>
|
||||||
<th className='sortable' onClick={sortShips('hp', 3)}>{translate('L')}</th>
|
</thead>
|
||||||
<th className='sortable' onClick={sortShips('hp', 4)}>{translate('H')}</th>
|
<tbody onMouseLeave={this._highlightShip.bind(this, null)}>
|
||||||
<th className='sortable' onClick={sortShips('hp', 0)}>{translate('U')}</th>
|
{detailRows}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
<th className='sortable lft' onClick={sortShips('int', 0)} >1</th>
|
|
||||||
<th className='sortable' onClick={sortShips('int', 1)} >2</th>
|
|
||||||
<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>
|
</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') {
|
||||||
|
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') {
|
||||||
|
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,9 @@ 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,
|
total: generatorStrength + boostersStrength + ship.shieldCells + shieldAddition,
|
||||||
recover,
|
recover,
|
||||||
recharge,
|
recharge,
|
||||||
};
|
};
|
||||||
@@ -425,34 +480,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 +560,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.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.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 +629,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 +835,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 +917,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 +928,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;
|
||||||
@@ -853,3 +1031,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,11 @@ 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',
|
||||||
|
|
||||||
// Hard Points
|
// Hard Points
|
||||||
bl: 'Beam Laser',
|
bl: 'Beam Laser',
|
||||||
@@ -75,7 +84,20 @@ export const ModuleGroupToName = {
|
|||||||
sb: 'Shield Booster',
|
sb: 'Shield Booster',
|
||||||
tp: 'Torpedo Pylon',
|
tp: 'Torpedo Pylon',
|
||||||
sfn: 'Shutdown Field Neutraliser',
|
sfn: 'Shutdown Field Neutraliser',
|
||||||
xs: 'Xeno Scanner'
|
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 = {};
|
||||||
|
|||||||
@@ -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,7 +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 || (e.class == id.charAt(0) && e.rating == id.charAt(1)));
|
let s = Modules.standard[type].find(e => e.id === id);
|
||||||
|
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 });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}],
|
}],
|
||||||
|
|||||||
@@ -7,9 +7,10 @@ import LZString from 'lz-string';
|
|||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import isEqual from 'lodash/lang';
|
import isEqual from 'lodash/lang';
|
||||||
import { Ships, Modifications } from 'coriolis-data/dist';
|
import { Ships, Modifications } from 'coriolis-data/dist';
|
||||||
|
import { chain } from 'lodash';
|
||||||
const zlib = require('zlib');
|
const zlib = require('zlib');
|
||||||
|
|
||||||
const UNIQUE_MODULES = ['psg', 'sg', 'bsg', 'rf', 'fs', 'fh'];
|
const UNIQUE_MODULES = ['psg', 'sg', 'bsg', 'rf', 'fs', 'fh', 'gfsb'];
|
||||||
|
|
||||||
// Constants for modifications struct
|
// Constants for modifications struct
|
||||||
const SLOT_ID_DONE = -1;
|
const SLOT_ID_DONE = -1;
|
||||||
@@ -70,7 +71,6 @@ function reduceToIDs(idArray, slot, slotIndex) {
|
|||||||
* Ship Model - Encapsulates and models in-game ship behavior
|
* Ship Model - Encapsulates and models in-game ship behavior
|
||||||
*/
|
*/
|
||||||
export default class Ship {
|
export default class Ship {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {String} id Unique ship Id / Key
|
* @param {String} id Unique ship Id / Key
|
||||||
* @param {Object} properties Basic ship properties such as name, manufacturer, mass, etc
|
* @param {Object} properties Basic ship properties such as name, manufacturer, mass, etc
|
||||||
@@ -151,7 +151,7 @@ export default class Ship {
|
|||||||
* @return {Number} Jump range in Light Years
|
* @return {Number} Jump range in Light Years
|
||||||
*/
|
*/
|
||||||
calcLadenRange(massDelta, fuel, fsd) {
|
calcLadenRange(massDelta, fuel, fsd) {
|
||||||
return Calc.jumpRange(this.ladenMass + (massDelta || 0), fsd || this.standard[2].m, fuel);
|
return Calc.jumpRange(this.ladenMass + (massDelta || 0), fsd || this.standard[2].m, fuel, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -164,7 +164,7 @@ export default class Ship {
|
|||||||
calcUnladenRange(massDelta, fuel, fsd) {
|
calcUnladenRange(massDelta, fuel, fsd) {
|
||||||
fsd = fsd || this.standard[2].m;
|
fsd = fsd || this.standard[2].m;
|
||||||
let fsdMaxFuelPerJump = fsd instanceof Module ? fsd.getMaxFuelPerJump() : fsd.maxfuel;
|
let fsdMaxFuelPerJump = fsd instanceof Module ? fsd.getMaxFuelPerJump() : fsd.maxfuel;
|
||||||
return Calc.jumpRange(this.unladenMass + (massDelta || 0) + Math.min(fsdMaxFuelPerJump, fuel || this.fuelCapacity), fsd || this.standard[2].m, fuel);
|
return Calc.jumpRange(this.unladenMass + (massDelta || 0) + Math.min(fsdMaxFuelPerJump, fuel || this.fuelCapacity), fsd || this.standard[2].m, fuel, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -239,7 +239,6 @@ export default class Ship {
|
|||||||
}
|
}
|
||||||
sg = sgSlot.m;
|
sg = sgSlot.m;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Not accurate if the ship has modified shield boosters
|
// TODO Not accurate if the ship has modified shield boosters
|
||||||
return Calc.shieldStrength(this.hullMass, this.baseShieldStrength, sg, 1 + (multiplierDelta || 0));
|
return Calc.shieldStrength(this.hullMass, this.baseShieldStrength, sg, 1 + (multiplierDelta || 0));
|
||||||
}
|
}
|
||||||
@@ -416,16 +415,16 @@ export default class Ship {
|
|||||||
clearModifications(m) {
|
clearModifications(m) {
|
||||||
m.mods = {};
|
m.mods = {};
|
||||||
this.updatePowerGenerated()
|
this.updatePowerGenerated()
|
||||||
.updatePowerUsed()
|
.updatePowerUsed()
|
||||||
.recalculateMass()
|
.recalculateMass()
|
||||||
.updateJumpStats()
|
.updateJumpStats()
|
||||||
.recalculateShield()
|
.recalculateShield()
|
||||||
.recalculateShieldCells()
|
.recalculateShieldCells()
|
||||||
.recalculateArmour()
|
.recalculateArmour()
|
||||||
.recalculateDps()
|
.recalculateDps()
|
||||||
.recalculateEps()
|
.recalculateEps()
|
||||||
.recalculateHps()
|
.recalculateHps()
|
||||||
.updateMovement();
|
.updateMovement();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -437,12 +436,15 @@ export default class Ship {
|
|||||||
m.blueprint = bp;
|
m.blueprint = bp;
|
||||||
this.clearModifications(m);
|
this.clearModifications(m);
|
||||||
// Set any hidden items for the blueprint now
|
// Set any hidden items for the blueprint now
|
||||||
const features = m.blueprint.grades[m.blueprint.grade].features;
|
if (m.blueprint.grades[m.blueprint.grade] && m.blueprint.grades[m.blueprint.grade].features) {
|
||||||
for (const featureName in features) {
|
const features = m.blueprint.grades[m.blueprint.grade].features;
|
||||||
if (Modifications.modifications[featureName].hidden) {
|
for (const featureName in features) {
|
||||||
this.setModification(m, featureName, bp.grades[bp.grade].features[featureName][0]);
|
if (Modifications.modifications[featureName].hidden) {
|
||||||
|
this.setModification(m, featureName, bp.grades[bp.grade].features[featureName][0]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updateModificationsString();
|
this.updateModificationsString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -464,7 +466,17 @@ export default class Ship {
|
|||||||
if (m.blueprint) {
|
if (m.blueprint) {
|
||||||
m.blueprint.special = special;
|
m.blueprint.special = special;
|
||||||
}
|
}
|
||||||
this.recalculateDps().recalculateHps().recalculateEps();
|
this.updatePowerGenerated()
|
||||||
|
.updatePowerUsed()
|
||||||
|
.recalculateMass()
|
||||||
|
.updateJumpStats()
|
||||||
|
.recalculateShield()
|
||||||
|
.recalculateShieldCells()
|
||||||
|
.recalculateArmour()
|
||||||
|
.recalculateDps()
|
||||||
|
.recalculateEps()
|
||||||
|
.recalculateHps()
|
||||||
|
.updateMovement();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -472,10 +484,7 @@ export default class Ship {
|
|||||||
* @param {Object} m The module for which to clear the blueprint
|
* @param {Object} m The module for which to clear the blueprint
|
||||||
*/
|
*/
|
||||||
clearModuleSpecial(m) {
|
clearModuleSpecial(m) {
|
||||||
if (m.blueprint) {
|
this.setModuleSpecial(m, null);
|
||||||
m.blueprint.special = null;
|
|
||||||
}
|
|
||||||
this.recalculateDps().recalculateHps().recalculateEps();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -484,68 +493,67 @@ export default class Ship {
|
|||||||
* @param {Object} name The name of the modification to change
|
* @param {Object} name The name of the modification to change
|
||||||
* @param {Number} value The new value of the modification. The value of the modification is scaled to provide two decimal places of precision in an integer. For example 1.23% is stored as 123
|
* @param {Number} value The new value of the modification. The value of the modification is scaled to provide two decimal places of precision in an integer. For example 1.23% is stored as 123
|
||||||
* @param {bool} sentfromui True if this update was sent from the UI
|
* @param {bool} sentfromui True if this update was sent from the UI
|
||||||
|
* @param {bool} isAbsolute True if value is an absolute value and not a
|
||||||
|
* modification value
|
||||||
*/
|
*/
|
||||||
setModification(m, name, value, sentfromui) {
|
setModification(m, name, value, sentfromui, isAbsolute) {
|
||||||
if (isNaN(value)) {
|
if (isNaN(value)) {
|
||||||
// Value passed is invalid; reset it to 0
|
// Value passed is invalid; reset it to 0
|
||||||
value = 0;
|
value = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isAbsolute) {
|
||||||
|
m.setPretty(name, value, sentfromui);
|
||||||
|
} else {
|
||||||
|
// Resistance modifiers scale with the base value
|
||||||
|
if (name == 'kinres' || name == 'thermres' || name == 'causres' || name == 'explres') {
|
||||||
|
let baseValue = m.get(name, false);
|
||||||
|
value = (1 - baseValue) * value;
|
||||||
|
}
|
||||||
|
m.setModValue(name, value, sentfromui);
|
||||||
|
}
|
||||||
|
|
||||||
// Handle special cases
|
// Handle special cases
|
||||||
if (name === 'pgen') {
|
if (name === 'pgen') {
|
||||||
// Power generation
|
// Power generation
|
||||||
m.setModValue(name, value, sentfromui);
|
|
||||||
this.updatePowerGenerated();
|
this.updatePowerGenerated();
|
||||||
} else if (name === 'power') {
|
} else if (name === 'power') {
|
||||||
// Power usage
|
// Power usage
|
||||||
m.setModValue(name, value, sentfromui);
|
|
||||||
this.updatePowerUsed();
|
this.updatePowerUsed();
|
||||||
} else if (name === 'mass') {
|
} else if (name === 'mass') {
|
||||||
// Mass
|
// Mass
|
||||||
m.setModValue(name, value, sentfromui);
|
|
||||||
this.recalculateMass();
|
this.recalculateMass();
|
||||||
this.updateMovement();
|
this.updateMovement();
|
||||||
this.updateJumpStats();
|
this.updateJumpStats();
|
||||||
} else if (name === 'maxfuel') {
|
} else if (name === 'maxfuel') {
|
||||||
m.setModValue(name, value, sentfromui);
|
|
||||||
this.updateJumpStats();
|
this.updateJumpStats();
|
||||||
} else if (name === 'optmass') {
|
} else if (name === 'optmass') {
|
||||||
m.setModValue(name, value, sentfromui);
|
|
||||||
// Could be for any of thrusters, FSD or shield
|
// Could be for any of thrusters, FSD or shield
|
||||||
this.updateMovement();
|
this.updateMovement();
|
||||||
this.updateJumpStats();
|
this.updateJumpStats();
|
||||||
this.recalculateShield();
|
this.recalculateShield();
|
||||||
} else if (name === 'optmul') {
|
} else if (name === 'optmul') {
|
||||||
m.setModValue(name, value, sentfromui);
|
|
||||||
// Could be for any of thrusters, FSD or shield
|
// Could be for any of thrusters, FSD or shield
|
||||||
this.updateMovement();
|
this.updateMovement();
|
||||||
this.updateJumpStats();
|
this.updateJumpStats();
|
||||||
this.recalculateShield();
|
this.recalculateShield();
|
||||||
} else if (name === 'shieldboost') {
|
} else if (name === 'shieldboost') {
|
||||||
m.setModValue(name, value, sentfromui);
|
|
||||||
this.recalculateShield();
|
this.recalculateShield();
|
||||||
} else if (name === 'hullboost' || name === 'hullreinforcement' || name === 'modulereinforcement') {
|
} else if (name === 'hullboost' || name === 'hullreinforcement' || name === 'modulereinforcement') {
|
||||||
m.setModValue(name, value, sentfromui);
|
|
||||||
this.recalculateArmour();
|
this.recalculateArmour();
|
||||||
} else if (name === 'shieldreinforcement') {
|
} else if (name === 'shieldreinforcement') {
|
||||||
m.setModValue(name, value, sentfromui);
|
|
||||||
this.recalculateShieldCells();
|
this.recalculateShieldCells();
|
||||||
} else if (name === 'burst' || name == 'burstrof' || name === 'clip' || name === 'damage' || name === 'distdraw' || name === 'jitter' || name === 'piercing' || name === 'range' || name === 'reload' || name === 'rof' || name === 'thermload') {
|
} else if (name === 'burst' || name == 'burstrof' || name === 'clip' || name === 'damage' || name === 'distdraw' || name === 'jitter' || name === 'piercing' || name === 'range' || name === 'reload' || name === 'rof' || name === 'thermload') {
|
||||||
m.setModValue(name, value, sentfromui);
|
|
||||||
this.recalculateDps();
|
this.recalculateDps();
|
||||||
this.recalculateHps();
|
this.recalculateHps();
|
||||||
this.recalculateEps();
|
this.recalculateEps();
|
||||||
} else if (name === 'explres' || name === 'kinres' || name === 'thermres') {
|
} else if (name === 'explres' || name === 'kinres' || name === 'thermres') {
|
||||||
m.setModValue(name, value, sentfromui);
|
|
||||||
// Could be for shields or armour
|
// Could be for shields or armour
|
||||||
this.recalculateArmour();
|
this.recalculateArmour();
|
||||||
this.recalculateShield();
|
this.recalculateShield();
|
||||||
} else if (name === 'engcap') {
|
} else if (name === 'engcap') {
|
||||||
m.setModValue(name, value, sentfromui);
|
|
||||||
// Might have resulted in a change in boostability
|
// Might have resulted in a change in boostability
|
||||||
this.updateMovement();
|
this.updateMovement();
|
||||||
} else {
|
|
||||||
// Generic
|
|
||||||
m.setModValue(name, value, sentfromui);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -692,16 +700,16 @@ export default class Ship {
|
|||||||
// Update aggragated stats
|
// Update aggragated stats
|
||||||
if (comps) {
|
if (comps) {
|
||||||
this.updatePowerGenerated()
|
this.updatePowerGenerated()
|
||||||
.updatePowerUsed()
|
.updatePowerUsed()
|
||||||
.recalculateMass()
|
.recalculateMass()
|
||||||
.updateJumpStats()
|
.updateJumpStats()
|
||||||
.recalculateShield()
|
.recalculateShield()
|
||||||
.recalculateShieldCells()
|
.recalculateShieldCells()
|
||||||
.recalculateArmour()
|
.recalculateArmour()
|
||||||
.recalculateDps()
|
.recalculateDps()
|
||||||
.recalculateEps()
|
.recalculateEps()
|
||||||
.recalculateHps()
|
.recalculateHps()
|
||||||
.updateMovement();
|
.updateMovement();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.updatePowerPrioritesString().updatePowerEnabledString().updateModificationsString();
|
return this.updatePowerPrioritesString().updatePowerEnabledString().updateModificationsString();
|
||||||
@@ -928,9 +936,14 @@ export default class Ship {
|
|||||||
let epsChanged = n && n.getEps() || old && old.getEps();
|
let epsChanged = n && n.getEps() || old && old.getEps();
|
||||||
let hpsChanged = n && n.getHps() || old && old.getHps();
|
let hpsChanged = n && n.getHps() || old && old.getHps();
|
||||||
|
|
||||||
let armourChange = (slot === this.bulkheads) || (n && n.grp === 'hr') || (old && old.grp === 'hr') || (n && n.grp === 'mrp') || (old && old.grp === 'mrp');
|
let armourChange = (slot === this.bulkheads) ||
|
||||||
|
(n && n.grp === 'hr') || (old && old.grp === 'hr') ||
|
||||||
|
(n && n.grp === 'ghrp') || (old && old.grp === 'ghrp') ||
|
||||||
|
(n && n.grp == 'mahr') || (old && old.grp == 'mahr') ||
|
||||||
|
(n && n.grp === 'mrp') || (old && old.grp === 'mrp') ||
|
||||||
|
(n && n.grp === 'gmrp') || (old && old.grp == 'gmrp');
|
||||||
|
|
||||||
let shieldChange = (n && n.grp === 'bsg') || (old && old.grp === 'bsg') || (n && n.grp === 'psg') || (old && old.grp === 'psg') || (n && n.grp === 'sg') || (old && old.grp === 'sg') || (n && n.grp === 'sb') || (old && old.grp === 'sb');
|
let shieldChange = (n && n.grp === 'bsg') || (old && old.grp === 'bsg') || (n && n.grp === 'psg') || (old && old.grp === 'psg') || (n && n.grp === 'sg') || (old && old.grp === 'sg') || (n && n.grp === 'sb') || (old && old.grp === 'sb') || (old && old.grp === 'gsrp') || (n && n.grp === 'gsrp');
|
||||||
|
|
||||||
let shieldCellsChange = (n && n.grp === 'scb') || (old && old.grp === 'scb');
|
let shieldCellsChange = (n && n.grp === 'scb') || (old && old.grp === 'scb');
|
||||||
|
|
||||||
@@ -987,25 +1000,6 @@ export default class Ship {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate diminishing returns value, where values below a given limit are returned
|
|
||||||
* as-is, and values between the lower and upper limit of the diminishing returns are
|
|
||||||
* given at half value.
|
|
||||||
* Commonly used for resistances.
|
|
||||||
* @param {Number} val The value
|
|
||||||
* @param {Number} drll The lower limit for diminishing returns
|
|
||||||
* @param {Number} drul The upper limit for diminishing returns
|
|
||||||
* @return {this} The ship instance (for chaining operations)
|
|
||||||
*/
|
|
||||||
diminishingReturns(val, drll, drul) {
|
|
||||||
if (val < drll) {
|
|
||||||
val = drll;
|
|
||||||
} else if (val < drul) {
|
|
||||||
val = drul - (drul - val) / 2;
|
|
||||||
}
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate damage per second and related items for weapons
|
* Calculate damage per second and related items for weapons
|
||||||
* @return {this} The ship instance (for chaining operations)
|
* @return {this} The ship instance (for chaining operations)
|
||||||
@@ -1192,38 +1186,35 @@ export default class Ship {
|
|||||||
|
|
||||||
unladenMass += this.bulkheads.m.getMass();
|
unladenMass += this.bulkheads.m.getMass();
|
||||||
|
|
||||||
for (let slotNum in this.standard) {
|
let slots = this.standard.concat(this.internal, this.hardpoints);
|
||||||
const slot = this.standard[slotNum];
|
// TODO: create class for slot and also add slot.get
|
||||||
if (slot.m) {
|
// handle unladen mass
|
||||||
unladenMass += slot.m.getMass();
|
unladenMass += chain(slots)
|
||||||
if (slot.m.grp === 'ft') {
|
.map(slot => slot.m ? slot.m.get('mass') : null)
|
||||||
fuelCapacity += slot.m.fuel;
|
.map(mass => mass || 0)
|
||||||
}
|
.reduce((sum, mass) => sum + mass)
|
||||||
}
|
.value();
|
||||||
}
|
|
||||||
|
|
||||||
for (let slotNum in this.internal) {
|
// handle fuel capacity
|
||||||
const slot = this.internal[slotNum];
|
fuelCapacity += chain(slots)
|
||||||
if (slot.m) {
|
.map(slot => slot.m ? slot.m.get('fuel') : null)
|
||||||
unladenMass += slot.m.getMass();
|
.map(fuel => fuel || 0)
|
||||||
if (slot.m.grp === 'ft') {
|
.reduce((sum, fuel) => sum + fuel)
|
||||||
fuelCapacity += slot.m.fuel;
|
.value();
|
||||||
} else if (slot.m.grp === 'cr') {
|
|
||||||
cargoCapacity += slot.m.cargo;
|
|
||||||
} else if (slot.m.grp.slice(0,2) === 'pc') {
|
|
||||||
if (slot.m.passengers) {
|
|
||||||
passengerCapacity += slot.m.passengers;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let slotNum in this.hardpoints) {
|
// handle cargo capacity
|
||||||
const slot = this.hardpoints[slotNum];
|
cargoCapacity += chain(slots)
|
||||||
if (slot.m) {
|
.map(slot => slot.m ? slot.m.get('cargo') : null)
|
||||||
unladenMass += slot.m.getMass();
|
.map(cargo => cargo || 0)
|
||||||
}
|
.reduce((sum, cargo) => sum + cargo)
|
||||||
}
|
.value();
|
||||||
|
|
||||||
|
// handle passenger capacity
|
||||||
|
passengerCapacity += chain(slots)
|
||||||
|
.map(slot => slot.m ? slot.m.get('passengers') : null)
|
||||||
|
.map(passengers => passengers || 0)
|
||||||
|
.reduce((sum, passengers) => sum + passengers)
|
||||||
|
.value();
|
||||||
|
|
||||||
// Update global stats
|
// Update global stats
|
||||||
this.unladenMass = unladenMass;
|
this.unladenMass = unladenMass;
|
||||||
@@ -1264,7 +1255,7 @@ export default class Ship {
|
|||||||
// Obtain shield metrics with 0 pips to sys (parts affected by SYS aren't used here)
|
// Obtain shield metrics with 0 pips to sys (parts affected by SYS aren't used here)
|
||||||
const metrics = Calc.shieldMetrics(this, 0);
|
const metrics = Calc.shieldMetrics(this, 0);
|
||||||
|
|
||||||
this.shield = metrics.generator ? metrics.generator + metrics.boosters : 0;
|
this.shield = metrics.generator ? metrics.generator + metrics.boosters + metrics.addition : 0;
|
||||||
this.shieldExplRes = this.shield > 0 ? 1 - metrics.explosive.total : null;
|
this.shieldExplRes = this.shield > 0 ? 1 - metrics.explosive.total : null;
|
||||||
this.shieldKinRes = this.shield > 0 ? 1 - metrics.kinetic.total : null;
|
this.shieldKinRes = this.shield > 0 ? 1 - metrics.kinetic.total : null;
|
||||||
this.shieldThermRes = this.shield > 0 ? 1 - metrics.thermal.total : null;
|
this.shieldThermRes = this.shield > 0 ? 1 - metrics.thermal.total : null;
|
||||||
@@ -1297,45 +1288,15 @@ export default class Ship {
|
|||||||
*/
|
*/
|
||||||
recalculateArmour() {
|
recalculateArmour() {
|
||||||
// Armour from bulkheads
|
// Armour from bulkheads
|
||||||
let bulkhead = this.bulkheads.m;
|
let metrics = Calc.armourMetrics(this);
|
||||||
let armour = this.baseArmour + (this.baseArmour * bulkhead.getHullBoost());
|
|
||||||
let modulearmour = 0;
|
|
||||||
let moduleprotection = 1;
|
|
||||||
let hullExplRes = 1 - bulkhead.getExplosiveResistance();
|
|
||||||
const hullExplResDRStart = hullExplRes * 0.7;
|
|
||||||
const hullExplResDREnd = hullExplRes * 0;
|
|
||||||
let hullKinRes = 1 - bulkhead.getKineticResistance();
|
|
||||||
const hullKinResDRStart = hullKinRes * 0.7;
|
|
||||||
const hullKinResDREnd = hullKinRes * 0;
|
|
||||||
let hullThermRes = 1 - bulkhead.getThermalResistance();
|
|
||||||
const hullThermResDRStart = hullThermRes * 0.7;
|
|
||||||
const hullThermResDREnd = hullThermRes * 0;
|
|
||||||
|
|
||||||
// Armour from HRPs and module armour from MRPs
|
|
||||||
for (let slot of this.internal) {
|
|
||||||
if (slot.m && slot.m.grp == 'hr') {
|
|
||||||
armour += slot.m.getHullReinforcement();
|
|
||||||
// Hull boost for HRPs is applied against the ship's base armour
|
|
||||||
armour += this.baseArmour * slot.m.getModValue('hullboost') / 10000;
|
|
||||||
|
|
||||||
hullExplRes *= (1 - slot.m.getExplosiveResistance());
|
|
||||||
hullKinRes *= (1 - slot.m.getKineticResistance());
|
|
||||||
hullThermRes *= (1 - slot.m.getThermalResistance());
|
|
||||||
}
|
|
||||||
if (slot.m && slot.m.grp == 'mrp') {
|
|
||||||
modulearmour += slot.m.getIntegrity();
|
|
||||||
moduleprotection = moduleprotection * (1 - slot.m.getProtection());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
moduleprotection = 1 - moduleprotection;
|
|
||||||
|
|
||||||
this.armour = armour;
|
|
||||||
this.modulearmour = modulearmour;
|
|
||||||
this.moduleprotection = moduleprotection;
|
|
||||||
this.hullExplRes = 1 - this.diminishingReturns(hullExplRes, hullExplResDREnd, hullExplResDRStart);
|
|
||||||
this.hullKinRes = 1 - this.diminishingReturns(hullKinRes, hullKinResDREnd, hullKinResDRStart);
|
|
||||||
this.hullThermRes = 1 - this.diminishingReturns(hullThermRes, hullThermResDREnd, hullThermResDRStart);
|
|
||||||
|
|
||||||
|
this.armour = metrics.total ? metrics.total : 0;
|
||||||
|
this.modulearmour = metrics.modulearmour;
|
||||||
|
this.moduleprotection = metrics.moduleprotection;
|
||||||
|
this.hullExplRes = 1 - metrics.explosive.total;
|
||||||
|
this.hullKinRes = 1 - metrics.kinetic.total;
|
||||||
|
this.hullThermRes = 1 - metrics.thermal.total;
|
||||||
|
this.hullCausRes = 1 - metrics.caustic.total;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1347,10 +1308,10 @@ export default class Ship {
|
|||||||
let fsd = this.standard[2].m; // Frame Shift Drive;
|
let fsd = this.standard[2].m; // Frame Shift Drive;
|
||||||
let { unladenMass, fuelCapacity } = this;
|
let { unladenMass, fuelCapacity } = this;
|
||||||
this.unladenRange = this.calcUnladenRange(); // Includes fuel weight for jump
|
this.unladenRange = this.calcUnladenRange(); // Includes fuel weight for jump
|
||||||
this.fullTankRange = Calc.jumpRange(unladenMass + fuelCapacity, fsd); // Full Tank
|
this.fullTankRange = Calc.jumpRange(unladenMass + fuelCapacity, fsd, fuelCapacity, this); // Full Tank
|
||||||
this.ladenRange = this.calcLadenRange(); // Includes full tank and caro
|
this.ladenRange = this.calcLadenRange(); // Includes full tank and caro
|
||||||
this.unladenFastestRange = Calc.totalJumpRange(unladenMass + this.fuelCapacity, fsd, fuelCapacity);
|
this.unladenFastestRange = Calc.totalJumpRange(unladenMass + this.fuelCapacity, fsd, fuelCapacity, this);
|
||||||
this.ladenFastestRange = Calc.totalJumpRange(unladenMass + this.fuelCapacity + this.cargoCapacity, fsd, fuelCapacity);
|
this.ladenFastestRange = Calc.totalJumpRange(unladenMass + this.fuelCapacity + this.cargoCapacity, fsd, fuelCapacity, this);
|
||||||
this.maxJumpCount = Math.ceil(fuelCapacity / fsd.getMaxFuelPerJump());
|
this.maxJumpCount = Math.ceil(fuelCapacity / fsd.getMaxFuelPerJump());
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -1545,7 +1506,7 @@ export default class Ship {
|
|||||||
} else {
|
} else {
|
||||||
buffer.writeInt32LE(slotMod.value, curpos);
|
buffer.writeInt32LE(slotMod.value, curpos);
|
||||||
}
|
}
|
||||||
// const modification = _.find(Modifications.modifications, function(o) { return o.id === slotMod.id; });
|
const modification = _.find(Modifications.modifications, function(o) { return o.id === slotMod.id; });
|
||||||
// console.log('ENCODE Slot ' + i + ': ' + modification.name + ' = ' + slotMod.value);
|
// console.log('ENCODE Slot ' + i + ': ' + modification.name + ' = ' + slotMod.value);
|
||||||
curpos += 4;
|
curpos += 4;
|
||||||
}
|
}
|
||||||
@@ -1558,6 +1519,7 @@ export default class Ship {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.serialized.modifications = zlib.gzipSync(buffer).toString('base64');
|
this.serialized.modifications = zlib.gzipSync(buffer).toString('base64');
|
||||||
|
// console.log(this.serialized.modifications)
|
||||||
} else {
|
} else {
|
||||||
this.serialized.modifications = null;
|
this.serialized.modifications = null;
|
||||||
}
|
}
|
||||||
@@ -1726,11 +1688,11 @@ export default class Ship {
|
|||||||
updated;
|
updated;
|
||||||
|
|
||||||
this.useBulkhead(0)
|
this.useBulkhead(0)
|
||||||
.use(standard[2], fsd) // FSD
|
.use(standard[2], fsd) // FSD
|
||||||
.use(standard[3], ls) // Life Support
|
.use(standard[3], ls) // Life Support
|
||||||
.use(standard[5], s) // Sensors
|
.use(standard[5], s) // Sensors
|
||||||
.use(standard[4], pd) // Power Distributor
|
.use(standard[4], pd) // Power Distributor
|
||||||
.use(standard[6], ft); // Fuel Tank
|
.use(standard[6], ft); // Fuel Tank
|
||||||
|
|
||||||
// Turn off nearly everything
|
// Turn off nearly everything
|
||||||
if (m.fsdDisabled) this.setSlotEnabled(this.standard[2], false);
|
if (m.fsdDisabled) this.setSlotEnabled(this.standard[2], false);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as ModuleUtils from './ModuleUtils'
|
import * as ModuleUtils from './ModuleUtils';
|
||||||
import { canMount } from '../utils/SlotFunctions'
|
import { canMount } from '../utils/SlotFunctions';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Standard / typical role for multi-purpose or combat (if shielded with better bulkheads)
|
* Standard / typical role for multi-purpose or combat (if shielded with better bulkheads)
|
||||||
@@ -7,20 +7,20 @@ import { canMount } from '../utils/SlotFunctions'
|
|||||||
* @param {Boolean} shielded True if shield generator should be included
|
* @param {Boolean} shielded True if shield generator should be included
|
||||||
* @param {integer} bulkheadIndex Bulkhead to use see Constants.BulkheadNames
|
* @param {integer} bulkheadIndex Bulkhead to use see Constants.BulkheadNames
|
||||||
*/
|
*/
|
||||||
export function multiPurpose (ship, shielded, bulkheadIndex) {
|
export function multiPurpose(ship, shielded, bulkheadIndex) {
|
||||||
ship.useStandard('A')
|
ship.useStandard('A')
|
||||||
.use(ship.standard[3], ModuleUtils.standard(3, ship.standard[3].maxClass + 'D')) // D Life Support
|
.use(ship.standard[3], ModuleUtils.standard(3, ship.standard[3].maxClass + 'D')) // D Life Support
|
||||||
.use(ship.standard[5], ModuleUtils.standard(5, ship.standard[5].maxClass + 'D')) // D Sensors
|
.use(ship.standard[5], ModuleUtils.standard(5, ship.standard[5].maxClass + 'D')) // D Sensors
|
||||||
.useBulkhead(bulkheadIndex)
|
.useBulkhead(bulkheadIndex);
|
||||||
|
|
||||||
if (shielded) {
|
if (shielded) {
|
||||||
ship.internal.some(function (slot) {
|
ship.internal.some(function(slot) {
|
||||||
if (canMount(ship, slot, 'sg')) { // Assuming largest slot can hold an eligible shield
|
if (canMount(ship, slot, 'sg')) { // Assuming largest slot can hold an eligible shield
|
||||||
ship.use(slot, ModuleUtils.findInternal('sg', slot.maxClass, 'A'))
|
ship.use(slot, ModuleUtils.findInternal('sg', slot.maxClass, 'A'));
|
||||||
ship.setSlotEnabled(slot, true)
|
ship.setSlotEnabled(slot, true);
|
||||||
return true
|
return true;
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,52 +30,51 @@ export function multiPurpose (ship, shielded, bulkheadIndex) {
|
|||||||
* @param {Boolean} shielded True if shield generator should be included
|
* @param {Boolean} shielded True if shield generator should be included
|
||||||
* @param {Object} standardOpts [Optional] Standard module optional overrides
|
* @param {Object} standardOpts [Optional] Standard module optional overrides
|
||||||
*/
|
*/
|
||||||
export function trader (ship, shielded, standardOpts) {
|
export function trader(ship, shielded, standardOpts) {
|
||||||
let usedSlots = []
|
let usedSlots = [];
|
||||||
let bstCount = 2
|
let bstCount = 2;
|
||||||
let sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass)
|
let sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass);
|
||||||
ship.useStandard('A')
|
ship.useStandard('A')
|
||||||
.use(ship.standard[3], ModuleUtils.standard(3, ship.standard[3].maxClass + 'D')) // D Life Support
|
.use(ship.standard[3], ModuleUtils.standard(3, ship.standard[3].maxClass + 'D')) // D Life Support
|
||||||
.use(ship.standard[1], ModuleUtils.standard(1, ship.standard[1].maxClass + 'D')) // D Life Support
|
.use(ship.standard[1], ModuleUtils.standard(1, ship.standard[1].maxClass + 'D')) // D Life Support
|
||||||
.use(ship.standard[4], ModuleUtils.standard(4, ship.standard[4].maxClass + 'D')) // D Life Support
|
.use(ship.standard[4], ModuleUtils.standard(4, ship.standard[4].maxClass + 'D')) // D Life Support
|
||||||
.use(ship.standard[5], ModuleUtils.standard(5, ship.standard[5].maxClass + 'D')) // D Sensors
|
.use(ship.standard[5], ModuleUtils.standard(5, ship.standard[5].maxClass + 'D')); // D Sensors
|
||||||
|
|
||||||
const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8]
|
const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8];
|
||||||
const shieldInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
const shieldInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
||||||
.filter(a => (!a.eligible) || a.eligible.sg)
|
.filter(a => (!a.eligible) || a.eligible.sg)
|
||||||
.filter(a => a.maxClass >= sg.class)
|
.filter(a => a.maxClass >= sg.class)
|
||||||
.sort((a, b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass))
|
.sort((a, b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass));
|
||||||
shieldInternals.some(function (slot) {
|
shieldInternals.some(function(slot) {
|
||||||
if (canMount(ship, slot, 'sg')) { // Assuming largest slot can hold an eligible shield
|
if (canMount(ship, slot, 'sg')) { // Assuming largest slot can hold an eligible shield
|
||||||
const shield = ModuleUtils.findInternal('sg', slot.maxClass, 'A')
|
const shield = ModuleUtils.findInternal('sg', slot.maxClass, 'A');
|
||||||
if (shield && shield.maxmass > ship.hullMass) {
|
if (shield && shield.maxmass > ship.hullMass) {
|
||||||
console.log(shield)
|
ship.use(slot, shield);
|
||||||
ship.use(slot, shield)
|
ship.setSlotEnabled(slot, true);
|
||||||
ship.setSlotEnabled(slot, true)
|
usedSlots.push(slot);
|
||||||
usedSlots.push(slot)
|
return true;
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
// Fill the empty internals with cargo racks
|
// Fill the empty internals with cargo racks
|
||||||
for (let i = ship.internal.length; i--;) {
|
for (let i = ship.internal.length; i--;) {
|
||||||
let slot = ship.internal[i]
|
let slot = ship.internal[i];
|
||||||
if (usedSlots.indexOf(slot) == -1 && canMount(ship, slot, 'cr')) {
|
if (usedSlots.indexOf(slot) == -1 && canMount(ship, slot, 'cr')) {
|
||||||
ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E'))
|
ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Empty the hardpoints
|
// Empty the hardpoints
|
||||||
for (let s of ship.hardpoints) {
|
for (let s of ship.hardpoints) {
|
||||||
ship.use(s, null)
|
ship.use(s, null);
|
||||||
}
|
}
|
||||||
for (let s of ship.hardpoints) {
|
for (let s of ship.hardpoints) {
|
||||||
if (s.maxClass == 0 && bstCount) { // Mount up to 2 boosters
|
if (s.maxClass == 0 && bstCount) { // Mount up to 2 boosters
|
||||||
ship.use(s, ModuleUtils.hardpoints('04'))
|
ship.use(s, ModuleUtils.hardpoints('04'));
|
||||||
bstCount--
|
bstCount--;
|
||||||
} else {
|
} else {
|
||||||
ship.use(s, null)
|
ship.use(s, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ship.useLightestStandard(standardOpts);
|
// ship.useLightestStandard(standardOpts);
|
||||||
@@ -86,127 +85,127 @@ export function trader (ship, shielded, standardOpts) {
|
|||||||
* @param {Ship} ship Ship instance
|
* @param {Ship} ship Ship instance
|
||||||
* @param {Boolean} planetary True if Planetary Vehicle Hangar (PVH) should be included
|
* @param {Boolean} planetary True if Planetary Vehicle Hangar (PVH) should be included
|
||||||
*/
|
*/
|
||||||
export function explorer (ship, planetary) {
|
export function explorer(ship, planetary) {
|
||||||
let standardOpts = {ppRating: 'A'},
|
let standardOpts = { ppRating: 'A' },
|
||||||
heatSinkCount = 2, // Fit 2 heat sinks if possible
|
heatSinkCount = 2, // Fit 2 heat sinks if possible
|
||||||
usedSlots = [],
|
usedSlots = [],
|
||||||
sgSlot,
|
sgSlot,
|
||||||
fuelScoopSlot,
|
fuelScoopSlot,
|
||||||
sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass)
|
sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass);
|
||||||
|
|
||||||
if (!planetary) { // Non-planetary explorers don't really need to boost
|
if (!planetary) { // Non-planetary explorers don't really need to boost
|
||||||
standardOpts.pd = '1D'
|
standardOpts.pd = '1D';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cargo hatch can be disabled
|
// Cargo hatch can be disabled
|
||||||
ship.setSlotEnabled(ship.cargoHatch, false)
|
ship.setSlotEnabled(ship.cargoHatch, false);
|
||||||
|
|
||||||
// Advanced Discovery Scanner - class 1 or higher
|
// Advanced Discovery Scanner - class 1 or higher
|
||||||
const adsOrder = [1, 2, 3, 4, 5, 6, 7, 8]
|
const adsOrder = [1, 2, 3, 4, 5, 6, 7, 8];
|
||||||
const adsInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
const adsInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
||||||
.filter(a => (!a.eligible) || a.eligible.sc)
|
.filter(a => (!a.eligible) || a.eligible.sc)
|
||||||
.sort((a, b) => adsOrder.indexOf(a.maxClass) - adsOrder.indexOf(b.maxClass))
|
.sort((a, b) => adsOrder.indexOf(a.maxClass) - adsOrder.indexOf(b.maxClass));
|
||||||
for (let i = 0; i < adsInternals.length; i++) {
|
for (let i = 0; i < adsInternals.length; i++) {
|
||||||
if (canMount(ship, adsInternals[i], 'sc')) {
|
if (canMount(ship, adsInternals[i], 'sc')) {
|
||||||
ship.use(adsInternals[i], ModuleUtils.internal('2f'))
|
ship.use(adsInternals[i], ModuleUtils.internal('2f'));
|
||||||
usedSlots.push(adsInternals[i])
|
usedSlots.push(adsInternals[i]);
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (planetary) {
|
if (planetary) {
|
||||||
// Planetary Vehicle Hangar - class 2 or higher
|
// Planetary Vehicle Hangar - class 2 or higher
|
||||||
const pvhOrder = [2, 3, 4, 5, 6, 7, 8, 1]
|
const pvhOrder = [2, 3, 4, 5, 6, 7, 8, 1];
|
||||||
const pvhInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
const pvhInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
||||||
.filter(a => (!a.eligible) || a.eligible.pv)
|
.filter(a => (!a.eligible) || a.eligible.pv)
|
||||||
.sort((a, b) => pvhOrder.indexOf(a.maxClass) - pvhOrder.indexOf(b.maxClass))
|
.sort((a, b) => pvhOrder.indexOf(a.maxClass) - pvhOrder.indexOf(b.maxClass));
|
||||||
for (let i = 0; i < pvhInternals.length; i++) {
|
for (let i = 0; i < pvhInternals.length; i++) {
|
||||||
if (canMount(ship, pvhInternals[i], 'pv')) {
|
if (canMount(ship, pvhInternals[i], 'pv')) {
|
||||||
// Planetary Vehical Hangar only has even classes
|
// Planetary Vehical Hangar only has even classes
|
||||||
const pvhClass = pvhInternals[i].maxClass % 2 === 1 ? pvhInternals[i].maxClass - 1 : pvhInternals[i].maxClass
|
const pvhClass = pvhInternals[i].maxClass % 2 === 1 ? pvhInternals[i].maxClass - 1 : pvhInternals[i].maxClass;
|
||||||
ship.use(pvhInternals[i], ModuleUtils.findInternal('pv', pvhClass, 'G')) // G is lower mass
|
ship.use(pvhInternals[i], ModuleUtils.findInternal('pv', pvhClass, 'G')); // G is lower mass
|
||||||
ship.setSlotEnabled(pvhInternals[i], false) // Disable power for Planetary Vehical Hangar
|
ship.setSlotEnabled(pvhInternals[i], false); // Disable power for Planetary Vehical Hangar
|
||||||
usedSlots.push(pvhInternals[i])
|
usedSlots.push(pvhInternals[i]);
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shield generator
|
// Shield generator
|
||||||
const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8]
|
const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8];
|
||||||
const shieldInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
const shieldInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
||||||
.filter(a => (!a.eligible) || a.eligible.sg)
|
.filter(a => (!a.eligible) || a.eligible.sg)
|
||||||
.filter(a => a.maxClass >= sg.class)
|
.filter(a => a.maxClass >= sg.class)
|
||||||
.sort((a, b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass))
|
.sort((a, b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass));
|
||||||
for (let i = 0; i < shieldInternals.length; i++) {
|
for (let i = 0; i < shieldInternals.length; i++) {
|
||||||
if (canMount(ship, shieldInternals[i], 'sg')) {
|
if (canMount(ship, shieldInternals[i], 'sg')) {
|
||||||
ship.use(shieldInternals[i], sg)
|
ship.use(shieldInternals[i], sg);
|
||||||
usedSlots.push(shieldInternals[i])
|
usedSlots.push(shieldInternals[i]);
|
||||||
sgSlot = shieldInternals[i]
|
sgSlot = shieldInternals[i];
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detailed Surface Scanner
|
// Detailed Surface Scanner
|
||||||
const dssOrder = [1, 2, 3, 4, 5, 6, 7, 8]
|
const dssOrder = [1, 2, 3, 4, 5, 6, 7, 8];
|
||||||
const dssInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
const dssInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
||||||
.filter(a => (!a.eligible) || a.eligible.sc)
|
.filter(a => (!a.eligible) || a.eligible.sc)
|
||||||
.sort((a, b) => dssOrder.indexOf(a.maxClass) - dssOrder.indexOf(b.maxClass))
|
.sort((a, b) => dssOrder.indexOf(a.maxClass) - dssOrder.indexOf(b.maxClass));
|
||||||
for (let i = 0; i < dssInternals.length; i++) {
|
for (let i = 0; i < dssInternals.length; i++) {
|
||||||
if (canMount(ship, dssInternals[i], 'sc')) {
|
if (canMount(ship, dssInternals[i], 'sc')) {
|
||||||
ship.use(dssInternals[i], ModuleUtils.internal('2i'))
|
ship.use(dssInternals[i], ModuleUtils.internal('2i'));
|
||||||
usedSlots.push(dssInternals[i])
|
usedSlots.push(dssInternals[i]);
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fuel scoop - best possible
|
// Fuel scoop - best possible
|
||||||
const fuelScoopOrder = [8, 7, 6, 5, 4, 3, 2, 1]
|
const fuelScoopOrder = [8, 7, 6, 5, 4, 3, 2, 1];
|
||||||
const fuelScoopInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
const fuelScoopInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
||||||
.filter(a => (!a.eligible) || a.eligible.fs)
|
.filter(a => (!a.eligible) || a.eligible.fs)
|
||||||
.sort((a, b) => fuelScoopOrder.indexOf(a.maxClass) - fuelScoopOrder.indexOf(b.maxClass))
|
.sort((a, b) => fuelScoopOrder.indexOf(a.maxClass) - fuelScoopOrder.indexOf(b.maxClass));
|
||||||
for (let i = 0; i < fuelScoopInternals.length; i++) {
|
for (let i = 0; i < fuelScoopInternals.length; i++) {
|
||||||
if (canMount(ship, fuelScoopInternals[i], 'fs')) {
|
if (canMount(ship, fuelScoopInternals[i], 'fs')) {
|
||||||
ship.use(fuelScoopInternals[i], ModuleUtils.findInternal('fs', fuelScoopInternals[i].maxClass, 'A'))
|
ship.use(fuelScoopInternals[i], ModuleUtils.findInternal('fs', fuelScoopInternals[i].maxClass, 'A'));
|
||||||
usedSlots.push(fuelScoopInternals[i])
|
usedSlots.push(fuelScoopInternals[i]);
|
||||||
fuelScoopSlot = fuelScoopInternals[i]
|
fuelScoopSlot = fuelScoopInternals[i];
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AFMUs - fill as they are 0-weight
|
// AFMUs - fill as they are 0-weight
|
||||||
const afmuOrder = [8, 7, 6, 5, 4, 3, 2, 1]
|
const afmuOrder = [8, 7, 6, 5, 4, 3, 2, 1];
|
||||||
const afmuInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
const afmuInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
||||||
.filter(a => (!a.eligible) || a.eligible.pc)
|
.filter(a => (!a.eligible) || a.eligible.pc)
|
||||||
.sort((a, b) => afmuOrder.indexOf(a.maxClass) - afmuOrder.indexOf(b.maxClass))
|
.sort((a, b) => afmuOrder.indexOf(a.maxClass) - afmuOrder.indexOf(b.maxClass));
|
||||||
for (let i = 0; i < afmuInternals.length; i++) {
|
for (let i = 0; i < afmuInternals.length; i++) {
|
||||||
if (canMount(ship, afmuInternals[i], 'am')) {
|
if (canMount(ship, afmuInternals[i], 'am')) {
|
||||||
ship.use(afmuInternals[i], ModuleUtils.findInternal('am', afmuInternals[i].maxClass, 'A'))
|
ship.use(afmuInternals[i], ModuleUtils.findInternal('am', afmuInternals[i].maxClass, 'A'));
|
||||||
usedSlots.push(afmuInternals[i])
|
usedSlots.push(afmuInternals[i]);
|
||||||
ship.setSlotEnabled(afmuInternals[i], false) // Disable power for AFM Unit
|
ship.setSlotEnabled(afmuInternals[i], false); // Disable power for AFM Unit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let s of ship.hardpoints) {
|
for (let s of ship.hardpoints) {
|
||||||
if (s.maxClass == 0 && heatSinkCount) { // Mount up to 2 heatsinks
|
if (s.maxClass == 0 && heatSinkCount) { // Mount up to 2 heatsinks
|
||||||
ship.use(s, ModuleUtils.hardpoints('02'))
|
ship.use(s, ModuleUtils.hardpoints('02'));
|
||||||
ship.setSlotEnabled(s, heatSinkCount == 2) // Only enable a single Heatsink
|
ship.setSlotEnabled(s, heatSinkCount == 2); // Only enable a single Heatsink
|
||||||
heatSinkCount--
|
heatSinkCount--;
|
||||||
} else {
|
} else {
|
||||||
ship.use(s, null)
|
ship.use(s, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sgSlot && fuelScoopSlot) {
|
if (sgSlot && fuelScoopSlot) {
|
||||||
// The SG and Fuel scoop to not need to be powered at the same time
|
// The SG and Fuel scoop to not need to be powered at the same time
|
||||||
if (sgSlot.m.getPowerUsage() > fuelScoopSlot.m.getPowerUsage()) { // The Shield generator uses the most power
|
if (sgSlot.m.getPowerUsage() > fuelScoopSlot.m.getPowerUsage()) { // The Shield generator uses the most power
|
||||||
ship.setSlotEnabled(fuelScoopSlot, false)
|
ship.setSlotEnabled(fuelScoopSlot, false);
|
||||||
} else { // The Fuel scoop uses the most power
|
} else { // The Fuel scoop uses the most power
|
||||||
ship.setSlotEnabled(sgSlot, false)
|
ship.setSlotEnabled(sgSlot, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ship.useLightestStandard(standardOpts)
|
ship.useLightestStandard(standardOpts);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -214,188 +213,188 @@ export function explorer (ship, planetary) {
|
|||||||
* @param {Ship} ship Ship instance
|
* @param {Ship} ship Ship instance
|
||||||
* @param {Boolean} shielded True if shield generator should be included
|
* @param {Boolean} shielded True if shield generator should be included
|
||||||
*/
|
*/
|
||||||
export function miner (ship, shielded) {
|
export function miner(ship, shielded) {
|
||||||
shielded = true
|
shielded = true;
|
||||||
let standardOpts = {ppRating: 'A'},
|
let standardOpts = { ppRating: 'A' },
|
||||||
miningLaserCount = 2,
|
miningLaserCount = 2,
|
||||||
usedSlots = [],
|
usedSlots = [],
|
||||||
sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass)
|
sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass);
|
||||||
|
|
||||||
// Cargo hatch should be enabled
|
// Cargo hatch should be enabled
|
||||||
ship.setSlotEnabled(ship.cargoHatch, true)
|
ship.setSlotEnabled(ship.cargoHatch, true);
|
||||||
|
|
||||||
// Largest possible refinery
|
// Largest possible refinery
|
||||||
const refineryOrder = [4, 5, 6, 7, 8, 3, 2, 1]
|
const refineryOrder = [4, 5, 6, 7, 8, 3, 2, 1];
|
||||||
const refineryInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
const refineryInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
||||||
.filter(a => (!a.eligible) || a.eligible.rf)
|
.filter(a => (!a.eligible) || a.eligible.rf)
|
||||||
.sort((a, b) => refineryOrder.indexOf(a.maxClass) - refineryOrder.indexOf(b.maxClass))
|
.sort((a, b) => refineryOrder.indexOf(a.maxClass) - refineryOrder.indexOf(b.maxClass));
|
||||||
for (let i = 0; i < refineryInternals.length; i++) {
|
for (let i = 0; i < refineryInternals.length; i++) {
|
||||||
if (canMount(ship, refineryInternals[i], 'rf')) {
|
if (canMount(ship, refineryInternals[i], 'rf')) {
|
||||||
ship.use(refineryInternals[i], ModuleUtils.findInternal('rf', Math.min(refineryInternals[i].maxClass, 4), 'A'))
|
ship.use(refineryInternals[i], ModuleUtils.findInternal('rf', Math.min(refineryInternals[i].maxClass, 4), 'A'));
|
||||||
usedSlots.push(refineryInternals[i])
|
usedSlots.push(refineryInternals[i]);
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prospector limpet controller - 3A if possible
|
// Prospector limpet controller - 3A if possible
|
||||||
const prospectorOrder = [3, 4, 5, 6, 7, 8, 2, 1]
|
const prospectorOrder = [3, 4, 5, 6, 7, 8, 2, 1];
|
||||||
const prospectorInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
const prospectorInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
||||||
.filter(a => (!a.eligible) || a.eligible.pc)
|
.filter(a => (!a.eligible) || a.eligible.pc)
|
||||||
.sort((a, b) => prospectorOrder.indexOf(a.maxClass) - prospectorOrder.indexOf(b.maxClass))
|
.sort((a, b) => prospectorOrder.indexOf(a.maxClass) - prospectorOrder.indexOf(b.maxClass));
|
||||||
for (let i = 0; i < prospectorInternals.length; i++) {
|
for (let i = 0; i < prospectorInternals.length; i++) {
|
||||||
if (canMount(ship, prospectorInternals[i], 'pc')) {
|
if (canMount(ship, prospectorInternals[i], 'pc')) {
|
||||||
// Prospector only has odd classes
|
// Prospector only has odd classes
|
||||||
const prospectorClass = prospectorInternals[i].maxClass % 2 === 0 ? prospectorInternals[i].maxClass - 1 : prospectorInternals[i].maxClass
|
const prospectorClass = prospectorInternals[i].maxClass % 2 === 0 ? prospectorInternals[i].maxClass - 1 : prospectorInternals[i].maxClass;
|
||||||
ship.use(prospectorInternals[i], ModuleUtils.findInternal('pc', prospectorClass, 'A'))
|
ship.use(prospectorInternals[i], ModuleUtils.findInternal('pc', prospectorClass, 'A'));
|
||||||
usedSlots.push(prospectorInternals[i])
|
usedSlots.push(prospectorInternals[i]);
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shield generator if required
|
// Shield generator if required
|
||||||
if (shielded) {
|
if (shielded) {
|
||||||
const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8]
|
const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8];
|
||||||
const shieldInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
const shieldInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
||||||
.filter(a => (!a.eligible) || a.eligible.sg)
|
.filter(a => (!a.eligible) || a.eligible.sg)
|
||||||
.filter(a => a.maxClass >= sg.class)
|
.filter(a => a.maxClass >= sg.class)
|
||||||
.sort((a, b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass))
|
.sort((a, b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass));
|
||||||
for (let i = 0; i < shieldInternals.length; i++) {
|
for (let i = 0; i < shieldInternals.length; i++) {
|
||||||
if (canMount(ship, shieldInternals[i], 'sg')) {
|
if (canMount(ship, shieldInternals[i], 'sg')) {
|
||||||
ship.use(shieldInternals[i], sg)
|
ship.use(shieldInternals[i], sg);
|
||||||
usedSlots.push(shieldInternals[i])
|
usedSlots.push(shieldInternals[i]);
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dual mining lasers of highest possible class; remove anything else
|
// Dual mining lasers of highest possible class; remove anything else
|
||||||
const miningLaserOrder = [2, 3, 4, 1, 0]
|
const miningLaserOrder = [2, 3, 4, 1, 0];
|
||||||
const miningLaserHardpoints = ship.hardpoints.concat().sort(function (a, b) {
|
const miningLaserHardpoints = ship.hardpoints.concat().sort(function(a, b) {
|
||||||
return miningLaserOrder.indexOf(a.maxClass) - miningLaserOrder.indexOf(b.maxClass)
|
return miningLaserOrder.indexOf(a.maxClass) - miningLaserOrder.indexOf(b.maxClass);
|
||||||
})
|
});
|
||||||
for (let s of miningLaserHardpoints) {
|
for (let s of miningLaserHardpoints) {
|
||||||
if (s.maxClass >= 1 && miningLaserCount) {
|
if (s.maxClass >= 1 && miningLaserCount) {
|
||||||
ship.use(s, ModuleUtils.hardpoints(s.maxClass >= 2 ? '2m' : '2l'))
|
ship.use(s, ModuleUtils.hardpoints(s.maxClass >= 2 ? '2m' : '2l'));
|
||||||
miningLaserCount--
|
miningLaserCount--;
|
||||||
} else {
|
} else {
|
||||||
ship.use(s, null)
|
ship.use(s, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Number of collector limpets required to be active is a function of the size of the ship and the power of the lasers
|
// Number of collector limpets required to be active is a function of the size of the ship and the power of the lasers
|
||||||
const miningLaserDps = ship.hardpoints.filter(h => h.m != null)
|
const miningLaserDps = ship.hardpoints.filter(h => h.m != null)
|
||||||
.reduce(function (a, b) {
|
.reduce(function(a, b) {
|
||||||
return a + b.m.getDps()
|
return a + b.m.getDps();
|
||||||
}, 0)
|
}, 0);
|
||||||
// Find out how many internal slots we have, and their potential cargo size
|
// Find out how many internal slots we have, and their potential cargo size
|
||||||
const potentialCargo = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
const potentialCargo = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
||||||
.filter(a => (!a.eligible) || a.eligible.cr)
|
.filter(a => (!a.eligible) || a.eligible.cr)
|
||||||
.map(b => Math.pow(2, b.maxClass))
|
.map(b => Math.pow(2, b.maxClass));
|
||||||
// One collector for each 1.25 DPS, multiply by 1.25 for medium ships and 1.5 for large ships as they have further to travel
|
// One collector for each 1.25 DPS, multiply by 1.25 for medium ships and 1.5 for large ships as they have further to travel
|
||||||
// 0 if we only have 1 cargo slot, otherwise minium of 1 and maximum of 6 (excluding size modifier)
|
// 0 if we only have 1 cargo slot, otherwise minium of 1 and maximum of 6 (excluding size modifier)
|
||||||
const sizeModifier = ship.class == 2 ? 1.2 : ship.class == 3 ? 1.5 : 1
|
const sizeModifier = ship.class == 2 ? 1.2 : ship.class == 3 ? 1.5 : 1;
|
||||||
let collectorLimpetsRequired = potentialCargo.length == 1 ? 0 : Math.ceil(sizeModifier * Math.min(6, Math.floor(miningLaserDps / 1.25)))
|
let collectorLimpetsRequired = potentialCargo.length == 1 ? 0 : Math.ceil(sizeModifier * Math.min(6, Math.floor(miningLaserDps / 1.25)));
|
||||||
|
|
||||||
if (collectorLimpetsRequired > 0) {
|
if (collectorLimpetsRequired > 0) {
|
||||||
const collectorOrder = [1, 2, 3, 4, 5, 6, 7, 8]
|
const collectorOrder = [1, 2, 3, 4, 5, 6, 7, 8];
|
||||||
const collectorInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
const collectorInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
||||||
.filter(a => (!a.eligible) || a.eligible.cc)
|
.filter(a => (!a.eligible) || a.eligible.cc)
|
||||||
.sort((a, b) => collectorOrder.indexOf(a.maxClass) - collectorOrder.indexOf(b.maxClass))
|
.sort((a, b) => collectorOrder.indexOf(a.maxClass) - collectorOrder.indexOf(b.maxClass));
|
||||||
// Always keep at least 2 slots free for cargo racks (1 for shielded)
|
// Always keep at least 2 slots free for cargo racks (1 for shielded)
|
||||||
for (let i = 0; i < collectorInternals.length - (shielded ? 1 : 2) && collectorLimpetsRequired > 0; i++) {
|
for (let i = 0; i < collectorInternals.length - (shielded ? 1 : 2) && collectorLimpetsRequired > 0; i++) {
|
||||||
if (canMount(ship, collectorInternals[i], 'cc')) {
|
if (canMount(ship, collectorInternals[i], 'cc')) {
|
||||||
// Collector only has odd classes
|
// Collector only has odd classes
|
||||||
const collectorClass = collectorInternals[i].maxClass % 2 === 0 ? collectorInternals[i].maxClass - 1 : collectorInternals[i].maxClass
|
const collectorClass = collectorInternals[i].maxClass % 2 === 0 ? collectorInternals[i].maxClass - 1 : collectorInternals[i].maxClass;
|
||||||
ship.use(collectorInternals[i], ModuleUtils.findInternal('cc', collectorClass, 'D'))
|
ship.use(collectorInternals[i], ModuleUtils.findInternal('cc', collectorClass, 'D'));
|
||||||
usedSlots.push(collectorInternals[i])
|
usedSlots.push(collectorInternals[i]);
|
||||||
collectorLimpetsRequired -= collectorInternals[i].m.maximum
|
collectorLimpetsRequired -= collectorInternals[i].m.maximum;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Power distributor to power the mining lasers indefinitely
|
// Power distributor to power the mining lasers indefinitely
|
||||||
const wepRateRequired = ship.hardpoints.filter(h => h.m != null)
|
const wepRateRequired = ship.hardpoints.filter(h => h.m != null)
|
||||||
.reduce(function (a, b) {
|
.reduce(function(a, b) {
|
||||||
return a + b.m.getEps()
|
return a + b.m.getEps();
|
||||||
}, 0)
|
}, 0);
|
||||||
standardOpts.pd = ship.getAvailableModules().matchingPowerDist({weprate: wepRateRequired}).id
|
standardOpts.pd = ship.getAvailableModules().matchingPowerDist({ weprate: wepRateRequired }).id;
|
||||||
|
|
||||||
// Fill the empty internals with cargo racks
|
// Fill the empty internals with cargo racks
|
||||||
for (let i = ship.internal.length; i--;) {
|
for (let i = ship.internal.length; i--;) {
|
||||||
let slot = ship.internal[i]
|
let slot = ship.internal[i];
|
||||||
if (usedSlots.indexOf(slot) == -1 && canMount(ship, slot, 'cr')) {
|
if (usedSlots.indexOf(slot) == -1 && canMount(ship, slot, 'cr')) {
|
||||||
ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E'))
|
ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ship.useLightestStandard(standardOpts)
|
ship.useLightestStandard(standardOpts);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Racer Role
|
* Racer Role
|
||||||
* @param {Ship} ship Ship instance
|
* @param {Ship} ship Ship instance
|
||||||
*/
|
*/
|
||||||
export function racer (ship) {
|
export function racer(ship) {
|
||||||
let standardOpts = {},
|
let standardOpts = {},
|
||||||
usedSlots = [],
|
usedSlots = [],
|
||||||
sgSlot,
|
sgSlot,
|
||||||
sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass)
|
sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass);
|
||||||
|
|
||||||
// Cargo hatch can be disabled
|
// Cargo hatch can be disabled
|
||||||
ship.setSlotEnabled(ship.cargoHatch, false)
|
ship.setSlotEnabled(ship.cargoHatch, false);
|
||||||
|
|
||||||
// Shield generator
|
// Shield generator
|
||||||
const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8]
|
const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8];
|
||||||
const shieldInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
const shieldInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
||||||
.filter(a => (!a.eligible) || a.eligible.sg)
|
.filter(a => (!a.eligible) || a.eligible.sg)
|
||||||
.filter(a => a.maxClass >= sg.class)
|
.filter(a => a.maxClass >= sg.class)
|
||||||
.sort((a, b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass))
|
.sort((a, b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass));
|
||||||
for (let i = 0; i < shieldInternals.length; i++) {
|
for (let i = 0; i < shieldInternals.length; i++) {
|
||||||
if (canMount(ship, shieldInternals[i], 'sg')) {
|
if (canMount(ship, shieldInternals[i], 'sg')) {
|
||||||
ship.use(shieldInternals[i], sg)
|
ship.use(shieldInternals[i], sg);
|
||||||
usedSlots.push(shieldInternals[i])
|
usedSlots.push(shieldInternals[i]);
|
||||||
sgSlot = shieldInternals[i]
|
sgSlot = shieldInternals[i];
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Empty the hardpoints
|
// Empty the hardpoints
|
||||||
for (let s of ship.hardpoints) {
|
for (let s of ship.hardpoints) {
|
||||||
ship.use(s, null)
|
ship.use(s, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Empty the internals
|
// Empty the internals
|
||||||
for (let i = ship.internal.length; i--;) {
|
for (let i = ship.internal.length; i--;) {
|
||||||
let slot = ship.internal[i]
|
let slot = ship.internal[i];
|
||||||
if (usedSlots.indexOf(slot) == -1) {
|
if (usedSlots.indexOf(slot) == -1) {
|
||||||
ship.use(slot, null)
|
ship.use(slot, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Best thrusters
|
// Best thrusters
|
||||||
if (ship.standard[1].maxClass === 3) {
|
if (ship.standard[1].maxClass === 3) {
|
||||||
standardOpts.th = 'tz'
|
standardOpts.th = 'tz';
|
||||||
} else if (ship.standard[1].maxClass === 2) {
|
} else if (ship.standard[1].maxClass === 2) {
|
||||||
standardOpts.th = 'u0'
|
standardOpts.th = 'u0';
|
||||||
} else {
|
} else {
|
||||||
standardOpts.th = ship.standard[1].maxClass + 'A'
|
standardOpts.th = ship.standard[1].maxClass + 'A';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Best power distributor for more boosting
|
// Best power distributor for more boosting
|
||||||
standardOpts.pd = ship.standard[4].maxClass + 'A'
|
standardOpts.pd = ship.standard[4].maxClass + 'A';
|
||||||
|
|
||||||
// Smallest possible FSD drive
|
// Smallest possible FSD drive
|
||||||
standardOpts.fsd = '2D'
|
standardOpts.fsd = '2D';
|
||||||
// Minimal fuel tank
|
// Minimal fuel tank
|
||||||
standardOpts.ft = '1C'
|
standardOpts.ft = '1C';
|
||||||
|
|
||||||
// Disable nearly everything
|
// Disable nearly everything
|
||||||
standardOpts.fsdDisabled = true
|
standardOpts.fsdDisabled = true;
|
||||||
standardOpts.sDisabled = true
|
standardOpts.sDisabled = true;
|
||||||
standardOpts.pdDisabled = true
|
standardOpts.pdDisabled = true;
|
||||||
standardOpts.lsDisabled = true
|
standardOpts.lsDisabled = true;
|
||||||
|
|
||||||
ship.useLightestStandard(standardOpts)
|
ship.useLightestStandard(standardOpts);
|
||||||
|
|
||||||
// Apply engineering to each module
|
// Apply engineering to each module
|
||||||
// ship.standard[1].m.blueprint = getBlueprint('Engine_Dirty', ship.standard[0]);
|
// ship.standard[1].m.blueprint = getBlueprint('Engine_Dirty', ship.standard[0]);
|
||||||
@@ -414,4 +413,3 @@ export function racer (ship) {
|
|||||||
// ship.standard[5].m.blueprint.grade = 5;
|
// ship.standard[5].m.blueprint.grade = 5;
|
||||||
// setBest(ship, ship.standard[5].m);
|
// setBest(ship, ship.standard[5].m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
83
src/app/shipyard/StatsFormatting.js
Normal file
83
src/app/shipyard/StatsFormatting.js
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
export const SI_PREFIXES = {
|
||||||
|
'Y': 1e+24, // Yotta
|
||||||
|
'Z': 1e+21, // Zetta
|
||||||
|
'E': 1e+18, // Peta
|
||||||
|
'P': 1e+15, // Peta
|
||||||
|
'T': 1e+12, // Tera
|
||||||
|
'G': 1e+9, // Giga
|
||||||
|
'M': 1e+6, // Mega
|
||||||
|
'k': 1e+3, // Kilo
|
||||||
|
'h': 1e+2, // Hekto
|
||||||
|
'da': 1e+1, // Deka
|
||||||
|
'': 1,
|
||||||
|
'd': 1e-1, // Dezi
|
||||||
|
'c': 1e-2, // Zenti
|
||||||
|
'm': 1e-3, // Milli
|
||||||
|
'μ': 1e-6, // mikro not supported due to charset
|
||||||
|
'n': 10e-9, // Nano
|
||||||
|
'p': 1e-12, // Nano
|
||||||
|
'f': 1e-15, // Femto
|
||||||
|
'a': 1e-18, // Atto
|
||||||
|
'z': 1e-21, // Zepto
|
||||||
|
'y': 1e-24 // Yokto
|
||||||
|
};
|
||||||
|
|
||||||
|
export const STATS_FORMATTING = {
|
||||||
|
'ammo': { 'format': 'int', },
|
||||||
|
'boot': { 'format': 'int', 'unit': 'secs' },
|
||||||
|
'brokenregen': { 'format': 'round1', 'unit': 'ps' },
|
||||||
|
'burst': { 'format': 'int' },
|
||||||
|
'burstrof': { 'format': 'round1', 'unit': 'ps' },
|
||||||
|
'causres': { 'format': 'pct' },
|
||||||
|
'clip': { 'format': 'int' },
|
||||||
|
'damage': { 'format': 'round' },
|
||||||
|
'dps': { 'format': 'round', 'units': 'ps', 'synthetic': 'getDps' },
|
||||||
|
'dpe': { 'format': 'round', 'units': 'ps', 'synthetic': 'getDpe' },
|
||||||
|
'distdraw': { 'format': 'round', 'unit': 'MW' },
|
||||||
|
'duration': { 'format': 'round1', 'unit': 's' },
|
||||||
|
'eff': { 'format': 'round2' },
|
||||||
|
'engcap': { 'format': 'round1', 'unit': 'MJ' },
|
||||||
|
'engrate': { 'format': 'round1', 'unit': 'MW' },
|
||||||
|
'eps': { 'format': 'round', 'units': 'ps', 'synthetic': 'getEps' },
|
||||||
|
'explres': { 'format': 'pct' },
|
||||||
|
'facinglimit': { 'format': 'round1', 'unit': 'ang' },
|
||||||
|
'falloff': { 'format': 'round', 'unit': 'km', 'storedUnit': 'm' },
|
||||||
|
'fallofffromrange': { 'format': 'round', 'unit': 'km', 'storedUnit': 'm', 'synthetic': 'getFalloff' },
|
||||||
|
'hps': { 'format': 'round', 'units': 'ps', 'synthetic': 'getHps' },
|
||||||
|
'hullboost': { 'format': 'pct1', 'change': 'additive' },
|
||||||
|
'hullreinforcement': { 'format': 'int' },
|
||||||
|
'integrity': { 'format': 'round1' },
|
||||||
|
'jitter': { 'format': 'round', 'unit': 'ang' },
|
||||||
|
'kinres': { 'format': 'pct' },
|
||||||
|
'mass': { 'format': 'round1', 'unit': 'T' },
|
||||||
|
'maxfuel': { 'format': 'round1', 'unit': 'T' },
|
||||||
|
'optmass': { 'format': 'int', 'unit': 'T' },
|
||||||
|
'optmul': { 'format': 'pct', 'change': 'additive' },
|
||||||
|
'pgen': { 'format': 'round1', 'unit': 'MW' },
|
||||||
|
'piercing': { 'format': 'int' },
|
||||||
|
'power': { 'format': 'round', 'unit': 'MW' },
|
||||||
|
'protection': { 'format': 'pct' },
|
||||||
|
'range': { 'format': 'f2', 'unit': 'km', 'storedUnit': 'm' },
|
||||||
|
'ranget': { 'format': 'f1', 'unit': 's' },
|
||||||
|
'regen': { 'format': 'round1', 'unit': 'ps' },
|
||||||
|
'reload': { 'format': 'int', 'unit': 's' },
|
||||||
|
'rof': { 'format': 'round1', 'unit': 'ps' },
|
||||||
|
'angle': { 'format': 'round1', 'unit': 'ang' },
|
||||||
|
'scanrate': { 'format': 'int' },
|
||||||
|
'scantime': { 'format': 'round1', 'unit': 's' },
|
||||||
|
'sdps': { 'format': 'round1', 'units': 'ps', 'synthetic': 'getSDps' },
|
||||||
|
'shield': { 'format': 'int', 'unit': 'MJ' },
|
||||||
|
'shieldaddition': { 'format': 'round1', 'unit': 'MJ' },
|
||||||
|
'shieldboost': { 'format': 'pct1', 'change': 'additive' },
|
||||||
|
'shieldreinforcement': { 'format': 'round1', 'unit': 'MJ' },
|
||||||
|
'shotspeed': { 'format': 'int', 'unit': 'm/s' },
|
||||||
|
'spinup': { 'format': 'round1', 'unit': 's' },
|
||||||
|
'syscap': { 'format': 'round1', 'unit': 'MJ' },
|
||||||
|
'sysrate': { 'format': 'round1', 'unit': 'MW' },
|
||||||
|
'thermload': { 'format': 'round1' },
|
||||||
|
'thermres': { 'format': 'pct' },
|
||||||
|
'wepcap': { 'format': 'round1', 'unit': 'MJ' },
|
||||||
|
'weprate': { 'format': 'round1', 'unit': 'MW' },
|
||||||
|
'jumpboost': { 'format': 'round1', 'unit': 'LY' },
|
||||||
|
'proberadius': { 'format': 'pct1', 'unit': 'pct' },
|
||||||
|
};
|
||||||
@@ -5,6 +5,7 @@ const LS_KEY_BUILDS = 'builds';
|
|||||||
const LS_KEY_COMPARISONS = 'comparisons';
|
const LS_KEY_COMPARISONS = 'comparisons';
|
||||||
const LS_KEY_LANG = 'NG_TRANSLATE_LANG_KEY';
|
const LS_KEY_LANG = 'NG_TRANSLATE_LANG_KEY';
|
||||||
const LS_KEY_COST_TAB = 'costTab';
|
const LS_KEY_COST_TAB = 'costTab';
|
||||||
|
const LS_KEY_CMDR_NAME = 'cmdrName';
|
||||||
const LS_KEY_OUTFITTING_TAB = 'outfittingTab';
|
const LS_KEY_OUTFITTING_TAB = 'outfittingTab';
|
||||||
const LS_KEY_INSURANCE = 'insurance';
|
const LS_KEY_INSURANCE = 'insurance';
|
||||||
const LS_KEY_SHIP_DISCOUNT = 'shipDiscount';
|
const LS_KEY_SHIP_DISCOUNT = 'shipDiscount';
|
||||||
@@ -13,6 +14,8 @@ const LS_KEY_STATE = 'state';
|
|||||||
const LS_KEY_SIZE_RATIO = 'sizeRatio';
|
const LS_KEY_SIZE_RATIO = 'sizeRatio';
|
||||||
const LS_KEY_TOOLTIPS = 'tooltips';
|
const LS_KEY_TOOLTIPS = 'tooltips';
|
||||||
const LS_KEY_MODULE_RESISTANCES = 'moduleResistances';
|
const LS_KEY_MODULE_RESISTANCES = 'moduleResistances';
|
||||||
|
const LS_KEY_ROLLS = 'matsPerGrade';
|
||||||
|
const LS_KEY_ORBIS = 'orbis';
|
||||||
|
|
||||||
let LS;
|
let LS;
|
||||||
|
|
||||||
@@ -67,7 +70,6 @@ function _delete(key) {
|
|||||||
* export is an instance (see end of this file).
|
* export is an instance (see end of this file).
|
||||||
*/
|
*/
|
||||||
export class Persist extends EventEmitter {
|
export class Persist extends EventEmitter {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an instance
|
* Create an instance
|
||||||
*/
|
*/
|
||||||
@@ -84,6 +86,8 @@ export class Persist extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let moduleResistances = _get(LS_KEY_MODULE_RESISTANCES);
|
let moduleResistances = _get(LS_KEY_MODULE_RESISTANCES);
|
||||||
|
let matsPerGrade = _get(LS_KEY_ROLLS);
|
||||||
|
let cmdrName = _get(LS_KEY_CMDR_NAME);
|
||||||
let tips = _get(LS_KEY_TOOLTIPS);
|
let tips = _get(LS_KEY_TOOLTIPS);
|
||||||
let insurance = _getString(LS_KEY_INSURANCE);
|
let insurance = _getString(LS_KEY_INSURANCE);
|
||||||
let shipDiscount = _get(LS_KEY_SHIP_DISCOUNT);
|
let shipDiscount = _get(LS_KEY_SHIP_DISCOUNT);
|
||||||
@@ -91,6 +95,7 @@ export class Persist extends EventEmitter {
|
|||||||
let buildJson = _get(LS_KEY_BUILDS);
|
let buildJson = _get(LS_KEY_BUILDS);
|
||||||
let comparisonJson = _get(LS_KEY_COMPARISONS);
|
let comparisonJson = _get(LS_KEY_COMPARISONS);
|
||||||
|
|
||||||
|
this.orbisCreds = _get(LS_KEY_ORBIS) || { email: '', password: '' };
|
||||||
this.onStorageChange = this.onStorageChange.bind(this);
|
this.onStorageChange = this.onStorageChange.bind(this);
|
||||||
this.langCode = _getString(LS_KEY_LANG) || 'en';
|
this.langCode = _getString(LS_KEY_LANG) || 'en';
|
||||||
this.insurance = insurance && Insurance[insurance.toLowerCase()] !== undefined ? insurance : 'standard';
|
this.insurance = insurance && Insurance[insurance.toLowerCase()] !== undefined ? insurance : 'standard';
|
||||||
@@ -102,6 +107,14 @@ export class Persist extends EventEmitter {
|
|||||||
this.outfittingTab = _getString(LS_KEY_OUTFITTING_TAB);
|
this.outfittingTab = _getString(LS_KEY_OUTFITTING_TAB);
|
||||||
this.state = _get(LS_KEY_STATE);
|
this.state = _get(LS_KEY_STATE);
|
||||||
this.sizeRatio = _get(LS_KEY_SIZE_RATIO) || 1;
|
this.sizeRatio = _get(LS_KEY_SIZE_RATIO) || 1;
|
||||||
|
this.matsPerGrade = matsPerGrade || {
|
||||||
|
1: 2,
|
||||||
|
2: 2,
|
||||||
|
3: 4,
|
||||||
|
4: 4,
|
||||||
|
5: 10
|
||||||
|
};
|
||||||
|
this.cmdrName = cmdrName || { selected: '', cmdrs: [] };
|
||||||
this.tooltipsEnabled = tips === null ? true : tips;
|
this.tooltipsEnabled = tips === null ? true : tips;
|
||||||
this.moduleResistancesEnabled = moduleResistances === null ? true : moduleResistances;
|
this.moduleResistancesEnabled = moduleResistances === null ? true : moduleResistances;
|
||||||
|
|
||||||
@@ -152,6 +165,14 @@ export class Persist extends EventEmitter {
|
|||||||
this.moduleResistancesEnabled = !!newValue && newValue.toLowerCase() == 'true';
|
this.moduleResistancesEnabled = !!newValue && newValue.toLowerCase() == 'true';
|
||||||
this.emit('moduleresistances', this.moduleResistancesEnabled);
|
this.emit('moduleresistances', this.moduleResistancesEnabled);
|
||||||
break;
|
break;
|
||||||
|
case LS_KEY_ROLLS:
|
||||||
|
this.matsPerGrade = JSON.parse(newValue);
|
||||||
|
this.emit('matsPerGrade', this.matsPerGrade);
|
||||||
|
break;
|
||||||
|
case LS_KEY_ORBIS:
|
||||||
|
this.orbisCreds = JSON.parse(newValue);
|
||||||
|
this.emit('orbis', this.orbisCreds);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// On JSON.Parse Error - don't sync or do anything
|
// On JSON.Parse Error - don't sync or do anything
|
||||||
@@ -177,6 +198,24 @@ export class Persist extends EventEmitter {
|
|||||||
this.emit('language', langCode);
|
this.emit('language', langCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current orbis.zone credentials
|
||||||
|
* @return {String} language code
|
||||||
|
*/
|
||||||
|
getOrbisCreds() {
|
||||||
|
return this.orbisCreds;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update and save the orbis.zone credentials
|
||||||
|
* @param {Object} creds object with username and password properties.
|
||||||
|
*/
|
||||||
|
setOrbisCreds(creds) {
|
||||||
|
this.langCode = creds;
|
||||||
|
_put(LS_KEY_ORBIS, creds);
|
||||||
|
this.emit('orbis', creds);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show tooltips setting
|
* Show tooltips setting
|
||||||
* @param {boolean} show Optional - update setting
|
* @param {boolean} show Optional - update setting
|
||||||
@@ -457,6 +496,31 @@ export class Persist extends EventEmitter {
|
|||||||
return this.moduleDiscount;
|
return this.moduleDiscount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the saved ship discount
|
||||||
|
* @param {Object} matsPerGrade # of rolls per grade
|
||||||
|
*/
|
||||||
|
setRolls(matsPerGrade) {
|
||||||
|
this.matsPerGrade = matsPerGrade;
|
||||||
|
_put(LS_KEY_ROLLS, this.matsPerGrade);
|
||||||
|
this.emit('matsPerGrade');
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get the saved Mats per grade
|
||||||
|
* @return {Object} # of rolls per grade
|
||||||
|
*/
|
||||||
|
getRolls() {
|
||||||
|
return this.matsPerGrade;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the saved Mats per grade
|
||||||
|
* @return {Object} # of rolls per grade
|
||||||
|
*/
|
||||||
|
getCmdr() {
|
||||||
|
return this.cmdrName;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Persist selected cost tab
|
* Persist selected cost tab
|
||||||
* @param {number} tabName Cost tab name
|
* @param {number} tabName Cost tab name
|
||||||
@@ -466,6 +530,16 @@ export class Persist extends EventEmitter {
|
|||||||
_put(LS_KEY_COST_TAB, tabName);
|
_put(LS_KEY_COST_TAB, tabName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Persist cmdr name
|
||||||
|
* @param {Object} cmdrName Commander name for EDEngineer
|
||||||
|
*/
|
||||||
|
setCmdr(cmdrName) {
|
||||||
|
this.cmdrName = cmdrName;
|
||||||
|
_put(LS_KEY_CMDR_NAME, cmdrName);
|
||||||
|
this.emit('cmdr');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the saved discount
|
* Get the saved discount
|
||||||
* @return {number} val Discount value/amount
|
* @return {number} val Discount value/amount
|
||||||
|
|||||||
@@ -1,319 +1,417 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Modifications } from 'coriolis-data/dist';
|
import { Modifications } from 'coriolis-data/dist';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a tooltip with details of a blueprint's effects
|
* Generate a tooltip with details of a blueprint's specials
|
||||||
* @param {Object} translate The translate object
|
* @param {Object} translate The translate object
|
||||||
* @param {Object} blueprint The blueprint at the required grade
|
* @param {Object} blueprint The blueprint at the required grade
|
||||||
* @param {Array} engineers The engineers supplying this blueprint
|
* @param {string} grp The group of the module
|
||||||
* @param {string} grp The group of the module
|
* @param {Object} m The module to compare with
|
||||||
* @param {Object} m The module to compare with
|
* @param {string} specialName The name of the special
|
||||||
* @returns {Object} The react components
|
* @returns {Object} The react components
|
||||||
*/
|
*/
|
||||||
export function blueprintTooltip(translate, blueprint, engineers, grp, m) {
|
export function specialToolTip(translate, blueprint, grp, m, specialName) {
|
||||||
const effects = [];
|
const effects = [];
|
||||||
for (const feature in blueprint.features) {
|
if (!blueprint || !blueprint.features) {
|
||||||
const featureIsBeneficial = isBeneficial(feature, blueprint.features[feature]);
|
return undefined;
|
||||||
const featureDef = Modifications.modifications[feature];
|
}
|
||||||
if (!featureDef.hidden) {
|
if (m) {
|
||||||
let symbol = '';
|
// We also add in any benefits from specials that aren't covered above
|
||||||
if (feature === 'jitter') {
|
if (m.blueprint) {
|
||||||
symbol = '°';
|
for (const feature in Modifications.modifierActions[specialName]) {
|
||||||
} else if (featureDef.type === 'percentage') {
|
// if (!blueprint.features[feature] && !m.mods.feature) {
|
||||||
symbol = '%';
|
const featureDef = Modifications.modifications[feature];
|
||||||
}
|
if (featureDef && !featureDef.hidden) {
|
||||||
let lowerBound = blueprint.features[feature][0];
|
let symbol = '';
|
||||||
let upperBound = blueprint.features[feature][1];
|
if (feature === 'jitter') {
|
||||||
if (featureDef.type === 'percentage') {
|
symbol = '°';
|
||||||
lowerBound = Math.round(lowerBound * 1000) / 10;
|
} else if (featureDef.type === 'percentage') {
|
||||||
upperBound = Math.round(upperBound * 1000) / 10;
|
symbol = '%';
|
||||||
}
|
}
|
||||||
const lowerIsBeneficial = isValueBeneficial(feature, lowerBound);
|
let current = m.getModValue(feature) - m.getModValue(feature, true);
|
||||||
const upperIsBeneficial = isValueBeneficial(feature, upperBound);
|
if (featureDef.type === 'percentage') {
|
||||||
if (m) {
|
current = Math.round(current / 10) / 10;
|
||||||
// We have a module - add in the current value
|
} else if (featureDef.type === 'numeric') {
|
||||||
let current = m.getModValue(feature);
|
current /= 100;
|
||||||
if (featureDef.type === 'percentage' || featureDef.name === 'burst' || featureDef.name === 'burstrof') {
|
}
|
||||||
current = Math.round(current / 10) / 10;
|
const currentIsBeneficial = isValueBeneficial(feature, current);
|
||||||
} else if (featureDef.type === 'numeric') {
|
|
||||||
current /= 100;
|
effects.push(
|
||||||
}
|
<tr key={feature + '_specialTT'}>
|
||||||
const currentIsBeneficial = isValueBeneficial(feature, current);
|
<td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td>
|
||||||
effects.push(
|
<td> </td>
|
||||||
<tr key={feature}>
|
<td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'}
|
||||||
<td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td>
|
style={{ textAlign: 'right' }}>{current}{symbol}</td>
|
||||||
<td className={lowerBound === 0 ? '' : lowerIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{lowerBound}{symbol}</td>
|
<td> </td>
|
||||||
<td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{current}{symbol}</td>
|
</tr>
|
||||||
<td className={upperBound === 0 ? '' : upperIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{upperBound}{symbol}</td>
|
);
|
||||||
</tr>
|
}
|
||||||
);
|
}
|
||||||
} else {
|
}
|
||||||
// We do not have a module, no value
|
}
|
||||||
effects.push(
|
|
||||||
<tr key={feature}>
|
return (
|
||||||
<td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td>
|
<div>
|
||||||
<td className={lowerBound === 0 ? '' : lowerIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{lowerBound}{symbol}</td>
|
<table width='100%'>
|
||||||
<td className={upperBound === 0 ? '' : upperIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{upperBound}{symbol}</td>
|
<tbody>
|
||||||
</tr>
|
{effects}
|
||||||
);
|
</tbody>
|
||||||
}
|
</table>
|
||||||
}
|
</div>
|
||||||
}
|
);
|
||||||
if (m) {
|
}
|
||||||
// Because we have a module add in any benefits that aren't part of the primary blueprint
|
|
||||||
for (const feature in m.mods) {
|
/**
|
||||||
if (!blueprint.features[feature]) {
|
* Generate a tooltip with details of a blueprint's effects
|
||||||
const featureDef = Modifications.modifications[feature];
|
* @param {Object} translate The translate object
|
||||||
if (featureDef && !featureDef.hidden) {
|
* @param {Object} blueprint The blueprint at the required grade
|
||||||
let symbol = '';
|
* @param {Array} engineers The engineers supplying this blueprint
|
||||||
if (feature === 'jitter') {
|
* @param {string} grp The group of the module
|
||||||
symbol = '°';
|
* @param {Object} m The module to compare with
|
||||||
} else if (featureDef.type === 'percentage') {
|
* @returns {Object} The react components
|
||||||
symbol = '%';
|
*/
|
||||||
}
|
export function blueprintTooltip(translate, blueprint, engineers, grp, m) {
|
||||||
let current = m.getModValue(feature);
|
const effects = [];
|
||||||
if (featureDef.type === 'percentage' || featureDef.name === 'burst' || featureDef.name === 'burstrof') {
|
if (!blueprint || !blueprint.features) {
|
||||||
current = Math.round(current / 10) / 10;
|
return undefined;
|
||||||
} else if (featureDef.type === 'numeric') {
|
}
|
||||||
current /= 100;
|
for (const feature in blueprint.features) {
|
||||||
}
|
const featureIsBeneficial = isBeneficial(feature, blueprint.features[feature]);
|
||||||
const currentIsBeneficial = isValueBeneficial(feature, current);
|
const featureDef = Modifications.modifications[feature];
|
||||||
effects.push(
|
if (!featureDef.hidden) {
|
||||||
<tr key={feature}>
|
let symbol = '';
|
||||||
<td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td>
|
if (feature === 'jitter') {
|
||||||
<td> </td>
|
symbol = '°';
|
||||||
<td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{current}{symbol}</td>
|
} else if (featureDef.type === 'percentage') {
|
||||||
<td> </td>
|
symbol = '%';
|
||||||
</tr>
|
}
|
||||||
);
|
let lowerBound = blueprint.features[feature][0];
|
||||||
}
|
let upperBound = blueprint.features[feature][1];
|
||||||
}
|
if (featureDef.type === 'percentage') {
|
||||||
}
|
lowerBound = Math.round(lowerBound * 1000) / 10;
|
||||||
|
upperBound = Math.round(upperBound * 1000) / 10;
|
||||||
// We also add in any benefits from specials that aren't covered above
|
}
|
||||||
if (m.blueprint && m.blueprint.special) {
|
const lowerIsBeneficial = isValueBeneficial(feature, lowerBound);
|
||||||
for (const feature in Modifications.modifierActions[m.blueprint.special.edname]) {
|
const upperIsBeneficial = isValueBeneficial(feature, upperBound);
|
||||||
if (!blueprint.features[feature] && !m.mods.feature) {
|
if (m) {
|
||||||
const featureDef = Modifications.modifications[feature];
|
// We have a module - add in the current value
|
||||||
if (featureDef && !featureDef.hidden) {
|
let current = m.getModValue(feature);
|
||||||
let symbol = '';
|
if (featureDef.type === 'percentage' || featureDef.name === 'burst' || featureDef.name === 'burstrof') {
|
||||||
if (feature === 'jitter') {
|
current = Math.round(current / 10) / 10;
|
||||||
symbol = '°';
|
} else if (featureDef.type === 'numeric') {
|
||||||
} else if (featureDef.type === 'percentage') {
|
current /= 100;
|
||||||
symbol = '%';
|
}
|
||||||
}
|
const currentIsBeneficial = isValueBeneficial(feature, current);
|
||||||
let current = m.getModValue(feature);
|
effects.push(
|
||||||
if (featureDef.type === 'percentage' || featureDef.name === 'burst' || featureDef.name === 'burstrof') {
|
<tr key={feature}>
|
||||||
current = Math.round(current / 10) / 10;
|
<td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td>
|
||||||
} else if (featureDef.type === 'numeric') {
|
<td className={lowerBound === 0 ? '' : lowerIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{lowerBound}{symbol}</td>
|
||||||
current /= 100;
|
<td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{current}{symbol}</td>
|
||||||
}
|
<td className={upperBound === 0 ? '' : upperIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{upperBound}{symbol}</td>
|
||||||
const currentIsBeneficial = isValueBeneficial(feature, current);
|
</tr>
|
||||||
effects.push(
|
);
|
||||||
<tr key={feature}>
|
} else {
|
||||||
<td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td>
|
// We do not have a module, no value
|
||||||
<td> </td>
|
effects.push(
|
||||||
<td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{current}{symbol}</td>
|
<tr key={feature}>
|
||||||
<td> </td>
|
<td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td>
|
||||||
</tr>
|
<td className={lowerBound === 0 ? '' : lowerIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{lowerBound}{symbol}</td>
|
||||||
);
|
<td className={upperBound === 0 ? '' : upperIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{upperBound}{symbol}</td>
|
||||||
}
|
</tr>
|
||||||
}
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (m) {
|
||||||
let components;
|
// Because we have a module add in any benefits that aren't part of the primary blueprint
|
||||||
if (!m) {
|
for (const feature in m.mods) {
|
||||||
components = [];
|
if (!blueprint.features[feature]) {
|
||||||
for (const component in blueprint.components) {
|
const featureDef = Modifications.modifications[feature];
|
||||||
components.push(
|
if (featureDef && !featureDef.hidden) {
|
||||||
<tr key={component}>
|
let symbol = '';
|
||||||
<td style={{ textAlign: 'left' }}>{translate(component)}</td>
|
if (feature === 'jitter') {
|
||||||
<td style={{ textAlign: 'right' }}>{blueprint.components[component]}</td>
|
symbol = '°';
|
||||||
</tr>
|
} else if (featureDef.type === 'percentage') {
|
||||||
);
|
symbol = '%';
|
||||||
}
|
}
|
||||||
}
|
let current = m.getModValue(feature);
|
||||||
|
if (featureDef.type === 'percentage' || featureDef.name === 'burst' || featureDef.name === 'burstrof') {
|
||||||
let engineersList;
|
current = Math.round(current / 10) / 10;
|
||||||
if (engineers) {
|
} else if (featureDef.type === 'numeric') {
|
||||||
engineersList = [];
|
current /= 100;
|
||||||
for (const engineer of engineers) {
|
}
|
||||||
engineersList.push(
|
const currentIsBeneficial = isValueBeneficial(feature, current);
|
||||||
<tr key={engineer}>
|
effects.push(
|
||||||
<td style={{ textAlign: 'left' }}>{engineer}</td>
|
<tr key={feature}>
|
||||||
</tr>
|
<td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td>
|
||||||
);
|
<td> </td>
|
||||||
}
|
<td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{current}{symbol}</td>
|
||||||
}
|
<td> </td>
|
||||||
|
</tr>
|
||||||
return (
|
);
|
||||||
<div>
|
}
|
||||||
<table width='100%'>
|
}
|
||||||
<thead>
|
}
|
||||||
<tr>
|
|
||||||
<td>{translate('feature')}</td>
|
// We also add in any benefits from specials that aren't covered above
|
||||||
<td>{translate('worst')}</td>
|
if (m.blueprint && m.blueprint.special) {
|
||||||
{m ? <td>{translate('current')}</td> : null }
|
for (const feature in Modifications.modifierActions[m.blueprint.special.edname]) {
|
||||||
<td>{translate('best')}</td>
|
if (!blueprint.features[feature] && !m.mods.feature) {
|
||||||
</tr>
|
const featureDef = Modifications.modifications[feature];
|
||||||
</thead>
|
if (featureDef && !featureDef.hidden) {
|
||||||
<tbody>
|
let symbol = '';
|
||||||
{effects}
|
if (feature === 'jitter') {
|
||||||
</tbody>
|
symbol = '°';
|
||||||
</table>
|
} else if (featureDef.type === 'percentage') {
|
||||||
{ components ? <table width='100%'>
|
symbol = '%';
|
||||||
<thead>
|
}
|
||||||
<tr>
|
let current = m.getModValue(feature);
|
||||||
<td>{translate('component')}</td>
|
if (featureDef.type === 'percentage' || featureDef.name === 'burst' || featureDef.name === 'burstrof') {
|
||||||
<td>{translate('amount')}</td>
|
current = Math.round(current / 10) / 10;
|
||||||
</tr>
|
} else if (featureDef.type === 'numeric') {
|
||||||
</thead>
|
current /= 100;
|
||||||
<tbody>
|
}
|
||||||
{components}
|
const currentIsBeneficial = isValueBeneficial(feature, current);
|
||||||
</tbody>
|
effects.push(
|
||||||
</table> : null }
|
<tr key={feature}>
|
||||||
{ engineersList ? <table width='100%'>
|
<td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td>
|
||||||
<thead>
|
<td> </td>
|
||||||
<tr>
|
<td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{current}{symbol}</td>
|
||||||
<td>{translate('engineers')}</td>
|
<td> </td>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
);
|
||||||
<tbody>
|
}
|
||||||
{engineersList}
|
}
|
||||||
</tbody>
|
}
|
||||||
</table> : null }
|
}
|
||||||
</div>
|
}
|
||||||
);
|
|
||||||
}
|
let components;
|
||||||
|
if (!m) {
|
||||||
/**
|
components = [];
|
||||||
* Is this blueprint feature beneficial?
|
for (const component in blueprint.components) {
|
||||||
* @param {string} feature The name of the feature
|
components.push(
|
||||||
* @param {array} values The value of the feature
|
<tr key={component}>
|
||||||
* @returns {boolean} True if this feature is beneficial
|
<td style={{ textAlign: 'left' }}>{translate(component)}</td>
|
||||||
*/
|
<td style={{ textAlign: 'right' }}>{blueprint.components[component]}</td>
|
||||||
export function isBeneficial(feature, values) {
|
</tr>
|
||||||
const fact = (values[0] < 0 || (values[0] === 0 && values[1] < 0));
|
);
|
||||||
if (Modifications.modifications[feature].higherbetter) {
|
}
|
||||||
return !fact;
|
}
|
||||||
} else {
|
|
||||||
return fact;
|
let engineersList;
|
||||||
}
|
if (engineers) {
|
||||||
}
|
engineersList = [];
|
||||||
|
for (const engineer of engineers) {
|
||||||
/**
|
engineersList.push(
|
||||||
* Is this feature value beneficial?
|
<tr key={engineer}>
|
||||||
* @param {string} feature The name of the feature
|
<td style={{ textAlign: 'left' }}>{engineer}</td>
|
||||||
* @param {number} value The value of the feature
|
</tr>
|
||||||
* @returns {boolean} True if this value is beneficial
|
);
|
||||||
*/
|
}
|
||||||
export function isValueBeneficial(feature, value) {
|
}
|
||||||
if (Modifications.modifications[feature].higherbetter) {
|
|
||||||
return value > 0;
|
return (
|
||||||
} else {
|
<div>
|
||||||
return value < 0;
|
<table width='100%'>
|
||||||
}
|
<thead>
|
||||||
}
|
<tr>
|
||||||
|
<td>{translate('feature')}</td>
|
||||||
/**
|
<td>{translate('worst')}</td>
|
||||||
* Get a blueprint with a given name and an optional module
|
{m ? <td>{translate('current')}</td> : null }
|
||||||
* @param {string} name The name of the blueprint
|
<td>{translate('best')}</td>
|
||||||
* @param {Object} module The module for which to obtain this blueprint
|
</tr>
|
||||||
* @returns {Object} The matching blueprint
|
</thead>
|
||||||
*/
|
<tbody>
|
||||||
export function getBlueprint(name, module) {
|
{effects}
|
||||||
// Start with a copy of the blueprint
|
</tbody>
|
||||||
const findMod = val => Object.keys(Modifications.blueprints).find(elem => elem.toString().toLowerCase().search(val.toString().toLowerCase().replace(/(OutfittingFieldType_|persecond)/igm, '')) >= 0)
|
</table>
|
||||||
const found = Modifications.blueprints[findMod(name)];
|
{ components ? <table width='100%'>
|
||||||
if (!found || !found.fdname) {
|
<thead>
|
||||||
return {};
|
<tr>
|
||||||
}
|
<td>{translate('component')}</td>
|
||||||
const blueprint = JSON.parse(JSON.stringify(found));
|
<td>{translate('amount')}</td>
|
||||||
if (module) {
|
</tr>
|
||||||
if (module.grp === 'sb') {
|
</thead>
|
||||||
// Shield boosters are treated internally as straight modifiers, so rather than (for example)
|
<tbody>
|
||||||
// being a 4% boost they are a 104% multiplier. We need to fix the values here so that they look
|
{components}
|
||||||
// accurate as per the information in Elite
|
</tbody>
|
||||||
for (const grade in blueprint.grades) {
|
</table> : null }
|
||||||
for (const feature in blueprint.grades[grade].features) {
|
{ engineersList ? <table width='100%'>
|
||||||
if (feature === 'shieldboost') {
|
<thead>
|
||||||
blueprint.grades[grade].features[feature][0] = ((1 + blueprint.grades[grade].features[feature][0]) * (1 + module.shieldboost) - 1) / module.shieldboost - 1;
|
<tr>
|
||||||
blueprint.grades[grade].features[feature][1] = ((1 + blueprint.grades[grade].features[feature][1]) * (1 + module.shieldboost) - 1) / module.shieldboost - 1;
|
<td>{translate('engineers')}</td>
|
||||||
}
|
</tr>
|
||||||
}
|
</thead>
|
||||||
}
|
<tbody>
|
||||||
}
|
{engineersList}
|
||||||
}
|
</tbody>
|
||||||
return blueprint;
|
</table> : null }
|
||||||
}
|
</div>
|
||||||
|
);
|
||||||
/**
|
}
|
||||||
* Provide 'worst' primary modifications
|
|
||||||
* @param {Object} ship The ship for which to perform the modifications
|
/**
|
||||||
* @param {Object} m The module for which to perform the modifications
|
* Is this blueprint feature beneficial?
|
||||||
* @param {Number} percent The percent to set values to of full.
|
* @param {string} feature The name of the feature
|
||||||
*/
|
* @param {array} values The value of the feature
|
||||||
export function setPercent(ship, m, percent) {
|
* @returns {boolean} True if this feature is beneficial
|
||||||
ship.clearModifications(m);
|
*/
|
||||||
const features = m.blueprint.grades[m.blueprint.grade].features;
|
export function isBeneficial(feature, values) {
|
||||||
for (const featureName in features) {
|
const fact = (values[0] < 0 || (values[0] === 0 && values[1] < 0));
|
||||||
const value = features[featureName][0];
|
if (Modifications.modifications[feature].higherbetter) {
|
||||||
const featureIsBeneficial = isBeneficial(featureName, features[featureName]);
|
return !fact;
|
||||||
if (featureIsBeneficial === true) {
|
} else {
|
||||||
_setValue(ship, m, featureName, (percent / 100) * value);
|
return fact;
|
||||||
} else {
|
}
|
||||||
_setValue(ship, m, featureName, value);
|
}
|
||||||
}
|
|
||||||
}
|
/**
|
||||||
}
|
* Is this feature value beneficial?
|
||||||
|
* @param {string} feature The name of the feature
|
||||||
/**
|
* @param {number} value The value of the feature
|
||||||
* Provide 'random' primary modifications
|
* @returns {boolean} True if this value is beneficial
|
||||||
* @param {Object} ship The ship for which to perform the modifications
|
*/
|
||||||
* @param {Object} m The module for which to perform the modifications
|
export function isValueBeneficial(feature, value) {
|
||||||
*/
|
if (Modifications.modifications[feature].higherbetter) {
|
||||||
export function setRandom(ship, m) {
|
return value > 0;
|
||||||
ship.clearModifications(m);
|
} else {
|
||||||
// Pick a single value for our randomness
|
return value < 0;
|
||||||
const mult = Math.random();
|
}
|
||||||
const features = m.blueprint.grades[m.blueprint.grade].features;
|
}
|
||||||
for (const featureName in features) {
|
|
||||||
let value;
|
/**
|
||||||
if (Modifications.modifications[featureName].higherbetter) {
|
* Get a blueprint with a given name and an optional module
|
||||||
// Higher is better, but is this making it better or worse?
|
* @param {string} name The name of the blueprint
|
||||||
if (features[featureName][0] < 0 || (features[featureName][0] === 0 && features[featureName][1] < 0)) {
|
* @param {Object} module The module for which to obtain this blueprint
|
||||||
value = features[featureName][1] + ((features[featureName][0] - features[featureName][1]) * mult);
|
* @returns {Object} The matching blueprint
|
||||||
} else {
|
*/
|
||||||
value = features[featureName][0] + ((features[featureName][1] - features[featureName][0]) * mult);
|
export function getBlueprint(name, module) {
|
||||||
}
|
// Start with a copy of the blueprint
|
||||||
} else {
|
const findMod = val => Object.keys(Modifications.blueprints).find(elem => elem.toString().toLowerCase().search(val.toString().toLowerCase().replace(/(OutfittingFieldType_|persecond)/igm, '')) >= 0);
|
||||||
// Higher is worse, but is this making it better or worse?
|
const found = Modifications.blueprints[findMod(name)];
|
||||||
if (features[featureName][0] < 0 || (features[featureName][0] === 0 && features[featureName][1] < 0)) {
|
if (!found || !found.fdname) {
|
||||||
value = features[featureName][0] + ((features[featureName][1] - features[featureName][0]) * mult);
|
return {};
|
||||||
} else {
|
}
|
||||||
value = features[featureName][1] + ((features[featureName][0] - features[featureName][1]) * mult);
|
const blueprint = JSON.parse(JSON.stringify(found));
|
||||||
}
|
return blueprint;
|
||||||
}
|
}
|
||||||
|
|
||||||
_setValue(ship, m, featureName, value);
|
/**
|
||||||
}
|
* Provide 'percent' primary modifications
|
||||||
}
|
* @param {Object} ship The ship for which to perform the modifications
|
||||||
|
* @param {Object} m The module for which to perform the modifications
|
||||||
/**
|
* @param {Number} percent The percent to set values to of full.
|
||||||
* Set a modification feature value
|
*/
|
||||||
* @param {Object} ship The ship for which to perform the modifications
|
export function setPercent(ship, m, percent) {
|
||||||
* @param {Object} m The module for which to perform the modifications
|
ship.clearModifications(m);
|
||||||
* @param {string} featureName The feature being set
|
// Pick given value as multiplier
|
||||||
* @param {number} value The value being set for the feature
|
const mult = percent / 100;
|
||||||
*/
|
const features = m.blueprint.grades[m.blueprint.grade].features;
|
||||||
function _setValue(ship, m, featureName, value) {
|
for (const featureName in features) {
|
||||||
if (Modifications.modifications[featureName].type == 'percentage') {
|
let value;
|
||||||
ship.setModification(m, featureName, value * 10000);
|
if (Modifications.modifications[featureName].higherbetter) {
|
||||||
} else if (Modifications.modifications[featureName].type == 'numeric') {
|
// Higher is better, but is this making it better or worse?
|
||||||
ship.setModification(m, featureName, value * 100);
|
if (features[featureName][0] < 0 || (features[featureName][0] === 0 && features[featureName][1] < 0)) {
|
||||||
} else {
|
value = features[featureName][1] + ((features[featureName][0] - features[featureName][1]) * mult);
|
||||||
ship.setModification(m, featureName, value);
|
} else {
|
||||||
}
|
value = features[featureName][0] + ((features[featureName][1] - features[featureName][0]) * mult);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Higher is worse, but is this making it better or worse?
|
||||||
|
if (features[featureName][0] < 0 || (features[featureName][0] === 0 && features[featureName][1] < 0)) {
|
||||||
|
value = features[featureName][0] + ((features[featureName][1] - features[featureName][0]) * mult);
|
||||||
|
} else {
|
||||||
|
value = features[featureName][1] + ((features[featureName][0] - features[featureName][1]) * mult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_setValue(ship, m, featureName, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide 'random' primary modifications
|
||||||
|
* @param {Object} ship The ship for which to perform the modifications
|
||||||
|
* @param {Object} m The module for which to perform the modifications
|
||||||
|
*/
|
||||||
|
export function setRandom(ship, m) {
|
||||||
|
// Pick a single value for our randomness
|
||||||
|
setPercent(ship, m, Math.random() * 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a modification feature value
|
||||||
|
* @param {Object} ship The ship for which to perform the modifications
|
||||||
|
* @param {Object} m The module for which to perform the modifications
|
||||||
|
* @param {string} featureName The feature being set
|
||||||
|
* @param {number} value The value being set for the feature
|
||||||
|
*/
|
||||||
|
function _setValue(ship, m, featureName, value) {
|
||||||
|
if (Modifications.modifications[featureName].type == 'percentage') {
|
||||||
|
ship.setModification(m, featureName, value * 10000);
|
||||||
|
} else if (Modifications.modifications[featureName].type == 'numeric') {
|
||||||
|
ship.setModification(m, featureName, value * 100);
|
||||||
|
} else {
|
||||||
|
ship.setModification(m, featureName, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide 'percent' primary query
|
||||||
|
* @param {Object} m The module for which to perform the query
|
||||||
|
* @returns {Number} percent The percentage indicator of current applied values.
|
||||||
|
*/
|
||||||
|
export function getPercent(m) {
|
||||||
|
let result = null;
|
||||||
|
const features = m.blueprint.grades[m.blueprint.grade].features;
|
||||||
|
for (const featureName in features) {
|
||||||
|
if (features[featureName][0] === features[featureName][1]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let value = _getValue(m, featureName);
|
||||||
|
let mult;
|
||||||
|
if (featureName == 'shieldboost') {
|
||||||
|
mult = ((1 + value) * (1 + m.shieldboost)) - 1 - m.shieldboost;
|
||||||
|
} else if (Modifications.modifications[featureName].higherbetter) {
|
||||||
|
// Higher is better, but is this making it better or worse?
|
||||||
|
if (features[featureName][0] < 0 || (features[featureName][0] === 0 && features[featureName][1] < 0)) {
|
||||||
|
mult = Math.round((value - features[featureName][1]) / (features[featureName][0] - features[featureName][1]) * 100);
|
||||||
|
} else {
|
||||||
|
mult = Math.round((value - features[featureName][0]) / (features[featureName][1] - features[featureName][0]) * 100);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Higher is worse, but is this making it better or worse?
|
||||||
|
if (features[featureName][0] < 0 || (features[featureName][0] === 0 && features[featureName][1] < 0)) {
|
||||||
|
mult = Math.round((value - features[featureName][0]) / (features[featureName][1] - features[featureName][0]) * 100);
|
||||||
|
} else {
|
||||||
|
mult = Math.round((value - features[featureName][1]) / (features[featureName][0] - features[featureName][1]) * 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result && result != mult) {
|
||||||
|
return null;
|
||||||
|
} else if (result != mult) {
|
||||||
|
result = mult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query a feature value
|
||||||
|
* @param {Object} m The module for which to perform the query
|
||||||
|
* @param {string} featureName The feature being queried
|
||||||
|
* @returns {number} The value of the modification as a %
|
||||||
|
*/
|
||||||
|
function _getValue(m, featureName) {
|
||||||
|
if (Modifications.modifications[featureName].type == 'percentage') {
|
||||||
|
return m.getModValue(featureName, true) / 10000;
|
||||||
|
} else if (Modifications.modifications[featureName].type == 'numeric') {
|
||||||
|
return m.getModValue(featureName, true) / 100;
|
||||||
|
} else {
|
||||||
|
return m.getModValue(featureName, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { getBlueprint } from '../utils/BlueprintFunctions';
|
|||||||
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
||||||
|
|
||||||
// mapping from fd's ship model names to coriolis'
|
// mapping from fd's ship model names to coriolis'
|
||||||
const SHIP_FD_NAME_TO_CORIOLIS_NAME = {
|
export const SHIP_FD_NAME_TO_CORIOLIS_NAME = {
|
||||||
'Adder': 'adder',
|
'Adder': 'adder',
|
||||||
'Anaconda': 'anaconda',
|
'Anaconda': 'anaconda',
|
||||||
'Asp': 'asp',
|
'Asp': 'asp',
|
||||||
@@ -29,6 +29,9 @@ const SHIP_FD_NAME_TO_CORIOLIS_NAME = {
|
|||||||
'FerDeLance': 'fer_de_lance',
|
'FerDeLance': 'fer_de_lance',
|
||||||
'Hauler': 'hauler',
|
'Hauler': 'hauler',
|
||||||
'Independant_Trader': 'keelback',
|
'Independant_Trader': 'keelback',
|
||||||
|
'Krait_MkII': 'krait_mkii',
|
||||||
|
'Mamba': 'mamba',
|
||||||
|
'Krait_Light': 'krait_phantom',
|
||||||
'Orca': 'orca',
|
'Orca': 'orca',
|
||||||
'Python': 'python',
|
'Python': 'python',
|
||||||
'SideWinder': 'sidewinder',
|
'SideWinder': 'sidewinder',
|
||||||
@@ -37,6 +40,8 @@ const SHIP_FD_NAME_TO_CORIOLIS_NAME = {
|
|||||||
'Type9': 'type_9_heavy',
|
'Type9': 'type_9_heavy',
|
||||||
'Type9_Military': 'type_10_defender',
|
'Type9_Military': 'type_10_defender',
|
||||||
'TypeX': 'alliance_chieftain',
|
'TypeX': 'alliance_chieftain',
|
||||||
|
'TypeX_2': 'alliance_crusader',
|
||||||
|
'TypeX_3': 'alliance_challenger',
|
||||||
'Viper': 'viper',
|
'Viper': 'viper',
|
||||||
'Viper_MkIV': 'viper_mk_iv',
|
'Viper_MkIV': 'viper_mk_iv',
|
||||||
'Vulture': 'vulture'
|
'Vulture': 'vulture'
|
||||||
@@ -148,15 +153,15 @@ export function shipFromJson(json) {
|
|||||||
|
|
||||||
// Add the bulkheads
|
// Add the bulkheads
|
||||||
const armourJson = json.modules.Armour.module;
|
const armourJson = json.modules.Armour.module;
|
||||||
if (armourJson.name.endsWith('_Armour_Grade1')) {
|
if (armourJson.name.toLowerCase().endsWith('_armour_grade1')) {
|
||||||
ship.useBulkhead(0, true);
|
ship.useBulkhead(0, true);
|
||||||
} else if (armourJson.name.endsWith('_Armour_Grade2')) {
|
} else if (armourJson.name.toLowerCase().endsWith('_armour_grade2')) {
|
||||||
ship.useBulkhead(1, true);
|
ship.useBulkhead(1, true);
|
||||||
} else if (armourJson.name.endsWith('_Armour_Grade3')) {
|
} else if (armourJson.name.toLowerCase().endsWith('_armour_grade3')) {
|
||||||
ship.useBulkhead(2, true);
|
ship.useBulkhead(2, true);
|
||||||
} else if (armourJson.name.endsWith('_Armour_Mirrored')) {
|
} else if (armourJson.name.toLowerCase().endsWith('_armour_mirrored')) {
|
||||||
ship.useBulkhead(3, true);
|
ship.useBulkhead(3, true);
|
||||||
} else if (armourJson.name.endsWith('_Armour_Reactive')) {
|
} else if (armourJson.name.toLowerCase().endsWith('_armour_reactive')) {
|
||||||
ship.useBulkhead(4, true);
|
ship.useBulkhead(4, true);
|
||||||
} else {
|
} else {
|
||||||
throw 'Unknown bulkheads "' + armourJson.name + '"';
|
throw 'Unknown bulkheads "' + armourJson.name + '"';
|
||||||
@@ -318,7 +323,7 @@ function _addModifications(module, modifiers, blueprint, grade, specialModificat
|
|||||||
if (!modifiers) return;
|
if (!modifiers) return;
|
||||||
let special;
|
let special;
|
||||||
if (specialModifications) {
|
if (specialModifications) {
|
||||||
special = Modifications.specials[Object.keys(specialModifications)[0]]
|
special = Modifications.specials[Object.keys(specialModifications)[0]];
|
||||||
}
|
}
|
||||||
for (const i in modifiers) {
|
for (const i in modifiers) {
|
||||||
// Some special modifications
|
// Some special modifications
|
||||||
@@ -345,7 +350,7 @@ function _addModifications(module, modifiers, blueprint, grade, specialModificat
|
|||||||
let value;
|
let value;
|
||||||
if (i === 'OutfittingFieldType_DefenceModifierShieldMultiplier') {
|
if (i === 'OutfittingFieldType_DefenceModifierShieldMultiplier') {
|
||||||
value = modifiers[i].value - 1;
|
value = modifiers[i].value - 1;
|
||||||
} else if (i === 'OutfittingFieldType_DefenceModifierHealthMultiplier' && blueprint.startsWith('Armour_')) {
|
} else if (i === 'OutfittingFieldType_DefenceModifierHealthMultiplier' && blueprint.startsWith('Armour_')) {
|
||||||
value = (modifiers[i].value - module.hullboost) / module.hullboost;
|
value = (modifiers[i].value - module.hullboost) / module.hullboost;
|
||||||
} else if (i === 'OutfittingFieldType_DefenceModifierHealthMultiplier') {
|
} else if (i === 'OutfittingFieldType_DefenceModifierHealthMultiplier') {
|
||||||
value = modifiers[i].value / module.hullboost;
|
value = modifiers[i].value / module.hullboost;
|
||||||
|
|||||||
@@ -1,292 +1,293 @@
|
|||||||
import Ship from '../shipyard/Ship'
|
import Ship from '../shipyard/Ship';
|
||||||
import { HARDPOINT_NUM_TO_CLASS, shipModelFromJson } from './CompanionApiUtils'
|
import { HARDPOINT_NUM_TO_CLASS, shipModelFromJson } from './CompanionApiUtils';
|
||||||
import { Ships } from 'coriolis-data/dist'
|
import { Ships } from 'coriolis-data/dist';
|
||||||
import Module from '../shipyard/Module'
|
import Module from '../shipyard/Module';
|
||||||
import { Modules } from 'coriolis-data/dist'
|
import { Modules } from 'coriolis-data/dist';
|
||||||
import { Modifications } from 'coriolis-data/dist'
|
import { Modifications } from 'coriolis-data/dist';
|
||||||
import { getBlueprint } from './BlueprintFunctions'
|
import { getBlueprint } from './BlueprintFunctions';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtain a module given its FD Name
|
* Obtain a module given its FD Name
|
||||||
* @param {string} fdname the FD Name of the module
|
* @param {string} fdname the FD Name of the module
|
||||||
* @return {Module} the module
|
* @return {Module} the module
|
||||||
*/
|
*/
|
||||||
function _moduleFromFdName (fdname) {
|
function _moduleFromFdName(fdname) {
|
||||||
if (!fdname) return null
|
if (!fdname) return null;
|
||||||
fdname = fdname.toLowerCase()
|
fdname = fdname.toLowerCase();
|
||||||
// Check standard modules
|
// Check standard modules
|
||||||
for (const grp in Modules.standard) {
|
for (const grp in Modules.standard) {
|
||||||
if (Modules.standard.hasOwnProperty(grp)) {
|
if (Modules.standard.hasOwnProperty(grp)) {
|
||||||
for (const i in Modules.standard[grp]) {
|
for (const i in Modules.standard[grp]) {
|
||||||
if (Modules.standard[grp][i].symbol && Modules.standard[grp][i].symbol.toLowerCase() === fdname) {
|
if (Modules.standard[grp][i].symbol && Modules.standard[grp][i].symbol.toLowerCase() === fdname) {
|
||||||
// Found it
|
// Found it
|
||||||
return new Module({template: Modules.standard[grp][i]})
|
return new Module({ template: Modules.standard[grp][i] });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check hardpoint modules
|
// Check hardpoint modules
|
||||||
for (const grp in Modules.hardpoints) {
|
for (const grp in Modules.hardpoints) {
|
||||||
if (Modules.hardpoints.hasOwnProperty(grp)) {
|
if (Modules.hardpoints.hasOwnProperty(grp)) {
|
||||||
for (const i in Modules.hardpoints[grp]) {
|
for (const i in Modules.hardpoints[grp]) {
|
||||||
if (Modules.hardpoints[grp][i].symbol && Modules.hardpoints[grp][i].symbol.toLowerCase() === fdname) {
|
if (Modules.hardpoints[grp][i].symbol && Modules.hardpoints[grp][i].symbol.toLowerCase() === fdname) {
|
||||||
// Found it
|
// Found it
|
||||||
return new Module({template: Modules.hardpoints[grp][i]})
|
return new Module({ template: Modules.hardpoints[grp][i] });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check internal modules
|
// Check internal modules
|
||||||
for (const grp in Modules.internal) {
|
for (const grp in Modules.internal) {
|
||||||
if (Modules.internal.hasOwnProperty(grp)) {
|
if (Modules.internal.hasOwnProperty(grp)) {
|
||||||
for (const i in Modules.internal[grp]) {
|
for (const i in Modules.internal[grp]) {
|
||||||
if (Modules.internal[grp][i].symbol && Modules.internal[grp][i].symbol.toLowerCase() === fdname) {
|
if (Modules.internal[grp][i].symbol && Modules.internal[grp][i].symbol.toLowerCase() === fdname) {
|
||||||
// Found it
|
// Found it
|
||||||
return new Module({template: Modules.internal[grp][i]})
|
return new Module({ template: Modules.internal[grp][i] });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not found
|
// Not found
|
||||||
return null
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build a ship from the journal Loadout event JSON
|
* Build a ship from the journal Loadout event JSON
|
||||||
* @param {object} json the Loadout event JSON
|
* @param {object} json the Loadout event JSON
|
||||||
* @return {Ship} the built ship
|
* @return {Ship} the built ship
|
||||||
*/
|
*/
|
||||||
export function shipFromLoadoutJSON (json) {
|
export function shipFromLoadoutJSON(json) {
|
||||||
// Start off building a basic ship
|
// Start off building a basic ship
|
||||||
const shipModel = shipModelFromJson(json)
|
const shipModel = shipModelFromJson(json);
|
||||||
if (!shipModel) {
|
if (!shipModel) {
|
||||||
throw 'No such ship found: "' + json.Ship + '"'
|
throw 'No such ship found: "' + json.Ship + '"';
|
||||||
}
|
}
|
||||||
const shipTemplate = Ships[shipModel]
|
const shipTemplate = Ships[shipModel];
|
||||||
|
|
||||||
let ship = new Ship(shipModel, shipTemplate.properties, shipTemplate.slots)
|
let ship = new Ship(shipModel, shipTemplate.properties, shipTemplate.slots);
|
||||||
ship.buildWith(null)
|
ship.buildWith(null);
|
||||||
// Initial Ship building, don't do engineering yet.
|
// Initial Ship building, don't do engineering yet.
|
||||||
let opts = [];
|
let modsToAdd = [];
|
||||||
|
|
||||||
for (const module of json.Modules) {
|
for (const module of json.Modules) {
|
||||||
switch (module.Slot) {
|
switch (module.Slot.toLowerCase()) {
|
||||||
// Cargo Hatch.
|
// Cargo Hatch.
|
||||||
case 'CargoHatch':
|
case 'cargohatch':
|
||||||
ship.cargoHatch.enabled = module.On
|
ship.cargoHatch.enabled = module.On;
|
||||||
ship.cargoHatch.priority = module.Priority
|
ship.cargoHatch.priority = module.Priority;
|
||||||
break
|
break;
|
||||||
// Add the bulkheads
|
// Add the bulkheads
|
||||||
case 'Armour':
|
case 'armour':
|
||||||
if (module.Item.toLowerCase().endsWith('_armour_grade1')) {
|
if (module.Item.toLowerCase().endsWith('_armour_grade1')) {
|
||||||
ship.useBulkhead(0, true)
|
ship.useBulkhead(0, true);
|
||||||
} else if (module.Item.toLowerCase().endsWith('_armour_grade2')) {
|
} else if (module.Item.toLowerCase().endsWith('_armour_grade2')) {
|
||||||
ship.useBulkhead(1, true)
|
ship.useBulkhead(1, true);
|
||||||
} else if (module.Item.toLowerCase().endsWith('_armour_grade3')) {
|
} else if (module.Item.toLowerCase().endsWith('_armour_grade3')) {
|
||||||
ship.useBulkhead(2, true)
|
ship.useBulkhead(2, true);
|
||||||
} else if (module.Item.toLowerCase().endsWith('_armour_mirrored')) {
|
} else if (module.Item.toLowerCase().endsWith('_armour_mirrored')) {
|
||||||
ship.useBulkhead(3, true)
|
ship.useBulkhead(3, true);
|
||||||
} else if (module.Item.toLowerCase().endsWith('_armour_reactive')) {
|
} else if (module.Item.toLowerCase().endsWith('_armour_reactive')) {
|
||||||
ship.useBulkhead(4, true)
|
ship.useBulkhead(4, true);
|
||||||
} else {
|
} else {
|
||||||
throw 'Unknown bulkheads "' + module.Item + '"'
|
throw 'Unknown bulkheads "' + module.Item + '"';
|
||||||
}
|
}
|
||||||
ship.bulkheads.enabled = true
|
ship.bulkheads.enabled = true;
|
||||||
if (module.Engineering) _addModifications(ship.bulkheads.m, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level)
|
if (module.Engineering) _addModifications(ship.bulkheads.m, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect);
|
||||||
break
|
break;
|
||||||
case 'PowerPlant':
|
case 'powerplant':
|
||||||
const powerplant = _moduleFromFdName(module.Item)
|
const powerplant = _moduleFromFdName(module.Item);
|
||||||
ship.use(ship.standard[0], powerplant, true)
|
ship.use(ship.standard[0], powerplant, true);
|
||||||
ship.standard[0].enabled = module.On
|
ship.standard[0].enabled = module.On;
|
||||||
ship.standard[0].priority = module.Priority
|
ship.standard[0].priority = module.Priority;
|
||||||
if (module.Engineering) _addModifications(powerplant, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level)
|
if (module.Engineering) _addModifications(powerplant, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect);
|
||||||
break
|
break;
|
||||||
case 'MainEngines':
|
case 'mainengines':
|
||||||
const thrusters = _moduleFromFdName(module.Item)
|
const thrusters = _moduleFromFdName(module.Item);
|
||||||
ship.use(ship.standard[1], thrusters, true)
|
ship.use(ship.standard[1], thrusters, true);
|
||||||
ship.standard[1].enabled = module.On
|
ship.standard[1].enabled = module.On;
|
||||||
ship.standard[1].priority = module.Priority
|
ship.standard[1].priority = module.Priority;
|
||||||
if (module.Engineering) _addModifications(thrusters, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level)
|
if (module.Engineering) _addModifications(thrusters, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect);
|
||||||
break
|
break;
|
||||||
case 'FrameShiftDrive':
|
case 'frameshiftdrive':
|
||||||
const frameshiftdrive = _moduleFromFdName(module.Item)
|
const frameshiftdrive = _moduleFromFdName(module.Item);
|
||||||
ship.use(ship.standard[2], frameshiftdrive, true)
|
ship.use(ship.standard[2], frameshiftdrive, true);
|
||||||
ship.standard[2].enabled = module.On
|
ship.standard[2].enabled = module.On;
|
||||||
ship.standard[2].priority = module.Priority
|
ship.standard[2].priority = module.Priority;
|
||||||
if (module.Engineering) _addModifications(frameshiftdrive, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level)
|
if (module.Engineering) _addModifications(frameshiftdrive, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect);
|
||||||
break
|
break;
|
||||||
case 'LifeSupport':
|
case 'lifesupport':
|
||||||
const lifesupport = _moduleFromFdName(module.Item)
|
const lifesupport = _moduleFromFdName(module.Item);
|
||||||
ship.use(ship.standard[3], lifesupport, true)
|
ship.use(ship.standard[3], lifesupport, true);
|
||||||
ship.standard[3].enabled = module.On === true
|
ship.standard[3].enabled = module.On === true;
|
||||||
ship.standard[3].priority = module.Priority
|
ship.standard[3].priority = module.Priority;
|
||||||
if (module.Engineering) _addModifications(lifesupport, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level)
|
if (module.Engineering) _addModifications(lifesupport, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect);
|
||||||
break
|
break;
|
||||||
case 'PowerDistributor':
|
case 'powerdistributor':
|
||||||
const powerdistributor = _moduleFromFdName(module.Item)
|
const powerdistributor = _moduleFromFdName(module.Item);
|
||||||
ship.use(ship.standard[4], powerdistributor, true)
|
ship.use(ship.standard[4], powerdistributor, true);
|
||||||
ship.standard[4].enabled = module.On
|
ship.standard[4].enabled = module.On;
|
||||||
ship.standard[4].priority = module.Priority
|
ship.standard[4].priority = module.Priority;
|
||||||
if (module.Engineering) _addModifications(powerdistributor, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level)
|
if (module.Engineering) _addModifications(powerdistributor, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect);
|
||||||
break
|
break;
|
||||||
case 'Radar':
|
case 'radar':
|
||||||
const sensors = _moduleFromFdName(module.Item)
|
const sensors = _moduleFromFdName(module.Item);
|
||||||
ship.use(ship.standard[5], sensors, true)
|
ship.use(ship.standard[5], sensors, true);
|
||||||
ship.standard[5].enabled = module.On
|
ship.standard[5].enabled = module.On;
|
||||||
ship.standard[5].priority = module.Priority
|
ship.standard[5].priority = module.Priority;
|
||||||
if (module.Engineering) _addModifications(sensors, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level)
|
if (module.Engineering) _addModifications(sensors, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect);
|
||||||
break
|
break;
|
||||||
case 'FuelTank':
|
case 'fueltank':
|
||||||
const fueltank = _moduleFromFdName(module.Item)
|
const fueltank = _moduleFromFdName(module.Item);
|
||||||
ship.use(ship.standard[6], fueltank, true)
|
ship.use(ship.standard[6], fueltank, true);
|
||||||
ship.standard[6].enabled = true
|
ship.standard[6].enabled = true;
|
||||||
ship.standard[6].priority = 0
|
ship.standard[6].priority = 0;
|
||||||
break
|
break;
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
for (const module of json.Modules) {
|
if (module.Slot.toLowerCase().search(/hardpoint/) !== -1) {
|
||||||
if (module.Slot.search(/Hardpoint/) !== -1) {
|
// Add hardpoints
|
||||||
// Add hardpoints
|
let hardpoint;
|
||||||
let hardpoint;
|
let hardpointClassNum = -1;
|
||||||
let hardpointClassNum = -1
|
let hardpointSlotNum = -1;
|
||||||
let hardpointSlotNum = -1
|
let hardpointArrayNum = 0;
|
||||||
let hardpointArrayNum = 0
|
for (let i in shipTemplate.slots.hardpoints) {
|
||||||
for (let i in shipTemplate.slots.hardpoints) {
|
if (shipTemplate.slots.hardpoints[i] === hardpointClassNum) {
|
||||||
if (shipTemplate.slots.hardpoints[i] === hardpointClassNum) {
|
// Another slot of the same class
|
||||||
// Another slot of the same class
|
hardpointSlotNum++;
|
||||||
hardpointSlotNum++
|
} else {
|
||||||
} else {
|
// The first slot of a new class
|
||||||
// The first slot of a new class
|
hardpointClassNum = shipTemplate.slots.hardpoints[i];
|
||||||
hardpointClassNum = shipTemplate.slots.hardpoints[i]
|
hardpointSlotNum = 1;
|
||||||
hardpointSlotNum = 1
|
}
|
||||||
}
|
|
||||||
|
// Now that we know what we're looking for, find it
|
||||||
// Now that we know what we're looking for, find it
|
const hardpointName = HARDPOINT_NUM_TO_CLASS[hardpointClassNum] + 'Hardpoint' + hardpointSlotNum;
|
||||||
const hardpointName = HARDPOINT_NUM_TO_CLASS[hardpointClassNum] + 'Hardpoint' + hardpointSlotNum
|
const hardpointSlot = json.Modules.find(elem => elem.Slot.toLowerCase() === hardpointName.toLowerCase());
|
||||||
const hardpointSlot = json.Modules.find(elem => elem.Slot === hardpointName)
|
if (!hardpointSlot) {
|
||||||
if (!hardpointSlot) {
|
// This can happen with old imports that don't contain new hardpoints
|
||||||
// This can happen with old imports that don't contain new hardpoints
|
} else if (!hardpointSlot) {
|
||||||
} else if (!hardpointSlot) {
|
// No module
|
||||||
// No module
|
} else {
|
||||||
} else {
|
hardpoint = _moduleFromFdName(hardpointSlot.Item);
|
||||||
hardpoint = _moduleFromFdName(hardpointSlot.Item)
|
ship.use(ship.hardpoints[hardpointArrayNum], hardpoint, true);
|
||||||
ship.use(ship.hardpoints[hardpointArrayNum], hardpoint, true)
|
ship.hardpoints[hardpointArrayNum].enabled = hardpointSlot.On;
|
||||||
ship.hardpoints[hardpointArrayNum].enabled = hardpointSlot.On
|
ship.hardpoints[hardpointArrayNum].priority = hardpointSlot.Priority;
|
||||||
ship.hardpoints[hardpointArrayNum].priority = hardpointSlot.Priority
|
modsToAdd.push({ coriolisMod: hardpoint, json: hardpointSlot });
|
||||||
opts.push({coriolisMod: hardpoint, json: hardpointSlot});
|
}
|
||||||
}
|
hardpointArrayNum++;
|
||||||
hardpointArrayNum++
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (module.Slot.search(/Slot\d/) !== -1) {
|
|
||||||
let internalSlotNum = 1
|
let internalSlotNum = 0;
|
||||||
let militarySlotNum = 1
|
let militarySlotNum = 1;
|
||||||
for (let i in shipTemplate.slots.internal) {
|
for (let i in shipTemplate.slots.internal) {
|
||||||
const isMilitary = isNaN(shipTemplate.slots.internal[i]) ? shipTemplate.slots.internal[i].name = 'Military' : false
|
if (!shipTemplate.slots.internal.hasOwnProperty(i)) {
|
||||||
|
continue;
|
||||||
// The internal slot might be a standard or a military slot. Military slots have a different naming system
|
}
|
||||||
let internalSlot = null
|
const isMilitary = isNaN(shipTemplate.slots.internal[i]) ? shipTemplate.slots.internal[i].name == 'Military' : false;
|
||||||
if (isMilitary) {
|
|
||||||
const internalName = 'Military0' + militarySlotNum
|
// The internal slot might be a standard or a military slot. Military slots have a different naming system
|
||||||
internalSlot = json.Modules.find(elem => elem.Slot === internalName)
|
let internalSlot = null;
|
||||||
militarySlotNum++
|
if (isMilitary) {
|
||||||
} else {
|
const internalName = 'Military0' + militarySlotNum;
|
||||||
// Slot numbers are not contiguous so handle skips.
|
internalSlot = json.Modules.find(elem => elem.Slot.toLowerCase() === internalName.toLowerCase());
|
||||||
while (internalSlot === null && internalSlotNum < 99) {
|
militarySlotNum++;
|
||||||
// Slot sizes have no relationship to the actual size, either, so check all possibilities
|
} else {
|
||||||
for (let slotsize = 0; slotsize < 9; slotsize++) {
|
// Slot numbers are not contiguous so handle skips.
|
||||||
const internalName = 'Slot' + (internalSlotNum <= 9 ? '0' : '') + internalSlotNum + '_Size' + slotsize
|
for (; internalSlot === null && internalSlotNum < 99; internalSlotNum++) {
|
||||||
if (json.Modules.find(elem => elem.Slot === internalName)) {
|
// Slot sizes have no relationship to the actual size, either, so check all possibilities
|
||||||
internalSlot = json.Modules.find(elem => elem.Slot === internalName);
|
for (let slotsize = 0; slotsize < 9; slotsize++) {
|
||||||
break
|
const internalName = 'Slot' + (internalSlotNum <= 9 ? '0' : '') + internalSlotNum + '_Size' + slotsize;
|
||||||
}
|
if (json.Modules.find(elem => elem.Slot.toLowerCase() === internalName.toLowerCase())) {
|
||||||
}
|
internalSlot = json.Modules.find(elem => elem.Slot.toLowerCase() === internalName.toLowerCase());
|
||||||
internalSlotNum++
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (!internalSlot) {
|
}
|
||||||
// This can happen with old imports that don't contain new slots
|
|
||||||
} else if (!internalSlot) {
|
if (!internalSlot) {
|
||||||
// No module
|
// This can happen with old imports that don't contain new slots
|
||||||
} else {
|
} else {
|
||||||
const internalJson = internalSlot
|
const internalJson = internalSlot;
|
||||||
const internal = _moduleFromFdName(internalJson.Item)
|
const internal = _moduleFromFdName(internalJson.Item);
|
||||||
ship.use(ship.internal[i], internal, true)
|
ship.use(ship.internal[i], internal, true);
|
||||||
ship.internal[i].enabled = internalJson.On === true
|
ship.internal[i].enabled = internalJson.On === true;
|
||||||
ship.internal[i].priority = internalJson.Priority
|
ship.internal[i].priority = internalJson.Priority;
|
||||||
opts.push({coriolisMod: internal, json: internalSlot});
|
modsToAdd.push({ coriolisMod: internal, json: internalSlot });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
for (const i of modsToAdd) {
|
||||||
}
|
if (i.json.Engineering) {
|
||||||
|
_addModifications(i.coriolisMod, i.json.Engineering.Modifiers, i.json.Engineering.BlueprintName, i.json.Engineering.Level, i.json.Engineering.ExperimentalEffect);
|
||||||
for (const i of opts) {
|
}
|
||||||
if (i.json.Engineering) _addModifications(i.coriolisMod, i.json.Engineering.Modifiers, i.json.Engineering.BlueprintName, i.json.Engineering.Level, i.json.Engineering.ExperimentalEffect)
|
}
|
||||||
}
|
// We don't have any information on it so guess it's priority 5 and disabled
|
||||||
// We don't have any information on it so guess it's priority 5 and disabled
|
if (!ship.cargoHatch) {
|
||||||
if (!ship.cargoHatch) {
|
ship.cargoHatch.enabled = false;
|
||||||
ship.cargoHatch.enabled = false
|
ship.cargoHatch.priority = 4;
|
||||||
ship.cargoHatch.priority = 4
|
}
|
||||||
}
|
|
||||||
console.log(ship)
|
// Now update the ship's codes before returning it
|
||||||
|
return ship.updatePowerPrioritesString().updatePowerEnabledString().updateModificationsString();
|
||||||
// Now update the ship's codes before returning it
|
}
|
||||||
return ship.updatePowerPrioritesString().updatePowerEnabledString().updateModificationsString()
|
|
||||||
}
|
/**
|
||||||
|
* Add the modifications for a module
|
||||||
/**
|
* @param {Module} module the module
|
||||||
* Add the modifications for a module
|
* @param {Object} modifiers the modifiers
|
||||||
* @param {Module} module the module
|
* @param {Object} blueprint the blueprint of the modification
|
||||||
* @param {Object} modifiers the modifiers
|
* @param {Object} grade the grade of the modification
|
||||||
* @param {Object} blueprint the blueprint of the modification
|
* @param {Object} specialModifications special modification
|
||||||
* @param {Object} grade the grade of the modification
|
*/
|
||||||
* @param {Object} specialModifications special modification
|
function _addModifications(module, modifiers, blueprint, grade, specialModifications) {
|
||||||
*/
|
if (!modifiers) return;
|
||||||
function _addModifications (module, modifiers, blueprint, grade, specialModifications) {
|
let special;
|
||||||
if (!modifiers) return
|
if (specialModifications) {
|
||||||
let special
|
special = Modifications.specials[specialModifications];
|
||||||
if (specialModifications) {
|
}
|
||||||
special = Modifications.specials[specialModifications]
|
// Add the blueprint definition, grade and special
|
||||||
}
|
if (blueprint) {
|
||||||
for (const i in modifiers) {
|
module.blueprint = getBlueprint(blueprint, module);
|
||||||
// Some special modifications
|
if (grade) {
|
||||||
// Look up the modifiers to find what we need to do
|
module.blueprint.grade = Number(grade);
|
||||||
const findMod = val => Object.keys(Modifications.modifierActions).find(elem => elem.toString().toLowerCase().search(val.toString().toLowerCase().replace(/(OutfittingFieldType_|persecond)/igm, '')) >= 0)
|
}
|
||||||
const modifierActions = Modifications.modifierActions[findMod(modifiers[i].Label)]
|
if (special) {
|
||||||
//TODO: Figure out how to scale this value.
|
module.blueprint.special = special;
|
||||||
if (!!modifiers[i].LessIsGood) {
|
}
|
||||||
|
}
|
||||||
}
|
for (const i in modifiers) {
|
||||||
let value = (modifiers[i].Value / modifiers[i].OriginalValue * 100 - 100) * 100;
|
// Some special modifications
|
||||||
if (value === Infinity) {
|
// Look up the modifiers to find what we need to do
|
||||||
value = modifiers[i].Value * 100;
|
const findMod = val => Object.keys(Modifications.modifierActions).find(elem => elem.toString().toLowerCase().replace(/(outfittingfieldtype_|persecond)/igm, '') === val.toString().toLowerCase().replace(/(outfittingfieldtype_|persecond)/igm, ''));
|
||||||
}
|
const modifierActions = Modifications.modifierActions[findMod(modifiers[i].Label)];
|
||||||
if (modifiers[i].Label.search('Resistance') >= 0) {
|
// TODO: Figure out how to scale this value.
|
||||||
value = (modifiers[i].Value * 100) - (modifiers[i].OriginalValue * 100)
|
if (!!modifiers[i].LessIsGood) {
|
||||||
}
|
|
||||||
// Carry out the required changes
|
}
|
||||||
for (const action in modifierActions) {
|
let value = (modifiers[i].Value / modifiers[i].OriginalValue * 100 - 100) * 100;
|
||||||
if (isNaN(modifierActions[action])) {
|
if (value === Infinity) {
|
||||||
module.setModValue(action, modifierActions[action])
|
value = modifiers[i].Value * 100;
|
||||||
} else {
|
}
|
||||||
module.setModValue(action, value)
|
if (modifiers[i].Label.search('Resistance') >= 0) {
|
||||||
}
|
value = (modifiers[i].Value * 100) - (modifiers[i].OriginalValue * 100);
|
||||||
}
|
}
|
||||||
}
|
if (modifiers[i].Label.search('ShieldMultiplier') >= 0 || modifiers[i].Label.search('DefenceModifierHealthMultiplier') >= 0) {
|
||||||
|
value = ((100 + modifiers[i].Value) / (100 + modifiers[i].OriginalValue) * 100 - 100) * 100;
|
||||||
// Add the blueprint definition, grade and special
|
}
|
||||||
if (blueprint) {
|
|
||||||
module.blueprint = getBlueprint(blueprint, module)
|
// Carry out the required changes
|
||||||
if (grade) {
|
for (const action in modifierActions) {
|
||||||
module.blueprint.grade = Number(grade)
|
if (isNaN(modifierActions[action])) {
|
||||||
}
|
module.setModValue(action, modifierActions[action]);
|
||||||
if (special) {
|
} else {
|
||||||
module.blueprint.special = special
|
module.setModValue(action, value, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,14 +1,23 @@
|
|||||||
import request from 'superagent';
|
import request from 'superagent';
|
||||||
|
|
||||||
|
let agent;
|
||||||
|
try {
|
||||||
|
agent = request.agent(); // apparently this crashes somehow
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
if (!agent) {
|
||||||
|
agent = request;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shorten a URL
|
* Shorten a URL
|
||||||
* @param {string} url The URL to shorten
|
* @param {string} url The URL to shorten
|
||||||
* @param {function} success Success callback
|
* @param {function} success Success callback
|
||||||
* @param {function} error Failure/Error callback
|
* @param {function} error Failure/Error callback
|
||||||
*/
|
*/
|
||||||
export default function shorternUrl(url, success, error) {
|
export default function shorternUrl(url, success, error) {
|
||||||
shortenUrlEddp(url, success, error);
|
orbisShorten(url, success, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
const SHORTEN_API_GOOGLE = 'https://www.googleapis.com/urlshortener/v1/url?key=';
|
const SHORTEN_API_GOOGLE = 'https://www.googleapis.com/urlshortener/v1/url?key=';
|
||||||
@@ -64,3 +73,68 @@ function shortenUrlEddp(url, success, error) {
|
|||||||
error('Not Online');
|
error('Not Online');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SHORTEN_API_ORBIS = 'https://s.orbis.zone/api.php';
|
||||||
|
/**
|
||||||
|
* Shorten a URL using Orbis's URL shortener API
|
||||||
|
* @param {string} url The URL to shorten
|
||||||
|
* @param {function} success Success callback
|
||||||
|
* @param {function} error Failure/Error callback
|
||||||
|
*/
|
||||||
|
function orbisShorten(url, success, error) {
|
||||||
|
if (window.navigator.onLine) {
|
||||||
|
try {
|
||||||
|
request.post(SHORTEN_API_ORBIS)
|
||||||
|
.field('action', 'shorturl')
|
||||||
|
.field('url', url)
|
||||||
|
.field('format', 'json')
|
||||||
|
.end(function(err, response) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
error('Bad Request');
|
||||||
|
} else {
|
||||||
|
success(response.body.shorturl);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
error(e.message ? e.message : e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error('Not Online');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const API_ORBIS = 'https://orbis.zone/api/builds/add';
|
||||||
|
/**
|
||||||
|
* Upload to Orbis
|
||||||
|
* @param {object} ship The URL to shorten
|
||||||
|
* @param {object} creds Orbis credentials
|
||||||
|
* @return {Promise<any>} Either a URL or error message.
|
||||||
|
*/
|
||||||
|
export function orbisUpload(ship, creds) {
|
||||||
|
return new Promise(async(resolve, reject) => {
|
||||||
|
if (window.navigator.onLine) {
|
||||||
|
try {
|
||||||
|
agent
|
||||||
|
.post(API_ORBIS)
|
||||||
|
.withCredentials()
|
||||||
|
.redirects(0)
|
||||||
|
.set('Content-Type', 'application/json')
|
||||||
|
.send(ship)
|
||||||
|
.end(function(err, response) {
|
||||||
|
if (err) {
|
||||||
|
reject('Bad Request');
|
||||||
|
} else {
|
||||||
|
resolve(response.body.link);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
reject(e.message ? e.message : e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reject('Not Online');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import cn from 'classnames';
|
|
||||||
import Module from '../shipyard/Module';
|
|
||||||
import { Infinite } from '../components/SvgIcons';
|
|
||||||
import Persist from '../stores/Persist';
|
import Persist from '../stores/Persist';
|
||||||
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
||||||
|
|
||||||
@@ -148,11 +145,11 @@ export function diffDetails(language, m, mm) {
|
|||||||
if (m.grp === 'pp') {
|
if (m.grp === 'pp') {
|
||||||
let mPowerGeneration = m.pgen || 0;
|
let mPowerGeneration = m.pgen || 0;
|
||||||
let mmPowerGeneration = mm ? mm.getPowerGeneration() : 0;
|
let mmPowerGeneration = mm ? mm.getPowerGeneration() : 0;
|
||||||
if (mPowerGeneration != mmPowerGeneration) propDiffs.push(<div key='pgen'>{translate('pgen')}: <span className={diffClass(mPowerGeneration, mmPowerGeneration)}>{diff(formats.round, mPowerGeneration, mmPowerGeneration)}{units.MJ}</span></div>);
|
if (mPowerGeneration != mmPowerGeneration) propDiffs.push(<div key='pgen'>{translate('pgen')}: <span className={diffClass(mPowerGeneration, mmPowerGeneration)}>{diff(formats.round, mPowerGeneration, mmPowerGeneration)}{units.MW}</span></div>);
|
||||||
} else {
|
} else {
|
||||||
let mPowerUsage = m.power || 0;
|
let mPowerUsage = m.power || 0;
|
||||||
let mmPowerUsage = mm ? mm.getPowerUsage() || 0 : 0;
|
let mmPowerUsage = mm ? mm.getPowerUsage() || 0 : 0;
|
||||||
if (mPowerUsage != mmPowerUsage) propDiffs.push(<div key='power'>{translate('power')}: <span className={diffClass(mPowerUsage, mmPowerUsage, true)}>{diff(formats.round, mPowerUsage, mmPowerUsage)}{units.MJ}</span></div>);
|
if (mPowerUsage != mmPowerUsage) propDiffs.push(<div key='power'>{translate('power')}: <span className={diffClass(mPowerUsage, mmPowerUsage, true)}>{diff(formats.round, mPowerUsage, mmPowerUsage)}{units.MW}</span></div>);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mDps = m.damage * (m.rpshot || 1) * (m.rof || 1);
|
let mDps = m.damage * (m.rpshot || 1) * (m.rof || 1);
|
||||||
|
|||||||
61
src/clippy.css
Normal file
61
src/clippy.css
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
.clippy, .clippy-balloon {
|
||||||
|
position: fixed;
|
||||||
|
z-index: 1000;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clippy-balloon {
|
||||||
|
|
||||||
|
background: #FFC;
|
||||||
|
color: black;
|
||||||
|
padding: 8px;
|
||||||
|
border: 1px solid black;
|
||||||
|
border-radius: 5px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.clippy-content {
|
||||||
|
max-width: 200px;
|
||||||
|
min-width: 120px;
|
||||||
|
font-family: "Microsoft Sans", sans-serif;
|
||||||
|
font-size: 10pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clippy-tip {
|
||||||
|
width: 10px;
|
||||||
|
height: 16px;
|
||||||
|
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAgCAMAAAAlvKiEAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAlQTFRF///MAAAA////52QwgAAAAAN0Uk5T//8A18oNQQAAAGxJREFUeNqs0kEOwCAIRFHn3//QTUU6xMyyxii+jQosrTPkyPEM6IN3FtzIRk1U4dFeKWQiH6pRRowMVKEmvronEynkwj0uZJgR22+YLopPSo9P34wJSamLSU7lSIWLJU7NkNomNlhqxUeAAQC+TQLZyEuJBwAAAABJRU5ErkJggg==) no-repeat;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clippy-top-left .clippy-tip {
|
||||||
|
top: 100%;
|
||||||
|
margin-top: 0px;
|
||||||
|
left: 100%;
|
||||||
|
margin-left: -50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clippy-top-right .clippy-tip {
|
||||||
|
top: 100%;
|
||||||
|
margin-top: 0px;
|
||||||
|
left: 0;
|
||||||
|
margin-left: 50px;
|
||||||
|
background-position: -10px 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.clippy-bottom-right .clippy-tip {
|
||||||
|
top: 0;
|
||||||
|
margin-top: -16px;
|
||||||
|
left: 0;
|
||||||
|
margin-left: 50px;
|
||||||
|
background-position: -10px -16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clippy-bottom-left .clippy-tip {
|
||||||
|
top: 0;
|
||||||
|
margin-top: -16px;
|
||||||
|
left: 100%;
|
||||||
|
margin-left: -50px;
|
||||||
|
background-position: 0px -16px;
|
||||||
|
}
|
||||||
1
src/clippy.min.js
vendored
Normal file
1
src/clippy.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
9
src/iframe.html
Normal file
9
src/iframe.html
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script src="xdLocalStoragePostMessageApi.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
This is the magical iframe
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
"density": "4.0"
|
"density": "4.0"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"start_url": "https:\/\/edcd.coriolis.io",
|
"start_url": "https:\/\/coriolis.io",
|
||||||
"display": "standalone",
|
"display": "standalone",
|
||||||
"orientation": "portrait"
|
"orientation": "portrait"
|
||||||
}
|
}
|
||||||
|
|||||||
108
src/index.ejs
108
src/index.ejs
@@ -1,16 +1,19 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html <%= htmlWebpackPlugin.options.appCache ? 'manifest=/' + htmlWebpackPlugin.options.appCache : '' %> >
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Coriolis EDCD Edition</title>
|
<title>Coriolis EDCD Edition</title>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<link rel="stylesheet" href="<%= htmlWebpackPlugin.files.css[0] %>">
|
<link rel="stylesheet" href="<%= htmlWebpackPlugin.files.css[0] %>">
|
||||||
<!-- Standard headers -->
|
<!-- Standard headers -->
|
||||||
<meta name="description" content="A ship builder, outfitting and comparison tool for Elite Dangerous">
|
<meta name="description" content="A ship builder, outfitting and comparison
|
||||||
|
tool for Elite Dangerous">
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0,
|
||||||
|
maximum-scale=1.0, user-scalable=0">
|
||||||
<link rel="manifest" href="/manifest.json">
|
<link rel="manifest" href="/manifest.json">
|
||||||
<link rel="shortcut icon" href=/favicon2.ico>
|
<link rel="shortcut icon" href=/favicon2.ico>
|
||||||
<link rel="icon" sizes="152x152 192x192" type="image/png" href="/192x192.png">
|
<link rel="icon" sizes="152x152 192x192" type="image/png"
|
||||||
|
href="/192x192.png">
|
||||||
|
|
||||||
<!-- Apple/iOS headers -->
|
<!-- Apple/iOS headers -->
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
@@ -26,45 +29,74 @@
|
|||||||
<script>
|
<script>
|
||||||
window.CORIOLIS_GAPI_KEY = '<%- htmlWebpackPlugin.options.gapiKey %>';
|
window.CORIOLIS_GAPI_KEY = '<%- htmlWebpackPlugin.options.gapiKey %>';
|
||||||
window.CORIOLIS_VERSION = '<%- htmlWebpackPlugin.options.version %>';
|
window.CORIOLIS_VERSION = '<%- htmlWebpackPlugin.options.version %>';
|
||||||
window.CORIOLIS_DATE = '<%- new Date().toISOString().slice(0, 10) %>';
|
window.CORIOLIS_DATE = '<%- htmlWebpackPlugin.options.date.toISOString().slice(0, 10) %>';
|
||||||
|
window.BUGSNAG_VERSION = '<%- htmlWebpackPlugin.options.version + '-' + htmlWebpackPlugin.options.date.toISOString() %>';
|
||||||
</script>
|
</script>
|
||||||
<% if (htmlWebpackPlugin.options.uaTracking) { %>
|
<% if (htmlWebpackPlugin.options.uaTracking) { %>
|
||||||
<script>
|
<script>
|
||||||
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
|
window.ga = window.ga || function() {(ga.q = ga.q || []).push(arguments);};
|
||||||
ga('create', '<%- htmlWebpackPlugin.options.uaTracking %>', 'auto');
|
ga.l = +new Date;
|
||||||
ga('send', 'pageview');
|
ga('create', '<%- htmlWebpackPlugin.options.uaTracking %>', 'auto');
|
||||||
</script>
|
ga('send', 'pageview');
|
||||||
<script async src='https://www.google-analytics.com/analytics.js'></script>
|
</script>
|
||||||
|
<script async src='https://www.google-analytics.com/analytics.js'></script>
|
||||||
<% } %>
|
<% } %>
|
||||||
|
|
||||||
<!-- Piwik -->
|
<!-- Piwik -->
|
||||||
<script type="text/javascript">
|
<!-- <script type="text/javascript">
|
||||||
var _paq = _paq || [];
|
var _paq = _paq || [];
|
||||||
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
|
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
|
||||||
_paq.push(["setCookieDomain", "*.coriolis.edcd.io"]);
|
_paq.push(["setCookieDomain", "*.coriolis.edcd.io"]);
|
||||||
_paq.push(['trackPageView']);
|
_paq.push(['trackPageView']);
|
||||||
_paq.push(['enableLinkTracking']);
|
_paq.push(['enableLinkTracking']);
|
||||||
(function() {
|
(function() {
|
||||||
var u="//stats.isadankme.me/";
|
var u="//stats.isadankme.me/";
|
||||||
_paq.push(['setTrackerUrl', u+'piwik.php']);
|
_paq.push(['setTrackerUrl', u+'piwik.php']);
|
||||||
_paq.push(['setSiteId', '4']);
|
_paq.push(['setSiteId', '4']);
|
||||||
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
||||||
g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s);
|
g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s);
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>-->
|
||||||
<!-- End Piwik Code -->
|
<!-- End Piwik Code -->
|
||||||
|
|
||||||
<!-- Bugsnag -->
|
<!-- Bugsnag -->
|
||||||
<script src="//d2wy8f7a9ursnm.cloudfront.net/v4/bugsnag.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.js"></script>
|
||||||
<script src="//d2wy8f7a9ursnm.cloudfront.net/bugsnag-plugins/v1/bugsnag-react.min.js"></script>
|
<script src="https://d2wy8f7a9ursnm.cloudfront.net/v5.0.0/bugsnag.min.js"></script>
|
||||||
<script>
|
<script src="/clippy.min.js"></script>
|
||||||
window.bugsnagClient = bugsnag('ba9fae819372850fb660755341fa6ef5', {appVersion: window.CORIOLIS_VERSION || undefined})
|
<script type="text/javascript">
|
||||||
window.Bugsnag = window.bugsnagClient
|
const advice = ['When all else fails, Put a docking computer in every slot. My name is Clippy.',
|
||||||
|
'Fly in a stright line and roll, your evasion increases by 200%!',
|
||||||
|
'Boost!!!',
|
||||||
|
'If you are running out of power, consider deactivating thrusters!',
|
||||||
|
'Make sure to set fire-groups for your SCBs!',
|
||||||
|
'If you find yourself dying often - git gud!',
|
||||||
|
'Remember, shield boosters increase skill!',
|
||||||
|
'Remember, B rated drives offer greater speed!',
|
||||||
|
'Point directly into the jet cone and full throttle for optimal FSD boost.',
|
||||||
|
'Raxxla can only be found in open play.',
|
||||||
|
'Try silent running when under focus to drop aggro, and reboot when your opponents lose sight of you!'];
|
||||||
|
clippy.load('Clippy', function(agent) {
|
||||||
|
// do anything with the loaded agent
|
||||||
|
agent.show();
|
||||||
|
|
||||||
|
setInterval(() => {
|
||||||
|
agent.animate();
|
||||||
|
const toSpeak = advice[Math.random() * advice.length >> 0];
|
||||||
|
agent.speak(toSpeak);
|
||||||
|
}, (20000));
|
||||||
|
setInterval(() => {
|
||||||
|
agent.animate();
|
||||||
|
agent.speak('@everyone FDL. :pray:');
|
||||||
|
}, (69000));
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
</head>
|
<script>
|
||||||
<body style="background-color:#000;">
|
window.bugsnagClient = bugsnag('ba9fae819372850fb660755341fa6ef5', { appVersion: window.BUGSNAG_VERSION || undefined });
|
||||||
<section id="coriolis"></section>
|
window.Bugsnag = window.bugsnagClient;
|
||||||
<script src="<%= htmlWebpackPlugin.files.chunks.lib.entry %>" charset="utf-8" crossorigin="anonymous"></script>
|
</script>
|
||||||
<script src="<%= htmlWebpackPlugin.files.chunks.app.entry %>" charset="utf-8" crossorigin="anonymous"></script>
|
</head>
|
||||||
</body>
|
<body style="background-color:#000;">
|
||||||
|
<section id="coriolis"></section>
|
||||||
|
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -171,3 +171,18 @@ footer {
|
|||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.announcement-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding-top: 10px;
|
||||||
|
justify-content: center;
|
||||||
|
flex-flow: row wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.announcement {
|
||||||
|
border: 1px @secondary solid;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@import "../../node_modules/clippy.js/build/clippy.css";
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user