mirror of
https://github.com/EDCD/coriolis.git
synced 2025-12-09 14:45:35 +00:00
Compare commits
284 Commits
v2.9.18
...
86b95981f1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
86b95981f1 | ||
|
|
fd009fe567 | ||
|
|
e28eccb6fb | ||
|
|
c19ca6648d | ||
|
|
de5ca7b5e6 | ||
|
|
8676deba7d | ||
|
|
d3ce8d4f7c | ||
|
|
0c3de95025 | ||
|
|
83f1f9aa2e | ||
|
|
dee14a5dee | ||
|
|
db13da95db | ||
|
|
cb08b10a63 | ||
|
|
189eb2b726 | ||
|
|
b9abf784f4 | ||
|
|
39287bc5d7 | ||
|
|
bcdd0c6044 | ||
|
|
f70455ce26 | ||
|
|
888f807a7b | ||
|
|
5040141096 | ||
|
|
46ba782911 | ||
|
|
524e204e01 | ||
|
|
a9753828c1 | ||
|
|
6d30a54925 | ||
|
|
7c58eb1cde | ||
|
|
4001e1e9ac | ||
|
|
0da00d38a4 | ||
|
|
1db6fe1a34 | ||
|
|
10b8bb95a9 | ||
|
|
8d9581900f | ||
|
|
2bb55d2c36 | ||
|
|
49c827b2c8 | ||
|
|
cf50537e3d | ||
|
|
804466f88a | ||
|
|
bded793374 | ||
|
|
fc918d893c | ||
|
|
5fe13b26a4 | ||
|
|
be1393994e | ||
|
|
dc6db31d43 | ||
|
|
840ce9f3e4 | ||
|
|
9674aa2367 | ||
|
|
d19b6b107f | ||
|
|
1b96c18ecb | ||
|
|
0f43c4d7eb | ||
|
|
4c70806a5a | ||
|
|
7de304bdbe | ||
|
|
34f9f28c16 | ||
|
|
13ec027772 | ||
|
|
3966f92454 | ||
|
|
38f72438dd | ||
|
|
0179382379 | ||
|
|
7f5c652f49 | ||
|
|
1f9b1e5d27 | ||
|
|
ebf4491901 | ||
|
|
d322a47592 | ||
|
|
06a58d22cb | ||
|
|
25d4520eee | ||
|
|
0087062468 | ||
|
|
14bb49a2bc | ||
|
|
ab671b0af5 | ||
|
|
304ddf9ea8 | ||
|
|
b3f320e69f | ||
|
|
3a63e08f80 | ||
|
|
a3feb42fd7 | ||
|
|
a77d991cf9 | ||
|
|
9ebe5dc786 | ||
|
|
baace95f83 | ||
|
|
fc5db94f9a | ||
|
|
c3b0e8d949 | ||
|
|
1b8c460876 | ||
|
|
67409a613b | ||
|
|
e4a826592f | ||
|
|
cee4c32551 | ||
|
|
081d8fb86a | ||
|
|
3dfd563d90 | ||
|
|
fd08cd219c | ||
|
|
6a15326d31 | ||
|
|
608ecc51b7 | ||
|
|
fcef26ebbb | ||
|
|
ba6d758ed5 | ||
|
|
43aa3e4e79 | ||
|
|
18f0e060a7 | ||
|
|
c7547e8baf | ||
|
|
ffff242abe | ||
|
|
b44c66b986 | ||
|
|
ae77ec6256 | ||
|
|
4f1e32b154 | ||
|
|
af37c2bfc5 | ||
|
|
5d4ab6f2ad | ||
|
|
0c9db53057 | ||
|
|
b689605ac2 | ||
|
|
baab91e371 | ||
|
|
70e69c7099 | ||
|
|
f4534fd3eb | ||
|
|
93594e1a65 | ||
|
|
b5e449ea54 | ||
|
|
0ff4b849aa | ||
|
|
b99e38043f | ||
|
|
28e3a59473 | ||
|
|
b20290fb60 | ||
|
|
2734beb6f8 | ||
|
|
345eec528c | ||
|
|
7a17e18a76 | ||
|
|
4697677457 | ||
|
|
7d8a5a1368 | ||
|
|
dd7402bd0e | ||
|
|
65592b0fc6 | ||
|
|
0ab66023a6 | ||
|
|
d6fad098ee | ||
|
|
1b5730d337 | ||
|
|
439b615b1b | ||
|
|
a8b30594dc | ||
|
|
9b6b1d328c | ||
|
|
ac2e2e4d69 | ||
|
|
3a5fb31860 | ||
|
|
c610eb8627 | ||
|
|
94980270c4 | ||
|
|
c685e002e3 | ||
|
|
1f665eed9e | ||
|
|
0c4fc1fd9a | ||
|
|
0fc033363e | ||
|
|
fb6e9538bc | ||
|
|
95b98fc4ed | ||
|
|
1840dafed0 | ||
|
|
1ad82b116c | ||
|
|
7bdd17504b | ||
|
|
2d820bb5d5 | ||
|
|
2e14512ed8 | ||
|
|
48ed583c6d | ||
|
|
dd444a17f3 | ||
|
|
2ea63c711e | ||
|
|
6d6d31db25 | ||
|
|
e9273dcb9b | ||
|
|
2bdc4562c6 | ||
|
|
9e8a5323e9 | ||
|
|
8e001063b3 | ||
|
|
dc88fab4c5 | ||
|
|
dfca917e50 | ||
|
|
ef7dfd6ca1 | ||
|
|
435c1b6d45 | ||
|
|
c5c9abe588 | ||
|
|
363735d36b | ||
|
|
2741e7701b | ||
|
|
3be442ea60 | ||
|
|
34cbeca201 | ||
|
|
b37e73ead6 | ||
|
|
ee775521d6 | ||
|
|
5f84aaef1b | ||
|
|
99ac58d999 | ||
|
|
f128a1e87d | ||
|
|
8c0768b451 | ||
|
|
319307136c | ||
|
|
a498452943 | ||
|
|
4b854b8305 | ||
|
|
b400db8216 | ||
|
|
fb811faf5e | ||
|
|
deeb525433 | ||
|
|
1cb88115f6 | ||
|
|
a181791500 | ||
|
|
94eec120da | ||
|
|
48092d4395 | ||
|
|
2457c30b94 | ||
|
|
593f069806 | ||
|
|
a073692632 | ||
|
|
7752d5c9db | ||
|
|
544e5acaef | ||
|
|
9ab35bbaf9 | ||
|
|
98782da200 | ||
|
|
2936364934 | ||
|
|
01e1609a9f | ||
|
|
f85a03a9ae | ||
|
|
2703c2aa23 | ||
|
|
954921c231 | ||
|
|
8bed35a8ba | ||
|
|
9f4ae60577 | ||
|
|
ee3c50e27d | ||
|
|
03020743b3 | ||
|
|
001fed67b7 | ||
|
|
3894915740 | ||
|
|
68fd13e8dc | ||
|
|
fdf16cd959 | ||
|
|
d916c67fe0 | ||
|
|
d8a8e224f4 | ||
|
|
e1c115747c | ||
|
|
e9b6d71606 | ||
|
|
e03e249d2f | ||
|
|
0cfb0b6878 | ||
|
|
600df162aa | ||
|
|
94141aa3c5 | ||
|
|
aca90d7077 | ||
|
|
a66fa8e83f | ||
|
|
194db07057 | ||
|
|
307886d4ae | ||
|
|
bbba048129 | ||
|
|
222173b388 | ||
|
|
ec0d05e081 | ||
|
|
b3be0bd639 | ||
|
|
529d80682c | ||
|
|
934de01803 | ||
|
|
3367580d78 | ||
|
|
fbf59219d0 | ||
|
|
77401a3b3f | ||
|
|
7c587c29aa | ||
|
|
2295dccd82 | ||
|
|
cc4ad6d132 | ||
|
|
8a2d27290a | ||
|
|
eda61a8e06 | ||
|
|
3987c4e681 | ||
|
|
71b90eb6f4 | ||
|
|
4e891f382c | ||
|
|
3d3f9e44b5 | ||
|
|
dd7a133caa | ||
|
|
ae247c4812 | ||
|
|
6c932f96a6 | ||
|
|
0ea25692d3 | ||
|
|
e129e1da39 | ||
|
|
8acd32b0fc | ||
|
|
8e5dd9fb8d | ||
|
|
97ce2828e0 | ||
|
|
f8f99a5aaa | ||
|
|
70cfa58896 | ||
|
|
6e79ced51e | ||
|
|
56571f9c1f | ||
|
|
0b10cac85c | ||
|
|
34c04a6354 | ||
|
|
4e337c4ca1 | ||
|
|
5048b7e094 | ||
|
|
27fbc1ad66 | ||
|
|
4ab376d9ed | ||
|
|
dfffc3a268 | ||
|
|
b59fa15e00 | ||
|
|
12bca4c44e | ||
|
|
2858ef3e93 | ||
|
|
7d99471f89 | ||
|
|
a2ab708ac9 | ||
|
|
a34a9c355f | ||
|
|
3215b3942d | ||
|
|
557c0afd9b | ||
|
|
d52365a204 | ||
|
|
14b2a14e58 | ||
|
|
7f24904f77 | ||
|
|
da07790594 | ||
|
|
5008c7cd74 | ||
|
|
a778b1b6e1 | ||
|
|
bd9771f9ba | ||
|
|
600c244f9b | ||
|
|
a599b1a076 | ||
|
|
3e0a5e22b1 | ||
|
|
3a6ac818c2 | ||
|
|
6f077d4c41 | ||
|
|
9c767c928c | ||
|
|
515f4ad3da | ||
|
|
4fcf074595 | ||
|
|
e5f8153a34 | ||
|
|
571854a11c | ||
|
|
1f22f249a1 | ||
|
|
718ac0a514 | ||
|
|
8f089cb1ee | ||
|
|
d19a7276dd | ||
|
|
10fffe67fc | ||
|
|
f0bf8e8ce2 | ||
|
|
598cf8d677 | ||
|
|
90f03de3fe | ||
|
|
e0766f4424 | ||
|
|
28a90768e4 | ||
|
|
f3d917ccbe | ||
|
|
7e5d52385d | ||
|
|
4368015dc0 | ||
|
|
1201da1811 | ||
|
|
d195b568b0 | ||
|
|
c9866c146b | ||
|
|
5d52809d0d | ||
|
|
8f0cca4fd9 | ||
|
|
e46bb425fe | ||
|
|
06dc110025 | ||
|
|
e9c34c636a | ||
|
|
59d38cbd33 | ||
|
|
51f5188efc | ||
|
|
be8934da80 | ||
|
|
18d78b3089 | ||
|
|
b1ff4e84f7 | ||
|
|
bed2ede701 | ||
|
|
124bd62d2c | ||
|
|
975846f4ab | ||
|
|
3f73f9be10 |
33
.babelrc
33
.babelrc
@@ -1,3 +1,34 @@
|
||||
{
|
||||
"presets": ["env", "react", "stage-0"]
|
||||
"presets": [
|
||||
["@babel/preset-env", {"modules": "commonjs"}],
|
||||
"@babel/preset-react"
|
||||
],
|
||||
"plugins": [
|
||||
"@babel/plugin-syntax-dynamic-import",
|
||||
"@babel/plugin-syntax-import-meta",
|
||||
["@babel/plugin-proposal-class-properties", { "loose": true }],
|
||||
"@babel/plugin-proposal-json-strings",
|
||||
[
|
||||
"@babel/plugin-proposal-decorators",
|
||||
{
|
||||
"legacy": true
|
||||
}
|
||||
],
|
||||
"@babel/plugin-proposal-function-sent",
|
||||
"@babel/plugin-proposal-export-namespace-from",
|
||||
"@babel/plugin-proposal-numeric-separator",
|
||||
"@babel/plugin-proposal-throw-expressions",
|
||||
"@babel/plugin-proposal-export-default-from",
|
||||
"@babel/plugin-proposal-logical-assignment-operators",
|
||||
"@babel/plugin-proposal-optional-chaining",
|
||||
[
|
||||
"@babel/plugin-proposal-pipeline-operator",
|
||||
{
|
||||
"proposal": "minimal"
|
||||
}
|
||||
],
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator",
|
||||
"@babel/plugin-proposal-do-expressions",
|
||||
"@babel/plugin-proposal-function-bind"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
node_modules
|
||||
npm-debug.log
|
||||
@@ -1,37 +0,0 @@
|
||||
### 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;"]
|
||||
@@ -1,34 +0,0 @@
|
||||
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
|
||||
@@ -1,45 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,7 @@
|
||||
"title": "Coriolis",
|
||||
"description": "Coriolis Shipyard for Elite Dangerous",
|
||||
"repository": "https://github.com/EDCD/coriolis",
|
||||
"site": "https://coriolis.edcd.io",
|
||||
"site": "https://coriolis.io",
|
||||
"author": "https://github.com/edcd",
|
||||
"image": "./src/images/logo/192x192.png"
|
||||
}
|
||||
@@ -81,7 +81,7 @@
|
||||
"title": "Coriolis",
|
||||
"description": "Coriolis Shipyard for Elite Dangerous",
|
||||
"repository": "https://github.com/EDCD/coriolis",
|
||||
"site": "https://coriolis.edcd.io",
|
||||
"site": "https://coriolis.io",
|
||||
"author": "https://github.com/edcd",
|
||||
"image": "./src/images/logo/192x192.png"
|
||||
}
|
||||
@@ -100,4 +100,4 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
16
.travis.yml
16
.travis.yml
@@ -1,16 +0,0 @@
|
||||
language: node_js
|
||||
notifications:
|
||||
email: false
|
||||
sudo: false
|
||||
node_js:
|
||||
- "4.8.1"
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
|
||||
before_install:
|
||||
- git clone https://github.com/EDCD/coriolis-data.git ../coriolis-data
|
||||
|
||||
script:
|
||||
- npm run lint
|
||||
- npm test
|
||||
24
LICENSE.md
Normal file
24
LICENSE.md
Normal file
@@ -0,0 +1,24 @@
|
||||
All Data and [associated JSON](https://github.com/EDCD/coriolis-data) files are intellectual property and copyright of Frontier Developments plc ('Frontier', 'Frontier Developments') and are subject to their
|
||||
[terms and conditions](https://www.frontierstore.net/terms-and-conditions/).
|
||||
|
||||
The code (Javascript, CSS, HTML, and SVG files only) specificially for Coriolis.io is released under the MIT License.
|
||||
|
||||
Copyright (c) 2015 Coriolis.io, Colin McLeod
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software (Javascript, CSS, HTML, and SVG files only), and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
62
README.md
62
README.md
@@ -1,4 +1,4 @@
|
||||
 [](https://travis-ci.org/EDCD/coriolis) [](https://discord.gg/0uwCh6R62aPRjk9w)
|
||||
[](https://discord.gg/0uwCh6R62aPRjk9w)
|
||||
|
||||
## About
|
||||
|
||||
@@ -8,51 +8,41 @@ Coriolis was created using assets and imagery from Elite: Dangerous, with the pe
|
||||
|
||||
## Contributing
|
||||
|
||||
Please [submit issues](https://github.com/EDCD/coriolis/issues), or better yet [pull requests](https://github.com/EDCD/coriolis/pulls) for any corrections or additions to the database or the code.
|
||||
|
||||
### Translations
|
||||
|
||||
Please use the OneSky translation site to suggest new translations: http://edcd-coriolis.oneskyapp.com
|
||||
These will be merged regularly by the project manager.
|
||||
|
||||
### Feature Requests, Suggestions & Bugs
|
||||
|
||||
Chat to us on [Discord](https://discord.gg/0uwCh6R62aPRjk9w)!
|
||||
- [Submit issues](https://github.com/EDCD/coriolis/issues)
|
||||
- [Submit pull requests](https://github.com/EDCD/coriolis/pulls) targetting `develop` branch
|
||||
- Chat to us on [Discord](https://discord.gg/0uwCh6R62aPRjk9w)!
|
||||
|
||||
## Development
|
||||
|
||||
See the [Developer's Guide](https://github.com/EDCD/coriolis/wiki/Developing-for-Coriolis) in the wiki.
|
||||
To get a local instance of coriolis running, perform the following steps in a shell:
|
||||
```sh
|
||||
> git clone https://github.com/EDCD/coriolis.git
|
||||
> git clone https://github.com/EDCD/coriolis-data.git
|
||||
> cd ./coriolis-data
|
||||
> npm install
|
||||
> cd ../coriolis
|
||||
> npm install
|
||||
> npm start
|
||||
```
|
||||
|
||||
Also see [the documentation site.](https://coriolis.willb.info/)
|
||||
You will then have a development server running on `localhost:3300`.
|
||||
|
||||
### Ship and Module Database
|
||||
|
||||
See the [Data wiki](https://github.com/cmmcleod/coriolis-data/wiki) for details on structure, etc.
|
||||
|
||||
## Deployment
|
||||
|
||||
## License
|
||||
Follow the steps for [Development](#development) as above, but instead
|
||||
of `npm start` you'll want to:
|
||||
|
||||
All Data and [associated JSON](https://github.com/EDCD/coriolis-data) files are intellectual property and copyright of Frontier Developments plc ('Frontier', 'Frontier Developments') and are subject to their
|
||||
[terms and conditions](https://www.frontierstore.net/terms-and-conditions/).
|
||||
```sh
|
||||
> npm run build
|
||||
```
|
||||
|
||||
The code (Javascript, CSS, HTML, and SVG files only) specificially for Coriolis.io is released under the MIT License.
|
||||
this will result in a `build/` directory being created containing all the necessary files.
|
||||
|
||||
Copyright (c) 2015 Coriolis.io, Colin McLeod
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software (Javascript, CSS, HTML, and SVG files only), and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
After this you need to serve the files in some manner.
|
||||
Either configure your webserver to make the actual `build/` directory
|
||||
visible on the web, or alternatively copy it to somewhere to serve it
|
||||
from.
|
||||
|
||||
@@ -1,50 +1,50 @@
|
||||
{
|
||||
"type_6_transporter": {
|
||||
"Cargo": "A0p0tdFal8d8s8f4-----04040303430101.Iw1/kA==.Aw1/kA==.",
|
||||
"Miner": "A0p5tdFal8d8s8f42l2l---040403451q0101.Iw1/kA==.Aw1/kA==.",
|
||||
"Hopper": "A0p0tdFal8d0s8f41717---030302024300-.Iw1/kA==.Aw1/kA==."
|
||||
"Cargo": "A0p0tdFal8d8s8f4-----04040303430101-.Iw18UA==.Aw18UA==.",
|
||||
"Miner": "A0p5tdFal8d8s8f42l2l---040403451q0101-.Iw18UA==.Aw18UA==.",
|
||||
"Hopper": "A0p0tdFal8d0s8f41717---030302024300--.Iw18UA==.Aw18UA==."
|
||||
},
|
||||
"type_7_transport": {
|
||||
"Cargo": "A0p0tiFfliddsdf5--------0505040403480101.Iw18aQ==.Aw18aQ==.",
|
||||
"Miner": "A0pdtiFflid8sdf5--2l2l----0505041v03450000.Iw18aQ==.Aw18aQ==."
|
||||
"Cargo": "A0p0tiFfliddsdf5--------0505040403480101--.Iw18eQ==.Aw18eQ==.",
|
||||
"Miner": "A0pdtiFflid8sdf5--2l2l----0505041v03450000--.Iw18eQ==.Aw18eQ==."
|
||||
},
|
||||
"federal_dropship": {
|
||||
"Cargo": "A0pdtiFflnddsif4-1717------05040448--020201.Iw18eQ==.Aw18eQ==."
|
||||
"Cargo": "A0pdtiFflnddsif4-1717------05040448--020201-.Iw18RQ==.Aw18RQ==."
|
||||
},
|
||||
"asp": {
|
||||
"Miner": "A2pftfFflidfskf50s0s24242l2l---04054a1q02022o27.Iw18WQ==.Aw18WQ==."
|
||||
"Miner": "A2pftfFflidfskf50s0s24242l2l---04054a1q02022o27-.Iw18eQ==.Aw18eQ==."
|
||||
},
|
||||
"imperial_clipper": {
|
||||
"Cargo": "A0p5tiFflndisnf4--0s0s----0605450302020101.Iw18aQ==.Aw18aQ==.",
|
||||
"Dream": "A2pktkFflndpskf40v0v0s0s0404040n4k5n5d2b29292o-.AwRj4yWU1I==.CwBhCYy6YRigzLIA.",
|
||||
"Current": "A0patkFflndfskf4----------------.AwRj4yWU1I==.CwBhCYy6YRigzLIA."
|
||||
"Cargo": "A0p5tiFflndisnf4--0s0s----0605450302020101-.Iw18WQ==.Aw18WQ==.",
|
||||
"Dream": "A2pktkFflndpskf40v0v0s0s0404040n4k5n5d2b29292o--.AwRj4yWU1Yg=.CwBhCYy6YRigzPIA.",
|
||||
"Current": "A0patkFflndfskf4-----------------.AwRj4yWU1Yg=.CwBhCYy6YRigzPIA."
|
||||
},
|
||||
"type_9_heavy": {
|
||||
"Current": "A0patsFklndnsif6---------0706054a0303020224.AwRj4yoo.EwBhEYy6dsg=."
|
||||
"Current": "A0patsFklndnsif6---------0706054a0303020224--.AwRj4yo5iA==.EwBhEYy6d6g=."
|
||||
},
|
||||
"python": {
|
||||
"Cargo": "A0patnFflidsssf5---------050505040448020201.Iw18eQ==.Aw18eQ==.",
|
||||
"Miner": "A0pktkFflidpspf50v0v0v2m2m0404--050505Ce4a1v02022o.Iw18eQ==.IwBhBYy6dkCYg===.",
|
||||
"Dream": "A2pptkFfliduspf50v0v0v27270404040m5n5n4f2d2d032t0201.Iw1+gDBxA===.EwBhEYy6e0WEA===.",
|
||||
"Missile": "A0pttoFjljdystf52f2g2d2ePh----04044j03---002h.Iw18eQ==.Aw18eQ==."
|
||||
"Cargo": "A0patnFflidsssf5---------050505040448020201-.Iw18eAMQ.Aw18RQ==.",
|
||||
"Miner": "A0pktkFflidpspf50v0v0v2m2m0404--050505Ce4a1v02022o-.Iw18eAMQ.IwBhBYy6dkCYRA==.",
|
||||
"Dream": "A2pptkFfliduspf50v0v0v27270404040m5n5n4f2d2d032t0201-.Iw1+gDByUA==.EwBhEYy6e0VEA===.",
|
||||
"Missile": "A0pttoFjljdystf52f2g2d2ePh----04044j03---00--.Iw18eAMQ.Aw18RQ==."
|
||||
},
|
||||
"anaconda": {
|
||||
"Dream": "A4putpFklndzsuf52c0o0o0o1m1m0q0q0404040l0b0100004k5n5n112d2d04-0303326b.AwRj4yo5dyg=.MwBhCYy6duvARiA=.",
|
||||
"Cargo": "A0patnFklndnsxf5----------------06050505040404-45030301.Iw18ZVA=.Aw18ZVA=.",
|
||||
"Current": "A0patnFklndksxf5----------------06050505040404-03034524.Iw18ZVA=.Aw18ZVA=.",
|
||||
"Explorer": "A0patnFklndksxf5--------0202------f7050505040s37-2f2i4524.AwRj4yVKJ9hA.AwhMIyumQRhEA===.",
|
||||
"Test": "A4putkFklkdzsuf52c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04---0303326b.Iw18ZVA=.Aw18ZVA=."
|
||||
"Dream": "A4putpFklndzsuf52c0o0o0o1m1m0q0q0404040l0b0100004k5n5n112d2d04-0303326b-.AwRj4yo5dzhA.MwBhCYy6duvARhEA.",
|
||||
"Cargo": "A0patnFklndnsxf5----------------06050505040404-45030301-.Iw18ZUAxA===.Aw18ZXEA.",
|
||||
"Current": "A0patnFklndksxf5----------------06050505040404-03034524-.Iw18ZUAxA===.Aw18ZXEA.",
|
||||
"Explorer": "A0patnFklndksxf5--------0202------f7050505040s37--2i4524-.AwRj4yVKJ9jCA===.AwhMIyumQRgkA===.",
|
||||
"Test": "A4putkFklkdzsuf52c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04---0303326b-.Iw18ZUAxA===.Aw18ZXEA."
|
||||
},
|
||||
"diamondback_explorer": {
|
||||
"Explorer": "A0p0tdFfldddsdf5---0202--320p432i2f-.AwRj4zTYg===.AwiMIyoo."
|
||||
"Explorer": "A0p0tdFfldddsdf5---0202--320p432i----.AwRj4zTZaA==.AwiMIyqo."
|
||||
},
|
||||
"vulture": {
|
||||
"Bounty Hunter": "A3patcFalddksff31e1e0404-0l4a-5d27662j.AwRj4z2I.MwBhBYy6oJmAjLIA."
|
||||
"Bounty Hunter": "A3patcFalddksff31e1e0404-0l4a-5d27662j--.AwRj4z2Gg===.MwBhBYy6oJmAjLMQ."
|
||||
},
|
||||
"fer_de_lance": {
|
||||
"Attack": "A2pfthFalidpsff31r0s0s0s0s000404-04-4a-5d27-.Iw18aQ==.CwBhrSu8EZyA."
|
||||
"Attack": "A2pfthFalidpsff31r0s0s0s0s000404-04-4a-5d27--.Iw18aAMQ.CwBhrSu8EZxEA===."
|
||||
},
|
||||
"eagle": {
|
||||
"Figther": "A4p0t5F5l3d5s5f20p0p24-4053-2j-.Iw18kA==.Aw18kA==."
|
||||
"Figther": "A4p0t5F5l3d5s5f20p0p24-4053-2j---.Iw18gDJQ.Aw19kA==."
|
||||
}
|
||||
}
|
||||
|
||||
366
__tests__/fixtures/slef-multiple-builds.json
Normal file
366
__tests__/fixtures/slef-multiple-builds.json
Normal file
@@ -0,0 +1,366 @@
|
||||
[
|
||||
{
|
||||
"header": {
|
||||
"appName": "Inara",
|
||||
"appVersion": "1.0",
|
||||
"appURL": "https:\/\/inara.cz\/cmdr-fleet\/123\/123\/",
|
||||
"appCustomProperties": {
|
||||
"inaraCommanderID": 123,
|
||||
"inaraShipID": 123
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"Ship": "krait_mkii",
|
||||
"ShipID": 7,
|
||||
"ShipName": "pancake hammer",
|
||||
"ShipIdent": "PH-01",
|
||||
"HullValue": 44160710,
|
||||
"ModulesValue": 111274094,
|
||||
"Rebuy": 7771743,
|
||||
"Modules": [
|
||||
{
|
||||
"Slot": "largehardpoint1",
|
||||
"Item": "hpt_mininglaser_fixed_small",
|
||||
"On": true
|
||||
},
|
||||
{
|
||||
"Slot": "largehardpoint2",
|
||||
"Item": "hpt_cannon_gimbal_large",
|
||||
"On": true,
|
||||
"Engineering": {
|
||||
"BlueprintName": "weapon_overcharged",
|
||||
"Level": 2,
|
||||
"Quality": 1,
|
||||
"ExperimentalEffect": "special_auto_loader"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Slot": "largehardpoint3",
|
||||
"Item": "hpt_cannon_gimbal_large",
|
||||
"On": true,
|
||||
"Engineering": {
|
||||
"BlueprintName": "weapon_overcharged",
|
||||
"Level": 2,
|
||||
"Quality": 1,
|
||||
"ExperimentalEffect": "special_auto_loader"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Slot": "mediumhardpoint1",
|
||||
"Item": "hpt_basicmissilerack_fixed_medium",
|
||||
"On": true,
|
||||
"Engineering": {
|
||||
"BlueprintName": "weapon_highcapacity",
|
||||
"Level": 5,
|
||||
"Quality": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"Slot": "mediumhardpoint2",
|
||||
"Item": "hpt_basicmissilerack_fixed_medium",
|
||||
"On": true
|
||||
},
|
||||
{
|
||||
"Slot": "tinyhardpoint1",
|
||||
"Item": "hpt_heatsinklauncher_turret_tiny",
|
||||
"On": true
|
||||
},
|
||||
{
|
||||
"Slot": "tinyhardpoint2",
|
||||
"Item": "hpt_cloudscanner_size0_class3",
|
||||
"On": true
|
||||
},
|
||||
{
|
||||
"Slot": "tinyhardpoint3",
|
||||
"Item": "hpt_shieldbooster_size0_class5",
|
||||
"On": true
|
||||
},
|
||||
{
|
||||
"Slot": "tinyhardpoint4",
|
||||
"Item": "hpt_shieldbooster_size0_class5",
|
||||
"On": true,
|
||||
"Priority": 1
|
||||
},
|
||||
{
|
||||
"Slot": "slot01_size6",
|
||||
"Item": "int_cargorack_size6_class1",
|
||||
"On": true,
|
||||
"Priority": 1
|
||||
},
|
||||
{
|
||||
"Slot": "slot02_size6",
|
||||
"Item": "int_cargorack_size6_class1",
|
||||
"On": true,
|
||||
"Priority": 1
|
||||
},
|
||||
{
|
||||
"Slot": "slot03_size5",
|
||||
"Item": "int_guardianfsdbooster_size5",
|
||||
"On": true
|
||||
},
|
||||
{
|
||||
"Slot": "slot04_size5",
|
||||
"Item": "int_fighterbay_size5_class1",
|
||||
"On": true
|
||||
},
|
||||
{
|
||||
"Slot": "slot05_size4",
|
||||
"Item": "int_shieldgenerator_size4_class5",
|
||||
"On": true
|
||||
},
|
||||
{
|
||||
"Slot": "slot06_size3",
|
||||
"Item": "int_dronecontrol_collection_size3_class4",
|
||||
"On": true
|
||||
},
|
||||
{
|
||||
"Slot": "slot07_size3",
|
||||
"Item": "int_dronecontrol_collection_size3_class4",
|
||||
"On": true
|
||||
},
|
||||
{
|
||||
"Slot": "slot08_size2",
|
||||
"Item": "int_refinery_size2_class2",
|
||||
"On": true
|
||||
},
|
||||
{
|
||||
"Slot": "slot09_size1",
|
||||
"Item": "int_dronecontrol_prospector_size1_class4",
|
||||
"On": true
|
||||
},
|
||||
{
|
||||
"Slot": "powerplant",
|
||||
"Item": "int_powerplant_size7_class5",
|
||||
"On": true,
|
||||
"Priority": 1
|
||||
},
|
||||
{
|
||||
"Slot": "mainengines",
|
||||
"Item": "int_engine_size6_class5",
|
||||
"On": true
|
||||
},
|
||||
{
|
||||
"Slot": "frameshiftdrive",
|
||||
"Item": "int_hyperdrive_size5_class5",
|
||||
"On": true,
|
||||
"Engineering": {
|
||||
"BlueprintName": "fsd_longrange",
|
||||
"Level": 2,
|
||||
"Quality": 0.861
|
||||
}
|
||||
},
|
||||
{
|
||||
"Slot": "lifesupport",
|
||||
"Item": "int_lifesupport_size4_class2",
|
||||
"On": true,
|
||||
"Priority": 3
|
||||
},
|
||||
{
|
||||
"Slot": "powerdistributor",
|
||||
"Item": "int_powerdistributor_size7_class5",
|
||||
"On": true
|
||||
},
|
||||
{
|
||||
"Slot": "radar",
|
||||
"Item": "int_sensors_size6_class2",
|
||||
"On": true
|
||||
},
|
||||
{
|
||||
"Slot": "fueltank",
|
||||
"Item": "int_fueltank_size5_class3",
|
||||
"On": true,
|
||||
"Priority": 1
|
||||
},
|
||||
{
|
||||
"Slot": "armour",
|
||||
"Item": "krait_mkii_armour_grade3",
|
||||
"On": true,
|
||||
"Priority": 1,
|
||||
"Engineering": {
|
||||
"BlueprintName": "armour_heavyduty",
|
||||
"Level": 5,
|
||||
"Quality": 1
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"header": {
|
||||
"appName": "Inara",
|
||||
"appVersion": "1.0",
|
||||
"appURL": "https:\/\/inara.cz\/cmdr-fleet\/123\/123\/",
|
||||
"appCustomProperties": {
|
||||
"inaraCommanderID": 123,
|
||||
"inaraShipID": 123
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"Ship": "diamondbackxl",
|
||||
"ShipID": 11,
|
||||
"ShipName": "star Hopper",
|
||||
"ShipIdent": "PH-02",
|
||||
"HullValue": 1615649,
|
||||
"ModulesValue": 16981039,
|
||||
"Rebuy": 929837,
|
||||
"Modules": [
|
||||
{
|
||||
"Slot": "tinyhardpoint1",
|
||||
"Item": "hpt_heatsinklauncher_turret_tiny",
|
||||
"On": true,
|
||||
"Value": 3072
|
||||
},
|
||||
{
|
||||
"Slot": "slot01_size4",
|
||||
"Item": "int_fuelscoop_size4_class5",
|
||||
"On": true,
|
||||
"Priority": 3,
|
||||
"Value": 2862364
|
||||
},
|
||||
{
|
||||
"Slot": "slot02_size4",
|
||||
"Item": "int_guardianfsdbooster_size4",
|
||||
"On": true,
|
||||
"Value": 2847499
|
||||
},
|
||||
{
|
||||
"Slot": "slot03_size3",
|
||||
"Item": "int_shieldgenerator_size3_class2",
|
||||
"On": true,
|
||||
"Value": 18812,
|
||||
"Engineering": {
|
||||
"BlueprintName": "shieldgenerator_thermic",
|
||||
"Level": 3,
|
||||
"Quality": 1,
|
||||
"ExperimentalEffect": "special_shield_health"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Slot": "slot04_size3",
|
||||
"Item": "int_repairer_size3_class5",
|
||||
"On": true,
|
||||
"Value": 2302911
|
||||
},
|
||||
{
|
||||
"Slot": "slot05_size2",
|
||||
"Item": "int_buggybay_size2_class2",
|
||||
"On": true,
|
||||
"Priority": 3,
|
||||
"Value": 21600
|
||||
},
|
||||
{
|
||||
"Slot": "slot06_size2",
|
||||
"Item": "int_cargorack_size2_class1",
|
||||
"On": true,
|
||||
"Priority": 1,
|
||||
"Value": 2852
|
||||
},
|
||||
{
|
||||
"Slot": "slot07_size1",
|
||||
"Item": "int_supercruiseassist",
|
||||
"On": true,
|
||||
"Priority": 3,
|
||||
"Value": 9121
|
||||
},
|
||||
{
|
||||
"Slot": "slot08_size1",
|
||||
"Item": "int_detailedsurfacescanner_tiny",
|
||||
"On": true,
|
||||
"Value": 250000,
|
||||
"Engineering": {
|
||||
"BlueprintName": "sensor_expanded",
|
||||
"Level": 5,
|
||||
"Quality": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"Slot": "powerplant",
|
||||
"Item": "int_powerplant_size4_class5",
|
||||
"On": true,
|
||||
"Priority": 1,
|
||||
"Value": 1441233,
|
||||
"Engineering": {
|
||||
"BlueprintName": "powerplant_boosted",
|
||||
"Level": 1,
|
||||
"Quality": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"Slot": "mainengines",
|
||||
"Item": "int_engine_size4_class5",
|
||||
"On": true,
|
||||
"Value": 1610080,
|
||||
"Engineering": {
|
||||
"BlueprintName": "engine_dirty",
|
||||
"Level": 5,
|
||||
"Quality": 1,
|
||||
"ExperimentalEffect": "special_engine_lightweight"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Slot": "frameshiftdrive",
|
||||
"Item": "int_hyperdrive_size5_class5",
|
||||
"On": true,
|
||||
"Value": 5103953,
|
||||
"Engineering": {
|
||||
"BlueprintName": "fsd_longrange",
|
||||
"Level": 5,
|
||||
"Quality": 1,
|
||||
"ExperimentalEffect": "special_fsd_lightweight"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Slot": "lifesupport",
|
||||
"Item": "int_lifesupport_size3_class2",
|
||||
"On": true,
|
||||
"Value": 10133,
|
||||
"Engineering": {
|
||||
"BlueprintName": "misc_lightweight",
|
||||
"Level": 3,
|
||||
"Quality": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"Slot": "powerdistributor",
|
||||
"Item": "int_powerdistributor_size4_class5",
|
||||
"On": true,
|
||||
"Value": 389022,
|
||||
"Engineering": {
|
||||
"BlueprintName": "powerdistributor_highfrequency",
|
||||
"Level": 4,
|
||||
"Quality": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"Slot": "radar",
|
||||
"Item": "int_sensors_size3_class2",
|
||||
"On": true,
|
||||
"Value": 10133,
|
||||
"Engineering": {
|
||||
"BlueprintName": "sensor_lightweight",
|
||||
"Level": 5,
|
||||
"Quality": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"Slot": "fueltank",
|
||||
"Item": "int_fueltank_size5_class3",
|
||||
"On": true,
|
||||
"Priority": 1,
|
||||
"Value": 97754
|
||||
},
|
||||
{
|
||||
"Slot": "armour",
|
||||
"Item": "diamondbackxl_armour_grade1",
|
||||
"On": true,
|
||||
"Priority": 1,
|
||||
"Engineering": {
|
||||
"BlueprintName": "armour_heavyduty",
|
||||
"Level": 5,
|
||||
"Quality": 1
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
8
__tests__/fixtures/slef-multiple-expected-builds.json
Normal file
8
__tests__/fixtures/slef-multiple-expected-builds.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"krait_mkii": {
|
||||
"Imported pancake hammer": "A2pptkFflidussf52l1o1o2g2g020g040405051Ofr45C9C91oP3.Iw18eQ==.AwRgzKIkA===."
|
||||
},
|
||||
"diamondback_explorer": {
|
||||
"Imported star Hopper": "A0pataFflddfsdf5---02---321P430iv6013w2i.Iw18SQ==.AwRm44GYpKg=."
|
||||
}
|
||||
}
|
||||
188
__tests__/fixtures/slef-single-build.json
Normal file
188
__tests__/fixtures/slef-single-build.json
Normal file
@@ -0,0 +1,188 @@
|
||||
[
|
||||
{
|
||||
"header": {
|
||||
"appName": "Inara",
|
||||
"appVersion": "1.0",
|
||||
"appURL": "https:\/\/inara.cz\/cmdr-fleet\/123\/123\/",
|
||||
"appCustomProperties": {
|
||||
"inaraCommanderID": 123,
|
||||
"inaraShipID": 123
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"Ship": "krait_mkii",
|
||||
"ShipID": 7,
|
||||
"ShipName": "pancake hammer",
|
||||
"ShipIdent": "PH-01",
|
||||
"HullValue": 44160710,
|
||||
"ModulesValue": 111274094,
|
||||
"Rebuy": 7771743,
|
||||
"Modules": [
|
||||
{
|
||||
"Slot": "largehardpoint1",
|
||||
"Item": "hpt_mininglaser_fixed_small",
|
||||
"On": true
|
||||
},
|
||||
{
|
||||
"Slot": "largehardpoint2",
|
||||
"Item": "hpt_cannon_gimbal_large",
|
||||
"On": true,
|
||||
"Engineering": {
|
||||
"BlueprintName": "weapon_overcharged",
|
||||
"Level": 2,
|
||||
"Quality": 1,
|
||||
"ExperimentalEffect": "special_auto_loader"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Slot": "largehardpoint3",
|
||||
"Item": "hpt_cannon_gimbal_large",
|
||||
"On": true,
|
||||
"Engineering": {
|
||||
"BlueprintName": "weapon_overcharged",
|
||||
"Level": 2,
|
||||
"Quality": 1,
|
||||
"ExperimentalEffect": "special_auto_loader"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Slot": "mediumhardpoint1",
|
||||
"Item": "hpt_basicmissilerack_fixed_medium",
|
||||
"On": true,
|
||||
"Engineering": {
|
||||
"BlueprintName": "weapon_highcapacity",
|
||||
"Level": 5,
|
||||
"Quality": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"Slot": "mediumhardpoint2",
|
||||
"Item": "hpt_basicmissilerack_fixed_medium",
|
||||
"On": true
|
||||
},
|
||||
{
|
||||
"Slot": "tinyhardpoint1",
|
||||
"Item": "hpt_heatsinklauncher_turret_tiny",
|
||||
"On": true
|
||||
},
|
||||
{
|
||||
"Slot": "tinyhardpoint2",
|
||||
"Item": "hpt_cloudscanner_size0_class3",
|
||||
"On": true
|
||||
},
|
||||
{
|
||||
"Slot": "tinyhardpoint3",
|
||||
"Item": "hpt_shieldbooster_size0_class5",
|
||||
"On": true
|
||||
},
|
||||
{
|
||||
"Slot": "tinyhardpoint4",
|
||||
"Item": "hpt_shieldbooster_size0_class5",
|
||||
"On": true,
|
||||
"Priority": 1
|
||||
},
|
||||
{
|
||||
"Slot": "slot01_size6",
|
||||
"Item": "int_cargorack_size6_class1",
|
||||
"On": true,
|
||||
"Priority": 1
|
||||
},
|
||||
{
|
||||
"Slot": "slot02_size6",
|
||||
"Item": "int_cargorack_size6_class1",
|
||||
"On": true,
|
||||
"Priority": 1
|
||||
},
|
||||
{
|
||||
"Slot": "slot03_size5",
|
||||
"Item": "int_guardianfsdbooster_size5",
|
||||
"On": true
|
||||
},
|
||||
{
|
||||
"Slot": "slot04_size5",
|
||||
"Item": "int_fighterbay_size5_class1",
|
||||
"On": true
|
||||
},
|
||||
{
|
||||
"Slot": "slot05_size4",
|
||||
"Item": "int_shieldgenerator_size4_class5",
|
||||
"On": true
|
||||
},
|
||||
{
|
||||
"Slot": "slot06_size3",
|
||||
"Item": "int_dronecontrol_collection_size3_class4",
|
||||
"On": true
|
||||
},
|
||||
{
|
||||
"Slot": "slot07_size3",
|
||||
"Item": "int_dronecontrol_collection_size3_class4",
|
||||
"On": true
|
||||
},
|
||||
{
|
||||
"Slot": "slot08_size2",
|
||||
"Item": "int_refinery_size2_class2",
|
||||
"On": true
|
||||
},
|
||||
{
|
||||
"Slot": "slot09_size1",
|
||||
"Item": "int_dronecontrol_prospector_size1_class4",
|
||||
"On": true
|
||||
},
|
||||
{
|
||||
"Slot": "powerplant",
|
||||
"Item": "int_powerplant_size7_class5",
|
||||
"On": true,
|
||||
"Priority": 1
|
||||
},
|
||||
{
|
||||
"Slot": "mainengines",
|
||||
"Item": "int_engine_size6_class5",
|
||||
"On": true
|
||||
},
|
||||
{
|
||||
"Slot": "frameshiftdrive",
|
||||
"Item": "int_hyperdrive_size5_class5",
|
||||
"On": true,
|
||||
"Engineering": {
|
||||
"BlueprintName": "fsd_longrange",
|
||||
"Level": 2,
|
||||
"Quality": 0.861
|
||||
}
|
||||
},
|
||||
{
|
||||
"Slot": "lifesupport",
|
||||
"Item": "int_lifesupport_size4_class2",
|
||||
"On": true,
|
||||
"Priority": 3
|
||||
},
|
||||
{
|
||||
"Slot": "powerdistributor",
|
||||
"Item": "int_powerdistributor_size7_class5",
|
||||
"On": true
|
||||
},
|
||||
{
|
||||
"Slot": "radar",
|
||||
"Item": "int_sensors_size6_class2",
|
||||
"On": true
|
||||
},
|
||||
{
|
||||
"Slot": "fueltank",
|
||||
"Item": "int_fueltank_size5_class3",
|
||||
"On": true,
|
||||
"Priority": 1
|
||||
},
|
||||
{
|
||||
"Slot": "armour",
|
||||
"Item": "krait_mkii_armour_grade3",
|
||||
"On": true,
|
||||
"Priority": 1,
|
||||
"Engineering": {
|
||||
"BlueprintName": "armour_heavyduty",
|
||||
"Level": 5,
|
||||
"Quality": 1
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -18,13 +18,13 @@ describe('Import Modal', function() {
|
||||
const mockContext = {
|
||||
language: getLanguage('en'),
|
||||
sizeRatio: 1,
|
||||
openMenu: jest.genMockFunction(),
|
||||
closeMenu: jest.genMockFunction(),
|
||||
showModal: jest.genMockFunction(),
|
||||
hideModal: jest.genMockFunction(),
|
||||
tooltip: jest.genMockFunction(),
|
||||
termtip: jest.genMockFunction(),
|
||||
onWindowResize: jest.genMockFunction()
|
||||
openMenu: jest.fn(),
|
||||
closeMenu: jest.fn(),
|
||||
showModal: jest.fn(),
|
||||
hideModal: jest.fn(),
|
||||
tooltip: jest.fn(),
|
||||
termtip: jest.fn(),
|
||||
onWindowResize: jest.fn()
|
||||
};
|
||||
|
||||
let modal, render, ContextProvider = Utils.createContextProvider(mockContext);
|
||||
@@ -110,21 +110,25 @@ describe('Import Modal', function() {
|
||||
it('catches an invalid backup', function() {
|
||||
const importData = require('./fixtures/valid-backup');
|
||||
let invalidImportData = Object.assign({}, importData);
|
||||
//invalidImportData.builds.asp = null; // Remove Asp Miner build used in comparison
|
||||
// Remove Asp Miner build used in comparison
|
||||
delete(invalidImportData.builds.asp);
|
||||
|
||||
pasteText('"this is not valid"');
|
||||
expect(modal.state.importValid).toBeFalsy();
|
||||
expect(modal.state.errorMsg).toEqual('Must be an object or array!');
|
||||
|
||||
pasteText('{ "builds": "Should not be a string" }');
|
||||
expect(modal.state.importValid).toBeFalsy();
|
||||
expect(modal.state.errorMsg).toEqual('builds must be an object!');
|
||||
pasteText(JSON.stringify(importData).replace('anaconda', 'invalid_ship'));
|
||||
|
||||
pasteText(JSON.stringify(importData).replace(/anaconda/g, 'invalid_ship'));
|
||||
expect(modal.state.importValid).toBeFalsy();
|
||||
expect(modal.state.errorMsg).toEqual('"invalid_ship" is not a valid Ship Id!');
|
||||
expect(Object.keys(modal.state.builds)).not.toContain('anaconda');
|
||||
|
||||
pasteText(JSON.stringify(importData).replace('Dream', ''));
|
||||
expect(modal.state.importValid).toBeFalsy();
|
||||
expect(modal.state.errorMsg).toEqual('Imperial Clipper build "" must be a string at least 1 character long!');
|
||||
expect(Object.keys(modal.state.builds.imperial_clipper).length).toEqual(3);
|
||||
|
||||
pasteText(JSON.stringify(invalidImportData));
|
||||
expect(modal.state.importValid).toBeFalsy();
|
||||
expect(modal.state.errorMsg).toEqual('asp build "Miner" data is missing!');
|
||||
@@ -144,7 +148,7 @@ describe('Import Modal', function() {
|
||||
expect(modal.state.singleBuild).toBe(true);
|
||||
clickProceed();
|
||||
expect(MockRouter.go.mock.calls.length).toBe(1);
|
||||
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/anaconda?code=A4putkFklkdzsuf52c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04---0303326b.AwRj4zNLaA%3D%3D.CwBhCYzBGW9qCTSqq5xA.&bn=Test%20My%20Ship');
|
||||
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/anaconda?code=A4putkFklkdzsuf52c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04---0303326b-.AwRj4zNLeI%3D%3D.CwBhCYzBGW9qCTSqq5JA.&bn=Test%20My%20Ship');
|
||||
});
|
||||
|
||||
it('catches an invalid build', function() {
|
||||
@@ -169,7 +173,7 @@ describe('Import Modal', function() {
|
||||
expect(modal.state.singleBuild).toBe(true);
|
||||
clickProceed();
|
||||
expect(MockRouter.go.mock.calls.length).toBe(1);
|
||||
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/anaconda?code=A4putkFklkdzsuf52c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04---0303326b.AwRj4zNLaA%3D%3D.CwBhCYzBGW9qCTSqq5xA.H4sIAAAAAAAAA2MUe8HMwPD%2FPwMAAGvB0AkAAAA%3D&bn=Test%20My%20Ship');
|
||||
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/anaconda?code=A4putkFklkdzsuf52c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04---0303326b-.AwRj4zNLeI%3D%3D.CwBhCYzBGW9qCTSqq5JA.H4sIAAAAAAAAE2MUe8HMwPD%2FPwMAAGvB0AkAAAA%3D&bn=Test%20My%20Ship');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -186,7 +190,7 @@ describe('Import Modal', function() {
|
||||
expect(modal.state.singleBuild).toBe(true);
|
||||
clickProceed();
|
||||
expect(MockRouter.go.mock.calls.length).toBe(1);
|
||||
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/asp?code=A0pftiFflfddsnf5------020202033c044002v62f2i.AwRj4yvI.CwRgDBldHnJA.H4sIAAAAAAAAA2P858DAwPCXEUhwHPvx%2F78YG5AltB7I%2F8%2F0TwImJboDSPJ%2F%2B%2Ff%2Fv%2FKlX%2F%2F%2Fi3AwMTBIfARK%2FGf%2BJwVSxArStVAYqOjvz%2F%2F%2FJVo5GRhE2IBc4SKQSSz%2FDGEmCa398P8%2F%2F2%2BgTf%2F%2FA7kMAExxqlSAAAAA&bn=Multi-purpose%20Asp%20Explorer');
|
||||
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/asp?code=A0pftiFflfddsnf5------020202033c044002v6-2i-.AwRj4yvYg%3D%3D%3D.CwRgDBldHn5A.H4sIAAAAAAAAE2P858DAwPCXEUhwHPvx%2F78YG5AltB7I%2F8%2F0TwImJboDSPJ%2F%2B%2Ff%2Fv%2FKlX%2F%2F%2Fi3AwMTBIfARK%2FGf%2BJwVSxArStVAYqOjvz%2F%2F%2FJVo5GRhE2IBc4SKQSSz%2FDGEmCa398P8%2F%2F2%2BgTf%2F%2FA7kMAExxqlSAAAAA&bn=Multi-purpose%20Asp%20Explorer');
|
||||
});
|
||||
|
||||
it('imports a valid v4 build with modifications', function() {
|
||||
@@ -198,11 +202,11 @@ describe('Import Modal', function() {
|
||||
expect(modal.state.singleBuild).toBe(true);
|
||||
clickProceed();
|
||||
expect(MockRouter.go.mock.calls.length).toBe(1);
|
||||
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/imperial_courier?code=A0patzF5l0das8f31a1a270202000e402t0101-2f.AwRj4zKA.CwRgDBldLiQ%3D.H4sIAAAAAAAAA12OPUvDYBSFT1OTfkRJjUkbbC3Yj8mlODgUISAtdOlety5ODv0Vgji7O7kJ%2FgzBQX%2BEY7Gg0NKhfY%2FnHQLFDBdynufe9%2BRMCmCb06g29oCgacjiRx6gY6oWKUT8UgLaszqQfHmSnpVFN1uSeXNsJVcj%2FA2EHlZkspIUpUc6UjTXGT85qwHuSEuVc%2F16r99kDQeSSjvSbSjpyUpNK10uJJ3aYqk6smwm1lQ9bOxw71TMm8VanEqq9JW1r3Qo%2BREOLnQHvbWmb7rZIu5VLIyGQGOukPv%2F0WQk5LeEAjPOUDwtAP6bShy2HKAz0HPO%2B5KsP25I79O2I7LvD%2Bz4Il1XAQAA&bn=Multi-purpose%20Imperial%20Courier');
|
||||
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/imperial_courier?code=A0patzF5l0das8f31a1a270202000e402t0101----.AwRj4zOYg%3D%3D%3D.CwRgDBldLuZA.H4sIAAAAAAAAE12OPUvDYBSFT1OTfkRJjUkbbC3Yj8mlODgUISAtdOlety5ODv0Vgji7O7kJ%2FgzBQX%2BEY7Gg0NKhfY%2FnHQLFDBdynufe9%2BRMCmCb06g29oCgacjiRx6gY6oWKUT8UgLaszqQfHmSnpVFN1uSeXNsJVcj%2FA2EHlZkspIUpUc6UjTXGT85qwHuSEuVc%2F16r99kDQeSSjvSbSjpyUpNK10uJJ3aYqk6smwm1lQ9bOxw71TMm8VanEqq9JW1r3Qo%2BREOLnQHvbWmb7rZIu5VLIyGQGOukPv%2F0WQk5LeEAjPOUDwtAP6bShy2HKAz0HPO%2B5KsP25I79O2I7LvD%2Bz4Il1XAQAA&bn=Multi-purpose%20Imperial%20Courier');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Import Detaild Builds Array', function() {
|
||||
describe('Import Detailed Builds Array', function() {
|
||||
|
||||
beforeEach(reset);
|
||||
|
||||
@@ -240,7 +244,7 @@ describe('Import Modal', function() {
|
||||
expect(modal.state.singleBuild).toBe(true);
|
||||
clickProceed();
|
||||
expect(MockRouter.go.mock.calls.length).toBe(1);
|
||||
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/federal_corvette?code=A2putsFklndzsxf50x0x7l28281919040404040402020l06p05sf63c5ifr--v66g2f.AwRj4zNaqA%3D%3D.CwRgDBldUExuBiIlUA%3D%3D.H4sIAAAAAAAAA12STy8DURTFb1szU53Ga8dg2qqqDmJDIoKFxJImumYjVrVqfAALC4lNbcUnkLCoDbEQu0bSlQVhI8JHsJBIQ73rXMkwMYuT9%2Bb87nl%2F7ovoRSL6ikD6TYNINZg5XsWUo7pfrBikr2USlRyXyDuLAhr6ZHanNLOzD5tjOiskysk5dOBvfTB7bjeRW0MNG3ohSBq1bKKxKwyLLUAjmwjpPu4wJx4xVbNI57heDfbUKUAy2xaRUQZpllHoHMHxKqjhhF4LgjtJiFHDmqbrEeVnUJOax7%2FSdRfRwBNotv9wo5kAuZMD2egKyDYcdYl1OBki6z%2BZQjaFnBPyFCM1LefF%2BcgrY0es9FKwbW8ZYj9gmBbxRVRdglMh6BNqnwsk4ouoO4HSIehNoBuBRHwR1QOmsBvHmk6IfMbd2fdCEka%2BjNSexPWGoEkcyX6CnxbxRZQtd%2BPpym%2B31xFtn0iSFPkf%2BBkttZlzB9KDFyBuFRfAGV0Ogoff8SSsCfjjD5hGWtLIwZB%2FgX5Zt%2BLHMI9My7sp6nzgZzekswTxVvCOkq%2FSXqb%2F3zfLxh6HrwIAAA%3D%3D&bn=Imported%20Federal%20Corvette');
|
||||
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/federal_corvette?code=A2putsFklndzsxf50x0x7l28281919040404040402020l06p05sf63c5ifr--v66g--.AwRj4zNapI%3D%3D.CwRgDBldUExuBiIlWIA%3D.&bn=Imported%20Federal%20Corvette');
|
||||
});
|
||||
|
||||
it('imports a valid companion API build', function() {
|
||||
@@ -252,7 +256,7 @@ describe('Import Modal', function() {
|
||||
expect(modal.state.singleBuild).toBe(true);
|
||||
clickProceed();
|
||||
expect(MockRouter.go.mock.calls.length).toBe(1);
|
||||
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/beluga?code=A0pktsFplCdpsnf70t0t2727270004040404043c4fmimlmm04mc0iv62i2f.AwRj4yukg%3D%3D%3D.CwRgDBldHi8IUA%3D%3D.H4sIAAAAAAAAA2P8Z8%2FAwPCXEUiIKTMxMPCv%2F%2Ff%2FP8cFIPGf6Z8YTEr0GjMDg%2FJWICERBOTzn%2Fn7%2F7%2FIO5Ai5n9SIEWsQEIoSxAolfbt%2F3%2BJPk4GBhE7YQYGYVmgcuVnf4Aq%2FwOVAAAyiFctbgAAAA%3D%3D&bn=Imported%20Beluga%20Liner');
|
||||
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/beluga?code=A0pktsFplCdpsnf70t0t2727270004040404043c4fmimlmm04mc0iv62i--.AwRj4yusg%3D%3D%3D.CwRgDBldHi8IWIA%3D.&bn=Imported%20Beluga%20Liner');
|
||||
});
|
||||
|
||||
it('imports a valid companion API build', function() {
|
||||
@@ -264,7 +268,7 @@ describe('Import Modal', function() {
|
||||
expect(modal.state.singleBuild).toBe(true);
|
||||
clickProceed();
|
||||
expect(MockRouter.go.mock.calls.length).toBe(1);
|
||||
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/type_7_transport?code=A0patfFflidasdf5----0404040005050504044d2402.AwRj4yrI.CwRgDBlVK7EiA%3D%3D%3D.&bn=Imported%20Type-7%20Transporter');
|
||||
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/type_7_transport?code=A0patfFflidasdf5----0404040005050504044d2402--.AwRj4yoo.CwRgDBlVK7HjEA%3D%3D.&bn=Imported%20Type-7%20Transporter');
|
||||
});
|
||||
|
||||
it('imports a valid companion API build', function() {
|
||||
@@ -276,7 +280,7 @@ describe('Import Modal', function() {
|
||||
expect(modal.state.singleBuild).toBe(true);
|
||||
clickProceed();
|
||||
expect(MockRouter.go.mock.calls.length).toBe(1);
|
||||
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/cobra_mk_iii?code=A0p0tdFaldd3sdf4------34---2f2i.AwRj4yKA.CwRgDMYExrezBUg%3D.&bn=Imported%20Cobra%20Mk%20III');
|
||||
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/cobra_mk_iii?code=A0p0tdFaldd3sdf4------34----2i--.AwRj4yqA.CwRgDMYExrezBig%3D.&bn=Imported%20Cobra%20Mk%20III');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -324,4 +328,41 @@ describe('Import Modal', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Imports SLEF data', () => {
|
||||
beforeEach(reset);
|
||||
|
||||
it('imports a single valid SLEF build', () => {
|
||||
const importData = require('./fixtures/slef-single-build.json');
|
||||
pasteText(JSON.stringify(importData));
|
||||
|
||||
expect(modal.state.importValid).toBeTruthy();
|
||||
expect(modal.state.errorMsg).toEqual(null);
|
||||
expect(modal.state.singleBuild).toBe(true);
|
||||
clickProceed();
|
||||
expect(MockRouter.go.mock.calls.length).toBe(1);
|
||||
expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/krait_mkii?code=A2pptkFflidussf52l1o1o2g2g020g040405051Ofr45C9C91oP3.Iw18eQ%3D%3D.AwRgzKIkA%3D%3D%3D.&bn=Imported%20pancake%20hammer');
|
||||
});
|
||||
|
||||
it('imports multiple SLEF builds', () => {
|
||||
const importData = require('./fixtures/slef-multiple-builds.json');
|
||||
const expectedBuilds = require('./fixtures/slef-multiple-expected-builds.json');
|
||||
pasteText(JSON.stringify(importData));
|
||||
|
||||
expect(modal.state.importValid).toBeTruthy();
|
||||
expect(modal.state.errorMsg).toEqual(null);
|
||||
expect(modal.state.singleBuild).toBe(false);
|
||||
clickProceed();
|
||||
expect(modal.state.processed).toBeTruthy();
|
||||
clickImport();
|
||||
|
||||
const builds = Persist.getBuilds();
|
||||
|
||||
for (const shipModel in builds) {
|
||||
for (const buildName in builds[shipModel]) {
|
||||
expect(builds[shipModel][buildName])
|
||||
.toEqual(expectedBuilds[shipModel][buildName]);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
11
d3-funcs.js
vendored
11
d3-funcs.js
vendored
@@ -1,11 +0,0 @@
|
||||
export {
|
||||
axisBottom,
|
||||
axisLeft,
|
||||
axisTop,
|
||||
formatLocale,
|
||||
line,
|
||||
scaleBand,
|
||||
scaleLinear,
|
||||
scaleOrdinal,
|
||||
select
|
||||
} from 'd3';
|
||||
59
nginx.conf
59
nginx.conf
@@ -1,59 +0,0 @@
|
||||
worker_processes 2;
|
||||
error_log ./nginx.error.log;
|
||||
worker_rlimit_nofile 8192;
|
||||
pid nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
multi_accept on;
|
||||
}
|
||||
|
||||
http {
|
||||
|
||||
access_log off;
|
||||
charset UTF-8;
|
||||
|
||||
types {
|
||||
text/html html htm shtml;
|
||||
text/css css;
|
||||
text/xml xml rss;
|
||||
image/gif gif;
|
||||
image/jpeg jpeg jpg;
|
||||
application/x-javascript js;
|
||||
text/plain txt;
|
||||
image/png png;
|
||||
image/svg+xml svg;
|
||||
image/x-icon ico;
|
||||
application/pdf pdf;
|
||||
text/cache-manifest appcache;
|
||||
}
|
||||
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_proxied any;
|
||||
gzip_comp_level 6;
|
||||
gzip_buffers 16 8k;
|
||||
gzip_http_version 1.1;
|
||||
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
|
||||
|
||||
server {
|
||||
listen 3301;
|
||||
server_name localhost;
|
||||
root ./build/;
|
||||
index index.html;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
271
package.json
271
package.json
@@ -1,125 +1,146 @@
|
||||
{
|
||||
"name": "coriolis_shipyard",
|
||||
"version": "2.9.18",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/EDCD/coriolis"
|
||||
},
|
||||
"homepage": "https://coriolis.edcd.io",
|
||||
"bugs": "https://github.com/EDCD/coriolis/issues",
|
||||
"private": true,
|
||||
"engine": "node >= 4.8.1",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"prepublish": "rollup -c && uglifyjs d3.js -c -m -o d3.min.js",
|
||||
"extract-translations": "grep -hroE \"(translate\\('[^']+'\\))|(tip.bind\\(null, '[^']+')\" src/* | grep -oE \"'[^']+'\" | grep -oE \"[^']+\" | sort -u -f",
|
||||
"clean": "rimraf build",
|
||||
"start": "node devServer.js",
|
||||
"lint": "eslint --ext .js,.jsx src",
|
||||
"test": "jest",
|
||||
"prod-serve": "nginx -p $(pwd) -c nginx.conf",
|
||||
"prod-stop": "kill -QUIT $(cat nginx.pid)",
|
||||
"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",
|
||||
"deploy": "npm run lint && npm test && npm run build && npm run rsync"
|
||||
},
|
||||
"jest": {
|
||||
"transform": {
|
||||
".*": "<rootDir>/node_modules/babel-jest"
|
||||
},
|
||||
"testRegex": "(/__tests__/test-.*|\\.(test|spec))\\.js$",
|
||||
"moduleFileExtensions": [
|
||||
"js",
|
||||
"json",
|
||||
"jsx"
|
||||
],
|
||||
"automock": true,
|
||||
"bail": false,
|
||||
"unmockedModulePathPatterns": [
|
||||
"<rootDir>/node_modules/lodash",
|
||||
"<rootDir>/node_modules/react",
|
||||
"<rootDir>/node_modules/react-dom",
|
||||
"<rootDir>/node_modules/react-transition-group",
|
||||
"<rootDir>/node_modules/react-testutils-additions",
|
||||
"<rootDir>/node_modules/fbjs",
|
||||
"<rootDir>/node_modules/fbemitter",
|
||||
"<rootDir>/node_modules/classnames",
|
||||
"<rootDir>/node_modules/d3",
|
||||
"<rootDir>/node_modules/lz-string",
|
||||
"<rootDir>/node_modules/jsen",
|
||||
"coriolis-data",
|
||||
"<rootDir>/src/app/shipyard",
|
||||
"<rootDir>/src/app/i18n",
|
||||
"<rootDir>/src/app/utils",
|
||||
"<rootDir>/src/schemas",
|
||||
"<rootDir>/__tests__"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"appcache-webpack-plugin": "^1.3.0",
|
||||
"babel-core": "*",
|
||||
"babel-eslint": "*",
|
||||
"babel-jest": "*",
|
||||
"babel-loader": "*",
|
||||
"babel-preset-env": "*",
|
||||
"babel-preset-react": "*",
|
||||
"babel-preset-stage-0": "*",
|
||||
"create-react-class": "^15.6.2",
|
||||
"cross-env": "^5.1.4",
|
||||
"css-loader": "^0.28.0",
|
||||
"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-plugin-react": "^6.10.3",
|
||||
"expose-loader": "^0.7.3",
|
||||
"express": "^4.15.2",
|
||||
"extract-text-webpack-plugin": "2.1.0",
|
||||
"file-loader": "^0.11.1",
|
||||
"html-webpack-plugin": "^2.28.0",
|
||||
"jest-cli": "^21.2.1",
|
||||
"jsen": "^0.6.4",
|
||||
"json-loader": "^0.5.4",
|
||||
"less": "^2.7.2",
|
||||
"less-loader": "^4.0.3",
|
||||
"react-addons-perf": "^15.4.2",
|
||||
"react-container-dimensions": "^1.4.1",
|
||||
"react-testutils-additions": "^15.2.0",
|
||||
"react-transition-group": "^1.1.2",
|
||||
"rimraf": "^2.6.1",
|
||||
"rollup": "0.41",
|
||||
"rollup-plugin-node-resolve": "3",
|
||||
"style-loader": "^0.16.1",
|
||||
"uglify-js": "^2.4.11",
|
||||
"url-loader": "^0.5.8",
|
||||
"webpack": "^2.4.1",
|
||||
"webpack-bugsnag-plugins": "^1.1.1",
|
||||
"webpack-dev-server": "^2.4.4",
|
||||
"webpack-notifier": "^1.6.0",
|
||||
"workbox-webpack-plugin": "^3.4.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-polyfill": "*",
|
||||
"browserify-zlib-next": "^1.0.1",
|
||||
"classnames": "^2.2.5",
|
||||
"coriolis-data": "../coriolis-data",
|
||||
"d3": "4.8.0",
|
||||
"detect-browser": "^1.7.0",
|
||||
"fbemitter": "^2.1.1",
|
||||
"lodash": "^4.17.10",
|
||||
"lz-string": "^1.4.4",
|
||||
"pako": "^1.0.6",
|
||||
"prop-types": "^15.5.8",
|
||||
"react": "^15.5.4",
|
||||
"react-dom": "^15.5.4",
|
||||
"react-ga": "^2.5.3",
|
||||
"react-number-editor": "Athanasius/react-number-editor.git#miggy",
|
||||
"recharts": "^0.22.3",
|
||||
"superagent": "^3.5.2"
|
||||
}
|
||||
}
|
||||
{
|
||||
"name": "coriolis_shipyard",
|
||||
"version": "3.0.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/EDCD/coriolis"
|
||||
},
|
||||
"homepage": "https://coriolis.io",
|
||||
"bugs": "https://github.com/EDCD/coriolis/issues",
|
||||
"private": true,
|
||||
"engine": "node >= 4.8.1",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"extract-translations": "grep -hroE \"(translate\\('[^']+'\\))|(tip.bind\\(null, '[^']+')\" src/* | grep -oE \"'[^']+'\" | grep -oE \"[^']+\" | sort -u -f",
|
||||
"clean": "rimraf build",
|
||||
"start": "node devServer.js",
|
||||
"lint": "eslint --ext .js,.jsx src",
|
||||
"test": "jest",
|
||||
"prod-serve": "nginx -p $(pwd) -c nginx.conf",
|
||||
"prod-stop": "kill -QUIT $(cat nginx.pid)",
|
||||
"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",
|
||||
"deploy": "npm run lint && npm test && npm run build && npm run rsync"
|
||||
},
|
||||
"jest": {
|
||||
"transform": {
|
||||
".*": "<rootDir>/node_modules/babel-jest"
|
||||
},
|
||||
"testRegex": "(/__tests__/test-.*|\\.(test|spec))\\.js$",
|
||||
"moduleFileExtensions": [
|
||||
"js",
|
||||
"json",
|
||||
"jsx"
|
||||
],
|
||||
"automock": true,
|
||||
"bail": false,
|
||||
"unmockedModulePathPatterns": [
|
||||
"<rootDir>/node_modules/lodash",
|
||||
"<rootDir>/node_modules/react",
|
||||
"<rootDir>/node_modules/react-dom",
|
||||
"<rootDir>/node_modules/react-transition-group",
|
||||
"<rootDir>/node_modules/react-testutils-additions",
|
||||
"<rootDir>/node_modules/fbjs",
|
||||
"<rootDir>/node_modules/fbemitter",
|
||||
"<rootDir>/node_modules/classnames",
|
||||
"<rootDir>/node_modules/d3",
|
||||
"<rootDir>/node_modules/lz-string",
|
||||
"<rootDir>/node_modules/jsen",
|
||||
"coriolis-data",
|
||||
"<rootDir>/src/app/shipyard",
|
||||
"<rootDir>/src/app/i18n",
|
||||
"<rootDir>/src/app/utils",
|
||||
"<rootDir>/src/schemas",
|
||||
"<rootDir>/__tests__"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/plugin-proposal-class-properties": "^7.0.0",
|
||||
"@babel/plugin-proposal-decorators": "^7.0.0",
|
||||
"@babel/plugin-proposal-do-expressions": "^7.0.0",
|
||||
"@babel/plugin-proposal-export-default-from": "^7.0.0",
|
||||
"@babel/plugin-proposal-export-namespace-from": "^7.0.0",
|
||||
"@babel/plugin-proposal-function-bind": "^7.0.0",
|
||||
"@babel/plugin-proposal-function-sent": "^7.0.0",
|
||||
"@babel/plugin-proposal-json-strings": "^7.0.0",
|
||||
"@babel/plugin-proposal-logical-assignment-operators": "^7.0.0",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.0.0",
|
||||
"@babel/plugin-proposal-numeric-separator": "^7.0.0",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.0.0",
|
||||
"@babel/plugin-proposal-pipeline-operator": "^7.0.0",
|
||||
"@babel/plugin-proposal-throw-expressions": "^7.0.0",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.0.0",
|
||||
"@babel/plugin-syntax-import-meta": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"@babel/preset-react": "^7.0.0",
|
||||
"appcache-webpack-plugin": "^1.4.0",
|
||||
"babel-core": "^7.0.0-bridge.0",
|
||||
"babel-eslint": "^10.0.1",
|
||||
"babel-jest": "^23.6.0",
|
||||
"babel-loader": "^8.0.0",
|
||||
"copy-webpack-plugin": "^4.5.2",
|
||||
"create-react-class": "^15.6.3",
|
||||
"cross-env": "^5.2.0",
|
||||
"css-loader": "^1.0.0",
|
||||
"d3-selection": "^1.3.2",
|
||||
"esdoc": "^1.1.0",
|
||||
"esdoc-custom-theme": "^1.4.2",
|
||||
"esdoc-ecmascript-proposal-plugin": "^1.0.0",
|
||||
"esdoc-jsx-plugin": "^1.0.0",
|
||||
"esdoc-publish-html-plugin": "^1.1.2",
|
||||
"esdoc-react-plugin": "^1.0.1",
|
||||
"esdoc-standard-plugin": "^1.0.0",
|
||||
"eslint": "^5.6.0",
|
||||
"eslint-plugin-react": "^7.11.1",
|
||||
"expose-loader": "^0.7.5",
|
||||
"express": "^4.16.3",
|
||||
"extract-text-webpack-plugin": "^4.0.0-beta.0",
|
||||
"file-loader": "^2.0.0",
|
||||
"html-webpack-plugin": "^3.0.7",
|
||||
"jest-cli": "^23.6.0",
|
||||
"jsen": "^0.6.4",
|
||||
"json-loader": "^0.5.4",
|
||||
"less": "^3.8.1",
|
||||
"less-loader": "^4.1.0",
|
||||
"react-addons-perf": "^15.4.2",
|
||||
"react-container-dimensions": "^1.4.1",
|
||||
"react-testutils-additions": "^16.0.0",
|
||||
"react-transition-group": "^2.5.0",
|
||||
"rimraf": "^2.6.1",
|
||||
"rollup": "^0.66.2",
|
||||
"rollup-plugin-node-resolve": "^3.4.0",
|
||||
"style-loader": "^0.23.0",
|
||||
"uglify-js": "^3.4.9",
|
||||
"url-loader": "^1.1.1",
|
||||
"webpack": "^4.20.2",
|
||||
"webpack-bugsnag-plugins": "^1.2.2",
|
||||
"webpack-cli": "^3.1.1",
|
||||
"webpack-dev-server": "^3.1.9",
|
||||
"webpack-notifier": "^1.6.0",
|
||||
"workbox-webpack-plugin": "^3.6.1"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"dependencies": {
|
||||
"@babel/polyfill": "^7.0.0",
|
||||
"browserify-zlib-next": "^1.0.1",
|
||||
"classnames": "^2.2.6",
|
||||
"coriolis-data": "../coriolis-data",
|
||||
"d3": "^5.7.0",
|
||||
"detect-browser": "^3.0.1",
|
||||
"fbemitter": "^2.1.1",
|
||||
"lodash": "^4.17.11",
|
||||
"lz-string": "^1.4.4",
|
||||
"pako": "^1.0.6",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^15.5.4",
|
||||
"react-dom": "^15.5.4",
|
||||
"react-extras": "^0.7.1",
|
||||
"react-fuzzy": "^0.5.2",
|
||||
"react-ga": "^2.5.3",
|
||||
"react-number-editor": "Athanasius/react-number-editor.git#miggy",
|
||||
"recharts": "^1.2.0",
|
||||
"register-service-worker": "^1.5.2",
|
||||
"superagent": "^3.8.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Router from './Router';
|
||||
import { register } from 'register-service-worker';
|
||||
import { EventEmitter } from 'fbemitter';
|
||||
import { getLanguage } from './i18n/Language';
|
||||
import Persist from './stores/Persist';
|
||||
|
||||
import Announcement from './components/Announcement';
|
||||
import Header from './components/Header';
|
||||
import Tooltip from './components/Tooltip';
|
||||
import ModalExport from './components/ModalExport';
|
||||
@@ -13,7 +15,6 @@ import ModalImport from './components/ModalImport';
|
||||
import ModalPermalink from './components/ModalPermalink';
|
||||
import * as CompanionApiUtils from './utils/CompanionApiUtils';
|
||||
import * as JournalUtils from './utils/JournalUtils';
|
||||
|
||||
import AboutPage from './pages/AboutPage';
|
||||
import NotFoundPage from './pages/NotFoundPage';
|
||||
import OutfittingPage from './pages/OutfittingPage';
|
||||
@@ -21,13 +22,14 @@ import ComparisonPage from './pages/ComparisonPage';
|
||||
import ShipyardPage from './pages/ShipyardPage';
|
||||
import ErrorDetails from './pages/ErrorDetails';
|
||||
|
||||
|
||||
const zlib = require('pako');
|
||||
const request = require('superagent');
|
||||
|
||||
/**
|
||||
* Coriolis App
|
||||
*/
|
||||
export default class Coriolis extends React.Component {
|
||||
|
||||
static childContextTypes = {
|
||||
closeMenu: PropTypes.func.isRequired,
|
||||
hideModal: PropTypes.func.isRequired,
|
||||
@@ -66,11 +68,11 @@ export default class Coriolis extends React.Component {
|
||||
this.state = {
|
||||
noTouch: !('ontouchstart' in window || navigator.msMaxTouchPoints || navigator.maxTouchPoints),
|
||||
page: null,
|
||||
announcements: [],
|
||||
language: getLanguage(Persist.getLangCode()),
|
||||
route: {},
|
||||
sizeRatio: Persist.getSizeRatio()
|
||||
};
|
||||
|
||||
Router('', (r) => this._setPage(ShipyardPage, r));
|
||||
Router('/import?', (r) => this._importBuild(r));
|
||||
Router('/import/:data', (r) => this._importBuild(r));
|
||||
@@ -95,15 +97,30 @@ export default class Coriolis extends React.Component {
|
||||
const json = JSON.parse(data);
|
||||
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);
|
||||
let ship, importString;
|
||||
if (json) {
|
||||
if (json.length && json[0].data) { // SLEF
|
||||
if (json.length > 1) { // Multiple builds, open modal
|
||||
importString = data;
|
||||
} else { // Single build, import directly
|
||||
ship = JournalUtils.shipFromLoadoutJSON(json[0].data);
|
||||
}
|
||||
} else { // not SLEF
|
||||
if (json.modules) {
|
||||
ship = CompanionApiUtils.shipFromJson(json);
|
||||
} else if (json.Modules) {
|
||||
ship = JournalUtils.shipFromLoadoutJSON(json);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ship) {
|
||||
r.params.ship = ship.id;
|
||||
r.params.code = ship.toString();
|
||||
this._setPage(OutfittingPage, r);
|
||||
} else if (importString) {
|
||||
this._setPage(ShipyardPage, r);
|
||||
this._showModal(<ModalImport importString={data}/>);
|
||||
}
|
||||
r.params.ship = ship.id;
|
||||
r.params.code = ship.toString();
|
||||
this._setPage(OutfittingPage, r);
|
||||
} catch (err) {
|
||||
this._onError('Failed to import ship', r.path, 0, 0, err);
|
||||
}
|
||||
@@ -334,47 +351,37 @@ export default class Coriolis extends React.Component {
|
||||
});
|
||||
}
|
||||
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);
|
||||
// Your service-worker.js *must* be located at the top-level directory relative to your site.
|
||||
// It won't be able to control pages unless it's located at the same level or higher than them.
|
||||
// *Don't* register service worker file in, e.g., a scripts/ sub-directory!
|
||||
// See https://github.com/slightlyoff/ServiceWorker/issues/468
|
||||
const self = this;
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
register('/service-worker.js', {
|
||||
ready(registration) {
|
||||
console.log('Service worker is active.');
|
||||
},
|
||||
registered(registration) {
|
||||
console.log('Service worker has been registered.');
|
||||
},
|
||||
cached(registration) {
|
||||
console.log('Content has been cached for offline use.');
|
||||
},
|
||||
updatefound(registration) {
|
||||
console.log('New content is downloading.');
|
||||
},
|
||||
updated(registration) {
|
||||
self.setState({ appCacheUpdate: true });
|
||||
console.log('New content is available; please refresh.');
|
||||
},
|
||||
offline() {
|
||||
console.log('No internet connection found. App is running in offline mode.');
|
||||
},
|
||||
error(error) {
|
||||
console.error('Error during service worker registration:', error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
window.onerror = this._onError.bind(this);
|
||||
window.addEventListener('resize', () => this.emitter.emit('windowResize'));
|
||||
@@ -392,22 +399,26 @@ export default class Coriolis extends React.Component {
|
||||
*/
|
||||
render() {
|
||||
let currentMenu = this.state.currentMenu;
|
||||
|
||||
return <div style={{ minHeight: '100%' }} onClick={this._closeMenu}
|
||||
className={this.state.noTouch ? 'no-touch' : null}>
|
||||
<Header appCacheUpdate={this.state.appCacheUpdate} currentMenu={currentMenu}/>
|
||||
<Header announcements={this.state.announcements} appCacheUpdate={this.state.appCacheUpdate}
|
||||
currentMenu={currentMenu}/>
|
||||
<div className="announcement-container">{this.state.announcements.map(a => <Announcement
|
||||
text={a.text}/>)}</div>
|
||||
{this.state.error ? this.state.error : this.state.page ? React.createElement(this.state.page, { currentMenu }) :
|
||||
<NotFoundPage/>}
|
||||
{this.state.modal}
|
||||
{this.state.tooltip}
|
||||
<footer>
|
||||
|
||||
<div className="right cap">
|
||||
<a href="https://github.com/EDCD/coriolis" target="_blank"
|
||||
<a href="https://github.com/EDCD/coriolis" target="_blank" rel="noopener noreferrer"
|
||||
title="Coriolis Github Project">{window.CORIOLIS_VERSION} - {window.CORIOLIS_DATE}</a>
|
||||
<br/>
|
||||
<a
|
||||
href={'https://github.com/EDCD/coriolis/compare/edcd:develop@{' + window.CORIOLIS_DATE + '}...edcd:develop'}
|
||||
target="_blank" title={'Coriolis Commits since' + window.CORIOLIS_DATE}>Commits since last release
|
||||
target="_blank" rel="noopener noreferrer" title={'Coriolis Commits since' + window.CORIOLIS_DATE}>Commits
|
||||
since last release
|
||||
({window.CORIOLIS_DATE})</a>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import Persist from './stores/Persist';
|
||||
import ReactGA from 'react-ga';
|
||||
ReactGA.initialize('UA-55840909-18');
|
||||
|
||||
let standalone = undefined;
|
||||
|
||||
/**
|
||||
@@ -73,6 +72,7 @@ Router.go = function(path, state) {
|
||||
gaTrack(path);
|
||||
let ctx = new Context(path, state);
|
||||
Router.dispatch(ctx);
|
||||
|
||||
if (!ctx.unhandled) {
|
||||
if (isStandAlone()) {
|
||||
Persist.setState(ctx);
|
||||
@@ -258,16 +258,8 @@ Route.prototype.match = function(path, params) {
|
||||
* @param {string} path Path to track
|
||||
*/
|
||||
function gaTrack(path) {
|
||||
const match = path.match(/\/outfit\/(.*)(\?code=.*)/);
|
||||
if (match) {
|
||||
if (match[1]) {
|
||||
ReactGA.ga('set', 'contentGroup1', match[1]);
|
||||
}
|
||||
if (match[2]) {
|
||||
ReactGA.ga('set', 'contentGroup2', match[2]);
|
||||
}
|
||||
}
|
||||
ReactGA.pageview(path);
|
||||
const _paq = window._paq || [];
|
||||
_paq.push(['trackPageView']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,7 +16,6 @@ function isActive(href) {
|
||||
* Active Link - Highlighted when URL matches window location
|
||||
*/
|
||||
export default class ActiveLink extends Link {
|
||||
|
||||
/**
|
||||
* Renders the component
|
||||
* @return {React.Component} The active link
|
||||
@@ -29,5 +28,4 @@ export default class ActiveLink extends Link {
|
||||
|
||||
return <a {...this.props} className={className} onClick={this.handler}>{this.props.children}</a>;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
31
src/app/components/Announcement.jsx
Normal file
31
src/app/components/Announcement.jsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { autoBind } from 'react-extras';
|
||||
|
||||
/**
|
||||
* Announcement component
|
||||
*/
|
||||
export default class Announcement extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
text: PropTypes.string
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param {Object} props React Component properties
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props);
|
||||
autoBind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the announcement
|
||||
* @return {React.Component} A href element
|
||||
*/
|
||||
render() {
|
||||
return <div className="announcement" >{this.props.text}</div>;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import TranslatedComponent from './TranslatedComponent';
|
||||
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
||||
import cn from 'classnames';
|
||||
import { MountFixed, MountGimballed, MountTurret } from './SvgIcons';
|
||||
import FuzzySearch from 'react-fuzzy';
|
||||
|
||||
const PRESS_THRESHOLD = 500; // mouse/touch down threshold
|
||||
|
||||
@@ -39,7 +40,7 @@ const GRPCAT = {
|
||||
'mc': 'projectiles',
|
||||
'axmc': 'experimental',
|
||||
'fc': 'projectiles',
|
||||
'rfl': 'experimental',
|
||||
'rfl': 'experimental',
|
||||
'pa': 'projectiles',
|
||||
'rg': 'projectiles',
|
||||
'mr': 'ordnance',
|
||||
@@ -72,7 +73,16 @@ const GRPCAT = {
|
||||
'gfsb': 'guardian',
|
||||
'gmrp': 'guardian',
|
||||
'gsc': 'guardian',
|
||||
'ghrp': 'guardian'
|
||||
'ghrp': 'guardian',
|
||||
|
||||
// Mining
|
||||
'scl': 'mining',
|
||||
'pwa': 'mining',
|
||||
'sdm': 'mining',
|
||||
|
||||
// Assists
|
||||
'dc': 'flight assists',
|
||||
'sua': 'flight assists',
|
||||
};
|
||||
// Order here is the order in which items will be shown in the modules menu
|
||||
const CATEGORIES = {
|
||||
@@ -87,9 +97,10 @@ const CATEGORIES = {
|
||||
'rf': ['rf'],
|
||||
'shields': ['sg', 'bsg', 'psg', 'scb'],
|
||||
'structural reinforcement': ['hr', 'mrp'],
|
||||
'dc': ['dc'],
|
||||
'flight assists': ['dc', 'sua'],
|
||||
|
||||
// Hardpoints
|
||||
'lasers': ['pl', 'ul', 'bl', 'ml'],
|
||||
'lasers': ['pl', 'ul', 'bl'],
|
||||
'projectiles': ['mc', 'c', 'fc', 'pa', 'rg'],
|
||||
'ordnance': ['mr', 'tp', 'nl'],
|
||||
// Utilities
|
||||
@@ -98,23 +109,24 @@ const CATEGORIES = {
|
||||
'defence': ['ch', 'po', 'ec'],
|
||||
'scanners': ['sc', 'ss', 'cs', 'kw', 'ws'], // Overloaded with internal scanners
|
||||
// Experimental
|
||||
'experimental': ['axmc', 'axmr', 'rfl', 'tbrfl', 'tbsc', 'tbem', 'xs', 'sfn', 'rcpl', 'dtl', 'rsl', 'mahr', ],
|
||||
'experimental': ['axmc', 'axmr', 'rfl', 'tbrfl', 'tbsc', 'tbem', 'xs', 'sfn', 'rcpl', 'dtl', 'rsl', 'mahr',],
|
||||
|
||||
// Guardian
|
||||
'guardian': ['gpp', 'gpd', 'gpc', 'ggc', 'gsrp', 'gfsb', 'ghrp', 'gmrp', 'gsc']
|
||||
'guardian': ['gpp', 'gpd', 'gpc', 'ggc', 'gsrp', 'gfsb', 'ghrp', 'gmrp', 'gsc'],
|
||||
|
||||
'mining': ['ml', 'scl', 'pwa', 'sdm', 'abl'],
|
||||
};
|
||||
|
||||
/**
|
||||
* Available modules menu
|
||||
*/
|
||||
export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
|
||||
static propTypes = {
|
||||
modules: PropTypes.oneOfType([PropTypes.object, PropTypes.array]).isRequired,
|
||||
onSelect: PropTypes.func.isRequired,
|
||||
diffDetails: PropTypes.func,
|
||||
m: PropTypes.object,
|
||||
shipMass: PropTypes.number,
|
||||
ship: PropTypes.object.isRequired,
|
||||
warning: PropTypes.func,
|
||||
firstSlotId: PropTypes.string,
|
||||
lastSlotId: PropTypes.string,
|
||||
@@ -122,10 +134,6 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
slotDiv: PropTypes.object
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
shipMass: 0
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param {Object} props React Component properties
|
||||
@@ -134,6 +142,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
constructor(props, context) {
|
||||
super(props);
|
||||
this._hideDiff = this._hideDiff.bind(this);
|
||||
this._showSearch = this._showSearch.bind(this);
|
||||
this.state = this._initState(props, context);
|
||||
this.slotItems = [];// Array to hold <li> refs.
|
||||
}
|
||||
@@ -146,21 +155,21 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
*/
|
||||
_initState(props, context) {
|
||||
let translate = context.language.translate;
|
||||
let { m, warning, shipMass, onSelect, modules, firstSlotId, lastSlotId } = props;
|
||||
let { m, warning, onSelect, modules, ship } = props;
|
||||
let list, currentGroup;
|
||||
|
||||
let buildGroup = this._buildGroup.bind(
|
||||
this,
|
||||
ship,
|
||||
translate,
|
||||
m,
|
||||
warning,
|
||||
shipMass - (m && m.mass ? m.mass : 0),
|
||||
(m, event) => {
|
||||
this._hideDiff(event);
|
||||
onSelect(m);
|
||||
}
|
||||
);
|
||||
|
||||
);
|
||||
let fuzzy = [];
|
||||
if (modules instanceof Array) {
|
||||
list = buildGroup(modules[0].grp, modules);
|
||||
} else {
|
||||
@@ -168,9 +177,11 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
// At present time slots with grouped options (Hardpoints and Internal) can be empty
|
||||
if (m) {
|
||||
let emptyId = 'empty';
|
||||
if(this.firstSlotId == null) this.firstSlotId = emptyId;
|
||||
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>);
|
||||
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
|
||||
@@ -198,7 +209,8 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
if (categories.length === 1) {
|
||||
// Show category header instead of group header
|
||||
if (m && grp == m.grp) {
|
||||
list.push(<div ref={(elem) => this.groupElem = elem} key={category} className={'select-category upp'}>{translate(category)}</div>);
|
||||
list.push(<div ref={(elem) => this.groupElem = elem} key={category}
|
||||
className={'select-category upp'}>{translate(category)}</div>);
|
||||
} else {
|
||||
list.push(<div key={category} className={'select-category upp'}>{translate(category)}</div>);
|
||||
}
|
||||
@@ -209,43 +221,56 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
categoryHeader = true;
|
||||
}
|
||||
if (m && grp == m.grp) {
|
||||
list.push(<div ref={(elem) => this.groupElem = elem} key={grp} className={'select-group cap'}>{translate(grp)}</div>);
|
||||
list.push(<div ref={(elem) => this.groupElem = elem} key={grp}
|
||||
className={'select-group cap'}>{translate(grp)}</div>);
|
||||
} else {
|
||||
list.push(<div key={grp} className={'select-group cap'}>{translate(grp)}</div>);
|
||||
}
|
||||
}
|
||||
list.push(buildGroup(grp, modules[grp]));
|
||||
for (const i of modules[grp]) {
|
||||
let mount = '';
|
||||
if (i.mount === 'F') {
|
||||
mount = 'Fixed';
|
||||
} else if (i.mount === 'G') {
|
||||
mount = 'Gimballed';
|
||||
} else if (i.mount === 'T') {
|
||||
mount = 'Turreted';
|
||||
}
|
||||
const fuzz = { grp, m: i, name: `${i.class}${i.rating}${mount ? ' ' + mount : ''} ${translate(grp)}` };
|
||||
fuzzy.push(fuzz);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let trackingFocus = false;
|
||||
return { list, currentGroup, trackingFocus };
|
||||
return { list, currentGroup, fuzzy, trackingFocus };
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate React Components for Module Group
|
||||
* @param {Ship} ship Ship the selection is for
|
||||
* @param {Function} translate Translate function
|
||||
* @param {Object} mountedModule Mounted Module
|
||||
* @param {Function} warningFunc Warning function
|
||||
* @param {number} mass Mass
|
||||
* @param {function} onSelect Select/Mount callback
|
||||
* @param {string} grp Group name
|
||||
* @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
|
||||
*/
|
||||
_buildGroup(translate, mountedModule, warningFunc, mass, onSelect, grp, modules, firstSlotId, lastSlotId) {
|
||||
_buildGroup(ship, translate, mountedModule, warningFunc, onSelect, grp, modules) {
|
||||
let prevClass = null, prevRating = null, prevName;
|
||||
let elems = [];
|
||||
|
||||
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
|
||||
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]));
|
||||
|
||||
let itemsOnThisRow = 0;
|
||||
@@ -256,10 +281,11 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
prevName = m.name;
|
||||
if (ModuleUtils.isShieldGenerator(m.grp)) {
|
||||
// Shield generators care about maximum hull mass
|
||||
disabled = mass > m.maxmass;
|
||||
} else if (m.maxmass) {
|
||||
// Thrusters care about total mass
|
||||
disabled = mass + m.mass > m.maxmass;
|
||||
disabled = ship.hullMass > m.maxmass;
|
||||
// If the mounted module is experimental as well, we can replace it so
|
||||
// the maximum does not apply
|
||||
} else if (m.experimental && (!mountedModule || !mountedModule.experimental)) {
|
||||
disabled = 4 <= ship.hardpoints.filter(o => o.m && o.m.experimental).length;
|
||||
}
|
||||
let active = mountedModule && mountedModule.id === m.id;
|
||||
let classes = cn(m.name ? 'lc' : 'c', {
|
||||
@@ -298,22 +324,29 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
};
|
||||
}
|
||||
|
||||
switch(m.mount) {
|
||||
case 'F': mount = <MountFixed className={'lg'} />; break;
|
||||
case 'G': mount = <MountGimballed className={'lg'}/>; break;
|
||||
case 'T': mount = <MountTurret className={'lg'}/>; break;
|
||||
switch (m.mount) {
|
||||
case 'F':
|
||||
mount = <MountFixed className={'lg'}/>;
|
||||
break;
|
||||
case 'G':
|
||||
mount = <MountGimballed 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)) {
|
||||
elems.push(<br key={'b' + m.grp + i} />);
|
||||
elems.push(<br key={'b' + m.grp + i}/>);
|
||||
itemsOnThisRow = 0;
|
||||
}
|
||||
let tbIdx = (classes.indexOf('disabled') < 0) ? 0 : undefined;
|
||||
elems.push(
|
||||
<li key={m.id} data-id={m.id} className={classes} {...eventHandlers} tabIndex={tbIdx} ref={slotItem => this.slotItems[m.id] = slotItem}>
|
||||
<li key={m.id} data-id={m.id} className={classes} {...eventHandlers} tabIndex={tbIdx}
|
||||
ref={slotItem => this.slotItems[m.id] = slotItem}>
|
||||
{mount}
|
||||
{(mount ? ' ' : '') + m.class + m.rating + (m.missile ? '/' + m.missile : '') + (m.name ? ' ' + translate(m.name) : '')}
|
||||
</li>
|
||||
@@ -341,6 +374,40 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate tooltip content for the difference between the
|
||||
* mounted module and the hovered modules
|
||||
*/
|
||||
_showSearch() {
|
||||
if (this.props.modules instanceof Array) {
|
||||
return;
|
||||
}
|
||||
return (
|
||||
<FuzzySearch
|
||||
list={this.state.fuzzy}
|
||||
keys={['grp', 'name']}
|
||||
tokenize={true}
|
||||
className={'input'}
|
||||
width={'100%'}
|
||||
style={{ padding: 0 }}
|
||||
onSelect={e => this.props.onSelect.bind(null, e.m)()}
|
||||
resultsTemplate={(props, state, styles, clickHandler) => {
|
||||
return state.results.map((val, i) => {
|
||||
return (
|
||||
<div
|
||||
key={i}
|
||||
className={'lc'}
|
||||
onClick={() => clickHandler(i)}
|
||||
>
|
||||
{val.name}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mouse over diff handler
|
||||
* @param {Function} showDiff diff tooltip callback
|
||||
@@ -406,7 +473,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
* @param {Function} select Select module callback
|
||||
* @param {SytheticEvent} event Event
|
||||
*/
|
||||
_keyUp(select,event) {
|
||||
_keyUp(select, event) {
|
||||
// nothing here yet
|
||||
}
|
||||
|
||||
@@ -476,12 +543,13 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
this.slotItems[this.firstSlotId].focus();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle focus if the component updates
|
||||
*
|
||||
*/
|
||||
componentWillUnmount() {
|
||||
if(this.props.slotDiv) {
|
||||
if (this.props.slotDiv) {
|
||||
this.props.slotDiv.focus();
|
||||
}
|
||||
}
|
||||
@@ -502,14 +570,14 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
render() {
|
||||
return (
|
||||
<div ref={node => this.node = node}
|
||||
className={cn('select', this.props.className)}
|
||||
onScroll={this._hideDiff}
|
||||
onClick={(e) => e.stopPropagation() }
|
||||
onContextMenu={stopCtxPropagation}
|
||||
className={cn('select', this.props.className)}
|
||||
onScroll={this._hideDiff}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onContextMenu={stopCtxPropagation}
|
||||
>
|
||||
{this._showSearch()}
|
||||
{this.state.list}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@ function insertLinebreaks(d) {
|
||||
* Bar Chart
|
||||
*/
|
||||
export default class BarChart extends TranslatedComponent {
|
||||
|
||||
static defaultProps = {
|
||||
colors: ['#7b6888', '#6b486b', '#3182bd', '#a05d56', '#d0743c'],
|
||||
labels: null,
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import { Ships } from 'coriolis-data/dist';
|
||||
import { nameComparator } from '../utils/SlotFunctions';
|
||||
import { Pip } from './SvgIcons';
|
||||
import LineChart from '../components/LineChart';
|
||||
import Slider from '../components/Slider';
|
||||
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
||||
import Module from '../shipyard/Module';
|
||||
|
||||
/**
|
||||
* Boost displays a boost button that toggles bosot
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import { Ships } from 'coriolis-data/dist';
|
||||
import Slider from '../components/Slider';
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,7 +10,6 @@ import { outfitURL } from '../utils/UrlGenerators';
|
||||
* Comparison Table
|
||||
*/
|
||||
export default class ComparisonTable extends TranslatedComponent {
|
||||
|
||||
static propTypes = {
|
||||
facets: PropTypes.array.isRequired,
|
||||
builds: PropTypes.array.isRequired,
|
||||
|
||||
@@ -13,7 +13,6 @@ import { ShoppingIcon } from '../components/SvgIcons';
|
||||
* Cost Section
|
||||
*/
|
||||
export default class CostSection extends TranslatedComponent {
|
||||
|
||||
static propTypes = {
|
||||
ship: PropTypes.object.isRequired,
|
||||
code: PropTypes.string.isRequired,
|
||||
@@ -307,8 +306,8 @@ export default class CostSection extends TranslatedComponent {
|
||||
<tr className='main'>
|
||||
<th colSpan='2' className='sortable le' onClick={this._sortCostBy.bind(this,'m')}>
|
||||
{translate('module')}
|
||||
{shipDiscount ? <u className='cap optional-hide' style={{ marginLeft: '0.5em' }}>{`[${translate('ship')} -${formats.pct(shipDiscount)}]`}</u> : null}
|
||||
{moduleDiscount ? <u className='cap optional-hide' style={{ marginLeft: '0.5em' }}>{`[${translate('modules')} -${formats.pct(moduleDiscount)}]`}</u> : null}
|
||||
{shipDiscount ? <u className='cap optional-hide' style={{ marginLeft: '0.5em' }}>{`[${translate('ship')} ${formats.pct(-1 * shipDiscount)}]`}</u> : null}
|
||||
{moduleDiscount ? <u className='cap optional-hide' style={{ marginLeft: '0.5em' }}>{`[${translate('modules')} ${formats.pct(-1 * moduleDiscount)}]`}</u> : null}
|
||||
</th>
|
||||
<th className='sortable le' onClick={this._sortCostBy.bind(this, 'cr')} >{translate('credits')}</th>
|
||||
</tr>
|
||||
@@ -361,11 +360,11 @@ export default class CostSection extends TranslatedComponent {
|
||||
for (let i = 0, l = retrofitCosts.length; i < l; i++) {
|
||||
let item = retrofitCosts[i];
|
||||
rows.push(<tr key={i} className={cn('highlight', { disabled: !item.retroItem.incCost })} onClick={this._toggleRetrofitCost.bind(this, item)}>
|
||||
<td className='ptr' style={{ width: '1em' }}>{item.sellClassRating}</td>
|
||||
<td className='le ptr shorten cap'>{translate(item.sellName)}</td>
|
||||
<td className='ptr' style={{ width: '1em' }}>{item.buyClassRating}</td>
|
||||
<td className='le ptr shorten cap'>{translate(item.buyName)}</td>
|
||||
<td colSpan='2' className={cn('ri ptr', item.retroItem.incCost ? item.netCost > 0 ? 'warning' : 'secondary-disabled' : 'disabled')}>{int(item.netCost)}{units.CR}</td>
|
||||
<td className='ptr' style={{ width: '1em' }}>{item.sellClassRating}</td>
|
||||
<td className='le ptr shorten cap'>{translate(item.sellName)}</td>
|
||||
<td className='ptr' style={{ width: '1em' }}>{item.buyClassRating}</td>
|
||||
<td className='le ptr shorten cap'>{translate(item.buyName)}</td>
|
||||
<td colSpan='2' className={cn('ri ptr', item.retroItem.incCost ? item.netCost > 0 ? 'warning' : 'secondary-disabled' : 'disabled')}>{int(item.netCost)}{units.CR}</td>
|
||||
</tr>);
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -52,12 +52,12 @@ export default class Defence extends TranslatedComponent {
|
||||
* @return {React.Component} contents
|
||||
*/
|
||||
render() {
|
||||
const { ship, sys, opponentWep } = this.props;
|
||||
const { opponent, sys, opponentWep } = this.props;
|
||||
const { language, tooltip, termtip } = this.context;
|
||||
const { formats, translate, units } = language;
|
||||
const { shield, armour, shielddamage, armourdamage } = this.state;
|
||||
|
||||
const pd = ship.standard[4].m;
|
||||
const pd = opponent.standard[4].m;
|
||||
|
||||
const shieldSourcesData = [];
|
||||
const effectiveShieldData = [];
|
||||
@@ -226,26 +226,26 @@ export default class Defence extends TranslatedComponent {
|
||||
return (
|
||||
<span id='defence'>
|
||||
{shield.total ? <span>
|
||||
<div className='group quarter'>
|
||||
<h2>{translate('shield metrics')}</h2>
|
||||
<br/>
|
||||
<h2 onMouseOver={termtip.bind(null, <div>{shieldSourcesTt}</div>)} onMouseOut={tooltip.bind(null, null)} className='summary'>{translate('raw shield strength')}<br/>{formats.int(shield.total)}{units.MJ}</h2>
|
||||
<h2 onMouseOver={termtip.bind(null, translate('TT_TIME_TO_LOSE_SHIELDS'))} onMouseOut={tooltip.bind(null, null)}>{translate('PHRASE_TIME_TO_LOSE_SHIELDS')}<br/>{shielddamage.totalsdps == 0 ? translate('ever') : formats.time(Calc.timeToDeplete(shield.total, shielddamage.totalsdps, shielddamage.totalseps, pd.getWeaponsCapacity(), pd.getWeaponsRechargeRate() * opponentWep / 4))}</h2>
|
||||
<h2 onMouseOver={termtip.bind(null, translate('PHRASE_SG_RECOVER'))} onMouseOut={tooltip.bind(null, null)}>{translate('PHRASE_TIME_TO_RECOVER_SHIELDS')}<br/>{shield.recover === Math.Inf ? translate('never') : formats.time(shield.recover)}</h2>
|
||||
<h2 onMouseOver={termtip.bind(null, translate('PHRASE_SG_RECHARGE'))} onMouseOut={tooltip.bind(null, null)}>{translate('PHRASE_TIME_TO_RECHARGE_SHIELDS')}<br/>{shield.recharge === Math.Inf ? translate('never') : formats.time(shield.recharge)}</h2>
|
||||
</div>
|
||||
<div className='group quarter'>
|
||||
<h2 onMouseOver={termtip.bind(null, translate('PHRASE_SHIELD_SOURCES'))} onMouseOut={tooltip.bind(null, null)}>{translate('shield sources')}</h2>
|
||||
<PieChart data={shieldSourcesData} />
|
||||
</div>
|
||||
<div className='group quarter'>
|
||||
<h2 onMouseOver={termtip.bind(null, translate('PHRASE_DAMAGE_TAKEN'))} onMouseOut={tooltip.bind(null, null)}>{translate('damage taken')}(%)</h2>
|
||||
<VerticalBarChart data={shieldDamageTakenData} yMax={140} />
|
||||
</div>
|
||||
<div className='group quarter'>
|
||||
<h2 onMouseOver={termtip.bind(null, translate('PHRASE_EFFECTIVE_SHIELD'))} onMouseOut={tooltip.bind(null, null)}>{translate('effective shield')}(MJ)</h2>
|
||||
<VerticalBarChart data={effectiveShieldData} yMax={maxEffectiveShield}/>
|
||||
</div>
|
||||
<div className='group quarter'>
|
||||
<h2>{translate('shield metrics')}</h2>
|
||||
<br/>
|
||||
<h2 onMouseOver={termtip.bind(null, <div>{shieldSourcesTt}</div>)} onMouseOut={tooltip.bind(null, null)} className='summary'>{translate('raw shield strength')}<br/>{formats.int(shield.total)}{units.MJ}</h2>
|
||||
<h2 onMouseOver={termtip.bind(null, translate('TT_TIME_TO_LOSE_SHIELDS'))} onMouseOut={tooltip.bind(null, null)}>{translate('PHRASE_TIME_TO_LOSE_SHIELDS')}<br/>{shielddamage.totalsdps == 0 ? translate('ever') : formats.time(Calc.timeToDeplete(shield.total, shielddamage.totalsdps, shielddamage.totalseps, pd.getWeaponsCapacity(), pd.getWeaponsRechargeRate() * opponentWep / 4))}</h2>
|
||||
<h2 onMouseOver={termtip.bind(null, translate('PHRASE_SG_RECOVER'))} onMouseOut={tooltip.bind(null, null)}>{translate('PHRASE_TIME_TO_RECOVER_SHIELDS')}<br/>{shield.recover === Math.Inf ? translate('never') : formats.time(shield.recover)}</h2>
|
||||
<h2 onMouseOver={termtip.bind(null, translate('PHRASE_SG_RECHARGE'))} onMouseOut={tooltip.bind(null, null)}>{translate('PHRASE_TIME_TO_RECHARGE_SHIELDS')}<br/>{shield.recharge === Math.Inf ? translate('never') : formats.time(shield.recharge)}</h2>
|
||||
</div>
|
||||
<div className='group quarter'>
|
||||
<h2 onMouseOver={termtip.bind(null, translate('PHRASE_SHIELD_SOURCES'))} onMouseOut={tooltip.bind(null, null)}>{translate('shield sources')}</h2>
|
||||
<PieChart data={shieldSourcesData} />
|
||||
</div>
|
||||
<div className='group quarter'>
|
||||
<h2 onMouseOver={termtip.bind(null, translate('PHRASE_DAMAGE_TAKEN'))} onMouseOut={tooltip.bind(null, null)}>{translate('damage taken')}(%)</h2>
|
||||
<VerticalBarChart data={shieldDamageTakenData} yMax={140} />
|
||||
</div>
|
||||
<div className='group quarter'>
|
||||
<h2 onMouseOver={termtip.bind(null, translate('PHRASE_EFFECTIVE_SHIELD'))} onMouseOut={tooltip.bind(null, null)}>{translate('effective shield')}(MJ)</h2>
|
||||
<VerticalBarChart data={effectiveShieldData} yMax={maxEffectiveShield}/>
|
||||
</div>
|
||||
</span> : null }
|
||||
|
||||
<div className='group quarter'>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import { Ships } from 'coriolis-data/dist';
|
||||
import Slider from '../components/Slider';
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import { Ships } from 'coriolis-data/dist';
|
||||
import { nameComparator } from '../utils/SlotFunctions';
|
||||
import LineChart from '../components/LineChart';
|
||||
import Slider from '../components/Slider';
|
||||
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
||||
import Module from '../shipyard/Module';
|
||||
import * as Calc from '../shipyard/Calculations';
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import { Ships } from 'coriolis-data/dist';
|
||||
import { nameComparator } from '../utils/SlotFunctions';
|
||||
import LineChart from '../components/LineChart';
|
||||
import Slider from '../components/Slider';
|
||||
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
||||
import Module from '../shipyard/Module';
|
||||
import * as Calc from '../shipyard/Calculations';
|
||||
|
||||
/**
|
||||
@@ -69,7 +64,7 @@ export default class FSDProfile extends TranslatedComponent {
|
||||
const { formats, translate, units } = language;
|
||||
const { ship, cargo, fuel } = this.props;
|
||||
|
||||
|
||||
|
||||
// Calculate bounds for our line chart - use thruster info for X
|
||||
const thrusters = ship.standard[1].m;
|
||||
const fsd = ship.standard[2].m;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import { Ships } from 'coriolis-data/dist';
|
||||
import Slider from '../components/Slider';
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,12 +2,21 @@ import React from 'react';
|
||||
import cn from 'classnames';
|
||||
import Slot from './Slot';
|
||||
import Persist from '../stores/Persist';
|
||||
import { DamageAbsolute, DamageKinetic, DamageThermal, DamageExplosive, MountFixed, MountGimballed, MountTurret, ListModifications, Modified } from './SvgIcons';
|
||||
import {
|
||||
DamageAbsolute,
|
||||
DamageKinetic,
|
||||
DamageThermal,
|
||||
DamageExplosive,
|
||||
MountFixed,
|
||||
MountGimballed,
|
||||
MountTurret,
|
||||
ListModifications,
|
||||
Modified
|
||||
} from './SvgIcons';
|
||||
import { Modifications } from 'coriolis-data/dist';
|
||||
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
||||
import { blueprintTooltip } from '../utils/BlueprintFunctions';
|
||||
|
||||
|
||||
/**
|
||||
* Hardpoint / Utility Slot
|
||||
*/
|
||||
@@ -27,7 +36,7 @@ export default class HardpointSlot extends Slot {
|
||||
* @return {string} Label
|
||||
*/
|
||||
_getMaxClassLabel(translate) {
|
||||
return translate(['U','S','M','L','H'][this.props.maxClass]);
|
||||
return translate(['U', 'S', 'M', 'L', 'H'][this.props.maxClass]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,42 +75,76 @@ export default class HardpointSlot extends Slot {
|
||||
return <div className={className} draggable='true' onDragStart={drag} onDragEnd={drop}>
|
||||
<div className={'cb'}>
|
||||
<div className={'l'}>
|
||||
{m.mount && m.mount == 'F' ? <span onMouseOver={termtip.bind(null, 'fixed')} onMouseOut={tooltip.bind(null, null)}><MountFixed /></span> : ''}
|
||||
{m.mount && m.mount == 'G' ? <span onMouseOver={termtip.bind(null, 'gimballed')} onMouseOut={tooltip.bind(null, null)}><MountGimballed /></span> : ''}
|
||||
{m.mount && m.mount == 'T' ? <span onMouseOver={termtip.bind(null, 'turreted')} onMouseOut={tooltip.bind(null, null)}><MountTurret /></span> : ''}
|
||||
{m.getDamageDist() && m.getDamageDist().K ? <span onMouseOver={termtip.bind(null, 'kinetic')} onMouseOut={tooltip.bind(null, null)}><DamageKinetic /></span> : ''}
|
||||
{m.getDamageDist() && m.getDamageDist().T ? <span onMouseOver={termtip.bind(null, 'thermal')} onMouseOut={tooltip.bind(null, null)}><DamageThermal /></span> : ''}
|
||||
{m.getDamageDist() && m.getDamageDist().E ? <span onMouseOver={termtip.bind(null, 'explosive')} onMouseOut={tooltip.bind(null, null)}><DamageExplosive /></span> : ''}
|
||||
{m.getDamageDist() && m.getDamageDist().A ? <span onMouseOver={termtip.bind(null, 'absolute')} onMouseOut={tooltip.bind(null, null)}><DamageAbsolute /></span> : ''}
|
||||
{classRating} {translate(m.name || m.grp)}{ m.mods && Object.keys(m.mods).length > 0 ? <span className='r' onMouseOver={termtip.bind(null, modTT)} onMouseOut={tooltip.bind(null, null)}><Modified /></span> : null }
|
||||
{m.mount && m.mount == 'F' ? <span onMouseOver={termtip.bind(null, 'fixed')}
|
||||
onMouseOut={tooltip.bind(null, null)}><MountFixed/></span> : ''}
|
||||
{m.mount && m.mount == 'G' ? <span onMouseOver={termtip.bind(null, 'gimballed')}
|
||||
onMouseOut={tooltip.bind(null, null)}><MountGimballed/></span> : ''}
|
||||
{m.mount && m.mount == 'T' ? <span onMouseOver={termtip.bind(null, 'turreted')}
|
||||
onMouseOut={tooltip.bind(null, null)}><MountTurret/></span> : ''}
|
||||
{m.getDamageDist() && m.getDamageDist().K ? <span onMouseOver={termtip.bind(null, 'kinetic')}
|
||||
onMouseOut={tooltip.bind(null, null)}><DamageKinetic/></span> : ''}
|
||||
{m.getDamageDist() && m.getDamageDist().T ? <span onMouseOver={termtip.bind(null, 'thermal')}
|
||||
onMouseOut={tooltip.bind(null, null)}><DamageThermal/></span> : ''}
|
||||
{m.getDamageDist() && m.getDamageDist().E ? <span onMouseOver={termtip.bind(null, 'explosive')}
|
||||
onMouseOut={tooltip.bind(null, null)}><DamageExplosive/></span> : ''}
|
||||
{m.getDamageDist() && m.getDamageDist().A ? <span onMouseOver={termtip.bind(null, 'absolute')}
|
||||
onMouseOut={tooltip.bind(null, null)}><DamageAbsolute/></span> : ''}
|
||||
{classRating} {translate(m.name || m.grp)}{m.mods && Object.keys(m.mods).length > 0 ? <span className='r'
|
||||
onMouseOver={termtip.bind(null, modTT)}
|
||||
onMouseOut={tooltip.bind(null, null)}><Modified/></span> : null}
|
||||
</div>
|
||||
|
||||
<div className={'r'}>{formats.round(m.getMass())}{u.T}</div>
|
||||
</div>
|
||||
<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.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.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.getRoF() ? <div className={'l'} onMouseOver={termtip.bind(null, 'rof')} onMouseOut={tooltip.bind(null, null)}>{translate('ROF')}: {formats.f1(m.getRoF())}{u.ps}</div> : null }
|
||||
{ m.getRange() ? <div className={'l'}>{translate('range', m.grp)} {formats.f1(m.getRange() / 1000)}{u.km}</div> : null }
|
||||
{ m.getScanTime() ? <div className={'l'}>{translate('scantime')} {formats.f1(m.getScanTime())}{u.s}</div> : null }
|
||||
{ m.getFalloff() ? <div className={'l'}>{translate('falloff')} {formats.round(m.getFalloff() / 1000)}{u.km}</div> : null }
|
||||
{ m.getShieldBoost() ? <div className={'l'}>+{formats.pct1(m.getShieldBoost())}</div> : null }
|
||||
{ m.getAmmo() ? <div className={'l'}>{translate('ammunition')}: {formats.int(m.getClip())}/{formats.int(m.getAmmo())}</div> : null }
|
||||
{ m.getReload() ? <div className={'l'}>{translate('reload')}: {formats.round(m.getReload())}{u.s}</div> : null }
|
||||
{ m.getShotSpeed() ? <div className={'l'}>{translate('shotspeed')}: {formats.int(m.getShotSpeed())}{u.mps}</div> : null }
|
||||
{ m.getPiercing() ? <div className={'l'}>{translate('piercing')}: {formats.int(m.getPiercing())}</div> : null }
|
||||
{ m.getJitter() ? <div className={'l'}>{translate('jitter')}: {formats.f2(m.getJitter())}°</div> : null }
|
||||
{ showModuleResistances && m.getExplosiveResistance() ? <div className='l'>{translate('explres')}: {formats.pct(m.getExplosiveResistance())}</div> : null }
|
||||
{ showModuleResistances && m.getKineticResistance() ? <div className='l'>{translate('kinres')}: {formats.pct(m.getKineticResistance())}</div> : null }
|
||||
{ showModuleResistances && m.getThermalResistance() ? <div className='l'>{translate('thermres')}: {formats.pct(m.getThermalResistance())}</div> : null }
|
||||
{ m.getIntegrity() ? <div className='l'>{translate('integrity')}: {formats.int(m.getIntegrity())}</div> : null }
|
||||
{ m && validMods.length > 0 ? <div className='r' tabIndex="0" ref={ modButton => this.modButton = modButton }><button tabIndex="-1" onClick={this._toggleModifications.bind(this)} onContextMenu={stopCtxPropagation} onMouseOver={termtip.bind(null, 'modifications')} onMouseOut={tooltip.bind(null, null)}><ListModifications /></button></div> : null }
|
||||
{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.getDamage() ? <div className={'l'} onMouseOver={termtip.bind(null, m.getDamage() ? 'shotdmg' : 'shotdmg')}
|
||||
onMouseOut={tooltip.bind(null, null)}>{translate('shotdmg')}: {formats.round1(m.getDamage())}</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.getEps() * m.getSustainedFactor())}{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.getHps() * m.getSustainedFactor())})</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.getRoF() ? <div className={'l'} onMouseOver={termtip.bind(null, 'rof')}
|
||||
onMouseOut={tooltip.bind(null, null)}>{translate('ROF')}: {formats.f1(m.getRoF())}{u.ps}</div> : null}
|
||||
{m.getRange() ? <div
|
||||
className={'l'}>{translate('range', m.grp)} {formats.f1(m.getRange() / 1000)}{u.km}</div> : null}
|
||||
{m.getScanTime() ? <div
|
||||
className={'l'}>{translate('scantime')} {formats.f1(m.getScanTime())}{u.s}</div> : null}
|
||||
{m.getFalloff() ? <div
|
||||
className={'l'}>{translate('falloff')} {formats.round(m.getFalloff() / 1000)}{u.km}</div> : null}
|
||||
{m.getShieldBoost() ? <div className={'l'}>+{formats.pct1(m.getShieldBoost())}</div> : null}
|
||||
{m.getAmmo() ? <div
|
||||
className={'l'}>{translate('ammunition')}: {formats.int(m.getClip())}/{formats.int(m.getAmmo())}</div> : null}
|
||||
{m.getReload() ? <div className={'l'}>{translate('wep_reload')}: {formats.round(m.getReload())}{u.s}</div> : null}
|
||||
{m.getShotSpeed() ? <div
|
||||
className={'l'}>{translate('shotspeed')}: {formats.int(m.getShotSpeed())}{u.mps}</div> : null}
|
||||
{m.getPiercing() ? <div className={'l'}>{translate('piercing')}: {formats.int(m.getPiercing())}</div> : null}
|
||||
{m.getJitter() ? <div className={'l'}>{translate('jitter')}: {formats.f2(m.getJitter())}°</div> : null}
|
||||
{m.getScanAngle() ? <div className={'l'}>{translate('scan angle')}: {formats.f2(m.getScanAngle())}°</div> : null}
|
||||
{m.getScanRange() ? <div className={'l'}>{translate('scan range')}: {formats.int(m.getScanRange())}{u.m}</div> : null}
|
||||
{m.getMaxAngle() ? <div className={'l'}>{translate('max angle')}: {formats.f2(m.getMaxAngle())}°</div> : null}
|
||||
{showModuleResistances && m.getExplosiveResistance() ? <div
|
||||
className='l'>{translate('explres')}: {formats.pct(m.getExplosiveResistance())}</div> : null}
|
||||
{showModuleResistances && m.getKineticResistance() ? <div
|
||||
className='l'>{translate('kinres')}: {formats.pct(m.getKineticResistance())}</div> : null}
|
||||
{showModuleResistances && m.getThermalResistance() ? <div
|
||||
className='l'>{translate('thermres')}: {formats.pct(m.getThermalResistance())}</div> : null}
|
||||
{m.getIntegrity() ? <div className='l'>{translate('integrity')}: {formats.int(m.getIntegrity())}</div> : null}
|
||||
{m && validMods.length > 0 ? <div className='r' tabIndex="0" ref={modButton => this.modButton = modButton}>
|
||||
<button tabIndex="-1" onClick={this._toggleModifications.bind(this)} onContextMenu={stopCtxPropagation}
|
||||
onMouseOver={termtip.bind(null, 'modifications')} onMouseOut={tooltip.bind(null, null)}>
|
||||
<ListModifications/></button>
|
||||
</div> : null}
|
||||
</div>
|
||||
</div>;
|
||||
} else {
|
||||
return <div className={'empty'}>{translate('empty')}</div>;
|
||||
return <div className={'empty'}>{translate('empty')}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import SlotSection from './SlotSection';
|
||||
import HardpointSlot from './HardpointSlot';
|
||||
import cn from 'classnames';
|
||||
import { MountFixed, MountGimballed, MountTurret } from '../components/SvgIcons';
|
||||
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
||||
|
||||
@@ -150,10 +149,19 @@ export default class HardpointSlotSection extends SlotSection {
|
||||
<ul>
|
||||
<li className='lc' tabIndex='0' onClick={_fill.bind(this, 'pa', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['pa-F'] = smRef}>{translate('pa')}</li>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('rg')}</div>
|
||||
<ul>
|
||||
<li className='lc' tabIndex='0' onClick={_fill.bind(this, 'rg', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['rg-F'] = smRef}>{translate('rg')}</li>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('nl')}</div>
|
||||
<ul>
|
||||
<li className='lc' tabIndex='0' onClick={_fill.bind(this, 'nl', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['nl-F'] = smRef}>{translate('nl')}</li>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('rfl')}</div>
|
||||
<ul>
|
||||
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'rfl', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['rfl-F'] = smRef}><MountFixed className='lg'/></li>
|
||||
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'rfl', 'T')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['rfl-T'] = smRef}><MountTurret className='lg'/></li>
|
||||
</ul>
|
||||
</div>;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,12 +10,12 @@ import { Ships } from 'coriolis-data/dist';
|
||||
import Persist from '../stores/Persist';
|
||||
import { toDetailedExport } from '../shipyard/Serializer';
|
||||
import Ship from '../shipyard/Ship';
|
||||
import ModalBatchOrbis from './ModalBatchOrbis';
|
||||
import ModalDeleteAll from './ModalDeleteAll';
|
||||
import ModalExport from './ModalExport';
|
||||
import ModalHelp from './ModalHelp';
|
||||
import ModalImport from './ModalImport';
|
||||
import Slider from './Slider';
|
||||
import Announcement from './Announcement';
|
||||
import { outfitURL } from '../utils/UrlGenerators';
|
||||
|
||||
const SIZE_MIN = 0.65;
|
||||
@@ -76,8 +76,11 @@ export default class Header extends TranslatedComponent {
|
||||
this._openShips = this._openMenu.bind(this, 's');
|
||||
this._openBuilds = this._openMenu.bind(this, 'b');
|
||||
this._openComp = this._openMenu.bind(this, 'comp');
|
||||
this._openAnnounce = this._openMenu.bind(this, 'announce');
|
||||
this._getAnnouncementsMenu = this._getAnnouncementsMenu.bind(this);
|
||||
this._openSettings = this._openMenu.bind(this, 'settings');
|
||||
this._showHelp = this._showHelp.bind(this);
|
||||
this.update = this.update.bind(this);
|
||||
this.languageOptions = [];
|
||||
this.insuranceOptions = [];
|
||||
this.state = {
|
||||
@@ -237,43 +240,6 @@ 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
|
||||
* @param {SyntheticEvent} e Event
|
||||
@@ -345,7 +311,7 @@ export default class Header extends TranslatedComponent {
|
||||
_getShipsMenu() {
|
||||
let shipList = [];
|
||||
|
||||
for (let s in Ships) {
|
||||
for (let s of this.shipOrder) {
|
||||
shipList.push(<ActiveLink key={s} href={outfitURL(s)} className='block'>{Ships[s].properties.name}</ActiveLink>);
|
||||
}
|
||||
|
||||
@@ -411,6 +377,32 @@ export default class Header extends TranslatedComponent {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the announcement menu
|
||||
* @return {React.Component} Menu
|
||||
*/
|
||||
_getAnnouncementsMenu() {
|
||||
let announcements;
|
||||
let translate = this.context.language.translate;
|
||||
|
||||
if (this.props.announcements) {
|
||||
announcements = [];
|
||||
for (let announce of this.props.announcements) {
|
||||
if (announce.expiry < Date.now()) {
|
||||
continue;
|
||||
}
|
||||
announcements.push(<Announcement text={announce.text} />);
|
||||
announcements.push(<hr/>);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<div className='menu-list' onClick={ (e) => e.stopPropagation() } style={{ whiteSpace: 'nowrap' }}>
|
||||
{announcements}
|
||||
<hr />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the settings menu
|
||||
* @return {React.Component} Menu
|
||||
@@ -469,7 +461,6 @@ export default class Header extends TranslatedComponent {
|
||||
{translate('builds')} & {translate('comparisons')}
|
||||
<li><Link href="#" className='block' onClick={this._showBackup.bind(this)}>{translate('backup')}</Link></li>
|
||||
<li><Link href="#" className='block' onClick={this._showDetailedExport.bind(this)}>{translate('detailed export')}</Link></li>
|
||||
<li><Link href="#" className='block' onClick={this._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._showDeleteAll.bind(this)}>{translate('delete all')}</Link></li>
|
||||
</ul>
|
||||
@@ -534,6 +525,15 @@ export default class Header extends TranslatedComponent {
|
||||
}
|
||||
}
|
||||
|
||||
async update() {
|
||||
const reg = await navigator.serviceWorker.getRegistration();
|
||||
if (!reg || !reg.waiting) {
|
||||
return window.location.reload();
|
||||
}
|
||||
reg.waiting.postMessage('skipWaiting');
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the header
|
||||
* @return {React.Component} Header
|
||||
@@ -544,7 +544,7 @@ export default class Header extends TranslatedComponent {
|
||||
let hasBuilds = Persist.hasBuilds();
|
||||
return (
|
||||
<header>
|
||||
{this.props.appCacheUpdate && <div id="app-update" onClick={() => window.location.reload() }>{translate('PHRASE_UPDATE_RDY')}</div>}
|
||||
{this.props.appCacheUpdate && <div id="app-update" onClick={this.update}>{translate('PHRASE_UPDATE_RDY')}</div>}
|
||||
{this.props.appCacheUpdate ? <a className={'view-changes'} href={'https://github.com/EDCD/coriolis/compare/edcd:develop@{' + window.CORIOLIS_DATE + '}...edcd:develop'} target="_blank">
|
||||
{'View Release Changes'}
|
||||
</a> : null}
|
||||
@@ -571,6 +571,13 @@ export default class Header extends TranslatedComponent {
|
||||
{openedMenu == 'comp' ? this._getComparisonsMenu() : null}
|
||||
</div>
|
||||
|
||||
<div className='l menu'>
|
||||
<div className={cn('menu-header', { selected: openedMenu == 'announce', disabled: this.props.announcements.length === 0})} onClick={this.props.announcements.length !== 0 && this._openAnnounce}>
|
||||
<span className='menu-item-label'>{translate('announcements')}</span>
|
||||
</div>
|
||||
{openedMenu == 'announce' ? this._getAnnouncementsMenu() : null}
|
||||
</div>
|
||||
|
||||
{window.location.origin.search('.edcd.io') >= 0 ?
|
||||
<div className='l menu'>
|
||||
<a href="https://youtu.be/4SvnLcefhtI" target="_blank">
|
||||
|
||||
@@ -78,7 +78,6 @@ export default class InternalSlot extends Slot {
|
||||
{ m.rangeLS ? <div className={'l'}>{translate('range')}: {m.rangeLS}{u.Ls}</div> : null }
|
||||
{ m.rangeLS === null ? <div className={'l'}>∞{u.Ls}</div> : null }
|
||||
{ m.rangeRating ? <div className={'l'}>{translate('range')}: {m.rangeRating}</div> : null }
|
||||
{ m.maximum ? <div className={'l'}>{translate('max')}: {(m.maximum)}</div> : null }
|
||||
{ m.passengers ? <div className={'l'}>{translate('passengers')}: {m.passengers}</div> : null }
|
||||
{ m.getRegenerationRate() ? <div className='l'>{translate('regen')}: {formats.round1(m.getRegenerationRate())}{u.ps}</div> : null }
|
||||
{ m.getBrokenRegenerationRate() ? <div className='l'>{translate('brokenregen')}: {formats.round1(m.getBrokenRegenerationRate())}{u.ps}</div> : null }
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React from 'react';
|
||||
import cn from 'classnames';
|
||||
import SlotSection from './SlotSection';
|
||||
import InternalSlot from './InternalSlot';
|
||||
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import { Ships } from 'coriolis-data/dist';
|
||||
import { nameComparator } from '../utils/SlotFunctions';
|
||||
import LineChart from '../components/LineChart';
|
||||
import Slider from '../components/Slider';
|
||||
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
||||
import Module from '../shipyard/Module';
|
||||
import * as Calc from '../shipyard/Calculations';
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import request from 'superagent';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import { orbisUpload } from '../utils/ShortenUrl';
|
||||
import Persist from '../stores/Persist';
|
||||
|
||||
/**
|
||||
* Permalink modal
|
||||
*/
|
||||
export default class ModalBatchOrbis extends TranslatedComponent {
|
||||
|
||||
static propTypes = {
|
||||
ships: PropTypes.any.isRequired
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param {Object} props React Component properties
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
orbisCreds: Persist.getOrbisCreds(),
|
||||
resp: ''
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Send ship to Orbis.zone
|
||||
* @param {SyntheticEvent} e React Event
|
||||
* @return {Promise} Promise sending post request to orbis
|
||||
*/
|
||||
sendToOrbis(e) {
|
||||
let agent;
|
||||
try {
|
||||
agent = request.agent(); // apparently this crashes somehow
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
if (!agent) {
|
||||
agent = request;
|
||||
}
|
||||
const API_ORBIS = 'https://orbis.zone/api/builds/add/batch';
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
agent
|
||||
.post(API_ORBIS)
|
||||
.withCredentials()
|
||||
.redirects(0)
|
||||
.set('Content-Type', 'application/json')
|
||||
.send(this.props.ships)
|
||||
.end((err, response) => {
|
||||
console.log(response);
|
||||
if (err) {
|
||||
console.error(err);
|
||||
this.setState({ resp: response.text });
|
||||
reject('Bad Request');
|
||||
} else {
|
||||
this.setState({ resp: 'All builds uploaded. Check https://orbis.zone' });
|
||||
resolve('All builds uploaded. Check https://orbis.zone');
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
reject(e.message ? e.message : e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the modal
|
||||
* @return {React.Component} Modal Content
|
||||
*/
|
||||
render() {
|
||||
let translate = this.context.language.translate;
|
||||
this.sendToOrbis = this.sendToOrbis.bind(this);
|
||||
|
||||
return <div className='modal' onClick={ (e) => e.stopPropagation() }>
|
||||
<h2>{translate('permalink')}</h2>
|
||||
<br/>
|
||||
<a className='button' href="https://orbis.zone/api/auth">Log in / signup to Orbis</a>
|
||||
<br/><br/>
|
||||
<h3 >{translate('success')}</h3>
|
||||
<input value={this.state.resp} readOnly size={25} onFocus={ (e) => e.target.select() }/>
|
||||
<br/><br/>
|
||||
<p>Orbis.zone is currently in a trial period, and may be wiped at any time as development progresses. Some elements are also still placeholders.</p>
|
||||
<button className={'l cb dismiss cap'} disabled={!!this.state.failed} onClick={this.sendToOrbis}>{translate('PHASE_UPLOAD_ORBIS')}</button>
|
||||
<button className={'r dismiss cap'} onClick={this.context.hideModal}>{translate('close')}</button>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import Persist from '../stores/Persist';
|
||||
* Delete All saved data modal
|
||||
*/
|
||||
export default class ModalDeleteAll extends TranslatedComponent {
|
||||
|
||||
/**
|
||||
* Delete everything and hide the modal
|
||||
*/
|
||||
|
||||
@@ -11,7 +11,10 @@ import * as ModuleUtils from '../shipyard/ModuleUtils';
|
||||
import { fromDetailedBuild } from '../shipyard/Serializer';
|
||||
import { Download } from './SvgIcons';
|
||||
import { outfitURL } from '../utils/UrlGenerators';
|
||||
import * as CompanionApiUtils from '../utils/CompanionApiUtils';
|
||||
import { shipFromJson, shipModelFromJson } from '../utils/CompanionApiUtils';
|
||||
import { shipFromLoadoutJSON } from '../utils/JournalUtils';
|
||||
|
||||
const zlib = require('pako');
|
||||
|
||||
const textBuildRegex = new RegExp('^\\[([\\w \\-]+)\\]\n');
|
||||
const lineRegex = new RegExp('^([\\dA-Z]{1,2}): (\\d)([A-I])[/]?([FGT])?([SD])? ([\\w\\- ]+)');
|
||||
@@ -86,6 +89,7 @@ export default class ModalImport extends TranslatedComponent {
|
||||
|
||||
|
||||
static propTypes = {
|
||||
importString: PropTypes.string, // Optional: Default data for import modal
|
||||
builds: PropTypes.object, // Optional: Import object
|
||||
};
|
||||
|
||||
@@ -99,11 +103,12 @@ export default class ModalImport extends TranslatedComponent {
|
||||
this.state = {
|
||||
builds: props.builds,
|
||||
canEdit: !props.builds,
|
||||
loadoutEvent: null,
|
||||
comparisons: null,
|
||||
shipDiscount: null,
|
||||
moduleDiscount: null,
|
||||
errorMsg: null,
|
||||
importString: null,
|
||||
importString: props.importString || null,
|
||||
importValid: false,
|
||||
insurance: null
|
||||
};
|
||||
@@ -111,12 +116,28 @@ export default class ModalImport extends TranslatedComponent {
|
||||
this._process = this._process.bind(this);
|
||||
this._import = this._import.bind(this);
|
||||
this._importBackup = this._importBackup.bind(this);
|
||||
this._importLoadout = this._importLoadout.bind(this);
|
||||
this._importDetailedArray = this._importDetailedArray.bind(this);
|
||||
this._importTextBuild = this._importTextBuild.bind(this);
|
||||
this._importCompanionApiBuild = this._importCompanionApiBuild.bind(this);
|
||||
this._validateImport = this._validateImport.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Import a Loadout event from Elite: Dangerous journal files
|
||||
* @param {Object} data Loadout event
|
||||
* @throws {string} If import fails
|
||||
*/
|
||||
_importLoadout(data) {
|
||||
if (data && data.Ship && data.Modules) {
|
||||
const deflated = zlib.deflate(JSON.stringify(data), { to: 'string' });
|
||||
let compressed = btoa(deflated);
|
||||
this.setState({loadoutEvent: compressed});
|
||||
} else {
|
||||
throw 'Loadout event must contain Ship and Modules';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Import a Coriolis backup
|
||||
* @param {Object} importData Backup Data
|
||||
@@ -159,7 +180,7 @@ export default class ModalImport extends TranslatedComponent {
|
||||
}
|
||||
// Check for module discount
|
||||
if (!isNaN(importData.moduleDiscount)) {
|
||||
this.setState({ shipDiscount: importData.moduleDiscount * 1 });
|
||||
this.setState({ moduleDiscount: importData.moduleDiscount * 1 });
|
||||
}
|
||||
|
||||
if (typeof importData.insurance == 'string') {
|
||||
@@ -195,8 +216,8 @@ export default class ModalImport extends TranslatedComponent {
|
||||
* @throws {string} if parse/import fails
|
||||
*/
|
||||
_importCompanionApiBuild(build) {
|
||||
const shipModel = CompanionApiUtils.shipModelFromJson(build);
|
||||
const ship = CompanionApiUtils.shipFromJson(build);
|
||||
const shipModel = shipModelFromJson(build);
|
||||
const ship = shipFromJson(build);
|
||||
|
||||
let builds = {};
|
||||
builds[shipModel] = {};
|
||||
@@ -302,6 +323,30 @@ export default class ModalImport extends TranslatedComponent {
|
||||
this.setState({ builds, singleBuild: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* Import SLEF formatted builds. Sets state to a map of the builds on success
|
||||
* and flags if there was only a single build.
|
||||
*
|
||||
* @param {string} importData - Array of the list of builds.
|
||||
* @throws {string} If parse / import fails
|
||||
*/
|
||||
_importSlefBuilds(importData) {
|
||||
const builds = importData.reduce((memo, { data }) => {
|
||||
const shipModel = shipModelFromJson(data);
|
||||
const ship = shipFromLoadoutJSON(data);
|
||||
const shipTemplate = Ships[shipModel];
|
||||
const shipName = data.ShipName || shipTemplate.properties.name;
|
||||
|
||||
const key = `Imported ${shipName}`;
|
||||
memo[shipModel] = {};
|
||||
memo[shipModel][key] = ship.toString();
|
||||
|
||||
return memo;
|
||||
}, {});
|
||||
|
||||
this.setState({ builds, singleBuild: Object.keys(builds).length === 1 });
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the import string / text box contents
|
||||
* @param {SyntheticEvent} event Event
|
||||
@@ -336,8 +381,10 @@ export default class ModalImport extends TranslatedComponent {
|
||||
throw 'Must be an object or array!';
|
||||
}
|
||||
|
||||
if (importData.modules != null && importData.modules.Armour != null) { // Only the companion API has this information
|
||||
this._importCompanionApiBuild(importData); // Single sihp definition
|
||||
if (importData?.[0]?.header?.appName) { // has SLEF envelope?
|
||||
this._importSlefBuilds(importData);
|
||||
} else if (importData.modules != null && importData.modules.Armour != null) { // Only the companion API has this information
|
||||
this._importCompanionApiBuild(importData); // Single ship definition
|
||||
} else if (importData.ship != null && importData.ship.modules != null && importData.ship.modules.Armour != null) { // Only the companion API has this information
|
||||
this._importCompanionApiBuild(importData.ship); // Complete API dump
|
||||
} else if (importData instanceof Array) { // Must be detailed export json
|
||||
@@ -345,12 +392,14 @@ export default class ModalImport extends TranslatedComponent {
|
||||
} else if (importData.ship && typeof importData.name !== undefined) { // Using JSON from a single ship build export
|
||||
this._importDetailedArray([importData]); // Convert to array with singleobject
|
||||
this.setState({ singleBuild: true });
|
||||
} else if (importData.Modules != null && importData.Modules[0] != null) {
|
||||
this._importLoadout(importData);
|
||||
} else { // Using Backup JSON
|
||||
this._importBackup(importData);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// console.log(e.stack);
|
||||
console.log(e);
|
||||
this.setState({ errorMsg: (typeof e == 'string') ? e : 'Cannot Parse the data!' });
|
||||
return;
|
||||
}
|
||||
@@ -364,6 +413,10 @@ export default class ModalImport extends TranslatedComponent {
|
||||
_process() {
|
||||
let builds = null, comparisons = null;
|
||||
|
||||
if (this.state.loadoutEvent) {
|
||||
return Router.go(`/import?data=${this.state.loadoutEvent}`);
|
||||
}
|
||||
|
||||
// If only importing a single build go straight to the outfitting page
|
||||
if (this.state.singleBuild) {
|
||||
builds = this.state.builds;
|
||||
@@ -480,7 +533,7 @@ export default class ModalImport extends TranslatedComponent {
|
||||
if (!state.processed) {
|
||||
importStage = (
|
||||
<div>
|
||||
<textarea className='cb json' ref={node => this.importField = node} onChange={this._validateImport} defaultValue={this.state.importString} placeholder={translate('PHRASE_IMPORT')} />
|
||||
<textarea spellCheck={false} className='cb json' ref={node => this.importField = node} onChange={this._validateImport} defaultValue={this.state.importString} placeholder={translate('PHRASE_IMPORT')} />
|
||||
<button id='proceed' className='l cap' onClick={this._process} disabled={!state.importValid} >{translate('proceed')}</button>
|
||||
<div className='l warning' style={{ marginLeft:'3em' }}>{state.errorMsg}</div>
|
||||
</div>
|
||||
@@ -517,7 +570,7 @@ export default class ModalImport extends TranslatedComponent {
|
||||
{comparisonRows}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
if(this.state.canEdit) {
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
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>;
|
||||
}
|
||||
}
|
||||
@@ -109,17 +109,18 @@ export default class ModalShoppingList extends TranslatedComponent {
|
||||
*/
|
||||
sendToEDEng(event) {
|
||||
event.preventDefault();
|
||||
let translate = this.context.language.translate;
|
||||
const target = event.target;
|
||||
target.disabled = this.state.blueprints.length > 0;
|
||||
if (this.state.blueprints.length === 0) {
|
||||
target.innerText = 'No modded components.';
|
||||
target.innerText = translate('No modded components.');
|
||||
target.disabled = true;
|
||||
setTimeout(() => {
|
||||
target.innerText = 'Send to EDEngineer';
|
||||
target.innerText = translate('Send to EDEngineer');
|
||||
target.disabled = false;
|
||||
}, 3000);
|
||||
} else {
|
||||
target.innerText = 'Sending...';
|
||||
target.innerText = translate('Sending...');
|
||||
}
|
||||
let countSent = 0;
|
||||
let countTotal = this.state.blueprints.length;
|
||||
@@ -139,7 +140,7 @@ export default class ModalShoppingList extends TranslatedComponent {
|
||||
countSent++;
|
||||
if (countSent === countTotal) {
|
||||
target.disabled = false;
|
||||
target.innerText = 'Send to EDEngineer';
|
||||
target.innerText = translate('Send to EDEngineer');
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -230,32 +231,32 @@ export default class ModalShoppingList extends TranslatedComponent {
|
||||
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>
|
||||
<label>{translate('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>
|
||||
<label>{translate('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>
|
||||
<label>{translate('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>
|
||||
<label>{translate('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>
|
||||
<label>{translate('Grade 5 rolls ')}</label>
|
||||
<input id={5} type={'number'} min={0} value={this.state.matsPerGrade[5]} onChange={this.changeHandler} />
|
||||
<div>
|
||||
<textarea className='cb json' readOnly value={this.state.matsList} />
|
||||
</div>
|
||||
<label hidden={!compatible} className={'l cap'}>CMDR Name </label>
|
||||
<label hidden={!compatible} className={'l cap'}>{translate('CMDR Name')}</label>
|
||||
<br/>
|
||||
<select hidden={!compatible} className={'cmdr-select l cap'} onChange={this.cmdrChangeHandler} defaultValue={this.state.cmdrName}>
|
||||
{this.state.cmdrs.map(e => <option key={e}>{e}</option>)}
|
||||
</select>
|
||||
<br/>
|
||||
<p hidden={!this.state.failed} id={'failed'} className={'l'}>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>
|
||||
<p hidden={!this.state.failed} id={'failed'} className={'l'}>{translate('PHRASE_FAIL_EDENGINEER')}</p>
|
||||
<p hidden={compatible} id={'browserbad'} className={'l'}>{translate('PHRASE_FIREFOX_EDENGINEER')}</p>
|
||||
<button className={'l cb dismiss cap'} disabled={!!this.state.failed || !compatible} onClick={this.sendToEDEng}>{translate('Send to EDEngineer')}</button>
|
||||
<button className={'r dismiss cap'} onClick={this.context.hideModal}>{translate('close')}</button>
|
||||
</div>;
|
||||
}
|
||||
|
||||
@@ -3,13 +3,13 @@ import PropTypes from 'prop-types';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import cn from 'classnames';
|
||||
import NumberEditor from 'react-number-editor';
|
||||
import { isValueBeneficial } from '../utils/BlueprintFunctions';
|
||||
import { isChangeValueBeneficial } from '../utils/BlueprintFunctions';
|
||||
import { Modifications } from 'coriolis-data/dist';
|
||||
|
||||
/**
|
||||
* Modification
|
||||
*/
|
||||
export default class Modification extends TranslatedComponent {
|
||||
|
||||
static propTypes = {
|
||||
ship: PropTypes.object.isRequired,
|
||||
m: PropTypes.object.isRequired,
|
||||
@@ -39,10 +39,24 @@ export default class Modification extends TranslatedComponent {
|
||||
* in a value by hand
|
||||
*/
|
||||
_updateValue(value) {
|
||||
let { m, name, ship } = this.props;
|
||||
value = Math.max(Math.min(value, 50000), -50000);
|
||||
ship.setModification(m, name, value, true, true);
|
||||
this.setState({ value });
|
||||
let reCast = String(Number(value));
|
||||
if (reCast.endsWith(value) || reCast.startsWith(value)) {
|
||||
let { m, name, ship } = this.props;
|
||||
value = Math.max(Math.min(value, 50000), -50000);
|
||||
ship.setModification(m, name, value, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggered when a key is pressed down with focus on the number editor.
|
||||
* @param {SyntheticEvent} event Key down event
|
||||
*/
|
||||
_keyDown(event) {
|
||||
if (event.key == 'Enter') {
|
||||
this._updateFinished();
|
||||
}
|
||||
this.props.onKeyDown(event);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,12 +80,18 @@ export default class Modification extends TranslatedComponent {
|
||||
let { translate, formats, units } = this.context.language;
|
||||
let { m, name } = this.props;
|
||||
let modValue = m.getChange(name);
|
||||
let isOverwrite = Modifications.modifications[name].method === 'overwrite';
|
||||
|
||||
if (name === 'damagedist') {
|
||||
// We don't show damage distribution
|
||||
return null;
|
||||
}
|
||||
|
||||
let inputClassNames = {
|
||||
'cb': true,
|
||||
'greyed-out': !this.props.highlight
|
||||
};
|
||||
|
||||
return (
|
||||
<div onBlur={this._updateFinished.bind(this)} key={name}
|
||||
className={cn('cb', 'modification-container')}
|
||||
@@ -84,25 +104,25 @@ export default class Modification extends TranslatedComponent {
|
||||
<td className={'input-container'}>
|
||||
<span>
|
||||
{this.props.editable ?
|
||||
<NumberEditor className={'cb'} value={this.state.value}
|
||||
<NumberEditor className={cn(inputClassNames)} value={this.state.value}
|
||||
decimals={2} style={{ textAlign: 'right' }} step={0.01}
|
||||
stepModifier={1} onKeyDown={ this.props.onKeyDown }
|
||||
stepModifier={1} onKeyDown={this._keyDown.bind(this)}
|
||||
onValueChange={this._updateValue.bind(this)} /> :
|
||||
<input type="text" value={formats.f2(this.state.value)}
|
||||
disabled className={'number-editor'}
|
||||
disabled className={cn('number-editor', 'greyed-out')}
|
||||
style={{ textAlign: 'right', cursor: 'inherit' }}/>
|
||||
}
|
||||
<span className={'unit-container'}>
|
||||
{units[m.getStoredUnitFor(name)]}
|
||||
{units[m.getStoredUnitFor(name)]}
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
<td style={{ textAlign: 'center' }} className={
|
||||
modValue ?
|
||||
isValueBeneficial(name, modValue) ? 'secondary': 'warning':
|
||||
''
|
||||
}>
|
||||
{formats.f2(modValue / 100) || 0}%
|
||||
modValue ?
|
||||
isChangeValueBeneficial(name, modValue) ? 'secondary' : 'warning' :
|
||||
''
|
||||
}>
|
||||
{formats.f2(modValue / 100) || 0}{isOverwrite ? '' : '%'}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import * as _ from 'lodash';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import { isEmpty, stopCtxPropagation } from '../utils/UtilityFunctions';
|
||||
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
||||
import cn from 'classnames';
|
||||
import { Modifications } from 'coriolis-data/dist';
|
||||
import Modification from './Modification';
|
||||
@@ -23,7 +23,6 @@ const MODIFICATIONS_COMPARATOR = (mod1, mod2) => {
|
||||
* Modifications menu
|
||||
*/
|
||||
export default class ModificationsMenu extends TranslatedComponent {
|
||||
|
||||
static propTypes = {
|
||||
ship: PropTypes.object.isRequired,
|
||||
m: PropTypes.object.isRequired,
|
||||
@@ -214,11 +213,11 @@ export default class ModificationsMenu extends TranslatedComponent {
|
||||
for (const modName of Modifications.modules[m.grp].modifications) {
|
||||
if (!Modifications.modifications[modName].hidden) {
|
||||
const key = modName + (m.getModValue(modName) / 100 || 0);
|
||||
const editable = modName !== 'fallofffromrange' &&
|
||||
m.blueprint.grades[m.blueprint.grade].features[modName];
|
||||
const editable = modName !== 'fallofffromrange';
|
||||
const highlight = m.blueprint.grades[m.blueprint.grade].features[modName];
|
||||
this.lastNeId = modName;
|
||||
(editable ? modifiableModifications : modifications).push(
|
||||
<Modification key={ key } ship={ ship } m={ m }
|
||||
(editable && highlight ? modifiableModifications : modifications).push(
|
||||
<Modification key={ key } ship={ ship } m={ m } highlight={highlight}
|
||||
value={m.getPretty(modName) || 0} modItems={this.modItems}
|
||||
onChange={onChange} onKeyDown={this._keyDown} name={modName}
|
||||
editable={editable} handleModChange = {this._handleModChange} />
|
||||
@@ -461,10 +460,10 @@ export default class ModificationsMenu extends TranslatedComponent {
|
||||
}
|
||||
return (
|
||||
<div
|
||||
className={cn('select', this.props.className)}
|
||||
onClick={(e) => e.stopPropagation() }
|
||||
onContextMenu={stopCtxPropagation}
|
||||
ref={modItem => this.modItems['modMainDiv'] = modItem}
|
||||
className={cn('select', this.props.className)}
|
||||
onClick={(e) => e.stopPropagation() }
|
||||
onContextMenu={stopCtxPropagation}
|
||||
ref={modItem => this.modItems['modMainDiv'] = modItem}
|
||||
>
|
||||
{ showBlueprintsMenu | showSpecialsMenu ? '' : haveBlueprint ?
|
||||
<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> :
|
||||
@@ -473,19 +472,19 @@ export default class ModificationsMenu extends TranslatedComponent {
|
||||
{ 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 }
|
||||
{ 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 ?
|
||||
{ showRolls ?
|
||||
|
||||
<table style={{ width: '100%', backgroundColor: 'transparent' }}>
|
||||
<tbody>
|
||||
{ showRolls ?
|
||||
<table style={{ width: '100%', backgroundColor: 'transparent' }}>
|
||||
<tbody>
|
||||
{ showRolls ?
|
||||
<tr>
|
||||
<td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: false }) }> { translate('roll') }: </td>
|
||||
<td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: false }) }> { translate('mroll') }: </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 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 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 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 }
|
||||
</tbody>
|
||||
</tbody>
|
||||
</table> : null }
|
||||
{ showMods ? <hr /> : null }
|
||||
{ showMods ?
|
||||
|
||||
@@ -35,13 +35,13 @@ export default class Movement extends TranslatedComponent {
|
||||
return (
|
||||
<span id='movement'>
|
||||
<svg viewBox='0 0 600 600' fillRule="evenodd" clipRule="evenodd">
|
||||
// Axes
|
||||
{/* Axes */}
|
||||
<path d="M150 250v300" strokeWidth='1'/>
|
||||
<path d="M150 250l236 236" strokeWidth='1'/>
|
||||
<path d="M150 250l350 -200" strokeWidth='1'/>
|
||||
// End Arrow
|
||||
{/* End Arrow */}
|
||||
<path d="M508 43.3L487 67l-10-17.3 31-6.4z"/>
|
||||
// Axes arcs and arrows
|
||||
{/* Axes arcs and arrows */}
|
||||
<path d="M71.7 251.7C64.2 259.2 60 269.4 60 280c0 22 18 40 40 40s40-18 40-40c0-10.6-4.2-20.8-11.7-28.3 7.5 7.5 11.7 17.7 11.7 28.3 0 22-18 40-40 40s-40-18-40-40c0-10.6 4.2-20.8 11.7-28.3z" strokeWidth='4' transform="matrix(.6 0 0 .3 87.5 376.3)"/>
|
||||
<path d="M142.8 453l-13.2 8.7-2.6-9.7 15.8 1z"/>
|
||||
<path d="M144.7 451.6l.5 1.6-16.2 10.6h-.4l-3.5-13 .7-.4 19.3 1.2zm-14.2 7.7l7.7-5-9.2-.7 1.5 5.7zm25.7-6.3l15.8-1-2.6 9.7-13.2-8.8z"/>
|
||||
@@ -57,13 +57,13 @@ export default class Movement extends TranslatedComponent {
|
||||
<path d="M359.5 422.4l-1.2 19.3-1.6.4-10.7-16 .2-.2 13-3.4.3.4zm-9 5l5.2 7.8.6-9.3-5.7 1.2zm-10.5 24l-13.2 8.6-2.6-9.7 15.8 1z"/>
|
||||
<path d="M342 450l.4 1.5-16.2 10.7-.4-.2-3.5-13 .3-.3L342 450zm-14.3 7.6l7.7-5-9.2-.6 1.5 5.6z"/>
|
||||
|
||||
// Speed
|
||||
{/* Speed */}
|
||||
<text x="470" y="30" strokeWidth='0'>{ship.canThrust(cargo, fuel) ? formats.int(ship.calcSpeed(eng, fuel, cargo, boost)) + 'm/s' : '-'}</text>
|
||||
// Pitch
|
||||
{/* Pitch */}
|
||||
<text x="355" y="410" strokeWidth='0'>{ship.canThrust(cargo, fuel) ? formats.int(ship.calcPitch(eng, fuel, cargo, boost)) + '°/s' : '-'}</text>
|
||||
// Roll
|
||||
{/* Roll */}
|
||||
<text x="450" y="110" strokeWidth='0'>{ship.canThrust(cargo, fuel) ? formats.int(ship.calcRoll(eng, fuel, cargo, boost)) + '°/s' : '-'}</text>
|
||||
// Yaw
|
||||
{/* Yaw */}
|
||||
<text x="160" y="430" strokeWidth='0'>{ship.canThrust(cargo, fuel) ? formats.int(ship.calcYaw(eng, fuel, cargo, boost)) + '°/s' : '-'}</text>
|
||||
</svg>
|
||||
</span>);
|
||||
|
||||
@@ -5,7 +5,6 @@ import * as Calc from '../shipyard/Calculations';
|
||||
import PieChart from './PieChart';
|
||||
import { nameComparator } from '../utils/SlotFunctions';
|
||||
import { MountFixed, MountGimballed, MountTurret } from './SvgIcons';
|
||||
import VerticalBarChart from './VerticalBarChart';
|
||||
|
||||
/**
|
||||
* Generates an internationalization friendly weapon comparator that will
|
||||
@@ -203,9 +202,9 @@ export default class Offence extends TranslatedComponent {
|
||||
|
||||
|
||||
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};
|
||||
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 = [];
|
||||
for (let i = 0; i < damage.length; i++) {
|
||||
@@ -244,8 +243,13 @@ export default class Offence extends TranslatedComponent {
|
||||
<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, effectivenessShieldsTooltipDetails)} onMouseOut={tooltip.bind(null, null)}>{formats.pct1(weapon.effectiveness.shields.total)}</span></td>
|
||||
|
||||
<td className='ri'><span>{formats.f1(weapon.effectiveness.shields.dpe)}</span></td>
|
||||
|
||||
<td className='ri'><span onMouseOver={termtip.bind(null, effectiveArmourSDpsTooltipDetails)} onMouseOut={tooltip.bind(null, null)}>{formats.f1(weapon.sdps.armour.total)}</span></td>
|
||||
<td className='ri'><span onMouseOver={termtip.bind(null, effectivenessArmourTooltipDetails)} onMouseOut={tooltip.bind(null, null)}>{formats.pct1(weapon.effectiveness.armour.total)}</span></td>
|
||||
|
||||
<td className='ri'><span>{formats.f1(weapon.effectiveness.armour.dpe)}</span></td>
|
||||
</tr>);
|
||||
}
|
||||
|
||||
@@ -267,22 +271,27 @@ export default class Offence extends TranslatedComponent {
|
||||
return (
|
||||
<span id='offence'>
|
||||
<div className='group full'>
|
||||
<table>
|
||||
<thead>
|
||||
<tr className='main'>
|
||||
<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 armour')}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th className='lft sortable' onMouseOver={termtip.bind(null, 'TT_EFFECTIVE_SDPS_SHIELDS')} onMouseOut={tooltip.bind(null, null)} onClick={sortOrder.bind(this, 'esdpss')}>{'sdps'}</th>
|
||||
<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='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>
|
||||
</tr>
|
||||
</thead>
|
||||
<table>
|
||||
<thead>
|
||||
<tr className='main'>
|
||||
<th rowSpan='2' className='sortable' onClick={sortOrder.bind(this, 'n')}>{translate('weapon')}</th>
|
||||
<th colSpan='1'>{translate('overall')}</th>
|
||||
<th colSpan='3'>{translate('opponent\'s shields')}</th>
|
||||
<th colSpan='3'>{translate('opponent\'s armour')}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th className='lft sortable' onMouseOver={termtip.bind(null, 'TT_EFFECTIVE_SDPS_SHIELDS')} onMouseOut={tooltip.bind(null, null)} onClick={sortOrder.bind(this, 'esdpss')}>{'sdps'}</th>
|
||||
<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'>{'dpe'}</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'>{'dpe'}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{rows}
|
||||
{rows.length > 0 &&
|
||||
@@ -291,12 +300,14 @@ export default class Offence extends TranslatedComponent {
|
||||
<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></td>
|
||||
<td className='ri'><span onMouseOver={termtip.bind(null, totalArmourSDpsTooltipDetails)} onMouseOut={tooltip.bind(null, null)}>={formats.f1(totalArmourSDps)}</span></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</table>
|
||||
</div>
|
||||
<div className='group quarter'>
|
||||
<h2>{translate('offence metrics')}</h2>
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import cn from 'classnames';
|
||||
import { Ships } from 'coriolis-data/dist';
|
||||
import Ship from '../shipyard/Ship';
|
||||
import Persist from '../stores/Persist';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import PowerManagement from './PowerManagement';
|
||||
@@ -176,7 +174,7 @@ export default class OutfittingSubpages extends TranslatedComponent {
|
||||
<th style={{ width:'25%' }} className={cn({ active: tab == 'power' })} onClick={this._showTab.bind(this, 'power')} >{translate('power and costs')}</th>
|
||||
<th style={{ width:'25%' }} className={cn({ active: tab == 'profiles' })} onClick={this._showTab.bind(this, 'profiles')} >{translate('profiles')}</th>
|
||||
<th style={{ width:'25%' }} className={cn({ active: tab == 'offence' })} onClick={this._showTab.bind(this, 'offence')} >{translate('offence')}</th>
|
||||
<th style={{ width:'25%' }} className={cn({ active: tab == 'defence' })} onClick={this._showTab.bind(this, 'defence')} >{translate('defence')}</th>
|
||||
<th style={{ width:'25%' }} className={cn({ active: tab == 'defence' })} onClick={this._showTab.bind(this, 'defence')} >{translate('tab_defence')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import { Ships } from 'coriolis-data/dist';
|
||||
import { nameComparator } from '../utils/SlotFunctions';
|
||||
import { Pip } from './SvgIcons';
|
||||
import LineChart from '../components/LineChart';
|
||||
import Slider from '../components/Slider';
|
||||
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
||||
import Module from '../shipyard/Module';
|
||||
import { autoBind } from 'react-extras';
|
||||
|
||||
/**
|
||||
* Pips displays SYS/ENG/WEP pips and allows users to change them with key presses by clicking on the relevant area.
|
||||
@@ -18,6 +13,9 @@ export default class Pips extends TranslatedComponent {
|
||||
sys: PropTypes.number.isRequired,
|
||||
eng: PropTypes.number.isRequired,
|
||||
wep: PropTypes.number.isRequired,
|
||||
mcSys: PropTypes.number.isRequired,
|
||||
mcEng: PropTypes.number.isRequired,
|
||||
mcWep: PropTypes.number.isRequired,
|
||||
onChange: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
@@ -28,9 +26,7 @@ export default class Pips extends TranslatedComponent {
|
||||
*/
|
||||
constructor(props, context) {
|
||||
super(props);
|
||||
const { sys, eng, wep } = props;
|
||||
|
||||
this._keyDown = this._keyDown.bind(this);
|
||||
autoBind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,30 +70,21 @@ export default class Pips extends TranslatedComponent {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a click
|
||||
* @param {string} which Which item was clicked
|
||||
*/
|
||||
onClick(which) {
|
||||
if (which == 'SYS') {
|
||||
this._incSys();
|
||||
} else if (which == 'ENG') {
|
||||
this._incEng();
|
||||
} else if (which == 'WEP') {
|
||||
this._incWep();
|
||||
} else if (which == 'RST') {
|
||||
this._reset();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the capacitor
|
||||
*/
|
||||
_reset() {
|
||||
let { sys, eng, wep } = this.props;
|
||||
if (sys != 2 || eng != 2 || wep != 2) {
|
||||
_reset(isMc) {
|
||||
let { sys, eng, wep, mcSys, mcEng, mcWep } = this.props;
|
||||
if (isMc) {
|
||||
if (mcSys || mcEng || mcWep) {
|
||||
sys -= mcSys;
|
||||
eng -= mcEng;
|
||||
wep -= mcWep;
|
||||
this.props.onChange(sys, eng, wep, 0, 0, 0);
|
||||
}
|
||||
} else if (sys != 2 || eng != 2 || wep != 2) {
|
||||
sys = eng = wep = 2;
|
||||
this.props.onChange(sys, eng, wep);
|
||||
this.props.onChange(sys + mcSys, eng + mcEng, wep + mcWep, mcSys, mcEng, mcWep);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,151 +92,133 @@ export default class Pips extends TranslatedComponent {
|
||||
* Increment the SYS capacitor
|
||||
*/
|
||||
_incSys() {
|
||||
let { sys, eng, wep } = this.props;
|
||||
|
||||
const required = Math.min(1, 4 - sys);
|
||||
if (required > 0) {
|
||||
if (required == 0.5) {
|
||||
// Take from whichever is larger
|
||||
if (eng > wep) {
|
||||
eng -= 0.5;
|
||||
sys += 0.5;
|
||||
} else {
|
||||
wep -= 0.5;
|
||||
sys += 0.5;
|
||||
}
|
||||
} else {
|
||||
// Required is 1 - take from both if possible
|
||||
if (eng == 0) {
|
||||
wep -= 1;
|
||||
sys += 1;
|
||||
} else if (wep == 0) {
|
||||
eng -= 1;
|
||||
sys += 1;
|
||||
} else {
|
||||
eng -= 0.5;
|
||||
wep -= 0.5;
|
||||
sys += 1;
|
||||
}
|
||||
}
|
||||
this.props.onChange(sys, eng, wep);
|
||||
}
|
||||
this._inc('sys', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the ENG capacitor
|
||||
*/
|
||||
_incEng() {
|
||||
let { sys, eng, wep } = this.props;
|
||||
|
||||
const required = Math.min(1, 4 - eng);
|
||||
if (required > 0) {
|
||||
if (required == 0.5) {
|
||||
// Take from whichever is larger
|
||||
if (sys > wep) {
|
||||
sys -= 0.5;
|
||||
eng += 0.5;
|
||||
} else {
|
||||
wep -= 0.5;
|
||||
eng += 0.5;
|
||||
}
|
||||
} else {
|
||||
// Required is 1 - take from both if possible
|
||||
if (sys == 0) {
|
||||
wep -= 1;
|
||||
eng += 1;
|
||||
} else if (wep == 0) {
|
||||
sys -= 1;
|
||||
eng += 1;
|
||||
} else {
|
||||
sys -= 0.5;
|
||||
wep -= 0.5;
|
||||
eng += 1;
|
||||
}
|
||||
}
|
||||
this.props.onChange(sys, eng, wep);
|
||||
}
|
||||
this._inc('eng', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the WEP capacitor
|
||||
*/
|
||||
_incWep() {
|
||||
let { sys, eng, wep } = this.props;
|
||||
this._inc('wep', false);
|
||||
}
|
||||
|
||||
const required = Math.min(1, 4 - wep);
|
||||
if (required > 0) {
|
||||
if (required == 0.5) {
|
||||
// Take from whichever is larger
|
||||
if (sys > eng) {
|
||||
sys -= 0.5;
|
||||
wep += 0.5;
|
||||
} else {
|
||||
eng -= 0.5;
|
||||
wep += 0.5;
|
||||
}
|
||||
_wrapMcClick(key) {
|
||||
return (event) => {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
if (key == 'rst') {
|
||||
this._reset(true);
|
||||
} else {
|
||||
// Required is 1 - take from both if possible
|
||||
if (sys == 0) {
|
||||
eng -= 1;
|
||||
wep += 1;
|
||||
} else if (eng == 0) {
|
||||
sys -= 1;
|
||||
wep += 1;
|
||||
this._inc(key, true);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Increases a given capacitor
|
||||
* @param {String} key Pip name to increase (one of 'sys', 'eng', 'wep')
|
||||
* @param {Boolean} isMc True when increase is by multi crew
|
||||
*/
|
||||
_inc(key, isMc) {
|
||||
if (!['sys', 'eng', 'wep'].includes(key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let { sys, eng, wep, mcSys, mcEng, mcWep } = this.props;
|
||||
let mc = key == 'sys' ? mcSys : (key == 'eng' ? mcEng : mcWep);
|
||||
let pips = this.props[key] - mc;
|
||||
let other1 = key == 'sys' ? eng - mcEng : sys - mcSys;
|
||||
let other2 = key == 'wep' ? eng - mcEng : wep - mcWep;
|
||||
|
||||
const required = Math.min(1, 4 - mc - pips);
|
||||
if (isMc) {
|
||||
// We can only set full pips in multi-crew also we can only set two pips
|
||||
if (required > 0.5 && mcSys + mcEng + mcWep < 2) {
|
||||
if (key == 'sys') {
|
||||
mcSys += 1;
|
||||
} else if (key == 'eng') {
|
||||
mcEng += 1;
|
||||
} else {
|
||||
sys -= 0.5;
|
||||
eng -= 0.5;
|
||||
wep += 1;
|
||||
mcWep += 1;
|
||||
}
|
||||
}
|
||||
this.props.onChange(sys, eng, wep);
|
||||
} else if (required > 0) {
|
||||
if (required == 0.5) {
|
||||
// Take from whichever is larger
|
||||
if (other1 > other2) {
|
||||
other1 -= 0.5;
|
||||
} else {
|
||||
other2 -= 0.5;
|
||||
}
|
||||
pips += 0.5;
|
||||
} else {
|
||||
// Required is 1 - take from both if possible
|
||||
if (other1 == 0) {
|
||||
other2 -= 1;
|
||||
} else if (other2 == 0) {
|
||||
other1 -= 1;
|
||||
} else {
|
||||
other1 -= 0.5;
|
||||
other2 -= 0.5;
|
||||
}
|
||||
pips += 1;
|
||||
}
|
||||
}
|
||||
|
||||
sys = mcSys + (key == 'sys' ? pips : other1);
|
||||
eng = mcEng + (key == 'eng' ? pips : (key == 'sys' ? other1 : other2));
|
||||
wep = mcWep + (key == 'wep' ? pips : other2);
|
||||
this.props.onChange(sys, eng, wep, mcSys, mcEng, mcWep);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the rendering for pips
|
||||
* @param {int} sys the SYS pips
|
||||
* @param {int} eng the ENG pips
|
||||
* @param {int} wep the WEP pips
|
||||
* @param {Number} sys the SYS pips
|
||||
* @param {Number} eng the ENG pips
|
||||
* @param {Number} wep the WEP pips
|
||||
* @param {Number} mcSys SYS pips from multi-crew
|
||||
* @param {Number} mcEng ENG pips from multi-crew
|
||||
* @param {Number} mcWep WEP pips from multi-crew
|
||||
* @returns {Object} Object containing the rendering for the pips
|
||||
*/
|
||||
_renderPips(sys, eng, wep) {
|
||||
const pipsSvg = {};
|
||||
_renderPips(sys, eng, wep, mcSys, mcEng, mcWep) {
|
||||
const pipsSvg = {
|
||||
SYS: [],
|
||||
ENG: [],
|
||||
WEP: [],
|
||||
};
|
||||
|
||||
// SYS
|
||||
pipsSvg['SYS'] = [];
|
||||
for (let i = 0; i < Math.floor(sys); i++) {
|
||||
pipsSvg['SYS'].push(<Pip className='full' key={i} />);
|
||||
}
|
||||
if (sys > Math.floor(sys)) {
|
||||
pipsSvg['SYS'].push(<Pip className='half' key={'half'} />);
|
||||
}
|
||||
for (let i = Math.floor(sys + 0.5); i < 4; i++) {
|
||||
pipsSvg['SYS'].push(<Pip className='empty' key={i} />);
|
||||
}
|
||||
// Multi-crew pipsSettings actually are included in the overall pip count therefore
|
||||
// we can consider [0, sys - mcSys] as normal pipsSettings whilst [sys - mcSys, sys]
|
||||
// are the multi-crew pipsSettings in what follows.
|
||||
|
||||
// ENG
|
||||
pipsSvg['ENG'] = [];
|
||||
for (let i = 0; i < Math.floor(eng); i++) {
|
||||
pipsSvg['ENG'].push(<Pip className='full' key={i} />);
|
||||
}
|
||||
if (eng > Math.floor(eng)) {
|
||||
pipsSvg['ENG'].push(<Pip className='half' key={'half'} />);
|
||||
}
|
||||
for (let i = Math.floor(eng + 0.5); i < 4; i++) {
|
||||
pipsSvg['ENG'].push(<Pip className='empty' key={i} />);
|
||||
}
|
||||
let pipsSettings = {
|
||||
SYS: [sys, mcSys],
|
||||
ENG: [eng, mcEng],
|
||||
WEP: [wep, mcWep],
|
||||
};
|
||||
|
||||
// WEP
|
||||
pipsSvg['WEP'] = [];
|
||||
for (let i = 0; i < Math.floor(wep); i++) {
|
||||
pipsSvg['WEP'].push(<Pip className='full' key={i} />);
|
||||
}
|
||||
if (wep > Math.floor(wep)) {
|
||||
pipsSvg['WEP'].push(<Pip className='half' key={'half'} />);
|
||||
}
|
||||
for (let i = Math.floor(wep + 0.5); i < 4; i++) {
|
||||
pipsSvg['WEP'].push(<Pip className='empty' key={i} />);
|
||||
for (let pipName in pipsSettings) {
|
||||
let [pips, mcPips] = pipsSettings[pipName];
|
||||
for (let i = 0; i < Math.floor(pips - mcPips); i++) {
|
||||
pipsSvg[pipName].push(<Pip key={i} className='full' />);
|
||||
}
|
||||
if (pips > Math.floor(pips)) {
|
||||
pipsSvg[pipName].push(<Pip className='half' key={'half'} />);
|
||||
}
|
||||
for (let i = pips - mcPips; i < Math.floor(pips); i++) {
|
||||
pipsSvg[pipName].push(<Pip key={i} className='mc' />);
|
||||
}
|
||||
for (let i = Math.floor(pips + 0.5); i < 4; i++) {
|
||||
pipsSvg[pipName].push(<Pip className='empty' key={i} />);
|
||||
}
|
||||
}
|
||||
|
||||
return pipsSvg;
|
||||
@@ -260,15 +229,11 @@ export default class Pips extends TranslatedComponent {
|
||||
* @return {React.Component} contents
|
||||
*/
|
||||
render() {
|
||||
const { tooltip, termtip } = this.context;
|
||||
const { formats, translate, units } = this.context.language;
|
||||
const { sys, eng, wep } = this.props;
|
||||
const { sys, eng, wep, mcSys, mcEng, mcWep } = this.props;
|
||||
|
||||
const onSysClicked = this.onClick.bind(this, 'SYS');
|
||||
const onEngClicked = this.onClick.bind(this, 'ENG');
|
||||
const onWepClicked = this.onClick.bind(this, 'WEP');
|
||||
const onRstClicked = this.onClick.bind(this, 'RST');
|
||||
|
||||
const pipsSvg = this._renderPips(sys, eng, wep);
|
||||
const pipsSvg = this._renderPips(sys, eng, wep, mcSys, mcEng, mcWep);
|
||||
return (
|
||||
<span id='pips'>
|
||||
<table>
|
||||
@@ -276,20 +241,38 @@ export default class Pips extends TranslatedComponent {
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td> </td>
|
||||
<td className='clickable' onClick={onEngClicked}>{pipsSvg['ENG']}</td>
|
||||
<td className='clickable' onClick={() => this._inc('eng')}
|
||||
onContextMenu={this._wrapMcClick('eng')}>{pipsSvg['ENG']}</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td className='clickable' onClick={onSysClicked}>{pipsSvg['SYS']}</td>
|
||||
<td className='clickable' onClick={onEngClicked}>{translate('ENG')}</td>
|
||||
<td className='clickable' onClick={onWepClicked}>{pipsSvg['WEP']}</td>
|
||||
<td className='clickable' onClick={this._incSys}
|
||||
onContextMenu={this._wrapMcClick('sys')}>{pipsSvg['SYS']}</td>
|
||||
<td className='clickable' onClick={this._incEng}
|
||||
onContextMenu={this._wrapMcClick('eng')}>{translate('ENG')}</td>
|
||||
<td className='clickable' onClick={this._incWep}
|
||||
onContextMenu={this._wrapMcClick('wep')}>{pipsSvg['WEP']}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td className='clickable' onClick={onSysClicked}>{translate('SYS')}</td>
|
||||
<td className='clickable' onClick={onRstClicked}>{translate('RST')}</td>
|
||||
<td className='clickable' onClick={onWepClicked}>{translate('WEP')}</td>
|
||||
<td className='clickable' onClick={this._incSys}
|
||||
onContextMenu={this._wrapMcClick('sys')}>{translate('SYS')}</td>
|
||||
<td className='clickable' onClick={this._reset.bind(this, false)}>
|
||||
{translate('RST')}
|
||||
</td>
|
||||
<td className='clickable' onClick={this._incWep}
|
||||
onContextMenu={this._wrapMcClick('wep')}>{translate('WEP')}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td> </td>
|
||||
<td className='clickable secondary' onClick={this._wrapMcClick('rst')}
|
||||
onMouseEnter={termtip.bind(null, 'PHRASE_MULTI_CREW_CAPACITOR_POINTS')}
|
||||
onMouseLeave={tooltip.bind(null, null)}>
|
||||
{translate('RST')}
|
||||
</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -32,7 +32,6 @@ function bandText(val, index, wattScale) {
|
||||
* Renders the SVG to simulate in-game power bands
|
||||
*/
|
||||
export default class PowerBands extends TranslatedComponent {
|
||||
|
||||
static propTypes = {
|
||||
bands: PropTypes.array.isRequired,
|
||||
available: PropTypes.number.isRequired,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import Ship from '../shipyard/Ship';
|
||||
import { Ships } from 'coriolis-data/dist';
|
||||
import { Rocket } from './SvgIcons';
|
||||
import Persist from '../stores/Persist';
|
||||
|
||||
@@ -50,8 +50,10 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
||||
const speedTooltip = canThrust ? 'TT_SUMMARY_SPEED' : 'TT_SUMMARY_SPEED_NONFUNCTIONAL';
|
||||
const canBoost = ship.canBoost(cargo, ship.fuelCapacity);
|
||||
const boostTooltip = canBoost ? 'TT_SUMMARY_BOOST' : canThrust ? 'TT_SUMMARY_BOOST_NONFUNCTIONAL' : 'TT_SUMMARY_SPEED_NONFUNCTIONAL';
|
||||
const canJump = ship.getSlotStatus(ship.standard[2]) == 3;
|
||||
const sgMetrics = Calc.shieldMetrics(ship, pips.sys);
|
||||
const shipBoost = canBoost ? Calc.calcBoost(ship) : 'No Boost';
|
||||
const restingHeat = Math.sqrt(((ship.standard[0].m.pgen * ship.standard[0].m.eff) / ship.heatCapacity) / 0.2);
|
||||
const armourMetrics = Calc.armourMetrics(ship);
|
||||
let shieldColour = 'blue';
|
||||
if (shieldGenerator && shieldGenerator.m.grp === 'psg') {
|
||||
@@ -70,7 +72,7 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
||||
<tr className='main'>
|
||||
<th rowSpan={2} className={ cn({ 'bg-warning-disabled': !canThrust }) }>{translate('speed')}</th>
|
||||
<th rowSpan={2} className={ cn({ 'bg-warning-disabled': !canBoost }) }>{translate('boost')}</th>
|
||||
<th colSpan={5}>{translate('jump range')}</th>
|
||||
<th colSpan={5} className={ cn({ 'bg-warning-disabled': !canJump }) }>{translate('jump range')}</th>
|
||||
<th rowSpan={2}>{translate('shield')}</th>
|
||||
<th rowSpan={2}>{translate('integrity')}</th>
|
||||
<th rowSpan={2}>{translate('DPS')}</th>
|
||||
@@ -78,20 +80,21 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
||||
<th rowSpan={2}>{translate('TTD')}</th>
|
||||
{/* <th onMouseEnter={termtip.bind(null, 'heat per second')} onMouseLeave={hide} rowSpan={2}>{translate('HPS')}</th> */}
|
||||
<th rowSpan={2}>{translate('cargo')}</th>
|
||||
<th rowSpan={2}>{translate('pax')}</th>
|
||||
<th rowSpan={2} onMouseEnter={termtip.bind(null, 'passenger capacity', { cap: 0 })} onMouseLeave={hide}>{translate('pax')}</th>
|
||||
<th rowSpan={2}>{translate('fuel')}</th>
|
||||
<th colSpan={3}>{translate('mass')}</th>
|
||||
<th onMouseEnter={termtip.bind(null, 'hull hardness', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('hrd')}</th>
|
||||
<th rowSpan={2}>{translate('crew')}</th>
|
||||
<th onMouseEnter={termtip.bind(null, 'mass lock factor', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('MLF')}</th>
|
||||
<th onMouseEnter={termtip.bind(null, 'TT_SUMMARY_BOOST_TIME', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('boost time')}</th>
|
||||
<th onMouseEnter={termtip.bind(null, 'TT_SUMMARY_BOOST_INTERVAL', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('boost interval')}</th>
|
||||
<th rowSpan={2}>{translate('resting heat (Beta)')}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th className='lft'>{translate('max')}</th>
|
||||
<th>{translate('unladen')}</th>
|
||||
<th>{translate('laden')}</th>
|
||||
<th>{translate('total unladen')}</th>
|
||||
<th>{translate('total laden')}</th>
|
||||
<th className={ cn({ 'lft': true, 'bg-warning-disabled': !canJump }) }>{translate('max')}</th>
|
||||
<th className={ cn({ 'bg-warning-disabled': !canJump }) }>{translate('unladen')}</th>
|
||||
<th className={ cn({ 'bg-warning-disabled': !canJump }) }>{translate('laden')}</th>
|
||||
<th className={ cn({ 'bg-warning-disabled': !canJump }) }>{translate('total unladen')}</th>
|
||||
<th className={ cn({ 'bg-warning-disabled': !canJump }) }>{translate('total laden')}</th>
|
||||
<th className='lft'>{translate('hull')}</th>
|
||||
<th>{translate('unladen')}</th>
|
||||
<th>{translate('laden')}</th>
|
||||
@@ -101,11 +104,11 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
||||
<tr>
|
||||
<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, 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><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><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><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_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_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_MAX_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{ canJump ? <span>{ f2(Calc.jumpRange(ship.unladenMass + ship.standard[2].m.getMaxFuelPerJump(), ship.standard[2].m, ship.standard[2].m.getMaxFuelPerJump(), ship))}{u.LY}</span> : <span className='warning'>0 <Warning/></span> }</td>
|
||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{ canJump ? <span>{f2(Calc.jumpRange(ship.unladenMass + ship.fuelCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</span> : <span className='warning'>0 <Warning/></span> }</td>
|
||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{ canJump ? <span>{f2(Calc.jumpRange(ship.unladenMass + ship.fuelCapacity + ship.cargoCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</span> : <span className='warning'>0 <Warning/></span> }</td>
|
||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_TOTAL_JUMP', { cap: 0 })} onMouseLeave={hide}>{ canJump ? <span>{f2(Calc.totalJumpRange(ship.unladenMass + ship.fuelCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</span> : <span className='warning'>0 <Warning/></span> }</td>
|
||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_TOTAL_JUMP', { cap: 0 })} onMouseLeave={hide}>{ canJump ? <span>{f2(Calc.totalJumpRange(ship.unladenMass + ship.fuelCapacity + ship.cargoCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</span> : <span className='warning'>0 <Warning/></span> }</td>
|
||||
<td className={sgClassNames} onMouseEnter={termtip.bind(null, sgTooltip, { cap: 0 })} onMouseLeave={hide}>{int(ship.shield)}{u.MJ}</td>
|
||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_INTEGRITY', { cap: 0 })} onMouseLeave={hide}>{int(ship.armour)}</td>
|
||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_DPS', { cap: 0 })} onMouseLeave={hide}>{f1(ship.totalDps)}</td>
|
||||
@@ -122,6 +125,7 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
||||
<td>{ship.crew}</td>
|
||||
<td>{ship.masslock}</td>
|
||||
<td>{shipBoost !== 'No Boost' ? formats.time(shipBoost) : 'No Boost'}</td>
|
||||
<td>{formats.pct(restingHeat)}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -156,10 +160,10 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
||||
<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>{int(ship && sgMetrics.summary > 0 ? sgMetrics.summary : 0)}{u.MJ}</td>
|
||||
<td>{int(ship && sgMetrics.summary > 0 ? sgMetrics.summary / sgMetrics.explosive.base : 0)}{u.MJ}</td>
|
||||
<td>{int(ship && sgMetrics.summary ? sgMetrics.summary / sgMetrics.kinetic.base : 0)}{u.MJ}</td>
|
||||
<td>{int(ship && sgMetrics.summary ? sgMetrics.summary / sgMetrics.thermal.base : 0)}{u.MJ}</td>
|
||||
<td></td>
|
||||
<td>{sgMetrics && sgMetrics.recover === Math.Inf ? translate('Never') : formats.time(sgMetrics.recover)}</td>
|
||||
<td>{sgMetrics && sgMetrics.recharge === Math.Inf ? translate('Never') : formats.time(sgMetrics.recharge)}</td>
|
||||
@@ -194,11 +198,11 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
||||
<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.total)}</td>
|
||||
<td>{int(armourMetrics.total / armourMetrics.explosive.total)}</td>
|
||||
<td>{int(armourMetrics.total/ armourMetrics.kinetic.total)}</td>
|
||||
<td>{int(armourMetrics.total / armourMetrics.thermal.total)}</td>
|
||||
<td>{int(armourMetrics.total/ armourMetrics.caustic.total)}</td>
|
||||
<td>{int(armourMetrics.modulearmour)}</td>
|
||||
<td>{int(armourMetrics.moduleprotection * 100) + '%'}</td>
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import AvailableModulesMenu from './AvailableModulesMenu';
|
||||
import ModificationsMenu from './ModificationsMenu';
|
||||
import { diffDetails } from '../utils/SlotFunctions';
|
||||
import { wrapCtxMenu } from '../utils/UtilityFunctions';
|
||||
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
||||
|
||||
/**
|
||||
* Abstract Slot
|
||||
@@ -88,7 +87,7 @@ export default class Slot extends TranslatedComponent {
|
||||
if(event.target.className == 'r') {
|
||||
this._toggleModifications();
|
||||
}
|
||||
this.props.onOpen(event);
|
||||
this.props.onOpen(event);
|
||||
}
|
||||
}
|
||||
/**
|
||||
@@ -128,8 +127,8 @@ export default class Slot extends TranslatedComponent {
|
||||
menu = <AvailableModulesMenu
|
||||
className={this._getClassNames()}
|
||||
modules={availableModules()}
|
||||
shipMass={ship.hullMass}
|
||||
m={m}
|
||||
ship={ship}
|
||||
onSelect={onSelect}
|
||||
warning={warning}
|
||||
diffDetails={diffDetails.bind(ship, this.context.language)}
|
||||
|
||||
@@ -195,6 +195,15 @@ export default class SlotSection extends TranslatedComponent {
|
||||
if (targetSlot && canMount(this.props.ship, targetSlot, m.grp, m.class)) {
|
||||
const mCopy = m.clone();
|
||||
this.props.ship.use(targetSlot, mCopy, false);
|
||||
let experimentalNum = this.props.ship.hardpoints
|
||||
.filter(s => s.m && s.m.experimental).length;
|
||||
// Remove the module on the last slot if we now exceed the number of
|
||||
// experimentals allowed
|
||||
if (m.experimental && 4 < experimentalNum) {
|
||||
this.props.ship.updateStats(originSlot, null, originSlot.m);
|
||||
originSlot.m = null; // Empty the slot
|
||||
originSlot.discountedCost = 0;
|
||||
}
|
||||
// Copy power info
|
||||
targetSlot.enabled = originSlot.enabled;
|
||||
targetSlot.priority = originSlot.priority;
|
||||
|
||||
@@ -109,8 +109,8 @@ export default class StandardSlot extends TranslatedComponent {
|
||||
menu = <AvailableModulesMenu
|
||||
className='standard'
|
||||
modules={modules}
|
||||
shipMass={ModuleUtils.isShieldGenerator(m.grp) ? ship.hullMass : ship.unladenMass}
|
||||
m={m}
|
||||
ship={ship}
|
||||
onSelect={onSelect}
|
||||
warning={warning}
|
||||
diffDetails={diffDetails.bind(ship, this.context.language)}
|
||||
|
||||
@@ -247,7 +247,8 @@ export class OrbisIcon extends SvgIcon {
|
||||
<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>);
|
||||
</g>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React from 'react';
|
||||
import cn from 'classnames';
|
||||
import SlotSection from './SlotSection';
|
||||
import HardpointSlot from './HardpointSlot';
|
||||
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
||||
@@ -8,7 +7,6 @@ import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
||||
* Utility Slot Section
|
||||
*/
|
||||
export default class UtilitySlotSection extends SlotSection {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param {Object} props React Component properties
|
||||
@@ -133,5 +131,4 @@ export default class UtilitySlotSection extends SlotSection {
|
||||
</ul>
|
||||
</div>;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import React, { PropTypes } from 'react';
|
||||
import ContainerDimensions from 'react-container-dimensions';
|
||||
import { BarChart, Bar, XAxis, YAxis } from 'recharts';
|
||||
import { BarChart, Bar, XAxis, YAxis, LabelList } from 'recharts';
|
||||
|
||||
const CORIOLIS_COLOURS = ['#FF8C0D', '#1FB0FF', '#71A052', '#D5D54D'];
|
||||
const LABEL_COLOUR = '#000000';
|
||||
@@ -17,7 +17,6 @@ const merge = function(one, two) {
|
||||
* A vertical bar chart
|
||||
*/
|
||||
export default class VerticalBarChart extends TranslatedComponent {
|
||||
|
||||
static propTypes = {
|
||||
data : PropTypes.array.isRequired,
|
||||
yMax : PropTypes.number
|
||||
@@ -54,7 +53,9 @@ export default class VerticalBarChart extends TranslatedComponent {
|
||||
<BarChart width={width} height={width * ASPECT} data={this.props.data} margin={{ top: 5, right: 5, left: 5, bottom: 5 }}>
|
||||
<XAxis interval={0} fontSize='0.8em' stroke={AXIS_COLOUR} dataKey='label' />
|
||||
<YAxis interval={'preserveStart'} tickCount={11} fontSize='0.8em' stroke={AXIS_COLOUR} type='number' domain={[0, localMax]}/>
|
||||
<Bar dataKey='value' label={<ValueLabel />} fill={CORIOLIS_COLOURS[0]} isAnimationActive={false} onMouseOver={this._termtip} onMouseOut={tooltip.bind(null, null)}/>
|
||||
<Bar dataKey='value' fill={CORIOLIS_COLOURS[0]} isAnimationActive={false} onMouseOver={this._termtip} onMouseOut={tooltip.bind(null, null)}>
|
||||
<LabelList dataKey='value' position='insideTop'/>
|
||||
</Bar>
|
||||
</BarChart>
|
||||
</div>
|
||||
)}
|
||||
@@ -77,29 +78,3 @@ export default class VerticalBarChart extends TranslatedComponent {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A label that displays the value within the bar of the chart
|
||||
*/
|
||||
class ValueLabel extends React.Component {
|
||||
static propTypes = {
|
||||
x: PropTypes.number,
|
||||
y: PropTypes.number,
|
||||
payload: PropTypes.object,
|
||||
value: PropTypes.number
|
||||
};
|
||||
|
||||
/**
|
||||
* Render offence
|
||||
* @return {React.Component} contents
|
||||
*/
|
||||
render() {
|
||||
const { x, y, payload, value } = this.props;
|
||||
|
||||
const em = value < 1000 ? '1em' : value < 1000 ? '0.8em' : '0.7em';
|
||||
|
||||
return (
|
||||
<text x={x} y={y} fill="#000000" textAnchor="middle" dy={20} style={{ fontSize: em }}>{value}</text>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import { Ships } from 'coriolis-data/dist';
|
||||
import { nameComparator } from '../utils/SlotFunctions';
|
||||
import { CollapseSection, ExpandSection, MountFixed, MountGimballed, MountTurret } from './SvgIcons';
|
||||
import LineChart from '../components/LineChart';
|
||||
import Slider from '../components/Slider';
|
||||
import * as Calc from '../shipyard/Calculations';
|
||||
import Module from '../shipyard/Module';
|
||||
|
||||
const DAMAGE_DEALT_COLORS = ['#FFFFFF', '#FF0000', '#00FF00', '#7777FF', '#FFFF00', '#FF00FF', '#00FFFF', '#777777'];
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import * as IT from './it';
|
||||
import * as RU from './ru';
|
||||
import * as PL from './pl';
|
||||
import * as PT from './pt';
|
||||
import * as CN from './cn';
|
||||
import * as d3 from 'd3';
|
||||
|
||||
let fallbackTerms = EN.terms;
|
||||
@@ -27,6 +28,7 @@ export function getLanguage(langCode) {
|
||||
case 'ru': lang = RU; break;
|
||||
case 'pl': lang = PL; break;
|
||||
case 'pt': lang = PT; break;
|
||||
case 'cn': lang = CN; break;
|
||||
default:
|
||||
lang = EN;
|
||||
}
|
||||
@@ -94,5 +96,6 @@ export const Languages = {
|
||||
fr: 'Français',
|
||||
ru: 'ру́сский',
|
||||
pl: 'polski',
|
||||
pt: 'português'
|
||||
pt: 'português',
|
||||
cn: '中文'
|
||||
};
|
||||
|
||||
16
src/app/i18n/cn.js
Normal file
16
src/app/i18n/cn.js
Normal file
@@ -0,0 +1,16 @@
|
||||
export const formats = {
|
||||
decimal: '.',
|
||||
thousands: ',',
|
||||
grouping: [3],
|
||||
currency: ['¥', ''],
|
||||
dateTime: '%a %b %e %X %Y',
|
||||
date: '%Y年%m月%d日',
|
||||
time: '%H:%M:%S',
|
||||
periods: ['AM', 'PM'],
|
||||
days: ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'],
|
||||
shortDays: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
|
||||
months: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
|
||||
shortMonths: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月']
|
||||
};
|
||||
|
||||
export { default as terms } from './cn.json';
|
||||
405
src/app/i18n/cn.json
Normal file
405
src/app/i18n/cn.json
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -59,7 +59,7 @@
|
||||
"empty all": "vide tout",
|
||||
"Enter Name": "Entrer nom",
|
||||
"Explorer": "explorateur",
|
||||
"fastest range": "gamme la plus rapide",
|
||||
"farthest range": "gamme la plus rapide",
|
||||
"fuel": "carburant",
|
||||
"fuel level": "niveau de carburant",
|
||||
"full tank": "Réservoir plein",
|
||||
|
||||
@@ -2,15 +2,15 @@ export const formats = {
|
||||
decimal: ',',
|
||||
thousands: '.',
|
||||
grouping: [3],
|
||||
currency: ['', ' €'],
|
||||
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']
|
||||
days: ['domingo', 'segunda', 'terça', 'quarta', 'quinta', 'sexta', 'sábado'],
|
||||
shortDays: ['dom', 'seg', 'ter', 'qua', 'qui', 'sex', 'sab'],
|
||||
months: ['janeiro', 'fevereiro', 'março', 'abril', 'maio', 'junho', 'julho', 'agosto', 'setembro', 'outubro', 'novembro', 'dezembro'],
|
||||
shortMonths: ['jan', 'fev', 'mar', 'abr', 'mai', 'jun', 'jul', 'ago', 'set', 'out', 'nov', 'dez']
|
||||
};
|
||||
|
||||
export { default as terms } from './pt.json';
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -16,13 +16,16 @@
|
||||
"PHRASE_ENGAGEMENT_RANGE": "Дистанция между кораблём и целью",
|
||||
"PHRASE_SELECT_BLUEPRINT": "Нажмите чтобы выбрать чертёж",
|
||||
"PHRASE_BLUEPRINT_WORST": "Худшие основные значения для чертежа",
|
||||
"PHRASE_BLUEPRINT_FIFTY": "50% значения для чертежа",
|
||||
"PHRASE_BLUEPRINT_SEVEN_FIVE": "75% значения для чертежа",
|
||||
"PHRASE_BLUEPRINT_RANDOM": "Случайный выбор между худшими и лучшими значениями для этого чертежа",
|
||||
"PHRASE_BLUEPRINT_BEST": "Лучшие основные значения для чертежа",
|
||||
"PHRASE_BLUEPRINT_EXTREME": "Лучшие положительные и худшие отрицательные основные значения для чертежа",
|
||||
"PHRASE_BLUEPRINT_RESET": "Убрать все изменения и чертёж",
|
||||
"PHRASE_BLUEPRINT_RESET": "Сбросить все модификаторы и чертеж",
|
||||
"PHRASE_SELECT_SPECIAL": "Нажмите, чтобы выбрать экспериментальный эффект",
|
||||
"PHRASE_NO_SPECIAL": "Без экспериментального эффекта",
|
||||
"PHRASE_SHOPPING_LIST": "Станции, что продают эту сборку",
|
||||
"PHRASE_SHOPPING_MATS": "Материалы которые нужны для сборки",
|
||||
"PHRASE_REFIT_SHOPPING_LIST": "Станции, что продают необходимые модули",
|
||||
"PHRASE_TOTAL_EFFECTIVE_SHIELD": "Общий урон, что может быть нанесён в каждым типе, если используются все щитонакопители",
|
||||
"PHRASE_TIME_TO_LOSE_SHIELDS": "Щиты продержатся",
|
||||
@@ -36,9 +39,11 @@
|
||||
"PHRASE_TIME_TO_LOSE_ARMOUR": "Броня продержится",
|
||||
"PHRASE_MODULE_PROTECTION_EXTERNAL": "Защита гнёзд",
|
||||
"PHRASE_MODULE_PROTECTION_INTERNAL": "Защита всех остальных модулей",
|
||||
"PHRASE_OVERALL_DAMAGE": "Разбивка источников устойчивого ДПС",
|
||||
"PHRASE_SHIELD_DAMAGE": "Подробности источников поддерживаемого ДПС против щитов",
|
||||
"PHRASE_ARMOUR_DAMAGE": "Подробности источников поддерживаемого ДПС против брони",
|
||||
"PHRASE_TIME_TO_REMOVE_SHIELDS": "Снимет щиты за",
|
||||
"PHRASE_MULTI_CREW_CAPACITOR_POINTS": "Щелкните правой кновкой мыши чтобы объединить в группу.",
|
||||
"TT_TIME_TO_REMOVE_SHIELDS": "Непрерывным огнём из всех орудий",
|
||||
"PHRASE_TIME_TO_REMOVE_ARMOUR": "Снимет броню за",
|
||||
"TT_TIME_TO_REMOVE_ARMOUR": "Непрерывным огнём из всех орудий",
|
||||
@@ -58,8 +63,10 @@
|
||||
"TT_SUMMARY_SPEED": "С полным топливным баком и 4 пунктами в ДВИ",
|
||||
"TT_SUMMARY_SPEED_NONFUNCTIONAL": "Маневровые двигатели выключены или превышена максимальная масса с топливом и грузом",
|
||||
"TT_SUMMARY_BOOST": "С полным топливным баком и 4 пунктами в ДВИ",
|
||||
"TT_SUMMARY_BOOST_INTERVAL": "Время заполнения с 4 пунктами в СИС",
|
||||
"TT_SUMMARY_BOOST_NONFUNCTIONAL": "Распределитель питания не может обеспечить достаточно энергии для форсажа",
|
||||
"TT_SUMMARY_SHIELDS": "Чистая сила щита, включая усилители",
|
||||
"TT_SUMMARY_SHIELDS_SCB": "Прочность щита, включая бустеры и SCB",
|
||||
"TT_SUMMARY_SHIELDS_NONFUNCTIONAL": "Шитогенератор отсутствует или выключен",
|
||||
"TT_SUMMARY_INTEGRITY": "Целостность корабля, включая переборки и наборы для усиления корпуса",
|
||||
"TT_SUMMARY_HULL_MASS": "Масса корпуса без каких-либо модулей",
|
||||
@@ -74,11 +81,14 @@
|
||||
"TT_SUMMARY_UNLADEN_TOTAL_JUMP": "Самая дальняя общая дистанция без груза, с полным топливным баком и при прыжках на максимальное расстояние",
|
||||
"TT_SUMMARY_LADEN_TOTAL_JUMP": "Самая дальняя общая дистанция с полным грузовым отсеком, с полным топливным баком и при прыжках на максимальное расстояние",
|
||||
"HELP_MODIFICATIONS_MENU": "Нажмите на номер, чтобы ввести новое значение, или потяните вдоль полосы для малых изменений",
|
||||
"PHRASE_FAIL_EDENGINEER": "Не удалось отправить в EDEngineer (запустите EDEngineer и убедитесь, что API запущен, затем обновите страницу).",
|
||||
"PHRASE_FIREFOX_EDENGINEER": "Отправка в EDEngineer несовместима с настройками безопасности Firefox. Пожалуйста, попробуйте еще раз в Google Chrome.",
|
||||
"am": "Блок Автом. Полевого Ремонта",
|
||||
"bh": "Переборки",
|
||||
"bl": "Пучковый лазер",
|
||||
"bsg": "Двухпоточный щитогенератор",
|
||||
"c": "Орудие",
|
||||
"causres": "Caustic resistance",
|
||||
"cc": "Контроллер магнитного снаряда для сбора",
|
||||
"ch": "Разбрасыватель дипольных отражателей",
|
||||
"cr": "Грузовой стеллаж",
|
||||
@@ -98,14 +108,17 @@
|
||||
"kw": "Сканер преступников",
|
||||
"ls": "Система жизнеобеспечения",
|
||||
"mc": "Многоствольное орудие",
|
||||
"axmc": "Многоствольное орудие АИ",
|
||||
"ml": "Проходочный лазер",
|
||||
"mr": "Ракетный лоток",
|
||||
"axmr": "Блок ракет АИ",
|
||||
"mrp": "Набор для усиления модуля",
|
||||
"nl": "Мины",
|
||||
"pa": "Ускоритель плазмы",
|
||||
"pas": "Комплект для сближения с планетой",
|
||||
"pc": "Контроллер магнитного снаряда для геологоразведки",
|
||||
"pce": "Каюта пассажира эконом-класса",
|
||||
"passenger capacity": "количество пассажиров",
|
||||
"pci": "Каюта пассажира бизнес-класса",
|
||||
"pcm": "Каюта пассажира первого класса",
|
||||
"pcq": "Каюта пассажира класса люкс",
|
||||
@@ -113,20 +126,46 @@
|
||||
"pl": "Импульсный лазер",
|
||||
"po": "Точечная оборона",
|
||||
"pp": "Силовая установка",
|
||||
"gpp": "Силовая установка Cтражей",
|
||||
"gpd": "Гибридный распределитель питания Стражей",
|
||||
"gpc": "Плазменная пушка Стражей",
|
||||
"ggc": "Пушка Гаусса Стражей",
|
||||
"gsrp": "Набор для усиления щита Стражей",
|
||||
"gfsb": "Ускоритель FSD Стражей",
|
||||
"ghrp": "Набор для усиления корпуса Стражей",
|
||||
"gmrp": "Набор для усиления модуля Стражей",
|
||||
"pwa": "Анализатор импульсных волн",
|
||||
"abl": "Абразивный бластер",
|
||||
"scl": "Пусковая установка для сейсмических снарядов",
|
||||
"sdm": "Вытесняющая ракета для добычи глубинных залежей",
|
||||
"tbsc": "Шоковое орудие",
|
||||
"gsc": "Осколочное орудие Стражей",
|
||||
"psg": "Призматический щитогенератор",
|
||||
"pv": "Гараж для планетарного транспорта",
|
||||
"rf": "Устройство переработки",
|
||||
"rfl": "Зенитная установка (снаряды с дистанционным подрывом)",
|
||||
"rg": "Электромагнитная пушка",
|
||||
"rsl": "Дроны-исследователи",
|
||||
"s": "Сенсоры",
|
||||
"sb": "Усилитель щита",
|
||||
"sc": "Сканер обнаружения",
|
||||
"scb": "Щитонакопитель",
|
||||
"sfn": "Нейтрализатор глушащего поля",
|
||||
"sg": "Щитогенератор",
|
||||
"ss": "Сканер поверхностей",
|
||||
"sua": "Помощь в гиперкрейсерском режиме",
|
||||
"t": "Маневровые двигатели",
|
||||
"tp": "Торпедная стойка",
|
||||
"ul": "Пульсирующие лазеры",
|
||||
"Send To EDEngineer": "Отправить в EDEngineer",
|
||||
"ws": "Сканер следа FSD",
|
||||
"rpl": "Дроны-ремонтники",
|
||||
"rcpl": "ДРоны-разведчики",
|
||||
"xs": "Сканер «инопланетянин»",
|
||||
"tbem": "Блок энзимных ракет",
|
||||
"tbrfl": "Установка для стрельбы стреловидными снарядами с дистанционным подрывом",
|
||||
"dtl": "Дроны-очистители",
|
||||
"mahr": "Набор для усиления корпуса из Метасплава",
|
||||
"emptyrestricted": "пусто (ограниченно)",
|
||||
"damage dealt to": "Урон нанесён",
|
||||
"damage received from": "Урон получен от",
|
||||
@@ -136,6 +175,7 @@
|
||||
"ammunition": "Припасы",
|
||||
"secs": "с",
|
||||
"rebuildsperbay": "Построек за полосу",
|
||||
"mroll": "Roll",
|
||||
"worst": "Худшее",
|
||||
"average": "Среднее",
|
||||
"random": "Случайное",
|
||||
@@ -148,6 +188,7 @@
|
||||
"dpssdps": "Урон в секунду (поддерживаемый урон в секунду)",
|
||||
"eps": "Энергия в секунду",
|
||||
"epsseps": "Энергия в секунду (поддерживаемая энергия в секунду)",
|
||||
"fallofffromrange": "Спад",
|
||||
"hps": "Нагрев в секунду",
|
||||
"hpsshps": "Нагрев в секунду (поддерживаемый нагрев в секунду)",
|
||||
"damage by": "Урон",
|
||||
@@ -164,13 +205,15 @@
|
||||
"internal protection": "Внутренняя защита",
|
||||
"external protection": "Внешняя защита",
|
||||
"engagement range": "Боевое расстояние",
|
||||
"boost interval": "Интервал повышения",
|
||||
"total": "Всего",
|
||||
"ammo": "Боекомплект",
|
||||
"ammo": "Макс. боекомплект",
|
||||
"boot": "Время загрузки",
|
||||
"hacktime": "Время взлома",
|
||||
"brokenregen": "Скорость восстановления при пробое",
|
||||
"burst": "Длина очереди",
|
||||
"burstrof": "Скорострельность очереди",
|
||||
"clip": "Боекомплект",
|
||||
"clip": "Размер боекомплекта",
|
||||
"damage": "Урон",
|
||||
"distdraw": "Тяга распределителя",
|
||||
"duration": "Продолжительность",
|
||||
@@ -199,11 +242,14 @@
|
||||
"rof": "Скорострельность",
|
||||
"angle": "Угол сканера",
|
||||
"scanrate": "Скорость сканера",
|
||||
"proberadius": "Радиус зонда",
|
||||
"scantime": "Время сканирования",
|
||||
"shield": "Щит",
|
||||
"armour": "Броня",
|
||||
"shieldboost": "Усиление щитов",
|
||||
"shieldreinforcement": "Усилитель щита",
|
||||
"shotspeed": "Скорость выстрела",
|
||||
"shotdmg": "Урон за выстрел(DPS)",
|
||||
"spinup": "Время раскрутки",
|
||||
"syscap": "Ресурс систем",
|
||||
"sysrate": "Перезарядка систем",
|
||||
@@ -234,9 +280,12 @@
|
||||
"explosive": "Взрывч.",
|
||||
"kinetic": "Механич.",
|
||||
"thermal": "Тепл.",
|
||||
"caustic": "Каустик",
|
||||
"generator": "Генератор",
|
||||
"boosters": "Усилители",
|
||||
"cells": "Ячейки",
|
||||
"shield addition": "ДОбавления к щиту",
|
||||
"jump addition": "ДОбавления к прыжку",
|
||||
"bulkheads": "Переборки",
|
||||
"reinforcement": "Усилители",
|
||||
"power and costs": "Энергия и стоимость",
|
||||
@@ -269,13 +318,45 @@
|
||||
"opponent": "Противник",
|
||||
"opponent's shields": "Щит противника",
|
||||
"opponent's armour": "Броня противника",
|
||||
"overall damage": "overall damage",
|
||||
"shield damage sources": "источники урона по щиту",
|
||||
"armour damage sources": "источники урона по броне",
|
||||
"never": "Никогда",
|
||||
"stock": "базовый",
|
||||
"boost": "форсаж",
|
||||
"/s": "/с",
|
||||
"m/s": "м/с",
|
||||
"tab_defence": "defence",
|
||||
"federation rank 1": "Рекрут",
|
||||
"federation rank 2": "Кадет",
|
||||
"federation rank 3": "Гардемарин",
|
||||
"federation rank 4": "Старшина",
|
||||
"federation rank 5": "Главный старшина",
|
||||
"federation rank 6": "Уорент-офицер",
|
||||
"federation rank 7": "Энсин",
|
||||
"federation rank 8": "Лейтенант",
|
||||
"federation rank 9": "Капитан-лейтенант",
|
||||
"federation rank 10": "Начальник гарнизона",
|
||||
"federation rank 11": "Командир корабля",
|
||||
"federation rank 12": "Контр-адмирал",
|
||||
"federation rank 13": " Вице-адмирал",
|
||||
"federation rank 14": "Адмирал",
|
||||
"federation rank required": "Минимальный ранг федерации для покупки",
|
||||
"empire rank 1": "Чужак",
|
||||
"empire rank 2": "Крепостной",
|
||||
"empire rank 3": "Мастер",
|
||||
"empire rank 4": "Оруженосец",
|
||||
"empire rank 5": "Рыцарь",
|
||||
"empire rank 6": "Лорд",
|
||||
"empire rank 7": "Барон",
|
||||
"empire rank 8": "Виконт",
|
||||
"empire rank 9": "Граф",
|
||||
"empire rank 10": "Эрл",
|
||||
"empire rank 11": "Маркиз",
|
||||
"empire rank 12": "Герцог",
|
||||
"empire rank 13": "Принц",
|
||||
"empire rank 14": "Король",
|
||||
"empire rank required": "Минимальный ранг империи для покупки",
|
||||
"\/s": "\/с",
|
||||
"m\/s": "м\/с",
|
||||
"Ls": "Св.сек",
|
||||
"LY": "Св.лет",
|
||||
"CR": "кр.",
|
||||
@@ -299,7 +380,7 @@
|
||||
"edit data": "Редактирование",
|
||||
"empty all": "пусто все",
|
||||
"Enter Name": "Введите имя",
|
||||
"fastest range": "быстрый диапазон",
|
||||
"farthest range": "быстрый диапазон",
|
||||
"fuel level": "уровень топлива",
|
||||
"full tank": "Полный бак",
|
||||
"internal compartments": "внутренние отсеки",
|
||||
@@ -315,7 +396,6 @@
|
||||
"about": "О ...",
|
||||
"action": "Действие",
|
||||
"added": "Добавлено",
|
||||
"armour": "Броня",
|
||||
"available": "доступно",
|
||||
"backup": "Резервная копия",
|
||||
"bins": "контейнеры",
|
||||
@@ -363,7 +443,7 @@
|
||||
"repair": "Починка",
|
||||
"ret": "Убр.",
|
||||
"retracted": "Убрано",
|
||||
"ROF": "В/сек",
|
||||
"ROF": "В\/сек",
|
||||
"save": "Сохранить",
|
||||
"sell": "Продать",
|
||||
"settings": "Настройки",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'babel-polyfill';
|
||||
import '@babel/polyfill';
|
||||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import '../less/app.less';
|
||||
|
||||
@@ -6,7 +6,6 @@ import { CoriolisLogo, GitHub } from '../components/SvgIcons';
|
||||
* About Page
|
||||
*/
|
||||
export default class AboutPage extends Page {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param {Object} props React Component properties
|
||||
@@ -23,33 +22,79 @@ export default class AboutPage extends Page {
|
||||
* @return {React.Component} The page contents
|
||||
*/
|
||||
renderPage() {
|
||||
return <div className={'page'} style={{ textAlign: 'left', maxWidth: 800, margin: '0 auto' }}>
|
||||
<h1><CoriolisLogo style={{ marginRight: '0.4em' }} className='xl'/><span className='warning'>Coriolis EDCD Edition</span></h1>
|
||||
return (
|
||||
<div
|
||||
className={'page'}
|
||||
style={{ textAlign: 'left', maxWidth: 800, margin: '0 auto' }}
|
||||
>
|
||||
<h1>
|
||||
<CoriolisLogo style={{ marginRight: '0.4em' }} className="xl" />
|
||||
<span className="warning">Coriolis EDCD Edition</span>
|
||||
</h1>
|
||||
|
||||
<p>This is a clone of the Coriolis project, whose original author is currently unable to maintain it. This clone is maintained by the <a href="http://edcd.github.io/">EDCD community</a>.</p>
|
||||
<p>To recover your builds, go to <a href='https://coriolis.io/' target='_blank'>https://coriolis.io/</a>, backup your builds (Settings / Backup), copy the text, return here and import (Settings / Import).</p>
|
||||
<p>The Coriolis project was inspired by <a href='http://www.edshipyard.com/' target='_blank'>E:D Shipyard</a> and, of course, <a href='http://www.elitedangerous.com' target='_blank'>Elite Dangerous</a>. The ultimate goal of Coriolis is to provide rich features to support in-game play and planning while engaging the E:D community to support its development.</p>
|
||||
<p>Coriolis was created using assets and imagery from Elite: Dangerous, with the permission of Frontier Developments plc, for non-commercial purposes. It is not endorsed by nor reflects the views or opinions of Frontier Developments. A number of assets were sourced from <a href='http://edassets.org' target='_blank'>ED Assets</a></p>
|
||||
<p>
|
||||
This is a clone of the Coriolis project, whose original author is
|
||||
currently unable to maintain it. This clone is maintained by the{' '}
|
||||
<a href="http://edcd.github.io/">EDCD community</a>.
|
||||
</p>
|
||||
<p>
|
||||
To recover your builds, go to{' '}
|
||||
<a href="https://coriolis.io/" target="_blank">
|
||||
https://coriolis.io/
|
||||
</a>
|
||||
, backup your builds (Settings / Backup), copy the text, return here
|
||||
and import (Settings / Import).
|
||||
</p>
|
||||
<p>
|
||||
The Coriolis project was inspired by{' '}
|
||||
<a href="http://www.edshipyard.com/" target="_blank">
|
||||
E:D Shipyard
|
||||
</a>{' '}
|
||||
and, of course,{' '}
|
||||
<a href="http://www.elitedangerous.com" target="_blank">
|
||||
Elite Dangerous
|
||||
</a>
|
||||
. The ultimate goal of Coriolis is to provide rich features to support
|
||||
in-game play and planning while engaging the E:D community to support
|
||||
its development.
|
||||
</p>
|
||||
<p>
|
||||
Coriolis was created using assets and imagery from Elite: Dangerous,
|
||||
with the permission of Frontier Developments plc, for non-commercial
|
||||
purposes. It is not endorsed by nor reflects the views or opinions of
|
||||
Frontier Developments. A number of assets were sourced from{' '}
|
||||
<a href="http://edassets.org" target="_blank">
|
||||
ED Assets
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<a style={{ display: 'block', textDecoration: 'none' }} href='https://github.com/EDCD/coriolis' target='_blank' title='Coriolis Github Project'>
|
||||
<GitHub style={{ margin: '0.4em' }} className='l fg xl'/>
|
||||
<h2 style={{ margin: 0, textDecoration: 'none' }}>Github</h2>
|
||||
github.com/EDCD/coriolis
|
||||
</a>
|
||||
<a
|
||||
style={{ display: 'block', textDecoration: 'none' }}
|
||||
href="https://github.com/EDCD/coriolis"
|
||||
target="_blank"
|
||||
title="Coriolis Github Project"
|
||||
>
|
||||
<GitHub style={{ margin: '0.4em' }} className="l fg xl" />
|
||||
<h2 style={{ margin: 0, textDecoration: 'none' }}>Github</h2>
|
||||
github.com/EDCD/coriolis
|
||||
</a>
|
||||
|
||||
<p>Coriolis is an open source project. Checkout the list of upcoming features and to-do list on github. Any and all contributions and feedback are welcome. If you encounter any bugs please report them and provide as much detail as possible.</p>
|
||||
<p>
|
||||
Coriolis is an open source project. Checkout the list of upcoming
|
||||
features and to-do list on github. Any and all contributions and
|
||||
feedback are welcome. If you encounter any bugs please report them and
|
||||
provide as much detail as possible.
|
||||
</p>
|
||||
|
||||
<h3>Chat</h3>
|
||||
<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>;
|
||||
<h3>Chat</h3>
|
||||
<p>
|
||||
You can chat to us on our{' '}
|
||||
<a href="https://discord.gg/0uwCh6R62aPRjk9w" target="_blank">
|
||||
EDCD Discord server
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,14 @@ import ModalCompare from '../components/ModalCompare';
|
||||
import ModalExport from '../components/ModalExport';
|
||||
import ModalPermalink from '../components/ModalPermalink';
|
||||
import ModalImport from '../components/ModalImport';
|
||||
import { FloppyDisk, Bin, Download, Embed, Rocket, LinkIcon } from '../components/SvgIcons';
|
||||
import {
|
||||
FloppyDisk,
|
||||
Bin,
|
||||
Download,
|
||||
Embed,
|
||||
Rocket,
|
||||
LinkIcon
|
||||
} from '../components/SvgIcons';
|
||||
import ShortenUrl from '../utils/ShortenUrl';
|
||||
import { comparisonBBCode } from '../utils/BBCode';
|
||||
const browser = require('detect-browser');
|
||||
@@ -42,7 +49,6 @@ function sortBy(predicate) {
|
||||
* Comparison Page
|
||||
*/
|
||||
export default class ComparisonPage extends Page {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param {Object} props React Component properties
|
||||
@@ -81,7 +87,13 @@ export default class ComparisonPage extends Page {
|
||||
for (let shipId in allBuilds) {
|
||||
for (let buildName in allBuilds[shipId]) {
|
||||
if (buildName && allBuilds[shipId][buildName]) {
|
||||
builds.push(this._createBuild(shipId, buildName, allBuilds[shipId][buildName]));
|
||||
builds.push(
|
||||
this._createBuild(
|
||||
shipId,
|
||||
buildName,
|
||||
allBuilds[shipId][buildName]
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -89,7 +101,9 @@ export default class ComparisonPage extends Page {
|
||||
let comparisonData = Persist.getComparison(name);
|
||||
if (comparisonData) {
|
||||
defaultFacets = comparisonData.facets;
|
||||
comparisonData.builds.forEach((b) => builds.push(this._createBuild(b.shipId, b.buildName)));
|
||||
comparisonData.builds.forEach(b =>
|
||||
builds.push(this._createBuild(b.shipId, b.buildName))
|
||||
);
|
||||
saved = true;
|
||||
newName = name;
|
||||
}
|
||||
@@ -101,7 +115,7 @@ export default class ComparisonPage extends Page {
|
||||
newName = name = comparisonData.n;
|
||||
predicate = comparisonData.p;
|
||||
desc = comparisonData.d;
|
||||
comparisonData.b.forEach((build) => {
|
||||
comparisonData.b.forEach(build => {
|
||||
builds.push(this._createBuild(build.s, build.n, build.c));
|
||||
if (!importObj[build.s]) {
|
||||
importObj[build.s] = {};
|
||||
@@ -118,9 +132,9 @@ export default class ComparisonPage extends Page {
|
||||
let selectedFacets = new Array(selectedLength);
|
||||
|
||||
for (let i = 0; i < ShipFacets.length; i++) {
|
||||
let facet = Object.assign({ }, ShipFacets[i]);
|
||||
let facet = Object.assign({}, ShipFacets[i]);
|
||||
let defaultIndex = defaultFacets.indexOf(facet.i);
|
||||
if(defaultIndex == -1) {
|
||||
if (defaultIndex == -1) {
|
||||
facets.push(facet);
|
||||
} else {
|
||||
facet.active = true;
|
||||
@@ -155,17 +169,18 @@ export default class ComparisonPage extends Page {
|
||||
_createBuild(id, name, code) {
|
||||
code = code ? code : Persist.getBuild(id, name); // Retrieve build code if not passed
|
||||
|
||||
if (!code) { // No build found
|
||||
if (!code) {
|
||||
// No build found
|
||||
return;
|
||||
}
|
||||
|
||||
let data = Ships[id]; // Get ship properties
|
||||
let data = Ships[id]; // Get ship properties
|
||||
let b = new Ship(id, data.properties, data.slots); // Create a new Ship instance
|
||||
b.buildFrom(code); // Populate components from code
|
||||
b.buildFrom(code); // Populate components from code
|
||||
b.buildName = name;
|
||||
b.applyDiscounts(Persist.getShipDiscount(), Persist.getModuleDiscount());
|
||||
return b;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Update state with the specified sort predicates
|
||||
@@ -184,13 +199,18 @@ export default class ComparisonPage extends Page {
|
||||
}
|
||||
|
||||
this.setState({ predicate, desc });
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Show selected builds modal
|
||||
*/
|
||||
_selectBuilds() {
|
||||
this.context.showModal(<ModalCompare onSelect={this._buildsSelected} builds={this.state.builds} />);
|
||||
this.context.showModal(
|
||||
<ModalCompare
|
||||
onSelect={this._buildsSelected}
|
||||
builds={this.state.builds}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -224,7 +244,7 @@ export default class ComparisonPage extends Page {
|
||||
_facetDrag(e) {
|
||||
this.nodeAfter = false;
|
||||
this.dragged = e.currentTarget;
|
||||
let placeholder = this.placeholder = document.createElement('li');
|
||||
let placeholder = (this.placeholder = document.createElement('li'));
|
||||
placeholder.style.width = Math.round(this.dragged.offsetWidth) + 'px';
|
||||
placeholder.className = 'facet-placeholder';
|
||||
if (!browser || (browser.name !== 'edge' && browser.name !== 'ie')) {
|
||||
@@ -262,7 +282,7 @@ export default class ComparisonPage extends Page {
|
||||
_facetDragOver(e) {
|
||||
e.preventDefault();
|
||||
|
||||
if(e.target.className == 'facet-placeholder') {
|
||||
if (e.target.className == 'facet-placeholder') {
|
||||
return;
|
||||
} else if (e.target != e.currentTarget) {
|
||||
this.over = e.target;
|
||||
@@ -272,7 +292,7 @@ export default class ComparisonPage extends Page {
|
||||
let parent = e.target.parentNode;
|
||||
|
||||
if (parent == e.currentTarget) {
|
||||
if(relX > width && this.dragged != e.target) {
|
||||
if (relX > width && this.dragged != e.target) {
|
||||
this.nodeAfter = true;
|
||||
parent.insertBefore(this.placeholder, e.target.nextElementSibling);
|
||||
} else {
|
||||
@@ -321,7 +341,7 @@ export default class ComparisonPage extends Page {
|
||||
let { newName, builds, facets } = this.state;
|
||||
let selectedFacets = [];
|
||||
|
||||
facets.forEach((f) => {
|
||||
facets.forEach(f => {
|
||||
if (f.active) {
|
||||
selectedFacets.unshift(f.i);
|
||||
}
|
||||
@@ -348,14 +368,20 @@ export default class ComparisonPage extends Page {
|
||||
|
||||
let code = fromComparison(name, builds, selectedFacets, predicate, desc);
|
||||
let loc = window.location;
|
||||
return loc.protocol + '//' + loc.host + '/comparison?code=' + encodeURIComponent(code);
|
||||
return (
|
||||
loc.protocol +
|
||||
'//' +
|
||||
loc.host +
|
||||
'/comparison?code=' +
|
||||
encodeURIComponent(code)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the long permalink URL
|
||||
*/
|
||||
_genPermalink() {
|
||||
this.context.showModal(<ModalPermalink url={this._buildUrl()}/>);
|
||||
this.context.showModal(<ModalPermalink url={this._buildUrl()} />);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -365,18 +391,25 @@ export default class ComparisonPage extends Page {
|
||||
let { translate, formats } = this.context.language;
|
||||
let { facets, builds } = this.state;
|
||||
|
||||
let generator = (callback) => {
|
||||
let generator = callback => {
|
||||
let url = this._buildUrl();
|
||||
ShortenUrl(url,
|
||||
(shortenedUrl) => callback(comparisonBBCode(translate, formats, facets, builds, shortenedUrl)),
|
||||
(error) => callback(comparisonBBCode(translate, formats, facets, builds, url))
|
||||
ShortenUrl(
|
||||
url,
|
||||
shortenedUrl =>
|
||||
callback(
|
||||
comparisonBBCode(translate, formats, facets, builds, shortenedUrl)
|
||||
),
|
||||
error =>
|
||||
callback(comparisonBBCode(translate, formats, facets, builds, url))
|
||||
);
|
||||
};
|
||||
|
||||
this.context.showModal(<ModalExport
|
||||
title={translate('forum') + ' BBCode'}
|
||||
generator={generator}
|
||||
/>);
|
||||
this.context.showModal(
|
||||
<ModalExport
|
||||
title={translate('forum') + ' BBCode'}
|
||||
generator={generator}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -409,7 +442,8 @@ export default class ComparisonPage extends Page {
|
||||
* @param {Object} nextContext Incoming/Next conext
|
||||
*/
|
||||
componentWillReceiveProps(nextProps, nextContext) {
|
||||
if (this.context.route !== nextContext.route) { // Only reinit state if the route has changed
|
||||
if (this.context.route !== nextContext.route) {
|
||||
// Only reinit state if the route has changed
|
||||
this.setState(this._initState(nextContext));
|
||||
}
|
||||
}
|
||||
@@ -419,7 +453,10 @@ export default class ComparisonPage extends Page {
|
||||
*/
|
||||
componentWillMount() {
|
||||
this.resizeListener = this.context.onWindowResize(this._updateDimensions);
|
||||
this.persistListener = Persist.addListener('discounts', this._updateDiscounts);
|
||||
this.persistListener = Persist.addListener(
|
||||
'discounts',
|
||||
this._updateDiscounts
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -444,65 +481,132 @@ export default class ComparisonPage extends Page {
|
||||
renderPage() {
|
||||
let translate = this.context.language.translate;
|
||||
let compareHeader;
|
||||
let { newName, name, saved, builds, facets, predicate, desc, chartWidth } = this.state;
|
||||
let {
|
||||
newName,
|
||||
name,
|
||||
saved,
|
||||
builds,
|
||||
facets,
|
||||
predicate,
|
||||
desc,
|
||||
chartWidth
|
||||
} = this.state;
|
||||
|
||||
if (this.state.compareMode) {
|
||||
compareHeader = <tr>
|
||||
<td className='head'>{translate('comparison')}</td>
|
||||
<td>
|
||||
<input value={newName} onChange={this._onNameChange} placeholder={translate('Enter Name')} maxLength='50' />
|
||||
<button onClick={this._save} disabled={!newName || newName == 'all' || saved}>
|
||||
<FloppyDisk className='lg'/><span className='button-lbl'>{translate('save')}</span>
|
||||
</button>
|
||||
<button onClick={this._delete} disabled={name == 'all' || !saved}><Bin className='lg warning'/></button>
|
||||
<button onClick={this._selectBuilds}>
|
||||
<Rocket className='lg'/><span className='button-lbl'>{translate('builds')}</span>
|
||||
</button>
|
||||
<button className='r' onClick={this._genPermalink} disabled={builds.length == 0}>
|
||||
<LinkIcon className='lg'/><span className='button-lbl'>{translate('permalink')}</span>
|
||||
</button>
|
||||
<button className='r' onClick={this._genBBcode} disabled={builds.length == 0}>
|
||||
<Embed className='lg'/><span className='button-lbl'>{translate('forum')}</span>
|
||||
</button>
|
||||
</td>
|
||||
</tr>;
|
||||
compareHeader = (
|
||||
<tr>
|
||||
<td className="head">{translate('comparison')}</td>
|
||||
<td>
|
||||
<input
|
||||
value={newName}
|
||||
onChange={this._onNameChange}
|
||||
placeholder={translate('Enter Name')}
|
||||
maxLength="50"
|
||||
/>
|
||||
<button
|
||||
onClick={this._save}
|
||||
disabled={!newName || newName == 'all' || saved}
|
||||
>
|
||||
<FloppyDisk className="lg" />
|
||||
<span className="button-lbl">{translate('save')}</span>
|
||||
</button>
|
||||
<button onClick={this._delete} disabled={name == 'all' || !saved}>
|
||||
<Bin className="lg warning" />
|
||||
</button>
|
||||
<button onClick={this._selectBuilds}>
|
||||
<Rocket className="lg" />
|
||||
<span className="button-lbl">{translate('builds')}</span>
|
||||
</button>
|
||||
<button
|
||||
className="r"
|
||||
onClick={this._genPermalink}
|
||||
disabled={builds.length == 0}
|
||||
>
|
||||
<LinkIcon className="lg" />
|
||||
<span className="button-lbl">{translate('permalink')}</span>
|
||||
</button>
|
||||
<button
|
||||
className="r"
|
||||
onClick={this._genBBcode}
|
||||
disabled={builds.length == 0}
|
||||
>
|
||||
<Embed className="lg" />
|
||||
<span className="button-lbl">{translate('forum')}</span>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
} else {
|
||||
compareHeader = <tr>
|
||||
<td className='head'>{translate('comparison')}</td>
|
||||
<td>
|
||||
<h3>{name}</h3>
|
||||
<button className='r' onClick={this._import}><Download className='lg'/>{translate('import')}</button>
|
||||
</td>
|
||||
</tr>;
|
||||
compareHeader = (
|
||||
<tr>
|
||||
<td className="head">{translate('comparison')}</td>
|
||||
<td>
|
||||
<h3>{name}</h3>
|
||||
<button className="r" onClick={this._import}>
|
||||
<Download className="lg" />
|
||||
{translate('import')}
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={'page'} style={{ fontSize: this.context.sizeRatio + 'em' }}>
|
||||
<table id='comparison'>
|
||||
<div
|
||||
className={'page'}
|
||||
style={{ fontSize: this.context.sizeRatio + 'em' }}
|
||||
>
|
||||
<table id="comparison">
|
||||
<tbody>
|
||||
{compareHeader}
|
||||
<tr key='facets'>
|
||||
<td className='head'>{translate('compare')}</td>
|
||||
<tr key="facets">
|
||||
<td className="head">{translate('compare')}</td>
|
||||
<td>
|
||||
<ul id='facet-container' onDragOver={this._facetDragOver}>
|
||||
{facets.map((f, i) =>
|
||||
<li key={f.title} data-i={i} draggable='true' onDragStart={this._facetDrag} onDragEnd={this._facetDrop} className={cn('facet', { active: f.active })} onClick={this._toggleFacet.bind(this, f)}>
|
||||
<ul id="facet-container" onDragOver={this._facetDragOver}>
|
||||
{facets.map((f, i) => (
|
||||
<li
|
||||
key={f.title}
|
||||
data-i={i}
|
||||
draggable="true"
|
||||
onDragStart={this._facetDrag}
|
||||
onDragEnd={this._facetDrop}
|
||||
className={cn('facet', { active: f.active })}
|
||||
onClick={this._toggleFacet.bind(this, f)}
|
||||
>
|
||||
{'↔ ' + translate(f.title)}
|
||||
</li>
|
||||
)}
|
||||
))}
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<ComparisonTable builds={builds} facets={facets} onSort={this._sortShips} predicate={predicate} desc={desc} />
|
||||
<ComparisonTable
|
||||
builds={builds}
|
||||
facets={facets}
|
||||
onSort={this._sortShips}
|
||||
predicate={predicate}
|
||||
desc={desc}
|
||||
/>
|
||||
|
||||
{!builds.length ?
|
||||
<div className='chart' ref={node => this.chartRef = node}>{translate('PHRASE_NO_BUILDS')}</div> :
|
||||
facets.filter((f) => f.active).map((f, i) =>
|
||||
<div key={f.title} className='chart' ref={ i == 0 ? node => this.chartRef = node : null}>
|
||||
<h3 className='ptr' onClick={this._sortShips.bind(this, f.props[0])}>{translate(f.title)}</h3>
|
||||
{!builds.length ? (
|
||||
<div className="chart" ref={node => (this.chartRef = node)}>
|
||||
{translate('PHRASE_NO_BUILDS')}
|
||||
</div>
|
||||
) : (
|
||||
facets.filter(f => f.active).map((f, i) => (
|
||||
<div
|
||||
key={f.title}
|
||||
className="chart"
|
||||
ref={i == 0 ? node => (this.chartRef = node) : null}
|
||||
>
|
||||
<h3
|
||||
className="ptr"
|
||||
onClick={this._sortShips.bind(this, f.props[0])}
|
||||
>
|
||||
{translate(f.title)}
|
||||
</h3>
|
||||
<BarChart
|
||||
width={chartWidth}
|
||||
data={builds}
|
||||
@@ -515,8 +619,8 @@ export default class ComparisonPage extends Page {
|
||||
desc={desc}
|
||||
/>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import Page from './Page';
|
||||
* 404 Page
|
||||
*/
|
||||
export default class NotFoundPage extends Page {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param {Object} props React Component properties
|
||||
@@ -22,6 +21,10 @@ export default class NotFoundPage extends Page {
|
||||
* @return {React.Component} The page contents
|
||||
*/
|
||||
renderPage() {
|
||||
return <div className='page' style={{ marginTop: 30 }}>Page <small>{this.context.route.path}</small> Not Found</div>;
|
||||
return (
|
||||
<div className="page" style={{ marginTop: 30 }}>
|
||||
Page <small>{this.context.route.path}</small> Not Found
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import {
|
||||
LinkIcon,
|
||||
ShoppingIcon,
|
||||
MatIcon,
|
||||
OrbisIcon
|
||||
} from '../components/SvgIcons';
|
||||
import LZString from 'lz-string';
|
||||
import ShipSummaryTable from '../components/ShipSummaryTable';
|
||||
@@ -37,7 +36,6 @@ import OutfittingSubpages from '../components/OutfittingSubpages';
|
||||
import ModalExport from '../components/ModalExport';
|
||||
import ModalPermalink from '../components/ModalPermalink';
|
||||
import ModalShoppingList from '../components/ModalShoppingList';
|
||||
import ModalOrbis from '../components/ModalOrbis';
|
||||
|
||||
/**
|
||||
* Document Title Generator
|
||||
@@ -53,7 +51,6 @@ function getTitle(shipName, buildName) {
|
||||
* The Outfitting Page
|
||||
*/
|
||||
export default class OutfittingPage extends Page {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param {Object} props React Component properties
|
||||
@@ -85,22 +82,38 @@ export default class OutfittingPage extends Page {
|
||||
let shipId = params.ship;
|
||||
let code = params.code;
|
||||
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);
|
||||
if (!data) {
|
||||
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) {
|
||||
ship.buildFrom(code); // Populate modules from serialized 'code' URL param
|
||||
ship.buildFrom(code); // Populate modules from serialized 'code' URL param
|
||||
} else {
|
||||
ship.buildWith(data.defaults); // Populate with default components
|
||||
ship.buildWith(data.defaults); // Populate with default components
|
||||
}
|
||||
|
||||
this._getTitle = getTitle.bind(this, data.properties.name);
|
||||
|
||||
// Obtain ship control from code
|
||||
const { sys, eng, wep, boost, fuel, cargo, opponent, opponentBuild, opponentSys, opponentEng, opponentWep, engagementRange } = this._obtainControlFromCode(ship, code);
|
||||
const {
|
||||
sys,
|
||||
eng,
|
||||
wep,
|
||||
mcSys,
|
||||
mcEng,
|
||||
mcWep,
|
||||
boost,
|
||||
fuel,
|
||||
cargo,
|
||||
opponent,
|
||||
opponentBuild,
|
||||
opponentSys,
|
||||
opponentEng,
|
||||
opponentWep,
|
||||
engagementRange
|
||||
} = this._obtainControlFromCode(ship, code);
|
||||
return {
|
||||
error: null,
|
||||
title: this._getTitle(buildName),
|
||||
@@ -114,6 +127,9 @@ export default class OutfittingPage extends Page {
|
||||
sys,
|
||||
eng,
|
||||
wep,
|
||||
mcSys,
|
||||
mcEng,
|
||||
mcWep,
|
||||
boost,
|
||||
fuel,
|
||||
cargo,
|
||||
@@ -136,7 +152,10 @@ export default class OutfittingPage extends Page {
|
||||
};
|
||||
|
||||
if (Persist.hasBuild(this.state.shipId, stateChanges.newBuildName)) {
|
||||
stateChanges.savedCode = Persist.getBuild(this.state.shipId, stateChanges.newBuildName);
|
||||
stateChanges.savedCode = Persist.getBuild(
|
||||
this.state.shipId,
|
||||
stateChanges.newBuildName
|
||||
);
|
||||
} else {
|
||||
stateChanges.savedCode = null;
|
||||
}
|
||||
@@ -162,7 +181,9 @@ export default class OutfittingPage extends Page {
|
||||
* @returns {string} the code for this ship
|
||||
*/
|
||||
_fullCode(ship, fuel, cargo) {
|
||||
return `${ship.toString()}.${LZString.compressToBase64(this._controlCode(fuel, cargo))}`;
|
||||
return `${ship.toString()}.${LZString.compressToBase64(
|
||||
this._controlCode(fuel, cargo)
|
||||
)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -176,10 +197,17 @@ export default class OutfittingPage extends Page {
|
||||
let sys = 2;
|
||||
let eng = 2;
|
||||
let wep = 2;
|
||||
let mcSys = 0;
|
||||
let mcEng = 0;
|
||||
let mcWep = 0;
|
||||
let boost = false;
|
||||
let fuel = ship.fuelCapacity;
|
||||
let cargo = ship.cargoCapacity;
|
||||
let opponent = new Ship('eagle', Ships['eagle'].properties, Ships['eagle'].slots).buildWith(Ships['eagle'].defaults);
|
||||
let opponent = new Ship(
|
||||
'eagle',
|
||||
Ships['eagle'].properties,
|
||||
Ships['eagle'].slots
|
||||
).buildWith(Ships['eagle'].defaults);
|
||||
let opponentSys = 2;
|
||||
let opponentEng = 2;
|
||||
let opponentWep = 2;
|
||||
@@ -191,16 +219,25 @@ export default class OutfittingPage extends Page {
|
||||
const parts = code.split('.');
|
||||
if (parts.length >= 5) {
|
||||
// We have control information in the code
|
||||
const control = LZString.decompressFromBase64(Utils.fromUrlSafe(parts[4])).split('/');
|
||||
const control = LZString.decompressFromBase64(
|
||||
Utils.fromUrlSafe(parts[4])
|
||||
).split('/');
|
||||
sys = parseFloat(control[0]);
|
||||
eng = parseFloat(control[1]);
|
||||
wep = parseFloat(control[2]);
|
||||
if (sys + eng + wep > 6) {
|
||||
sys = eng = wep = 2;
|
||||
}
|
||||
boost = control[3] == 1 ? true : false;
|
||||
fuel = parseFloat(control[4]);
|
||||
cargo = parseInt(control[5]);
|
||||
fuel = parseFloat(control[4]) || fuel;
|
||||
cargo = parseInt(control[5]) || cargo;
|
||||
if (control[6]) {
|
||||
const shipId = control[6];
|
||||
opponent = new Ship(shipId, Ships[shipId].properties, Ships[shipId].slots);
|
||||
opponent = new Ship(
|
||||
shipId,
|
||||
Ships[shipId].properties,
|
||||
Ships[shipId].slots
|
||||
);
|
||||
if (control[7] && Persist.getBuild(shipId, control[7])) {
|
||||
// Ship is a particular build
|
||||
const opponentCode = Persist.getBuild(shipId, control[7]);
|
||||
@@ -210,10 +247,12 @@ export default class OutfittingPage extends Page {
|
||||
// Obtain opponent's sys/eng/wep pips from their code
|
||||
const opponentParts = opponentCode.split('.');
|
||||
if (opponentParts.length >= 5) {
|
||||
const opponentControl = LZString.decompressFromBase64(Utils.fromUrlSafe(opponentParts[4])).split('/');
|
||||
opponentSys = parseFloat(opponentControl[0]);
|
||||
opponentEng = parseFloat(opponentControl[1]);
|
||||
opponentWep = parseFloat(opponentControl[2]);
|
||||
const opponentControl = LZString.decompressFromBase64(
|
||||
Utils.fromUrlSafe(opponentParts[4])
|
||||
).split('/');
|
||||
opponentSys = parseFloat(opponentControl[0]) || opponentSys;
|
||||
opponentEng = parseFloat(opponentControl[1]) || opponentEng;
|
||||
opponentWep = parseFloat(opponentControl[2]) || opponentWep;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -221,21 +260,50 @@ export default class OutfittingPage extends Page {
|
||||
opponent.buildWith(Ships[shipId].defaults);
|
||||
}
|
||||
}
|
||||
engagementRange = parseInt(control[8]);
|
||||
engagementRange = parseInt(control[8]) || engagementRange;
|
||||
|
||||
// Multi-crew pips were introduced later on so assign default values
|
||||
// because those values might not be present.
|
||||
mcSys = parseInt(control[9]) || mcSys;
|
||||
mcEng = parseInt(control[10]) || mcEng;
|
||||
mcWep = parseInt(control[11]) || mcWep;
|
||||
}
|
||||
}
|
||||
|
||||
return { sys, eng, wep, boost, fuel, cargo, opponent, opponentBuild, opponentSys, opponentEng, opponentWep, engagementRange };
|
||||
return {
|
||||
sys,
|
||||
eng,
|
||||
wep,
|
||||
mcSys,
|
||||
mcEng,
|
||||
mcWep,
|
||||
boost,
|
||||
fuel,
|
||||
cargo,
|
||||
opponent,
|
||||
opponentBuild,
|
||||
opponentSys,
|
||||
opponentEng,
|
||||
opponentWep,
|
||||
engagementRange
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggered when pips have been updated
|
||||
* Triggered when pips have been updated. Multi-crew pips are already included
|
||||
* in sys, eng and wep but mcSys, mcEng and mcWep make clear where each pip
|
||||
* comes from.
|
||||
* @param {number} sys SYS pips
|
||||
* @param {number} eng ENG pips
|
||||
* @param {number} wep WEP pips
|
||||
* @param {number} mcSys SYS pips from multi-crew
|
||||
* @param {number} mcEng ENG pips from multi-crew
|
||||
* @param {number} mcWep WEP pips from multi-crew
|
||||
*/
|
||||
_pipsUpdated(sys, eng, wep) {
|
||||
this.setState({ sys, eng, wep }, () => this._updateRouteOnControlChange());
|
||||
_pipsUpdated(sys, eng, wep, mcSys, mcEng, mcWep) {
|
||||
this.setState({ sys, eng, wep, mcSys, mcEng, mcWep }, () =>
|
||||
this._updateRouteOnControlChange()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -243,7 +311,7 @@ export default class OutfittingPage extends Page {
|
||||
* @param {boolean} boost true if boosting
|
||||
*/
|
||||
_boostUpdated(boost) {
|
||||
this.setState({ boost }, () => this._updateRouteOnControlChange());
|
||||
this.setState({ boost }, () => this._updateRouteOnControlChange());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -251,7 +319,7 @@ export default class OutfittingPage extends Page {
|
||||
* @param {number} fuel the amount of fuel, in T
|
||||
*/
|
||||
_fuelUpdated(fuel) {
|
||||
this.setState({ fuel }, () => this._updateRouteOnControlChange());
|
||||
this.setState({ fuel }, () => this._updateRouteOnControlChange());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -259,7 +327,7 @@ export default class OutfittingPage extends Page {
|
||||
* @param {number} cargo the amount of cargo, in T
|
||||
*/
|
||||
_cargoUpdated(cargo) {
|
||||
this.setState({ cargo }, () => this._updateRouteOnControlChange());
|
||||
this.setState({ cargo }, () => this._updateRouteOnControlChange());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -267,7 +335,9 @@ export default class OutfittingPage extends Page {
|
||||
* @param {number} engagementRange the engagement range, in m
|
||||
*/
|
||||
_engagementRangeUpdated(engagementRange) {
|
||||
this.setState({ engagementRange }, () => this._updateRouteOnControlChange());
|
||||
this.setState({ engagementRange }, () =>
|
||||
this._updateRouteOnControlChange()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -276,7 +346,11 @@ export default class OutfittingPage extends Page {
|
||||
* @param {string} opponentBuild the name of the opponent's build
|
||||
*/
|
||||
_opponentUpdated(opponent, opponentBuild) {
|
||||
const opponentShip = new Ship(opponent, Ships[opponent].properties, Ships[opponent].slots);
|
||||
const opponentShip = new Ship(
|
||||
opponent,
|
||||
Ships[opponent].properties,
|
||||
Ships[opponent].slots
|
||||
);
|
||||
let opponentSys = this.state.opponentSys;
|
||||
let opponentEng = this.state.opponentEng;
|
||||
let opponentWep = this.state.opponentWep;
|
||||
@@ -284,9 +358,13 @@ export default class OutfittingPage extends Page {
|
||||
// Ship is a particular build
|
||||
opponentShip.buildFrom(Persist.getBuild(opponent, opponentBuild));
|
||||
// Set pips for opponent
|
||||
const opponentParts = Persist.getBuild(opponent, opponentBuild).split('.');
|
||||
const opponentParts = Persist.getBuild(opponent, opponentBuild).split(
|
||||
'.'
|
||||
);
|
||||
if (opponentParts.length >= 5) {
|
||||
const opponentControl = LZString.decompressFromBase64(Utils.fromUrlSafe(opponentParts[4])).split('/');
|
||||
const opponentControl = LZString.decompressFromBase64(
|
||||
Utils.fromUrlSafe(opponentParts[4])
|
||||
).split('/');
|
||||
opponentSys = parseFloat(opponentControl[0]);
|
||||
opponentEng = parseFloat(opponentControl[1]);
|
||||
opponentWep = parseFloat(opponentControl[2]);
|
||||
@@ -299,7 +377,16 @@ export default class OutfittingPage extends Page {
|
||||
opponentWep = 2;
|
||||
}
|
||||
|
||||
this.setState({ opponent: opponentShip, opponentBuild, opponentSys, opponentEng, opponentWep }, () => this._updateRouteOnControlChange());
|
||||
this.setState(
|
||||
{
|
||||
opponent: opponentShip,
|
||||
opponentBuild,
|
||||
opponentSys,
|
||||
opponentEng,
|
||||
opponentWep
|
||||
},
|
||||
() => this._updateRouteOnControlChange()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -309,8 +396,22 @@ export default class OutfittingPage extends Page {
|
||||
* @returns {string} The control code
|
||||
*/
|
||||
_controlCode(fuel, cargo) {
|
||||
const { sys, eng, wep, boost, opponent, opponentBuild, engagementRange } = this.state;
|
||||
const code = `${sys}/${eng}/${wep}/${boost ? 1 : 0}/${fuel || this.state.fuel}/${cargo || this.state.cargo}/${opponent.id}/${opponentBuild ? opponentBuild : ''}/${engagementRange}`;
|
||||
const {
|
||||
sys,
|
||||
eng,
|
||||
wep,
|
||||
mcSys,
|
||||
mcEng,
|
||||
mcWep,
|
||||
boost,
|
||||
opponent,
|
||||
opponentBuild,
|
||||
engagementRange
|
||||
} = this.state;
|
||||
const code = `${sys}/${eng}/${wep}/${boost ? 1 : 0}/${fuel ||
|
||||
this.state.fuel}/${cargo || this.state.cargo}/${opponent.id}/${
|
||||
opponentBuild ? opponentBuild : ''
|
||||
}/${engagementRange}/${mcSys}/${mcEng}/${mcWep}`;
|
||||
return code;
|
||||
}
|
||||
|
||||
@@ -321,27 +422,44 @@ export default class OutfittingPage extends Page {
|
||||
const { ship, buildName, newBuildName, shipId } = this.state;
|
||||
|
||||
// If this is a stock ship the code won't be set, so ensure that we have it
|
||||
const code = this.state.code || ship.toString();
|
||||
const code = this.state.code || ship.toString();
|
||||
|
||||
Persist.saveBuild(shipId, newBuildName, code);
|
||||
this._updateRoute(shipId, newBuildName, code);
|
||||
|
||||
let opponent, opponentBuild, opponentSys, opponentEng, opponentWep;
|
||||
if (shipId === this.state.opponent.id && buildName === this.state.opponentBuild) {
|
||||
if (
|
||||
shipId === this.state.opponent.id &&
|
||||
buildName === this.state.opponentBuild
|
||||
) {
|
||||
// This is a save of our current opponent build; update it
|
||||
opponentBuild = newBuildName;
|
||||
opponent = new Ship(shipId, Ships[shipId].properties, Ships[shipId].slots).buildFrom(code);
|
||||
opponent = new Ship(
|
||||
shipId,
|
||||
Ships[shipId].properties,
|
||||
Ships[shipId].slots
|
||||
).buildFrom(code);
|
||||
opponentSys = this.state.sys;
|
||||
opponentEng = this.state.eng;
|
||||
opponentWep = this.state.wep;
|
||||
} else {
|
||||
opponentBuild = this.state.opponentBuild;
|
||||
opponent = this.state.opponent;
|
||||
opponent = this.state.opponent;
|
||||
opponentSys = this.state.opponentSys;
|
||||
opponentEng = this.state.opponentEng;
|
||||
opponentWep = this.state.opponentWep;
|
||||
}
|
||||
this.setState({ buildName: newBuildName, code, savedCode: code, opponent, opponentBuild, opponentSys, opponentEng, opponentWep, title: this._getTitle(newBuildName) });
|
||||
this.setState({
|
||||
buildName: newBuildName,
|
||||
code,
|
||||
savedCode: code,
|
||||
opponent,
|
||||
opponentBuild,
|
||||
opponentSys,
|
||||
opponentEng,
|
||||
opponentWep,
|
||||
title: this._getTitle(newBuildName)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -353,7 +471,12 @@ export default class OutfittingPage extends Page {
|
||||
Persist.deleteBuild(shipId, buildName);
|
||||
Persist.saveBuild(shipId, newBuildName, code);
|
||||
this._updateRoute(shipId, newBuildName, code);
|
||||
this.setState({ buildName: newBuildName, code, savedCode: code, opponentBuild: newBuildName });
|
||||
this.setState({
|
||||
buildName: newBuildName,
|
||||
code,
|
||||
savedCode: code,
|
||||
opponentBuild: newBuildName
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -373,19 +496,38 @@ export default class OutfittingPage extends Page {
|
||||
ship.buildWith(Ships[shipId].defaults);
|
||||
// Reset controls
|
||||
const code = ship.toString();
|
||||
const { sys, eng, wep, boost, fuel, cargo, opponent, opponentBuild, engagementRange } = this._obtainControlFromCode(ship, code);
|
||||
// Update state, and refresh the ship
|
||||
this.setState({
|
||||
const {
|
||||
sys,
|
||||
eng,
|
||||
wep,
|
||||
mcSys,
|
||||
mcEng,
|
||||
mcWep,
|
||||
boost,
|
||||
fuel,
|
||||
cargo,
|
||||
opponent,
|
||||
opponentBuild,
|
||||
engagementRange
|
||||
}, () => this._updateRoute(shipId, buildName, code));
|
||||
} = this._obtainControlFromCode(ship, code);
|
||||
// Update state, and refresh the ship
|
||||
this.setState(
|
||||
{
|
||||
sys,
|
||||
eng,
|
||||
wep,
|
||||
mcSys,
|
||||
mcEng,
|
||||
mcWep,
|
||||
boost,
|
||||
fuel,
|
||||
cargo,
|
||||
opponent,
|
||||
opponentBuild,
|
||||
engagementRange
|
||||
},
|
||||
() => this._updateRoute(shipId, buildName, code)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -396,7 +538,10 @@ export default class OutfittingPage extends Page {
|
||||
Persist.deleteBuild(shipId, buildName);
|
||||
|
||||
let opponentBuild;
|
||||
if (shipId === this.state.opponent.id && buildName === this.state.opponentBuild) {
|
||||
if (
|
||||
shipId === this.state.opponent.id &&
|
||||
buildName === this.state.opponentBuild
|
||||
) {
|
||||
// Our current opponent has been deleted; revert to stock
|
||||
opponentBuild = null;
|
||||
} else {
|
||||
@@ -413,11 +558,13 @@ export default class OutfittingPage extends Page {
|
||||
_exportBuild() {
|
||||
let translate = this.context.language.translate;
|
||||
let { buildName, ship } = this.state;
|
||||
this.context.showModal(<ModalExport
|
||||
title={(buildName || ship.name) + ' ' + translate('export')}
|
||||
description={translate('PHRASE_EXPORT_DESC')}
|
||||
data={toDetailedBuild(buildName, ship, ship.toString())}
|
||||
/>);
|
||||
this.context.showModal(
|
||||
<ModalExport
|
||||
title={(buildName || ship.name) + ' ' + translate('export')}
|
||||
description={translate('PHRASE_EXPORT_DESC')}
|
||||
data={toDetailedBuild(buildName, ship, ship.toString())}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -430,19 +577,38 @@ export default class OutfittingPage extends Page {
|
||||
this.state.ship.buildFrom(code);
|
||||
|
||||
// Obtain controls from the code
|
||||
const { sys, eng, wep, boost, fuel, cargo, opponent, opponentBuild, engagementRange } = this._obtainControlFromCode(ship, code);
|
||||
// Update state, and refresh the route when complete
|
||||
this.setState({
|
||||
const {
|
||||
sys,
|
||||
eng,
|
||||
wep,
|
||||
mcSys,
|
||||
mcEng,
|
||||
mcWep,
|
||||
boost,
|
||||
fuel,
|
||||
cargo,
|
||||
opponent,
|
||||
opponentBuild,
|
||||
engagementRange
|
||||
}, () => this._updateRoute(shipId, buildName, code));
|
||||
} = this._obtainControlFromCode(ship, code);
|
||||
// Update state, and refresh the route when complete
|
||||
this.setState(
|
||||
{
|
||||
sys,
|
||||
eng,
|
||||
wep,
|
||||
mcSys,
|
||||
mcEng,
|
||||
mcWep,
|
||||
boost,
|
||||
fuel,
|
||||
cargo,
|
||||
opponent,
|
||||
opponentBuild,
|
||||
engagementRange
|
||||
},
|
||||
() => this._updateRoute(shipId, buildName, code)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -458,8 +624,14 @@ export default class OutfittingPage extends Page {
|
||||
}
|
||||
const code = this._fullCode(ship, fuel, cargo);
|
||||
// Only update the state if this really has been updated
|
||||
if (this.state.code != code || this.state.cargo != cargo || this.state.fuel != fuel) {
|
||||
this.setState({ code, cargo, fuel }, () => this._updateRoute(shipId, buildName, code));
|
||||
if (
|
||||
this.state.code != code ||
|
||||
this.state.cargo != cargo ||
|
||||
this.state.fuel != fuel
|
||||
) {
|
||||
this.setState({ code, cargo, fuel }, () =>
|
||||
this._updateRoute(shipId, buildName, code)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -479,7 +651,8 @@ export default class OutfittingPage extends Page {
|
||||
* @param {Object} nextContext Incoming/Next conext
|
||||
*/
|
||||
componentWillReceiveProps(nextProps, nextContext) {
|
||||
if (this.context.route !== nextContext.route) { // Only reinit state if the route has changed
|
||||
if (this.context.route !== nextContext.route) {
|
||||
// Only reinit state if the route has changed
|
||||
this.setState(this._initState(nextProps, nextContext));
|
||||
}
|
||||
}
|
||||
@@ -502,24 +675,7 @@ export default class OutfittingPage extends Page {
|
||||
* Generates the short URL
|
||||
*/
|
||||
_genShortlink() {
|
||||
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}/>);
|
||||
this.context.showModal(<ModalPermalink url={window.location.href} />);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -530,17 +686,23 @@ export default class OutfittingPage extends Page {
|
||||
|
||||
const shipId = Ships[ship.id].eddbID;
|
||||
// Provide unique list of non-PP module EDDB IDs
|
||||
const modIds = ship.internal.concat(ship.bulkheads, ship.standard, ship.hardpoints).filter(slot => slot !== null && slot.m !== null && !slot.m.pp).map(slot => slot.m.eddbID).filter((v, i, a) => a.indexOf(v) === i);
|
||||
const modIds = ship.internal
|
||||
.concat(ship.bulkheads, ship.standard, ship.hardpoints)
|
||||
.filter(slot => slot !== null && slot.m !== null && !slot.m.pp)
|
||||
.map(slot => slot.m.eddbID)
|
||||
.filter((v, i, a) => a.indexOf(v) === i);
|
||||
|
||||
// Open up the relevant URL
|
||||
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}/>);
|
||||
this.context.showModal(<ModalShoppingList ship={this.state.ship} />);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -550,8 +712,9 @@ export default class OutfittingPage extends Page {
|
||||
_keyDown(e) {
|
||||
// .keyCode will eventually be replaced with .key
|
||||
switch (e.keyCode) {
|
||||
case 69: // 'e'
|
||||
if (e.ctrlKey || e.metaKey) { // CTRL/CMD + e
|
||||
case 69: // 'e'
|
||||
if (e.ctrlKey || e.metaKey) {
|
||||
// CTRL/CMD + e
|
||||
e.preventDefault();
|
||||
this._exportBuild();
|
||||
}
|
||||
@@ -567,7 +730,28 @@ export default class OutfittingPage extends Page {
|
||||
let state = this.state,
|
||||
{ language, termtip, tooltip, sizeRatio, onWindowResize } = this.context,
|
||||
{ translate, units, formats } = language,
|
||||
{ ship, code, savedCode, buildName, newBuildName, sys, eng, wep, boost, fuel, cargo, opponent, opponentBuild, opponentSys, opponentEng, opponentWep, engagementRange } = state,
|
||||
{
|
||||
ship,
|
||||
code,
|
||||
savedCode,
|
||||
buildName,
|
||||
newBuildName,
|
||||
sys,
|
||||
eng,
|
||||
wep,
|
||||
mcSys,
|
||||
mcEng,
|
||||
mcWep,
|
||||
boost,
|
||||
fuel,
|
||||
cargo,
|
||||
opponent,
|
||||
opponentBuild,
|
||||
opponentSys,
|
||||
opponentEng,
|
||||
opponentWep,
|
||||
engagementRange
|
||||
} = state,
|
||||
hide = tooltip.bind(null, null),
|
||||
menu = this.props.currentMenu,
|
||||
shipUpdated = this._shipUpdated,
|
||||
@@ -585,11 +769,15 @@ export default class OutfittingPage extends Page {
|
||||
const _pStr = `${ship.getPowerEnabledString()}${ship.getPowerPrioritiesString()}`;
|
||||
const _mStr = ship.getModificationsString();
|
||||
|
||||
const standardSlotMarker = `${ship.name}${_sStr}${_pStr}${_mStr}${ship.ladenMass}${cargo}${fuel}`;
|
||||
const standardSlotMarker = `${ship.name}${_sStr}${_pStr}${_mStr}${
|
||||
ship.ladenMass
|
||||
}${cargo}${fuel}`;
|
||||
const internalSlotMarker = `${ship.name}${_iStr}${_pStr}${_mStr}`;
|
||||
const hardpointsSlotMarker = `${ship.name}${_hStr}${_pStr}${_mStr}`;
|
||||
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 = [];
|
||||
@@ -601,94 +789,268 @@ export default class OutfittingPage extends Page {
|
||||
*/
|
||||
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>);
|
||||
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>);
|
||||
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');
|
||||
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 (
|
||||
<div id='outfit' className={'page'} style={{ fontSize: (sizeRatio * 0.9) + 'em' }}>
|
||||
<div id='overview'>
|
||||
<div
|
||||
id="outfit"
|
||||
className={'page'}
|
||||
style={{ fontSize: sizeRatio * 0.9 + 'em' }}
|
||||
>
|
||||
<div id="overview">
|
||||
<h1>{ship.name}</h1>
|
||||
<div id='requirements'>{requirementElements}</div>
|
||||
<div id='build'>
|
||||
<input value={newBuildName || ''} onChange={this._buildNameChange} placeholder={translate('Enter Name')} maxLength={50} />
|
||||
<button onClick={canSave && this._saveBuild} disabled={!canSave} onMouseOver={termtip.bind(null, 'save')} onMouseOut={hide}>
|
||||
<FloppyDisk className='lg' />
|
||||
<div id="requirements">{requirementElements}</div>
|
||||
<div id="build">
|
||||
<input
|
||||
value={newBuildName || ''}
|
||||
onChange={this._buildNameChange}
|
||||
placeholder={translate('Enter Name')}
|
||||
maxLength={50}
|
||||
/>
|
||||
<button
|
||||
onClick={canSave && this._saveBuild}
|
||||
disabled={!canSave}
|
||||
onMouseOver={termtip.bind(null, 'save')}
|
||||
onMouseOut={hide}
|
||||
>
|
||||
<FloppyDisk className="lg" />
|
||||
</button>
|
||||
<button onClick={canRename && this._renameBuild} disabled={!canRename} onMouseOver={termtip.bind(null, 'rename')} onMouseOut={hide}>
|
||||
<span style={{ textTransform: 'none', fontSize: '1.8em' }}>a|</span>
|
||||
<button
|
||||
onClick={canRename && this._renameBuild}
|
||||
disabled={!canRename}
|
||||
onMouseOver={termtip.bind(null, 'rename')}
|
||||
onMouseOut={hide}
|
||||
>
|
||||
<span style={{ textTransform: 'none', fontSize: '1.8em' }}>
|
||||
a|
|
||||
</span>
|
||||
</button>
|
||||
<button onClick={canReload && this._reloadBuild} disabled={!canReload} onMouseOver={termtip.bind(null, 'reload')} onMouseOut={hide}>
|
||||
<Reload className='lg'/>
|
||||
<button
|
||||
onClick={canReload && this._reloadBuild}
|
||||
disabled={!canReload}
|
||||
onMouseOver={termtip.bind(null, 'reload')}
|
||||
onMouseOut={hide}
|
||||
>
|
||||
<Reload className="lg" />
|
||||
</button>
|
||||
<button className={'danger'} onClick={savedCode && this._deleteBuild} disabled={!savedCode} onMouseOver={termtip.bind(null, 'delete')} onMouseOut={hide}>
|
||||
<Bin className='lg'/>
|
||||
<button
|
||||
className={'danger'}
|
||||
onClick={savedCode && this._deleteBuild}
|
||||
disabled={!savedCode}
|
||||
onMouseOver={termtip.bind(null, 'delete')}
|
||||
onMouseOut={hide}
|
||||
>
|
||||
<Bin className="lg" />
|
||||
</button>
|
||||
<button onClick={code && this._resetBuild} disabled={!code} onMouseOver={termtip.bind(null, 'reset')} onMouseOut={hide}>
|
||||
<Switch className='lg'/>
|
||||
<button
|
||||
onClick={code && this._resetBuild}
|
||||
disabled={!code}
|
||||
onMouseOver={termtip.bind(null, 'reset')}
|
||||
onMouseOut={hide}
|
||||
>
|
||||
<Switch className="lg" />
|
||||
</button>
|
||||
<button onClick={buildName && this._exportBuild} disabled={!buildName} onMouseOver={termtip.bind(null, 'export')} onMouseOut={hide}>
|
||||
<Download className='lg'/>
|
||||
<button
|
||||
onClick={buildName && this._exportBuild}
|
||||
disabled={!buildName}
|
||||
onMouseOver={termtip.bind(null, 'export')}
|
||||
onMouseOut={hide}
|
||||
>
|
||||
<Download className="lg" />
|
||||
</button>
|
||||
<button onClick={this._eddbShoppingList} onMouseOver={termtip.bind(null, 'PHRASE_SHOPPING_LIST')} onMouseOut={hide}>
|
||||
<ShoppingIcon className='lg' />
|
||||
<button
|
||||
onClick={this._eddbShoppingList}
|
||||
onMouseOver={termtip.bind(null, 'PHRASE_SHOPPING_LIST')}
|
||||
onMouseOut={hide}
|
||||
>
|
||||
<ShoppingIcon className="lg" />
|
||||
</button>
|
||||
<button onClick={this._genShortlink} onMouseOver={termtip.bind(null, 'shortlink')} onMouseOut={hide}>
|
||||
<LinkIcon className='lg' />
|
||||
<button
|
||||
onClick={this._genShortlink}
|
||||
onMouseOver={termtip.bind(null, 'shortlink')}
|
||||
onMouseOut={hide}
|
||||
>
|
||||
<LinkIcon className="lg" />
|
||||
</button>
|
||||
<button onClick={this._genOrbis} onMouseOver={termtip.bind(null, 'PHASE_UPLOAD_ORBIS')} onMouseOut={hide}>
|
||||
<OrbisIcon className='lg' />
|
||||
</button>
|
||||
<button onClick={this._genShoppingList} onMouseOver={termtip.bind(null, 'PHRASE_SHOPPING_MATS')} onMouseOut={hide}>
|
||||
<MatIcon className='lg' />
|
||||
<button
|
||||
onClick={this._genShoppingList}
|
||||
onMouseOver={termtip.bind(null, 'PHRASE_SHOPPING_MATS')}
|
||||
onMouseOut={hide}
|
||||
>
|
||||
<MatIcon className="lg" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Main tables */}
|
||||
<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} sectionMenuRefs={this._sectionMenuRefs}/>
|
||||
<InternalSlotSection ship={ship} code={internalSlotMarker} onChange={shipUpdated} onCargoChange={this._cargoUpdated} onFuelChange={this._fuelUpdated} currentMenu={menu} sectionMenuRefs={this._sectionMenuRefs}/>
|
||||
<HardpointSlotSection ship={ship} code={hardpointsSlotMarker} onChange={shipUpdated} onCargoChange={this._cargoUpdated} onFuelChange={this._fuelUpdated} currentMenu={menu} sectionMenuRefs={this._sectionMenuRefs}/>
|
||||
<UtilitySlotSection ship={ship} code={hardpointsSlotMarker} onChange={shipUpdated} onCargoChange={this._cargoUpdated} onFuelChange={this._fuelUpdated} currentMenu={menu} sectionMenuRefs={this._sectionMenuRefs}/>
|
||||
<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}
|
||||
sectionMenuRefs={this._sectionMenuRefs}
|
||||
/>
|
||||
<InternalSlotSection
|
||||
ship={ship}
|
||||
code={internalSlotMarker}
|
||||
onChange={shipUpdated}
|
||||
onCargoChange={this._cargoUpdated}
|
||||
onFuelChange={this._fuelUpdated}
|
||||
currentMenu={menu}
|
||||
sectionMenuRefs={this._sectionMenuRefs}
|
||||
/>
|
||||
<HardpointSlotSection
|
||||
ship={ship}
|
||||
code={hardpointsSlotMarker}
|
||||
onChange={shipUpdated}
|
||||
onCargoChange={this._cargoUpdated}
|
||||
onFuelChange={this._fuelUpdated}
|
||||
currentMenu={menu}
|
||||
sectionMenuRefs={this._sectionMenuRefs}
|
||||
/>
|
||||
<UtilitySlotSection
|
||||
ship={ship}
|
||||
code={hardpointsSlotMarker}
|
||||
onChange={shipUpdated}
|
||||
onCargoChange={this._cargoUpdated}
|
||||
onFuelChange={this._fuelUpdated}
|
||||
currentMenu={menu}
|
||||
sectionMenuRefs={this._sectionMenuRefs}
|
||||
/>
|
||||
|
||||
{/* Control of ship and opponent */}
|
||||
<div className='group quarter'>
|
||||
<div className='group half'>
|
||||
<h2 style={{ verticalAlign: 'middle', textAlign: 'left' }}>{translate('ship control')}</h2>
|
||||
<div className="group quarter">
|
||||
<div className="group half">
|
||||
<h2 style={{ verticalAlign: 'middle', textAlign: 'left' }}>
|
||||
{translate('ship control')}
|
||||
</h2>
|
||||
</div>
|
||||
<div className='group half'>
|
||||
<Boost marker={boostMarker} ship={ship} boost={boost} onChange={this._boostUpdated} />
|
||||
<div className="group half">
|
||||
<Boost
|
||||
marker={boostMarker}
|
||||
ship={ship}
|
||||
boost={boost}
|
||||
onChange={this._boostUpdated}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='group quarter'>
|
||||
<Pips sys={sys} eng={eng} wep={wep} onChange={this._pipsUpdated} />
|
||||
<div className="group quarter">
|
||||
<Pips
|
||||
sys={sys}
|
||||
eng={eng}
|
||||
wep={wep}
|
||||
mcSys={mcSys}
|
||||
mcEng={mcEng}
|
||||
mcWep={mcWep}
|
||||
onChange={this._pipsUpdated}
|
||||
/>
|
||||
</div>
|
||||
<div className='group quarter'>
|
||||
<Fuel fuelCapacity={ship.fuelCapacity} fuel={fuel} onChange={this._fuelUpdated}/>
|
||||
<div className="group quarter">
|
||||
<Fuel
|
||||
fuelCapacity={ship.fuelCapacity}
|
||||
fuel={fuel}
|
||||
onChange={this._fuelUpdated}
|
||||
/>
|
||||
</div>
|
||||
<div className='group quarter'>
|
||||
{ ship.cargoCapacity > 0 ? <Cargo cargoCapacity={ship.cargoCapacity} cargo={cargo} onChange={this._cargoUpdated}/> : null }
|
||||
<div className="group quarter">
|
||||
{ship.cargoCapacity > 0 ? (
|
||||
<Cargo
|
||||
cargoCapacity={ship.cargoCapacity}
|
||||
cargo={cargo}
|
||||
onChange={this._cargoUpdated}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
<div className='group half'>
|
||||
<div className='group quarter'>
|
||||
<h2 style={{ verticalAlign: 'middle', textAlign: 'left' }}>{translate('opponent')}</h2>
|
||||
<div className="group half">
|
||||
<div className="group quarter">
|
||||
<h2 style={{ verticalAlign: 'middle', textAlign: 'left' }}>
|
||||
{translate('opponent')}
|
||||
</h2>
|
||||
</div>
|
||||
<div className='group threequarters'>
|
||||
<ShipPicker ship={opponent.id} build={opponentBuild} onChange={this._opponentUpdated}/>
|
||||
<div className="group threequarters">
|
||||
<ShipPicker
|
||||
ship={opponent.id}
|
||||
build={opponentBuild}
|
||||
onChange={this._opponentUpdated}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='group half'>
|
||||
<EngagementRange ship={ship} engagementRange={engagementRange} onChange={this._engagementRangeUpdated}/>
|
||||
<div className="group half">
|
||||
<EngagementRange
|
||||
ship={ship}
|
||||
engagementRange={engagementRange}
|
||||
onChange={this._engagementRangeUpdated}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Tabbed subpages */}
|
||||
|
||||
@@ -51,23 +51,25 @@ export default class Page extends React.Component {
|
||||
}
|
||||
|
||||
/**
|
||||
* Pages are 'pure' components that only render when props, state, or context changes.
|
||||
* This method performs a shallow comparison to determine change.
|
||||
*
|
||||
* @param {Object} np Next/Incoming properties
|
||||
* @param {Object} ns Next/Incoming state
|
||||
* @param {Object} nc Next/Incoming context
|
||||
* @return {Boolean} True if props, state, or context has changed
|
||||
* Update the window title upon mount
|
||||
*/
|
||||
shouldComponentUpdate(np, ns, nc) {
|
||||
return !shallowEqual(this.props, np) || !shallowEqual(this.state, ns) || !shallowEqual(this.context, nc);
|
||||
componentWillMount() {
|
||||
document.title = this.state.title || 'Coriolis';
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the window title upon mount
|
||||
*/
|
||||
componentWillMount() {
|
||||
componentDidMount() {
|
||||
document.title = this.state.title || 'Coriolis';
|
||||
try {
|
||||
(window.adsbygoogle = window.adsbygoogle || []).push({
|
||||
google_ad_client: "ca-pub-3709458261881414",
|
||||
enable_page_level_ads: true
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,9 +22,11 @@ function countHp(slot) {
|
||||
*/
|
||||
function countInt(slot) {
|
||||
let crEligible = !slot.eligible || slot.eligible.cr;
|
||||
this.int[slot.maxClass - 1]++; // Subtract 1 since there is no Class 0 Internal compartment
|
||||
this.int[slot.maxClass - 1]++; // Subtract 1 since there is no Class 0 Internal compartment
|
||||
this.intCount++;
|
||||
this.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;
|
||||
@@ -42,7 +44,9 @@ function countInt(slot) {
|
||||
passSlotType = 'pcq';
|
||||
passSlotRating = 'B';
|
||||
}
|
||||
let passengerBay = passSlotType ? ModuleUtils.findMaxInternal(passSlotType, slot.maxClass, passSlotRating) : null;
|
||||
let passengerBay = passSlotType ?
|
||||
ModuleUtils.findMaxInternal(passSlotType, slot.maxClass, passSlotRating) :
|
||||
null;
|
||||
this.maxPassengers += passengerBay ? passengerBay.passengers : 0;
|
||||
}
|
||||
|
||||
@@ -57,23 +61,27 @@ function shipSummary(shipId, shipData) {
|
||||
id: shipId,
|
||||
hpCount: 0,
|
||||
intCount: 0,
|
||||
beta: shipData.beta,
|
||||
maxCargo: 0,
|
||||
maxPassengers: 0,
|
||||
hp: [0, 0, 0, 0, 0], // Utility, Small, Medium, Large, Huge
|
||||
int: [0, 0, 0, 0, 0, 0, 0, 0], // Sizes 1 - 8
|
||||
standard: shipData.slots.standard,
|
||||
agility: shipData.properties.pitch + shipData.properties.yaw + shipData.properties.roll
|
||||
agility:
|
||||
shipData.properties.pitch +
|
||||
shipData.properties.yaw +
|
||||
shipData.properties.roll
|
||||
};
|
||||
Object.assign(summary, shipData.properties);
|
||||
let ship = new Ship(shipId, shipData.properties, shipData.slots);
|
||||
|
||||
// Build Ship
|
||||
ship.buildWith(shipData.defaults); // Populate with stock/default components
|
||||
ship.buildWith(shipData.defaults); // Populate with stock/default components
|
||||
ship.hardpoints.forEach(countHp.bind(summary)); // Count Hardpoints by class
|
||||
ship.internal.forEach(countInt.bind(summary)); // Count Internal Compartments by class
|
||||
summary.retailCost = ship.totalCost; // Record Stock/Default/retail cost
|
||||
ship.optimizeMass({ pd: '1D' }); // Optimize Mass with 1D PD for maximum possible jump range
|
||||
summary.maxJumpRange = ship.unladenRange; // Record Jump Range
|
||||
ship.internal.forEach(countInt.bind(summary)); // Count Internal Compartments by class
|
||||
summary.retailCost = ship.totalCost; // Record Stock/Default/retail cost
|
||||
ship.optimizeMass({ pd: '1D' }); // Optimize Mass with 1D PD for maximum possible jump range
|
||||
summary.maxJumpRange = ship.unladenRange; // Record Jump Range
|
||||
|
||||
// Best thrusters
|
||||
let th;
|
||||
@@ -97,7 +105,6 @@ function shipSummary(shipId, shipData) {
|
||||
* The Shipyard summary page
|
||||
*/
|
||||
export default class ShipyardPage extends Page {
|
||||
|
||||
static cachedShipSummaries = null;
|
||||
|
||||
/**
|
||||
@@ -119,12 +126,14 @@ export default class ShipyardPage extends Page {
|
||||
title: 'Coriolis EDCD Edition - Shipyard',
|
||||
shipPredicate: 'name',
|
||||
shipDesc: true,
|
||||
shipSummaries: ShipyardPage.cachedShipSummaries
|
||||
shipSummaries: ShipyardPage.cachedShipSummaries,
|
||||
compare: {},
|
||||
groupCompared: false,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Higlight the current ship in the table
|
||||
* Higlight the current ship in the table on mouse over
|
||||
* @param {String} shipId Ship Id
|
||||
* @param {SyntheticEvent} event Event
|
||||
*/
|
||||
@@ -133,6 +142,24 @@ export default class ShipyardPage extends Page {
|
||||
this.setState({ shipId });
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle compare highlighting for ships in the table
|
||||
* @param {String} shipId Ship Id
|
||||
*/
|
||||
_toggleCompare(shipId) {
|
||||
let compare = this.state.compare;
|
||||
compare[shipId] = !compare[shipId];
|
||||
this.setState({ compare });
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle grouping of compared ships in the table
|
||||
* @private
|
||||
*/
|
||||
_toggleGroupCompared() {
|
||||
this.setState({groupCompared: !this.state.groupCompared})
|
||||
}
|
||||
|
||||
/**
|
||||
* Update state with the specified sort predicates
|
||||
* @param {String} shipPredicate Sort predicate - property name
|
||||
@@ -145,12 +172,15 @@ export default class ShipyardPage extends Page {
|
||||
shipPredicateIndex = undefined;
|
||||
}
|
||||
|
||||
if (this.state.shipPredicate == shipPredicate && this.state.shipPredicateIndex == shipPredicateIndex) {
|
||||
if (
|
||||
this.state.shipPredicate == shipPredicate &&
|
||||
this.state.shipPredicateIndex == shipPredicateIndex
|
||||
) {
|
||||
shipDesc = !shipDesc;
|
||||
}
|
||||
|
||||
this.setState({ shipPredicate, shipDesc, shipPredicateIndex });
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the table row summary for the ship
|
||||
@@ -159,56 +189,60 @@ export default class ShipyardPage extends Page {
|
||||
* @param {Object} u Localized unit map
|
||||
* @param {Function} fInt Localized integer formatter
|
||||
* @param {Function} fRound Localized round formatter
|
||||
* @param {Boolean} highlight Should this row be highlighted
|
||||
* @return {React.Component} Table Row
|
||||
*/
|
||||
_shipRowElement(s, translate, u, fInt, fRound, highlight) {
|
||||
_shipRowElement(s, translate, u, fInt, fRound) {
|
||||
let noTouch = this.context.noTouch;
|
||||
|
||||
return <tr
|
||||
return (
|
||||
<tr
|
||||
key={s.id}
|
||||
style={{ height: '1.5em' }}
|
||||
className={cn({ highlighted: noTouch && this.state.shipId === s.id, alt: highlight })}
|
||||
className={cn({
|
||||
highlighted: noTouch && this.state.shipId === s.id,
|
||||
comparehighlight: this.state.compare[s.id],
|
||||
})}
|
||||
onMouseEnter={noTouch && this._highlightShip.bind(this, s.id)}
|
||||
onClick={() => this._toggleCompare(s.id)}
|
||||
>
|
||||
<td className='ri'>{s.manufacturer}</td>
|
||||
<td className='ri'>{fInt(s.retailCost)}</td>
|
||||
<td className='ri cap'>{translate(SizeMap[s.class])}</td>
|
||||
<td className='ri'>{fInt(s.crew)}</td>
|
||||
<td className='ri'>{s.masslock}</td>
|
||||
<td className='ri'>{fInt(s.agility)}</td>
|
||||
<td className='ri'>{fInt(s.hardness)}</td>
|
||||
<td className='ri'>{fInt(s.hullMass)}</td>
|
||||
<td className='ri'>{fInt(s.speed)}</td>
|
||||
<td className='ri'>{fInt(s.boost)}</td>
|
||||
<td className='ri'>{fInt(s.baseArmour)}</td>
|
||||
<td className='ri'>{fInt(s.baseShieldStrength)}</td>
|
||||
<td className='ri'>{fInt(s.topSpeed)}</td>
|
||||
<td className='ri'>{fInt(s.topBoost)}</td>
|
||||
<td className='ri'>{fRound(s.maxJumpRange)}</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[1]}</td>
|
||||
<td className='cn'>{s.standard[2]}</td>
|
||||
<td className='cn'>{s.standard[3]}</td>
|
||||
<td className='cn'>{s.standard[4]}</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[2] })}>{s.hp[2]}</td>
|
||||
<td className={cn({ disabled: !s.hp[3] })}>{s.hp[3]}</td>
|
||||
<td className={cn({ disabled: !s.hp[4] })}>{s.hp[4]}</td>
|
||||
<td className={cn({ disabled: !s.hp[0] })}>{s.hp[0]}</td>
|
||||
<td className={cn({ disabled: !s.int[0] })}>{s.int[0]}</td>
|
||||
<td className={cn({ disabled: !s.int[1] })}>{s.int[1]}</td>
|
||||
<td className={cn({ disabled: !s.int[2] })}>{s.int[2]}</td>
|
||||
<td className={cn({ disabled: !s.int[3] })}>{s.int[3]}</td>
|
||||
<td className={cn({ disabled: !s.int[4] })}>{s.int[4]}</td>
|
||||
<td className={cn({ disabled: !s.int[5] })}>{s.int[5]}</td>
|
||||
<td className={cn({ disabled: !s.int[6] })}>{s.int[6]}</td>
|
||||
<td className={cn({ disabled: !s.int[7] })}>{s.int[7]}</td>
|
||||
</tr>;
|
||||
<td className="ri">{fInt(s.retailCost)}</td>
|
||||
<td className="ri cap">{translate(SizeMap[s.class])}</td>
|
||||
<td className="ri">{fInt(s.crew)}</td>
|
||||
<td className="ri">{s.masslock}</td>
|
||||
<td className="ri">{fInt(s.agility)}</td>
|
||||
<td className="ri">{fInt(s.hardness)}</td>
|
||||
<td className="ri">{fInt(s.hullMass)}</td>
|
||||
<td className="ri">{fInt(s.speed)}</td>
|
||||
<td className="ri">{fInt(s.boost)}</td>
|
||||
<td className="ri">{fInt(s.baseArmour)}</td>
|
||||
<td className="ri">{fInt(s.baseShieldStrength)}</td>
|
||||
<td className="ri">{fInt(s.topSpeed)}</td>
|
||||
<td className="ri">{fInt(s.topBoost)}</td>
|
||||
<td className="ri">{fRound(s.maxJumpRange)}</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[1]}</td>
|
||||
<td className="cn">{s.standard[2]}</td>
|
||||
<td className="cn">{s.standard[3]}</td>
|
||||
<td className="cn">{s.standard[4]}</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[2] })}>{s.hp[2]}</td>
|
||||
<td className={cn({ disabled: !s.hp[3] })}>{s.hp[3]}</td>
|
||||
<td className={cn({ disabled: !s.hp[4] })}>{s.hp[4]}</td>
|
||||
<td className={cn({ disabled: !s.hp[0] })}>{s.hp[0]}</td>
|
||||
<td className={cn({ disabled: !s.int[0] })}>{s.int[0]}</td>
|
||||
<td className={cn({ disabled: !s.int[1] })}>{s.int[1]}</td>
|
||||
<td className={cn({ disabled: !s.int[2] })}>{s.int[2]}</td>
|
||||
<td className={cn({ disabled: !s.int[3] })}>{s.int[3]}</td>
|
||||
<td className={cn({ disabled: !s.int[4] })}>{s.int[4]}</td>
|
||||
<td className={cn({ disabled: !s.int[5] })}>{s.int[5]}</td>
|
||||
<td className={cn({ disabled: !s.int[6] })}>{s.int[6]}</td>
|
||||
<td className={cn({ disabled: !s.int[7] })}>{s.int[7]}</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -221,8 +255,9 @@ export default class ShipyardPage extends Page {
|
||||
let hide = this.context.tooltip.bind(null, null);
|
||||
let fInt = formats.int;
|
||||
let fRound = formats.round;
|
||||
let { shipSummaries, shipPredicate, shipPredicateIndex } = this.state;
|
||||
let sortShips = (predicate, index) => this._sortShips.bind(this, predicate, index);
|
||||
let { shipSummaries, shipPredicate, shipPredicateIndex, compare, groupCompared } = this.state;
|
||||
let sortShips = (predicate, index) =>
|
||||
this._sortShips.bind(this, predicate, index);
|
||||
|
||||
let filters = {
|
||||
// 'class': { 1: 1, 2: 1}
|
||||
@@ -239,7 +274,8 @@ export default class ShipyardPage extends Page {
|
||||
|
||||
// Sort shipsOverview
|
||||
shipSummaries.sort((a, b) => {
|
||||
let valA = a[shipPredicate], valB = b[shipPredicate];
|
||||
let valA = a[shipPredicate],
|
||||
valB = b[shipPredicate];
|
||||
|
||||
if (shipPredicateIndex != undefined) {
|
||||
valA = valA[shipPredicateIndex];
|
||||
@@ -252,7 +288,16 @@ export default class ShipyardPage extends Page {
|
||||
valB = val;
|
||||
}
|
||||
|
||||
if(valA == valB) {
|
||||
if (groupCompared) {
|
||||
if (compare[a.id] && !compare[b.id]) {
|
||||
return -1;
|
||||
}
|
||||
if (!compare[a.id] && compare[b.id]) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (valA == valB) {
|
||||
if (a.name > b.name) {
|
||||
return 1;
|
||||
} else {
|
||||
@@ -269,130 +314,310 @@ export default class ShipyardPage extends Page {
|
||||
let shipRows = new Array(shipSummaries.length);
|
||||
let detailRows = new Array(shipSummaries.length);
|
||||
|
||||
let lastShipSortValue = null;
|
||||
let backgroundHighlight = false;
|
||||
|
||||
for (let s of shipSummaries) {
|
||||
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);
|
||||
detailRows[i] = this._shipRowElement(
|
||||
s,
|
||||
translate,
|
||||
units,
|
||||
fInt,
|
||||
formats.f1,
|
||||
);
|
||||
shipRows[i] = (
|
||||
<tr
|
||||
key={i}
|
||||
style={{ height: '1.5em' }}
|
||||
className={cn({ highlighted: noTouch && this.state.shipId === s.id, alt: backgroundHighlight })}
|
||||
className={cn({
|
||||
highlighted: noTouch && this.state.shipId === s.id,
|
||||
comparehighlight: this.state.compare[s.id],
|
||||
})}
|
||||
onMouseEnter={noTouch && this._highlightShip.bind(this, s.id)}
|
||||
onClick={() => this._toggleCompare(s.id)}
|
||||
>
|
||||
<td className='le'><Link href={'/outfit/' + s.id}>{s.name}</Link></td>
|
||||
<td className="le">
|
||||
<Link href={'/outfit/' + s.id}>{s.name} {s.beta === true ? '(Beta)' : null}</Link>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
i++;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='page' style={{ fontSize: sizeRatio + 'em' }}>
|
||||
<div style={{ whiteSpace: 'nowrap', margin: '0 auto', fontSize: '0.8em', position: 'relative', display: 'inline-block', maxWidth: '100%' }}>
|
||||
<table style={{ width: '12em', position: 'absolute', zIndex: 1 }}>
|
||||
<div className="page" style={{fontSize: sizeRatio + 'em'}}>
|
||||
<div className="content-wrapper">
|
||||
<div className="shipyard-table-wrapper">
|
||||
<table style={{width: '12em', position: 'absolute', zIndex: 1}} className="shipyard-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className='le rgt'> </th>
|
||||
<th className="le rgt"> </th>
|
||||
</tr>
|
||||
<tr className='main'>
|
||||
<th className='sortable le rgt' onClick={sortShips('name')}>{translate('ship')}</th>
|
||||
<tr className="main">
|
||||
<th className="sortable le rgt" onClick={sortShips('name')}>
|
||||
{translate('ship')}
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th className='le rgt invisible'>{units['m/s']}</th>
|
||||
<th className="le rgt invisible">{units['m/s']}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody onMouseLeave={this._highlightShip.bind(this, null)}>
|
||||
{shipRows}
|
||||
</tbody>
|
||||
</table>
|
||||
<div style={{ overflowX: 'scroll', maxWidth: '100%' }}>
|
||||
<table style={{ marginLeft: 'calc(12em - 1px)', zIndex: 0 }}>
|
||||
<thead>
|
||||
<tr className='main'>
|
||||
<th rowSpan={3} className='sortable' onClick={sortShips('manufacturer')}>{translate('manufacturer')}</th>
|
||||
<th> </th>
|
||||
<th rowSpan={3} className='sortable' onClick={sortShips('class')}>{translate('size')}</th>
|
||||
<th rowSpan={3} className='sortable' onClick={sortShips('crew')}>{translate('crew')}</th>
|
||||
<th rowSpan={3} className='sortable' onMouseEnter={termtip.bind(null, 'mass lock factor')} onMouseLeave={hide} onClick={sortShips('masslock')} >{translate('MLF')}</th>
|
||||
<th rowSpan={3} className='sortable' onClick={sortShips('agility')}>{translate('agility')}</th>
|
||||
<th rowSpan={3} className='sortable' onMouseEnter={termtip.bind(null, 'hardness')} onMouseLeave={hide} onClick={sortShips('hardness')}>{translate('hrd')}</th>
|
||||
<th> </th>
|
||||
<th colSpan={4}>{translate('base')}</th>
|
||||
<th colSpan={5}>{translate('max')}</th>
|
||||
<th className='lft' colSpan={7}></th>
|
||||
<th className='lft' colSpan={5}></th>
|
||||
<th className='lft' colSpan={8}></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th className='sortable lft' onClick={sortShips('retailCost')}>{translate('cost')}</th>
|
||||
<th className='sortable lft' onClick={sortShips('hullMass')}>{translate('hull')}</th>
|
||||
<th className='sortable lft' onClick={sortShips('speed')}>{translate('speed')}</th>
|
||||
<th className='sortable' onClick={sortShips('boost')}>{translate('boost')}</th>
|
||||
<th className='sortable' onClick={sortShips('baseArmour')}>{translate('armour')}</th>
|
||||
<th className='sortable' onClick={sortShips('baseShieldStrength')}>{translate('shields')}</th>
|
||||
<div style={{ overflowX: 'auto', maxWidth: '100%' }}>
|
||||
<table style={{ marginLeft: 'calc(12em - 1px)', zIndex: 0 }} className="shipyard-table">
|
||||
<thead>
|
||||
<tr className="main">
|
||||
<th> </th>
|
||||
<th
|
||||
rowSpan={3}
|
||||
className="sortable"
|
||||
onClick={sortShips('class')}
|
||||
>
|
||||
{translate('size')}
|
||||
</th>
|
||||
<th
|
||||
rowSpan={3}
|
||||
className="sortable"
|
||||
onClick={sortShips('crew')}
|
||||
>
|
||||
{translate('crew')}
|
||||
</th>
|
||||
<th
|
||||
rowSpan={3}
|
||||
className="sortable"
|
||||
onMouseEnter={termtip.bind(null, 'mass lock factor')}
|
||||
onMouseLeave={hide}
|
||||
onClick={sortShips('masslock')}
|
||||
>
|
||||
{translate('MLF')}
|
||||
</th>
|
||||
<th
|
||||
rowSpan={3}
|
||||
className="sortable"
|
||||
onClick={sortShips('agility')}
|
||||
>
|
||||
{translate('agility')}
|
||||
</th>
|
||||
<th
|
||||
rowSpan={3}
|
||||
className="sortable"
|
||||
onMouseEnter={termtip.bind(null, 'hardness')}
|
||||
onMouseLeave={hide}
|
||||
onClick={sortShips('hardness')}
|
||||
>
|
||||
{translate('hrd')}
|
||||
</th>
|
||||
<th> </th>
|
||||
<th colSpan={4}>{translate('base')}</th>
|
||||
<th colSpan={5}>{translate('max')}</th>
|
||||
<th className="lft" colSpan={7} />
|
||||
<th className="lft" colSpan={5} />
|
||||
<th className="lft" colSpan={8} />
|
||||
</tr>
|
||||
<tr>
|
||||
<th
|
||||
className="sortable lft"
|
||||
onClick={sortShips('retailCost')}
|
||||
>
|
||||
{translate('cost')}
|
||||
</th>
|
||||
<th className="sortable lft" onClick={sortShips('hullMass')}>
|
||||
{translate('hull')}
|
||||
</th>
|
||||
<th className="sortable lft" onClick={sortShips('speed')}>
|
||||
{translate('speed')}
|
||||
</th>
|
||||
<th className="sortable" onClick={sortShips('boost')}>
|
||||
{translate('boost')}
|
||||
</th>
|
||||
<th className="sortable" onClick={sortShips('baseArmour')}>
|
||||
{translate('armour')}
|
||||
</th>
|
||||
<th
|
||||
className="sortable"
|
||||
onClick={sortShips('baseShieldStrength')}
|
||||
>
|
||||
{translate('shields')}
|
||||
</th>
|
||||
|
||||
<th className='sortable lft' onClick={sortShips('topSpeed')}>{translate('speed')}</th>
|
||||
<th className='sortable' onClick={sortShips('topBoost')}>{translate('boost')}</th>
|
||||
<th className='sortable' onClick={sortShips('maxJumpRange')}>{translate('jump')}</th>
|
||||
<th className='sortable' onClick={sortShips('maxCargo')}>{translate('cargo')}</th>
|
||||
<th className='sortable' onClick={sortShips('maxPassengers')}>{translate('pax')}</th>
|
||||
<th className="sortable lft" onClick={sortShips('topSpeed')}>
|
||||
{translate('speed')}
|
||||
</th>
|
||||
<th className="sortable" onClick={sortShips('topBoost')}>
|
||||
{translate('boost')}
|
||||
</th>
|
||||
<th className="sortable" onClick={sortShips('maxJumpRange')}>
|
||||
{translate('jump')}
|
||||
</th>
|
||||
<th className="sortable" onClick={sortShips('maxCargo')}>
|
||||
{translate('cargo')}
|
||||
</th>
|
||||
<th className="sortable" onClick={sortShips('maxPassengers')} onMouseEnter={termtip.bind(null, 'passenger capacity')}
|
||||
onMouseLeave={hide}>
|
||||
{translate('pax')}
|
||||
</th>
|
||||
<th className="lft" colSpan={7}>
|
||||
{translate('core module classes')}
|
||||
</th>
|
||||
<th
|
||||
colSpan={5}
|
||||
className="sortable lft"
|
||||
onClick={sortShips('hpCount')}
|
||||
>
|
||||
{translate('hardpoints')}
|
||||
</th>
|
||||
<th
|
||||
colSpan={8}
|
||||
className="sortable lft"
|
||||
onClick={sortShips('intCount')}
|
||||
>
|
||||
{translate('internal compartments')}
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th
|
||||
className="sortable lft"
|
||||
onClick={sortShips('retailCost')}
|
||||
>
|
||||
{units.CR}
|
||||
</th>
|
||||
<th className="sortable lft" onClick={sortShips('hullMass')}>
|
||||
{units.T}
|
||||
</th>
|
||||
<th className="sortable lft" onClick={sortShips('speed')}>
|
||||
{units['m/s']}
|
||||
</th>
|
||||
<th className="sortable" onClick={sortShips('boost')}>
|
||||
{units['m/s']}
|
||||
</th>
|
||||
<th> </th>
|
||||
<th
|
||||
className="sortable"
|
||||
onClick={sortShips('baseShieldStrength')}
|
||||
>
|
||||
{units.MJ}
|
||||
</th>
|
||||
<th className="sortable lft" onClick={sortShips('topSpeed')}>
|
||||
{units['m/s']}
|
||||
</th>
|
||||
<th className="sortable" onClick={sortShips('topBoost')}>
|
||||
{units['m/s']}
|
||||
</th>
|
||||
<th className="sortable" onClick={sortShips('maxJumpRange')}>
|
||||
{units.LY}
|
||||
</th>
|
||||
<th className="sortable" onClick={sortShips('maxCargo')}>
|
||||
{units.T}
|
||||
</th>
|
||||
<th> </th>
|
||||
<th
|
||||
className="sortable lft"
|
||||
onMouseEnter={termtip.bind(null, 'power plant')}
|
||||
onMouseLeave={hide}
|
||||
onClick={sortShips('standard', 0)}
|
||||
>
|
||||
{'pp'}
|
||||
</th>
|
||||
<th
|
||||
className="sortable"
|
||||
onMouseEnter={termtip.bind(null, 'thrusters')}
|
||||
onMouseLeave={hide}
|
||||
onClick={sortShips('standard', 1)}
|
||||
>
|
||||
{'th'}
|
||||
</th>
|
||||
<th
|
||||
className="sortable"
|
||||
onMouseEnter={termtip.bind(null, 'frame shift drive')}
|
||||
onMouseLeave={hide}
|
||||
onClick={sortShips('standard', 2)}
|
||||
>
|
||||
{'fsd'}
|
||||
</th>
|
||||
<th
|
||||
className="sortable"
|
||||
onMouseEnter={termtip.bind(null, 'life support')}
|
||||
onMouseLeave={hide}
|
||||
onClick={sortShips('standard', 3)}
|
||||
>
|
||||
{'ls'}
|
||||
</th>
|
||||
<th
|
||||
className="sortable"
|
||||
onMouseEnter={termtip.bind(null, 'power distriubtor')}
|
||||
onMouseLeave={hide}
|
||||
onClick={sortShips('standard', 4)}
|
||||
>
|
||||
{'pd'}
|
||||
</th>
|
||||
<th
|
||||
className="sortable"
|
||||
onMouseEnter={termtip.bind(null, 'sensors')}
|
||||
onMouseLeave={hide}
|
||||
onClick={sortShips('standard', 5)}
|
||||
>
|
||||
{'s'}
|
||||
</th>
|
||||
<th
|
||||
className="sortable"
|
||||
onMouseEnter={termtip.bind(null, 'fuel tank')}
|
||||
onMouseLeave={hide}
|
||||
onClick={sortShips('standard', 6)}
|
||||
>
|
||||
{'ft'}
|
||||
</th>
|
||||
<th className="sortable lft" onClick={sortShips('hp', 1)}>
|
||||
{translate('S')}
|
||||
</th>
|
||||
<th className="sortable" onClick={sortShips('hp', 2)}>
|
||||
{translate('M')}
|
||||
</th>
|
||||
<th className="sortable" onClick={sortShips('hp', 3)}>
|
||||
{translate('L')}
|
||||
</th>
|
||||
<th className="sortable" onClick={sortShips('hp', 4)}>
|
||||
{translate('H')}
|
||||
</th>
|
||||
<th className="sortable" onClick={sortShips('hp', 0)}>
|
||||
{translate('U')}
|
||||
</th>
|
||||
|
||||
<th className='lft' colSpan={7}>{translate('core module classes')}</th>
|
||||
<th colSpan={5} className='sortable lft' onClick={sortShips('hpCount')}>{translate('hardpoints')}</th>
|
||||
<th colSpan={8} className='sortable lft' onClick={sortShips('intCount')}>{translate('internal compartments')}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th className='sortable lft' onClick={sortShips('retailCost')}>{units.CR}</th>
|
||||
<th className='sortable lft' onClick={sortShips('hullMass')}>{units.T}</th>
|
||||
<th className='sortable lft' onClick={sortShips('speed')}>{units['m/s']}</th>
|
||||
<th className='sortable' onClick={sortShips('boost')}>{units['m/s']}</th>
|
||||
<th> </th>
|
||||
<th className='sortable' onClick={sortShips('baseShieldStrength')}>{units.MJ}</th>
|
||||
<th className='sortable lft' onClick={sortShips('topSpeed')}>{units['m/s']}</th>
|
||||
<th className='sortable' onClick={sortShips('topBoost')}>{units['m/s']}</th>
|
||||
<th className='sortable' onClick={sortShips('maxJumpRange')}>{units.LY}</th>
|
||||
<th className='sortable' onClick={sortShips('maxCargo')}>{units.T}</th>
|
||||
<th> </th>
|
||||
<th className='sortable lft' onMouseEnter={termtip.bind(null, 'power plant')} onMouseLeave={hide} onClick={sortShips('standard', 0)}>{'pp'}</th>
|
||||
<th className='sortable' onMouseEnter={termtip.bind(null, 'thrusters')} onMouseLeave={hide} onClick={sortShips('standard', 1)}>{'th'}</th>
|
||||
<th className='sortable' onMouseEnter={termtip.bind(null, 'frame shift drive')} onMouseLeave={hide} onClick={sortShips('standard', 2)}>{'fsd'}</th>
|
||||
<th className='sortable' onMouseEnter={termtip.bind(null, 'life support')} onMouseLeave={hide} onClick={sortShips('standard', 3)}>{'ls'}</th>
|
||||
<th className='sortable' onMouseEnter={termtip.bind(null, 'power distriubtor')} onMouseLeave={hide} onClick={sortShips('standard', 4)}>{'pd'}</th>
|
||||
<th className='sortable' onMouseEnter={termtip.bind(null, 'sensors')} onMouseLeave={hide} onClick={sortShips('standard', 5)}>{'s'}</th>
|
||||
<th className='sortable' onMouseEnter={termtip.bind(null, 'fuel tank')} onMouseLeave={hide} onClick={sortShips('standard', 6)}>{'ft'}</th>
|
||||
<th className='sortable lft' onClick={sortShips('hp',1)}>{translate('S')}</th>
|
||||
<th className='sortable' onClick={sortShips('hp', 2)}>{translate('M')}</th>
|
||||
<th className='sortable' onClick={sortShips('hp', 3)}>{translate('L')}</th>
|
||||
<th className='sortable' onClick={sortShips('hp', 4)}>{translate('H')}</th>
|
||||
<th className='sortable' onClick={sortShips('hp', 0)}>{translate('U')}</th>
|
||||
|
||||
<th className='sortable lft' onClick={sortShips('int', 0)} >1</th>
|
||||
<th className='sortable' onClick={sortShips('int', 1)} >2</th>
|
||||
<th className='sortable' onClick={sortShips('int', 2)} >3</th>
|
||||
<th className='sortable' onClick={sortShips('int', 3)} >4</th>
|
||||
<th className='sortable' onClick={sortShips('int', 4)} >5</th>
|
||||
<th className='sortable' onClick={sortShips('int', 5)} >6</th>
|
||||
<th className='sortable' onClick={sortShips('int', 6)} >7</th>
|
||||
<th className='sortable' onClick={sortShips('int', 7)} >8</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody onMouseLeave={this._highlightShip.bind(this, null)}>
|
||||
{detailRows}
|
||||
</tbody>
|
||||
</table>
|
||||
<th className="sortable lft" onClick={sortShips('int', 0)}>
|
||||
1
|
||||
</th>
|
||||
<th className="sortable" onClick={sortShips('int', 1)}>
|
||||
2
|
||||
</th>
|
||||
<th className="sortable" onClick={sortShips('int', 2)}>
|
||||
3
|
||||
</th>
|
||||
<th className="sortable" onClick={sortShips('int', 3)}>
|
||||
4
|
||||
</th>
|
||||
<th className="sortable" onClick={sortShips('int', 4)}>
|
||||
5
|
||||
</th>
|
||||
<th className="sortable" onClick={sortShips('int', 5)}>
|
||||
6
|
||||
</th>
|
||||
<th className="sortable" onClick={sortShips('int', 6)}>
|
||||
7
|
||||
</th>
|
||||
<th className="sortable" onClick={sortShips('int', 7)}>
|
||||
8
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody onMouseLeave={this._highlightShip.bind(this, null)}>
|
||||
{detailRows}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div className="table-tools" >
|
||||
<label><input type="checkbox" checked={this.state.groupCompared} onClick={() => this._toggleGroupCompared()}/>Group highlighted ships</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,8 +14,9 @@ export function jumpRange(mass, fsd, fuel, ship) {
|
||||
const fsdOptimalMass = fsd instanceof Module ? fsd.getOptMass() : fsd.optmass;
|
||||
let jumpAddition = 0;
|
||||
if (ship) {
|
||||
mass += ship.reserveFuelCapacity || 0;
|
||||
for (const module of ship.internal) {
|
||||
if (module && module.m && module.m.grp === 'gfsb') {
|
||||
if (module && module.m && module.m.grp === 'gfsb' && ship.getSlotStatus(module) == 3) {
|
||||
jumpAddition += module.m.getJumpBoost();
|
||||
}
|
||||
}
|
||||
@@ -340,67 +341,34 @@ export function shieldMetrics(ship, sys) {
|
||||
const maxSysResistance = this.sysResistance(4);
|
||||
|
||||
let shield = {};
|
||||
const dimReturnLine = (res) => 1 - (1 - res) * 0.7;
|
||||
|
||||
const shieldGeneratorSlot = ship.findInternalByGroup('sg');
|
||||
if (shieldGeneratorSlot && shieldGeneratorSlot.enabled && shieldGeneratorSlot.m) {
|
||||
const shieldGenerator = shieldGeneratorSlot.m;
|
||||
let res = {
|
||||
kin: shieldGenerator.kinres,
|
||||
therm: shieldGenerator.thermres,
|
||||
expl: shieldGenerator.explres
|
||||
};
|
||||
// Boosters
|
||||
let boost = 1;
|
||||
let boosterExplDmg = 1;
|
||||
let boosterKinDmg = 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) {
|
||||
if (slot.enabled && slot.m && slot.m.grp == 'sb') {
|
||||
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());
|
||||
boosterKinDmg = boosterKinDmg * (1 - slot.m.getKineticResistance());
|
||||
boosterThermDmg = boosterThermDmg * (1 - slot.m.getThermalResistance());
|
||||
}
|
||||
if (slot.m && slot.m.grp == 'gsrp') {
|
||||
|
||||
}
|
||||
}
|
||||
// Calculate diminishing returns for boosters
|
||||
// Diminishing returns not currently in-game
|
||||
// boost = Math.min(boost, (1 - Math.pow(Math.E, -0.7 * boost)) * 2.5);
|
||||
|
||||
|
||||
// Remove base shield generator strength
|
||||
boost -= 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') {
|
||||
if (module && module.m && module.m.grp === 'gsrp' && module.enabled) {
|
||||
shieldAddition += module.m.getShieldAddition();
|
||||
}
|
||||
}
|
||||
@@ -465,6 +433,7 @@ export function shieldMetrics(ship, sys) {
|
||||
boosters: boostersStrength,
|
||||
addition: shieldAddition,
|
||||
cells: ship.shieldCells,
|
||||
summary: generatorStrength + boostersStrength + shieldAddition,
|
||||
total: generatorStrength + boostersStrength + ship.shieldCells + shieldAddition,
|
||||
recover,
|
||||
recharge,
|
||||
@@ -497,7 +466,7 @@ export function shieldMetrics(ship, sys) {
|
||||
*/
|
||||
|
||||
let sgExplosiveDmg = 1 - shieldGenerator.getExplosiveResistance();
|
||||
let sgSbExplosiveDmg = diminishDamageMult(sgExplosiveDmg * 0.7, (1 - shieldGenerator.getExplosiveResistance()) * boosterExplDmg);
|
||||
let sgSbExplosiveDmg = diminishingReturnsShields(sgExplosiveDmg, sgExplosiveDmg * boosterExplDmg);
|
||||
/** @type {ShieldDamageMults} */
|
||||
shield.explosive = {
|
||||
generator: sgExplosiveDmg,
|
||||
@@ -509,7 +478,7 @@ export function shieldMetrics(ship, sys) {
|
||||
};
|
||||
|
||||
let sgKineticDmg = 1 - shieldGenerator.getKineticResistance();
|
||||
let sgSbKineticDmg = diminishDamageMult(sgKineticDmg * 0.7, (1 - shieldGenerator.getKineticResistance()) * boosterKinDmg);
|
||||
let sgSbKineticDmg = diminishingReturnsShields(sgKineticDmg, sgKineticDmg * boosterKinDmg);
|
||||
/** @type {ShieldDamageMults} */
|
||||
shield.kinetic = {
|
||||
generator: sgKineticDmg,
|
||||
@@ -521,7 +490,7 @@ export function shieldMetrics(ship, sys) {
|
||||
};
|
||||
|
||||
let sgThermalDmg = 1 - shieldGenerator.getThermalResistance();
|
||||
let sgSbThermalDmg = diminishDamageMult(sgThermalDmg * 0.7, (1 - shieldGenerator.getThermalResistance()) * boosterThermDmg);
|
||||
let sgSbThermalDmg = diminishingReturnsShields(sgThermalDmg , sgThermalDmg * boosterThermDmg);
|
||||
/** @type {ShieldDamageMults} */
|
||||
shield.thermal = {
|
||||
generator: sgThermalDmg,
|
||||
@@ -561,58 +530,28 @@ export function armourMetrics(ship) {
|
||||
let moduleArmour = 0;
|
||||
let moduleProtection = 1;
|
||||
const bulkheads = ship.bulkheads.m;
|
||||
let hullExplDmg = 1;
|
||||
let hullKinDmg = 1;
|
||||
let hullThermDmg = 1;
|
||||
let hullCausDmg = 1;
|
||||
// const dimReturnLine = (res) => 1 - (1 - res) * 0.7;
|
||||
// let res = {
|
||||
// kin: 0,
|
||||
// therm: 0,
|
||||
// expl: 0
|
||||
// };
|
||||
let hullExplDmgs = [];
|
||||
let hullKinDmgs = [];
|
||||
let hullThermDmgs = [];
|
||||
let hullCausDmgs = [];
|
||||
// Armour from HRPs and module armour from MRPs
|
||||
for (let slot of ship.internal) {
|
||||
if (slot.m && (slot.m.grp === 'hr' || slot.m.grp === 'ghrp' || slot.m.grp == 'mahr')) {
|
||||
if (slot.m && slot.enabled && (slot.m.grp === 'hr' || slot.m.grp === 'ghrp' || slot.m.grp == 'mahr')) {
|
||||
armourReinforcement += slot.m.getHullReinforcement();
|
||||
// Hull boost for HRPs is applied against the ship's base armour
|
||||
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());
|
||||
hullKinDmg = hullKinDmg * (1 - slot.m.getKineticResistance());
|
||||
hullThermDmg = hullThermDmg * (1 - slot.m.getThermalResistance());
|
||||
hullCausDmg = hullCausDmg * (1 - slot.m.getCausticResistance());
|
||||
hullExplDmgs.push(1 - slot.m.getExplosiveResistance());
|
||||
hullKinDmgs.push(1 - slot.m.getKineticResistance());
|
||||
hullThermDmgs.push(1 - slot.m.getThermalResistance());
|
||||
hullCausDmgs.push(1 - slot.m.getCausticResistance());
|
||||
}
|
||||
if (slot.m && (slot.m.grp == 'mrp' || slot.m.grp == 'gmrp')) {
|
||||
if (slot.m && slot.enabled && (slot.m.grp == 'mrp' || slot.m.grp == 'gmrp')) {
|
||||
moduleArmour += slot.m.getIntegrity();
|
||||
moduleProtection = moduleProtection * (1 - slot.m.getProtection());
|
||||
}
|
||||
}
|
||||
moduleProtection = 1 - moduleProtection;
|
||||
|
||||
// const explDim = dimReturnLine(bulkheads.explres);
|
||||
// const thermDim = dimReturnLine(bulkheads.thermres);
|
||||
// const kinDim = dimReturnLine(bulkheads.kinres);
|
||||
// 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 = {
|
||||
bulkheads: armourBulkheads,
|
||||
reinforcement: armourReinforcement,
|
||||
@@ -629,8 +568,8 @@ export function armourMetrics(ship) {
|
||||
total: 1
|
||||
};
|
||||
|
||||
let armourExplDmg = diminishDamageMult(0.7, 1 - ship.bulkheads.m.getExplosiveResistance());
|
||||
let armourReinforcedExplDmg = diminishDamageMult(0.7, (1 - ship.bulkheads.m.getExplosiveResistance()) * hullExplDmg);
|
||||
let armourExplDmg = 1 - ship.bulkheads.m.getExplosiveResistance();
|
||||
let armourReinforcedExplDmg = diminishingReturnsArmour(armourExplDmg, ...hullExplDmgs);
|
||||
armour.explosive = {
|
||||
bulkheads: armourExplDmg,
|
||||
reinforcement: armourReinforcedExplDmg / armourExplDmg,
|
||||
@@ -638,8 +577,8 @@ export function armourMetrics(ship) {
|
||||
res: 1 - armourReinforcedExplDmg
|
||||
};
|
||||
|
||||
let armourKinDmg = diminishDamageMult(0.7, 1 - ship.bulkheads.m.getKineticResistance());
|
||||
let armourReinforcedKinDmg = diminishDamageMult(0.7, (1 - ship.bulkheads.m.getKineticResistance()) * hullKinDmg);
|
||||
let armourKinDmg = 1 - ship.bulkheads.m.getKineticResistance();
|
||||
let armourReinforcedKinDmg = diminishingReturnsArmour(armourKinDmg, ...hullKinDmgs);
|
||||
armour.kinetic = {
|
||||
bulkheads: armourKinDmg,
|
||||
reinforcement: armourReinforcedKinDmg / armourKinDmg,
|
||||
@@ -647,8 +586,8 @@ export function armourMetrics(ship) {
|
||||
res: 1 - armourReinforcedKinDmg
|
||||
};
|
||||
|
||||
let armourThermDmg = diminishDamageMult(0.7, 1 - ship.bulkheads.m.getThermalResistance());
|
||||
let armourReinforcedThermDmg = diminishDamageMult(0.7, (1 - ship.bulkheads.m.getThermalResistance()) * hullThermDmg);
|
||||
let armourThermDmg = 1 - ship.bulkheads.m.getThermalResistance();
|
||||
let armourReinforcedThermDmg = diminishingReturnsArmour(armourThermDmg, ...hullThermDmgs);
|
||||
armour.thermal = {
|
||||
bulkheads: armourThermDmg,
|
||||
reinforcement: armourReinforcedThermDmg / armourThermDmg,
|
||||
@@ -656,8 +595,8 @@ export function armourMetrics(ship) {
|
||||
res: 1 - armourReinforcedThermDmg
|
||||
};
|
||||
|
||||
let armourCausDmg = diminishDamageMult(0.7, 1 - ship.bulkheads.m.getCausticResistance());
|
||||
let armourReinforcedCausDmg = diminishDamageMult(0.7, (1 - ship.bulkheads.m.getCausticResistance()) * hullCausDmg);
|
||||
let armourCausDmg = 1 - ship.bulkheads.m.getCausticResistance();
|
||||
let armourReinforcedCausDmg = diminishingReturnsArmour(armourCausDmg, ...hullCausDmgs);
|
||||
armour.caustic = {
|
||||
bulkheads: armourCausDmg,
|
||||
reinforcement: armourReinforcedCausDmg / armourCausDmg,
|
||||
@@ -903,12 +842,14 @@ export function _weaponSustainedDps(m, opponent, opponentShields, opponentArmour
|
||||
shields: {
|
||||
range: 1,
|
||||
sys: opponentHasShields ? opponentShields.absolute.sys : 1,
|
||||
resistance: 1
|
||||
resistance: 1,
|
||||
dpe: 1
|
||||
},
|
||||
armour: {
|
||||
range: 1,
|
||||
hardness: 1,
|
||||
resistance: 1
|
||||
resistance: 1,
|
||||
dpe: 1
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -968,11 +909,19 @@ export function _weaponSustainedDps(m, opponent, opponentShields, opponentArmour
|
||||
weapon.damage.shields.total = weapon.damage.shields.absolute + weapon.damage.shields.explosive + weapon.damage.shields.kinetic + weapon.damage.shields.thermal;
|
||||
weapon.damage.armour.total = weapon.damage.armour.absolute + weapon.damage.armour.explosive + weapon.damage.armour.kinetic + weapon.damage.armour.thermal;
|
||||
|
||||
|
||||
|
||||
weapon.effectiveness.shields.resistance *= shieldsResistance;
|
||||
weapon.effectiveness.armour.resistance *= armourResistance;
|
||||
|
||||
|
||||
weapon.effectiveness.shields.total = weapon.effectiveness.shields.range * weapon.effectiveness.shields.sys * weapon.effectiveness.shields.resistance;
|
||||
weapon.effectiveness.armour.total = weapon.effectiveness.armour.range * weapon.effectiveness.armour.resistance * weapon.effectiveness.armour.hardness;
|
||||
|
||||
weapon.effectiveness.shields.dpe = weapon.damage.shields.total / m.getEps();
|
||||
weapon.effectiveness.armour.dpe = weapon.damage.armour.total / m.getEps();
|
||||
|
||||
|
||||
return weapon;
|
||||
}
|
||||
|
||||
@@ -1014,7 +963,10 @@ export function timeToDrainWep(ship, wep) {
|
||||
*/
|
||||
export function timeToDeplete(amount, dps, eps, capacity, recharge) {
|
||||
const drainPerSecond = eps - recharge;
|
||||
if (drainPerSecond <= 0) {
|
||||
// If there is nothing to remove, we're don instantly
|
||||
if (!amount) {
|
||||
return 0;
|
||||
} if (drainPerSecond <= 0) {
|
||||
// Simple result
|
||||
return amount / dps;
|
||||
} else {
|
||||
@@ -1033,15 +985,50 @@ 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
|
||||
* Checks whether diminishing returns should be applied to shield damage
|
||||
* multipliers and does so if necessary.
|
||||
* @param {number} shieldMult Damage multiplier of shield generator
|
||||
* @param {number} combinedMult Damage multiplier of shields and shield boosters
|
||||
* @returns {number} Overall damage multiplier
|
||||
*/
|
||||
export function diminishDamageMult(diminishFrom, damageMult) {
|
||||
if (damageMult > diminishFrom) {
|
||||
return damageMult;
|
||||
export function diminishingReturnsShields(shieldMult, combinedMult) {
|
||||
let max = shieldMult * 0.7;
|
||||
if (combinedMult < max) {
|
||||
return mapIntoDiminishingRange(max / 2, max, combinedMult);
|
||||
} else {
|
||||
return (diminishFrom / 2) + 0.5 * damageMult;
|
||||
return combinedMult;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether diminishing returns should be applied to armour damage
|
||||
* multipliers and does so if necessary.
|
||||
* @param {...any} mults Damage multipliers of alloys and hull reinforcement
|
||||
* packages
|
||||
* @returns {number} Overall damage multiplier
|
||||
*/
|
||||
export function diminishingReturnsArmour(...mults) {
|
||||
let max = Math.min(0.7, ...mults);
|
||||
let combined = mults.reduce((aggr, v) => aggr * v);
|
||||
let diminished = mapIntoDiminishingRange(0.35, max, combined);
|
||||
if (diminished < 0.7) {
|
||||
return diminished;
|
||||
} else {
|
||||
return combined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies diminishing returns to a damage multiplier. Effictively, the range
|
||||
* [`0`, `max`]` is mapped into the range [`min`, `max`] for the value `now`.
|
||||
* It can also happen, that `now` is outside of the range [`min`, `max`], then
|
||||
* `now` is actually improved, i.e. enlarged.
|
||||
* @param {number} min Best theoretical damage multiplier
|
||||
* @param {number} max Damage multiplier from which diminishing returns start to
|
||||
* be applied
|
||||
* @param {number} now The current damage multiplier
|
||||
* @returns {number} Remapped damage multiplier
|
||||
*/
|
||||
export function mapIntoDiminishingRange(min, max, now) {
|
||||
return min + (max - min) * (now / max);
|
||||
}
|
||||
|
||||
@@ -57,6 +57,7 @@ export const ModuleGroupToName = {
|
||||
ghrp: 'Guardian Hull Reinforcement Package',
|
||||
gmrp: 'Guardian Module Reinforcement Package',
|
||||
mahr: 'Meta Alloy Hull Reinforcement Package',
|
||||
sua: 'Supercruise Assist',
|
||||
|
||||
// Hard Points
|
||||
bl: 'Beam Laser',
|
||||
@@ -94,6 +95,10 @@ export const ModuleGroupToName = {
|
||||
gsc: 'Guardian Shard Cannon',
|
||||
tbem: 'Enzyme Missile Rack',
|
||||
tbrfl: 'Remote Release Flechette Launcher',
|
||||
pwa: 'Pulse Wave Analyser',
|
||||
abl: 'Abrasion Blaster',
|
||||
scl: 'Seismic Charge Launcher',
|
||||
sdm: 'Sub-Surface Displacement Missile',
|
||||
};
|
||||
|
||||
let GrpNameToCodeMap = {};
|
||||
@@ -202,7 +207,7 @@ export const ShipFacets = [
|
||||
i: 9
|
||||
},
|
||||
{ // 10
|
||||
title: 'fastest range',
|
||||
title: 'farthest range',
|
||||
props: ['unladenFastestRange', 'ladenFastestRange'],
|
||||
lbls: ['unladen', 'laden'],
|
||||
unit: 'LY',
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
* Modification - a modification and its value
|
||||
*/
|
||||
export default class Modification {
|
||||
|
||||
/**
|
||||
* @param {String} id Unique modification ID
|
||||
* @param {Number} value Value of the modification
|
||||
@@ -11,5 +10,4 @@ export default class Modification {
|
||||
this.id = id;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import { STATS_FORMATTING, SI_PREFIXES } from './StatsFormatting';
|
||||
* Module - active module in a ship's buildout
|
||||
*/
|
||||
export default class Module {
|
||||
|
||||
/**
|
||||
* Construct a new module
|
||||
* @param {Object} params Module parameters. Either grp/id or template
|
||||
@@ -42,6 +41,7 @@ export default class Module {
|
||||
* @return {object} The value of the modification. If it is a numeric value then it is returned as an integer value scaled so that 1.23% == 123
|
||||
*/
|
||||
getModValue(name, raw) {
|
||||
let baseVal = this[name];
|
||||
let result = this.mods && this.mods[name] ? this.mods[name] : null;
|
||||
|
||||
if ((!raw) && this.blueprint && this.blueprint.special) {
|
||||
@@ -52,39 +52,18 @@ export default class Module {
|
||||
const modification = Modifications.modifications[name];
|
||||
const multiplier = modification.type === 'percentage' ? 10000 : 100;
|
||||
if (name === 'explres' || name === 'kinres' || name === 'thermres' || name === 'causres') {
|
||||
// Resistance modifications in itself are additive, however their
|
||||
// special effects are multiplicative. They affect the overall result
|
||||
// by (special effect resistance) * (damage mult after modification),
|
||||
// i. e. we need to apply the special effect as a multiplier to the
|
||||
// overall result and then calculate the difference.
|
||||
let baseMult = this[name] ? 1 - this[name] : 1;
|
||||
result = (baseMult - (baseMult - result / multiplier) * (1 - modifierActions[name] / 100)) * multiplier;
|
||||
// Apply resistance modding mechanisms to special effects subsequently
|
||||
result = result + modifierActions[name] * (1 - (this[name] + result / multiplier)) * 100;
|
||||
} else if (modification.method === 'additive') {
|
||||
result = result + modifierActions[name] * 100;
|
||||
} else if (modification.method === 'overwrite') {
|
||||
result = modifierActions[name];
|
||||
} else {
|
||||
// rate of fire is special, as it's really burst interval. Handle that here
|
||||
let mod = null;
|
||||
if (name === 'rof') {
|
||||
mod = 1 / (1 + modifierActions[name]) - 1;
|
||||
} else {
|
||||
mod = modifierActions[name];
|
||||
}
|
||||
result = (((1 + result / multiplier) * (1 + mod)) - 1) * multiplier;
|
||||
result = (((1 + result / multiplier) * (1 + modifierActions[name])) - 1) * multiplier;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Resistance modding for hull reinforcement packages has additional
|
||||
// diminishing returns implemented. The mod value gets lowered by
|
||||
// the amount of base resistance the hrp has.
|
||||
if (!isNaN(result) && this.grp === 'hr' &&
|
||||
(name === 'kinres' || name === 'thermres' || name === 'explres')) {
|
||||
let baseRes = this[name];
|
||||
result = result * (1 - baseRes);
|
||||
}
|
||||
|
||||
// Sanitise the resultant value to 4dp equivalent
|
||||
return isNaN(result) ? result : Math.round(result);
|
||||
}
|
||||
@@ -109,24 +88,17 @@ export default class Module {
|
||||
// This special effect modifies the value being set, so we need to revert it prior to storing the value
|
||||
const modification = Modifications.modifications[name];
|
||||
if (name === 'explres' || name === 'kinres' || name === 'thermres' || name === 'causres') {
|
||||
// Resistance modifications in itself are additive but their
|
||||
// experimentals are applied multiplicatively therefor we must handle
|
||||
// them differently here (cf. documentation in getModValue).
|
||||
let baseMult = (this[name] ? 1 - this[name] : 1);
|
||||
value = ((baseMult - value / 10000) / (1 - modifierActions[name] / 100) - baseMult) * -10000;
|
||||
let res = (this[name] ? this[name] : 0) + value / 10000;
|
||||
let experimental = modifierActions[name] / 100;
|
||||
value = (experimental - res) / (experimental - 1) - this[name];
|
||||
value *= 10000;
|
||||
// value = ((baseMult - value / 10000) / (1 - modifierActions[name] / 100) - baseMult) * -10000;
|
||||
} else if (modification.method === 'additive') {
|
||||
value = value - modifierActions[name];
|
||||
} else if (modification.method === 'overwrite') {
|
||||
value = null;
|
||||
} else {
|
||||
// rate of fire is special, as it's really burst interval. Handle that here
|
||||
let mod = null;
|
||||
if (name === 'rof') {
|
||||
mod = 1 / (1 + modifierActions[name]) - 1;
|
||||
} else {
|
||||
mod = modifierActions[name];
|
||||
}
|
||||
value = ((value / 10000 + 1) / (1 + mod) - 1) * 10000;
|
||||
value = ((value / 10000 + 1) / (1 + modifierActions[name]) - 1) * 10000;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -145,6 +117,13 @@ export default class Module {
|
||||
* @return {Number} The value queried
|
||||
*/
|
||||
get(name, modified = true) {
|
||||
if (name == 'rof' && isNaN(this[name])) {
|
||||
let fireint = this['fireint'];
|
||||
if (!isNaN(fireint)) {
|
||||
this['rof'] = 1 / fireint;
|
||||
}
|
||||
}
|
||||
|
||||
let val;
|
||||
if (modified) {
|
||||
val = this._getModifiedValue(name);
|
||||
@@ -178,21 +157,23 @@ export default class Module {
|
||||
baseValue = 0;
|
||||
}
|
||||
modValue = value - baseValue;
|
||||
if (this.grp === 'hr' &&
|
||||
(name === 'kinres' || name === 'thermres' || name === 'explres')) {
|
||||
modValue = modValue / (1 - baseValue);
|
||||
}
|
||||
} else if (name === 'shieldboost' || name === 'hullboost') {
|
||||
modValue = (1 + value) / (1 + baseValue) - 1;
|
||||
} else if (name === 'rof') {
|
||||
let burst = this.get('burst', true) || 1;
|
||||
let burstInt = 1 / (this.get('burstrof', true) / 1);
|
||||
|
||||
let interval = burst / value;
|
||||
let newFireint = (interval - (burst - 1) * burstInt);
|
||||
modValue = newFireint / this['fireint'] - 1;
|
||||
} else { // multiplicative
|
||||
modValue = value / baseValue - 1;
|
||||
modValue = baseValue == 0 ? 0 : value / baseValue - 1;
|
||||
}
|
||||
|
||||
if (modification.type === 'percentage') {
|
||||
modValue = modValue * 10000;
|
||||
} else if (modification.type === 'numeric' && name !== 'burst' &&
|
||||
name !== 'burstrof') {
|
||||
modValue = modValue * 100;
|
||||
} else if (modification.type === 'numeric') {
|
||||
modValue = modValue * 100;
|
||||
}
|
||||
|
||||
this.setModValue(name, modValue, valueIsWithSpecial);
|
||||
@@ -209,7 +190,14 @@ export default class Module {
|
||||
*/
|
||||
getPretty(name, modified = true, places = 2) {
|
||||
const formattingOptions = STATS_FORMATTING[name];
|
||||
let val = this.get(name, modified) || 0;
|
||||
let val;
|
||||
if (formattingOptions && formattingOptions.synthetic) {
|
||||
val = (this[formattingOptions.synthetic]).call(this, modified);
|
||||
} else {
|
||||
val = this.get(name, modified);
|
||||
}
|
||||
val = val || 0;
|
||||
|
||||
if (formattingOptions && formattingOptions.format.startsWith('pct')) {
|
||||
return 100 * val;
|
||||
}
|
||||
@@ -242,38 +230,43 @@ export default class Module {
|
||||
const modification = Modifications.modifications[name];
|
||||
let result = this[name];
|
||||
|
||||
if (modification) {
|
||||
// We store percentages as decimals, so to get them back we need to divide by 10000. Otherwise
|
||||
// we divide by 100. Both ways we end up with a value with two decimal places
|
||||
let modValue;
|
||||
if (modification.type === 'percentage') {
|
||||
modValue = this.getModValue(name) / 10000;
|
||||
} else if (modification.type === 'numeric') {
|
||||
modValue = this.getModValue(name) / 100;
|
||||
} else {
|
||||
modValue = this.getModValue(name);
|
||||
if (modification) {
|
||||
// We store percentages as decimals, so to get them back we need to divide by 10000. Otherwise
|
||||
// we divide by 100. Both ways we end up with a value with two decimal places
|
||||
let modValue;
|
||||
if (modification.type === 'percentage') {
|
||||
modValue = this.getModValue(name) / 10000;
|
||||
} else if (modification.type === 'numeric') {
|
||||
modValue = this.getModValue(name) / 100;
|
||||
} else {
|
||||
modValue = this.getModValue(name);
|
||||
}
|
||||
if (modValue) {
|
||||
if (!result && modification.method === 'additive') {
|
||||
// If the modification is additive and no value is given by default we
|
||||
// start at zero
|
||||
result = 0;
|
||||
}
|
||||
if (modValue) {
|
||||
if (!result && modification.method === 'additive') {
|
||||
// If the modification is additive and no value is given by default we
|
||||
// start at zero
|
||||
result = 0;
|
||||
}
|
||||
|
||||
if (result !== undefined) {
|
||||
if (modification.method === 'additive') {
|
||||
result = result + modValue;
|
||||
} else if (modification.method === 'overwrite') {
|
||||
result = modValue;
|
||||
} else if (name === 'shieldboost' || name === 'hullboost') {
|
||||
result = (1 + result) * (1 + modValue) - 1;
|
||||
} else {
|
||||
result = result * (1 + modValue);
|
||||
if (result !== undefined) {
|
||||
if (modification.method === 'additive') {
|
||||
result = result + modValue;
|
||||
} else if (modification.method === 'overwrite') {
|
||||
result = modValue;
|
||||
} else if (name === 'shieldboost' || name === 'hullboost') {
|
||||
result = (1 + result) * (1 + modValue) - 1;
|
||||
} else {
|
||||
// Rate of fire modifiers are special as they actually are modifiers
|
||||
// for fire interval. Translate them accordingly here:
|
||||
if (name == 'rof') {
|
||||
modValue = 1 / (1 + modValue) - 1;
|
||||
}
|
||||
} else if (name === 'burst' || name === 'burstrof') {
|
||||
// Burst and burst rate of fire are special, as it can not exist but
|
||||
// have a modification
|
||||
result = modValue / 100;
|
||||
result = result * (1 + modValue);
|
||||
}
|
||||
} else if (name === 'burstrof' || name === 'burst') {
|
||||
// Burst and burst rate of fire are special, as it can not exist but
|
||||
// have a modification
|
||||
result = modValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -295,11 +288,7 @@ export default class Module {
|
||||
formatModifiedValue(name, language, unit, val) {
|
||||
const formattingOptions = STATS_FORMATTING[name];
|
||||
if (val === undefined) {
|
||||
if (formattingOptions && formattingOptions.synthetic) {
|
||||
val = (this[formattingOptions.synthetic]).call(this, true);
|
||||
} else {
|
||||
val = this._getModifiedValue(name);
|
||||
}
|
||||
val = this.getPretty(name, true);
|
||||
}
|
||||
|
||||
val = val || 0;
|
||||
@@ -369,14 +358,18 @@ export default class Module {
|
||||
|
||||
if (formattingOptions && formattingOptions.change) {
|
||||
let changeFormatting = formattingOptions.change;
|
||||
let baseVal = this[name];
|
||||
let baseVal = this[name] || 0;
|
||||
let absVal = this._getModifiedValue(name);
|
||||
if (changeFormatting === 'additive') {
|
||||
val = absVal - baseVal;
|
||||
} else if (changeFormatting === 'multiplicative') {
|
||||
val = absVal / baseVal - 1;
|
||||
}
|
||||
val *= 10000;
|
||||
if (Modifications.modifications[name].method === 'overwrite') {
|
||||
val *= 100;
|
||||
} else {
|
||||
val *= 10000;
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
@@ -590,20 +583,9 @@ export default class Module {
|
||||
* @return {Number} the falloff of this module
|
||||
*/
|
||||
getFalloff(modified = true) {
|
||||
if (!modified) {
|
||||
const range = this.getRange(false);
|
||||
const falloff = this.get('falloff', false);
|
||||
return (falloff > range ? range : falloff);
|
||||
}
|
||||
|
||||
// Falloff from range is mapped to range
|
||||
if (this.mods && this.mods['fallofffromrange']) {
|
||||
if (modified && this.mods && this.mods['fallofffromrange']) {
|
||||
return this.getRange();
|
||||
// Need to find out if we have a focused modification, in which case our
|
||||
// falloff is scaled to range
|
||||
} else if (this.blueprint && this.blueprint.name === 'Focused') {
|
||||
const rangeMod = this.getModValue('range') / 10000;
|
||||
return this.falloff * (1 + rangeMod);
|
||||
// Standard falloff calculation
|
||||
} else {
|
||||
const range = this.getRange();
|
||||
@@ -657,15 +639,6 @@ export default class Module {
|
||||
return this.get('protection', modified);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the delay for this module
|
||||
* @param {Boolean} [modified=true] Whether to take modifications into account
|
||||
* @return {Number} the delay of this module
|
||||
*/
|
||||
getDelay(modified = true) {
|
||||
return this.get('delay', modified);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the duration for this module
|
||||
* @param {Boolean} [modified=true] Whether to take modifications into account
|
||||
@@ -721,8 +694,7 @@ export default class Module {
|
||||
let result = 0;
|
||||
if (this['maxmass']) {
|
||||
result = this['maxmass'];
|
||||
// max mass is only modified for non-shield boosters
|
||||
if (result && modified && this.grp !== 'sg') {
|
||||
if (result && modified && !ModuleUtils.isShieldGenerator(this['grp'])) {
|
||||
let mult = this.getModValue('optmass') / 10000;
|
||||
if (mult) { result = result * (1 + mult); }
|
||||
}
|
||||
@@ -811,24 +783,6 @@ export default class Module {
|
||||
return this.get('distdraw', modified);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the thermal load for this module
|
||||
* @param {Boolean} [modified=true] Whether to take modifications into account
|
||||
* @return {Number} the thermal load of this module
|
||||
*/
|
||||
getThermalLoad(modified = true) {
|
||||
return this.get('thermload', modified);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the rounds per shot for this module
|
||||
* @param {Boolean} [modified=true] Whether to take modifications into account
|
||||
* @return {Number} the rounds per shot of this module
|
||||
*/
|
||||
getRoundsPerShot(modified = true) {
|
||||
return this.get('roundspershot', modified);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the DPS for this module
|
||||
* @param {Boolean} [modified=true] Whether to take modifications into account
|
||||
@@ -853,26 +807,40 @@ export default class Module {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the SDPS for this module
|
||||
* Return the factor that gets applied when calculating certain "sustained"
|
||||
* values, e.g. `SDPS = this.getSustainedFactor() * DPS`.
|
||||
* @param {Boolean} [modified=true] Whether to take modifications into account
|
||||
* @return {Number} The SDPS of this module
|
||||
*/
|
||||
getSDps(modified = true) {
|
||||
let dps = this.getDps(modified);
|
||||
if (this.getClip(modified)) {
|
||||
let clipSize = this.getClip(modified);
|
||||
getSustainedFactor(modified = true) {
|
||||
let clipSize = this.getClip(modified);
|
||||
if (clipSize) {
|
||||
// If auto-loader is applied, effective clip size will be nearly doubled
|
||||
// as you get one reload for every two shots fired.
|
||||
if (this.blueprint && this.blueprint.special && this.blueprint.special.edname === 'special_auto_loader' && modified) {
|
||||
clipSize += clipSize - 1;
|
||||
}
|
||||
let timeToDeplete = clipSize / this.getRoF(modified);
|
||||
return dps * timeToDeplete / (timeToDeplete + this.getReload(modified));
|
||||
|
||||
let burstSize = this.get('burst', modified) || 1;
|
||||
let rof = this.getRoF(modified);
|
||||
// rof averages burstfire + pause until next interval but for sustained
|
||||
// rof we need to take another burst without pause into account
|
||||
let burstOverhead = (burstSize - 1) / (this.get('burstrof', modified) || 1);
|
||||
let srof = clipSize / ((clipSize - burstSize) / rof + burstOverhead + this.getReload(modified));
|
||||
return srof / rof;
|
||||
} else {
|
||||
return dps;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the SDPS for this module
|
||||
* @param {Boolean} [modified=true] Whether to take modifications into account
|
||||
* @return {Number} The SDPS of this module
|
||||
*/
|
||||
getSDps(modified = true) {
|
||||
return this.getDps(modified) * this.getSustainedFactor(modified);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the EPS for this module
|
||||
* @param {Boolean} [modified=true] Whether to take modifications into account
|
||||
@@ -894,7 +862,7 @@ export default class Module {
|
||||
*/
|
||||
getHps(modified = true) {
|
||||
// HPS is a synthetic value
|
||||
let heat = this.getThermalLoad(modified);
|
||||
let heat = this.get('thermload', modified);
|
||||
// We don't use rpshot here as dist draw is per combined shot
|
||||
let rof = this.getRoF(modified) || 1;
|
||||
|
||||
@@ -931,24 +899,6 @@ export default class Module {
|
||||
return this.get('reload', modified);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the burst size for this module
|
||||
* @param {Boolean} [modified=true] Whether to take modifications into account
|
||||
* @return {Number} the burst size of this module
|
||||
*/
|
||||
getBurst(modified = true) {
|
||||
return this.get('burst', modified);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the burst rate of fire for this module
|
||||
* @param {Boolean} [modified=true] Whether to take modifications into account
|
||||
* @return {Number} the burst rate of fire of this module
|
||||
*/
|
||||
getBurstRoF(modified = true) {
|
||||
return this.get('burstrof', modified);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the rate of fire for this module.
|
||||
* The rate of fire is a combination value, and needs to take in to account
|
||||
@@ -959,8 +909,8 @@ export default class Module {
|
||||
* @return {Number} the rate of fire for this module
|
||||
*/
|
||||
getRoF(modified = true) {
|
||||
const burst = this.getBurst(modified) || 1;
|
||||
const burstRoF = this.getBurstRoF(modified) || 1;
|
||||
const burst = this.get('burst', modified) || 1;
|
||||
const burstRoF = this.get('burstrof', modified) || 1;
|
||||
const intRoF = this.get('rof', modified);
|
||||
|
||||
return burst / (((burst - 1) / burstRoF) + 1 / intRoF);
|
||||
@@ -1092,4 +1042,30 @@ export default class Module {
|
||||
return this.get('hacktime', modified);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the scan range for this module
|
||||
* @param {Boolean} [modified=true] Whether to take modifications into account
|
||||
* @return {string} the time for this module
|
||||
*/
|
||||
getScanRange(modified = true) {
|
||||
return this.get('scanrange', modified);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the scan angle for this module
|
||||
* @param {Boolean} [modified=true] Whether to take modifications into account
|
||||
* @return {string} the time for this module
|
||||
*/
|
||||
getScanAngle(modified = true) {
|
||||
return this.get('scanangle', modified);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the max angle for this module
|
||||
* @param {Boolean} [modified=true] Whether to take modifications into account
|
||||
* @return {string} the time for this module
|
||||
*/
|
||||
getMaxAngle(modified = true) {
|
||||
return this.get('maxangle', modified);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ function filter(arr, maxClass, minClass, mass) {
|
||||
* The available module set for a specific ship
|
||||
*/
|
||||
export default class ModuleSet {
|
||||
|
||||
/**
|
||||
* Instantiate the module set
|
||||
* @param {Object} modules All Modules
|
||||
|
||||
@@ -63,7 +63,10 @@ export function standard(type, id) {
|
||||
if (!isNaN(type)) {
|
||||
type = StandardArray[type];
|
||||
}
|
||||
let s = Modules.standard[type].find(e => e.id == id || (e.class == id.charAt(0) && e.rating == id.charAt(1)));
|
||||
let s = Modules.standard[type].find(e => e.id === id);
|
||||
if (!s) {
|
||||
s = Modules.standard[type].find(e => (e.class == id.charAt(0) && e.rating == id.charAt(1)));
|
||||
}
|
||||
if (s) {
|
||||
s = new Module({ template: s });
|
||||
}
|
||||
|
||||
@@ -85,12 +85,12 @@ export function toDetailedBuild(buildName, ship) {
|
||||
code = ship.toString();
|
||||
|
||||
let data = {
|
||||
$schema: 'https://coriolis.edcd.io/schemas/ship-loadout/4.json#',
|
||||
$schema: 'https://coriolis.io/schemas/ship-loadout/4.json#',
|
||||
name: buildName,
|
||||
ship: ship.name,
|
||||
references: [{
|
||||
name: 'Coriolis.io',
|
||||
url: 'https://coriolis.edcd.io' + outfitURL(ship.id, code, buildName),
|
||||
url: 'https://coriolis.io' + outfitURL(ship.id, code, buildName),
|
||||
code,
|
||||
shipId: ship.id
|
||||
}],
|
||||
|
||||
@@ -10,7 +10,7 @@ import { Ships, Modifications } from 'coriolis-data/dist';
|
||||
import { chain } from 'lodash';
|
||||
const zlib = require('zlib');
|
||||
|
||||
const UNIQUE_MODULES = ['psg', 'sg', 'bsg', 'rf', 'fs', 'fh', 'gfsb'];
|
||||
const UNIQUE_MODULES = ['psg', 'sg', 'bsg', 'rf', 'fs', 'fh', 'gfsb', 'dc'];
|
||||
|
||||
// Constants for modifications struct
|
||||
const SLOT_ID_DONE = -1;
|
||||
@@ -71,7 +71,6 @@ function reduceToIDs(idArray, slot, slotIndex) {
|
||||
* Ship Model - Encapsulates and models in-game ship behavior
|
||||
*/
|
||||
export default class Ship {
|
||||
|
||||
/**
|
||||
* @param {String} id Unique ship Id / Key
|
||||
* @param {Object} properties Basic ship properties such as name, manufacturer, mass, etc
|
||||
@@ -416,16 +415,16 @@ export default class Ship {
|
||||
clearModifications(m) {
|
||||
m.mods = {};
|
||||
this.updatePowerGenerated()
|
||||
.updatePowerUsed()
|
||||
.recalculateMass()
|
||||
.updateJumpStats()
|
||||
.recalculateShield()
|
||||
.recalculateShieldCells()
|
||||
.recalculateArmour()
|
||||
.recalculateDps()
|
||||
.recalculateEps()
|
||||
.recalculateHps()
|
||||
.updateMovement();
|
||||
.updatePowerUsed()
|
||||
.recalculateMass()
|
||||
.updateJumpStats()
|
||||
.recalculateShield()
|
||||
.recalculateShieldCells()
|
||||
.recalculateArmour()
|
||||
.recalculateDps()
|
||||
.recalculateEps()
|
||||
.recalculateHps()
|
||||
.updateMovement();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -506,6 +505,11 @@ export default class Ship {
|
||||
if (isAbsolute) {
|
||||
m.setPretty(name, value, sentfromui);
|
||||
} else {
|
||||
// Resistance modifiers scale with the base value
|
||||
if (name == 'kinres' || name == 'thermres' || name == 'causres' || name == 'explres') {
|
||||
let baseValue = m.get(name, false);
|
||||
value = (1 - baseValue) * value;
|
||||
}
|
||||
m.setModValue(name, value, sentfromui);
|
||||
}
|
||||
|
||||
@@ -696,16 +700,16 @@ export default class Ship {
|
||||
// Update aggragated stats
|
||||
if (comps) {
|
||||
this.updatePowerGenerated()
|
||||
.updatePowerUsed()
|
||||
.recalculateMass()
|
||||
.updateJumpStats()
|
||||
.recalculateShield()
|
||||
.recalculateShieldCells()
|
||||
.recalculateArmour()
|
||||
.recalculateDps()
|
||||
.recalculateEps()
|
||||
.recalculateHps()
|
||||
.updateMovement();
|
||||
.updatePowerUsed()
|
||||
.recalculateMass()
|
||||
.updateJumpStats()
|
||||
.recalculateShield()
|
||||
.recalculateShieldCells()
|
||||
.recalculateArmour()
|
||||
.recalculateDps()
|
||||
.recalculateEps()
|
||||
.recalculateHps()
|
||||
.updateMovement();
|
||||
}
|
||||
|
||||
return this.updatePowerPrioritesString().updatePowerEnabledString().updateModificationsString();
|
||||
@@ -1187,28 +1191,28 @@ export default class Ship {
|
||||
// handle unladen mass
|
||||
unladenMass += chain(slots)
|
||||
.map(slot => slot.m ? slot.m.get('mass') : null)
|
||||
.filter()
|
||||
.map(mass => mass || 0)
|
||||
.reduce((sum, mass) => sum + mass)
|
||||
.value();
|
||||
|
||||
// handle fuel capacity
|
||||
fuelCapacity += chain(slots)
|
||||
.map(slot => slot.m ? slot.m.get('fuel') : null)
|
||||
.filter()
|
||||
.map(fuel => fuel || 0)
|
||||
.reduce((sum, fuel) => sum + fuel)
|
||||
.value();
|
||||
|
||||
// handle cargo capacity
|
||||
cargoCapacity += chain(slots)
|
||||
.map(slot => slot.m ? slot.m.get('cargo') : null)
|
||||
.filter()
|
||||
.map(cargo => cargo || 0)
|
||||
.reduce((sum, cargo) => sum + cargo)
|
||||
.value();
|
||||
|
||||
// handle passenger capacity
|
||||
// handle passenger capacity
|
||||
passengerCapacity += chain(slots)
|
||||
.map(slot => slot.m ? slot.m.get('passengers') : null)
|
||||
.filter()
|
||||
.map(passengers => passengers || 0)
|
||||
.reduce((sum, passengers) => sum + passengers)
|
||||
.value();
|
||||
|
||||
@@ -1304,7 +1308,7 @@ export default class Ship {
|
||||
let fsd = this.standard[2].m; // Frame Shift Drive;
|
||||
let { unladenMass, fuelCapacity } = this;
|
||||
this.unladenRange = this.calcUnladenRange(); // Includes fuel weight for jump
|
||||
this.fullTankRange = Calc.jumpRange(unladenMass + fuelCapacity, fsd, this); // Full Tank
|
||||
this.fullTankRange = Calc.jumpRange(unladenMass + fuelCapacity, fsd, fuelCapacity, this); // Full Tank
|
||||
this.ladenRange = this.calcLadenRange(); // Includes full tank and caro
|
||||
this.unladenFastestRange = Calc.totalJumpRange(unladenMass + this.fuelCapacity, fsd, fuelCapacity, this);
|
||||
this.ladenFastestRange = Calc.totalJumpRange(unladenMass + this.fuelCapacity + this.cargoCapacity, fsd, fuelCapacity, this);
|
||||
@@ -1502,7 +1506,7 @@ export default class Ship {
|
||||
} else {
|
||||
buffer.writeInt32LE(slotMod.value, curpos);
|
||||
}
|
||||
// const modification = _.find(Modifications.modifications, function(o) { return o.id === slotMod.id; });
|
||||
const modification = _.find(Modifications.modifications, function(o) { return o.id === slotMod.id; });
|
||||
// console.log('ENCODE Slot ' + i + ': ' + modification.name + ' = ' + slotMod.value);
|
||||
curpos += 4;
|
||||
}
|
||||
@@ -1515,6 +1519,7 @@ export default class Ship {
|
||||
}
|
||||
|
||||
this.serialized.modifications = zlib.gzipSync(buffer).toString('base64');
|
||||
// console.log(this.serialized.modifications)
|
||||
} else {
|
||||
this.serialized.modifications = null;
|
||||
}
|
||||
@@ -1683,11 +1688,11 @@ export default class Ship {
|
||||
updated;
|
||||
|
||||
this.useBulkhead(0)
|
||||
.use(standard[2], fsd) // FSD
|
||||
.use(standard[3], ls) // Life Support
|
||||
.use(standard[5], s) // Sensors
|
||||
.use(standard[4], pd) // Power Distributor
|
||||
.use(standard[6], ft); // Fuel Tank
|
||||
.use(standard[2], fsd) // FSD
|
||||
.use(standard[3], ls) // Life Support
|
||||
.use(standard[5], s) // Sensors
|
||||
.use(standard[4], pd) // Power Distributor
|
||||
.use(standard[6], ft); // Fuel Tank
|
||||
|
||||
// Turn off nearly everything
|
||||
if (m.fsdDisabled) this.setSlotEnabled(this.standard[2], false);
|
||||
|
||||
@@ -26,8 +26,8 @@ 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' },
|
||||
'burst': { 'format': 'int', 'change': 'additive' },
|
||||
'burstrof': { 'format': 'round1', 'unit': 'ps', 'change': 'additive' },
|
||||
'causres': { 'format': 'pct' },
|
||||
'clip': { 'format': 'int' },
|
||||
'damage': { 'format': 'round' },
|
||||
@@ -61,7 +61,7 @@ export const STATS_FORMATTING = {
|
||||
'ranget': { 'format': 'f1', 'unit': 's' },
|
||||
'regen': { 'format': 'round1', 'unit': 'ps' },
|
||||
'reload': { 'format': 'int', 'unit': 's' },
|
||||
'rof': { 'format': 'round1', 'unit': 'ps' },
|
||||
'rof': { 'format': 'round1', 'unit': 'ps', 'synthetic': 'getRoF', 'higherbetter': true },
|
||||
'angle': { 'format': 'round1', 'unit': 'ang' },
|
||||
'scanrate': { 'format': 'int' },
|
||||
'scantime': { 'format': 'round1', 'unit': 's' },
|
||||
@@ -78,5 +78,6 @@ export const STATS_FORMATTING = {
|
||||
'thermres': { 'format': 'pct' },
|
||||
'wepcap': { 'format': 'round1', 'unit': 'MJ' },
|
||||
'weprate': { 'format': 'round1', 'unit': 'MW' },
|
||||
'jumpboost': { 'format': 'round1', 'unit': 'LY' }
|
||||
'jumpboost': { 'format': 'round1', 'unit': 'LY' },
|
||||
'proberadius': { 'format': 'pct1', 'unit': 'pct' },
|
||||
};
|
||||
|
||||
@@ -15,7 +15,6 @@ const LS_KEY_SIZE_RATIO = 'sizeRatio';
|
||||
const LS_KEY_TOOLTIPS = 'tooltips';
|
||||
const LS_KEY_MODULE_RESISTANCES = 'moduleResistances';
|
||||
const LS_KEY_ROLLS = 'matsPerGrade';
|
||||
const LS_KEY_ORBIS = 'orbis';
|
||||
|
||||
let LS;
|
||||
|
||||
@@ -70,7 +69,6 @@ function _delete(key) {
|
||||
* export is an instance (see end of this file).
|
||||
*/
|
||||
export class Persist extends EventEmitter {
|
||||
|
||||
/**
|
||||
* Create an instance
|
||||
*/
|
||||
@@ -96,7 +94,6 @@ export class Persist extends EventEmitter {
|
||||
let buildJson = _get(LS_KEY_BUILDS);
|
||||
let comparisonJson = _get(LS_KEY_COMPARISONS);
|
||||
|
||||
this.orbisCreds = _get(LS_KEY_ORBIS) || { email: '', password: '' };
|
||||
this.onStorageChange = this.onStorageChange.bind(this);
|
||||
this.langCode = _getString(LS_KEY_LANG) || 'en';
|
||||
this.insurance = insurance && Insurance[insurance.toLowerCase()] !== undefined ? insurance : 'standard';
|
||||
@@ -170,10 +167,6 @@ export class Persist extends EventEmitter {
|
||||
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) {
|
||||
// On JSON.Parse Error - don't sync or do anything
|
||||
@@ -199,24 +192,6 @@ export class Persist extends EventEmitter {
|
||||
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
|
||||
* @param {boolean} show Optional - update setting
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Modifications } from 'coriolis-data/dist';
|
||||
import { STATS_FORMATTING } from '../shipyard/StatsFormatting';
|
||||
|
||||
/**
|
||||
* Generate a tooltip with details of a blueprint's specials
|
||||
@@ -19,7 +20,7 @@ export function specialToolTip(translate, blueprint, grp, m, specialName) {
|
||||
// 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) {
|
||||
// if (!blueprint.features[feature] && !m.mods.feature) {
|
||||
const featureDef = Modifications.modifications[feature];
|
||||
if (featureDef && !featureDef.hidden) {
|
||||
let symbol = '';
|
||||
@@ -37,14 +38,14 @@ export function specialToolTip(translate, blueprint, grp, m, specialName) {
|
||||
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>
|
||||
);
|
||||
<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>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,7 +55,7 @@ export function specialToolTip(translate, blueprint, grp, m, specialName) {
|
||||
<div>
|
||||
<table width='100%'>
|
||||
<tbody>
|
||||
{effects}
|
||||
{effects}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -215,12 +216,12 @@ export function blueprintTooltip(translate, blueprint, engineers, grp, m) {
|
||||
<div>
|
||||
<table width='100%'>
|
||||
<thead>
|
||||
<tr>
|
||||
<td>{translate('feature')}</td>
|
||||
<td>{translate('worst')}</td>
|
||||
<tr>
|
||||
<td>{translate('feature')}</td>
|
||||
<td>{translate('worst')}</td>
|
||||
{m ? <td>{translate('current')}</td> : null }
|
||||
<td>{translate('best')}</td>
|
||||
</tr>
|
||||
<td>{translate('best')}</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{effects}
|
||||
@@ -228,10 +229,10 @@ export function blueprintTooltip(translate, blueprint, engineers, grp, m) {
|
||||
</table>
|
||||
{ components ? <table width='100%'>
|
||||
<thead>
|
||||
<tr>
|
||||
<td>{translate('component')}</td>
|
||||
<td>{translate('amount')}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{translate('component')}</td>
|
||||
<td>{translate('amount')}</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{components}
|
||||
@@ -239,9 +240,9 @@ export function blueprintTooltip(translate, blueprint, engineers, grp, m) {
|
||||
</table> : null }
|
||||
{ engineersList ? <table width='100%'>
|
||||
<thead>
|
||||
<tr>
|
||||
<td>{translate('engineers')}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{translate('engineers')}</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{engineersList}
|
||||
@@ -280,6 +281,25 @@ export function isValueBeneficial(feature, value) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the change as shown beneficial?
|
||||
* @param {string} feature The name of the feature
|
||||
* @param {number} value The value of the feature as percentage change
|
||||
* @returns True if the value is beneficial
|
||||
*/
|
||||
export function isChangeValueBeneficial(feature, value) {
|
||||
let changeHigherBetter = STATS_FORMATTING[feature].higherbetter;
|
||||
if (changeHigherBetter === undefined) {
|
||||
return isValueBeneficial(feature, value);
|
||||
}
|
||||
|
||||
if (changeHigherBetter) {
|
||||
return value > 0;
|
||||
} else {
|
||||
return value < 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a blueprint with a given name and an optional module
|
||||
* @param {string} name The name of the blueprint
|
||||
@@ -372,9 +392,7 @@ export function getPercent(m) {
|
||||
|
||||
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) {
|
||||
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);
|
||||
|
||||
@@ -30,6 +30,8 @@ export const SHIP_FD_NAME_TO_CORIOLIS_NAME = {
|
||||
'Hauler': 'hauler',
|
||||
'Independant_Trader': 'keelback',
|
||||
'Krait_MkII': 'krait_mkii',
|
||||
'Mamba': 'mamba',
|
||||
'Krait_Light': 'krait_phantom',
|
||||
'Orca': 'orca',
|
||||
'Python': 'python',
|
||||
'SideWinder': 'sidewinder',
|
||||
|
||||
@@ -147,83 +147,79 @@ export function shipFromLoadoutJSON(json) {
|
||||
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(/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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
// 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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let internalSlotNum = 0;
|
||||
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.
|
||||
for (; internalSlot === null && internalSlotNum < 99; internalSlotNum++) {
|
||||
// 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' : '') + internalSlotNum + '_Size' + slotsize;
|
||||
if (json.Modules.find(elem => elem.Slot.toLowerCase() === internalName.toLowerCase())) {
|
||||
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 });
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -253,6 +249,13 @@ function _addModifications(module, modifiers, blueprint, grade, specialModificat
|
||||
if (!modifiers) return;
|
||||
let special;
|
||||
if (specialModifications) {
|
||||
if (specialModifications == 'special_plasma_slug') {
|
||||
if (module.symbol.match(/PlasmaAccelerator/i)) {
|
||||
specialModifications = 'special_plasma_slug_pa';
|
||||
} else {
|
||||
specialModifications = 'special_plasma_slug_cooled';
|
||||
}
|
||||
}
|
||||
special = Modifications.specials[specialModifications];
|
||||
}
|
||||
// Add the blueprint definition, grade and special
|
||||
@@ -278,6 +281,9 @@ function _addModifications(module, modifiers, blueprint, grade, specialModificat
|
||||
if (value === Infinity) {
|
||||
value = modifiers[i].Value * 100;
|
||||
}
|
||||
if (modifiers[i].Label.search('DamageFalloffRange') >= 0) {
|
||||
value = (modifiers[i].Value / module.range - 1) * 100;
|
||||
}
|
||||
if (modifiers[i].Label.search('Resistance') >= 0) {
|
||||
value = (modifiers[i].Value * 100) - (modifiers[i].OriginalValue * 100);
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ function orbisShorten(url, success, error) {
|
||||
}
|
||||
}
|
||||
|
||||
const API_ORBIS = 'https://orbis.zone/api/builds/add';
|
||||
const API_ORBIS = 'https://api.orbis.zone/ships';
|
||||
/**
|
||||
* Upload to Orbis
|
||||
* @param {object} ship The URL to shorten
|
||||
@@ -113,7 +113,7 @@ const API_ORBIS = 'https://orbis.zone/api/builds/add';
|
||||
* @return {Promise<any>} Either a URL or error message.
|
||||
*/
|
||||
export function orbisUpload(ship, creds) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
return new Promise(async(resolve, reject) => {
|
||||
if (window.navigator.onLine) {
|
||||
try {
|
||||
agent
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import React from 'react';
|
||||
import cn from 'classnames';
|
||||
import Module from '../shipyard/Module';
|
||||
import { Infinite } from '../components/SvgIcons';
|
||||
import Persist from '../stores/Persist';
|
||||
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
||||
import Module from '../shipyard/Module';
|
||||
|
||||
/**
|
||||
* Determine if a slot on a ship can mount a module of a particular class and group
|
||||
@@ -142,20 +140,21 @@ function diff(format, mVal, mmVal) {
|
||||
export function diffDetails(language, m, mm) {
|
||||
let { formats, translate, units } = language;
|
||||
let propDiffs = [];
|
||||
m = new Module(m);
|
||||
|
||||
// Module-specific items
|
||||
|
||||
if (m.grp === 'pp') {
|
||||
let mPowerGeneration = m.pgen || 0;
|
||||
let mPowerGeneration = m.getPowerGeneration() || 0;
|
||||
let mmPowerGeneration = mm ? mm.getPowerGeneration() : 0;
|
||||
if (mPowerGeneration != mmPowerGeneration) propDiffs.push(<div key='pgen'>{translate('pgen')}: <span className={diffClass(mPowerGeneration, mmPowerGeneration)}>{diff(formats.round, mPowerGeneration, mmPowerGeneration)}{units.MJ}</span></div>);
|
||||
if (mPowerGeneration != mmPowerGeneration) propDiffs.push(<div key='pgen'>{translate('pgen')}: <span className={diffClass(mPowerGeneration, mmPowerGeneration)}>{diff(formats.round, mPowerGeneration, mmPowerGeneration)}{units.MW}</span></div>);
|
||||
} else {
|
||||
let mPowerUsage = m.power || 0;
|
||||
let mPowerUsage = m.getPowerUsage() || 0;
|
||||
let mmPowerUsage = mm ? mm.getPowerUsage() || 0 : 0;
|
||||
if (mPowerUsage != mmPowerUsage) propDiffs.push(<div key='power'>{translate('power')}: <span className={diffClass(mPowerUsage, mmPowerUsage, true)}>{diff(formats.round, mPowerUsage, mmPowerUsage)}{units.MJ}</span></div>);
|
||||
if (mPowerUsage != mmPowerUsage) propDiffs.push(<div key='power'>{translate('power')}: <span className={diffClass(mPowerUsage, mmPowerUsage, true)}>{diff(formats.round, mPowerUsage, mmPowerUsage)}{units.MW}</span></div>);
|
||||
}
|
||||
|
||||
let mDps = m.damage * (m.rpshot || 1) * (m.rof || 1);
|
||||
let mDps = m.getDps() || 0;
|
||||
let mmDps = mm ? mm.getDps() || 0 : 0;
|
||||
if (mDps && mDps != mmDps) propDiffs.push(<div key='dps'>{translate('dps')}: <span className={diffClass(mmDps, mDps, true)}>{diff(formats.round, mDps, mmDps)}</span></div>);
|
||||
|
||||
@@ -167,7 +166,7 @@ export function diffDetails(language, m, mm) {
|
||||
|
||||
if (mAffectsShield) {
|
||||
if (m.grp == 'sb') { // Both m and mm must be utility modules if this is true
|
||||
newShield = this.calcShieldStrengthWith(null, m.shieldboost - (mm ? mm.getShieldBoost() || 0 : 0));
|
||||
newShield = this.calcShieldStrengthWith(null, m.getShieldBoost() - (mm ? mm.getShieldBoost() || 0 : 0));
|
||||
} else {
|
||||
newShield = this.calcShieldStrengthWith(m);
|
||||
}
|
||||
@@ -182,7 +181,7 @@ export function diffDetails(language, m, mm) {
|
||||
}
|
||||
|
||||
if (m.grp === 'mrp') {
|
||||
let mProtection = m.protection;
|
||||
let mProtection = m.getProtection();
|
||||
let mmProtection = mm ? mm.getProtection() || 0 : 0;
|
||||
if (mProtection != mmProtection) {
|
||||
propDiffs.push(<div key='protection'>{translate('protection')}: <span className={diffClass(mmProtection, mProtection, true)}>{diff(formats.pct, mProtection, mmProtection)}</span></div>);
|
||||
@@ -190,7 +189,7 @@ export function diffDetails(language, m, mm) {
|
||||
}
|
||||
|
||||
if (m.grp === 'hr') {
|
||||
let mHullReinforcement = m.hullreinforcement;
|
||||
let mHullReinforcement = m.getHullReinforcement();
|
||||
let mmHullReinforcement = mm ? mm.getHullReinforcement() || 0 : 0;
|
||||
if (mHullReinforcement && mHullReinforcement != mmHullReinforcement) propDiffs.push(<div key='hullreinforcement'>{translate('hullreinforcement')}: <span className={diffClass(mmHullReinforcement, mHullReinforcement, true)}>{diff(formats.round, mHullReinforcement, mmHullReinforcement)}</span></div>);
|
||||
}
|
||||
@@ -222,7 +221,7 @@ export function diffDetails(language, m, mm) {
|
||||
let mmCost = mm ? mm.cost : 0;
|
||||
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.getMass() || 0;
|
||||
let mmMass = mm ? mm.getMass() : 0;
|
||||
if (mMass != mmMass) propDiffs.push(<div key='mass'>{translate('mass')}: <span className={diffClass(mMass, mmMass, true)}>{diff(formats.round, mMass, mmMass)}{units.T}</span></div>);
|
||||
|
||||
@@ -243,7 +242,7 @@ export function diffDetails(language, m, mm) {
|
||||
}
|
||||
}
|
||||
|
||||
let mIntegrity = m.integrity || 0;
|
||||
let mIntegrity = m.getIntegrity() || 0;
|
||||
let mmIntegrity = mm ? mm.getIntegrity() || 0 : 0;
|
||||
if (mIntegrity != mmIntegrity) {
|
||||
propDiffs.push(<div key='integrity'>{translate('integrity')}: <span className={diffClass(mmIntegrity, mIntegrity, true)}>{diff(formats.round, mIntegrity, mmIntegrity)}</span></div>);
|
||||
|
||||
9
src/iframe.html
Normal file
9
src/iframe.html
Normal file
@@ -0,0 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script src="xdLocalStoragePostMessageApi.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
This is the magical iframe
|
||||
</body>
|
||||
</html>
|
||||
@@ -27,7 +27,7 @@
|
||||
"density": "4.0"
|
||||
}
|
||||
],
|
||||
"start_url": "https:\/\/edcd.coriolis.io",
|
||||
"start_url": "https:\/\/coriolis.io",
|
||||
"display": "standalone",
|
||||
"orientation": "portrait"
|
||||
}
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
<html manifest="/">
|
||||
<html>
|
||||
<head>
|
||||
<title>Coriolis EDCD Edition</title>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="<%= htmlWebpackPlugin.files.css[0] %>">
|
||||
<!-- Standard headers -->
|
||||
<meta name="description" content="A ship builder, outfitting and comparison tool for Elite Dangerous">
|
||||
<meta name="description" content="A ship builder, outfitting and comparison
|
||||
tool for Elite Dangerous">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
|
||||
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0,
|
||||
maximum-scale=1.0, user-scalable=0">
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
<link rel="shortcut icon" href=/favicon2.ico>
|
||||
<link rel="icon" sizes="152x152 192x192" type="image/png" href="/192x192.png">
|
||||
<link rel="icon" sizes="152x152 192x192" type="image/png"
|
||||
href="/192x192.png">
|
||||
|
||||
<!-- Apple/iOS headers -->
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
@@ -22,50 +27,35 @@
|
||||
<meta name="msapplication-TileImage" content="/mstile-144x144.png">
|
||||
<meta name="msapplication-config" content="/browserconfig.xml">
|
||||
<meta name="theme-color" content="#000000">
|
||||
|
||||
<!-- <script data-ad-client="ca-pub-3709458261881414" async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script> -->
|
||||
<script>
|
||||
window.CORIOLIS_GAPI_KEY = '<%- htmlWebpackPlugin.options.gapiKey %>';
|
||||
window.CORIOLIS_VERSION = '<%- htmlWebpackPlugin.options.version %>';
|
||||
window.CORIOLIS_DATE = '<%- htmlWebpackPlugin.options.date.toISOString().slice(0, 10) %>';
|
||||
window.BUGSNAG_VERSION = '<%- htmlWebpackPlugin.options.version + '-' + htmlWebpackPlugin.options.date.toISOString() %>';
|
||||
</script>
|
||||
<% if (htmlWebpackPlugin.options.uaTracking) { %>
|
||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-55840909-18"></script>
|
||||
<script>
|
||||
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
|
||||
ga('create', '<%- htmlWebpackPlugin.options.uaTracking %>', 'auto');
|
||||
ga('send', 'pageview');
|
||||
</script>
|
||||
<script async src='https://www.google-analytics.com/analytics.js'></script>
|
||||
<% } %>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
|
||||
gtag('config', 'UA-55840909-18');
|
||||
</script>
|
||||
|
||||
|
||||
<!-- Piwik -->
|
||||
<!-- <script type="text/javascript">
|
||||
var _paq = _paq || [];
|
||||
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
|
||||
_paq.push(["setCookieDomain", "*.coriolis.edcd.io"]);
|
||||
_paq.push(['trackPageView']);
|
||||
_paq.push(['enableLinkTracking']);
|
||||
(function() {
|
||||
var u="//stats.isadankme.me/";
|
||||
_paq.push(['setTrackerUrl', u+'piwik.php']);
|
||||
_paq.push(['setSiteId', '4']);
|
||||
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);
|
||||
})();
|
||||
</script>-->
|
||||
<!-- End Piwik Code -->
|
||||
|
||||
<!-- Bugsnag -->
|
||||
<script src="//d2wy8f7a9ursnm.cloudfront.net/v4/bugsnag.min.js"></script>
|
||||
<script src="//d2wy8f7a9ursnm.cloudfront.net/bugsnag-plugins/v1/bugsnag-react.min.js"></script>
|
||||
<script>
|
||||
<script src="https://d2wy8f7a9ursnm.cloudfront.net/v5.0.0/bugsnag.min.js"></script>
|
||||
<script>
|
||||
window.bugsnagClient = bugsnag('ba9fae819372850fb660755341fa6ef5', {appVersion: window.BUGSNAG_VERSION || undefined})
|
||||
window.Bugsnag = window.bugsnagClient
|
||||
</script>
|
||||
</head>
|
||||
<body style="background-color:#000;">
|
||||
<section id="coriolis"></section>
|
||||
<script src="<%= htmlWebpackPlugin.files.chunks.lib.entry %>" charset="utf-8" crossorigin="anonymous"></script>
|
||||
<script src="<%= htmlWebpackPlugin.files.chunks.app.entry %>" charset="utf-8" crossorigin="anonymous"></script>
|
||||
</body>
|
||||
</html>
|
||||
</head>
|
||||
<body style="background-color:#000;">
|
||||
<section id="coriolis">
|
||||
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -171,3 +171,16 @@ footer {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
.announcement-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-top: 10px;
|
||||
justify-content: center;
|
||||
flex-flow: row wrap;
|
||||
}
|
||||
|
||||
.announcement {
|
||||
border: 1px @secondary solid;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
@bgDarken: 40%;
|
||||
@disabledDarken: 15%;
|
||||
@bgTransparency: 10%;
|
||||
@bgHighlight: 5%;
|
||||
@fgHighlight: 10%;
|
||||
|
||||
// Foreground colors
|
||||
@fg: #CCC;
|
||||
@@ -21,9 +23,14 @@
|
||||
@bgBlack: #000;
|
||||
@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); // Blue background
|
||||
@warning-bg: fadeout(darken(@warning, @bgDarken), @bgTransparency); // Dark Red
|
||||
|
||||
@alt-primary-bg-highlighted: lighten(@alt-primary-bg, @bgHighlight);
|
||||
@fg-highlighted: lighten(@fg, @fgHighlight);
|
||||
@primary-darker: darken(@primary, 30%);
|
||||
|
||||
|
||||
|
||||
.fg {
|
||||
color: @fg;
|
||||
|
||||
@@ -54,7 +54,7 @@ textarea {
|
||||
width:100%;
|
||||
min-height: 10em;
|
||||
resize: vertical;
|
||||
user-select: auto;
|
||||
user-select: text;
|
||||
margin:2em 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,12 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
// A multi-crew pip
|
||||
.mc {
|
||||
stroke: @secondary;
|
||||
fill: @secondary;
|
||||
}
|
||||
|
||||
// A full pip
|
||||
.full {
|
||||
stroke: @primary;
|
||||
|
||||
@@ -22,6 +22,15 @@ select {
|
||||
}
|
||||
}
|
||||
|
||||
.react-fuzzy-search > * {
|
||||
padding: 0 !important;
|
||||
color: @primary;
|
||||
& > input {
|
||||
border: 1px solid @primary !important;
|
||||
color: @primary-bg;
|
||||
}
|
||||
}
|
||||
|
||||
.cmdr-select {
|
||||
border: 1px solid @primary;
|
||||
padding: 0.5em 0.5em;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user