mirror of
https://github.com/EDCD/coriolis.git
synced 2025-12-09 14:45:35 +00:00
Compare commits
437 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d218101708 | ||
|
|
608ce12156 | ||
|
|
80d653483a | ||
|
|
f9a1e5afd9 | ||
|
|
7f5181a9c7 | ||
|
|
93d8f15f26 | ||
|
|
06d8930777 | ||
|
|
23f4024e09 | ||
|
|
a5e9a71037 | ||
|
|
423d42bc8a | ||
|
|
4075bab3d0 | ||
|
|
469f7a0a48 | ||
|
|
25d9f8ec61 | ||
|
|
1151bd1614 | ||
|
|
2456ce330b | ||
|
|
f86ce62c27 | ||
|
|
c295a9f4e4 | ||
|
|
c4186faa4a | ||
|
|
00f4889c93 | ||
|
|
b6c83f4aba | ||
|
|
3fa040c210 | ||
|
|
8c4957c21e | ||
|
|
a178c59aa3 | ||
|
|
0d9fcd97d7 | ||
|
|
b02ca117be | ||
|
|
ca2e2a22dd | ||
|
|
035e217124 | ||
|
|
6424ab283a | ||
|
|
f30f10c904 | ||
|
|
8ce09a607f | ||
|
|
5b48e30798 | ||
|
|
0d7e6af2e6 | ||
|
|
be3ca23aee | ||
|
|
28f464ba5a | ||
|
|
8cedd1b8bc | ||
|
|
56cbc49d04 | ||
|
|
5d1ccd2a20 | ||
|
|
f898fbc55e | ||
|
|
63acad3aeb | ||
|
|
5b4b0b8dff | ||
|
|
8492dd74f9 | ||
|
|
77e652cd34 | ||
|
|
5069d7e464 | ||
|
|
8b3e9c0f63 | ||
|
|
99e76e480b | ||
|
|
c161ed2298 | ||
|
|
464770f096 | ||
|
|
9fba1be814 | ||
|
|
b419b8b104 | ||
|
|
0cae955ca2 | ||
|
|
ec70ad5d29 | ||
|
|
8a386c4ece | ||
|
|
013460ada4 | ||
|
|
d46ad89dc5 | ||
|
|
86c53c8e46 | ||
|
|
df14786e79 | ||
|
|
24f206ad82 | ||
|
|
242c3efe45 | ||
|
|
07a848b906 | ||
|
|
1b5402fd2d | ||
|
|
903d791549 | ||
|
|
f03c164f1c | ||
|
|
107bab0192 | ||
|
|
41f5ebb2f1 | ||
|
|
266090dc2a | ||
|
|
83f29f3d62 | ||
|
|
3e6137ca96 | ||
|
|
58510bbd22 | ||
|
|
2f7be75bcf | ||
|
|
0aecbbf892 | ||
|
|
9ef2f4179b | ||
|
|
afbfe3ea12 | ||
|
|
b145a2da85 | ||
|
|
a679f7dd98 | ||
|
|
f343366ea1 | ||
|
|
1bbea7dda0 | ||
|
|
da097e0955 | ||
|
|
1307474755 | ||
|
|
80f5a95297 | ||
|
|
e619966679 | ||
|
|
3f94382925 | ||
|
|
ea7a6f8872 | ||
|
|
4e87b3a0b8 | ||
|
|
8463dd46f7 | ||
|
|
3febe465f6 | ||
|
|
45b834c424 | ||
|
|
1ca041097b | ||
|
|
587f7acd5b | ||
|
|
9e64e4a26b | ||
|
|
96e09ab36c | ||
|
|
4268d3f07b | ||
|
|
1767a2aed5 | ||
|
|
9f061506bb | ||
|
|
0eb9a2048d | ||
|
|
faeafb2402 | ||
|
|
2a0acfa6cb | ||
|
|
0ac44ac267 | ||
|
|
b9ae3a4d5a | ||
|
|
b1aefb0003 | ||
|
|
f6a41ec55c | ||
|
|
39650cc584 | ||
|
|
d837287da7 | ||
|
|
6e718a39d1 | ||
|
|
2e7db02238 | ||
|
|
dbfd108819 | ||
|
|
82c0f6fc0f | ||
|
|
9e6bbaa67d | ||
|
|
012a203c4a | ||
|
|
c7ba129ed7 | ||
|
|
fbe57d00db | ||
|
|
dc4fdf215c | ||
|
|
2166ac1584 | ||
|
|
504756de09 | ||
|
|
90f04dba94 | ||
|
|
bfd7645fb7 | ||
|
|
c37c6983c3 | ||
|
|
736c700aa0 | ||
|
|
de1cb901fc | ||
|
|
3d9d6397f6 | ||
|
|
dbe836729f | ||
|
|
82fa93e676 | ||
|
|
90a3392b80 | ||
|
|
b7f715bd5e | ||
|
|
5f88f46770 | ||
|
|
2244c91a64 | ||
|
|
78134404c3 | ||
|
|
c9cd6b175d | ||
|
|
5f70d283e0 | ||
|
|
cd1e27fd11 | ||
|
|
0fd1c19514 | ||
|
|
a82dffd77d | ||
|
|
d3eed87077 | ||
|
|
d99f8ad7e7 | ||
|
|
e933305407 | ||
|
|
690bc5a64a | ||
|
|
d2380a5c9c | ||
|
|
c2d0a6e9e0 | ||
|
|
a8c44fddca | ||
|
|
0e6b306bdc | ||
|
|
9b2a47ba0c | ||
|
|
3038ebc1c7 | ||
|
|
5a48d5b400 | ||
|
|
f36edfe98d | ||
|
|
3e80d6e13b | ||
|
|
c8f0a1ccc6 | ||
|
|
79325a3129 | ||
|
|
1ca0fffe5e | ||
|
|
f5b9842b2e | ||
|
|
72ea3f125e | ||
|
|
f7a2408e44 | ||
|
|
c05de9a085 | ||
|
|
fc657f3b2d | ||
|
|
e388de0364 | ||
|
|
26e55afb29 | ||
|
|
9b8e1039f6 | ||
|
|
082d14ba50 | ||
|
|
87ead595c6 | ||
|
|
fb073acdc3 | ||
|
|
3dda4e24bf | ||
|
|
c966bd08ed | ||
|
|
692c73a6d0 | ||
|
|
849b484b4d | ||
|
|
9489baccda | ||
|
|
62cac15222 | ||
|
|
0ee9ce2958 | ||
|
|
a83007fc6c | ||
|
|
901f266dad | ||
|
|
6009426f04 | ||
|
|
6fb89df4cd | ||
|
|
48b99425a3 | ||
|
|
9569f9b09b | ||
|
|
7421896902 | ||
|
|
b837c8d66c | ||
|
|
e180f96eab | ||
|
|
de1f1f790e | ||
|
|
fefa74ce0f | ||
|
|
a2ceb70aa4 | ||
|
|
634103f144 | ||
|
|
f3379de81e | ||
|
|
064ee74c1a | ||
|
|
0e0c15fe3b | ||
|
|
3c855c608d | ||
|
|
414516289c | ||
|
|
d79fa71337 | ||
|
|
39c2ca94c8 | ||
|
|
acf1c5ce04 | ||
|
|
fd7923155f | ||
|
|
2aa6a6daa2 | ||
|
|
9662621980 | ||
|
|
37d007d9ab | ||
|
|
a4e48c359a | ||
|
|
8ef0101a6e | ||
|
|
e21f109026 | ||
|
|
737837eebd | ||
|
|
ba09b54409 | ||
|
|
417091c648 | ||
|
|
5a02368298 | ||
|
|
da7c739497 | ||
|
|
0842281466 | ||
|
|
b480f879b1 | ||
|
|
f8a215d790 | ||
|
|
da69f3b2c8 | ||
|
|
fc442c1a42 | ||
|
|
cf59a6b9fd | ||
|
|
34afcd511a | ||
|
|
e2444a2e4e | ||
|
|
692516de9b | ||
|
|
d51009c823 | ||
|
|
2f121bef5e | ||
|
|
61f7d376d2 | ||
|
|
9e253012e6 | ||
|
|
e4e5b1327b | ||
|
|
6ef3b227b8 | ||
|
|
bf657a0945 | ||
|
|
c3dd1886c9 | ||
|
|
32498bb8a7 | ||
|
|
69bb90a0e4 | ||
|
|
8e2988edf0 | ||
|
|
876a352cfd | ||
|
|
84e44cabfa | ||
|
|
36a838d565 | ||
|
|
9ee8693f40 | ||
|
|
6f02965149 | ||
|
|
27ce82de3b | ||
|
|
3d5a9ef220 | ||
|
|
9b81f6efd2 | ||
|
|
3e77e23d71 | ||
|
|
120c032c82 | ||
|
|
46e15b8ecd | ||
|
|
d71d87041b | ||
|
|
124bd58b9f | ||
|
|
257b9b0562 | ||
|
|
b8e15f691d | ||
|
|
2255e3bfc4 | ||
|
|
8797d84605 | ||
|
|
719179a923 | ||
|
|
1d544099f6 | ||
|
|
9b131a762a | ||
|
|
08c5d2256a | ||
|
|
ed6ee4341f | ||
|
|
157c1148fb | ||
|
|
507ea9e09e | ||
|
|
af68cba7be | ||
|
|
224fbe0e8f | ||
|
|
49a6c5f2c4 | ||
|
|
07c936897c | ||
|
|
3786fb907c | ||
|
|
752d9f0c68 | ||
|
|
baf59aafcb | ||
|
|
8787303d2a | ||
|
|
298eaa8b4b | ||
|
|
118c80af27 | ||
|
|
7b87038a8c | ||
|
|
d103939e45 | ||
|
|
fdc9171c69 | ||
|
|
2a6ade3cab | ||
|
|
c8c42689f9 | ||
|
|
da2f472f4d | ||
|
|
3ba878237b | ||
|
|
7577fb53a2 | ||
|
|
f2509f89ee | ||
|
|
9dd1f78330 | ||
|
|
ebb6c2c420 | ||
|
|
9d23dc1763 | ||
|
|
5fa3f8703e | ||
|
|
797885faea | ||
|
|
68e7e9f5b7 | ||
|
|
d1d165ad51 | ||
|
|
f43bd100e6 | ||
|
|
0ebb247666 | ||
|
|
aa73bc2809 | ||
|
|
c3fcdb918f | ||
|
|
5e3722bcfd | ||
|
|
6a4fca2eb1 | ||
|
|
fbba0e3ea5 | ||
|
|
6922cfd047 | ||
|
|
d93fc1d2d0 | ||
|
|
b15695128f | ||
|
|
48290b2e75 | ||
|
|
b62abef618 | ||
|
|
e0c0778d82 | ||
|
|
b4a82ae7c2 | ||
|
|
fc73102b30 | ||
|
|
b14e7473f3 | ||
|
|
f4cc9fc722 | ||
|
|
8d19ef7783 | ||
|
|
32be186ec5 | ||
|
|
abb0c7f90d | ||
|
|
71ddbdfe75 | ||
|
|
38463ad9a6 | ||
|
|
0870b90443 | ||
|
|
70375f94c8 | ||
|
|
2d4336116a | ||
|
|
f52880765e | ||
|
|
dbfe68decb | ||
|
|
659f337de9 | ||
|
|
1d36d41da1 | ||
|
|
0f90efaa54 | ||
|
|
48ccab152b | ||
|
|
4442930a82 | ||
|
|
912a775088 | ||
|
|
24a229d818 | ||
|
|
66afb61494 | ||
|
|
e7511cc05b | ||
|
|
926f19a936 | ||
|
|
9e6f86b963 | ||
|
|
059de43a9a | ||
|
|
e246b737b2 | ||
|
|
f3bc900f16 | ||
|
|
38842417b0 | ||
|
|
82d485a98e | ||
|
|
fac71feea7 | ||
|
|
ec148847a9 | ||
|
|
445c63878b | ||
|
|
0474af912a | ||
|
|
7df5953824 | ||
|
|
e391b563fb | ||
|
|
53f62f96d0 | ||
|
|
18745979a0 | ||
|
|
c1966a38ff | ||
|
|
d86973f3b1 | ||
|
|
56b8d19649 | ||
|
|
7cb037e0bc | ||
|
|
56e1b3f9e9 | ||
|
|
c792323a8a | ||
|
|
4d2865de13 | ||
|
|
0d360bc367 | ||
|
|
91c9b46b91 | ||
|
|
7e67bd80dd | ||
|
|
2f775ea09b | ||
|
|
0603c55089 | ||
|
|
41f25a44e9 | ||
|
|
35440b7273 | ||
|
|
7d68b91018 | ||
|
|
deaa61b848 | ||
|
|
2d00cbc41b | ||
|
|
57c1e83c67 | ||
|
|
b7079dbd4e | ||
|
|
be642a5373 | ||
|
|
ffc691c1a2 | ||
|
|
6122d99369 | ||
|
|
b73a8bcdab | ||
|
|
dd624537b7 | ||
|
|
50a67f73fd | ||
|
|
2262a980d4 | ||
|
|
84964ceb5f | ||
|
|
c4bdb7a66e | ||
|
|
21309e129f | ||
|
|
928e02c718 | ||
|
|
7f494dd200 | ||
|
|
f38e743e59 | ||
|
|
58b55eb3da | ||
|
|
8375ad95b3 | ||
|
|
506d027a2d | ||
|
|
fbeb6237cf | ||
|
|
bd95e2c5a5 | ||
|
|
f3276e557a | ||
|
|
8b813e0e7f | ||
|
|
d660d2959f | ||
|
|
014ebda7d2 | ||
|
|
607398d364 | ||
|
|
97d141ce2b | ||
|
|
9f492db9c6 | ||
|
|
6ed82b366c | ||
|
|
a7ca037f48 | ||
|
|
6e21d0e74a | ||
|
|
e6c75bf2af | ||
|
|
270c2f386e | ||
|
|
83f7880c58 | ||
|
|
51d29aee38 | ||
|
|
c7754c0365 | ||
|
|
94037cea38 | ||
|
|
5c1a9d9eea | ||
|
|
3835c73ffd | ||
|
|
b8c1effecb | ||
|
|
dd07241dd9 | ||
|
|
5ad828a613 | ||
|
|
9856df5527 | ||
|
|
7b249900ec | ||
|
|
e3b9267c3f | ||
|
|
a996b8135a | ||
|
|
5e1237390b | ||
|
|
83571b4bef | ||
|
|
c2a0dad9a8 | ||
|
|
f6f057689c | ||
|
|
4378f0020e | ||
|
|
2e9e7c4fc6 | ||
|
|
b7b8ee5580 | ||
|
|
c630dbbed0 | ||
|
|
fa6703a3b8 | ||
|
|
bdcb64c9d1 | ||
|
|
a1afc869a7 | ||
|
|
d8ce26c7cf | ||
|
|
95c474dc05 | ||
|
|
56ca73b4ad | ||
|
|
d82cd6a89e | ||
|
|
bf20d32364 | ||
|
|
e968e62fca | ||
|
|
b9c9ca9fa1 | ||
|
|
6e965e2e98 | ||
|
|
7d569f9036 | ||
|
|
df5a77199d | ||
|
|
3c8dfebfdc | ||
|
|
3cc422596f | ||
|
|
4ed167de22 | ||
|
|
b420647501 | ||
|
|
0c318b5e68 | ||
|
|
1c627297b8 | ||
|
|
f41e2d0552 | ||
|
|
1a1d539c60 | ||
|
|
ba2e46f88f | ||
|
|
c2f1fa81af | ||
|
|
752e03fa0f | ||
|
|
1da69664d7 | ||
|
|
9aa986a133 | ||
|
|
bdbfb28c4a | ||
|
|
162156bb2b | ||
|
|
64c5d542e9 | ||
|
|
31dc789f6e | ||
|
|
b0e2cfd7db | ||
|
|
54ddb0d014 | ||
|
|
e19688e96f | ||
|
|
4f53d75999 | ||
|
|
7277460060 | ||
|
|
93c4f6f3c0 | ||
|
|
c6919a7518 | ||
|
|
ca428e67dc | ||
|
|
c6726cf020 | ||
|
|
56ae1378da | ||
|
|
e982ab1a3b | ||
|
|
0d6aa87e89 | ||
|
|
94d06e4025 | ||
|
|
cee5b297ac | ||
|
|
c549213ce0 | ||
|
|
f3f9112767 | ||
|
|
fb325ea3e2 | ||
|
|
3773f6f7ec |
2
.babelrc
2
.babelrc
@@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"presets": ["es2015", "react", "stage-0"]
|
"presets": ["env", "react", "stage-0"]
|
||||||
}
|
}
|
||||||
2
.docker/.dockerignore
Normal file
2
.docker/.dockerignore
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
node_modules
|
||||||
|
npm-debug.log
|
||||||
37
.docker/Dockerfile
Normal file
37
.docker/Dockerfile
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
### 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
|
||||||
|
|
||||||
|
COPY ./coriolis/ /src/app/coriolis
|
||||||
|
COPY ./coriolis-data/ /src/app/coriolis-data
|
||||||
|
|
||||||
|
RUN apk update
|
||||||
|
RUN apk add git
|
||||||
|
|
||||||
|
RUN npm i -g npm
|
||||||
|
|
||||||
|
# Set up coriolis-data
|
||||||
|
WORKDIR /src/app/coriolis-data
|
||||||
|
RUN git fetch --all
|
||||||
|
RUN git reset --hard origin/$BRANCH
|
||||||
|
RUN npm install --no-package-lock
|
||||||
|
RUN npm start
|
||||||
|
|
||||||
|
WORKDIR /src/app/coriolis
|
||||||
|
RUN git fetch --all
|
||||||
|
RUN git reset --hard origin/$BRANCH
|
||||||
|
RUN npm install --no-package-lock
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
|
||||||
|
### STAGE 2: Production Environment ###
|
||||||
|
FROM nginx:1.13.12-alpine as web
|
||||||
|
COPY coriolis/.docker/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;"]
|
||||||
34
.docker/docker-compose.yml
Normal file
34
.docker/docker-compose.yml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
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.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.basic.frontend.rule=Host:beta.coriolis.io,beta.coriolis.edcd.io"
|
||||||
|
- "traefik.basic.port=80"
|
||||||
|
- "traefik.basic.protocol=http"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
web:
|
||||||
|
external: true
|
||||||
45
.docker/nginx.conf
Normal file
45
.docker/nginx.conf
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
worker_processes 1;
|
||||||
|
user nobody nobody;
|
||||||
|
error_log /tmp/error.log;
|
||||||
|
pid /tmp/nginx.pid;
|
||||||
|
|
||||||
|
events {
|
||||||
|
|
||||||
|
worker_connections 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
|
||||||
|
include /etc/nginx/mime.types;
|
||||||
|
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;
|
||||||
|
|
||||||
|
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)$ {
|
||||||
|
expires -1;
|
||||||
|
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 / {
|
||||||
|
try_files $uri $uri/ /index.html =404;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
21
.editorconfig
Normal file
21
.editorconfig
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
|
||||||
|
[*]
|
||||||
|
|
||||||
|
# change these settings to your own preference
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
# we recommend you to keep these unchanged
|
||||||
|
end_of_line = lf
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
[{package,bower}.json]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
103
.esdoc.json
Normal file
103
.esdoc.json
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
{
|
||||||
|
"source": "./src/app",
|
||||||
|
"includes": ["\\.js$", "\\.jsx$"],
|
||||||
|
"destination": "./docs",
|
||||||
|
"index": "./README.md",
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"name": "esdoc-standard-plugin",
|
||||||
|
"option": {
|
||||||
|
"lint": {
|
||||||
|
"enable": false
|
||||||
|
},
|
||||||
|
"coverage": {
|
||||||
|
"enable": false
|
||||||
|
},
|
||||||
|
"accessor": {
|
||||||
|
"access": [
|
||||||
|
"public",
|
||||||
|
"protected",
|
||||||
|
"private"
|
||||||
|
],
|
||||||
|
"autoPrivate": true
|
||||||
|
},
|
||||||
|
"undocumentIdentifier": {
|
||||||
|
"enable": true
|
||||||
|
},
|
||||||
|
"unexportedIdentifier": {
|
||||||
|
"enable": false
|
||||||
|
},
|
||||||
|
"typeInference": {
|
||||||
|
"enable": true
|
||||||
|
},
|
||||||
|
"brand": {
|
||||||
|
"logo": "./src/images/logo/192x192.png",
|
||||||
|
"title": "Coriolis",
|
||||||
|
"description": "Coriolis Shipyard for Elite Dangerous",
|
||||||
|
"repository": "https://github.com/EDCD/coriolis",
|
||||||
|
"site": "https://coriolis.edcd.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.edcd.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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
18
ChangeLog.md
18
ChangeLog.md
@@ -1,3 +1,21 @@
|
|||||||
|
#2.5.1
|
||||||
|
* Passenger count on main page
|
||||||
|
* AX Modules
|
||||||
|
* Engineering fixes
|
||||||
|
* Use coriolis-data 2.5.1
|
||||||
|
|
||||||
|
#2.5.0
|
||||||
|
* willyb321 and myself have conquered engineering. Mainly him though...
|
||||||
|
* Use coriolis-data 2.5.0
|
||||||
|
|
||||||
|
#2.4.2
|
||||||
|
Lots of kind people have helped out for this release! Check out the PR history!
|
||||||
|
* Uses coriolis-data update:
|
||||||
|
* Fixes issues with repair limpets
|
||||||
|
* Adds requirement data
|
||||||
|
* Adds requirements panel
|
||||||
|
* Adds comma formatting to tooltip numbers
|
||||||
|
|
||||||
#2.4.1
|
#2.4.1
|
||||||
* Small patches and changes
|
* Small patches and changes
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
878
d3.js
vendored
878
d3.js
vendored
@@ -1340,6 +1340,12 @@ Selection.prototype = selection.prototype = {
|
|||||||
dispatch: selection_dispatch
|
dispatch: selection_dispatch
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var select = function(selector) {
|
||||||
|
return typeof selector === "string"
|
||||||
|
? new Selection([[document.querySelector(selector)]], [document.documentElement])
|
||||||
|
: new Selection([[selector]], root);
|
||||||
|
};
|
||||||
|
|
||||||
var define = function(constructor, factory, prototype) {
|
var define = function(constructor, factory, prototype) {
|
||||||
constructor.prototype = factory.prototype = prototype;
|
constructor.prototype = factory.prototype = prototype;
|
||||||
prototype.constructor = constructor;
|
prototype.constructor = constructor;
|
||||||
@@ -6947,861 +6953,7 @@ var cool = cubehelixLong(cubehelix(260, 0.75, 0.35), cubehelix(80, 1.50, 0.8));
|
|||||||
|
|
||||||
var rainbow = cubehelix();
|
var rainbow = cubehelix();
|
||||||
|
|
||||||
var xhtml$1 = "http://www.w3.org/1999/xhtml";
|
|
||||||
|
|
||||||
var namespaces$1 = {
|
|
||||||
svg: "http://www.w3.org/2000/svg",
|
|
||||||
xhtml: xhtml$1,
|
|
||||||
xlink: "http://www.w3.org/1999/xlink",
|
|
||||||
xml: "http://www.w3.org/XML/1998/namespace",
|
|
||||||
xmlns: "http://www.w3.org/2000/xmlns/"
|
|
||||||
};
|
|
||||||
|
|
||||||
var namespace$1 = function(name) {
|
|
||||||
var prefix = name += "", i = prefix.indexOf(":");
|
|
||||||
if (i >= 0 && (prefix = name.slice(0, i)) !== "xmlns") name = name.slice(i + 1);
|
|
||||||
return namespaces$1.hasOwnProperty(prefix) ? {space: namespaces$1[prefix], local: name} : name;
|
|
||||||
};
|
|
||||||
|
|
||||||
function creatorInherit$1(name) {
|
|
||||||
return function() {
|
|
||||||
var document = this.ownerDocument,
|
|
||||||
uri = this.namespaceURI;
|
|
||||||
return uri === xhtml$1 && document.documentElement.namespaceURI === xhtml$1
|
|
||||||
? document.createElement(name)
|
|
||||||
: document.createElementNS(uri, name);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function creatorFixed$1(fullname) {
|
|
||||||
return function() {
|
|
||||||
return this.ownerDocument.createElementNS(fullname.space, fullname.local);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var creator$1 = function(name) {
|
|
||||||
var fullname = namespace$1(name);
|
|
||||||
return (fullname.local
|
|
||||||
? creatorFixed$1
|
|
||||||
: creatorInherit$1)(fullname);
|
|
||||||
};
|
|
||||||
|
|
||||||
var matcher$2 = function(selector) {
|
|
||||||
return function() {
|
|
||||||
return this.matches(selector);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
if (typeof document !== "undefined") {
|
|
||||||
var element$2 = document.documentElement;
|
|
||||||
if (!element$2.matches) {
|
|
||||||
var vendorMatches$1 = element$2.webkitMatchesSelector
|
|
||||||
|| element$2.msMatchesSelector
|
|
||||||
|| element$2.mozMatchesSelector
|
|
||||||
|| element$2.oMatchesSelector;
|
|
||||||
matcher$2 = function(selector) {
|
|
||||||
return function() {
|
|
||||||
return vendorMatches$1.call(this, selector);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var matcher$3 = matcher$2;
|
|
||||||
|
|
||||||
var filterEvents$1 = {};
|
|
||||||
|
|
||||||
var event$1 = null;
|
|
||||||
|
|
||||||
if (typeof document !== "undefined") {
|
|
||||||
var element$3 = document.documentElement;
|
|
||||||
if (!("onmouseenter" in element$3)) {
|
|
||||||
filterEvents$1 = {mouseenter: "mouseover", mouseleave: "mouseout"};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function filterContextListener$1(listener, index, group) {
|
|
||||||
listener = contextListener$1(listener, index, group);
|
|
||||||
return function(event) {
|
|
||||||
var related = event.relatedTarget;
|
|
||||||
if (!related || (related !== this && !(related.compareDocumentPosition(this) & 8))) {
|
|
||||||
listener.call(this, event);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function contextListener$1(listener, index, group) {
|
|
||||||
return function(event1) {
|
|
||||||
var event0 = event$1; // Events can be reentrant (e.g., focus).
|
|
||||||
event$1 = event1;
|
|
||||||
try {
|
|
||||||
listener.call(this, this.__data__, index, group);
|
|
||||||
} finally {
|
|
||||||
event$1 = event0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseTypenames$2(typenames) {
|
|
||||||
return typenames.trim().split(/^|\s+/).map(function(t) {
|
|
||||||
var name = "", i = t.indexOf(".");
|
|
||||||
if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);
|
|
||||||
return {type: t, name: name};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function onRemove$1(typename) {
|
|
||||||
return function() {
|
|
||||||
var on = this.__on;
|
|
||||||
if (!on) return;
|
|
||||||
for (var j = 0, i = -1, m = on.length, o; j < m; ++j) {
|
|
||||||
if (o = on[j], (!typename.type || o.type === typename.type) && o.name === typename.name) {
|
|
||||||
this.removeEventListener(o.type, o.listener, o.capture);
|
|
||||||
} else {
|
|
||||||
on[++i] = o;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (++i) on.length = i;
|
|
||||||
else delete this.__on;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function onAdd$1(typename, value, capture) {
|
|
||||||
var wrap = filterEvents$1.hasOwnProperty(typename.type) ? filterContextListener$1 : contextListener$1;
|
|
||||||
return function(d, i, group) {
|
|
||||||
var on = this.__on, o, listener = wrap(value, i, group);
|
|
||||||
if (on) for (var j = 0, m = on.length; j < m; ++j) {
|
|
||||||
if ((o = on[j]).type === typename.type && o.name === typename.name) {
|
|
||||||
this.removeEventListener(o.type, o.listener, o.capture);
|
|
||||||
this.addEventListener(o.type, o.listener = listener, o.capture = capture);
|
|
||||||
o.value = value;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.addEventListener(typename.type, listener, capture);
|
|
||||||
o = {type: typename.type, name: typename.name, value: value, listener: listener, capture: capture};
|
|
||||||
if (!on) this.__on = [o];
|
|
||||||
else on.push(o);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var selection_on$1 = function(typename, value, capture) {
|
|
||||||
var typenames = parseTypenames$2(typename + ""), i, n = typenames.length, t;
|
|
||||||
|
|
||||||
if (arguments.length < 2) {
|
|
||||||
var on = this.node().__on;
|
|
||||||
if (on) for (var j = 0, m = on.length, o; j < m; ++j) {
|
|
||||||
for (i = 0, o = on[j]; i < n; ++i) {
|
|
||||||
if ((t = typenames[i]).type === o.type && t.name === o.name) {
|
|
||||||
return o.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
on = value ? onAdd$1 : onRemove$1;
|
|
||||||
if (capture == null) capture = false;
|
|
||||||
for (i = 0; i < n; ++i) this.each(on(typenames[i], value, capture));
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
function none$1() {}
|
|
||||||
|
|
||||||
var selector$1 = function(selector) {
|
|
||||||
return selector == null ? none$1 : function() {
|
|
||||||
return this.querySelector(selector);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
var selection_select$1 = function(select) {
|
|
||||||
if (typeof select !== "function") select = selector$1(select);
|
|
||||||
|
|
||||||
for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
|
|
||||||
for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {
|
|
||||||
if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {
|
|
||||||
if ("__data__" in node) subnode.__data__ = node.__data__;
|
|
||||||
subgroup[i] = subnode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Selection$2(subgroups, this._parents);
|
|
||||||
};
|
|
||||||
|
|
||||||
function empty$2() {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
var selectorAll$1 = function(selector) {
|
|
||||||
return selector == null ? empty$2 : function() {
|
|
||||||
return this.querySelectorAll(selector);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
var selection_selectAll$1 = function(select) {
|
|
||||||
if (typeof select !== "function") select = selectorAll$1(select);
|
|
||||||
|
|
||||||
for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {
|
|
||||||
for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
|
|
||||||
if (node = group[i]) {
|
|
||||||
subgroups.push(select.call(node, node.__data__, i, group));
|
|
||||||
parents.push(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Selection$2(subgroups, parents);
|
|
||||||
};
|
|
||||||
|
|
||||||
var selection_filter$1 = function(match) {
|
|
||||||
if (typeof match !== "function") match = matcher$3(match);
|
|
||||||
|
|
||||||
for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
|
|
||||||
for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {
|
|
||||||
if ((node = group[i]) && match.call(node, node.__data__, i, group)) {
|
|
||||||
subgroup.push(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Selection$2(subgroups, this._parents);
|
|
||||||
};
|
|
||||||
|
|
||||||
var sparse$1 = function(update) {
|
|
||||||
return new Array(update.length);
|
|
||||||
};
|
|
||||||
|
|
||||||
var selection_enter$1 = function() {
|
|
||||||
return new Selection$2(this._enter || this._groups.map(sparse$1), this._parents);
|
|
||||||
};
|
|
||||||
|
|
||||||
function EnterNode$1(parent, datum) {
|
|
||||||
this.ownerDocument = parent.ownerDocument;
|
|
||||||
this.namespaceURI = parent.namespaceURI;
|
|
||||||
this._next = null;
|
|
||||||
this._parent = parent;
|
|
||||||
this.__data__ = datum;
|
|
||||||
}
|
|
||||||
|
|
||||||
EnterNode$1.prototype = {
|
|
||||||
constructor: EnterNode$1,
|
|
||||||
appendChild: function(child) { return this._parent.insertBefore(child, this._next); },
|
|
||||||
insertBefore: function(child, next) { return this._parent.insertBefore(child, next); },
|
|
||||||
querySelector: function(selector) { return this._parent.querySelector(selector); },
|
|
||||||
querySelectorAll: function(selector) { return this._parent.querySelectorAll(selector); }
|
|
||||||
};
|
|
||||||
|
|
||||||
var constant$10 = function(x) {
|
var constant$10 = function(x) {
|
||||||
return function() {
|
|
||||||
return x;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
var keyPrefix$2 = "$"; // Protect against keys like “__proto__”.
|
|
||||||
|
|
||||||
function bindIndex$1(parent, group, enter, update, exit, data) {
|
|
||||||
var i = 0,
|
|
||||||
node,
|
|
||||||
groupLength = group.length,
|
|
||||||
dataLength = data.length;
|
|
||||||
|
|
||||||
// Put any non-null nodes that fit into update.
|
|
||||||
// Put any null nodes into enter.
|
|
||||||
// Put any remaining data into enter.
|
|
||||||
for (; i < dataLength; ++i) {
|
|
||||||
if (node = group[i]) {
|
|
||||||
node.__data__ = data[i];
|
|
||||||
update[i] = node;
|
|
||||||
} else {
|
|
||||||
enter[i] = new EnterNode$1(parent, data[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Put any non-null nodes that don’t fit into exit.
|
|
||||||
for (; i < groupLength; ++i) {
|
|
||||||
if (node = group[i]) {
|
|
||||||
exit[i] = node;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function bindKey$1(parent, group, enter, update, exit, data, key) {
|
|
||||||
var i,
|
|
||||||
node,
|
|
||||||
nodeByKeyValue = {},
|
|
||||||
groupLength = group.length,
|
|
||||||
dataLength = data.length,
|
|
||||||
keyValues = new Array(groupLength),
|
|
||||||
keyValue;
|
|
||||||
|
|
||||||
// Compute the key for each node.
|
|
||||||
// If multiple nodes have the same key, the duplicates are added to exit.
|
|
||||||
for (i = 0; i < groupLength; ++i) {
|
|
||||||
if (node = group[i]) {
|
|
||||||
keyValues[i] = keyValue = keyPrefix$2 + key.call(node, node.__data__, i, group);
|
|
||||||
if (keyValue in nodeByKeyValue) {
|
|
||||||
exit[i] = node;
|
|
||||||
} else {
|
|
||||||
nodeByKeyValue[keyValue] = node;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute the key for each datum.
|
|
||||||
// If there a node associated with this key, join and add it to update.
|
|
||||||
// If there is not (or the key is a duplicate), add it to enter.
|
|
||||||
for (i = 0; i < dataLength; ++i) {
|
|
||||||
keyValue = keyPrefix$2 + key.call(parent, data[i], i, data);
|
|
||||||
if (node = nodeByKeyValue[keyValue]) {
|
|
||||||
update[i] = node;
|
|
||||||
node.__data__ = data[i];
|
|
||||||
nodeByKeyValue[keyValue] = null;
|
|
||||||
} else {
|
|
||||||
enter[i] = new EnterNode$1(parent, data[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add any remaining nodes that were not bound to data to exit.
|
|
||||||
for (i = 0; i < groupLength; ++i) {
|
|
||||||
if ((node = group[i]) && (nodeByKeyValue[keyValues[i]] === node)) {
|
|
||||||
exit[i] = node;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var selection_data$1 = function(value, key) {
|
|
||||||
if (!value) {
|
|
||||||
data = new Array(this.size()), j = -1;
|
|
||||||
this.each(function(d) { data[++j] = d; });
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
var bind = key ? bindKey$1 : bindIndex$1,
|
|
||||||
parents = this._parents,
|
|
||||||
groups = this._groups;
|
|
||||||
|
|
||||||
if (typeof value !== "function") value = constant$10(value);
|
|
||||||
|
|
||||||
for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) {
|
|
||||||
var parent = parents[j],
|
|
||||||
group = groups[j],
|
|
||||||
groupLength = group.length,
|
|
||||||
data = value.call(parent, parent && parent.__data__, j, parents),
|
|
||||||
dataLength = data.length,
|
|
||||||
enterGroup = enter[j] = new Array(dataLength),
|
|
||||||
updateGroup = update[j] = new Array(dataLength),
|
|
||||||
exitGroup = exit[j] = new Array(groupLength);
|
|
||||||
|
|
||||||
bind(parent, group, enterGroup, updateGroup, exitGroup, data, key);
|
|
||||||
|
|
||||||
// Now connect the enter nodes to their following update node, such that
|
|
||||||
// appendChild can insert the materialized enter node before this node,
|
|
||||||
// rather than at the end of the parent node.
|
|
||||||
for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) {
|
|
||||||
if (previous = enterGroup[i0]) {
|
|
||||||
if (i0 >= i1) i1 = i0 + 1;
|
|
||||||
while (!(next = updateGroup[i1]) && ++i1 < dataLength);
|
|
||||||
previous._next = next || null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
update = new Selection$2(update, parents);
|
|
||||||
update._enter = enter;
|
|
||||||
update._exit = exit;
|
|
||||||
return update;
|
|
||||||
};
|
|
||||||
|
|
||||||
var selection_exit$1 = function() {
|
|
||||||
return new Selection$2(this._exit || this._groups.map(sparse$1), this._parents);
|
|
||||||
};
|
|
||||||
|
|
||||||
var selection_merge$1 = function(selection) {
|
|
||||||
|
|
||||||
for (var groups0 = this._groups, groups1 = selection._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) {
|
|
||||||
for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {
|
|
||||||
if (node = group0[i] || group1[i]) {
|
|
||||||
merge[i] = node;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (; j < m0; ++j) {
|
|
||||||
merges[j] = groups0[j];
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Selection$2(merges, this._parents);
|
|
||||||
};
|
|
||||||
|
|
||||||
var selection_order$1 = function() {
|
|
||||||
|
|
||||||
for (var groups = this._groups, j = -1, m = groups.length; ++j < m;) {
|
|
||||||
for (var group = groups[j], i = group.length - 1, next = group[i], node; --i >= 0;) {
|
|
||||||
if (node = group[i]) {
|
|
||||||
if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next);
|
|
||||||
next = node;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
var selection_sort$1 = function(compare) {
|
|
||||||
if (!compare) compare = ascending$2;
|
|
||||||
|
|
||||||
function compareNode(a, b) {
|
|
||||||
return a && b ? compare(a.__data__, b.__data__) : !a - !b;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var groups = this._groups, m = groups.length, sortgroups = new Array(m), j = 0; j < m; ++j) {
|
|
||||||
for (var group = groups[j], n = group.length, sortgroup = sortgroups[j] = new Array(n), node, i = 0; i < n; ++i) {
|
|
||||||
if (node = group[i]) {
|
|
||||||
sortgroup[i] = node;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sortgroup.sort(compareNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Selection$2(sortgroups, this._parents).order();
|
|
||||||
};
|
|
||||||
|
|
||||||
function ascending$2(a, b) {
|
|
||||||
return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
|
|
||||||
}
|
|
||||||
|
|
||||||
var selection_call$1 = function() {
|
|
||||||
var callback = arguments[0];
|
|
||||||
arguments[0] = this;
|
|
||||||
callback.apply(null, arguments);
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
var selection_nodes$1 = function() {
|
|
||||||
var nodes = new Array(this.size()), i = -1;
|
|
||||||
this.each(function() { nodes[++i] = this; });
|
|
||||||
return nodes;
|
|
||||||
};
|
|
||||||
|
|
||||||
var selection_node$1 = function() {
|
|
||||||
|
|
||||||
for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {
|
|
||||||
for (var group = groups[j], i = 0, n = group.length; i < n; ++i) {
|
|
||||||
var node = group[i];
|
|
||||||
if (node) return node;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
var selection_size$1 = function() {
|
|
||||||
var size = 0;
|
|
||||||
this.each(function() { ++size; });
|
|
||||||
return size;
|
|
||||||
};
|
|
||||||
|
|
||||||
var selection_empty$1 = function() {
|
|
||||||
return !this.node();
|
|
||||||
};
|
|
||||||
|
|
||||||
var selection_each$1 = function(callback) {
|
|
||||||
|
|
||||||
for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {
|
|
||||||
for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) {
|
|
||||||
if (node = group[i]) callback.call(node, node.__data__, i, group);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
function attrRemove$2(name) {
|
|
||||||
return function() {
|
|
||||||
this.removeAttribute(name);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function attrRemoveNS$2(fullname) {
|
|
||||||
return function() {
|
|
||||||
this.removeAttributeNS(fullname.space, fullname.local);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function attrConstant$2(name, value) {
|
|
||||||
return function() {
|
|
||||||
this.setAttribute(name, value);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function attrConstantNS$2(fullname, value) {
|
|
||||||
return function() {
|
|
||||||
this.setAttributeNS(fullname.space, fullname.local, value);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function attrFunction$2(name, value) {
|
|
||||||
return function() {
|
|
||||||
var v = value.apply(this, arguments);
|
|
||||||
if (v == null) this.removeAttribute(name);
|
|
||||||
else this.setAttribute(name, v);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function attrFunctionNS$2(fullname, value) {
|
|
||||||
return function() {
|
|
||||||
var v = value.apply(this, arguments);
|
|
||||||
if (v == null) this.removeAttributeNS(fullname.space, fullname.local);
|
|
||||||
else this.setAttributeNS(fullname.space, fullname.local, v);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var selection_attr$1 = function(name, value) {
|
|
||||||
var fullname = namespace$1(name);
|
|
||||||
|
|
||||||
if (arguments.length < 2) {
|
|
||||||
var node = this.node();
|
|
||||||
return fullname.local
|
|
||||||
? node.getAttributeNS(fullname.space, fullname.local)
|
|
||||||
: node.getAttribute(fullname);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.each((value == null
|
|
||||||
? (fullname.local ? attrRemoveNS$2 : attrRemove$2) : (typeof value === "function"
|
|
||||||
? (fullname.local ? attrFunctionNS$2 : attrFunction$2)
|
|
||||||
: (fullname.local ? attrConstantNS$2 : attrConstant$2)))(fullname, value));
|
|
||||||
};
|
|
||||||
|
|
||||||
var defaultView = function(node) {
|
|
||||||
return (node.ownerDocument && node.ownerDocument.defaultView) // node is a Node
|
|
||||||
|| (node.document && node) // node is a Window
|
|
||||||
|| node.defaultView; // node is a Document
|
|
||||||
};
|
|
||||||
|
|
||||||
function styleRemove$2(name) {
|
|
||||||
return function() {
|
|
||||||
this.style.removeProperty(name);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function styleConstant$2(name, value, priority) {
|
|
||||||
return function() {
|
|
||||||
this.style.setProperty(name, value, priority);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function styleFunction$2(name, value, priority) {
|
|
||||||
return function() {
|
|
||||||
var v = value.apply(this, arguments);
|
|
||||||
if (v == null) this.style.removeProperty(name);
|
|
||||||
else this.style.setProperty(name, v, priority);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var selection_style$1 = function(name, value, priority) {
|
|
||||||
var node;
|
|
||||||
return arguments.length > 1
|
|
||||||
? this.each((value == null
|
|
||||||
? styleRemove$2 : typeof value === "function"
|
|
||||||
? styleFunction$2
|
|
||||||
: styleConstant$2)(name, value, priority == null ? "" : priority))
|
|
||||||
: defaultView(node = this.node())
|
|
||||||
.getComputedStyle(node, null)
|
|
||||||
.getPropertyValue(name);
|
|
||||||
};
|
|
||||||
|
|
||||||
function propertyRemove$1(name) {
|
|
||||||
return function() {
|
|
||||||
delete this[name];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function propertyConstant$1(name, value) {
|
|
||||||
return function() {
|
|
||||||
this[name] = value;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function propertyFunction$1(name, value) {
|
|
||||||
return function() {
|
|
||||||
var v = value.apply(this, arguments);
|
|
||||||
if (v == null) delete this[name];
|
|
||||||
else this[name] = v;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var selection_property$1 = function(name, value) {
|
|
||||||
return arguments.length > 1
|
|
||||||
? this.each((value == null
|
|
||||||
? propertyRemove$1 : typeof value === "function"
|
|
||||||
? propertyFunction$1
|
|
||||||
: propertyConstant$1)(name, value))
|
|
||||||
: this.node()[name];
|
|
||||||
};
|
|
||||||
|
|
||||||
function classArray$1(string) {
|
|
||||||
return string.trim().split(/^|\s+/);
|
|
||||||
}
|
|
||||||
|
|
||||||
function classList$1(node) {
|
|
||||||
return node.classList || new ClassList$1(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ClassList$1(node) {
|
|
||||||
this._node = node;
|
|
||||||
this._names = classArray$1(node.getAttribute("class") || "");
|
|
||||||
}
|
|
||||||
|
|
||||||
ClassList$1.prototype = {
|
|
||||||
add: function(name) {
|
|
||||||
var i = this._names.indexOf(name);
|
|
||||||
if (i < 0) {
|
|
||||||
this._names.push(name);
|
|
||||||
this._node.setAttribute("class", this._names.join(" "));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
remove: function(name) {
|
|
||||||
var i = this._names.indexOf(name);
|
|
||||||
if (i >= 0) {
|
|
||||||
this._names.splice(i, 1);
|
|
||||||
this._node.setAttribute("class", this._names.join(" "));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
contains: function(name) {
|
|
||||||
return this._names.indexOf(name) >= 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function classedAdd$1(node, names) {
|
|
||||||
var list = classList$1(node), i = -1, n = names.length;
|
|
||||||
while (++i < n) list.add(names[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function classedRemove$1(node, names) {
|
|
||||||
var list = classList$1(node), i = -1, n = names.length;
|
|
||||||
while (++i < n) list.remove(names[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function classedTrue$1(names) {
|
|
||||||
return function() {
|
|
||||||
classedAdd$1(this, names);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function classedFalse$1(names) {
|
|
||||||
return function() {
|
|
||||||
classedRemove$1(this, names);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function classedFunction$1(names, value) {
|
|
||||||
return function() {
|
|
||||||
(value.apply(this, arguments) ? classedAdd$1 : classedRemove$1)(this, names);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var selection_classed$1 = function(name, value) {
|
|
||||||
var names = classArray$1(name + "");
|
|
||||||
|
|
||||||
if (arguments.length < 2) {
|
|
||||||
var list = classList$1(this.node()), i = -1, n = names.length;
|
|
||||||
while (++i < n) if (!list.contains(names[i])) return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.each((typeof value === "function"
|
|
||||||
? classedFunction$1 : value
|
|
||||||
? classedTrue$1
|
|
||||||
: classedFalse$1)(names, value));
|
|
||||||
};
|
|
||||||
|
|
||||||
function textRemove$1() {
|
|
||||||
this.textContent = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
function textConstant$2(value) {
|
|
||||||
return function() {
|
|
||||||
this.textContent = value;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function textFunction$2(value) {
|
|
||||||
return function() {
|
|
||||||
var v = value.apply(this, arguments);
|
|
||||||
this.textContent = v == null ? "" : v;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var selection_text$1 = function(value) {
|
|
||||||
return arguments.length
|
|
||||||
? this.each(value == null
|
|
||||||
? textRemove$1 : (typeof value === "function"
|
|
||||||
? textFunction$2
|
|
||||||
: textConstant$2)(value))
|
|
||||||
: this.node().textContent;
|
|
||||||
};
|
|
||||||
|
|
||||||
function htmlRemove$1() {
|
|
||||||
this.innerHTML = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
function htmlConstant$1(value) {
|
|
||||||
return function() {
|
|
||||||
this.innerHTML = value;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function htmlFunction$1(value) {
|
|
||||||
return function() {
|
|
||||||
var v = value.apply(this, arguments);
|
|
||||||
this.innerHTML = v == null ? "" : v;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var selection_html$1 = function(value) {
|
|
||||||
return arguments.length
|
|
||||||
? this.each(value == null
|
|
||||||
? htmlRemove$1 : (typeof value === "function"
|
|
||||||
? htmlFunction$1
|
|
||||||
: htmlConstant$1)(value))
|
|
||||||
: this.node().innerHTML;
|
|
||||||
};
|
|
||||||
|
|
||||||
function raise$2() {
|
|
||||||
if (this.nextSibling) this.parentNode.appendChild(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
var selection_raise$1 = function() {
|
|
||||||
return this.each(raise$2);
|
|
||||||
};
|
|
||||||
|
|
||||||
function lower$1() {
|
|
||||||
if (this.previousSibling) this.parentNode.insertBefore(this, this.parentNode.firstChild);
|
|
||||||
}
|
|
||||||
|
|
||||||
var selection_lower$1 = function() {
|
|
||||||
return this.each(lower$1);
|
|
||||||
};
|
|
||||||
|
|
||||||
var selection_append$1 = function(name) {
|
|
||||||
var create = typeof name === "function" ? name : creator$1(name);
|
|
||||||
return this.select(function() {
|
|
||||||
return this.appendChild(create.apply(this, arguments));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
function constantNull$1() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var selection_insert$1 = function(name, before) {
|
|
||||||
var create = typeof name === "function" ? name : creator$1(name),
|
|
||||||
select = before == null ? constantNull$1 : typeof before === "function" ? before : selector$1(before);
|
|
||||||
return this.select(function() {
|
|
||||||
return this.insertBefore(create.apply(this, arguments), select.apply(this, arguments) || null);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
function remove$1() {
|
|
||||||
var parent = this.parentNode;
|
|
||||||
if (parent) parent.removeChild(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
var selection_remove$1 = function() {
|
|
||||||
return this.each(remove$1);
|
|
||||||
};
|
|
||||||
|
|
||||||
var selection_datum$1 = function(value) {
|
|
||||||
return arguments.length
|
|
||||||
? this.property("__data__", value)
|
|
||||||
: this.node().__data__;
|
|
||||||
};
|
|
||||||
|
|
||||||
function dispatchEvent$1(node, type, params) {
|
|
||||||
var window = defaultView(node),
|
|
||||||
event = window.CustomEvent;
|
|
||||||
|
|
||||||
if (event) {
|
|
||||||
event = new event(type, params);
|
|
||||||
} else {
|
|
||||||
event = window.document.createEvent("Event");
|
|
||||||
if (params) event.initEvent(type, params.bubbles, params.cancelable), event.detail = params.detail;
|
|
||||||
else event.initEvent(type, false, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
node.dispatchEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
function dispatchConstant$1(type, params) {
|
|
||||||
return function() {
|
|
||||||
return dispatchEvent$1(this, type, params);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function dispatchFunction$1(type, params) {
|
|
||||||
return function() {
|
|
||||||
return dispatchEvent$1(this, type, params.apply(this, arguments));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var selection_dispatch$1 = function(type, params) {
|
|
||||||
return this.each((typeof params === "function"
|
|
||||||
? dispatchFunction$1
|
|
||||||
: dispatchConstant$1)(type, params));
|
|
||||||
};
|
|
||||||
|
|
||||||
var root$2 = [null];
|
|
||||||
|
|
||||||
function Selection$2(groups, parents) {
|
|
||||||
this._groups = groups;
|
|
||||||
this._parents = parents;
|
|
||||||
}
|
|
||||||
|
|
||||||
function selection$2() {
|
|
||||||
return new Selection$2([[document.documentElement]], root$2);
|
|
||||||
}
|
|
||||||
|
|
||||||
Selection$2.prototype = selection$2.prototype = {
|
|
||||||
constructor: Selection$2,
|
|
||||||
select: selection_select$1,
|
|
||||||
selectAll: selection_selectAll$1,
|
|
||||||
filter: selection_filter$1,
|
|
||||||
data: selection_data$1,
|
|
||||||
enter: selection_enter$1,
|
|
||||||
exit: selection_exit$1,
|
|
||||||
merge: selection_merge$1,
|
|
||||||
order: selection_order$1,
|
|
||||||
sort: selection_sort$1,
|
|
||||||
call: selection_call$1,
|
|
||||||
nodes: selection_nodes$1,
|
|
||||||
node: selection_node$1,
|
|
||||||
size: selection_size$1,
|
|
||||||
empty: selection_empty$1,
|
|
||||||
each: selection_each$1,
|
|
||||||
attr: selection_attr$1,
|
|
||||||
style: selection_style$1,
|
|
||||||
property: selection_property$1,
|
|
||||||
classed: selection_classed$1,
|
|
||||||
text: selection_text$1,
|
|
||||||
html: selection_html$1,
|
|
||||||
raise: selection_raise$1,
|
|
||||||
lower: selection_lower$1,
|
|
||||||
append: selection_append$1,
|
|
||||||
insert: selection_insert$1,
|
|
||||||
remove: selection_remove$1,
|
|
||||||
datum: selection_datum$1,
|
|
||||||
on: selection_on$1,
|
|
||||||
dispatch: selection_dispatch$1
|
|
||||||
};
|
|
||||||
|
|
||||||
var select$1 = function(selector) {
|
|
||||||
return typeof selector === "string"
|
|
||||||
? new Selection$2([[document.querySelector(selector)]], [document.documentElement])
|
|
||||||
: new Selection$2([[selector]], root$2);
|
|
||||||
};
|
|
||||||
|
|
||||||
var constant$11 = function(x) {
|
|
||||||
return function constant() {
|
return function constant() {
|
||||||
return x;
|
return x;
|
||||||
};
|
};
|
||||||
@@ -7850,7 +7002,7 @@ function y$3(p) {
|
|||||||
var line = function() {
|
var line = function() {
|
||||||
var x$$1 = x$3,
|
var x$$1 = x$3,
|
||||||
y$$1 = y$3,
|
y$$1 = y$3,
|
||||||
defined = constant$11(true),
|
defined = constant$10(true),
|
||||||
context = null,
|
context = null,
|
||||||
curve = curveLinear,
|
curve = curveLinear,
|
||||||
output = null;
|
output = null;
|
||||||
@@ -7876,15 +7028,15 @@ var line = function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
line.x = function(_) {
|
line.x = function(_) {
|
||||||
return arguments.length ? (x$$1 = typeof _ === "function" ? _ : constant$11(+_), line) : x$$1;
|
return arguments.length ? (x$$1 = typeof _ === "function" ? _ : constant$10(+_), line) : x$$1;
|
||||||
};
|
};
|
||||||
|
|
||||||
line.y = function(_) {
|
line.y = function(_) {
|
||||||
return arguments.length ? (y$$1 = typeof _ === "function" ? _ : constant$11(+_), line) : y$$1;
|
return arguments.length ? (y$$1 = typeof _ === "function" ? _ : constant$10(+_), line) : y$$1;
|
||||||
};
|
};
|
||||||
|
|
||||||
line.defined = function(_) {
|
line.defined = function(_) {
|
||||||
return arguments.length ? (defined = typeof _ === "function" ? _ : constant$11(!!_), line) : defined;
|
return arguments.length ? (defined = typeof _ === "function" ? _ : constant$10(!!_), line) : defined;
|
||||||
};
|
};
|
||||||
|
|
||||||
line.curve = function(_) {
|
line.curve = function(_) {
|
||||||
@@ -7924,7 +7076,7 @@ function slope2(that, t) {
|
|||||||
// According to https://en.wikipedia.org/wiki/Cubic_Hermite_spline#Representations
|
// According to https://en.wikipedia.org/wiki/Cubic_Hermite_spline#Representations
|
||||||
// "you can express cubic Hermite interpolation in terms of cubic Bézier curves
|
// "you can express cubic Hermite interpolation in terms of cubic Bézier curves
|
||||||
// with respect to the four values p0, p0 + m0 / 3, p1 - m1 / 3, p1".
|
// with respect to the four values p0, p0 + m0 / 3, p1 - m1 / 3, p1".
|
||||||
function point$6(that, t0, t1) {
|
function point$5(that, t0, t1) {
|
||||||
var x0 = that._x0,
|
var x0 = that._x0,
|
||||||
y0 = that._y0,
|
y0 = that._y0,
|
||||||
x1 = that._x1,
|
x1 = that._x1,
|
||||||
@@ -7953,7 +7105,7 @@ MonotoneX.prototype = {
|
|||||||
lineEnd: function() {
|
lineEnd: function() {
|
||||||
switch (this._point) {
|
switch (this._point) {
|
||||||
case 2: this._context.lineTo(this._x1, this._y1); break;
|
case 2: this._context.lineTo(this._x1, this._y1); break;
|
||||||
case 3: point$6(this, this._t0, slope2(this, this._t0)); break;
|
case 3: point$5(this, this._t0, slope2(this, this._t0)); break;
|
||||||
}
|
}
|
||||||
if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
|
if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
|
||||||
this._line = 1 - this._line;
|
this._line = 1 - this._line;
|
||||||
@@ -7966,8 +7118,8 @@ MonotoneX.prototype = {
|
|||||||
switch (this._point) {
|
switch (this._point) {
|
||||||
case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
|
case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
|
||||||
case 1: this._point = 2; break;
|
case 1: this._point = 2; break;
|
||||||
case 2: this._point = 3; point$6(this, slope2(this, t1 = slope3(this, x, y)), t1); break;
|
case 2: this._point = 3; point$5(this, slope2(this, t1 = slope3(this, x, y)), t1); break;
|
||||||
default: point$6(this, this._t0, t1 = slope3(this, x, y)); break;
|
default: point$5(this, this._t0, t1 = slope3(this, x, y)); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._x0 = this._x1, this._x1 = x;
|
this._x0 = this._x1, this._x1 = x;
|
||||||
@@ -8153,7 +7305,7 @@ exports.line = line;
|
|||||||
exports.scaleBand = band;
|
exports.scaleBand = band;
|
||||||
exports.scaleLinear = linear$2;
|
exports.scaleLinear = linear$2;
|
||||||
exports.scaleOrdinal = ordinal;
|
exports.scaleOrdinal = ordinal;
|
||||||
exports.select = select$1;
|
exports.select = select;
|
||||||
|
|
||||||
Object.defineProperty(exports, '__esModule', { value: true });
|
Object.defineProperty(exports, '__esModule', { value: true });
|
||||||
|
|
||||||
|
|||||||
1
devServer.js
Normal file → Executable file
1
devServer.js
Normal file → Executable file
@@ -5,6 +5,7 @@ var config = require('./webpack.config.dev');
|
|||||||
new WebpackDevServer(webpack(config), {
|
new WebpackDevServer(webpack(config), {
|
||||||
publicPath: config.output.publicPath,
|
publicPath: config.output.publicPath,
|
||||||
hot: true,
|
hot: true,
|
||||||
|
disableHostCheck: true,
|
||||||
headers: { "Access-Control-Allow-Origin": "*" },
|
headers: { "Access-Control-Allow-Origin": "*" },
|
||||||
historyApiFallback: {
|
historyApiFallback: {
|
||||||
rewrites: [
|
rewrites: [
|
||||||
|
|||||||
35
package.json
35
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "coriolis_shipyard",
|
"name": "coriolis_shipyard",
|
||||||
"version": "2.4.1",
|
"version": "2.9.18",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/EDCD/coriolis"
|
"url": "https://github.com/EDCD/coriolis"
|
||||||
@@ -19,12 +19,14 @@
|
|||||||
"test": "jest",
|
"test": "jest",
|
||||||
"prod-serve": "nginx -p $(pwd) -c nginx.conf",
|
"prod-serve": "nginx -p $(pwd) -c nginx.conf",
|
||||||
"prod-stop": "kill -QUIT $(cat nginx.pid)",
|
"prod-stop": "kill -QUIT $(cat nginx.pid)",
|
||||||
"build": "npm run clean && NODE_ENV=production webpack -p --config webpack.config.prod.js",
|
"build": "npm run clean && cross-env NODE_ENV=production webpack -p --config webpack.config.prod.js",
|
||||||
"rsync": "rsync -ae \"ssh -i $CORIOLIS_PEM\" --delete build/ $CORIOLIS_USER@$CORIOLIS_HOST:~/wwws",
|
"rsync": "rsync -ae \"ssh -i $CORIOLIS_PEM\" --delete build/ $CORIOLIS_USER@$CORIOLIS_HOST:~/wwws",
|
||||||
"deploy": "npm run lint && npm test && npm run build && npm run rsync"
|
"deploy": "npm run lint && npm test && npm run build && npm run rsync"
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"transform": {".*": "<rootDir>/node_modules/babel-jest"},
|
"transform": {
|
||||||
|
".*": "<rootDir>/node_modules/babel-jest"
|
||||||
|
},
|
||||||
"testRegex": "(/__tests__/test-.*|\\.(test|spec))\\.js$",
|
"testRegex": "(/__tests__/test-.*|\\.(test|spec))\\.js$",
|
||||||
"moduleFileExtensions": [
|
"moduleFileExtensions": [
|
||||||
"js",
|
"js",
|
||||||
@@ -37,6 +39,7 @@
|
|||||||
"<rootDir>/node_modules/lodash",
|
"<rootDir>/node_modules/lodash",
|
||||||
"<rootDir>/node_modules/react",
|
"<rootDir>/node_modules/react",
|
||||||
"<rootDir>/node_modules/react-dom",
|
"<rootDir>/node_modules/react-dom",
|
||||||
|
"<rootDir>/node_modules/react-transition-group",
|
||||||
"<rootDir>/node_modules/react-testutils-additions",
|
"<rootDir>/node_modules/react-testutils-additions",
|
||||||
"<rootDir>/node_modules/fbjs",
|
"<rootDir>/node_modules/fbjs",
|
||||||
"<rootDir>/node_modules/fbemitter",
|
"<rootDir>/node_modules/fbemitter",
|
||||||
@@ -58,11 +61,20 @@
|
|||||||
"babel-eslint": "*",
|
"babel-eslint": "*",
|
||||||
"babel-jest": "*",
|
"babel-jest": "*",
|
||||||
"babel-loader": "*",
|
"babel-loader": "*",
|
||||||
"babel-preset-es2015": "*",
|
"babel-preset-env": "*",
|
||||||
"babel-preset-react": "*",
|
"babel-preset-react": "*",
|
||||||
"babel-preset-stage-0": "*",
|
"babel-preset-stage-0": "*",
|
||||||
|
"create-react-class": "^15.6.2",
|
||||||
|
"cross-env": "^5.1.4",
|
||||||
"css-loader": "^0.28.0",
|
"css-loader": "^0.28.0",
|
||||||
"d3-selection": "1",
|
"d3-selection": "1",
|
||||||
|
"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": "3.19.0",
|
"eslint": "3.19.0",
|
||||||
"eslint-plugin-react": "^6.10.3",
|
"eslint-plugin-react": "^6.10.3",
|
||||||
"expose-loader": "^0.7.3",
|
"expose-loader": "^0.7.3",
|
||||||
@@ -70,37 +82,42 @@
|
|||||||
"extract-text-webpack-plugin": "2.1.0",
|
"extract-text-webpack-plugin": "2.1.0",
|
||||||
"file-loader": "^0.11.1",
|
"file-loader": "^0.11.1",
|
||||||
"html-webpack-plugin": "^2.28.0",
|
"html-webpack-plugin": "^2.28.0",
|
||||||
"jest-cli": "^19.0.2",
|
"jest-cli": "^21.2.1",
|
||||||
"jsen": "^0.6.4",
|
"jsen": "^0.6.4",
|
||||||
"json-loader": "^0.5.4",
|
"json-loader": "^0.5.4",
|
||||||
"less": "^2.7.2",
|
"less": "^2.7.2",
|
||||||
"less-loader": "^4.0.3",
|
"less-loader": "^4.0.3",
|
||||||
"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": "^15.2.0",
|
||||||
|
"react-transition-group": "^1.1.2",
|
||||||
"rimraf": "^2.6.1",
|
"rimraf": "^2.6.1",
|
||||||
"rollup": "0.41",
|
"rollup": "0.41",
|
||||||
"rollup-plugin-node-resolve": "3",
|
"rollup-plugin-node-resolve": "3",
|
||||||
"style-loader": "^0.16.1",
|
"style-loader": "^0.16.1",
|
||||||
|
"uglify-js": "^2.4.11",
|
||||||
"url-loader": "^0.5.8",
|
"url-loader": "^0.5.8",
|
||||||
"webpack": "^2.4.1",
|
"webpack": "^2.4.1",
|
||||||
|
"webpack-bugsnag-plugins": "^1.1.1",
|
||||||
"webpack-dev-server": "^2.4.4",
|
"webpack-dev-server": "^2.4.4",
|
||||||
"uglify-js": "^2.4.11"
|
"webpack-notifier": "^1.6.0",
|
||||||
|
"workbox-webpack-plugin": "^3.4.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"babel-polyfill": "*",
|
"babel-polyfill": "*",
|
||||||
"browserify-zlib-next": "^1.0.1",
|
"browserify-zlib-next": "^1.0.1",
|
||||||
"classnames": "^2.2.5",
|
"classnames": "^2.2.5",
|
||||||
"coriolis-data": "EDCD/coriolis-data",
|
"coriolis-data": "../coriolis-data",
|
||||||
"d3": "4.8.0",
|
"d3": "4.8.0",
|
||||||
"detect-browser": "^1.7.0",
|
"detect-browser": "^1.7.0",
|
||||||
"fbemitter": "^2.1.1",
|
"fbemitter": "^2.1.1",
|
||||||
"lodash": "^4.17.4",
|
"lodash": "^4.17.10",
|
||||||
"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.5.8",
|
||||||
"react": "^15.5.4",
|
"react": "^15.5.4",
|
||||||
"react-dom": "^15.5.4",
|
"react-dom": "^15.5.4",
|
||||||
|
"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": "^0.22.3",
|
||||||
"superagent": "^3.5.2"
|
"superagent": "^3.5.2"
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import ModalHelp from './components/ModalHelp';
|
|||||||
import ModalImport from './components/ModalImport';
|
import ModalImport from './components/ModalImport';
|
||||||
import ModalPermalink from './components/ModalPermalink';
|
import ModalPermalink from './components/ModalPermalink';
|
||||||
import * as CompanionApiUtils from './utils/CompanionApiUtils';
|
import * as CompanionApiUtils from './utils/CompanionApiUtils';
|
||||||
|
import * as JournalUtils from './utils/JournalUtils';
|
||||||
|
|
||||||
import AboutPage from './pages/AboutPage';
|
import AboutPage from './pages/AboutPage';
|
||||||
import NotFoundPage from './pages/NotFoundPage';
|
import NotFoundPage from './pages/NotFoundPage';
|
||||||
@@ -92,7 +93,14 @@ 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);
|
||||||
const ship = CompanionApiUtils.shipFromJson(json);
|
console.info('Ship import data: ');
|
||||||
|
console.info(json);
|
||||||
|
let ship;
|
||||||
|
if (json && json.modules) {
|
||||||
|
ship = CompanionApiUtils.shipFromJson(json);
|
||||||
|
} else if (json && json.Modules) {
|
||||||
|
ship = JournalUtils.shipFromLoadoutJSON(json);
|
||||||
|
}
|
||||||
r.params.ship = ship.id;
|
r.params.ship = ship.id;
|
||||||
r.params.code = ship.toString();
|
r.params.code = ship.toString();
|
||||||
this._setPage(OutfittingPage, r);
|
this._setPage(OutfittingPage, r);
|
||||||
@@ -126,9 +134,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) {
|
||||||
Bugsnag.notifyException(errObj) // eslint-disable-line
|
bugsnagClient.notify(errObj); // eslint-disable-line
|
||||||
} else if (errObj instanceof String) {
|
} else if (errObj instanceof String) {
|
||||||
Bugsnag.notify(msg, errObj) // eslint-disable-line
|
bugsnagClient.notify(msg, errObj); // eslint-disable-line
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
@@ -172,13 +180,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'
|
||||||
@@ -200,7 +208,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 });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,7 +286,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
|
||||||
@@ -314,14 +322,60 @@ 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) {
|
||||||
|
window.addEventListener('load', () => {
|
||||||
|
// 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;
|
||||||
|
navigator.serviceWorker.register('/service-worker.js').then(function(reg) {
|
||||||
|
// updatefound is fired if service-worker.js changes.
|
||||||
|
reg.onupdatefound = function() {
|
||||||
|
// The updatefound event implies that reg.installing is set; see
|
||||||
|
// https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#service-worker-container-updatefound-event
|
||||||
|
var installingWorker = reg.installing;
|
||||||
|
|
||||||
|
installingWorker.onstatechange = function() {
|
||||||
|
switch (installingWorker.state) {
|
||||||
|
case 'installed':
|
||||||
|
if (navigator.serviceWorker.controller) {
|
||||||
|
// At this point, the old content will have been purged and the fresh content will
|
||||||
|
// have been added to the cache.
|
||||||
|
// It's the perfect time to display a "New content is available; please refresh."
|
||||||
|
// message in the page's interface.
|
||||||
|
console.log('New or updated content is available.');
|
||||||
|
self.setState({ appCacheUpdate: true }); // Browser downloaded a new app cache.
|
||||||
|
} else {
|
||||||
|
// At this point, everything has been precached.
|
||||||
|
// It's the perfect time to display a "Content is cached for offline use." message.
|
||||||
|
console.log('Content is now available offline!');
|
||||||
|
self.setState({ appCacheUpdate: true }); // Browser downloaded a new app cache.
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'redundant':
|
||||||
|
console.error('The installing service worker became redundant.');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}).catch(function(e) {
|
||||||
|
console.error('Error during service worker registration:', e);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
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());
|
||||||
@@ -339,14 +393,22 @@ 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 appCacheUpdate={this.state.appCacheUpdate} currentMenu={currentMenu}/>
|
||||||
{ this.state.modal }
|
{this.state.error ? this.state.error : this.state.page ? React.createElement(this.state.page, { currentMenu }) :
|
||||||
{ this.state.tooltip }
|
<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"
|
||||||
|
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" title={'Coriolis Commits since' + window.CORIOLIS_DATE}>Commits since last release
|
||||||
|
({window.CORIOLIS_DATE})</a>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</div>;
|
</div>;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
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 +258,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -37,10 +37,20 @@ const GRPCAT = {
|
|||||||
'ml': 'lasers',
|
'ml': 'lasers',
|
||||||
'c': 'projectiles',
|
'c': 'projectiles',
|
||||||
'mc': 'projectiles',
|
'mc': 'projectiles',
|
||||||
|
'axmc': 'experimental',
|
||||||
'fc': 'projectiles',
|
'fc': 'projectiles',
|
||||||
|
'rfl': 'experimental',
|
||||||
'pa': 'projectiles',
|
'pa': 'projectiles',
|
||||||
'rg': 'projectiles',
|
'rg': 'projectiles',
|
||||||
'mr': 'ordnance',
|
'mr': 'ordnance',
|
||||||
|
'axmr': 'experimental',
|
||||||
|
'rcpl': 'experimental',
|
||||||
|
'dtl': 'experimental',
|
||||||
|
'tbsc': 'experimental',
|
||||||
|
'tbem': 'experimental',
|
||||||
|
'tbrfl': 'experimental',
|
||||||
|
'mahr': 'experimental',
|
||||||
|
'rsl': 'experimental',
|
||||||
'tp': 'ordnance',
|
'tp': 'ordnance',
|
||||||
'nl': 'ordnance',
|
'nl': 'ordnance',
|
||||||
'sc': 'scanners',
|
'sc': 'scanners',
|
||||||
@@ -49,9 +59,20 @@ const GRPCAT = {
|
|||||||
'cs': 'scanners',
|
'cs': 'scanners',
|
||||||
'kw': 'scanners',
|
'kw': 'scanners',
|
||||||
'ws': 'scanners',
|
'ws': 'scanners',
|
||||||
|
'xs': 'scanners',
|
||||||
'ch': 'defence',
|
'ch': 'defence',
|
||||||
'po': 'defence',
|
'po': 'defence',
|
||||||
'ec': 'defence',
|
'ec': 'defence',
|
||||||
|
'sfn': 'defence',
|
||||||
|
// Guardian
|
||||||
|
'gpp': 'guardian',
|
||||||
|
'gpc': 'guardian',
|
||||||
|
'gsrp': 'guardian',
|
||||||
|
'ggc': 'guardian',
|
||||||
|
'gfsb': 'guardian',
|
||||||
|
'gmrp': 'guardian',
|
||||||
|
'gsc': 'guardian',
|
||||||
|
'ghrp': 'guardian'
|
||||||
};
|
};
|
||||||
// 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 = {
|
||||||
@@ -76,6 +97,11 @@ const CATEGORIES = {
|
|||||||
'hs': ['hs'],
|
'hs': ['hs'],
|
||||||
'defence': ['ch', 'po', 'ec'],
|
'defence': ['ch', 'po', 'ec'],
|
||||||
'scanners': ['sc', 'ss', 'cs', 'kw', 'ws'], // Overloaded with internal scanners
|
'scanners': ['sc', 'ss', 'cs', 'kw', 'ws'], // Overloaded with internal scanners
|
||||||
|
// Experimental
|
||||||
|
'experimental': ['axmc', 'axmr', 'rfl', 'tbrfl', 'tbsc', 'tbem', 'xs', 'sfn', 'rcpl', 'dtl', 'rsl', 'mahr', ],
|
||||||
|
|
||||||
|
// Guardian
|
||||||
|
'guardian': ['gpp', 'gpd', 'gpc', 'ggc', 'gsrp', 'gfsb', 'ghrp', 'gmrp', 'gsc']
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -89,7 +115,11 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
|||||||
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 = {
|
||||||
@@ -105,6 +135,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
|||||||
super(props);
|
super(props);
|
||||||
this._hideDiff = this._hideDiff.bind(this);
|
this._hideDiff = this._hideDiff.bind(this);
|
||||||
this.state = this._initState(props, context);
|
this.state = this._initState(props, context);
|
||||||
|
this.slotItems = [];// Array to hold <li> refs.
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -115,8 +146,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,
|
||||||
@@ -127,7 +159,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
|||||||
this._hideDiff(event);
|
this._hideDiff(event);
|
||||||
onSelect(m);
|
onSelect(m);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (modules instanceof Array) {
|
if (modules instanceof Array) {
|
||||||
list = buildGroup(modules[0].grp, modules);
|
list = buildGroup(modules[0].grp, modules);
|
||||||
@@ -135,7 +167,10 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
|||||||
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
|
||||||
@@ -185,37 +220,40 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let trackingFocus = false;
|
||||||
return { list, currentGroup };
|
return { list, currentGroup, 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;
|
||||||
@@ -231,9 +269,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);
|
||||||
|
|
||||||
@@ -242,7 +292,9 @@ 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)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,24 +303,28 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
|||||||
case 'G': mount = <MountGimballed className={'lg'}/>; break;
|
case 'G': mount = <MountGimballed className={'lg'}/>; break;
|
||||||
case 'T': mount = <MountTurret className={'lg'}/>; break;
|
case 'T': mount = <MountTurret className={'lg'}/>; break;
|
||||||
}
|
}
|
||||||
|
if (m.name && m.name === prevName) {
|
||||||
|
// elems.push(<br key={'b' + m.grp + i} />);
|
||||||
|
itemsOnThisRow = 0;
|
||||||
|
}
|
||||||
if (itemsOnThisRow == 6 || i > 0 && sortedModules.length > 3 && itemsPerClass > 2 && m.class != prevClass && (m.rating != prevRating || m.mount)) {
|
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;
|
||||||
}
|
}
|
||||||
|
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>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -319,6 +375,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
|
||||||
@@ -376,6 +467,23 @@ 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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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,12 +213,15 @@ 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'>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export default class EngagementRange extends TranslatedComponent {
|
|||||||
|
|
||||||
const { ship } = props;
|
const { ship } = props;
|
||||||
|
|
||||||
const maxRange = this._calcMaxRange(ship);
|
const maxRange = Math.round(this._calcMaxRange(ship));
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
maxRange
|
maxRange
|
||||||
|
|||||||
@@ -57,7 +57,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -77,7 +77,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);
|
||||||
|
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ export default class HardpointSlot extends Slot {
|
|||||||
<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')} onMouseOut={tooltip.bind(null, null)}>{translate('DPS')}: {formats.round1(m.getDps())} { m.getClip() ? <span>({formats.round1(m.getSDps()) })</span> : null }</div> : null }
|
||||||
{ 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 }
|
{ 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 }
|
||||||
{ 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 }
|
{ 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 }
|
||||||
{ 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.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 }
|
||||||
@@ -97,11 +97,12 @@ export default class HardpointSlot extends Slot {
|
|||||||
{ 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 }
|
||||||
{ 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 {
|
||||||
return <div className={'empty'}>{translate('empty')}</div>;
|
return <div className={'empty'}>{translate('empty')}</div>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,14 +17,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 +48,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 +107,52 @@ 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>
|
</ul>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ 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';
|
||||||
@@ -235,6 +237,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
|
||||||
@@ -430,6 +469,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>
|
||||||
@@ -505,6 +545,9 @@ export default class Header extends TranslatedComponent {
|
|||||||
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={() => window.location.reload() }>{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 +571,16 @@ export default class Header extends TranslatedComponent {
|
|||||||
{openedMenu == 'comp' ? this._getComparisonsMenu() : null}
|
{openedMenu == 'comp' ? this._getComparisonsMenu() : null}
|
||||||
</div>
|
</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>
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ export default class InternalSlot extends Slot {
|
|||||||
let modTT = translate('modified');
|
let modTT = translate('modified');
|
||||||
if (m && m.blueprint && m.blueprint.name) {
|
if (m && m.blueprint && m.blueprint.name) {
|
||||||
modTT = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
|
modTT = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
|
||||||
|
if (m.blueprint.special && m.blueprint.special.id >= 0) {
|
||||||
|
modTT += ', ' + translate(m.blueprint.special.name);
|
||||||
|
}
|
||||||
modTT = (
|
modTT = (
|
||||||
<div>
|
<div>
|
||||||
<div>{modTT}</div>
|
<div>{modTT}</div>
|
||||||
@@ -60,6 +63,9 @@ export default class InternalSlot extends Slot {
|
|||||||
{ m.getSpinup() ? <div className={'l'}>{translate('spinup')}: {formats.f1(m.getSpinup())}{u.s}</div> : null }
|
{ m.getSpinup() ? <div className={'l'}>{translate('spinup')}: {formats.f1(m.getSpinup())}{u.s}</div> : null }
|
||||||
{ m.getDuration() ? <div className={'l'}>{translate('duration')}: {formats.f1(m.getDuration())}{u.s}</div> : null }
|
{ m.getDuration() ? <div className={'l'}>{translate('duration')}: {formats.f1(m.getDuration())}{u.s}</div> : null }
|
||||||
{ m.grp === 'scb' ? <div className={'l'}>{translate('cells')}: {formats.int(m.getAmmo() + 1)}</div> : null }
|
{ m.grp === 'scb' ? <div className={'l'}>{translate('cells')}: {formats.int(m.getAmmo() + 1)}</div> : null }
|
||||||
|
{ m.grp === 'gsrp' ? <div className={'l'}>{translate('shield addition')}: {formats.f1(m.getShieldAddition())}{u.MJ}</div> : null }
|
||||||
|
{ m.grp === 'gfsb' ? <div className={'l'}>{translate('jump addition')}: {formats.f1(m.getJumpBoost())}{u.LY}</div> : null }
|
||||||
|
{ m.grp === 'gs' ? <div className={'l'}>{translate('shield addition')}: {formats.f1(m.getShieldAddition())}{u.MJ}</div> : null }
|
||||||
{ m.getShieldReinforcement() ? <div className={'l'}>{translate('shieldreinforcement')}: {formats.f1(m.getDuration() * m.getShieldReinforcement())}{u.MJ}</div> : null }
|
{ m.getShieldReinforcement() ? <div className={'l'}>{translate('shieldreinforcement')}: {formats.f1(m.getDuration() * m.getShieldReinforcement())}{u.MJ}</div> : null }
|
||||||
{ m.getShieldReinforcement() ? <div className={'l'}>{translate('total')}: {formats.int((m.getAmmo() + 1) * (m.getDuration() * m.getShieldReinforcement()))}{u.MJ}</div> : null }
|
{ m.getShieldReinforcement() ? <div className={'l'}>{translate('total')}: {formats.int((m.getAmmo() + 1) * (m.getDuration() * m.getShieldReinforcement()))}{u.MJ}</div> : null }
|
||||||
{ m.repair ? <div className={'l'}>{translate('repair')}: {m.repair}</div> : null }
|
{ m.repair ? <div className={'l'}>{translate('repair')}: {m.repair}</div> : null }
|
||||||
@@ -67,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 }
|
||||||
@@ -78,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 {
|
||||||
|
|||||||
@@ -18,7 +18,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 +28,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 +56,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 +73,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 +90,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 +107,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 +124,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 +141,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 +158,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 +178,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 +195,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 +260,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>;
|
||||||
|
|||||||
@@ -61,7 +61,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,6 +1,6 @@
|
|||||||
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';
|
||||||
|
|
||||||
@@ -67,21 +67,17 @@ export default class LineChart extends TranslatedComponent {
|
|||||||
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
|
* Update tooltip content
|
||||||
* @param {number} xPos x coordinate
|
* @param {number} xPos x coordinate
|
||||||
|
* @param {number} width current container width
|
||||||
*/
|
*/
|
||||||
_tooltip(xPos) {
|
_tooltip(xPos, width) {
|
||||||
let { xLabel, yLabel, xUnit, yUnit, func, series } = this.props;
|
let { xLabel, yLabel, xUnit, yUnit, func, series } = this.props;
|
||||||
let { xScale, yScale } = this.state;
|
let { xScale, yScale } = this.state;
|
||||||
let { width } = this.state.dimensions;
|
|
||||||
let { formats, translate } = this.context.language;
|
let { formats, translate } = this.context.language;
|
||||||
let x0 = xScale.invert(xPos),
|
let x0 = xScale.invert(xPos),
|
||||||
y0 = func(x0),
|
y0 = func(x0),
|
||||||
@@ -120,11 +116,11 @@ export default class LineChart extends TranslatedComponent {
|
|||||||
* Update dimensions based on properties and scale
|
* Update dimensions based on properties and scale
|
||||||
* @param {Object} props React Component properties
|
* @param {Object} props React Component properties
|
||||||
* @param {number} scale size ratio / scale
|
* @param {number} scale size ratio / scale
|
||||||
|
* @param {number} width current width of the container
|
||||||
* @returns {Object} calculated dimensions
|
* @returns {Object} calculated dimensions
|
||||||
*/
|
*/
|
||||||
_updateDimensions(props, scale) {
|
_updateDimensions(props, scale, width) {
|
||||||
const { xMax, xMin, yMin, yMax } = props;
|
const { xMax, xMin, yMin, yMax } = props;
|
||||||
const { width, height } = this.state.dimensions;
|
|
||||||
const innerWidth = width - MARGIN.left - MARGIN.right;
|
const innerWidth = width - MARGIN.left - MARGIN.right;
|
||||||
const outerHeight = Math.round(width * props.aspect);
|
const outerHeight = Math.round(width * props.aspect);
|
||||||
const innerHeight = outerHeight - MARGIN.top - MARGIN.bottom;
|
const innerHeight = outerHeight - MARGIN.top - MARGIN.bottom;
|
||||||
@@ -149,10 +145,11 @@ export default class LineChart extends TranslatedComponent {
|
|||||||
/**
|
/**
|
||||||
* Move and update tooltip
|
* Move and update tooltip
|
||||||
* @param {SyntheticEvent} e Event
|
* @param {SyntheticEvent} e Event
|
||||||
|
* @param {number} width current container width
|
||||||
*/
|
*/
|
||||||
_moveTip(e) {
|
_moveTip(e, width) {
|
||||||
let clientX = e.touches ? e.touches[0].clientX : e.clientX;
|
let clientX = e.touches ? e.touches[0].clientX : e.clientX;
|
||||||
this._tooltip(Math.round(clientX - e.currentTarget.getBoundingClientRect().left));
|
this._tooltip(Math.round(clientX - e.currentTarget.getBoundingClientRect().left), width);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -227,57 +224,58 @@ export default class LineChart extends TranslatedComponent {
|
|||||||
* @return {React.Component} Chart SVG
|
* @return {React.Component} Chart SVG
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
const { innerWidth, outerHeight, innerHeight } = this._updateDimensions(this.props, this.context.sizeRatio);
|
|
||||||
const { width, height } = this.state.dimensions;
|
|
||||||
const { xMin, xMax, xLabel, yLabel, xUnit, yUnit, xMark, colors } = this.props;
|
|
||||||
const { tipHeight, detailElems, markerElems, seriesData, seriesLines } = this.state;
|
|
||||||
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'} /> : '';
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Measure width='100%' whitelist={['width', 'top']} onMeasure={ (dimensions) => { this.setState({ dimensions }); }}>
|
<ContainerDimensions>
|
||||||
<div width={width} height={height}>
|
{ ({ width, height }) => {
|
||||||
<svg style={{ width: '100%', height: outerHeight }}>
|
const { innerWidth, outerHeight, innerHeight } = this._updateDimensions(this.props, this.context.sizeRatio, width, height);
|
||||||
<g transform={`translate(${MARGIN.left},${MARGIN.top})`}>
|
const { xMin, xMax, xLabel, yLabel, xUnit, yUnit, xMark, colors } = this.props;
|
||||||
<g>{xmark}</g>
|
const { tipHeight, detailElems, markerElems, seriesData, seriesLines } = this.state;
|
||||||
<g>{lines}</g>
|
const lines = seriesLines.map((line, i) => <path key={i} className='line' fill='none' stroke={colors[i]} strokeWidth='1' d={line(seriesData)} />).reverse();
|
||||||
<g className='x axis' ref={(elem) => d3.select(elem).call(this.xAxis)} transform={`translate(0,${innerHeight})`}>
|
|
||||||
<text className='cap' y='30' dy='.1em' x={innerWidth / 2} style={{ textAnchor: 'middle' }}>
|
const markX = xMark ? innerWidth * (xMark - xMin) / (xMax - xMin) : 0;
|
||||||
<tspan>{xLabel}</tspan>
|
const xmark = xMark ? <path key={'mark'} className='line' fill='none' strokeDasharray='5,5' stroke={'#ff8c0d'} strokeWidth='1' d={'M ' + markX + ' ' + innerHeight + ' L ' + markX + ' 0'} /> : '';
|
||||||
<tspan className='metric'> ({xUnit})</tspan>
|
return (
|
||||||
</text>
|
<div width={width} height={height}>
|
||||||
</g>
|
<svg style={{ width: '100%', height: outerHeight }}>
|
||||||
<g className='y axis' ref={(elem) => d3.select(elem).call(this.yAxis)}>
|
<g transform={`translate(${MARGIN.left},${MARGIN.top})`}>
|
||||||
<text className='cap' transform='rotate(-90)' y='-50' dy='.1em' x={innerHeight / -2} style={{ textAnchor: 'middle' }}>
|
<g>{xmark}</g>
|
||||||
<tspan>{yLabel}</tspan>
|
<g>{lines}</g>
|
||||||
{ yUnit && <tspan className='metric'> ({yUnit})</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 ref={(g) => this.tipContainer = d3.select(g)} style={{ display: 'none' }}>
|
<tspan className='metric'> ({xUnit})</tspan>
|
||||||
<rect className='tooltip' height={tipHeight + 'em'}></rect>
|
</text>
|
||||||
{detailElems}
|
</g>
|
||||||
</g>
|
<g className='y axis' ref={(elem) => d3.select(elem).call(this.yAxis)}>
|
||||||
<g ref={(g) => this.markersContainer = d3.select(g)} style={{ display: 'none' }}>
|
<text className='cap' transform='rotate(-90)' y='-50' dy='.1em' x={innerHeight / -2} style={{ textAnchor: 'middle' }}>
|
||||||
{markerElems}
|
<tspan>{yLabel}</tspan>
|
||||||
</g>
|
{ yUnit && <tspan className='metric'> ({yUnit})</tspan> }
|
||||||
<rect
|
</text>
|
||||||
fillOpacity='0'
|
</g>
|
||||||
height={innerHeight}
|
<g ref={(g) => this.tipContainer = d3.select(g)} style={{ display: 'none' }}>
|
||||||
width={innerWidth + 1}
|
<rect className='tooltip' height={tipHeight + 'em'}></rect>
|
||||||
onMouseEnter={this._showTip}
|
{detailElems}
|
||||||
onTouchStart={this._showTip}
|
</g>
|
||||||
onMouseLeave={this._hideTip}
|
<g ref={(g) => this.markersContainer = d3.select(g)} style={{ display: 'none' }}>
|
||||||
onTouchEnd={this._hideTip}
|
{markerElems}
|
||||||
onMouseMove={this._moveTip}
|
</g>
|
||||||
onTouchMove={this._moveTip}
|
<rect
|
||||||
/>
|
fillOpacity='0'
|
||||||
</g>
|
height={innerHeight}
|
||||||
</svg>
|
width={innerWidth + 1}
|
||||||
</div>
|
onMouseEnter={this._showTip}
|
||||||
</Measure>
|
onTouchStart={this._showTip}
|
||||||
|
onMouseLeave={this._hideTip}
|
||||||
|
onTouchEnd={this._hideTip}
|
||||||
|
onMouseMove={e => this._moveTip(e, width)}
|
||||||
|
onTouchMove={e => this._moveTip(e, width)}
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</ContainerDimensions>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
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>;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 });
|
||||||
|
|||||||
117
src/app/components/ModalOrbis.jsx
Normal file
117
src/app/components/ModalOrbis.jsx
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
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: '...',
|
||||||
|
authenticatedStatus: 'Checking...'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 >{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>;
|
||||||
}
|
}
|
||||||
|
|||||||
262
src/app/components/ModalShoppingList.jsx
Normal file
262
src/app/components/ModalShoppingList.jsx
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
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();
|
||||||
|
const target = event.target;
|
||||||
|
target.disabled = this.state.blueprints.length > 0;
|
||||||
|
if (this.state.blueprints.length === 0) {
|
||||||
|
target.innerText = 'No modded components.';
|
||||||
|
target.disabled = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
target.innerText = 'Send to EDEngineer';
|
||||||
|
target.disabled = false;
|
||||||
|
}, 3000);
|
||||||
|
} else {
|
||||||
|
target.innerText = '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 = '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>Grade 1 rolls </label>
|
||||||
|
<input id={1} type={'number'} min={0} defaultValue={this.state.matsPerGrade[1]} onChange={this.changeHandler} />
|
||||||
|
<br/>
|
||||||
|
<label>Grade 2 rolls </label>
|
||||||
|
<input id={2} type={'number'} min={0} defaultValue={this.state.matsPerGrade[2]} onChange={this.changeHandler} />
|
||||||
|
<br/>
|
||||||
|
<label>Grade 3 rolls </label>
|
||||||
|
<input id={3} type={'number'} min={0} value={this.state.matsPerGrade[3]} onChange={this.changeHandler} />
|
||||||
|
<br/>
|
||||||
|
<label>Grade 4 rolls </label>
|
||||||
|
<input id={4} type={'number'} min={0} value={this.state.matsPerGrade[4]} onChange={this.changeHandler} />
|
||||||
|
<br/>
|
||||||
|
<label>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'}>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'}>Failed to send to EDEngineer (Launch EDEngineer and make sure the API is started then refresh the page.)</p>
|
||||||
|
<p hidden={compatible} id={'browserbad'} className={'l'}>Sending to EDEngineer is not compatible with Firefox's security settings. Please try again with Chrome.</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,6 +3,7 @@ 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
|
||||||
@@ -14,7 +15,10 @@ export default class Modification extends TranslatedComponent {
|
|||||||
m: PropTypes.object.isRequired,
|
m: PropTypes.object.isRequired,
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
value: PropTypes.number.isRequired,
|
value: PropTypes.number.isRequired,
|
||||||
onChange: PropTypes.func.isRequired
|
onChange: PropTypes.func.isRequired,
|
||||||
|
onKeyDown: PropTypes.func.isRequired,
|
||||||
|
modItems: PropTypes.array.isRequired,
|
||||||
|
handleModChange: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -35,31 +39,23 @@ 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 { m, name, ship } = this.props;
|
||||||
|
value = Math.max(Math.min(value, 50000), -50000);
|
||||||
let scaledValue = Math.round(Number(value) * 100);
|
ship.setModification(m, name, value, true, true);
|
||||||
// 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 });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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 +63,50 @@ 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;
|
|
||||||
if (name === 'jitter') {
|
|
||||||
symbol = '°';
|
|
||||||
} 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={'cb'} value={this.state.value}
|
||||||
|
decimals={2} style={{ textAlign: 'right' }} step={0.01}
|
||||||
|
stepModifier={1} onKeyDown={ this.props.onKeyDown }
|
||||||
|
onValueChange={this._updateValue.bind(this)} /> :
|
||||||
|
<input type="text" value={formats.f2(this.state.value)}
|
||||||
|
disabled className={'number-editor'}
|
||||||
|
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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,18 @@ import { isEmpty, stopCtxPropagation } from '../utils/UtilityFunctions';
|
|||||||
import cn from 'classnames';
|
import cn from 'classnames';
|
||||||
import { Modifications } from 'coriolis-data/dist';
|
import { Modifications } from 'coriolis-data/dist';
|
||||||
import Modification from './Modification';
|
import Modification from './Modification';
|
||||||
import { getBlueprint, blueprintTooltip, setWorst, setBest, setExtreme, setRandom } from '../utils/BlueprintFunctions';
|
import {
|
||||||
|
getBlueprint,
|
||||||
|
blueprintTooltip,
|
||||||
|
setPercent,
|
||||||
|
getPercent,
|
||||||
|
setRandom,
|
||||||
|
specialToolTip
|
||||||
|
} from '../utils/BlueprintFunctions';
|
||||||
|
|
||||||
|
const MODIFICATIONS_COMPARATOR = (mod1, mod2) => {
|
||||||
|
return mod1.props.name.localeCompare(mod2.props.name);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modifications menu
|
* Modifications menu
|
||||||
@@ -17,7 +28,8 @@ export default class ModificationsMenu extends TranslatedComponent {
|
|||||||
ship: PropTypes.object.isRequired,
|
ship: PropTypes.object.isRequired,
|
||||||
m: PropTypes.object.isRequired,
|
m: PropTypes.object.isRequired,
|
||||||
marker: PropTypes.string.isRequired,
|
marker: PropTypes.string.isRequired,
|
||||||
onChange: PropTypes.func.isRequired
|
onChange: PropTypes.func.isRequired,
|
||||||
|
modButton:PropTypes.object
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -30,14 +42,24 @@ export default class ModificationsMenu extends TranslatedComponent {
|
|||||||
|
|
||||||
this._toggleBlueprintsMenu = this._toggleBlueprintsMenu.bind(this);
|
this._toggleBlueprintsMenu = this._toggleBlueprintsMenu.bind(this);
|
||||||
this._toggleSpecialsMenu = this._toggleSpecialsMenu.bind(this);
|
this._toggleSpecialsMenu = this._toggleSpecialsMenu.bind(this);
|
||||||
this._rollWorst = this._rollWorst.bind(this);
|
this._rollFifty = this._rollFifty.bind(this);
|
||||||
this._rollRandom = this._rollRandom.bind(this);
|
this._rollRandom = this._rollRandom.bind(this);
|
||||||
this._rollBest = this._rollBest.bind(this);
|
this._rollBest = this._rollBest.bind(this);
|
||||||
this._rollExtreme = this._rollExtreme.bind(this);
|
this._rollWorst = this._rollWorst.bind(this);
|
||||||
this._reset = this._reset.bind(this);
|
this._reset = this._reset.bind(this);
|
||||||
|
this._keyDown = this._keyDown.bind(this);
|
||||||
|
this.modItems = [];// Array to hold various element refs (<li>, <div>, <ul>, etc.)
|
||||||
|
this.firstModId = null;
|
||||||
|
this.firstBPLabel = null;// First item in mod menu
|
||||||
|
this.lastModId = null;
|
||||||
|
this.selectedModId = null;
|
||||||
|
this.selectedSpecialId = null;
|
||||||
|
this.lastNeId = null;// Last number editor id. Used to set focus to last number editor when shift-tab pressed on first element in mod menu.
|
||||||
|
this.modValDidChange = false; // used to determine if component update was caused by change in modification value.
|
||||||
|
this._handleModChange = this._handleModChange.bind(this);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
blueprintMenuOpened: false,
|
blueprintMenuOpened: !(props.m.blueprint && props.m.blueprint.name),
|
||||||
specialMenuOpened: false
|
specialMenuOpened: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -52,7 +74,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);
|
||||||
@@ -66,9 +87,13 @@ export default class ModificationsMenu extends TranslatedComponent {
|
|||||||
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 +101,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 +169,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 +209,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' &&
|
||||||
|
m.blueprint.grades[m.blueprint.grade].features[modName];
|
||||||
|
this.lastNeId = modName;
|
||||||
|
(editable ? modifiableModifications : modifications).push(
|
||||||
|
<Modification key={ key } ship={ ship } m={ m }
|
||||||
|
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 +250,9 @@ export default class ModificationsMenu extends TranslatedComponent {
|
|||||||
const blueprint = getBlueprint(fdname, m);
|
const blueprint = getBlueprint(fdname, m);
|
||||||
blueprint.grade = grade;
|
blueprint.grade = grade;
|
||||||
ship.setModuleBlueprint(m, blueprint);
|
ship.setModuleBlueprint(m, blueprint);
|
||||||
|
setPercent(ship, m, 100);
|
||||||
|
|
||||||
this.setState({ blueprintMenuOpened: false });
|
this.setState({ blueprintMenuOpened: false, specialMenuOpened: true });
|
||||||
this.props.onChange();
|
this.props.onChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,11 +283,15 @@ export default class ModificationsMenu extends TranslatedComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provide a 'worst' roll within the information we have
|
* Provide a '50%' roll within the information we have
|
||||||
*/
|
*/
|
||||||
_rollWorst() {
|
_rollFifty() {
|
||||||
const { m, ship } = this.props;
|
const { m, ship } = this.props;
|
||||||
setWorst(ship, m);
|
setPercent(ship, m, 50);
|
||||||
|
|
||||||
|
// this will change the values in the modifications. Set modDidChange to true to prevent focus change when component updates
|
||||||
|
this._handleModChange(true);
|
||||||
|
|
||||||
this.props.onChange();
|
this.props.onChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,6 +301,10 @@ export default class ModificationsMenu extends TranslatedComponent {
|
|||||||
_rollRandom() {
|
_rollRandom() {
|
||||||
const { m, ship } = this.props;
|
const { m, ship } = this.props;
|
||||||
setRandom(ship, m);
|
setRandom(ship, m);
|
||||||
|
|
||||||
|
// this will change the values in the modifications. Set modDidChange to true to prevent focus change when component updates
|
||||||
|
this._handleModChange(true);
|
||||||
|
|
||||||
this.props.onChange();
|
this.props.onChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,16 +313,22 @@ export default class ModificationsMenu extends TranslatedComponent {
|
|||||||
*/
|
*/
|
||||||
_rollBest() {
|
_rollBest() {
|
||||||
const { m, ship } = this.props;
|
const { m, ship } = this.props;
|
||||||
setBest(ship, m);
|
setPercent(ship, m, 100);
|
||||||
|
|
||||||
|
// this will change the values in the modifications. Set modDidChange to true to prevent focus change when component updates
|
||||||
|
this._handleModChange(true);
|
||||||
|
|
||||||
this.props.onChange();
|
this.props.onChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provide an 'extreme' roll within the information we have
|
* Provide a 'worst' roll within the information we have
|
||||||
*/
|
*/
|
||||||
_rollExtreme() {
|
_rollWorst() {
|
||||||
const { m, ship } = this.props;
|
const { m, ship } = this.props;
|
||||||
setExtreme(ship, m);
|
setPercent(ship, m, 0);
|
||||||
|
// this will change the values in the modifications. Set modDidChange to true to prevent focus change when component updates
|
||||||
|
this._handleModChange(true);
|
||||||
this.props.onChange();
|
this.props.onChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,10 +339,67 @@ export default class ModificationsMenu extends TranslatedComponent {
|
|||||||
const { m, ship } = this.props;
|
const { m, ship } = this.props;
|
||||||
ship.clearModifications(m);
|
ship.clearModifications(m);
|
||||||
ship.clearModuleBlueprint(m);
|
ship.clearModuleBlueprint(m);
|
||||||
|
this.selectedModId = null;
|
||||||
|
this.selectedSpecialId = null;
|
||||||
this.props.onChange();
|
this.props.onChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set mod did change boolean
|
||||||
|
* @param {boolean} b Boolean to determine if a change has been made to a module
|
||||||
|
*/
|
||||||
|
_handleModChange(b) {
|
||||||
|
this.modValDidChange = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set focus on first element in modifications menu
|
||||||
|
* after it first mounts
|
||||||
|
*/
|
||||||
|
componentDidMount() {
|
||||||
|
let firstEleCn = this.modItems['modMainDiv'].children.length > 0 ? this.modItems['modMainDiv'].children[0].className : null;
|
||||||
|
if (firstEleCn.indexOf('select-group cap') >= 0) {
|
||||||
|
this.modItems['modMainDiv'].children[1].firstElementChild.focus();
|
||||||
|
} else {
|
||||||
|
this.modItems['modMainDiv'].firstElementChild.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set focus on first element in modifications menu
|
||||||
|
* if component updates, unless update is due to value change
|
||||||
|
* in a modification
|
||||||
|
*/
|
||||||
|
componentDidUpdate() {
|
||||||
|
if (!this.modValDidChange) {
|
||||||
|
if (this.modItems['modMainDiv'].children.length > 0) {
|
||||||
|
if (this.modItems[this.selectedModId]) {
|
||||||
|
this.modItems[this.selectedModId].focus();
|
||||||
|
return;
|
||||||
|
} else if (this.modItems[this.selectedSpecialId]) {
|
||||||
|
this.modItems[this.selectedSpecialId].focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let firstEleCn = this.modItems['modMainDiv'].children[0].className;
|
||||||
|
if (firstEleCn.indexOf('button-inline-menu') >= 0) {
|
||||||
|
this.modItems['modMainDiv'].firstElementChild.focus();
|
||||||
|
} else if (firstEleCn.indexOf('select-group cap') >= 0) {
|
||||||
|
this.modItems['modMainDiv'].children[1].firstElementChild.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this._handleModChange(false);// Need to reset if component update due to value change
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* set focus to the modification menu icon after mod menu is unmounted.
|
||||||
|
*/
|
||||||
|
componentWillUnmount() {
|
||||||
|
if (this.props.modButton) {
|
||||||
|
this.props.modButton.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render the list
|
* Render the list
|
||||||
* @return {React.Component} List
|
* @return {React.Component} List
|
||||||
@@ -226,66 +412,82 @@ export default class ModificationsMenu extends TranslatedComponent {
|
|||||||
|
|
||||||
const _toggleBlueprintsMenu = this._toggleBlueprintsMenu;
|
const _toggleBlueprintsMenu = this._toggleBlueprintsMenu;
|
||||||
const _toggleSpecialsMenu = this._toggleSpecialsMenu;
|
const _toggleSpecialsMenu = this._toggleSpecialsMenu;
|
||||||
const _rollBest = this._rollBest;
|
const _rollFull = this._rollBest;
|
||||||
const _rollExtreme = this._rollExtreme;
|
|
||||||
const _rollWorst = this._rollWorst;
|
const _rollWorst = this._rollWorst;
|
||||||
|
const _rollFifty = this._rollFifty;
|
||||||
const _rollRandom = this._rollRandom;
|
const _rollRandom = this._rollRandom;
|
||||||
const _reset = this._reset;
|
const _reset = this._reset;
|
||||||
|
|
||||||
let blueprintLabel;
|
let blueprintLabel;
|
||||||
let haveBlueprint = false;
|
let haveBlueprint = false;
|
||||||
let blueprintTt;
|
let blueprintTt;
|
||||||
if (m.blueprint && m.blueprint.name) {
|
let blueprintCv;
|
||||||
|
// TODO: Fix this to actually find the correct blueprint.
|
||||||
|
if (!m.blueprint || !m.blueprint.name || !m.blueprint.fdname || !Modifications.modules[m.grp].blueprints || !Modifications.modules[m.grp].blueprints[m.blueprint.fdname]) {
|
||||||
|
this.props.ship.clearModuleBlueprint(m);
|
||||||
|
this.props.ship.clearModuleSpecial(m);
|
||||||
|
}
|
||||||
|
if (m.blueprint && m.blueprint.name && Modifications.modules[m.grp].blueprints[m.blueprint.fdname].grades[m.blueprint.grade]) {
|
||||||
blueprintLabel = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
|
blueprintLabel = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
|
||||||
haveBlueprint = true;
|
haveBlueprint = true;
|
||||||
blueprintTt = blueprintTooltip(translate, m.blueprint.grades[m.blueprint.grade], Modifications.modules[m.grp].blueprints[m.blueprint.fdname].grades[m.blueprint.grade].engineers, m.grp);
|
blueprintTt = blueprintTooltip(translate, m.blueprint.grades[m.blueprint.grade], Modifications.modules[m.grp].blueprints[m.blueprint.fdname].grades[m.blueprint.grade].engineers, m.grp);
|
||||||
|
blueprintCv = getPercent(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
let specialLabel;
|
let specialLabel;
|
||||||
|
let specialTt;
|
||||||
if (m.blueprint && m.blueprint.special) {
|
if (m.blueprint && m.blueprint.special) {
|
||||||
specialLabel = m.blueprint.special.name;
|
specialLabel = m.blueprint.special.name;
|
||||||
|
specialTt = specialToolTip(translate, m.blueprint.grades[m.blueprint.grade], m.grp, m, m.blueprint.special.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 }
|
||||||
|
{ showRolls ?
|
||||||
|
|
||||||
<table style={{ width: '100%', backgroundColor: 'transparent' }}>
|
<table style={{ width: '100%', backgroundColor: 'transparent' }}>
|
||||||
<tbody>
|
<tbody>
|
||||||
{ showRolls ?
|
{ showRolls ?
|
||||||
<tr>
|
<tr>
|
||||||
<td> { translate('roll') }: </td>
|
<td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: false }) }> { translate('roll') }: </td>
|
||||||
<td style={{ cursor: 'pointer' }} onClick={_rollWorst} onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_WORST')} onMouseOut={tooltip.bind(null, null)}> { translate('worst') } </td>
|
<td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: blueprintCv === 0 }) } style={{ cursor: 'pointer' }} onClick={_rollWorst} onKeyDown={ this._keyDown } onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_WORST')} onMouseOut={tooltip.bind(null, null)}> { translate('0%') } </td>
|
||||||
<td style={{ cursor: 'pointer' }} onClick={_rollBest}onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_BEST')} onMouseOut={tooltip.bind(null, null)}> { translate('best') } </td>
|
<td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: blueprintCv === 50 })} style={{ cursor: 'pointer' }} onClick={_rollFifty} onKeyDown={ this._keyDown } onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_FIFTY')} onMouseOut={tooltip.bind(null, null)}> { translate('50%') } </td>
|
||||||
<td style={{ cursor: 'pointer' }} onClick={_rollExtreme}onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_EXTREME')} onMouseOut={tooltip.bind(null, null)}> { translate('extreme') } </td>
|
<td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: blueprintCv === 100 })} style={{ cursor: 'pointer' }} onClick={_rollFull} onKeyDown={ this._keyDown } onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_BEST')} onMouseOut={tooltip.bind(null, null)}> { translate('100%') } </td>
|
||||||
<td style={{ cursor: 'pointer' }} onClick={_rollRandom} onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_RANDOM')} onMouseOut={tooltip.bind(null, null)}> { translate('random') } </td>
|
<td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: blueprintCv === null || blueprintCv % 50 != 0 })} style={{ cursor: 'pointer' }} onClick={_rollRandom} onKeyDown={ this._keyDown } onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_RANDOM')} onMouseOut={tooltip.bind(null, null)}> { translate('random') } </td>
|
||||||
</tr> : null }
|
|
||||||
{ showReset ?
|
|
||||||
<tr>
|
|
||||||
<td colSpan={'5'} style={{ cursor: 'pointer' }} onClick={_reset}onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_RESET')} onMouseOut={tooltip.bind(null, null)}> { translate('reset') } </td>
|
|
||||||
</tr> : null }
|
</tr> : null }
|
||||||
</tbody>
|
</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) }
|
||||||
|
|||||||
@@ -42,6 +42,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:
|
||||||
@@ -144,51 +201,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,6 +241,7 @@ 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>
|
||||||
@@ -206,20 +249,17 @@ export default class Offence extends TranslatedComponent {
|
|||||||
</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));
|
||||||
@@ -231,19 +271,31 @@ export default class Offence extends TranslatedComponent {
|
|||||||
<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='1'>{translate('overall')}</th>
|
||||||
<th colSpan='2'>{translate('opponent\'s shields')}</th>
|
<th colSpan='2'>{translate('opponent\'s shields')}</th>
|
||||||
<th colSpan='2'>{translate('opponent\'s armour')}</th>
|
<th colSpan='2'>{translate('opponent\'s armour')}</th>
|
||||||
</tr>
|
</tr>
|
||||||
<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>
|
||||||
<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_SHIELDS')} onMouseOut={tooltip.bind(null, null)} onClick={sortOrder.bind(this, 'esdpss')}>{'sdps'}</th>
|
||||||
<th className='sortable' onMouseOver={termtip.bind(null, 'TT_EFFECTIVENESS_SHIELDS')} onMouseOut={tooltip.bind(null, null)}onClick={sortOrder.bind(this, 'es')}>{'eft'}</th>
|
<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_ARMOUR')} onMouseOut={tooltip.bind(null, null)}onClick={sortOrder.bind(this, 'esdpsh')}>{'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='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_ARMOUR')} onMouseOut={tooltip.bind(null, null)}onClick={sortOrder.bind(this, 'eh')}>{'eft'}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{rows}
|
{rows}
|
||||||
</tbody>
|
{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>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div className='group quarter'>
|
<div className='group quarter'>
|
||||||
@@ -254,6 +306,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,6 +1,6 @@
|
|||||||
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'];
|
||||||
@@ -27,13 +27,6 @@ export default class PieChart extends Component {
|
|||||||
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
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -41,15 +34,15 @@ export default class PieChart extends Component {
|
|||||||
* Generate a slice of the pie chart
|
* Generate a slice of the pie chart
|
||||||
* @param {Object} d the data for this slice
|
* @param {Object} d the data for this slice
|
||||||
* @param {number} i the index of 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
|
* @returns {Object} the SVG for the slice
|
||||||
*/
|
*/
|
||||||
sliceGenerator(d, i) {
|
sliceGenerator(d, i, width) {
|
||||||
if (!d || d.value == 0) {
|
if (!d || d.value == 0) {
|
||||||
// Ignore 0 values
|
// Ignore 0 values
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { width, height } = this.state.dimensions;
|
|
||||||
const { data } = this.props;
|
const { data } = this.props;
|
||||||
|
|
||||||
// Push the labels further out from the centre of the slice
|
// Push the labels further out from the centre of the slice
|
||||||
@@ -76,22 +69,24 @@ export default class PieChart extends Component {
|
|||||||
* @returns {object} Markup
|
* @returns {object} Markup
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
const { width, height } = this.state.dimensions;
|
|
||||||
const pie = this.pie(this.props.data),
|
|
||||||
translate = `translate(${width / 2}, ${width * 0.4})`;
|
|
||||||
|
|
||||||
this.arc.outerRadius(width * 0.4);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Measure width='100%' whitelist={['width', 'top']} onMeasure={ (dimensions) => { this.setState({ dimensions }); }}>
|
<ContainerDimensions>
|
||||||
<div width={width} height={width}>
|
{ ({ width }) => {
|
||||||
<svg style={{ stroke: 'None' }} width={width} height={width * 0.9}>
|
const pie = this.pie(this.props.data),
|
||||||
<g transform={translate}>
|
translate = `translate(${width / 2}, ${width * 0.4})`;
|
||||||
{pie.map((d, i) => this.sliceGenerator(d, i))}
|
|
||||||
</g>
|
this.arc.outerRadius(width * 0.4);
|
||||||
</svg>
|
return (
|
||||||
</div>
|
<div width={width} height={width}>
|
||||||
</Measure>
|
<svg style={{ stroke: 'None' }} width={width} height={width * 0.9}>
|
||||||
|
<g transform={translate}>
|
||||||
|
{pie.map((d, i) => this.sliceGenerator(d, i, width))}
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</ContainerDimensions>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,22 +15,34 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
|||||||
cargo: PropTypes.number.isRequired,
|
cargo: PropTypes.number.isRequired,
|
||||||
fuel: PropTypes.number.isRequired,
|
fuel: PropTypes.number.isRequired,
|
||||||
marker: PropTypes.string.isRequired,
|
marker: PropTypes.string.isRequired,
|
||||||
|
pips: PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ShipSummaryTable constructor
|
||||||
|
* @param {Object} props The props
|
||||||
|
*/
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.didContextChange = this.didContextChange.bind(this);
|
||||||
|
this.state = {
|
||||||
|
shieldColour: 'blue'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render the table
|
* Render the table
|
||||||
* @return {React.Component} Summary table
|
* @return {React.Component} Summary table
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
const { ship, cargo, fuel } = this.props;
|
const { ship, cargo, fuel, pips } = this.props;
|
||||||
let { language, tooltip, termtip } = this.context;
|
let { language, tooltip, termtip } = this.context;
|
||||||
let translate = language.translate;
|
let translate = language.translate;
|
||||||
let u = language.units;
|
let u = language.units;
|
||||||
let formats = language.formats;
|
let formats = language.formats;
|
||||||
let { time, int, round, f1, f2 } = formats;
|
let { time, int, round, f1, f2 } = formats;
|
||||||
let hide = tooltip.bind(null, null);
|
let hide = tooltip.bind(null, null);
|
||||||
|
const shieldGenerator = ship.findInternalByGroup('sg') || ship.findInternalByGroup('psg');
|
||||||
const shieldGenerator = ship.findInternalByGroup('sg');
|
|
||||||
const sgClassNames = cn({ warning: shieldGenerator && !ship.shield, muted: !shieldGenerator });
|
const sgClassNames = cn({ warning: shieldGenerator && !ship.shield, muted: !shieldGenerator });
|
||||||
const sgTooltip = shieldGenerator ? 'TT_SUMMARY_SHIELDS' : 'TT_SUMMARY_SHIELDS_NONFUNCTIONAL';
|
const sgTooltip = shieldGenerator ? 'TT_SUMMARY_SHIELDS' : 'TT_SUMMARY_SHIELDS_NONFUNCTIONAL';
|
||||||
const timeToDrain = Calc.timeToDrainWep(ship, 4);
|
const timeToDrain = Calc.timeToDrainWep(ship, 4);
|
||||||
@@ -38,64 +50,163 @@ 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 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('fuel')}</th>
|
{/* <th onMouseEnter={termtip.bind(null, 'heat per second')} onMouseLeave={hide} rowSpan={2}>{translate('HPS')}</th> */}
|
||||||
<th colSpan={3}>{translate('mass')}</th>
|
<th rowSpan={2}>{translate('cargo')}</th>
|
||||||
<th onMouseEnter={termtip.bind(null, 'hull hardness', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('hrd')}</th>
|
<th rowSpan={2}>{translate('pax')}</th>
|
||||||
<th rowSpan={2}>{translate('crew')}</th>
|
<th rowSpan={2}>{translate('fuel')}</th>
|
||||||
<th onMouseEnter={termtip.bind(null, 'mass lock factor', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('MLF')}</th>
|
<th colSpan={3}>{translate('mass')}</th>
|
||||||
</tr>
|
<th onMouseEnter={termtip.bind(null, 'hull hardness', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('hrd')}</th>
|
||||||
<tr>
|
<th rowSpan={2}>{translate('crew')}</th>
|
||||||
<th className='lft'>{translate('max')}</th>
|
<th onMouseEnter={termtip.bind(null, 'mass lock factor', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('MLF')}</th>
|
||||||
<th>{translate('unladen')}</th>
|
<th onMouseEnter={termtip.bind(null, 'TT_SUMMARY_BOOST_TIME', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('boost time')}</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>{round(ship.fuelCapacity)}{u.T}</td>
|
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_EPS', { cap: 0 })} onMouseLeave={hide}>{f1(ship.totalEps)}</td>
|
||||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_HULL_MASS', { cap: 0 })} onMouseLeave={hide}>{ship.hullMass}{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_UNLADEN_MASS', { cap: 0 })} onMouseLeave={hide}>{int(ship.unladenMass)}{u.T}</td>
|
{/* <td>{f1(ship.totalHps)}</td> */}
|
||||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_MASS', { cap: 0 })} onMouseLeave={hide}>{int(ship.ladenMass)}{u.T}</td>
|
<td>{round(ship.cargoCapacity)}{u.T}</td>
|
||||||
<td>{int(ship.hardness)}</td>
|
<td>{ship.passengerCapacity}</td>
|
||||||
<td>{ship.crew}</td>
|
<td>{round(ship.fuelCapacity)}{u.T}</td>
|
||||||
<td>{ship.masslock}</td>
|
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_HULL_MASS', { cap: 0 })} onMouseLeave={hide}>{ship.hullMass}{u.T}</td>
|
||||||
</tr>
|
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_MASS', { cap: 0 })} onMouseLeave={hide}>{int(ship.unladenMass)}{u.T}</td>
|
||||||
</tbody>
|
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_MASS', { cap: 0 })} onMouseLeave={hide}>{int(ship.ladenMass)}{u.T}</td>
|
||||||
</table>
|
<td>{int(ship.hardness)}</td>
|
||||||
|
<td>{ship.crew}</td>
|
||||||
|
<td>{ship.masslock}</td>
|
||||||
|
<td>{shipBoost !== 'No Boost' ? formats.time(shipBoost) : 'No Boost'}</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>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,12 +17,12 @@ export default class Slider extends React.Component {
|
|||||||
|
|
||||||
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
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -35,6 +35,11 @@ export default class Slider extends React.Component {
|
|||||||
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._keyup = this._keyup.bind(this);
|
||||||
|
this._keydown = this._keydown.bind(this);
|
||||||
|
this._touchstart = this._touchstart.bind(this);
|
||||||
|
this._touchend = this._touchend.bind(this);
|
||||||
|
|
||||||
this._updatePercent = this._updatePercent.bind(this);
|
this._updatePercent = this._updatePercent.bind(this);
|
||||||
this._updateDimensions = this._updateDimensions.bind(this);
|
this._updateDimensions = this._updateDimensions.bind(this);
|
||||||
|
|
||||||
@@ -50,6 +55,7 @@ export default class Slider extends React.Component {
|
|||||||
this.left = rect.left;
|
this.left = rect.left;
|
||||||
this.width = rect.width;
|
this.width = rect.width;
|
||||||
this._move(event);
|
this._move(event);
|
||||||
|
this.touchStartTimer = setTimeout(() => this.sliderInputBox._setDisplay('block'), 1500);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -69,11 +75,70 @@ export default class Slider extends React.Component {
|
|||||||
* @param {Event} event DOM Event
|
* @param {Event} event DOM Event
|
||||||
*/
|
*/
|
||||||
_up(event) {
|
_up(event) {
|
||||||
|
this.sliderInputBox.sliderVal.focus();
|
||||||
|
clearTimeout(this.touchStartTimer);
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
this.left = null;
|
this.left = null;
|
||||||
this.width = null;
|
this.width = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Key up handler for keyboard.
|
||||||
|
* display the number field then set focus to it
|
||||||
|
* when "Enter" key is pressed
|
||||||
|
* @param {Event} event Keyboard event
|
||||||
|
*/
|
||||||
|
_keyup(event) {
|
||||||
|
switch (event.key) {
|
||||||
|
case 'Enter':
|
||||||
|
event.preventDefault();
|
||||||
|
this.sliderInputBox._setDisplay('block');
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Key down handler
|
||||||
|
* increment slider position by +/- 1 when right/left arrow key is pressed or held
|
||||||
|
* @param {Event} event Keyboard even
|
||||||
|
*/
|
||||||
|
_keydown(event) {
|
||||||
|
let newVal = this.props.percent * this.props.max;
|
||||||
|
switch (event.key) {
|
||||||
|
case 'ArrowRight':
|
||||||
|
newVal += 1;
|
||||||
|
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);
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Touch start handler
|
||||||
|
* @param {Event} event DOM Event
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
_touchstart(event) {
|
||||||
|
this.touchStartTimer = setTimeout(() => this.sliderInputBox._setDisplay('block'), 1500);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Touch end handler
|
||||||
|
* @param {Event} event DOM Event
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
_touchend(event) {
|
||||||
|
this.sliderInputBox.sliderVal.focus();
|
||||||
|
clearTimeout(this.touchStartTimer);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if the user is still dragging
|
* Determine if the user is still dragging
|
||||||
* @param {SyntheticEvent} event Event
|
* @param {SyntheticEvent} event Event
|
||||||
@@ -136,31 +201,186 @@ export default class Slider extends React.Component {
|
|||||||
render() {
|
render() {
|
||||||
let outerWidth = this.state.outerWidth;
|
let outerWidth = this.state.outerWidth;
|
||||||
let { axis, axisUnit, min, max, scale } = this.props;
|
let { axis, axisUnit, min, max, scale } = this.props;
|
||||||
|
|
||||||
let style = {
|
let style = {
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: axis ? '2.5em' : '1.5em',
|
height: axis ? '2.5em' : '1.5em',
|
||||||
boxSizing: 'border-box'
|
boxSizing: 'border-box'
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!outerWidth) {
|
if (!outerWidth) {
|
||||||
return <svg style={style} ref={node => this.node = node} />;
|
return <svg style={style} ref={node => this.node = node} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
let margin = MARGIN_LR * scale;
|
let margin = MARGIN_LR * scale;
|
||||||
let width = outerWidth - (margin * 2);
|
let width = outerWidth - (margin * 2);
|
||||||
let pctPos = width * this.props.percent;
|
let pctPos = width * this.props.percent;
|
||||||
|
return <div><svg
|
||||||
return <svg onMouseUp={this._up} onMouseEnter={this._enter.bind(this)} onMouseMove={this._move} onTouchEnd={this._up} style={style} ref={node => this.node = node}>
|
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' 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' />
|
<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} />
|
<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} />
|
<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' }}>
|
{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={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='50%' style={{ textAnchor: 'middle' }}>{(min + max / 2) + axisUnit}</text>
|
||||||
<text className='primary-disabled' y='3em' x='100%' style={{ textAnchor: 'end' }}>{max + axisUnit}</text>
|
<text className='primary-disabled' y='3em' x='100%' style={{ textAnchor: 'end' }}>{max + axisUnit}</text>
|
||||||
</g>}
|
</g>}
|
||||||
</svg>;
|
</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>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,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 +75,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 +122,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 +133,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 +141,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 +151,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;
|
||||||
|
|
||||||
@@ -56,6 +77,9 @@ export default class StandardSlot extends TranslatedComponent {
|
|||||||
let modTT = translate('modified');
|
let modTT = translate('modified');
|
||||||
if (m && m.blueprint && m.blueprint.name) {
|
if (m && m.blueprint && m.blueprint.name) {
|
||||||
modTT = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
|
modTT = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
|
||||||
|
if (m.blueprint.special && m.blueprint.special.id >= 0) {
|
||||||
|
modTT += ', ' + translate(m.blueprint.special.name);
|
||||||
|
}
|
||||||
modTT = (
|
modTT = (
|
||||||
<div>
|
<div>
|
||||||
<div>{modTT}</div>
|
<div>{modTT}</div>
|
||||||
@@ -79,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
|
||||||
@@ -89,14 +114,15 @@ 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'}>{slot.maxClass}</div>
|
<div className={'sz'}>{m.grp == 'bh' ? m.name.charAt(0) : slot.maxClass}</div>
|
||||||
<div>
|
<div>
|
||||||
<div className={'l'}>{classRating} {translate(m.name || m.grp)}{m.mods && Object.keys(m.mods).length > 0 ? <span className='r' onMouseOver={termtip.bind(null, modTT)} onMouseOut={tooltip.bind(null, null)}><Modified /></span> : null }</div>
|
<div className={'l'}>{classRating} {translate(m.name || m.grp)}{m.mods && Object.keys(m.mods).length > 0 ? <span className='r' onMouseOver={termtip.bind(null, modTT)} onMouseOut={tooltip.bind(null, null)}><Modified /></span> : null }</div>
|
||||||
<div className={'r'}>{formats.round(mass)}{units.T}</div>
|
<div className={'r'}>{formats.round(mass)}{units.T}</div>
|
||||||
@@ -118,7 +144,7 @@ export default class StandardSlot extends TranslatedComponent {
|
|||||||
{ showModuleResistances && m.getKineticResistance() ? <div className='l'>{translate('kinres')}: {formats.pct(m.getKineticResistance())}</div> : null }
|
{ showModuleResistances && m.getKineticResistance() ? <div className='l'>{translate('kinres')}: {formats.pct(m.getKineticResistance())}</div> : null }
|
||||||
{ showModuleResistances && m.getThermalResistance() ? <div className='l'>{translate('thermres')}: {formats.pct(m.getThermalResistance())}</div> : null }
|
{ showModuleResistances && m.getThermalResistance() ? <div className='l'>{translate('thermres')}: {formats.pct(m.getThermalResistance())}</div> : null }
|
||||||
{ m.getIntegrity() ? <div className='l'>{translate('integrity')}: {formats.int(m.getIntegrity())}</div> : null }
|
{ m.getIntegrity() ? <div className='l'>{translate('integrity')}: {formats.int(m.getIntegrity())}</div> : null }
|
||||||
{ validMods.length > 0 ? <div className='r' ><button onClick={this._toggleModifications.bind(this)} onContextMenu={stopCtxPropagation} onMouseOver={termtip.bind(null, 'modifications')} onMouseOut={tooltip.bind(null, null)}><ListModifications /></button></div> : null }
|
{ validMods.length > 0 ? <div className='r' tabIndex="0" ref={ modButton => this.modButton = modButton }><button tabIndex="-1" onClick={this._toggleModifications.bind(this)} onContextMenu={stopCtxPropagation} onMouseOver={termtip.bind(null, 'modifications')} onMouseOut={tooltip.bind(null, null)}><ListModifications /></button></div> : null }
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -20,12 +20,23 @@ export default class StandardSlotSection extends SlotSection {
|
|||||||
super(props, context, 'standard', 'core internal');
|
super(props, context, 'standard', 'core internal');
|
||||||
this._optimizeStandard = this._optimizeStandard.bind(this);
|
this._optimizeStandard = this._optimizeStandard.bind(this);
|
||||||
this._selectBulkhead = this._selectBulkhead.bind(this);
|
this._selectBulkhead = this._selectBulkhead.bind(this);
|
||||||
|
this.selectedRefId = null;
|
||||||
|
this.firstRefId = 'maxjump';
|
||||||
|
this.lastRefId = 'racer';
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Handle focus if the component updates
|
||||||
|
* @param {Object} prevProps React Component properties
|
||||||
|
*/
|
||||||
|
componentDidUpdate(prevProps) {
|
||||||
|
this._handleSectionFocus(prevProps,this.firstRefId, this.lastRefId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use the lightest/optimal available standard modules
|
* Use the lightest/optimal available standard modules
|
||||||
*/
|
*/
|
||||||
_optimizeStandard() {
|
_optimizeStandard() {
|
||||||
|
this.selectedRefId = 'maxjump';
|
||||||
this.props.ship.useLightestStandard();
|
this.props.ship.useLightestStandard();
|
||||||
this.props.onChange();
|
this.props.onChange();
|
||||||
this.props.onCargoChange(this.props.ship.cargoCapacity);
|
this.props.onCargoChange(this.props.ship.cargoCapacity);
|
||||||
@@ -39,6 +50,8 @@ export default class StandardSlotSection extends SlotSection {
|
|||||||
* @param {integer} bulkheadIndex Bulkhead to use see Constants.BulkheadNames
|
* @param {integer} bulkheadIndex Bulkhead to use see Constants.BulkheadNames
|
||||||
*/
|
*/
|
||||||
_multiPurpose(shielded, bulkheadIndex) {
|
_multiPurpose(shielded, bulkheadIndex) {
|
||||||
|
this.selectedRefId = 'multipurpose';
|
||||||
|
if (bulkheadIndex === 2) this.selectedRefId = 'combat';
|
||||||
ShipRoles.multiPurpose(this.props.ship, shielded, bulkheadIndex);
|
ShipRoles.multiPurpose(this.props.ship, shielded, bulkheadIndex);
|
||||||
this.props.onChange();
|
this.props.onChange();
|
||||||
this.props.onCargoChange(this.props.ship.cargoCapacity);
|
this.props.onCargoChange(this.props.ship.cargoCapacity);
|
||||||
@@ -51,6 +64,7 @@ export default class StandardSlotSection extends SlotSection {
|
|||||||
* @param {Boolean} shielded True if shield generator should be included
|
* @param {Boolean} shielded True if shield generator should be included
|
||||||
*/
|
*/
|
||||||
_optimizeCargo(shielded) {
|
_optimizeCargo(shielded) {
|
||||||
|
this.selectedRefId = 'trader';
|
||||||
ShipRoles.trader(this.props.ship, shielded);
|
ShipRoles.trader(this.props.ship, shielded);
|
||||||
this.props.onChange();
|
this.props.onChange();
|
||||||
this.props.onCargoChange(this.props.ship.cargoCapacity);
|
this.props.onCargoChange(this.props.ship.cargoCapacity);
|
||||||
@@ -63,6 +77,7 @@ export default class StandardSlotSection extends SlotSection {
|
|||||||
* @param {Boolean} shielded True if shield generator should be included
|
* @param {Boolean} shielded True if shield generator should be included
|
||||||
*/
|
*/
|
||||||
_optimizeMiner(shielded) {
|
_optimizeMiner(shielded) {
|
||||||
|
this.selectedRefId = 'miner';
|
||||||
ShipRoles.miner(this.props.ship, shielded);
|
ShipRoles.miner(this.props.ship, shielded);
|
||||||
this.props.onChange();
|
this.props.onChange();
|
||||||
this.props.onCargoChange(this.props.ship.cargoCapacity);
|
this.props.onCargoChange(this.props.ship.cargoCapacity);
|
||||||
@@ -75,6 +90,8 @@ export default class StandardSlotSection extends SlotSection {
|
|||||||
* @param {Boolean} planetary True if Planetary Vehicle Hangar (PVH) should be included
|
* @param {Boolean} planetary True if Planetary Vehicle Hangar (PVH) should be included
|
||||||
*/
|
*/
|
||||||
_optimizeExplorer(planetary) {
|
_optimizeExplorer(planetary) {
|
||||||
|
this.selectedRefId = 'explorer';
|
||||||
|
if (planetary) this.selectedRefId = 'planetary';
|
||||||
ShipRoles.explorer(this.props.ship, planetary);
|
ShipRoles.explorer(this.props.ship, planetary);
|
||||||
this.props.onChange();
|
this.props.onChange();
|
||||||
this.props.onCargoChange(this.props.ship.cargoCapacity);
|
this.props.onCargoChange(this.props.ship.cargoCapacity);
|
||||||
@@ -86,6 +103,7 @@ export default class StandardSlotSection extends SlotSection {
|
|||||||
* Racer role
|
* Racer role
|
||||||
*/
|
*/
|
||||||
_optimizeRacer() {
|
_optimizeRacer() {
|
||||||
|
this.selectedRefId = 'racer';
|
||||||
ShipRoles.racer(this.props.ship);
|
ShipRoles.racer(this.props.ship);
|
||||||
this.props.onChange();
|
this.props.onChange();
|
||||||
this.props.onCargoChange(this.props.ship.cargoCapacity);
|
this.props.onCargoChange(this.props.ship.cargoCapacity);
|
||||||
@@ -229,19 +247,17 @@ export default class StandardSlotSection extends SlotSection {
|
|||||||
let planetaryDisabled = this.props.ship.internal.length < 4;
|
let planetaryDisabled = this.props.ship.internal.length < 4;
|
||||||
return <div className='select' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
|
return <div className='select' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
|
||||||
<ul>
|
<ul>
|
||||||
<li className='lc' onClick={this._optimizeStandard}>{translate('Maximize Jump Range')}</li>
|
<li className='lc' tabIndex="0" onClick={this._optimizeStandard} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['maxjump'] = smRef}>{translate('Maximize Jump Range')}</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div className='select-group cap'>{translate('roles')}</div>
|
<div className='select-group cap'>{translate('roles')}</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li className='lc' onClick={this._multiPurpose.bind(this, false, 0)}>{translate('Multi-purpose')}</li>
|
<li className='lc' tabIndex="0" onClick={this._multiPurpose.bind(this, false, 0)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['multipurpose'] = smRef}>{translate('Multi-purpose')}</li>
|
||||||
<li className='lc' onClick={this._multiPurpose.bind(this, true, 2)}>{translate('Combat')}</li>
|
<li className='lc' tabIndex="0" onClick={this._multiPurpose.bind(this, true, 2)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['combat'] = smRef}>{translate('Combat')}</li>
|
||||||
<li className='lc' onClick={this._optimizeCargo.bind(this, false)}>{translate('Trader')}</li>
|
<li className='lc' tabIndex="0" onClick={this._optimizeCargo.bind(this, true)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['trader'] = smRef}>{translate('Trader')}</li>
|
||||||
<li className='lc' onClick={this._optimizeCargo.bind(this, true)}>{translate('Shielded Trader')}</li>
|
<li className='lc' tabIndex="0" onClick={this._optimizeExplorer.bind(this, false)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['explorer'] = smRef}>{translate('Explorer')}</li>
|
||||||
<li className='lc' onClick={this._optimizeExplorer.bind(this, false)}>{translate('Explorer')}</li>
|
<li className={cn('lc', { disabled: planetaryDisabled })} tabIndex={planetaryDisabled ? '' : '0'} onClick={!planetaryDisabled && this._optimizeExplorer.bind(this, true)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['planetary'] = smRef}>{translate('Planetary Explorer')}</li>
|
||||||
<li className={cn('lc', { disabled: planetaryDisabled })} onClick={!planetaryDisabled && this._optimizeExplorer.bind(this, true)}>{translate('Planetary Explorer')}</li>
|
<li className='lc' tabIndex="0" onClick={this._optimizeMiner.bind(this, true)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['miner'] = smRef}>{translate('Miner')}</li>
|
||||||
<li className='lc' onClick={this._optimizeMiner.bind(this, false)}>{translate('Miner')}</li>
|
<li className='lc' tabIndex="0" onClick={this._optimizeRacer.bind(this)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['racer'] = smRef}>{translate('Racer')}</li>
|
||||||
<li className='lc' onClick={this._optimizeMiner.bind(this, true)}>{translate('Shielded Miner')}</li>
|
|
||||||
<li className='lc' onClick={this._optimizeRacer.bind(this)}>{translate('Racer')}</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -228,6 +228,96 @@ export class LinkIcon extends SvgIcon {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Link / Permalink / Chain
|
||||||
|
*/
|
||||||
|
export class OrbisIcon extends SvgIcon {
|
||||||
|
/**
|
||||||
|
* Generate the SVG
|
||||||
|
* @return {React.Component} SVG Contents
|
||||||
|
*/
|
||||||
|
svg() {
|
||||||
|
return (
|
||||||
|
<g transform="scale(.037296)">
|
||||||
|
<path d="m429 319c60.75 0 110 49.248 110 110 0 60.75-49.247 110-110 110s-110-49.247-110-110c0-60.749 49.248-110 110-110m0-20c-34.724 0-67.369 13.522-91.922 38.075s-38.075 57.198-38.075 91.922 13.522 67.369 38.075 91.922c24.553 24.554 57.198 38.075 91.922 38.075s67.369-13.521 91.922-38.075c24.554-24.553 38.075-57.198 38.075-91.922s-13.521-67.369-38.075-91.922c-24.553-24.553-57.198-38.075-91.922-38.075z" />
|
||||||
|
<path d="m429 235c107.14 0 194 86.855 194 194s-86.855 194-194 194-194-86.855-194-194 86.855-194 194-194m0-20c-28.881 0-56.908 5.661-83.304 16.825-25.485 10.779-48.368 26.207-68.016 45.853-19.646 19.647-35.074 42.53-45.853 68.015-11.163 26.396-16.824 54.423-16.824 83.304s5.661 56.908 16.825 83.304c10.779 25.484 26.207 48.368 45.853 68.016 19.647 19.646 42.53 35.073 68.015 45.854 26.396 11.164 54.423 16.825 83.304 16.825s56.908-5.661 83.304-16.825c25.484-10.779 48.368-26.206 68.016-45.854 19.646-19.646 35.073-42.53 45.854-68.016 11.164-26.396 16.825-54.423 16.825-83.304s-5.661-56.908-16.825-83.304c-10.779-25.485-26.206-48.368-45.854-68.015-19.646-19.646-42.53-35.074-68.016-45.853-26.396-11.164-54.423-16.825-83.304-16.825z" />
|
||||||
|
<path d="m429 63c202.14 0 366 163.86 366 366s-163.86 366-366 366-366-163.86-366-366 163.86-366 366-366m0-20c-52.101 0-102.65 10.208-150.25 30.342-45.966 19.442-87.244 47.271-122.69 82.714s-63.272 76.721-82.714 122.69c-20.134 47.601-30.342 98.153-30.342 150.25s10.208 102.65 30.342 150.25c19.442 45.967 47.271 87.244 82.714 122.69 35.443 35.442 76.721 63.271 122.69 82.715 47.601 20.133 98.153 30.342 150.25 30.342s102.65-10.209 150.25-30.342c45.967-19.442 87.244-47.271 122.69-82.715 35.441-35.442 63.271-76.721 82.714-122.69 20.133-47.601 30.342-98.153 30.342-150.25s-10.209-102.65-30.342-150.25c-19.442-45.966-47.271-87.244-82.714-122.69s-76.722-63.272-122.69-82.714c-47.601-20.134-98.153-30.342-150.25-30.342z"/>
|
||||||
|
<path d="m429 63c202.14 0 366 163.86 366 366s-163.86 366-366 366-366-163.86-366-366 163.86-366 366-366m0-20c-52.101 0-102.65 10.208-150.25 30.342-45.966 19.442-87.244 47.271-122.69 82.714s-63.272 76.721-82.714 122.69c-20.134 47.601-30.342 98.153-30.342 150.25s10.208 102.65 30.342 150.25c19.442 45.967 47.271 87.244 82.714 122.69 35.443 35.442 76.721 63.271 122.69 82.715 47.601 20.133 98.153 30.342 150.25 30.342s102.65-10.209 150.25-30.342c45.967-19.442 87.244-47.271 122.69-82.715 35.441-35.442 63.271-76.721 82.714-122.69 20.133-47.601 30.342-98.153 30.342-150.25s-10.209-102.65-30.342-150.25c-19.442-45.966-47.271-87.244-82.714-122.69s-76.722-63.272-122.69-82.714c-47.601-20.134-98.153-30.342-150.25-30.342z" />
|
||||||
|
<path d="m429 20c225.88 0 409 183.11 409 409s-183.11 409-409 409-409-183.11-409-409 183.11-409 409-409m0-20c-57.905 0-114.09 11.345-166.99 33.721-51.087 21.608-96.963 52.538-136.36 91.93s-70.321 85.269-91.93 136.36c-22.376 52.902-33.721 109.09-33.721 166.99s11.345 114.09 33.721 166.99c21.608 51.087 52.538 96.964 91.93 136.35 39.392 39.392 85.269 70.321 136.36 91.931 52.902 22.375 109.09 33.721 166.99 33.721s114.09-11.346 166.99-33.721c51.087-21.608 96.964-52.538 136.35-91.931 39.392-39.392 70.321-85.269 91.931-136.35 22.375-52.902 33.721-109.09 33.721-166.99s-11.346-114.09-33.721-166.99c-21.608-51.087-52.538-96.963-91.931-136.36-39.392-39.392-85.269-70.321-136.35-91.93-52.902-22.376-109.09-33.721-166.99-33.721z"/>
|
||||||
|
<path d="m155.34 679.12 173.25-190.21-15.626-13.721-170.9 190.4zm31.01 31.714 202.41-169.1-16.418-14.417-198.76 170.43z"/>
|
||||||
|
<path d="m702.66 178.87-173.25 190.21 15.625 13.721 170.9-190.4zm-31.01-31.714-202.41 169.1 16.418 14.417 198.76-170.43z" />
|
||||||
|
<rect transform="matrix(-.7071 -.7071 .7071 -.7071 429.34 1036.2)" x="387.09" y="420.77" width="84.379" height="16.859" />
|
||||||
|
</g>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Material
|
||||||
|
*/
|
||||||
|
export class MatIcon extends SvgIcon {
|
||||||
|
/**
|
||||||
|
* Generate the SVG
|
||||||
|
* @return {React.Component} SVG Contents
|
||||||
|
*/
|
||||||
|
svg() {
|
||||||
|
return<g xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#FF7100" d="M 24.86,4.18
|
||||||
|
C 24.86,4.18 17.17,7.82 17.17,7.82
|
||||||
|
17.17,7.82 15.35,14.55 15.35,14.55
|
||||||
|
15.35,14.55 24.70,9.75 24.70,9.75
|
||||||
|
24.70,9.75 24.86,4.18 24.86,4.18 Z
|
||||||
|
M 32.21,17.45
|
||||||
|
C 32.21,17.45 26.41,11.18 26.41,11.18
|
||||||
|
26.41,11.18 19.51,11.51 19.51,11.51
|
||||||
|
19.51,11.51 26.92,19.01 26.92,19.01
|
||||||
|
26.92,19.01 32.21,17.45 32.21,17.45 Z
|
||||||
|
M 21.99,28.62
|
||||||
|
C 21.99,28.62 26.10,21.10 26.10,21.10
|
||||||
|
26.10,21.10 23.66,14.57 23.66,14.57
|
||||||
|
23.66,14.57 18.89,24.01 18.89,24.01
|
||||||
|
18.89,24.01 21.99,28.62 21.99,28.62 Z
|
||||||
|
M 8.33,22.24
|
||||||
|
C 8.33,22.24 16.67,23.87 16.67,23.87
|
||||||
|
16.67,23.87 22.06,19.51 22.06,19.51
|
||||||
|
22.06,19.51 11.70,17.84 11.70,17.84
|
||||||
|
11.70,17.84 8.33,22.24 8.33,22.24 Z
|
||||||
|
M 10.11,7.14
|
||||||
|
C 10.11,7.14 11.15,15.66 11.15,15.66
|
||||||
|
11.15,15.66 16.92,19.49 16.92,19.49
|
||||||
|
16.92,19.49 15.29,9.02 15.29,9.02
|
||||||
|
15.29,9.02 10.11,7.14 10.11,7.14 Z
|
||||||
|
M 27.69,2.67
|
||||||
|
C 27.69,2.67 35.89,16.00 35.89,16.00
|
||||||
|
35.89,16.00 27.69,29.33 27.69,29.33
|
||||||
|
27.69,29.33 11.31,29.33 11.31,29.33
|
||||||
|
11.31,29.33 3.11,16.00 3.11,16.00
|
||||||
|
3.11,16.00 11.31,2.67 11.31,2.67
|
||||||
|
11.31,2.67 27.67,2.67 27.67,2.67M 29.16,0.00
|
||||||
|
C 29.16,0.00 27.69,0.00 27.69,0.00
|
||||||
|
27.69,0.00 11.31,0.00 11.31,0.00
|
||||||
|
11.31,0.00 9.84,0.00 9.84,0.00
|
||||||
|
9.84,0.00 9.06,1.25 9.06,1.25
|
||||||
|
9.06,1.25 0.87,14.57 0.87,14.57
|
||||||
|
0.87,14.57 0.00,15.98 0.00,15.98
|
||||||
|
0.00,15.98 0.87,17.39 0.87,17.39
|
||||||
|
0.87,17.39 9.06,30.73 9.06,30.73
|
||||||
|
9.06,30.73 9.84,32.00 9.84,32.00
|
||||||
|
9.84,32.00 11.31,32.00 11.31,32.00
|
||||||
|
11.31,32.00 27.69,32.00 27.69,32.00
|
||||||
|
27.69,32.00 29.16,32.00 29.16,32.00
|
||||||
|
29.16,32.00 29.94,30.73 29.94,30.73
|
||||||
|
29.94,30.73 38.13,17.39 38.13,17.39
|
||||||
|
38.13,17.39 39.00,15.98 39.00,15.98
|
||||||
|
39.00,15.98 38.13,14.57 38.13,14.57
|
||||||
|
38.13,14.57 29.94,1.25 29.94,1.25
|
||||||
|
29.94,1.25 29.16,0.00 29.16,0.00
|
||||||
|
29.16,0.00 29.16,0.00 29.16,0.00 Z" />
|
||||||
|
</g>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shopping icon (dollar sign)
|
* Shopping icon (dollar sign)
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -17,12 +17,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 +47,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,28 +108,28 @@ 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,6 +1,6 @@
|
|||||||
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 } from 'recharts';
|
||||||
|
|
||||||
const CORIOLIS_COLOURS = ['#FF8C0D', '#1FB0FF', '#71A052', '#D5D54D'];
|
const CORIOLIS_COLOURS = ['#FF8C0D', '#1FB0FF', '#71A052', '#D5D54D'];
|
||||||
@@ -32,13 +32,6 @@ export default class VerticalBarChart extends TranslatedComponent {
|
|||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this._termtip = this._termtip.bind(this);
|
this._termtip = this._termtip.bind(this);
|
||||||
|
|
||||||
this.state = {
|
|
||||||
dimensions: {
|
|
||||||
width: 300,
|
|
||||||
height: 300
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -46,7 +39,6 @@ export default class VerticalBarChart extends TranslatedComponent {
|
|||||||
* @returns {Object} the markup
|
* @returns {Object} the markup
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
const { width, height } = this.state.dimensions;
|
|
||||||
const { tooltip, termtip } = this.context;
|
const { tooltip, termtip } = this.context;
|
||||||
|
|
||||||
// Calculate maximum for Y
|
// Calculate maximum for Y
|
||||||
@@ -56,15 +48,17 @@ export default class VerticalBarChart extends TranslatedComponent {
|
|||||||
const localMax = Math.max(dataMax, yMax);
|
const localMax = Math.max(dataMax, yMax);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Measure whitelist={['width', 'top']} onMeasure={ (dimensions) => this.setState({ dimensions }) }>
|
<ContainerDimensions>
|
||||||
<div width='100%'>
|
{ ({ width }) => (
|
||||||
<BarChart width={width} height={width * ASPECT} data={this.props.data} margin={{ top: 5, right: 5, left: 5, bottom: 5 }}>
|
<div width='100%'>
|
||||||
<XAxis interval={0} fontSize='0.8em' stroke={AXIS_COLOUR} dataKey='label' />
|
<BarChart width={width} height={width * ASPECT} data={this.props.data} margin={{ top: 5, right: 5, left: 5, bottom: 5 }}>
|
||||||
<YAxis interval={'preserveStart'} tickCount={11} fontSize='0.8em' stroke={AXIS_COLOUR} type='number' domain={[0, localMax]}/>
|
<XAxis interval={0} fontSize='0.8em' stroke={AXIS_COLOUR} dataKey='label' />
|
||||||
<Bar dataKey='value' label={<ValueLabel />} fill={CORIOLIS_COLOURS[0]} isAnimationActive={false} onMouseOver={this._termtip} onMouseOut={tooltip.bind(null, null)}/>
|
<YAxis interval={'preserveStart'} tickCount={11} fontSize='0.8em' stroke={AXIS_COLOUR} type='number' domain={[0, localMax]}/>
|
||||||
</BarChart>
|
<Bar dataKey='value' label={<ValueLabel />} fill={CORIOLIS_COLOURS[0]} isAnimationActive={false} onMouseOver={this._termtip} onMouseOut={tooltip.bind(null, null)}/>
|
||||||
</div>
|
</BarChart>
|
||||||
</Measure>
|
</div>
|
||||||
|
)}
|
||||||
|
</ContainerDimensions>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -184,7 +184,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 +198,7 @@ export default class WeaponDamageChart extends TranslatedComponent {
|
|||||||
points={200}
|
points={200}
|
||||||
code={code}
|
code={code}
|
||||||
/>
|
/>
|
||||||
</span>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import * as FR from './fr';
|
|||||||
import * as IT from './it';
|
import * as IT from './it';
|
||||||
import * as RU from './ru';
|
import * as RU from './ru';
|
||||||
import * as PL from './pl';
|
import * as PL from './pl';
|
||||||
|
import * as PT from './pt';
|
||||||
import * as d3 from 'd3';
|
import * as d3 from 'd3';
|
||||||
|
|
||||||
let fallbackTerms = EN.terms;
|
let fallbackTerms = EN.terms;
|
||||||
@@ -25,6 +26,7 @@ export function getLanguage(langCode) {
|
|||||||
case 'it': lang = IT; break;
|
case 'it': lang = IT; break;
|
||||||
case 'ru': lang = RU; break;
|
case 'ru': lang = RU; break;
|
||||||
case 'pl': lang = PL; break;
|
case 'pl': lang = PL; break;
|
||||||
|
case 'pt': lang = PT; break;
|
||||||
default:
|
default:
|
||||||
lang = EN;
|
lang = EN;
|
||||||
}
|
}
|
||||||
@@ -58,17 +60,20 @@ export function getLanguage(langCode) {
|
|||||||
},
|
},
|
||||||
translate,
|
translate,
|
||||||
units: {
|
units: {
|
||||||
|
ang: '°', // Angle
|
||||||
CR: <u>{translate('CR')}</u>, // Credits
|
CR: <u>{translate('CR')}</u>, // Credits
|
||||||
kg: <u>{translate('kg')}</u>, // Kilograms
|
kg: <u>{translate('kg')}</u>, // Kilograms
|
||||||
kgs: <u>{translate('kg/s')}</u>, // Kilograms per second
|
kgs: <u>{translate('kg/s')}</u>, // Kilograms per second
|
||||||
km: <u>{translate('km')}</u>, // Kilometers
|
km: <u>{translate('km')}</u>, // Kilometers
|
||||||
Ls: <u>{translate('Ls')}</u>, // Light Seconds
|
Ls: <u>{translate('Ls')}</u>, // Light Seconds
|
||||||
LY: <u>{translate('LY')}</u>, // Light Years
|
LY: <u>{translate('LY')}</u>, // Light Years
|
||||||
|
m: <u>{translate('m')}</u>, // Meters
|
||||||
MJ: <u>{translate('MJ')}</u>, // Mega Joules
|
MJ: <u>{translate('MJ')}</u>, // Mega Joules
|
||||||
'm/s': <u>{translate('m/s')}</u>, // Meters per second
|
'm/s': <u>{translate('m/s')}</u>, // Meters per second
|
||||||
'°/s': <u>{translate('°/s')}</u>, // Degrees per second
|
'°/s': <u>{translate('°/s')}</u>, // Degrees per second
|
||||||
MW: <u>{translate('MW')}</u>, // Mega Watts (same as Mega Joules per second)
|
MW: <u>{translate('MW')}</u>, // Mega Watts (same as Mega Joules per second)
|
||||||
mps: <u>{translate('m/s')}</u>, // Metres per second
|
mps: <u>{translate('m/s')}</u>, // Metres per second
|
||||||
|
pct: '%', // Percent
|
||||||
ps: <u>{translate('/s')}</u>, // per second
|
ps: <u>{translate('/s')}</u>, // per second
|
||||||
pm: <u>{translate('/min')}</u>, // per minute
|
pm: <u>{translate('/min')}</u>, // per minute
|
||||||
s: <u>{translate('secs')}</u>, // Seconds
|
s: <u>{translate('secs')}</u>, // Seconds
|
||||||
@@ -88,5 +93,6 @@ export const Languages = {
|
|||||||
es: 'Español',
|
es: 'Español',
|
||||||
fr: 'Français',
|
fr: 'Français',
|
||||||
ru: 'ру́сский',
|
ru: 'ру́сский',
|
||||||
pl: 'polski'
|
pl: 'polski',
|
||||||
|
pt: 'português'
|
||||||
};
|
};
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
16
src/app/i18n/pt.js
Normal file
16
src/app/i18n/pt.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
export const formats = {
|
||||||
|
decimal: ',',
|
||||||
|
thousands: '.',
|
||||||
|
grouping: [3],
|
||||||
|
currency: ['', ' €'],
|
||||||
|
dateTime: '%A, %e de %B de %Y, %X',
|
||||||
|
date: '%d/%m/%Y',
|
||||||
|
time: '%H:%M:%S',
|
||||||
|
periods: ['AM', 'PM'],
|
||||||
|
days: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes', 'sábado'],
|
||||||
|
shortDays: ['dom', 'lun', 'mar', 'mié', 'jue', 'vie', 'sáb'],
|
||||||
|
months: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio', 'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre'],
|
||||||
|
shortMonths: ['ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul', 'ago', 'sep', 'oct', 'nov', 'dic']
|
||||||
|
};
|
||||||
|
|
||||||
|
export { default as terms } from './pt.json';
|
||||||
278
src/app/i18n/pt.json
Normal file
278
src/app/i18n/pt.json
Normal file
File diff suppressed because one or more lines are too long
@@ -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": "Оптимизированная масса",
|
||||||
|
|||||||
@@ -41,6 +41,15 @@ export default class AboutPage extends Page {
|
|||||||
|
|
||||||
<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>
|
||||||
|
<p>Coriolis is an open source project, and I work on it in my free time. I have set up a patreon at <a href='https://www.patreon.com/coriolis_elite'>patreon.com/coriolis_elite</a>, which will be used to keep Coriolis up to date and the servers running.</p>
|
||||||
|
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
|
||||||
|
<input type="hidden" name="cmd" value="_s-xclick"/>
|
||||||
|
<input type="hidden" name="hosted_button_id" value="SJBKT2SWEEU68" />
|
||||||
|
<input type="image" src="https://www.paypalobjects.com/en_AU/i/btn/btn_donate_SM.gif" border="0" name="submit" alt="PayPal – The safer, easier way to pay online!" />
|
||||||
|
<img alt="" border="0" src="https://www.paypalobjects.com/en_AU/i/scr/pixel.gif" width="1" height="1" />
|
||||||
|
</form>
|
||||||
</div>;
|
</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
|
||||||
@@ -58,6 +71,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 = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -73,13 +87,10 @@ export default class OutfittingPage extends Page {
|
|||||||
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 {
|
||||||
@@ -494,6 +505,23 @@ export default class OutfittingPage extends Page {
|
|||||||
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}/>);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open up a window for EDDB with a shopping list of our components
|
* Open up a window for EDDB with a shopping list of our components
|
||||||
*/
|
*/
|
||||||
@@ -508,6 +536,13 @@ export default class OutfittingPage extends Page {
|
|||||||
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}/>);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle Key Down
|
* Handle Key Down
|
||||||
* @param {Event} e Keyboard Event
|
* @param {Event} e Keyboard Event
|
||||||
@@ -556,10 +591,34 @@ export default class OutfittingPage extends Page {
|
|||||||
const boostMarker = `${ship.canBoost(cargo, fuel)}`;
|
const boostMarker = `${ship.canBoost(cargo, fuel)}`;
|
||||||
const shipSummaryMarker = `${ship.name}${_sStr}${_iStr}${_hStr}${_pStr}${_mStr}${ship.ladenMass}${cargo}${fuel}`;
|
const shipSummaryMarker = `${ship.name}${_sStr}${_iStr}${_hStr}${_pStr}${_mStr}${ship.ladenMass}${cargo}${fuel}`;
|
||||||
|
|
||||||
|
const requirements = Ships[ship.id].requirements;
|
||||||
|
let requirementElements = [];
|
||||||
|
/**
|
||||||
|
* Render the requirements for a ship / etc
|
||||||
|
* @param {string} className Class names
|
||||||
|
* @param {string} textKey The key for translating
|
||||||
|
* @param {String} tooltipTextKey Tooltip key
|
||||||
|
*/
|
||||||
|
function renderRequirement(className, textKey, tooltipTextKey) {
|
||||||
|
if (textKey.startsWith('empire') || textKey.startsWith('federation')) {
|
||||||
|
requirementElements.push(<div key={textKey} className={className} onMouseEnter={termtip.bind(null, tooltipTextKey)} onMouseLeave={hide}><a href={textKey.startsWith('empire') ? 'http://elite-dangerous.wikia.com/wiki/Empire/Ranks' : 'http://elite-dangerous.wikia.com/wiki/Federation/Ranks'} target="_blank" rel="noopener">{translate(textKey)}</a></div>);
|
||||||
|
} else {
|
||||||
|
requirementElements.push(<div key={textKey} className={className} onMouseEnter={termtip.bind(null, tooltipTextKey)} onMouseLeave={hide}>{translate(textKey)}</div>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requirements) {
|
||||||
|
requirements.federationRank && renderRequirement('federation', 'federation rank ' + requirements.federationRank, 'federation rank required');
|
||||||
|
requirements.empireRank && renderRequirement('empire', 'empire rank ' + requirements.empireRank, 'empire rank required');
|
||||||
|
requirements.horizons && renderRequirement('horizons', 'horizons', 'horizons required');
|
||||||
|
requirements.horizonsEarlyAdoption && renderRequirement('horizons', 'horizons early adoption', 'horizons early adoption required');
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id='outfit' className={'page'} style={{ fontSize: (sizeRatio * 0.9) + 'em' }}>
|
<div id='outfit' className={'page'} style={{ fontSize: (sizeRatio * 0.9) + 'em' }}>
|
||||||
<div id='overview'>
|
<div id='overview'>
|
||||||
<h1>{ship.name}</h1>
|
<h1>{ship.name}</h1>
|
||||||
|
<div id='requirements'>{requirementElements}</div>
|
||||||
<div id='build'>
|
<div id='build'>
|
||||||
<input value={newBuildName || ''} onChange={this._buildNameChange} placeholder={translate('Enter Name')} maxLength={50} />
|
<input value={newBuildName || ''} onChange={this._buildNameChange} placeholder={translate('Enter Name')} maxLength={50} />
|
||||||
<button onClick={canSave && this._saveBuild} disabled={!canSave} onMouseOver={termtip.bind(null, 'save')} onMouseOut={hide}>
|
<button onClick={canSave && this._saveBuild} disabled={!canSave} onMouseOver={termtip.bind(null, 'save')} onMouseOut={hide}>
|
||||||
@@ -586,15 +645,21 @@ export default class OutfittingPage extends Page {
|
|||||||
<button onClick={this._genShortlink} onMouseOver={termtip.bind(null, 'shortlink')} onMouseOut={hide}>
|
<button onClick={this._genShortlink} onMouseOver={termtip.bind(null, 'shortlink')} onMouseOut={hide}>
|
||||||
<LinkIcon className='lg' />
|
<LinkIcon className='lg' />
|
||||||
</button>
|
</button>
|
||||||
|
<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>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Main tables */}
|
{/* Main tables */}
|
||||||
<ShipSummaryTable ship={ship} fuel={fuel} cargo={cargo} marker={shipSummaryMarker} />
|
<ShipSummaryTable ship={ship} fuel={fuel} cargo={cargo} 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} />
|
<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} />
|
<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} />
|
<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} />
|
<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'>
|
||||||
|
|||||||
@@ -25,6 +25,25 @@ function countInt(slot) {
|
|||||||
this.int[slot.maxClass - 1]++; // Subtract 1 since there is no Class 0 Internal compartment
|
this.int[slot.maxClass - 1]++; // Subtract 1 since there is no Class 0 Internal compartment
|
||||||
this.intCount++;
|
this.intCount++;
|
||||||
this.maxCargo += crEligible ? ModuleUtils.findInternal('cr', slot.maxClass, 'E').cargo : 0;
|
this.maxCargo += crEligible ? ModuleUtils.findInternal('cr', slot.maxClass, 'E').cargo : 0;
|
||||||
|
|
||||||
|
// if no eligiblity, then assume pce
|
||||||
|
let passSlotType = null;
|
||||||
|
let passSlotRating = null;
|
||||||
|
if (!slot.eligible || slot.eligible.pce) {
|
||||||
|
passSlotType = 'pce';
|
||||||
|
passSlotRating = 'E';
|
||||||
|
} else if (slot.eligible.pci) {
|
||||||
|
passSlotType = 'pci';
|
||||||
|
passSlotRating = 'D';
|
||||||
|
} else if (slot.eligible.pcm) {
|
||||||
|
passSlotType = 'pcm';
|
||||||
|
passSlotRating = 'C';
|
||||||
|
} else if (slot.eligible.pcq) {
|
||||||
|
passSlotType = 'pcq';
|
||||||
|
passSlotRating = 'B';
|
||||||
|
}
|
||||||
|
let passengerBay = passSlotType ? ModuleUtils.findMaxInternal(passSlotType, slot.maxClass, passSlotRating) : null;
|
||||||
|
this.maxPassengers += passengerBay ? passengerBay.passengers : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -39,6 +58,7 @@ function shipSummary(shipId, shipData) {
|
|||||||
hpCount: 0,
|
hpCount: 0,
|
||||||
intCount: 0,
|
intCount: 0,
|
||||||
maxCargo: 0,
|
maxCargo: 0,
|
||||||
|
maxPassengers: 0,
|
||||||
hp: [0, 0, 0, 0, 0], // Utility, Small, Medium, Large, Huge
|
hp: [0, 0, 0, 0, 0], // Utility, Small, Medium, Large, Huge
|
||||||
int: [0, 0, 0, 0, 0, 0, 0, 0], // Sizes 1 - 8
|
int: [0, 0, 0, 0, 0, 0, 0, 0], // Sizes 1 - 8
|
||||||
standard: shipData.slots.standard,
|
standard: shipData.slots.standard,
|
||||||
@@ -139,15 +159,16 @@ 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) {
|
_shipRowElement(s, translate, u, fInt, fRound, highlight) {
|
||||||
let noTouch = this.context.noTouch;
|
let noTouch = this.context.noTouch;
|
||||||
|
|
||||||
return <tr
|
return <tr
|
||||||
key={s.id}
|
key={s.id}
|
||||||
style={{ height: '1.5em' }}
|
style={{ height: '1.5em' }}
|
||||||
className={cn({ highlighted: noTouch && this.state.shipId === s.id })}
|
className={cn({ highlighted: noTouch && this.state.shipId === s.id, alt: highlight })}
|
||||||
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'>{s.manufacturer}</td>
|
||||||
@@ -166,12 +187,14 @@ export default class ShipyardPage extends Page {
|
|||||||
<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='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({ 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>
|
||||||
@@ -246,13 +269,26 @@ 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) {
|
||||||
detailRows[i] = this._shipRowElement(s, translate, units, fInt, formats.f1);
|
let shipSortValue = s[shipPredicate];
|
||||||
|
if(shipPredicateIndex != undefined) {
|
||||||
|
shipSortValue = shipSortValue[shipPredicateIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(shipSortValue != lastShipSortValue) {
|
||||||
|
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 })}
|
className={cn({ highlighted: noTouch && this.state.shipId === s.id, alt: backgroundHighlight })}
|
||||||
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}>{s.name}</Link></td>
|
||||||
@@ -293,8 +329,8 @@ export default class ShipyardPage extends Page {
|
|||||||
<th rowSpan={3} className='sortable' onMouseEnter={termtip.bind(null, 'hardness')} onMouseLeave={hide} onClick={sortShips('hardness')}>{translate('hrd')}</th>
|
<th rowSpan={3} className='sortable' onMouseEnter={termtip.bind(null, 'hardness')} onMouseLeave={hide} onClick={sortShips('hardness')}>{translate('hrd')}</th>
|
||||||
<th> </th>
|
<th> </th>
|
||||||
<th colSpan={4}>{translate('base')}</th>
|
<th colSpan={4}>{translate('base')}</th>
|
||||||
<th colSpan={4}>{translate('max')}</th>
|
<th colSpan={5}>{translate('max')}</th>
|
||||||
<th className='lft' colSpan={6}></th>
|
<th className='lft' colSpan={7}></th>
|
||||||
<th className='lft' colSpan={5}></th>
|
<th className='lft' colSpan={5}></th>
|
||||||
<th className='lft' colSpan={8}></th>
|
<th className='lft' colSpan={8}></th>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -310,29 +346,31 @@ export default class ShipyardPage extends Page {
|
|||||||
<th className='sortable' onClick={sortShips('topBoost')}>{translate('boost')}</th>
|
<th className='sortable' onClick={sortShips('topBoost')}>{translate('boost')}</th>
|
||||||
<th className='sortable' onClick={sortShips('maxJumpRange')}>{translate('jump')}</th>
|
<th className='sortable' onClick={sortShips('maxJumpRange')}>{translate('jump')}</th>
|
||||||
<th className='sortable' onClick={sortShips('maxCargo')}>{translate('cargo')}</th>
|
<th className='sortable' onClick={sortShips('maxCargo')}>{translate('cargo')}</th>
|
||||||
|
<th className='sortable' onClick={sortShips('maxPassengers')}>{translate('pax')}</th>
|
||||||
|
|
||||||
<th className='lft' colSpan={6}>{translate('core module classes')}</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={5} className='sortable lft' onClick={sortShips('hpCount')}>{translate('hardpoints')}</th>
|
||||||
<th colSpan={8} className='sortable lft' onClick={sortShips('intCount')}>{translate('internal compartments')}</th>
|
<th colSpan={8} className='sortable lft' onClick={sortShips('intCount')}>{translate('internal compartments')}</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th className='sortable lft' onClick={sortShips('retailCost')}>{units.CR}</th>
|
<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('hullMass')}>{units.T}</th>
|
||||||
<th className='sortable lft' onClick={sortShips('speed')}>{units['m/s']}</th>
|
<th className='sortable lft' onClick={sortShips('speed')}>{units['m/s']}</th>
|
||||||
<th className='sortable' onClick={sortShips('boost')}>{units['m/s']}</th>
|
<th className='sortable' onClick={sortShips('boost')}>{units['m/s']}</th>
|
||||||
<th> </th>
|
<th> </th>
|
||||||
<th className='sortable' onClick={sortShips('baseShieldStrength')}>{units.MJ}</th>
|
<th className='sortable' onClick={sortShips('baseShieldStrength')}>{units.MJ}</th>
|
||||||
<th className='sortable lft' onClick={sortShips('topSpeed')}>{units['m/s']}</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('topBoost')}>{units['m/s']}</th>
|
||||||
<th className='sortable' onClick={sortShips('maxJumpRange')}>{units.LY}</th>
|
<th className='sortable' onClick={sortShips('maxJumpRange')}>{units.LY}</th>
|
||||||
<th className='sortable' onClick={sortShips('maxCargo')}>{units.T}</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 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, '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, '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, '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, '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, '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 lft' onClick={sortShips('hp',1)}>{translate('S')}</th>
|
||||||
<th className='sortable' onClick={sortShips('hp', 2)}>{translate('M')}</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', 3)}>{translate('L')}</th>
|
||||||
|
|||||||
@@ -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',
|
||||||
@@ -58,19 +67,33 @@ export const ModuleGroupToName = {
|
|||||||
cm: 'Countermeasure',
|
cm: 'Countermeasure',
|
||||||
ec: 'Electronic Countermeasure',
|
ec: 'Electronic Countermeasure',
|
||||||
fc: 'Fragment Cannon',
|
fc: 'Fragment Cannon',
|
||||||
|
rfl: 'Remote Release Flak Launcher',
|
||||||
hs: 'Heat Sink Launcher',
|
hs: 'Heat Sink Launcher',
|
||||||
ws: 'Frame Shift Wake Scanner',
|
ws: 'Frame Shift Wake Scanner',
|
||||||
kw: 'Kill Warrant Scanner',
|
kw: 'Kill Warrant Scanner',
|
||||||
nl: 'Mine Launcher',
|
nl: 'Mine Launcher',
|
||||||
ml: 'Mining Laser',
|
ml: 'Mining Laser',
|
||||||
mr: 'Missile Rack',
|
mr: 'Missile Rack',
|
||||||
|
axmr: 'AX Missile Rack',
|
||||||
pa: 'Plasma Accelerator',
|
pa: 'Plasma Accelerator',
|
||||||
po: 'Point Defence',
|
po: 'Point Defence',
|
||||||
mc: 'Multi-cannon',
|
mc: 'Multi-cannon',
|
||||||
|
axmc: 'AX Multi-cannon',
|
||||||
pl: 'Pulse Laser',
|
pl: 'Pulse Laser',
|
||||||
rg: 'Rail Gun',
|
rg: 'Rail Gun',
|
||||||
sb: 'Shield Booster',
|
sb: 'Shield Booster',
|
||||||
tp: 'Torpedo Pylon'
|
tp: 'Torpedo Pylon',
|
||||||
|
sfn: 'Shutdown Field Neutraliser',
|
||||||
|
xs: 'Xeno Scanner',
|
||||||
|
rcpl: 'Recon Limpet Controller',
|
||||||
|
rsl: 'Research Limpet Controller',
|
||||||
|
dtl: 'Decontamination Limpet Controller',
|
||||||
|
gpc: 'Guardian Plasma Charger',
|
||||||
|
ggc: 'Guardian Gauss Cannon',
|
||||||
|
tbsc: 'Shock Cannon',
|
||||||
|
gsc: 'Guardian Shard Cannon',
|
||||||
|
tbem: 'Enzyme Missile Rack',
|
||||||
|
tbrfl: 'Remote Release Flechette Launcher',
|
||||||
};
|
};
|
||||||
|
|
||||||
let GrpNameToCodeMap = {};
|
let GrpNameToCodeMap = {};
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -63,7 +63,6 @@ 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 || (e.class == id.charAt(0) && e.rating == id.charAt(1)));
|
||||||
if (s) {
|
if (s) {
|
||||||
s = new Module({ template: s });
|
s = new Module({ template: s });
|
||||||
@@ -196,6 +195,29 @@ export function findInternal(groupName, clss, rating, name) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds an internal module based on Class, Rating, Group and/or name.
|
||||||
|
* At least one of Group name or unique module name must be provided.
|
||||||
|
* will start searching at specified class and proceed lower until a
|
||||||
|
* module is found or 0 is hit
|
||||||
|
* Uses findInternal internally
|
||||||
|
*
|
||||||
|
* @param {String} groupName [Optional] Full name or abbreviated name for module group
|
||||||
|
* @param {integer} clss module Class
|
||||||
|
* @param {String} rating module Rating
|
||||||
|
* @param {String} name [Optional] Long/unique name for module -e.g. 'Advanced Discover Scanner'
|
||||||
|
* @return {Object} The module if found, null if not found
|
||||||
|
*/
|
||||||
|
export function findMaxInternal(groupName, clss, rating, name) {
|
||||||
|
let foundModule = null;
|
||||||
|
let currentClss = clss;
|
||||||
|
while (currentClss > 0 && foundModule == null) {
|
||||||
|
foundModule = findInternal(groupName, currentClss, rating, name);
|
||||||
|
currentClss = currentClss - 1;
|
||||||
|
}
|
||||||
|
return foundModule;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds an internal Module ID based on Class, Rating, Group and/or name.
|
* Finds an internal Module ID based on Class, Rating, Group and/or name.
|
||||||
* At least one ofGroup name or unique module name must be provided
|
* At least one ofGroup name or unique module name must be provided
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -151,7 +152,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 +165,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 +240,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));
|
||||||
}
|
}
|
||||||
@@ -437,12 +437,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 +467,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 +485,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,69 +494,62 @@ 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 {
|
||||||
|
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -569,6 +572,7 @@ export default class Ship {
|
|||||||
// Reset Cumulative stats
|
// Reset Cumulative stats
|
||||||
this.fuelCapacity = 0;
|
this.fuelCapacity = 0;
|
||||||
this.cargoCapacity = 0;
|
this.cargoCapacity = 0;
|
||||||
|
this.passengerCapacity = 0;
|
||||||
this.ladenMass = 0;
|
this.ladenMass = 0;
|
||||||
this.armour = this.baseArmour;
|
this.armour = this.baseArmour;
|
||||||
this.shield = this.baseShieldStrength;
|
this.shield = this.baseShieldStrength;
|
||||||
@@ -616,7 +620,7 @@ export default class Ship {
|
|||||||
standard[i].cat = 0;
|
standard[i].cat = 0;
|
||||||
standard[i].priority = priorities && priorities[i + 1] ? priorities[i + 1] * 1 : 0;
|
standard[i].priority = priorities && priorities[i + 1] ? priorities[i + 1] * 1 : 0;
|
||||||
standard[i].type = 'SYS';
|
standard[i].type = 'SYS';
|
||||||
standard[i].m = null; // Resetting 'old' modul if there was one
|
standard[i].m = null; // Resetting 'old' module if there was one
|
||||||
standard[i].discountedCost = 0;
|
standard[i].discountedCost = 0;
|
||||||
if (comps) {
|
if (comps) {
|
||||||
let module = ModuleUtils.standard(i, comps.standard[i]);
|
let module = ModuleUtils.standard(i, comps.standard[i]);
|
||||||
@@ -928,9 +932,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 +996,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)
|
||||||
@@ -1188,42 +1178,45 @@ export default class Ship {
|
|||||||
let unladenMass = this.hullMass;
|
let unladenMass = this.hullMass;
|
||||||
let cargoCapacity = 0;
|
let cargoCapacity = 0;
|
||||||
let fuelCapacity = 0;
|
let fuelCapacity = 0;
|
||||||
|
let passengerCapacity = 0;
|
||||||
|
|
||||||
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;
|
.filter()
|
||||||
}
|
.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();
|
.filter()
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
.filter()
|
||||||
}
|
.reduce((sum, cargo) => sum + cargo)
|
||||||
}
|
.value();
|
||||||
|
|
||||||
|
// handle passenger capacity
|
||||||
|
passengerCapacity += chain(slots)
|
||||||
|
.map(slot => slot.m ? slot.m.get('passengers') : null)
|
||||||
|
.filter()
|
||||||
|
.reduce((sum, passengers) => sum + passengers)
|
||||||
|
.value();
|
||||||
|
|
||||||
// Update global stats
|
// Update global stats
|
||||||
this.unladenMass = unladenMass;
|
this.unladenMass = unladenMass;
|
||||||
this.cargoCapacity = cargoCapacity;
|
this.cargoCapacity = cargoCapacity;
|
||||||
this.fuelCapacity = fuelCapacity;
|
this.fuelCapacity = fuelCapacity;
|
||||||
|
this.passengerCapacity = passengerCapacity;
|
||||||
this.ladenMass = unladenMass + fuelCapacity + cargoCapacity;
|
this.ladenMass = unladenMass + fuelCapacity + cargoCapacity;
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
@@ -1258,7 +1251,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;
|
||||||
@@ -1291,45 +1284,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1341,10 +1304,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, 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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ import { canMount } from '../utils/SlotFunctions';
|
|||||||
*/
|
*/
|
||||||
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) {
|
||||||
@@ -31,24 +31,31 @@ export function multiPurpose(ship, shielded, bulkheadIndex) {
|
|||||||
* @param {Object} standardOpts [Optional] Standard module optional overrides
|
* @param {Object} standardOpts [Optional] Standard module optional overrides
|
||||||
*/
|
*/
|
||||||
export function trader(ship, shielded, standardOpts) {
|
export function trader(ship, shielded, standardOpts) {
|
||||||
let usedSlots = [],
|
let usedSlots = [];
|
||||||
sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass);
|
let bstCount = 2;
|
||||||
|
let sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass);
|
||||||
|
ship.useStandard('A')
|
||||||
|
.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[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
|
||||||
|
|
||||||
// Shield generator if required
|
const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8];
|
||||||
if (shielded) {
|
const shieldInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
||||||
const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8];
|
.filter(a => (!a.eligible) || a.eligible.sg)
|
||||||
const shieldInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
.filter(a => a.maxClass >= sg.class)
|
||||||
.filter(a => (!a.eligible) || a.eligible.sg)
|
.sort((a, b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass));
|
||||||
.filter(a => a.maxClass >= sg.class)
|
shieldInternals.some(function(slot) {
|
||||||
.sort((a,b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass));
|
if (canMount(ship, slot, 'sg')) { // Assuming largest slot can hold an eligible shield
|
||||||
for (let i = 0; i < shieldInternals.length; i++) {
|
const shield = ModuleUtils.findInternal('sg', slot.maxClass, 'A');
|
||||||
if (canMount(ship, shieldInternals[i], 'sg')) {
|
if (shield && shield.maxmass > ship.hullMass) {
|
||||||
ship.use(shieldInternals[i], sg);
|
ship.use(slot, shield);
|
||||||
usedSlots.push(shieldInternals[i]);
|
ship.setSlotEnabled(slot, true);
|
||||||
break;
|
usedSlots.push(slot);
|
||||||
|
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--;) {
|
||||||
@@ -62,8 +69,15 @@ export function trader(ship, shielded, standardOpts) {
|
|||||||
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) {
|
||||||
ship.useLightestStandard(standardOpts);
|
if (s.maxClass == 0 && bstCount) { // Mount up to 2 boosters
|
||||||
|
ship.use(s, ModuleUtils.hardpoints('04'));
|
||||||
|
bstCount--;
|
||||||
|
} else {
|
||||||
|
ship.use(s, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ship.useLightestStandard(standardOpts);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -89,8 +103,8 @@ export function explorer(ship, planetary) {
|
|||||||
// 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'));
|
||||||
@@ -103,8 +117,8 @@ export function explorer(ship, 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
|
||||||
@@ -120,9 +134,9 @@ export function explorer(ship, planetary) {
|
|||||||
// 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);
|
||||||
@@ -135,8 +149,8 @@ export function explorer(ship, planetary) {
|
|||||||
// 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'));
|
||||||
@@ -148,8 +162,8 @@ export function explorer(ship, planetary) {
|
|||||||
// 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'));
|
||||||
@@ -162,8 +176,8 @@ export function explorer(ship, planetary) {
|
|||||||
// 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'));
|
||||||
@@ -200,6 +214,7 @@ export function explorer(ship, planetary) {
|
|||||||
* @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;
|
||||||
let standardOpts = { ppRating: 'A' },
|
let standardOpts = { ppRating: 'A' },
|
||||||
miningLaserCount = 2,
|
miningLaserCount = 2,
|
||||||
usedSlots = [],
|
usedSlots = [],
|
||||||
@@ -211,8 +226,8 @@ export function miner(ship, shielded) {
|
|||||||
// 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'));
|
||||||
@@ -224,8 +239,8 @@ export function miner(ship, shielded) {
|
|||||||
// 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
|
||||||
@@ -240,9 +255,9 @@ export function miner(ship, shielded) {
|
|||||||
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);
|
||||||
@@ -254,7 +269,7 @@ export function miner(ship, shielded) {
|
|||||||
|
|
||||||
// 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) {
|
||||||
@@ -268,13 +283,13 @@ export function miner(ship, shielded) {
|
|||||||
|
|
||||||
// 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;
|
||||||
@@ -283,8 +298,8 @@ export function miner(ship, shielded) {
|
|||||||
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')) {
|
||||||
@@ -299,9 +314,9 @@ export function miner(ship, shielded) {
|
|||||||
|
|
||||||
// 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
|
||||||
@@ -331,9 +346,9 @@ export function racer(ship) {
|
|||||||
// 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);
|
||||||
@@ -398,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
82
src/app/shipyard/StatsFormatting.js
Normal file
82
src/app/shipyard/StatsFormatting.js
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
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' }
|
||||||
|
};
|
||||||
@@ -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;
|
||||||
|
|
||||||
@@ -84,6 +87,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 +96,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 +108,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 +166,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 +199,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 +497,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 +531,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,6 +1,66 @@
|
|||||||
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 specials
|
||||||
|
* @param {Object} translate The translate object
|
||||||
|
* @param {Object} blueprint The blueprint at the required grade
|
||||||
|
* @param {string} grp The group of the module
|
||||||
|
* @param {Object} m The module to compare with
|
||||||
|
* @param {string} specialName The name of the special
|
||||||
|
* @returns {Object} The react components
|
||||||
|
*/
|
||||||
|
export function specialToolTip(translate, blueprint, grp, m, specialName) {
|
||||||
|
const effects = [];
|
||||||
|
if (!blueprint || !blueprint.features) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
if (m) {
|
||||||
|
// We also add in any benefits from specials that aren't covered above
|
||||||
|
if (m.blueprint) {
|
||||||
|
for (const feature in Modifications.modifierActions[specialName]) {
|
||||||
|
// if (!blueprint.features[feature] && !m.mods.feature) {
|
||||||
|
const featureDef = Modifications.modifications[feature];
|
||||||
|
if (featureDef && !featureDef.hidden) {
|
||||||
|
let symbol = '';
|
||||||
|
if (feature === 'jitter') {
|
||||||
|
symbol = '°';
|
||||||
|
} else if (featureDef.type === 'percentage') {
|
||||||
|
symbol = '%';
|
||||||
|
}
|
||||||
|
let current = m.getModValue(feature) - m.getModValue(feature, true);
|
||||||
|
if (featureDef.type === 'percentage') {
|
||||||
|
current = Math.round(current / 10) / 10;
|
||||||
|
} else if (featureDef.type === 'numeric') {
|
||||||
|
current /= 100;
|
||||||
|
}
|
||||||
|
const currentIsBeneficial = isValueBeneficial(feature, current);
|
||||||
|
|
||||||
|
effects.push(
|
||||||
|
<tr key={feature + '_specialTT'}>
|
||||||
|
<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%'>
|
||||||
|
<tbody>
|
||||||
|
{effects}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a tooltip with details of a blueprint's effects
|
* Generate a tooltip with details of a blueprint's effects
|
||||||
* @param {Object} translate The translate object
|
* @param {Object} translate The translate object
|
||||||
@@ -12,6 +72,9 @@ import { Modifications } from 'coriolis-data/dist';
|
|||||||
*/
|
*/
|
||||||
export function blueprintTooltip(translate, blueprint, engineers, grp, m) {
|
export function blueprintTooltip(translate, blueprint, engineers, grp, m) {
|
||||||
const effects = [];
|
const effects = [];
|
||||||
|
if (!blueprint || !blueprint.features) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
for (const feature in blueprint.features) {
|
for (const feature in blueprint.features) {
|
||||||
const featureIsBeneficial = isBeneficial(feature, blueprint.features[feature]);
|
const featureIsBeneficial = isBeneficial(feature, blueprint.features[feature]);
|
||||||
const featureDef = Modifications.modifications[feature];
|
const featureDef = Modifications.modifications[feature];
|
||||||
@@ -225,95 +288,41 @@ export function isValueBeneficial(feature, value) {
|
|||||||
*/
|
*/
|
||||||
export function getBlueprint(name, module) {
|
export function getBlueprint(name, module) {
|
||||||
// Start with a copy of the blueprint
|
// Start with a copy of the blueprint
|
||||||
const blueprint = JSON.parse(JSON.stringify(Modifications.blueprints[name]));
|
const findMod = val => Object.keys(Modifications.blueprints).find(elem => elem.toString().toLowerCase().search(val.toString().toLowerCase().replace(/(OutfittingFieldType_|persecond)/igm, '')) >= 0);
|
||||||
if (module) {
|
const found = Modifications.blueprints[findMod(name)];
|
||||||
if (module.grp === 'bh' || module.grp === 'hr' || module.grp === 'sg' || module.grp === 'psg' || module.grp === 'bsg') {
|
if (!found || !found.fdname) {
|
||||||
// Bulkheads, hull reinforcements and shield generators need to have their resistances altered by the base values
|
return {};
|
||||||
for (const grade in blueprint.grades) {
|
|
||||||
for (const feature in blueprint.grades[grade].features) {
|
|
||||||
if (feature === 'explres') {
|
|
||||||
blueprint.grades[grade].features[feature][0] *= (1 - module.explres);
|
|
||||||
blueprint.grades[grade].features[feature][1] *= (1 - module.explres);
|
|
||||||
}
|
|
||||||
if (feature === 'kinres') {
|
|
||||||
blueprint.grades[grade].features[feature][0] *= (1 - module.kinres);
|
|
||||||
blueprint.grades[grade].features[feature][1] *= (1 - module.kinres);
|
|
||||||
}
|
|
||||||
if (feature === 'thermres') {
|
|
||||||
blueprint.grades[grade].features[feature][0] *= (1 - module.thermres);
|
|
||||||
blueprint.grades[grade].features[feature][1] *= (1 - module.thermres);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (module.grp === 'sb') {
|
|
||||||
// Shield boosters are treated internally as straight modifiers, so rather than (for example)
|
|
||||||
// being a 4% boost they are a 104% multiplier. We need to fix the values here so that they look
|
|
||||||
// accurate as per the information in Elite
|
|
||||||
for (const grade in blueprint.grades) {
|
|
||||||
for (const feature in blueprint.grades[grade].features) {
|
|
||||||
if (feature === 'shieldboost') {
|
|
||||||
blueprint.grades[grade].features[feature][0] = ((1 + blueprint.grades[grade].features[feature][0]) * (1 + module.shieldboost) - 1) / module.shieldboost - 1;
|
|
||||||
blueprint.grades[grade].features[feature][1] = ((1 + blueprint.grades[grade].features[feature][1]) * (1 + module.shieldboost) - 1) / module.shieldboost - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
const blueprint = JSON.parse(JSON.stringify(found));
|
||||||
return blueprint;
|
return blueprint;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provide 'worst' primary modifications
|
* Provide 'percent' primary modifications
|
||||||
* @param {Object} ship The ship for which to perform the modifications
|
* @param {Object} ship The ship for which to perform the modifications
|
||||||
* @param {Object} m The module 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.
|
||||||
*/
|
*/
|
||||||
export function setWorst(ship, m) {
|
export function setPercent(ship, m, percent) {
|
||||||
ship.clearModifications(m);
|
|
||||||
const features = m.blueprint.grades[m.blueprint.grade].features;
|
|
||||||
for (const featureName in features) {
|
|
||||||
const value = features[featureName][0];
|
|
||||||
_setValue(ship, m, featureName, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provide 'best' 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 setBest(ship, m) {
|
|
||||||
ship.clearModifications(m);
|
|
||||||
const features = m.blueprint.grades[m.blueprint.grade].features;
|
|
||||||
for (const featureName in features) {
|
|
||||||
const value = features[featureName][1];
|
|
||||||
_setValue(ship, m, featureName, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provide 'extreme' 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 setExtreme(ship, m) {
|
|
||||||
ship.clearModifications(m);
|
ship.clearModifications(m);
|
||||||
|
// Pick given value as multiplier
|
||||||
|
const mult = percent / 100;
|
||||||
const features = m.blueprint.grades[m.blueprint.grade].features;
|
const features = m.blueprint.grades[m.blueprint.grade].features;
|
||||||
for (const featureName in features) {
|
for (const featureName in features) {
|
||||||
let value;
|
let value;
|
||||||
if (Modifications.modifications[featureName].higherbetter) {
|
if (Modifications.modifications[featureName].higherbetter) {
|
||||||
// Higher is better, but is this making it better or worse?
|
// Higher is better, but is this making it better or worse?
|
||||||
if (features[featureName][0] < 0 || (features[featureName][0] === 0 && features[featureName][1] < 0)) {
|
if (features[featureName][0] < 0 || (features[featureName][0] === 0 && features[featureName][1] < 0)) {
|
||||||
value = features[featureName][0];
|
value = features[featureName][1] + ((features[featureName][0] - features[featureName][1]) * mult);
|
||||||
} else {
|
} else {
|
||||||
value = features[featureName][1];
|
value = features[featureName][0] + ((features[featureName][1] - features[featureName][0]) * mult);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Higher is worse, but is this making it better or worse?
|
// Higher is worse, but is this making it better or worse?
|
||||||
if (features[featureName][0] < 0 || (features[featureName][0] === 0 && features[featureName][1] < 0)) {
|
if (features[featureName][0] < 0 || (features[featureName][0] === 0 && features[featureName][1] < 0)) {
|
||||||
value = features[featureName][1];
|
value = features[featureName][0] + ((features[featureName][1] - features[featureName][0]) * mult);
|
||||||
} else {
|
} else {
|
||||||
value = features[featureName][0];
|
value = features[featureName][1] + ((features[featureName][0] - features[featureName][1]) * mult);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -327,30 +336,8 @@ export function setExtreme(ship, m) {
|
|||||||
* @param {Object} m The module for which to perform the modifications
|
* @param {Object} m The module for which to perform the modifications
|
||||||
*/
|
*/
|
||||||
export function setRandom(ship, m) {
|
export function setRandom(ship, m) {
|
||||||
ship.clearModifications(m);
|
|
||||||
// Pick a single value for our randomness
|
// Pick a single value for our randomness
|
||||||
const mult = Math.random();
|
setPercent(ship, m, Math.random() * 100);
|
||||||
const features = m.blueprint.grades[m.blueprint.grade].features;
|
|
||||||
for (const featureName in features) {
|
|
||||||
let value;
|
|
||||||
if (Modifications.modifications[featureName].higherbetter) {
|
|
||||||
// Higher is better, but is this making it better or worse?
|
|
||||||
if (features[featureName][0] < 0 || (features[featureName][0] === 0 && features[featureName][1] < 0)) {
|
|
||||||
value = features[featureName][1] + ((features[featureName][0] - features[featureName][1]) * mult);
|
|
||||||
} 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -369,3 +356,62 @@ function _setValue(ship, m, featureName, value) {
|
|||||||
ship.setModification(m, featureName, value);
|
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,19 +29,24 @@ 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',
|
||||||
'Orca': 'orca',
|
'Orca': 'orca',
|
||||||
'Python': 'python',
|
'Python': 'python',
|
||||||
'SideWinder': 'sidewinder',
|
'SideWinder': 'sidewinder',
|
||||||
'Type6': 'type_6_transporter',
|
'Type6': 'type_6_transporter',
|
||||||
'Type7': 'type_7_transport',
|
'Type7': 'type_7_transport',
|
||||||
'Type9': 'type_9_heavy',
|
'Type9': 'type_9_heavy',
|
||||||
|
'Type9_Military': 'type_10_defender',
|
||||||
|
'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'
|
||||||
};
|
};
|
||||||
|
|
||||||
// Mapping from hardpoint class to name in companion API
|
// Mapping from hardpoint class to name in companion API
|
||||||
const HARDPOINT_NUM_TO_CLASS = {
|
export const HARDPOINT_NUM_TO_CLASS = {
|
||||||
0: 'Tiny',
|
0: 'Tiny',
|
||||||
1: 'Small',
|
1: 'Small',
|
||||||
2: 'Medium',
|
2: 'Medium',
|
||||||
@@ -104,7 +109,7 @@ function _moduleFromEdId(edId) {
|
|||||||
* @return {string} the Coriolis model of the ship
|
* @return {string} the Coriolis model of the ship
|
||||||
*/
|
*/
|
||||||
function _shipModelFromEDName(edName) {
|
function _shipModelFromEDName(edName) {
|
||||||
return SHIP_FD_NAME_TO_CORIOLIS_NAME[edName];
|
return SHIP_FD_NAME_TO_CORIOLIS_NAME[Object.keys(SHIP_FD_NAME_TO_CORIOLIS_NAME).find(elem => elem.toLowerCase() === edName.toLowerCase())];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -113,7 +118,7 @@ function _shipModelFromEDName(edName) {
|
|||||||
* @return {string} the Coriolis model of the ship
|
* @return {string} the Coriolis model of the ship
|
||||||
*/
|
*/
|
||||||
export function shipModelFromJson(json) {
|
export function shipModelFromJson(json) {
|
||||||
return _shipModelFromEDName(json.name);
|
return _shipModelFromEDName(json.name || json.Ship);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -142,29 +147,33 @@ export function shipFromJson(json) {
|
|||||||
ship.cargoHatch.priority = 4;
|
ship.cargoHatch.priority = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let rootModule;
|
||||||
|
|
||||||
// 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 + '"';
|
||||||
}
|
}
|
||||||
ship.bulkheads.enabled = true;
|
ship.bulkheads.enabled = true;
|
||||||
if (armourJson.modifiers) _addModifications(ship.bulkheads.m, armourJson.modifiers, armourJson.recipeName, armourJson.recipeLevel);
|
rootModule = json.modules.Armour;
|
||||||
|
if (rootModule.WorkInProgress_modifications) _addModifications(ship.bulkheads.m, rootModule.WorkInProgress_modifications, rootModule.engineer.recipeName, rootModule.engineer.recipeLevel);
|
||||||
|
|
||||||
// Add the standard modules
|
// Add the standard modules
|
||||||
// Power plant
|
// Power plant
|
||||||
const powerplantJson = json.modules.PowerPlant.module;
|
const powerplantJson = json.modules.PowerPlant.module;
|
||||||
const powerplant = _moduleFromEdId(powerplantJson.id);
|
const powerplant = _moduleFromEdId(powerplantJson.id);
|
||||||
if (powerplantJson.modifiers) _addModifications(powerplant, powerplantJson.modifiers, powerplantJson.recipeName, powerplantJson.recipeLevel);
|
rootModule = json.modules.PowerPlant;
|
||||||
|
if (rootModule.WorkInProgress_modifications) _addModifications(powerplant, rootModule.WorkInProgress_modifications, rootModule.engineer.recipeName, rootModule.engineer.recipeLevel);
|
||||||
ship.use(ship.standard[0], powerplant, true);
|
ship.use(ship.standard[0], powerplant, true);
|
||||||
ship.standard[0].enabled = powerplantJson.on === true;
|
ship.standard[0].enabled = powerplantJson.on === true;
|
||||||
ship.standard[0].priority = powerplantJson.priority;
|
ship.standard[0].priority = powerplantJson.priority;
|
||||||
@@ -172,7 +181,8 @@ export function shipFromJson(json) {
|
|||||||
// Thrusters
|
// Thrusters
|
||||||
const thrustersJson = json.modules.MainEngines.module;
|
const thrustersJson = json.modules.MainEngines.module;
|
||||||
const thrusters = _moduleFromEdId(thrustersJson.id);
|
const thrusters = _moduleFromEdId(thrustersJson.id);
|
||||||
if (thrustersJson.modifiers) _addModifications(thrusters, thrustersJson.modifiers, thrustersJson.recipeName, thrustersJson.recipeLevel);
|
rootModule = json.modules.MainEngines;
|
||||||
|
if (rootModule.WorkInProgress_modifications) _addModifications(thrusters, rootModule.WorkInProgress_modifications, rootModule.engineer.recipeName, rootModule.engineer.recipeLevel);
|
||||||
ship.use(ship.standard[1], thrusters, true);
|
ship.use(ship.standard[1], thrusters, true);
|
||||||
ship.standard[1].enabled = thrustersJson.on === true;
|
ship.standard[1].enabled = thrustersJson.on === true;
|
||||||
ship.standard[1].priority = thrustersJson.priority;
|
ship.standard[1].priority = thrustersJson.priority;
|
||||||
@@ -180,7 +190,8 @@ export function shipFromJson(json) {
|
|||||||
// FSD
|
// FSD
|
||||||
const frameshiftdriveJson = json.modules.FrameShiftDrive.module;
|
const frameshiftdriveJson = json.modules.FrameShiftDrive.module;
|
||||||
const frameshiftdrive = _moduleFromEdId(frameshiftdriveJson.id);
|
const frameshiftdrive = _moduleFromEdId(frameshiftdriveJson.id);
|
||||||
if (frameshiftdriveJson.modifiers) _addModifications(frameshiftdrive, frameshiftdriveJson.modifiers, frameshiftdriveJson.recipeName, frameshiftdriveJson.recipeLevel);
|
rootModule = json.modules.FrameShiftDrive;
|
||||||
|
if (rootModule.WorkInProgress_modifications) _addModifications(frameshiftdrive, rootModule.WorkInProgress_modifications, rootModule.engineer.recipeName, rootModule.engineer.recipeLevel);
|
||||||
ship.use(ship.standard[2], frameshiftdrive, true);
|
ship.use(ship.standard[2], frameshiftdrive, true);
|
||||||
ship.standard[2].enabled = frameshiftdriveJson.on === true;
|
ship.standard[2].enabled = frameshiftdriveJson.on === true;
|
||||||
ship.standard[2].priority = frameshiftdriveJson.priority;
|
ship.standard[2].priority = frameshiftdriveJson.priority;
|
||||||
@@ -188,7 +199,8 @@ export function shipFromJson(json) {
|
|||||||
// Life support
|
// Life support
|
||||||
const lifesupportJson = json.modules.LifeSupport.module;
|
const lifesupportJson = json.modules.LifeSupport.module;
|
||||||
const lifesupport = _moduleFromEdId(lifesupportJson.id);
|
const lifesupport = _moduleFromEdId(lifesupportJson.id);
|
||||||
if (lifesupportJson.modifiers)_addModifications(lifesupport, lifesupportJson.modifiers, lifesupportJson.recipeName, lifesupportJson.recipeLevel);
|
rootModule = json.modules.LifeSupport;
|
||||||
|
if (rootModule.WorkInProgress_modifications) _addModifications(lifesupport, rootModule.WorkInProgress_modifications, rootModule.engineer.recipeName, rootModule.engineer.recipeLevel);
|
||||||
ship.use(ship.standard[3], lifesupport, true);
|
ship.use(ship.standard[3], lifesupport, true);
|
||||||
ship.standard[3].enabled = lifesupportJson.on === true;
|
ship.standard[3].enabled = lifesupportJson.on === true;
|
||||||
ship.standard[3].priority = lifesupportJson.priority;
|
ship.standard[3].priority = lifesupportJson.priority;
|
||||||
@@ -196,7 +208,8 @@ export function shipFromJson(json) {
|
|||||||
// Power distributor
|
// Power distributor
|
||||||
const powerdistributorJson = json.modules.PowerDistributor.module;
|
const powerdistributorJson = json.modules.PowerDistributor.module;
|
||||||
const powerdistributor = _moduleFromEdId(powerdistributorJson.id);
|
const powerdistributor = _moduleFromEdId(powerdistributorJson.id);
|
||||||
if (powerdistributorJson.modifiers) _addModifications(powerdistributor, powerdistributorJson.modifiers, powerdistributorJson.recipeName, powerdistributorJson.recipeLevel);
|
rootModule = json.modules.PowerDistributor;
|
||||||
|
if (rootModule.WorkInProgress_modifications) _addModifications(powerdistributor, rootModule.WorkInProgress_modifications, rootModule.engineer.recipeName, rootModule.engineer.recipeLevel);
|
||||||
ship.use(ship.standard[4], powerdistributor, true);
|
ship.use(ship.standard[4], powerdistributor, true);
|
||||||
ship.standard[4].enabled = powerdistributorJson.on === true;
|
ship.standard[4].enabled = powerdistributorJson.on === true;
|
||||||
ship.standard[4].priority = powerdistributorJson.priority;
|
ship.standard[4].priority = powerdistributorJson.priority;
|
||||||
@@ -204,7 +217,8 @@ export function shipFromJson(json) {
|
|||||||
// Sensors
|
// Sensors
|
||||||
const sensorsJson = json.modules.Radar.module;
|
const sensorsJson = json.modules.Radar.module;
|
||||||
const sensors = _moduleFromEdId(sensorsJson.id);
|
const sensors = _moduleFromEdId(sensorsJson.id);
|
||||||
if (sensorsJson.modifiers) _addModifications(sensors, sensorsJson.modifiers, sensorsJson.recipeName, sensorsJson.recipeLevel);
|
rootModule = json.modules.Radar;
|
||||||
|
if (rootModule.WorkInProgress_modifications) _addModifications(sensors, rootModule.WorkInProgress_modifications, rootModule.engineer.recipeName, rootModule.engineer.recipeLevel);
|
||||||
ship.use(ship.standard[5], sensors, true);
|
ship.use(ship.standard[5], sensors, true);
|
||||||
ship.standard[5].enabled = sensorsJson.on === true;
|
ship.standard[5].enabled = sensorsJson.on === true;
|
||||||
ship.standard[5].priority = sensorsJson.priority;
|
ship.standard[5].priority = sensorsJson.priority;
|
||||||
@@ -240,7 +254,8 @@ export function shipFromJson(json) {
|
|||||||
} else {
|
} else {
|
||||||
const hardpointJson = hardpointSlot.module;
|
const hardpointJson = hardpointSlot.module;
|
||||||
const hardpoint = _moduleFromEdId(hardpointJson.id);
|
const hardpoint = _moduleFromEdId(hardpointJson.id);
|
||||||
if (hardpointJson.modifiers) _addModifications(hardpoint, hardpointJson.modifiers, hardpointJson.recipeName, hardpointJson.recipeLevel);
|
rootModule = hardpointSlot;
|
||||||
|
if (rootModule.WorkInProgress_modifications) _addModifications(hardpoint, rootModule.WorkInProgress_modifications, rootModule.engineer.recipeName, rootModule.engineer.recipeLevel, rootModule.specialModifications);
|
||||||
ship.use(ship.hardpoints[hardpointArrayNum], hardpoint, true);
|
ship.use(ship.hardpoints[hardpointArrayNum], hardpoint, true);
|
||||||
ship.hardpoints[hardpointArrayNum].enabled = hardpointJson.on === true;
|
ship.hardpoints[hardpointArrayNum].enabled = hardpointJson.on === true;
|
||||||
ship.hardpoints[hardpointArrayNum].priority = hardpointJson.priority;
|
ship.hardpoints[hardpointArrayNum].priority = hardpointJson.priority;
|
||||||
@@ -282,7 +297,8 @@ export function shipFromJson(json) {
|
|||||||
} else {
|
} else {
|
||||||
const internalJson = internalSlot.module;
|
const internalJson = internalSlot.module;
|
||||||
const internal = _moduleFromEdId(internalJson.id);
|
const internal = _moduleFromEdId(internalJson.id);
|
||||||
if (internalJson.modifiers) _addModifications(internal, internalJson.modifiers, internalJson.recipeName, internalJson.recipeLevel);
|
rootModule = internalSlot;
|
||||||
|
if (rootModule.WorkInProgress_modifications) _addModifications(internal, rootModule.WorkInProgress_modifications, rootModule.engineer.recipeName, rootModule.engineer.recipeLevel);
|
||||||
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;
|
||||||
@@ -299,35 +315,48 @@ export function shipFromJson(json) {
|
|||||||
* @param {Object} modifiers the modifiers
|
* @param {Object} modifiers the modifiers
|
||||||
* @param {Object} blueprint the blueprint of the modification
|
* @param {Object} blueprint the blueprint of the modification
|
||||||
* @param {Object} grade the grade of the modification
|
* @param {Object} grade the grade of the modification
|
||||||
|
* @param {Object} specialModifications special modification
|
||||||
*/
|
*/
|
||||||
function _addModifications(module, modifiers, blueprint, grade) {
|
function _addModifications(module, modifiers, blueprint, grade, specialModifications) {
|
||||||
if (!modifiers || !modifiers.modifiers) return;
|
if (!modifiers) return;
|
||||||
|
|
||||||
let special;
|
let special;
|
||||||
for (const i in modifiers.modifiers) {
|
if (specialModifications) {
|
||||||
|
special = Modifications.specials[Object.keys(specialModifications)[0]];
|
||||||
|
}
|
||||||
|
for (const i in modifiers) {
|
||||||
// Some special modifications
|
// Some special modifications
|
||||||
if (modifiers.modifiers[i].name === 'mod_weapon_clip_size_override') {
|
if (modifiers[i].name === 'mod_weapon_clip_size_override') {
|
||||||
// This is a numeric addition to the clip size, but we need to work it out in terms of being a percentage so
|
// This is a numeric addition to the clip size, but we need to work it out in terms of being a percentage so
|
||||||
// that it works the same as other modifications
|
// that it works the same as other modifications
|
||||||
const origClip = module.clip || 1;
|
const origClip = module.clip || 1;
|
||||||
module.setModValue('clip', ((modifiers.modifiers[i].value - origClip) / origClip) * 10000);
|
module.setModValue('clip', ((modifiers[i].value - origClip) / origClip) * 10000);
|
||||||
} else if (modifiers.modifiers[i].name === 'mod_weapon_burst_size') {
|
} else if (modifiers[i].name === 'mod_weapon_burst_size') {
|
||||||
// This is an absolute number that acts as an override
|
// This is an absolute number that acts as an override
|
||||||
module.setModValue('burst', modifiers.modifiers[i].value * 100);
|
module.setModValue('burst', modifiers[i].value * 100);
|
||||||
} else if (modifiers.modifiers[i].name === 'mod_weapon_burst_rof') {
|
} else if (modifiers[i].name === 'mod_weapon_burst_rof') {
|
||||||
// This is an absolute number that acts as an override
|
// This is an absolute number that acts as an override
|
||||||
module.setModValue('burstrof', modifiers.modifiers[i].value * 100);
|
module.setModValue('burstrof', modifiers[i].value * 100);
|
||||||
} else if (modifiers.modifiers[i].name === 'mod_weapon_falloffrange_from_range') {
|
} else if (modifiers[i].name === 'mod_weapon_falloffrange_from_range') {
|
||||||
// Obtain the falloff value directly from the range
|
// Obtain the falloff value directly from the range
|
||||||
module.setModValue('fallofffromrange', 1);
|
module.setModValue('fallofffromrange', 1);
|
||||||
} else if (modifiers.modifiers[i].name && modifiers.modifiers[i].name.startsWith('special_')) {
|
} else if (modifiers[i].name && modifiers[i].name.startsWith('special_')) {
|
||||||
// We don't add special effects directly, but keep a note of them so they can be added when fetching values
|
// We don't add special effects directly, but keep a note of them so they can be added when fetching values
|
||||||
special = Modifications.specials[modifiers.modifiers[i].name];
|
special = Modifications.specials[modifiers[i].name];
|
||||||
} else {
|
} else {
|
||||||
// Look up the modifiers to find what we need to do
|
// Look up the modifiers to find what we need to do
|
||||||
const modifierActions = Modifications.modifierActions[modifiers.modifiers[i].name];
|
const modifierActions = Modifications.modifierActions[i];
|
||||||
const value = modifiers.modifiers[i].value;
|
let value;
|
||||||
|
if (i === 'OutfittingFieldType_DefenceModifierShieldMultiplier') {
|
||||||
|
value = modifiers[i].value - 1;
|
||||||
|
} else if (i === 'OutfittingFieldType_DefenceModifierHealthMultiplier' && blueprint.startsWith('Armour_')) {
|
||||||
|
value = (modifiers[i].value - module.hullboost) / module.hullboost;
|
||||||
|
} else if (i === 'OutfittingFieldType_DefenceModifierHealthMultiplier') {
|
||||||
|
value = modifiers[i].value / module.hullboost;
|
||||||
|
} else if (i === 'OutfittingFieldType_RateOfFire') {
|
||||||
|
value = (1 / Math.abs(modifiers[i].value));
|
||||||
|
} else {
|
||||||
|
value = modifiers[i].value - 1;
|
||||||
|
}
|
||||||
// Carry out the required changes
|
// Carry out the required changes
|
||||||
for (const action in modifierActions) {
|
for (const action in modifierActions) {
|
||||||
if (isNaN(modifierActions[action])) {
|
if (isNaN(modifierActions[action])) {
|
||||||
@@ -423,7 +452,7 @@ function _addModifications(module, modifiers, blueprint, grade) {
|
|||||||
// Bulkhead boost is based off the inherent boost of the module
|
// Bulkhead boost is based off the inherent boost of the module
|
||||||
if (module.grp == 'bh') {
|
if (module.grp == 'bh') {
|
||||||
const alteredBoost = (1 + module.hullboost) * (1 + module.getModValue('hullboost') / 10000) - 1;
|
const alteredBoost = (1 + module.hullboost) * (1 + module.getModValue('hullboost') / 10000) - 1;
|
||||||
module.setModValue('hullboost', (alteredBoost / module.hullboost - 1) * 10000);
|
module.setModValue('hullboost', (alteredBoost / module.hullboost - 1) * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Jitter is an absolute number, so we need to divide it by 100
|
// Jitter is an absolute number, so we need to divide it by 100
|
||||||
@@ -431,11 +460,6 @@ function _addModifications(module, modifiers, blueprint, grade) {
|
|||||||
module.setModValue('jitter', module.getModValue('jitter') / 100);
|
module.setModValue('jitter', module.getModValue('jitter') / 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FD uses interval between bursts internally, so we need to translate this to a real rate of fire
|
|
||||||
if (module.getModValue('rof')) {
|
|
||||||
module.setModValue('rof', ((1 / (1 + module.getModValue('rof') / 10000)) - 1) * 10000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clip size is rounded up so that the result is a whole number
|
// Clip size is rounded up so that the result is a whole number
|
||||||
if (module.getModValue('clip')) {
|
if (module.getModValue('clip')) {
|
||||||
const individual = 1 / (module.clip || 1);
|
const individual = 1 / (module.clip || 1);
|
||||||
|
|||||||
297
src/app/utils/JournalUtils.js
Normal file
297
src/app/utils/JournalUtils.js
Normal file
@@ -0,0 +1,297 @@
|
|||||||
|
import Ship from '../shipyard/Ship';
|
||||||
|
import { HARDPOINT_NUM_TO_CLASS, shipModelFromJson } from './CompanionApiUtils';
|
||||||
|
import { Ships } from 'coriolis-data/dist';
|
||||||
|
import Module from '../shipyard/Module';
|
||||||
|
import { Modules } from 'coriolis-data/dist';
|
||||||
|
import { Modifications } from 'coriolis-data/dist';
|
||||||
|
import { getBlueprint } from './BlueprintFunctions';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain a module given its FD Name
|
||||||
|
* @param {string} fdname the FD Name of the module
|
||||||
|
* @return {Module} the module
|
||||||
|
*/
|
||||||
|
function _moduleFromFdName(fdname) {
|
||||||
|
if (!fdname) return null;
|
||||||
|
fdname = fdname.toLowerCase();
|
||||||
|
// Check standard modules
|
||||||
|
for (const grp in Modules.standard) {
|
||||||
|
if (Modules.standard.hasOwnProperty(grp)) {
|
||||||
|
for (const i in Modules.standard[grp]) {
|
||||||
|
if (Modules.standard[grp][i].symbol && Modules.standard[grp][i].symbol.toLowerCase() === fdname) {
|
||||||
|
// Found it
|
||||||
|
return new Module({ template: Modules.standard[grp][i] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check hardpoint modules
|
||||||
|
for (const grp in Modules.hardpoints) {
|
||||||
|
if (Modules.hardpoints.hasOwnProperty(grp)) {
|
||||||
|
for (const i in Modules.hardpoints[grp]) {
|
||||||
|
if (Modules.hardpoints[grp][i].symbol && Modules.hardpoints[grp][i].symbol.toLowerCase() === fdname) {
|
||||||
|
// Found it
|
||||||
|
return new Module({ template: Modules.hardpoints[grp][i] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check internal modules
|
||||||
|
for (const grp in Modules.internal) {
|
||||||
|
if (Modules.internal.hasOwnProperty(grp)) {
|
||||||
|
for (const i in Modules.internal[grp]) {
|
||||||
|
if (Modules.internal[grp][i].symbol && Modules.internal[grp][i].symbol.toLowerCase() === fdname) {
|
||||||
|
// Found it
|
||||||
|
return new Module({ template: Modules.internal[grp][i] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not found
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a ship from the journal Loadout event JSON
|
||||||
|
* @param {object} json the Loadout event JSON
|
||||||
|
* @return {Ship} the built ship
|
||||||
|
*/
|
||||||
|
export function shipFromLoadoutJSON(json) {
|
||||||
|
// Start off building a basic ship
|
||||||
|
const shipModel = shipModelFromJson(json);
|
||||||
|
if (!shipModel) {
|
||||||
|
throw 'No such ship found: "' + json.Ship + '"';
|
||||||
|
}
|
||||||
|
const shipTemplate = Ships[shipModel];
|
||||||
|
|
||||||
|
let ship = new Ship(shipModel, shipTemplate.properties, shipTemplate.slots);
|
||||||
|
ship.buildWith(null);
|
||||||
|
// Initial Ship building, don't do engineering yet.
|
||||||
|
let modsToAdd = [];
|
||||||
|
|
||||||
|
for (const module of json.Modules) {
|
||||||
|
switch (module.Slot.toLowerCase()) {
|
||||||
|
// Cargo Hatch.
|
||||||
|
case 'cargohatch':
|
||||||
|
ship.cargoHatch.enabled = module.On;
|
||||||
|
ship.cargoHatch.priority = module.Priority;
|
||||||
|
break;
|
||||||
|
// Add the bulkheads
|
||||||
|
case 'armour':
|
||||||
|
if (module.Item.toLowerCase().endsWith('_armour_grade1')) {
|
||||||
|
ship.useBulkhead(0, true);
|
||||||
|
} else if (module.Item.toLowerCase().endsWith('_armour_grade2')) {
|
||||||
|
ship.useBulkhead(1, true);
|
||||||
|
} else if (module.Item.toLowerCase().endsWith('_armour_grade3')) {
|
||||||
|
ship.useBulkhead(2, true);
|
||||||
|
} else if (module.Item.toLowerCase().endsWith('_armour_mirrored')) {
|
||||||
|
ship.useBulkhead(3, true);
|
||||||
|
} else if (module.Item.toLowerCase().endsWith('_armour_reactive')) {
|
||||||
|
ship.useBulkhead(4, true);
|
||||||
|
} else {
|
||||||
|
throw 'Unknown bulkheads "' + module.Item + '"';
|
||||||
|
}
|
||||||
|
ship.bulkheads.enabled = true;
|
||||||
|
if (module.Engineering) _addModifications(ship.bulkheads.m, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect);
|
||||||
|
break;
|
||||||
|
case 'powerplant':
|
||||||
|
const powerplant = _moduleFromFdName(module.Item);
|
||||||
|
ship.use(ship.standard[0], powerplant, true);
|
||||||
|
ship.standard[0].enabled = module.On;
|
||||||
|
ship.standard[0].priority = module.Priority;
|
||||||
|
if (module.Engineering) _addModifications(powerplant, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect);
|
||||||
|
break;
|
||||||
|
case 'mainengines':
|
||||||
|
const thrusters = _moduleFromFdName(module.Item);
|
||||||
|
ship.use(ship.standard[1], thrusters, true);
|
||||||
|
ship.standard[1].enabled = module.On;
|
||||||
|
ship.standard[1].priority = module.Priority;
|
||||||
|
if (module.Engineering) _addModifications(thrusters, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect);
|
||||||
|
break;
|
||||||
|
case 'frameshiftdrive':
|
||||||
|
const frameshiftdrive = _moduleFromFdName(module.Item);
|
||||||
|
ship.use(ship.standard[2], frameshiftdrive, true);
|
||||||
|
ship.standard[2].enabled = module.On;
|
||||||
|
ship.standard[2].priority = module.Priority;
|
||||||
|
if (module.Engineering) _addModifications(frameshiftdrive, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect);
|
||||||
|
break;
|
||||||
|
case 'lifesupport':
|
||||||
|
const lifesupport = _moduleFromFdName(module.Item);
|
||||||
|
ship.use(ship.standard[3], lifesupport, true);
|
||||||
|
ship.standard[3].enabled = module.On === true;
|
||||||
|
ship.standard[3].priority = module.Priority;
|
||||||
|
if (module.Engineering) _addModifications(lifesupport, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect);
|
||||||
|
break;
|
||||||
|
case 'powerdistributor':
|
||||||
|
const powerdistributor = _moduleFromFdName(module.Item);
|
||||||
|
ship.use(ship.standard[4], powerdistributor, true);
|
||||||
|
ship.standard[4].enabled = module.On;
|
||||||
|
ship.standard[4].priority = module.Priority;
|
||||||
|
if (module.Engineering) _addModifications(powerdistributor, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect);
|
||||||
|
break;
|
||||||
|
case 'radar':
|
||||||
|
const sensors = _moduleFromFdName(module.Item);
|
||||||
|
ship.use(ship.standard[5], sensors, true);
|
||||||
|
ship.standard[5].enabled = module.On;
|
||||||
|
ship.standard[5].priority = module.Priority;
|
||||||
|
if (module.Engineering) _addModifications(sensors, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect);
|
||||||
|
break;
|
||||||
|
case 'fueltank':
|
||||||
|
const fueltank = _moduleFromFdName(module.Item);
|
||||||
|
ship.use(ship.standard[6], fueltank, true);
|
||||||
|
ship.standard[6].enabled = true;
|
||||||
|
ship.standard[6].priority = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
for (const module of json.Modules) {
|
||||||
|
if (module.Slot.toLowerCase().search(/hardpoint/) !== -1) {
|
||||||
|
// Add hardpoints
|
||||||
|
let hardpoint;
|
||||||
|
let hardpointClassNum = -1;
|
||||||
|
let hardpointSlotNum = -1;
|
||||||
|
let hardpointArrayNum = 0;
|
||||||
|
for (let i in shipTemplate.slots.hardpoints) {
|
||||||
|
if (shipTemplate.slots.hardpoints[i] === hardpointClassNum) {
|
||||||
|
// Another slot of the same class
|
||||||
|
hardpointSlotNum++;
|
||||||
|
} else {
|
||||||
|
// The first slot of a new class
|
||||||
|
hardpointClassNum = shipTemplate.slots.hardpoints[i];
|
||||||
|
hardpointSlotNum = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now that we know what we're looking for, find it
|
||||||
|
const hardpointName = HARDPOINT_NUM_TO_CLASS[hardpointClassNum] + 'Hardpoint' + hardpointSlotNum;
|
||||||
|
const hardpointSlot = json.Modules.find(elem => elem.Slot.toLowerCase() === hardpointName.toLowerCase());
|
||||||
|
if (!hardpointSlot) {
|
||||||
|
// This can happen with old imports that don't contain new hardpoints
|
||||||
|
} else if (!hardpointSlot) {
|
||||||
|
// No module
|
||||||
|
} else {
|
||||||
|
hardpoint = _moduleFromFdName(hardpointSlot.Item);
|
||||||
|
ship.use(ship.hardpoints[hardpointArrayNum], hardpoint, true);
|
||||||
|
ship.hardpoints[hardpointArrayNum].enabled = hardpointSlot.On;
|
||||||
|
ship.hardpoints[hardpointArrayNum].priority = hardpointSlot.Priority;
|
||||||
|
modsToAdd.push({ coriolisMod: hardpoint, json: hardpointSlot });
|
||||||
|
}
|
||||||
|
hardpointArrayNum++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (module.Slot.toLowerCase().search(/slot\d/) !== -1) {
|
||||||
|
let internalSlotNum = 1;
|
||||||
|
let militarySlotNum = 1;
|
||||||
|
for (let i in shipTemplate.slots.internal) {
|
||||||
|
if (!shipTemplate.slots.internal.hasOwnProperty(i)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const isMilitary = isNaN(shipTemplate.slots.internal[i]) ? shipTemplate.slots.internal[i].name = 'military' : false;
|
||||||
|
|
||||||
|
// The internal slot might be a standard or a military slot. Military slots have a different naming system
|
||||||
|
let internalSlot = null;
|
||||||
|
if (isMilitary) {
|
||||||
|
const internalName = 'Military0' + militarySlotNum;
|
||||||
|
internalSlot = json.Modules.find(elem => elem.Slot.toLowerCase() === internalName.toLowerCase());
|
||||||
|
militarySlotNum++;
|
||||||
|
} else {
|
||||||
|
// Slot numbers are not contiguous so handle skips.
|
||||||
|
while (internalSlot === null && internalSlotNum < 99) {
|
||||||
|
// Slot sizes have no relationship to the actual size, either, so check all possibilities
|
||||||
|
for (let slotsize = 0; slotsize < 9; slotsize++) {
|
||||||
|
const internalName = 'Slot' + (internalSlotNum <= 9 ? '0' : '0') + internalSlotNum + '_Size' + slotsize;
|
||||||
|
if (json.Modules.find(elem => elem.Slot.toLowerCase() === internalName.toLowerCase())) {
|
||||||
|
internalSlot = json.Modules.find(elem => elem.Slot.toLowerCase() === internalName.toLowerCase());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internalSlotNum++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!internalSlot) {
|
||||||
|
// This can happen with old imports that don't contain new slots
|
||||||
|
} else {
|
||||||
|
const internalJson = internalSlot;
|
||||||
|
const internal = _moduleFromFdName(internalJson.Item);
|
||||||
|
ship.use(ship.internal[i], internal, true);
|
||||||
|
ship.internal[i].enabled = internalJson.On === true;
|
||||||
|
ship.internal[i].priority = internalJson.Priority;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// We don't have any information on it so guess it's priority 5 and disabled
|
||||||
|
if (!ship.cargoHatch) {
|
||||||
|
ship.cargoHatch.enabled = false;
|
||||||
|
ship.cargoHatch.priority = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
* @param {Object} modifiers the modifiers
|
||||||
|
* @param {Object} blueprint the blueprint of the modification
|
||||||
|
* @param {Object} grade the grade of the modification
|
||||||
|
* @param {Object} specialModifications special modification
|
||||||
|
*/
|
||||||
|
function _addModifications(module, modifiers, blueprint, grade, specialModifications) {
|
||||||
|
if (!modifiers) return;
|
||||||
|
let special;
|
||||||
|
if (specialModifications) {
|
||||||
|
special = Modifications.specials[specialModifications];
|
||||||
|
}
|
||||||
|
// Add the blueprint definition, grade and special
|
||||||
|
if (blueprint) {
|
||||||
|
module.blueprint = getBlueprint(blueprint, module);
|
||||||
|
if (grade) {
|
||||||
|
module.blueprint.grade = Number(grade);
|
||||||
|
}
|
||||||
|
if (special) {
|
||||||
|
module.blueprint.special = special;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const i in modifiers) {
|
||||||
|
// Some special modifications
|
||||||
|
// Look up the modifiers to find what we need to do
|
||||||
|
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)];
|
||||||
|
// TODO: Figure out how to scale this value.
|
||||||
|
if (!!modifiers[i].LessIsGood) {
|
||||||
|
|
||||||
|
}
|
||||||
|
let value = (modifiers[i].Value / modifiers[i].OriginalValue * 100 - 100) * 100;
|
||||||
|
if (value === Infinity) {
|
||||||
|
value = modifiers[i].Value * 100;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Carry out the required changes
|
||||||
|
for (const action in modifierActions) {
|
||||||
|
if (isNaN(modifierActions[action])) {
|
||||||
|
module.setModValue(action, modifierActions[action]);
|
||||||
|
} else {
|
||||||
|
module.setModValue(action, value, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,14 @@
|
|||||||
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
|
||||||
@@ -8,7 +17,7 @@ import request from 'superagent';
|
|||||||
* @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');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -220,7 +220,7 @@ export function diffDetails(language, m, mm) {
|
|||||||
|
|
||||||
let mCost = m.cost || 0;
|
let mCost = m.cost || 0;
|
||||||
let mmCost = mm ? mm.cost : 0;
|
let mmCost = mm ? mm.cost : 0;
|
||||||
if (mCost != mmCost) propDiffs.push(<div key='cost'>{translate('cost')}: <span className={diffClass(mCost, mmCost, true) }>{mCost ? Math.round(mCost * (1 - Persist.getModuleDiscount())) : 0}{units.CR}</span></div>);
|
if (mCost != mmCost) propDiffs.push(<div key='cost'>{translate('cost')}: <span className={diffClass(mCost, mmCost, true) }>{formats.int(mCost ? Math.round(mCost * (1 - Persist.getModuleDiscount())) : 0)}{units.CR}</span></div>);
|
||||||
|
|
||||||
let mMass = m.mass || 0;
|
let mMass = m.mass || 0;
|
||||||
let mmMass = mm ? mm.getMass() : 0;
|
let mmMass = mm ? mm.getMass() : 0;
|
||||||
|
|||||||
6
src/images/Empire.svg
Normal file
6
src/images/Empire.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg width="1718.2px" height="999.8px" viewBox="0 0 1718.2 999.8" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<defs/>
|
||||||
|
<g>
|
||||||
|
<path stroke="none" fill="#FF8C0D" d="M1717.55 -0.05 L1717.65 -0.25 1718.2 -0.1 1718.6 0.35 1719.2 1.55 1719.3 2.25 1718.95 2.85 1709.75 11.35 1010.8 687.7 1008.05 690.5 1005.45 693.4 1003 696.35 1000.75 699.45 998.65 702.65 996.7 705.95 994.9 709.4 993.25 713.05 934.9 850.7 876.4 988.25 871.55 998.95 871.3 999.3 870.9 999.5 870.6 999.55 870 999.4 869.5 999.05 868.3 997.95 867.35 996.7 866.6 995.25 864.7 989.85 762.3 730.55 754.8 711.85 751.1 702.45 746.6 690.65 745.5 688.4 744.25 686.3 742.9 684.35 741.4 682.45 739.75 680.6 737.95 678.85 736 677.15 8.1 61.2 5.75 59.15 3.65 57.1 1.6 54.6 1.1 53.7 1.1 53.25 1.25 52.9 1.5 52.65 1.8 52.45 2.15 52.4 2.55 52.5 9.75 55.7 713.05 398.5 780.95 431.7 848.75 464.95 851.7 466.25 854.4 467.15 856.95 467.6 859.45 467.65 861.9 467.25 864.4 466.45 866.95 465.2 869.6 463.5 1416.95 79.15 1419.7 77.35 1422.45 75.7 1425.25 74.2 1428.1 72.85 1431 71.6 1434 70.5 1437.05 69.55 1440.2 68.7 1574.85 35.15 1709.5 1.5 1713.75 0.6 1717.55 -0.05 M903.9 129.8 L904 129.65 930.9 129.75 934.3 129.85 937.55 130.15 940.65 130.65 943.6 131.35 946.4 132.2 949.2 133.3 951.95 134.6 954.55 136.05 957 137.7 959.35 139.5 961.6 141.5 963.75 143.65 965.8 145.95 967.75 148.4 969.6 151 971.35 153.8 974.25 159 976.8 164.3 979.05 169.7 981 175.2 982.7 180.8 984.2 186.45 985.55 192.15 986.75 197.9 987.85 203.75 988.8 209.65 990.4 221.45 991.6 233.25 992.55 245.1 995 280.65 996 292.5 996.2 295.1 996.25 297.55 996.15 299.85 995.9 302 995.5 304.1 994.95 306.1 994.25 308 993.45 309.7 992.4 311.5 991.25 313.15 989.95 314.75 988.45 316.3 986.8 317.8 985 319.25 980.85 322.15 946.35 344.45 912.05 367.15 843.7 413.05 841.45 414.45 839.25 415.55 836.9 416.35 834.85 416.75 832.6 416.85 830.15 416.5 827.75 415.8 825.25 414.7 812.45 408.55 799.4 402.55 773.35 390.75 773 390.5 772.8 390.15 772.75 389.75 772.85 389.35 794.5 343.45 801.35 328.15 811 305.8 820.45 283.4 839 238.45 841 233 842.7 227.35 844.1 221.65 845.25 215.95 845.7 213.05 845.8 210.65 845.55 208.55 845.3 207.75 844.9 206.9 843.85 205.55 842.3 204.4 840.2 203.4 837.55 202.55 830.2 200.55 822.85 198.85 819.15 198.15 815.5 197.6 811.85 197.2 808.2 196.95 804.55 196.9 800.9 197.05 797.25 197.4 793.6 198 789.9 198.9 786.25 200.05 782.6 201.55 778.55 203.5 777 203.65 776.65 203.6 776.1 203.15 775.95 202.8 775.5 200.55 775.25 197.65 775.3 194.75 775.65 191.9 776.25 189.05 777.05 186.3 778 183.6 779.05 180.9 780.5 177.65 782.1 174.5 783.8 171.5 785.65 168.6 787.6 165.85 789.7 163.2 791.9 160.7 794.25 158.3 796.7 156 799.3 153.85 802 151.8 804.8 149.9 807.7 148.1 810.75 146.4 813.9 144.85 817.15 143.4 824.4 140.6 831.75 138.2 839.15 136.15 846.65 134.45 854.2 133.05 861.8 131.95 869.45 131.1 877.1 130.5 883.8 130.15 890.5 129.95 903.9 129.8"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.8 KiB |
6
src/images/Federation.svg
Normal file
6
src/images/Federation.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 30 KiB |
71
src/images/surface_port_pm.svg
Normal file
71
src/images/surface_port_pm.svg
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
width="631.072px" height="713.591px" viewBox="0 0 631.072 713.591" enable-background="new 0 0 631.072 713.591"
|
||||||
|
xml:space="preserve">
|
||||||
|
<g id="Layer_2">
|
||||||
|
<g>
|
||||||
|
|
||||||
|
<rect x="460.344" y="668.509" transform="matrix(0.7399 0.6727 -0.6727 0.7399 586.8318 -155.8918)" fill="#FF8C0D" width="69.376" height="24.999"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
|
||||||
|
<rect x="499.816" y="630.511" transform="matrix(0.8742 0.4856 -0.4856 0.8742 379.9255 -180.1477)" fill="#FF8C0D" width="75.501" height="24.999"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
|
||||||
|
<rect x="537.078" y="590.363" transform="matrix(0.9477 0.319 -0.319 0.9477 222.2531 -151.1744)" fill="#FF8C0D" width="71.047" height="25"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
|
||||||
|
<rect x="561.364" y="544.916" transform="matrix(0.9741 0.2263 -0.2263 0.9741 141.5892 -120.2454)" fill="#FF8C0D" width="67.758" height="25"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
|
||||||
|
<rect x="113.454" y="633.977" transform="matrix(0.6727 0.7399 -0.7399 0.6727 536.0002 125.674)" fill="#FF8C0D" width="24.999" height="69.375"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
|
||||||
|
<rect x="72.787" y="594.581" transform="matrix(0.4857 0.8741 -0.8741 0.4857 596.6155 250.6765)" fill="#FF8C0D" width="25" height="75.501"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
|
||||||
|
<rect x="43.454" y="554.475" transform="matrix(0.319 0.9477 -0.9477 0.319 597.2715 348.7429)" fill="#FF8C0D" width="25" height="71.046"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
|
||||||
|
<rect x="23.329" y="507.12" transform="matrix(0.2263 0.9741 -0.9741 0.2263 554.6862 383.6775)" fill="#FF8C0D" width="25" height="67.758"/>
|
||||||
|
</g>
|
||||||
|
<path fill="#FF8C0D" d="M556.439,425.748c-13.466-24.188-32.583-45.79-56.821-64.205c-30.562-23.22-67.327-39.855-107.331-48.986
|
||||||
|
v25.737c93.31,23.32,160.681,90.053,160.681,168.752c0,98.096-104.668,177.619-233.785,177.619
|
||||||
|
c-129.115,0-233.784-79.523-233.784-177.619c0-75.918,62.696-140.699,150.889-166.118v-25.964
|
||||||
|
c-36.209,9.508-69.489,25.266-97.539,46.578c-24.238,18.415-43.355,40.017-56.821,64.205
|
||||||
|
c-14.286,25.66-21.529,53.014-21.529,81.299c0,28.284,7.243,55.637,21.529,81.298c13.465,24.188,32.583,45.789,56.821,64.204
|
||||||
|
c48.479,36.832,112.559,57.117,180.435,57.117c67.876,0,131.956-20.285,180.436-57.117c24.238-18.415,43.355-40.017,56.821-64.204
|
||||||
|
c14.285-25.661,21.528-53.014,21.528-81.298C577.968,478.761,570.725,451.408,556.439,425.748z"/>
|
||||||
|
<path fill="#FF8C0D" d="M473.97,427.082c-8.958-16.091-21.621-30.42-37.64-42.591c-13.129-9.975-27.996-18.079-44.043-24.152
|
||||||
|
v27.053c42.591,19.056,71.178,54.248,71.178,94.519c0,60.541-64.597,109.619-144.282,109.619
|
||||||
|
c-79.684,0-144.281-49.078-144.281-109.619c0-37.101,24.263-69.892,61.386-89.727v-27.819c-12.323,5.48-23.835,12.212-34.25,20.125
|
||||||
|
c-16.019,12.171-28.683,26.5-37.64,42.591c-9.618,17.276-14.495,35.724-14.495,54.829c0,19.104,4.877,37.552,14.495,54.829
|
||||||
|
c8.958,16.091,21.622,30.42,37.64,42.59c31.574,23.989,73.178,37.2,117.146,37.2c43.969,0,85.572-13.211,117.147-37.2
|
||||||
|
c16.019-12.17,28.682-26.499,37.64-42.59c9.618-17.277,14.495-35.725,14.495-54.829
|
||||||
|
C488.465,462.805,483.588,444.358,473.97,427.082z"/>
|
||||||
|
</g>
|
||||||
|
<g id="Layer_3">
|
||||||
|
<path fill="#FF8C0D" d="M253.551,232.333c-0.157-0.981-0.264-1.978-0.264-3c0-10.844,9.173-19.667,20.448-19.667
|
||||||
|
s20.448,8.822,20.448,19.667c0,1.022-0.106,2.019-0.264,3h25.15c0.067-0.993,0.113-1.991,0.113-3
|
||||||
|
c0-24.668-20.348-44.667-45.448-44.667s-45.448,19.998-45.448,44.667c0,1.009,0.046,2.007,0.113,3H253.551z"/>
|
||||||
|
<path fill="#FF8C0D" d="M302.831,48c-0.136-0.669-0.21-1.337-0.21-2c0-9.913,15.539-21,36.333-21s36.333,11.087,36.333,21
|
||||||
|
c0,0.663-0.074,1.331-0.21,2h25.143c0.038-0.664,0.067-1.329,0.067-2c0-25.405-27.46-46-61.333-46
|
||||||
|
c-33.874,0-61.333,20.595-61.333,46c0,0.671,0.03,1.336,0.067,2H302.831z"/>
|
||||||
|
<ellipse fill="#FF8C0D" cx="338.954" cy="68.499" rx="36.333" ry="6.75"/>
|
||||||
|
<path fill="#FF8C0D" d="M375.287,46c0,2.208-16.267,3.999-36.333,3.999S302.621,48.208,302.621,46h-25v158.499h25v-136h72.667
|
||||||
|
V473.51c-4.165,3.092-16.463,8.535-36.333,8.535c-7.763,0-14.353-0.836-19.771-2.033l-10.749,23.051
|
||||||
|
c8.99,2.529,19.405,3.982,30.52,3.982c33.873,0,61.333-13.432,61.333-30V46H375.287z"/>
|
||||||
|
<ellipse fill="#FF8C0D" cx="273.735" cy="253.132" rx="20.448" ry="3.916"/>
|
||||||
|
<path fill="#FF8C0D" d="M294.184,229.333c0,1.151-9.155,2.084-20.448,2.084s-20.448-0.933-20.448-2.084h-25v257.666h0.128
|
||||||
|
c1.667,14.529,21.313,26,45.32,26s43.653-11.471,45.32-26h0.128V229.333H294.184z M294.184,254.333v228.201
|
||||||
|
c-2.473,2.008-9.464,5.465-20.448,5.465s-17.975-3.456-20.448-5.465V253.132h40.896V254.333z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 4.6 KiB |
@@ -1,5 +1,5 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html <%= htmlWebpackPlugin.options.appCache ? 'manifest=/' + htmlWebpackPlugin.options.appCache : '' %> >
|
<html manifest="/">
|
||||||
<head>
|
<head>
|
||||||
<title>Coriolis EDCD Edition</title>
|
<title>Coriolis EDCD Edition</title>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
@@ -26,7 +26,8 @@
|
|||||||
<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>
|
||||||
@@ -38,7 +39,7 @@
|
|||||||
<% } %>
|
<% } %>
|
||||||
|
|
||||||
<!-- 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"]);
|
||||||
@@ -51,14 +52,16 @@
|
|||||||
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
|
<script src="//d2wy8f7a9ursnm.cloudfront.net/v4/bugsnag.min.js"></script>
|
||||||
src="//d2wy8f7a9ursnm.cloudfront.net/bugsnag-3.min.js"
|
<script src="//d2wy8f7a9ursnm.cloudfront.net/bugsnag-plugins/v1/bugsnag-react.min.js"></script>
|
||||||
data-apikey="2382691c622937f28f8fa82a1bfd797a"></script>
|
<script>
|
||||||
|
window.bugsnagClient = bugsnag('ba9fae819372850fb660755341fa6ef5', {appVersion: window.BUGSNAG_VERSION || undefined})
|
||||||
|
window.Bugsnag = window.bugsnagClient
|
||||||
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body style="background-color:#000;">
|
<body style="background-color:#000;">
|
||||||
<section id="coriolis"></section>
|
<section id="coriolis"></section>
|
||||||
|
|||||||
@@ -31,6 +31,50 @@ button {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.button-inline-menu {
|
||||||
|
white-space: nowrap;
|
||||||
|
line-height: 1.5em;
|
||||||
|
text-align: center;
|
||||||
|
margin: 0.5em 0;
|
||||||
|
padding-left: 5px;
|
||||||
|
border-top: 1px solid @primary-disabled;
|
||||||
|
border-bottom: 1px solid @primary-disabled;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
background: @primary-bg;
|
||||||
|
|
||||||
|
&.warning {
|
||||||
|
border-color: @warning-disabled;
|
||||||
|
color: @warning-disabled;
|
||||||
|
stroke: @warning-disabled;
|
||||||
|
|
||||||
|
.no-touch &:hover {
|
||||||
|
border-color: @warning;
|
||||||
|
color: @warning;
|
||||||
|
stroke: @warning;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disabled, &.disabled:hover {
|
||||||
|
cursor: not-allowed;
|
||||||
|
border-color: @disabled;
|
||||||
|
color: @disabled;
|
||||||
|
stroke: @disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
border-color: @secondary;
|
||||||
|
color: @secondary;
|
||||||
|
stroke: @secondary;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: @primary;
|
||||||
|
color: @primary;
|
||||||
|
stroke: @primary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.button-lbl {
|
.button-lbl {
|
||||||
margin-left: 0.5em;
|
margin-left: 0.5em;
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
@secondary: #1FB0FF; // Light blue
|
@secondary: #1FB0FF; // Light blue
|
||||||
@warning: #FF3B00; // Dark Orange
|
@warning: #FF3B00; // Dark Orange
|
||||||
@disabled: #555; // Light grey
|
@disabled: #555; // Light grey
|
||||||
|
@success: #71a052; // Green
|
||||||
|
@purple: #800080; // Purple
|
||||||
@primary-disabled: darken(@primary, @disabledDarken);
|
@primary-disabled: darken(@primary, @disabledDarken);
|
||||||
@secondary-disabled: darken(@secondary, @disabledDarken);
|
@secondary-disabled: darken(@secondary, @disabledDarken);
|
||||||
@warning-disabled: darken(@warning, @disabledDarken);
|
@warning-disabled: darken(@warning, @disabledDarken);
|
||||||
@@ -18,6 +20,7 @@
|
|||||||
@bg: rgba(30,30,30,1);
|
@bg: rgba(30,30,30,1);
|
||||||
@bgBlack: #000;
|
@bgBlack: #000;
|
||||||
@primary-bg: fadeout(darken(@primary, 47%), 15%);
|
@primary-bg: fadeout(darken(@primary, 47%), 15%);
|
||||||
|
@alt-primary-bg: fadeout(darken(@primary, 42%), 15%); // Lighter brown background
|
||||||
@secondary-bg: fadeout(darken(@secondary, @bgDarken), @bgTransparency); // Brown background
|
@secondary-bg: fadeout(darken(@secondary, @bgDarken), @bgTransparency); // Brown background
|
||||||
@warning-bg: fadeout(darken(@warning, @bgDarken), @bgTransparency); // Dark Red
|
@warning-bg: fadeout(darken(@warning, @bgDarken), @bgTransparency); // Dark Red
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,20 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.view-changes {
|
||||||
|
position: fixed;
|
||||||
|
top: 3em;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 3em;
|
||||||
|
z-index: 3;
|
||||||
|
line-height: 3em;
|
||||||
|
text-align: center;
|
||||||
|
background-color: @bg;
|
||||||
|
color: @warning;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
header {
|
header {
|
||||||
background-color: @bg;
|
background-color: @bg;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ textarea {
|
|||||||
width:100%;
|
width:100%;
|
||||||
min-height: 10em;
|
min-height: 10em;
|
||||||
resize: vertical;
|
resize: vertical;
|
||||||
|
user-select: auto;
|
||||||
margin:2em 0;
|
margin:2em 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,13 +21,47 @@
|
|||||||
|
|
||||||
#overview {
|
#overview {
|
||||||
h1 {
|
h1 {
|
||||||
margin: 0;
|
margin: 0 1em 0.2em 0;
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin: 0.5em;
|
margin: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#requirements {
|
||||||
|
margin: 0;
|
||||||
|
float: left;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
div {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 1em 0.5em 0;
|
||||||
|
height: 1.6em;
|
||||||
|
line-height: 1.6em;
|
||||||
|
text-align: left;
|
||||||
|
vertical-align: middle;
|
||||||
|
cursor: default;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: left center;
|
||||||
|
background-size: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
.federation {
|
||||||
|
background-image: url('../images/Federation.svg');
|
||||||
|
padding-left: 1.985em + 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empire {
|
||||||
|
background-image: url('../images/Empire.svg');
|
||||||
|
padding-left: 2.750em + 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.horizons {
|
||||||
|
background-image: url('../images/surface_port_pm.svg');
|
||||||
|
padding-left: 1.415em + 0.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#summary {
|
#summary {
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
-webkit-overflow-scrolling: touch;
|
-webkit-overflow-scrolling: touch;
|
||||||
@@ -36,10 +70,32 @@
|
|||||||
padding: 0.5em 0.2em;
|
padding: 0.5em 0.2em;
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
|
|
||||||
#summaryTable {
|
.summaryTable {
|
||||||
.user-select-none();
|
.user-select-none();
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
|
|
||||||
|
& > thead.blue {
|
||||||
|
background-color: @secondary;
|
||||||
|
border-left: 1px solid @primary-bg;
|
||||||
|
color: @primary-bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > thead.green {
|
||||||
|
background-color: @success;
|
||||||
|
border-left: 1px solid @primary-bg;
|
||||||
|
color: @primary-bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > thead.purple {
|
||||||
|
background-color: @purple;
|
||||||
|
border-left: 1px solid @primary-bg;
|
||||||
|
color: @primary-bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
& thead th.bordered {
|
||||||
|
border-left: 1px solid @primary-bg;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,11 @@ select {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cmdr-select {
|
||||||
|
border: 1px solid @primary;
|
||||||
|
padding: 0.5em 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
.select {
|
.select {
|
||||||
color: @primary-disabled;
|
color: @primary-disabled;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|||||||
@@ -37,10 +37,48 @@
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modification-container {
|
||||||
|
@input-container-width: 75%;
|
||||||
|
td {
|
||||||
|
width: 100% - @input-container-width;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-container {
|
||||||
|
width: @input-container-width;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
width: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.unit-container {
|
||||||
|
width: 30px;
|
||||||
|
padding: 3px;
|
||||||
|
text-align: left;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-adjuster {
|
||||||
|
width: 100% - @input-container-width;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.cb {
|
.cb {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input.cb:focus {
|
||||||
|
border-color:#fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:disabled {
|
||||||
|
border-color: #888;
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
.l {
|
.l {
|
||||||
text-transform: capitalize;
|
text-transform: capitalize;
|
||||||
margin-right: 0.8em;
|
margin-right: 0.8em;
|
||||||
|
|||||||
@@ -54,6 +54,10 @@ tbody tr {
|
|||||||
.no-touch &.highlight:hover, .no-touch &.highlighted {
|
.no-touch &.highlight:hover, .no-touch &.highlighted {
|
||||||
background-color: @warning-bg;
|
background-color: @warning-bg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.alt {
|
||||||
|
background-color: @alt-primary-bg;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
td {
|
td {
|
||||||
|
|||||||
46
src/sw.js
Normal file
46
src/sw.js
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
console.log('Hello from sw.js');
|
||||||
|
|
||||||
|
if (workbox) {
|
||||||
|
workbox.skipWaiting();
|
||||||
|
workbox.clientsClaim();
|
||||||
|
console.log('Yay! Workbox is loaded 🎉');
|
||||||
|
workbox.routing.registerRoute(
|
||||||
|
new RegExp('https://fonts.(?:googleapis|gstatic).com/(.*)'),
|
||||||
|
workbox.strategies.cacheFirst({
|
||||||
|
cacheName: 'google-fonts',
|
||||||
|
plugins: [
|
||||||
|
new workbox.expiration.Plugin({
|
||||||
|
maxEntries: 30
|
||||||
|
}),
|
||||||
|
new workbox.cacheableResponse.Plugin({
|
||||||
|
statuses: [0, 200]
|
||||||
|
})
|
||||||
|
]
|
||||||
|
})
|
||||||
|
);
|
||||||
|
workbox.routing.registerRoute(
|
||||||
|
/\.(?:png|gif|jpg|jpeg|svg)$/,
|
||||||
|
workbox.strategies.cacheFirst({
|
||||||
|
cacheName: 'images',
|
||||||
|
plugins: [
|
||||||
|
new workbox.expiration.Plugin({
|
||||||
|
maxEntries: 60,
|
||||||
|
maxAgeSeconds: 30 * 24 * 60 * 60 // 30 Days
|
||||||
|
})
|
||||||
|
]
|
||||||
|
})
|
||||||
|
);
|
||||||
|
workbox.routing.registerRoute(
|
||||||
|
/\.(?:js|css)$/,
|
||||||
|
workbox.strategies.staleWhileRevalidate({
|
||||||
|
cacheName: 'static-resources'
|
||||||
|
})
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
workbox.googleAnalytics.initialize();
|
||||||
|
} catch (e) {
|
||||||
|
console.log('Probably an ad-blocker');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log('Boo! Workbox didn\'t load 😬');
|
||||||
|
}
|
||||||
@@ -1,28 +1,30 @@
|
|||||||
var path = require('path');
|
const path = require('path');
|
||||||
var exec = require('child_process').exec;
|
const exec = require('child_process').exec;
|
||||||
var webpack = require('webpack');
|
const webpack = require('webpack');
|
||||||
var pkgJson = require('./package');
|
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
var HtmlWebpackPlugin = require("html-webpack-plugin");
|
const ExtractTextPlugin = require('extract-text-webpack-plugin');
|
||||||
var ExtractTextPlugin = require("extract-text-webpack-plugin");
|
const WebpackNotifierPlugin = require('webpack-notifier');
|
||||||
|
const pkgJson = require('./package');
|
||||||
|
const buildDate = new Date();
|
||||||
function CopyDirPlugin(source, destination) {
|
function CopyDirPlugin(source, destination) {
|
||||||
this.source = source;
|
this.source = source;
|
||||||
this.destination = destination;
|
this.destination = destination;
|
||||||
}
|
}
|
||||||
|
|
||||||
CopyDirPlugin.prototype.apply = function(compiler) {
|
CopyDirPlugin.prototype.apply = function(compiler) {
|
||||||
compiler.plugin('done', function() {
|
compiler.plugin('done', () => {
|
||||||
console.log(compiler.outputPath, this.destination);
|
console.log(compiler.outputPath, this.destination);
|
||||||
exec('cp -r ' + this.source + ' ' + path.join(compiler.outputPath, this.destination));
|
exec('cp -r ' + this.source + ' ' + path.join(compiler.outputPath, this.destination));
|
||||||
}.bind(this));
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
devtool: 'eval',
|
devtool: 'source-map',
|
||||||
devServer: {
|
devServer: {
|
||||||
headers: { "Access-Control-Allow-Origin": "*" }
|
headers: { 'Access-Control-Allow-Origin': '*' }
|
||||||
},
|
},
|
||||||
entry: {
|
entry: {
|
||||||
app: [ 'webpack-dev-server/client?http://0.0.0.0:3300', 'webpack/hot/only-dev-server', path.join(__dirname, "src/app/index.js") ],
|
app: ['webpack-dev-server/client?http://0.0.0.0:3300', 'webpack/hot/only-dev-server', path.join(__dirname, 'src/app/index.js')],
|
||||||
lib: ['d3', 'react', 'react-dom', 'classnames', 'fbemitter', 'lz-string']
|
lib: ['d3', 'react', 'react-dom', 'classnames', 'fbemitter', 'lz-string']
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
@@ -37,28 +39,30 @@ module.exports = {
|
|||||||
plugins: [
|
plugins: [
|
||||||
new CopyDirPlugin(path.join(__dirname, 'src/.htaccess'), ''),
|
new CopyDirPlugin(path.join(__dirname, 'src/.htaccess'), ''),
|
||||||
new webpack.optimize.CommonsChunkPlugin({
|
new webpack.optimize.CommonsChunkPlugin({
|
||||||
name: 'lib',
|
name: 'lib',
|
||||||
filename: 'lib.js'
|
filename: 'lib.js'
|
||||||
}),
|
}),
|
||||||
new HtmlWebpackPlugin({
|
new HtmlWebpackPlugin({
|
||||||
inject: false,
|
inject: false,
|
||||||
template: path.join(__dirname, "src/index.ejs"),
|
template: path.join(__dirname, 'src/index.ejs'),
|
||||||
version: pkgJson.version,
|
version: pkgJson.version,
|
||||||
gapiKey: process.env.CORIOLIS_GAPI_KEY || '',
|
date: buildDate,
|
||||||
|
gapiKey: process.env.CORIOLIS_GAPI_KEY || ''
|
||||||
}),
|
}),
|
||||||
new ExtractTextPlugin({
|
new ExtractTextPlugin({
|
||||||
filename: 'app.css',
|
filename: 'app.css',
|
||||||
disable: false,
|
disable: false,
|
||||||
allChunks: true
|
allChunks: true
|
||||||
}),
|
}),
|
||||||
|
new WebpackNotifierPlugin({ alwaysNotify: true }),
|
||||||
new webpack.HotModuleReplacementPlugin(),
|
new webpack.HotModuleReplacementPlugin(),
|
||||||
new webpack.NoEmitOnErrorsPlugin()
|
new webpack.NoEmitOnErrorsPlugin()
|
||||||
],
|
],
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
{ test: /\.css$/, loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader'}) },
|
{ test: /\.css$/, loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader' }) },
|
||||||
{ test: /\.less$/, loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader!less-loader'}) },
|
{ test: /\.less$/, loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader!less-loader' }) },
|
||||||
{ test: /\.(js|jsx)$/, loaders: [ 'babel-loader' ], include: path.join(__dirname, 'src') },
|
{ test: /\.(js|jsx)$/, loaders: ['babel-loader'], include: path.join(__dirname, 'src') },
|
||||||
{ test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/font-woff' },
|
{ test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/font-woff' },
|
||||||
{ test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/font-woff' },
|
{ test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/font-woff' },
|
||||||
{ test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/octet-stream' },
|
{ test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/octet-stream' },
|
||||||
|
|||||||
@@ -1,24 +1,28 @@
|
|||||||
var path = require('path');
|
const path = require('path');
|
||||||
var exec = require('child_process').exec;
|
const exec = require('child_process').exec;
|
||||||
var webpack = require('webpack');
|
const webpack = require('webpack');
|
||||||
var pkgJson = require('./package');
|
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
var HtmlWebpackPlugin = require("html-webpack-plugin");
|
const ExtractTextPlugin = require('extract-text-webpack-plugin');
|
||||||
var ExtractTextPlugin = require("extract-text-webpack-plugin");
|
const { InjectManifest } = require('workbox-webpack-plugin');
|
||||||
var AppCachePlugin = require('appcache-webpack-plugin');
|
|
||||||
|
|
||||||
|
const { BugsnagSourceMapUploaderPlugin } = require('webpack-bugsnag-plugins');
|
||||||
|
const pkgJson = require('./package');
|
||||||
|
const buildDate = new Date();
|
||||||
function CopyDirPlugin(source, destination) {
|
function CopyDirPlugin(source, destination) {
|
||||||
this.source = source;
|
this.source = source;
|
||||||
this.destination = destination;
|
this.destination = destination;
|
||||||
}
|
}
|
||||||
|
|
||||||
CopyDirPlugin.prototype.apply = function(compiler) {
|
CopyDirPlugin.prototype.apply = function(compiler) {
|
||||||
compiler.plugin('done', function() {
|
compiler.plugin('done', () => {
|
||||||
console.log(compiler.outputPath, this.destination);
|
console.log(compiler.outputPath, this.destination);
|
||||||
exec('cp -r ' + this.source + ' ' + path.join(compiler.outputPath, this.destination));
|
exec('cp -r ' + this.source + ' ' + path.join(compiler.outputPath, this.destination));
|
||||||
}.bind(this));
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
cache: true,
|
cache: true,
|
||||||
|
devtool: 'source-map',
|
||||||
entry: {
|
entry: {
|
||||||
app: ['babel-polyfill', path.resolve(__dirname, 'src/app/index')],
|
app: ['babel-polyfill', path.resolve(__dirname, 'src/app/index')],
|
||||||
lib: ['d3', 'react', 'react-dom', 'classnames', 'fbemitter', 'lz-string']
|
lib: ['d3', 'react', 'react-dom', 'classnames', 'fbemitter', 'lz-string']
|
||||||
@@ -34,49 +38,54 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
new webpack.optimize.UglifyJsPlugin({
|
new webpack.optimize.UglifyJsPlugin({
|
||||||
'screw-ie8': true
|
'screw-ie8': true,
|
||||||
|
sourceMap: true
|
||||||
}),
|
}),
|
||||||
//new webpack.optimize.CommonsChunkPlugin({
|
// new webpack.optimize.CommonsChunkPlugin({
|
||||||
// name: 'lib',
|
// name: 'lib',
|
||||||
// filename: 'lib.[chunkhash:6].js'
|
// filename: 'lib.[chunkhash:6].js'
|
||||||
//}),
|
// }),
|
||||||
new HtmlWebpackPlugin({
|
new HtmlWebpackPlugin({
|
||||||
inject: false,
|
inject: false,
|
||||||
appCache: 'coriolis.appcache',
|
appCache: 'coriolis.appcache',
|
||||||
minify: {
|
minify: {
|
||||||
collapseBooleanAttributes: true,
|
collapseBooleanAttributes: true,
|
||||||
collapseWhitespace: true,
|
collapseWhitespace: true,
|
||||||
removeAttributeQuotes: true,
|
removeAttributeQuotes: true,
|
||||||
removeComments: true,
|
removeComments: true,
|
||||||
removeEmptyAttributes: true,
|
removeEmptyAttributes: true,
|
||||||
removeRedundantAttributes: true,
|
removeRedundantAttributes: true,
|
||||||
removeScriptTypeAttributes: true,
|
removeScriptTypeAttributes: true,
|
||||||
removeStyleLinkTypeAttributes: true
|
removeStyleLinkTypeAttributes: true
|
||||||
},
|
},
|
||||||
template: path.join(__dirname, "src/index.ejs"),
|
template: path.join(__dirname, 'src/index.ejs'),
|
||||||
uaTracking: process.env.CORIOLIS_UA_TRACKING || '',
|
uaTracking: process.env.CORIOLIS_UA_TRACKING || '',
|
||||||
gapiKey: process.env.CORIOLIS_GAPI_KEY || '',
|
gapiKey: process.env.CORIOLIS_GAPI_KEY || '',
|
||||||
version: pkgJson.version
|
date: buildDate,
|
||||||
|
version: pkgJson.version
|
||||||
}),
|
}),
|
||||||
new ExtractTextPlugin({
|
new ExtractTextPlugin({
|
||||||
filename: '[contenthash:6].css',
|
filename: '[contenthash:6].css',
|
||||||
disable: false,
|
disable: false,
|
||||||
allChunks: true
|
allChunks: true
|
||||||
|
}),
|
||||||
|
new BugsnagSourceMapUploaderPlugin({
|
||||||
|
apiKey: 'ba9fae819372850fb660755341fa6ef5',
|
||||||
|
appVersion: `${pkgJson.version}-${buildDate.toISOString()}`
|
||||||
}),
|
}),
|
||||||
new CopyDirPlugin(path.join(__dirname, 'src/schemas'), 'schemas'),
|
new CopyDirPlugin(path.join(__dirname, 'src/schemas'), 'schemas'),
|
||||||
new CopyDirPlugin(path.join(__dirname, 'src/images/logo/*'), ''),
|
new CopyDirPlugin(path.join(__dirname, 'src/images/logo/*'), ''),
|
||||||
new CopyDirPlugin(path.join(__dirname, 'src/.htaccess'), ''),
|
new CopyDirPlugin(path.join(__dirname, 'src/.htaccess'), ''),
|
||||||
new AppCachePlugin({
|
new InjectManifest({
|
||||||
network: ['*'],
|
swSrc: './src/sw.js',
|
||||||
settings: ['prefer-online'],
|
importWorkboxFrom: 'cdn',
|
||||||
exclude: ['index.html', /.*\.map$/],
|
swDest: 'service-worker.js'
|
||||||
output: 'coriolis.appcache'
|
}),
|
||||||
})
|
|
||||||
],
|
],
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
{ test: /\.css$/, loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader'}) },
|
{ test: /\.css$/, loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader' }) },
|
||||||
{ test: /\.less$/, loader: ExtractTextPlugin.extract({ fallback: 'style-loader',use: 'css-loader!less-loader'}) },
|
{ test: /\.less$/, loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader!less-loader' }) },
|
||||||
{ test: /\.(js|jsx)$/, loader: 'babel-loader?cacheDirectory=true', include: path.join(__dirname, 'src') },
|
{ test: /\.(js|jsx)$/, loader: 'babel-loader?cacheDirectory=true', include: path.join(__dirname, 'src') },
|
||||||
{ test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/font-woff' },
|
{ test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/font-woff' },
|
||||||
{ test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/font-woff' },
|
{ test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/font-woff' },
|
||||||
|
|||||||
Reference in New Issue
Block a user