mirror of
https://github.com/EDCD/coriolis.git
synced 2025-12-10 07:05:35 +00:00
Compare commits
116 Commits
00d3a93b91
...
ed-forge
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
25c30e46a6 | ||
|
|
870c033c64 | ||
|
|
46b0200b28 | ||
|
|
e8bcb18f12 | ||
|
|
68ca24ef96 | ||
|
|
7ca2754934 | ||
|
|
e82ab0aec0 | ||
|
|
d406018f0b | ||
|
|
2438aa1f48 | ||
|
|
c5bbeacc6a | ||
|
|
d9763f2db7 | ||
|
|
5692cc6fe3 | ||
|
|
22cdfcdecf | ||
|
|
3db3b09c22 | ||
|
|
23c264e321 | ||
|
|
d2c3787165 | ||
|
|
1e18d6e463 | ||
|
|
044fea2d33 | ||
|
|
bc865f534b | ||
|
|
796694a4e0 | ||
|
|
829815a40b | ||
|
|
7a2c849ece | ||
|
|
873dfaa305 | ||
|
|
7050356bce | ||
|
|
88c9bb0254 | ||
|
|
45f1dd2da9 | ||
|
|
46bcc2313f | ||
|
|
9e012c1490 | ||
|
|
50f9c0faa1 | ||
|
|
74829a09c0 | ||
|
|
45508ba2d4 | ||
|
|
624adf2b64 | ||
|
|
821daefeb8 | ||
|
|
86b95981f1 | ||
|
|
688eebb9ea | ||
|
|
d719da2cde | ||
|
|
19c1851e14 | ||
|
|
414bf4cb20 | ||
|
|
c674459376 | ||
|
|
fd009fe567 | ||
|
|
e28eccb6fb | ||
|
|
301c97db58 | ||
|
|
2eed1bc85b | ||
|
|
3469af10b6 | ||
|
|
629ba35bc5 | ||
|
|
c19ca6648d | ||
|
|
e453ff73b7 | ||
|
|
cfdb92ecc6 | ||
|
|
de5ca7b5e6 | ||
|
|
c73ce1c234 | ||
|
|
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 |
@@ -1,77 +0,0 @@
|
|||||||
node_modules
|
|
||||||
npm-debug.log
|
|
||||||
### Node template
|
|
||||||
# Logs
|
|
||||||
logs
|
|
||||||
*.log
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
|
|
||||||
# Runtime data
|
|
||||||
pids
|
|
||||||
*.pid
|
|
||||||
*.seed
|
|
||||||
*.pid.lock
|
|
||||||
|
|
||||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
|
||||||
lib-cov
|
|
||||||
|
|
||||||
# Coverage directory used by tools like istanbul
|
|
||||||
coverage
|
|
||||||
|
|
||||||
# nyc test coverage
|
|
||||||
.nyc_output
|
|
||||||
|
|
||||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
|
||||||
.grunt
|
|
||||||
|
|
||||||
# Bower dependency directory (https://bower.io/)
|
|
||||||
bower_components
|
|
||||||
|
|
||||||
# node-waf configuration
|
|
||||||
.lock-wscript
|
|
||||||
|
|
||||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
|
||||||
build/Release
|
|
||||||
|
|
||||||
# Dependency directories
|
|
||||||
node_modules/
|
|
||||||
jspm_packages/
|
|
||||||
|
|
||||||
# TypeScript v1 declaration files
|
|
||||||
typings/
|
|
||||||
|
|
||||||
# Optional npm cache directory
|
|
||||||
.npm
|
|
||||||
|
|
||||||
# Optional eslint cache
|
|
||||||
.eslintcache
|
|
||||||
|
|
||||||
# Optional REPL history
|
|
||||||
.node_repl_history
|
|
||||||
|
|
||||||
# Output of 'npm pack'
|
|
||||||
*.tgz
|
|
||||||
|
|
||||||
# Yarn Integrity file
|
|
||||||
.yarn-integrity
|
|
||||||
|
|
||||||
# dotenv environment variables file
|
|
||||||
.env
|
|
||||||
|
|
||||||
# parcel-bundler cache (https://parceljs.org/)
|
|
||||||
.cache
|
|
||||||
|
|
||||||
# next.js build output
|
|
||||||
.next
|
|
||||||
|
|
||||||
# nuxt.js build output
|
|
||||||
.nuxt
|
|
||||||
|
|
||||||
# vuepress build output
|
|
||||||
.vuepress/dist
|
|
||||||
|
|
||||||
# Serverless directories
|
|
||||||
.serverless
|
|
||||||
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
image: docker:stable
|
|
||||||
services:
|
|
||||||
- docker:dind
|
|
||||||
|
|
||||||
stages:
|
|
||||||
- Build image
|
|
||||||
|
|
||||||
docker build:
|
|
||||||
stage: Build image
|
|
||||||
script:
|
|
||||||
- img build --build-arg branch=$CI_COMMIT_REF_NAME -t edcd/coriolis:$CI_COMMIT_REF_NAME .
|
|
||||||
- echo "$REGISTRY_PASSWORD" | img login --username "$REGISTRY_USER" --password-stdin
|
|
||||||
- img push edcd/coriolis:$CI_COMMIT_REF_NAME
|
|
||||||
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
|
|
||||||
35
Dockerfile
35
Dockerfile
@@ -1,35 +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
|
|
||||||
|
|
||||||
RUN apk add --update git
|
|
||||||
|
|
||||||
COPY . /src/app/coriolis
|
|
||||||
|
|
||||||
RUN npm i -g npm
|
|
||||||
|
|
||||||
# Set up coriolis-data
|
|
||||||
WORKDIR /src/app/coriolis-data
|
|
||||||
RUN git clone https://github.com/EDCD/coriolis-data.git .
|
|
||||||
RUN git checkout ${BRANCH}
|
|
||||||
RUN npm install --no-package-lock
|
|
||||||
RUN npm start
|
|
||||||
|
|
||||||
# Set up coriolis
|
|
||||||
WORKDIR /src/app/coriolis
|
|
||||||
RUN git checkout ${BRANCH}
|
|
||||||
RUN npm install --no-package-lock
|
|
||||||
RUN npm run build
|
|
||||||
|
|
||||||
|
|
||||||
### STAGE 2: Production Environment ###
|
|
||||||
FROM fholzer/nginx-brotli as web
|
|
||||||
COPY nginx.conf /etc/nginx/nginx.conf
|
|
||||||
COPY --from=builder /src/app/coriolis/build /usr/share/nginx/html
|
|
||||||
WORKDIR /usr/share/nginx/html
|
|
||||||
EXPOSE 80
|
|
||||||
CMD ["nginx", "-c", "/etc/nginx/nginx.conf", "-g", "daemon off;"]
|
|
||||||
64
README.md
64
README.md
@@ -1,4 +1,4 @@
|
|||||||
 [](https://travis-ci.org/EDCD/coriolis) [](https://discord.gg/0uwCh6R62aPRjk9w)
|
[](https://discord.gg/0uwCh6R62aPRjk9w)
|
||||||
|
|
||||||
## About
|
## About
|
||||||
|
|
||||||
@@ -8,53 +8,41 @@ Coriolis was created using assets and imagery from Elite: Dangerous, with the pe
|
|||||||
|
|
||||||
## Contributing
|
## 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.
|
- [Submit issues](https://github.com/EDCD/coriolis/issues)
|
||||||
|
- [Submit pull requests](https://github.com/EDCD/coriolis/pulls) targetting `develop` branch
|
||||||
### Translations
|
- Chat to us on [Discord](https://discord.gg/0uwCh6R62aPRjk9w)!
|
||||||
|
|
||||||
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)!
|
|
||||||
|
|
||||||
## Development
|
## 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
|
### Ship and Module Database
|
||||||
|
|
||||||
See the [Data wiki](https://github.com/cmmcleod/coriolis-data/wiki) for details on structure, etc.
|
See the [Data wiki](https://github.com/cmmcleod/coriolis-data/wiki) for details on structure, etc.
|
||||||
|
|
||||||
You can find hosted and compiled versions of these data-jsons under https://coriolis.io/data/ and https://beta.coriolis.io/data/.
|
## Deployment
|
||||||
You might want to load these as depedency instead of reyling on the npm-dependency.
|
|
||||||
|
|
||||||
## 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
|
```sh
|
||||||
[terms and conditions](https://www.frontierstore.net/terms-and-conditions/).
|
> 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
|
After this you need to serve the files in some manner.
|
||||||
|
Either configure your webserver to make the actual `build/` directory
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
visible on the web, or alternatively copy it to somewhere to serve it
|
||||||
of this software (Javascript, CSS, HTML, and SVG files only), and associated documentation files (the "Software"), to deal
|
from.
|
||||||
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.
|
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
{
|
|
||||||
"adder": {
|
|
||||||
"t3": {"speed": 205, "boost": 298, "pitch": 35.37, "roll": 93.09, "yaw": 13.03},
|
|
||||||
"t2": {"speed": 209, "boost": 304, "pitch": 36.06, "roll": 94.90, "yaw": 13.29},
|
|
||||||
"t1": {"speed": 213, "boost": 310, "pitch": 36.80, "roll": 96.84, "yaw": 13.56},
|
|
||||||
"t0": {"speed": 218, "boost": 317, "pitch": 37.70, "roll": 99.20, "yaw": 13.89},
|
|
||||||
"t9": {"speed": 220, "boost": 321, "pitch": 38.08, "roll": 100.21, "yaw": 14.03},
|
|
||||||
"t8": {"speed": 225, "boost": 327, "pitch": 38.86, "roll": 102.26, "yaw": 14.32},
|
|
||||||
"t7": {"speed": 230, "boost": 334, "pitch": 39.69, "roll": 104.44, "yaw": 14.62},
|
|
||||||
"t6": {"speed": 234, "boost": 340, "pitch": 40.41, "roll": 106.34, "yaw": 14.89},
|
|
||||||
"t5": {"speed": 242, "boost": 351, "pitch": 41.71, "roll": 109.78, "yaw": 15.37}
|
|
||||||
},
|
|
||||||
"eagle": {
|
|
||||||
"t2": {"speed": 223, "boost": 325, "pitch": 46.45, "roll": 111.48, "yaw": 16.72},
|
|
||||||
"t1": {"speed": 229, "boost": 334, "pitch": 47.69, "roll": 114.46, "yaw": 17.17},
|
|
||||||
"t0": {"speed": 235, "boost": 343, "pitch": 49.00, "roll": 117.60, "yaw": 17.64},
|
|
||||||
"t9": {"speed": 239, "boost": 349, "pitch": 49.80, "roll": 119.53, "yaw": 17.93},
|
|
||||||
"t8": {"speed": 243, "boost": 355, "pitch": 50.70, "roll": 121.69, "yaw": 18.25},
|
|
||||||
"t7": {"speed": 248, "boost": 361, "pitch": 51.62, "roll": 123.89, "yaw": 18.58},
|
|
||||||
"t6": {"speed": 252, "boost": 367, "pitch": 52.46, "roll": 125.91, "yaw": 18.89},
|
|
||||||
"t5": {"speed": 259, "boost": 378, "pitch": 53.99, "roll": 129.56, "yaw": 19.43}
|
|
||||||
},
|
|
||||||
"hauler": {
|
|
||||||
"t4": {"speed": 203, "boost": 305, "pitch": 36.61, "roll": 101.71, "yaw": 14.24},
|
|
||||||
"t3": {"speed": 209, "boost": 314, "pitch": 37.63, "roll": 104.54, "yaw": 14.64},
|
|
||||||
"t2": {"speed": 216, "boost": 324, "pitch": 38.89, "roll": 108.03, "yaw": 15.12},
|
|
||||||
"t1": {"speed": 222, "boost": 333, "pitch": 39.97, "roll": 111.02, "yaw": 15.54},
|
|
||||||
"t0": {"speed": 232, "boost": 348, "pitch": 41.76, "roll": 116.00, "yaw": 16.24}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,289 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "http://cdn.coriolis.io/schemas/ship-loadout/3.json#",
|
|
||||||
"name": "Test My Ship",
|
|
||||||
"ship": "Anaconda",
|
|
||||||
"references": [
|
|
||||||
{
|
|
||||||
"name": "Coriolis.io",
|
|
||||||
"url": "http://localhost:3300/outfit/anaconda/48A6A6A5A8A8A5C2c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.AwRj4zNKqA==.CwBhCYzBGW9qCTSqs5xA?bn=Test%20My%20Ship",
|
|
||||||
"old-code": "48A6A6A5A8A8A5C2c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.AwRj4zNKqA==.CwBhCYzBGW9qCTSqs5xA",
|
|
||||||
"code": "4putkFklkdzsuf52c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.AwRj4zNKqA==.CwBhCYzBGW9qCTSqs5xA",
|
|
||||||
"shipId": "anaconda"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"components": {
|
|
||||||
"standard": {
|
|
||||||
"bulkheads": "Reactive Surface Composite",
|
|
||||||
"cargoHatch": {
|
|
||||||
"enabled": false,
|
|
||||||
"priority": 5
|
|
||||||
},
|
|
||||||
"powerPlant": {
|
|
||||||
"class": 8,
|
|
||||||
"rating": "A",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1
|
|
||||||
},
|
|
||||||
"thrusters": {
|
|
||||||
"class": 6,
|
|
||||||
"rating": "A",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1
|
|
||||||
},
|
|
||||||
"frameShiftDrive": {
|
|
||||||
"class": 6,
|
|
||||||
"rating": "A",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 3
|
|
||||||
},
|
|
||||||
"lifeSupport": {
|
|
||||||
"class": 5,
|
|
||||||
"rating": "A",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1
|
|
||||||
},
|
|
||||||
"powerDistributor": {
|
|
||||||
"class": 8,
|
|
||||||
"rating": "A",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1
|
|
||||||
},
|
|
||||||
"sensors": {
|
|
||||||
"class": 8,
|
|
||||||
"rating": "A",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1
|
|
||||||
},
|
|
||||||
"fuelTank": {
|
|
||||||
"class": 5,
|
|
||||||
"rating": "C",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"hardpoints": [
|
|
||||||
{
|
|
||||||
"class": 4,
|
|
||||||
"rating": "A",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 2,
|
|
||||||
"group": "Plasma Accelerator",
|
|
||||||
"mount": "Fixed"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 3,
|
|
||||||
"rating": "D",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 2,
|
|
||||||
"group": "Beam Laser",
|
|
||||||
"mount": "Turret"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 3,
|
|
||||||
"rating": "D",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 2,
|
|
||||||
"group": "Beam Laser",
|
|
||||||
"mount": "Turret"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 3,
|
|
||||||
"rating": "D",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 2,
|
|
||||||
"group": "Beam Laser",
|
|
||||||
"mount": "Turret"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 2,
|
|
||||||
"rating": "E",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 2,
|
|
||||||
"group": "Cannon",
|
|
||||||
"mount": "Turret"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 2,
|
|
||||||
"rating": "E",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 2,
|
|
||||||
"group": "Cannon",
|
|
||||||
"mount": "Turret"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 1,
|
|
||||||
"rating": "F",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 2,
|
|
||||||
"group": "Beam Laser",
|
|
||||||
"mount": "Turret"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 1,
|
|
||||||
"rating": "F",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 2,
|
|
||||||
"group": "Beam Laser",
|
|
||||||
"mount": "Turret"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"utility": [
|
|
||||||
{
|
|
||||||
"class": 0,
|
|
||||||
"rating": "A",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"group": "Shield Booster"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 0,
|
|
||||||
"rating": "A",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"group": "Shield Booster"
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
{
|
|
||||||
"class": 0,
|
|
||||||
"rating": "C",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 2,
|
|
||||||
"group": "Kill Warrant Scanner"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 0,
|
|
||||||
"rating": "C",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 2,
|
|
||||||
"group": "Cargo Scanner"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 0,
|
|
||||||
"rating": "F",
|
|
||||||
"enabled": false,
|
|
||||||
"priority": 1,
|
|
||||||
"group": "Countermeasure",
|
|
||||||
"name": "Electronic Countermeasure"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 0,
|
|
||||||
"rating": "I",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"group": "Countermeasure",
|
|
||||||
"name": "Chaff Launcher"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 0,
|
|
||||||
"rating": "I",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 2,
|
|
||||||
"group": "Countermeasure",
|
|
||||||
"name": "Point Defence"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"internal": [
|
|
||||||
{
|
|
||||||
"class": 7,
|
|
||||||
"rating": "A",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"group": "Shield Generator"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 6,
|
|
||||||
"rating": "A",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"group": "Shield Cell Bank"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 6,
|
|
||||||
"rating": "E",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"group": "Cargo Rack"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 5,
|
|
||||||
"rating": "D",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"group": "Hull Reinforcement Package"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 5,
|
|
||||||
"rating": "E",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"group": "Cargo Rack"
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
{
|
|
||||||
"class": 4,
|
|
||||||
"rating": "E",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"group": "Cargo Rack"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 4,
|
|
||||||
"rating": "E",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"group": "Cargo Rack"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 4,
|
|
||||||
"rating": "A",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 3,
|
|
||||||
"group": "Fuel Scoop"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 2,
|
|
||||||
"rating": "A",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 3,
|
|
||||||
"group": "Frame Shift Drive Interdictor"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"stats": {
|
|
||||||
"class": 3,
|
|
||||||
"hullCost": 141889930,
|
|
||||||
"speed": 180,
|
|
||||||
"topSpeed": 186.5,
|
|
||||||
"boost": 240,
|
|
||||||
"boostEnergy": 29,
|
|
||||||
"topBoost": 248.66,
|
|
||||||
"agility": 2,
|
|
||||||
"baseShieldStrength": 350,
|
|
||||||
"baseArmour": 945,
|
|
||||||
"hullMass": 400,
|
|
||||||
"masslock": 23,
|
|
||||||
"pipSpeed": 0.14,
|
|
||||||
"moduleCostMultiplier": 1,
|
|
||||||
"fuelCapacity": 32,
|
|
||||||
"cargoCapacity": 128,
|
|
||||||
"ladenMass": 1339.2,
|
|
||||||
"armour": 2228,
|
|
||||||
"armourAdded": 390,
|
|
||||||
"armourMultiplier": 1.95,
|
|
||||||
"shieldMultiplier": 1.4,
|
|
||||||
"totalCost": 882362060,
|
|
||||||
"unladenMass": 1179.2,
|
|
||||||
"totalDps": 29,
|
|
||||||
"powerAvailable": 36,
|
|
||||||
"powerRetracted": 23.33,
|
|
||||||
"powerDeployed": 34.76,
|
|
||||||
"unladenRange": 18.49,
|
|
||||||
"fullTankRange": 18.12,
|
|
||||||
"ladenRange": 16.39,
|
|
||||||
"unladenFastestRange": 73.21,
|
|
||||||
"ladenFastestRange": 66.15,
|
|
||||||
"maxJumpCount": 4,
|
|
||||||
"shieldStrength": 833
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,325 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "http://cdn.coriolis.io/schemas/ship-loadout/4.json#",
|
|
||||||
"name": "Test My Ship",
|
|
||||||
"ship": "Anaconda",
|
|
||||||
"references": [
|
|
||||||
{
|
|
||||||
"name": "Coriolis.io",
|
|
||||||
"url": "http://localhost:3300/outfit/anaconda/48A6A6A5A8A8A5C2c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.AwRj4zNKqA==.CwBhCYzBGW9qCTSqs5xA.H4sIAAAAAAAAA2MUe8HMwPD-PwDDhxeuCAAAAA==?bn=Test%20My%20Ship",
|
|
||||||
"old-code": "48A6A6A5A8A8A5C2c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.AwRj4zNKqA==.CwBhCYzBGW9qCTSqs5xA.H4sIAAAAAAAAA2MUe8HMwPD-PwDDhxeuCAAAAA==",
|
|
||||||
"code": "4putkFklkdzsuf52c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04--0303326b.AwRj4zNKqA==.CwBhCYzBGW9qCTSqs5xA.H4sIAAAAAAAAA2MUe8HMwPD-PwDDhxeuCAAAAA==",
|
|
||||||
"shipId": "anaconda"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"components": {
|
|
||||||
"standard": {
|
|
||||||
"bulkheads": "Reactive Surface Composite",
|
|
||||||
"cargoHatch": {
|
|
||||||
"enabled": false,
|
|
||||||
"priority": 5
|
|
||||||
},
|
|
||||||
"powerPlant": {
|
|
||||||
"class": 8,
|
|
||||||
"rating": "A",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"modifications": {
|
|
||||||
"pgen": 1000
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"thrusters": {
|
|
||||||
"class": 6,
|
|
||||||
"rating": "A",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1
|
|
||||||
},
|
|
||||||
"frameShiftDrive": {
|
|
||||||
"class": 6,
|
|
||||||
"rating": "A",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 3
|
|
||||||
},
|
|
||||||
"lifeSupport": {
|
|
||||||
"class": 5,
|
|
||||||
"rating": "A",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1
|
|
||||||
},
|
|
||||||
"powerDistributor": {
|
|
||||||
"class": 8,
|
|
||||||
"rating": "A",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1
|
|
||||||
},
|
|
||||||
"sensors": {
|
|
||||||
"class": 8,
|
|
||||||
"rating": "A",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1
|
|
||||||
},
|
|
||||||
"fuelTank": {
|
|
||||||
"class": 5,
|
|
||||||
"rating": "C",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"hardpoints": [
|
|
||||||
{
|
|
||||||
"class": 4,
|
|
||||||
"rating": "A",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 2,
|
|
||||||
"group": "Plasma Accelerator",
|
|
||||||
"mount": "Fixed"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 3,
|
|
||||||
"rating": "D",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 2,
|
|
||||||
"group": "Beam Laser",
|
|
||||||
"mount": "Turret"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 3,
|
|
||||||
"rating": "D",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 2,
|
|
||||||
"group": "Beam Laser",
|
|
||||||
"mount": "Turret"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 3,
|
|
||||||
"rating": "D",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 2,
|
|
||||||
"group": "Beam Laser",
|
|
||||||
"mount": "Turret"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 2,
|
|
||||||
"rating": "E",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 2,
|
|
||||||
"group": "Cannon",
|
|
||||||
"mount": "Turret"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 2,
|
|
||||||
"rating": "E",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 2,
|
|
||||||
"group": "Cannon",
|
|
||||||
"mount": "Turret"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 1,
|
|
||||||
"rating": "F",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 2,
|
|
||||||
"group": "Beam Laser",
|
|
||||||
"mount": "Turret"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 1,
|
|
||||||
"rating": "F",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 2,
|
|
||||||
"group": "Beam Laser",
|
|
||||||
"mount": "Turret"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"utility": [
|
|
||||||
{
|
|
||||||
"class": 0,
|
|
||||||
"rating": "A",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"group": "Shield Booster"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 0,
|
|
||||||
"rating": "A",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"group": "Shield Booster"
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
{
|
|
||||||
"class": 0,
|
|
||||||
"rating": "C",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 2,
|
|
||||||
"group": "Kill Warrant Scanner"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 0,
|
|
||||||
"rating": "C",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 2,
|
|
||||||
"group": "Cargo Scanner"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 0,
|
|
||||||
"rating": "F",
|
|
||||||
"enabled": false,
|
|
||||||
"priority": 1,
|
|
||||||
"group": "Electronic Countermeasure",
|
|
||||||
"name": "Electronic Countermeasure"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 0,
|
|
||||||
"rating": "I",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"group": "Chaff Launcher",
|
|
||||||
"name": "Chaff Launcher"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 0,
|
|
||||||
"rating": "I",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 2,
|
|
||||||
"group": "Point Defence",
|
|
||||||
"name": "Point Defence"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"internal": [
|
|
||||||
{
|
|
||||||
"class": 7,
|
|
||||||
"rating": "A",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"group": "Shield Generator"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 6,
|
|
||||||
"rating": "A",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"group": "Shield Cell Bank"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 6,
|
|
||||||
"rating": "E",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"group": "Cargo Rack"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 5,
|
|
||||||
"rating": "D",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"group": "Hull Reinforcement Package"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 5,
|
|
||||||
"rating": "E",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"group": "Cargo Rack"
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
{
|
|
||||||
"class": 4,
|
|
||||||
"rating": "E",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"group": "Cargo Rack"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 4,
|
|
||||||
"rating": "E",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"group": "Cargo Rack"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 4,
|
|
||||||
"rating": "A",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 3,
|
|
||||||
"group": "Fuel Scoop"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 2,
|
|
||||||
"rating": "A",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 3,
|
|
||||||
"group": "Frame Shift Drive Interdictor"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"stats": {
|
|
||||||
"class": 3,
|
|
||||||
"fighterHangars": 1,
|
|
||||||
"hullCost": 141889930,
|
|
||||||
"speed": 180,
|
|
||||||
"topSpeed": 186.5,
|
|
||||||
"boost": 240,
|
|
||||||
"boostEnergy": 27,
|
|
||||||
"topBoost": 249.34,
|
|
||||||
"topPitch": 25.97,
|
|
||||||
"topRoll": 62.34,
|
|
||||||
"topYaw": 10.39,
|
|
||||||
"topSpeed": 187.01,
|
|
||||||
"totalCost": 882362058,
|
|
||||||
"totalDpe": 142.68,
|
|
||||||
"totalDps": 101.13,
|
|
||||||
"totalEps": 18.71,
|
|
||||||
"totalExplDpe": 0,
|
|
||||||
"totalExplDps": 0,
|
|
||||||
"totalExplSDps": 0,
|
|
||||||
"totalAbsDpe": 3.57,
|
|
||||||
"totalAbsDps": 18.78,
|
|
||||||
"totalAbsSDps": 14.45,
|
|
||||||
"totalHps": 28.28,
|
|
||||||
"totalKinDpe": 117.48,
|
|
||||||
"totalKinDps": 22.27,
|
|
||||||
"totalKinSDps": 16.91,
|
|
||||||
"totalSDps": 89.99,
|
|
||||||
"totalThermDpe": 21.63,
|
|
||||||
"totalThermDps": 60.08,
|
|
||||||
"totalThermSDps": 58.64,
|
|
||||||
"baseShieldStrength": 350,
|
|
||||||
"baseArmour": 945,
|
|
||||||
"hullExplRes": 0.22,
|
|
||||||
"hullKinRes": 0.27,
|
|
||||||
"hullMass": 400,
|
|
||||||
"hullThermRes": -0.36,
|
|
||||||
"masslock": 23,
|
|
||||||
"pipSpeed": 0.14,
|
|
||||||
"pitch": 25,
|
|
||||||
"moduleCostMultiplier": 1,
|
|
||||||
"modulearmour": 0,
|
|
||||||
"moduleprotection": 0,
|
|
||||||
"fuelCapacity": 32,
|
|
||||||
"cargoCapacity": 128,
|
|
||||||
"ladenMass": 1323.2,
|
|
||||||
"armour": 2227.5,
|
|
||||||
"baseArmour": 525,
|
|
||||||
"unladenMass": 1163.2,
|
|
||||||
"powerAvailable": 39.6,
|
|
||||||
"powerRetracted": 23.33,
|
|
||||||
"powerDeployed": 34.13,
|
|
||||||
"roll": 60,
|
|
||||||
"unladenRange": 18.74,
|
|
||||||
"yaw": 10,
|
|
||||||
"fullTankRange": 18.36,
|
|
||||||
"hardness": 65,
|
|
||||||
"ladenRange": 16.59,
|
|
||||||
"unladenFastestRange": 74.2,
|
|
||||||
"ladenFastestRange": 66.96,
|
|
||||||
"maxJumpCount": 4,
|
|
||||||
"shield": 833,
|
|
||||||
"shieldCells": 1840,
|
|
||||||
"shieldExplRes": 0.5,
|
|
||||||
"shieldKinRes": 0.4,
|
|
||||||
"shieldThermRes": -0.2,
|
|
||||||
"crew": 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,255 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "http://cdn.coriolis.io/schemas/ship-loadout/4.json#",
|
|
||||||
"name": "Multi-purpose Asp Explorer",
|
|
||||||
"ship": "Asp Explorer",
|
|
||||||
"references": [
|
|
||||||
{
|
|
||||||
"name": "Coriolis.io",
|
|
||||||
"url": "https://coriolis.edcd.io/outfit/asp?code=0pftiFflfddsnf5------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%2FAwDFxwtofAAAAA%3D%3D&bn=Multi-purpose%20Asp%20Explorer",
|
|
||||||
"code": "0pftiFflfddsnf5------020202033c044002v62f2i.AwRj4yvI.CwRgDBldHnJA.H4sIAAAAAAAAA2P858DAwPCXEUhwHPvx/78YG5AltB7I/8/0TwImJboDSPJ/+/f/v/KlX///i3AwMTBIfARK/Gf+JwVSxArStVAYqOjvz///JVo5GRhE2IBc4SKQSSz/DGEmCa398P8//2+gTf//AwDFxwtofAAAAA==",
|
|
||||||
"shipId": "asp"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"components": {
|
|
||||||
"standard": {
|
|
||||||
"bulkheads": "Lightweight Alloy",
|
|
||||||
"cargoHatch": {
|
|
||||||
"enabled": false,
|
|
||||||
"priority": 5
|
|
||||||
},
|
|
||||||
"powerPlant": {
|
|
||||||
"class": 5,
|
|
||||||
"rating": "A",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 2,
|
|
||||||
"modifications": {
|
|
||||||
"eff": -1850,
|
|
||||||
"pgen": 6,
|
|
||||||
"mass": 431
|
|
||||||
},
|
|
||||||
"blueprint": {
|
|
||||||
"id": 64,
|
|
||||||
"name": "Low emissions",
|
|
||||||
"grade": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"thrusters": {
|
|
||||||
"class": 5,
|
|
||||||
"rating": "D",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"modifications": {
|
|
||||||
"optmul": 440,
|
|
||||||
"integrity": -266,
|
|
||||||
"thermload": -1326,
|
|
||||||
"optmass": 520,
|
|
||||||
"power": 241
|
|
||||||
},
|
|
||||||
"blueprint": {
|
|
||||||
"id": 24,
|
|
||||||
"name": "Clean",
|
|
||||||
"grade": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"frameShiftDrive": {
|
|
||||||
"class": 5,
|
|
||||||
"rating": "A",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"modifications": {
|
|
||||||
"mass": 5025,
|
|
||||||
"integrity": -1539,
|
|
||||||
"power": 2437,
|
|
||||||
"optmass": 4870,
|
|
||||||
"maxfuel": 370
|
|
||||||
},
|
|
||||||
"blueprint": {
|
|
||||||
"id": 26,
|
|
||||||
"name": "Increased range",
|
|
||||||
"grade": 5
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"lifeSupport": {
|
|
||||||
"class": 4,
|
|
||||||
"rating": "A",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"modifications": {
|
|
||||||
"mass": -3923,
|
|
||||||
"integrity": -1797
|
|
||||||
},
|
|
||||||
"blueprint": {
|
|
||||||
"id": 49,
|
|
||||||
"name": "Lightweight",
|
|
||||||
"grade": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"powerDistributor": {
|
|
||||||
"class": 3,
|
|
||||||
"rating": "D",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1
|
|
||||||
},
|
|
||||||
"sensors": {
|
|
||||||
"class": 5,
|
|
||||||
"rating": "D",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1
|
|
||||||
},
|
|
||||||
"fuelTank": {
|
|
||||||
"class": 5,
|
|
||||||
"rating": "C",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"hardpoints": [
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null
|
|
||||||
],
|
|
||||||
"utility": [
|
|
||||||
{
|
|
||||||
"class": 0,
|
|
||||||
"rating": "I",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"group": "Heat Sink Launcher",
|
|
||||||
"name": "Heat Sink Launcher"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 0,
|
|
||||||
"rating": "I",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"group": "Heat Sink Launcher",
|
|
||||||
"name": "Heat Sink Launcher"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 0,
|
|
||||||
"rating": "I",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"group": "Heat Sink Launcher",
|
|
||||||
"name": "Heat Sink Launcher"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 0,
|
|
||||||
"rating": "I",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"group": "Point Defence",
|
|
||||||
"name": "Point Defence"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"internal": [
|
|
||||||
{
|
|
||||||
"class": 6,
|
|
||||||
"rating": "A",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"group": "Fuel Scoop"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 5,
|
|
||||||
"rating": "E",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 2,
|
|
||||||
"group": "Cargo Rack"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 3,
|
|
||||||
"rating": "A",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"group": "Shield Generator"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 3,
|
|
||||||
"rating": "E",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 2,
|
|
||||||
"group": "Cargo Rack"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 2,
|
|
||||||
"rating": "G",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"group": "Planetary Vehicle Hangar"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 1,
|
|
||||||
"rating": "C",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 2,
|
|
||||||
"group": "Scanner",
|
|
||||||
"name": "Advanced Discovery Scanner"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 1,
|
|
||||||
"rating": "C",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 2,
|
|
||||||
"group": "Scanner",
|
|
||||||
"name": "Detailed Surface Scanner"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"stats": {
|
|
||||||
"class": 2,
|
|
||||||
"hullCost": 6135660,
|
|
||||||
"speed": 250,
|
|
||||||
"boost": 340,
|
|
||||||
"boostEnergy": 13,
|
|
||||||
"agility": 6,
|
|
||||||
"baseShieldStrength": 140,
|
|
||||||
"baseArmour": 210,
|
|
||||||
"hullMass": 280,
|
|
||||||
"masslock": 11,
|
|
||||||
"pipSpeed": 0.13,
|
|
||||||
"moduleCostMultiplier": 1,
|
|
||||||
"fuelCapacity": 32,
|
|
||||||
"cargoCapacity": 40,
|
|
||||||
"ladenMass": 435.26,
|
|
||||||
"armour": 378,
|
|
||||||
"shield": 113.43,
|
|
||||||
"shieldCells": 0,
|
|
||||||
"totalCost": 48402550,
|
|
||||||
"unladenMass": 363.26,
|
|
||||||
"totalDpe": 0,
|
|
||||||
"totalExplDpe": 0,
|
|
||||||
"totalKinDpe": 0,
|
|
||||||
"totalThermDpe": 0,
|
|
||||||
"totalDps": 0,
|
|
||||||
"totalExplDps": 0,
|
|
||||||
"totalKinDps": 0,
|
|
||||||
"totalThermDps": 0,
|
|
||||||
"totalSDps": 0,
|
|
||||||
"totalExplSDps": 0,
|
|
||||||
"totalKinSDps": 0,
|
|
||||||
"totalThermSDps": 0,
|
|
||||||
"totalEps": 1.2,
|
|
||||||
"totalHps": 1,
|
|
||||||
"shieldExplRes": 0.5,
|
|
||||||
"shieldKinRes": 0.6,
|
|
||||||
"shieldThermRes": 1.2,
|
|
||||||
"hullExplRes": 1.4,
|
|
||||||
"hullKinRes": 1.2,
|
|
||||||
"hullThermRes": 1,
|
|
||||||
"powerAvailable": 20.41,
|
|
||||||
"powerRetracted": 11.91,
|
|
||||||
"powerDeployed": 11.91,
|
|
||||||
"unladenRange": 50.45,
|
|
||||||
"fullTankRange": 47.03,
|
|
||||||
"ladenRange": 42.71,
|
|
||||||
"unladenFastestRange": 317.24,
|
|
||||||
"ladenFastestRange": 287.02,
|
|
||||||
"maxJumpCount": 7,
|
|
||||||
"topSpeed": 274.01,
|
|
||||||
"topBoost": 372.65
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,552 +0,0 @@
|
|||||||
{
|
|
||||||
"cargo": {
|
|
||||||
"capacity": 32
|
|
||||||
},
|
|
||||||
"free": false,
|
|
||||||
"fuel": {
|
|
||||||
"main": {
|
|
||||||
"capacity": 128
|
|
||||||
},
|
|
||||||
"reserve": {
|
|
||||||
"capacity": 0.81
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"id": 31,
|
|
||||||
"modules": {
|
|
||||||
"Armour": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128049346,
|
|
||||||
"name": "BelugaLiner_Armour_Grade1",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Bobble01": [],
|
|
||||||
"Bobble02": [],
|
|
||||||
"Bobble03": [],
|
|
||||||
"Bobble04": [],
|
|
||||||
"Bobble05": [],
|
|
||||||
"Bobble06": [],
|
|
||||||
"Bobble07": [],
|
|
||||||
"Bobble08": [],
|
|
||||||
"Bobble09": [],
|
|
||||||
"Bobble10": [],
|
|
||||||
"Decal1": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128667757,
|
|
||||||
"name": "Decal_Explorer_Ranger",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Decal2": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128667742,
|
|
||||||
"name": "Decal_Combat_Deadly",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Decal3": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128667750,
|
|
||||||
"name": "Decal_Trade_Tycoon",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"EngineColour": [],
|
|
||||||
"FrameShiftDrive": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128064132,
|
|
||||||
"modifiers": {
|
|
||||||
"engineerID": 300100,
|
|
||||||
"id": 175,
|
|
||||||
"modifiers": [
|
|
||||||
{
|
|
||||||
"name": "mod_mass",
|
|
||||||
"type": 1,
|
|
||||||
"value": 0.4457540512085
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mod_health",
|
|
||||||
"type": 1,
|
|
||||||
"value": -0.24584779143333
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mod_passive_power",
|
|
||||||
"type": 1,
|
|
||||||
"value": 0.24457727372646
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mod_fsd_optimised_mass",
|
|
||||||
"type": 1,
|
|
||||||
"value": 0.49257898330688
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mod_fsd_max_fuel_per_jump",
|
|
||||||
"type": 2,
|
|
||||||
"value": 0.028505677357316
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mod_fsd_heat_rate",
|
|
||||||
"type": 2,
|
|
||||||
"value": -0.079360365867615
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"moduleTags": [
|
|
||||||
16
|
|
||||||
],
|
|
||||||
"recipeID": 128673694,
|
|
||||||
"slotIndex": 53
|
|
||||||
},
|
|
||||||
"name": "Int_Hyperdrive_Size7_Class5",
|
|
||||||
"on": true,
|
|
||||||
"priority": 0,
|
|
||||||
"recipeLevel": 5,
|
|
||||||
"recipeName": "FSD_LongRange",
|
|
||||||
"recipeValue": 0,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 46160201
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"FuelTank": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128064352,
|
|
||||||
"name": "Int_FuelTank_Size7_Class3",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"unloaned": 1602822,
|
|
||||||
"value": 1602822
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"LifeSupport": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128064174,
|
|
||||||
"name": "Int_LifeSupport_Size8_Class2",
|
|
||||||
"on": true,
|
|
||||||
"priority": 0,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 1569565
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"MainEngines": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128064094,
|
|
||||||
"modifiers": {
|
|
||||||
"engineerID": 300100,
|
|
||||||
"id": 253,
|
|
||||||
"modifiers": [
|
|
||||||
{
|
|
||||||
"name": "mod_engine_mass_curve_multiplier",
|
|
||||||
"type": 1,
|
|
||||||
"value": 0.098235413432121
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mod_engine_heat",
|
|
||||||
"type": 1,
|
|
||||||
"value": 0.18069696426392
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mod_passive_power",
|
|
||||||
"type": 1,
|
|
||||||
"value": 0.033788848668337
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mod_health",
|
|
||||||
"type": 1,
|
|
||||||
"value": -0.056404989212751
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mod_engine_mass_curve",
|
|
||||||
"type": 1,
|
|
||||||
"value": -0.027384582906961
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mod_engine_heat",
|
|
||||||
"type": 2,
|
|
||||||
"value": -0.072683908045292
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"moduleTags": [
|
|
||||||
17
|
|
||||||
],
|
|
||||||
"recipeID": 128673655,
|
|
||||||
"slotIndex": 52
|
|
||||||
},
|
|
||||||
"name": "Int_Engine_Size7_Class2",
|
|
||||||
"on": true,
|
|
||||||
"priority": 0,
|
|
||||||
"recipeLevel": 1,
|
|
||||||
"recipeName": "Engine_Dirty",
|
|
||||||
"recipeValue": 0,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 1709638
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"MediumHardpoint1": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128049436,
|
|
||||||
"name": "Hpt_BeamLaser_Turret_Medium",
|
|
||||||
"on": true,
|
|
||||||
"priority": 0,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 1889910
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"MediumHardpoint2": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128049436,
|
|
||||||
"name": "Hpt_BeamLaser_Turret_Medium",
|
|
||||||
"on": true,
|
|
||||||
"priority": 0,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 1889910
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"MediumHardpoint3": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128049460,
|
|
||||||
"name": "Hpt_MultiCannon_Gimbal_Medium",
|
|
||||||
"on": true,
|
|
||||||
"priority": 0,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 51300
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"MediumHardpoint4": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128049460,
|
|
||||||
"name": "Hpt_MultiCannon_Gimbal_Medium",
|
|
||||||
"on": true,
|
|
||||||
"priority": 0,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 51300
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"MediumHardpoint5": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128049460,
|
|
||||||
"name": "Hpt_MultiCannon_Gimbal_Medium",
|
|
||||||
"on": true,
|
|
||||||
"priority": 0,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 51300
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PaintJob": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128732290,
|
|
||||||
"name": "PaintJob_BelugaLiner_Tactical_White",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PlanetaryApproachSuite": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128672317,
|
|
||||||
"name": "Int_PlanetApproachSuite",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"unloaned": 450,
|
|
||||||
"value": 450
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PowerDistributor": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128064207,
|
|
||||||
"name": "Int_PowerDistributor_Size6_Class5",
|
|
||||||
"on": true,
|
|
||||||
"priority": 0,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 3128120
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PowerPlant": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128064057,
|
|
||||||
"modifiers": {
|
|
||||||
"engineerID": 300100,
|
|
||||||
"id": 277,
|
|
||||||
"modifiers": [
|
|
||||||
{
|
|
||||||
"name": "mod_powerplant_power",
|
|
||||||
"type": 1,
|
|
||||||
"value": 0.054692290723324
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mod_health",
|
|
||||||
"type": 1,
|
|
||||||
"value": -0.033690698444843
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mod_powerplant_heat",
|
|
||||||
"type": 1,
|
|
||||||
"value": 0.027470717206597
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mod_powerplant_heat",
|
|
||||||
"type": 2,
|
|
||||||
"value": -0.056317910552025
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"moduleTags": [
|
|
||||||
18
|
|
||||||
],
|
|
||||||
"recipeID": 128673765,
|
|
||||||
"slotIndex": 51
|
|
||||||
},
|
|
||||||
"name": "Int_Powerplant_Size6_Class5",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"recipeLevel": 1,
|
|
||||||
"recipeName": "PowerPlant_Boosted",
|
|
||||||
"recipeValue": 0,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 14561578
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Radar": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128064239,
|
|
||||||
"name": "Int_Sensors_Size5_Class2",
|
|
||||||
"on": true,
|
|
||||||
"priority": 0,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 71500
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Slot01_Size6": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128666681,
|
|
||||||
"name": "Int_FuelScoop_Size6_Class5",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 25887249
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Slot02_Size6": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128064287,
|
|
||||||
"name": "Int_ShieldGenerator_Size6_Class5",
|
|
||||||
"on": true,
|
|
||||||
"priority": 0,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 14561578
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Slot03_Size6": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128727927,
|
|
||||||
"name": "Int_PassengerCabin_Size6_Class2",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"unloaned": 165808,
|
|
||||||
"value": 165808
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Slot04_Size6": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128727928,
|
|
||||||
"name": "Int_PassengerCabin_Size6_Class3",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 497429
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Slot05_Size5": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128727925,
|
|
||||||
"name": "Int_PassengerCabin_Size5_Class4",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 1492286
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Slot06_Size5": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128064342,
|
|
||||||
"name": "Int_CargoRack_Size5_Class1",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"unloaned": 100409,
|
|
||||||
"value": 100409
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Slot07_Size4": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128727922,
|
|
||||||
"name": "Int_PassengerCabin_Size4_Class1",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 17059
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Slot08_Size3": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128667632,
|
|
||||||
"name": "Int_Repairer_Size3_Class5",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 2361960
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Slot09_Size3": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128672289,
|
|
||||||
"name": "Int_BuggyBay_Size2_Class2",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 19440
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Slot10_Size3": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128666634,
|
|
||||||
"name": "Int_DetailedSurfaceScanner_Tiny",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 225000
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Slot11_Size3": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128663561,
|
|
||||||
"name": "Int_StellarBodyDiscoveryScanner_Advanced",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 1390500
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"TinyHardpoint1": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128049513,
|
|
||||||
"name": "Hpt_ChaffLauncher_Tiny",
|
|
||||||
"on": true,
|
|
||||||
"priority": 0,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 7650
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"TinyHardpoint2": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128668536,
|
|
||||||
"name": "Hpt_ShieldBooster_Size0_Class5",
|
|
||||||
"on": true,
|
|
||||||
"priority": 0,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 252900
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"TinyHardpoint3": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128668536,
|
|
||||||
"name": "Hpt_ShieldBooster_Size0_Class5",
|
|
||||||
"on": true,
|
|
||||||
"priority": 0,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 252900
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"TinyHardpoint4": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128668536,
|
|
||||||
"name": "Hpt_ShieldBooster_Size0_Class5",
|
|
||||||
"on": true,
|
|
||||||
"priority": 0,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 281000
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"TinyHardpoint5": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128668536,
|
|
||||||
"name": "Hpt_ShieldBooster_Size0_Class5",
|
|
||||||
"on": true,
|
|
||||||
"priority": 0,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 281000
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"TinyHardpoint6": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128668536,
|
|
||||||
"name": "Hpt_ShieldBooster_Size0_Class5",
|
|
||||||
"on": true,
|
|
||||||
"priority": 0,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 281000
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"WeaponColour": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128732194,
|
|
||||||
"name": "WeaponCustomisation_Purple",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"name": "BelugaLiner",
|
|
||||||
"value": {
|
|
||||||
"hull": 71688743,
|
|
||||||
"modules": 120812762,
|
|
||||||
"unloaned": 1869489
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,314 +0,0 @@
|
|||||||
{
|
|
||||||
"cargo": {
|
|
||||||
"capacity": 264
|
|
||||||
},
|
|
||||||
"free": false,
|
|
||||||
"fuel": {
|
|
||||||
"main": {
|
|
||||||
"capacity": 32
|
|
||||||
},
|
|
||||||
"reserve": {
|
|
||||||
"capacity": 0.52
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"id": 4,
|
|
||||||
"modules": {
|
|
||||||
"Armour": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128049298,
|
|
||||||
"name": "Type7_Armour_Grade1",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Bobble01": [],
|
|
||||||
"Bobble02": [],
|
|
||||||
"Bobble03": [],
|
|
||||||
"Bobble04": [],
|
|
||||||
"Bobble05": [],
|
|
||||||
"Bobble06": [],
|
|
||||||
"Bobble07": [],
|
|
||||||
"Bobble08": [],
|
|
||||||
"Bobble09": [],
|
|
||||||
"Bobble10": [],
|
|
||||||
"Decal1": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128667746,
|
|
||||||
"name": "Decal_Trade_Dealer",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Decal2": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128667738,
|
|
||||||
"name": "Decal_Combat_Competent",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Decal3": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128667753,
|
|
||||||
"name": "Decal_Explorer_Scout",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"EngineColour": [],
|
|
||||||
"FrameShiftDrive": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128064122,
|
|
||||||
"name": "Int_Hyperdrive_Size5_Class5",
|
|
||||||
"on": true,
|
|
||||||
"priority": 0,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 5103953
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"FuelTank": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128064350,
|
|
||||||
"name": "Int_FuelTank_Size5_Class3",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"unloaned": 97754,
|
|
||||||
"value": 97754
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"LifeSupport": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128064154,
|
|
||||||
"name": "Int_LifeSupport_Size4_Class2",
|
|
||||||
"on": true,
|
|
||||||
"priority": 0,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 28373
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"MainEngines": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128064087,
|
|
||||||
"name": "Int_Engine_Size5_Class5",
|
|
||||||
"on": true,
|
|
||||||
"priority": 0,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 5103953
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PaintJob": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128671422,
|
|
||||||
"name": "PaintJob_Type7_Tactical_White",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PlanetaryApproachSuite": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128672317,
|
|
||||||
"name": "Int_PlanetApproachSuite",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"unloaned": 500,
|
|
||||||
"value": 500
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PowerDistributor": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128064192,
|
|
||||||
"name": "Int_PowerDistributor_Size3_Class5",
|
|
||||||
"on": true,
|
|
||||||
"priority": 0,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 158331
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PowerPlant": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128064047,
|
|
||||||
"name": "Int_Powerplant_Size4_Class5",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 1610080
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Radar": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128064229,
|
|
||||||
"name": "Int_Sensors_Size3_Class2",
|
|
||||||
"on": true,
|
|
||||||
"priority": 0,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 10133
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Slot01_Size6": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128064343,
|
|
||||||
"name": "Int_CargoRack_Size6_Class1",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 362591
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Slot02_Size6": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128064343,
|
|
||||||
"name": "Int_CargoRack_Size6_Class1",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 362591
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Slot03_Size5": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128064343,
|
|
||||||
"name": "Int_CargoRack_Size6_Class1",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 362591
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Slot04_Size5": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128064342,
|
|
||||||
"name": "Int_CargoRack_Size5_Class1",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"unloaned": 111566,
|
|
||||||
"value": 111566
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Slot05_Size4": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128064342,
|
|
||||||
"name": "Int_CargoRack_Size5_Class1",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"unloaned": 111566,
|
|
||||||
"value": 111566
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Slot06_Size4": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128064279,
|
|
||||||
"name": "Int_ShieldGenerator_Size5_Class2",
|
|
||||||
"on": true,
|
|
||||||
"priority": 0,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 189035
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Slot07_Size2": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128049549,
|
|
||||||
"name": "Int_DockingComputer_Standard",
|
|
||||||
"on": true,
|
|
||||||
"priority": 0,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 4500
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Slot08_Size2": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128064340,
|
|
||||||
"name": "Int_CargoRack_Size3_Class1",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 10563
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"SmallHardpoint1": [],
|
|
||||||
"SmallHardpoint2": [],
|
|
||||||
"SmallHardpoint3": [],
|
|
||||||
"SmallHardpoint4": [],
|
|
||||||
"TinyHardpoint1": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128668536,
|
|
||||||
"name": "Hpt_ShieldBooster_Size0_Class5",
|
|
||||||
"on": true,
|
|
||||||
"priority": 0,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 281000
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"TinyHardpoint2": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128668536,
|
|
||||||
"name": "Hpt_ShieldBooster_Size0_Class5",
|
|
||||||
"on": true,
|
|
||||||
"priority": 0,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 281000
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"TinyHardpoint3": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128668536,
|
|
||||||
"name": "Hpt_ShieldBooster_Size0_Class5",
|
|
||||||
"on": true,
|
|
||||||
"priority": 0,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 281000
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"TinyHardpoint4": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128049513,
|
|
||||||
"name": "Hpt_ChaffLauncher_Tiny",
|
|
||||||
"on": true,
|
|
||||||
"priority": 0,
|
|
||||||
"unloaned": 0,
|
|
||||||
"value": 8500
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"WeaponColour": []
|
|
||||||
},
|
|
||||||
"name": "Type7",
|
|
||||||
"value": {
|
|
||||||
"hull": 16780009,
|
|
||||||
"modules": 14479580,
|
|
||||||
"unloaned": 321386
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,225 +0,0 @@
|
|||||||
{
|
|
||||||
"free": false,
|
|
||||||
"id": 2,
|
|
||||||
"modules": {
|
|
||||||
"Armour": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128049280,
|
|
||||||
"name": "CobraMkIII_Armour_Grade1",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"value": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"FrameShiftDrive": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128064117,
|
|
||||||
"name": "Int_Hyperdrive_Size4_Class5",
|
|
||||||
"on": true,
|
|
||||||
"priority": 4,
|
|
||||||
"value": 1610080
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"FuelTank": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128064349,
|
|
||||||
"name": "Int_FuelTank_Size4_Class3",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"value": 24734
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"LifeSupport": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128064149,
|
|
||||||
"name": "Int_LifeSupport_Size3_Class2",
|
|
||||||
"on": true,
|
|
||||||
"priority": 0,
|
|
||||||
"value": 10133
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"MainEngines": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128064079,
|
|
||||||
"name": "Int_Engine_Size4_Class2",
|
|
||||||
"on": true,
|
|
||||||
"priority": 0,
|
|
||||||
"value": 59633
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PaintJob": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128741033,
|
|
||||||
"name": "PaintJob_CobraMKIII_Corrosive_05",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"value": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PlanetaryApproachSuite": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128672317,
|
|
||||||
"name": "Int_PlanetApproachSuite",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"value": 500
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PowerDistributor": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128064179,
|
|
||||||
"name": "Int_PowerDistributor_Size1_Class2",
|
|
||||||
"on": true,
|
|
||||||
"priority": 2,
|
|
||||||
"value": 1293
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PowerPlant": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128064037,
|
|
||||||
"name": "Int_Powerplant_Size2_Class5",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"value": 160224
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Radar": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128064229,
|
|
||||||
"name": "Int_Sensors_Size3_Class2",
|
|
||||||
"on": true,
|
|
||||||
"priority": 0,
|
|
||||||
"value": 10133
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ShipID0": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128758976,
|
|
||||||
"name": "Nameplate_ShipID_Black",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"value": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ShipID1": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128758976,
|
|
||||||
"name": "Nameplate_ShipID_Black",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"value": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ShipKitBumper": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128740698,
|
|
||||||
"name": "CobraMkIII_ShipkitRaider1_Bumper1",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"value": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ShipKitSpoiler": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128740701,
|
|
||||||
"name": "CobraMkIII_ShipkitRaider1_Spoiler1",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"value": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ShipKitTail": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128740705,
|
|
||||||
"name": "CobraMkIII_ShipkitRaider1_Tail2",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"value": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ShipKitWings": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128740707,
|
|
||||||
"name": "CobraMkIII_ShipkitRaider1_Wings1",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"value": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ShipName0": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128758944,
|
|
||||||
"name": "Nameplate_Explorer01_Black",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"value": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ShipName1": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128758944,
|
|
||||||
"name": "Nameplate_Explorer01_Black",
|
|
||||||
"on": true,
|
|
||||||
"priority": 1,
|
|
||||||
"value": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Slot01_Size4": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128666663,
|
|
||||||
"name": "Int_FuelScoop_Size4_Class3",
|
|
||||||
"on": true,
|
|
||||||
"priority": 2,
|
|
||||||
"value": 178898
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Slot02_Size4": [],
|
|
||||||
"Slot03_Size4": [],
|
|
||||||
"Slot04_Size2": [],
|
|
||||||
"Slot05_Size2": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128663561,
|
|
||||||
"name": "Int_StellarBodyDiscoveryScanner_Advanced",
|
|
||||||
"on": true,
|
|
||||||
"priority": 2,
|
|
||||||
"value": 1545000
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Slot06_Size2": {
|
|
||||||
"module": {
|
|
||||||
"free": false,
|
|
||||||
"id": 128666634,
|
|
||||||
"name": "Int_DetailedSurfaceScanner_Tiny",
|
|
||||||
"on": true,
|
|
||||||
"priority": 2,
|
|
||||||
"value": 250000
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"name": "CobraMkIII",
|
|
||||||
"value": {
|
|
||||||
"hull": 205287,
|
|
||||||
"modules": 3850628,
|
|
||||||
"unloaned": 1751109
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,327 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "http://cdn.coriolis.io/schemas/ship-loadout/4.json#",
|
|
||||||
"name": "Multi-purpose Imperial Courier",
|
|
||||||
"ship": "Imperial Courier",
|
|
||||||
"references": [
|
|
||||||
{
|
|
||||||
"name": "Coriolis.io",
|
|
||||||
"url": "https://coriolis.edcd.io/outfit/imperial_courier?code=0patzF5l0das8f31a1a270202000e402t0101-2f.AwRj4zKA.CwRgDBldLiQ%3D.H4sIAAAAAAAAA12OP0tCYRjFj9fuVbvF1du9ekkT8s%2FkIg4NElyIBBd321yaGvwUQTS3N7UFfYygIT9EoyQUJA36ns47XJCWA%2B%2Fz%2Bz3Pe3ImBbDNKaqNPSBoGrL4ngfomKpFGiJ%2BLgHteR1IPjxJT5pF11uSeXNsJVcRfgdC92syWUuK0iMdKZqrjJ%2F0aoA71lJ5oKf38knWcCiptCPdhJIerdS00vlK0qktlqoj983UmqqHjQ33VsW8eazFmaTyULP2hQ4lX8LBme6g%2F6v0TTdbxJ2KhdEIaCw15MF%2FNB0L%2BS2hwEwyFM8KgP%2BqEpWWA3Qu9Z3z9kPWHzakt7Dt%2BAeD7ghSTgEAAA%3D%3D&bn=Multi-purpose%20Imperial%20Courier",
|
|
||||||
"code": "0patzF5l0das8f31a1a270202000e402t0101-2f.AwRj4zKA.CwRgDBldLiQ=.H4sIAAAAAAAAA12OP0tCYRjFj9fuVbvF1du9ekkT8s/kIg4NElyIBBd321yaGvwUQTS3N7UFfYygIT9EoyQUJA36ns47XJCWA+/z+z3Pe3ImBbDNKaqNPSBoGrL4ngfomKpFGiJ+LgHteR1IPjxJT5pF11uSeXNsJVcRfgdC92syWUuK0iMdKZqrjJ/0aoA71lJ5oKf38knWcCiptCPdhJIerdS00vlK0qktlqoj983UmqqHjQ33VsW8eazFmaTyULP2hQ4lX8LBme6g/6v0TTdbxJ2KhdEIaCw15MF/NB0L+S2hwEwyFM8KgP+qEpWWA3Qu9Z3z9kPWHzakt7Dt+AeD7ghSTgEAAA==",
|
|
||||||
"shipId": "imperial_courier"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"components": {
|
|
||||||
"standard": {
|
|
||||||
"bulkheads": "Lightweight Alloy",
|
|
||||||
"cargoHatch": {
|
|
||||||
"enabled": false,
|
|
||||||
"priority": 5
|
|
||||||
},
|
|
||||||
"powerPlant": {
|
|
||||||
"class": 4,
|
|
||||||
"rating": "A",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 2,
|
|
||||||
"modifications": {
|
|
||||||
"pgen": 1052,
|
|
||||||
"integrity": -482,
|
|
||||||
"eff": 974
|
|
||||||
},
|
|
||||||
"blueprint": {
|
|
||||||
"id": 63,
|
|
||||||
"name": "Overcharged",
|
|
||||||
"grade": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"thrusters": {
|
|
||||||
"class": 3,
|
|
||||||
"rating": "A",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"name": "Enhanced Performance",
|
|
||||||
"modifications": {
|
|
||||||
"optmul": 2476,
|
|
||||||
"thermload": 7023,
|
|
||||||
"power": 1763,
|
|
||||||
"integrity": 165,
|
|
||||||
"optmass": -667
|
|
||||||
},
|
|
||||||
"blueprint": {
|
|
||||||
"id": 22,
|
|
||||||
"name": "Dirty",
|
|
||||||
"grade": 4
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"frameShiftDrive": {
|
|
||||||
"class": 3,
|
|
||||||
"rating": "A",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"modifications": {
|
|
||||||
"mass": 4082,
|
|
||||||
"integrity": -2422,
|
|
||||||
"power": 1782,
|
|
||||||
"optmass": 4927
|
|
||||||
},
|
|
||||||
"blueprint": {
|
|
||||||
"id": 26,
|
|
||||||
"name": "Increased range",
|
|
||||||
"grade": 5
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"lifeSupport": {
|
|
||||||
"class": 1,
|
|
||||||
"rating": "A",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1
|
|
||||||
},
|
|
||||||
"powerDistributor": {
|
|
||||||
"class": 3,
|
|
||||||
"rating": "A",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1
|
|
||||||
},
|
|
||||||
"sensors": {
|
|
||||||
"class": 2,
|
|
||||||
"rating": "D",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1
|
|
||||||
},
|
|
||||||
"fuelTank": {
|
|
||||||
"class": 3,
|
|
||||||
"rating": "C",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"hardpoints": [
|
|
||||||
{
|
|
||||||
"class": 2,
|
|
||||||
"rating": "F",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"group": "Pulse Laser",
|
|
||||||
"mount": "Fixed",
|
|
||||||
"modifications": {
|
|
||||||
"rof": 5931,
|
|
||||||
"damage": -184,
|
|
||||||
"jitter": 50,
|
|
||||||
"distdraw": -4689,
|
|
||||||
"piercing": 3328
|
|
||||||
},
|
|
||||||
"blueprint": {
|
|
||||||
"id": 89,
|
|
||||||
"name": "Rapid fire",
|
|
||||||
"grade": 5
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 2,
|
|
||||||
"rating": "F",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"group": "Pulse Laser",
|
|
||||||
"mount": "Fixed",
|
|
||||||
"modifications": {
|
|
||||||
"rof": 4715,
|
|
||||||
"damage": -97,
|
|
||||||
"jitter": 30,
|
|
||||||
"distdraw": -4548,
|
|
||||||
"piercing": 1057,
|
|
||||||
"integrity": 319
|
|
||||||
},
|
|
||||||
"blueprint": {
|
|
||||||
"id": 89,
|
|
||||||
"name": "Rapid fire",
|
|
||||||
"grade": 5
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 2,
|
|
||||||
"rating": "F",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"group": "Multi-cannon",
|
|
||||||
"mount": "Gimballed",
|
|
||||||
"modifications": {
|
|
||||||
"damage": 2437,
|
|
||||||
"distdraw": 5487,
|
|
||||||
"rof": 1120,
|
|
||||||
"jitter": 58,
|
|
||||||
"thermload": 1346,
|
|
||||||
"power": 1009,
|
|
||||||
"integrity": -202,
|
|
||||||
"ammo": -2000
|
|
||||||
},
|
|
||||||
"blueprint": {
|
|
||||||
"id": 88,
|
|
||||||
"name": "Overcharged",
|
|
||||||
"grade": 3,
|
|
||||||
"special": {
|
|
||||||
"id": 3,
|
|
||||||
"name": "Corrosive shell"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"utility": [
|
|
||||||
{
|
|
||||||
"class": 0,
|
|
||||||
"rating": "I",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"group": "Heat Sink Launcher",
|
|
||||||
"name": "Heat Sink Launcher",
|
|
||||||
"modifications": {
|
|
||||||
"ammo": 5000,
|
|
||||||
"mass": 17684,
|
|
||||||
"reload": 9707
|
|
||||||
},
|
|
||||||
"blueprint": {
|
|
||||||
"id": 37,
|
|
||||||
"name": "Ammo capacity",
|
|
||||||
"grade": 3
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 0,
|
|
||||||
"rating": "I",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"group": "Heat Sink Launcher",
|
|
||||||
"name": "Heat Sink Launcher",
|
|
||||||
"modifications": {
|
|
||||||
"ammo": 5000,
|
|
||||||
"mass": 18520,
|
|
||||||
"reload": 8715
|
|
||||||
},
|
|
||||||
"blueprint": {
|
|
||||||
"id": 37,
|
|
||||||
"name": "Ammo capacity",
|
|
||||||
"grade": 3
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 0,
|
|
||||||
"rating": "I",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"group": "Chaff Launcher",
|
|
||||||
"name": "Chaff Launcher"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 0,
|
|
||||||
"rating": "A",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"group": "Frame Shift Wake Scanner"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"internal": [
|
|
||||||
{
|
|
||||||
"class": 3,
|
|
||||||
"rating": "A",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"group": "Shield Generator",
|
|
||||||
"modifications": {
|
|
||||||
"optmul": 1888,
|
|
||||||
"explres": 455,
|
|
||||||
"kinres": 546,
|
|
||||||
"thermres": 1092,
|
|
||||||
"brokenregen": -2614,
|
|
||||||
"regen": -876,
|
|
||||||
"distdraw": 463
|
|
||||||
},
|
|
||||||
"blueprint": {
|
|
||||||
"id": 77,
|
|
||||||
"name": "Reinforced",
|
|
||||||
"grade": 3
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 3,
|
|
||||||
"rating": "A",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 1,
|
|
||||||
"group": "Fuel Scoop"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 2,
|
|
||||||
"rating": "E",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 2,
|
|
||||||
"group": "Cargo Rack"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"class": 2,
|
|
||||||
"rating": "E",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 2,
|
|
||||||
"group": "Cargo Rack"
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
{
|
|
||||||
"class": 1,
|
|
||||||
"rating": "C",
|
|
||||||
"enabled": true,
|
|
||||||
"priority": 2,
|
|
||||||
"group": "Scanner",
|
|
||||||
"name": "Advanced Discovery Scanner"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"stats": {
|
|
||||||
"class": 1,
|
|
||||||
"hullCost": 2481550,
|
|
||||||
"speed": 280,
|
|
||||||
"boost": 380,
|
|
||||||
"boostEnergy": 10,
|
|
||||||
"agility": 6,
|
|
||||||
"baseShieldStrength": 200,
|
|
||||||
"baseArmour": 80,
|
|
||||||
"hullMass": 35,
|
|
||||||
"masslock": 7,
|
|
||||||
"pipSpeed": 0.05,
|
|
||||||
"moduleCostMultiplier": 1,
|
|
||||||
"fuelCapacity": 8,
|
|
||||||
"cargoCapacity": 8,
|
|
||||||
"ladenMass": 104.25,
|
|
||||||
"armour": 144,
|
|
||||||
"shield": 404.19,
|
|
||||||
"shieldCells": 0,
|
|
||||||
"totalCost": 14059860,
|
|
||||||
"unladenMass": 88.25,
|
|
||||||
"totalDpe": 32.25,
|
|
||||||
"totalExplDpe": 0,
|
|
||||||
"totalKinDpe": 9.41,
|
|
||||||
"totalThermDpe": 22.84,
|
|
||||||
"totalDps": 53.8,
|
|
||||||
"totalExplDps": 0,
|
|
||||||
"totalKinDps": 17.44,
|
|
||||||
"totalThermDps": 36.35,
|
|
||||||
"totalSDps": 48.99,
|
|
||||||
"totalExplSDps": 0,
|
|
||||||
"totalKinSDps": 12.64,
|
|
||||||
"totalThermSDps": 36.35,
|
|
||||||
"totalEps": 9.84,
|
|
||||||
"totalHps": 12.28,
|
|
||||||
"shieldExplRes": 0.48,
|
|
||||||
"shieldKinRes": 0.55,
|
|
||||||
"shieldThermRes": 1.09,
|
|
||||||
"hullExplRes": 1.4,
|
|
||||||
"hullKinRes": 1.2,
|
|
||||||
"hullThermRes": 1,
|
|
||||||
"powerAvailable": 17.24,
|
|
||||||
"powerRetracted": 11.3,
|
|
||||||
"powerDeployed": 16.41,
|
|
||||||
"unladenRange": 25.57,
|
|
||||||
"fullTankRange": 23.92,
|
|
||||||
"ladenRange": 22.09,
|
|
||||||
"unladenFastestRange": 116.23,
|
|
||||||
"ladenFastestRange": 107,
|
|
||||||
"maxJumpCount": 5,
|
|
||||||
"topSpeed": 386.56,
|
|
||||||
"topBoost": 524.62
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"buildText": "[Imaginary Ship]\nbla bla",
|
|
||||||
"errorMsg": "No such ship found: \"Imaginary Ship\""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"buildText": "[Viper]\nS: 1F/F Pulse Laser\nsome un-parseable nonsense\nS: 1F/F Pulse Laser\n",
|
|
||||||
"errorMsg": "Error parsing: \"some un-parseable nonsense\""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"buildText": "[Sidewinder]\nS: 2F/F Pulse Laser\nS: 1F/F Pulse Laser\n",
|
|
||||||
"errorMsg": "2F Pulse Laser exceeds slot size: \"S: 2F/F Pulse Laser\""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"buildText": "[Sidewinder]\nL: 2F/F Pulse Laser\nS: 1F/F Pulse Laser\n",
|
|
||||||
"errorMsg": "No hardpoint slot available for: \"L: 2F/F Pulse Laser\""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"buildText": "[Sidewinder]\nS: 1F/F Magic Thing\nS: 1F/F Pulse Laser\n",
|
|
||||||
"errorMsg": "Unknown component: \"S: 1F/F Magic Thing\""
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"shipId": "anaconda",
|
|
||||||
"buildName": "Imported Anaconda",
|
|
||||||
"buildCode": "0pyttFolodDsyf5------1717--------05044j-03--2h--00.Iw18ZlA=.Aw18ZlA=.",
|
|
||||||
"buildText": "[Anaconda]\nS: 1F/F Pulse Laser\nS: 1F/F Pulse Laser\n\nBH: 1I Lightweight Alloy\nRB: 8E Power Plant\nTM: 7E Thrusters\nFH: 6E Frame Shift Drive\nEC: 5E Life Support\nPC: 8E Power Distributor\nSS: 8E Sensors\nFS: 5C Fuel Tank (Capacity: 32)\n\n7: 6E Cargo Rack (Capacity: 64)\n6: 5E Cargo Rack (Capacity: 32)\n6: 6E Shield Generator\n5: 4E Cargo Rack (Capacity: 16)\n4: 1E Basic Discovery Scanner\n2: 1E Cargo Rack (Capacity: 2)\n"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"shipId": "anaconda",
|
|
||||||
"buildName": "Imported Anaconda",
|
|
||||||
"buildCode": "0pyttFolodDsyf5------1717--------05044j-03--2h--00.Iw18ZlA=.Aw18ZlA=.",
|
|
||||||
"buildText": "\n\n \t[Anaconda]\nS: 1F/F Pulse Laser\nS: 1F/F Pulse Laser\n\nBH: 1I Lightweight Alloy\nRB: 8E Power Plant\nTM: 7E Thrusters\nFH: 6E Frame Shift Drive\nEC: 5E Life Support\nPC: 8E Power Distributor\nSS: 8E Sensors\nFS: 5C Fuel Tank (Capacity: 32)\n\n7: 6E Cargo Rack (Capacity: 64)\n6: 5E Cargo Rack (Capacity: 32)\n6: 6E Shield Generator\n5: 4E Cargo Rack (Capacity: 16)\n4: 1E Basic Discovery Scanner\n2: 1E Cargo Rack (Capacity: 2)\n"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"shipId": "cobra_mk_iii",
|
|
||||||
"buildName": "Imported Cobra Mk III",
|
|
||||||
"buildCode": "0patcFeldd5sdf41712222503040202490f242h.Iw1-kA==.Aw1-kA==.",
|
|
||||||
"buildText": "[Cobra Mk III]\nM: 1F/F Pulse Laser\nM: 1G/G Burst Laser\nS: 1E/T Fragment Cannon\nS: 1G/T Multi-cannon\nU: 0I Point Defence\nU: 0A Shield Booster\n\nBH: 1I Lightweight Alloy\nRB: 4A Power Plant\nTM: 4C Thrusters\nFH: 4E Frame Shift Drive\nEC: 3D Life Support\nPC: 2A Power Distributor\nSS: 3D Sensors\nFS: 4C Fuel Tank (Capacity: 16)\n\n4: 3E Cargo Rack (Capacity: 8)\n4: 3E Cargo Rack (Capacity: 8)\n4: 4E Shield Generator\n2: 2C Auto Field-Maintenance Unit\n2: 1E Standard Docking Computer\n2: 1E Basic Discovery Scanner\n---\nShield: 112.29 MJ\nPower : 10.45 MW retracted (67%)\n 12.16 MW deployed (78%)\n 15.60 MW available\nCargo : 16 T\nFuel : 16 T\nMass : 235.5 T empty\n 267.5 T full\nRange : 10.69 LY unladen\n 10.05 LY laden\nPrice : 2,929,040 CR\nRe-Buy: 146,452 CR @ 95% insurance\n"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"shipId": "type_9_heavy",
|
|
||||||
"buildName": "Imported Type-9 Heavy",
|
|
||||||
"buildCode": "3pftsFklkdisif57e2k2f2h110001020306054j03022f01242i.Iw18eQ==.Aw18eQ==.",
|
|
||||||
"buildText": "[Type-9 Heavy]\nM: 2D/G Fragment Cannon\nM: 2I/F Mine Launcher\nM: 2B/FD Missile Rack\nS: 1I/FS Torpedo Pylon\nS: 1F/F Burst Laser\nU: 0I Chaff Launcher\nU: 0F Electronic Countermeasure\nU: 0I Heat Sink Launcher\nU: 0I Point Defence\n\nBH: 1I Mirrored Surface Composite\nRB: 5A Power Plant\nTM: 7D Thrusters\nFH: 6A Frame Shift Drive\nEC: 5A Life Support\nPC: 4D Power Distributor\nSS: 4D Sensors\nFS: 5C Fuel Tank (Capacity: 32)\n\n8: 7E Cargo Rack (Capacity: 128)\n7: 6E Cargo Rack (Capacity: 64)\n6: 6E Shield Generator\n5: 4E Cargo Rack (Capacity: 16)\n4: 3E Cargo Rack (Capacity: 8)\n4: 1C Advanced Discovery Scanner\n3: 2E Cargo Rack (Capacity: 4)\n3: 1E Standard Docking Computer\n2: 1C Detailed Surface Scanner\n"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"shipId": "vulture",
|
|
||||||
"buildName": "Imported Vulture",
|
|
||||||
"buildCode": "4patfFalddksif31e1e0e0j04044a0n532jf1.Iw19kA==.Aw19kA==.",
|
|
||||||
"buildText": "[Vulture]\nL: 3E/G Pulse Laser\nL: 3E/G Pulse Laser\nU: 0A Frame Shift Wake Scanner\nU: 0A Kill Warrant Scanner\nU: 0A Shield Booster\nU: 0A Shield Booster\n\nBH: 1I Reactive Surface Composite\nRB: 4A Power Plant\nTM: 5A Thrusters\nFH: 4A Frame Shift Drive\nEC: 3D Life Support\nPC: 5A Power Distributor\nSS: 4D Sensors\nFS: 3C Fuel Tank (Capacity: 8)\n\n5: 5A Shield Generator\n4: 4A Auto Field-Maintenance Unit\n2: 2A Shield Cell Bank\n1: 1A Fuel Scoop\n1: 1C Fuel Tank (Capacity: 2)"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
{
|
|
||||||
"type_6_transporter": {
|
|
||||||
"Cargo": "A0p0tdFal8d8s8f4-----04040303430101.Iw1/kA==.Aw1/kA==.",
|
|
||||||
"Miner": "A0p5tdFal8d8s8f42l2l---040403451q0101.Iw1/kA==.Aw1/kA==.",
|
|
||||||
"Hopper": "A0p0tdFal8d0s8f41717---030302024300-.Iw1/kA==.Aw1/kA==."
|
|
||||||
},
|
|
||||||
"type_7_transport": {
|
|
||||||
"Cargo": "A0p0tiFfliddsdf5--------0505040403480101.Iw18aQ==.Aw18aQ==.",
|
|
||||||
"Miner": "A0pdtiFflid8sdf5--2l2l----0505041v03450000.Iw18aQ==.Aw18aQ==."
|
|
||||||
},
|
|
||||||
"federal_dropship": {
|
|
||||||
"Cargo": "A0pdtiFflnddsif4-1717------05040448--020201.Iw18eQ==.Aw18eQ==."
|
|
||||||
},
|
|
||||||
"asp": {
|
|
||||||
"Miner": "A2pftfFflidfskf50s0s24242l2l---04054a1q02022o27.Iw18WQ==.Aw18WQ==."
|
|
||||||
},
|
|
||||||
"imperial_clipper": {
|
|
||||||
"Cargo": "A0p5tiFflndisnf4--0s0s----0605450302020101.Iw18aQ==.Aw18aQ==.",
|
|
||||||
"Dream": "A2pktkFflndpskf40v0v0s0s0404040n4k5n5d2b29292o-.AwRj4yWU1I==.CwBhCYy6YRigzLIA.",
|
|
||||||
"Current": "A0patkFflndfskf4----------------.AwRj4yWU1I==.CwBhCYy6YRigzLIA."
|
|
||||||
},
|
|
||||||
"type_9_heavy": {
|
|
||||||
"Current": "A0patsFklndnsif6---------0706054a0303020224.AwRj4yoo.EwBhEYy6dsg=."
|
|
||||||
},
|
|
||||||
"python": {
|
|
||||||
"Cargo": "A0patnFflidsssf5---------050505040448020201.Iw18eQ==.Aw18eQ==.",
|
|
||||||
"Miner": "A0pktkFflidpspf50v0v0v2m2m0404--050505Ce4a1v02022o.Iw18eQ==.IwBhBYy6dkCYg===.",
|
|
||||||
"Dream": "A2pptkFfliduspf50v0v0v27270404040m5n5n4f2d2d032t0201.Iw1+gDBxA===.EwBhEYy6e0WEA===.",
|
|
||||||
"Missile": "A0pttoFjljdystf52f2g2d2ePh----04044j03---002h.Iw18eQ==.Aw18eQ==."
|
|
||||||
},
|
|
||||||
"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=."
|
|
||||||
},
|
|
||||||
"diamondback_explorer": {
|
|
||||||
"Explorer": "A0p0tdFfldddsdf5---0202--320p432i2f-.AwRj4zTYg===.AwiMIyoo."
|
|
||||||
},
|
|
||||||
"vulture": {
|
|
||||||
"Bounty Hunter": "A3patcFalddksff31e1e0404-0l4a-5d27662j.AwRj4z2I.MwBhBYy6oJmAjLIA."
|
|
||||||
},
|
|
||||||
"fer_de_lance": {
|
|
||||||
"Attack": "A2pfthFalidpsff31r0s0s0s0s000404-04-4a-5d27-.Iw18aQ==.CwBhrSu8EZyA."
|
|
||||||
},
|
|
||||||
"eagle": {
|
|
||||||
"Figther": "A4p0t5F5l3d5s5f20p0p24-4053-2j-.Iw18kA==.Aw18kA==."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
{
|
|
||||||
"builds": {
|
|
||||||
"type_6_transporter": {
|
|
||||||
"Cargo": "02A4D4A2D2D2D4C-----04040303430101",
|
|
||||||
"Miner": "03A4D4A2D2D2D4C2l2l---040403451q0101",
|
|
||||||
"Hopper": "02A4D4A2D1A2D4C1717---030302024300-"
|
|
||||||
},
|
|
||||||
"type_7_transport": {
|
|
||||||
"Cargo": "02A5D5A4D3D3D5C--------0505040403480101",
|
|
||||||
"Miner": "04D5D5A4D2D3D5C--2l2l----0505041v03450000"
|
|
||||||
},
|
|
||||||
"federal_dropship": {
|
|
||||||
"Cargo": "04D5D5A5D3D4D4C-1717------05040448020201"
|
|
||||||
},
|
|
||||||
"asp": {
|
|
||||||
"Miner": "25A5A5A4D4A5A5C0s0s24242l2l---04054a1q02022o27"
|
|
||||||
},
|
|
||||||
"imperial_clipper": {
|
|
||||||
"Cargo": "03A5D5A5D4D5D4C--0s0s----0605450302020101",
|
|
||||||
"Dream": "26A6A5A5D6A5A4C0v0v0s0s0404040n4k5n5d2b29292o-.AwRj4yWU1I==.CwBhCYy6YRigzLIA",
|
|
||||||
"Current": "04A6A5A5D4A5A4C----------------.AwRj4yWU1I==.CwBhCYy6YRigzLIA"
|
|
||||||
},
|
|
||||||
"type_9_heavy": {
|
|
||||||
"Current": "04A7D6A5D5D4D6C---------0706054a0303020224.AwRj4yoo.EwBhEYy6dsg="
|
|
||||||
},
|
|
||||||
"python": {
|
|
||||||
"Cargo": "04A6D5A4D6D6D5C---------050505040448020201.Iw18eQ==.Aw18eQ==",
|
|
||||||
"Miner": "06A6A5A4D6A6A5C0v0v0v2m2m0404--050505Ce4a1v02022o.Iw18eQ==.IwBhBYy6dkCYg===",
|
|
||||||
"Dream": "27A6A5A4D7A6A5C0v0v0v27270404040m5n5n4f2d2d032t0201.Iw1+gDBxA===.EwBhEYy6e0WEA==="
|
|
||||||
},
|
|
||||||
"anaconda": {
|
|
||||||
"Dream": "48A7A6A5D8A8A5C2c0o0o0o1m1m0q0q0404040l0b0100004k5n5n112d2d040303326b.AwRj4yo5dig=.MwBhCYy6du3ARiA=",
|
|
||||||
"Cargo": "04A6D6A5D5D8D5C----------------0605050504040445030301.Iw18ZlA=.Aw18ZlA=",
|
|
||||||
"Current": "04A6D6A5D5A8D5C----------------0605050504040403034524.Iw18ZlA=.Aw18ZlA=",
|
|
||||||
"Explorer": "04A6D6A5D5A8D5C--------0202------f7050505040s372f2i4524.AwRj4yVKJthA.AwhMIyungRhEA==="
|
|
||||||
},
|
|
||||||
"diamondback_explorer": {
|
|
||||||
"Explorer": "02A4D5A3D3D3D5C---0202--320p432i2f.AwRj4zTI.AwiMIypI"
|
|
||||||
},
|
|
||||||
"vulture": {
|
|
||||||
"Bounty Hunter": "34A4C4A3D5A4A3C1e1e0404-0l4a5d27662j.AwRj4y2I.MwBhBYy6wJmAjLIA"
|
|
||||||
},
|
|
||||||
"fer_de_lance": {
|
|
||||||
"Attack": "25A5C4A4D6A4A3C1r0s0s0s0s000404-04-4a-5d27-.Iw18aQ==.CwBhrSu8EZyA"
|
|
||||||
},
|
|
||||||
"eagle": {
|
|
||||||
"Figther": "42A3A3A1D2A2A2C0p0p24-40532j.AwRj49iA.AwgsIkEZigmIA==="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
{
|
|
||||||
"builds": {
|
|
||||||
"type_6_transporter": {
|
|
||||||
"Cargo": "02A4D4A2D2D2D4C-----04040303430101",
|
|
||||||
"Miner": "03A4D4A2D2D2D4C2l2l---040403451q0101",
|
|
||||||
"Hopper": "02A4D4A2D1A2D4C1717---030302024300-"
|
|
||||||
},
|
|
||||||
"type_7_transport": {
|
|
||||||
"Cargo": "02A5D5A4D3D3D5C--------0505040403480101",
|
|
||||||
"Miner": "04D5D5A4D2D3D5C--2l2l----0505041v03450000"
|
|
||||||
},
|
|
||||||
"federal_dropship": {
|
|
||||||
"Cargo": "04D5D5A5D3D4D4C-1717------05040448020201"
|
|
||||||
},
|
|
||||||
"asp": {
|
|
||||||
"Miner": "25A5A5A4D4A5A5C0s0s24242l2l---04054a1q02022o27"
|
|
||||||
},
|
|
||||||
"cobra_mk_iii": {
|
|
||||||
"Example": "24A4A4A3D3A3A4C0s0s2d2d0m0445032b2o2753.AwRj4yKA.CwBhEYyrKhmMQ==="
|
|
||||||
},
|
|
||||||
"imperial_clipper": {
|
|
||||||
"Cargo": "03A5D5A5D4D5D4C--0s0s----0605450302020101",
|
|
||||||
"Multi-purpose": "26A4A5A5D6A5A4C0v0v272704090j0h064f2c0302020101",
|
|
||||||
"Current": "05A6D5A5D6A5A4C0v0v27270404050n4m05035d29292o01.AwRj4yrI.AwhMIyuBGNiA",
|
|
||||||
"Dream": "26A6A5A5D6A5A4C0v0v0s0s04040c0n064f5d2b02022o0d.AwRj49UlmI==.AwiMIyuo"
|
|
||||||
},
|
|
||||||
"type_9_heavy": {
|
|
||||||
"Cargo": "04A6D6A5D4D4D5C---------07064f040303010201.AwRj4yoo.EwBhEYy6dsg="
|
|
||||||
},
|
|
||||||
"python": {
|
|
||||||
"Cargo": "04A6D5A4D6D6D5C---------050505044a03020201",
|
|
||||||
"Miner": "04A6D5A4D6D6D5C---2m2m----050505044d1v02022o"
|
|
||||||
},
|
|
||||||
"anaconda": {
|
|
||||||
"Dream": "48A6A6A5A8A8A5C2c0o0o0o1m1m0q0q0404040l0b0100034k5n05050404040303326b.AwRj4yo5dig=.MwBhEYy6duwEziA=",
|
|
||||||
"Cargo": "03A7D6A5D4D8D5C----------------060505054d040403030301.AwRj4yuqg===.Aw18ZlA=",
|
|
||||||
"Modified": "0pyttFolodDsyf5------1717--------05044j-03----2h00.Iw18ZlA=.Aw18ZlA=.H4sIAAAAAAAAA2MUe8HMwPD-PwDDhxeuCAAAAA=="
|
|
||||||
},
|
|
||||||
"diamondback_explorer": {
|
|
||||||
"Explorer": "02A4D5A3D3D3D5C-------320p432i2f.AwRj4zTI.AwiMIypI"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"comparisons": {
|
|
||||||
"Test": {
|
|
||||||
"facets": [ 9, 6, 4, 1, 3, 2 ],
|
|
||||||
"builds": [
|
|
||||||
{
|
|
||||||
"shipId": "anaconda",
|
|
||||||
"buildName": "Dream"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"shipId": "asp",
|
|
||||||
"buildName": "Miner"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"shipId": "diamondback_explorer",
|
|
||||||
"buildName": "Explorer"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"insurance": "Beta",
|
|
||||||
"discounts": [
|
|
||||||
1,
|
|
||||||
1
|
|
||||||
]
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,90 +0,0 @@
|
|||||||
import Ship from '../src/app/shipyard/Ship';
|
|
||||||
import { Ships } from 'coriolis-data/dist';
|
|
||||||
import * as ModuleUtils from '../src/app/shipyard/ModuleUtils';
|
|
||||||
|
|
||||||
describe("Agility", function() {
|
|
||||||
|
|
||||||
it("correctly calculates speed", function() {
|
|
||||||
let agilityData = require('./fixtures/agility-data');
|
|
||||||
|
|
||||||
for (let shipId in agilityData) {
|
|
||||||
for (let thrusterId in agilityData[shipId]) {
|
|
||||||
const thrusterData = agilityData[shipId][thrusterId];
|
|
||||||
let shipData = Ships[shipId];
|
|
||||||
let ship = new Ship(shipId, shipData.properties, shipData.slots);
|
|
||||||
ship.buildWith(shipData.defaults);
|
|
||||||
ship.use(ship.standard[1], ModuleUtils.findModule('t', thrusterId));
|
|
||||||
|
|
||||||
expect(Math.round(ship.topSpeed)).toBe(thrusterData.speed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it("correctly calculates boost", function() {
|
|
||||||
let agilityData = require('./fixtures/agility-data');
|
|
||||||
|
|
||||||
for (let shipId in agilityData) {
|
|
||||||
for (let thrusterId in agilityData[shipId]) {
|
|
||||||
const thrusterData = agilityData[shipId][thrusterId];
|
|
||||||
let shipData = Ships[shipId];
|
|
||||||
let ship = new Ship(shipId, shipData.properties, shipData.slots);
|
|
||||||
ship.buildWith(shipData.defaults);
|
|
||||||
// Turn off internals to ensure we have enough power to boost
|
|
||||||
for (let internal in ship.internal) {
|
|
||||||
ship.internal[internal].enabled = 0;
|
|
||||||
}
|
|
||||||
ship.use(ship.standard[1], ModuleUtils.findModule('t', thrusterId));
|
|
||||||
|
|
||||||
expect(Math.round(ship.topBoost)).toBe(thrusterData.boost);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it("correctly calculates pitch", function() {
|
|
||||||
let agilityData = require('./fixtures/agility-data');
|
|
||||||
|
|
||||||
for (let shipId in agilityData) {
|
|
||||||
for (let thrusterId in agilityData[shipId]) {
|
|
||||||
const thrusterData = agilityData[shipId][thrusterId];
|
|
||||||
let shipData = Ships[shipId];
|
|
||||||
let ship = new Ship(shipId, shipData.properties, shipData.slots);
|
|
||||||
ship.buildWith(shipData.defaults);
|
|
||||||
ship.use(ship.standard[1], ModuleUtils.findModule('t', thrusterId));
|
|
||||||
|
|
||||||
expect(Math.round(ship.pitches[4] * 100) / 100).toBeCloseTo(thrusterData.pitch, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it("correctly calculates roll", function() {
|
|
||||||
let agilityData = require('./fixtures/agility-data');
|
|
||||||
|
|
||||||
for (let shipId in agilityData) {
|
|
||||||
for (let thrusterId in agilityData[shipId]) {
|
|
||||||
const thrusterData = agilityData[shipId][thrusterId];
|
|
||||||
let shipData = Ships[shipId];
|
|
||||||
let ship = new Ship(shipId, shipData.properties, shipData.slots);
|
|
||||||
ship.buildWith(shipData.defaults);
|
|
||||||
ship.use(ship.standard[1], ModuleUtils.findModule('t', thrusterId));
|
|
||||||
|
|
||||||
expect(Math.round(ship.rolls[4] * 100) / 100).toBeCloseTo(thrusterData.roll, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it("correctly calculates yaw", function() {
|
|
||||||
let agilityData = require('./fixtures/agility-data');
|
|
||||||
|
|
||||||
for (let shipId in agilityData) {
|
|
||||||
for (let thrusterId in agilityData[shipId]) {
|
|
||||||
const thrusterData = agilityData[shipId][thrusterId];
|
|
||||||
let shipData = Ships[shipId];
|
|
||||||
let ship = new Ship(shipId, shipData.properties, shipData.slots);
|
|
||||||
ship.buildWith(shipData.defaults);
|
|
||||||
ship.use(ship.standard[1], ModuleUtils.findModule('t', thrusterId));
|
|
||||||
|
|
||||||
expect(Math.round(ship.yaws[4] * 100) / 100).toBeCloseTo(thrusterData.yaw, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,327 +0,0 @@
|
|||||||
jest.unmock('../src/app/stores/Persist');
|
|
||||||
jest.unmock('../src/app/components/TranslatedComponent');
|
|
||||||
jest.unmock('../src/app/components/ModalImport');
|
|
||||||
jest.unmock('prop-types');
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import ReactDOM from 'react-dom';
|
|
||||||
import TU from 'react-testutils-additions';
|
|
||||||
import Utils from './testUtils';
|
|
||||||
import { getLanguage } from '../src/app/i18n/Language';
|
|
||||||
|
|
||||||
describe('Import Modal', function() {
|
|
||||||
|
|
||||||
let MockRouter = require('../src/app/Router').default;
|
|
||||||
const Persist = require('../src/app/stores/Persist').default;
|
|
||||||
const ModalImport = require('../src/app/components/ModalImport').default;
|
|
||||||
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()
|
|
||||||
};
|
|
||||||
|
|
||||||
let modal, render, ContextProvider = Utils.createContextProvider(mockContext);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear saved builds, and reset React DOM
|
|
||||||
*/
|
|
||||||
function reset() {
|
|
||||||
MockRouter.go.mockClear();
|
|
||||||
Persist.deleteAll();
|
|
||||||
render = TU.renderIntoDocument(<ContextProvider><ModalImport /></ContextProvider>);
|
|
||||||
modal = TU.findRenderedComponentWithType(render, ModalImport);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simulate user import text entry / paste
|
|
||||||
* @param {string} text Import text / raw data
|
|
||||||
*/
|
|
||||||
function pasteText(text) {
|
|
||||||
let textarea = TU.findRenderedDOMComponentWithTag(render, 'textarea');
|
|
||||||
TU.Simulate.change(textarea, { target: { value: text } });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simulate click on Proceed button
|
|
||||||
*/
|
|
||||||
function clickProceed() {
|
|
||||||
let proceedButton = TU.findRenderedDOMComponentWithId(render, 'proceed');
|
|
||||||
TU.Simulate.click(proceedButton);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simulate click on Import button
|
|
||||||
*/
|
|
||||||
function clickImport() {
|
|
||||||
let importButton = TU.findRenderedDOMComponentWithId(render, 'import');
|
|
||||||
TU.Simulate.click(importButton);
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('Import Backup', function() {
|
|
||||||
|
|
||||||
beforeEach(reset);
|
|
||||||
|
|
||||||
it('imports a valid backup', function() {
|
|
||||||
let importData = require('./fixtures/valid-backup');
|
|
||||||
let importString = JSON.stringify(importData);
|
|
||||||
|
|
||||||
expect(modal.state.importValid).toEqual(false);
|
|
||||||
expect(modal.state.errorMsg).toEqual(null);
|
|
||||||
pasteText(importString);
|
|
||||||
expect(modal.state.importValid).toBe(true);
|
|
||||||
expect(modal.state.errorMsg).toEqual(null);
|
|
||||||
expect(modal.state.builds).toEqual(importData.builds);
|
|
||||||
expect(modal.state.comparisons).toEqual(importData.comparisons);
|
|
||||||
expect(modal.state.shipDiscount).toEqual(importData.discounts[0]);
|
|
||||||
expect(modal.state.moduleDiscount).toEqual(importData.discounts[1]);
|
|
||||||
expect(modal.state.insurance).toBe(importData.insurance.toLowerCase());
|
|
||||||
clickProceed();
|
|
||||||
expect(modal.state.processed).toBe(true);
|
|
||||||
expect(modal.state.errorMsg).toEqual(null);
|
|
||||||
clickImport();
|
|
||||||
expect(Persist.getBuilds()).toEqual(importData.builds);
|
|
||||||
expect(Persist.getComparisons()).toEqual(importData.comparisons);
|
|
||||||
expect(Persist.getInsurance()).toEqual(importData.insurance.toLowerCase());
|
|
||||||
expect(Persist.getShipDiscount()).toEqual(importData.discounts[0]);
|
|
||||||
expect(Persist.getModuleDiscount()).toEqual(importData.discounts[1]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('imports an old valid backup', function() {
|
|
||||||
const importData = require('./fixtures/old-valid-export');
|
|
||||||
const importStr = JSON.stringify(importData);
|
|
||||||
|
|
||||||
pasteText(importStr);
|
|
||||||
expect(modal.state.builds).toEqual(importData.builds);
|
|
||||||
expect(modal.state.importValid).toBe(true);
|
|
||||||
expect(modal.state.errorMsg).toEqual(null);
|
|
||||||
clickProceed();
|
|
||||||
expect(modal.state.processed).toBeTruthy();
|
|
||||||
clickImport();
|
|
||||||
expect(Persist.getBuilds()).toEqual(importData.builds);
|
|
||||||
});
|
|
||||||
|
|
||||||
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
|
|
||||||
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'));
|
|
||||||
expect(modal.state.importValid).toBeFalsy();
|
|
||||||
expect(modal.state.errorMsg).toEqual('"invalid_ship" is not a valid Ship Id!');
|
|
||||||
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!');
|
|
||||||
pasteText(JSON.stringify(invalidImportData));
|
|
||||||
expect(modal.state.importValid).toBeFalsy();
|
|
||||||
expect(modal.state.errorMsg).toEqual('asp build "Miner" data is missing!');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Import Detailed V3 Build', function() {
|
|
||||||
|
|
||||||
beforeEach(reset);
|
|
||||||
|
|
||||||
it('imports a valid v3 build', function() {
|
|
||||||
const importData = require('./fixtures/anaconda-test-detailed-export-v3');
|
|
||||||
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/anaconda?code=A4putkFklkdzsuf52c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04---0303326b.AwRj4zNLaA%3D%3D.CwBhCYzBGW9qCTSqq5xA.&bn=Test%20My%20Ship');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('catches an invalid build', function() {
|
|
||||||
const importData = require('./fixtures/anaconda-test-detailed-export-v3');
|
|
||||||
pasteText(JSON.stringify(importData).replace('references', 'refs'));
|
|
||||||
|
|
||||||
expect(modal.state.importValid).toBeFalsy();
|
|
||||||
expect(modal.state.errorMsg).toEqual('Anaconda Build "Test My Ship": Invalid data');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Import Detailed V4 Build', function() {
|
|
||||||
|
|
||||||
beforeEach(reset);
|
|
||||||
|
|
||||||
it('imports a valid v4 build', function() {
|
|
||||||
const importData = require('./fixtures/anaconda-test-detailed-export-v4');
|
|
||||||
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/anaconda?code=A4putkFklkdzsuf52c0o0o0o1m1m0q0q0404-0l0b0100034k5n052d04---0303326b.AwRj4zNLaA%3D%3D.CwBhCYzBGW9qCTSqq5xA.H4sIAAAAAAAAA2MUe8HMwPD%2FPwMAAGvB0AkAAAA%3D&bn=Test%20My%20Ship');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Import Detailed Engineered V4 Build', function() {
|
|
||||||
|
|
||||||
beforeEach(reset);
|
|
||||||
|
|
||||||
it('imports a valid v4 build', function() {
|
|
||||||
const importData = require('./fixtures/asp-test-detailed-export-v4');
|
|
||||||
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/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');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('imports a valid v4 build with modifications', function() {
|
|
||||||
const importData = require('./fixtures/courier-test-detailed-export-v4');
|
|
||||||
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/imperial_courier?code=A0patzF5l0das8f31a1a270202000e402t0101-2f.AwRj4zKA.CwRgDBldLiQ%3D.H4sIAAAAAAAAA12OPUvDYBSFT1OTfkRJjUkbbC3Yj8mlODgUISAtdOlety5ODv0Vgji7O7kJ%2FgzBQX%2BEY7Gg0NKhfY%2FnHQLFDBdynufe9%2BRMCmCb06g29oCgacjiRx6gY6oWKUT8UgLaszqQfHmSnpVFN1uSeXNsJVcj%2FA2EHlZkspIUpUc6UjTXGT85qwHuSEuVc%2F16r99kDQeSSjvSbSjpyUpNK10uJJ3aYqk6smwm1lQ9bOxw71TMm8VanEqq9JW1r3Qo%2BREOLnQHvbWmb7rZIu5VLIyGQGOukPv%2F0WQk5LeEAjPOUDwtAP6bShy2HKAz0HPO%2B5KsP25I79O2I7LvD%2Bz4Il1XAQAA&bn=Multi-purpose%20Imperial%20Courier');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Import Detaild Builds Array', function() {
|
|
||||||
|
|
||||||
beforeEach(reset);
|
|
||||||
|
|
||||||
it('imports all builds', function() {
|
|
||||||
const importData = require('./fixtures/valid-detailed-export');
|
|
||||||
const expectedBuilds = require('./fixtures/expected-builds');
|
|
||||||
|
|
||||||
pasteText(JSON.stringify(importData));
|
|
||||||
expect(modal.state.importValid).toBeTruthy();
|
|
||||||
expect(modal.state.errorMsg).toEqual(null);
|
|
||||||
clickProceed();
|
|
||||||
expect(modal.state.processed).toBeTruthy();
|
|
||||||
clickImport();
|
|
||||||
|
|
||||||
let builds = Persist.getBuilds();
|
|
||||||
|
|
||||||
for (let s in builds) {
|
|
||||||
for (let b in builds[s]) {
|
|
||||||
expect(builds[s][b]).toEqual(expectedBuilds[s][b]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Import Companion API Build', function() {
|
|
||||||
|
|
||||||
beforeEach(reset);
|
|
||||||
|
|
||||||
it('imports a valid companion API build', function() {
|
|
||||||
const importData = require('./fixtures/companion-api-import-1');
|
|
||||||
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/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');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('imports a valid companion API build', function() {
|
|
||||||
const importData = require('./fixtures/companion-api-import-2');
|
|
||||||
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/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');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('imports a valid companion API build', function() {
|
|
||||||
const importData = require('./fixtures/companion-api-import-3');
|
|
||||||
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/type_7_transport?code=A0patfFflidasdf5----0404040005050504044d2402.AwRj4yrI.CwRgDBlVK7EiA%3D%3D%3D.&bn=Imported%20Type-7%20Transporter');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('imports a valid companion API build', function() {
|
|
||||||
const importData = require('./fixtures/companion-api-import-4');
|
|
||||||
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/cobra_mk_iii?code=A0p0tdFaldd3sdf4------34---2f2i.AwRj4yKA.CwRgDMYExrezBUg%3D.&bn=Imported%20Cobra%20Mk%20III');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Import E:D Shipyard Builds', function() {
|
|
||||||
|
|
||||||
// it('imports a valid build', function() {
|
|
||||||
// const imports = require('./fixtures/ed-shipyard-import-valid');
|
|
||||||
//
|
|
||||||
// for (let i = 0; i < imports.length; i++ ) {
|
|
||||||
// reset();
|
|
||||||
// let fixture = imports[i];
|
|
||||||
// pasteText(fixture.buildText);
|
|
||||||
// expect(modal.state.importValid).toBeTruthy();
|
|
||||||
// expect(modal.state.errorMsg).toEqual(null);
|
|
||||||
// clickProceed();
|
|
||||||
// expect(MockRouter.go.mock.calls.length).toBe(1);
|
|
||||||
// expect(MockRouter.go.mock.calls[0][0]).toBe('/outfit/' + fixture.shipId + '?code=' + encodeURIComponent(fixture.buildCode) + '&bn=' + encodeURIComponent(fixture.buildName));
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
it('catches invalid builds', function() {
|
|
||||||
const imports = require('./fixtures/ed-shipyard-import-invalid');
|
|
||||||
|
|
||||||
for (let i = 0; i < imports.length; i++ ) {
|
|
||||||
reset();
|
|
||||||
pasteText(imports[i].buildText);
|
|
||||||
expect(modal.state.importValid).toBeFalsy();
|
|
||||||
expect(modal.state.errorMsg).toEqual(imports[i].errorMsg);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Imports from a Comparison', function() {
|
|
||||||
|
|
||||||
it('imports a valid comparison', function() {
|
|
||||||
const importBuilds = require('./fixtures/valid-backup').builds;
|
|
||||||
Persist.deleteAll();
|
|
||||||
render = TU.renderIntoDocument(<ContextProvider><ModalImport builds={importBuilds} /></ContextProvider>);
|
|
||||||
modal = TU.findRenderedComponentWithType(render, ModalImport);
|
|
||||||
|
|
||||||
expect(modal.state.processed).toBe(true);
|
|
||||||
expect(modal.state.errorMsg).toEqual(null);
|
|
||||||
clickImport();
|
|
||||||
expect(Persist.getBuilds()).toEqual(importBuilds);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
@@ -1,143 +0,0 @@
|
|||||||
jest.unmock('../src/app/stores/Persist');
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import ReactDOM from 'react-dom';
|
|
||||||
import TU from 'react-testutils-additions';
|
|
||||||
|
|
||||||
let origAddEventListener = window.addEventListener;
|
|
||||||
let storageListener;
|
|
||||||
let ls = {};
|
|
||||||
|
|
||||||
// Implment mock localStorage
|
|
||||||
let localStorage = {
|
|
||||||
getItem: function(key) {
|
|
||||||
return ls[key];
|
|
||||||
},
|
|
||||||
setItem: function(key, value) {
|
|
||||||
ls[key] = value;
|
|
||||||
},
|
|
||||||
removeItem: function(key) {
|
|
||||||
delete ls[key];
|
|
||||||
},
|
|
||||||
clear: function() {
|
|
||||||
ls = {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener = function(eventName, listener) {
|
|
||||||
|
|
||||||
if(eventName == 'storage') {
|
|
||||||
storageListener = listener; // Keep track of latest storage listener
|
|
||||||
} else {
|
|
||||||
origAddEventListener.apply(arguments);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('Persist', function() {
|
|
||||||
|
|
||||||
const Persist = require('../src/app/stores/Persist').Persist;
|
|
||||||
|
|
||||||
describe('Multi tab/window', function() {
|
|
||||||
it("syncs builds", function() {
|
|
||||||
window.localStorage = localStorage;
|
|
||||||
ls = {};
|
|
||||||
let p = new Persist();
|
|
||||||
let newBuilds = {
|
|
||||||
anaconda: { test: '1234' }
|
|
||||||
};
|
|
||||||
|
|
||||||
storageListener({ key: 'builds', newValue: JSON.stringify(newBuilds) });
|
|
||||||
expect(p.getBuild('anaconda', 'test')).toBe('1234');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('General and Settings', function() {
|
|
||||||
it("has defaults", function() {
|
|
||||||
window.localStorage = localStorage;
|
|
||||||
ls = {};
|
|
||||||
let p = new Persist();
|
|
||||||
expect(p.getLangCode()).toBe('en');
|
|
||||||
expect(p.showTooltips()).toBe(true);
|
|
||||||
expect(p.getInsurance()).toBe('standard');
|
|
||||||
expect(p.getShipDiscount()).toBe(0);
|
|
||||||
expect(p.getModuleDiscount()).toBe(0);
|
|
||||||
expect(p.getSizeRatio()).toBe(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("loads from localStorage correctly", function() {
|
|
||||||
window.localStorage = localStorage;
|
|
||||||
let savedData = require('./fixtures/valid-backup');
|
|
||||||
ls = {};
|
|
||||||
ls.builds = JSON.stringify(savedData.builds);
|
|
||||||
ls.NG_TRANSLATE_LANG_KEY = 'de';
|
|
||||||
ls.insurance = 'Standard';
|
|
||||||
ls.shipDiscount = 0.25;
|
|
||||||
ls.moduleDiscount = 0.15;
|
|
||||||
let p = new Persist();
|
|
||||||
|
|
||||||
expect(p.getInsurance()).toBe('standard');
|
|
||||||
expect(p.getShipDiscount()).toBe(0.25);
|
|
||||||
expect(p.getModuleDiscount()).toBe(0.15);
|
|
||||||
expect(p.getLangCode()).toEqual('de');
|
|
||||||
expect(p.getBuilds('anaconda')).toEqual(savedData.builds.anaconda);
|
|
||||||
expect(p.getBuilds('python')).toEqual(savedData.builds.python);
|
|
||||||
expect(p.getBuildsNamesFor('imperial_clipper')).toEqual(['Cargo', 'Current', 'Dream', 'Multi-purpose']);
|
|
||||||
expect(p.getBuild('type_7_transport', 'Cargo')).toEqual('02A5D5A4D3D3D5C--------0505040403480101');
|
|
||||||
});
|
|
||||||
|
|
||||||
it("uses defaults from a corrupted localStorage", function() {
|
|
||||||
window.localStorage = localStorage;
|
|
||||||
ls = {};
|
|
||||||
ls.builds = "not valid json";
|
|
||||||
ls.comparisons = "1, 3, 4";
|
|
||||||
ls.insurance = 'this insurance does not exist';
|
|
||||||
ls.shipDiscount = 'this is not a number';
|
|
||||||
ls.moduleDiscount = 10; // Way to big
|
|
||||||
|
|
||||||
let p = new Persist();
|
|
||||||
expect(p.getLangCode()).toBe('en');
|
|
||||||
expect(p.showTooltips()).toBe(true);
|
|
||||||
expect(p.getInsurance()).toBe('standard');
|
|
||||||
expect(p.getShipDiscount()).toBe(0);
|
|
||||||
expect(p.getModuleDiscount()).toBe(0);
|
|
||||||
expect(p.getBuilds()).toEqual({});
|
|
||||||
expect(p.getComparisons()).toEqual({});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("works without localStorage", function() {
|
|
||||||
window.localStorage = null;
|
|
||||||
let p = new Persist();
|
|
||||||
expect(p.getLangCode()).toBe('en');
|
|
||||||
expect(p.showTooltips()).toBe(true);
|
|
||||||
expect(p.getInsurance()).toBe('standard');
|
|
||||||
expect(p.getShipDiscount()).toBe(0);
|
|
||||||
expect(p.getModuleDiscount()).toBe(0);
|
|
||||||
expect(p.getSizeRatio()).toBe(1);
|
|
||||||
|
|
||||||
p.saveBuild('anaconda', 'test', '12345');
|
|
||||||
expect(p.getBuild('anaconda', 'test')).toBe('12345');
|
|
||||||
|
|
||||||
p.deleteBuild('anaconda', 'test');
|
|
||||||
expect(p.hasBuilds()).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("generates the backup", function() {
|
|
||||||
window.localStorage = localStorage;
|
|
||||||
let savedData = require('./fixtures/valid-backup');
|
|
||||||
ls = {};
|
|
||||||
ls.builds = JSON.stringify(savedData.builds);
|
|
||||||
ls.insurance = 'Beta';
|
|
||||||
ls.shipDiscount = 0.25;
|
|
||||||
ls.moduleDiscount = 0.15;
|
|
||||||
|
|
||||||
let p = new Persist();
|
|
||||||
let backup = p.getAll();
|
|
||||||
|
|
||||||
expect(backup.insurance).toBe('beta');
|
|
||||||
expect(backup.shipDiscount).toBe(0.25);
|
|
||||||
expect(backup.moduleDiscount).toBe(0.15);
|
|
||||||
expect(backup.builds).toEqual(savedData.builds);
|
|
||||||
expect(backup.comparisons).toEqual({});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
})
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
import Ship from '../src/app/shipyard/Ship';
|
|
||||||
import { Ships } from 'coriolis-data/dist';
|
|
||||||
import * as Serializer from '../src/app/shipyard/Serializer';
|
|
||||||
import jsen from 'jsen';
|
|
||||||
|
|
||||||
describe("Serializer", function() {
|
|
||||||
const anacondaTestExport = require.requireActual('./fixtures/anaconda-test-detailed-export-v4');
|
|
||||||
const code = anacondaTestExport.references[0].code;
|
|
||||||
const anaconda = Ships.anaconda;
|
|
||||||
const validate = jsen(require('../src/schemas/ship-loadout/4'));
|
|
||||||
|
|
||||||
describe("To Detailed Build", function() {
|
|
||||||
let testBuild = new Ship('anaconda', anaconda.properties, anaconda.slots).buildFrom(code);
|
|
||||||
let exportData = Serializer.toDetailedBuild('Test My Ship', testBuild);
|
|
||||||
|
|
||||||
it("conforms to the v4 ship-loadout schema", function() {
|
|
||||||
expect(validate(exportData)).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("contains the correct components and stats", function() {
|
|
||||||
expect(exportData.components).toEqual(anacondaTestExport.components);
|
|
||||||
expect(exportData.stats).toEqual(anacondaTestExport.stats);
|
|
||||||
expect(exportData.ship).toEqual(anacondaTestExport.ship);
|
|
||||||
expect(exportData.name).toEqual(anacondaTestExport.name);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("Export Detailed Builds", function() {
|
|
||||||
const expectedExport = require('./fixtures/valid-detailed-export');
|
|
||||||
const builds = require('./fixtures/expected-builds');
|
|
||||||
const exportData = Serializer.toDetailedExport(builds);
|
|
||||||
|
|
||||||
it("conforms to the v4 ship-loadout schema", function() {
|
|
||||||
expect(exportData instanceof Array).toBe(true);
|
|
||||||
|
|
||||||
for (let detailedBuild of exportData) {
|
|
||||||
expect(validate(detailedBuild)).toBe(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("From Detailed Build", function() {
|
|
||||||
|
|
||||||
it("builds the ship correctly", function() {
|
|
||||||
let testBuildA = new Ship('anaconda', anaconda.properties, anaconda.slots);
|
|
||||||
testBuildA.buildFrom(code);
|
|
||||||
let testBuildB = Serializer.fromDetailedBuild(anacondaTestExport);
|
|
||||||
|
|
||||||
for(var p in testBuildB) {
|
|
||||||
if (p == 'availCS') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
expect(testBuildB[p]).toEqual(testBuildA[p], p + ' does not match');
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
||||||
@@ -1,156 +0,0 @@
|
|||||||
import Ship from '../src/app/shipyard/Ship';
|
|
||||||
import { Ships } from 'coriolis-data/dist';
|
|
||||||
import * as ModuleUtils from '../src/app/shipyard/ModuleUtils';
|
|
||||||
|
|
||||||
describe("Ship", function() {
|
|
||||||
|
|
||||||
it("can build all ships", function() {
|
|
||||||
for (let s in Ships) {
|
|
||||||
let shipData = Ships[s];
|
|
||||||
let ship = new Ship(s, shipData.properties, shipData.slots);
|
|
||||||
|
|
||||||
for (let p in shipData.properties) {
|
|
||||||
expect(ship[p]).toEqual(shipData.properties[p], s + ' property [' + p + '] does not match when built');
|
|
||||||
}
|
|
||||||
|
|
||||||
ship.buildWith(shipData.defaults);
|
|
||||||
|
|
||||||
expect(ship.totalCost).toEqual(shipData.retailCost, s + ' retail cost does not match default build cost');
|
|
||||||
expect(ship.cargoCapacity).toBeDefined();
|
|
||||||
expect(ship.priorityBands[0].retracted).toBeGreaterThan(0, s + ' priorityBands');
|
|
||||||
expect(ship.powerAvailable).toBeGreaterThan(0, s + ' powerAvailable');
|
|
||||||
expect(ship.unladenRange).toBeGreaterThan(0, s + ' unladenRange');
|
|
||||||
expect(ship.ladenRange).toBeGreaterThan(0, s + ' ladenRange');
|
|
||||||
expect(ship.fuelCapacity).toBeGreaterThan(0, s + ' fuelCapacity');
|
|
||||||
expect(ship.unladenFastestRange).toBeGreaterThan(0, s + ' unladenFastestRange');
|
|
||||||
expect(ship.ladenFastestRange).toBeGreaterThan(0, s + ' ladenFastestRange');
|
|
||||||
expect(ship.shield).toBeGreaterThan(0, s + ' shield');
|
|
||||||
expect(ship.armour).toBeGreaterThan(0, s + ' armour');
|
|
||||||
expect(ship.topSpeed).toBeGreaterThan(0, s + ' topSpeed');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it("resets and rebuilds properly", function() {
|
|
||||||
var id = 'cobra_mk_iii';
|
|
||||||
var cobra = Ships[id];
|
|
||||||
var shipA = new Ship(id, cobra.properties, cobra.slots);
|
|
||||||
var shipB = new Ship(id, cobra.properties, cobra.slots);
|
|
||||||
var testShip = new Ship(id, cobra.properties, cobra.slots);
|
|
||||||
|
|
||||||
var buildA = cobra.defaults;
|
|
||||||
var buildB = {
|
|
||||||
standard:['4A', '4A', '4A', '3D', '3A', '3A', '4C'],
|
|
||||||
hardpoints: ['0s', '0s', '2d', '2d', 0, '04'],
|
|
||||||
internal: ['45', '03', '2b', '2o', '27', '53']
|
|
||||||
};
|
|
||||||
|
|
||||||
shipA.buildWith(buildA); // Build A
|
|
||||||
shipB.buildWith(buildB);// Build B
|
|
||||||
testShip.buildWith(buildA);
|
|
||||||
|
|
||||||
for(var p in testShip) {
|
|
||||||
if (p == 'availCS') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
expect(testShip[p]).toEqual(shipA[p], p + ' does not match');
|
|
||||||
}
|
|
||||||
|
|
||||||
testShip.buildWith(buildB);
|
|
||||||
|
|
||||||
for(var p in testShip) {
|
|
||||||
if (p == 'availCS') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
expect(testShip[p]).toEqual(shipB[p], p + ' does not match');
|
|
||||||
}
|
|
||||||
|
|
||||||
testShip.buildWith(buildA);
|
|
||||||
|
|
||||||
for(var p in testShip) {
|
|
||||||
if (p == 'availCS') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
expect(testShip[p]).toEqual(shipA[p], p + ' does not match');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it("discounts hull and components properly", function() {
|
|
||||||
var id = 'cobra_mk_iii';
|
|
||||||
var cobra = Ships[id];
|
|
||||||
var testShip = new Ship(id, cobra.properties, cobra.slots);
|
|
||||||
testShip.buildWith(cobra.defaults);
|
|
||||||
|
|
||||||
var originalHullCost = testShip.hullCost;
|
|
||||||
var originalTotalCost = testShip.totalCost;
|
|
||||||
var discount = 0.1;
|
|
||||||
|
|
||||||
expect(testShip.m.discountedCost).toEqual(originalHullCost, 'Hull cost does not match');
|
|
||||||
|
|
||||||
testShip.applyDiscounts(discount, discount);
|
|
||||||
|
|
||||||
// Floating point errors cause miniscule decimal places which are handled in the app by rounding/formatting
|
|
||||||
|
|
||||||
expect(Math.floor(testShip.m.discountedCost)).toEqual(Math.floor(originalHullCost * (1 - discount)), 'Discounted Hull cost does not match');
|
|
||||||
expect(Math.floor(testShip.totalCost)).toEqual(Math.floor(originalTotalCost * (1 - discount)), 'Discounted Total cost does not match');
|
|
||||||
|
|
||||||
testShip.applyDiscounts(0, 0); // No discount, 100% of cost
|
|
||||||
|
|
||||||
expect(testShip.m.discountedCost).toEqual(originalHullCost, 'Hull cost does not match');
|
|
||||||
expect(testShip.totalCost).toEqual(originalTotalCost, 'Total cost does not match');
|
|
||||||
|
|
||||||
testShip.applyDiscounts(discount, 0); // Only discount hull
|
|
||||||
|
|
||||||
expect(Math.floor(testShip.m.discountedCost)).toEqual(Math.round(originalHullCost * (1 - discount)), 'Discounted Hull cost does not match');
|
|
||||||
expect(testShip.totalCost).toEqual(originalTotalCost - originalHullCost + testShip.m.discountedCost, 'Total cost does not match');
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
it("enforces a single shield generator", function() {
|
|
||||||
var id = 'anaconda';
|
|
||||||
var anacondaData = Ships[id];
|
|
||||||
var anaconda = new Ship(id, anacondaData.properties, anacondaData.slots);
|
|
||||||
anaconda.buildWith(anacondaData.defaults);
|
|
||||||
|
|
||||||
expect(anaconda.internal[2].m.grp).toEqual('sg', 'Anaconda default shield generator slot');
|
|
||||||
|
|
||||||
anaconda.use(anaconda.internal[1], ModuleUtils.internal('4j')); // 6E Shield Generator
|
|
||||||
|
|
||||||
expect(anaconda.internal[2].m).toEqual(null, 'Anaconda default shield generator slot is empty');
|
|
||||||
expect(anaconda.internal[1].m.id).toEqual('4j', 'Slot 1 should have SG 4j in it');
|
|
||||||
expect(anaconda.internal[1].m.grp).toEqual('sg','Slot 1 should have SG 4j in it');
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
it("enforces a single shield fuel scoop", function() {
|
|
||||||
var id = 'anaconda';
|
|
||||||
var anacondaData = Ships[id];
|
|
||||||
var anaconda = new Ship(id, anacondaData.properties, anacondaData.slots);
|
|
||||||
anaconda.buildWith(anacondaData.defaults);
|
|
||||||
|
|
||||||
anaconda.use(anaconda.internal[4], ModuleUtils.internal('32')); // 4A Fuel Scoop
|
|
||||||
expect(anaconda.internal[4].m.grp).toEqual('fs', 'Anaconda fuel scoop slot');
|
|
||||||
|
|
||||||
anaconda.use(anaconda.internal[3], ModuleUtils.internal('32'));
|
|
||||||
|
|
||||||
expect(anaconda.internal[4].m).toEqual(null, 'Anaconda original fuel scoop slot is empty');
|
|
||||||
expect(anaconda.internal[3].m.id).toEqual('32', 'Slot 1 should have FS 32 in it');
|
|
||||||
expect(anaconda.internal[3].m.grp).toEqual('fs','Slot 1 should have FS 32 in it');
|
|
||||||
});
|
|
||||||
|
|
||||||
it("enforces a single refinery", function() {
|
|
||||||
var id = 'anaconda';
|
|
||||||
var anacondaData = Ships[id];
|
|
||||||
var anaconda = new Ship(id, anacondaData.properties, anacondaData.slots);
|
|
||||||
anaconda.buildWith(anacondaData.defaults);
|
|
||||||
|
|
||||||
anaconda.use(anaconda.internal[4], ModuleUtils.internal('23')); // 4E Refinery
|
|
||||||
expect(anaconda.internal[4].m.grp).toEqual('rf', 'Anaconda refinery slot');
|
|
||||||
|
|
||||||
anaconda.use(anaconda.internal[3], ModuleUtils.internal('23'));
|
|
||||||
|
|
||||||
expect(anaconda.internal[4].m).toEqual(null, 'Anaconda original refinery slot is empty');
|
|
||||||
expect(anaconda.internal[3].m.id).toEqual('23', 'Slot 1 should have RF 23 in it');
|
|
||||||
expect(anaconda.internal[3].m.grp).toEqual('rf','Slot 1 should have RF 23 in it');
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
const TestUtils = {
|
|
||||||
createContextProvider: function(context) {
|
|
||||||
var _contextTypes = {};
|
|
||||||
|
|
||||||
Object.keys(context).forEach(function(key) {
|
|
||||||
_contextTypes[key] = PropTypes.any;
|
|
||||||
});
|
|
||||||
|
|
||||||
return React.createClass({
|
|
||||||
displayName: 'ContextProvider',
|
|
||||||
childContextTypes: _contextTypes,
|
|
||||||
getChildContext() { return context; },
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return React.Children.only(this.props.children);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
export default TestUtils;
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
version: '2.2'
|
|
||||||
|
|
||||||
services:
|
|
||||||
coriolis_prod:
|
|
||||||
image: edcd/coriolis:master
|
|
||||||
build:
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
args:
|
|
||||||
branch: master
|
|
||||||
restart: always
|
|
||||||
volumes:
|
|
||||||
- ./nginx.conf:/etc/nginx/nginx.conf
|
|
||||||
networks:
|
|
||||||
- web
|
|
||||||
labels:
|
|
||||||
- "traefik.docker.network=web"
|
|
||||||
- "traefik.enable=true"
|
|
||||||
- "traefik.basic.frontend.rule=Host:coriolis.io,coriolis.edcd.io"
|
|
||||||
- "traefik.basic.port=80"
|
|
||||||
- "traefik.basic.protocol=http"
|
|
||||||
|
|
||||||
coriolis_dev:
|
|
||||||
image: edcd/coriolis:develop
|
|
||||||
build:
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
args:
|
|
||||||
branch: develop
|
|
||||||
restart: always
|
|
||||||
volumes:
|
|
||||||
- ./nginx.conf:/etc/nginx/nginx.conf
|
|
||||||
networks:
|
|
||||||
- web
|
|
||||||
labels:
|
|
||||||
- "traefik.docker.network=web"
|
|
||||||
- "traefik.enable=true"
|
|
||||||
- "traefik.basic.frontend.rule=Host:beta.coriolis.io,beta.coriolis.edcd.io"
|
|
||||||
- "traefik.basic.port=80"
|
|
||||||
- "traefik.basic.protocol=http"
|
|
||||||
|
|
||||||
coriolis_dw2:
|
|
||||||
image: edcd/coriolis:dw2
|
|
||||||
restart: always
|
|
||||||
volumes:
|
|
||||||
- ./nginx.conf:/etc/nginx/nginx.conf
|
|
||||||
networks:
|
|
||||||
- web
|
|
||||||
labels:
|
|
||||||
- "traefik.docker.network=web"
|
|
||||||
- "traefik.enable=true"
|
|
||||||
- "traefik.basic.frontend.rule=Host:dw2.coriolis.io"
|
|
||||||
- "traefik.basic.port=80"
|
|
||||||
- "traefik.basic.protocol=http"
|
|
||||||
|
|
||||||
networks:
|
|
||||||
web:
|
|
||||||
external: true
|
|
||||||
96
nginx.conf
96
nginx.conf
@@ -1,96 +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;
|
|
||||||
|
|
||||||
# https://nginx.org/en/docs/http/ngx_http_gzip_module.html
|
|
||||||
# Enable gzip compression.
|
|
||||||
# Default: off
|
|
||||||
gzip off;
|
|
||||||
|
|
||||||
# Compression level (1-9).
|
|
||||||
# 5 is a perfect compromise between size and CPU usage, offering about
|
|
||||||
# 75% reduction for most ASCII files (almost identical to level 9).
|
|
||||||
# Default: 1
|
|
||||||
gzip_comp_level 5;
|
|
||||||
|
|
||||||
# Don't compress anything that's already small and unlikely to shrink much
|
|
||||||
# if at all (the default is 20 bytes, which is bad as that usually leads to
|
|
||||||
# larger files after gzipping).
|
|
||||||
# Default: 20
|
|
||||||
gzip_min_length 256;
|
|
||||||
|
|
||||||
# Compress data even for clients that are connecting to us via proxies,
|
|
||||||
# identified by the "Via" header (required for CloudFront).
|
|
||||||
# Default: off
|
|
||||||
gzip_proxied any;
|
|
||||||
|
|
||||||
# Tell proxies to cache both the gzipped and regular version of a resource
|
|
||||||
# whenever the client's Accept-Encoding capabilities header varies;
|
|
||||||
# Avoids the issue where a non-gzip capable client (which is extremely rare
|
|
||||||
# today) would display gibberish if their proxy gave them the gzipped version.
|
|
||||||
# Default: off
|
|
||||||
gzip_vary on;
|
|
||||||
|
|
||||||
# Compress all output labeled with one of the following MIME-types.
|
|
||||||
# text/html is always compressed by gzip module.
|
|
||||||
# Default: text/html
|
|
||||||
gzip_types *;
|
|
||||||
brotli on;
|
|
||||||
# brotli_static on;
|
|
||||||
brotli_types *;
|
|
||||||
# This should be turned on if you are going to have pre-compressed copies (.gz) of
|
|
||||||
# static files available. If not it should be left off as it will cause extra I/O
|
|
||||||
# for the check. It is best if you enable this in a location{} block for
|
|
||||||
# a specific directory, or on an individual server{} level.
|
|
||||||
# gzip_static on;
|
|
||||||
keepalive_timeout 3000;
|
|
||||||
server {
|
|
||||||
listen 80;
|
|
||||||
listen [::]:80;
|
|
||||||
index index.html;
|
|
||||||
server_name localhost;
|
|
||||||
root /usr/share/nginx/html;
|
|
||||||
autoindex on;
|
|
||||||
|
|
||||||
location ~* \.(?:manifest|appcache|html?|xml|json|css|js|map|jpg|jpeg|gif|png|ico|svg|eot|ttf|woff|woff2)$ {
|
|
||||||
add_header Access-Control-Allow-Origin *;
|
|
||||||
add_header Access-Control-Allow-Credentials true;
|
|
||||||
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
|
|
||||||
add_header Access-Control-Allow-Headers 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
|
|
||||||
access_log off;
|
|
||||||
}
|
|
||||||
location /service-worker.js {
|
|
||||||
expires -1;
|
|
||||||
add_header Access-Control-Allow-Origin *;
|
|
||||||
add_header Access-Control-Allow-Credentials true;
|
|
||||||
add_header Access-Control-Allow-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;
|
|
||||||
}
|
|
||||||
location /iframe.html {
|
|
||||||
try_files $uri $uri/ /iframe.html =404;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
28575
package-lock.json
generated
28575
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
38
package.json
38
package.json
@@ -15,45 +15,12 @@
|
|||||||
"clean": "rimraf build",
|
"clean": "rimraf build",
|
||||||
"start": "node devServer.js",
|
"start": "node devServer.js",
|
||||||
"lint": "eslint --ext .js,.jsx src",
|
"lint": "eslint --ext .js,.jsx src",
|
||||||
"test": "jest",
|
|
||||||
"prod-serve": "nginx -p $(pwd) -c nginx.conf",
|
"prod-serve": "nginx -p $(pwd) -c nginx.conf",
|
||||||
"prod-stop": "kill -QUIT $(cat nginx.pid)",
|
"prod-stop": "kill -QUIT $(cat nginx.pid)",
|
||||||
"build": "npm run clean && cross-env NODE_ENV=production webpack -p --config webpack.config.prod.js",
|
"build": "npm run clean && cross-env NODE_ENV=production webpack -p --config webpack.config.prod.js",
|
||||||
"rsync": "rsync -ae \"ssh -i $CORIOLIS_PEM\" --delete build/ $CORIOLIS_USER@$CORIOLIS_HOST:~/wwws",
|
"rsync": "rsync -ae \"ssh -i $CORIOLIS_PEM\" --delete build/ $CORIOLIS_USER@$CORIOLIS_HOST:~/wwws",
|
||||||
"deploy": "npm run lint && npm test && npm run build && npm run rsync"
|
"deploy": "npm run lint && npm test && npm run build && npm run rsync"
|
||||||
},
|
},
|
||||||
"jest": {
|
|
||||||
"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": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.0.0",
|
"@babel/core": "^7.0.0",
|
||||||
"@babel/plugin-proposal-class-properties": "^7.0.0",
|
"@babel/plugin-proposal-class-properties": "^7.0.0",
|
||||||
@@ -77,7 +44,6 @@
|
|||||||
"appcache-webpack-plugin": "^1.4.0",
|
"appcache-webpack-plugin": "^1.4.0",
|
||||||
"babel-core": "^7.0.0-bridge.0",
|
"babel-core": "^7.0.0-bridge.0",
|
||||||
"babel-eslint": "^10.0.1",
|
"babel-eslint": "^10.0.1",
|
||||||
"babel-jest": "^23.6.0",
|
|
||||||
"babel-loader": "^8.0.0",
|
"babel-loader": "^8.0.0",
|
||||||
"copy-webpack-plugin": "^4.5.2",
|
"copy-webpack-plugin": "^4.5.2",
|
||||||
"create-react-class": "^15.6.3",
|
"create-react-class": "^15.6.3",
|
||||||
@@ -98,7 +64,6 @@
|
|||||||
"extract-text-webpack-plugin": "^4.0.0-beta.0",
|
"extract-text-webpack-plugin": "^4.0.0-beta.0",
|
||||||
"file-loader": "^2.0.0",
|
"file-loader": "^2.0.0",
|
||||||
"html-webpack-plugin": "^3.0.7",
|
"html-webpack-plugin": "^3.0.7",
|
||||||
"jest-cli": "^23.6.0",
|
|
||||||
"jsen": "^0.6.4",
|
"jsen": "^0.6.4",
|
||||||
"json-loader": "^0.5.4",
|
"json-loader": "^0.5.4",
|
||||||
"less": "^3.8.1",
|
"less": "^3.8.1",
|
||||||
@@ -126,10 +91,9 @@
|
|||||||
"auto-bind": "^2.1.1",
|
"auto-bind": "^2.1.1",
|
||||||
"browserify-zlib-next": "^1.0.1",
|
"browserify-zlib-next": "^1.0.1",
|
||||||
"classnames": "^2.2.6",
|
"classnames": "^2.2.6",
|
||||||
"coriolis-data": "../coriolis-data",
|
|
||||||
"d3": "^5.7.0",
|
"d3": "^5.7.0",
|
||||||
"detect-browser": "^3.0.1",
|
"detect-browser": "^3.0.1",
|
||||||
"ed-forge": "github:EDCD/ed-forge",
|
"ed-forge": "../ed-forge",
|
||||||
"fbemitter": "^2.1.1",
|
"fbemitter": "^2.1.1",
|
||||||
"lodash": "^4.17.11",
|
"lodash": "^4.17.11",
|
||||||
"lz-string": "^1.4.4",
|
"lz-string": "^1.4.4",
|
||||||
|
|||||||
@@ -16,12 +16,9 @@ import ModalPermalink from './components/ModalPermalink';
|
|||||||
import AboutPage from './pages/AboutPage';
|
import AboutPage from './pages/AboutPage';
|
||||||
import NotFoundPage from './pages/NotFoundPage';
|
import NotFoundPage from './pages/NotFoundPage';
|
||||||
import OutfittingPage from './pages/OutfittingPage';
|
import OutfittingPage from './pages/OutfittingPage';
|
||||||
import ComparisonPage from './pages/ComparisonPage';
|
|
||||||
import ShipyardPage from './pages/ShipyardPage';
|
import ShipyardPage from './pages/ShipyardPage';
|
||||||
import ErrorDetails from './pages/ErrorDetails';
|
import ErrorDetails from './pages/ErrorDetails';
|
||||||
|
|
||||||
const request = require('superagent');
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Coriolis App
|
* Coriolis App
|
||||||
*/
|
*/
|
||||||
@@ -68,16 +65,14 @@ export default class Coriolis extends React.Component {
|
|||||||
route: {},
|
route: {},
|
||||||
sizeRatio: Persist.getSizeRatio()
|
sizeRatio: Persist.getSizeRatio()
|
||||||
};
|
};
|
||||||
this._getAnnouncements();
|
// TODO: New mechanism for announcements
|
||||||
|
// this._getAnnouncements();
|
||||||
Router('', (r) => this._setPage(ShipyardPage, r));
|
Router('', (r) => this._setPage(ShipyardPage, r));
|
||||||
Router('/import?', (r) => this._importBuild(r));
|
Router('/import?', (r) => this._importBuild(r));
|
||||||
Router('/import/:data', (r) => this._importBuild(r));
|
Router('/import/:data', (r) => this._importBuild(r));
|
||||||
Router('/outfit/?', (r) => this._setPage(OutfittingPage, r));
|
Router('/outfit/?', (r) => this._setPage(OutfittingPage, r));
|
||||||
Router('/outfit/:ship/?', (r) => this._setPage(OutfittingPage, r));
|
Router('/outfit/:ship/?', (r) => this._setPage(OutfittingPage, r));
|
||||||
Router('/outfit/:ship/:code?', (r) => this._setPage(OutfittingPage, r));
|
Router('/outfit/:ship/:code?', (r) => this._setPage(OutfittingPage, r));
|
||||||
Router('/compare/:name?', (r) => this._setPage(ComparisonPage, r));
|
|
||||||
Router('/comparison?', (r) => this._setPage(ComparisonPage, r));
|
|
||||||
Router('/comparison/:code', (r) => this._setPage(ComparisonPage, r));
|
|
||||||
Router('/about', (r) => this._setPage(AboutPage, r));
|
Router('/about', (r) => this._setPage(AboutPage, r));
|
||||||
Router('*', (r) => this._setPage(null, r));
|
Router('*', (r) => this._setPage(null, r));
|
||||||
}
|
}
|
||||||
@@ -98,15 +93,15 @@ export default class Coriolis extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async _getAnnouncements() {
|
// async _getAnnouncements() {
|
||||||
try {
|
// try {
|
||||||
const announces = await request.get('https://orbis.zone/api/announcement')
|
// const announces = await request.get('https://orbis.zone/api/announcement')
|
||||||
.query({ showInCoriolis: true });
|
// .query({ showInCoriolis: true });
|
||||||
this.setState({ announcements: announces.body });
|
// this.setState({ announcements: announces.body });
|
||||||
} catch (err) {
|
// } catch (err) {
|
||||||
console.error(err)
|
// console.error(err)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates / Sets the page and route context
|
* Updates / Sets the page and route context
|
||||||
@@ -381,18 +376,19 @@ export default class Coriolis extends React.Component {
|
|||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
let currentMenu = this.state.currentMenu;
|
let currentMenu = this.state.currentMenu;
|
||||||
|
|
||||||
return <div style={{ minHeight: '100%' }} onClick={this._closeMenu}
|
return <div style={{ minHeight: '100%' }} onClick={this._closeMenu}
|
||||||
className={this.state.noTouch ? 'no-touch' : null}>
|
className={this.state.noTouch ? 'no-touch' : null}
|
||||||
|
>
|
||||||
<Header announcements={this.state.announcements} appCacheUpdate={this.state.appCacheUpdate}
|
<Header announcements={this.state.announcements} appCacheUpdate={this.state.appCacheUpdate}
|
||||||
currentMenu={currentMenu}/>
|
currentMenu={currentMenu}/>
|
||||||
<div className="announcement-container">{this.state.announcements.map(a => <Announcement
|
{/* <div className="announcement-container">{this.state.announcements.map(a => <Announcement
|
||||||
text={a.message}/>)}</div>
|
text={a.message}/>)}</div> */}
|
||||||
{this.state.error ? this.state.error : this.state.page ? React.createElement(this.state.page, { currentMenu }) :
|
{this.state.error ? this.state.error : this.state.page ? React.createElement(this.state.page, { currentMenu }) :
|
||||||
<NotFoundPage/>}
|
<NotFoundPage/>}
|
||||||
{this.state.modal}
|
{this.state.modal}
|
||||||
{this.state.tooltip}
|
{this.state.tooltip}
|
||||||
<footer>
|
<footer>
|
||||||
|
|
||||||
<div className="right cap">
|
<div className="right cap">
|
||||||
<a href="https://github.com/EDCD/coriolis" target="_blank" rel="noopener noreferrer"
|
<a href="https://github.com/EDCD/coriolis" target="_blank" rel="noopener noreferrer"
|
||||||
title="Coriolis Github Project">{window.CORIOLIS_VERSION} - {window.CORIOLIS_DATE}</a>
|
title="Coriolis Github Project">{window.CORIOLIS_VERSION} - {window.CORIOLIS_DATE}</a>
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ Router.go = function(path, state) {
|
|||||||
gaTrack(path);
|
gaTrack(path);
|
||||||
let ctx = new Context(path, state);
|
let ctx = new Context(path, state);
|
||||||
Router.dispatch(ctx);
|
Router.dispatch(ctx);
|
||||||
|
|
||||||
if (!ctx.unhandled) {
|
if (!ctx.unhandled) {
|
||||||
if (isStandAlone()) {
|
if (isStandAlone()) {
|
||||||
Persist.setState(ctx);
|
Persist.setState(ctx);
|
||||||
|
|||||||
@@ -5,9 +5,11 @@ import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
|||||||
import cn from 'classnames';
|
import cn from 'classnames';
|
||||||
import { MountFixed, MountGimballed, MountTurret } from './SvgIcons';
|
import { MountFixed, MountGimballed, MountTurret } from './SvgIcons';
|
||||||
import FuzzySearch from 'react-fuzzy';
|
import FuzzySearch from 'react-fuzzy';
|
||||||
import { getModuleInfo } from 'ed-forge/lib/data/items';
|
import { getModuleInfo } from 'ed-forge/lib/src/data/items';
|
||||||
import { groupBy, mapValues, sortBy } from 'lodash';
|
import { get, groupBy, mapValues, sortBy, zip, zipWith } from 'lodash';
|
||||||
import autoBind from 'auto-bind';
|
import autoBind from 'auto-bind';
|
||||||
|
import MODULE_STATS from 'ed-forge/lib/src/module-stats';
|
||||||
|
import { SHOW } from '../shipyard/StatsMapping';
|
||||||
|
|
||||||
const PRESS_THRESHOLD = 500; // mouse/touch down threshold
|
const PRESS_THRESHOLD = 500; // mouse/touch down threshold
|
||||||
|
|
||||||
@@ -23,7 +25,6 @@ const MOUNT_MAP = {
|
|||||||
export default class AvailableModulesMenu extends TranslatedComponent {
|
export default class AvailableModulesMenu extends TranslatedComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
onSelect: PropTypes.func.isRequired,
|
onSelect: PropTypes.func.isRequired,
|
||||||
diffDetails: PropTypes.func,
|
|
||||||
hideSearch: PropTypes.bool,
|
hideSearch: PropTypes.bool,
|
||||||
m: PropTypes.object,
|
m: PropTypes.object,
|
||||||
warning: PropTypes.func,
|
warning: PropTypes.func,
|
||||||
@@ -155,7 +156,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const mountSymbol = MOUNT_MAP[meta.mount] || '';
|
const mountSymbol = MOUNT_MAP[meta.mount];
|
||||||
const li = (
|
const li = (
|
||||||
<li key={Item} data-id={Item}
|
<li key={Item} data-id={Item}
|
||||||
ref={Item === mountedModule.getItem() ? (ref) => { this.activeSlotRef = ref; } : undefined}
|
ref={Item === mountedModule.getItem() ? (ref) => { this.activeSlotRef = ref; } : undefined}
|
||||||
@@ -166,7 +167,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
|||||||
hardpoint: mountSymbol,
|
hardpoint: mountSymbol,
|
||||||
})}
|
})}
|
||||||
{...eventHandlers}
|
{...eventHandlers}
|
||||||
>{meta.type === 'armour' ? Item : `${mountSymbol}${meta.class}${meta.rating}`}</li>
|
>{mountSymbol}{meta.type === 'armour' ? Item : `${meta.class}${meta.rating}`}</li>
|
||||||
);
|
);
|
||||||
|
|
||||||
const tail = elems.pop();
|
const tail = elems.pop();
|
||||||
@@ -196,14 +197,47 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
|||||||
* @param {DOMRect} rect DOMRect for target element
|
* @param {DOMRect} rect DOMRect for target element
|
||||||
*/
|
*/
|
||||||
_showDiff(mountedModule, hoveringModule, rect) {
|
_showDiff(mountedModule, hoveringModule, rect) {
|
||||||
if (this.props.diffDetails) {
|
const { tooltip, language } = this.context;
|
||||||
|
const { formats, translate, units } = language;
|
||||||
this.touchTimeout = null;
|
this.touchTimeout = null;
|
||||||
// TODO:
|
const mountedIsEmpty = mountedModule.isEmpty();
|
||||||
// this.context.tooltip(
|
const props = (
|
||||||
// this.props.diffDetails(hoveringModule, mountedModule),
|
mountedIsEmpty ? ['mass'] : Object.keys(hoveringModule.props)
|
||||||
// rect,
|
).map((prop) => SHOW[prop] ? SHOW[prop].as : prop);
|
||||||
// );
|
const oldProps = mountedIsEmpty ?
|
||||||
|
[{ value: 0 }] :
|
||||||
|
props.map((prop) => mountedModule.getFormatted(prop, false));
|
||||||
|
const newProps = mountedModule.try(() => {
|
||||||
|
mountedModule.setItem(hoveringModule.proto.Item);
|
||||||
|
return props.map((prop) => mountedModule.getFormatted(prop, false));
|
||||||
|
});
|
||||||
|
|
||||||
|
const diffs = zipWith(oldProps, newProps, (oldVal, newVal) => {
|
||||||
|
const { unit, value } = newVal;
|
||||||
|
if (!oldVal.value) {
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
|
return { value, diff: value - oldVal.value, unit };
|
||||||
|
});
|
||||||
|
const namedDiffs = zip(props, diffs).filter(([_, stat]) => stat !== undefined);
|
||||||
|
namedDiffs.push(['cost', {
|
||||||
|
value: hoveringModule.meta.cost,
|
||||||
|
diff: hoveringModule.meta.cost - (mountedIsEmpty ? 0 : mountedModule.readMeta('cost')),
|
||||||
|
unit: units.CR,
|
||||||
|
}]);
|
||||||
|
|
||||||
|
const tt = <div className='cap' style={{ whiteSpace: 'nowrap' }}>
|
||||||
|
{sortBy(namedDiffs, ([prop, _]) => prop).map(([prop, stats]) => {
|
||||||
|
const { unit, value, diff } = stats;
|
||||||
|
const beneficial = get(MODULE_STATS, [prop, 'higherbetter'], false) === diff > 0;
|
||||||
|
return <div key={prop}>
|
||||||
|
{translate(prop)}: <span className={diff === 0 ? 'disabled' : beneficial ? 'secondary' : 'warning'}>
|
||||||
|
{formats.round(value)} {diff !== 0 && ` (${diff > 0 ? '+' : ''}${formats.round(diff)})`}{unit}
|
||||||
|
</span>
|
||||||
|
</div>;
|
||||||
|
})}
|
||||||
|
</div>;
|
||||||
|
tooltip(tt, rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import TranslatedComponent from './TranslatedComponent';
|
|||||||
import { ShoppingIcon } from './SvgIcons';
|
import { ShoppingIcon } from './SvgIcons';
|
||||||
import autoBind from 'auto-bind';
|
import autoBind from 'auto-bind';
|
||||||
import { assign, differenceBy, sortBy, reverse } from 'lodash';
|
import { assign, differenceBy, sortBy, reverse } from 'lodash';
|
||||||
import { FUEL_CAPACITY } from 'ed-forge/lib/ship-stats';
|
import { FUEL_CAPACITY } from 'ed-forge/lib/src/ship-stats';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cost Section
|
* Cost Section
|
||||||
@@ -160,8 +160,8 @@ export default class CostSection extends TranslatedComponent {
|
|||||||
<tr className='main'>
|
<tr className='main'>
|
||||||
<th colSpan='2' className='sortable le' onClick={() => this._sortBy('m')}>
|
<th colSpan='2' className='sortable le' onClick={() => this._sortBy('m')}>
|
||||||
{translate('module')}
|
{translate('module')}
|
||||||
{shipDiscount ? <u className='cap optional-hide' style={{ marginLeft: '0.5em' }}>{`[${translate('ship')} -${formats.pct(shipDiscount)}]`}</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(moduleDiscount)}]`}</u> : null}
|
{moduleDiscount ? <u className='cap optional-hide' style={{ marginLeft: '0.5em' }}>{`[${translate('modules')} ${formats.pct(-1 * moduleDiscount)}]`}</u> : null}
|
||||||
</th>
|
</th>
|
||||||
<th className='sortable le' onClick={() => this._sortBy('cr')} >{translate('credits')}</th>
|
<th className='sortable le' onClick={() => this._sortBy('cr')} >{translate('credits')}</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import TranslatedComponent from './TranslatedComponent';
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
import * as Calc from '../shipyard/Calculations';
|
|
||||||
import PieChart from './PieChart';
|
import PieChart from './PieChart';
|
||||||
import VerticalBarChart from './VerticalBarChart';
|
import VerticalBarChart from './VerticalBarChart';
|
||||||
import autoBind from 'auto-bind';
|
import autoBind from 'auto-bind';
|
||||||
import { ARMOUR_METRICS, MODULE_PROTECTION_METRICS, SHIELD_METRICS } from 'ed-forge/lib/ship-stats';
|
import { ARMOUR_METRICS, MODULE_PROTECTION_METRICS, SHIELD_METRICS } from 'ed-forge/lib/src/ship-stats';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defence information
|
* Defence information
|
||||||
|
|||||||
134
src/app/components/EDEngineerButton.jsx
Normal file
134
src/app/components/EDEngineerButton.jsx
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import autoBind from 'auto-bind';
|
||||||
|
import Persist from '../stores/Persist';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { getBlueprintUuid, getExperimentalUuid } from 'ed-forge/lib/src/data/blueprints';
|
||||||
|
import { Loader, MatIcon } from '../components/SvgIcons';
|
||||||
|
import request from 'superagent';
|
||||||
|
import { chain, entries } from 'lodash';
|
||||||
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
|
|
||||||
|
const STATE = {
|
||||||
|
READY: 0,
|
||||||
|
LOADING: 1,
|
||||||
|
ERROR: 2,
|
||||||
|
DONE: 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export default class EDEngineerButton extends TranslatedComponent {
|
||||||
|
static propTypes = {
|
||||||
|
ship: PropTypes.object.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param {Object} props React Component properties
|
||||||
|
*/
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
autoBind(this);
|
||||||
|
|
||||||
|
const { ship } = props;
|
||||||
|
const uuids = chain(ship.getModules())
|
||||||
|
.filter((m) => m.getBlueprint())
|
||||||
|
.map((m) => {
|
||||||
|
const uuids = [getBlueprintUuid(m.getBlueprint(), m.getBlueprintGrade())];
|
||||||
|
const exp = m.getExperimental();
|
||||||
|
if (exp) {
|
||||||
|
uuids.push(getExperimentalUuid(exp));
|
||||||
|
}
|
||||||
|
return uuids;
|
||||||
|
})
|
||||||
|
.flatMap()
|
||||||
|
.groupBy()
|
||||||
|
.mapValues((v) => v.length)
|
||||||
|
.value();
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
status: STATE.READY,
|
||||||
|
uuids,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the shopping list
|
||||||
|
*/
|
||||||
|
_sendToEDEngineer() {
|
||||||
|
const { uuids } = this.state;
|
||||||
|
this.setState({ status: STATE.LOADING });
|
||||||
|
request.get('http://localhost:44405/commanders')
|
||||||
|
.then((data) => {
|
||||||
|
const [cmdr] = JSON.parse(data.text);
|
||||||
|
return Promise.all(
|
||||||
|
entries(uuids).map(
|
||||||
|
(entry) => {
|
||||||
|
const [uuid, n] = entry;
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
request.patch(`http://localhost:44405/${cmdr}/shopping-list`)
|
||||||
|
.field('uuid', uuid)
|
||||||
|
.field('size', n)
|
||||||
|
.end((err, res) => {
|
||||||
|
console.log('request goes out!');
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
resolve(res);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.then(() => this.setState({ status: STATE.DONE }))
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
this.setState({ status: STATE.ERROR });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks for browser compatibility of sending to ED Engineer.
|
||||||
|
* @returns {boolean} True if browser is compatible
|
||||||
|
*/
|
||||||
|
_browserIsCompatible() {
|
||||||
|
// !== Firefox 1.0+
|
||||||
|
// TODO: Double check if this really doesn't work in firefox
|
||||||
|
return typeof InstallTrigger === 'undefined';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
render() {
|
||||||
|
const { termtip, tooltip } = this.context;
|
||||||
|
const hide = tooltip.bind(null, null);
|
||||||
|
const { status } = this.state;
|
||||||
|
|
||||||
|
let msg = 'PHRASE_FIREFOX_EDENGINEER';
|
||||||
|
if (this._browserIsCompatible()) {
|
||||||
|
switch (status) {
|
||||||
|
case STATE.READY: msg = 'Send to EDEngineer'; break;
|
||||||
|
case STATE.LOADING: msg = 'Sending...'; break;
|
||||||
|
case STATE.ERROR: msg = 'Error sending to EDEngineer'; break;
|
||||||
|
case STATE.DONE: msg = 'Success! Clicking sends again.'; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (<button
|
||||||
|
disabled={!this._browserIsCompatible()}
|
||||||
|
onClick={status !== STATE.LOADING && this._sendToEDEngineer}
|
||||||
|
onMouseOver={termtip.bind(null, msg)}
|
||||||
|
onMouseOut={hide}
|
||||||
|
>
|
||||||
|
{status === STATE.LOADING ?
|
||||||
|
<Loader className="lg" /> :
|
||||||
|
<MatIcon className="lg" />
|
||||||
|
}
|
||||||
|
</button>);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ import React from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import TranslatedComponent from './TranslatedComponent';
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
import Slider from '../components/Slider';
|
import Slider from '../components/Slider';
|
||||||
|
import { moduleReduce } from 'ed-forge/lib/src/helper';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Engagement range slider
|
* Engagement range slider
|
||||||
@@ -21,35 +22,18 @@ export default class EngagementRange extends TranslatedComponent {
|
|||||||
*/
|
*/
|
||||||
constructor(props, context) {
|
constructor(props, context) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
const { ship } = props;
|
|
||||||
|
|
||||||
const maxRange = Math.round(this._calcMaxRange(ship));
|
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
maxRange
|
maxRange: moduleReduce(
|
||||||
|
this.props.ship.getHardpoints(),
|
||||||
|
'maximumrange',
|
||||||
|
true,
|
||||||
|
// Don't use plain `Math.max` because callback will be passed four args
|
||||||
|
(a, v) => Math.max(a, v),
|
||||||
|
1000,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate the maximum range of a ship's weapons
|
|
||||||
* @param {Object} ship The ship
|
|
||||||
* @returns {int} The maximum range, in metres
|
|
||||||
*/
|
|
||||||
_calcMaxRange(ship) {
|
|
||||||
let maxRange = 1000;
|
|
||||||
for (let i = 0; i < ship.hardpoints.length; i++) {
|
|
||||||
if (ship.hardpoints[i].maxClass > 0 && ship.hardpoints[i].m && ship.hardpoints[i].enabled) {
|
|
||||||
const thisRange = ship.hardpoints[i].m.getRange();
|
|
||||||
if (thisRange > maxRange) {
|
|
||||||
maxRange = thisRange;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return maxRange;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update range
|
* Update range
|
||||||
* @param {number} rangeLevel percentage level from 0 to 1
|
* @param {number} rangeLevel percentage level from 0 to 1
|
||||||
@@ -61,7 +45,9 @@ export default class EngagementRange extends TranslatedComponent {
|
|||||||
const range = Math.round(rangeLevel * maxRange);
|
const range = Math.round(rangeLevel * maxRange);
|
||||||
|
|
||||||
if (range !== this.props.engagementRange) {
|
if (range !== this.props.engagementRange) {
|
||||||
this.props.onChange(range);
|
const { onChange, ship } = this.props;
|
||||||
|
ship.setEngagementRange(range);
|
||||||
|
onChange(range);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,8 +56,8 @@ export default class EngagementRange extends TranslatedComponent {
|
|||||||
* @return {React.Component} contents
|
* @return {React.Component} contents
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
const { language, onWindowResize, sizeRatio, tooltip, termtip } = this.context;
|
const { language, onWindowResize, sizeRatio } = this.context;
|
||||||
const { formats, translate, units } = language;
|
const { formats, translate } = language;
|
||||||
const { engagementRange } = this.props;
|
const { engagementRange } = this.props;
|
||||||
const { maxRange } = this.state;
|
const { maxRange } = this.state;
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import TranslatedComponent from './TranslatedComponent';
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
import LineChart from '../components/LineChart';
|
import LineChart from '../components/LineChart';
|
||||||
import { getBoostMultiplier, getSpeedMultipliers } from 'ed-forge/lib/stats/SpeedProfile';
|
import { getBoostMultiplier, getSpeedMultipliers } from 'ed-forge/lib/src/stats/SpeedProfile';
|
||||||
import { ShipProps } from 'ed-forge';
|
import { ShipProps } from 'ed-forge';
|
||||||
const { LADEN_MASS } = ShipProps;
|
const { LADEN_MASS } = ShipProps;
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ import React from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import TranslatedComponent from './TranslatedComponent';
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
import LineChart from '../components/LineChart';
|
import LineChart from '../components/LineChart';
|
||||||
import * as Calc from '../shipyard/Calculations';
|
import { calculateJumpRange } from 'ed-forge/lib/src/stats/JumpRangeProfile';
|
||||||
import { calculateJumpRange } from 'ed-forge/lib/stats/JumpRangeProfile';
|
|
||||||
import { ShipProps } from 'ed-forge';
|
import { ShipProps } from 'ed-forge';
|
||||||
const { LADEN_MASS } = ShipProps;
|
const { LADEN_MASS } = ShipProps;
|
||||||
|
|
||||||
@@ -18,18 +17,6 @@ export default class FSDProfile extends TranslatedComponent {
|
|||||||
fuel: PropTypes.number.isRequired,
|
fuel: PropTypes.number.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate the maximum range for this ship across its applicable mass
|
|
||||||
* @param {Object} ship The ship
|
|
||||||
* @param {Object} fuel The fuel on the ship
|
|
||||||
* @param {Object} mass The mass at which to calculate the maximum range
|
|
||||||
* @return {number} The maximum range
|
|
||||||
*/
|
|
||||||
_calcMaxRange(ship, fuel, mass) {
|
|
||||||
// Obtain the maximum range
|
|
||||||
return Calc.jumpRange(mass, ship.standard[2].m, Math.min(fuel, ship.standard[2].m.getMaxFuelPerJump()), ship);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render FSD profile
|
* Render FSD profile
|
||||||
* @return {React.Component} contents
|
* @return {React.Component} contents
|
||||||
@@ -42,7 +29,7 @@ export default class FSDProfile extends TranslatedComponent {
|
|||||||
const minMass = ship.readProp('hullmass');
|
const minMass = ship.readProp('hullmass');
|
||||||
const maxMass = ship.getThrusters().get('enginemaximalmass');
|
const maxMass = ship.getThrusters().get('enginemaximalmass');
|
||||||
const mass = ship.get(LADEN_MASS);
|
const mass = ship.get(LADEN_MASS);
|
||||||
const cb = (mass) => calculateJumpRange(ship, mass, Infinity, true);
|
const cb = (mass) => calculateJumpRange(ship.getFSD(), 0, mass, Infinity, true);
|
||||||
return (
|
return (
|
||||||
<LineChart
|
<LineChart
|
||||||
xMin={minMass}
|
xMin={minMass}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import { MountFixed, MountGimballed, MountTurret } from '../components/SvgIcons'
|
|||||||
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
||||||
import autoBind from 'auto-bind';
|
import autoBind from 'auto-bind';
|
||||||
|
|
||||||
|
const SIZE_ORDER = ['huge', 'large', 'medium', 'small'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hardpoint slot section
|
* Hardpoint slot section
|
||||||
*/
|
*/
|
||||||
@@ -22,28 +24,35 @@ export default class HardpointSlotSection extends SlotSection {
|
|||||||
* Empty all slots
|
* Empty all slots
|
||||||
*/
|
*/
|
||||||
_empty() {
|
_empty() {
|
||||||
// TODO:
|
this.props.ship.getHardpoints(undefined, true).forEach((slot) => slot.reset());
|
||||||
// this.props.ship.emptyWeapons();
|
|
||||||
this._close();
|
this._close();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fill slots with specified module
|
* Fill slots with specified module
|
||||||
* @param {string} group Group name
|
* @param {string} type Type of item
|
||||||
* @param {string} mount Mount Type - F, G, T
|
* @param {string} rating Mount Type - (fixed, gimbal, turret)
|
||||||
* @param {SyntheticEvent} event Event
|
* @param {SyntheticEvent} event Event
|
||||||
*/
|
*/
|
||||||
_fill(group, mount, event) {
|
_fill(type, rating, event) {
|
||||||
// TODO:
|
const fillAll = event.getModifierState('Alt');
|
||||||
// this.props.ship.useWeapon(group, mount, null, event.getModifierState('Alt'));
|
this.props.ship.getHardpoints(undefined, true).forEach((slot) => {
|
||||||
this._close();
|
if (slot.isEmpty() || fillAll) {
|
||||||
|
const slotSize = slot.getSize();
|
||||||
|
const fittingSizes = SIZE_ORDER.slice(SIZE_ORDER.findIndex((e) => e === slotSize));
|
||||||
|
for (const size of fittingSizes) {
|
||||||
|
try {
|
||||||
|
slot.setItem(type, size, rating);
|
||||||
|
} catch (err) {
|
||||||
|
// Try next item if this doesn't fit/exist
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
// If still here, we were able to apply the module
|
||||||
/**
|
break;
|
||||||
* Empty all on section header right click
|
}
|
||||||
*/
|
}
|
||||||
_contextMenu() {
|
});
|
||||||
this._empty();
|
this._close();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -58,7 +67,6 @@ export default class HardpointSlotSection extends SlotSection {
|
|||||||
for (let h of ship.getHardpoints(undefined, true)) {
|
for (let h of ship.getHardpoints(undefined, true)) {
|
||||||
slots.push(<Slot
|
slots.push(<Slot
|
||||||
key={h.object.Slot}
|
key={h.object.Slot}
|
||||||
maxClass={h.getSize()}
|
|
||||||
currentMenu={currentMenu}
|
currentMenu={currentMenu}
|
||||||
drag={this._drag.bind(this, h)}
|
drag={this._drag.bind(this, h)}
|
||||||
dragOver={this._dragOverSlot.bind(this, h)}
|
dragOver={this._dragOverSlot.bind(this, h)}
|
||||||
@@ -85,61 +93,61 @@ export default class HardpointSlotSection extends SlotSection {
|
|||||||
|
|
||||||
return <div className='select hardpoint' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
|
return <div className='select hardpoint' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
|
||||||
<ul>
|
<ul>
|
||||||
<li className='lc' tabIndex="0" onClick={this._empty} ref={smRef => this.sectionRefArr['emptyall'] = smRef}>{translate('empty all')}</li>
|
<li className='lc' tabIndex="0" onClick={this._empty}>{translate('empty all')}</li>
|
||||||
<li className='optional-hide' style={{ textAlign: 'center', marginTop: '1em' }}>{translate('PHRASE_ALT_ALL')}</li>
|
<li className='optional-hide' style={{ textAlign: 'center', marginTop: '1em' }}>{translate('PHRASE_ALT_ALL')}</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div className='select-group cap'>{translate('pl')}</div>
|
<div className='select-group cap'>{translate('pulselaser')}</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li className="c" tabIndex="0" onClick={_fill.bind(this, 'pl', 'F')}><MountFixed className='lg'/></li>
|
<li className="c hardpoint" tabIndex="0" onClick={_fill.bind(this, 'pulselaser', 'fixed')}><MountFixed className='lg'/></li>
|
||||||
<li className="c" tabIndex="0" onClick={_fill.bind(this, 'pl', 'G')}><MountGimballed className='lg'/></li>
|
<li className="c hardpoint" tabIndex="0" onClick={_fill.bind(this, 'pulselaser', 'gimbal')}><MountGimballed className='lg'/></li>
|
||||||
<li className="c" tabIndex="0" onClick={_fill.bind(this, 'pl', 'T')}><MountTurret className='lg'/></li>
|
<li className="c hardpoint" tabIndex="0" onClick={_fill.bind(this, 'pulselaser', 'turret')}><MountTurret className='lg'/></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div className='select-group cap'>{translate('ul')}</div>
|
<div className='select-group cap'>{translate('burstlaser')}</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li className="c" tabIndex="0" onClick={_fill.bind(this, 'ul', 'F')}><MountFixed className='lg'/></li>
|
<li className="c hardpoint" tabIndex="0" onClick={_fill.bind(this, 'burstlaser', 'fixed')}><MountFixed className='lg'/></li>
|
||||||
<li className="c" tabIndex="0" onClick={_fill.bind(this, 'ul', 'G')}><MountGimballed className='lg'/></li>
|
<li className="c hardpoint" tabIndex="0" onClick={_fill.bind(this, 'burstlaser', 'gimbal')}><MountGimballed className='lg'/></li>
|
||||||
<li className="c" tabIndex="0" onClick={_fill.bind(this, 'ul', 'T')}><MountTurret className='lg'/></li>
|
<li className="c hardpoint" tabIndex="0" onClick={_fill.bind(this, 'burstlaser', 'turret')}><MountTurret className='lg'/></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div className='select-group cap'>{translate('bl')}</div>
|
<div className='select-group cap'>{translate('beamlaser')}</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li className="c" tabIndex="0" onClick={_fill.bind(this, 'bl', 'F')}><MountFixed className='lg'/></li>
|
<li className="c hardpoint" tabIndex="0" onClick={_fill.bind(this, 'beamlaser', 'fixed')}><MountFixed className='lg'/></li>
|
||||||
<li className="c" tabIndex="0" onClick={_fill.bind(this, 'bl', 'G')}><MountGimballed className='lg'/></li>
|
<li className="c hardpoint" tabIndex="0" onClick={_fill.bind(this, 'beamlaser', 'gimbal')}><MountGimballed className='lg'/></li>
|
||||||
<li className="c" tabIndex="0" onClick={_fill.bind(this, 'bl', 'T')}><MountTurret className='lg'/></li>
|
<li className="c hardpoint" tabIndex="0" onClick={_fill.bind(this, 'beamlaser', 'turret')}><MountTurret className='lg'/></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div className='select-group cap'>{translate('mc')}</div>
|
<div className='select-group cap'>{translate('multicannon')}</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li className="c" tabIndex="0" onClick={_fill.bind(this, 'mc', 'F')}><MountFixed className='lg'/></li>
|
<li className="c hardpoint" tabIndex="0" onClick={_fill.bind(this, 'multicannon', 'fixed')}><MountFixed className='lg'/></li>
|
||||||
<li className="c" tabIndex="0" onClick={_fill.bind(this, 'mc', 'G')}><MountGimballed className='lg'/></li>
|
<li className="c hardpoint" tabIndex="0" onClick={_fill.bind(this, 'multicannon', 'gimbal')}><MountGimballed className='lg'/></li>
|
||||||
<li className="c" tabIndex="0" onClick={_fill.bind(this, 'mc', 'T')}><MountTurret className='lg'/></li>
|
<li className="c hardpoint" tabIndex="0" onClick={_fill.bind(this, 'multicannon', 'turret')}><MountTurret className='lg'/></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div className='select-group cap'>{translate('c')}</div>
|
<div className='select-group cap'>{translate('cannon')}</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li className="c" tabIndex="0" onClick={_fill.bind(this, 'c', 'F')}><MountFixed className='lg'/></li>
|
<li className="c hardpoint" tabIndex="0" onClick={_fill.bind(this, 'cannon', 'fixed')}><MountFixed className='lg'/></li>
|
||||||
<li className="c" tabIndex="0" onClick={_fill.bind(this, 'c', 'G')}><MountGimballed className='lg'/></li>
|
<li className="c hardpoint" tabIndex="0" onClick={_fill.bind(this, 'cannon', 'gimbal')}><MountGimballed className='lg'/></li>
|
||||||
<li className="c" tabIndex="0" onClick={_fill.bind(this, 'c', 'T')}><MountTurret className='lg'/></li>
|
<li className="c hardpoint" tabIndex="0" onClick={_fill.bind(this, 'cannon', 'turret')}><MountTurret className='lg'/></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div className='select-group cap'>{translate('fc')}</div>
|
<div className='select-group cap'>{translate('fragcannon')}</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li className="c" tabIndex="0" onClick={_fill.bind(this, 'fc', 'F')}><MountFixed className='lg'/></li>
|
<li className="c hardpoint" tabIndex="0" onClick={_fill.bind(this, 'fragcannon', 'fixed')}><MountFixed className='lg'/></li>
|
||||||
<li className="c" tabIndex="0" onClick={_fill.bind(this, 'fc', 'G')}><MountGimballed className='lg'/></li>
|
<li className="c hardpoint" tabIndex="0" onClick={_fill.bind(this, 'fragcannon', 'gimbal')}><MountGimballed className='lg'/></li>
|
||||||
<li className="c" tabIndex="0" onClick={_fill.bind(this, 'fc', 'T')}><MountTurret className='lg'/></li>
|
<li className="c hardpoint" tabIndex="0" onClick={_fill.bind(this, 'fragcannon', 'turret')}><MountTurret className='lg'/></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div className='select-group cap'>{translate('pa')}</div>
|
<div className='select-group cap'>{translate('plasmaacc')}</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li className='lc' tabIndex="0" onClick={_fill.bind(this, 'pa', 'F')}>{translate('pa')}</li>
|
<li className='lc' tabIndex="0" onClick={_fill.bind(this, 'plasmaacc', 'fixed')}>{translate('pa')}</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div className='select-group cap'>{translate('rg')}</div>
|
<div className='select-group cap'>{translate('railgun')}</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li className='lc' tabIndex="0" onClick={_fill.bind(this, 'rg', 'F')}>{translate('rg')}</li>
|
<li className='lc' tabIndex="0" onClick={_fill.bind(this, 'railgun', 'fixed')}>{translate('rg')}</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div className='select-group cap'>{translate('nl')}</div>
|
<div className='select-group cap'>{translate('minelauncher')}</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li className='lc' tabIndex="0" onClick={_fill.bind(this, 'nl', 'F')}>{translate('nl')}</li>
|
<li className='lc' tabIndex="0" onClick={_fill.bind(this, 'minelauncher', 'fixed')}>{translate('nl')}</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div className='select-group cap'>{translate('rfl')}</div>
|
<div className='select-group cap'>{translate('flaklauncher')}</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li className="c" tabIndex="0" onClick={_fill.bind(this, 'rfl', 'F')}><MountFixed className='lg'/></li>
|
<li className="c hardpoint" tabIndex="0" onClick={_fill.bind(this, 'flaklauncher', 'fixed')}><MountFixed className='lg'/></li>
|
||||||
<li className="c" tabIndex="0" onClick={_fill.bind(this, 'rfl', 'T')}><MountTurret className='lg'/></li>
|
<li className="c hardpoint" tabIndex="0" onClick={_fill.bind(this, 'flaklauncher', 'turret')}><MountTurret className='lg'/></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,11 +6,7 @@ import Link from './Link';
|
|||||||
import ActiveLink from './ActiveLink';
|
import ActiveLink from './ActiveLink';
|
||||||
import cn from 'classnames';
|
import cn from 'classnames';
|
||||||
import { Cogs, CoriolisLogo, Hammer, Help, Rocket, StatsBars } from './SvgIcons';
|
import { Cogs, CoriolisLogo, Hammer, Help, Rocket, StatsBars } from './SvgIcons';
|
||||||
import { Ships } from 'coriolis-data/dist';
|
|
||||||
import Persist from '../stores/Persist';
|
import Persist from '../stores/Persist';
|
||||||
import { toDetailedExport } from '../shipyard/Serializer';
|
|
||||||
import Ship from '../shipyard/Ship';
|
|
||||||
import ModalBatchOrbis from './ModalBatchOrbis';
|
|
||||||
import ModalDeleteAll from './ModalDeleteAll';
|
import ModalDeleteAll from './ModalDeleteAll';
|
||||||
import ModalExport from './ModalExport';
|
import ModalExport from './ModalExport';
|
||||||
import ModalHelp from './ModalHelp';
|
import ModalHelp from './ModalHelp';
|
||||||
@@ -18,6 +14,9 @@ import ModalImport from './ModalImport';
|
|||||||
import Slider from './Slider';
|
import Slider from './Slider';
|
||||||
import Announcement from './Announcement';
|
import Announcement from './Announcement';
|
||||||
import { outfitURL } from '../utils/UrlGenerators';
|
import { outfitURL } from '../utils/UrlGenerators';
|
||||||
|
import autoBind from 'auto-bind';
|
||||||
|
import { Factory, Ship } from 'ed-forge';
|
||||||
|
import { chain, entries } from 'lodash';
|
||||||
|
|
||||||
const SIZE_MIN = 0.65;
|
const SIZE_MIN = 0.65;
|
||||||
const SIZE_RANGE = 0.55;
|
const SIZE_RANGE = 0.55;
|
||||||
@@ -63,24 +62,14 @@ export default class Header extends TranslatedComponent {
|
|||||||
*/
|
*/
|
||||||
constructor(props, context) {
|
constructor(props, context) {
|
||||||
super(props);
|
super(props);
|
||||||
this.shipOrder = Object.keys(Ships).sort();
|
autoBind(this);
|
||||||
|
this.ships = Factory.getAllShipTypes().sort();
|
||||||
|
|
||||||
this._setLanguage = this._setLanguage.bind(this);
|
|
||||||
this._setInsurance = this._setInsurance.bind(this);
|
|
||||||
this._setShipDiscount = this._setShipDiscount.bind(this);
|
|
||||||
this._changeShipDiscount = this._changeShipDiscount.bind(this);
|
|
||||||
this._kpShipDiscount = this._kpShipDiscount.bind(this);
|
|
||||||
this._setModuleDiscount = this._setModuleDiscount.bind(this);
|
|
||||||
this._changeModuleDiscount = this._changeModuleDiscount.bind(this);
|
|
||||||
this._kpModuleDiscount = this._kpModuleDiscount.bind(this);
|
|
||||||
this._openShips = this._openMenu.bind(this, 's');
|
this._openShips = this._openMenu.bind(this, 's');
|
||||||
this._openBuilds = this._openMenu.bind(this, 'b');
|
this._openBuilds = this._openMenu.bind(this, 'b');
|
||||||
this._openComp = this._openMenu.bind(this, 'comp');
|
this._openComp = this._openMenu.bind(this, 'comp');
|
||||||
this._openAnnounce = this._openMenu.bind(this, 'announce');
|
this._openAnnounce = this._openMenu.bind(this, 'announce');
|
||||||
this._getAnnouncementsMenu = this._getAnnouncementsMenu.bind(this);
|
|
||||||
this._openSettings = this._openMenu.bind(this, 'settings');
|
this._openSettings = this._openMenu.bind(this, 'settings');
|
||||||
this._showHelp = this._showHelp.bind(this);
|
|
||||||
this.update = this.update.bind(this);
|
|
||||||
this.languageOptions = [];
|
this.languageOptions = [];
|
||||||
this.insuranceOptions = [];
|
this.insuranceOptions = [];
|
||||||
this.state = {
|
this.state = {
|
||||||
@@ -210,13 +199,6 @@ export default class Header extends TranslatedComponent {
|
|||||||
Persist.showTooltips(!Persist.showTooltips());
|
Persist.showTooltips(!Persist.showTooltips());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Toggle module resistances setting
|
|
||||||
*/
|
|
||||||
_toggleModuleResistances() {
|
|
||||||
Persist.showModuleResistances(!Persist.showModuleResistances());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show delete all modal
|
* Show delete all modal
|
||||||
* @param {SyntheticEvent} e Event
|
* @param {SyntheticEvent} e Event
|
||||||
@@ -226,57 +208,6 @@ export default class Header extends TranslatedComponent {
|
|||||||
this.context.showModal(<ModalDeleteAll />);
|
this.context.showModal(<ModalDeleteAll />);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Show export modal with backup data
|
|
||||||
* @param {SyntheticEvent} e Event
|
|
||||||
*/
|
|
||||||
_showBackup(e) {
|
|
||||||
let translate = this.context.language.translate;
|
|
||||||
e.preventDefault();
|
|
||||||
this.context.showModal(<ModalExport
|
|
||||||
title={translate('backup')}
|
|
||||||
description={translate('PHRASE_BACKUP_DESC')}
|
|
||||||
data={Persist.getAll()}
|
|
||||||
/>);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Uploads all ship-builds to orbis
|
|
||||||
* @param {e} e Event
|
|
||||||
*/
|
|
||||||
_uploadAllBuildsToOrbis(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
const data = Persist.getBuilds();
|
|
||||||
let postObject = [];
|
|
||||||
for (const ship in data) {
|
|
||||||
for (const code in data[ship]) {
|
|
||||||
const shipModel = ship;
|
|
||||||
if (!shipModel) {
|
|
||||||
throw 'No such ship found: "' + ship + '"';
|
|
||||||
}
|
|
||||||
const shipTemplate = Ships[shipModel];
|
|
||||||
const shipPostObject = {};
|
|
||||||
let shipInstance = new Ship(shipModel, shipTemplate.properties, shipTemplate.slots);
|
|
||||||
shipInstance.buildWith(null);
|
|
||||||
shipInstance.buildFrom(data[ship][code]);
|
|
||||||
shipPostObject.coriolisId = shipInstance.id;
|
|
||||||
shipPostObject.coriolisShip = shipInstance;
|
|
||||||
|
|
||||||
shipPostObject.coriolisShip.url = window.location.origin + outfitURL(shipModel, data[ship][code], code);
|
|
||||||
shipPostObject.title = code || shipInstance.id;
|
|
||||||
shipPostObject.description = code || shipInstance.id;
|
|
||||||
shipPostObject.ShipName = shipInstance.id;
|
|
||||||
shipPostObject.Ship = shipInstance.id;
|
|
||||||
postObject.push(shipPostObject);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
console.log(postObject);
|
|
||||||
|
|
||||||
this.context.showModal(<ModalBatchOrbis
|
|
||||||
ships={postObject}
|
|
||||||
/>);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show export modal with detailed export
|
* Show export modal with detailed export
|
||||||
* @param {SyntheticEvent} e Event
|
* @param {SyntheticEvent} e Event
|
||||||
@@ -285,10 +216,22 @@ export default class Header extends TranslatedComponent {
|
|||||||
let translate = this.context.language.translate;
|
let translate = this.context.language.translate;
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
|
const builds = chain(Persist.getBuilds())
|
||||||
|
.values()
|
||||||
|
.map((builds) => Object.values(builds))
|
||||||
|
.flatMap()
|
||||||
|
.map((code) => new Ship(code))
|
||||||
|
.value();
|
||||||
|
|
||||||
this.context.showModal(<ModalExport
|
this.context.showModal(<ModalExport
|
||||||
title={translate('detailed export')}
|
title={translate('detailed export')}
|
||||||
description={translate('PHRASE_EXPORT_DESC')}
|
description={translate('PHRASE_EXPORT_DESC')}
|
||||||
data={toDetailedExport(Persist.getBuilds())}
|
data={JSON.stringify(builds.map((build) => {
|
||||||
|
return {
|
||||||
|
header: { appName: 'Inara', 'appVersion': '1.0' },
|
||||||
|
data: build.toJSON(),
|
||||||
|
};
|
||||||
|
}))}
|
||||||
/>);
|
/>);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -346,15 +289,10 @@ export default class Header extends TranslatedComponent {
|
|||||||
* @return {React.Component} Menu
|
* @return {React.Component} Menu
|
||||||
*/
|
*/
|
||||||
_getShipsMenu() {
|
_getShipsMenu() {
|
||||||
let shipList = [];
|
const { translate } = this.context.language;
|
||||||
|
|
||||||
for (let s of this.shipOrder) {
|
|
||||||
shipList.push(<ActiveLink key={s} href={outfitURL(s)} className='block'>{Ships[s].properties.name}</ActiveLink>);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='menu-list dbl no-wrap' onClick={ (e) => e.stopPropagation() }>
|
<div className='menu-list dbl no-wrap' onClick={ (e) => e.stopPropagation() }>
|
||||||
{shipList}
|
{this.ships.map((s) => <ActiveLink key={s} href={outfitURL(s)} className='block'>{translate(s)}</ActiveLink>)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -364,9 +302,10 @@ export default class Header extends TranslatedComponent {
|
|||||||
* @return {React.Component} Menu
|
* @return {React.Component} Menu
|
||||||
*/
|
*/
|
||||||
_getBuildsMenu() {
|
_getBuildsMenu() {
|
||||||
|
const { translate } = this.context.language;
|
||||||
let builds = Persist.getBuilds();
|
let builds = Persist.getBuilds();
|
||||||
let buildList = [];
|
let buildList = [];
|
||||||
for (let shipId of this.shipOrder) {
|
for (let shipId of this.ships) {
|
||||||
if (builds[shipId]) {
|
if (builds[shipId]) {
|
||||||
let shipBuilds = [];
|
let shipBuilds = [];
|
||||||
let buildNameOrder = Object.keys(builds[shipId]).sort();
|
let buildNameOrder = Object.keys(builds[shipId]).sort();
|
||||||
@@ -374,7 +313,7 @@ export default class Header extends TranslatedComponent {
|
|||||||
let href = outfitURL(shipId, builds[shipId][buildName], buildName);
|
let href = outfitURL(shipId, builds[shipId][buildName], buildName);
|
||||||
shipBuilds.push(<li key={shipId + '-' + buildName} ><ActiveLink href={href} className='block'>{buildName}</ActiveLink></li>);
|
shipBuilds.push(<li key={shipId + '-' + buildName} ><ActiveLink href={href} className='block'>{buildName}</ActiveLink></li>);
|
||||||
}
|
}
|
||||||
buildList.push(<ul key={shipId}>{Ships[shipId].properties.name}{shipBuilds}</ul>);
|
buildList.push(<ul key={shipId}>{translate(shipId)}{shipBuilds}</ul>);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -425,7 +364,10 @@ export default class Header extends TranslatedComponent {
|
|||||||
if (this.props.announcements) {
|
if (this.props.announcements) {
|
||||||
announcements = [];
|
announcements = [];
|
||||||
for (let announce of this.props.announcements) {
|
for (let announce of this.props.announcements) {
|
||||||
announcements.push(<Announcement text={announce.message} />);
|
if (announce.expiry < Date.now()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
announcements.push(<Announcement text={announce.text} />);
|
||||||
announcements.push(<hr/>);
|
announcements.push(<hr/>);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -444,7 +386,6 @@ export default class Header extends TranslatedComponent {
|
|||||||
_getSettingsMenu() {
|
_getSettingsMenu() {
|
||||||
let translate = this.context.language.translate;
|
let translate = this.context.language.translate;
|
||||||
let tips = Persist.showTooltips();
|
let tips = Persist.showTooltips();
|
||||||
let moduleResistances = Persist.showModuleResistances();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='menu-list no-wrap cap' onClick={ (e) => e.stopPropagation() }>
|
<div className='menu-list no-wrap cap' onClick={ (e) => e.stopPropagation() }>
|
||||||
@@ -462,10 +403,6 @@ export default class Header extends TranslatedComponent {
|
|||||||
<td>{translate('tooltips')}</td>
|
<td>{translate('tooltips')}</td>
|
||||||
<td className={cn('ri', { disabled: !tips, 'primary-disabled': tips })}>{(tips ? '✓' : '✗')}</td>
|
<td className={cn('ri', { disabled: !tips, 'primary-disabled': tips })}>{(tips ? '✓' : '✗')}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr className='cap ptr' onClick={this._toggleModuleResistances} >
|
|
||||||
<td>{translate('module resistances')}</td>
|
|
||||||
<td className={cn('ri', { disabled: !moduleResistances, 'primary-disabled': moduleResistances })}>{(moduleResistances ? '✓' : '✗')}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>{translate('insurance')}</td>
|
<td>{translate('insurance')}</td>
|
||||||
<td className='ri'>
|
<td className='ri'>
|
||||||
@@ -493,11 +430,9 @@ export default class Header extends TranslatedComponent {
|
|||||||
<hr />
|
<hr />
|
||||||
<ul style={{ width: '100%' }}>
|
<ul style={{ width: '100%' }}>
|
||||||
{translate('builds')} & {translate('comparisons')}
|
{translate('builds')} & {translate('comparisons')}
|
||||||
<li><Link href="#" className='block' onClick={this._showBackup.bind(this)}>{translate('backup')}</Link></li>
|
<li><Link href="#" className='block' onClick={this._showDetailedExport}>{translate('detailed export')}</Link></li>
|
||||||
<li><Link href="#" className='block' onClick={this._showDetailedExport.bind(this)}>{translate('detailed export')}</Link></li>
|
<li><Link href="#" className='block' onClick={this._showImport}>{translate('import')}</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._showDeleteAll}>{translate('delete all')}</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>
|
</ul>
|
||||||
<hr />
|
<hr />
|
||||||
<table style={{ width: 300, backgroundColor: 'transparent' }}>
|
<table style={{ width: 300, backgroundColor: 'transparent' }}>
|
||||||
@@ -529,7 +464,6 @@ export default class Header extends TranslatedComponent {
|
|||||||
Persist.addListener('deletedAll', update);
|
Persist.addListener('deletedAll', update);
|
||||||
Persist.addListener('builds', update);
|
Persist.addListener('builds', update);
|
||||||
Persist.addListener('tooltips', update);
|
Persist.addListener('tooltips', update);
|
||||||
Persist.addListener('moduleresistances', update);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -599,19 +533,13 @@ export default class Header extends TranslatedComponent {
|
|||||||
{openedMenu == 'b' ? this._getBuildsMenu() : null}
|
{openedMenu == 'b' ? this._getBuildsMenu() : null}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='l menu'>
|
{/* TODO: Enable */}
|
||||||
<div className={cn('menu-header', { selected: openedMenu == 'comp', disabled: !hasBuilds })} onClick={hasBuilds && this._openComp}>
|
{/* <div className='l menu'>
|
||||||
<StatsBars className={cn('warning', { 'warning-disabled': !hasBuilds })} /><span className='menu-item-label'>{translate('compare')}</span>
|
|
||||||
</div>
|
|
||||||
{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}>
|
<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>
|
<span className='menu-item-label'>{translate('announcements')}</span>
|
||||||
</div>
|
</div>
|
||||||
{openedMenu == 'announce' ? this._getAnnouncementsMenu() : null}
|
{openedMenu == 'announce' ? this._getAnnouncementsMenu() : null}
|
||||||
</div>
|
</div> */}
|
||||||
|
|
||||||
{window.location.origin.search('.edcd.io') >= 0 ?
|
{window.location.origin.search('.edcd.io') >= 0 ?
|
||||||
<div className='l menu'>
|
<div className='l menu'>
|
||||||
|
|||||||
@@ -1,10 +1,27 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import SlotSection from './SlotSection';
|
import SlotSection from './SlotSection';
|
||||||
import Slot from './Slot';
|
import Slot from './Slot';
|
||||||
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
|
||||||
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
||||||
import { canMount } from '../utils/SlotFunctions';
|
|
||||||
import autoBind from 'auto-bind';
|
import autoBind from 'auto-bind';
|
||||||
|
import { TYPES } from 'ed-forge/lib/src/data/slots';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets all empty slots of a ship to a item of the given size.
|
||||||
|
* @param {Ship} ship Ship to set items for
|
||||||
|
* @param {boolean} fillAll True to also fill occupied
|
||||||
|
* @param {string} type Item type
|
||||||
|
* @param {string} rating Item rating
|
||||||
|
*/
|
||||||
|
function setAllEmpty(ship, fillAll, type, rating = '') {
|
||||||
|
ship.getModules(TYPES.ANY_INTERNAL, undefined, true).forEach((slot) => {
|
||||||
|
if (slot.isEmpty() || fillAll) {
|
||||||
|
try {
|
||||||
|
// Maybe the item does not exist. Simply catch this error.
|
||||||
|
slot.setItem(type, slot.getSize(), rating);
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal slot section
|
* Internal slot section
|
||||||
@@ -23,8 +40,7 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
* Empty all slots
|
* Empty all slots
|
||||||
*/
|
*/
|
||||||
_empty() {
|
_empty() {
|
||||||
// TODO:
|
this.props.ship.getModules(TYPES.ANY_INTERNAL).forEach((slot) => slot.reset());
|
||||||
// this.props.ship.emptyInternal();
|
|
||||||
this._close();
|
this._close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,13 +49,8 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
* @param {SyntheticEvent} event Event
|
* @param {SyntheticEvent} event Event
|
||||||
*/
|
*/
|
||||||
_fillWithCargo(event) {
|
_fillWithCargo(event) {
|
||||||
let clobber = event.getModifierState('Alt');
|
const fillAll = event.getModifierState('Alt');
|
||||||
let ship = this.props.ship;
|
setAllEmpty(this.props.ship, fillAll, 'cargorack');
|
||||||
ship.internal.forEach((slot) => {
|
|
||||||
if ((clobber || !slot.m) && canMount(ship, slot, 'cr')) {
|
|
||||||
ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E'));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this._close();
|
this._close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,13 +59,8 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
* @param {SyntheticEvent} event Event
|
* @param {SyntheticEvent} event Event
|
||||||
*/
|
*/
|
||||||
_fillWithFuelTanks(event) {
|
_fillWithFuelTanks(event) {
|
||||||
let clobber = event.getModifierState('Alt');
|
const fillAll = event.getModifierState('Alt');
|
||||||
let ship = this.props.ship;
|
setAllEmpty(this.props.ship, fillAll, 'fueltank', '3');
|
||||||
ship.internal.forEach((slot) => {
|
|
||||||
if ((clobber || !slot.m) && canMount(ship, slot, 'ft')) {
|
|
||||||
ship.use(slot, ModuleUtils.findInternal('ft', slot.maxClass, 'C'));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this._close();
|
this._close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,13 +69,8 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
* @param {SyntheticEvent} event Event
|
* @param {SyntheticEvent} event Event
|
||||||
*/
|
*/
|
||||||
_fillWithLuxuryCabins(event) {
|
_fillWithLuxuryCabins(event) {
|
||||||
let clobber = event.getModifierState('Alt');
|
const fillAll = event.getModifierState('Alt');
|
||||||
let ship = this.props.ship;
|
setAllEmpty(this.props.ship, fillAll, 'passengercabins', '4');
|
||||||
ship.internal.forEach((slot) => {
|
|
||||||
if ((clobber || !slot.m) && canMount(ship, slot, 'pcq')) {
|
|
||||||
ship.use(slot, ModuleUtils.findInternal('pcq', Math.min(slot.maxClass, 6), 'B')); // Passenger cabins top out at 6
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this._close();
|
this._close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,13 +79,8 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
* @param {SyntheticEvent} event Event
|
* @param {SyntheticEvent} event Event
|
||||||
*/
|
*/
|
||||||
_fillWithFirstClassCabins(event) {
|
_fillWithFirstClassCabins(event) {
|
||||||
let clobber = event.getModifierState('Alt');
|
const fillAll = event.getModifierState('Alt');
|
||||||
let ship = this.props.ship;
|
setAllEmpty(this.props.ship, fillAll, 'passengercabins', '3');
|
||||||
ship.internal.forEach((slot) => {
|
|
||||||
if ((clobber || !slot.m) && canMount(ship, slot, 'pcm')) {
|
|
||||||
ship.use(slot, ModuleUtils.findInternal('pcm', Math.min(slot.maxClass, 6), 'C')); // Passenger cabins top out at 6
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this._close();
|
this._close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,13 +89,8 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
* @param {SyntheticEvent} event Event
|
* @param {SyntheticEvent} event Event
|
||||||
*/
|
*/
|
||||||
_fillWithBusinessClassCabins(event) {
|
_fillWithBusinessClassCabins(event) {
|
||||||
let clobber = event.getModifierState('Alt');
|
const fillAll = event.getModifierState('Alt');
|
||||||
let ship = this.props.ship;
|
setAllEmpty(this.props.ship, fillAll, 'passengercabins', '2');
|
||||||
ship.internal.forEach((slot) => {
|
|
||||||
if ((clobber || !slot.m) && canMount(ship, slot, 'pci')) {
|
|
||||||
ship.use(slot, ModuleUtils.findInternal('pci', Math.min(slot.maxClass, 6), 'D')); // Passenger cabins top out at 6
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this._close();
|
this._close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,13 +99,8 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
* @param {SyntheticEvent} event Event
|
* @param {SyntheticEvent} event Event
|
||||||
*/
|
*/
|
||||||
_fillWithEconomyClassCabins(event) {
|
_fillWithEconomyClassCabins(event) {
|
||||||
let clobber = event.getModifierState('Alt');
|
const fillAll = event.getModifierState('Alt');
|
||||||
let ship = this.props.ship;
|
setAllEmpty(this.props.ship, fillAll, 'passengercabins', '1');
|
||||||
ship.internal.forEach((slot) => {
|
|
||||||
if ((clobber || !slot.m) && canMount(ship, slot, 'pce')) {
|
|
||||||
ship.use(slot, ModuleUtils.findInternal('pce', Math.min(slot.maxClass, 6), 'E')); // Passenger cabins top out at 6
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this._close();
|
this._close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,16 +109,8 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
* @param {SyntheticEvent} event Event
|
* @param {SyntheticEvent} event Event
|
||||||
*/
|
*/
|
||||||
_fillWithCells(event) {
|
_fillWithCells(event) {
|
||||||
let clobber = event.getModifierState('Alt');
|
const fillAll = event.getModifierState('Alt');
|
||||||
let ship = this.props.ship;
|
setAllEmpty(this.props.ship, fillAll, 'scb', '5');
|
||||||
let chargeCap = 0; // Capacity of single activation
|
|
||||||
ship.internal.forEach(function(slot) {
|
|
||||||
if ((clobber && !(slot.m && ModuleUtils.isShieldGenerator(slot.m.grp)) || !slot.m) && canMount(ship, slot, 'scb')) {
|
|
||||||
ship.use(slot, ModuleUtils.findInternal('scb', slot.maxClass, 'A'));
|
|
||||||
ship.setSlotEnabled(slot, chargeCap <= ship.shieldStrength); // Don't waste cell capacity on overcharge
|
|
||||||
chargeCap += slot.m.recharge;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this._close();
|
this._close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,13 +119,8 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
* @param {SyntheticEvent} event Event
|
* @param {SyntheticEvent} event Event
|
||||||
*/
|
*/
|
||||||
_fillWithArmor(event) {
|
_fillWithArmor(event) {
|
||||||
let clobber = event.getModifierState('Alt');
|
const fillAll = event.getModifierState('Alt');
|
||||||
let ship = this.props.ship;
|
setAllEmpty(this.props.ship, fillAll, 'hrp', '2');
|
||||||
ship.internal.forEach((slot) => {
|
|
||||||
if ((clobber || !slot.m) && canMount(ship, slot, 'hr')) {
|
|
||||||
ship.use(slot, ModuleUtils.findInternal('hr', Math.min(slot.maxClass, 5), 'D')); // Hull reinforcements top out at 5D
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this._close();
|
this._close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,23 +129,11 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
* @param {SyntheticEvent} event Event
|
* @param {SyntheticEvent} event Event
|
||||||
*/
|
*/
|
||||||
_fillWithModuleReinforcementPackages(event) {
|
_fillWithModuleReinforcementPackages(event) {
|
||||||
let clobber = event.getModifierState('Alt');
|
const fillAll = event.getModifierState('Alt');
|
||||||
let ship = this.props.ship;
|
setAllEmpty(this.props.ship, fillAll, 'mrp', '2');
|
||||||
ship.internal.forEach((slot) => {
|
|
||||||
if ((clobber || !slot.m) && canMount(ship, slot, 'mrp')) {
|
|
||||||
ship.use(slot, ModuleUtils.findInternal('mrp', Math.min(slot.maxClass, 5), 'D')); // Module reinforcements top out at 5D
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this._close();
|
this._close();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Empty all on section header right click
|
|
||||||
*/
|
|
||||||
_contextMenu() {
|
|
||||||
this._empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate the slot React Components
|
* Generate the slot React Components
|
||||||
* @return {Array} Array of Slots
|
* @return {Array} Array of Slots
|
||||||
@@ -219,7 +180,7 @@ export default class InternalSlotSection extends SlotSection {
|
|||||||
<li className='lc' tabIndex='0' onClick={this._fillWithEconomyClassCabins}>{translate('pce')}</li>
|
<li className='lc' tabIndex='0' onClick={this._fillWithEconomyClassCabins}>{translate('pce')}</li>
|
||||||
<li className='lc' tabIndex='0' onClick={this._fillWithBusinessClassCabins}>{translate('pci')}</li>
|
<li className='lc' tabIndex='0' onClick={this._fillWithBusinessClassCabins}>{translate('pci')}</li>
|
||||||
<li className='lc' tabIndex='0' onClick={this._fillWithFirstClassCabins} onKeyDown={ship.luxuryCabins ? '' : this._keyDown}>{translate('pcm')}</li>
|
<li className='lc' tabIndex='0' onClick={this._fillWithFirstClassCabins} onKeyDown={ship.luxuryCabins ? '' : this._keyDown}>{translate('pcm')}</li>
|
||||||
{ ship.luxuryCabins ? <li className='lc' tabIndex='0' onClick={this._fillWithLuxuryCabins}>{translate('pcq')}</li> : ''}
|
{ ship.readMeta('luxuryCabins') ? <li className='lc' tabIndex='0' onClick={this._fillWithLuxuryCabins}>{translate('pcq')}</li> : ''}
|
||||||
<li className='optional-hide' style={{ textAlign: 'center', marginTop: '1em' }}>{translate('PHRASE_ALT_ALL')}</li>
|
<li className='optional-hide' style={{ textAlign: 'center', marginTop: '1em' }}>{translate('PHRASE_ALT_ALL')}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>;
|
</div>;
|
||||||
|
|||||||
@@ -1,120 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import TranslatedComponent from './TranslatedComponent';
|
|
||||||
import LineChart from '../components/LineChart';
|
|
||||||
import Slider from '../components/Slider';
|
|
||||||
import * as Calc from '../shipyard/Calculations';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Jump range for a given ship
|
|
||||||
*/
|
|
||||||
export default class JumpRange extends TranslatedComponent {
|
|
||||||
static propTypes = {
|
|
||||||
ship: PropTypes.object.isRequired,
|
|
||||||
code: PropTypes.string.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
* @param {Object} props React Component properties
|
|
||||||
* @param {Object} context React Component context
|
|
||||||
*/
|
|
||||||
constructor(props, context) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
const ship = this.props.ship;
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
fuelLevel: 1,
|
|
||||||
calcJumpRangeFunc: this._calcJumpRange.bind(this, ship)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the state if our ship changes
|
|
||||||
* @param {Object} nextProps Incoming/Next properties
|
|
||||||
* @param {Object} nextContext Incoming/Next conext
|
|
||||||
* @return {boolean} Returns true if the component should be rerendered
|
|
||||||
*/
|
|
||||||
componentWillReceiveProps(nextProps, nextContext) {
|
|
||||||
if (nextProps.code != this.props.code) {
|
|
||||||
this.setState({ fuelLevel: 1,
|
|
||||||
calcJumpRangeFunc: this._calcJumpRange.bind(this, nextProps.ship) });
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate the jump range this ship at a given cargo
|
|
||||||
* @param {Object} ship The ship
|
|
||||||
* @param {Object} cargo The cargo
|
|
||||||
* @return {number} The jump range
|
|
||||||
*/
|
|
||||||
_calcJumpRange(ship, cargo) {
|
|
||||||
// Obtain the FSD for this ship
|
|
||||||
const fsd = ship.standard[2].m;
|
|
||||||
|
|
||||||
const fuel = this.state.fuelLevel * ship.fuelCapacity;
|
|
||||||
|
|
||||||
// Obtain the jump range
|
|
||||||
return Calc.jumpRange(ship.unladenMass + fuel + cargo, fsd, fuel, ship);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update fuel level
|
|
||||||
* @param {number} fuelLevel Fuel level 0 - 1
|
|
||||||
*/
|
|
||||||
_fuelChange(fuelLevel) {
|
|
||||||
this.setState({
|
|
||||||
fuelLevel,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Render engine profile
|
|
||||||
* @return {React.Component} contents
|
|
||||||
*/
|
|
||||||
render() {
|
|
||||||
const { language, onWindowResize, sizeRatio, tooltip, termtip } = this.context;
|
|
||||||
const { formats, translate, units } = language;
|
|
||||||
const { ship } = this.props;
|
|
||||||
const { fuelLevel } = this.state;
|
|
||||||
|
|
||||||
const code = ship.toString() + '.' + ship.getModificationsString() + '.' + fuelLevel;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<span>
|
|
||||||
<h1>{translate('jump range')}</h1>
|
|
||||||
<LineChart
|
|
||||||
xMax={ship.cargoCapacity}
|
|
||||||
yMax={ship.unladenRange}
|
|
||||||
xLabel={translate('cargo')}
|
|
||||||
xUnit={translate('T')}
|
|
||||||
yLabel={translate('jump range')}
|
|
||||||
yUnit={translate('LY')}
|
|
||||||
func={this.state.calcJumpRangeFunc}
|
|
||||||
points={200}
|
|
||||||
code={code}
|
|
||||||
/>
|
|
||||||
<h3>{translate('fuel carried')}: {formats.f2(fuelLevel * ship.fuelCapacity)}{units.T}</h3>
|
|
||||||
<table style={{ width: '100%', lineHeight: '1em', backgroundColor: 'transparent' }}>
|
|
||||||
<tbody >
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<Slider
|
|
||||||
axis={true}
|
|
||||||
onChange={this._fuelChange.bind(this)}
|
|
||||||
axisUnit={translate('T')}
|
|
||||||
percent={fuelLevel}
|
|
||||||
max={ship.fuelCapacity}
|
|
||||||
scale={sizeRatio}
|
|
||||||
onResize={onWindowResize}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,92 +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>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,90 +2,23 @@ import React from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import cn from 'classnames';
|
import cn from 'classnames';
|
||||||
import TranslatedComponent from './TranslatedComponent';
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
import Router from '../Router';
|
|
||||||
import Persist from '../stores/Persist';
|
import Persist from '../stores/Persist';
|
||||||
import { Ships } from 'coriolis-data/dist';
|
import autoBind from 'auto-bind';
|
||||||
import Ship from '../shipyard/Ship';
|
import { isArray } from 'lodash';
|
||||||
import { ModuleNameToGroup, Insurance } from '../shipyard/Constants';
|
import { Ship } from 'ed-forge';
|
||||||
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';
|
|
||||||
|
|
||||||
const zlib = require('pako');
|
const STATE = {
|
||||||
|
READY: 0,
|
||||||
const textBuildRegex = new RegExp('^\\[([\\w \\-]+)\\]\n');
|
PARSED: 1,
|
||||||
const lineRegex = new RegExp('^([\\dA-Z]{1,2}): (\\d)([A-I])[/]?([FGT])?([SD])? ([\\w\\- ]+)');
|
ERROR: 2,
|
||||||
const mountMap = { 'H': 4, 'L': 3, 'M': 2, 'S': 1, 'U': 0 };
|
};
|
||||||
const standardMap = { 'RB': 0, 'TM': 1, 'FH': 2, 'EC': 3, 'PC': 4, 'SS': 5, 'FS': 6 };
|
|
||||||
const bhMap = { 'lightweight alloy': 0, 'reinforced alloy': 1, 'military grade composite': 2, 'mirrored surface composite': 3, 'reactive surface composite': 4 };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check is slot is empty
|
|
||||||
* @param {Object} slot Slot model
|
|
||||||
* @return {Boolean} True if empty
|
|
||||||
*/
|
|
||||||
function isEmptySlot(slot) {
|
|
||||||
return slot.maxClass == this && slot.m === null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if a build is valid
|
|
||||||
* @param {string} shipId Ship ID
|
|
||||||
* @param {string} code Serialzied ship build 'code'
|
|
||||||
* @param {string} name Build name
|
|
||||||
* @throws {string} If build is not valid
|
|
||||||
*/
|
|
||||||
function validateBuild(shipId, code, name) {
|
|
||||||
let shipData = Ships[shipId];
|
|
||||||
|
|
||||||
if (!shipData) {
|
|
||||||
throw '"' + shipId + '" is not a valid Ship Id!';
|
|
||||||
}
|
|
||||||
if (typeof name != 'string' || name.length == 0) {
|
|
||||||
throw shipData.properties.name + ' build "' + name + '" must be a string at least 1 character long!';
|
|
||||||
}
|
|
||||||
if (typeof code != 'string' || code.length < 10) {
|
|
||||||
throw shipData.properties.name + ' build "' + name + '" is not valid!';
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
let ship = new Ship(shipId, shipData.properties, shipData.slots);
|
|
||||||
ship.buildFrom(code);
|
|
||||||
} catch (e) {
|
|
||||||
throw shipData.properties.name + ' build "' + name + '" is not valid!';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert a ship-loadout JSON object to a Coriolis build
|
|
||||||
* @param {Object} detailedBuild ship-loadout
|
|
||||||
* @return {Object} Coriolis build
|
|
||||||
*/
|
|
||||||
function detailedJsonToBuild(detailedBuild) {
|
|
||||||
let ship;
|
|
||||||
if (!detailedBuild.name) {
|
|
||||||
throw 'Build Name missing!';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!detailedBuild.name.trim()) {
|
|
||||||
throw 'Build Name must be a string at least 1 character long!';
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
ship = fromDetailedBuild(detailedBuild);
|
|
||||||
} catch (e) {
|
|
||||||
throw detailedBuild.ship + ' Build "' + detailedBuild.name + '": Invalid data';
|
|
||||||
}
|
|
||||||
|
|
||||||
return { shipId: ship.id, name: detailedBuild.name, code: ship.toString() };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Import Modal
|
* Import Modal
|
||||||
*/
|
*/
|
||||||
export default class ModalImport extends TranslatedComponent {
|
export default class ModalImport extends TranslatedComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
importString: PropTypes.string, // Optional: Default data for import modal
|
||||||
builds: PropTypes.object, // Optional: Import object
|
builds: PropTypes.object, // Optional: Import object
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -95,228 +28,36 @@ export default class ModalImport extends TranslatedComponent {
|
|||||||
*/
|
*/
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
autoBind(this);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
builds: props.builds,
|
status: STATE.READY,
|
||||||
canEdit: !props.builds,
|
builds: props.builds || [],
|
||||||
loadoutEvent: null,
|
|
||||||
comparisons: null,
|
|
||||||
shipDiscount: null,
|
|
||||||
moduleDiscount: null,
|
|
||||||
errorMsg: null,
|
|
||||||
importString: null,
|
|
||||||
importValid: false,
|
|
||||||
insurance: null
|
|
||||||
};
|
};
|
||||||
|
|
||||||
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
|
* Import SLEF formatted builds. Sets state to a map of the builds on success
|
||||||
* @param {Object} data Loadout event
|
* and flags if there was only a single build.
|
||||||
* @throws {string} If import fails
|
*
|
||||||
*/
|
* @param {string} importData - Array of the list of builds.
|
||||||
_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
|
|
||||||
* @throws {string} If import fails
|
|
||||||
*/
|
|
||||||
_importBackup(importData) {
|
|
||||||
if (importData.builds && typeof importData.builds == 'object') {
|
|
||||||
for (let shipId in importData.builds) {
|
|
||||||
for (let buildName in importData.builds[shipId]) {
|
|
||||||
try {
|
|
||||||
validateBuild(shipId, importData.builds[shipId][buildName], buildName);
|
|
||||||
} catch (err) {
|
|
||||||
delete importData.builds[shipId][buildName];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.setState({ builds: importData.builds });
|
|
||||||
} else {
|
|
||||||
throw 'builds must be an object!';
|
|
||||||
}
|
|
||||||
if (importData.comparisons) {
|
|
||||||
for (let compName in importData.comparisons) {
|
|
||||||
let comparison = importData.comparisons[compName];
|
|
||||||
for (let i = 0, l = comparison.builds.length; i < l; i++) {
|
|
||||||
let build = comparison.builds[i];
|
|
||||||
if (!importData.builds[build.shipId] || !importData.builds[build.shipId][build.buildName]) {
|
|
||||||
throw build.shipId + ' build "' + build.buildName + '" data is missing!';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.setState({ comparisons: importData.comparisons });
|
|
||||||
}
|
|
||||||
// Check for old/deprecated discounts
|
|
||||||
if (importData.discounts instanceof Array && importData.discounts.length == 2) {
|
|
||||||
this.setState({ shipDiscount: importData.discounts[0], moduleDiscount: importData.discounts[1] });
|
|
||||||
}
|
|
||||||
// Check for ship discount
|
|
||||||
if (!isNaN(importData.shipDiscount)) {
|
|
||||||
this.setState({ shipDiscount: importData.shipDiscount * 1 });
|
|
||||||
}
|
|
||||||
// Check for module discount
|
|
||||||
if (!isNaN(importData.moduleDiscount)) {
|
|
||||||
this.setState({ moduleDiscount: importData.moduleDiscount * 1 });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof importData.insurance == 'string') {
|
|
||||||
let insurance = importData.insurance.toLowerCase();
|
|
||||||
|
|
||||||
if (Insurance[insurance] !== undefined) {
|
|
||||||
this.setState({ insurance });
|
|
||||||
} else {
|
|
||||||
throw 'Invalid insurance type: ' + insurance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Import an array of ship-loadout objects / builds
|
|
||||||
* @param {Array} importArr Array of ship-loadout JSON Schema builds
|
|
||||||
*/
|
|
||||||
_importDetailedArray(importArr) {
|
|
||||||
let builds = {};
|
|
||||||
for (let i = 0, l = importArr.length; i < l; i++) {
|
|
||||||
let build = detailedJsonToBuild(importArr[i]);
|
|
||||||
if (!builds[build.shipId]) {
|
|
||||||
builds[build.shipId] = {};
|
|
||||||
}
|
|
||||||
builds[build.shipId][build.name] = build.code;
|
|
||||||
}
|
|
||||||
this.setState({ builds });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Import a build direct from the companion API
|
|
||||||
* @param {string} build JSON from the companion API information
|
|
||||||
* @throws {string} if parse/import fails
|
|
||||||
*/
|
|
||||||
_importCompanionApiBuild(build) {
|
|
||||||
const shipModel = CompanionApiUtils.shipModelFromJson(build);
|
|
||||||
const ship = CompanionApiUtils.shipFromJson(build);
|
|
||||||
|
|
||||||
let builds = {};
|
|
||||||
builds[shipModel] = {};
|
|
||||||
builds[shipModel]['Imported ' + Ships[shipModel].properties.name] = ship.toString();
|
|
||||||
this.setState({ builds, singleBuild: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Import a text build from ED Shipyard
|
|
||||||
* @param {string} buildStr Build string
|
|
||||||
* @throws {string} If parse / import fails
|
* @throws {string} If parse / import fails
|
||||||
*/
|
*/
|
||||||
_importTextBuild(buildStr) {
|
_importSlefBuilds(importData) {
|
||||||
let buildName = textBuildRegex.exec(buildStr)[1].trim();
|
const builds = importData.reduce((memo, { data }) => {
|
||||||
let shipName = buildName.toLowerCase();
|
const shipModel = shipModelFromJson(data);
|
||||||
let shipId = null;
|
const ship = shipFromLoadoutJSON(data);
|
||||||
|
const shipTemplate = Ships[shipModel];
|
||||||
|
const shipName = data.ShipName || shipTemplate.properties.name;
|
||||||
|
|
||||||
for (let sId in Ships) {
|
const key = `Imported ${shipName}`;
|
||||||
if (Ships[sId].properties.name.toLowerCase() == shipName) {
|
memo[shipModel] = {};
|
||||||
shipId = sId;
|
memo[shipModel][key] = ship.toString();
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!shipId) {
|
return memo;
|
||||||
throw 'No such ship found: "' + buildName + '"';
|
}, {});
|
||||||
}
|
|
||||||
|
|
||||||
let lines = buildStr.split('\n');
|
this.setState({ builds, singleBuild: Object.keys(builds).length === 1 });
|
||||||
let ship = new Ship(shipId, Ships[shipId].properties, Ships[shipId].slots);
|
|
||||||
ship.buildWith(null);
|
|
||||||
|
|
||||||
for (let i = 1; i < lines.length; i++) {
|
|
||||||
let line = lines[i].trim();
|
|
||||||
|
|
||||||
if (!line) { continue; }
|
|
||||||
if (line.substring(0, 3) == '---') { break; }
|
|
||||||
|
|
||||||
let parts = lineRegex.exec(line);
|
|
||||||
|
|
||||||
if (!parts) { throw 'Error parsing: "' + line + '"'; }
|
|
||||||
|
|
||||||
let typeSize = parts[1];
|
|
||||||
let cl = parts[2];
|
|
||||||
let rating = parts[3];
|
|
||||||
let mount = parts[4];
|
|
||||||
let missile = parts[5];
|
|
||||||
let name = parts[6].trim();
|
|
||||||
let slot, group;
|
|
||||||
|
|
||||||
if (isNaN(typeSize)) { // Standard or Hardpoint
|
|
||||||
if (typeSize.length == 1) { // Hardpoint
|
|
||||||
let slotClass = mountMap[typeSize];
|
|
||||||
|
|
||||||
if (cl > slotClass) { throw cl + rating + ' ' + name + ' exceeds slot size: "' + line + '"'; }
|
|
||||||
|
|
||||||
slot = ship.hardpoints.find(isEmptySlot, slotClass);
|
|
||||||
|
|
||||||
if (!slot) { throw 'No hardpoint slot available for: "' + line + '"'; }
|
|
||||||
|
|
||||||
group = ModuleNameToGroup[name.toLowerCase()];
|
|
||||||
|
|
||||||
let hp = ModuleUtils.findHardpoint(group, cl, rating, group ? null : name, mount, missile);
|
|
||||||
|
|
||||||
if (!hp) { throw 'Unknown component: "' + line + '"'; }
|
|
||||||
|
|
||||||
ship.use(slot, hp, true);
|
|
||||||
} else if (typeSize == 'BH') {
|
|
||||||
let bhId = bhMap[name.toLowerCase()];
|
|
||||||
|
|
||||||
if (bhId === undefined) { throw 'Unknown bulkhead: "' + line + '"'; }
|
|
||||||
|
|
||||||
ship.useBulkhead(bhId, true);
|
|
||||||
} else if (standardMap[typeSize] != undefined) {
|
|
||||||
let standardIndex = standardMap[typeSize];
|
|
||||||
|
|
||||||
if (ship.standard[standardIndex].maxClass < cl) { throw name + ' exceeds max class for the ' + ship.name; }
|
|
||||||
|
|
||||||
ship.use(ship.standard[standardIndex], ModuleUtils.standard(standardIndex, cl + rating), true);
|
|
||||||
} else {
|
|
||||||
throw 'Unknown component: "' + line + '"';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (cl > typeSize) { throw cl + rating + ' ' + name + ' exceeds slot size: "' + line + '"'; }
|
|
||||||
|
|
||||||
slot = ship.internal.find(isEmptySlot, typeSize);
|
|
||||||
|
|
||||||
if (!slot) { throw 'No internal slot available for: "' + line + '"'; }
|
|
||||||
|
|
||||||
group = ModuleNameToGroup[name.toLowerCase()];
|
|
||||||
|
|
||||||
let intComp = ModuleUtils.findInternal(group, cl, rating, group ? null : name);
|
|
||||||
|
|
||||||
if (!intComp) { throw 'Unknown component: "' + line + '"'; }
|
|
||||||
|
|
||||||
ship.use(slot, intComp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let builds = {};
|
|
||||||
builds[shipId] = {};
|
|
||||||
builds[shipId]['Imported ' + buildName] = ship.toString();
|
|
||||||
this.setState({ builds, singleBuild: true });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -324,164 +65,50 @@ export default class ModalImport extends TranslatedComponent {
|
|||||||
* @param {SyntheticEvent} event Event
|
* @param {SyntheticEvent} event Event
|
||||||
* @throws {string} If validation fails
|
* @throws {string} If validation fails
|
||||||
*/
|
*/
|
||||||
_validateImport(event) {
|
_parse(event) {
|
||||||
let importData = null;
|
const importString = event.target.value.trim();
|
||||||
let importString = event.target.value.trim();
|
|
||||||
this.setState({
|
|
||||||
builds: null,
|
|
||||||
comparisons: null,
|
|
||||||
shipDiscount: null,
|
|
||||||
moduleDiscount: null,
|
|
||||||
errorMsg: null,
|
|
||||||
importValid: false,
|
|
||||||
insurance: null,
|
|
||||||
singleBuild: false,
|
|
||||||
importString,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!importString) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (textBuildRegex.test(importString)) { // E:D Shipyard build text
|
let data = JSON.parse(importString);
|
||||||
this._importTextBuild(importString);
|
if (!isArray(data)) {
|
||||||
} else { // JSON Build data
|
data = [data];
|
||||||
importData = JSON.parse(importString);
|
|
||||||
|
|
||||||
if (!importData || typeof importData != 'object') {
|
|
||||||
throw 'Must be an object or array!';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (importData.modules != null && importData.modules.Armour != null) { // Only the companion API has this information
|
const ships = data.map((item) => {
|
||||||
this._importCompanionApiBuild(importData); // Single sihp definition
|
try {
|
||||||
} else if (importData.ship != null && importData.ship.modules != null && importData.ship.modules.Armour != null) { // Only the companion API has this information
|
return new Ship(item.data ? item.data : item);
|
||||||
this._importCompanionApiBuild(importData.ship); // Complete API dump
|
} catch (err) {
|
||||||
} else if (importData instanceof Array) { // Must be detailed export json
|
return err;
|
||||||
this._importDetailedArray(importData);
|
}
|
||||||
} 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({ ships, status: STATE.PARSED });
|
||||||
this.setState({ singleBuild: true });
|
} catch (err) {
|
||||||
} else if (importData.Modules != null && importData.Modules[0] != null) {
|
this.setState({ err, status: STATE.ERROR });
|
||||||
this._importLoadout(importData);
|
|
||||||
} else { // Using Backup JSON
|
|
||||||
this._importBackup(importData);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
|
||||||
console.log(e);
|
|
||||||
this.setState({ errorMsg: (typeof e == 'string') ? e : 'Cannot Parse the data!' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({ importValid: true });
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process imported data
|
* Process imported data
|
||||||
*/
|
*/
|
||||||
_process() {
|
_process() {
|
||||||
let builds = null, comparisons = null;
|
for (const build of this.state.builds) {
|
||||||
|
if (!build instanceof Error) {
|
||||||
if (this.state.loadoutEvent) {
|
Persist.saveBuild(build.Ship, build.CoriolisBuildName || build.ShipName, build.compress());
|
||||||
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;
|
|
||||||
let shipId = Object.keys(builds)[0];
|
|
||||||
let name = Object.keys(builds[shipId])[0];
|
|
||||||
Router.go(outfitURL(shipId, builds[shipId][name], name));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (this.state.builds) {
|
|
||||||
builds = {}; // Create new builds object such that orginal name retained, but can be renamed
|
|
||||||
for (let shipId in this.state.builds) {
|
|
||||||
let shipbuilds = this.state.builds[shipId];
|
|
||||||
builds[shipId] = {};
|
|
||||||
for (let buildName in shipbuilds) {
|
|
||||||
builds[shipId][buildName] = {
|
|
||||||
code: shipbuilds[buildName],
|
|
||||||
useName: buildName
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.setState({ builds: [], status: STATE.READY });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.comparisons) {
|
|
||||||
comparisons = {};
|
|
||||||
for (let name in this.state.comparisons) {
|
|
||||||
comparisons[name] = Object.assign({ useName: name }, this.state.comparisons[name]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({ processed: true, builds, comparisons });
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Import parsed, processed data and save
|
|
||||||
*/
|
|
||||||
_import() {
|
|
||||||
let state = this.state;
|
|
||||||
if (state.builds) {
|
|
||||||
let builds = state.builds;
|
|
||||||
for (let shipId in builds) {
|
|
||||||
for (let buildName in builds[shipId]) {
|
|
||||||
let build = builds[shipId][buildName];
|
|
||||||
let name = build.useName.trim();
|
|
||||||
if (name) {
|
|
||||||
Persist.saveBuild(shipId, name, build.code);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.comparisons) {
|
|
||||||
let comparisons = state.comparisons;
|
|
||||||
for (let comp in comparisons) {
|
|
||||||
let comparison = comparisons[comp];
|
|
||||||
let useName = comparison.useName.trim();
|
|
||||||
if (useName) {
|
|
||||||
Persist.saveComparison(useName, comparison.builds, comparison.facets);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.shipDiscount !== undefined) {
|
|
||||||
Persist.setShipDiscount(state.shipDiscount);
|
|
||||||
}
|
|
||||||
if (state.moduleDiscount !== undefined) {
|
|
||||||
Persist.setModuleDiscount(state.moduleDiscount);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.insurance) {
|
|
||||||
Persist.setInsurance(state.insurance);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.context.hideModal();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Capture build name changes
|
* Capture build name changes
|
||||||
* @param {Object} item Build/Comparison import object
|
* @param {Object} index Build/Comparison import object
|
||||||
* @param {SyntheticEvent} e Event
|
* @param {SyntheticEvent} event Event
|
||||||
*/
|
*/
|
||||||
_changeName(item, e) {
|
_changeName(index, event) {
|
||||||
item.useName = e.target.value;
|
const { builds } = this.state;
|
||||||
this.forceUpdate();
|
builds[index].CoriolisBuildName = event.target.value.trim();
|
||||||
|
this.setState({ builds });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* If imported data is already provided process immediately on mount
|
|
||||||
*/
|
|
||||||
componentWillMount() {
|
|
||||||
if (this.props.builds) {
|
|
||||||
this._process();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* If textarea is shown focus on mount
|
* If textarea is shown focus on mount
|
||||||
*/
|
*/
|
||||||
@@ -496,77 +123,34 @@ export default class ModalImport extends TranslatedComponent {
|
|||||||
* @return {React.Component} Modal contents
|
* @return {React.Component} Modal contents
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
let translate = this.context.language.translate;
|
const { translate } = this.context.language;
|
||||||
let state = this.state;
|
const { status, builds, err } = this.state;
|
||||||
let importStage;
|
|
||||||
|
|
||||||
if (!state.processed) {
|
const buildRows = builds.map((build, i) => {
|
||||||
importStage = (
|
if (build instanceof Error) {
|
||||||
|
return <tr key={i} className='cb'>
|
||||||
|
<td colSpan={3} className='warning'>Error: {build.name}</td>
|
||||||
|
</tr>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const exists = Persist.hasBuild(build.Ship, build.CoriolisBuildName);
|
||||||
|
const saveName = build.CoriolisBuildName || build.ShipName;
|
||||||
|
return <tr key={i} className='cb'>
|
||||||
|
<td>{translate(build.Ship)}</td>
|
||||||
|
<td><input type='text' onChange={this._changeName.bind(this, i)} value={saveName}/></td>
|
||||||
|
<td style={{ textAlign: 'center' }} className={cn('cap', { warning: exists, disabled: saveName === '' })}>
|
||||||
|
{translate(saveName === '' ? 'skip' : (exists ? 'overwrite' : 'create'))}
|
||||||
|
</td>
|
||||||
|
</tr>;
|
||||||
|
});
|
||||||
|
|
||||||
|
return <div className='modal' onClick={ (e) => e.stopPropagation() }>
|
||||||
|
<h2 >{translate('import')}</h2>
|
||||||
<div>
|
<div>
|
||||||
<textarea spellCheck={false} 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._parse} defaultValue={this.state.importString} placeholder={translate('PHRASE_IMPORT')} />
|
||||||
<button id='proceed' className='l cap' onClick={this._process} disabled={!state.importValid} >{translate('proceed')}</button>
|
{status === STATE.ERROR && <div className='l warning' style={{ marginLeft:'3em' }}>{err.toString()}</div>}
|
||||||
<div className='l warning' style={{ marginLeft:'3em' }}>{state.errorMsg}</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
{builds.length && <div>
|
||||||
} else {
|
|
||||||
let comparisonTable, edit, buildRows = [];
|
|
||||||
if (state.comparisons) {
|
|
||||||
let comparisonRows = [];
|
|
||||||
|
|
||||||
for (let name in state.comparisons) {
|
|
||||||
let comparison = state.comparisons[name];
|
|
||||||
let hasComparison = Persist.hasComparison(comparison.useName);
|
|
||||||
comparisonRows.push(
|
|
||||||
<tr key={name} className='cb'>
|
|
||||||
<td>
|
|
||||||
<input type='text' onChange={this._changeName.bind(this, comparison)} value={comparison.useName}/>
|
|
||||||
</td>
|
|
||||||
<td style={{ textAlign:'center' }} className={ cn('cap', { warning: hasComparison, disabled: comparison.useName == '' }) }>
|
|
||||||
{translate(comparison.useName == '' ? 'skip' : (hasComparison ? 'overwrite' : 'create'))}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
comparisonTable = (
|
|
||||||
<table className='l' style={{ overflow:'hidden', margin: '1em 0', width: '100%' }} >
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th style={{ textAlign:'left' }}>{translate('comparison')}</th>
|
|
||||||
<th>{translate('action')}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{comparisonRows}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(this.state.canEdit) {
|
|
||||||
edit = <button className='l cap' style={{ marginLeft: '2em' }} onClick={() => this.setState({ processed: false })}>{translate('edit data')}</button>;
|
|
||||||
}
|
|
||||||
|
|
||||||
let builds = this.state.builds;
|
|
||||||
for (let shipId in builds) {
|
|
||||||
let shipBuilds = builds[shipId];
|
|
||||||
for (let buildName in shipBuilds) {
|
|
||||||
let b = shipBuilds[buildName];
|
|
||||||
let hasBuild = Persist.hasBuild(shipId, b.useName);
|
|
||||||
buildRows.push(
|
|
||||||
<tr key={shipId + buildName} className='cb'>
|
|
||||||
<td>{Ships[shipId].properties.name}</td>
|
|
||||||
<td><input type='text' onChange={this._changeName.bind(this, b)} value={b.useName}/></td>
|
|
||||||
<td style={{ textAlign: 'center' }} className={cn('cap', { warning: hasBuild, disabled: b.useName == '' })}>
|
|
||||||
{translate(b.useName == '' ? 'skip' : (hasBuild ? 'overwrite' : 'create'))}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
importStage = (
|
|
||||||
<div>
|
|
||||||
<table className='l' style={{ overflow:'hidden', margin: '1em 0', width: '100%' }}>
|
<table className='l' style={{ overflow:'hidden', margin: '1em 0', width: '100%' }}>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -579,17 +163,14 @@ export default class ModalImport extends TranslatedComponent {
|
|||||||
{buildRows}
|
{buildRows}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{comparisonTable}
|
</div>}
|
||||||
<button id='import' className='cl l' onClick={this._import}><Download/> {translate('import')}</button>
|
<button id='proceed' className='l cap' onClick={this._process}
|
||||||
{edit}
|
disabled={status !== STATE.PARSED} >
|
||||||
</div>
|
{translate('proceed')}
|
||||||
);
|
</button>
|
||||||
}
|
<button className={'r dismiss cap'} onClick={this.context.hideModal}>
|
||||||
|
{translate('close')}
|
||||||
return <div className='modal' onClick={ (e) => e.stopPropagation() }>
|
</button>
|
||||||
<h2 >{translate('import')}</h2>
|
|
||||||
{importStage}
|
|
||||||
<button className={'r dismiss cap'} onClick={this.context.hideModal}>{translate('close')}</button>
|
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,140 +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: '...',
|
|
||||||
ship: this.props.ship,
|
|
||||||
authenticatedStatus: 'Checking...'
|
|
||||||
};
|
|
||||||
this.orbisCategory = this.orbisCategory.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send ship to Orbis.zone
|
|
||||||
* @param {SyntheticEvent} e React Event
|
|
||||||
*/
|
|
||||||
sendToOrbis(e) {
|
|
||||||
const target = e.target;
|
|
||||||
target.disabled = true;
|
|
||||||
this.setState({ orbisUrl: 'Sending...' }, () => {
|
|
||||||
orbisUpload(this.props.ship, this.state.orbisCreds)
|
|
||||||
.then(orbisUrl => {
|
|
||||||
target.disabled = false;
|
|
||||||
this.setState({ orbisUrl });
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
target.disabled = false;
|
|
||||||
this.setState({ orbisUrl: 'Error - ' + err });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get Orbis.zone auth status
|
|
||||||
* @returns {Object} auth status
|
|
||||||
*/
|
|
||||||
getOrbisAuthStatus() {
|
|
||||||
return fetch('https://orbis.zone/api/checkauth', {
|
|
||||||
credentials: 'include',
|
|
||||||
mode: 'cors'
|
|
||||||
})
|
|
||||||
.then(data => data.json())
|
|
||||||
.then(res => {
|
|
||||||
this.setState({ authenticatedStatus: res.status || res.error });
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.error(err);
|
|
||||||
this.setState({ authenticatedStatus: err.message });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handler for changing cmdr name
|
|
||||||
* @param {SyntheticEvent} e React Event
|
|
||||||
*/
|
|
||||||
orbisPasswordHandler(e) {
|
|
||||||
let password = e.target.value;
|
|
||||||
this.setState({ orbisCreds: { email: this.state.orbisCreds.email, password } }, () => {
|
|
||||||
Persist.setOrbisCreds(this.state.orbisCreds);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handler for changing cmdr name
|
|
||||||
* @param {SyntheticEvent} e React Event
|
|
||||||
*/
|
|
||||||
orbisUsername(e) {
|
|
||||||
let orbisUsername = e.target.value;
|
|
||||||
this.setState({ orbisCreds: { email: orbisUsername, password: this.state.orbisCreds.password } }, () => {
|
|
||||||
Persist.setOrbisCreds(this.state.orbisCreds);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handler for changing category
|
|
||||||
* @param {SyntheticEvent} e React Event
|
|
||||||
*/
|
|
||||||
orbisCategory(e) {
|
|
||||||
let ship = this.state.ship;
|
|
||||||
let cat = e.target.value;
|
|
||||||
ship.category = cat;
|
|
||||||
this.setState({ ship });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Render the modal
|
|
||||||
* @return {React.Component} Modal Content
|
|
||||||
*/
|
|
||||||
render() {
|
|
||||||
let translate = this.context.language.translate;
|
|
||||||
this.orbisPasswordHandler = this.orbisPasswordHandler.bind(this);
|
|
||||||
this.orbisUsername = this.orbisUsername.bind(this);
|
|
||||||
this.sendToOrbis = this.sendToOrbis.bind(this);
|
|
||||||
this.getOrbisAuthStatus();
|
|
||||||
return <div className='modal' onClick={ (e) => e.stopPropagation() }>
|
|
||||||
<h2>{translate('upload to orbis')}</h2>
|
|
||||||
<br/>
|
|
||||||
<label>Orbis auth status: </label>
|
|
||||||
<input value={this.state.authenticatedStatus} readOnly size={25} onFocus={ (e) => e.target.select() }/>
|
|
||||||
<br/><br/>
|
|
||||||
<a className='button' href="https://orbis.zone/api/auth">Log in / signup to Orbis</a>
|
|
||||||
<br/><br/>
|
|
||||||
<h3>Category</h3>
|
|
||||||
<select onChange={this.orbisCategory}>
|
|
||||||
<option value="">No Category</option>
|
|
||||||
<option>Combat</option>
|
|
||||||
<option>Mining</option>
|
|
||||||
<option>Trading</option>
|
|
||||||
<option>Exploration</option>
|
|
||||||
<option>Passenger Liner</option>
|
|
||||||
<option>PvP</option>
|
|
||||||
</select>
|
|
||||||
<br/><br/>
|
|
||||||
<h3 >{translate('Orbis link')}</h3>
|
|
||||||
<input value={this.state.orbisUrl} readOnly size={25} onFocus={ (e) => e.target.select() }/>
|
|
||||||
<br/><br/>
|
|
||||||
<p>Orbis.zone is currently in a trial period, and may be wiped at any time as development progresses. Some elements are also still placeholders.</p>
|
|
||||||
<button className={'l cb dismiss cap'} disabled={!!this.state.failed} onClick={this.sendToOrbis}>{translate('PHASE_UPLOAD_ORBIS')}</button>
|
|
||||||
<button className={'r dismiss cap'} onClick={this.context.hideModal}>{translate('close')}</button>
|
|
||||||
</div>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,262 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import TranslatedComponent from './TranslatedComponent';
|
|
||||||
import request from 'superagent';
|
|
||||||
import Persist from '../stores/Persist';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Permalink modal
|
|
||||||
*/
|
|
||||||
export default class ModalShoppingList extends TranslatedComponent {
|
|
||||||
static propTypes = {
|
|
||||||
ship: PropTypes.object.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
* @param {Object} props React Component properties
|
|
||||||
*/
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
matsList: '',
|
|
||||||
mats: {},
|
|
||||||
failed: false,
|
|
||||||
cmdrName: Persist.getCmdr().selected,
|
|
||||||
cmdrs: Persist.getCmdr().cmdrs,
|
|
||||||
matsPerGrade: Persist.getRolls(),
|
|
||||||
blueprints: []
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* React component did mount
|
|
||||||
*/
|
|
||||||
componentDidMount() {
|
|
||||||
this.renderMats();
|
|
||||||
if (this.checkBrowserIsCompatible()) {
|
|
||||||
this.getCommanders();
|
|
||||||
this.registerBPs();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find all blueprints needed to make a build.
|
|
||||||
*/
|
|
||||||
registerBPs() {
|
|
||||||
const ship = this.props.ship;
|
|
||||||
let blueprints = [];
|
|
||||||
for (const module of ship.costList) {
|
|
||||||
if (module.type === 'SHIP') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (module.m && module.m.blueprint) {
|
|
||||||
if (!module.m.blueprint.grade || !module.m.blueprint.grades) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (module.m.blueprint.special) {
|
|
||||||
console.log(module.m.blueprint.special);
|
|
||||||
blueprints.push({ uuid: module.m.blueprint.special.uuid, number: 1 });
|
|
||||||
}
|
|
||||||
for (const g in module.m.blueprint.grades) {
|
|
||||||
if (!module.m.blueprint.grades.hasOwnProperty(g)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (g > module.m.blueprint.grade) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
blueprints.push({ uuid: module.m.blueprint.grades[g].uuid, number: this.state.matsPerGrade[g] });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.setState({ blueprints });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check browser isn't firefox.
|
|
||||||
* @return {boolean} true if compatible, false if not.
|
|
||||||
*/
|
|
||||||
checkBrowserIsCompatible() {
|
|
||||||
// Firefox 1.0+
|
|
||||||
return typeof InstallTrigger === 'undefined';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a list of commanders from EDEngineer.
|
|
||||||
*/
|
|
||||||
getCommanders() {
|
|
||||||
request
|
|
||||||
.get('http://localhost:44405/commanders')
|
|
||||||
.end((err, res) => {
|
|
||||||
if (err) {
|
|
||||||
console.log(err);
|
|
||||||
return this.setState({ failed: true });
|
|
||||||
}
|
|
||||||
const cmdrs = JSON.parse(res.text);
|
|
||||||
if (!this.state.cmdrName) {
|
|
||||||
this.setState({ cmdrName: cmdrs[0] });
|
|
||||||
}
|
|
||||||
this.setState({ cmdrs }, () => {
|
|
||||||
Persist.setCmdr({ selected: this.state.cmdrName, cmdrs });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send all blueprints to ED Engineer
|
|
||||||
* @param {Event} event React event
|
|
||||||
*/
|
|
||||||
sendToEDEng(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
let translate = this.context.language.translate;
|
|
||||||
const target = event.target;
|
|
||||||
target.disabled = this.state.blueprints.length > 0;
|
|
||||||
if (this.state.blueprints.length === 0) {
|
|
||||||
target.innerText = translate('No modded components.');
|
|
||||||
target.disabled = true;
|
|
||||||
setTimeout(() => {
|
|
||||||
target.innerText = translate('Send to EDEngineer');
|
|
||||||
target.disabled = false;
|
|
||||||
}, 3000);
|
|
||||||
} else {
|
|
||||||
target.innerText = translate('Sending...');
|
|
||||||
}
|
|
||||||
let countSent = 0;
|
|
||||||
let countTotal = this.state.blueprints.length;
|
|
||||||
|
|
||||||
for (const i of this.state.blueprints) {
|
|
||||||
request
|
|
||||||
.patch(`http://localhost:44405/${this.state.cmdrName}/shopping-list`)
|
|
||||||
.field('uuid', i.uuid)
|
|
||||||
.field('size', i.number)
|
|
||||||
.end(err => {
|
|
||||||
if (err) {
|
|
||||||
console.log(err);
|
|
||||||
if (err.message !== 'Bad Request') {
|
|
||||||
this.setState({ failed: true });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
countSent++;
|
|
||||||
if (countSent === countTotal) {
|
|
||||||
target.disabled = false;
|
|
||||||
target.innerText = translate('Send to EDEngineer');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert mats object to string
|
|
||||||
*/
|
|
||||||
renderMats() {
|
|
||||||
const ship = this.props.ship;
|
|
||||||
let mats = {};
|
|
||||||
for (const module of ship.costList) {
|
|
||||||
if (module.type === 'SHIP') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (module.m && module.m.blueprint) {
|
|
||||||
if (!module.m.blueprint.grade || !module.m.blueprint.grades) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (const g in module.m.blueprint.grades) {
|
|
||||||
if (!module.m.blueprint.grades.hasOwnProperty(g)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (g > module.m.blueprint.grade) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (const i in module.m.blueprint.grades[g].components) {
|
|
||||||
if (!module.m.blueprint.grades[g].components.hasOwnProperty(i)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (mats[i]) {
|
|
||||||
mats[i] += module.m.blueprint.grades[g].components[i] * this.state.matsPerGrade[g];
|
|
||||||
} else {
|
|
||||||
mats[i] = module.m.blueprint.grades[g].components[i] * this.state.matsPerGrade[g];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let matsString = '';
|
|
||||||
for (const i in mats) {
|
|
||||||
if (!mats.hasOwnProperty(i)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (mats[i] === 0) {
|
|
||||||
delete mats[i];
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
matsString += `${i}: ${mats[i]}\n`;
|
|
||||||
}
|
|
||||||
this.setState({ matsList: matsString, mats });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handler for changing roll amounts
|
|
||||||
* @param {SyntheticEvent} e React Event
|
|
||||||
*/
|
|
||||||
changeHandler(e) {
|
|
||||||
let grade = e.target.id;
|
|
||||||
let newState = this.state.matsPerGrade;
|
|
||||||
newState[grade] = parseInt(e.target.value);
|
|
||||||
this.setState({ matsPerGrade: newState });
|
|
||||||
Persist.setRolls(newState);
|
|
||||||
this.renderMats();
|
|
||||||
this.registerBPs();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handler for changing cmdr name
|
|
||||||
* @param {SyntheticEvent} e React Event
|
|
||||||
*/
|
|
||||||
cmdrChangeHandler(e) {
|
|
||||||
let cmdrName = e.target.value;
|
|
||||||
this.setState({ cmdrName }, () => {
|
|
||||||
Persist.setCmdr({ selected: this.state.cmdrName, cmdrs: this.state.cmdrs });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Render the modal
|
|
||||||
* @return {React.Component} Modal Content
|
|
||||||
*/
|
|
||||||
render() {
|
|
||||||
let translate = this.context.language.translate;
|
|
||||||
this.changeHandler = this.changeHandler.bind(this);
|
|
||||||
const compatible = this.checkBrowserIsCompatible();
|
|
||||||
this.cmdrChangeHandler = this.cmdrChangeHandler.bind(this);
|
|
||||||
this.sendToEDEng = this.sendToEDEng.bind(this);
|
|
||||||
return <div className='modal' onClick={ (e) => e.stopPropagation() }>
|
|
||||||
<h2>{translate('PHRASE_SHOPPING_MATS')}</h2>
|
|
||||||
<label>{translate('Grade 1 rolls ')}</label>
|
|
||||||
<input id={1} type={'number'} min={0} defaultValue={this.state.matsPerGrade[1]} onChange={this.changeHandler} />
|
|
||||||
<br/>
|
|
||||||
<label>{translate('Grade 2 rolls ')}</label>
|
|
||||||
<input id={2} type={'number'} min={0} defaultValue={this.state.matsPerGrade[2]} onChange={this.changeHandler} />
|
|
||||||
<br/>
|
|
||||||
<label>{translate('Grade 3 rolls ')}</label>
|
|
||||||
<input id={3} type={'number'} min={0} value={this.state.matsPerGrade[3]} onChange={this.changeHandler} />
|
|
||||||
<br/>
|
|
||||||
<label>{translate('Grade 4 rolls ')}</label>
|
|
||||||
<input id={4} type={'number'} min={0} value={this.state.matsPerGrade[4]} onChange={this.changeHandler} />
|
|
||||||
<br/>
|
|
||||||
<label>{translate('Grade 5 rolls ')}</label>
|
|
||||||
<input id={5} type={'number'} min={0} value={this.state.matsPerGrade[5]} onChange={this.changeHandler} />
|
|
||||||
<div>
|
|
||||||
<textarea className='cb json' readOnly value={this.state.matsList} />
|
|
||||||
</div>
|
|
||||||
<label hidden={!compatible} className={'l cap'}>{translate('CMDR Name')}</label>
|
|
||||||
<br/>
|
|
||||||
<select hidden={!compatible} className={'cmdr-select l cap'} onChange={this.cmdrChangeHandler} defaultValue={this.state.cmdrName}>
|
|
||||||
{this.state.cmdrs.map(e => <option key={e}>{e}</option>)}
|
|
||||||
</select>
|
|
||||||
<br/>
|
|
||||||
<p hidden={!this.state.failed} id={'failed'} className={'l'}>{translate('PHRASE_FAIL_EDENGINEER')}</p>
|
|
||||||
<p hidden={compatible} id={'browserbad'} className={'l'}>{translate('PHRASE_FIREFOX_EDENGINEER')}</p>
|
|
||||||
<button className={'l cb dismiss cap'} disabled={!!this.state.failed || !compatible} onClick={this.sendToEDEng}>{translate('Send to EDEngineer')}</button>
|
|
||||||
<button className={'r dismiss cap'} onClick={this.context.hideModal}>{translate('close')}</button>
|
|
||||||
</div>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,8 +9,8 @@ import {
|
|||||||
blueprintTooltip,
|
blueprintTooltip,
|
||||||
specialToolTip
|
specialToolTip
|
||||||
} from '../utils/BlueprintFunctions';
|
} from '../utils/BlueprintFunctions';
|
||||||
import { getBlueprintInfo, getExperimentalInfo } from 'ed-forge/lib/data/blueprints';
|
import { getBlueprintInfo, getExperimentalInfo } from 'ed-forge/lib/src/data/blueprints';
|
||||||
import { getModuleInfo } from 'ed-forge/lib/data/items';
|
import { getModuleInfo } from 'ed-forge/lib/src/data/items';
|
||||||
import { SHOW } from '../shipyard/StatsMapping';
|
import { SHOW } from '../shipyard/StatsMapping';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import TranslatedComponent from './TranslatedComponent';
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
import * as Calc from '../shipyard/Calculations';
|
|
||||||
import PieChart from './PieChart';
|
import PieChart from './PieChart';
|
||||||
import { MountFixed, MountGimballed, MountTurret } from './SvgIcons';
|
import { MountFixed, MountGimballed, MountTurret } from './SvgIcons';
|
||||||
import { Ship } from 'ed-forge';
|
import { Ship } from 'ed-forge';
|
||||||
import autoBind from 'auto-bind';
|
import autoBind from 'auto-bind';
|
||||||
import { DAMAGE_METRICS } from 'ed-forge/lib/ship-stats';
|
import { DAMAGE_METRICS } from 'ed-forge/lib/src/ship-stats';
|
||||||
import { clone, mapValues, mergeWith, reverse, sortBy, sum, toPairs, values } from 'lodash';
|
import { clone, mapValues, mergeWith, reverse, sortBy, sum, toPairs, values } from 'lodash';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -82,12 +81,14 @@ export default class Offence extends TranslatedComponent {
|
|||||||
const { formats, translate, units } = language;
|
const { formats, translate, units } = language;
|
||||||
const sortOrder = this._sortOrder;
|
const sortOrder = this._sortOrder;
|
||||||
|
|
||||||
const damage = ship.getMetrics(DAMAGE_METRICS);
|
const {
|
||||||
|
drained, sustained, rangeMultiplier, hardnessMultiplier, timeToDrain
|
||||||
|
} = ship.getMetrics(DAMAGE_METRICS);
|
||||||
const portions = {
|
const portions = {
|
||||||
Absolute: damage.types.abs,
|
Absolute: sustained.types.abs,
|
||||||
Explosive: damage.types.expl,
|
Explosive: sustained.types.expl,
|
||||||
Kinetic: damage.types.kin,
|
Kinetic: sustained.types.kin,
|
||||||
Thermic: damage.types.therm,
|
Thermic: sustained.types.therm,
|
||||||
};
|
};
|
||||||
|
|
||||||
const oppShield = ship.getOpponent().getShield();
|
const oppShield = ship.getOpponent().getShield();
|
||||||
@@ -106,8 +107,8 @@ export default class Offence extends TranslatedComponent {
|
|||||||
Thermic: oppArmour.thermal.damageMultiplier,
|
Thermic: oppArmour.thermal.damageMultiplier,
|
||||||
};
|
};
|
||||||
|
|
||||||
let rows = [];
|
const weapons = sortBy(ship.getHardpoints(), (m) => m.get('distributordraw'));
|
||||||
for (let weapon of ship.getHardpoints()) {
|
let rows = weapons.map((weapon) => {
|
||||||
const sdps = weapon.get('sustaineddamagepersecond');
|
const sdps = weapon.get('sustaineddamagepersecond');
|
||||||
const byRange = weapon.getRangeEffectiveness();
|
const byRange = weapon.getRangeEffectiveness();
|
||||||
const weaponPortions = {
|
const weaponPortions = {
|
||||||
@@ -176,7 +177,7 @@ export default class Offence extends TranslatedComponent {
|
|||||||
if (exp) {
|
if (exp) {
|
||||||
bpTitle += `, ${translate(exp)}`;
|
bpTitle += `, ${translate(exp)}`;
|
||||||
}
|
}
|
||||||
rows.push({
|
return {
|
||||||
slot: weapon.getSlot(),
|
slot: weapon.getSlot(),
|
||||||
mount: weapon.mount,
|
mount: weapon.mount,
|
||||||
classRating: weapon.getClassRating(),
|
classRating: weapon.getClassRating(),
|
||||||
@@ -192,8 +193,9 @@ export default class Offence extends TranslatedComponent {
|
|||||||
armourEft,
|
armourEft,
|
||||||
armourSdpsTooltip,
|
armourSdpsTooltip,
|
||||||
armourEftTooltip,
|
armourEftTooltip,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
}
|
|
||||||
const { predicate, desc } = this.state;
|
const { predicate, desc } = this.state;
|
||||||
rows = sortBy(rows, (row) => row[predicate]);
|
rows = sortBy(rows, (row) => row[predicate]);
|
||||||
if (desc) {
|
if (desc) {
|
||||||
@@ -202,18 +204,18 @@ export default class Offence extends TranslatedComponent {
|
|||||||
|
|
||||||
const sdpsTooltip = objToTooltip(
|
const sdpsTooltip = objToTooltip(
|
||||||
translate,
|
translate,
|
||||||
mapValues(portions, (p) => formats.f1(damage.sustained.dps * p)),
|
mapValues(portions, (p) => formats.f1(sustained.dps * p)),
|
||||||
);
|
);
|
||||||
const sdpsPie = objToPie(
|
const sdpsPie = objToPie(
|
||||||
translate,
|
translate,
|
||||||
mapValues(portions, (p) => Math.round(damage.sustained.dps * p)),
|
mapValues(portions, (p) => Math.round(sustained.dps * p)),
|
||||||
);
|
);
|
||||||
|
|
||||||
const shieldSdpsSrcs = mergeWith(
|
const shieldSdpsSrcs = mergeWith(
|
||||||
clone(portions),
|
clone(portions),
|
||||||
shieldMults,
|
shieldMults,
|
||||||
(objV, srcV) => damage.sustained.dps * oppShield.absolute.bySys *
|
(objV, srcV) => sustained.dps * oppShield.absolute.bySys *
|
||||||
damage.rangeMultiplier * objV * srcV,
|
rangeMultiplier * objV * srcV,
|
||||||
);
|
);
|
||||||
const shieldsSdps = sum(values(shieldSdpsSrcs));
|
const shieldsSdps = sum(values(shieldSdpsSrcs));
|
||||||
const shieldsSdpsTooltip = objToTooltip(
|
const shieldsSdpsTooltip = objToTooltip(
|
||||||
@@ -228,8 +230,8 @@ export default class Offence extends TranslatedComponent {
|
|||||||
const armourSdpsSrcs = mergeWith(
|
const armourSdpsSrcs = mergeWith(
|
||||||
clone(portions),
|
clone(portions),
|
||||||
armourMults,
|
armourMults,
|
||||||
(objV, srcV) => damage.sustained.dps * damage.hardnessMultiplier *
|
(objV, srcV) => sustained.dps * hardnessMultiplier * rangeMultiplier *
|
||||||
damage.rangeMultiplier * objV * srcV,
|
objV * srcV,
|
||||||
);
|
);
|
||||||
const armourSdps = sum(values(armourSdpsSrcs));
|
const armourSdps = sum(values(armourSdpsSrcs));
|
||||||
const totalArmourSDpsTooltipDetails = objToTooltip(
|
const totalArmourSDpsTooltipDetails = objToTooltip(
|
||||||
@@ -241,10 +243,45 @@ export default class Offence extends TranslatedComponent {
|
|||||||
mapValues(armourSdpsSrcs, (v) => Math.round(v)),
|
mapValues(armourSdpsSrcs, (v) => Math.round(v)),
|
||||||
);
|
);
|
||||||
|
|
||||||
const pd = ship.getPowerDistributor();
|
const drainedPortions = {
|
||||||
const timeToDrain = damage.sustained.timeToDrain[ship.getDistributorSettings().Wep];
|
Absolute: drained.types.abs,
|
||||||
// const timeToDepleteShields = Calc.timeToDeplete(opponentShields.total, shieldsSdps, totalSEps, pd.getWeaponsCapacity(), pd.getWeaponsRechargeRate() * (wep / 4));
|
Explosive: drained.types.expl,
|
||||||
// const timeToDepleteArmour = Calc.timeToDeplete(opponentArmour.total, armourSdps, totalSEps, pd.getWeaponsCapacity(), pd.getWeaponsRechargeRate() * (wep / 4));
|
Kinetic: drained.types.kin,
|
||||||
|
Thermic: drained.types.therm,
|
||||||
|
};
|
||||||
|
|
||||||
|
// How much damage do we deal, before the capacitor is empty?
|
||||||
|
const armourLeft = oppArmour.armour - (timeToDrain * armourSdps);
|
||||||
|
// If we can't kill the enemy on one capacitor, factor in drained damage
|
||||||
|
let timeToDepleteArmour;
|
||||||
|
if (armourLeft > 0) {
|
||||||
|
const effectiveDrainedDps = sum(values(mergeWith(
|
||||||
|
clone(drainedPortions),
|
||||||
|
armourMults,
|
||||||
|
(objV, srcV) => objV * srcV,
|
||||||
|
))) * drained.dps * rangeMultiplier *
|
||||||
|
hardnessMultiplier;
|
||||||
|
timeToDepleteArmour = effectiveDrainedDps === 0 ? Infinity :
|
||||||
|
timeToDrain + (armourLeft / effectiveDrainedDps);
|
||||||
|
} else {
|
||||||
|
timeToDepleteArmour = oppArmour.armour / armourSdps;
|
||||||
|
}
|
||||||
|
|
||||||
|
// How much damage do we deal, before the capacitor is empty?
|
||||||
|
const shieldsLeft = oppShield.withSCBs - (timeToDrain * shieldsSdps);
|
||||||
|
// If we can't kill the enemy on one capacitor, factor in drained damage
|
||||||
|
let timeToDepleteShields;
|
||||||
|
if (shieldsLeft > 0) {
|
||||||
|
const effectiveDrainedDps = sum(values(mergeWith(
|
||||||
|
clone(drainedPortions),
|
||||||
|
shieldMults,
|
||||||
|
(objV, srcV) => objV * srcV,
|
||||||
|
))) * drained.dps * rangeMultiplier;
|
||||||
|
timeToDepleteShields = effectiveDrainedDps === 0 ? Infinity :
|
||||||
|
timeToDrain + (shieldsLeft / effectiveDrainedDps);
|
||||||
|
} else {
|
||||||
|
timeToDepleteShields = oppShield.withSCBs / shieldsSdps;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span id='offence'>
|
<span id='offence'>
|
||||||
@@ -254,8 +291,8 @@ export default class Offence extends TranslatedComponent {
|
|||||||
<tr className='main'>
|
<tr className='main'>
|
||||||
<th rowSpan='2' className='sortable' onClick={sortOrder.bind(this, 'classRating')}>{translate('weapon')}</th>
|
<th rowSpan='2' className='sortable' onClick={sortOrder.bind(this, 'classRating')}>{translate('weapon')}</th>
|
||||||
<th colSpan='1'>{translate('overall')}</th>
|
<th colSpan='1'>{translate('overall')}</th>
|
||||||
<th colSpan='2'>{translate('opponent\'s shields')}</th>
|
<th colSpan='3'>{translate('opponent\'s shields')}</th>
|
||||||
<th colSpan='2'>{translate('opponent\'s armour')}</th>
|
<th colSpan='3'>{translate('opponent\'s armour')}</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th className='lft sortable' onMouseOver={termtip.bind(null, 'TT_EFFECTIVE_SDPS_SHIELDS')}
|
<th className='lft sortable' onMouseOver={termtip.bind(null, 'TT_EFFECTIVE_SDPS_SHIELDS')}
|
||||||
@@ -307,7 +344,7 @@ export default class Offence extends TranslatedComponent {
|
|||||||
<td></td>
|
<td></td>
|
||||||
<td className='ri'>
|
<td className='ri'>
|
||||||
<span onMouseOver={termtip.bind(null, sdpsTooltip)} onMouseOut={tooltip.bind(null, null)}>
|
<span onMouseOver={termtip.bind(null, sdpsTooltip)} onMouseOut={tooltip.bind(null, null)}>
|
||||||
={formats.f1(damage.sustained.dps)}
|
={formats.f1(sustained.dps)}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td className='ri'>
|
<td className='ri'>
|
||||||
@@ -322,6 +359,7 @@ export default class Offence extends TranslatedComponent {
|
|||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
</tbody>
|
</tbody>
|
||||||
@@ -342,8 +380,7 @@ export default class Offence extends TranslatedComponent {
|
|||||||
<h2 onMouseOver={termtip.bind(null, translate('TT_TIME_TO_REMOVE_SHIELDS'))}
|
<h2 onMouseOver={termtip.bind(null, translate('TT_TIME_TO_REMOVE_SHIELDS'))}
|
||||||
onMouseOut={tooltip.bind(null, null)}>
|
onMouseOut={tooltip.bind(null, null)}>
|
||||||
{translate('PHRASE_TIME_TO_REMOVE_SHIELDS')}<br/>
|
{translate('PHRASE_TIME_TO_REMOVE_SHIELDS')}<br/>
|
||||||
ToDo
|
{timeToDepleteShields === Infinity ? translate('never') : formats.time(timeToDepleteShields)}
|
||||||
{/* {timeToDepleteShields === Infinity ? translate('never') : formats.time(timeToDepleteShields)} */}
|
|
||||||
</h2>
|
</h2>
|
||||||
<h2 onMouseOver={termtip.bind(null, translate('TT_EFFECTIVE_SDPS_ARMOUR'))}
|
<h2 onMouseOver={termtip.bind(null, translate('TT_EFFECTIVE_SDPS_ARMOUR'))}
|
||||||
onMouseOut={tooltip.bind(null, null)}>
|
onMouseOut={tooltip.bind(null, null)}>
|
||||||
@@ -353,8 +390,7 @@ export default class Offence extends TranslatedComponent {
|
|||||||
<h2 onMouseOver={termtip.bind(null, translate('TT_TIME_TO_REMOVE_ARMOUR'))}
|
<h2 onMouseOver={termtip.bind(null, translate('TT_TIME_TO_REMOVE_ARMOUR'))}
|
||||||
onMouseOut={tooltip.bind(null, null)}>
|
onMouseOut={tooltip.bind(null, null)}>
|
||||||
{translate('PHRASE_TIME_TO_REMOVE_ARMOUR')}<br/>
|
{translate('PHRASE_TIME_TO_REMOVE_ARMOUR')}<br/>
|
||||||
ToDo
|
{timeToDepleteArmour === Infinity ? translate('never') : formats.time(timeToDepleteArmour)}
|
||||||
{/* {timeToDepleteArmour === Infinity ? translate('never') : formats.time(timeToDepleteArmour)} */}
|
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<div className='group quarter'>
|
<div className='group quarter'>
|
||||||
|
|||||||
@@ -145,12 +145,11 @@ export default class OutfittingSubpages extends TranslatedComponent {
|
|||||||
<h1>{translate('movement profile')}</h1>
|
<h1>{translate('movement profile')}</h1>
|
||||||
<Movement code={code} ship={ship} boost={boost} pips={pips} />
|
<Movement code={code} ship={ship} boost={boost} pips={pips} />
|
||||||
</div>
|
</div>
|
||||||
<div className='group third'>
|
<div className='group half'>
|
||||||
<h1>{translate('damage to opponent\'s shields')}</h1>
|
<h1>{translate('damage to opponent\'s shields')}</h1>
|
||||||
<WeaponDamageChart code={code} ship={ship} opponentDefence={opponent.getShield()} engagementRange={engagementRange} />
|
<WeaponDamageChart code={code} ship={ship} opponentDefence={opponent.getShield()} engagementRange={engagementRange} />
|
||||||
</div>
|
</div>
|
||||||
|
<div className='group half'>
|
||||||
<div className='group third'>
|
|
||||||
<h1>{translate('damage to opponent\'s hull')}</h1>
|
<h1>{translate('damage to opponent\'s hull')}</h1>
|
||||||
<WeaponDamageChart code={code} ship={ship} opponentDefence={opponent.getArmour()} engagementRange={engagementRange} />
|
<WeaponDamageChart code={code} ship={ship} opponentDefence={opponent.getArmour()} engagementRange={engagementRange} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import cn from 'classnames';
|
|||||||
import TranslatedComponent from './TranslatedComponent';
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
import { wrapCtxMenu } from '../utils/UtilityFunctions';
|
import { wrapCtxMenu } from '../utils/UtilityFunctions';
|
||||||
import { Ship } from 'ed-forge';
|
import { Ship } from 'ed-forge';
|
||||||
import { POWER_METRICS } from 'ed-forge/lib/ship-stats';
|
import { POWER_METRICS } from 'ed-forge/lib/src/ship-stats';
|
||||||
import autoBind from 'auto-bind';
|
import autoBind from 'auto-bind';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import TranslatedComponent from './TranslatedComponent';
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
import { Ships } from 'coriolis-data/dist';
|
|
||||||
import { Rocket } from './SvgIcons';
|
import { Rocket } from './SvgIcons';
|
||||||
import Persist from '../stores/Persist';
|
import Persist from '../stores/Persist';
|
||||||
import cn from 'classnames';
|
import cn from 'classnames';
|
||||||
|
import { Factory, Ship } from 'ed-forge';
|
||||||
import autoBind from 'auto-bind';
|
import autoBind from 'auto-bind';
|
||||||
|
import { isEqual } from 'lodash';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ship picker
|
* Ship picker
|
||||||
@@ -14,14 +15,9 @@ import autoBind from 'auto-bind';
|
|||||||
export default class ShipPicker extends TranslatedComponent {
|
export default class ShipPicker extends TranslatedComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
onChange: PropTypes.func.isRequired,
|
onChange: PropTypes.func.isRequired,
|
||||||
ship: PropTypes.string.isRequired,
|
ship: PropTypes.instanceOf(Ship).isRequired,
|
||||||
build: PropTypes.string
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
|
||||||
ship: 'eagle'
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* constructor
|
* constructor
|
||||||
* @param {object} props Properties react
|
* @param {object} props Properties react
|
||||||
@@ -30,20 +26,38 @@ export default class ShipPicker extends TranslatedComponent {
|
|||||||
constructor(props, context) {
|
constructor(props, context) {
|
||||||
super(props);
|
super(props);
|
||||||
autoBind(this);
|
autoBind(this);
|
||||||
this.state = { menuOpen: false };
|
this.state = {
|
||||||
|
menuOpen: false,
|
||||||
|
opponent: {
|
||||||
|
self: true,
|
||||||
|
type: props.ship.getShipType(),
|
||||||
|
stock: false,
|
||||||
|
id: undefined,
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update ship
|
* Update ship
|
||||||
* @param {object} ship the ship
|
* @param {boolean} self True to compare with ship itself
|
||||||
* @param {string} build the build, if present
|
* @param {object} type The ship type
|
||||||
|
* @param {boolean} stock True to compare with a stock version of given type
|
||||||
|
* @param {string} id The build's stored ID
|
||||||
*/
|
*/
|
||||||
_shipChange(ship, build) {
|
_shipChange(self, type, stock = false, id = null) {
|
||||||
this._closeMenu();
|
const opponent = { self, type, stock, id };
|
||||||
|
if (isEqual(opponent, this.state.opponent)) {
|
||||||
// Ensure that the ship has changed
|
this.setState({ menuOpen: false });
|
||||||
if (ship !== this.props.ship || build !== this.props.build) {
|
} else {
|
||||||
this.props.onChange(ship, build);
|
const { onChange } = this.props;
|
||||||
|
if (self) {
|
||||||
|
onChange(this.props.ship);
|
||||||
|
} else if (stock) {
|
||||||
|
onChange(Factory.newShip(type));
|
||||||
|
} else {
|
||||||
|
onChange(new Ship(Persist.getBuild(type, id)));
|
||||||
|
}
|
||||||
|
this.setState({ menuOpen: false, opponent });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,26 +66,41 @@ export default class ShipPicker extends TranslatedComponent {
|
|||||||
* @returns {object} the picker menu
|
* @returns {object} the picker menu
|
||||||
*/
|
*/
|
||||||
_renderPickerMenu() {
|
_renderPickerMenu() {
|
||||||
const { ship, build } = this.props;
|
const { menuOpen } = this.state;
|
||||||
const _shipChange = this._shipChange;
|
if (!menuOpen) {
|
||||||
const builds = Persist.getBuilds();
|
return null;
|
||||||
const buildList = [];
|
|
||||||
for (let shipId of this.shipOrder) {
|
|
||||||
const shipBuilds = [];
|
|
||||||
// Add stock build
|
|
||||||
const stockSelected = (ship == shipId && !build);
|
|
||||||
shipBuilds.push(<li key={shipId} className={ cn({ 'selected': stockSelected })} onClick={_shipChange.bind(this, shipId, null)}>Stock</li>);
|
|
||||||
if (builds[shipId]) {
|
|
||||||
let buildNameOrder = Object.keys(builds[shipId]).sort();
|
|
||||||
for (let buildName of buildNameOrder) {
|
|
||||||
const buildSelected = ship === shipId && build === buildName;
|
|
||||||
shipBuilds.push(<li key={shipId + '-' + buildName} className={ cn({ 'selected': buildSelected })} onClick={_shipChange.bind(this, shipId, buildName)}>{buildName}</li>);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buildList.push(<ul key={shipId} className='block'>{Ships[shipId].properties.name}{shipBuilds}</ul>);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return buildList;
|
const { translate } = this.context.language;
|
||||||
|
const { self, type, stock, id } = this.state.opponent;
|
||||||
|
return <div className='menu-list' onClick={(e) => e.stopPropagation()}>
|
||||||
|
<div className='quad'>
|
||||||
|
{Factory.getAllShipTypes().sort().map((shipType) =>
|
||||||
|
<ul key={shipType} className='block'>
|
||||||
|
{translate(shipType)}
|
||||||
|
{/* Add stock build */}
|
||||||
|
<li key={shipType}
|
||||||
|
onClick={this._shipChange.bind(this, false, shipType, true)}
|
||||||
|
className={cn({ selected: stock && type === shipType })}>
|
||||||
|
{translate('stock')}
|
||||||
|
</li>
|
||||||
|
{Persist.getBuildsNamesFor(shipType).sort().map((storedId) =>
|
||||||
|
<li key={`${shipType}-${storedId}`}
|
||||||
|
onClick={this._shipChange.bind(this, false, shipType, false, storedId)}
|
||||||
|
className={ cn({ selected: type === shipType && id === storedId })}>
|
||||||
|
{storedId}
|
||||||
|
</li>)}
|
||||||
|
{/* Add ship itself */}
|
||||||
|
{(this.props.ship.getShipType() === shipType ?
|
||||||
|
<li key='self'
|
||||||
|
onClick={this._shipChange.bind(this, true, shipType)}
|
||||||
|
className={cn({ selected: self })}>
|
||||||
|
{translate('THIS_SHIP')}
|
||||||
|
</li> :
|
||||||
|
null)}
|
||||||
|
</ul>)}
|
||||||
|
</div>
|
||||||
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -82,41 +111,36 @@ export default class ShipPicker extends TranslatedComponent {
|
|||||||
this.setState({ menuOpen: !menuOpen });
|
this.setState({ menuOpen: !menuOpen });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Close the menu
|
|
||||||
*/
|
|
||||||
_closeMenu() {
|
|
||||||
const { menuOpen } = this.state;
|
|
||||||
if (menuOpen) {
|
|
||||||
this._toggleMenu();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render picker
|
* Render picker
|
||||||
* @return {React.Component} contents
|
* @return {React.Component} contents
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
const { language, onWindowResize, sizeRatio, tooltip, termtip } = this.context;
|
const { translate } = this.context.language;
|
||||||
const { formats, translate, units } = language;
|
const { ship } = this.props;
|
||||||
const { ship, build } = this.props;
|
|
||||||
const { menuOpen } = this.state;
|
const { menuOpen } = this.state;
|
||||||
|
const { self, type, stock, id } = this.state.opponent;
|
||||||
|
|
||||||
|
let label;
|
||||||
|
if (self) {
|
||||||
|
label = translate('THIS_SHIP');
|
||||||
|
} else if (stock) {
|
||||||
|
label = translate('stock');
|
||||||
|
} else {
|
||||||
|
label = id;
|
||||||
|
}
|
||||||
|
|
||||||
const shipString = ship + ': ' + (build ? build : translate('stock'));
|
|
||||||
return (
|
return (
|
||||||
<div className='shippicker' onClick={ (e) => e.stopPropagation() }>
|
<div className='shippicker' onClick={ (e) => e.stopPropagation() }>
|
||||||
<div className='menu'>
|
<div className='menu'>
|
||||||
<div className={cn('menu-header', { selected: menuOpen })} onClick={this._toggleMenu}>
|
<div className={cn('menu-header', { selected: menuOpen })} onClick={this._toggleMenu}>
|
||||||
<span><Rocket className='warning' /></span>
|
<span><Rocket className='warning' /></span>
|
||||||
<span className='menu-item-label'>{shipString}</span>
|
<span className='menu-item-label'>
|
||||||
|
{`${translate(type)}: ${label}`}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{ menuOpen ?
|
|
||||||
<div className='menu-list' onClick={ (e) => e.stopPropagation() }>
|
|
||||||
<div className='quad'>
|
|
||||||
{this._renderPickerMenu()}
|
{this._renderPickerMenu()}
|
||||||
</div>
|
</div>
|
||||||
</div> : null }
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,10 @@ import cn from 'classnames';
|
|||||||
import { Warning } from './SvgIcons';
|
import { Warning } from './SvgIcons';
|
||||||
|
|
||||||
import { ShipProps } from 'ed-forge';
|
import { ShipProps } from 'ed-forge';
|
||||||
|
import { BOOST_INTERVAL, MINIMUM_MASS } from 'ed-forge/lib/src/ship-stats';
|
||||||
const {
|
const {
|
||||||
SPEED, BOOST_SPEED, DAMAGE_METRICS, JUMP_METRICS, SHIELD_METRICS,
|
SPEED, BOOST_SPEED, DAMAGE_METRICS, JUMP_METRICS, SHIELD_METRICS,
|
||||||
ARMOUR_METRICS, CARGO_CAPACITY, FUEL_CAPACITY, UNLADEN_MASS, MAXIMUM_MASS,
|
ARMOUR_METRICS, CARGO_CAPACITY, FUEL_CAPACITY, UNLADEN_MASS, LADEN_MASS,
|
||||||
MODULE_PROTECTION_METRICS, PASSENGER_CAPACITY
|
MODULE_PROTECTION_METRICS, PASSENGER_CAPACITY
|
||||||
} = ShipProps;
|
} = ShipProps;
|
||||||
|
|
||||||
@@ -48,6 +49,7 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
|||||||
|
|
||||||
const speed = ship.get(SPEED);
|
const speed = ship.get(SPEED);
|
||||||
const shipBoost = ship.get(BOOST_SPEED);
|
const shipBoost = ship.get(BOOST_SPEED);
|
||||||
|
const boostInterval = ship.get(BOOST_INTERVAL);
|
||||||
const canThrust = 0 < speed;
|
const canThrust = 0 < speed;
|
||||||
const canBoost = canThrust && !isNaN(shipBoost);
|
const canBoost = canThrust && !isNaN(shipBoost);
|
||||||
const speedTooltip = canThrust ? 'TT_SUMMARY_SPEED' : 'TT_SUMMARY_SPEED_NONFUNCTIONAL';
|
const speedTooltip = canThrust ? 'TT_SUMMARY_SPEED' : 'TT_SUMMARY_SPEED_NONFUNCTIONAL';
|
||||||
@@ -57,7 +59,7 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
|||||||
const armourMetrics = ship.get(ARMOUR_METRICS);
|
const armourMetrics = ship.get(ARMOUR_METRICS);
|
||||||
const damageMetrics = ship.get(DAMAGE_METRICS);
|
const damageMetrics = ship.get(DAMAGE_METRICS);
|
||||||
const moduleProtectionMetrics = ship.get(MODULE_PROTECTION_METRICS);
|
const moduleProtectionMetrics = ship.get(MODULE_PROTECTION_METRICS);
|
||||||
const timeToDrain = damageMetrics.timeToDrain[8];
|
const timeToDrain = damageMetrics.timeToDrain;
|
||||||
|
|
||||||
const shieldGenerator = ship.getShieldGenerator();
|
const shieldGenerator = ship.getShieldGenerator();
|
||||||
const sgClassNames = cn({
|
const sgClassNames = cn({
|
||||||
@@ -74,8 +76,6 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
|||||||
this.state = { shieldColour };
|
this.state = { shieldColour };
|
||||||
|
|
||||||
const jumpRangeMetrics = ship.getMetrics(JUMP_METRICS);
|
const jumpRangeMetrics = ship.getMetrics(JUMP_METRICS);
|
||||||
// TODO:
|
|
||||||
const canJump = true;
|
|
||||||
|
|
||||||
return <div id='summary'>
|
return <div id='summary'>
|
||||||
<div style={{ display: 'table', width: '100%' }}>
|
<div style={{ display: 'table', width: '100%' }}>
|
||||||
@@ -85,7 +85,7 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
|||||||
<tr className='main'>
|
<tr className='main'>
|
||||||
<th rowSpan={2} className={ cn({ 'bg-warning-disabled': speed == 0 }) }>{translate('speed')}</th>
|
<th rowSpan={2} className={ cn({ 'bg-warning-disabled': speed == 0 }) }>{translate('speed')}</th>
|
||||||
<th rowSpan={2} className={ cn({ 'bg-warning-disabled': !canBoost }) }>{translate('boost')}</th>
|
<th rowSpan={2} className={ cn({ 'bg-warning-disabled': !canBoost }) }>{translate('boost')}</th>
|
||||||
<th colSpan={5} className={ cn({ 'bg-warning-disabled': jumpRangeMetrics.jumpRange == 0 }) }>{translate('jump range')}</th>
|
<th colSpan={5} className={ cn({ 'bg-warning-disabled': jumpRangeMetrics.jumpRangeCurrent == 0 }) }>{translate('jump range')}</th>
|
||||||
<th rowSpan={2}>{translate('shield')}</th>
|
<th rowSpan={2}>{translate('shield')}</th>
|
||||||
<th rowSpan={2}>{translate('integrity')}</th>
|
<th rowSpan={2}>{translate('integrity')}</th>
|
||||||
<th rowSpan={2}>{translate('DPS')}</th>
|
<th rowSpan={2}>{translate('DPS')}</th>
|
||||||
@@ -100,7 +100,8 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
|||||||
<th rowSpan={2}>{translate('crew')}</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, 'mass lock factor', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('MLF')}</th>
|
||||||
<th onMouseEnter={termtip.bind(null, 'TT_SUMMARY_BOOST_INTERVAL', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('boost interval')}</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>
|
{/* TODO: Resting heat */}
|
||||||
|
{/* <th rowSpan={2}>{translate('resting heat (Beta)')}</th> */}
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th className="lft">{translate('max')}</th>
|
<th className="lft">{translate('max')}</th>
|
||||||
@@ -129,37 +130,19 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
|||||||
}</td>
|
}</td>
|
||||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_MAX_SINGLE_JUMP', { cap: 0 })}
|
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_MAX_SINGLE_JUMP', { cap: 0 })}
|
||||||
onMouseLeave={hide}
|
onMouseLeave={hide}
|
||||||
>{canJump ?
|
>{<span>{f2(jumpRangeMetrics.jumpRangeMax)}{u.LY}</span>}</td>
|
||||||
// TODO:
|
|
||||||
<span>{NaN}{u.LY}</span> :
|
|
||||||
<span className='warning'>0<Warning/></span>
|
|
||||||
}</td>
|
|
||||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_SINGLE_JUMP', { cap: 0 })}
|
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_SINGLE_JUMP', { cap: 0 })}
|
||||||
onMouseLeave={hide}
|
onMouseLeave={hide}
|
||||||
>{canJump ?
|
>{<span>{f2(jumpRangeMetrics.jumpRangeUnladen)}{u.LY}</span>}</td>
|
||||||
// TODO:
|
|
||||||
<span>{NaN}{u.LY}</span> :
|
|
||||||
<span className='warning'>0<Warning/></span>
|
|
||||||
}</td>
|
|
||||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_SINGLE_JUMP', { cap: 0 })}
|
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_SINGLE_JUMP', { cap: 0 })}
|
||||||
onMouseLeave={hide}
|
onMouseLeave={hide}
|
||||||
>{canJump ?
|
>{<span>{f2(jumpRangeMetrics.jumpRangeLaden)}{u.LY}</span>}</td>
|
||||||
<span>{f2(jumpRangeMetrics.jumpRange)}{u.LY}</span> :
|
|
||||||
<span className='warning'>0<Warning/></span>
|
|
||||||
}</td>
|
|
||||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_TOTAL_JUMP', { cap: 0 })}
|
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_TOTAL_JUMP', { cap: 0 })}
|
||||||
onMouseLeave={hide}
|
onMouseLeave={hide}
|
||||||
>{canJump ?
|
>{<span>{f2(jumpRangeMetrics.totalRangeUnladen)}{u.LY}</span>}</td>
|
||||||
// TODO:
|
|
||||||
<span>{NaN}{u.LY}</span> :
|
|
||||||
<span className='warning'>0 <Warning/></span>
|
|
||||||
}</td>
|
|
||||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_TOTAL_JUMP', { cap: 0 })}
|
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_TOTAL_JUMP', { cap: 0 })}
|
||||||
onMouseLeave={hide}
|
onMouseLeave={hide}
|
||||||
>{canJump ?
|
>{<span>{f2(jumpRangeMetrics.totalRangeLaden)}{u.LY}</span>}</td>
|
||||||
<span>{f2(jumpRangeMetrics.totalRange)}{u.LY}</span> :
|
|
||||||
<span className='warning'>0<Warning/></span>
|
|
||||||
}</td>
|
|
||||||
<td className={sgClassNames}
|
<td className={sgClassNames}
|
||||||
onMouseEnter={termtip.bind(null, sgTooltip, { cap: 0 })}
|
onMouseEnter={termtip.bind(null, sgTooltip, { cap: 0 })}
|
||||||
onMouseLeave={hide}
|
onMouseLeave={hide}
|
||||||
@@ -185,17 +168,16 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
|||||||
>{ship.readProp('hullmass')}{u.T}</td>
|
>{ship.readProp('hullmass')}{u.T}</td>
|
||||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_MASS', { cap: 0 })}
|
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_MASS', { cap: 0 })}
|
||||||
onMouseLeave={hide}
|
onMouseLeave={hide}
|
||||||
>{int(ship.get(UNLADEN_MASS))}{u.T}</td>
|
>{int(ship.get(MINIMUM_MASS))}{u.T}</td>
|
||||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_MASS', { cap: 0 })}
|
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_MASS', { cap: 0 })}
|
||||||
onMouseLeave={hide}
|
onMouseLeave={hide}
|
||||||
>{int(ship.get(MAXIMUM_MASS))}{u.T}</td>
|
>{int(ship.get(LADEN_MASS))}{u.T}</td>
|
||||||
<td>{int(ship.readProp('hardness'))}</td>
|
<td>{int(ship.readProp('hardness'))}</td>
|
||||||
<td>{ship.readMeta('crew')}</td>
|
<td>{ship.readMeta('crew')}</td>
|
||||||
<td>{ship.readProp('masslock')}</td>
|
<td>{ship.readProp('masslock')}</td>
|
||||||
{/* TODO: boost intervall */}
|
<td>{time(boostInterval)}</td>
|
||||||
<td>{NaN}</td>
|
|
||||||
{/* TODO: resting heat */}
|
{/* TODO: resting heat */}
|
||||||
<td>{NaN}</td>
|
{/* <td>{NaN}</td> */}
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@@ -235,8 +217,8 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
|||||||
<td>{int(sgMetrics.shieldStrength / sgMetrics.kinetic.damageMultiplier || 0)}{u.MJ}</td>
|
<td>{int(sgMetrics.shieldStrength / sgMetrics.kinetic.damageMultiplier || 0)}{u.MJ}</td>
|
||||||
<td>{int(sgMetrics.shieldStrength / sgMetrics.thermal.damageMultiplier || 0)}{u.MJ}</td>
|
<td>{int(sgMetrics.shieldStrength / sgMetrics.thermal.damageMultiplier || 0)}{u.MJ}</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td>{formats.time(sgMetrics.recover) || translate('Never')}</td>
|
<td>{isNaN(sgMetrics.recover) ? translate('Never') : formats.time(sgMetrics.recover)}</td>
|
||||||
<td>{formats.time(sgMetrics.recharge) || translate('Never')}</td>
|
<td>{isNaN(sgMetrics.recharge) ? translate('Never') : formats.time(sgMetrics.recharge)}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
<thead>
|
<thead>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import autoBind from 'auto-bind';
|
||||||
|
|
||||||
const MARGIN_LR = 8; // Left/ Right margin
|
const MARGIN_LR = 8; // Left/ Right margin
|
||||||
|
|
||||||
@@ -7,7 +8,6 @@ const MARGIN_LR = 8; // Left/ Right margin
|
|||||||
* Horizontal Slider
|
* Horizontal Slider
|
||||||
*/
|
*/
|
||||||
export default class Slider extends React.Component {
|
export default class Slider extends React.Component {
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
axis: false,
|
axis: false,
|
||||||
min: 0,
|
min: 0,
|
||||||
@@ -32,16 +32,7 @@ export default class Slider extends React.Component {
|
|||||||
*/
|
*/
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this._down = this._down.bind(this);
|
autoBind(this);
|
||||||
this._move = this._move.bind(this);
|
|
||||||
this._up = this._up.bind(this);
|
|
||||||
this._keyup = this._keyup.bind(this);
|
|
||||||
this._keydown = this._keydown.bind(this);
|
|
||||||
this._touchstart = this._touchstart.bind(this);
|
|
||||||
this._touchend = this._touchend.bind(this);
|
|
||||||
|
|
||||||
this._updatePercent = this._updatePercent.bind(this);
|
|
||||||
this._updateDimensions = this._updateDimensions.bind(this);
|
|
||||||
|
|
||||||
this.state = { width: 0 };
|
this.state = { width: 0 };
|
||||||
}
|
}
|
||||||
@@ -55,7 +46,6 @@ export default class Slider extends React.Component {
|
|||||||
this.left = rect.left;
|
this.left = rect.left;
|
||||||
this.width = rect.width;
|
this.width = rect.width;
|
||||||
this._move(event);
|
this._move(event);
|
||||||
this.touchStartTimer = setTimeout(() => this.sliderInputBox._setDisplay('block'), 1500);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -75,70 +65,11 @@ export default class Slider extends React.Component {
|
|||||||
* @param {Event} event DOM Event
|
* @param {Event} event DOM Event
|
||||||
*/
|
*/
|
||||||
_up(event) {
|
_up(event) {
|
||||||
this.sliderInputBox.sliderVal.focus();
|
|
||||||
clearTimeout(this.touchStartTimer);
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
this.left = null;
|
this.left = null;
|
||||||
this.width = null;
|
this.width = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Key up handler for keyboard.
|
|
||||||
* display the number field then set focus to it
|
|
||||||
* when "Enter" key is pressed
|
|
||||||
* @param {Event} event Keyboard event
|
|
||||||
*/
|
|
||||||
_keyup(event) {
|
|
||||||
switch (event.key) {
|
|
||||||
case 'Enter':
|
|
||||||
event.preventDefault();
|
|
||||||
this.sliderInputBox._setDisplay('block');
|
|
||||||
return;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Key down handler
|
|
||||||
* increment slider position by +/- 1 when right/left arrow key is pressed or held
|
|
||||||
* @param {Event} event Keyboard even
|
|
||||||
*/
|
|
||||||
_keydown(event) {
|
|
||||||
let newVal = this.props.percent * this.props.max;
|
|
||||||
switch (event.key) {
|
|
||||||
case 'ArrowRight':
|
|
||||||
newVal += 1;
|
|
||||||
if (newVal <= this.props.max) this.props.onChange(newVal / this.props.max);
|
|
||||||
return;
|
|
||||||
case 'ArrowLeft':
|
|
||||||
newVal -= 1;
|
|
||||||
if (newVal >= 0) this.props.onChange(newVal / this.props.max);
|
|
||||||
return;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Touch start handler
|
|
||||||
* @param {Event} event DOM Event
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
_touchstart(event) {
|
|
||||||
this.touchStartTimer = setTimeout(() => this.sliderInputBox._setDisplay('block'), 1500);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Touch end handler
|
|
||||||
* @param {Event} event DOM Event
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
_touchend(event) {
|
|
||||||
this.sliderInputBox.sliderVal.focus();
|
|
||||||
clearTimeout(this.touchStartTimer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if the user is still dragging
|
* Determine if the user is still dragging
|
||||||
* @param {SyntheticEvent} event Event
|
* @param {SyntheticEvent} event Event
|
||||||
@@ -213,7 +144,7 @@ export default class Slider extends React.Component {
|
|||||||
let width = outerWidth - (margin * 2);
|
let width = outerWidth - (margin * 2);
|
||||||
let pctPos = width * this.props.percent;
|
let pctPos = width * this.props.percent;
|
||||||
return <div><svg
|
return <div><svg
|
||||||
onMouseUp={this._up} onMouseEnter={this._enter.bind(this)} onMouseMove={this._move} onKeyUp={this._keyup} onKeyDown={this._keydown} style={style} ref={node => this.node = node} tabIndex="0">
|
onMouseUp={this._up} onMouseEnter={this._enter.bind(this)} onMouseMove={this._move} style={style} ref={node => this.node = node} tabIndex="0">
|
||||||
<rect className='primary' style={{ opacity: 0.3 }} x={margin} y='0.25em' rx='0.3em' ry='0.3em' width={width} height='0.7em' />
|
<rect className='primary' style={{ opacity: 0.3 }} x={margin} y='0.25em' rx='0.3em' ry='0.3em' width={width} height='0.7em' />
|
||||||
<rect className='primary-disabled' x={margin} y='0.45em' rx='0.15em' ry='0.15em' width={pctPos} height='0.3em' />
|
<rect className='primary-disabled' x={margin} y='0.45em' rx='0.15em' ry='0.15em' width={pctPos} height='0.3em' />
|
||||||
<circle className='primary' r={margin} cy='0.6em' cx={pctPos + margin} />
|
<circle className='primary' r={margin} cy='0.6em' cx={pctPos + margin} />
|
||||||
@@ -224,163 +155,6 @@ export default class Slider extends React.Component {
|
|||||||
<text className='primary-disabled' y='3em' x='100%' style={{ textAnchor: 'end' }}>{max + axisUnit}</text>
|
<text className='primary-disabled' y='3em' x='100%' style={{ textAnchor: 'end' }}>{max + axisUnit}</text>
|
||||||
</g>}
|
</g>}
|
||||||
</svg>
|
</svg>
|
||||||
<TextInputBox ref={(tb) => this.sliderInputBox = tb}
|
|
||||||
onChange={this.props.onChange}
|
|
||||||
percent={this.props.percent}
|
|
||||||
axisUnit={this.props.axisUnit}
|
|
||||||
scale={this.props.scale}
|
|
||||||
max={this.props.max}
|
|
||||||
/>
|
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* New component to add keyboard support for sliders - works on all devices (desktop, iOS, Android)
|
|
||||||
**/
|
|
||||||
class TextInputBox extends React.Component {
|
|
||||||
static propTypes = {
|
|
||||||
axisUnit: PropTypes.string,// units (T, M, etc.)
|
|
||||||
max: PropTypes.number,
|
|
||||||
onChange: PropTypes.func.isRequired,// function which determins percent value
|
|
||||||
percent: PropTypes.number.isRequired,// value of slider
|
|
||||||
scale: PropTypes.number
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Determine if the user is still dragging
|
|
||||||
* @param {Object} props React Component properties
|
|
||||||
*/
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this._handleFocus = this._handleFocus.bind(this);
|
|
||||||
this._handleBlur = this._handleBlur.bind(this);
|
|
||||||
this._handleChange = this._handleChange.bind(this);
|
|
||||||
this._keyup = this._keyup.bind(this);
|
|
||||||
this.state = this._getInitialState();
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Update input value if slider changes will change props/state
|
|
||||||
* @param {Object} nextProps React Component properites
|
|
||||||
* @param {Object} nextState React Component state values
|
|
||||||
*/
|
|
||||||
componentWillReceiveProps(nextProps, nextState) {
|
|
||||||
let nextValue = nextProps.percent * nextProps.max;
|
|
||||||
// See https://stackoverflow.com/questions/32414308/updating-state-on-props-change-in-react-form
|
|
||||||
if (nextValue !== this.state.inputValue && nextValue <= nextProps.max) {
|
|
||||||
this.setState({ inputValue: nextValue });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Update slider textbox visibility/values if changes are made to slider
|
|
||||||
* @param {Object} prevProps React Component properites
|
|
||||||
* @param {Object} prevState React Component state values
|
|
||||||
*/
|
|
||||||
componentDidUpdate(prevProps, prevState) {
|
|
||||||
if (prevState.divStyle.display == 'none' && this.state.divStyle.display == 'block') {
|
|
||||||
this.enterTimer = setTimeout(() => this.sliderVal.focus(), 10);
|
|
||||||
}
|
|
||||||
if (prevProps.max !== this.props.max && this.state.inputValue > this.props.max) {
|
|
||||||
// they chose a different module
|
|
||||||
this.setState({ inputValue: this.props.max });
|
|
||||||
}
|
|
||||||
if (this.state.inputValue != prevState.inputValue && prevProps.max == this.props.max) {
|
|
||||||
this.props.onChange(this.state.inputValue / this.props.max);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Set initial state for the textbox.
|
|
||||||
* We may want to rethink this to
|
|
||||||
* try and make it a stateless component
|
|
||||||
* @returns {object} React state object with initial values set
|
|
||||||
*/
|
|
||||||
_getInitialState() {
|
|
||||||
return {
|
|
||||||
divStyle: { display:'none' },
|
|
||||||
inputStyle: { width:'4em' },
|
|
||||||
labelStyle: { marginLeft: '.1em' },
|
|
||||||
maxLength:5,
|
|
||||||
size:5,
|
|
||||||
min:0,
|
|
||||||
tabIndex:-1,
|
|
||||||
type:'number',
|
|
||||||
readOnly: true,
|
|
||||||
inputValue: this.props.percent * this.props.max
|
|
||||||
};
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param {string} val block or none
|
|
||||||
*/
|
|
||||||
_setDisplay(val) {
|
|
||||||
this.setState({
|
|
||||||
divStyle: { display:val }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Update the input value
|
|
||||||
* when textbox gets focus
|
|
||||||
*/
|
|
||||||
_handleFocus() {
|
|
||||||
this.setState({
|
|
||||||
inputValue:this._getValue()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Update inputValue when textbox loses focus
|
|
||||||
*/
|
|
||||||
_handleBlur() {
|
|
||||||
this._setDisplay('none');
|
|
||||||
if (this.state.inputValue !== '') {
|
|
||||||
this.props.onChange(this.state.inputValue / this.props.max);
|
|
||||||
} else {
|
|
||||||
this.setState({
|
|
||||||
inputValue: this.props.percent * this.props.max
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Get the value in the text box
|
|
||||||
* @returns {number} inputValue Value of the input box
|
|
||||||
*/
|
|
||||||
_getValue() {
|
|
||||||
return this.state.inputValue;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Update and set limits on input box
|
|
||||||
* values depending on what user
|
|
||||||
* has selected
|
|
||||||
*
|
|
||||||
* @param {SyntheticEvent} event ReactJs onChange event
|
|
||||||
*/
|
|
||||||
_handleChange(event) {
|
|
||||||
if (event.target.value < 0) {
|
|
||||||
this.setState({ inputValue: 0 });
|
|
||||||
} else if (event.target.value <= this.props.max) {
|
|
||||||
this.setState({ inputValue: event.target.value });
|
|
||||||
} else {
|
|
||||||
this.setState({ inputValue: this.props.max });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Key up handler for input field.
|
|
||||||
* If user hits Enter key, blur/close the input field
|
|
||||||
* @param {Event} event Keyboard event
|
|
||||||
*/
|
|
||||||
_keyup(event) {
|
|
||||||
switch (event.key) {
|
|
||||||
case 'Enter':
|
|
||||||
this.sliderVal.blur();
|
|
||||||
return;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Get the value in the text box
|
|
||||||
* @return {React.Component} Text Input component for Slider
|
|
||||||
*/
|
|
||||||
render() {
|
|
||||||
let { axisUnit, onChange, percent, scale } = this.props;
|
|
||||||
return <div style={this.state.divStyle}><input style={this.state.inputStyle} value={this._getValue()} min={this.state.min} max={this.props.max} onChange={this._handleChange} onKeyUp={this._keyup} tabIndex={this.state.tabIndex} maxLength={this.state.maxLength} size={this.state.size} onBlur={() => {this._handleBlur();}} onFocus={() => {this._handleFocus();}} type={this.state.type} ref={(ip) => this.sliderVal = ip}/><text className="primary upp" style={this.state.labelStyle}>{this.props.axisUnit}</text></div>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,11 +5,10 @@ import cn from 'classnames';
|
|||||||
import { ListModifications, Modified } from './SvgIcons';
|
import { ListModifications, Modified } from './SvgIcons';
|
||||||
import AvailableModulesMenu from './AvailableModulesMenu';
|
import AvailableModulesMenu from './AvailableModulesMenu';
|
||||||
import ModificationsMenu from './ModificationsMenu';
|
import ModificationsMenu from './ModificationsMenu';
|
||||||
import { diffDetails } from '../utils/SlotFunctions';
|
|
||||||
import { stopCtxPropagation, wrapCtxMenu } from '../utils/UtilityFunctions';
|
import { stopCtxPropagation, wrapCtxMenu } from '../utils/UtilityFunctions';
|
||||||
import { blueprintTooltip } from '../utils/BlueprintFunctions';
|
import { blueprintTooltip } from '../utils/BlueprintFunctions';
|
||||||
import { Module } from 'ed-forge';
|
import { Module } from 'ed-forge';
|
||||||
import { REG_MILITARY_SLOT, REG_HARDPOINT_SLOT } from 'ed-forge/lib/data/slots';
|
import { TYPES } from 'ed-forge/lib/src/data/slots';
|
||||||
import autoBind from 'auto-bind';
|
import autoBind from 'auto-bind';
|
||||||
import { toPairs } from 'lodash';
|
import { toPairs } from 'lodash';
|
||||||
|
|
||||||
@@ -79,7 +78,7 @@ export default class Slot extends TranslatedComponent {
|
|||||||
if (m.isEmpty()) {
|
if (m.isEmpty()) {
|
||||||
return <div className="empty">
|
return <div className="empty">
|
||||||
{translate(
|
{translate(
|
||||||
m.getSlot().match(REG_MILITARY_SLOT) ? 'emptyrestricted' : 'empty'
|
m.isOnSlot(TYPES.MILITARY) ? 'emptyrestricted' : 'empty'
|
||||||
)}
|
)}
|
||||||
</div>;
|
</div>;
|
||||||
} else {
|
} else {
|
||||||
@@ -167,14 +166,14 @@ export default class Slot extends TranslatedComponent {
|
|||||||
*/
|
*/
|
||||||
_getMaxClassLabel() {
|
_getMaxClassLabel() {
|
||||||
const { m } = this.props;
|
const { m } = this.props;
|
||||||
let size = m.getSize();
|
let size = m.getSizeNum();
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case m.getSlot() === 'armour':
|
case m.getSlot() === 'armour':
|
||||||
return '';
|
return '';
|
||||||
case size === 0:
|
case size === 0:
|
||||||
// This can also happen for armour but that case was handled above
|
// This can also happen for armour but that case was handled above
|
||||||
return 'U';
|
return 'U';
|
||||||
case Boolean(m.getSlot().match(REG_HARDPOINT_SLOT)):
|
case m.isOnSlot(TYPES.HARDPOINT):
|
||||||
return HARDPOINT_SLOT_LABELS[size];
|
return HARDPOINT_SLOT_LABELS[size];
|
||||||
default:
|
default:
|
||||||
return size;
|
return size;
|
||||||
@@ -234,7 +233,6 @@ export default class Slot extends TranslatedComponent {
|
|||||||
this.context.closeMenu();
|
this.context.closeMenu();
|
||||||
}}
|
}}
|
||||||
warning={warning}
|
warning={warning}
|
||||||
// diffDetails={diffDetails.bind(ship, this.context.language)}
|
|
||||||
/>}
|
/>}
|
||||||
{selected && menuIndex === 1 &&
|
{selected && menuIndex === 1 &&
|
||||||
<ModificationsMenu m={m} propsToShow={propsToShow}
|
<ModificationsMenu m={m} propsToShow={propsToShow}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import React from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import TranslatedComponent from './TranslatedComponent';
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
import { wrapCtxMenu } from '../utils/UtilityFunctions';
|
import { wrapCtxMenu } from '../utils/UtilityFunctions';
|
||||||
import { canMount } from '../utils/SlotFunctions';
|
|
||||||
import { Equalizer } from '../components/SvgIcons';
|
import { Equalizer } from '../components/SvgIcons';
|
||||||
import cn from 'classnames';
|
import cn from 'classnames';
|
||||||
import { Ship } from 'ed-forge';
|
import { Ship } from 'ed-forge';
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import cn from 'classnames';
|
|
||||||
import SlotSection from './SlotSection';
|
import SlotSection from './SlotSection';
|
||||||
import Slot from './Slot';
|
import Slot from './Slot';
|
||||||
import Module from '../shipyard/Module';
|
|
||||||
import * as ShipRoles from '../shipyard/ShipRoles';
|
|
||||||
import autoBind from 'auto-bind';
|
import autoBind from 'auto-bind';
|
||||||
import { stopCtxPropagation, moduleGet } from '../utils/UtilityFunctions';
|
import { stopCtxPropagation, moduleGet } from '../utils/UtilityFunctions';
|
||||||
import { ShipProps } from 'ed-forge';
|
import { ShipProps, Module } from 'ed-forge';
|
||||||
|
import { getModuleInfo } from 'ed-forge/lib/src/data/items';
|
||||||
const { CONSUMED_RETR, LADEN_MASS } = ShipProps;
|
const { CONSUMED_RETR, LADEN_MASS } = ShipProps;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -24,65 +22,35 @@ export default class StandardSlotSection extends SlotSection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use the lightest/optimal available standard modules
|
* Resets all modules of the ship
|
||||||
*/
|
*/
|
||||||
_optimizeStandard() {
|
_emptyAll() {
|
||||||
this.props.ship.useLightestStandard();
|
this.props.ship.getModules().forEach((slot) => slot.reset());
|
||||||
this._close();
|
this._close();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fill all standard slots with the specificed rating (using max class)
|
* Sets all modules to a specific rating
|
||||||
* @param {Boolean} shielded True if shield generator should be included
|
* @param {string} rating Module rating to set
|
||||||
* @param {integer} bulkheadIndex Bulkhead to use see Constants.BulkheadNames
|
* @param {string} fsdPPException Custom rating for FSD
|
||||||
*/
|
*/
|
||||||
_multiPurpose(shielded, bulkheadIndex) {
|
_nRated(rating, fsdPPException) {
|
||||||
ShipRoles.multiPurpose(this.props.ship, shielded, bulkheadIndex);
|
const { ship } = this.props;
|
||||||
|
const pp = ship.getPowerPlant();
|
||||||
|
pp.setItem('powerplant', pp.getSize(), fsdPPException || rating);
|
||||||
|
const eng = ship.getThrusters();
|
||||||
|
eng.setItem('thrusters', eng.getSize(), rating);
|
||||||
|
const fsd = ship.getFSD();
|
||||||
|
fsd.setItem('fsd', fsd.getSize(), fsdPPException || rating);
|
||||||
|
const ls = ship.getLifeSupport();
|
||||||
|
ls.setItem('lifesupport', ls.getSize(), rating);
|
||||||
|
const pd = ship.getPowerDistributor();
|
||||||
|
pd.setItem('powerdistributor', pd.getSize(), rating);
|
||||||
|
const sen = ship.getSensors();
|
||||||
|
sen.setItem('sensors', sen.getSize(), rating);
|
||||||
this._close();
|
this._close();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Trader Build
|
|
||||||
* @param {Boolean} shielded True if shield generator should be included
|
|
||||||
*/
|
|
||||||
_optimizeCargo(shielded) {
|
|
||||||
ShipRoles.trader(this.props.ship, shielded);
|
|
||||||
this._close();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Miner Build
|
|
||||||
* @param {Boolean} shielded True if shield generator should be included
|
|
||||||
*/
|
|
||||||
_optimizeMiner(shielded) {
|
|
||||||
ShipRoles.miner(this.props.ship, shielded);
|
|
||||||
this._close();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Explorer role
|
|
||||||
* @param {Boolean} planetary True if Planetary Vehicle Hangar (PVH) should be included
|
|
||||||
*/
|
|
||||||
_optimizeExplorer(planetary) {
|
|
||||||
ShipRoles.explorer(this.props.ship, planetary);
|
|
||||||
this._close();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Racer role
|
|
||||||
*/
|
|
||||||
_optimizeRacer() {
|
|
||||||
ShipRoles.racer(this.props.ship);
|
|
||||||
this._close();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* On right click optimize the standard modules
|
|
||||||
*/
|
|
||||||
_contextMenu() {
|
|
||||||
this._optimizeStandard();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new slot for a given module.
|
* Creates a new slot for a given module.
|
||||||
* @param {Module} m Module to create the slot for
|
* @param {Module} m Module to create the slot for
|
||||||
@@ -136,17 +104,13 @@ export default class StandardSlotSection extends SlotSection {
|
|||||||
const { translate } = this.context.language;
|
const { translate } = this.context.language;
|
||||||
return <div className='select' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
|
return <div className='select' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
|
||||||
<ul>
|
<ul>
|
||||||
<li className='lc' tabIndex="0" onClick={this._optimizeStandard}>{translate('Maximize Jump Range')}</li>
|
<li className='lc' tabIndex="0" onClick={this._emptyAll}>{translate('empty all slots')}</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div className='select-group cap'>{translate('roles')}</div>
|
<div className='select-group cap'>{translate('core')}</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li className='lc' tabIndex="0" onClick={this._multiPurpose.bind(this, false, 0)}>{translate('Multi-purpose')}</li>
|
<li className='lc' tabIndex="0" onClick={this._nRated.bind(this, '5', undefined)}>{translate('A-rated')}</li>
|
||||||
<li className='lc' tabIndex="0" onClick={this._multiPurpose.bind(this, true, 2)}>{translate('Combat')}</li>
|
<li className='lc' tabIndex="0" onClick={this._nRated.bind(this, '2', undefined)}>{translate('D-rated')}</li>
|
||||||
<li className='lc' tabIndex="0" onClick={this._optimizeCargo.bind(this, true)}>{translate('Trader')}</li>
|
<li className='lc' tabIndex="0" onClick={this._nRated.bind(this, '2', '5')}>{translate('D-rated + A-rated FSD/PP')}</li>
|
||||||
<li className='lc' tabIndex="0" onClick={this._optimizeExplorer.bind(this, false)}>{translate('Explorer')}</li>
|
|
||||||
<li className='lc' tabIndex="0" onClick={this._optimizeExplorer.bind(this, true)}>{translate('Planetary Explorer')}</li>
|
|
||||||
<li className='lc' tabIndex="0" onClick={this._optimizeMiner.bind(this, true)}>{translate('Miner')}</li>
|
|
||||||
<li className='lc' tabIndex="0" onClick={this._optimizeRacer.bind(this)}>{translate('Racer')}</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -262,7 +262,7 @@ export class MatIcon extends SvgIcon {
|
|||||||
*/
|
*/
|
||||||
svg() {
|
svg() {
|
||||||
return<g xmlns="http://www.w3.org/2000/svg">
|
return<g xmlns="http://www.w3.org/2000/svg">
|
||||||
<path fill="#FF7100" d="M 24.86,4.18
|
<path d="M 24.86,4.18
|
||||||
C 24.86,4.18 17.17,7.82 17.17,7.82
|
C 24.86,4.18 17.17,7.82 17.17,7.82
|
||||||
17.17,7.82 15.35,14.55 15.35,14.55
|
17.17,7.82 15.35,14.55 15.35,14.55
|
||||||
15.35,14.55 24.70,9.75 24.70,9.75
|
15.35,14.55 24.70,9.75 24.70,9.75
|
||||||
|
|||||||
@@ -21,29 +21,24 @@ export default class UtilitySlotSection extends SlotSection {
|
|||||||
* Empty all utility slots and close the menu
|
* Empty all utility slots and close the menu
|
||||||
*/
|
*/
|
||||||
_empty() {
|
_empty() {
|
||||||
this.props.ship.emptyUtility();
|
this.props.ship.getUtilities().forEach((slot) => slot.reset());
|
||||||
this.props.onChange();
|
|
||||||
this._close();
|
this._close();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mount module in utility slot, replace all if Alt is held
|
* Mount module in utility slot, replace all if Alt is held
|
||||||
* @param {string} group Module Group name
|
* @param {string} type Module type
|
||||||
* @param {string} rating Module Rating
|
* @param {string} rating Module Rating
|
||||||
* @param {string} name Module name
|
|
||||||
* @param {Synthetic} event Event
|
* @param {Synthetic} event Event
|
||||||
*/
|
*/
|
||||||
_use(group, rating, name, event) {
|
_use(type, rating, event) {
|
||||||
this.props.ship.useUtility(group, rating, name, event.getModifierState('Alt'));
|
const fillAll = event.getModifierState('Alt');
|
||||||
this.props.onChange();
|
for (const slot of this.props.ship.getUtilities(undefined, true)) {
|
||||||
this._close();
|
if (slot.isEmpty() || fillAll) {
|
||||||
|
slot.setItem(type, '', rating);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/**
|
this._close();
|
||||||
* Empty all utility slots on right-click
|
|
||||||
*/
|
|
||||||
_contextMenu() {
|
|
||||||
this._empty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -58,8 +53,6 @@ export default class UtilitySlotSection extends SlotSection {
|
|||||||
for (let h of ship.getUtilities(undefined, true)) {
|
for (let h of ship.getUtilities(undefined, true)) {
|
||||||
slots.push(<Slot
|
slots.push(<Slot
|
||||||
key={h.object.Slot}
|
key={h.object.Slot}
|
||||||
maxClass={h.getSize()}
|
|
||||||
onChange={this.props.onChange}
|
|
||||||
currentMenu={currentMenu}
|
currentMenu={currentMenu}
|
||||||
drag={this._drag.bind(this, h)}
|
drag={this._drag.bind(this, h)}
|
||||||
dragOver={this._dragOverSlot.bind(this, h)}
|
dragOver={this._dragOverSlot.bind(this, h)}
|
||||||
@@ -91,23 +84,23 @@ export default class UtilitySlotSection extends SlotSection {
|
|||||||
</ul>
|
</ul>
|
||||||
<div className='select-group cap'>{translate('sb')}</div>
|
<div className='select-group cap'>{translate('sb')}</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li className='c' tabIndex='0' onClick={_use.bind(this, 'sb', 'A', null)}>A</li>
|
<li className='c' tabIndex='0' onClick={_use.bind(this, 'shieldbooster', '5')}>A</li>
|
||||||
<li className='c' tabIndex='0' onClick={_use.bind(this, 'sb', 'B', null)}>B</li>
|
<li className='c' tabIndex='0' onClick={_use.bind(this, 'shieldbooster', '4')}>B</li>
|
||||||
<li className='c' tabIndex='0' onClick={_use.bind(this, 'sb', 'C', null)}>C</li>
|
<li className='c' tabIndex='0' onClick={_use.bind(this, 'shieldbooster', '3')}>C</li>
|
||||||
<li className='c' tabIndex='0' onClick={_use.bind(this, 'sb', 'D', null)}>D</li>
|
<li className='c' tabIndex='0' onClick={_use.bind(this, 'shieldbooster', '2')}>D</li>
|
||||||
<li className='c' tabIndex='0' onClick={_use.bind(this, 'sb', 'E', null)}>E</li>
|
<li className='c' tabIndex='0' onClick={_use.bind(this, 'shieldbooster', '1')}>E</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div className='select-group cap'>{translate('hs')}</div>
|
<div className='select-group cap'>{translate('hs')}</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li className='lc' tabIndex='0' onClick={_use.bind(this, 'hs', null, 'Heat Sink Launcher')}>{translate('Heat Sink Launcher')}</li>
|
<li className='lc' tabIndex='0' onClick={_use.bind(this, 'heatsinklauncher', '')}>{translate('Heat Sink Launcher')}</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div className='select-group cap'>{translate('ch')}</div>
|
<div className='select-group cap'>{translate('ch')}</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li className='lc' tabIndex='0' onClick={_use.bind(this, 'ch', null, 'Chaff Launcher')}>{translate('Chaff Launcher')}</li>
|
<li className='lc' tabIndex='0' onClick={_use.bind(this, 'chafflauncher', '')}>{translate('Chaff Launcher')}</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div className='select-group cap'>{translate('po')}</div>
|
<div className='select-group cap'>{translate('po')}</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li className='lc' tabIndex='0' onClick={_use.bind(this, 'po', null, 'Point Defence')}>{translate('Point Defence')}</li>
|
<li className='lc' tabIndex='0' onClick={_use.bind(this, 'pointdefence', '')}>{translate('Point Defence')}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ import React from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import TranslatedComponent from './TranslatedComponent';
|
import TranslatedComponent from './TranslatedComponent';
|
||||||
import LineChart from '../components/LineChart';
|
import LineChart from '../components/LineChart';
|
||||||
import * as Calc from '../shipyard/Calculations';
|
import { moduleReduce } from 'ed-forge/lib/src/helper';
|
||||||
import { moduleReduce } from 'ed-forge/lib/helper';
|
|
||||||
import { chain, keys, mapValues, values } from 'lodash';
|
import { chain, keys, mapValues, values } from 'lodash';
|
||||||
|
|
||||||
const DAMAGE_DEALT_COLORS = ['#FFFFFF', '#FF0000', '#00FF00', '#7777FF', '#FFFF00', '#FF00FF', '#00FFFF', '#777777'];
|
const DAMAGE_DEALT_COLORS = ['#FFFFFF', '#FF0000', '#00FF00', '#7777FF', '#FFFF00', '#FF00FF', '#00FFFF', '#777777'];
|
||||||
|
|||||||
@@ -133,7 +133,7 @@
|
|||||||
"pv": "Planetenfahrzeug-Hangar",
|
"pv": "Planetenfahrzeug-Hangar",
|
||||||
"rf": "Raffinerie",
|
"rf": "Raffinerie",
|
||||||
"rg": "Schienenkanone",
|
"rg": "Schienenkanone",
|
||||||
"rsl": "Steuerung Aufklärungsdrohne",
|
"rsl": "Steuerung Forschungsdrohne",
|
||||||
"s": "Sensoren",
|
"s": "Sensoren",
|
||||||
"sb": "Schildverstärker",
|
"sb": "Schildverstärker",
|
||||||
"sc": "Himmelskörperscanner",
|
"sc": "Himmelskörperscanner",
|
||||||
@@ -146,7 +146,7 @@
|
|||||||
"ul": "Salvenlaser",
|
"ul": "Salvenlaser",
|
||||||
"ws": "FS-Sogwolkenscanner",
|
"ws": "FS-Sogwolkenscanner",
|
||||||
"rpl": "Steuerung Reparaturdrohne",
|
"rpl": "Steuerung Reparaturdrohne",
|
||||||
"rcpl": "Recon Limpet Controller",
|
"rcpl": "Steuerung Aufklärungsdrohne",
|
||||||
"hrd": "Hüllenhärte",
|
"hrd": "Hüllenhärte",
|
||||||
"pax": "Pass",
|
"pax": "Pass",
|
||||||
"axmc": "AX-Mehrfachgeschütz",
|
"axmc": "AX-Mehrfachgeschütz",
|
||||||
@@ -162,7 +162,7 @@
|
|||||||
"life support": "Lebenserhaltung",
|
"life support": "Lebenserhaltung",
|
||||||
"power plant": "Kraftwerk",
|
"power plant": "Kraftwerk",
|
||||||
"thrusters": "Antriebe",
|
"thrusters": "Antriebe",
|
||||||
"power distriubtor": "Energieverteiler",
|
"power distributor": "Energieverteiler",
|
||||||
"sensors": "Sensoren",
|
"sensors": "Sensoren",
|
||||||
"bins": "Behältnisse",
|
"bins": "Behältnisse",
|
||||||
"bays": "Slots",
|
"bays": "Slots",
|
||||||
|
|||||||
@@ -82,7 +82,7 @@
|
|||||||
"TT_SUMMARY_LADEN_TOTAL_JUMP": "Farthest possible range with full cargo, a full fuel tank, and jumping as far as possible each time",
|
"TT_SUMMARY_LADEN_TOTAL_JUMP": "Farthest possible range with full cargo, a full fuel tank, and jumping as far as possible each time",
|
||||||
"HELP_MODIFICATIONS_MENU": "Click on a number to enter a new value, or drag along the bar for small changes",
|
"HELP_MODIFICATIONS_MENU": "Click on a number to enter a new value, or drag along the bar for small changes",
|
||||||
"PHRASE_FAIL_EDENGINEER": "Failed to send to EDEngineer (Launch EDEngineer and make sure the API is started then refresh the page.)",
|
"PHRASE_FAIL_EDENGINEER": "Failed to send to EDEngineer (Launch EDEngineer and make sure the API is started then refresh the page.)",
|
||||||
"PHRASE_FIREFOX_EDENGINEER": "Sending to EDEngineer is not compatible with Firefox's security settings. Please try again with Chrome.",
|
"PHRASE_FIREFOX_EDENGINEER": "Send to EDEngineer is incompatible with Firefox.",
|
||||||
"am": "Auto Field-Maintenance Unit",
|
"am": "Auto Field-Maintenance Unit",
|
||||||
"bh": "Bulkheads",
|
"bh": "Bulkheads",
|
||||||
"bl": "Beam Laser",
|
"bl": "Beam Laser",
|
||||||
@@ -205,7 +205,7 @@
|
|||||||
"internal protection": "Internal protection",
|
"internal protection": "Internal protection",
|
||||||
"external protection": "External protection",
|
"external protection": "External protection",
|
||||||
"engagement range": "Engagement range",
|
"engagement range": "Engagement range",
|
||||||
"boost interval": "Boost intervall",
|
"boost interval": "Boost interval",
|
||||||
"total": "Total",
|
"total": "Total",
|
||||||
"ammo": "Ammunition maximum",
|
"ammo": "Ammunition maximum",
|
||||||
"boot": "Boot time",
|
"boot": "Boot time",
|
||||||
|
|||||||
@@ -2,15 +2,15 @@ export const formats = {
|
|||||||
decimal: ',',
|
decimal: ',',
|
||||||
thousands: '.',
|
thousands: '.',
|
||||||
grouping: [3],
|
grouping: [3],
|
||||||
currency: ['', ' €'],
|
currency: ['$', ''],
|
||||||
dateTime: '%A, %e de %B de %Y, %X',
|
dateTime: '%A, %e de %B de %Y, %X',
|
||||||
date: '%d/%m/%Y',
|
date: '%d/%m/%Y',
|
||||||
time: '%H:%M:%S',
|
time: '%H:%M:%S',
|
||||||
periods: ['AM', 'PM'],
|
periods: ['AM', 'PM'],
|
||||||
days: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes', 'sábado'],
|
days: ['domingo', 'segunda', 'terça', 'quarta', 'quinta', 'sexta', 'sábado'],
|
||||||
shortDays: ['dom', 'lun', 'mar', 'mié', 'jue', 'vie', 'sáb'],
|
shortDays: ['dom', 'seg', 'ter', 'qua', 'qui', 'sex', 'sab'],
|
||||||
months: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio', 'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre'],
|
months: ['janeiro', 'fevereiro', 'março', 'abril', 'maio', 'junho', 'julho', 'agosto', 'setembro', 'outubro', 'novembro', 'dezembro'],
|
||||||
shortMonths: ['ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul', 'ago', 'sep', 'oct', 'nov', 'dic']
|
shortMonths: ['jan', 'fev', 'mar', 'abr', 'mai', 'jun', 'jul', 'ago', 'set', 'out', 'nov', 'dez']
|
||||||
};
|
};
|
||||||
|
|
||||||
export { default as terms } from './pt.json';
|
export { default as terms } from './pt.json';
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -5,7 +5,7 @@
|
|||||||
"PHRASE_EXPORT_DESC": "Детальный JSON-экспорт вашей сборки для использования в других местах и инструментах",
|
"PHRASE_EXPORT_DESC": "Детальный JSON-экспорт вашей сборки для использования в других местах и инструментах",
|
||||||
"PHRASE_FASTEST_RANGE": "Последовательные прыжки максимальной дальности",
|
"PHRASE_FASTEST_RANGE": "Последовательные прыжки максимальной дальности",
|
||||||
"PHRASE_IMPORT": "Для импорта вставьте код в эту форму",
|
"PHRASE_IMPORT": "Для импорта вставьте код в эту форму",
|
||||||
"PHRASE_LADEN": "Масса корабля с учётом топлива и грузов",
|
"PHRASE_LADEN": "Масса корабля с учетом топлива и грузов",
|
||||||
"PHRASE_NO_BUILDS": "Нечего сравнивать",
|
"PHRASE_NO_BUILDS": "Нечего сравнивать",
|
||||||
"PHRASE_NO_RETROCH": "Нет ранних версий сборки",
|
"PHRASE_NO_RETROCH": "Нет ранних версий сборки",
|
||||||
"PHRASE_SELECT_BUILDS": "Выберите конфигурацию для сравнения",
|
"PHRASE_SELECT_BUILDS": "Выберите конфигурацию для сравнения",
|
||||||
@@ -13,18 +13,21 @@
|
|||||||
"PHRASE_SG_RECOVER": "Восстановление с 0% до 50% объема щита, учитывая полный аккумулятор СИС в начале",
|
"PHRASE_SG_RECOVER": "Восстановление с 0% до 50% объема щита, учитывая полный аккумулятор СИС в начале",
|
||||||
"PHRASE_UNLADEN": "Масса корабля без учета топлива и грузов",
|
"PHRASE_UNLADEN": "Масса корабля без учета топлива и грузов",
|
||||||
"PHRASE_UPDATE_RDY": "Доступна новая версия. Нажмите для обновления.",
|
"PHRASE_UPDATE_RDY": "Доступна новая версия. Нажмите для обновления.",
|
||||||
"PHRASE_ENGAGEMENT_RANGE": "Дистанция между кораблём и целью",
|
"PHRASE_ENGAGEMENT_RANGE": "Дистанция между кораблем и целью",
|
||||||
"PHRASE_SELECT_BLUEPRINT": "Нажмите чтобы выбрать чертёж",
|
"PHRASE_SELECT_BLUEPRINT": "Нажмите чтобы выбрать чертеж",
|
||||||
"PHRASE_BLUEPRINT_WORST": "Худшие основные значения для чертежа",
|
"PHRASE_BLUEPRINT_WORST": "Худшие основные значения для чертежа",
|
||||||
|
"PHRASE_BLUEPRINT_FIFTY": "50% значения для чертежа",
|
||||||
|
"PHRASE_BLUEPRINT_SEVEN_FIVE": "75% значения для чертежа",
|
||||||
"PHRASE_BLUEPRINT_RANDOM": "Случайный выбор между худшими и лучшими значениями для этого чертежа",
|
"PHRASE_BLUEPRINT_RANDOM": "Случайный выбор между худшими и лучшими значениями для этого чертежа",
|
||||||
"PHRASE_BLUEPRINT_BEST": "Лучшие основные значения для чертежа",
|
"PHRASE_BLUEPRINT_BEST": "Лучшие основные значения для чертежа",
|
||||||
"PHRASE_BLUEPRINT_EXTREME": "Лучшие положительные и худшие отрицательные основные значения для чертежа",
|
"PHRASE_BLUEPRINT_EXTREME": "Лучшие положительные и худшие отрицательные основные значения для чертежа",
|
||||||
"PHRASE_BLUEPRINT_RESET": "Убрать все изменения и чертёж",
|
"PHRASE_BLUEPRINT_RESET": "Сбросить все модификаторы и чертеж",
|
||||||
"PHRASE_SELECT_SPECIAL": "Нажмите, чтобы выбрать экспериментальный эффект",
|
"PHRASE_SELECT_SPECIAL": "Нажмите, чтобы выбрать экспериментальный эффект",
|
||||||
"PHRASE_NO_SPECIAL": "Без экспериментального эффекта",
|
"PHRASE_NO_SPECIAL": "Без экспериментального эффекта",
|
||||||
"PHRASE_SHOPPING_LIST": "Станции, что продают эту сборку",
|
"PHRASE_SHOPPING_LIST": "Станции, что продают эту сборку",
|
||||||
|
"PHRASE_SHOPPING_MATS": "Материалы которые нужны для сборки",
|
||||||
"PHRASE_REFIT_SHOPPING_LIST": "Станции, что продают необходимые модули",
|
"PHRASE_REFIT_SHOPPING_LIST": "Станции, что продают необходимые модули",
|
||||||
"PHRASE_TOTAL_EFFECTIVE_SHIELD": "Общий урон, что может быть нанесён в каждым типе, если используются все щитонакопители",
|
"PHRASE_TOTAL_EFFECTIVE_SHIELD": "Общий урон, что может быть нанесен в каждым типе, если используются все щитонакопители",
|
||||||
"PHRASE_TIME_TO_LOSE_SHIELDS": "Щиты продержатся",
|
"PHRASE_TIME_TO_LOSE_SHIELDS": "Щиты продержатся",
|
||||||
"PHRASE_TIME_TO_RECOVER_SHIELDS": "Щиты восстановятся за",
|
"PHRASE_TIME_TO_RECOVER_SHIELDS": "Щиты восстановятся за",
|
||||||
"PHRASE_TIME_TO_RECHARGE_SHIELDS": "Щиты будут заряжены за",
|
"PHRASE_TIME_TO_RECHARGE_SHIELDS": "Щиты будут заряжены за",
|
||||||
@@ -34,51 +37,59 @@
|
|||||||
"PHRASE_EFFECTIVE_ARMOUR": "Эффективная сила брони против разных типов урона",
|
"PHRASE_EFFECTIVE_ARMOUR": "Эффективная сила брони против разных типов урона",
|
||||||
"PHRASE_DAMAGE_TAKEN": "% общих повреждений полученных в разных типах урона",
|
"PHRASE_DAMAGE_TAKEN": "% общих повреждений полученных в разных типах урона",
|
||||||
"PHRASE_TIME_TO_LOSE_ARMOUR": "Броня продержится",
|
"PHRASE_TIME_TO_LOSE_ARMOUR": "Броня продержится",
|
||||||
"PHRASE_MODULE_PROTECTION_EXTERNAL": "Защита гнёзд",
|
"PHRASE_MODULE_PROTECTION_EXTERNAL": "Защита гнезд",
|
||||||
"PHRASE_MODULE_PROTECTION_INTERNAL": "Защита всех остальных модулей",
|
"PHRASE_MODULE_PROTECTION_INTERNAL": "Защита всех остальных модулей",
|
||||||
|
"PHRASE_OVERALL_DAMAGE": "Разбивка источников устойчивого ДПС",
|
||||||
"PHRASE_SHIELD_DAMAGE": "Подробности источников поддерживаемого ДПС против щитов",
|
"PHRASE_SHIELD_DAMAGE": "Подробности источников поддерживаемого ДПС против щитов",
|
||||||
"PHRASE_ARMOUR_DAMAGE": "Подробности источников поддерживаемого ДПС против брони",
|
"PHRASE_ARMOUR_DAMAGE": "Подробности источников поддерживаемого ДПС против брони",
|
||||||
"PHRASE_TIME_TO_REMOVE_SHIELDS": "Снимет щиты за",
|
"PHRASE_TIME_TO_REMOVE_SHIELDS": "Снимет щиты за",
|
||||||
"TT_TIME_TO_REMOVE_SHIELDS": "Непрерывным огнём из всех орудий",
|
"PHRASE_MULTI_CREW_CAPACITOR_POINTS": "Щелкните правой кновкой мыши чтобы объединить в группу.",
|
||||||
|
"TT_TIME_TO_REMOVE_SHIELDS": "Непрерывным огнем из всех орудий",
|
||||||
"PHRASE_TIME_TO_REMOVE_ARMOUR": "Снимет броню за",
|
"PHRASE_TIME_TO_REMOVE_ARMOUR": "Снимет броню за",
|
||||||
"TT_TIME_TO_REMOVE_ARMOUR": "Непрерывным огнём из всех орудий",
|
"TT_TIME_TO_REMOVE_ARMOUR": "Непрерывным огнем из всех орудий",
|
||||||
"PHRASE_TIME_TO_DRAIN_WEP": "Опустошит ОРУЖ за",
|
"PHRASE_TIME_TO_DRAIN_WEP": "Опустошит ОРУ за",
|
||||||
"TT_TIME_TO_DRAIN_WEP": "Время, за которое опустошится аккумулятор ОРУЖ при стрельбе из всех орудий",
|
"TT_TIME_TO_DRAIN_WEP": "Время, за которое опустошится аккумулятор ОРУ при стрельбе из всех орудий",
|
||||||
"TT_TIME_TO_LOSE_SHIELDS": "Против поддерживаемой стрельбы из всех орудий противника",
|
"TT_TIME_TO_LOSE_SHIELDS": "Против поддерживаемой стрельбы из всех орудий противника",
|
||||||
"TT_TIME_TO_LOSE_ARMOUR": "Против поддерживаемой стрельбы из всех орудий противника",
|
"TT_TIME_TO_LOSE_ARMOUR": "Против поддерживаемой стрельбы из всех орудий противника",
|
||||||
"TT_MODULE_ARMOUR": "Броня, защищающая модули от урона",
|
"TT_MODULE_ARMOUR": "Броня, защищающая модули от урона",
|
||||||
"TT_MODULE_PROTECTION_EXTERNAL": "Процент урона, перенаправленного от гнёзд на наборы для усиления модулей",
|
"TT_MODULE_PROTECTION_EXTERNAL": "Процент урона, перенаправленного от гнезд на наборы для усиления модулей",
|
||||||
"TT_MODULE_PROTECTION_INTERNAL": "Процент урона, перенаправленного от модулей вне гнёзд на наборы для усиления модулей",
|
"TT_MODULE_PROTECTION_INTERNAL": "Процент урона, перенаправленного от модулей вне гнезд на наборы для усиления модулей",
|
||||||
"TT_EFFECTIVE_SDPS_SHIELDS": "Реальный поддерживаемый ДПС пока аккумулятор ОРУЖ не пуст",
|
"TT_EFFECTIVE_SDPS_SHIELDS": "Реальный поддерживаемый ДПС пока аккумулятор ОРУ не пуст",
|
||||||
"TT_EFFECTIVENESS_SHIELDS": "Эффективность в сравнении с попаданием по цели с 0-сопротивляемостью без пунктов в СИС на 0 метрах",
|
"TT_EFFECTIVENESS_SHIELDS": "Эффективность в сравнении с попаданием по цели с 0-сопротивляемостью без пунктов в СИС на 0 метрах",
|
||||||
"TT_EFFECTIVE_SDPS_ARMOUR": "Реальный поддерживаемый ДПС пока аккумулятор ОРУЖ не пуст",
|
"TT_EFFECTIVE_SDPS_ARMOUR": "Реальный поддерживаемый ДПС пока аккумулятор ОРУ не пуст",
|
||||||
"TT_EFFECTIVENESS_ARMOUR": "Эффективность в сравнении с попаданием по цели с 0-сопротивляемостью на 0 метрах",
|
"TT_EFFECTIVENESS_ARMOUR": "Эффективность в сравнении с попаданием по цели с 0-сопротивляемостью на 0 метрах",
|
||||||
"PHRASE_EFFECTIVE_SDPS_SHIELDS": "ПДПС против щитов",
|
"PHRASE_EFFECTIVE_SDPS_SHIELDS": "ПДПС против щитов",
|
||||||
"PHRASE_EFFECTIVE_SDPS_ARMOUR": "ПДПС против брони",
|
"PHRASE_EFFECTIVE_SDPS_ARMOUR": "ПДПС против брони",
|
||||||
"TT_SUMMARY_SPEED": "С полным топливным баком и 4 пунктами в ДВИ",
|
"TT_SUMMARY_SPEED": "С полным топливным баком и 4 пунктами в ДВГ",
|
||||||
"TT_SUMMARY_SPEED_NONFUNCTIONAL": "Маневровые двигатели выключены или превышена максимальная масса с топливом и грузом",
|
"TT_SUMMARY_SPEED_NONFUNCTIONAL": "Маневровые двигатели выключены или превышена максимальная масса с топливом и грузом",
|
||||||
"TT_SUMMARY_BOOST": "С полным топливным баком и 4 пунктами в ДВИ",
|
"TT_SUMMARY_BOOST": "С полным топливным баком и 4 пунктами в ДВГ",
|
||||||
|
"TT_SUMMARY_BOOST_INTERVAL": "Время заполнения с 4 пунктами в СИС",
|
||||||
"TT_SUMMARY_BOOST_NONFUNCTIONAL": "Распределитель питания не может обеспечить достаточно энергии для форсажа",
|
"TT_SUMMARY_BOOST_NONFUNCTIONAL": "Распределитель питания не может обеспечить достаточно энергии для форсажа",
|
||||||
"TT_SUMMARY_SHIELDS": "Чистая сила щита, включая усилители",
|
"TT_SUMMARY_SHIELDS": "Чистая сила щита, включая усилители",
|
||||||
|
"TT_SUMMARY_SHIELDS_SCB": "Прочность щита, включая бустеры и SCB",
|
||||||
"TT_SUMMARY_SHIELDS_NONFUNCTIONAL": "Шитогенератор отсутствует или выключен",
|
"TT_SUMMARY_SHIELDS_NONFUNCTIONAL": "Шитогенератор отсутствует или выключен",
|
||||||
"TT_SUMMARY_INTEGRITY": "Целостность корабля, включая переборки и наборы для усиления корпуса",
|
"TT_SUMMARY_INTEGRITY": "Целостность корабля, включая переборки и наборы для усиления корпуса",
|
||||||
"TT_SUMMARY_HULL_MASS": "Масса корпуса без каких-либо модулей",
|
"TT_SUMMARY_HULL_MASS": "Масса корпуса без каких-либо модулей",
|
||||||
"TT_SUMMARY_UNLADEN_MASS": "Масса корпуса и модулей без топлива и груза",
|
"TT_SUMMARY_UNLADEN_MASS": "Масса корпуса и модулей без топлива и груза",
|
||||||
"TT_SUMMARY_LADEN_MASS": "Масса корпуса и модулей с топливом и грузом",
|
"TT_SUMMARY_LADEN_MASS": "Масса корпуса и модулей с топливом и грузом",
|
||||||
"TT_SUMMARY_DPS": "Урон в секунду при стрельбе из всех орудий",
|
"TT_SUMMARY_DPS": "Урон в секунду при стрельбе из всех орудий",
|
||||||
"TT_SUMMARY_EPS": "Расход аккумулятора ОРУЖ в секунду при стрельбе из всех орудий",
|
"TT_SUMMARY_EPS": "Расход аккумулятора ОРУ в секунду при стрельбе из всех орудий",
|
||||||
"TT_SUMMARY_TTD": "Время расхода аккумулятора ОРУЖ при стрельбе из всех орудий и с 4 пунктами в ОРУЖ",
|
"TT_SUMMARY_TTD": "Время расхода аккумулятора ОРУ при стрельбе из всех орудий и с 4 пунктами в ОРУ",
|
||||||
"TT_SUMMARY_MAX_SINGLE_JUMP": "Самый дальний возможный прыжок без груза и с топливом, достаточным только на сам прыжок",
|
"TT_SUMMARY_MAX_SINGLE_JUMP": "Самый дальний возможный прыжок без груза и с топливом, достаточным только на сам прыжок",
|
||||||
"TT_SUMMARY_UNLADEN_SINGLE_JUMP": "Самый дальний возможный прыжок без груза и с полным топливным баком",
|
"TT_SUMMARY_UNLADEN_SINGLE_JUMP": "Самый дальний возможный прыжок без груза и с полным топливным баком",
|
||||||
"TT_SUMMARY_LADEN_SINGLE_JUMP": "Самый дальний возможный прыжок с полным грузовым отсеком и с полным топливным баком",
|
"TT_SUMMARY_LADEN_SINGLE_JUMP": "Самый дальний возможный прыжок с полным грузовым отсеком и с полным топливным баком",
|
||||||
"TT_SUMMARY_UNLADEN_TOTAL_JUMP": "Самая дальняя общая дистанция без груза, с полным топливным баком и при прыжках на максимальное расстояние",
|
"TT_SUMMARY_UNLADEN_TOTAL_JUMP": "Самая дальняя общая дистанция без груза, с полным топливным баком и при прыжках на максимальное расстояние",
|
||||||
"TT_SUMMARY_LADEN_TOTAL_JUMP": "Самая дальняя общая дистанция с полным грузовым отсеком, с полным топливным баком и при прыжках на максимальное расстояние",
|
"TT_SUMMARY_LADEN_TOTAL_JUMP": "Самая дальняя общая дистанция с полным грузовым отсеком, с полным топливным баком и при прыжках на максимальное расстояние",
|
||||||
"HELP_MODIFICATIONS_MENU": "Нажмите на номер, чтобы ввести новое значение, или потяните вдоль полосы для малых изменений",
|
"HELP_MODIFICATIONS_MENU": "Нажмите на номер, чтобы ввести новое значение, или потяните вдоль полосы для малых изменений",
|
||||||
|
"PHRASE_FAIL_EDENGINEER": "Не удалось отправить в EDEngineer (запустите EDEngineer и убедитесь, что API запущен, затем обновите страницу).",
|
||||||
|
"PHRASE_FIREFOX_EDENGINEER": "Отправка в EDEngineer несовместима с настройками безопасности Firefox. Пожалуйста, попробуйте еще раз в Google Chrome.",
|
||||||
"am": "Блок Автом. Полевого Ремонта",
|
"am": "Блок Автом. Полевого Ремонта",
|
||||||
"bh": "Переборки",
|
"bh": "Переборки",
|
||||||
"bl": "Пучковый лазер",
|
"bl": "Пучковый лазер",
|
||||||
"bsg": "Двухпоточный щитогенератор",
|
"bsg": "Двухпоточный щитогенератор",
|
||||||
"c": "Орудие",
|
"c": "Пушка",
|
||||||
|
"causres": "Каустическое сопротивление",
|
||||||
|
"Caustic resistance": "Каустическое сопротивление",
|
||||||
"cc": "Контроллер магнитного снаряда для сбора",
|
"cc": "Контроллер магнитного снаряда для сбора",
|
||||||
"ch": "Разбрасыватель дипольных отражателей",
|
"ch": "Разбрасыватель дипольных отражателей",
|
||||||
"cr": "Грузовой стеллаж",
|
"cr": "Грузовой стеллаж",
|
||||||
@@ -98,14 +109,17 @@
|
|||||||
"kw": "Сканер преступников",
|
"kw": "Сканер преступников",
|
||||||
"ls": "Система жизнеобеспечения",
|
"ls": "Система жизнеобеспечения",
|
||||||
"mc": "Многоствольное орудие",
|
"mc": "Многоствольное орудие",
|
||||||
|
"axmc": "Многоствольное орудие АИ",
|
||||||
"ml": "Проходочный лазер",
|
"ml": "Проходочный лазер",
|
||||||
"mr": "Ракетный лоток",
|
"mr": "Ракетный лоток",
|
||||||
|
"axmr": "Блок ракет АИ",
|
||||||
"mrp": "Набор для усиления модуля",
|
"mrp": "Набор для усиления модуля",
|
||||||
"nl": "Мины",
|
"nl": "Мины",
|
||||||
"pa": "Ускоритель плазмы",
|
"pa": "Ускоритель плазмы",
|
||||||
"pas": "Комплект для сближения с планетой",
|
"pas": "Комплект для сближения с планетой",
|
||||||
"pc": "Контроллер магнитного снаряда для геологоразведки",
|
"pc": "Контроллер магнитного снаряда для геологоразведки",
|
||||||
"pce": "Каюта пассажира эконом-класса",
|
"pce": "Каюта пассажира эконом-класса",
|
||||||
|
"passenger capacity": "Количество пассажиров",
|
||||||
"pci": "Каюта пассажира бизнес-класса",
|
"pci": "Каюта пассажира бизнес-класса",
|
||||||
"pcm": "Каюта пассажира первого класса",
|
"pcm": "Каюта пассажира первого класса",
|
||||||
"pcq": "Каюта пассажира класса люкс",
|
"pcq": "Каюта пассажира класса люкс",
|
||||||
@@ -113,33 +127,63 @@
|
|||||||
"pl": "Импульсный лазер",
|
"pl": "Импульсный лазер",
|
||||||
"po": "Точечная оборона",
|
"po": "Точечная оборона",
|
||||||
"pp": "Силовая установка",
|
"pp": "Силовая установка",
|
||||||
|
"gpp": "Силовая установка Стражей",
|
||||||
|
"gpd": "Гибридный распределитель питания Стражей",
|
||||||
|
"gpc": "Плазменная пушка Стражей",
|
||||||
|
"ggc": "Пушка Гаусса Стражей",
|
||||||
|
"gsrp": "Набор для усиления щита Стражей",
|
||||||
|
"gfsb": "Ускоритель FSD Стражей",
|
||||||
|
"ghrp": "Набор для усиления корпуса Стражей",
|
||||||
|
"gmrp": "Набор для усиления модуля Стражей",
|
||||||
|
"pwa": "Анализатор импульсных волн",
|
||||||
|
"abl": "Абразивный бластер",
|
||||||
|
"scl": "Пусковая установка для сейсмических снарядов",
|
||||||
|
"sdm": "Вытесняющая ракета для добычи глубинных залежей",
|
||||||
|
"tbsc": "Шоковое орудие",
|
||||||
|
"gsc": "Осколочное орудие Стражей",
|
||||||
"psg": "Призматический щитогенератор",
|
"psg": "Призматический щитогенератор",
|
||||||
"pv": "Гараж для планетарного транспорта",
|
"pv": "Гараж для планетарного транспорта",
|
||||||
"rf": "Устройство переработки",
|
"rf": "Устройство переработки",
|
||||||
|
"rfl": "Зенитная установка (снаряды с дистанционным подрывом)",
|
||||||
"rg": "Электромагнитная пушка",
|
"rg": "Электромагнитная пушка",
|
||||||
|
"rsl": "Дроны-исследователи",
|
||||||
"s": "Сенсоры",
|
"s": "Сенсоры",
|
||||||
"sb": "Усилитель щита",
|
"sb": "Усилитель щита",
|
||||||
"sc": "Сканер обнаружения",
|
"sc": "Сканер обнаружения",
|
||||||
"scb": "Щитонакопитель",
|
"scb": "Щитонакопитель",
|
||||||
|
"sfn": "Нейтрализатор глушащего поля",
|
||||||
"sg": "Щитогенератор",
|
"sg": "Щитогенератор",
|
||||||
"ss": "Сканер поверхностей",
|
"ss": "Сканер поверхностей",
|
||||||
|
"sua": "Помощь в гиперкрейсерском режиме",
|
||||||
"t": "Маневровые двигатели",
|
"t": "Маневровые двигатели",
|
||||||
"tp": "Торпедная стойка",
|
"tp": "Торпедная стойка",
|
||||||
"ul": "Пульсирующие лазеры",
|
"ul": "Пульсирующие лазеры",
|
||||||
|
"Send To EDEngineer": "Отправить в EDEngineer",
|
||||||
"ws": "Сканер следа FSD",
|
"ws": "Сканер следа FSD",
|
||||||
|
"rpl": "Дроны-ремонтники",
|
||||||
|
"rcpl": "Дроны-разведчики",
|
||||||
|
"xs": "Сканер «инопланетянин»",
|
||||||
|
"tbem": "Блок энзимных ракет",
|
||||||
|
"tbrfl": "Установка для стрельбы стреловидными снарядами с дистанционным подрывом",
|
||||||
|
"dtl": "Дроны-очистители",
|
||||||
|
"mahr": "Набор для усиления корпуса из Метасплава",
|
||||||
"emptyrestricted": "пусто (ограниченно)",
|
"emptyrestricted": "пусто (ограниченно)",
|
||||||
"damage dealt to": "Урон нанесён",
|
"damage dealt to": "Урон нанесен",
|
||||||
"damage received from": "Урон получен от",
|
"damage received from": "Урон получен от",
|
||||||
"against shields": "Против щитов",
|
"against shields": "Против щитов",
|
||||||
"against hull": "Против корпуса",
|
"against hull": "Против корпуса",
|
||||||
"total effective shield": "Общие эффективные щиты",
|
"total effective shield": "Общие эффективные щиты",
|
||||||
"ammunition": "Припасы",
|
"ammunition": "Припасы",
|
||||||
"secs": "с",
|
"secs": "с",
|
||||||
"rebuildsperbay": "Построек за полосу",
|
"bays": "Ячейки",
|
||||||
|
"rebuildsperbay": "Истребителей в ячейке",
|
||||||
|
"mroll": "Roll",
|
||||||
|
"feature": "Свойство",
|
||||||
"worst": "Худшее",
|
"worst": "Худшее",
|
||||||
"average": "Среднее",
|
"average": "Среднее",
|
||||||
"random": "Случайное",
|
"random": "Случайное",
|
||||||
"best": "Лучшее",
|
"best": "Лучшее",
|
||||||
|
"current": "Текущее",
|
||||||
"extreme": "Экстремальное",
|
"extreme": "Экстремальное",
|
||||||
"reset": "Сброс",
|
"reset": "Сброс",
|
||||||
"dpe": "Урон на МДж энергии",
|
"dpe": "Урон на МДж энергии",
|
||||||
@@ -148,6 +192,8 @@
|
|||||||
"dpssdps": "Урон в секунду (поддерживаемый урон в секунду)",
|
"dpssdps": "Урон в секунду (поддерживаемый урон в секунду)",
|
||||||
"eps": "Энергия в секунду",
|
"eps": "Энергия в секунду",
|
||||||
"epsseps": "Энергия в секунду (поддерживаемая энергия в секунду)",
|
"epsseps": "Энергия в секунду (поддерживаемая энергия в секунду)",
|
||||||
|
"fallofffromrange": "Спад",
|
||||||
|
"falloff": "Спад",
|
||||||
"hps": "Нагрев в секунду",
|
"hps": "Нагрев в секунду",
|
||||||
"hpsshps": "Нагрев в секунду (поддерживаемый нагрев в секунду)",
|
"hpsshps": "Нагрев в секунду (поддерживаемый нагрев в секунду)",
|
||||||
"damage by": "Урон",
|
"damage by": "Урон",
|
||||||
@@ -164,13 +210,15 @@
|
|||||||
"internal protection": "Внутренняя защита",
|
"internal protection": "Внутренняя защита",
|
||||||
"external protection": "Внешняя защита",
|
"external protection": "Внешняя защита",
|
||||||
"engagement range": "Боевое расстояние",
|
"engagement range": "Боевое расстояние",
|
||||||
|
"boost interval": "Интервал повыш.",
|
||||||
"total": "Всего",
|
"total": "Всего",
|
||||||
"ammo": "Боекомплект",
|
"ammo": "Макс. боекомплект",
|
||||||
"boot": "Время загрузки",
|
"boot": "Время загрузки",
|
||||||
|
"hacktime": "Время взлома",
|
||||||
"brokenregen": "Скорость восстановления при пробое",
|
"brokenregen": "Скорость восстановления при пробое",
|
||||||
"burst": "Длина очереди",
|
"burst": "Длина очереди",
|
||||||
"burstrof": "Скорострельность очереди",
|
"burstrof": "Скорострельность очереди",
|
||||||
"clip": "Боекомплект",
|
"clip": "Размер боекомплекта",
|
||||||
"damage": "Урон",
|
"damage": "Урон",
|
||||||
"distdraw": "Тяга распределителя",
|
"distdraw": "Тяга распределителя",
|
||||||
"duration": "Продолжительность",
|
"duration": "Продолжительность",
|
||||||
@@ -199,11 +247,16 @@
|
|||||||
"rof": "Скорострельность",
|
"rof": "Скорострельность",
|
||||||
"angle": "Угол сканера",
|
"angle": "Угол сканера",
|
||||||
"scanrate": "Скорость сканера",
|
"scanrate": "Скорость сканера",
|
||||||
|
"proberadius": "Радиус зонда",
|
||||||
"scantime": "Время сканирования",
|
"scantime": "Время сканирования",
|
||||||
|
"scan range": "Дальность",
|
||||||
|
"max angle": "Макс. угол",
|
||||||
"shield": "Щит",
|
"shield": "Щит",
|
||||||
|
"armour": "Броня",
|
||||||
"shieldboost": "Усиление щитов",
|
"shieldboost": "Усиление щитов",
|
||||||
"shieldreinforcement": "Усилитель щита",
|
"shieldreinforcement": "Усилитель щита",
|
||||||
"shotspeed": "Скорость выстрела",
|
"shotspeed": "Скорость выстрела",
|
||||||
|
"shotdmg": "Урон за выстрел(DPS)",
|
||||||
"spinup": "Время раскрутки",
|
"spinup": "Время раскрутки",
|
||||||
"syscap": "Ресурс систем",
|
"syscap": "Ресурс систем",
|
||||||
"sysrate": "Перезарядка систем",
|
"sysrate": "Перезарядка систем",
|
||||||
@@ -234,9 +287,12 @@
|
|||||||
"explosive": "Взрывч.",
|
"explosive": "Взрывч.",
|
||||||
"kinetic": "Механич.",
|
"kinetic": "Механич.",
|
||||||
"thermal": "Тепл.",
|
"thermal": "Тепл.",
|
||||||
|
"caustic": "Каустич.",
|
||||||
"generator": "Генератор",
|
"generator": "Генератор",
|
||||||
"boosters": "Усилители",
|
"boosters": "Усилители",
|
||||||
"cells": "Ячейки",
|
"cells": "Ячейки",
|
||||||
|
"shield addition": "ДОбавления к щиту",
|
||||||
|
"jump addition": "ДОбавления к прыжку",
|
||||||
"bulkheads": "Переборки",
|
"bulkheads": "Переборки",
|
||||||
"reinforcement": "Усилители",
|
"reinforcement": "Усилители",
|
||||||
"power and costs": "Энергия и стоимость",
|
"power and costs": "Энергия и стоимость",
|
||||||
@@ -250,7 +306,7 @@
|
|||||||
"damage to opponent's shields": "Урон щиту противника",
|
"damage to opponent's shields": "Урон щиту противника",
|
||||||
"damage to opponent's hull": "Урон корпусу противника",
|
"damage to opponent's hull": "Урон корпусу противника",
|
||||||
"offence": "Нападение",
|
"offence": "Нападение",
|
||||||
"defence": "Оборона",
|
"defence": "Защита",
|
||||||
"shield metrics": "Данные щита",
|
"shield metrics": "Данные щита",
|
||||||
"raw shield strength": "Чистая мощность щита",
|
"raw shield strength": "Чистая мощность щита",
|
||||||
"shield sources": "Ресурсы щита",
|
"shield sources": "Ресурсы щита",
|
||||||
@@ -265,28 +321,70 @@
|
|||||||
"defence metrics": "Данные обороны",
|
"defence metrics": "Данные обороны",
|
||||||
"fuel carried": "Топливо на борту",
|
"fuel carried": "Топливо на борту",
|
||||||
"cargo carried": "Груз на борту",
|
"cargo carried": "Груз на борту",
|
||||||
"ship control": "Управление кораблём",
|
"ship control": "Управление кораблем",
|
||||||
"opponent": "Противник",
|
"opponent": "Противник",
|
||||||
"opponent's shields": "Щит противника",
|
"opponent's shields": "Щит противника",
|
||||||
"opponent's armour": "Броня противника",
|
"opponent's armour": "Броня противника",
|
||||||
|
"overall damage": "общий урон",
|
||||||
|
"overall": "общий",
|
||||||
"shield damage sources": "источники урона по щиту",
|
"shield damage sources": "источники урона по щиту",
|
||||||
"armour damage sources": "источники урона по броне",
|
"armour damage sources": "источники урона по броне",
|
||||||
"never": "Никогда",
|
"never": "Никогда",
|
||||||
"stock": "базовый",
|
"stock": "базовый",
|
||||||
"boost": "форсаж",
|
"boost": "форсаж",
|
||||||
|
"tab_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": "Минимальный ранг империи для покупки",
|
||||||
|
"kg": "кг",
|
||||||
|
"kg/s": "кг/с",
|
||||||
|
"km": "км",
|
||||||
|
"m": "м",
|
||||||
|
"MJ": "МДж",
|
||||||
|
"MW": "МВт",
|
||||||
|
"T": "т",
|
||||||
|
"°/s": "°/с",
|
||||||
"/s": "/с",
|
"/s": "/с",
|
||||||
|
"/min": "/мин",
|
||||||
"m/s": "м/с",
|
"m/s": "м/с",
|
||||||
"Ls": "Св.сек",
|
"Ls": "Св.сек",
|
||||||
"LY": "Св.лет",
|
"LY": "Св.лет",
|
||||||
"CR": "кр.",
|
"CR": "кр.",
|
||||||
"S": "M",
|
"S": "М",
|
||||||
"M": "С",
|
"M": "С",
|
||||||
"L": "б",
|
"L": "Б",
|
||||||
"H": "O",
|
"H": "О",
|
||||||
"U": "B",
|
"U": "В",
|
||||||
"small": "Малый",
|
"small": "Малый",
|
||||||
"medium": "Средний",
|
"medium": "Средний",
|
||||||
"large": "большой",
|
"large": "Большой",
|
||||||
"alpha": "Альфа",
|
"alpha": "Альфа",
|
||||||
"beta": "Бета",
|
"beta": "Бета",
|
||||||
"standard": "Стандартный",
|
"standard": "Стандартный",
|
||||||
@@ -304,23 +402,24 @@
|
|||||||
"full tank": "Полный бак",
|
"full tank": "Полный бак",
|
||||||
"internal compartments": "внутренние отсеки",
|
"internal compartments": "внутренние отсеки",
|
||||||
"jump range": "Дальность прыжка",
|
"jump range": "Дальность прыжка",
|
||||||
"mass lock factor": "Масс. блок",
|
"mass lock factor": "Коэффициент гравитационного захвата",
|
||||||
"max mass": "Максимальная масса",
|
"max mass": "Максимальная масса",
|
||||||
|
"minimum mass": "Минимальная масса",
|
||||||
|
"optimal mass": "Оптимальная масса",
|
||||||
"net cost": "разница в цене",
|
"net cost": "разница в цене",
|
||||||
"none created": "не создано",
|
"none created": "не создано",
|
||||||
"refuel time": "Время дозаправки",
|
"refuel time": "Время дозаправки",
|
||||||
"retrofit from": "модификация от",
|
"retrofit from": "модификация от",
|
||||||
"T-Load": "Тепл.",
|
"T-Load": "Тепл.",
|
||||||
"utility mounts": "Вспомогательное оборудование",
|
"utility mounts": "Вспомогательное оборудование",
|
||||||
"about": "О ...",
|
"about": "О сайте",
|
||||||
"action": "Действие",
|
"action": "Действие",
|
||||||
"added": "Добавлено",
|
"added": "Добавлено",
|
||||||
"armour": "Броня",
|
|
||||||
"available": "доступно",
|
"available": "доступно",
|
||||||
"backup": "Резервная копия",
|
"backup": "Резервная копия",
|
||||||
"bins": "контейнеры",
|
"bins": "контейнеры",
|
||||||
"build": "cборка",
|
"build": "сборка",
|
||||||
"builds": "cборки",
|
"builds": "сборки",
|
||||||
"buy": "купить",
|
"buy": "купить",
|
||||||
"cancel": "отменить",
|
"cancel": "отменить",
|
||||||
"cargo": "Груз",
|
"cargo": "Груз",
|
||||||
@@ -339,16 +438,16 @@
|
|||||||
"DPS": "УВС",
|
"DPS": "УВС",
|
||||||
"efficiency": "Эффективность",
|
"efficiency": "Эффективность",
|
||||||
"empty": "пусто",
|
"empty": "пусто",
|
||||||
"ENG": "ДВИ",
|
"ENG": "ДВГ",
|
||||||
"export": "Экспорт",
|
"export": "Экспорт",
|
||||||
"forum": "Форум",
|
"forum": "Форум",
|
||||||
"fuel": "Топливо",
|
"fuel": "Топл.",
|
||||||
"hardpoints": "Орудийные порты",
|
"hardpoints": "Орудийные порты",
|
||||||
"hull": "Корпус",
|
"hull": "Корпус",
|
||||||
"import": "импортировать ",
|
"import": "импортировать ",
|
||||||
"insurance": "Страховка",
|
"insurance": "Страховка",
|
||||||
"jumps": "Прыжков",
|
"jumps": "Прыжков",
|
||||||
"laden": "Груженый",
|
"laden": "Груж",
|
||||||
"language": "Язык",
|
"language": "Язык",
|
||||||
"maneuverability": "Маневренность",
|
"maneuverability": "Маневренность",
|
||||||
"max": "Макс",
|
"max": "Макс",
|
||||||
@@ -361,13 +460,15 @@
|
|||||||
"rate": "скорость",
|
"rate": "скорость",
|
||||||
"rename": "Переименовать",
|
"rename": "Переименовать",
|
||||||
"repair": "Починка",
|
"repair": "Починка",
|
||||||
"ret": "Убр.",
|
"ret": "Убр",
|
||||||
"retracted": "Убрано",
|
"retracted": "Убрано",
|
||||||
"ROF": "В/сек",
|
"ROF": "В\/сек",
|
||||||
"save": "Сохранить",
|
"save": "Сохранить",
|
||||||
"sell": "Продать",
|
"sell": "Продать",
|
||||||
"settings": "Настройки",
|
"settings": "Настройки",
|
||||||
"shields": "Щиты",
|
"shields": "Щиты",
|
||||||
|
"No Shield": "Нет щита",
|
||||||
|
"Never": "Никогда",
|
||||||
"ship": "Корабль",
|
"ship": "Корабль",
|
||||||
"ships": "Корабли",
|
"ships": "Корабли",
|
||||||
"shortened": "Укороченный",
|
"shortened": "Укороченный",
|
||||||
@@ -377,8 +478,296 @@
|
|||||||
"SYS": "СИС",
|
"SYS": "СИС",
|
||||||
"time": "Время",
|
"time": "Время",
|
||||||
"type": "Тип",
|
"type": "Тип",
|
||||||
"unladen": "Пустой",
|
"unladen": "Пуст",
|
||||||
"URL": "Ссылка",
|
"URL": "Ссылка",
|
||||||
"WEP": "ОРУЖ",
|
"WEP": "ОРУ",
|
||||||
"yes": "Да"
|
"yes": "Да",
|
||||||
|
"crew": "экипаж",
|
||||||
|
"jump": "прыж",
|
||||||
|
"pax": "псж",
|
||||||
|
"RST": "СБР",
|
||||||
|
"grade": "уровень",
|
||||||
|
"total laden": "всего груж",
|
||||||
|
"total unladen": "всего пуст",
|
||||||
|
"module": "модуль",
|
||||||
|
"announcements": "объявления",
|
||||||
|
"resistance": "сопротивление",
|
||||||
|
"Lightweight Alloy": "Легкие сплавы",
|
||||||
|
"base": "базовые",
|
||||||
|
"core module classes": "основные модули",
|
||||||
|
"Group highlighted ships": "Сгруппировать выделенные корабли",
|
||||||
|
"tooltips": "всплывающие подсказки",
|
||||||
|
"module resistances": "сопротивление модулей",
|
||||||
|
"power plant": "силовая установка",
|
||||||
|
"thrusters": "маневровые двигатели",
|
||||||
|
"frame shift drive": "рамочно-сместительный двигатель",
|
||||||
|
"life support": "система жизнеобеспечения",
|
||||||
|
"power distributor": "распределитель питания",
|
||||||
|
"sensors": "сенсоры",
|
||||||
|
"fuel tank": "топливный бак",
|
||||||
|
"resting heat (Beta)": "тепло покоя (бета)",
|
||||||
|
"hull hardness": "Прочность корпуса",
|
||||||
|
"weapon": "оружие",
|
||||||
|
"maximum speed": "максимальная скорость",
|
||||||
|
"maximum range": "максимальная дальность",
|
||||||
|
"shortlink": "короткая ссылка",
|
||||||
|
"guardian": "стражи",
|
||||||
|
"engineers": "инженеры",
|
||||||
|
"component": "компонент",
|
||||||
|
"amount": "кол-во",
|
||||||
|
"core internal": "основное оборуднование",
|
||||||
|
"optional internal": "доп. оборудование",
|
||||||
|
"Heat Sink Launcher": "Теплоотводная катапульта",
|
||||||
|
"scanners": "сканеры",
|
||||||
|
"experimental": "экспериментальное",
|
||||||
|
"mining": "шахтерство",
|
||||||
|
"lasers": "лазеры",
|
||||||
|
"ordnance": "артиллерия",
|
||||||
|
"projectiles": "с боеприпасами",
|
||||||
|
"hangars": "ангары",
|
||||||
|
"limpet controllers": "контроллеры снарядов",
|
||||||
|
"passenger cabins": "каюты пассажиров",
|
||||||
|
"structural reinforcement": "структурные усиления",
|
||||||
|
"flight assists": "помощники в полете",
|
||||||
|
"modifications": "модификации",
|
||||||
|
"wep_reload": "перезарядка",
|
||||||
|
"optimal multiplier": "оптимальный усилитель",
|
||||||
|
"Cargo Hatch": "Грузовой люк",
|
||||||
|
"Chaff Launcher": "Разбрасыватель дипольных отражателей",
|
||||||
|
"Point Defence": "Точечная оборона",
|
||||||
|
"Electronic Countermeasure": "Электронное противодействие",
|
||||||
|
"Xeno Scanner": "Сканер «инопланетянин»",
|
||||||
|
"Shutdown Field Neutraliser": "Нейтрализатор глушащего поля",
|
||||||
|
"Disruptor": "Диверсант",
|
||||||
|
"Pacifier": "Миротворец",
|
||||||
|
"Advanced Plasma Accelerator": "Улучшенный ускоритель плазмы",
|
||||||
|
"Cytoscrambler": "Дезинтегратор",
|
||||||
|
"Retributor": "Каратель",
|
||||||
|
"Enforcer": "Убийца",
|
||||||
|
"Imperial Hammer": "Имперский молот",
|
||||||
|
"Rocket Propelled FSD Disruptor": "Ракетный FSD-разрушитель",
|
||||||
|
"Pack-Hound": "Гончие",
|
||||||
|
"Shock Mine Launcher": "Установщик шоковых мин",
|
||||||
|
"Mining Lance": "Копье шахтера",
|
||||||
|
"Corrosion Resistant": "Коррозийно-устойчивый стеллаж",
|
||||||
|
"Standard Docking Computer": "Стандартный стыковочный компьютер",
|
||||||
|
"Advanced Docking Computer": "Улучшенный стыковочный компьютер",
|
||||||
|
"Detailed Surface Scanner": "Подробный сканер поверхности",
|
||||||
|
"Supercruise Assist": "Помощь в гиперкрейсерском режиме",
|
||||||
|
"Guardian Power Distributor": "Рапределитель питания Стражей",
|
||||||
|
"Enhanced Performance": "Усиленные маневровые двигатели",
|
||||||
|
"Guardian Hybrid Power Plant": "Гибридная силовая установка Стражей",
|
||||||
|
"Reinforced Alloy": "Укрепленные сплавы",
|
||||||
|
"Military Grade Composite": "Композит военного класса",
|
||||||
|
"Mirrored Surface Composite": "Композит с зеркальной поверхностью",
|
||||||
|
"Reactive Surface Composite": "Композит с реактивной поверхностью",
|
||||||
|
"Proto Light Alloys": "Опытные легкие сплавы",
|
||||||
|
"Ammo capacity": "Вместимость магазина",
|
||||||
|
"Lightweight": "Облегченный",
|
||||||
|
"Reinforced": "Усиленный",
|
||||||
|
"Shielded": "Защищенный",
|
||||||
|
"Fast scan": "Быстрое сканирование",
|
||||||
|
"Long range": "Дальнего действия",
|
||||||
|
"Wide angle": "Широкоугольный",
|
||||||
|
"Blast resistant": "Взрывостойкий",
|
||||||
|
"Heavy duty": "Надежный",
|
||||||
|
"Kinetic resistant": "Противокинетический",
|
||||||
|
"Resistance augmented": "С универсальной защитой",
|
||||||
|
"Thermal resistant": "Термостойкий",
|
||||||
|
"Thermo Block": "Блокировка тепла",
|
||||||
|
"Force Block": "Усиленная блокировка",
|
||||||
|
"Blast Block": "Блокировка взрыва",
|
||||||
|
"Flow Control": "Контроль интенсивности",
|
||||||
|
"Double Braced": "Двойная прочность",
|
||||||
|
"Super Capacitors": "Суперконденсаторы",
|
||||||
|
"Efficient": "Эффективный",
|
||||||
|
"Focused": "Точный",
|
||||||
|
"Overcharged": "Усиленный",
|
||||||
|
"Rapid fire": "Скорострельный",
|
||||||
|
"Short range": "Ближнего действия",
|
||||||
|
"Sturdy": "Прочный",
|
||||||
|
"Oversized": "Сверхразмер",
|
||||||
|
"Stripped Down": "Урезанный вариант",
|
||||||
|
"Phasing sequence": "Последовательность фазирования",
|
||||||
|
"Concordant sequence": "Последовательность координации",
|
||||||
|
"Scramble spectrum": "Отключающая сетка",
|
||||||
|
"Thermal shock": "Тепловой удар",
|
||||||
|
"Emissive munitions": "Эмиссионные припасы",
|
||||||
|
"Multi-servos": "Сервосистема",
|
||||||
|
"Inertial impact": "Инерционный удар",
|
||||||
|
"Thermal vent": "Теплоотдача",
|
||||||
|
"Regeneration sequence": "Последовательность восстановления",
|
||||||
|
"Thermal conduit": "Проводник тепла",
|
||||||
|
"High capacity": "Вместительный магазин",
|
||||||
|
"Incendiary rounds": "Зажигательные припасы",
|
||||||
|
"Auto loader": "Автоматическая система заряжения",
|
||||||
|
"Smart rounds": "Умные боеприпасы",
|
||||||
|
"Corrosive shell": "Разъедающие припасы",
|
||||||
|
"Force shell": "Усиленные снаряды",
|
||||||
|
"High yield shell": "Снаряд большой мощности",
|
||||||
|
"Dispersal field": "Рассеивающее поле",
|
||||||
|
"Thermal cascade": "Термический залп",
|
||||||
|
"Double shot": "Двойной выстрел",
|
||||||
|
"Dazzle shell": "Ослепляющие снаряды",
|
||||||
|
"Screening shell": "Заслоняющие снаряды",
|
||||||
|
"Drag munitions": "Замедляющие боеприпасы",
|
||||||
|
"Target lock breaker": "Генератор помех для системы захвата цели",
|
||||||
|
"Plasma Slug": "Плазменный рельсовый снаряд",
|
||||||
|
"Feedback Cascade": "Ответный запл",
|
||||||
|
"Super Penetrator": "Модуль сверхпробития",
|
||||||
|
"Overload munitions": "Вызывающие перезагрузку боеприпасы",
|
||||||
|
"Penetrator payload": "Бронебойные снаряды",
|
||||||
|
"Penetrator Payload": "Бронебойные снаряды",
|
||||||
|
"FSD interrupt": "Помеха для FSD",
|
||||||
|
"Penetrator Munitions": "Бронебойные боеголовки",
|
||||||
|
"Mass lock munition": "Боеприпасы с гравитационным захватом",
|
||||||
|
"Mass Lock Munition": "Боеприпасы с гравитационным захватом",
|
||||||
|
"Reverberating cascade": "Отраженный залп",
|
||||||
|
"Shift-lock canister": "Рамоблокирующая кассета",
|
||||||
|
"Ion disruptor": "Ионный дестабилизатор",
|
||||||
|
"Radiant Canister": "Светящаяся кассета",
|
||||||
|
"Expanded capture arc": "Расширенная дуга захвата",
|
||||||
|
"Enhanced low power": "Улучшенное энергосбережение",
|
||||||
|
"Fast Charge": "Быстрый заряд",
|
||||||
|
"Multi-weave": "Мультипрошивка",
|
||||||
|
"Hi-Cap": "Высокая ёмкость",
|
||||||
|
"Lo-draw": "Пониженное потребление",
|
||||||
|
"Rapid charge": "Быстрая зарядка",
|
||||||
|
"Specialised": "Адаптивный",
|
||||||
|
"Boss Cells": "Босс-ячейки",
|
||||||
|
"Recycling Cell": "Рециркуляционная ячейка",
|
||||||
|
"Reflective Plating": "Отражающая броня",
|
||||||
|
"Angled Plating": "Угловая броня",
|
||||||
|
"Layered Plating": "Многослойная броня",
|
||||||
|
"Deep Plating": "Утолщенная броня",
|
||||||
|
"Expanded Probe Scanning Radius": "Увеличение радиуса сканирования зондов",
|
||||||
|
"High charge capacity": "Высокоёмкий",
|
||||||
|
"Charge enhanced": "Быстрозаряжающийся",
|
||||||
|
"Engine focused": "Фокус на двигатель",
|
||||||
|
"System focused": "Фокус на систему",
|
||||||
|
"Weapon focused": "Фокус на орудия",
|
||||||
|
"Super Conduits": "Сверхпроводники",
|
||||||
|
"Cluster Capacitors": "Кассетные конденсаторы",
|
||||||
|
"Increased range": "Увеличенная дальность",
|
||||||
|
"Faster boot sequence": "Ускорение запуска",
|
||||||
|
"Deep Charge": "Заряд повышенной мощности",
|
||||||
|
"Thermal Spread": "Рассеивание тепла",
|
||||||
|
"Mass Manager": "Распределитель гравитации",
|
||||||
|
"Dirty": "«Грязная» донастройка",
|
||||||
|
"Clean": "«Чистая» донастройка",
|
||||||
|
"Drag Drives": "Ускорители",
|
||||||
|
"Drive Distributors": "Распределители тяги",
|
||||||
|
"Armoured": "Бронированный",
|
||||||
|
"Low emissions": "Малое излучение",
|
||||||
|
"Monstered": "Монстрация",
|
||||||
|
"roles": "роли",
|
||||||
|
"Maximize Jump Range": "Максимизировать дальность прыжка",
|
||||||
|
"Multi-purpose": "Многоцелевой",
|
||||||
|
"Combat": "Боец",
|
||||||
|
"Trader": "Торговец",
|
||||||
|
"Explorer": "Исследователь",
|
||||||
|
"Planetary Explorer": "Планетарный исследователь",
|
||||||
|
"Miner": "Шахтер",
|
||||||
|
"Racer": "Гонщик",
|
||||||
|
|
||||||
|
"Aberrant Shield Pattern Analysis": "Анализ аномального поведения щита",
|
||||||
|
"Abnormal Compact Emissions Data": "Аномальные компактные данные об излучении",
|
||||||
|
"Aerogel": "Аэрогель",
|
||||||
|
"Adaptive Encryptors Capture": "Захват адаптивного шифровальщика",
|
||||||
|
"Anomalous Bulk Scan Data": "Аномальный массив данных сканирования",
|
||||||
|
"Anomalous FSD Telemetry": "Аномальная телеметрия FSD",
|
||||||
|
"Antimony": "Сурьма",
|
||||||
|
"Arsenic": "Мышьяк",
|
||||||
|
"Atypical Disrupted Wake Echoes": "Атипичное эхо поврежденного следа",
|
||||||
|
"Atypical Encryption Archives": "Нетипичные архивы шифрования",
|
||||||
|
"Basic Conductors": "Простые проводники",
|
||||||
|
"Bio-Mechanical Conduits": "Биомеханические энергопроводники",
|
||||||
|
"Biotech Conductors": "Биотехнические проводники",
|
||||||
|
"Boron": "Бор",
|
||||||
|
"Cadmium": "Кадмий",
|
||||||
|
"Carbon": "Углерод",
|
||||||
|
"Carbon Fibre Plating": "Углеволоконная броня",
|
||||||
|
"Chemical Distillery": "Оборудование для перегонки химикатов",
|
||||||
|
"Chemical Manipulators": "Манипуляторы для работы с химикатами",
|
||||||
|
"Chemical Processors": "Оборудование для химобработки",
|
||||||
|
"Chromium": "Хром",
|
||||||
|
"Classified Scan Databanks": "Засекреченные базы данных сканированоя",
|
||||||
|
"Classified Scan Fragment": "Засекреченные фрагменты данных сканирования",
|
||||||
|
"Compound Shielding": "Многоступенчатая защита",
|
||||||
|
"Conductive Ceramics": "Проводящая керамика",
|
||||||
|
"Conductive Components": "Проводящие компоненты",
|
||||||
|
"Conductive Polymers": "Проводящие полимеры",
|
||||||
|
"Configurable Components": "Настраиваемые компоненты",
|
||||||
|
"Core Dynamics Composites": "Композиты Core Dynamics",
|
||||||
|
"Cracked Industrial Firmware": "Взломанные промышленные микропрограммы",
|
||||||
|
"Datamined Wake Exceptions": "Исключения из глубинного анализа данных следа",
|
||||||
|
"Decoded Emission Data": "Расшифрованные данные об излучении",
|
||||||
|
"Distorted Shield Cycle Recordings": "Поврежденные цикличные записи щита",
|
||||||
|
"Divergent Scan Data": "Неформатные данные сканирования",
|
||||||
|
"Eccentric Hyperspace Trajectories": "Аномальные траектории в гиперпространстве",
|
||||||
|
"Electrochemical Arrays": "Электрохимические массивы",
|
||||||
|
"Exceptional Scrambled Emission Data": "Исключительные зашифрованные данные об излучении",
|
||||||
|
"Exquisite Focus Crystals": "Отборные фокусировочные кристаллы",
|
||||||
|
"Flawed Focus Crystals": "Поврежденные фокусировочные кристаллы",
|
||||||
|
"Focus Crystals": "Фокусировочные кристаллы",
|
||||||
|
"Galvanising Alloys": "Сплавы для гальванизации",
|
||||||
|
"Germanium": "Германий",
|
||||||
|
"Grid Resistors": "Наборные резисторы",
|
||||||
|
"Heat Conduction Wiring": "Теплопроводящие провода",
|
||||||
|
"Heat Dispersion Plate": "Теплорассеивающая пластина",
|
||||||
|
"Heat Exchangers": "Теплообменные агрегаты",
|
||||||
|
"Heat Vanes": "Тепловые заслонки",
|
||||||
|
"High Density Composites": "Высокоплотные композиты",
|
||||||
|
"Hybrid Capacitors": "Гибридные конденсаторы",
|
||||||
|
"Imperial Shielding": "Имперская защита",
|
||||||
|
"Improvised Components": "Кустарные компоненты",
|
||||||
|
"Inconsistent Shield Soak Analysis": "Неполный анализ поглощения щита",
|
||||||
|
"Iron": "Железо",
|
||||||
|
"Irregular Emission Data": "Нестандартные данные об излучении",
|
||||||
|
"Manganese": "Марганец",
|
||||||
|
"Mechanical Components": "Механические компоненты",
|
||||||
|
"Mechanical Equipment": "Механическое оборудование",
|
||||||
|
"Mechanical Scrap": "Механические отходы",
|
||||||
|
"Mercury": "Ртуть",
|
||||||
|
"Military Grade Alloys": "Сплавы военного назначения",
|
||||||
|
"Military Supercapacitors": "Военные суперконденсаторы",
|
||||||
|
"Modified Consumer Firmware": "Измененные пользовательские микропрограммы",
|
||||||
|
"Modified Embedded Firmware": "Измененные встроенные микропрограммы",
|
||||||
|
"Molybdenum": "Молибден",
|
||||||
|
"Nickel": "Никель",
|
||||||
|
"Niobium": "Ниобий",
|
||||||
|
"Open Symmetric Keys": "Открытые симметричные ключи",
|
||||||
|
"Pharmaceutical Isolators": "Фармацевтические изоляционные материалы",
|
||||||
|
"Phase Alloys": "Фазовые сплавы",
|
||||||
|
"Phosphorus": "Фосфор",
|
||||||
|
"Polymer Capacitors": "Полимерные конденсаторы",
|
||||||
|
"Precipitated Alloys": "Осажденные сплавы",
|
||||||
|
"Proprietary Composites": "Патентованные композиты",
|
||||||
|
"Proto Heat Radiators": "Прототипы теплоизлучателей",
|
||||||
|
"Proto Radiolic Alloys": "Сплавы для изготовления зондов",
|
||||||
|
"Refined Focus Crystals": "Обработанные фокусировочные кристаллы",
|
||||||
|
"Ruthenium": "Рутений",
|
||||||
|
"Salvaged Alloys": "Захваченные сплавы",
|
||||||
|
"Security Firmware Patch": "Обновление для защитной микропрограммы",
|
||||||
|
"Selenium": "Селениум",
|
||||||
|
"Shield Emitters": "Щитоизлучатели",
|
||||||
|
"Shielding Sensors": "Сенсоры системы экранирования",
|
||||||
|
"Specialised Legacy Firmware": "Специальные микропрограммы предыдущего поколения",
|
||||||
|
"Strange Wake Solutions": "Странные расчеты следа",
|
||||||
|
"Sulphur": "Сера",
|
||||||
|
"Tagged Encryption Codes": "Меченные шифровальные коды",
|
||||||
|
"Technetium": "Технеций",
|
||||||
|
"Tellurium": "Теллурий",
|
||||||
|
"Thermic Alloys": "Термические сплавы",
|
||||||
|
"Tin": "Олово",
|
||||||
|
"Tungsten": "Вольфрам",
|
||||||
|
"Unexpected Emission Data": "Неожиданные данные об излучении",
|
||||||
|
"Unidentified Scan Archives": "Неопознанные архивы сканирования",
|
||||||
|
"Untypical Shield Scans": "Нетипичные данные сканирования щитов",
|
||||||
|
"Unusual Encrypted Files": "Особые зашифрованные файлы",
|
||||||
|
"Vanadium": "Ванадий",
|
||||||
|
"Worn Shield Emitters": "Изношенные щитоизлучатели",
|
||||||
|
"Yttrium": "Иттрий",
|
||||||
|
"Zinc": "Цинк",
|
||||||
|
"Zirconium": "Цирконий"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,39 +94,6 @@ export default class AboutPage extends Page {
|
|||||||
</a>
|
</a>
|
||||||
.
|
.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h3>Supporting Coriolis</h3>
|
|
||||||
<p>
|
|
||||||
Coriolis is an open source project, and I work on it in my free time.
|
|
||||||
I have set up a patreon at{' '}
|
|
||||||
<a href="https://www.patreon.com/coriolis_elite">
|
|
||||||
patreon.com/coriolis_elite
|
|
||||||
</a>
|
|
||||||
, which will be used to keep Coriolis up to date and the servers
|
|
||||||
running.
|
|
||||||
</p>
|
|
||||||
<form
|
|
||||||
action="https://www.paypal.com/cgi-bin/webscr"
|
|
||||||
method="post"
|
|
||||||
target="_top"
|
|
||||||
>
|
|
||||||
<input type="hidden" name="cmd" value="_s-xclick" />
|
|
||||||
<input type="hidden" name="hosted_button_id" value="SJBKT2SWEEU68" />
|
|
||||||
<input
|
|
||||||
type="image"
|
|
||||||
src="https://www.paypalobjects.com/en_AU/i/btn/btn_donate_SM.gif"
|
|
||||||
border="0"
|
|
||||||
name="submit"
|
|
||||||
alt="PayPal – The safer, easier way to pay online!"
|
|
||||||
/>
|
|
||||||
<img
|
|
||||||
alt=""
|
|
||||||
border="0"
|
|
||||||
src="https://www.paypalobjects.com/en_AU/i/scr/pixel.gif"
|
|
||||||
width="1"
|
|
||||||
height="1"
|
|
||||||
/>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,627 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import Page from './Page';
|
|
||||||
import Router from '../Router';
|
|
||||||
import cn from 'classnames';
|
|
||||||
import { Ships } from 'coriolis-data/dist';
|
|
||||||
import Ship from '../shipyard/Ship';
|
|
||||||
import { fromComparison, toComparison } from '../shipyard/Serializer';
|
|
||||||
import Persist from '../stores/Persist';
|
|
||||||
import { SizeMap, ShipFacets } from '../shipyard/Constants';
|
|
||||||
import ComparisonTable from '../components/ComparisonTable';
|
|
||||||
import BarChart from '../components/BarChart';
|
|
||||||
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 ShortenUrl from '../utils/ShortenUrl';
|
|
||||||
import { comparisonBBCode } from '../utils/BBCode';
|
|
||||||
const browser = require('detect-browser');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a comparator based on the specified predicate
|
|
||||||
* @param {string} predicate Predicate / propterty name
|
|
||||||
* @return {Function} Comparator
|
|
||||||
*/
|
|
||||||
function sortBy(predicate) {
|
|
||||||
return (a, b) => {
|
|
||||||
if (a[predicate] === b[predicate]) {
|
|
||||||
if (a.name == b.name) {
|
|
||||||
a.buildName.toLowerCase() > b.buildName.toLowerCase() ? 1 : -1;
|
|
||||||
}
|
|
||||||
return a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1;
|
|
||||||
}
|
|
||||||
if (typeof a[predicate] == 'string') {
|
|
||||||
return a[predicate].toLowerCase() > b[predicate].toLowerCase() ? 1 : -1;
|
|
||||||
}
|
|
||||||
return a[predicate] > b[predicate] ? 1 : -1;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Comparison Page
|
|
||||||
*/
|
|
||||||
export default class ComparisonPage extends Page {
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
* @param {Object} props React Component properties
|
|
||||||
* @param {Object} context React Component context
|
|
||||||
*/
|
|
||||||
constructor(props, context) {
|
|
||||||
super(props, context);
|
|
||||||
this._sortShips = this._sortShips.bind(this);
|
|
||||||
this._buildsSelected = this._buildsSelected.bind(this);
|
|
||||||
this._updateDiscounts = this._updateDiscounts.bind(this);
|
|
||||||
this.state = this._initState(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* [Re]Create initial state from context
|
|
||||||
* @param {context} context React component context
|
|
||||||
* @return {Object} New state object
|
|
||||||
*/
|
|
||||||
_initState(context) {
|
|
||||||
let defaultFacets = [13, 12, 11, 9, 6, 4, 1, 3, 2]; // Reverse order of Armour, Shields, Speed, Jump Range, Cargo Capacity, Cost, DPS, EPS, HPS
|
|
||||||
let params = context.route.params;
|
|
||||||
let code = params.code;
|
|
||||||
let name = params.name ? decodeURIComponent(params.name) : null;
|
|
||||||
let newName = '';
|
|
||||||
let compareMode = !code;
|
|
||||||
let builds = [];
|
|
||||||
let saved = false;
|
|
||||||
let predicate = 'name';
|
|
||||||
let desc = false;
|
|
||||||
let importObj = {};
|
|
||||||
|
|
||||||
if (compareMode) {
|
|
||||||
if (name == 'all') {
|
|
||||||
let allBuilds = Persist.getBuilds();
|
|
||||||
newName = name;
|
|
||||||
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]
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let comparisonData = Persist.getComparison(name);
|
|
||||||
if (comparisonData) {
|
|
||||||
defaultFacets = comparisonData.facets;
|
|
||||||
comparisonData.builds.forEach(b =>
|
|
||||||
builds.push(this._createBuild(b.shipId, b.buildName))
|
|
||||||
);
|
|
||||||
saved = true;
|
|
||||||
newName = name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
let comparisonData = toComparison(code);
|
|
||||||
defaultFacets = comparisonData.f;
|
|
||||||
newName = name = comparisonData.n;
|
|
||||||
predicate = comparisonData.p;
|
|
||||||
desc = comparisonData.d;
|
|
||||||
comparisonData.b.forEach(build => {
|
|
||||||
builds.push(this._createBuild(build.s, build.n, build.c));
|
|
||||||
if (!importObj[build.s]) {
|
|
||||||
importObj[build.s] = {};
|
|
||||||
}
|
|
||||||
importObj[build.s][build.n] = build.c;
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
throw { type: 'bad-comparison', message: e.message, details: e };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let facets = [];
|
|
||||||
let selectedLength = defaultFacets.length;
|
|
||||||
let selectedFacets = new Array(selectedLength);
|
|
||||||
|
|
||||||
for (let i = 0; i < ShipFacets.length; i++) {
|
|
||||||
let facet = Object.assign({}, ShipFacets[i]);
|
|
||||||
let defaultIndex = defaultFacets.indexOf(facet.i);
|
|
||||||
if (defaultIndex == -1) {
|
|
||||||
facets.push(facet);
|
|
||||||
} else {
|
|
||||||
facet.active = true;
|
|
||||||
selectedFacets[selectedLength - defaultIndex - 1] = facet;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
facets = selectedFacets.concat(facets);
|
|
||||||
builds.sort(sortBy(predicate));
|
|
||||||
|
|
||||||
return {
|
|
||||||
title: 'Coriolis EDCD Edition - Compare',
|
|
||||||
predicate,
|
|
||||||
desc,
|
|
||||||
facets,
|
|
||||||
builds,
|
|
||||||
compareMode,
|
|
||||||
code,
|
|
||||||
name,
|
|
||||||
newName,
|
|
||||||
saved,
|
|
||||||
importObj
|
|
||||||
};
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Create a Ship instance / build
|
|
||||||
* @param {string} id Ship Id
|
|
||||||
* @param {name} name Build name
|
|
||||||
* @param {string} code Optional - Serialized ship code
|
|
||||||
* @return {Object} Ship instance with build name
|
|
||||||
*/
|
|
||||||
_createBuild(id, name, code) {
|
|
||||||
code = code ? code : Persist.getBuild(id, name); // Retrieve build code if not passed
|
|
||||||
|
|
||||||
if (!code) {
|
|
||||||
// No build found
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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.buildName = name;
|
|
||||||
b.applyDiscounts(Persist.getShipDiscount(), Persist.getModuleDiscount());
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update state with the specified sort predicates
|
|
||||||
* @param {String} predicate Sort predicate - property name
|
|
||||||
*/
|
|
||||||
_sortShips(predicate) {
|
|
||||||
let { builds, desc } = this.state;
|
|
||||||
if (this.state.predicate == predicate) {
|
|
||||||
desc = !desc;
|
|
||||||
}
|
|
||||||
|
|
||||||
builds.sort(sortBy(predicate));
|
|
||||||
|
|
||||||
if (desc) {
|
|
||||||
builds.reverse();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({ predicate, desc });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show selected builds modal
|
|
||||||
*/
|
|
||||||
_selectBuilds() {
|
|
||||||
this.context.showModal(
|
|
||||||
<ModalCompare
|
|
||||||
onSelect={this._buildsSelected}
|
|
||||||
builds={this.state.builds}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update selected builds with new list
|
|
||||||
* @param {Array} newBuilds List of new builds
|
|
||||||
*/
|
|
||||||
_buildsSelected(newBuilds) {
|
|
||||||
this.context.hideModal();
|
|
||||||
let builds = [];
|
|
||||||
|
|
||||||
for (let b of newBuilds) {
|
|
||||||
builds.push(this._createBuild(b.id, b.buildName));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({ builds, saved: false });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Toggle facet display
|
|
||||||
* @param {string} facet Facet / Ship Property
|
|
||||||
*/
|
|
||||||
_toggleFacet(facet) {
|
|
||||||
facet.active = !facet.active;
|
|
||||||
this.setState({ facets: [].concat(this.state.facets), saved: false });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle facet drag
|
|
||||||
* @param {Event} e Drag Event
|
|
||||||
*/
|
|
||||||
_facetDrag(e) {
|
|
||||||
this.nodeAfter = false;
|
|
||||||
this.dragged = e.currentTarget;
|
|
||||||
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')) {
|
|
||||||
e.dataTransfer.effectAllowed = 'move';
|
|
||||||
e.dataTransfer.setData('text/html', e.currentTarget);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle facet drop
|
|
||||||
* @param {Event} e Drop Event
|
|
||||||
*/
|
|
||||||
_facetDrop(e) {
|
|
||||||
this.dragged.parentNode.removeChild(this.placeholder);
|
|
||||||
let facets = this.state.facets;
|
|
||||||
let frm = Number(this.dragged.dataset.i);
|
|
||||||
let to = Number(this.over.dataset.i);
|
|
||||||
|
|
||||||
if (frm < to) {
|
|
||||||
to--;
|
|
||||||
}
|
|
||||||
if (this.nodeAfter) {
|
|
||||||
to++;
|
|
||||||
}
|
|
||||||
|
|
||||||
facets.splice(to, 0, facets.splice(frm, 1)[0]);
|
|
||||||
this.dragged.style.display = null;
|
|
||||||
this.setState({ facets: [].concat(facets), saved: false });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle facet drag over
|
|
||||||
* @param {Event} e Drag over Event
|
|
||||||
*/
|
|
||||||
_facetDragOver(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
if (e.target.className == 'facet-placeholder') {
|
|
||||||
return;
|
|
||||||
} else if (e.target != e.currentTarget) {
|
|
||||||
this.over = e.target;
|
|
||||||
this.dragged.style.display = 'none';
|
|
||||||
let relX = e.clientX - this.over.getBoundingClientRect().left;
|
|
||||||
let width = this.over.offsetWidth / 2;
|
|
||||||
let parent = e.target.parentNode;
|
|
||||||
|
|
||||||
if (parent == e.currentTarget) {
|
|
||||||
if (relX > width && this.dragged != e.target) {
|
|
||||||
this.nodeAfter = true;
|
|
||||||
parent.insertBefore(this.placeholder, e.target.nextElementSibling);
|
|
||||||
} else {
|
|
||||||
this.nodeAfter = false;
|
|
||||||
parent.insertBefore(this.placeholder, e.target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Handle name change and update state
|
|
||||||
* @param {SyntheticEvent} e Event
|
|
||||||
*/
|
|
||||||
_onNameChange(e) {
|
|
||||||
this.setState({ newName: e.target.value, saved: false });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete the current comparison
|
|
||||||
*/
|
|
||||||
_delete() {
|
|
||||||
Persist.deleteComparison(this.state.name);
|
|
||||||
Router.go('/compare');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Import the comparison builds
|
|
||||||
*/
|
|
||||||
_import() {
|
|
||||||
let builds = {};
|
|
||||||
|
|
||||||
for (let ship of this.state.builds) {
|
|
||||||
if (!builds[ship.id]) {
|
|
||||||
builds[ship.id] = {};
|
|
||||||
}
|
|
||||||
builds[ship.id][ship.buildName] = ship.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.context.showModal(<ModalImport builds={builds} />);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Save the current comparison
|
|
||||||
*/
|
|
||||||
_save() {
|
|
||||||
let { newName, builds, facets } = this.state;
|
|
||||||
let selectedFacets = [];
|
|
||||||
|
|
||||||
facets.forEach(f => {
|
|
||||||
if (f.active) {
|
|
||||||
selectedFacets.unshift(f.i);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Persist.saveComparison(newName, builds, selectedFacets);
|
|
||||||
Router.replace(`/compare/${encodeURIComponent(this.state.newName)}`);
|
|
||||||
this.setState({ name: newName, saved: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Serialize and generate a long URL for the current comparison
|
|
||||||
* @return {string} URL for serialized comparison
|
|
||||||
*/
|
|
||||||
_buildUrl() {
|
|
||||||
let { facets, builds, name, predicate, desc } = this.state;
|
|
||||||
let selectedFacets = [];
|
|
||||||
|
|
||||||
for (let f of facets) {
|
|
||||||
if (f.active) {
|
|
||||||
selectedFacets.unshift(f.i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let code = fromComparison(name, builds, selectedFacets, predicate, desc);
|
|
||||||
let loc = window.location;
|
|
||||||
return (
|
|
||||||
loc.protocol +
|
|
||||||
'//' +
|
|
||||||
loc.host +
|
|
||||||
'/comparison?code=' +
|
|
||||||
encodeURIComponent(code)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates the long permalink URL
|
|
||||||
*/
|
|
||||||
_genPermalink() {
|
|
||||||
this.context.showModal(<ModalPermalink url={this._buildUrl()} />);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate E:D Forum BBCode and show in the export modal
|
|
||||||
*/
|
|
||||||
_genBBcode() {
|
|
||||||
let { translate, formats } = this.context.language;
|
|
||||||
let { facets, builds } = this.state;
|
|
||||||
|
|
||||||
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))
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.context.showModal(
|
|
||||||
<ModalExport
|
|
||||||
title={translate('forum') + ' BBCode'}
|
|
||||||
generator={generator}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update dimenions from rendered DOM
|
|
||||||
*/
|
|
||||||
_updateDimensions() {
|
|
||||||
this.setState({
|
|
||||||
chartWidth: this.chartRef.offsetWidth
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update all ship costs on disount change
|
|
||||||
*/
|
|
||||||
_updateDiscounts() {
|
|
||||||
let shipDiscount = Persist.getShipDiscount();
|
|
||||||
let moduleDiscount = Persist.getModuleDiscount();
|
|
||||||
let builds = [];
|
|
||||||
|
|
||||||
for (let b of this.state.builds) {
|
|
||||||
builds.push(b.applyDiscounts(shipDiscount, moduleDiscount));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({ builds });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update state based on context changes
|
|
||||||
* @param {Object} nextProps Incoming/Next properties
|
|
||||||
* @param {Object} nextContext Incoming/Next conext
|
|
||||||
*/
|
|
||||||
componentWillReceiveProps(nextProps, nextContext) {
|
|
||||||
if (this.context.route !== nextContext.route) {
|
|
||||||
// Only reinit state if the route has changed
|
|
||||||
this.setState(this._initState(nextContext));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add listeners when about to mount
|
|
||||||
*/
|
|
||||||
componentWillMount() {
|
|
||||||
this.resizeListener = this.context.onWindowResize(this._updateDimensions);
|
|
||||||
this.persistListener = Persist.addListener(
|
|
||||||
'discounts',
|
|
||||||
this._updateDiscounts
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Trigger DOM updates on mount
|
|
||||||
*/
|
|
||||||
componentDidMount() {
|
|
||||||
this._updateDimensions();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove listeners on unmount
|
|
||||||
*/
|
|
||||||
componentWillUnmount() {
|
|
||||||
this.resizeListener.remove();
|
|
||||||
this.persistListener.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Render the Page
|
|
||||||
* @return {React.Component} The page contents
|
|
||||||
*/
|
|
||||||
renderPage() {
|
|
||||||
let translate = this.context.language.translate;
|
|
||||||
let compareHeader;
|
|
||||||
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>
|
|
||||||
);
|
|
||||||
} 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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={'page'}
|
|
||||||
style={{ fontSize: this.context.sizeRatio + 'em' }}
|
|
||||||
>
|
|
||||||
<table id="comparison">
|
|
||||||
<tbody>
|
|
||||||
{compareHeader}
|
|
||||||
<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)}
|
|
||||||
>
|
|
||||||
{'↔ ' + translate(f.title)}
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<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>
|
|
||||||
<BarChart
|
|
||||||
width={chartWidth}
|
|
||||||
data={builds}
|
|
||||||
properties={f.props}
|
|
||||||
labels={f.lbls}
|
|
||||||
unit={translate(f.unit)}
|
|
||||||
format={f.fmt}
|
|
||||||
title={translate(f.title)}
|
|
||||||
predicate={predicate}
|
|
||||||
desc={desc}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +1,11 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
// import Perf from 'react-addons-perf';
|
// import Perf from 'react-addons-perf';
|
||||||
import { Ships } from 'coriolis-data/dist';
|
|
||||||
import cn from 'classnames';
|
import cn from 'classnames';
|
||||||
import Page from './Page';
|
import Page from './Page';
|
||||||
import Router from '../Router';
|
import Router from '../Router';
|
||||||
import Persist from '../stores/Persist';
|
import Persist from '../stores/Persist';
|
||||||
import * as Utils from '../utils/UtilityFunctions';
|
|
||||||
import { Factory, Ship } from 'ed-forge';
|
import { Factory, Ship } from 'ed-forge';
|
||||||
import { STATE_EVENT, OBJECT_EVENT } from 'ed-forge/lib/Ship';
|
import { OBJECT_EVENT } from 'ed-forge/lib/src/Ship';
|
||||||
import * as _ from 'lodash';
|
|
||||||
import { toDetailedBuild } from '../shipyard/Serializer';
|
|
||||||
import { outfitURL } from '../utils/UrlGenerators';
|
import { outfitURL } from '../utils/UrlGenerators';
|
||||||
import {
|
import {
|
||||||
FloppyDisk,
|
FloppyDisk,
|
||||||
@@ -20,27 +16,18 @@ import {
|
|||||||
LinkIcon,
|
LinkIcon,
|
||||||
ShoppingIcon,
|
ShoppingIcon,
|
||||||
MatIcon,
|
MatIcon,
|
||||||
OrbisIcon
|
|
||||||
} from '../components/SvgIcons';
|
} from '../components/SvgIcons';
|
||||||
import LZString from 'lz-string';
|
|
||||||
import ShipSummaryTable from '../components/ShipSummaryTable';
|
import ShipSummaryTable from '../components/ShipSummaryTable';
|
||||||
import StandardSlotSection from '../components/StandardSlotSection';
|
import StandardSlotSection from '../components/StandardSlotSection';
|
||||||
import HardpointSlotSection from '../components/HardpointSlotSection';
|
import HardpointSlotSection from '../components/HardpointSlotSection';
|
||||||
import InternalSlotSection from '../components/InternalSlotSection';
|
import InternalSlotSection from '../components/InternalSlotSection';
|
||||||
import UtilitySlotSection from '../components/UtilitySlotSection';
|
import UtilitySlotSection from '../components/UtilitySlotSection';
|
||||||
import Pips from '../components/Pips';
|
|
||||||
import Boost from '../components/Boost';
|
|
||||||
import Fuel from '../components/Fuel';
|
|
||||||
import Cargo from '../components/Cargo';
|
|
||||||
import ShipPicker from '../components/ShipPicker';
|
|
||||||
import EngagementRange from '../components/EngagementRange';
|
|
||||||
import OutfittingSubpages from '../components/OutfittingSubpages';
|
import OutfittingSubpages from '../components/OutfittingSubpages';
|
||||||
import ModalExport from '../components/ModalExport';
|
import ModalExport from '../components/ModalExport';
|
||||||
import ModalPermalink from '../components/ModalPermalink';
|
import ModalPermalink from '../components/ModalPermalink';
|
||||||
import ModalShoppingList from '../components/ModalShoppingList';
|
|
||||||
import ModalOrbis from '../components/ModalOrbis';
|
|
||||||
import autoBind from 'auto-bind';
|
import autoBind from 'auto-bind';
|
||||||
import { assign } from 'lodash';
|
import { assign } from 'lodash';
|
||||||
|
import EDEngineerButton from '../components/EDEngineerButton';
|
||||||
|
|
||||||
const SHOW_BY_DEFAULT = {
|
const SHOW_BY_DEFAULT = {
|
||||||
'cabincapacity': true,
|
'cabincapacity': true,
|
||||||
@@ -140,7 +127,6 @@ export default class OutfittingPage extends Page {
|
|||||||
let code = params.code || savedCode;
|
let code = params.code || savedCode;
|
||||||
// Create a new Ship instance
|
// Create a new Ship instance
|
||||||
const ship = code ? new Ship(code) : Factory.newShip(shipId);
|
const ship = code ? new Ship(code) : Factory.newShip(shipId);
|
||||||
ship.on(STATE_EVENT, this._shipUpdated);
|
|
||||||
ship.on(OBJECT_EVENT, this._shipUpdated);
|
ship.on(OBJECT_EVENT, this._shipUpdated);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -208,60 +194,12 @@ export default class OutfittingPage extends Page {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Triggered when target ship has been updated
|
|
||||||
* @param {string} opponent the opponent's ship model
|
|
||||||
* @param {string} opponentBuild the name of the opponent's build
|
|
||||||
*/
|
|
||||||
_opponentUpdated(opponent, opponentBuild) {
|
|
||||||
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;
|
|
||||||
if (opponentBuild && Persist.getBuild(opponent, opponentBuild)) {
|
|
||||||
// Ship is a particular build
|
|
||||||
opponentShip.buildFrom(Persist.getBuild(opponent, opponentBuild));
|
|
||||||
// Set pips for opponent
|
|
||||||
const opponentParts = Persist.getBuild(opponent, opponentBuild).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]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Ship is a stock build
|
|
||||||
opponentShip.buildWith(Ships[opponent].defaults);
|
|
||||||
opponentSys = 2;
|
|
||||||
opponentEng = 2;
|
|
||||||
opponentWep = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState(
|
|
||||||
{
|
|
||||||
opponent: opponentShip,
|
|
||||||
opponentBuild,
|
|
||||||
opponentSys,
|
|
||||||
opponentEng,
|
|
||||||
opponentWep
|
|
||||||
},
|
|
||||||
() => this._updateRoute()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
_propToShowToggled(propertyName, newStatus) {
|
_propToShowToggled(propertyName, newStatus) {
|
||||||
const { propsToShow } = this.state;
|
const { propsToShow } = this.state;
|
||||||
if (newStatus === propsToShow[propertyName]) {
|
if (newStatus === propsToShow[propertyName]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newStatus) {
|
if (newStatus) {
|
||||||
propsToShow[propertyName] = true;
|
propsToShow[propertyName] = true;
|
||||||
} else {
|
} else {
|
||||||
@@ -277,43 +215,16 @@ export default class OutfittingPage extends Page {
|
|||||||
const { ship, buildName, newBuildName } = this.state;
|
const { ship, buildName, newBuildName } = this.state;
|
||||||
const shipId = ship.getShipType();
|
const shipId = ship.getShipType();
|
||||||
|
|
||||||
// If this is a stock ship the code won't be set, so ensure that we have it
|
ship.write('CoriolisBuildName', buildName);
|
||||||
const code = this.state.code || ship.compress();
|
const code = ship.compress();
|
||||||
|
|
||||||
Persist.saveBuild(shipId, newBuildName, code);
|
Persist.saveBuild(shipId, newBuildName, code);
|
||||||
this._setRoute();
|
this._setRoute();
|
||||||
|
|
||||||
let opponent, opponentBuild, opponentSys, opponentEng, opponentWep;
|
|
||||||
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);
|
|
||||||
opponentSys = this.state.sys;
|
|
||||||
opponentEng = this.state.eng;
|
|
||||||
opponentWep = this.state.wep;
|
|
||||||
} else {
|
|
||||||
opponentBuild = this.state.opponentBuild;
|
|
||||||
opponent = this.state.opponent;
|
|
||||||
opponentSys = this.state.opponentSys;
|
|
||||||
opponentEng = this.state.opponentEng;
|
|
||||||
opponentWep = this.state.opponentWep;
|
|
||||||
}
|
|
||||||
this.setState({
|
this.setState({
|
||||||
buildName: newBuildName,
|
buildName: newBuildName,
|
||||||
code,
|
code,
|
||||||
savedCode: code,
|
savedCode: code,
|
||||||
opponent,
|
|
||||||
opponentBuild,
|
|
||||||
opponentSys,
|
|
||||||
opponentEng,
|
|
||||||
opponentWep,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -331,7 +242,6 @@ export default class OutfittingPage extends Page {
|
|||||||
buildName: newBuildName,
|
buildName: newBuildName,
|
||||||
code,
|
code,
|
||||||
savedCode: code,
|
savedCode: code,
|
||||||
opponentBuild: newBuildName
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -362,19 +272,7 @@ export default class OutfittingPage extends Page {
|
|||||||
const shipId = ship.getShipType();
|
const shipId = ship.getShipType();
|
||||||
Persist.deleteBuild(shipId, buildName);
|
Persist.deleteBuild(shipId, buildName);
|
||||||
|
|
||||||
let opponentBuild;
|
|
||||||
if (
|
|
||||||
shipId === this.state.opponent.id &&
|
|
||||||
buildName === this.state.opponentBuild
|
|
||||||
) {
|
|
||||||
// Our current opponent has been deleted; revert to stock
|
|
||||||
opponentBuild = null;
|
|
||||||
} else {
|
|
||||||
opponentBuild = this.state.opponentBuild;
|
|
||||||
}
|
|
||||||
Router.go(outfitURL(shipId));
|
Router.go(outfitURL(shipId));
|
||||||
|
|
||||||
this.setState({ opponentBuild });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -387,20 +285,17 @@ export default class OutfittingPage extends Page {
|
|||||||
<ModalExport
|
<ModalExport
|
||||||
title={(buildName || ship.name) + ' ' + translate('export')}
|
title={(buildName || ship.name) + ' ' + translate('export')}
|
||||||
description={translate('PHRASE_EXPORT_DESC')}
|
description={translate('PHRASE_EXPORT_DESC')}
|
||||||
data={toDetailedBuild(buildName, ship, ship.toString())}
|
data={ship.toJSON()}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the code for the ship has been updated, to synchronise the rest of the data
|
* Called when the code for the ship has been updated, to synchronize the rest of the data
|
||||||
*/
|
*/
|
||||||
_codeUpdated() {
|
_codeUpdated() {
|
||||||
const { ship, code, buildName } = this.state;
|
|
||||||
const shipId = ship.getShipType();
|
|
||||||
|
|
||||||
this.setState(
|
this.setState(
|
||||||
{ ship: new Ship(code), },
|
{ ship: new Ship(this.state.code), },
|
||||||
() => this._setRoute(),
|
() => this._setRoute(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -431,11 +326,11 @@ export default class OutfittingPage extends Page {
|
|||||||
/**
|
/**
|
||||||
* Update state based on context changes
|
* Update state based on context changes
|
||||||
* @param {Object} nextProps Incoming/Next properties
|
* @param {Object} nextProps Incoming/Next properties
|
||||||
* @param {Object} nextContext Incoming/Next conext
|
* @param {Object} nextContext Incoming/Next context
|
||||||
*/
|
*/
|
||||||
componentWillReceiveProps(nextProps, nextContext) {
|
componentWillReceiveProps(nextProps, nextContext) {
|
||||||
if (this.context.route !== nextContext.route) {
|
if (this.context.route !== nextContext.route) {
|
||||||
// Only reinit state if the route has changed
|
// Only re-init state if the route has changed
|
||||||
this.setState(this._initState(nextProps, nextContext));
|
this.setState(this._initState(nextProps, nextContext));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -461,36 +356,17 @@ export default class OutfittingPage extends Page {
|
|||||||
this.context.showModal(<ModalPermalink url={window.location.href} />);
|
this.context.showModal(<ModalPermalink url={window.location.href} />);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate Orbis link
|
|
||||||
*/
|
|
||||||
_genOrbis() {
|
|
||||||
const data = {};
|
|
||||||
const ship = this.state.ship;
|
|
||||||
ship.coriolisId = ship.id;
|
|
||||||
data.coriolisShip = ship;
|
|
||||||
data.url = window.location.href;
|
|
||||||
data.title = this.state.buildName || ship.id;
|
|
||||||
data.description = this.state.buildName || ship.id;
|
|
||||||
data.ShipName = ship.id;
|
|
||||||
data.Ship = ship.id;
|
|
||||||
console.log(data);
|
|
||||||
this.context.showModal(<ModalOrbis ship={data} />);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open up a window for EDDB with a shopping list of our components
|
* Open up a window for EDDB with a shopping list of our components
|
||||||
*/
|
*/
|
||||||
_eddbShoppingList() {
|
_eddbShoppingList() {
|
||||||
const ship = this.state.ship;
|
const { ship } = this.state;
|
||||||
|
|
||||||
const shipId = Ships[ship.id].eddbID;
|
const shipId = ship.readMeta('eddbID');
|
||||||
// Provide unique list of non-PP module EDDB IDs
|
// Provide unique list of non-PP module EDDB IDs
|
||||||
const modIds = ship.internal
|
const modIds = ship.getModules()
|
||||||
.concat(ship.bulkheads, ship.standard, ship.hardpoints)
|
.map((m) => m.readMeta('eddbID'))
|
||||||
.filter(slot => slot !== null && slot.m !== null && !slot.m.pp)
|
.filter(Boolean);
|
||||||
.map(slot => slot.m.eddbID)
|
|
||||||
.filter((v, i, a) => a.indexOf(v) === i);
|
|
||||||
|
|
||||||
// Open up the relevant URL
|
// Open up the relevant URL
|
||||||
window.open(
|
window.open(
|
||||||
@@ -498,13 +374,6 @@ export default class OutfittingPage extends Page {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates the shopping list
|
|
||||||
*/
|
|
||||||
_genShoppingList() {
|
|
||||||
this.context.showModal(<ModalShoppingList ship={this.state.ship} />);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle Key Down
|
* Handle Key Down
|
||||||
* @param {Event} e Keyboard Event
|
* @param {Event} e Keyboard Event
|
||||||
@@ -527,8 +396,7 @@ export default class OutfittingPage extends Page {
|
|||||||
* @return {React.Component} The page contents
|
* @return {React.Component} The page contents
|
||||||
*/
|
*/
|
||||||
renderPage() {
|
renderPage() {
|
||||||
let state = this.state,
|
let { language, termtip, tooltip, sizeRatio } = this.context,
|
||||||
{ language, termtip, tooltip, sizeRatio } = this.context,
|
|
||||||
{ translate } = language,
|
{ translate } = language,
|
||||||
{
|
{
|
||||||
ship,
|
ship,
|
||||||
@@ -536,13 +404,8 @@ export default class OutfittingPage extends Page {
|
|||||||
savedCode,
|
savedCode,
|
||||||
buildName,
|
buildName,
|
||||||
newBuildName,
|
newBuildName,
|
||||||
// opponent,
|
propsToShow,
|
||||||
// opponentBuild,
|
} = this.state,
|
||||||
// opponentSys,
|
|
||||||
// opponentEng,
|
|
||||||
// opponentWep,
|
|
||||||
// engagementRange
|
|
||||||
} = state,
|
|
||||||
hide = tooltip.bind(null, null),
|
hide = tooltip.bind(null, null),
|
||||||
menu = this.props.currentMenu,
|
menu = this.props.currentMenu,
|
||||||
canSave = (newBuildName || buildName) && code !== savedCode,
|
canSave = (newBuildName || buildName) && code !== savedCode,
|
||||||
@@ -684,7 +547,7 @@ export default class OutfittingPage extends Page {
|
|||||||
<Download className="lg" />
|
<Download className="lg" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
// onClick={this._eddbShoppingList}
|
onClick={this._eddbShoppingList}
|
||||||
onMouseOver={termtip.bind(null, 'PHRASE_SHOPPING_LIST')}
|
onMouseOver={termtip.bind(null, 'PHRASE_SHOPPING_LIST')}
|
||||||
onMouseOut={hide}
|
onMouseOut={hide}
|
||||||
>
|
>
|
||||||
@@ -697,20 +560,7 @@ export default class OutfittingPage extends Page {
|
|||||||
>
|
>
|
||||||
<LinkIcon className="lg" />
|
<LinkIcon className="lg" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<EDEngineerButton ship={ship} />
|
||||||
onClick={this._genOrbis}
|
|
||||||
onMouseOver={termtip.bind(null, 'PHASE_UPLOAD_ORBIS')}
|
|
||||||
onMouseOut={hide}
|
|
||||||
>
|
|
||||||
<OrbisIcon className="lg" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
// onClick={this._genShoppingList}
|
|
||||||
onMouseOver={termtip.bind(null, 'PHRASE_SHOPPING_MATS')}
|
|
||||||
onMouseOut={hide}
|
|
||||||
>
|
|
||||||
<MatIcon className="lg" />
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -725,88 +575,8 @@ export default class OutfittingPage extends Page {
|
|||||||
<UtilitySlotSection ship={ship} code={code} currentMenu={menu}
|
<UtilitySlotSection ship={ship} code={code} currentMenu={menu}
|
||||||
propsToShow={propsToShow} onPropToggle={this._propToShowToggled} />
|
propsToShow={propsToShow} onPropToggle={this._propToShowToggled} />
|
||||||
|
|
||||||
{/* Control of ship and opponent */}
|
|
||||||
{/* <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>
|
|
||||||
</div>
|
|
||||||
<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}
|
|
||||||
onChange={this._fuelUpdated}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="group quarter">
|
|
||||||
{ship.cargoCapacity > 0 ? (
|
|
||||||
<Cargo
|
|
||||||
cargoCapacity={ship.cargoCapacity}
|
|
||||||
onChange={this._cargoUpdated}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
<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>
|
|
||||||
</div>
|
|
||||||
<div className="group half">
|
|
||||||
<EngagementRange
|
|
||||||
ship={ship}
|
|
||||||
engagementRange={engagementRange}
|
|
||||||
onChange={this._engagementRangeUpdated}
|
|
||||||
/>
|
|
||||||
</div> */}
|
|
||||||
|
|
||||||
{/* Tabbed subpages */}
|
{/* Tabbed subpages */}
|
||||||
{/* <OutfittingSubpages
|
<OutfittingSubpages ship={ship} code={code} buildName={buildName} />
|
||||||
ship={ship}
|
|
||||||
code={code}
|
|
||||||
buildName={buildName}
|
|
||||||
onChange={shipUpdated}
|
|
||||||
sys={sys}
|
|
||||||
eng={eng}
|
|
||||||
wep={wep}
|
|
||||||
boost={boost}
|
|
||||||
cargo={cargo}
|
|
||||||
fuel={fuel}
|
|
||||||
engagementRange={engagementRange}
|
|
||||||
opponent={opponent}
|
|
||||||
opponentBuild={opponentBuild}
|
|
||||||
opponentSys={opponentSys}
|
|
||||||
opponentEng={opponentEng}
|
|
||||||
opponentWep={opponentWep}
|
|
||||||
/> */}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,6 +61,14 @@ export default class Page extends React.Component {
|
|||||||
*/
|
*/
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
document.title = this.state.title || 'Coriolis';
|
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) {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React from 'react';
|
|||||||
import Page from './Page';
|
import Page from './Page';
|
||||||
import cn from 'classnames';
|
import cn from 'classnames';
|
||||||
import { Factory } from 'ed-forge';
|
import { Factory } from 'ed-forge';
|
||||||
import { JUMP_METRICS } from 'ed-forge/lib/ship-stats';
|
import { JUMP_METRICS } from 'ed-forge/lib/src/ship-stats';
|
||||||
import { SizeMap } from '../shipyard/Constants';
|
import { SizeMap } from '../shipyard/Constants';
|
||||||
import Link from '../components/Link';
|
import Link from '../components/Link';
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ function shipSummary(shipId) {
|
|||||||
let ship = Factory.newShip(shipId);
|
let ship = Factory.newShip(shipId);
|
||||||
|
|
||||||
let coreSizes = ship.readMeta('coreSizes');
|
let coreSizes = ship.readMeta('coreSizes');
|
||||||
let { jumpRange, totalRange } = ship.getMetrics(JUMP_METRICS);
|
let { jumpRangeLaden, totalRange } = ship.getMetrics(JUMP_METRICS);
|
||||||
let summary = {
|
let summary = {
|
||||||
agility: ship.readProp('pitch') + ship.readProp('yaw') + ship.readProp('roll'),
|
agility: ship.readProp('pitch') + ship.readProp('yaw') + ship.readProp('roll'),
|
||||||
baseArmour: ship.readProp('basearmour'),
|
baseArmour: ship.readProp('basearmour'),
|
||||||
@@ -32,7 +32,7 @@ function shipSummary(shipId) {
|
|||||||
masslock: ship.readProp('masslock'),
|
masslock: ship.readProp('masslock'),
|
||||||
hp: [0, 0, 0, 0, 0], // Utility, Small, Medium, Large, Huge
|
hp: [0, 0, 0, 0, 0], // Utility, Small, Medium, Large, Huge
|
||||||
int: [0, 0, 0, 0, 0, 0, 0, 0], // Sizes 1 - 8
|
int: [0, 0, 0, 0, 0, 0, 0, 0], // Sizes 1 - 8
|
||||||
jumpRange,
|
jumpRangeLaden,
|
||||||
pitch: ship.readProp('pitch'),
|
pitch: ship.readProp('pitch'),
|
||||||
retailCost: ship.readMeta('retailCost'),
|
retailCost: ship.readMeta('retailCost'),
|
||||||
roll: ship.readProp('roll'),
|
roll: ship.readProp('roll'),
|
||||||
@@ -52,13 +52,13 @@ function shipSummary(shipId) {
|
|||||||
|
|
||||||
// Count Hardpoints by class
|
// Count Hardpoints by class
|
||||||
ship.getHardpoints(undefined, true).forEach(hardpoint => {
|
ship.getHardpoints(undefined, true).forEach(hardpoint => {
|
||||||
summary.hp[hardpoint.getSize()]++;
|
summary.hp[hardpoint.getSizeNum()]++;
|
||||||
summary.hpCount++;
|
summary.hpCount++;
|
||||||
});
|
});
|
||||||
// Count Internal Compartments by class
|
// Count Internal Compartments by class
|
||||||
let maxCargo = 0, maxPassengers = 0;
|
let maxCargo = 0, maxPassengers = 0;
|
||||||
ship.getInternals(undefined, true).forEach(internal => {
|
ship.getInternals(undefined, true).forEach(internal => {
|
||||||
const size = String(internal.getSize());
|
const size = String(internal.getSizeNum());
|
||||||
summary.int[size]++;
|
summary.int[size]++;
|
||||||
summary.intCount++;
|
summary.intCount++;
|
||||||
|
|
||||||
@@ -195,7 +195,7 @@ export default class ShipyardPage extends Page {
|
|||||||
<td className="ri">{fInt(s.pitch)}</td>
|
<td className="ri">{fInt(s.pitch)}</td>
|
||||||
<td className="ri">{fInt(s.yaw)}</td>
|
<td className="ri">{fInt(s.yaw)}</td>
|
||||||
<td className="ri">{fInt(s.roll)}</td>
|
<td className="ri">{fInt(s.roll)}</td>
|
||||||
<td className="ri">{fRound(s.jumpRange)}</td>
|
<td className="ri">{fRound(s.jumpRangeLaden)}</td>
|
||||||
<td className="ri">{fRound(s.totalRange)}</td>
|
<td className="ri">{fRound(s.totalRange)}</td>
|
||||||
<td className="ri">{fInt(s.hardness)}</td>
|
<td className="ri">{fInt(s.hardness)}</td>
|
||||||
<td className="ri">{fInt(s.baseArmour)}</td>
|
<td className="ri">{fInt(s.baseArmour)}</td>
|
||||||
@@ -400,7 +400,7 @@ export default class ShipyardPage extends Page {
|
|||||||
<th className="sortable" onClick={sortShips('roll')}>
|
<th className="sortable" onClick={sortShips('roll')}>
|
||||||
{translate('roll')}
|
{translate('roll')}
|
||||||
</th>
|
</th>
|
||||||
<th className="sortable lft" onClick={sortShips('jumpRange')}>
|
<th className="sortable lft" onClick={sortShips('jumpRangeLaden')}>
|
||||||
{translate('jump')}
|
{translate('jump')}
|
||||||
</th>
|
</th>
|
||||||
<th className="sortable" onClick={sortShips('totalRange')}>
|
<th className="sortable" onClick={sortShips('totalRange')}>
|
||||||
@@ -466,7 +466,7 @@ export default class ShipyardPage extends Page {
|
|||||||
<th className="sortable" onClick={sortShips('roll')}>
|
<th className="sortable" onClick={sortShips('roll')}>
|
||||||
{units['°/s']}
|
{units['°/s']}
|
||||||
</th>
|
</th>
|
||||||
<th className="sortable lft" onClick={sortShips('jumpRange')}>
|
<th className="sortable lft" onClick={sortShips('jumpRangeLaden')}>
|
||||||
{units.LY}
|
{units.LY}
|
||||||
</th>
|
</th>
|
||||||
<th className="sortable" onClick={sortShips('totalRange')}>
|
<th className="sortable" onClick={sortShips('totalRange')}>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,13 +0,0 @@
|
|||||||
/**
|
|
||||||
* Modification - a modification and its value
|
|
||||||
*/
|
|
||||||
export default class Modification {
|
|
||||||
/**
|
|
||||||
* @param {String} id Unique modification ID
|
|
||||||
* @param {Number} value Value of the modification
|
|
||||||
*/
|
|
||||||
constructor(id, value) {
|
|
||||||
this.id = id;
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,197 +0,0 @@
|
|||||||
import Module from './Module';
|
|
||||||
import { BulkheadNames } from './Constants';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filter eligble modules based on parameters
|
|
||||||
* @param {Array} arr Available modules array
|
|
||||||
* @param {number} maxClass Max class
|
|
||||||
* @param {number} minClass Minimum class
|
|
||||||
* @param {number} mass Mass
|
|
||||||
* @return {Array} Fitlered module subset
|
|
||||||
*/
|
|
||||||
function filter(arr, maxClass, minClass, mass) {
|
|
||||||
return arr.filter(m => m.class <= maxClass && m.class >= minClass && (m.maxmass === undefined || mass <= m.maxmass));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The available module set for a specific ship
|
|
||||||
*/
|
|
||||||
export default class ModuleSet {
|
|
||||||
/**
|
|
||||||
* Instantiate the module set
|
|
||||||
* @param {Object} modules All Modules
|
|
||||||
* @param {Object} shipData Ship Specifications Data (see coriolis-data/Ships)
|
|
||||||
*/
|
|
||||||
constructor(modules, shipData) {
|
|
||||||
let maxInternal = isNaN(shipData.slots.internal[0]) ? shipData.slots.internal[0].class : shipData.slots.internal[0];
|
|
||||||
let mass = shipData.properties.hullMass + 6.5;
|
|
||||||
let maxStandardArr = shipData.slots.standard;
|
|
||||||
let maxHardPoint = shipData.slots.hardpoints[0];
|
|
||||||
let stnd = modules.standard;
|
|
||||||
this.mass = mass;
|
|
||||||
this.standard = {};
|
|
||||||
this.internal = {};
|
|
||||||
this.hardpoints = {};
|
|
||||||
this.hpClass = {};
|
|
||||||
this.intClass = {};
|
|
||||||
|
|
||||||
this.bulkheads = shipData.bulkheads.map((b, i) => {
|
|
||||||
return Object.assign(new Module(), { grp: 'bh', id: i, name: BulkheadNames[i], index: i, class: '', rating: '' }, b);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.standard[0] = filter(stnd.pp, maxStandardArr[0], 0, mass); // Power Plant
|
|
||||||
this.standard[2] = filter(stnd.fsd, maxStandardArr[2], 0, mass); // FSD
|
|
||||||
this.standard[4] = filter(stnd.pd, maxStandardArr[4], 0, mass); // Power Distributor
|
|
||||||
this.standard[6] = filter(stnd.ft, maxStandardArr[6], 0, mass); // Fuel Tank
|
|
||||||
// Thrusters, filter modules by class only (to show full list of ratings for that class)
|
|
||||||
let minThrusterClass = stnd.t.reduce((clazz, th) => (th.maxmass >= mass && th.class < clazz) ? th.class : clazz, maxStandardArr[1]);
|
|
||||||
this.standard[1] = filter(stnd.t, maxStandardArr[1], minThrusterClass, 0); // Thrusters
|
|
||||||
// Slots where module class must be equal to slot class
|
|
||||||
this.standard[3] = filter(stnd.ls, maxStandardArr[3], maxStandardArr[3], 0); // Life Supprt
|
|
||||||
this.standard[5] = filter(stnd.s, maxStandardArr[5], maxStandardArr[5], mass); // Sensors
|
|
||||||
|
|
||||||
for (let h in modules.hardpoints) {
|
|
||||||
this.hardpoints[h] = filter(modules.hardpoints[h], maxHardPoint, 0, mass);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let g in modules.internal) {
|
|
||||||
this.internal[g] = filter(modules.internal[g], maxInternal, 0, mass);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the specified bulkhead
|
|
||||||
* @param {integer} index Bulkhead index
|
|
||||||
* @return {Object} Bulkhead module details
|
|
||||||
*/
|
|
||||||
getBulkhead(index) {
|
|
||||||
return this.bulkheads[index] ? new Module({ template: this.bulkheads[index] }) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine the modules that areeligible for an internal slot
|
|
||||||
* @param {Object} ship The ship
|
|
||||||
* @param {integer} c The max class module that can be mounted in the slot
|
|
||||||
* @param {Object} eligible) The map of eligible internal groups
|
|
||||||
* @return {object} A map of all eligible modules by group
|
|
||||||
*/
|
|
||||||
getInts(ship, c, eligible) {
|
|
||||||
let o = {};
|
|
||||||
for (let key in this.internal) {
|
|
||||||
if (eligible && !eligible[key]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (key == 'pcq' && !(ship.luxuryCabins && ship.luxuryCabins === true)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (key == 'fh' && !(ship.fighterHangars && ship.fighterHangars === true)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let data = filter(this.internal[key], c, 0, this.mass);
|
|
||||||
if (data.length) { // If group is not empty
|
|
||||||
o[key] = data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determining the modules that are eligible for an hardpoint slot
|
|
||||||
* @param {integer} c The max class module that can be mounted in the slot
|
|
||||||
* @param {Object} eligible) The map of eligible hardpoint groups
|
|
||||||
* @return {object} A map of all eligible modules by group
|
|
||||||
*/
|
|
||||||
getHps(c, eligible) {
|
|
||||||
let o = {};
|
|
||||||
for (let key in this.hardpoints) {
|
|
||||||
if (eligible && !eligible[key]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let data = filter(this.hardpoints[key], c, c ? 1 : 0, this.mass);
|
|
||||||
if (data.length) { // If group is not empty
|
|
||||||
o[key] = data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find the lightest Power Distributor that provides sufficient
|
|
||||||
* energy to boost.
|
|
||||||
* @param {number} boostEnergy The energy that is required to boost
|
|
||||||
* @return {Object} Power Distributor
|
|
||||||
*/
|
|
||||||
lightestPowerDist(boostEnergy) {
|
|
||||||
let pd = this.standard[4][0];
|
|
||||||
for (let p of this.standard[4]) {
|
|
||||||
if (p.mass < pd.mass && p.engcap > boostEnergy) {
|
|
||||||
pd = p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new Module({ template: pd });
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Find the power distributor that matches the requirements
|
|
||||||
* @param {Object} requirements The requirements to be met (currently only support 'weprate')
|
|
||||||
* @return {Object} Power distributor
|
|
||||||
*/
|
|
||||||
matchingPowerDist(requirements) {
|
|
||||||
let pd = this.standard[4][0];
|
|
||||||
for (let p of this.standard[4]) {
|
|
||||||
if (p.weprate >= requirements.weprate || p.weprate >= pd.weprate) {
|
|
||||||
pd = p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new Module({ template: pd });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds the lightest Thruster that can handle the specified tonnage
|
|
||||||
* @param {number} ladenMass Ship laden mass (mass + cargo + fuel)
|
|
||||||
* @return {Object} Thruster
|
|
||||||
*/
|
|
||||||
lightestThruster(ladenMass) {
|
|
||||||
let th = this.standard[1][0];
|
|
||||||
|
|
||||||
for (let t of this.standard[1]) {
|
|
||||||
if (t.mass < th.mass && t.maxmass >= ladenMass) {
|
|
||||||
th = t;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new Module({ template: th });
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds the lightest usable Shield Generator
|
|
||||||
* @param {number} hullMass Ship hull mass
|
|
||||||
* @return {Object} Thruster
|
|
||||||
*/
|
|
||||||
lightestShieldGenerator(hullMass) {
|
|
||||||
let sg = this.internal.sg[0];
|
|
||||||
|
|
||||||
for (let s of this.internal.sg) {
|
|
||||||
if (s.mass < sg.mass && s.maxmass > hullMass) {
|
|
||||||
sg = s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new Module({ template: sg });
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find the lightest Power Plant that provides sufficient power
|
|
||||||
* @param {number} powerNeeded Power requirements in MJ
|
|
||||||
* @param {string} rating The optional rating of the power plant
|
|
||||||
* @return {Object} Power Plant
|
|
||||||
*/
|
|
||||||
lightestPowerPlant(powerNeeded, rating) {
|
|
||||||
let pp = this.standard[0][0];
|
|
||||||
|
|
||||||
for (let p of this.standard[0]) {
|
|
||||||
// Provides enough power, is lighter or the same mass as current power plant but better output/efficiency
|
|
||||||
if (p.pgen >= powerNeeded && (p.mass < pp.mass || (p.mass == pp.mass && p.pgen > pp.pgen)) && (!rating || rating == p.rating)) {
|
|
||||||
pp = p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new Module({ template: pp });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,336 +0,0 @@
|
|||||||
import { ModuleNameToGroup, BulkheadNames, StandardArray } from './Constants';
|
|
||||||
import ModuleSet from './ModuleSet';
|
|
||||||
import Module from './Module';
|
|
||||||
import { Ships, Modules } from 'coriolis-data/dist';
|
|
||||||
|
|
||||||
/*
|
|
||||||
* All functions below must return a fresh Module rather than a definition or existing module, as
|
|
||||||
* the resultant object can be altered with modifications.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created a cargo hatch model
|
|
||||||
* @return {Object} Cargo hatch model
|
|
||||||
*/
|
|
||||||
export function cargoHatch() {
|
|
||||||
let hatch = new Module();
|
|
||||||
Object.assign(hatch, { name: 'Cargo Hatch', class: 1, rating: 'H', power: 0.6 });
|
|
||||||
return hatch;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds the module with the specific group and ID
|
|
||||||
* @param {String} grp Module group (pp - power plant, pl - pulse laser etc)
|
|
||||||
* @param {String} id The module ID
|
|
||||||
* @return {Object} The module or null
|
|
||||||
*/
|
|
||||||
export function findModule(grp, id) {
|
|
||||||
// See if it's a standard module
|
|
||||||
if (Modules.standard[grp]) {
|
|
||||||
let standardmod = Modules.standard[grp].find(e => e.id == id);
|
|
||||||
if (standardmod != null) {
|
|
||||||
return new Module({ template: standardmod });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// See if it's an internal module
|
|
||||||
if (Modules.internal[grp]) {
|
|
||||||
let internalmod = Modules.internal[grp].find(e => e.id == id);
|
|
||||||
if (internalmod != null) {
|
|
||||||
return new Module({ template: internalmod });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// See if it's a hardpoint module
|
|
||||||
if (Modules.hardpoints[grp]) {
|
|
||||||
let hardpointmod = Modules.hardpoints[grp].find(e => e.id == id);
|
|
||||||
if (hardpointmod != null) {
|
|
||||||
return new Module({ template: hardpointmod });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds the standard module type with the specified ID
|
|
||||||
* @param {String|Number} type Standard Module Type (0/pp - Power Plant, 1/t - Thrusters, etc)
|
|
||||||
* @param {String} id The module ID or '[Class][Rating]'
|
|
||||||
* @return {Object} The standard module or null
|
|
||||||
*/
|
|
||||||
export function standard(type, id) {
|
|
||||||
if (!isNaN(type)) {
|
|
||||||
type = StandardArray[type];
|
|
||||||
}
|
|
||||||
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 });
|
|
||||||
}
|
|
||||||
return s || null;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds the hardpoint with the specified ID
|
|
||||||
* @param {String} id Hardpoint ID
|
|
||||||
* @return {Object} Hardpoint module or null
|
|
||||||
*/
|
|
||||||
export function hardpoints(id) {
|
|
||||||
for (let n in Modules.hardpoints) {
|
|
||||||
let group = Modules.hardpoints[n];
|
|
||||||
for (let i = 0; i < group.length; i++) {
|
|
||||||
if (group[i].id == id) {
|
|
||||||
return new Module({ template: group[i] });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds the internal module with the specified ID
|
|
||||||
* @param {String} id Internal module ID
|
|
||||||
* @return {Object} Internal module or null
|
|
||||||
*/
|
|
||||||
export function internal(id) {
|
|
||||||
for (let n in Modules.internal) {
|
|
||||||
let group = Modules.internal[n];
|
|
||||||
for (let i = 0; i < group.length; i++) {
|
|
||||||
if (group[i].id == id) {
|
|
||||||
return new Module({ template: group[i] });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds a standard module based on Class, Rating, Group and/or name.
|
|
||||||
* At least one of Group name or unique module name must be provided
|
|
||||||
*
|
|
||||||
* @param {String} groupName [Optional] Full name or abbreviated name for module group
|
|
||||||
* @param {integer} clss module Class
|
|
||||||
* @param {String} rating module Rating
|
|
||||||
* @param {String} name [Optional] Long/unique name for module -e.g. 'Advanced Discover Scanner'
|
|
||||||
* @return {Object} The module if found, null if not found
|
|
||||||
*/
|
|
||||||
export function findStandard(groupName, clss, rating, name) {
|
|
||||||
let groups = {};
|
|
||||||
|
|
||||||
if (groupName) {
|
|
||||||
if (Modules.standard[groupName]) {
|
|
||||||
groups[groupName] = Modules.standard[groupName];
|
|
||||||
} else {
|
|
||||||
let grpCode = ModuleNameToGroup[groupName.toLowerCase()];
|
|
||||||
if (grpCode && Modules.standard[grpCode]) {
|
|
||||||
groups[grpCode] = Modules.standard[grpCode];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (name) {
|
|
||||||
groups = Modules.standard;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let g in groups) {
|
|
||||||
let group = groups[g];
|
|
||||||
for (let i = 0, l = group.length; i < l; i++) {
|
|
||||||
if (group[i].class == clss && group[i].rating == rating && ((!name && !group[i].name) || group[i].name == name)) {
|
|
||||||
return group[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds a standard Module ID based on Class, Rating, Group and/or name.
|
|
||||||
* At least one of Group name or unique module name must be provided
|
|
||||||
*
|
|
||||||
* @param {String} groupName [Optional] Full name or abbreviated name for module group
|
|
||||||
* @param {integer} clss module Class
|
|
||||||
* @param {String} rating Module Rating
|
|
||||||
* @param {String} name [Optional] Long/unique name for module -e.g. 'Advanced Discover Scanner'
|
|
||||||
* @return {String} The id of the module if found, null if not found
|
|
||||||
*/
|
|
||||||
export function findStandardId(groupName, clss, rating, name) {
|
|
||||||
let i = this.findStandard(groupName, clss, rating, name);
|
|
||||||
return i ? i.id : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds an internal module based on Class, Rating, Group and/or name.
|
|
||||||
* At least one ofGroup name or unique module name must be provided
|
|
||||||
*
|
|
||||||
* @param {String} groupName [Optional] Full name or abbreviated name for module group
|
|
||||||
* @param {integer} clss module Class
|
|
||||||
* @param {String} rating module Rating
|
|
||||||
* @param {String} name [Optional] Long/unique name for module -e.g. 'Advanced Discover Scanner'
|
|
||||||
* @return {Object} The module if found, null if not found
|
|
||||||
*/
|
|
||||||
export function findInternal(groupName, clss, rating, name) {
|
|
||||||
let groups = {};
|
|
||||||
|
|
||||||
if (groupName) {
|
|
||||||
if (Modules.internal[groupName]) {
|
|
||||||
groups[groupName] = Modules.internal[groupName];
|
|
||||||
} else {
|
|
||||||
let grpCode = ModuleNameToGroup[groupName.toLowerCase()];
|
|
||||||
if (grpCode && Modules.internal[grpCode]) {
|
|
||||||
groups[grpCode] = Modules.internal[grpCode];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (name) {
|
|
||||||
groups = Modules.internal;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let g in groups) {
|
|
||||||
let group = groups[g];
|
|
||||||
for (let i = 0, l = group.length; i < l; i++) {
|
|
||||||
if (group[i].class == clss && group[i].rating == rating && ((!name && !group[i].name) || group[i].name == name)) {
|
|
||||||
return group[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds an internal module based on Class, Rating, Group and/or name.
|
|
||||||
* At least one of Group name or unique module name must be provided.
|
|
||||||
* will start searching at specified class and proceed lower until a
|
|
||||||
* module is found or 0 is hit
|
|
||||||
* Uses findInternal internally
|
|
||||||
*
|
|
||||||
* @param {String} groupName [Optional] Full name or abbreviated name for module group
|
|
||||||
* @param {integer} clss module Class
|
|
||||||
* @param {String} rating module Rating
|
|
||||||
* @param {String} name [Optional] Long/unique name for module -e.g. 'Advanced Discover Scanner'
|
|
||||||
* @return {Object} The module if found, null if not found
|
|
||||||
*/
|
|
||||||
export function findMaxInternal(groupName, clss, rating, name) {
|
|
||||||
let foundModule = null;
|
|
||||||
let currentClss = clss;
|
|
||||||
while (currentClss > 0 && foundModule == null) {
|
|
||||||
foundModule = findInternal(groupName, currentClss, rating, name);
|
|
||||||
currentClss = currentClss - 1;
|
|
||||||
}
|
|
||||||
return foundModule;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds an internal Module ID based on Class, Rating, Group and/or name.
|
|
||||||
* At least one ofGroup name or unique module name must be provided
|
|
||||||
*
|
|
||||||
* @param {String} groupName [Optional] Full name or abbreviated name for module group
|
|
||||||
* @param {integer} clss module Class
|
|
||||||
* @param {String} rating Module Rating
|
|
||||||
* @param {String} name [Optional] Long/unique name for module -e.g. 'Advanced Discover Scanner'
|
|
||||||
* @return {String} The id of the module if found, null if not found
|
|
||||||
*/
|
|
||||||
export function findInternalId(groupName, clss, rating, name) {
|
|
||||||
let i = this.findInternal(groupName, clss, rating, name);
|
|
||||||
return i ? i.id : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds a hardpoint Module based on Class, Rating, Group and/or name.
|
|
||||||
* At least one ofGroup name or unique module name must be provided
|
|
||||||
*
|
|
||||||
* @param {String} groupName [Optional] Full name or abbreviated name for module group
|
|
||||||
* @param {integer} clss Module Class
|
|
||||||
* @param {String} rating [Optional] module Rating
|
|
||||||
* @param {String} name [Optional] Long/unique name for module -e.g. 'Heat Sink Launcher'
|
|
||||||
* @param {String} mount Mount type - [F]ixed, [G]imballed, [T]urret
|
|
||||||
* @param {String} missile [Optional] Missile type - [D]umbfire, [S]eeker
|
|
||||||
* @return {String} The id of the module if found, null if not found
|
|
||||||
*/
|
|
||||||
export function findHardpoint(groupName, clss, rating, name, mount, missile) {
|
|
||||||
let groups = {};
|
|
||||||
|
|
||||||
if (groupName) {
|
|
||||||
if (Modules.hardpoints[groupName]) {
|
|
||||||
groups[groupName] = Modules.hardpoints[groupName];
|
|
||||||
} else {
|
|
||||||
let grpCode = ModuleNameToGroup[groupName.toLowerCase()];
|
|
||||||
if (grpCode && Modules.hardpoints[grpCode]) {
|
|
||||||
groups[grpCode] = Modules.hardpoints[grpCode];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (name) {
|
|
||||||
groups = Modules.hardpoints;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let g in groups) {
|
|
||||||
let group = groups[g];
|
|
||||||
for (let h of group) {
|
|
||||||
if (h.class == clss && (!rating || h.rating == rating) && h.mount == mount && h.name == name && h.missile == missile) {
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds a hardpoint module ID based on Class, Rating, Group and/or name.
|
|
||||||
* At least one of Group name or unique module name must be provided
|
|
||||||
*
|
|
||||||
* @param {String} groupName [Optional] Full name or abbreviated name for module group
|
|
||||||
* @param {integer} clss module Class
|
|
||||||
* @param {String} rating module Rating
|
|
||||||
* @param {String} name [Optional] Long/unique name for module -e.g. 'Heat Sink Launcher'
|
|
||||||
* @param {String} mount Mount type - [F]ixed, [G]imballed, [T]urret
|
|
||||||
* @param {String} missile [Optional] Missile type - [D]umbfire, [S]eeker
|
|
||||||
* @return {String} The id of the module if found, null if not found
|
|
||||||
*/
|
|
||||||
export function findHardpointId(groupName, clss, rating, name, mount, missile) {
|
|
||||||
let h = this.findHardpoint(groupName, clss, rating, name, mount, missile);
|
|
||||||
if (h) {
|
|
||||||
return h.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Countermeasures used to be lumped in a single group but have been broken, out. If we have been given a groupName of 'Countermeasure' then
|
|
||||||
// rely on the unique name to find it
|
|
||||||
if (groupName === 'cm' || groupName === 'Countermeasure') {
|
|
||||||
h = this.findHardpoint(null, clss, rating, name, mount, missile);
|
|
||||||
}
|
|
||||||
|
|
||||||
return h ? h.id : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the bulkhead index for the given bulkhead name
|
|
||||||
* @param {String} bulkheadName Bulkhead name in english
|
|
||||||
* @return {number} Bulkhead index
|
|
||||||
*/
|
|
||||||
export function bulkheadIndex(bulkheadName) {
|
|
||||||
return BulkheadNames.indexOf(bulkheadName);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if a module group is a shield generator
|
|
||||||
* @param {String} g Module Group name
|
|
||||||
* @return {Boolean} True if the group is a shield generator
|
|
||||||
*/
|
|
||||||
export function isShieldGenerator(g) {
|
|
||||||
return g == 'sg' || g == 'psg' || g == 'bsg';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new ModuleSet that contains all available modules
|
|
||||||
* that the specified ship is eligible to use.
|
|
||||||
*
|
|
||||||
* 6.5 T is the lightest possible mass of standard components that any ship can use
|
|
||||||
*
|
|
||||||
* @param {String} shipId Unique ship Id/Key
|
|
||||||
* @return {ModuleSet} The set of modules the ship can install
|
|
||||||
*/
|
|
||||||
export function forShip(shipId) {
|
|
||||||
return new ModuleSet(Modules, Ships[shipId]);
|
|
||||||
}
|
|
||||||
@@ -1,195 +0,0 @@
|
|||||||
import { ModuleGroupToName, MountMap, BulkheadNames } from './Constants';
|
|
||||||
import { Ships } from 'coriolis-data/dist';
|
|
||||||
import Ship from './Ship';
|
|
||||||
import * as Utils from '../utils/UtilityFunctions';
|
|
||||||
import LZString from 'lz-string';
|
|
||||||
import { outfitURL } from '../utils/UrlGenerators';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates ship-loadout JSON Schema standard object
|
|
||||||
* @param {Object} standard model
|
|
||||||
* @return {Object} JSON Schema
|
|
||||||
*/
|
|
||||||
function standardToSchema(standard) {
|
|
||||||
if (standard.m) {
|
|
||||||
let o = {
|
|
||||||
class: standard.m.class,
|
|
||||||
rating: standard.m.rating,
|
|
||||||
enabled: Boolean(standard.enabled),
|
|
||||||
priority: standard.priority + 1
|
|
||||||
};
|
|
||||||
|
|
||||||
if (standard.m.name) {
|
|
||||||
o.name = standard.m.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (standard.m.mods && Object.keys(standard.m.mods).length > 0) {
|
|
||||||
o.modifications = standard.m.mods;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (standard.m.blueprint && Object.keys(standard.m.blueprint).length > 0) {
|
|
||||||
o.blueprint = standard.m.blueprint;
|
|
||||||
}
|
|
||||||
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates ship-loadout JSON Schema slot object
|
|
||||||
* @param {Object} slot Slot model
|
|
||||||
* @return {Object} JSON Schema Slot
|
|
||||||
*/
|
|
||||||
function slotToSchema(slot) {
|
|
||||||
if (slot.m) {
|
|
||||||
let o = {
|
|
||||||
class: slot.m.class,
|
|
||||||
rating: slot.m.rating,
|
|
||||||
enabled: Boolean(slot.enabled),
|
|
||||||
priority: slot.priority + 1,
|
|
||||||
group: ModuleGroupToName[slot.m.grp]
|
|
||||||
};
|
|
||||||
|
|
||||||
if (slot.m.name) {
|
|
||||||
o.name = slot.m.name;
|
|
||||||
}
|
|
||||||
if (slot.m.mount) {
|
|
||||||
o.mount = MountMap[slot.m.mount];
|
|
||||||
}
|
|
||||||
if (slot.m.missile) {
|
|
||||||
o.missile = slot.m.missile;
|
|
||||||
}
|
|
||||||
if (slot.m.mods && Object.keys(slot.m.mods).length > 0) {
|
|
||||||
o.modifications = slot.m.mods;
|
|
||||||
}
|
|
||||||
if (slot.m.blueprint && Object.keys(slot.m.blueprint).length > 0) {
|
|
||||||
o.blueprint = slot.m.blueprint;
|
|
||||||
}
|
|
||||||
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates an object conforming to the ship-loadout JSON schema from a Ship model
|
|
||||||
* @param {string} buildName The build name
|
|
||||||
* @param {Ship} ship Ship instance
|
|
||||||
* @return {Object} ship-loadout object
|
|
||||||
*/
|
|
||||||
export function toDetailedBuild(buildName, ship) {
|
|
||||||
let standard = ship.standard,
|
|
||||||
hardpoints = ship.hardpoints,
|
|
||||||
internal = ship.internal,
|
|
||||||
code = ship.toString();
|
|
||||||
|
|
||||||
let data = {
|
|
||||||
$schema: 'https://coriolis.io/schemas/ship-loadout/4.json#',
|
|
||||||
name: buildName,
|
|
||||||
ship: ship.name,
|
|
||||||
references: [{
|
|
||||||
name: 'Coriolis.io',
|
|
||||||
url: 'https://coriolis.io' + outfitURL(ship.id, code, buildName),
|
|
||||||
code,
|
|
||||||
shipId: ship.id
|
|
||||||
}],
|
|
||||||
components: {
|
|
||||||
standard: {
|
|
||||||
bulkheads: BulkheadNames[ship.bulkheads.m.index],
|
|
||||||
cargoHatch: { enabled: Boolean(ship.cargoHatch.enabled), priority: ship.cargoHatch.priority + 1 },
|
|
||||||
powerPlant: standardToSchema(standard[0]),
|
|
||||||
thrusters: standardToSchema(standard[1]),
|
|
||||||
frameShiftDrive: standardToSchema(standard[2]),
|
|
||||||
lifeSupport: standardToSchema(standard[3]),
|
|
||||||
powerDistributor: standardToSchema(standard[4]),
|
|
||||||
sensors: standardToSchema(standard[5]),
|
|
||||||
fuelTank: standardToSchema(standard[6])
|
|
||||||
},
|
|
||||||
hardpoints: hardpoints.filter(slot => slot.maxClass > 0).map(slotToSchema),
|
|
||||||
utility: hardpoints.filter(slot => slot.maxClass === 0).map(slotToSchema),
|
|
||||||
internal: internal.map(slotToSchema)
|
|
||||||
},
|
|
||||||
stats: {}
|
|
||||||
};
|
|
||||||
|
|
||||||
for (let stat in ship) {
|
|
||||||
if (!isNaN(ship[stat])) {
|
|
||||||
data.stats[stat] = Math.round(ship[stat] * 100) / 100;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instantiates a ship from a ship-loadout object
|
|
||||||
* @param {Object} detailedBuild ship-loadout object
|
|
||||||
* @return {Ship} Ship instance
|
|
||||||
*/
|
|
||||||
export function fromDetailedBuild(detailedBuild) {
|
|
||||||
let shipId = Object.keys(Ships).find((shipId) => Ships[shipId].properties.name.toLowerCase() == detailedBuild.ship.toLowerCase());
|
|
||||||
if (!shipId) {
|
|
||||||
throw 'No such ship: ' + detailedBuild.ship;
|
|
||||||
}
|
|
||||||
|
|
||||||
let shipData = Ships[shipId];
|
|
||||||
let ship = new Ship(shipId, shipData.properties, shipData.slots);
|
|
||||||
|
|
||||||
if (!detailedBuild.references[0] || !detailedBuild.references[0].code) {
|
|
||||||
throw 'Missing reference code';
|
|
||||||
}
|
|
||||||
|
|
||||||
ship.buildFrom(detailedBuild.references[0].code);
|
|
||||||
|
|
||||||
return ship;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates an array of ship-loadout JSON Schema object for export
|
|
||||||
* @param {Array} builds Array of ship builds
|
|
||||||
* @return {Array} Array of of ship-loadout objects
|
|
||||||
*/
|
|
||||||
export function toDetailedExport(builds) {
|
|
||||||
let data = [];
|
|
||||||
|
|
||||||
for (let shipId in builds) {
|
|
||||||
for (let buildName in builds[shipId]) {
|
|
||||||
let code = builds[shipId][buildName];
|
|
||||||
let shipData = Ships[shipId];
|
|
||||||
let ship = new Ship(shipId, shipData.properties, shipData.slots);
|
|
||||||
ship.buildFrom(code);
|
|
||||||
data.push(toDetailedBuild(buildName, ship, code));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Serializes a comparion and all of the ships to zipped
|
|
||||||
* Base 64 encoded JSON.
|
|
||||||
* @param {string} name Comparison name
|
|
||||||
* @param {array} builds Array of ship builds
|
|
||||||
* @param {array} facets Selected facets
|
|
||||||
* @param {string} predicate sort predicate
|
|
||||||
* @param {boolean} desc sort order
|
|
||||||
* @return {string} Zipped Base 64 encoded JSON
|
|
||||||
*/
|
|
||||||
export function fromComparison(name, builds, facets, predicate, desc) {
|
|
||||||
return LZString.compressToBase64(JSON.stringify({
|
|
||||||
n: name,
|
|
||||||
b: builds.map((b) => { return { s: b.id, n: b.buildName, c: b.toString() }; }),
|
|
||||||
f: facets,
|
|
||||||
p: predicate,
|
|
||||||
d: desc ? 1 : 0
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses the comarison data string back to an object.
|
|
||||||
* @param {string} code Zipped Base 64 encoded JSON comparison data
|
|
||||||
* @return {Object} Comparison data object
|
|
||||||
*/
|
|
||||||
export function toComparison(code) {
|
|
||||||
return JSON.parse(LZString.decompressFromBase64(Utils.fromUrlSafe(code)));
|
|
||||||
};
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,415 +0,0 @@
|
|||||||
import * as ModuleUtils from './ModuleUtils';
|
|
||||||
import { canMount } from '../utils/SlotFunctions';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Standard / typical role for multi-purpose or combat (if shielded with better bulkheads)
|
|
||||||
* @param {Ship} ship Ship instance
|
|
||||||
* @param {Boolean} shielded True if shield generator should be included
|
|
||||||
* @param {integer} bulkheadIndex Bulkhead to use see Constants.BulkheadNames
|
|
||||||
*/
|
|
||||||
export function multiPurpose(ship, shielded, bulkheadIndex) {
|
|
||||||
ship.useStandard('A')
|
|
||||||
.use(ship.standard[3], ModuleUtils.standard(3, ship.standard[3].maxClass + 'D')) // D Life Support
|
|
||||||
.use(ship.standard[5], ModuleUtils.standard(5, ship.standard[5].maxClass + 'D')) // D Sensors
|
|
||||||
.useBulkhead(bulkheadIndex);
|
|
||||||
|
|
||||||
if (shielded) {
|
|
||||||
ship.internal.some(function(slot) {
|
|
||||||
if (canMount(ship, slot, 'sg')) { // Assuming largest slot can hold an eligible shield
|
|
||||||
ship.use(slot, ModuleUtils.findInternal('sg', slot.maxClass, 'A'));
|
|
||||||
ship.setSlotEnabled(slot, true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Trader Role
|
|
||||||
* @param {Ship} ship Ship instance
|
|
||||||
* @param {Boolean} shielded True if shield generator should be included
|
|
||||||
* @param {Object} standardOpts [Optional] Standard module optional overrides
|
|
||||||
*/
|
|
||||||
export function trader(ship, shielded, standardOpts) {
|
|
||||||
let usedSlots = [];
|
|
||||||
let bstCount = 2;
|
|
||||||
let sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass);
|
|
||||||
ship.useStandard('A')
|
|
||||||
.use(ship.standard[3], ModuleUtils.standard(3, ship.standard[3].maxClass + 'D')) // D Life Support
|
|
||||||
.use(ship.standard[1], ModuleUtils.standard(1, ship.standard[1].maxClass + 'D')) // D Life Support
|
|
||||||
.use(ship.standard[4], ModuleUtils.standard(4, ship.standard[4].maxClass + 'D')) // D Life Support
|
|
||||||
.use(ship.standard[5], ModuleUtils.standard(5, ship.standard[5].maxClass + 'D')); // D Sensors
|
|
||||||
|
|
||||||
const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8];
|
|
||||||
const shieldInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
|
||||||
.filter(a => (!a.eligible) || a.eligible.sg)
|
|
||||||
.filter(a => a.maxClass >= sg.class)
|
|
||||||
.sort((a, b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass));
|
|
||||||
shieldInternals.some(function(slot) {
|
|
||||||
if (canMount(ship, slot, 'sg')) { // Assuming largest slot can hold an eligible shield
|
|
||||||
const shield = ModuleUtils.findInternal('sg', slot.maxClass, 'A');
|
|
||||||
if (shield && shield.maxmass > ship.hullMass) {
|
|
||||||
ship.use(slot, shield);
|
|
||||||
ship.setSlotEnabled(slot, true);
|
|
||||||
usedSlots.push(slot);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Fill the empty internals with cargo racks
|
|
||||||
for (let i = ship.internal.length; i--;) {
|
|
||||||
let slot = ship.internal[i];
|
|
||||||
if (usedSlots.indexOf(slot) == -1 && canMount(ship, slot, 'cr')) {
|
|
||||||
ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Empty the hardpoints
|
|
||||||
for (let s of ship.hardpoints) {
|
|
||||||
ship.use(s, null);
|
|
||||||
}
|
|
||||||
for (let s of ship.hardpoints) {
|
|
||||||
if (s.maxClass == 0 && bstCount) { // Mount up to 2 boosters
|
|
||||||
ship.use(s, ModuleUtils.hardpoints('04'));
|
|
||||||
bstCount--;
|
|
||||||
} else {
|
|
||||||
ship.use(s, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ship.useLightestStandard(standardOpts);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Explorer Role
|
|
||||||
* @param {Ship} ship Ship instance
|
|
||||||
* @param {Boolean} planetary True if Planetary Vehicle Hangar (PVH) should be included
|
|
||||||
*/
|
|
||||||
export function explorer(ship, planetary) {
|
|
||||||
let standardOpts = { ppRating: 'A' },
|
|
||||||
heatSinkCount = 2, // Fit 2 heat sinks if possible
|
|
||||||
usedSlots = [],
|
|
||||||
sgSlot,
|
|
||||||
fuelScoopSlot,
|
|
||||||
sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass);
|
|
||||||
|
|
||||||
if (!planetary) { // Non-planetary explorers don't really need to boost
|
|
||||||
standardOpts.pd = '1D';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cargo hatch can be disabled
|
|
||||||
ship.setSlotEnabled(ship.cargoHatch, false);
|
|
||||||
|
|
||||||
// Advanced Discovery Scanner - class 1 or higher
|
|
||||||
const adsOrder = [1, 2, 3, 4, 5, 6, 7, 8];
|
|
||||||
const adsInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
|
||||||
.filter(a => (!a.eligible) || a.eligible.sc)
|
|
||||||
.sort((a, b) => adsOrder.indexOf(a.maxClass) - adsOrder.indexOf(b.maxClass));
|
|
||||||
for (let i = 0; i < adsInternals.length; i++) {
|
|
||||||
if (canMount(ship, adsInternals[i], 'sc')) {
|
|
||||||
ship.use(adsInternals[i], ModuleUtils.internal('2f'));
|
|
||||||
usedSlots.push(adsInternals[i]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (planetary) {
|
|
||||||
// Planetary Vehicle Hangar - class 2 or higher
|
|
||||||
const pvhOrder = [2, 3, 4, 5, 6, 7, 8, 1];
|
|
||||||
const pvhInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
|
||||||
.filter(a => (!a.eligible) || a.eligible.pv)
|
|
||||||
.sort((a, b) => pvhOrder.indexOf(a.maxClass) - pvhOrder.indexOf(b.maxClass));
|
|
||||||
for (let i = 0; i < pvhInternals.length; i++) {
|
|
||||||
if (canMount(ship, pvhInternals[i], 'pv')) {
|
|
||||||
// Planetary Vehical Hangar only has even classes
|
|
||||||
const pvhClass = pvhInternals[i].maxClass % 2 === 1 ? pvhInternals[i].maxClass - 1 : pvhInternals[i].maxClass;
|
|
||||||
ship.use(pvhInternals[i], ModuleUtils.findInternal('pv', pvhClass, 'G')); // G is lower mass
|
|
||||||
ship.setSlotEnabled(pvhInternals[i], false); // Disable power for Planetary Vehical Hangar
|
|
||||||
usedSlots.push(pvhInternals[i]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shield generator
|
|
||||||
const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8];
|
|
||||||
const shieldInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
|
||||||
.filter(a => (!a.eligible) || a.eligible.sg)
|
|
||||||
.filter(a => a.maxClass >= sg.class)
|
|
||||||
.sort((a, b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass));
|
|
||||||
for (let i = 0; i < shieldInternals.length; i++) {
|
|
||||||
if (canMount(ship, shieldInternals[i], 'sg')) {
|
|
||||||
ship.use(shieldInternals[i], sg);
|
|
||||||
usedSlots.push(shieldInternals[i]);
|
|
||||||
sgSlot = shieldInternals[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detailed Surface Scanner
|
|
||||||
const dssOrder = [1, 2, 3, 4, 5, 6, 7, 8];
|
|
||||||
const dssInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
|
||||||
.filter(a => (!a.eligible) || a.eligible.sc)
|
|
||||||
.sort((a, b) => dssOrder.indexOf(a.maxClass) - dssOrder.indexOf(b.maxClass));
|
|
||||||
for (let i = 0; i < dssInternals.length; i++) {
|
|
||||||
if (canMount(ship, dssInternals[i], 'sc')) {
|
|
||||||
ship.use(dssInternals[i], ModuleUtils.internal('2i'));
|
|
||||||
usedSlots.push(dssInternals[i]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fuel scoop - best possible
|
|
||||||
const fuelScoopOrder = [8, 7, 6, 5, 4, 3, 2, 1];
|
|
||||||
const fuelScoopInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
|
||||||
.filter(a => (!a.eligible) || a.eligible.fs)
|
|
||||||
.sort((a, b) => fuelScoopOrder.indexOf(a.maxClass) - fuelScoopOrder.indexOf(b.maxClass));
|
|
||||||
for (let i = 0; i < fuelScoopInternals.length; i++) {
|
|
||||||
if (canMount(ship, fuelScoopInternals[i], 'fs')) {
|
|
||||||
ship.use(fuelScoopInternals[i], ModuleUtils.findInternal('fs', fuelScoopInternals[i].maxClass, 'A'));
|
|
||||||
usedSlots.push(fuelScoopInternals[i]);
|
|
||||||
fuelScoopSlot = fuelScoopInternals[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AFMUs - fill as they are 0-weight
|
|
||||||
const afmuOrder = [8, 7, 6, 5, 4, 3, 2, 1];
|
|
||||||
const afmuInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
|
||||||
.filter(a => (!a.eligible) || a.eligible.pc)
|
|
||||||
.sort((a, b) => afmuOrder.indexOf(a.maxClass) - afmuOrder.indexOf(b.maxClass));
|
|
||||||
for (let i = 0; i < afmuInternals.length; i++) {
|
|
||||||
if (canMount(ship, afmuInternals[i], 'am')) {
|
|
||||||
ship.use(afmuInternals[i], ModuleUtils.findInternal('am', afmuInternals[i].maxClass, 'A'));
|
|
||||||
usedSlots.push(afmuInternals[i]);
|
|
||||||
ship.setSlotEnabled(afmuInternals[i], false); // Disable power for AFM Unit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let s of ship.hardpoints) {
|
|
||||||
if (s.maxClass == 0 && heatSinkCount) { // Mount up to 2 heatsinks
|
|
||||||
ship.use(s, ModuleUtils.hardpoints('02'));
|
|
||||||
ship.setSlotEnabled(s, heatSinkCount == 2); // Only enable a single Heatsink
|
|
||||||
heatSinkCount--;
|
|
||||||
} else {
|
|
||||||
ship.use(s, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sgSlot && fuelScoopSlot) {
|
|
||||||
// The SG and Fuel scoop to not need to be powered at the same time
|
|
||||||
if (sgSlot.m.getPowerUsage() > fuelScoopSlot.m.getPowerUsage()) { // The Shield generator uses the most power
|
|
||||||
ship.setSlotEnabled(fuelScoopSlot, false);
|
|
||||||
} else { // The Fuel scoop uses the most power
|
|
||||||
ship.setSlotEnabled(sgSlot, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ship.useLightestStandard(standardOpts);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Miner Role
|
|
||||||
* @param {Ship} ship Ship instance
|
|
||||||
* @param {Boolean} shielded True if shield generator should be included
|
|
||||||
*/
|
|
||||||
export function miner(ship, shielded) {
|
|
||||||
shielded = true;
|
|
||||||
let standardOpts = { ppRating: 'A' },
|
|
||||||
miningLaserCount = 2,
|
|
||||||
usedSlots = [],
|
|
||||||
sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass);
|
|
||||||
|
|
||||||
// Cargo hatch should be enabled
|
|
||||||
ship.setSlotEnabled(ship.cargoHatch, true);
|
|
||||||
|
|
||||||
// Largest possible refinery
|
|
||||||
const refineryOrder = [4, 5, 6, 7, 8, 3, 2, 1];
|
|
||||||
const refineryInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
|
||||||
.filter(a => (!a.eligible) || a.eligible.rf)
|
|
||||||
.sort((a, b) => refineryOrder.indexOf(a.maxClass) - refineryOrder.indexOf(b.maxClass));
|
|
||||||
for (let i = 0; i < refineryInternals.length; i++) {
|
|
||||||
if (canMount(ship, refineryInternals[i], 'rf')) {
|
|
||||||
ship.use(refineryInternals[i], ModuleUtils.findInternal('rf', Math.min(refineryInternals[i].maxClass, 4), 'A'));
|
|
||||||
usedSlots.push(refineryInternals[i]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prospector limpet controller - 3A if possible
|
|
||||||
const prospectorOrder = [3, 4, 5, 6, 7, 8, 2, 1];
|
|
||||||
const prospectorInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
|
||||||
.filter(a => (!a.eligible) || a.eligible.pc)
|
|
||||||
.sort((a, b) => prospectorOrder.indexOf(a.maxClass) - prospectorOrder.indexOf(b.maxClass));
|
|
||||||
for (let i = 0; i < prospectorInternals.length; i++) {
|
|
||||||
if (canMount(ship, prospectorInternals[i], 'pc')) {
|
|
||||||
// Prospector only has odd classes
|
|
||||||
const prospectorClass = prospectorInternals[i].maxClass % 2 === 0 ? prospectorInternals[i].maxClass - 1 : prospectorInternals[i].maxClass;
|
|
||||||
ship.use(prospectorInternals[i], ModuleUtils.findInternal('pc', prospectorClass, 'A'));
|
|
||||||
usedSlots.push(prospectorInternals[i]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shield generator if required
|
|
||||||
if (shielded) {
|
|
||||||
const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8];
|
|
||||||
const shieldInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
|
||||||
.filter(a => (!a.eligible) || a.eligible.sg)
|
|
||||||
.filter(a => a.maxClass >= sg.class)
|
|
||||||
.sort((a, b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass));
|
|
||||||
for (let i = 0; i < shieldInternals.length; i++) {
|
|
||||||
if (canMount(ship, shieldInternals[i], 'sg')) {
|
|
||||||
ship.use(shieldInternals[i], sg);
|
|
||||||
usedSlots.push(shieldInternals[i]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dual mining lasers of highest possible class; remove anything else
|
|
||||||
const miningLaserOrder = [2, 3, 4, 1, 0];
|
|
||||||
const miningLaserHardpoints = ship.hardpoints.concat().sort(function(a, b) {
|
|
||||||
return miningLaserOrder.indexOf(a.maxClass) - miningLaserOrder.indexOf(b.maxClass);
|
|
||||||
});
|
|
||||||
for (let s of miningLaserHardpoints) {
|
|
||||||
if (s.maxClass >= 1 && miningLaserCount) {
|
|
||||||
ship.use(s, ModuleUtils.hardpoints(s.maxClass >= 2 ? '2m' : '2l'));
|
|
||||||
miningLaserCount--;
|
|
||||||
} else {
|
|
||||||
ship.use(s, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Number of collector limpets required to be active is a function of the size of the ship and the power of the lasers
|
|
||||||
const miningLaserDps = ship.hardpoints.filter(h => h.m != null)
|
|
||||||
.reduce(function(a, b) {
|
|
||||||
return a + b.m.getDps();
|
|
||||||
}, 0);
|
|
||||||
// Find out how many internal slots we have, and their potential cargo size
|
|
||||||
const potentialCargo = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
|
||||||
.filter(a => (!a.eligible) || a.eligible.cr)
|
|
||||||
.map(b => Math.pow(2, b.maxClass));
|
|
||||||
// One collector for each 1.25 DPS, multiply by 1.25 for medium ships and 1.5 for large ships as they have further to travel
|
|
||||||
// 0 if we only have 1 cargo slot, otherwise minium of 1 and maximum of 6 (excluding size modifier)
|
|
||||||
const sizeModifier = ship.class == 2 ? 1.2 : ship.class == 3 ? 1.5 : 1;
|
|
||||||
let collectorLimpetsRequired = potentialCargo.length == 1 ? 0 : Math.ceil(sizeModifier * Math.min(6, Math.floor(miningLaserDps / 1.25)));
|
|
||||||
|
|
||||||
if (collectorLimpetsRequired > 0) {
|
|
||||||
const collectorOrder = [1, 2, 3, 4, 5, 6, 7, 8];
|
|
||||||
const collectorInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
|
||||||
.filter(a => (!a.eligible) || a.eligible.cc)
|
|
||||||
.sort((a, b) => collectorOrder.indexOf(a.maxClass) - collectorOrder.indexOf(b.maxClass));
|
|
||||||
// Always keep at least 2 slots free for cargo racks (1 for shielded)
|
|
||||||
for (let i = 0; i < collectorInternals.length - (shielded ? 1 : 2) && collectorLimpetsRequired > 0; i++) {
|
|
||||||
if (canMount(ship, collectorInternals[i], 'cc')) {
|
|
||||||
// Collector only has odd classes
|
|
||||||
const collectorClass = collectorInternals[i].maxClass % 2 === 0 ? collectorInternals[i].maxClass - 1 : collectorInternals[i].maxClass;
|
|
||||||
ship.use(collectorInternals[i], ModuleUtils.findInternal('cc', collectorClass, 'D'));
|
|
||||||
usedSlots.push(collectorInternals[i]);
|
|
||||||
collectorLimpetsRequired -= collectorInternals[i].m.maximum;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Power distributor to power the mining lasers indefinitely
|
|
||||||
const wepRateRequired = ship.hardpoints.filter(h => h.m != null)
|
|
||||||
.reduce(function(a, b) {
|
|
||||||
return a + b.m.getEps();
|
|
||||||
}, 0);
|
|
||||||
standardOpts.pd = ship.getAvailableModules().matchingPowerDist({ weprate: wepRateRequired }).id;
|
|
||||||
|
|
||||||
// Fill the empty internals with cargo racks
|
|
||||||
for (let i = ship.internal.length; i--;) {
|
|
||||||
let slot = ship.internal[i];
|
|
||||||
if (usedSlots.indexOf(slot) == -1 && canMount(ship, slot, 'cr')) {
|
|
||||||
ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ship.useLightestStandard(standardOpts);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Racer Role
|
|
||||||
* @param {Ship} ship Ship instance
|
|
||||||
*/
|
|
||||||
export function racer(ship) {
|
|
||||||
let standardOpts = {},
|
|
||||||
usedSlots = [],
|
|
||||||
sgSlot,
|
|
||||||
sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass);
|
|
||||||
|
|
||||||
// Cargo hatch can be disabled
|
|
||||||
ship.setSlotEnabled(ship.cargoHatch, false);
|
|
||||||
|
|
||||||
// Shield generator
|
|
||||||
const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8];
|
|
||||||
const shieldInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
|
||||||
.filter(a => (!a.eligible) || a.eligible.sg)
|
|
||||||
.filter(a => a.maxClass >= sg.class)
|
|
||||||
.sort((a, b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass));
|
|
||||||
for (let i = 0; i < shieldInternals.length; i++) {
|
|
||||||
if (canMount(ship, shieldInternals[i], 'sg')) {
|
|
||||||
ship.use(shieldInternals[i], sg);
|
|
||||||
usedSlots.push(shieldInternals[i]);
|
|
||||||
sgSlot = shieldInternals[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Empty the hardpoints
|
|
||||||
for (let s of ship.hardpoints) {
|
|
||||||
ship.use(s, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Empty the internals
|
|
||||||
for (let i = ship.internal.length; i--;) {
|
|
||||||
let slot = ship.internal[i];
|
|
||||||
if (usedSlots.indexOf(slot) == -1) {
|
|
||||||
ship.use(slot, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Best thrusters
|
|
||||||
if (ship.standard[1].maxClass === 3) {
|
|
||||||
standardOpts.th = 'tz';
|
|
||||||
} else if (ship.standard[1].maxClass === 2) {
|
|
||||||
standardOpts.th = 'u0';
|
|
||||||
} else {
|
|
||||||
standardOpts.th = ship.standard[1].maxClass + 'A';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Best power distributor for more boosting
|
|
||||||
standardOpts.pd = ship.standard[4].maxClass + 'A';
|
|
||||||
|
|
||||||
// Smallest possible FSD drive
|
|
||||||
standardOpts.fsd = '2D';
|
|
||||||
// Minimal fuel tank
|
|
||||||
standardOpts.ft = '1C';
|
|
||||||
|
|
||||||
// Disable nearly everything
|
|
||||||
standardOpts.fsdDisabled = true;
|
|
||||||
standardOpts.sDisabled = true;
|
|
||||||
standardOpts.pdDisabled = true;
|
|
||||||
standardOpts.lsDisabled = true;
|
|
||||||
|
|
||||||
ship.useLightestStandard(standardOpts);
|
|
||||||
|
|
||||||
// Apply engineering to each module
|
|
||||||
// ship.standard[1].m.blueprint = getBlueprint('Engine_Dirty', ship.standard[0]);
|
|
||||||
// ship.standard[1].m.blueprint.grade = 5;
|
|
||||||
// setBest(ship, ship.standard[1].m);
|
|
||||||
|
|
||||||
// ship.standard[3].m.blueprint = getBlueprint('LifeSupport_LightWeight', ship.standard[3]);
|
|
||||||
// ship.standard[3].m.blueprint.grade = 4;
|
|
||||||
// setBest(ship, ship.standard[3].m);
|
|
||||||
|
|
||||||
// ship.standard[4].m.blueprint = getBlueprint('PowerDistributor_PriorityEngines', ship.standard[4]);
|
|
||||||
// ship.standard[4].m.blueprint.grade = 3;
|
|
||||||
// setBest(ship, ship.standard[4].m);
|
|
||||||
|
|
||||||
// ship.standard[5].m.blueprint = getBlueprint('Sensor_Sensor_LightWeight', ship.standard[5]);
|
|
||||||
// ship.standard[5].m.blueprint.grade = 5;
|
|
||||||
// setBest(ship, ship.standard[5].m);
|
|
||||||
}
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
export const SI_PREFIXES = {
|
|
||||||
'Y': 1e+24, // Yotta
|
|
||||||
'Z': 1e+21, // Zetta
|
|
||||||
'E': 1e+18, // Peta
|
|
||||||
'P': 1e+15, // Peta
|
|
||||||
'T': 1e+12, // Tera
|
|
||||||
'G': 1e+9, // Giga
|
|
||||||
'M': 1e+6, // Mega
|
|
||||||
'k': 1e+3, // Kilo
|
|
||||||
'h': 1e+2, // Hekto
|
|
||||||
'da': 1e+1, // Deka
|
|
||||||
'': 1,
|
|
||||||
'd': 1e-1, // Dezi
|
|
||||||
'c': 1e-2, // Zenti
|
|
||||||
'm': 1e-3, // Milli
|
|
||||||
'μ': 1e-6, // mikro not supported due to charset
|
|
||||||
'n': 10e-9, // Nano
|
|
||||||
'p': 1e-12, // Nano
|
|
||||||
'f': 1e-15, // Femto
|
|
||||||
'a': 1e-18, // Atto
|
|
||||||
'z': 1e-21, // Zepto
|
|
||||||
'y': 1e-24 // Yokto
|
|
||||||
};
|
|
||||||
|
|
||||||
export const STATS_FORMATTING = {
|
|
||||||
'ammo': { 'format': 'int', },
|
|
||||||
'boot': { 'format': 'int', 'unit': 'secs' },
|
|
||||||
'brokenregen': { 'format': 'round1', 'unit': 'ps' },
|
|
||||||
'burst': { 'format': 'int', 'change': 'additive' },
|
|
||||||
'burstrof': { 'format': 'round1', 'unit': 'ps', 'change': 'additive' },
|
|
||||||
'causres': { 'format': 'pct' },
|
|
||||||
'clip': { 'format': 'int' },
|
|
||||||
'damage': { 'format': 'round' },
|
|
||||||
'dps': { 'format': 'round', 'units': 'ps', 'synthetic': 'getDps' },
|
|
||||||
'dpe': { 'format': 'round', 'units': 'ps', 'synthetic': 'getDpe' },
|
|
||||||
'distdraw': { 'format': 'round', 'unit': 'MW' },
|
|
||||||
'duration': { 'format': 'round1', 'unit': 's' },
|
|
||||||
'eff': { 'format': 'round2' },
|
|
||||||
'engcap': { 'format': 'round1', 'unit': 'MJ' },
|
|
||||||
'engrate': { 'format': 'round1', 'unit': 'MW' },
|
|
||||||
'eps': { 'format': 'round', 'units': 'ps', 'synthetic': 'getEps' },
|
|
||||||
'explres': { 'format': 'pct' },
|
|
||||||
'facinglimit': { 'format': 'round1', 'unit': 'ang' },
|
|
||||||
'falloff': { 'format': 'round', 'unit': 'km', 'storedUnit': 'm' },
|
|
||||||
'fallofffromrange': { 'format': 'round', 'unit': 'km', 'storedUnit': 'm', 'synthetic': 'getFalloff' },
|
|
||||||
'hps': { 'format': 'round', 'units': 'ps', 'synthetic': 'getHps' },
|
|
||||||
'hullboost': { 'format': 'pct1', 'change': 'additive' },
|
|
||||||
'hullreinforcement': { 'format': 'int' },
|
|
||||||
'integrity': { 'format': 'round1' },
|
|
||||||
'jitter': { 'format': 'round', 'unit': 'ang' },
|
|
||||||
'kinres': { 'format': 'pct' },
|
|
||||||
'mass': { 'format': 'round1', 'unit': 'T' },
|
|
||||||
'maxfuel': { 'format': 'round1', 'unit': 'T' },
|
|
||||||
'optmass': { 'format': 'int', 'unit': 'T' },
|
|
||||||
'optmul': { 'format': 'pct', 'change': 'additive' },
|
|
||||||
'pgen': { 'format': 'round1', 'unit': 'MW' },
|
|
||||||
'piercing': { 'format': 'int' },
|
|
||||||
'power': { 'format': 'round', 'unit': 'MW' },
|
|
||||||
'protection': { 'format': 'pct' },
|
|
||||||
'range': { 'format': 'f2', 'unit': 'km', 'storedUnit': 'm' },
|
|
||||||
'ranget': { 'format': 'f1', 'unit': 's' },
|
|
||||||
'regen': { 'format': 'round1', 'unit': 'ps' },
|
|
||||||
'reload': { 'format': 'int', 'unit': 's' },
|
|
||||||
'rof': { 'format': 'round1', 'unit': 'ps', 'synthetic': 'getRoF', 'higherbetter': true },
|
|
||||||
'angle': { 'format': 'round1', 'unit': 'ang' },
|
|
||||||
'scanrate': { 'format': 'int' },
|
|
||||||
'scantime': { 'format': 'round1', 'unit': 's' },
|
|
||||||
'sdps': { 'format': 'round1', 'units': 'ps', 'synthetic': 'getSDps' },
|
|
||||||
'shield': { 'format': 'int', 'unit': 'MJ' },
|
|
||||||
'shieldaddition': { 'format': 'round1', 'unit': 'MJ' },
|
|
||||||
'shieldboost': { 'format': 'pct1', 'change': 'additive' },
|
|
||||||
'shieldreinforcement': { 'format': 'round1', 'unit': 'MJ' },
|
|
||||||
'shotspeed': { 'format': 'int', 'unit': 'm/s' },
|
|
||||||
'spinup': { 'format': 'round1', 'unit': 's' },
|
|
||||||
'syscap': { 'format': 'round1', 'unit': 'MJ' },
|
|
||||||
'sysrate': { 'format': 'round1', 'unit': 'MW' },
|
|
||||||
'thermload': { 'format': 'round1' },
|
|
||||||
'thermres': { 'format': 'pct' },
|
|
||||||
'wepcap': { 'format': 'round1', 'unit': 'MJ' },
|
|
||||||
'weprate': { 'format': 'round1', 'unit': 'MW' },
|
|
||||||
'jumpboost': { 'format': 'round1', 'unit': 'LY' },
|
|
||||||
'proberadius': { 'format': 'pct1', 'unit': 'pct' },
|
|
||||||
};
|
|
||||||
@@ -2,7 +2,6 @@ import { EventEmitter } from 'fbemitter';
|
|||||||
import { Insurance } from '../shipyard/Constants';
|
import { Insurance } from '../shipyard/Constants';
|
||||||
|
|
||||||
const LS_KEY_BUILDS = 'builds';
|
const LS_KEY_BUILDS = 'builds';
|
||||||
const LS_KEY_COMPARISONS = 'comparisons';
|
|
||||||
const LS_KEY_LANG = 'NG_TRANSLATE_LANG_KEY';
|
const LS_KEY_LANG = 'NG_TRANSLATE_LANG_KEY';
|
||||||
const LS_KEY_COST_TAB = 'costTab';
|
const LS_KEY_COST_TAB = 'costTab';
|
||||||
const LS_KEY_CMDR_NAME = 'cmdrName';
|
const LS_KEY_CMDR_NAME = 'cmdrName';
|
||||||
@@ -13,9 +12,7 @@ const LS_KEY_MOD_DISCOUNT = 'moduleDiscount';
|
|||||||
const LS_KEY_STATE = 'state';
|
const LS_KEY_STATE = 'state';
|
||||||
const LS_KEY_SIZE_RATIO = 'sizeRatio';
|
const LS_KEY_SIZE_RATIO = 'sizeRatio';
|
||||||
const LS_KEY_TOOLTIPS = 'tooltips';
|
const LS_KEY_TOOLTIPS = 'tooltips';
|
||||||
const LS_KEY_MODULE_RESISTANCES = 'moduleResistances';
|
|
||||||
const LS_KEY_ROLLS = 'matsPerGrade';
|
const LS_KEY_ROLLS = 'matsPerGrade';
|
||||||
const LS_KEY_ORBIS = 'orbis';
|
|
||||||
|
|
||||||
let LS;
|
let LS;
|
||||||
|
|
||||||
@@ -85,7 +82,6 @@ export class Persist extends EventEmitter {
|
|||||||
LS = null;
|
LS = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
let moduleResistances = _get(LS_KEY_MODULE_RESISTANCES);
|
|
||||||
let matsPerGrade = _get(LS_KEY_ROLLS);
|
let matsPerGrade = _get(LS_KEY_ROLLS);
|
||||||
let cmdrName = _get(LS_KEY_CMDR_NAME);
|
let cmdrName = _get(LS_KEY_CMDR_NAME);
|
||||||
let tips = _get(LS_KEY_TOOLTIPS);
|
let tips = _get(LS_KEY_TOOLTIPS);
|
||||||
@@ -93,16 +89,13 @@ export class Persist extends EventEmitter {
|
|||||||
let shipDiscount = _get(LS_KEY_SHIP_DISCOUNT);
|
let shipDiscount = _get(LS_KEY_SHIP_DISCOUNT);
|
||||||
let moduleDiscount = _get(LS_KEY_MOD_DISCOUNT);
|
let moduleDiscount = _get(LS_KEY_MOD_DISCOUNT);
|
||||||
let buildJson = _get(LS_KEY_BUILDS);
|
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.onStorageChange = this.onStorageChange.bind(this);
|
||||||
this.langCode = _getString(LS_KEY_LANG) || 'en';
|
this.langCode = _getString(LS_KEY_LANG) || 'en';
|
||||||
this.insurance = insurance && Insurance[insurance.toLowerCase()] !== undefined ? insurance : 'standard';
|
this.insurance = insurance && Insurance[insurance.toLowerCase()] !== undefined ? insurance : 'standard';
|
||||||
this.shipDiscount = !isNaN(shipDiscount) && shipDiscount < 1 ? shipDiscount * 1 : 0;
|
this.shipDiscount = !isNaN(shipDiscount) && shipDiscount < 1 ? shipDiscount * 1 : 0;
|
||||||
this.moduleDiscount = !isNaN(moduleDiscount) && moduleDiscount < 1 ? moduleDiscount * 1 : 0;
|
this.moduleDiscount = !isNaN(moduleDiscount) && moduleDiscount < 1 ? moduleDiscount * 1 : 0;
|
||||||
this.builds = buildJson && typeof buildJson == 'object' ? buildJson : {};
|
this.builds = buildJson && typeof buildJson == 'object' ? buildJson : {};
|
||||||
this.comparisons = comparisonJson && typeof comparisonJson == 'object' ? comparisonJson : {};
|
|
||||||
this.costTab = _getString(LS_KEY_COST_TAB);
|
this.costTab = _getString(LS_KEY_COST_TAB);
|
||||||
this.outfittingTab = _getString(LS_KEY_OUTFITTING_TAB);
|
this.outfittingTab = _getString(LS_KEY_OUTFITTING_TAB);
|
||||||
this.state = _get(LS_KEY_STATE);
|
this.state = _get(LS_KEY_STATE);
|
||||||
@@ -116,7 +109,6 @@ export class Persist extends EventEmitter {
|
|||||||
};
|
};
|
||||||
this.cmdrName = cmdrName || { selected: '', cmdrs: [] };
|
this.cmdrName = cmdrName || { selected: '', cmdrs: [] };
|
||||||
this.tooltipsEnabled = tips === null ? true : tips;
|
this.tooltipsEnabled = tips === null ? true : tips;
|
||||||
this.moduleResistancesEnabled = moduleResistances === null ? true : moduleResistances;
|
|
||||||
|
|
||||||
if (LS) {
|
if (LS) {
|
||||||
window.addEventListener('storage', this.onStorageChange);
|
window.addEventListener('storage', this.onStorageChange);
|
||||||
@@ -137,10 +129,6 @@ export class Persist extends EventEmitter {
|
|||||||
this.builds = newValue ? JSON.parse(newValue) : {};
|
this.builds = newValue ? JSON.parse(newValue) : {};
|
||||||
this.emit('builds');
|
this.emit('builds');
|
||||||
break;
|
break;
|
||||||
case LS_KEY_COMPARISONS:
|
|
||||||
this.comparisons = newValue ? JSON.parse(newValue) : {};
|
|
||||||
this.emit('comparisons');
|
|
||||||
break;
|
|
||||||
case LS_KEY_LANG:
|
case LS_KEY_LANG:
|
||||||
this.langCode = newValue;
|
this.langCode = newValue;
|
||||||
this.emit('language', newValue);
|
this.emit('language', newValue);
|
||||||
@@ -161,18 +149,10 @@ export class Persist extends EventEmitter {
|
|||||||
this.tooltipsEnabled = !!newValue && newValue.toLowerCase() == 'true';
|
this.tooltipsEnabled = !!newValue && newValue.toLowerCase() == 'true';
|
||||||
this.emit('tooltips', this.tooltipsEnabled);
|
this.emit('tooltips', this.tooltipsEnabled);
|
||||||
break;
|
break;
|
||||||
case LS_KEY_MODULE_RESISTANCES:
|
|
||||||
this.moduleResistancesEnabled = !!newValue && newValue.toLowerCase() == 'true';
|
|
||||||
this.emit('moduleresistances', this.moduleResistancesEnabled);
|
|
||||||
break;
|
|
||||||
case LS_KEY_ROLLS:
|
case LS_KEY_ROLLS:
|
||||||
this.matsPerGrade = JSON.parse(newValue);
|
this.matsPerGrade = JSON.parse(newValue);
|
||||||
this.emit('matsPerGrade', this.matsPerGrade);
|
this.emit('matsPerGrade', this.matsPerGrade);
|
||||||
break;
|
break;
|
||||||
case LS_KEY_ORBIS:
|
|
||||||
this.orbisCreds = JSON.parse(newValue);
|
|
||||||
this.emit('orbis', this.orbisCreds);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// On JSON.Parse Error - don't sync or do anything
|
// On JSON.Parse Error - don't sync or do anything
|
||||||
@@ -198,24 +178,6 @@ export class Persist extends EventEmitter {
|
|||||||
this.emit('language', langCode);
|
this.emit('language', langCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the current orbis.zone credentials
|
|
||||||
* @return {String} language code
|
|
||||||
*/
|
|
||||||
getOrbisCreds() {
|
|
||||||
return this.orbisCreds;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update and save the orbis.zone credentials
|
|
||||||
* @param {Object} creds object with username and password properties.
|
|
||||||
*/
|
|
||||||
setOrbisCreds(creds) {
|
|
||||||
this.langCode = creds;
|
|
||||||
_put(LS_KEY_ORBIS, creds);
|
|
||||||
this.emit('orbis', creds);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show tooltips setting
|
* Show tooltips setting
|
||||||
* @param {boolean} show Optional - update setting
|
* @param {boolean} show Optional - update setting
|
||||||
@@ -231,21 +193,6 @@ export class Persist extends EventEmitter {
|
|||||||
return this.tooltipsEnabled;
|
return this.tooltipsEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Show module resistances setting
|
|
||||||
* @param {boolean} show Optional - update setting
|
|
||||||
* @return {boolean} True if module resistances should be shown
|
|
||||||
*/
|
|
||||||
showModuleResistances(show) {
|
|
||||||
if (show !== undefined) {
|
|
||||||
this.moduleResistancesEnabled = !!show;
|
|
||||||
_put(LS_KEY_MODULE_RESISTANCES, this.moduleResistancesEnabled);
|
|
||||||
this.emit('moduleresistances', this.moduleResistancesEnabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.moduleResistancesEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Persist a ship build in local storage.
|
* Persist a ship build in local storage.
|
||||||
*
|
*
|
||||||
@@ -334,97 +281,16 @@ export class Persist extends EventEmitter {
|
|||||||
delete this.builds[shipId];
|
delete this.builds[shipId];
|
||||||
}
|
}
|
||||||
_put(LS_KEY_BUILDS, this.builds);
|
_put(LS_KEY_BUILDS, this.builds);
|
||||||
// Check if the build was used in existing comparisons
|
|
||||||
let comps = this.comparisons;
|
|
||||||
for (let c in comps) {
|
|
||||||
for (let i = 0; i < comps[c].builds.length; i++) { // For all builds in the current comparison
|
|
||||||
if (comps[c].builds[i].shipId == shipId && comps[c].builds[i].buildName == name) {
|
|
||||||
comps[c].builds.splice(i, 1);
|
|
||||||
break; // A build is unique per comparison
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_put(LS_KEY_COMPARISONS, this.comparisons);
|
|
||||||
this.emit('builds');
|
this.emit('builds');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Persist a comparison in localstorage.
|
* Delete all builds from localStorage
|
||||||
*
|
|
||||||
* @param {String} name The name of the comparison
|
|
||||||
* @param {array} builds Array of builds
|
|
||||||
* @param {array} facets Array of facet indices
|
|
||||||
*/
|
|
||||||
saveComparison(name, builds, facets) {
|
|
||||||
if (!this.comparisons[name]) {
|
|
||||||
this.comparisons[name] = {};
|
|
||||||
}
|
|
||||||
this.comparisons[name] = {
|
|
||||||
facets,
|
|
||||||
builds: builds.map(b => { return { shipId: b.id || b.shipId, buildName: b.buildName }; })
|
|
||||||
};
|
|
||||||
_put(LS_KEY_COMPARISONS, this.comparisons);
|
|
||||||
this.emit('comparisons');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a comparison
|
|
||||||
* @param {String} name Comparison name
|
|
||||||
* @return {Object} Object containing array of facets and ship id + build names
|
|
||||||
*/
|
|
||||||
getComparison(name) {
|
|
||||||
if (this.comparisons[name]) {
|
|
||||||
return this.comparisons[name];
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all saved comparisons
|
|
||||||
* @return {Object} All comparisons
|
|
||||||
*/
|
|
||||||
getComparisons() {
|
|
||||||
return this.comparisons;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if a comparison has been saved
|
|
||||||
* @param {String} name Comparison name
|
|
||||||
* @return {Boolean} True if a comparison has been saved
|
|
||||||
*/
|
|
||||||
hasComparison(name) {
|
|
||||||
return !!this.comparisons[name];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if any comparisons have been saved
|
|
||||||
* @return {Boolean} True if any comparisons have been saved
|
|
||||||
*/
|
|
||||||
hasComparisons() {
|
|
||||||
return Object.keys(this.comparisons).length > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the comparison from localstorage.
|
|
||||||
* @param {String} name Comparison name
|
|
||||||
*/
|
|
||||||
deleteComparison(name) {
|
|
||||||
if (this.comparisons[name]) {
|
|
||||||
delete this.comparisons[name];
|
|
||||||
_put(LS_KEY_COMPARISONS, this.comparisons);
|
|
||||||
this.emit('comparisons');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete all builds and comparisons from localStorage
|
|
||||||
*/
|
*/
|
||||||
deleteAll() {
|
deleteAll() {
|
||||||
this.builds = {};
|
this.builds = {};
|
||||||
this.comparisons = {};
|
|
||||||
_put(LS_KEY_BUILDS, {});
|
_put(LS_KEY_BUILDS, {});
|
||||||
_put(LS_KEY_COMPARISONS, {});
|
|
||||||
this.emit('deletedAll');
|
this.emit('deletedAll');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -435,7 +301,6 @@ export class Persist extends EventEmitter {
|
|||||||
getAll() {
|
getAll() {
|
||||||
let data = {};
|
let data = {};
|
||||||
data[LS_KEY_BUILDS] = this.getBuilds();
|
data[LS_KEY_BUILDS] = this.getBuilds();
|
||||||
data[LS_KEY_COMPARISONS] = this.getComparisons();
|
|
||||||
data[LS_KEY_INSURANCE] = this.getInsurance();
|
data[LS_KEY_INSURANCE] = this.getInsurance();
|
||||||
data[LS_KEY_SHIP_DISCOUNT] = this.shipDiscount;
|
data[LS_KEY_SHIP_DISCOUNT] = this.shipDiscount;
|
||||||
data[LS_KEY_MOD_DISCOUNT] = this.moduleDiscount;
|
data[LS_KEY_MOD_DISCOUNT] = this.moduleDiscount;
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
/**
|
|
||||||
* Generate a BBCode (Forum) compatible table from comparisons
|
|
||||||
* @param {Function} translate Translate language function
|
|
||||||
* @param {object} formats Number Formats
|
|
||||||
* @param {array} facets Ship Facets
|
|
||||||
* @param {object} builds Ship builds
|
|
||||||
* @param {string} link Link to the comparison
|
|
||||||
* @return {string} the BBCode
|
|
||||||
*/
|
|
||||||
export function comparisonBBCode(translate, formats, facets, builds, link) {
|
|
||||||
let colCount = 2, b, i, j, k, f, fl, p, pl, l = [];
|
|
||||||
|
|
||||||
for (i = 0; i < facets.length; i++) {
|
|
||||||
if (facets[i].active) {
|
|
||||||
f = facets[i];
|
|
||||||
p = f.props;
|
|
||||||
|
|
||||||
if (p.length == 1) {
|
|
||||||
l.push('[th][B][COLOR=#FF8C0D]', translate(f.title).toUpperCase(), '[/COLOR][/B][/th]');
|
|
||||||
colCount++;
|
|
||||||
} else {
|
|
||||||
for (j = 0; j < p.length; j++) {
|
|
||||||
l.push('[th][B][COLOR=#FF8C0D]', translate(f.title).toUpperCase(), '\n', translate(f.lbls[j]).toUpperCase(), '[/COLOR][/B][/th]');
|
|
||||||
colCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
l.push('[/tr]\n');
|
|
||||||
|
|
||||||
for (i = 0; i < builds.length; i++) {
|
|
||||||
b = builds[i];
|
|
||||||
|
|
||||||
l.push('[tr][td]', b.name, '[/td][td]', b.buildName, '[/td]');
|
|
||||||
|
|
||||||
for (j = 0, fl = facets.length; j < fl; j++) {
|
|
||||||
if (facets[j].active) {
|
|
||||||
f = facets[j];
|
|
||||||
p = f.props;
|
|
||||||
for (k = 0, pl = p.length; k < pl; k++) {
|
|
||||||
l.push('[td="align: right"]', formats[f.fmt](b[p[k]]), ' [size=-2]', translate(f.unit), '[/size][/td]');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
l.push('[/tr]\n');
|
|
||||||
}
|
|
||||||
l.push('[tr][td="align: center, colspan:', colCount, '"][size=-3]\n[url=', link, ']Interactive Comparison at Coriolis.io[/url][/td][/tr]\n[/size][/table]');
|
|
||||||
l.unshift('[table="width:', colCount * 90, ',align: center"]\n[tr][th][B][COLOR=#FF8C0D]Ship[/COLOR][/B][/th][th][B][COLOR="#FF8C0D"]Build[/COLOR][/B][/th]');
|
|
||||||
return l.join('');
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,25 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Modifications } from 'coriolis-data/dist';
|
|
||||||
import { Module } from 'ed-forge';
|
import { Module } from 'ed-forge';
|
||||||
import { getBlueprintInfo, getExperimentalInfo } from 'ed-forge/lib/data/blueprints';
|
import { getBlueprintInfo, getExperimentalInfo } from 'ed-forge/lib/src/data/blueprints';
|
||||||
import { entries, keys, uniq } from 'lodash';
|
import { fromPairs, keys, uniq } from 'lodash';
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Module} module Module to get modifiers for
|
||||||
|
* @param {string[]} props Properties to get modifiers for
|
||||||
|
* @returns {React.Component[]} Table-cell modifiers
|
||||||
|
*/
|
||||||
|
function _getModifiers(formats, module, props) {
|
||||||
|
return fromPairs(props.map((prop) => {
|
||||||
|
const { value, unit, beneficial } = module.getModifierFormatted(prop);
|
||||||
|
return [
|
||||||
|
prop,
|
||||||
|
<td className={beneficial === undefined ? '' : beneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>
|
||||||
|
{formats.round(value || 0)}{unit}
|
||||||
|
</td>
|
||||||
|
];
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a tooltip with details of a blueprint's specials
|
* Generate a tooltip with details of a blueprint's specials
|
||||||
@@ -13,28 +30,28 @@ import { entries, keys, uniq } from 'lodash';
|
|||||||
*/
|
*/
|
||||||
export function specialToolTip(language, m, specialName) {
|
export function specialToolTip(language, m, specialName) {
|
||||||
const { formats, translate } = language;
|
const { formats, translate } = language;
|
||||||
|
const features = keys(getExperimentalInfo(specialName).features);
|
||||||
|
const currents = _getModifiers(formats, m, features);
|
||||||
|
const thens = m.try(() => {
|
||||||
|
m.setExperimental(specialName);
|
||||||
|
return _getModifiers(formats, m, features);
|
||||||
|
});
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<table width='100%'>
|
<table width='100%'>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td>{translate('feature')}</td>
|
||||||
|
<td>{translate('current')}</td>
|
||||||
|
<td>{translate('then')}</td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{entries(getExperimentalInfo(specialName).features).map(
|
{features.map((prop) => {
|
||||||
([prop, feats]) => {
|
|
||||||
const { max, only } = feats;
|
|
||||||
if (only && !m.getItem().match(only)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { value, unit, beneficial } = m.getModifierFormatted(prop);
|
|
||||||
// If the product of value and min/max is positive, both values
|
|
||||||
// point into the same direction, i.e. positive/negative.
|
|
||||||
const specialBeneficial = (value * max) > 0 === beneficial;
|
|
||||||
|
|
||||||
return <tr key={prop + '_specialTT'}>
|
return <tr key={prop + '_specialTT'}>
|
||||||
<td style={{ textAlign: 'left' }}>{translate(prop)}</td>
|
<td style={{ textAlign: 'left' }}>{translate(prop)}</td>
|
||||||
<td> </td>
|
{currents[prop]}
|
||||||
<td className={specialBeneficial ? 'secondary' : 'warning'}
|
{thens[prop]}
|
||||||
style={{ textAlign: 'right' }}>{formats.round(max * 100)}{unit}</td>
|
|
||||||
<td> </td>
|
|
||||||
</tr>;
|
</tr>;
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
@@ -60,8 +77,18 @@ export function blueprintTooltip(language, m, previewBP, previewGrade) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bpFeatures = getBlueprintInfo(blueprint).features[grade];
|
const features = uniq(m.getModifiedProperties().concat(
|
||||||
const features = uniq(m.getModifiedProperties().concat(keys(bpFeatures)));
|
keys(getBlueprintInfo(blueprint).features[grade])
|
||||||
|
));
|
||||||
|
const mins = m.try(() => {
|
||||||
|
m.setBlueprint(blueprint, grade, 0);
|
||||||
|
return _getModifiers(formats, m, features);
|
||||||
|
});
|
||||||
|
const currents = _getModifiers(formats, m, features);
|
||||||
|
const maxs = m.try(() => {
|
||||||
|
m.setBlueprint(blueprint, grade, 1);
|
||||||
|
return _getModifiers(formats, m, features);
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -76,31 +103,11 @@ export function blueprintTooltip(language, m, previewBP, previewGrade) {
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{features.map((prop) => {
|
{features.map((prop) => {
|
||||||
const { min, max, only } = bpFeatures[prop] || {};
|
|
||||||
// Skip this property if it doesn't apply to this module
|
|
||||||
if (only && !m.getItem().match(only)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const { value, unit, beneficial } = m.getModifierFormatted(prop);
|
|
||||||
if (!bpFeatures[prop] && !value) {
|
|
||||||
// Can happen for exported synthetics
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// If the product of value and min/max is positive, both values
|
|
||||||
// point into the same direction, i.e. positive/negative.
|
|
||||||
const minBeneficial = (value * min) > 0 === beneficial;
|
|
||||||
const maxBeneficial = (value * max) > 0 === beneficial;
|
|
||||||
return (<tr key={prop}>
|
return (<tr key={prop}>
|
||||||
<td style={{ textAlign: 'left' }}>{translate(prop)}</td>
|
<td style={{ textAlign: 'left' }}>{translate(prop)}</td>
|
||||||
<td className={!min ? '' : minBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>
|
{mins[prop]}
|
||||||
{!isNaN(min) && formats.round(min * 100)}{!isNaN(min) && unit}
|
{currents[prop]}
|
||||||
</td>
|
{maxs[prop]}
|
||||||
<td className={!value ? '' : beneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>
|
|
||||||
{formats.round(value || 0)}{unit}
|
|
||||||
</td>
|
|
||||||
<td className={!max ? '' : maxBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>
|
|
||||||
{!isNaN(max) && formats.round(max * 100)}{!isNaN(max) && unit}
|
|
||||||
</td>
|
|
||||||
</tr>);
|
</tr>);
|
||||||
})}
|
})}
|
||||||
</tbody>
|
</tbody>
|
||||||
@@ -108,20 +115,3 @@ export function blueprintTooltip(language, m, previewBP, previewGrade) {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a blueprint with a given name and an optional module
|
|
||||||
* @param {string} name The name of the blueprint
|
|
||||||
* @param {Object} module The module for which to obtain this blueprint
|
|
||||||
* @returns {Object} The matching blueprint
|
|
||||||
*/
|
|
||||||
export function getBlueprint(name, module) {
|
|
||||||
// Start with a copy of the blueprint
|
|
||||||
const findMod = val => Object.keys(Modifications.blueprints).find(elem => elem.toString().toLowerCase().search(val.toString().toLowerCase().replace(/(OutfittingFieldType_|persecond)/igm, '')) >= 0);
|
|
||||||
const found = Modifications.blueprints[findMod(name)];
|
|
||||||
if (!found || !found.fdname) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
const blueprint = JSON.parse(JSON.stringify(found));
|
|
||||||
return blueprint;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,470 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Modifications, Modules, Ships } from 'coriolis-data/dist';
|
|
||||||
import Module from '../shipyard/Module';
|
|
||||||
import Ship from '../shipyard/Ship';
|
|
||||||
import { getBlueprint } from '../utils/BlueprintFunctions';
|
|
||||||
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
|
||||||
|
|
||||||
// mapping from fd's ship model names to coriolis'
|
|
||||||
export const SHIP_FD_NAME_TO_CORIOLIS_NAME = {
|
|
||||||
'Adder': 'adder',
|
|
||||||
'Anaconda': 'anaconda',
|
|
||||||
'Asp': 'asp',
|
|
||||||
'Asp_Scout': 'asp_scout',
|
|
||||||
'BelugaLiner': 'beluga',
|
|
||||||
'CobraMkIII': 'cobra_mk_iii',
|
|
||||||
'CobraMkIV': 'cobra_mk_iv',
|
|
||||||
'Cutter': 'imperial_cutter',
|
|
||||||
'DiamondBackXL': 'diamondback_explorer',
|
|
||||||
'DiamondBack': 'diamondback',
|
|
||||||
'Dolphin': 'dolphin',
|
|
||||||
'Eagle': 'eagle',
|
|
||||||
'Empire_Courier': 'imperial_courier',
|
|
||||||
'Empire_Eagle': 'imperial_eagle',
|
|
||||||
'Empire_Trader': 'imperial_clipper',
|
|
||||||
'Federation_Corvette': 'federal_corvette',
|
|
||||||
'Federation_Dropship': 'federal_dropship',
|
|
||||||
'Federation_Dropship_MkII': 'federal_assault_ship',
|
|
||||||
'Federation_Gunship': 'federal_gunship',
|
|
||||||
'FerDeLance': 'fer_de_lance',
|
|
||||||
'Hauler': 'hauler',
|
|
||||||
'Independant_Trader': 'keelback',
|
|
||||||
'Krait_MkII': 'krait_mkii',
|
|
||||||
'Mamba': 'mamba',
|
|
||||||
'Krait_Light': 'krait_phantom',
|
|
||||||
'Orca': 'orca',
|
|
||||||
'Python': 'python',
|
|
||||||
'SideWinder': 'sidewinder',
|
|
||||||
'Type6': 'type_6_transporter',
|
|
||||||
'Type7': 'type_7_transport',
|
|
||||||
'Type9': 'type_9_heavy',
|
|
||||||
'Type9_Military': 'type_10_defender',
|
|
||||||
'TypeX': 'alliance_chieftain',
|
|
||||||
'TypeX_2': 'alliance_crusader',
|
|
||||||
'TypeX_3': 'alliance_challenger',
|
|
||||||
'Viper': 'viper',
|
|
||||||
'Viper_MkIV': 'viper_mk_iv',
|
|
||||||
'Vulture': 'vulture'
|
|
||||||
};
|
|
||||||
|
|
||||||
// Mapping from hardpoint class to name in companion API
|
|
||||||
export const HARDPOINT_NUM_TO_CLASS = {
|
|
||||||
0: 'Tiny',
|
|
||||||
1: 'Small',
|
|
||||||
2: 'Medium',
|
|
||||||
3: 'Large',
|
|
||||||
4: 'Huge'
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Obtain a module given its ED ID
|
|
||||||
* @param {Integer} edId the Elite ID of the module
|
|
||||||
* @return {Module} the module
|
|
||||||
*/
|
|
||||||
function _moduleFromEdId(edId) {
|
|
||||||
if (!edId) return null;
|
|
||||||
|
|
||||||
// Check standard modules
|
|
||||||
for (const grp in Modules.standard) {
|
|
||||||
if (Modules.standard.hasOwnProperty(grp)) {
|
|
||||||
for (const i in Modules.standard[grp]) {
|
|
||||||
if (Modules.standard[grp][i].edID === edId) {
|
|
||||||
// Found it
|
|
||||||
return new Module({ template: Modules.standard[grp][i] });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check hardpoint modules
|
|
||||||
for (const grp in Modules.hardpoints) {
|
|
||||||
if (Modules.hardpoints.hasOwnProperty(grp)) {
|
|
||||||
for (const i in Modules.hardpoints[grp]) {
|
|
||||||
if (Modules.hardpoints[grp][i].edID === edId) {
|
|
||||||
// Found it
|
|
||||||
return new Module({ template: Modules.hardpoints[grp][i] });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check internal modules
|
|
||||||
for (const grp in Modules.internal) {
|
|
||||||
if (Modules.internal.hasOwnProperty(grp)) {
|
|
||||||
for (const i in Modules.internal[grp]) {
|
|
||||||
if (Modules.internal[grp][i].edID === edId) {
|
|
||||||
// Found it
|
|
||||||
return new Module({ template: Modules.internal[grp][i] });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not found
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Obtain the model of a ship given its ED name
|
|
||||||
* @param {string} edName the Elite name of the ship
|
|
||||||
* @return {string} the Coriolis model of the ship
|
|
||||||
*/
|
|
||||||
function _shipModelFromEDName(edName) {
|
|
||||||
return SHIP_FD_NAME_TO_CORIOLIS_NAME[Object.keys(SHIP_FD_NAME_TO_CORIOLIS_NAME).find(elem => elem.toLowerCase() === edName.toLowerCase())];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Obtain a ship's model from the companion API JSON
|
|
||||||
* @param {object} json the companion API JSON
|
|
||||||
* @return {string} the Coriolis model of the ship
|
|
||||||
*/
|
|
||||||
export function shipModelFromJson(json) {
|
|
||||||
return _shipModelFromEDName(json.name || json.Ship);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build a ship from the companion API JSON
|
|
||||||
* @param {object} json the companion API JSON
|
|
||||||
* @return {Ship} the built ship
|
|
||||||
*/
|
|
||||||
export function shipFromJson(json) {
|
|
||||||
// Start off building a basic ship
|
|
||||||
const shipModel = shipModelFromJson(json);
|
|
||||||
if (!shipModel) {
|
|
||||||
throw 'No such ship found: "' + json.name + '"';
|
|
||||||
}
|
|
||||||
const shipTemplate = Ships[shipModel];
|
|
||||||
|
|
||||||
let ship = new Ship(shipModel, shipTemplate.properties, shipTemplate.slots);
|
|
||||||
ship.buildWith(null);
|
|
||||||
|
|
||||||
// Set the cargo hatch
|
|
||||||
if (json.modules.CargoHatch) {
|
|
||||||
ship.cargoHatch.enabled = json.modules.CargoHatch.module.on == true;
|
|
||||||
ship.cargoHatch.priority = json.modules.CargoHatch.module.priority;
|
|
||||||
} else {
|
|
||||||
// We don't have any information on it so guess it's priority 5 and disabled
|
|
||||||
ship.cargoHatch.enabled = false;
|
|
||||||
ship.cargoHatch.priority = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
let rootModule;
|
|
||||||
|
|
||||||
// Add the bulkheads
|
|
||||||
const armourJson = json.modules.Armour.module;
|
|
||||||
if (armourJson.name.toLowerCase().endsWith('_armour_grade1')) {
|
|
||||||
ship.useBulkhead(0, true);
|
|
||||||
} else if (armourJson.name.toLowerCase().endsWith('_armour_grade2')) {
|
|
||||||
ship.useBulkhead(1, true);
|
|
||||||
} else if (armourJson.name.toLowerCase().endsWith('_armour_grade3')) {
|
|
||||||
ship.useBulkhead(2, true);
|
|
||||||
} else if (armourJson.name.toLowerCase().endsWith('_armour_mirrored')) {
|
|
||||||
ship.useBulkhead(3, true);
|
|
||||||
} else if (armourJson.name.toLowerCase().endsWith('_armour_reactive')) {
|
|
||||||
ship.useBulkhead(4, true);
|
|
||||||
} else {
|
|
||||||
throw 'Unknown bulkheads "' + armourJson.name + '"';
|
|
||||||
}
|
|
||||||
ship.bulkheads.enabled = true;
|
|
||||||
rootModule = json.modules.Armour;
|
|
||||||
if (rootModule.WorkInProgress_modifications) _addModifications(ship.bulkheads.m, rootModule.WorkInProgress_modifications, rootModule.engineer.recipeName, rootModule.engineer.recipeLevel);
|
|
||||||
|
|
||||||
// Add the standard modules
|
|
||||||
// Power plant
|
|
||||||
const powerplantJson = json.modules.PowerPlant.module;
|
|
||||||
const powerplant = _moduleFromEdId(powerplantJson.id);
|
|
||||||
rootModule = json.modules.PowerPlant;
|
|
||||||
if (rootModule.WorkInProgress_modifications) _addModifications(powerplant, rootModule.WorkInProgress_modifications, rootModule.engineer.recipeName, rootModule.engineer.recipeLevel);
|
|
||||||
ship.use(ship.standard[0], powerplant, true);
|
|
||||||
ship.standard[0].enabled = powerplantJson.on === true;
|
|
||||||
ship.standard[0].priority = powerplantJson.priority;
|
|
||||||
|
|
||||||
// Thrusters
|
|
||||||
const thrustersJson = json.modules.MainEngines.module;
|
|
||||||
const thrusters = _moduleFromEdId(thrustersJson.id);
|
|
||||||
rootModule = json.modules.MainEngines;
|
|
||||||
if (rootModule.WorkInProgress_modifications) _addModifications(thrusters, rootModule.WorkInProgress_modifications, rootModule.engineer.recipeName, rootModule.engineer.recipeLevel);
|
|
||||||
ship.use(ship.standard[1], thrusters, true);
|
|
||||||
ship.standard[1].enabled = thrustersJson.on === true;
|
|
||||||
ship.standard[1].priority = thrustersJson.priority;
|
|
||||||
|
|
||||||
// FSD
|
|
||||||
const frameshiftdriveJson = json.modules.FrameShiftDrive.module;
|
|
||||||
const frameshiftdrive = _moduleFromEdId(frameshiftdriveJson.id);
|
|
||||||
rootModule = json.modules.FrameShiftDrive;
|
|
||||||
if (rootModule.WorkInProgress_modifications) _addModifications(frameshiftdrive, rootModule.WorkInProgress_modifications, rootModule.engineer.recipeName, rootModule.engineer.recipeLevel);
|
|
||||||
ship.use(ship.standard[2], frameshiftdrive, true);
|
|
||||||
ship.standard[2].enabled = frameshiftdriveJson.on === true;
|
|
||||||
ship.standard[2].priority = frameshiftdriveJson.priority;
|
|
||||||
|
|
||||||
// Life support
|
|
||||||
const lifesupportJson = json.modules.LifeSupport.module;
|
|
||||||
const lifesupport = _moduleFromEdId(lifesupportJson.id);
|
|
||||||
rootModule = json.modules.LifeSupport;
|
|
||||||
if (rootModule.WorkInProgress_modifications) _addModifications(lifesupport, rootModule.WorkInProgress_modifications, rootModule.engineer.recipeName, rootModule.engineer.recipeLevel);
|
|
||||||
ship.use(ship.standard[3], lifesupport, true);
|
|
||||||
ship.standard[3].enabled = lifesupportJson.on === true;
|
|
||||||
ship.standard[3].priority = lifesupportJson.priority;
|
|
||||||
|
|
||||||
// Power distributor
|
|
||||||
const powerdistributorJson = json.modules.PowerDistributor.module;
|
|
||||||
const powerdistributor = _moduleFromEdId(powerdistributorJson.id);
|
|
||||||
rootModule = json.modules.PowerDistributor;
|
|
||||||
if (rootModule.WorkInProgress_modifications) _addModifications(powerdistributor, rootModule.WorkInProgress_modifications, rootModule.engineer.recipeName, rootModule.engineer.recipeLevel);
|
|
||||||
ship.use(ship.standard[4], powerdistributor, true);
|
|
||||||
ship.standard[4].enabled = powerdistributorJson.on === true;
|
|
||||||
ship.standard[4].priority = powerdistributorJson.priority;
|
|
||||||
|
|
||||||
// Sensors
|
|
||||||
const sensorsJson = json.modules.Radar.module;
|
|
||||||
const sensors = _moduleFromEdId(sensorsJson.id);
|
|
||||||
rootModule = json.modules.Radar;
|
|
||||||
if (rootModule.WorkInProgress_modifications) _addModifications(sensors, rootModule.WorkInProgress_modifications, rootModule.engineer.recipeName, rootModule.engineer.recipeLevel);
|
|
||||||
ship.use(ship.standard[5], sensors, true);
|
|
||||||
ship.standard[5].enabled = sensorsJson.on === true;
|
|
||||||
ship.standard[5].priority = sensorsJson.priority;
|
|
||||||
|
|
||||||
// Fuel tank
|
|
||||||
const fueltankJson = json.modules.FuelTank.module;
|
|
||||||
const fueltank = _moduleFromEdId(fueltankJson.id);
|
|
||||||
ship.use(ship.standard[6], fueltank, true);
|
|
||||||
ship.standard[6].enabled = true;
|
|
||||||
ship.standard[6].priority = 0;
|
|
||||||
|
|
||||||
// Add hardpoints
|
|
||||||
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[hardpointName];
|
|
||||||
if (!hardpointSlot) {
|
|
||||||
// This can happen with old imports that don't contain new hardpoints
|
|
||||||
} else if (!hardpointSlot.module) {
|
|
||||||
// No module
|
|
||||||
} else {
|
|
||||||
const hardpointJson = hardpointSlot.module;
|
|
||||||
const hardpoint = _moduleFromEdId(hardpointJson.id);
|
|
||||||
rootModule = hardpointSlot;
|
|
||||||
if (rootModule.WorkInProgress_modifications) _addModifications(hardpoint, rootModule.WorkInProgress_modifications, rootModule.engineer.recipeName, rootModule.engineer.recipeLevel, rootModule.specialModifications);
|
|
||||||
ship.use(ship.hardpoints[hardpointArrayNum], hardpoint, true);
|
|
||||||
ship.hardpoints[hardpointArrayNum].enabled = hardpointJson.on === true;
|
|
||||||
ship.hardpoints[hardpointArrayNum].priority = hardpointJson.priority;
|
|
||||||
}
|
|
||||||
hardpointArrayNum++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add internal compartments
|
|
||||||
let internalSlotNum = 1;
|
|
||||||
let militarySlotNum = 1;
|
|
||||||
for (let i in shipTemplate.slots.internal) {
|
|
||||||
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[internalName];
|
|
||||||
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' : '') + internalSlotNum + '_Size' + slotsize;
|
|
||||||
if (json.modules[internalName]) {
|
|
||||||
internalSlot = json.modules[internalName];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
internalSlotNum++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!internalSlot) {
|
|
||||||
// This can happen with old imports that don't contain new slots
|
|
||||||
} else if (!internalSlot.module) {
|
|
||||||
// No module
|
|
||||||
} else {
|
|
||||||
const internalJson = internalSlot.module;
|
|
||||||
const internal = _moduleFromEdId(internalJson.id);
|
|
||||||
rootModule = internalSlot;
|
|
||||||
if (rootModule.WorkInProgress_modifications) _addModifications(internal, rootModule.WorkInProgress_modifications, rootModule.engineer.recipeName, rootModule.engineer.recipeLevel);
|
|
||||||
ship.use(ship.internal[i], internal, true);
|
|
||||||
ship.internal[i].enabled = internalJson.on === true;
|
|
||||||
ship.internal[i].priority = internalJson.priority;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now update the ship's codes before returning it
|
|
||||||
return ship.updatePowerPrioritesString().updatePowerEnabledString().updateModificationsString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add the modifications for a module
|
|
||||||
* @param {Module} module the module
|
|
||||||
* @param {Object} modifiers the modifiers
|
|
||||||
* @param {Object} blueprint the blueprint of the modification
|
|
||||||
* @param {Object} grade the grade of the modification
|
|
||||||
* @param {Object} specialModifications special modification
|
|
||||||
*/
|
|
||||||
function _addModifications(module, modifiers, blueprint, grade, specialModifications) {
|
|
||||||
if (!modifiers) return;
|
|
||||||
let special;
|
|
||||||
if (specialModifications) {
|
|
||||||
special = Modifications.specials[Object.keys(specialModifications)[0]];
|
|
||||||
}
|
|
||||||
for (const i in modifiers) {
|
|
||||||
// Some special modifications
|
|
||||||
if (modifiers[i].name === 'mod_weapon_clip_size_override') {
|
|
||||||
// This is a numeric addition to the clip size, but we need to work it out in terms of being a percentage so
|
|
||||||
// that it works the same as other modifications
|
|
||||||
const origClip = module.clip || 1;
|
|
||||||
module.setModValue('clip', ((modifiers[i].value - origClip) / origClip) * 10000);
|
|
||||||
} else if (modifiers[i].name === 'mod_weapon_burst_size') {
|
|
||||||
// This is an absolute number that acts as an override
|
|
||||||
module.setModValue('burst', modifiers[i].value * 100);
|
|
||||||
} else if (modifiers[i].name === 'mod_weapon_burst_rof') {
|
|
||||||
// This is an absolute number that acts as an override
|
|
||||||
module.setModValue('burstrof', modifiers[i].value * 100);
|
|
||||||
} else if (modifiers[i].name === 'mod_weapon_falloffrange_from_range') {
|
|
||||||
// Obtain the falloff value directly from the range
|
|
||||||
module.setModValue('fallofffromrange', 1);
|
|
||||||
} else if (modifiers[i].name && modifiers[i].name.startsWith('special_')) {
|
|
||||||
// We don't add special effects directly, but keep a note of them so they can be added when fetching values
|
|
||||||
special = Modifications.specials[modifiers[i].name];
|
|
||||||
} else {
|
|
||||||
// Look up the modifiers to find what we need to do
|
|
||||||
const modifierActions = Modifications.modifierActions[i];
|
|
||||||
let value;
|
|
||||||
if (i === 'OutfittingFieldType_DefenceModifierShieldMultiplier') {
|
|
||||||
value = modifiers[i].value - 1;
|
|
||||||
} else if (i === 'OutfittingFieldType_DefenceModifierHealthMultiplier' && blueprint.startsWith('Armour_')) {
|
|
||||||
value = (modifiers[i].value - module.hullboost) / module.hullboost;
|
|
||||||
} else if (i === 'OutfittingFieldType_DefenceModifierHealthMultiplier') {
|
|
||||||
value = modifiers[i].value / module.hullboost;
|
|
||||||
} else if (i === 'OutfittingFieldType_RateOfFire') {
|
|
||||||
value = (1 / Math.abs(modifiers[i].value));
|
|
||||||
} else {
|
|
||||||
value = modifiers[i].value - 1;
|
|
||||||
}
|
|
||||||
// Carry out the required changes
|
|
||||||
for (const action in modifierActions) {
|
|
||||||
if (isNaN(modifierActions[action])) {
|
|
||||||
module.setModValue(action, modifierActions[action]);
|
|
||||||
} else {
|
|
||||||
const actionValue = modifierActions[action] * value;
|
|
||||||
let mod = module.getModValue(action) / 10000;
|
|
||||||
if (!mod) {
|
|
||||||
mod = 0;
|
|
||||||
}
|
|
||||||
module.setModValue(action, ((1 + mod) * (1 + actionValue) - 1) * 10000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the blueprint definition, grade and special
|
|
||||||
if (blueprint) {
|
|
||||||
module.blueprint = getBlueprint(blueprint, module);
|
|
||||||
if (grade) {
|
|
||||||
module.blueprint.grade = Number(grade);
|
|
||||||
}
|
|
||||||
if (special) {
|
|
||||||
module.blueprint.special = special;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Need to fix up a few items
|
|
||||||
|
|
||||||
// Shield boosters are treated internally as straight modifiers, so rather than (for example)
|
|
||||||
// being a 4% boost they are a 104% multiplier. Unfortunately this means that our % modification
|
|
||||||
// is incorrect so we fix it
|
|
||||||
if (module.grp === 'sb' && module.getModValue('shieldboost')) {
|
|
||||||
const alteredBoost = (1 + module.shieldboost) * (module.getModValue('shieldboost') / 10000);
|
|
||||||
module.setModValue('shieldboost', alteredBoost * 10000 / module.shieldboost);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shield booster resistance is actually a damage modifier, so needs to be inverted.
|
|
||||||
if (module.grp === 'sb') {
|
|
||||||
if (module.getModValue('explres')) {
|
|
||||||
module.setModValue('explres', ((module.getModValue('explres') / 10000) * -1) * 10000);
|
|
||||||
}
|
|
||||||
if (module.getModValue('kinres')) {
|
|
||||||
module.setModValue('kinres', ((module.getModValue('kinres') / 10000) * -1) * 10000);
|
|
||||||
}
|
|
||||||
if (module.getModValue('thermres')) {
|
|
||||||
module.setModValue('thermres', ((module.getModValue('thermres') / 10000) * -1) * 10000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shield generator resistance is actually a damage modifier, so needs to be inverted.
|
|
||||||
// In addition, the modification is based off the inherent resistance of the module
|
|
||||||
if (ModuleUtils.isShieldGenerator(module.grp)) {
|
|
||||||
if (module.getModValue('explres')) {
|
|
||||||
module.setModValue('explres', ((1 - (1 - module.explres) * (1 + module.getModValue('explres') / 10000)) - module.explres) * 10000);
|
|
||||||
}
|
|
||||||
if (module.getModValue('kinres')) {
|
|
||||||
module.setModValue('kinres', ((1 - (1 - module.kinres) * (1 + module.getModValue('kinres') / 10000)) - module.kinres) * 10000);
|
|
||||||
}
|
|
||||||
if (module.getModValue('thermres')) {
|
|
||||||
module.setModValue('thermres', ((1 - (1 - module.thermres) * (1 + module.getModValue('thermres') / 10000)) - module.thermres) * 10000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hull reinforcement package resistance is actually a damage modifier, so needs to be inverted.
|
|
||||||
// In addition, the modification is based off the inherent resistance of the module
|
|
||||||
if (module.grp === 'hr') {
|
|
||||||
if (module.getModValue('explres')) {
|
|
||||||
module.setModValue('explres', ((1 - (1 - module.explres) * (1 + module.getModValue('explres') / 10000)) - module.explres) * 10000);
|
|
||||||
}
|
|
||||||
if (module.getModValue('kinres')) {
|
|
||||||
module.setModValue('kinres', ((1 - (1 - module.kinres) * (1 + module.getModValue('kinres') / 10000)) - module.kinres) * 10000);
|
|
||||||
}
|
|
||||||
if (module.getModValue('thermres')) {
|
|
||||||
module.setModValue('thermres', ((1 - (1 - module.thermres) * (1 + module.getModValue('thermres') / 10000)) - module.thermres) * 10000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bulkhead resistance is actually a damage modifier, so needs to be inverted.
|
|
||||||
// In addition, the modification is based off the inherent resistance of the module
|
|
||||||
if (module.grp == 'bh') {
|
|
||||||
if (module.getModValue('explres')) {
|
|
||||||
module.setModValue('explres', ((1 - (1 - module.explres) * (1 + module.getModValue('explres') / 10000)) - module.explres) * 10000);
|
|
||||||
}
|
|
||||||
if (module.getModValue('kinres')) {
|
|
||||||
module.setModValue('kinres', ((1 - (1 - module.kinres) * (1 + module.getModValue('kinres') / 10000)) - module.kinres) * 10000);
|
|
||||||
}
|
|
||||||
if (module.getModValue('thermres')) {
|
|
||||||
module.setModValue('thermres', ((1 - (1 - module.thermres) * (1 + module.getModValue('thermres') / 10000)) - module.thermres) * 10000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bulkhead boost is based off the inherent boost of the module
|
|
||||||
if (module.grp == 'bh') {
|
|
||||||
const alteredBoost = (1 + module.hullboost) * (1 + module.getModValue('hullboost') / 10000) - 1;
|
|
||||||
module.setModValue('hullboost', (alteredBoost / module.hullboost - 1) * 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Jitter is an absolute number, so we need to divide it by 100
|
|
||||||
if (module.getModValue('jitter')) {
|
|
||||||
module.setModValue('jitter', module.getModValue('jitter') / 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clip size is rounded up so that the result is a whole number
|
|
||||||
if (module.getModValue('clip')) {
|
|
||||||
const individual = 1 / (module.clip || 1);
|
|
||||||
module.setModValue('clip', Math.ceil((module.getModValue('clip') / 10000) / individual) * individual * 10000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,303 +0,0 @@
|
|||||||
import Ship from '../shipyard/Ship';
|
|
||||||
import { HARDPOINT_NUM_TO_CLASS, shipModelFromJson } from './CompanionApiUtils';
|
|
||||||
import { Ships } from 'coriolis-data/dist';
|
|
||||||
import Module from '../shipyard/Module';
|
|
||||||
import { Modules } from 'coriolis-data/dist';
|
|
||||||
import { Modifications } from 'coriolis-data/dist';
|
|
||||||
import { getBlueprint } from './BlueprintFunctions';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Obtain a module given its FD Name
|
|
||||||
* @param {string} fdname the FD Name of the module
|
|
||||||
* @return {Module} the module
|
|
||||||
*/
|
|
||||||
function _moduleFromFdName(fdname) {
|
|
||||||
if (!fdname) return null;
|
|
||||||
fdname = fdname.toLowerCase();
|
|
||||||
// Check standard modules
|
|
||||||
for (const grp in Modules.standard) {
|
|
||||||
if (Modules.standard.hasOwnProperty(grp)) {
|
|
||||||
for (const i in Modules.standard[grp]) {
|
|
||||||
if (Modules.standard[grp][i].symbol && Modules.standard[grp][i].symbol.toLowerCase() === fdname) {
|
|
||||||
// Found it
|
|
||||||
return new Module({ template: Modules.standard[grp][i] });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check hardpoint modules
|
|
||||||
for (const grp in Modules.hardpoints) {
|
|
||||||
if (Modules.hardpoints.hasOwnProperty(grp)) {
|
|
||||||
for (const i in Modules.hardpoints[grp]) {
|
|
||||||
if (Modules.hardpoints[grp][i].symbol && Modules.hardpoints[grp][i].symbol.toLowerCase() === fdname) {
|
|
||||||
// Found it
|
|
||||||
return new Module({ template: Modules.hardpoints[grp][i] });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check internal modules
|
|
||||||
for (const grp in Modules.internal) {
|
|
||||||
if (Modules.internal.hasOwnProperty(grp)) {
|
|
||||||
for (const i in Modules.internal[grp]) {
|
|
||||||
if (Modules.internal[grp][i].symbol && Modules.internal[grp][i].symbol.toLowerCase() === fdname) {
|
|
||||||
// Found it
|
|
||||||
return new Module({ template: Modules.internal[grp][i] });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not found
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build a ship from the journal Loadout event JSON
|
|
||||||
* @param {object} json the Loadout event JSON
|
|
||||||
* @return {Ship} the built ship
|
|
||||||
*/
|
|
||||||
export function shipFromLoadoutJSON(json) {
|
|
||||||
// Start off building a basic ship
|
|
||||||
const shipModel = shipModelFromJson(json);
|
|
||||||
if (!shipModel) {
|
|
||||||
throw 'No such ship found: "' + json.Ship + '"';
|
|
||||||
}
|
|
||||||
const shipTemplate = Ships[shipModel];
|
|
||||||
|
|
||||||
let ship = new Ship(shipModel, shipTemplate.properties, shipTemplate.slots);
|
|
||||||
ship.buildWith(null);
|
|
||||||
// Initial Ship building, don't do engineering yet.
|
|
||||||
let modsToAdd = [];
|
|
||||||
|
|
||||||
for (const module of json.Modules) {
|
|
||||||
switch (module.Slot.toLowerCase()) {
|
|
||||||
// Cargo Hatch.
|
|
||||||
case 'cargohatch':
|
|
||||||
ship.cargoHatch.enabled = module.On;
|
|
||||||
ship.cargoHatch.priority = module.Priority;
|
|
||||||
break;
|
|
||||||
// Add the bulkheads
|
|
||||||
case 'armour':
|
|
||||||
if (module.Item.toLowerCase().endsWith('_armour_grade1')) {
|
|
||||||
ship.useBulkhead(0, true);
|
|
||||||
} else if (module.Item.toLowerCase().endsWith('_armour_grade2')) {
|
|
||||||
ship.useBulkhead(1, true);
|
|
||||||
} else if (module.Item.toLowerCase().endsWith('_armour_grade3')) {
|
|
||||||
ship.useBulkhead(2, true);
|
|
||||||
} else if (module.Item.toLowerCase().endsWith('_armour_mirrored')) {
|
|
||||||
ship.useBulkhead(3, true);
|
|
||||||
} else if (module.Item.toLowerCase().endsWith('_armour_reactive')) {
|
|
||||||
ship.useBulkhead(4, true);
|
|
||||||
} else {
|
|
||||||
throw 'Unknown bulkheads "' + module.Item + '"';
|
|
||||||
}
|
|
||||||
ship.bulkheads.enabled = true;
|
|
||||||
if (module.Engineering) _addModifications(ship.bulkheads.m, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect);
|
|
||||||
break;
|
|
||||||
case 'powerplant':
|
|
||||||
const powerplant = _moduleFromFdName(module.Item);
|
|
||||||
ship.use(ship.standard[0], powerplant, true);
|
|
||||||
ship.standard[0].enabled = module.On;
|
|
||||||
ship.standard[0].priority = module.Priority;
|
|
||||||
if (module.Engineering) _addModifications(powerplant, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect);
|
|
||||||
break;
|
|
||||||
case 'mainengines':
|
|
||||||
const thrusters = _moduleFromFdName(module.Item);
|
|
||||||
ship.use(ship.standard[1], thrusters, true);
|
|
||||||
ship.standard[1].enabled = module.On;
|
|
||||||
ship.standard[1].priority = module.Priority;
|
|
||||||
if (module.Engineering) _addModifications(thrusters, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect);
|
|
||||||
break;
|
|
||||||
case 'frameshiftdrive':
|
|
||||||
const frameshiftdrive = _moduleFromFdName(module.Item);
|
|
||||||
ship.use(ship.standard[2], frameshiftdrive, true);
|
|
||||||
ship.standard[2].enabled = module.On;
|
|
||||||
ship.standard[2].priority = module.Priority;
|
|
||||||
if (module.Engineering) _addModifications(frameshiftdrive, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect);
|
|
||||||
break;
|
|
||||||
case 'lifesupport':
|
|
||||||
const lifesupport = _moduleFromFdName(module.Item);
|
|
||||||
ship.use(ship.standard[3], lifesupport, true);
|
|
||||||
ship.standard[3].enabled = module.On === true;
|
|
||||||
ship.standard[3].priority = module.Priority;
|
|
||||||
if (module.Engineering) _addModifications(lifesupport, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect);
|
|
||||||
break;
|
|
||||||
case 'powerdistributor':
|
|
||||||
const powerdistributor = _moduleFromFdName(module.Item);
|
|
||||||
ship.use(ship.standard[4], powerdistributor, true);
|
|
||||||
ship.standard[4].enabled = module.On;
|
|
||||||
ship.standard[4].priority = module.Priority;
|
|
||||||
if (module.Engineering) _addModifications(powerdistributor, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect);
|
|
||||||
break;
|
|
||||||
case 'radar':
|
|
||||||
const sensors = _moduleFromFdName(module.Item);
|
|
||||||
ship.use(ship.standard[5], sensors, true);
|
|
||||||
ship.standard[5].enabled = module.On;
|
|
||||||
ship.standard[5].priority = module.Priority;
|
|
||||||
if (module.Engineering) _addModifications(sensors, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect);
|
|
||||||
break;
|
|
||||||
case 'fueltank':
|
|
||||||
const fueltank = _moduleFromFdName(module.Item);
|
|
||||||
ship.use(ship.standard[6], fueltank, true);
|
|
||||||
ship.standard[6].enabled = true;
|
|
||||||
ship.standard[6].priority = 0;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
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++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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());
|
|
||||||
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) {
|
|
||||||
if (i.json.Engineering) {
|
|
||||||
_addModifications(i.coriolisMod, i.json.Engineering.Modifiers, i.json.Engineering.BlueprintName, i.json.Engineering.Level, i.json.Engineering.ExperimentalEffect);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// We don't have any information on it so guess it's priority 5 and disabled
|
|
||||||
if (!ship.cargoHatch) {
|
|
||||||
ship.cargoHatch.enabled = false;
|
|
||||||
ship.cargoHatch.priority = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now update the ship's codes before returning it
|
|
||||||
return ship.updatePowerPrioritesString().updatePowerEnabledString().updateModificationsString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add the modifications for a module
|
|
||||||
* @param {Module} module the module
|
|
||||||
* @param {Object} modifiers the modifiers
|
|
||||||
* @param {Object} blueprint the blueprint of the modification
|
|
||||||
* @param {Object} grade the grade of the modification
|
|
||||||
* @param {Object} specialModifications special modification
|
|
||||||
*/
|
|
||||||
function _addModifications(module, modifiers, blueprint, grade, specialModifications) {
|
|
||||||
if (!modifiers) return;
|
|
||||||
let special;
|
|
||||||
if (specialModifications) {
|
|
||||||
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
|
|
||||||
if (blueprint) {
|
|
||||||
module.blueprint = getBlueprint(blueprint, module);
|
|
||||||
if (grade) {
|
|
||||||
module.blueprint.grade = Number(grade);
|
|
||||||
}
|
|
||||||
if (special) {
|
|
||||||
module.blueprint.special = special;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const i in modifiers) {
|
|
||||||
// Some special modifications
|
|
||||||
// Look up the modifiers to find what we need to do
|
|
||||||
const findMod = val => Object.keys(Modifications.modifierActions).find(elem => elem.toString().toLowerCase().replace(/(outfittingfieldtype_|persecond)/igm, '') === val.toString().toLowerCase().replace(/(outfittingfieldtype_|persecond)/igm, ''));
|
|
||||||
const modifierActions = Modifications.modifierActions[findMod(modifiers[i].Label)];
|
|
||||||
// TODO: Figure out how to scale this value.
|
|
||||||
if (!!modifiers[i].LessIsGood) {
|
|
||||||
|
|
||||||
}
|
|
||||||
let value = (modifiers[i].Value / modifiers[i].OriginalValue * 100 - 100) * 100;
|
|
||||||
if (value === Infinity) {
|
|
||||||
value = modifiers[i].Value * 100;
|
|
||||||
}
|
|
||||||
if (modifiers[i].Label.search('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);
|
|
||||||
}
|
|
||||||
if (modifiers[i].Label.search('ShieldMultiplier') >= 0 || modifiers[i].Label.search('DefenceModifierHealthMultiplier') >= 0) {
|
|
||||||
value = ((100 + modifiers[i].Value) / (100 + modifiers[i].OriginalValue) * 100 - 100) * 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Carry out the required changes
|
|
||||||
for (const action in modifierActions) {
|
|
||||||
if (isNaN(modifierActions[action])) {
|
|
||||||
module.setModValue(action, modifierActions[action]);
|
|
||||||
} else {
|
|
||||||
module.setModValue(action, value, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -20,60 +20,6 @@ export default function shorternUrl(url, success, error) {
|
|||||||
orbisShorten(url, success, error);
|
orbisShorten(url, success, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
const SHORTEN_API_GOOGLE = 'https://www.googleapis.com/urlshortener/v1/url?key=';
|
|
||||||
/**
|
|
||||||
* Shorten a URL using Google's URL shortener API
|
|
||||||
* @param {string} url The URL to shorten
|
|
||||||
* @param {function} success Success callback
|
|
||||||
* @param {function} error Failure/Error callback
|
|
||||||
*/
|
|
||||||
function shortenUrlGoogle(url, success, error) {
|
|
||||||
if (window.navigator.onLine) {
|
|
||||||
try {
|
|
||||||
request.post(SHORTEN_API_GOOGLE + window.CORIOLIS_GAPI_KEY)
|
|
||||||
.send({ longUrl: url })
|
|
||||||
.end(function(err, response) {
|
|
||||||
if (err) {
|
|
||||||
error(response.statusText == 'OK' ? 'Bad Request' : response.statusText);
|
|
||||||
} else {
|
|
||||||
success(response.body.id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
error(e.message ? e.message : e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
error('Not Online');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const SHORTEN_API_EDDP = 'https://eddp.co/u';
|
|
||||||
/**
|
|
||||||
* Shorten a URL using EDDP's URL shortener API
|
|
||||||
* @param {string} url The URL to shorten
|
|
||||||
* @param {function} success Success callback
|
|
||||||
* @param {function} error Failure/Error callback
|
|
||||||
*/
|
|
||||||
function shortenUrlEddp(url, success, error) {
|
|
||||||
if (window.navigator.onLine) {
|
|
||||||
try {
|
|
||||||
request.post(SHORTEN_API_EDDP)
|
|
||||||
.send(url)
|
|
||||||
.end(function(err, response) {
|
|
||||||
if (err) {
|
|
||||||
error('Bad Request');
|
|
||||||
} else {
|
|
||||||
success(response.header['location']);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
error(e.message ? e.message : e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
error('Not Online');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const SHORTEN_API_ORBIS = 'https://s.orbis.zone/api.php';
|
const SHORTEN_API_ORBIS = 'https://s.orbis.zone/api.php';
|
||||||
/**
|
/**
|
||||||
* Shorten a URL using Orbis's URL shortener API
|
* Shorten a URL using Orbis's URL shortener API
|
||||||
@@ -105,7 +51,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
|
* Upload to Orbis
|
||||||
* @param {object} ship The URL to shorten
|
* @param {object} ship The URL to shorten
|
||||||
|
|||||||
@@ -1,252 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
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
|
|
||||||
* @param {Object} ship Ship object
|
|
||||||
* @param {Object} slot Slot object
|
|
||||||
* @param {String} group Module group/type abbrivation/code
|
|
||||||
* @param {Integer} clazz [Optional] Module Class/Size
|
|
||||||
* @return {Boolean} True if the slot can mount the module
|
|
||||||
*/
|
|
||||||
export function canMount(ship, slot, group, clazz) {
|
|
||||||
if (slot &&
|
|
||||||
(!slot.eligible || slot.eligible[group]) &&
|
|
||||||
(group != 'pcq' || (ship.luxuryCabins && ship.luxuryCabins === true)) &&
|
|
||||||
(group != 'fh' || (ship.fighterHangars && ship.fighterHangars === true)) &&
|
|
||||||
(clazz === undefined || slot.maxClass >= clazz)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the translate name for the module mounted in the specified
|
|
||||||
* slot.
|
|
||||||
* @param {function} translate Translation function
|
|
||||||
* @param {object} slot Slot object
|
|
||||||
* @return {string} The translated name
|
|
||||||
*/
|
|
||||||
export function slotName(translate, slot) {
|
|
||||||
return slot.m ? translate(slot.m.name || slot.m.grp) : '';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Slot name comparator
|
|
||||||
* @param {Function} translate Translate function
|
|
||||||
* @param {Object} a Slot object
|
|
||||||
* @param {Object} b Slot object
|
|
||||||
* @return {Number} 1, 0, -1
|
|
||||||
*/
|
|
||||||
export function nameComparator(translate, a, b) {
|
|
||||||
return translate(a.name || a.grp).localeCompare(translate(b.name || b.grp));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates an internationalization friendly slot comparator that will
|
|
||||||
* sort by specified property (if provided) then by name/group, class, rating
|
|
||||||
* @param {function} translate Tranlation function
|
|
||||||
* @param {function} propComparator Optional property comparator
|
|
||||||
* @param {boolean} desc Use descending order
|
|
||||||
* @return {function} Comparator function for slot names
|
|
||||||
*/
|
|
||||||
export function slotComparator(translate, propComparator, desc) {
|
|
||||||
return (a, b) => {
|
|
||||||
// retain descending order when sorting sorting by name/group/class/rating
|
|
||||||
let am = a.m; // Slot A's mounted module
|
|
||||||
let bm = b.m; // Slot B's mounted module
|
|
||||||
|
|
||||||
if (!desc) { // Flip A and B if ascending order
|
|
||||||
let t = a;
|
|
||||||
a = b;
|
|
||||||
b = t;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for empty slots first
|
|
||||||
if (a.m && !b.m) {
|
|
||||||
return 1;
|
|
||||||
} else if (!a.m && b.m) {
|
|
||||||
return -1;
|
|
||||||
} else if (!a.m && !b.m) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
// If a property comparator is provided use it first
|
|
||||||
let diff = propComparator ? propComparator(a, b) : nameComparator(translate, a.m, b.m);
|
|
||||||
|
|
||||||
if (diff) {
|
|
||||||
return diff;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Property matches so sort by name / group, then class, rating
|
|
||||||
if (am.name === bm.name && am.grp === bm.grp) {
|
|
||||||
if(am.class == bm.class) {
|
|
||||||
return am.rating > bm.rating ? 1 : -1;
|
|
||||||
}
|
|
||||||
return am.class - bm.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nameComparator(translate, am, bm);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine the appropriate class based on diff value
|
|
||||||
* @param {Number} a Potential Module (cannot be null)
|
|
||||||
* @param {Number} b Currently mounted module (optional - null)
|
|
||||||
* @param {Boolean} negative A positive diff has a negative implication
|
|
||||||
* @return {String} CSS Class name
|
|
||||||
*/
|
|
||||||
function diffClass(a, b, negative) {
|
|
||||||
if (b === undefined || a === b) {
|
|
||||||
return 'muted';
|
|
||||||
} else if (a > b) {
|
|
||||||
return negative ? 'warning' : 'secondary';
|
|
||||||
}
|
|
||||||
return negative ? 'secondary' : 'warning';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine the displayable diff of a module's proprty
|
|
||||||
* @param {Function} format Formatter
|
|
||||||
* @param {Number} mVal Potential Module property value
|
|
||||||
* @param {Number} mmVal Currently mounted module property value
|
|
||||||
* @return {string | React.Component} Component to be rendered
|
|
||||||
*/
|
|
||||||
function diff(format, mVal, mmVal) {
|
|
||||||
if (mVal == Infinity) {
|
|
||||||
return '∞';
|
|
||||||
} else {
|
|
||||||
let diff = mVal - mmVal;
|
|
||||||
if (!diff || mVal === undefined || diff == mVal || Math.abs(diff) == Infinity) {
|
|
||||||
return format(mVal);
|
|
||||||
}
|
|
||||||
return `${format(mVal)} (${diff > 0 ? '+' : ''}${format(diff)})`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a a summary and diff of the potential module
|
|
||||||
* versus the currently mounted module if there is one. Must be bound
|
|
||||||
* to a ship instance
|
|
||||||
*
|
|
||||||
* @this {Ship}
|
|
||||||
* @param {Object} language Current language
|
|
||||||
* @param {Object} m Potential Module (cannot be null)
|
|
||||||
* @param {Object} mm Currently mounted module (optional - null if empty)
|
|
||||||
* @return {React.Component} Component to be rendered
|
|
||||||
*/
|
|
||||||
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.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.MW}</span></div>);
|
|
||||||
} else {
|
|
||||||
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.MW}</span></div>);
|
|
||||||
}
|
|
||||||
|
|
||||||
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>);
|
|
||||||
|
|
||||||
let mAffectsShield = ModuleUtils.isShieldGenerator(m.grp) || m.grp == 'sb';
|
|
||||||
let mmAffectsShield = mm ? ModuleUtils.isShieldGenerator(m.grp) || mm.grp == 'sb' : false;
|
|
||||||
if (mAffectsShield || mmAffectsShield) {
|
|
||||||
let shield = this.calcShieldStrengthWith(); // Get shield strength regardless of slot active / inactive
|
|
||||||
let newShield = 0;
|
|
||||||
|
|
||||||
if (mAffectsShield) {
|
|
||||||
if (m.grp == 'sb') { // Both m and mm must be utility modules if this is true
|
|
||||||
newShield = this.calcShieldStrengthWith(null, m.getShieldBoost() - (mm ? mm.getShieldBoost() || 0 : 0));
|
|
||||||
} else {
|
|
||||||
newShield = this.calcShieldStrengthWith(m);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Old module must be a shield booster
|
|
||||||
newShield = this.calcShieldStrengthWith(null, -mm.getShieldBoost());
|
|
||||||
}
|
|
||||||
|
|
||||||
let sgDiffClass = Math.round((newShield - shield) * 100) / 100 == 0 ? 'muted' : (newShield > shield ? 'secondary' : 'warning');
|
|
||||||
|
|
||||||
propDiffs.push(<div key='shields'>{translate('shields')}: <span className={sgDiffClass}>{diff(formats.int, newShield, shield)}{units.MJ}</span></div>);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m.grp === 'mrp') {
|
|
||||||
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>);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m.grp === 'hr') {
|
|
||||||
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>);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m.grp == 'pd') {
|
|
||||||
propDiffs.push(<div key='wep'>
|
|
||||||
{`${translate('WEP')}: `}
|
|
||||||
<span className={diffClass(m.wepcap, mm.getWeaponsCapacity())}>{m.wepcap}{units.MJ}</span>
|
|
||||||
{' / '}
|
|
||||||
<span className={diffClass(m.weprate, mm.getWeaponsRechargeRate())}>{m.weprate}{units.MW}</span>
|
|
||||||
</div>);
|
|
||||||
propDiffs.push(<div key='sys'>
|
|
||||||
{`${translate('SYS')}: `}
|
|
||||||
<span className={diffClass(m.syscap, mm.getSystemsCapacity())}>{m.syscap}{units.MJ}</span>
|
|
||||||
{' / '}
|
|
||||||
<span className={diffClass(m.sysrate, mm.getSystemsRechargeRate())}>{m.sysrate}{units.MW}</span>
|
|
||||||
</div>);
|
|
||||||
propDiffs.push(<div key='eng'>
|
|
||||||
{`${translate('ENG')}: `}
|
|
||||||
<span className={diffClass(m.engcap, mm.getEnginesCapacity())}>{m.engcap}{units.MJ}</span>
|
|
||||||
{' / '}
|
|
||||||
<span className={diffClass(m.engrate, mm.getEnginesRechargeRate())}>{m.engrate}{units.MW}</span>
|
|
||||||
</div>);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Common items
|
|
||||||
|
|
||||||
let mCost = m.cost || 0;
|
|
||||||
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.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>);
|
|
||||||
|
|
||||||
let massDiff = mMass - mmMass;
|
|
||||||
let mCap = m.fuel || m.cargo || 0;
|
|
||||||
let mmCap = mm ? mm.fuel || mm.cargo || 0 : 0;
|
|
||||||
let capDiff = mCap - mmCap;
|
|
||||||
if (m.grp == 'fsd' || massDiff || capDiff) {
|
|
||||||
let fsd = m.grp == 'fsd' ? m : null;
|
|
||||||
let maxRange = this.calcUnladenRange(massDiff, m.fuel, fsd);
|
|
||||||
let ladenRange = this.calcLadenRange(massDiff + capDiff, m.fuel, fsd);
|
|
||||||
|
|
||||||
if (maxRange != this.unladenRange) {
|
|
||||||
propDiffs.push(<div key='maxRange'>{translate('max')} {translate('jump range')}: <span className={maxRange > this.unladenRange ? 'secondary' : 'warning'}>{formats.round(maxRange)}{units.LY}</span></div>);
|
|
||||||
}
|
|
||||||
if (ladenRange != this.ladenRange) {
|
|
||||||
propDiffs.push(<div key='unladenRange'>{translate('laden')} {translate('jump range')}: <span className={ladenRange > this.ladenRange ? 'secondary' : 'warning'}>{formats.round(ladenRange)}{units.LY}</span></div>);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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>);
|
|
||||||
}
|
|
||||||
|
|
||||||
return propDiffs.length > 0 ? <div className='cap' style={{ whiteSpace: 'nowrap' }}>{propDiffs}</div> : null;
|
|
||||||
}
|
|
||||||
@@ -2,19 +2,14 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Coriolis EDCD Edition</title>
|
<title>Coriolis EDCD Edition</title>
|
||||||
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
|
|
||||||
<script>
|
|
||||||
(adsbygoogle = window.adsbygoogle || []).push({
|
|
||||||
google_ad_client: "ca-pub-3709458261881414",
|
|
||||||
enable_page_level_ads: true
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<link rel="stylesheet" href="<%= htmlWebpackPlugin.files.css[0] %>">
|
<link rel="stylesheet" href="<%= htmlWebpackPlugin.files.css[0] %>">
|
||||||
<!-- Standard headers -->
|
<!-- Standard headers -->
|
||||||
<meta name="description" content="A ship builder, outfitting and comparison
|
<meta name="description" content="A ship builder, outfitting and comparison
|
||||||
tool for Elite Dangerous">
|
tool for Elite Dangerous">
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
|
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0,
|
<meta name="viewport" content="width=device-width, initial-scale=1.0,
|
||||||
maximum-scale=1.0, user-scalable=0">
|
maximum-scale=1.0, user-scalable=0">
|
||||||
<link rel="manifest" href="/manifest.json">
|
<link rel="manifest" href="/manifest.json">
|
||||||
@@ -32,7 +27,7 @@
|
|||||||
<meta name="msapplication-TileImage" content="/mstile-144x144.png">
|
<meta name="msapplication-TileImage" content="/mstile-144x144.png">
|
||||||
<meta name="msapplication-config" content="/browserconfig.xml">
|
<meta name="msapplication-config" content="/browserconfig.xml">
|
||||||
<meta name="theme-color" content="#000000">
|
<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>
|
<script>
|
||||||
window.CORIOLIS_GAPI_KEY = '<%- htmlWebpackPlugin.options.gapiKey %>';
|
window.CORIOLIS_GAPI_KEY = '<%- htmlWebpackPlugin.options.gapiKey %>';
|
||||||
window.CORIOLIS_VERSION = '<%- htmlWebpackPlugin.options.version %>';
|
window.CORIOLIS_VERSION = '<%- htmlWebpackPlugin.options.version %>';
|
||||||
@@ -59,7 +54,8 @@
|
|||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body style="background-color:#000;">
|
<body style="background-color:#000;">
|
||||||
<section id="coriolis"></section>
|
<section id="coriolis">
|
||||||
|
|
||||||
|
</section>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
104
src/migrate.html
Normal file
104
src/migrate.html
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<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="mobile-web-app-capable" content="yes" />
|
||||||
|
<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"
|
||||||
|
/>
|
||||||
|
<!-- Bugsnag -->
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<!-- Apple/iOS headers -->
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||||
|
<meta name="apple-mobile-web-app-title" content="Coriolis" />
|
||||||
|
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
|
||||||
|
|
||||||
|
<!-- Microsoft Windows Phone/Tablet headers -->
|
||||||
|
<meta name="msapplication-TileColor" content="#000000" />
|
||||||
|
<meta name="msapplication-TileImage" content="/mstile-144x144.png" />
|
||||||
|
<meta name="msapplication-config" content="/browserconfig.xml" />
|
||||||
|
<meta name="theme-color" content="#000000" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<body style="background-color:#000;">
|
||||||
|
<section id="coriolis">
|
||||||
|
<div class="modal">
|
||||||
|
<h2>
|
||||||
|
Please migrate to <a href="https://coriolis.io">coriolis.io</a>
|
||||||
|
</h2>
|
||||||
|
You are currently on coriolis.<strong>.edcd</strong>.io This domain is
|
||||||
|
considered deprecated. To migrate your builds, copy the below text and
|
||||||
|
go to
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
href="https://coriolis.io"
|
||||||
|
>this link</a
|
||||||
|
>, press ctrl + i and then paste in the data. (If you are on mobile,
|
||||||
|
you can go to the settings and hit import)
|
||||||
|
<div>
|
||||||
|
<textarea id="data" class="cb json"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</body>
|
||||||
|
<script>
|
||||||
|
const LS = localStorage;
|
||||||
|
/**
|
||||||
|
* Safe localstorage get string
|
||||||
|
* @param {String} key key
|
||||||
|
* @return {String} The stored string
|
||||||
|
*/
|
||||||
|
function _getString(key) {
|
||||||
|
return LS ? LS.getItem(key) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Safe localstorage get
|
||||||
|
* @param {String} key key
|
||||||
|
* @return {object | number} The stored data
|
||||||
|
*/
|
||||||
|
function _get(key) {
|
||||||
|
let str = _getString(key);
|
||||||
|
try {
|
||||||
|
return str ? JSON.parse(str) : null;
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const textarea = document.querySelector("#data");
|
||||||
|
const data = {
|
||||||
|
builds: _get("builds") || {},
|
||||||
|
comparisons: _get("comparisons") || {},
|
||||||
|
insurance: _get("insurance") || "standard",
|
||||||
|
shipDiscount: _get("shipDiscount") || 0,
|
||||||
|
moduleDiscount: _get("moduleDiscount") || 0
|
||||||
|
};
|
||||||
|
textarea.textContent = JSON.stringify(data, null, 2);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user