mirror of
https://github.com/EDCD/coriolis.git
synced 2025-12-09 06:43:24 +00:00
Compare commits
104 Commits
d2c3787165
...
25c30e46a6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
25c30e46a6 | ||
|
|
870c033c64 | ||
|
|
46b0200b28 | ||
|
|
e8bcb18f12 | ||
|
|
68ca24ef96 | ||
|
|
7ca2754934 | ||
|
|
e82ab0aec0 | ||
|
|
d406018f0b | ||
|
|
2438aa1f48 | ||
|
|
c5bbeacc6a | ||
|
|
d9763f2db7 | ||
|
|
5692cc6fe3 | ||
|
|
22cdfcdecf | ||
|
|
3db3b09c22 | ||
|
|
23c264e321 | ||
|
|
873dfaa305 | ||
|
|
7050356bce | ||
|
|
88c9bb0254 | ||
|
|
45f1dd2da9 | ||
|
|
46bcc2313f | ||
|
|
9e012c1490 | ||
|
|
50f9c0faa1 | ||
|
|
74829a09c0 | ||
|
|
45508ba2d4 | ||
|
|
624adf2b64 | ||
|
|
821daefeb8 | ||
|
|
86b95981f1 | ||
|
|
688eebb9ea | ||
|
|
d719da2cde | ||
|
|
19c1851e14 | ||
|
|
414bf4cb20 | ||
|
|
c674459376 | ||
|
|
fd009fe567 | ||
|
|
e28eccb6fb | ||
|
|
301c97db58 | ||
|
|
c19ca6648d | ||
|
|
cfdb92ecc6 | ||
|
|
de5ca7b5e6 | ||
|
|
8676deba7d | ||
|
|
d3ce8d4f7c | ||
|
|
0c3de95025 | ||
|
|
83f1f9aa2e | ||
|
|
dee14a5dee | ||
|
|
db13da95db | ||
|
|
cb08b10a63 | ||
|
|
189eb2b726 | ||
|
|
b9abf784f4 | ||
|
|
39287bc5d7 | ||
|
|
bcdd0c6044 | ||
|
|
f70455ce26 | ||
|
|
888f807a7b | ||
|
|
5040141096 | ||
|
|
46ba782911 | ||
|
|
524e204e01 | ||
|
|
a9753828c1 | ||
|
|
6d30a54925 | ||
|
|
7c58eb1cde | ||
|
|
4001e1e9ac | ||
|
|
0da00d38a4 | ||
|
|
1db6fe1a34 | ||
|
|
10b8bb95a9 | ||
|
|
8d9581900f | ||
|
|
2bb55d2c36 | ||
|
|
49c827b2c8 | ||
|
|
cf50537e3d | ||
|
|
804466f88a | ||
|
|
bded793374 | ||
|
|
fc918d893c | ||
|
|
5fe13b26a4 | ||
|
|
be1393994e | ||
|
|
dc6db31d43 | ||
|
|
840ce9f3e4 | ||
|
|
9674aa2367 | ||
|
|
d19b6b107f | ||
|
|
1b96c18ecb | ||
|
|
0f43c4d7eb | ||
|
|
4c70806a5a | ||
|
|
7de304bdbe | ||
|
|
34f9f28c16 | ||
|
|
13ec027772 | ||
|
|
3966f92454 | ||
|
|
38f72438dd | ||
|
|
0179382379 | ||
|
|
7f5c652f49 | ||
|
|
1f9b1e5d27 | ||
|
|
ebf4491901 | ||
|
|
d322a47592 | ||
|
|
06a58d22cb | ||
|
|
25d4520eee | ||
|
|
0087062468 | ||
|
|
14bb49a2bc | ||
|
|
ab671b0af5 | ||
|
|
304ddf9ea8 | ||
|
|
b3f320e69f | ||
|
|
3a63e08f80 | ||
|
|
a3feb42fd7 | ||
|
|
a77d991cf9 | ||
|
|
9ebe5dc786 | ||
|
|
baace95f83 | ||
|
|
fc5db94f9a | ||
|
|
c3b0e8d949 | ||
|
|
1b8c460876 | ||
|
|
67409a613b | ||
|
|
e4a826592f |
@@ -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
|
||||
|
||||
@@ -8,53 +8,41 @@ Coriolis was created using assets and imagery from Elite: Dangerous, with the pe
|
||||
|
||||
## Contributing
|
||||
|
||||
Please [submit issues](https://github.com/EDCD/coriolis/issues), or better yet [pull requests](https://github.com/EDCD/coriolis/pulls) for any corrections or additions to the database or the code.
|
||||
|
||||
### Translations
|
||||
|
||||
Please use the OneSky translation site to suggest new translations: http://edcd-coriolis.oneskyapp.com
|
||||
These will be merged regularly by the project manager.
|
||||
|
||||
### Feature Requests, Suggestions & Bugs
|
||||
|
||||
Chat to us on [Discord](https://discord.gg/0uwCh6R62aPRjk9w)!
|
||||
- [Submit issues](https://github.com/EDCD/coriolis/issues)
|
||||
- [Submit pull requests](https://github.com/EDCD/coriolis/pulls) targetting `develop` branch
|
||||
- Chat to us on [Discord](https://discord.gg/0uwCh6R62aPRjk9w)!
|
||||
|
||||
## Development
|
||||
|
||||
See the [Developer's Guide](https://github.com/EDCD/coriolis/wiki/Developing-for-Coriolis) in the wiki.
|
||||
To get a local instance of coriolis running, perform the following steps in a shell:
|
||||
```sh
|
||||
> git clone https://github.com/EDCD/coriolis.git
|
||||
> git clone https://github.com/EDCD/coriolis-data.git
|
||||
> cd ./coriolis-data
|
||||
> npm install
|
||||
> cd ../coriolis
|
||||
> npm install
|
||||
> npm start
|
||||
```
|
||||
|
||||
Also see [the documentation site.](https://coriolis.willb.info/)
|
||||
You will then have a development server running on `localhost:3300`.
|
||||
|
||||
### Ship and Module Database
|
||||
|
||||
See the [Data wiki](https://github.com/cmmcleod/coriolis-data/wiki) for details on structure, etc.
|
||||
|
||||
You can find hosted and compiled versions of these data-jsons under https://coriolis.io/data/ and https://beta.coriolis.io/data/.
|
||||
You might want to load these as depedency instead of reyling on the npm-dependency.
|
||||
## Deployment
|
||||
|
||||
## License
|
||||
Follow the steps for [Development](#development) as above, but instead
|
||||
of `npm start` you'll want to:
|
||||
|
||||
All Data and [associated JSON](https://github.com/EDCD/coriolis-data) files are intellectual property and copyright of Frontier Developments plc ('Frontier', 'Frontier Developments') and are subject to their
|
||||
[terms and conditions](https://www.frontierstore.net/terms-and-conditions/).
|
||||
```sh
|
||||
> npm run build
|
||||
```
|
||||
|
||||
The code (Javascript, CSS, HTML, and SVG files only) specificially for Coriolis.io is released under the MIT License.
|
||||
this will result in a `build/` directory being created containing all the necessary files.
|
||||
|
||||
Copyright (c) 2015 Coriolis.io, Colin McLeod
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software (Javascript, CSS, HTML, and SVG files only), and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
After this you need to serve the files in some manner.
|
||||
Either configure your webserver to make the actual `build/` directory
|
||||
visible on the web, or alternatively copy it to somewhere to serve it
|
||||
from.
|
||||
|
||||
@@ -1,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
28577
package-lock.json
generated
28577
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",
|
||||
"start": "node devServer.js",
|
||||
"lint": "eslint --ext .js,.jsx src",
|
||||
"test": "jest",
|
||||
"prod-serve": "nginx -p $(pwd) -c nginx.conf",
|
||||
"prod-stop": "kill -QUIT $(cat nginx.pid)",
|
||||
"build": "npm run clean && cross-env NODE_ENV=production webpack -p --config webpack.config.prod.js",
|
||||
"rsync": "rsync -ae \"ssh -i $CORIOLIS_PEM\" --delete build/ $CORIOLIS_USER@$CORIOLIS_HOST:~/wwws",
|
||||
"deploy": "npm run lint && npm test && npm run build && npm run rsync"
|
||||
},
|
||||
"jest": {
|
||||
"transform": {
|
||||
".*": "<rootDir>/node_modules/babel-jest"
|
||||
},
|
||||
"testRegex": "(/__tests__/test-.*|\\.(test|spec))\\.js$",
|
||||
"moduleFileExtensions": [
|
||||
"js",
|
||||
"json",
|
||||
"jsx"
|
||||
],
|
||||
"automock": true,
|
||||
"bail": false,
|
||||
"unmockedModulePathPatterns": [
|
||||
"<rootDir>/node_modules/lodash",
|
||||
"<rootDir>/node_modules/react",
|
||||
"<rootDir>/node_modules/react-dom",
|
||||
"<rootDir>/node_modules/react-transition-group",
|
||||
"<rootDir>/node_modules/react-testutils-additions",
|
||||
"<rootDir>/node_modules/fbjs",
|
||||
"<rootDir>/node_modules/fbemitter",
|
||||
"<rootDir>/node_modules/classnames",
|
||||
"<rootDir>/node_modules/d3",
|
||||
"<rootDir>/node_modules/lz-string",
|
||||
"<rootDir>/node_modules/jsen",
|
||||
"coriolis-data",
|
||||
"<rootDir>/src/app/shipyard",
|
||||
"<rootDir>/src/app/i18n",
|
||||
"<rootDir>/src/app/utils",
|
||||
"<rootDir>/src/schemas",
|
||||
"<rootDir>/__tests__"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/plugin-proposal-class-properties": "^7.0.0",
|
||||
@@ -77,7 +44,6 @@
|
||||
"appcache-webpack-plugin": "^1.4.0",
|
||||
"babel-core": "^7.0.0-bridge.0",
|
||||
"babel-eslint": "^10.0.1",
|
||||
"babel-jest": "^23.6.0",
|
||||
"babel-loader": "^8.0.0",
|
||||
"copy-webpack-plugin": "^4.5.2",
|
||||
"create-react-class": "^15.6.3",
|
||||
@@ -98,7 +64,6 @@
|
||||
"extract-text-webpack-plugin": "^4.0.0-beta.0",
|
||||
"file-loader": "^2.0.0",
|
||||
"html-webpack-plugin": "^3.0.7",
|
||||
"jest-cli": "^23.6.0",
|
||||
"jsen": "^0.6.4",
|
||||
"json-loader": "^0.5.4",
|
||||
"less": "^3.8.1",
|
||||
@@ -126,10 +91,9 @@
|
||||
"auto-bind": "^2.1.1",
|
||||
"browserify-zlib-next": "^1.0.1",
|
||||
"classnames": "^2.2.6",
|
||||
"coriolis-data": "../coriolis-data",
|
||||
"d3": "^5.7.0",
|
||||
"detect-browser": "^3.0.1",
|
||||
"ed-forge": "github:EDCD/ed-forge",
|
||||
"ed-forge": "../ed-forge",
|
||||
"fbemitter": "^2.1.1",
|
||||
"lodash": "^4.17.11",
|
||||
"lz-string": "^1.4.4",
|
||||
|
||||
@@ -16,12 +16,9 @@ import ModalPermalink from './components/ModalPermalink';
|
||||
import AboutPage from './pages/AboutPage';
|
||||
import NotFoundPage from './pages/NotFoundPage';
|
||||
import OutfittingPage from './pages/OutfittingPage';
|
||||
import ComparisonPage from './pages/ComparisonPage';
|
||||
import ShipyardPage from './pages/ShipyardPage';
|
||||
import ErrorDetails from './pages/ErrorDetails';
|
||||
|
||||
const request = require('superagent');
|
||||
|
||||
/**
|
||||
* Coriolis App
|
||||
*/
|
||||
@@ -76,9 +73,6 @@ export default class Coriolis extends React.Component {
|
||||
Router('/outfit/?', (r) => this._setPage(OutfittingPage, r));
|
||||
Router('/outfit/:ship/?', (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('*', (r) => this._setPage(null, r));
|
||||
}
|
||||
@@ -382,7 +376,6 @@ export default class Coriolis extends React.Component {
|
||||
*/
|
||||
render() {
|
||||
let currentMenu = this.state.currentMenu;
|
||||
|
||||
return <div style={{ minHeight: '100%' }} onClick={this._closeMenu}
|
||||
className={this.state.noTouch ? 'no-touch' : null}
|
||||
>
|
||||
@@ -395,6 +388,7 @@ export default class Coriolis extends React.Component {
|
||||
{this.state.modal}
|
||||
{this.state.tooltip}
|
||||
<footer>
|
||||
|
||||
<div className="right cap">
|
||||
<a href="https://github.com/EDCD/coriolis" target="_blank" rel="noopener noreferrer"
|
||||
title="Coriolis Github Project">{window.CORIOLIS_VERSION} - {window.CORIOLIS_DATE}</a>
|
||||
|
||||
@@ -72,6 +72,7 @@ Router.go = function(path, state) {
|
||||
gaTrack(path);
|
||||
let ctx = new Context(path, state);
|
||||
Router.dispatch(ctx);
|
||||
|
||||
if (!ctx.unhandled) {
|
||||
if (isStandAlone()) {
|
||||
Persist.setState(ctx);
|
||||
|
||||
@@ -5,9 +5,11 @@ import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
||||
import cn from 'classnames';
|
||||
import { MountFixed, MountGimballed, MountTurret } from './SvgIcons';
|
||||
import FuzzySearch from 'react-fuzzy';
|
||||
import { getModuleInfo } from 'ed-forge/lib/data/items';
|
||||
import { groupBy, mapValues, sortBy } from 'lodash';
|
||||
import { getModuleInfo } from 'ed-forge/lib/src/data/items';
|
||||
import { get, groupBy, mapValues, sortBy, zip, zipWith } from 'lodash';
|
||||
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
|
||||
|
||||
@@ -23,7 +25,6 @@ const MOUNT_MAP = {
|
||||
export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
static propTypes = {
|
||||
onSelect: PropTypes.func.isRequired,
|
||||
diffDetails: PropTypes.func,
|
||||
hideSearch: PropTypes.bool,
|
||||
m: PropTypes.object,
|
||||
warning: PropTypes.func,
|
||||
@@ -196,14 +197,47 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
* @param {DOMRect} rect DOMRect for target element
|
||||
*/
|
||||
_showDiff(mountedModule, hoveringModule, rect) {
|
||||
if (this.props.diffDetails) {
|
||||
this.touchTimeout = null;
|
||||
// TODO:
|
||||
// this.context.tooltip(
|
||||
// this.props.diffDetails(hoveringModule, mountedModule),
|
||||
// rect,
|
||||
// );
|
||||
}
|
||||
const { tooltip, language } = this.context;
|
||||
const { formats, translate, units } = language;
|
||||
this.touchTimeout = null;
|
||||
const mountedIsEmpty = mountedModule.isEmpty();
|
||||
const props = (
|
||||
mountedIsEmpty ? ['mass'] : Object.keys(hoveringModule.props)
|
||||
).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 autoBind from 'auto-bind';
|
||||
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
|
||||
@@ -160,8 +160,8 @@ export default class CostSection extends TranslatedComponent {
|
||||
<tr className='main'>
|
||||
<th colSpan='2' className='sortable le' onClick={() => this._sortBy('m')}>
|
||||
{translate('module')}
|
||||
{shipDiscount ? <u className='cap optional-hide' style={{ marginLeft: '0.5em' }}>{`[${translate('ship')} -${formats.pct(shipDiscount)}]`}</u> : null}
|
||||
{moduleDiscount ? <u className='cap optional-hide' style={{ marginLeft: '0.5em' }}>{`[${translate('modules')} -${formats.pct(moduleDiscount)}]`}</u> : null}
|
||||
{shipDiscount ? <u className='cap optional-hide' style={{ marginLeft: '0.5em' }}>{`[${translate('ship')} ${formats.pct(-1 * shipDiscount)}]`}</u> : null}
|
||||
{moduleDiscount ? <u className='cap optional-hide' style={{ marginLeft: '0.5em' }}>{`[${translate('modules')} ${formats.pct(-1 * moduleDiscount)}]`}</u> : null}
|
||||
</th>
|
||||
<th className='sortable le' onClick={() => this._sortBy('cr')} >{translate('credits')}</th>
|
||||
</tr>
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import * as Calc from '../shipyard/Calculations';
|
||||
import PieChart from './PieChart';
|
||||
import VerticalBarChart from './VerticalBarChart';
|
||||
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
|
||||
|
||||
@@ -2,7 +2,7 @@ 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/data/blueprints';
|
||||
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';
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import Slider from '../components/Slider';
|
||||
import { moduleReduce } from 'ed-forge/lib/helper';
|
||||
import { moduleReduce } from 'ed-forge/lib/src/helper';
|
||||
|
||||
/**
|
||||
* Engagement range slider
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
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';
|
||||
const { LADEN_MASS } = ShipProps;
|
||||
|
||||
|
||||
@@ -2,8 +2,7 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import LineChart from '../components/LineChart';
|
||||
import * as Calc from '../shipyard/Calculations';
|
||||
import { calculateJumpRange } from 'ed-forge/lib/stats/JumpRangeProfile';
|
||||
import { calculateJumpRange } from 'ed-forge/lib/src/stats/JumpRangeProfile';
|
||||
import { ShipProps } from 'ed-forge';
|
||||
const { LADEN_MASS } = ShipProps;
|
||||
|
||||
@@ -18,18 +17,6 @@ export default class FSDProfile extends TranslatedComponent {
|
||||
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
|
||||
* @return {React.Component} contents
|
||||
@@ -42,7 +29,7 @@ export default class FSDProfile extends TranslatedComponent {
|
||||
const minMass = ship.readProp('hullmass');
|
||||
const maxMass = ship.getThrusters().get('enginemaximalmass');
|
||||
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 (
|
||||
<LineChart
|
||||
xMin={minMass}
|
||||
|
||||
@@ -5,6 +5,8 @@ import { MountFixed, MountGimballed, MountTurret } from '../components/SvgIcons'
|
||||
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
||||
import autoBind from 'auto-bind';
|
||||
|
||||
const SIZE_ORDER = ['huge', 'large', 'medium', 'small'];
|
||||
|
||||
/**
|
||||
* Hardpoint slot section
|
||||
*/
|
||||
@@ -22,30 +24,37 @@ export default class HardpointSlotSection extends SlotSection {
|
||||
* Empty all slots
|
||||
*/
|
||||
_empty() {
|
||||
// TODO:
|
||||
// this.props.ship.emptyWeapons();
|
||||
this.props.ship.getHardpoints(undefined, true).forEach((slot) => slot.reset());
|
||||
this._close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill slots with specified module
|
||||
* @param {string} group Group name
|
||||
* @param {string} mount Mount Type - F, G, T
|
||||
* @param {SyntheticEvent} event Event
|
||||
* @param {string} type Type of item
|
||||
* @param {string} rating Mount Type - (fixed, gimbal, turret)
|
||||
* @param {SyntheticEvent} event Event
|
||||
*/
|
||||
_fill(group, mount, event) {
|
||||
// TODO:
|
||||
// this.props.ship.useWeapon(group, mount, null, event.getModifierState('Alt'));
|
||||
_fill(type, rating, event) {
|
||||
const fillAll = event.getModifierState('Alt');
|
||||
this.props.ship.getHardpoints(undefined, true).forEach((slot) => {
|
||||
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;
|
||||
}
|
||||
}
|
||||
});
|
||||
this._close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Empty all on section header right click
|
||||
*/
|
||||
_contextMenu() {
|
||||
this._empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the slot React Components
|
||||
* @return {Array} Array of Slots
|
||||
@@ -58,7 +67,6 @@ export default class HardpointSlotSection extends SlotSection {
|
||||
for (let h of ship.getHardpoints(undefined, true)) {
|
||||
slots.push(<Slot
|
||||
key={h.object.Slot}
|
||||
maxClass={h.getSize()}
|
||||
currentMenu={currentMenu}
|
||||
drag={this._drag.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}>
|
||||
<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>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('pl')}</div>
|
||||
<div className='select-group cap'>{translate('pulselaser')}</div>
|
||||
<ul>
|
||||
<li className="c" tabIndex="0" onClick={_fill.bind(this, 'pl', 'F')}><MountFixed className='lg'/></li>
|
||||
<li className="c" tabIndex="0" onClick={_fill.bind(this, 'pl', 'G')}><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', 'fixed')}><MountFixed className='lg'/></li>
|
||||
<li className="c hardpoint" tabIndex="0" onClick={_fill.bind(this, 'pulselaser', 'gimbal')}><MountGimballed className='lg'/></li>
|
||||
<li className="c hardpoint" tabIndex="0" onClick={_fill.bind(this, 'pulselaser', 'turret')}><MountTurret className='lg'/></li>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('ul')}</div>
|
||||
<div className='select-group cap'>{translate('burstlaser')}</div>
|
||||
<ul>
|
||||
<li className="c" tabIndex="0" onClick={_fill.bind(this, 'ul', 'F')}><MountFixed className='lg'/></li>
|
||||
<li className="c" tabIndex="0" onClick={_fill.bind(this, 'ul', 'G')}><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', 'fixed')}><MountFixed className='lg'/></li>
|
||||
<li className="c hardpoint" tabIndex="0" onClick={_fill.bind(this, 'burstlaser', 'gimbal')}><MountGimballed className='lg'/></li>
|
||||
<li className="c hardpoint" tabIndex="0" onClick={_fill.bind(this, 'burstlaser', 'turret')}><MountTurret className='lg'/></li>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('bl')}</div>
|
||||
<div className='select-group cap'>{translate('beamlaser')}</div>
|
||||
<ul>
|
||||
<li className="c" tabIndex="0" onClick={_fill.bind(this, 'bl', 'F')}><MountFixed className='lg'/></li>
|
||||
<li className="c" tabIndex="0" onClick={_fill.bind(this, 'bl', 'G')}><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', 'fixed')}><MountFixed className='lg'/></li>
|
||||
<li className="c hardpoint" tabIndex="0" onClick={_fill.bind(this, 'beamlaser', 'gimbal')}><MountGimballed className='lg'/></li>
|
||||
<li className="c hardpoint" tabIndex="0" onClick={_fill.bind(this, 'beamlaser', 'turret')}><MountTurret className='lg'/></li>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('mc')}</div>
|
||||
<div className='select-group cap'>{translate('multicannon')}</div>
|
||||
<ul>
|
||||
<li className="c" tabIndex="0" onClick={_fill.bind(this, 'mc', 'F')}><MountFixed className='lg'/></li>
|
||||
<li className="c" tabIndex="0" onClick={_fill.bind(this, 'mc', 'G')}><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', 'fixed')}><MountFixed className='lg'/></li>
|
||||
<li className="c hardpoint" tabIndex="0" onClick={_fill.bind(this, 'multicannon', 'gimbal')}><MountGimballed className='lg'/></li>
|
||||
<li className="c hardpoint" tabIndex="0" onClick={_fill.bind(this, 'multicannon', 'turret')}><MountTurret className='lg'/></li>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('c')}</div>
|
||||
<div className='select-group cap'>{translate('cannon')}</div>
|
||||
<ul>
|
||||
<li className="c" tabIndex="0" onClick={_fill.bind(this, 'c', 'F')}><MountFixed className='lg'/></li>
|
||||
<li className="c" tabIndex="0" onClick={_fill.bind(this, 'c', 'G')}><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', 'fixed')}><MountFixed className='lg'/></li>
|
||||
<li className="c hardpoint" tabIndex="0" onClick={_fill.bind(this, 'cannon', 'gimbal')}><MountGimballed className='lg'/></li>
|
||||
<li className="c hardpoint" tabIndex="0" onClick={_fill.bind(this, 'cannon', 'turret')}><MountTurret className='lg'/></li>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('fc')}</div>
|
||||
<div className='select-group cap'>{translate('fragcannon')}</div>
|
||||
<ul>
|
||||
<li className="c" tabIndex="0" onClick={_fill.bind(this, 'fc', 'F')}><MountFixed className='lg'/></li>
|
||||
<li className="c" tabIndex="0" onClick={_fill.bind(this, 'fc', 'G')}><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', 'fixed')}><MountFixed className='lg'/></li>
|
||||
<li className="c hardpoint" tabIndex="0" onClick={_fill.bind(this, 'fragcannon', 'gimbal')}><MountGimballed className='lg'/></li>
|
||||
<li className="c hardpoint" tabIndex="0" onClick={_fill.bind(this, 'fragcannon', 'turret')}><MountTurret className='lg'/></li>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('pa')}</div>
|
||||
<div className='select-group cap'>{translate('plasmaacc')}</div>
|
||||
<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>
|
||||
<div className='select-group cap'>{translate('rg')}</div>
|
||||
<div className='select-group cap'>{translate('railgun')}</div>
|
||||
<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>
|
||||
<div className='select-group cap'>{translate('nl')}</div>
|
||||
<div className='select-group cap'>{translate('minelauncher')}</div>
|
||||
<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>
|
||||
<div className='select-group cap'>{translate('rfl')}</div>
|
||||
<div className='select-group cap'>{translate('flaklauncher')}</div>
|
||||
<ul>
|
||||
<li className="c" tabIndex="0" onClick={_fill.bind(this, 'rfl', 'F')}><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', 'fixed')}><MountFixed className='lg'/></li>
|
||||
<li className="c hardpoint" tabIndex="0" onClick={_fill.bind(this, 'flaklauncher', 'turret')}><MountTurret className='lg'/></li>
|
||||
</ul>
|
||||
</div>;
|
||||
}
|
||||
|
||||
@@ -7,9 +7,6 @@ import ActiveLink from './ActiveLink';
|
||||
import cn from 'classnames';
|
||||
import { Cogs, CoriolisLogo, Hammer, Help, Rocket, StatsBars } from './SvgIcons';
|
||||
import Persist from '../stores/Persist';
|
||||
import { toDetailedExport } from '../shipyard/Serializer';
|
||||
import Ship from '../shipyard/Ship';
|
||||
import ModalBatchOrbis from './ModalBatchOrbis';
|
||||
import ModalDeleteAll from './ModalDeleteAll';
|
||||
import ModalExport from './ModalExport';
|
||||
import ModalHelp from './ModalHelp';
|
||||
@@ -18,7 +15,8 @@ import Slider from './Slider';
|
||||
import Announcement from './Announcement';
|
||||
import { outfitURL } from '../utils/UrlGenerators';
|
||||
import autoBind from 'auto-bind';
|
||||
import { Factory } from 'ed-forge';
|
||||
import { Factory, Ship } from 'ed-forge';
|
||||
import { chain, entries } from 'lodash';
|
||||
|
||||
const SIZE_MIN = 0.65;
|
||||
const SIZE_RANGE = 0.55;
|
||||
@@ -201,13 +199,6 @@ export default class Header extends TranslatedComponent {
|
||||
Persist.showTooltips(!Persist.showTooltips());
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle module resistances setting
|
||||
*/
|
||||
_toggleModuleResistances() {
|
||||
Persist.showModuleResistances(!Persist.showModuleResistances());
|
||||
}
|
||||
|
||||
/**
|
||||
* Show delete all modal
|
||||
* @param {SyntheticEvent} e Event
|
||||
@@ -217,20 +208,6 @@ export default class Header extends TranslatedComponent {
|
||||
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()}
|
||||
/>);
|
||||
};
|
||||
|
||||
/**
|
||||
* Show export modal with detailed export
|
||||
* @param {SyntheticEvent} e Event
|
||||
@@ -239,10 +216,22 @@ export default class Header extends TranslatedComponent {
|
||||
let translate = this.context.language.translate;
|
||||
e.preventDefault();
|
||||
|
||||
const builds = chain(Persist.getBuilds())
|
||||
.values()
|
||||
.map((builds) => Object.values(builds))
|
||||
.flatMap()
|
||||
.map((code) => new Ship(code))
|
||||
.value();
|
||||
|
||||
this.context.showModal(<ModalExport
|
||||
title={translate('detailed export')}
|
||||
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(),
|
||||
};
|
||||
}))}
|
||||
/>);
|
||||
}
|
||||
|
||||
@@ -375,7 +364,10 @@ export default class Header extends TranslatedComponent {
|
||||
if (this.props.announcements) {
|
||||
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/>);
|
||||
}
|
||||
}
|
||||
@@ -394,7 +386,6 @@ export default class Header extends TranslatedComponent {
|
||||
_getSettingsMenu() {
|
||||
let translate = this.context.language.translate;
|
||||
let tips = Persist.showTooltips();
|
||||
let moduleResistances = Persist.showModuleResistances();
|
||||
|
||||
return (
|
||||
<div className='menu-list no-wrap cap' onClick={ (e) => e.stopPropagation() }>
|
||||
@@ -412,10 +403,6 @@ export default class Header extends TranslatedComponent {
|
||||
<td>{translate('tooltips')}</td>
|
||||
<td className={cn('ri', { disabled: !tips, 'primary-disabled': tips })}>{(tips ? '✓' : '✗')}</td>
|
||||
</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>
|
||||
<td>{translate('insurance')}</td>
|
||||
<td className='ri'>
|
||||
@@ -443,11 +430,9 @@ export default class Header extends TranslatedComponent {
|
||||
<hr />
|
||||
<ul style={{ width: '100%' }}>
|
||||
{translate('builds')} & {translate('comparisons')}
|
||||
<li><Link href="#" className='block' onClick={this._showBackup.bind(this)}>{translate('backup')}</Link></li>
|
||||
<li><Link href="#" className='block' onClick={this._showDetailedExport.bind(this)}>{translate('detailed export')}</Link></li>
|
||||
<li><Link href="#" className='block' onClick={this._uploadAllBuildsToOrbis.bind(this)}>{translate('upload all builds to orbis')}</Link></li>
|
||||
<li><Link href="#" className='block' onClick={this._showImport.bind(this)}>{translate('import')}</Link></li>
|
||||
<li><Link href="#" className='block' onClick={this._showDeleteAll.bind(this)}>{translate('delete all')}</Link></li>
|
||||
<li><Link href="#" className='block' onClick={this._showDetailedExport}>{translate('detailed export')}</Link></li>
|
||||
<li><Link href="#" className='block' onClick={this._showImport}>{translate('import')}</Link></li>
|
||||
<li><Link href="#" className='block' onClick={this._showDeleteAll}>{translate('delete all')}</Link></li>
|
||||
</ul>
|
||||
<hr />
|
||||
<table style={{ width: 300, backgroundColor: 'transparent' }}>
|
||||
@@ -479,7 +464,6 @@ export default class Header extends TranslatedComponent {
|
||||
Persist.addListener('deletedAll', update);
|
||||
Persist.addListener('builds', update);
|
||||
Persist.addListener('tooltips', update);
|
||||
Persist.addListener('moduleresistances', update);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -549,13 +533,6 @@ export default class Header extends TranslatedComponent {
|
||||
{openedMenu == 'b' ? this._getBuildsMenu() : null}
|
||||
</div>
|
||||
|
||||
<div className='l menu'>
|
||||
<div className={cn('menu-header', { selected: openedMenu == 'comp', disabled: !hasBuilds })} onClick={hasBuilds && this._openComp}>
|
||||
<StatsBars className={cn('warning', { 'warning-disabled': !hasBuilds })} /><span className='menu-item-label'>{translate('compare')}</span>
|
||||
</div>
|
||||
{openedMenu == 'comp' ? this._getComparisonsMenu() : null}
|
||||
</div>
|
||||
|
||||
{/* TODO: Enable */}
|
||||
{/* <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}>
|
||||
|
||||
@@ -1,10 +1,27 @@
|
||||
import React from 'react';
|
||||
import SlotSection from './SlotSection';
|
||||
import Slot from './Slot';
|
||||
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
||||
import { stopCtxPropagation } from '../utils/UtilityFunctions';
|
||||
import { canMount } from '../utils/SlotFunctions';
|
||||
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
|
||||
@@ -23,8 +40,7 @@ export default class InternalSlotSection extends SlotSection {
|
||||
* Empty all slots
|
||||
*/
|
||||
_empty() {
|
||||
// TODO:
|
||||
// this.props.ship.emptyInternal();
|
||||
this.props.ship.getModules(TYPES.ANY_INTERNAL).forEach((slot) => slot.reset());
|
||||
this._close();
|
||||
}
|
||||
|
||||
@@ -33,13 +49,8 @@ export default class InternalSlotSection extends SlotSection {
|
||||
* @param {SyntheticEvent} event Event
|
||||
*/
|
||||
_fillWithCargo(event) {
|
||||
let clobber = event.getModifierState('Alt');
|
||||
let ship = this.props.ship;
|
||||
ship.internal.forEach((slot) => {
|
||||
if ((clobber || !slot.m) && canMount(ship, slot, 'cr')) {
|
||||
ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E'));
|
||||
}
|
||||
});
|
||||
const fillAll = event.getModifierState('Alt');
|
||||
setAllEmpty(this.props.ship, fillAll, 'cargorack');
|
||||
this._close();
|
||||
}
|
||||
|
||||
@@ -48,13 +59,8 @@ export default class InternalSlotSection extends SlotSection {
|
||||
* @param {SyntheticEvent} event Event
|
||||
*/
|
||||
_fillWithFuelTanks(event) {
|
||||
let clobber = event.getModifierState('Alt');
|
||||
let ship = this.props.ship;
|
||||
ship.internal.forEach((slot) => {
|
||||
if ((clobber || !slot.m) && canMount(ship, slot, 'ft')) {
|
||||
ship.use(slot, ModuleUtils.findInternal('ft', slot.maxClass, 'C'));
|
||||
}
|
||||
});
|
||||
const fillAll = event.getModifierState('Alt');
|
||||
setAllEmpty(this.props.ship, fillAll, 'fueltank', '3');
|
||||
this._close();
|
||||
}
|
||||
|
||||
@@ -63,13 +69,8 @@ export default class InternalSlotSection extends SlotSection {
|
||||
* @param {SyntheticEvent} event Event
|
||||
*/
|
||||
_fillWithLuxuryCabins(event) {
|
||||
let clobber = event.getModifierState('Alt');
|
||||
let ship = this.props.ship;
|
||||
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
|
||||
}
|
||||
});
|
||||
const fillAll = event.getModifierState('Alt');
|
||||
setAllEmpty(this.props.ship, fillAll, 'passengercabins', '4');
|
||||
this._close();
|
||||
}
|
||||
|
||||
@@ -78,13 +79,8 @@ export default class InternalSlotSection extends SlotSection {
|
||||
* @param {SyntheticEvent} event Event
|
||||
*/
|
||||
_fillWithFirstClassCabins(event) {
|
||||
let clobber = event.getModifierState('Alt');
|
||||
let ship = this.props.ship;
|
||||
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
|
||||
}
|
||||
});
|
||||
const fillAll = event.getModifierState('Alt');
|
||||
setAllEmpty(this.props.ship, fillAll, 'passengercabins', '3');
|
||||
this._close();
|
||||
}
|
||||
|
||||
@@ -93,13 +89,8 @@ export default class InternalSlotSection extends SlotSection {
|
||||
* @param {SyntheticEvent} event Event
|
||||
*/
|
||||
_fillWithBusinessClassCabins(event) {
|
||||
let clobber = event.getModifierState('Alt');
|
||||
let ship = this.props.ship;
|
||||
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
|
||||
}
|
||||
});
|
||||
const fillAll = event.getModifierState('Alt');
|
||||
setAllEmpty(this.props.ship, fillAll, 'passengercabins', '2');
|
||||
this._close();
|
||||
}
|
||||
|
||||
@@ -108,13 +99,8 @@ export default class InternalSlotSection extends SlotSection {
|
||||
* @param {SyntheticEvent} event Event
|
||||
*/
|
||||
_fillWithEconomyClassCabins(event) {
|
||||
let clobber = event.getModifierState('Alt');
|
||||
let ship = this.props.ship;
|
||||
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
|
||||
}
|
||||
});
|
||||
const fillAll = event.getModifierState('Alt');
|
||||
setAllEmpty(this.props.ship, fillAll, 'passengercabins', '1');
|
||||
this._close();
|
||||
}
|
||||
|
||||
@@ -123,16 +109,8 @@ export default class InternalSlotSection extends SlotSection {
|
||||
* @param {SyntheticEvent} event Event
|
||||
*/
|
||||
_fillWithCells(event) {
|
||||
let clobber = event.getModifierState('Alt');
|
||||
let ship = this.props.ship;
|
||||
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;
|
||||
}
|
||||
});
|
||||
const fillAll = event.getModifierState('Alt');
|
||||
setAllEmpty(this.props.ship, fillAll, 'scb', '5');
|
||||
this._close();
|
||||
}
|
||||
|
||||
@@ -141,13 +119,8 @@ export default class InternalSlotSection extends SlotSection {
|
||||
* @param {SyntheticEvent} event Event
|
||||
*/
|
||||
_fillWithArmor(event) {
|
||||
let clobber = event.getModifierState('Alt');
|
||||
let ship = this.props.ship;
|
||||
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
|
||||
}
|
||||
});
|
||||
const fillAll = event.getModifierState('Alt');
|
||||
setAllEmpty(this.props.ship, fillAll, 'hrp', '2');
|
||||
this._close();
|
||||
}
|
||||
|
||||
@@ -156,23 +129,11 @@ export default class InternalSlotSection extends SlotSection {
|
||||
* @param {SyntheticEvent} event Event
|
||||
*/
|
||||
_fillWithModuleReinforcementPackages(event) {
|
||||
let clobber = event.getModifierState('Alt');
|
||||
let ship = this.props.ship;
|
||||
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
|
||||
}
|
||||
});
|
||||
const fillAll = event.getModifierState('Alt');
|
||||
setAllEmpty(this.props.ship, fillAll, 'mrp', '2');
|
||||
this._close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Empty all on section header right click
|
||||
*/
|
||||
_contextMenu() {
|
||||
this._empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the slot React Components
|
||||
* @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._fillWithBusinessClassCabins}>{translate('pci')}</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>
|
||||
</ul>
|
||||
</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 cn from 'classnames';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import Router from '../Router';
|
||||
import Persist from '../stores/Persist';
|
||||
import { Ships } from 'coriolis-data/dist';
|
||||
import Ship from '../shipyard/Ship';
|
||||
import { ModuleNameToGroup, Insurance } from '../shipyard/Constants';
|
||||
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
||||
import { fromDetailedBuild } from '../shipyard/Serializer';
|
||||
import { Download } from './SvgIcons';
|
||||
import { outfitURL } from '../utils/UrlGenerators';
|
||||
import * as CompanionApiUtils from '../utils/CompanionApiUtils';
|
||||
import autoBind from 'auto-bind';
|
||||
import { isArray } from 'lodash';
|
||||
import { Ship } from 'ed-forge';
|
||||
|
||||
const zlib = require('pako');
|
||||
|
||||
const textBuildRegex = new RegExp('^\\[([\\w \\-]+)\\]\n');
|
||||
const lineRegex = new RegExp('^([\\dA-Z]{1,2}): (\\d)([A-I])[/]?([FGT])?([SD])? ([\\w\\- ]+)');
|
||||
const mountMap = { 'H': 4, 'L': 3, 'M': 2, 'S': 1, 'U': 0 };
|
||||
const 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() };
|
||||
}
|
||||
const STATE = {
|
||||
READY: 0,
|
||||
PARSED: 1,
|
||||
ERROR: 2,
|
||||
};
|
||||
|
||||
/**
|
||||
* Import Modal
|
||||
*/
|
||||
export default class ModalImport extends TranslatedComponent {
|
||||
static propTypes = {
|
||||
importString: PropTypes.string, // Optional: Default data for import modal
|
||||
builds: PropTypes.object, // Optional: Import object
|
||||
};
|
||||
|
||||
@@ -95,228 +28,36 @@ export default class ModalImport extends TranslatedComponent {
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props);
|
||||
autoBind(this);
|
||||
|
||||
this.state = {
|
||||
builds: props.builds,
|
||||
canEdit: !props.builds,
|
||||
loadoutEvent: null,
|
||||
comparisons: null,
|
||||
shipDiscount: null,
|
||||
moduleDiscount: null,
|
||||
errorMsg: null,
|
||||
importString: null,
|
||||
importValid: false,
|
||||
insurance: null
|
||||
status: STATE.READY,
|
||||
builds: props.builds || [],
|
||||
};
|
||||
|
||||
this._process = this._process.bind(this);
|
||||
this._import = this._import.bind(this);
|
||||
this._importBackup = this._importBackup.bind(this);
|
||||
this._importLoadout = this._importLoadout.bind(this);
|
||||
this._importDetailedArray = this._importDetailedArray.bind(this);
|
||||
this._importTextBuild = this._importTextBuild.bind(this);
|
||||
this._importCompanionApiBuild = this._importCompanionApiBuild.bind(this);
|
||||
this._validateImport = this._validateImport.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Import a Loadout event from Elite: Dangerous journal files
|
||||
* @param {Object} data Loadout event
|
||||
* @throws {string} If import fails
|
||||
*/
|
||||
_importLoadout(data) {
|
||||
if (data && data.Ship && data.Modules) {
|
||||
const deflated = zlib.deflate(JSON.stringify(data), { to: 'string' });
|
||||
let compressed = btoa(deflated);
|
||||
this.setState({ loadoutEvent: compressed });
|
||||
} else {
|
||||
throw 'Loadout event must contain Ship and Modules';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Import a Coriolis backup
|
||||
* @param {Object} importData Backup Data
|
||||
* @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
|
||||
* Import SLEF formatted builds. Sets state to a map of the builds on success
|
||||
* and flags if there was only a single build.
|
||||
*
|
||||
* @param {string} importData - Array of the list of builds.
|
||||
* @throws {string} If parse / import fails
|
||||
*/
|
||||
_importTextBuild(buildStr) {
|
||||
let buildName = textBuildRegex.exec(buildStr)[1].trim();
|
||||
let shipName = buildName.toLowerCase();
|
||||
let shipId = null;
|
||||
_importSlefBuilds(importData) {
|
||||
const builds = importData.reduce((memo, { data }) => {
|
||||
const shipModel = shipModelFromJson(data);
|
||||
const ship = shipFromLoadoutJSON(data);
|
||||
const shipTemplate = Ships[shipModel];
|
||||
const shipName = data.ShipName || shipTemplate.properties.name;
|
||||
|
||||
for (let sId in Ships) {
|
||||
if (Ships[sId].properties.name.toLowerCase() == shipName) {
|
||||
shipId = sId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const key = `Imported ${shipName}`;
|
||||
memo[shipModel] = {};
|
||||
memo[shipModel][key] = ship.toString();
|
||||
|
||||
if (!shipId) {
|
||||
throw 'No such ship found: "' + buildName + '"';
|
||||
}
|
||||
return memo;
|
||||
}, {});
|
||||
|
||||
let lines = buildStr.split('\n');
|
||||
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 });
|
||||
this.setState({ builds, singleBuild: Object.keys(builds).length === 1 });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -324,164 +65,50 @@ export default class ModalImport extends TranslatedComponent {
|
||||
* @param {SyntheticEvent} event Event
|
||||
* @throws {string} If validation fails
|
||||
*/
|
||||
_validateImport(event) {
|
||||
let importData = null;
|
||||
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;
|
||||
}
|
||||
|
||||
_parse(event) {
|
||||
const importString = event.target.value.trim();
|
||||
try {
|
||||
if (textBuildRegex.test(importString)) { // E:D Shipyard build text
|
||||
this._importTextBuild(importString);
|
||||
} else { // JSON Build 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
|
||||
this._importCompanionApiBuild(importData); // Single sihp definition
|
||||
} else if (importData.ship != null && importData.ship.modules != null && importData.ship.modules.Armour != null) { // Only the companion API has this information
|
||||
this._importCompanionApiBuild(importData.ship); // Complete API dump
|
||||
} else if (importData instanceof Array) { // Must be detailed export json
|
||||
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({ singleBuild: true });
|
||||
} else if (importData.Modules != null && importData.Modules[0] != null) {
|
||||
this._importLoadout(importData);
|
||||
} else { // Using Backup JSON
|
||||
this._importBackup(importData);
|
||||
}
|
||||
let data = JSON.parse(importString);
|
||||
if (!isArray(data)) {
|
||||
data = [data];
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
this.setState({ errorMsg: (typeof e == 'string') ? e : 'Cannot Parse the data!' });
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({ importValid: true });
|
||||
};
|
||||
const ships = data.map((item) => {
|
||||
try {
|
||||
return new Ship(item.data ? item.data : item);
|
||||
} catch (err) {
|
||||
return err;
|
||||
}
|
||||
});
|
||||
this.setState({ ships, status: STATE.PARSED });
|
||||
} catch (err) {
|
||||
this.setState({ err, status: STATE.ERROR });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process imported data
|
||||
*/
|
||||
_process() {
|
||||
let builds = null, comparisons = null;
|
||||
|
||||
if (this.state.loadoutEvent) {
|
||||
return Router.go(`/import?data=${this.state.loadoutEvent}`);
|
||||
}
|
||||
|
||||
// If only importing a single build go straight to the outfitting page
|
||||
if (this.state.singleBuild) {
|
||||
builds = this.state.builds;
|
||||
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
|
||||
};
|
||||
}
|
||||
for (const build of this.state.builds) {
|
||||
if (!build instanceof Error) {
|
||||
Persist.saveBuild(build.Ship, build.CoriolisBuildName || build.ShipName, build.compress());
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
};
|
||||
this.setState({ builds: [], status: STATE.READY });
|
||||
}
|
||||
|
||||
/**
|
||||
* Capture build name changes
|
||||
* @param {Object} item Build/Comparison import object
|
||||
* @param {SyntheticEvent} e Event
|
||||
* @param {Object} index Build/Comparison import object
|
||||
* @param {SyntheticEvent} event Event
|
||||
*/
|
||||
_changeName(item, e) {
|
||||
item.useName = e.target.value;
|
||||
this.forceUpdate();
|
||||
_changeName(index, event) {
|
||||
const { builds } = this.state;
|
||||
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
|
||||
*/
|
||||
@@ -496,100 +123,54 @@ export default class ModalImport extends TranslatedComponent {
|
||||
* @return {React.Component} Modal contents
|
||||
*/
|
||||
render() {
|
||||
let translate = this.context.language.translate;
|
||||
let state = this.state;
|
||||
let importStage;
|
||||
const { translate } = this.context.language;
|
||||
const { status, builds, err } = this.state;
|
||||
|
||||
if (!state.processed) {
|
||||
importStage = (
|
||||
<div>
|
||||
<textarea spellCheck={false} className='cb json' ref={node => this.importField = node} onChange={this._validateImport} defaultValue={this.state.importString} placeholder={translate('PHRASE_IMPORT')} />
|
||||
<button id='proceed' className='l cap' onClick={this._process} disabled={!state.importValid} >{translate('proceed')}</button>
|
||||
<div className='l warning' style={{ marginLeft:'3em' }}>{state.errorMsg}</div>
|
||||
</div>
|
||||
);
|
||||
} 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>
|
||||
);
|
||||
const buildRows = builds.map((build, i) => {
|
||||
if (build instanceof Error) {
|
||||
return <tr key={i} className='cb'>
|
||||
<td colSpan={3} className='warning'>Error: {build.name}</td>
|
||||
</tr>;
|
||||
}
|
||||
|
||||
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%' }}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style={{ textAlign: 'left' }} >{translate('ship')}</th>
|
||||
<th style={{ textAlign: 'left' }} >{translate('build name')}</th>
|
||||
<th>{translate('action')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{buildRows}
|
||||
</tbody>
|
||||
</table>
|
||||
{comparisonTable}
|
||||
<button id='import' className='cl l' onClick={this._import}><Download/> {translate('import')}</button>
|
||||
{edit}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
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>
|
||||
{importStage}
|
||||
<button className={'r dismiss cap'} onClick={this.context.hideModal}>{translate('close')}</button>
|
||||
<div>
|
||||
<textarea spellCheck={false} className='cb json' ref={node => this.importField = node} onChange={this._parse} defaultValue={this.state.importString} placeholder={translate('PHRASE_IMPORT')} />
|
||||
{status === STATE.ERROR && <div className='l warning' style={{ marginLeft:'3em' }}>{err.toString()}</div>}
|
||||
</div>
|
||||
{builds.length && <div>
|
||||
<table className='l' style={{ overflow:'hidden', margin: '1em 0', width: '100%' }}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style={{ textAlign: 'left' }} >{translate('ship')}</th>
|
||||
<th style={{ textAlign: 'left' }} >{translate('build name')}</th>
|
||||
<th>{translate('action')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{buildRows}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>}
|
||||
<button id='proceed' className='l cap' onClick={this._process}
|
||||
disabled={status !== STATE.PARSED} >
|
||||
{translate('proceed')}
|
||||
</button>
|
||||
<button className={'r dismiss cap'} onClick={this.context.hideModal}>
|
||||
{translate('close')}
|
||||
</button>
|
||||
</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>;
|
||||
}
|
||||
}
|
||||
@@ -9,8 +9,8 @@ import {
|
||||
blueprintTooltip,
|
||||
specialToolTip
|
||||
} from '../utils/BlueprintFunctions';
|
||||
import { getBlueprintInfo, getExperimentalInfo } from 'ed-forge/lib/data/blueprints';
|
||||
import { getModuleInfo } from 'ed-forge/lib/data/items';
|
||||
import { getBlueprintInfo, getExperimentalInfo } from 'ed-forge/lib/src/data/blueprints';
|
||||
import { getModuleInfo } from 'ed-forge/lib/src/data/items';
|
||||
import { SHOW } from '../shipyard/StatsMapping';
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import * as Calc from '../shipyard/Calculations';
|
||||
import PieChart from './PieChart';
|
||||
import { MountFixed, MountGimballed, MountTurret } from './SvgIcons';
|
||||
import { Ship } from 'ed-forge';
|
||||
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';
|
||||
|
||||
/**
|
||||
@@ -292,8 +291,8 @@ export default class Offence extends TranslatedComponent {
|
||||
<tr className='main'>
|
||||
<th rowSpan='2' className='sortable' onClick={sortOrder.bind(this, 'classRating')}>{translate('weapon')}</th>
|
||||
<th colSpan='1'>{translate('overall')}</th>
|
||||
<th colSpan='2'>{translate('opponent\'s shields')}</th>
|
||||
<th colSpan='2'>{translate('opponent\'s armour')}</th>
|
||||
<th colSpan='3'>{translate('opponent\'s shields')}</th>
|
||||
<th colSpan='3'>{translate('opponent\'s armour')}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th className='lft sortable' onMouseOver={termtip.bind(null, 'TT_EFFECTIVE_SDPS_SHIELDS')}
|
||||
@@ -360,6 +359,7 @@ export default class Offence extends TranslatedComponent {
|
||||
</span>
|
||||
</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
|
||||
@@ -145,12 +145,11 @@ export default class OutfittingSubpages extends TranslatedComponent {
|
||||
<h1>{translate('movement profile')}</h1>
|
||||
<Movement code={code} ship={ship} boost={boost} pips={pips} />
|
||||
</div>
|
||||
<div className='group third'>
|
||||
<div className='group half'>
|
||||
<h1>{translate('damage to opponent\'s shields')}</h1>
|
||||
<WeaponDamageChart code={code} ship={ship} opponentDefence={opponent.getShield()} engagementRange={engagementRange} />
|
||||
</div>
|
||||
|
||||
<div className='group third'>
|
||||
<div className='group half'>
|
||||
<h1>{translate('damage to opponent\'s hull')}</h1>
|
||||
<WeaponDamageChart code={code} ship={ship} opponentDefence={opponent.getArmour()} engagementRange={engagementRange} />
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@ import cn from 'classnames';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import { wrapCtxMenu } from '../utils/UtilityFunctions';
|
||||
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';
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,9 +6,10 @@ import cn from 'classnames';
|
||||
import { Warning } from './SvgIcons';
|
||||
|
||||
import { ShipProps } from 'ed-forge';
|
||||
import { BOOST_INTERVAL, MINIMUM_MASS } from 'ed-forge/lib/src/ship-stats';
|
||||
const {
|
||||
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
|
||||
} = ShipProps;
|
||||
|
||||
@@ -48,6 +49,7 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
||||
|
||||
const speed = ship.get(SPEED);
|
||||
const shipBoost = ship.get(BOOST_SPEED);
|
||||
const boostInterval = ship.get(BOOST_INTERVAL);
|
||||
const canThrust = 0 < speed;
|
||||
const canBoost = canThrust && !isNaN(shipBoost);
|
||||
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 damageMetrics = ship.get(DAMAGE_METRICS);
|
||||
const moduleProtectionMetrics = ship.get(MODULE_PROTECTION_METRICS);
|
||||
const timeToDrain = damageMetrics.timeToDrain[8];
|
||||
const timeToDrain = damageMetrics.timeToDrain;
|
||||
|
||||
const shieldGenerator = ship.getShieldGenerator();
|
||||
const sgClassNames = cn({
|
||||
@@ -74,8 +76,6 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
||||
this.state = { shieldColour };
|
||||
|
||||
const jumpRangeMetrics = ship.getMetrics(JUMP_METRICS);
|
||||
// TODO:
|
||||
const canJump = true;
|
||||
|
||||
return <div id='summary'>
|
||||
<div style={{ display: 'table', width: '100%' }}>
|
||||
@@ -85,7 +85,7 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
||||
<tr className='main'>
|
||||
<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 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('integrity')}</th>
|
||||
<th rowSpan={2}>{translate('DPS')}</th>
|
||||
@@ -100,7 +100,8 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
||||
<th rowSpan={2}>{translate('crew')}</th>
|
||||
<th onMouseEnter={termtip.bind(null, 'mass lock factor', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('MLF')}</th>
|
||||
<th onMouseEnter={termtip.bind(null, 'TT_SUMMARY_BOOST_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>
|
||||
<th className="lft">{translate('max')}</th>
|
||||
@@ -129,37 +130,19 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
||||
}</td>
|
||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_MAX_SINGLE_JUMP', { cap: 0 })}
|
||||
onMouseLeave={hide}
|
||||
>{canJump ?
|
||||
// TODO:
|
||||
<span>{NaN}{u.LY}</span> :
|
||||
<span className='warning'>0<Warning/></span>
|
||||
}</td>
|
||||
>{<span>{f2(jumpRangeMetrics.jumpRangeMax)}{u.LY}</span>}</td>
|
||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_SINGLE_JUMP', { cap: 0 })}
|
||||
onMouseLeave={hide}
|
||||
>{canJump ?
|
||||
// TODO:
|
||||
<span>{NaN}{u.LY}</span> :
|
||||
<span className='warning'>0<Warning/></span>
|
||||
}</td>
|
||||
>{<span>{f2(jumpRangeMetrics.jumpRangeUnladen)}{u.LY}</span>}</td>
|
||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_SINGLE_JUMP', { cap: 0 })}
|
||||
onMouseLeave={hide}
|
||||
>{canJump ?
|
||||
<span>{f2(jumpRangeMetrics.jumpRange)}{u.LY}</span> :
|
||||
<span className='warning'>0<Warning/></span>
|
||||
}</td>
|
||||
>{<span>{f2(jumpRangeMetrics.jumpRangeLaden)}{u.LY}</span>}</td>
|
||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_TOTAL_JUMP', { cap: 0 })}
|
||||
onMouseLeave={hide}
|
||||
>{canJump ?
|
||||
// TODO:
|
||||
<span>{NaN}{u.LY}</span> :
|
||||
<span className='warning'>0 <Warning/></span>
|
||||
}</td>
|
||||
>{<span>{f2(jumpRangeMetrics.totalRangeUnladen)}{u.LY}</span>}</td>
|
||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_TOTAL_JUMP', { cap: 0 })}
|
||||
onMouseLeave={hide}
|
||||
>{canJump ?
|
||||
<span>{f2(jumpRangeMetrics.totalRange)}{u.LY}</span> :
|
||||
<span className='warning'>0<Warning/></span>
|
||||
}</td>
|
||||
>{<span>{f2(jumpRangeMetrics.totalRangeLaden)}{u.LY}</span>}</td>
|
||||
<td className={sgClassNames}
|
||||
onMouseEnter={termtip.bind(null, sgTooltip, { cap: 0 })}
|
||||
onMouseLeave={hide}
|
||||
@@ -185,17 +168,16 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
||||
>{ship.readProp('hullmass')}{u.T}</td>
|
||||
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_MASS', { cap: 0 })}
|
||||
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 })}
|
||||
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>{ship.readMeta('crew')}</td>
|
||||
<td>{ship.readProp('masslock')}</td>
|
||||
{/* TODO: boost intervall */}
|
||||
<td>{NaN}</td>
|
||||
<td>{time(boostInterval)}</td>
|
||||
{/* TODO: resting heat */}
|
||||
<td>{NaN}</td>
|
||||
{/* <td>{NaN}</td> */}
|
||||
</tr>
|
||||
</tbody>
|
||||
</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.thermal.damageMultiplier || 0)}{u.MJ}</td>
|
||||
<td></td>
|
||||
<td>{formats.time(sgMetrics.recover) || translate('Never')}</td>
|
||||
<td>{formats.time(sgMetrics.recharge) || translate('Never')}</td>
|
||||
<td>{isNaN(sgMetrics.recover) ? translate('Never') : formats.time(sgMetrics.recover)}</td>
|
||||
<td>{isNaN(sgMetrics.recharge) ? translate('Never') : formats.time(sgMetrics.recharge)}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<thead>
|
||||
|
||||
@@ -5,11 +5,10 @@ import cn from 'classnames';
|
||||
import { ListModifications, Modified } from './SvgIcons';
|
||||
import AvailableModulesMenu from './AvailableModulesMenu';
|
||||
import ModificationsMenu from './ModificationsMenu';
|
||||
import { diffDetails } from '../utils/SlotFunctions';
|
||||
import { stopCtxPropagation, wrapCtxMenu } from '../utils/UtilityFunctions';
|
||||
import { blueprintTooltip } from '../utils/BlueprintFunctions';
|
||||
import { Module } from 'ed-forge';
|
||||
import { TYPES } from 'ed-forge/lib/data/slots';
|
||||
import { TYPES } from 'ed-forge/lib/src/data/slots';
|
||||
import autoBind from 'auto-bind';
|
||||
import { toPairs } from 'lodash';
|
||||
|
||||
@@ -167,7 +166,7 @@ export default class Slot extends TranslatedComponent {
|
||||
*/
|
||||
_getMaxClassLabel() {
|
||||
const { m } = this.props;
|
||||
let size = m.getSize();
|
||||
let size = m.getSizeNum();
|
||||
switch (true) {
|
||||
case m.getSlot() === 'armour':
|
||||
return '';
|
||||
@@ -234,7 +233,6 @@ export default class Slot extends TranslatedComponent {
|
||||
this.context.closeMenu();
|
||||
}}
|
||||
warning={warning}
|
||||
// diffDetails={diffDetails.bind(ship, this.context.language)}
|
||||
/>}
|
||||
{selected && menuIndex === 1 &&
|
||||
<ModificationsMenu m={m} propsToShow={propsToShow}
|
||||
|
||||
@@ -2,7 +2,6 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import { wrapCtxMenu } from '../utils/UtilityFunctions';
|
||||
import { canMount } from '../utils/SlotFunctions';
|
||||
import { Equalizer } from '../components/SvgIcons';
|
||||
import cn from 'classnames';
|
||||
import { Ship } from 'ed-forge';
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import React from 'react';
|
||||
import cn from 'classnames';
|
||||
import SlotSection from './SlotSection';
|
||||
import Slot from './Slot';
|
||||
import Module from '../shipyard/Module';
|
||||
import * as ShipRoles from '../shipyard/ShipRoles';
|
||||
import autoBind from 'auto-bind';
|
||||
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;
|
||||
|
||||
/**
|
||||
@@ -24,65 +22,35 @@ export default class StandardSlotSection extends SlotSection {
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the lightest/optimal available standard modules
|
||||
* Resets all modules of the ship
|
||||
*/
|
||||
_optimizeStandard() {
|
||||
this.props.ship.useLightestStandard();
|
||||
_emptyAll() {
|
||||
this.props.ship.getModules().forEach((slot) => slot.reset());
|
||||
this._close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill all standard slots with the specificed rating (using max class)
|
||||
* @param {Boolean} shielded True if shield generator should be included
|
||||
* @param {integer} bulkheadIndex Bulkhead to use see Constants.BulkheadNames
|
||||
* Sets all modules to a specific rating
|
||||
* @param {string} rating Module rating to set
|
||||
* @param {string} fsdPPException Custom rating for FSD
|
||||
*/
|
||||
_multiPurpose(shielded, bulkheadIndex) {
|
||||
ShipRoles.multiPurpose(this.props.ship, shielded, bulkheadIndex);
|
||||
_nRated(rating, fsdPPException) {
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param {Module} m Module to create the slot for
|
||||
@@ -136,17 +104,13 @@ export default class StandardSlotSection extends SlotSection {
|
||||
const { translate } = this.context.language;
|
||||
return <div className='select' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
|
||||
<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>
|
||||
<div className='select-group cap'>{translate('roles')}</div>
|
||||
<div className='select-group cap'>{translate('core')}</div>
|
||||
<ul>
|
||||
<li className='lc' tabIndex="0" onClick={this._multiPurpose.bind(this, false, 0)}>{translate('Multi-purpose')}</li>
|
||||
<li className='lc' tabIndex="0" onClick={this._multiPurpose.bind(this, true, 2)}>{translate('Combat')}</li>
|
||||
<li className='lc' tabIndex="0" onClick={this._optimizeCargo.bind(this, true)}>{translate('Trader')}</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>
|
||||
<li className='lc' tabIndex="0" onClick={this._nRated.bind(this, '5', undefined)}>{translate('A-rated')}</li>
|
||||
<li className='lc' tabIndex="0" onClick={this._nRated.bind(this, '2', undefined)}>{translate('D-rated')}</li>
|
||||
<li className='lc' tabIndex="0" onClick={this._nRated.bind(this, '2', '5')}>{translate('D-rated + A-rated FSD/PP')}</li>
|
||||
</ul>
|
||||
</div>;
|
||||
}
|
||||
|
||||
@@ -21,31 +21,26 @@ export default class UtilitySlotSection extends SlotSection {
|
||||
* Empty all utility slots and close the menu
|
||||
*/
|
||||
_empty() {
|
||||
this.props.ship.emptyUtility();
|
||||
this.props.onChange();
|
||||
this.props.ship.getUtilities().forEach((slot) => slot.reset());
|
||||
this._close();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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} name Module name
|
||||
* @param {Synthetic} event Event
|
||||
*/
|
||||
_use(group, rating, name, event) {
|
||||
this.props.ship.useUtility(group, rating, name, event.getModifierState('Alt'));
|
||||
this.props.onChange();
|
||||
_use(type, rating, event) {
|
||||
const fillAll = event.getModifierState('Alt');
|
||||
for (const slot of this.props.ship.getUtilities(undefined, true)) {
|
||||
if (slot.isEmpty() || fillAll) {
|
||||
slot.setItem(type, '', rating);
|
||||
}
|
||||
}
|
||||
this._close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Empty all utility slots on right-click
|
||||
*/
|
||||
_contextMenu() {
|
||||
this._empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create all HardpointSlots (React component) for the slots
|
||||
* @return {Array} Array of HardpointSlots
|
||||
@@ -58,8 +53,6 @@ export default class UtilitySlotSection extends SlotSection {
|
||||
for (let h of ship.getUtilities(undefined, true)) {
|
||||
slots.push(<Slot
|
||||
key={h.object.Slot}
|
||||
maxClass={h.getSize()}
|
||||
onChange={this.props.onChange}
|
||||
currentMenu={currentMenu}
|
||||
drag={this._drag.bind(this, h)}
|
||||
dragOver={this._dragOverSlot.bind(this, h)}
|
||||
@@ -91,23 +84,23 @@ export default class UtilitySlotSection extends SlotSection {
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('sb')}</div>
|
||||
<ul>
|
||||
<li className='c' tabIndex='0' onClick={_use.bind(this, 'sb', 'A', null)}>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, 'sb', 'C', null)}>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, 'sb', 'E', null)}>E</li>
|
||||
<li className='c' tabIndex='0' onClick={_use.bind(this, 'shieldbooster', '5')}>A</li>
|
||||
<li className='c' tabIndex='0' onClick={_use.bind(this, 'shieldbooster', '4')}>B</li>
|
||||
<li className='c' tabIndex='0' onClick={_use.bind(this, 'shieldbooster', '3')}>C</li>
|
||||
<li className='c' tabIndex='0' onClick={_use.bind(this, 'shieldbooster', '2')}>D</li>
|
||||
<li className='c' tabIndex='0' onClick={_use.bind(this, 'shieldbooster', '1')}>E</li>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('hs')}</div>
|
||||
<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>
|
||||
<div className='select-group cap'>{translate('ch')}</div>
|
||||
<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>
|
||||
<div className='select-group cap'>{translate('po')}</div>
|
||||
<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>
|
||||
</div>;
|
||||
}
|
||||
|
||||
@@ -2,8 +2,7 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import LineChart from '../components/LineChart';
|
||||
import * as Calc from '../shipyard/Calculations';
|
||||
import { moduleReduce } from 'ed-forge/lib/helper';
|
||||
import { moduleReduce } from 'ed-forge/lib/src/helper';
|
||||
import { chain, keys, mapValues, values } from 'lodash';
|
||||
|
||||
const DAMAGE_DEALT_COLORS = ['#FFFFFF', '#FF0000', '#00FF00', '#7777FF', '#FFFF00', '#FF00FF', '#00FFFF', '#777777'];
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -205,7 +205,7 @@
|
||||
"internal protection": "Internal protection",
|
||||
"external protection": "External protection",
|
||||
"engagement range": "Engagement range",
|
||||
"boost interval": "Boost intervall",
|
||||
"boost interval": "Boost interval",
|
||||
"total": "Total",
|
||||
"ammo": "Ammunition maximum",
|
||||
"boot": "Boot time",
|
||||
|
||||
@@ -2,15 +2,15 @@ export const formats = {
|
||||
decimal: ',',
|
||||
thousands: '.',
|
||||
grouping: [3],
|
||||
currency: ['', ' €'],
|
||||
currency: ['$', ''],
|
||||
dateTime: '%A, %e de %B de %Y, %X',
|
||||
date: '%d/%m/%Y',
|
||||
time: '%H:%M:%S',
|
||||
periods: ['AM', 'PM'],
|
||||
days: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes', 'sábado'],
|
||||
shortDays: ['dom', 'lun', 'mar', 'mié', 'jue', 'vie', 'sáb'],
|
||||
months: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio', 'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre'],
|
||||
shortMonths: ['ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul', 'ago', 'sep', 'oct', 'nov', 'dic']
|
||||
days: ['domingo', 'segunda', 'terça', 'quarta', 'quinta', 'sexta', 'sábado'],
|
||||
shortDays: ['dom', 'seg', 'ter', 'qua', 'qui', 'sex', 'sab'],
|
||||
months: ['janeiro', 'fevereiro', 'março', 'abril', 'maio', 'junho', 'julho', 'agosto', 'setembro', 'outubro', 'novembro', 'dezembro'],
|
||||
shortMonths: ['jan', 'fev', 'mar', 'abr', 'mai', 'jun', 'jul', 'ago', 'set', 'out', 'nov', 'dez']
|
||||
};
|
||||
|
||||
export { default as terms } from './pt.json';
|
||||
|
||||
File diff suppressed because one or more lines are too long
1157
src/app/i18n/ru.json
1157
src/app/i18n/ru.json
File diff suppressed because it is too large
Load Diff
@@ -94,39 +94,6 @@ export default class AboutPage extends Page {
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
|
||||
<h3>Supporting Coriolis</h3>
|
||||
<p>
|
||||
Coriolis is an open source project, and I work on it in my free time.
|
||||
I have set up a patreon at{' '}
|
||||
<a href="https://www.patreon.com/coriolis_elite">
|
||||
patreon.com/coriolis_elite
|
||||
</a>
|
||||
, which will be used to keep Coriolis up to date and the servers
|
||||
running.
|
||||
</p>
|
||||
<form
|
||||
action="https://www.paypal.com/cgi-bin/webscr"
|
||||
method="post"
|
||||
target="_top"
|
||||
>
|
||||
<input type="hidden" name="cmd" value="_s-xclick" />
|
||||
<input type="hidden" name="hosted_button_id" value="SJBKT2SWEEU68" />
|
||||
<input
|
||||
type="image"
|
||||
src="https://www.paypalobjects.com/en_AU/i/btn/btn_donate_SM.gif"
|
||||
border="0"
|
||||
name="submit"
|
||||
alt="PayPal – The safer, easier way to pay online!"
|
||||
/>
|
||||
<img
|
||||
alt=""
|
||||
border="0"
|
||||
src="https://www.paypalobjects.com/en_AU/i/scr/pixel.gif"
|
||||
width="1"
|
||||
height="1"
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import Page from './Page';
|
||||
import Router from '../Router';
|
||||
import Persist from '../stores/Persist';
|
||||
import { Factory, Ship } from 'ed-forge';
|
||||
import { OBJECT_EVENT } from 'ed-forge/lib/Ship';
|
||||
import { OBJECT_EVENT } from 'ed-forge/lib/src/Ship';
|
||||
import { outfitURL } from '../utils/UrlGenerators';
|
||||
import {
|
||||
FloppyDisk,
|
||||
@@ -16,7 +16,6 @@ import {
|
||||
LinkIcon,
|
||||
ShoppingIcon,
|
||||
MatIcon,
|
||||
OrbisIcon
|
||||
} from '../components/SvgIcons';
|
||||
import ShipSummaryTable from '../components/ShipSummaryTable';
|
||||
import StandardSlotSection from '../components/StandardSlotSection';
|
||||
@@ -26,7 +25,6 @@ import UtilitySlotSection from '../components/UtilitySlotSection';
|
||||
import OutfittingSubpages from '../components/OutfittingSubpages';
|
||||
import ModalExport from '../components/ModalExport';
|
||||
import ModalPermalink from '../components/ModalPermalink';
|
||||
import ModalOrbis from '../components/ModalOrbis';
|
||||
import autoBind from 'auto-bind';
|
||||
import { assign } from 'lodash';
|
||||
import EDEngineerButton from '../components/EDEngineerButton';
|
||||
@@ -217,8 +215,8 @@ export default class OutfittingPage extends Page {
|
||||
const { ship, buildName, newBuildName } = this.state;
|
||||
const shipId = ship.getShipType();
|
||||
|
||||
// If this is a stock ship the code won't be set, so ensure that we have it
|
||||
const code = this.state.code || ship.compress();
|
||||
ship.write('CoriolisBuildName', buildName);
|
||||
const code = ship.compress();
|
||||
|
||||
Persist.saveBuild(shipId, newBuildName, code);
|
||||
this._setRoute();
|
||||
@@ -358,23 +356,6 @@ export default class OutfittingPage extends Page {
|
||||
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
|
||||
*/
|
||||
@@ -579,13 +560,6 @@ export default class OutfittingPage extends Page {
|
||||
>
|
||||
<LinkIcon className="lg" />
|
||||
</button>
|
||||
<button
|
||||
onClick={this._genOrbis}
|
||||
onMouseOver={termtip.bind(null, 'PHASE_UPLOAD_ORBIS')}
|
||||
onMouseOut={hide}
|
||||
>
|
||||
<OrbisIcon className="lg" />
|
||||
</button>
|
||||
<EDEngineerButton ship={ship} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -61,6 +61,14 @@ export default class Page extends React.Component {
|
||||
*/
|
||||
componentDidMount() {
|
||||
document.title = this.state.title || 'Coriolis';
|
||||
try {
|
||||
(window.adsbygoogle = window.adsbygoogle || []).push({
|
||||
google_ad_client: "ca-pub-3709458261881414",
|
||||
enable_page_level_ads: true
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import Page from './Page';
|
||||
import cn from 'classnames';
|
||||
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 Link from '../components/Link';
|
||||
|
||||
@@ -16,7 +16,7 @@ function shipSummary(shipId) {
|
||||
let ship = Factory.newShip(shipId);
|
||||
|
||||
let coreSizes = ship.readMeta('coreSizes');
|
||||
let { jumpRange, totalRange } = ship.getMetrics(JUMP_METRICS);
|
||||
let { jumpRangeLaden, totalRange } = ship.getMetrics(JUMP_METRICS);
|
||||
let summary = {
|
||||
agility: ship.readProp('pitch') + ship.readProp('yaw') + ship.readProp('roll'),
|
||||
baseArmour: ship.readProp('basearmour'),
|
||||
@@ -32,7 +32,7 @@ function shipSummary(shipId) {
|
||||
masslock: ship.readProp('masslock'),
|
||||
hp: [0, 0, 0, 0, 0], // Utility, Small, Medium, Large, Huge
|
||||
int: [0, 0, 0, 0, 0, 0, 0, 0], // Sizes 1 - 8
|
||||
jumpRange,
|
||||
jumpRangeLaden,
|
||||
pitch: ship.readProp('pitch'),
|
||||
retailCost: ship.readMeta('retailCost'),
|
||||
roll: ship.readProp('roll'),
|
||||
@@ -52,13 +52,13 @@ function shipSummary(shipId) {
|
||||
|
||||
// Count Hardpoints by class
|
||||
ship.getHardpoints(undefined, true).forEach(hardpoint => {
|
||||
summary.hp[hardpoint.getSize()]++;
|
||||
summary.hp[hardpoint.getSizeNum()]++;
|
||||
summary.hpCount++;
|
||||
});
|
||||
// Count Internal Compartments by class
|
||||
let maxCargo = 0, maxPassengers = 0;
|
||||
ship.getInternals(undefined, true).forEach(internal => {
|
||||
const size = String(internal.getSize());
|
||||
const size = String(internal.getSizeNum());
|
||||
summary.int[size]++;
|
||||
summary.intCount++;
|
||||
|
||||
@@ -195,7 +195,7 @@ export default class ShipyardPage extends Page {
|
||||
<td className="ri">{fInt(s.pitch)}</td>
|
||||
<td className="ri">{fInt(s.yaw)}</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">{fInt(s.hardness)}</td>
|
||||
<td className="ri">{fInt(s.baseArmour)}</td>
|
||||
@@ -400,7 +400,7 @@ export default class ShipyardPage extends Page {
|
||||
<th className="sortable" onClick={sortShips('roll')}>
|
||||
{translate('roll')}
|
||||
</th>
|
||||
<th className="sortable lft" onClick={sortShips('jumpRange')}>
|
||||
<th className="sortable lft" onClick={sortShips('jumpRangeLaden')}>
|
||||
{translate('jump')}
|
||||
</th>
|
||||
<th className="sortable" onClick={sortShips('totalRange')}>
|
||||
@@ -466,7 +466,7 @@ export default class ShipyardPage extends Page {
|
||||
<th className="sortable" onClick={sortShips('roll')}>
|
||||
{units['°/s']}
|
||||
</th>
|
||||
<th className="sortable lft" onClick={sortShips('jumpRange')}>
|
||||
<th className="sortable lft" onClick={sortShips('jumpRangeLaden')}>
|
||||
{units.LY}
|
||||
</th>
|
||||
<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,145 +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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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';
|
||||
|
||||
const LS_KEY_BUILDS = 'builds';
|
||||
const LS_KEY_COMPARISONS = 'comparisons';
|
||||
const LS_KEY_LANG = 'NG_TRANSLATE_LANG_KEY';
|
||||
const LS_KEY_COST_TAB = 'costTab';
|
||||
const LS_KEY_CMDR_NAME = 'cmdrName';
|
||||
@@ -13,9 +12,7 @@ const LS_KEY_MOD_DISCOUNT = 'moduleDiscount';
|
||||
const LS_KEY_STATE = 'state';
|
||||
const LS_KEY_SIZE_RATIO = 'sizeRatio';
|
||||
const LS_KEY_TOOLTIPS = 'tooltips';
|
||||
const LS_KEY_MODULE_RESISTANCES = 'moduleResistances';
|
||||
const LS_KEY_ROLLS = 'matsPerGrade';
|
||||
const LS_KEY_ORBIS = 'orbis';
|
||||
|
||||
let LS;
|
||||
|
||||
@@ -85,7 +82,6 @@ export class Persist extends EventEmitter {
|
||||
LS = null;
|
||||
}
|
||||
|
||||
let moduleResistances = _get(LS_KEY_MODULE_RESISTANCES);
|
||||
let matsPerGrade = _get(LS_KEY_ROLLS);
|
||||
let cmdrName = _get(LS_KEY_CMDR_NAME);
|
||||
let tips = _get(LS_KEY_TOOLTIPS);
|
||||
@@ -93,16 +89,13 @@ export class Persist extends EventEmitter {
|
||||
let shipDiscount = _get(LS_KEY_SHIP_DISCOUNT);
|
||||
let moduleDiscount = _get(LS_KEY_MOD_DISCOUNT);
|
||||
let buildJson = _get(LS_KEY_BUILDS);
|
||||
let comparisonJson = _get(LS_KEY_COMPARISONS);
|
||||
|
||||
this.orbisCreds = _get(LS_KEY_ORBIS) || { email: '', password: '' };
|
||||
this.onStorageChange = this.onStorageChange.bind(this);
|
||||
this.langCode = _getString(LS_KEY_LANG) || 'en';
|
||||
this.insurance = insurance && Insurance[insurance.toLowerCase()] !== undefined ? insurance : 'standard';
|
||||
this.shipDiscount = !isNaN(shipDiscount) && shipDiscount < 1 ? shipDiscount * 1 : 0;
|
||||
this.moduleDiscount = !isNaN(moduleDiscount) && moduleDiscount < 1 ? moduleDiscount * 1 : 0;
|
||||
this.builds = buildJson && typeof buildJson == 'object' ? buildJson : {};
|
||||
this.comparisons = comparisonJson && typeof comparisonJson == 'object' ? comparisonJson : {};
|
||||
this.costTab = _getString(LS_KEY_COST_TAB);
|
||||
this.outfittingTab = _getString(LS_KEY_OUTFITTING_TAB);
|
||||
this.state = _get(LS_KEY_STATE);
|
||||
@@ -116,7 +109,6 @@ export class Persist extends EventEmitter {
|
||||
};
|
||||
this.cmdrName = cmdrName || { selected: '', cmdrs: [] };
|
||||
this.tooltipsEnabled = tips === null ? true : tips;
|
||||
this.moduleResistancesEnabled = moduleResistances === null ? true : moduleResistances;
|
||||
|
||||
if (LS) {
|
||||
window.addEventListener('storage', this.onStorageChange);
|
||||
@@ -137,10 +129,6 @@ export class Persist extends EventEmitter {
|
||||
this.builds = newValue ? JSON.parse(newValue) : {};
|
||||
this.emit('builds');
|
||||
break;
|
||||
case LS_KEY_COMPARISONS:
|
||||
this.comparisons = newValue ? JSON.parse(newValue) : {};
|
||||
this.emit('comparisons');
|
||||
break;
|
||||
case LS_KEY_LANG:
|
||||
this.langCode = newValue;
|
||||
this.emit('language', newValue);
|
||||
@@ -161,18 +149,10 @@ export class Persist extends EventEmitter {
|
||||
this.tooltipsEnabled = !!newValue && newValue.toLowerCase() == 'true';
|
||||
this.emit('tooltips', this.tooltipsEnabled);
|
||||
break;
|
||||
case LS_KEY_MODULE_RESISTANCES:
|
||||
this.moduleResistancesEnabled = !!newValue && newValue.toLowerCase() == 'true';
|
||||
this.emit('moduleresistances', this.moduleResistancesEnabled);
|
||||
break;
|
||||
case LS_KEY_ROLLS:
|
||||
this.matsPerGrade = JSON.parse(newValue);
|
||||
this.emit('matsPerGrade', this.matsPerGrade);
|
||||
break;
|
||||
case LS_KEY_ORBIS:
|
||||
this.orbisCreds = JSON.parse(newValue);
|
||||
this.emit('orbis', this.orbisCreds);
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
// On JSON.Parse Error - don't sync or do anything
|
||||
@@ -198,24 +178,6 @@ export class Persist extends EventEmitter {
|
||||
this.emit('language', langCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current orbis.zone credentials
|
||||
* @return {String} language code
|
||||
*/
|
||||
getOrbisCreds() {
|
||||
return this.orbisCreds;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update and save the orbis.zone credentials
|
||||
* @param {Object} creds object with username and password properties.
|
||||
*/
|
||||
setOrbisCreds(creds) {
|
||||
this.langCode = creds;
|
||||
_put(LS_KEY_ORBIS, creds);
|
||||
this.emit('orbis', creds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show tooltips setting
|
||||
* @param {boolean} show Optional - update setting
|
||||
@@ -231,21 +193,6 @@ export class Persist extends EventEmitter {
|
||||
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.
|
||||
*
|
||||
@@ -334,97 +281,16 @@ export class Persist extends EventEmitter {
|
||||
delete this.builds[shipId];
|
||||
}
|
||||
_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');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist a comparison in 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
|
||||
* Delete all builds from localStorage
|
||||
*/
|
||||
deleteAll() {
|
||||
this.builds = {};
|
||||
this.comparisons = {};
|
||||
_put(LS_KEY_BUILDS, {});
|
||||
_put(LS_KEY_COMPARISONS, {});
|
||||
this.emit('deletedAll');
|
||||
}
|
||||
|
||||
@@ -435,7 +301,6 @@ export class Persist extends EventEmitter {
|
||||
getAll() {
|
||||
let data = {};
|
||||
data[LS_KEY_BUILDS] = this.getBuilds();
|
||||
data[LS_KEY_COMPARISONS] = this.getComparisons();
|
||||
data[LS_KEY_INSURANCE] = this.getInsurance();
|
||||
data[LS_KEY_SHIP_DISCOUNT] = this.shipDiscount;
|
||||
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,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Modifications } from 'coriolis-data/dist';
|
||||
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 { fromPairs, keys, uniq } from 'lodash';
|
||||
|
||||
/**
|
||||
@@ -116,20 +115,3 @@ export function blueprintTooltip(language, m, previewBP, previewGrade) {
|
||||
</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);
|
||||
}
|
||||
|
||||
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';
|
||||
/**
|
||||
* 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
|
||||
* @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>
|
||||
<head>
|
||||
<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">
|
||||
<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">
|
||||
@@ -32,7 +27,7 @@
|
||||
<meta name="msapplication-TileImage" content="/mstile-144x144.png">
|
||||
<meta name="msapplication-config" content="/browserconfig.xml">
|
||||
<meta name="theme-color" content="#000000">
|
||||
|
||||
<!-- <script data-ad-client="ca-pub-3709458261881414" async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script> -->
|
||||
<script>
|
||||
window.CORIOLIS_GAPI_KEY = '<%- htmlWebpackPlugin.options.gapiKey %>';
|
||||
window.CORIOLIS_VERSION = '<%- htmlWebpackPlugin.options.version %>';
|
||||
@@ -59,7 +54,8 @@
|
||||
</script>
|
||||
</head>
|
||||
<body style="background-color:#000;">
|
||||
<section id="coriolis"></section>
|
||||
<section id="coriolis">
|
||||
|
||||
</section>
|
||||
</body>
|
||||
</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