Compare commits

...

122 Commits

Author SHA1 Message Date
felixlinker
934593fe2a Migrate changes of 87fc3b6cbb 2019-10-08 14:00:54 +02:00
willyb321
e4a826592f remove ads, trial didnt work 2019-09-27 07:15:13 +10:00
willyb321
cee4c32551 Merge branch 'master' into develop 2019-09-23 08:47:08 +10:00
willyb321
081d8fb86a google analytics 2019-09-23 08:41:34 +10:00
willyb321
3dfd563d90 testing ads again 2019-09-23 08:39:53 +10:00
William
fd08cd219c Merge pull request #546 from EDCD/fix/bugs
Various bug fixes
2019-09-18 05:59:58 +10:00
sascha-dev
6a15326d31 Wrong translation corrected. 2019-09-15 12:46:41 +02:00
sascha-dev
608ecc51b7 german translation extended 2019-09-15 12:46:41 +02:00
sascha-dev
fcef26ebbb german translation extended. fixed some typos. 2019-09-15 12:46:41 +02:00
Felix Linker
ba6d758ed5 Import plasma slug correctly
References #450
2019-09-13 22:36:56 +02:00
Felix Linker
43aa3e4e79 Merge pull request #541 from monopoint/export_select
bugfix: unable to copy from export textarea (#540)
2019-09-11 22:27:23 +02:00
Unknown
18f0e060a7 fix for scrollbar on demand 2019-09-11 22:16:55 +02:00
EspenMH
c7547e8baf shipyard: group compared, scrollbar on demand, borders 2019-09-11 22:16:55 +02:00
spinmh
ffff242abe userselect: auto does not resove correct if parent has user-select: none (it has)
set explicitly to "text"
2019-09-07 18:42:48 +02:00
Felix Linker
b44c66b986 Merge pull request #536 from monopoint/develop
448: compare highlights in shipyard
2019-08-25 13:32:08 +02:00
Felix Linker
ae77ec6256 Implement blueprint changes to focused weapon 2019-08-24 15:36:51 +02:00
Felix Linker
4f1e32b154 Move sustained factor calculation into own function
Closes #517
2019-08-24 14:16:50 +02:00
Felix Linker
af37c2bfc5 Fix import falloff
References #535
2019-08-24 14:16:50 +02:00
felixlinker
5d4ab6f2ad Disabling guardian shield/module/hull reinforcement packages has an effect now
Closes #523
2019-08-24 14:16:49 +02:00
felixlinker
0c9db53057 Use shield and armour metrics in ship summary table 2019-08-24 14:16:49 +02:00
felixlinker
b689605ac2 Maximum active limpets does not show twice now
Closes #532
2019-08-19 18:59:11 +02:00
felixlinker
baab91e371 Rename "Boost Time" to "Boost Interval"
Closes #537
2019-08-19 18:52:15 +02:00
EspenMH
70e69c7099 448: removed double quotes in js. slight text highlight on compared ships in shipyard 2019-08-15 15:06:37 +02:00
spinmh
f4534fd3eb Remove manual ShouldComponentUpdate eval 2019-08-15 02:19:38 +02:00
spinmh
93594e1a65 Compare Highlight in Shipyard 2019-08-15 02:18:00 +02:00
William
b5e449ea54 Merge pull request #525 from TranslucentSabre/feature/fix_module_discount_import
Fix backup import to properly handle module discount.
2019-07-01 08:18:07 +10:00
William
0ff4b849aa Update Header.jsx 2019-06-29 11:34:58 +10:00
William
b99e38043f Update Header.jsx 2019-06-29 11:34:03 +10:00
Timothy Myers
28e3a59473 Fix backup import to properly handle module discount. 2019-06-24 11:22:10 -05:00
Felix Linker
b20290fb60 Add mention of compiled and hosted coriolis-data-files in README 2019-05-27 21:18:54 +02:00
willyb321
2734beb6f8 Merge remote-tracking branch 'origin/master' 2019-05-23 16:59:02 +10:00
willyb321
345eec528c RIP 2019-05-23 16:58:14 +10:00
Felix Linker
7a17e18a76 Remove undefined variable 2019-05-19 23:22:07 +02:00
willyb321
4697677457 Merge branch 'develop' 2019-05-20 06:36:43 +10:00
felixlinker
7d8a5a1368 Clear original slot if an experimental is copied and exceeds the allowed number
References #367
2019-05-19 16:01:48 +02:00
felixlinker
dd7402bd0e Allow experimental modules to be replaced with such even if 4 are present
References #367
2019-05-19 16:00:16 +02:00
felixlinker
65592b0fc6 Allow any thruster to be applied
Closes #512
2019-05-19 15:59:33 +02:00
felixlinker
0ab66023a6 Update calculation of SHPS/SEPS to match DPS~SDPS
References #504
2019-05-19 01:25:51 +02:00
felixlinker
d6fad098ee Restrict experimental weapons to at 4 at max
Closes #367
2019-05-19 01:12:50 +02:00
felixlinker
1b5730d337 Revert "Max 4 items per row when selecting modules"
This reverts commit 9b6b1d328c.
2019-05-19 00:49:09 +02:00
felixlinker
439b615b1b Revert "Don't allow more than four experimental weapons at once"
This reverts commit ac2e2e4d69.
2019-05-19 00:41:12 +02:00
Felix Linker
a8b30594dc Merge pull request #511 from EDCD/fix/data
Not more than 4 experimentals
2019-05-18 12:48:44 +02:00
felixlinker
9b6b1d328c Max 4 items per row when selecting modules 2019-05-18 12:16:39 +02:00
felixlinker
ac2e2e4d69 Don't allow more than four experimental weapons at once
Closes https://github.com/EDCD/coriolis/issues/367
2019-05-18 12:01:31 +02:00
Felix Linker
3a5fb31860 Merge pull request #508 from EDCD/fix/dps
Fixes to DPS and ROF calculations
2019-05-17 22:57:32 +02:00
felixlinker
c610eb8627 Fix burst(rof) modding and burst sdps calculation
References #504 #388
2019-05-17 21:02:17 +02:00
felixlinker
94980270c4 Fix SDPS calculations
Closes #504
2019-05-17 20:22:57 +02:00
felixlinker
c685e002e3 Fastest range is farthest range
Closes #471
2019-05-17 19:43:12 +02:00
felixlinker
1f665eed9e We don't need any time to remove no shields
Closes #426
2019-05-17 18:51:09 +02:00
felixlinker
0c4fc1fd9a Correctly update jump range for turned off modules
Closes #506
2019-05-17 18:40:20 +02:00
felixlinker
0fc033363e Don't handle inverting rof twice
Closes #500
2019-05-17 17:42:51 +02:00
willyb321
fb6e9538bc change service worker caching 2019-04-29 06:44:03 +10:00
willyb321
95b98fc4ed remove google analytics 2019-04-29 06:33:11 +10:00
Felix Linker
1840dafed0 Fix rate of fire modification (#497)
* Fix absolute setting of burst/burstrof

* Improve change display for stats with overwrite

* Module.getPretty takes synthetic getters into account

* Allow custom higherbetter for changes of stats when formatted

* Fix rof modding
2019-04-29 06:05:43 +10:00
willyb321
1ad82b116c Merge branch 'develop' 2019-04-27 10:31:37 +10:00
Felix Linker
7bdd17504b Fixes (#495)
* Create class-child module for selection when comparing

Closes #198

* Remove broken exception for shieldboost in getPercent

Closes #274

* Remove barely used functions
2019-04-27 10:29:49 +10:00
willyb321
2d820bb5d5 test 2019-04-27 10:22:21 +10:00
willyb321
2e14512ed8 Merge branch 'develop' 2019-04-24 07:33:52 +10:00
willyb321
48ed583c6d Merge branch 'feature/3.4' into develop 2019-04-24 07:32:35 +10:00
willyb321
dd444a17f3 handle journal-style imports
Closes #391
2019-04-17 11:06:48 +10:00
willyb321
2ea63c711e docking computer is now a unique module
you cannot have more than 1 standard dc, or a standard and an advanced dc, nor more than 1 advanced
2019-04-15 07:01:08 +10:00
willyb321
6d6d31db25 rename assists to flight assists 2019-04-15 07:00:48 +10:00
willyb321
e9273dcb9b move dc to assists category, 3.4 prep 2019-04-15 06:43:55 +10:00
willyb321
2bdc4562c6 add rg to autofill 2019-04-05 08:51:37 +11:00
willyb321
9e8a5323e9 Merge branch 'develop' 2019-03-31 10:34:37 +11:00
willyb321
8e001063b3 fix regex 2019-03-31 10:34:22 +11:00
willyb321
dc88fab4c5 Merge branch 'develop' 2019-03-31 10:24:03 +11:00
willyb321
dfca917e50 build image in docker compose 2019-03-31 10:23:41 +11:00
willyb321
ef7dfd6ca1 checkout branch in docker build 2019-03-31 10:17:58 +11:00
Mingming Cui
435c1b6d45 Chinese translation (#481)
* Chinese translation

* Added Chinese translation for UI

* Made some strings translatable.

Changed the translation ID of certain words which were used with different meanings in difference places in order to achieve a more accurate translation.

* fixed en format

* Fixed one capitalization mistake

* removed vs folder
2019-03-05 08:31:29 +11:00
willyb321
c5c9abe588 Merge branch 'master' into develop 2019-02-27 08:24:25 +11:00
opl-
363735d36b Fix ships menu not sorted by name (#477) 2019-02-26 08:00:09 +11:00
William
2741e7701b Update .gitlab-ci.yml 2019-02-18 12:38:40 +11:00
neilser
3be442ea60 Fix wrong units for pgen and power in pop-ups (#468) 2019-01-31 08:14:35 +11:00
Willyb321
34cbeca201 Merge branch 'develop' 2019-01-24 07:40:39 +11:00
Willyb321
b37e73ead6 one build stage 2019-01-23 08:58:12 +11:00
Willyb321
ee775521d6 Merge branch 'develop' of gitlab.willb.info:coriolis/coriolis into develop 2019-01-23 08:57:33 +11:00
Willyb321
5f84aaef1b add redirect from .edcd.io to .io 2019-01-23 08:57:17 +11:00
William
99ac58d999 Update .gitlab-ci.yml 2019-01-22 19:53:07 +11:00
William
f128a1e87d Create .gitlab-ci.yml 2019-01-22 19:45:28 +11:00
William
8c0768b451 Add license file using license from README 2019-01-22 09:30:20 +11:00
Felix Linker
319307136c Remove constraints for modification of maxmass 2019-01-15 19:17:45 +01:00
Felix Linker
a498452943 Multiple bug fixes (#463)
* Don't allow manually modifying multiplicative mods with base value zero

* Add missing argument when calculating fullTankRange

* Use opponent PD when calculating how long shields will hold

Closes #430

* Allow modifying max mass ONLY for shield generators

Closes #453
2019-01-15 10:32:33 +11:00
Willyb321
4b854b8305 Search tweaks 2019-01-04 09:28:21 +11:00
William Blythe
b400db8216 only cache if not 404 2019-01-04 08:01:02 +11:00
Willyb321
fb811faf5e Merge branch 'develop' 2019-01-03 08:54:49 +11:00
Willyb321
deeb525433 Remove dynamic import for pages
Closes #441
2019-01-03 08:54:22 +11:00
Willyb321
1cb88115f6 Merge branch 'develop' 2019-01-03 08:46:29 +11:00
Willyb321
a181791500 We don't run ads now. 2019-01-03 08:45:20 +11:00
William Blythe
94eec120da Merge branch 'develop' 2019-01-02 12:13:46 +11:00
William Blythe
48092d4395 trycatch announcements check 2019-01-02 12:13:27 +11:00
William Blythe
2457c30b94 Merge branch 'develop' 2018-12-31 08:19:07 +11:00
William Blythe
593f069806 Fix title being page not found even after page has loaded 2018-12-31 08:18:36 +11:00
William Blythe
a073692632 add probe radius 2018-12-27 08:18:38 +11:00
William Blythe
7752d5c9db Fix #445 2018-12-26 09:23:44 +11:00
William Blythe
544e5acaef remove snow 2018-12-26 09:14:45 +11:00
William Blythe
9ab35bbaf9 Merge branch 'develop' 2018-12-26 09:07:21 +11:00
William Blythe
98782da200 make shipyardpage not dynamic import 2018-12-26 09:07:05 +11:00
William Blythe
2936364934 prep for abl 2018-12-26 08:50:00 +11:00
William Blythe
01e1609a9f Merge branch 'develop' 2018-12-26 08:30:00 +11:00
William Blythe
f85a03a9ae remove christmas theme
its 8:30am in sydney ok
2018-12-26 08:29:45 +11:00
William Blythe
2703c2aa23 move mining laser to new mining group 2018-12-25 09:35:01 +11:00
William Blythe
954921c231 probably fix scrolling 2018-12-25 09:07:39 +11:00
William Blythe
8bed35a8ba probably fix scrolling 2018-12-25 09:06:00 +11:00
William Blythe
9f4ae60577 add christmas snow
Credit: https://codepen.io/keithclark/pen/yBcsr
2018-12-25 08:53:50 +11:00
William Blythe
ee3c50e27d add christmas snow
Credit: https://codepen.io/keithclark/pen/yBcsr
2018-12-25 08:53:05 +11:00
Willyb321
03020743b3 some docker adjustments, fix brotli comprpession 2018-12-23 07:51:42 +11:00
William Blythe
001fed67b7 change order of compression 2018-12-23 06:51:50 +11:00
William Blythe
3894915740 fix nginx conf 2018-12-23 06:39:32 +11:00
William Blythe
68fd13e8dc /shrgu 2018-12-22 08:25:53 +11:00
William Blythe
fdf16cd959 maybe 2018-12-22 08:25:10 +11:00
William Blythe
d916c67fe0 f 2018-12-22 08:13:06 +11:00
William Blythe
d8a8e224f4 enable brotli 2018-12-22 08:07:20 +11:00
William Blythe
e1c115747c nginx compression 2018-12-22 08:03:35 +11:00
William Blythe
e9b6d71606 dynamically import pages, should make things a bit faster 2018-12-22 07:48:35 +11:00
William Blythe
e03e249d2f probably fix .dockerignore 2018-12-22 07:19:53 +11:00
monopoint
0cfb0b6878 Fix background highlight on sort in shipyard (#447)
* Fix background highlight on sort in shipyard

* removed docs line
2018-12-21 12:14:43 -08:00
William Blythe
600df162aa Prep for sub surface displacement missile 2018-12-22 06:45:52 +11:00
Willyb321
94141aa3c5 mining tools in their own section, prep for scl 2018-12-19 08:21:13 +11:00
Willyb321
aca90d7077 prep for pulse wave analysers 2018-12-18 08:25:30 +11:00
felixlinker
a66fa8e83f Merge branch 'develop' 2018-12-05 20:32:55 +00:00
felixlinker
194db07057 Hotfix pip parsing in control code 2018-12-05 20:31:46 +00:00
60 changed files with 1289 additions and 502 deletions

View File

@@ -1,2 +0,0 @@
node_modules
npm-debug.log

View File

@@ -1,45 +0,0 @@
worker_processes 1;
user nobody nobody;
error_log /tmp/error.log;
pid /tmp/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
client_body_temp_path /tmp/client_body;
fastcgi_temp_path /tmp/fastcgi_temp;
proxy_temp_path /tmp/proxy_temp;
scgi_temp_path /tmp/scgi_temp;
uwsgi_temp_path /tmp/uwsgi_temp;
access_log /tmp/access.log;
error_log /tmp/error.log;
keepalive_timeout 3000;
server {
listen 80;
listen [::]:80;
index index.html;
server_name localhost;
root /usr/share/nginx/html;
autoindex on;
location ~* \.(?:manifest|appcache|html?|xml|json|css|js|map|jpg|jpeg|gif|png|ico|svg|eot|ttf|woff|woff2)$ {
expires -1;
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Credentials true;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
access_log off;
}
location / {
try_files $uri $uri/ /index.html =404;
}
}
}

77
.dockerignore Normal file
View File

@@ -0,0 +1,77 @@
node_modules
npm-debug.log
### Node template
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# parcel-bundler cache (https://parceljs.org/)
.cache
# next.js build output
.next
# nuxt.js build output
.nuxt
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless

View File

@@ -35,7 +35,7 @@
"title": "Coriolis",
"description": "Coriolis Shipyard for Elite Dangerous",
"repository": "https://github.com/EDCD/coriolis",
"site": "https://coriolis.edcd.io",
"site": "https://coriolis.io",
"author": "https://github.com/edcd",
"image": "./src/images/logo/192x192.png"
}
@@ -81,7 +81,7 @@
"title": "Coriolis",
"description": "Coriolis Shipyard for Elite Dangerous",
"repository": "https://github.com/EDCD/coriolis",
"site": "https://coriolis.edcd.io",
"site": "https://coriolis.io",
"author": "https://github.com/edcd",
"image": "./src/images/logo/192x192.png"
}
@@ -100,4 +100,4 @@
}
}
]
}
}

13
.gitlab-ci.yml Normal file
View File

@@ -0,0 +1,13 @@
image: docker:stable
services:
- docker:dind
stages:
- Build image
docker build:
stage: Build image
script:
- img build --build-arg branch=$CI_COMMIT_REF_NAME -t edcd/coriolis:$CI_COMMIT_REF_NAME .
- echo "$REGISTRY_PASSWORD" | img login --username "$REGISTRY_USER" --password-stdin
- img push edcd/coriolis:$CI_COMMIT_REF_NAME

View File

@@ -6,29 +6,29 @@ WORKDIR /src/app
RUN mkdir -p /src/app/coriolis
RUN mkdir -p /src/app/coriolis-data
COPY ./coriolis/ /src/app/coriolis
COPY ./coriolis-data/ /src/app/coriolis-data
RUN apk add --update git
RUN apk update
RUN apk add git
COPY . /src/app/coriolis
RUN npm i -g npm
# Set up coriolis-data
WORKDIR /src/app/coriolis-data
RUN git fetch --all
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 fetch --all
RUN git checkout ${BRANCH}
RUN npm install --no-package-lock
RUN npm run build
### STAGE 2: Production Environment ###
FROM nginx:1.13.12-alpine as web
COPY coriolis/.docker/nginx.conf /etc/nginx/nginx.conf
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

24
LICENSE.md Normal file
View File

@@ -0,0 +1,24 @@
All Data and [associated JSON](https://github.com/EDCD/coriolis-data) files are intellectual property and copyright of Frontier Developments plc ('Frontier', 'Frontier Developments') and are subject to their
[terms and conditions](https://www.frontierstore.net/terms-and-conditions/).
The code (Javascript, CSS, HTML, and SVG files only) specificially for Coriolis.io is released under the MIT License.
Copyright (c) 2015 Coriolis.io, Colin McLeod
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software (Javascript, CSS, HTML, and SVG files only), and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -29,6 +29,8 @@ Also see [the documentation site.](https://coriolis.willb.info/)
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.
## License

View File

@@ -3,6 +3,10 @@ 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
@@ -17,6 +21,10 @@ services:
coriolis_dev:
image: edcd/coriolis:develop
build:
dockerfile: Dockerfile
args:
branch: develop
restart: always
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf

View File

@@ -1,61 +1,96 @@
worker_processes 2;
error_log ./nginx.error.log;
worker_rlimit_nofile 8192;
pid nginx.pid;
worker_processes 1;
user nobody nobody;
error_log /tmp/error.log;
pid /tmp/nginx.pid;
events {
worker_connections 1024;
multi_accept on;
worker_connections 1024;
}
http {
access_log off;
charset UTF-8;
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;
types {
text/html html htm shtml;
text/css css;
text/xml xml rss;
image/gif gif;
image/jpeg jpeg jpg;
application/x-javascript js;
text/plain txt;
image/png png;
image/svg+xml svg;
image/x-icon ico;
application/pdf pdf;
text/cache-manifest appcache;
}
# https://nginx.org/en/docs/http/ngx_http_gzip_module.html
# Enable gzip compression.
# Default: off
gzip off;
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
# 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;
server {
listen 3301;
server_name localhost;
root ./build/;
index index.html;
# 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;
location ~* \.(?:manifest|appcache|html?|xml|json|css|js|map|jpg|jpeg|gif|png|ico|svg|eot|ttf|woff|woff2)$ {
expires -1;
add_header Access-Control-Allow-Origin *;
# 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;
}
}
access_log off;
}
location / {
try_files $uri $uri/ /index.html =404;
}
location /iframe.html {
try_files $uri $uri/ /iframe.html =404;
}
}
}

View File

@@ -5,7 +5,7 @@
"type": "git",
"url": "https://github.com/EDCD/coriolis"
},
"homepage": "https://coriolis.edcd.io",
"homepage": "https://coriolis.io",
"bugs": "https://github.com/EDCD/coriolis/issues",
"private": true,
"engine": "node >= 4.8.1",
@@ -120,6 +120,7 @@
"webpack-notifier": "^1.6.0",
"workbox-webpack-plugin": "^3.6.1"
},
"sideEffects": false,
"dependencies": {
"@babel/polyfill": "^7.0.0",
"browserify-zlib-next": "^1.0.1",

View File

@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import Router from './Router';
import { register } from 'register-service-worker'
import { register } from 'register-service-worker';
import { EventEmitter } from 'fbemitter';
import { getLanguage } from './i18n/Language';
import Persist from './stores/Persist';
@@ -72,7 +72,7 @@ export default class Coriolis extends React.Component {
route: {},
sizeRatio: Persist.getSizeRatio()
};
this._getAnnouncements()
this._getAnnouncements();
Router('', (r) => this._setPage(ShipyardPage, r));
Router('/import?', (r) => this._importBuild(r));
Router('/import/:data', (r) => this._importBuild(r));
@@ -105,18 +105,20 @@ export default class Coriolis extends React.Component {
}
r.params.ship = ship.id;
r.params.code = ship.toString();
this._setPage(OutfittingPage, r);
this._setPage(OutfittingPage, r)
} catch (err) {
this._onError('Failed to import ship', r.path, 0, 0, err);
}
}
_getAnnouncements() {
return request.get('https://orbis.zone/api/announcement')
.query({showInCoriolis: true})
.then(announces => {
this.setState({ announcements: announces.body })
})
async _getAnnouncements() {
try {
const announces = await request.get('https://orbis.zone/api/announcement')
.query({ showInCoriolis: true });
this.setState({ announcements: announces.body });
} catch (err) {
console.error(err)
}
}
/**
@@ -351,27 +353,27 @@ export default class Coriolis extends React.Component {
const self = this;
if (process.env.NODE_ENV === 'production') {
register('/service-worker.js', {
ready (registration) {
console.log('Service worker is active.')
ready(registration) {
console.log('Service worker is active.');
},
registered (registration) {
console.log('Service worker has been registered.')
registered(registration) {
console.log('Service worker has been registered.');
},
cached (registration) {
console.log('Content has been cached for offline use.')
cached(registration) {
console.log('Content has been cached for offline use.');
},
updatefound (registration) {
console.log('New content is downloading.')
updatefound(registration) {
console.log('New content is downloading.');
},
updated (registration) {
updated(registration) {
self.setState({ appCacheUpdate: true });
console.log('New content is available; please refresh.')
console.log('New content is available; please refresh.');
},
offline () {
console.log('No internet connection found. App is running in offline mode.')
offline() {
console.log('No internet connection found. App is running in offline mode.');
},
error (error) {
console.error('Error during service worker registration:', error)
error(error) {
console.error('Error during service worker registration:', error);
}
});
}
@@ -394,21 +396,24 @@ export default class Coriolis extends React.Component {
let currentMenu = this.state.currentMenu;
return <div style={{ minHeight: '100%' }} onClick={this._closeMenu}
className={this.state.noTouch ? 'no-touch' : null}>
<Header announcements={this.state.announcements} appCacheUpdate={this.state.appCacheUpdate} currentMenu={currentMenu} />
<div className="announcement-container">{this.state.announcements.map(a => <Announcement text={a.message}/>)}</div>
className={this.state.noTouch ? 'no-touch' : null}>
<Header announcements={this.state.announcements} appCacheUpdate={this.state.appCacheUpdate}
currentMenu={currentMenu}/>
<div className="announcement-container">{this.state.announcements.map(a => <Announcement
text={a.message}/>)}</div>
{this.state.error ? this.state.error : this.state.page ? React.createElement(this.state.page, { currentMenu }) :
<NotFoundPage />}
<NotFoundPage/>}
{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>
title="Coriolis Github Project">{window.CORIOLIS_VERSION} - {window.CORIOLIS_DATE}</a>
<br/>
<a
href={'https://github.com/EDCD/coriolis/compare/edcd:develop@{' + window.CORIOLIS_DATE + '}...edcd:develop'}
target="_blank" rel="noopener noreferrer" title={'Coriolis Commits since' + window.CORIOLIS_DATE}>Commits since last release
target="_blank" rel="noopener noreferrer" title={'Coriolis Commits since' + window.CORIOLIS_DATE}>Commits
since last release
({window.CORIOLIS_DATE})</a>
</div>
</footer>

View File

@@ -1,7 +1,5 @@
import Persist from './stores/Persist';
import ReactGA from 'react-ga';
ReactGA.initialize('UA-55840909-18');
let standalone = undefined;
/**
@@ -259,16 +257,8 @@ Route.prototype.match = function(path, params) {
* @param {string} path Path to track
*/
function gaTrack(path) {
const match = path.match(/\/outfit\/(.*)(\?code=.*)/);
if (match) {
if (match[1]) {
ReactGA.ga('set', 'contentGroup1', match[1]);
}
if (match[2]) {
ReactGA.ga('set', 'contentGroup2', match[2]);
}
}
ReactGA.pageview(path);
const _paq = window._paq || [];
_paq.push(['trackPageView']);
}
/**

View File

@@ -73,7 +73,16 @@ const GRPCAT = {
'gfsb': 'guardian',
'gmrp': 'guardian',
'gsc': 'guardian',
'ghrp': 'guardian'
'ghrp': 'guardian',
// Mining
'scl': 'mining',
'pwa': 'mining',
'sdm': 'mining',
// Assists
'dc': 'flight assists',
'sua': 'flight assists',
};
// Order here is the order in which items will be shown in the modules menu
const CATEGORIES = {
@@ -88,9 +97,10 @@ const CATEGORIES = {
'rf': ['rf'],
'shields': ['sg', 'bsg', 'psg', 'scb'],
'structural reinforcement': ['hr', 'mrp'],
'dc': ['dc'],
'flight assists': ['dc', 'sua'],
// Hardpoints
'lasers': ['pl', 'ul', 'bl', 'ml'],
'lasers': ['pl', 'ul', 'bl'],
'projectiles': ['mc', 'c', 'fc', 'pa', 'rg'],
'ordnance': ['mr', 'tp', 'nl'],
// Utilities
@@ -102,7 +112,9 @@ const CATEGORIES = {
'experimental': ['axmc', 'axmr', 'rfl', 'tbrfl', 'tbsc', 'tbem', 'xs', 'sfn', 'rcpl', 'dtl', 'rsl', 'mahr',],
// Guardian
'guardian': ['gpp', 'gpd', 'gpc', 'ggc', 'gsrp', 'gfsb', 'ghrp', 'gmrp', 'gsc']
'guardian': ['gpp', 'gpd', 'gpc', 'ggc', 'gsrp', 'gfsb', 'ghrp', 'gmrp', 'gsc'],
'mining': ['ml', 'scl', 'pwa', 'sdm', 'abl'],
};
/**
@@ -114,7 +126,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
onSelect: PropTypes.func.isRequired,
diffDetails: PropTypes.func,
m: PropTypes.object,
shipMass: PropTypes.number,
ship: PropTypes.object.isRequired,
warning: PropTypes.func,
firstSlotId: PropTypes.string,
lastSlotId: PropTypes.string,
@@ -122,10 +134,6 @@ export default class AvailableModulesMenu extends TranslatedComponent {
slotDiv: PropTypes.object
};
static defaultProps = {
shipMass: 0
};
/**
* Constructor
* @param {Object} props React Component properties
@@ -147,15 +155,15 @@ export default class AvailableModulesMenu extends TranslatedComponent {
*/
_initState(props, context) {
let translate = context.language.translate;
let { m, warning, shipMass, onSelect, modules, firstSlotId, lastSlotId } = props;
let { m, warning, onSelect, modules, ship } = props;
let list, currentGroup;
let buildGroup = this._buildGroup.bind(
this,
ship,
translate,
m,
warning,
shipMass - (m && m.mass ? m.mass : 0),
(m, event) => {
this._hideDiff(event);
onSelect(m);
@@ -221,7 +229,16 @@ export default class AvailableModulesMenu extends TranslatedComponent {
}
list.push(buildGroup(grp, modules[grp]));
for (const i of modules[grp]) {
fuzzy.push({ grp, m: i, name: `${i.class}${i.rating} ${translate(grp)} ${i.mount ? i.mount : ''}` });
let mount = '';
if (i.mount === 'F') {
mount = 'Fixed';
} else if (i.mount === 'G') {
mount = 'Gimballed';
} else if (i.mount === 'T') {
mount = 'Turreted';
}
const fuzz = { grp, m: i, name: `${i.class}${i.rating}${mount ? ' ' + mount : ''} ${translate(grp)}` };
fuzzy.push(fuzz);
}
}
}
@@ -234,18 +251,16 @@ export default class AvailableModulesMenu extends TranslatedComponent {
/**
* Generate React Components for Module Group
* @param {Ship} ship Ship the selection is for
* @param {Function} translate Translate function
* @param {Object} mountedModule Mounted Module
* @param {Function} warningFunc Warning function
* @param {number} mass Mass
* @param {function} onSelect Select/Mount callback
* @param {string} grp Group name
* @param {Array} modules Available modules
* @param {string} firstSlotId id of first slot item
* @param {string} lastSlotId id of last slot item
* @return {React.Component} Available Module Group contents
*/
_buildGroup(translate, mountedModule, warningFunc, mass, onSelect, grp, modules, firstSlotId, lastSlotId) {
_buildGroup(ship, translate, mountedModule, warningFunc, onSelect, grp, modules) {
let prevClass = null, prevRating = null, prevName;
let elems = [];
@@ -266,10 +281,11 @@ export default class AvailableModulesMenu extends TranslatedComponent {
prevName = m.name;
if (ModuleUtils.isShieldGenerator(m.grp)) {
// Shield generators care about maximum hull mass
disabled = mass > m.maxmass;
} else if (m.maxmass) {
// Thrusters care about total mass
disabled = mass + m.mass > m.maxmass;
disabled = ship.hullMass > m.maxmass;
// If the mounted module is experimental as well, we can replace it so
// the maximum does not apply
} else if (m.experimental && (!mountedModule || !mountedModule.experimental)) {
disabled = 4 <= ship.hardpoints.filter(o => o.m && o.m.experimental).length;
}
let active = mountedModule && mountedModule.id === m.id;
let classes = cn(m.name ? 'lc' : 'c', {
@@ -363,10 +379,14 @@ export default class AvailableModulesMenu extends TranslatedComponent {
* mounted module and the hovered modules
*/
_showSearch() {
if (this.props.modules instanceof Array) {
return;
}
return (
<FuzzySearch
list={this.state.fuzzy}
keys={['grp', 'name']}
tokenize={true}
className={'input'}
width={'100%'}
style={{ padding: 0 }}

View File

@@ -52,12 +52,12 @@ export default class Defence extends TranslatedComponent {
* @return {React.Component} contents
*/
render() {
const { ship, sys, opponentWep } = this.props;
const { opponent, sys, opponentWep } = this.props;
const { language, tooltip, termtip } = this.context;
const { formats, translate, units } = language;
const { shield, armour, shielddamage, armourdamage } = this.state;
const pd = ship.standard[4].m;
const pd = opponent.standard[4].m;
const shieldSourcesData = [];
const effectiveShieldData = [];

View File

@@ -104,10 +104,10 @@ export default class HardpointSlot extends Slot {
onMouseOut={tooltip.bind(null, null)}>{translate('shotdmg')}: {formats.round1(m.getDamage())}</div> : null}
{m.getEps() ? <div className={'l'} onMouseOver={termtip.bind(null, m.getClip() ? 'epsseps' : 'eps')}
onMouseOut={tooltip.bind(null, null)}>{translate('EPS')}: {formats.round1(m.getEps())}{u.MW} {m.getClip() ?
<span>({formats.round1((m.getClip() * m.getEps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload()))}{u.MW})</span> : null}</div> : null}
<span>({formats.round1(m.getEps() * m.getSustainedFactor())}{u.MW})</span> : null}</div> : null}
{m.getHps() ? <div className={'l'} onMouseOver={termtip.bind(null, m.getClip() ? 'hpsshps' : 'hps')}
onMouseOut={tooltip.bind(null, null)}>{translate('HPS')}: {formats.round1(m.getHps())} {m.getClip() ?
<span>({formats.round1((m.getClip() * m.getHps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload()))})</span> : null}</div> : null}
<span>({formats.round1(m.getHps() * m.getSustainedFactor())})</span> : null}</div> : null}
{m.getDps() && m.getEps() ? <div className={'l'} onMouseOver={termtip.bind(null, 'dpe')}
onMouseOut={tooltip.bind(null, null)}>{translate('DPE')}: {formats.f1(m.getDps() / m.getEps())}</div> : null}
{m.getRoF() ? <div className={'l'} onMouseOver={termtip.bind(null, 'rof')}
@@ -121,11 +121,14 @@ export default class HardpointSlot extends Slot {
{m.getShieldBoost() ? <div className={'l'}>+{formats.pct1(m.getShieldBoost())}</div> : null}
{m.getAmmo() ? <div
className={'l'}>{translate('ammunition')}: {formats.int(m.getClip())}/{formats.int(m.getAmmo())}</div> : null}
{m.getReload() ? <div className={'l'}>{translate('reload')}: {formats.round(m.getReload())}{u.s}</div> : null}
{m.getReload() ? <div className={'l'}>{translate('wep_reload')}: {formats.round(m.getReload())}{u.s}</div> : null}
{m.getShotSpeed() ? <div
className={'l'}>{translate('shotspeed')}: {formats.int(m.getShotSpeed())}{u.mps}</div> : null}
{m.getPiercing() ? <div className={'l'}>{translate('piercing')}: {formats.int(m.getPiercing())}</div> : null}
{m.getJitter() ? <div className={'l'}>{translate('jitter')}: {formats.f2(m.getJitter())}°</div> : null}
{m.getScanAngle() ? <div className={'l'}>{translate('scan angle')}: {formats.f2(m.getScanAngle())}°</div> : null}
{m.getScanRange() ? <div className={'l'}>{translate('scan range')}: {formats.int(m.getScanRange())}{u.m}</div> : null}
{m.getMaxAngle() ? <div className={'l'}>{translate('max angle')}: {formats.f2(m.getMaxAngle())}°</div> : null}
{showModuleResistances && m.getExplosiveResistance() ? <div
className='l'>{translate('explres')}: {formats.pct(m.getExplosiveResistance())}</div> : null}
{showModuleResistances && m.getKineticResistance() ? <div

View File

@@ -149,14 +149,14 @@ export default class HardpointSlotSection extends SlotSection {
<ul>
<li className='lc' tabIndex='0' onClick={_fill.bind(this, 'pa', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['pa-F'] = smRef}>{translate('pa')}</li>
</ul>
<div className='select-group cap'>{translate('rg')}</div>
<ul>
<li className='lc' tabIndex='0' onClick={_fill.bind(this, 'rg', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['rg-F'] = smRef}>{translate('rg')}</li>
</ul>
<div className='select-group cap'>{translate('nl')}</div>
<ul>
<li className='lc' tabIndex='0' onClick={_fill.bind(this, 'nl', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['nl-F'] = smRef}>{translate('nl')}</li>
</ul>
<div className='select-group cap'>{translate('ggc')}</div>
<ul>
<li className='lc' tabIndex='0' onClick={_fill.bind(this, 'ggc', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['ggc-F'] = smRef}>{translate('ggc')}</li>
</ul>
<div className='select-group cap'>{translate('rfl')}</div>
<ul>
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'rfl', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['rfl-F'] = smRef}><MountFixed className='lg'/></li>

View File

@@ -349,7 +349,7 @@ export default class Header extends TranslatedComponent {
_getShipsMenu() {
let shipList = [];
for (let s in Ships) {
for (let s of this.shipOrder) {
shipList.push(<ActiveLink key={s} href={outfitURL(s)} className='block'>{Ships[s].properties.name}</ActiveLink>);
}

View File

@@ -78,7 +78,6 @@ export default class InternalSlot extends Slot {
{ m.rangeLS ? <div className={'l'}>{translate('range')}: {m.rangeLS}{u.Ls}</div> : null }
{ m.rangeLS === null ? <div className={'l'}>{u.Ls}</div> : null }
{ m.rangeRating ? <div className={'l'}>{translate('range')}: {m.rangeRating}</div> : null }
{ m.maximum ? <div className={'l'}>{translate('max')}: {(m.maximum)}</div> : null }
{ m.passengers ? <div className={'l'}>{translate('passengers')}: {m.passengers}</div> : null }
{ m.getRegenerationRate() ? <div className='l'>{translate('regen')}: {formats.round1(m.getRegenerationRate())}{u.ps}</div> : null }
{ m.getBrokenRegenerationRate() ? <div className='l'>{translate('brokenregen')}: {formats.round1(m.getBrokenRegenerationRate())}{u.ps}</div> : null }

View File

@@ -13,6 +13,8 @@ import { Download } from './SvgIcons';
import { outfitURL } from '../utils/UrlGenerators';
import * as CompanionApiUtils from '../utils/CompanionApiUtils';
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 };
@@ -99,6 +101,7 @@ export default class ModalImport extends TranslatedComponent {
this.state = {
builds: props.builds,
canEdit: !props.builds,
loadoutEvent: null,
comparisons: null,
shipDiscount: null,
moduleDiscount: null,
@@ -111,12 +114,28 @@ export default class ModalImport extends TranslatedComponent {
this._process = this._process.bind(this);
this._import = this._import.bind(this);
this._importBackup = this._importBackup.bind(this);
this._importLoadout = this._importLoadout.bind(this);
this._importDetailedArray = this._importDetailedArray.bind(this);
this._importTextBuild = this._importTextBuild.bind(this);
this._importCompanionApiBuild = this._importCompanionApiBuild.bind(this);
this._validateImport = this._validateImport.bind(this);
}
/**
* Import a Loadout event from Elite: Dangerous journal files
* @param {Object} data Loadout event
* @throws {string} If import fails
*/
_importLoadout(data) {
if (data && data.Ship && data.Modules) {
const deflated = zlib.deflate(JSON.stringify(data), { to: 'string' });
let compressed = btoa(deflated);
this.setState({loadoutEvent: compressed});
} else {
throw 'Loadout event must contain Ship and Modules';
}
}
/**
* Import a Coriolis backup
* @param {Object} importData Backup Data
@@ -159,7 +178,7 @@ export default class ModalImport extends TranslatedComponent {
}
// Check for module discount
if (!isNaN(importData.moduleDiscount)) {
this.setState({ shipDiscount: importData.moduleDiscount * 1 });
this.setState({ moduleDiscount: importData.moduleDiscount * 1 });
}
if (typeof importData.insurance == 'string') {
@@ -345,12 +364,14 @@ export default class ModalImport extends TranslatedComponent {
} else if (importData.ship && typeof importData.name !== undefined) { // Using JSON from a single ship build export
this._importDetailedArray([importData]); // Convert to array with singleobject
this.setState({ singleBuild: true });
} else if (importData.Modules != null && importData.Modules[0] != null) {
this._importLoadout(importData);
} else { // Using Backup JSON
this._importBackup(importData);
}
}
} catch (e) {
// console.log(e.stack);
console.log(e);
this.setState({ errorMsg: (typeof e == 'string') ? e : 'Cannot Parse the data!' });
return;
}
@@ -364,6 +385,10 @@ export default class ModalImport extends TranslatedComponent {
_process() {
let builds = null, comparisons = null;
if (this.state.loadoutEvent) {
return Router.go(`/import?data=${this.state.loadoutEvent}`);
}
// If only importing a single build go straight to the outfitting page
if (this.state.singleBuild) {
builds = this.state.builds;
@@ -480,7 +505,7 @@ export default class ModalImport extends TranslatedComponent {
if (!state.processed) {
importStage = (
<div>
<textarea className='cb json' ref={node => this.importField = node} onChange={this._validateImport} defaultValue={this.state.importString} placeholder={translate('PHRASE_IMPORT')} />
<textarea spellCheck={false} className='cb json' ref={node => this.importField = node} onChange={this._validateImport} defaultValue={this.state.importString} placeholder={translate('PHRASE_IMPORT')} />
<button id='proceed' className='l cap' onClick={this._process} disabled={!state.importValid} >{translate('proceed')}</button>
<div className='l warning' style={{ marginLeft:'3em' }}>{state.errorMsg}</div>
</div>

View File

@@ -109,17 +109,18 @@ export default class ModalShoppingList extends TranslatedComponent {
*/
sendToEDEng(event) {
event.preventDefault();
let translate = this.context.language.translate;
const target = event.target;
target.disabled = this.state.blueprints.length > 0;
if (this.state.blueprints.length === 0) {
target.innerText = 'No modded components.';
target.innerText = translate('No modded components.');
target.disabled = true;
setTimeout(() => {
target.innerText = 'Send to EDEngineer';
target.innerText = translate('Send to EDEngineer');
target.disabled = false;
}, 3000);
} else {
target.innerText = 'Sending...';
target.innerText = translate('Sending...');
}
let countSent = 0;
let countTotal = this.state.blueprints.length;
@@ -139,7 +140,7 @@ export default class ModalShoppingList extends TranslatedComponent {
countSent++;
if (countSent === countTotal) {
target.disabled = false;
target.innerText = 'Send to EDEngineer';
target.innerText = translate('Send to EDEngineer');
}
});
}
@@ -230,32 +231,32 @@ export default class ModalShoppingList extends TranslatedComponent {
this.sendToEDEng = this.sendToEDEng.bind(this);
return <div className='modal' onClick={ (e) => e.stopPropagation() }>
<h2>{translate('PHRASE_SHOPPING_MATS')}</h2>
<label>Grade 1 rolls </label>
<label>{translate('Grade 1 rolls ')}</label>
<input id={1} type={'number'} min={0} defaultValue={this.state.matsPerGrade[1]} onChange={this.changeHandler} />
<br/>
<label>Grade 2 rolls </label>
<label>{translate('Grade 2 rolls ')}</label>
<input id={2} type={'number'} min={0} defaultValue={this.state.matsPerGrade[2]} onChange={this.changeHandler} />
<br/>
<label>Grade 3 rolls </label>
<label>{translate('Grade 3 rolls ')}</label>
<input id={3} type={'number'} min={0} value={this.state.matsPerGrade[3]} onChange={this.changeHandler} />
<br/>
<label>Grade 4 rolls </label>
<label>{translate('Grade 4 rolls ')}</label>
<input id={4} type={'number'} min={0} value={this.state.matsPerGrade[4]} onChange={this.changeHandler} />
<br/>
<label>Grade 5 rolls </label>
<label>{translate('Grade 5 rolls ')}</label>
<input id={5} type={'number'} min={0} value={this.state.matsPerGrade[5]} onChange={this.changeHandler} />
<div>
<textarea className='cb json' readOnly value={this.state.matsList} />
</div>
<label hidden={!compatible} className={'l cap'}>CMDR Name </label>
<label hidden={!compatible} className={'l cap'}>{translate('CMDR Name')}</label>
<br/>
<select hidden={!compatible} className={'cmdr-select l cap'} onChange={this.cmdrChangeHandler} defaultValue={this.state.cmdrName}>
{this.state.cmdrs.map(e => <option key={e}>{e}</option>)}
</select>
<br/>
<p hidden={!this.state.failed} id={'failed'} className={'l'}>Failed to send to EDEngineer (Launch EDEngineer and make sure the API is started then refresh the page.)</p>
<p hidden={compatible} id={'browserbad'} className={'l'}>Sending to EDEngineer is not compatible with Firefox's security settings. Please try again with Chrome.</p>
<button className={'l cb dismiss cap'} disabled={!!this.state.failed || !compatible} onClick={this.sendToEDEng}>{translate('Send To EDEngineer')}</button>
<p hidden={!this.state.failed} id={'failed'} className={'l'}>{translate('PHRASE_FAIL_EDENGINEER')}</p>
<p hidden={compatible} id={'browserbad'} className={'l'}>{translate('PHRASE_FIREFOX_EDENGINEER')}</p>
<button className={'l cb dismiss cap'} disabled={!!this.state.failed || !compatible} onClick={this.sendToEDEng}>{translate('Send to EDEngineer')}</button>
<button className={'r dismiss cap'} onClick={this.context.hideModal}>{translate('close')}</button>
</div>;
}

View File

@@ -3,7 +3,8 @@ import PropTypes from 'prop-types';
import TranslatedComponent from './TranslatedComponent';
import cn from 'classnames';
import NumberEditor from 'react-number-editor';
import { isValueBeneficial } from '../utils/BlueprintFunctions';
import { isChangeValueBeneficial } from '../utils/BlueprintFunctions';
import { Modifications } from 'coriolis-data/dist';
/**
* Modification
@@ -79,6 +80,7 @@ export default class Modification extends TranslatedComponent {
let { translate, formats, units } = this.context.language;
let { m, name } = this.props;
let modValue = m.getChange(name);
let isOverwrite = Modifications.modifications[name].method === 'overwrite';
if (name === 'damagedist') {
// We don't show damage distribution
@@ -117,10 +119,10 @@ export default class Modification extends TranslatedComponent {
</td>
<td style={{ textAlign: 'center' }} className={
modValue ?
isValueBeneficial(name, modValue) ? 'secondary' : 'warning' :
isChangeValueBeneficial(name, modValue) ? 'secondary' : 'warning' :
''
}>
{formats.f2(modValue / 100) || 0}%
{formats.f2(modValue / 100) || 0}{isOverwrite ? '' : '%'}
</td>
</tr>
</tbody>

View File

@@ -178,7 +178,7 @@ export default class ModificationsMenu extends TranslatedComponent {
continue;
}
const classes = cn('button-inline-menu', {
active: m.blueprint && m.blueprint.special && m.blueprint.special.edname == specialName
active: m.blueprint && m.blueprint.special && m.blueprint.special.key == specialName
});
if (classes.indexOf('active') >= 0) this.selectedSpecialId = specialName;
const close = this._specialSelected.bind(this, specialName);
@@ -437,7 +437,7 @@ export default class ModificationsMenu extends TranslatedComponent {
let specialTt;
if (m.blueprint && m.blueprint.special) {
specialLabel = m.blueprint.special.name;
specialTt = specialToolTip(translate, m.blueprint.grades[m.blueprint.grade], m.grp, m, m.blueprint.special.edname);
specialTt = specialToolTip(translate, m.blueprint.grades[m.blueprint.grade], m.grp, m, m.blueprint.special.key);
} else {
specialLabel = translate('PHRASE_SELECT_SPECIAL');
}
@@ -478,7 +478,7 @@ export default class ModificationsMenu extends TranslatedComponent {
<tbody>
{ showRolls ?
<tr>
<td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: false }) }> { translate('roll') }: </td>
<td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: false }) }> { translate('mroll') }: </td>
<td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: blueprintCv === 0 }) } style={{ cursor: 'pointer' }} onClick={_rollWorst} onKeyDown={ this._keyDown } onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_WORST')} onMouseOut={tooltip.bind(null, null)}> { translate('0%') } </td>
<td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: blueprintCv === 50 })} style={{ cursor: 'pointer' }} onClick={_rollFifty} onKeyDown={ this._keyDown } onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_FIFTY')} onMouseOut={tooltip.bind(null, null)}> { translate('50%') } </td>
<td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: blueprintCv === 100 })} style={{ cursor: 'pointer' }} onClick={_rollFull} onKeyDown={ this._keyDown } onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_BEST')} onMouseOut={tooltip.bind(null, null)}> { translate('100%') } </td>

View File

@@ -174,7 +174,7 @@ export default class OutfittingSubpages extends TranslatedComponent {
<th style={{ width:'25%' }} className={cn({ active: tab == 'power' })} onClick={this._showTab.bind(this, 'power')} >{translate('power and costs')}</th>
<th style={{ width:'25%' }} className={cn({ active: tab == 'profiles' })} onClick={this._showTab.bind(this, 'profiles')} >{translate('profiles')}</th>
<th style={{ width:'25%' }} className={cn({ active: tab == 'offence' })} onClick={this._showTab.bind(this, 'offence')} >{translate('offence')}</th>
<th style={{ width:'25%' }} className={cn({ active: tab == 'defence' })} onClick={this._showTab.bind(this, 'defence')} >{translate('defence')}</th>
<th style={{ width:'25%' }} className={cn({ active: tab == 'defence' })} onClick={this._showTab.bind(this, 'defence')} >{translate('tab_defence')}</th>
</tr>
</thead>
</table>

View File

@@ -50,6 +50,7 @@ export default class ShipSummaryTable extends TranslatedComponent {
const speedTooltip = canThrust ? 'TT_SUMMARY_SPEED' : 'TT_SUMMARY_SPEED_NONFUNCTIONAL';
const canBoost = ship.canBoost(cargo, ship.fuelCapacity);
const boostTooltip = canBoost ? 'TT_SUMMARY_BOOST' : canThrust ? 'TT_SUMMARY_BOOST_NONFUNCTIONAL' : 'TT_SUMMARY_SPEED_NONFUNCTIONAL';
const canJump = ship.getSlotStatus(ship.standard[2]) == 3;
const sgMetrics = Calc.shieldMetrics(ship, pips.sys);
const shipBoost = canBoost ? Calc.calcBoost(ship) : 'No Boost';
const restingHeat = Math.sqrt(((ship.standard[0].m.pgen * ship.standard[0].m.eff) / ship.heatCapacity) / 0.2);
@@ -71,7 +72,7 @@ export default class ShipSummaryTable extends TranslatedComponent {
<tr className='main'>
<th rowSpan={2} className={ cn({ 'bg-warning-disabled': !canThrust }) }>{translate('speed')}</th>
<th rowSpan={2} className={ cn({ 'bg-warning-disabled': !canBoost }) }>{translate('boost')}</th>
<th colSpan={5}>{translate('jump range')}</th>
<th colSpan={5} className={ cn({ 'bg-warning-disabled': !canJump }) }>{translate('jump range')}</th>
<th rowSpan={2}>{translate('shield')}</th>
<th rowSpan={2}>{translate('integrity')}</th>
<th rowSpan={2}>{translate('DPS')}</th>
@@ -85,15 +86,15 @@ export default class ShipSummaryTable extends TranslatedComponent {
<th onMouseEnter={termtip.bind(null, 'hull hardness', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('hrd')}</th>
<th rowSpan={2}>{translate('crew')}</th>
<th onMouseEnter={termtip.bind(null, 'mass lock factor', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('MLF')}</th>
<th onMouseEnter={termtip.bind(null, 'TT_SUMMARY_BOOST_TIME', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('boost time')}</th>
<th onMouseEnter={termtip.bind(null, 'TT_SUMMARY_BOOST_INTERVAL', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('boost interval')}</th>
<th rowSpan={2}>{translate('resting heat (Beta)')}</th>
</tr>
<tr>
<th className='lft'>{translate('max')}</th>
<th>{translate('unladen')}</th>
<th>{translate('laden')}</th>
<th>{translate('total unladen')}</th>
<th>{translate('total laden')}</th>
<th className={ cn({ 'lft': true, 'bg-warning-disabled': !canJump }) }>{translate('max')}</th>
<th className={ cn({ 'bg-warning-disabled': !canJump }) }>{translate('unladen')}</th>
<th className={ cn({ 'bg-warning-disabled': !canJump }) }>{translate('laden')}</th>
<th className={ cn({ 'bg-warning-disabled': !canJump }) }>{translate('total unladen')}</th>
<th className={ cn({ 'bg-warning-disabled': !canJump }) }>{translate('total laden')}</th>
<th className='lft'>{translate('hull')}</th>
<th>{translate('unladen')}</th>
<th>{translate('laden')}</th>
@@ -103,11 +104,11 @@ export default class ShipSummaryTable extends TranslatedComponent {
<tr>
<td onMouseEnter={termtip.bind(null, speedTooltip, { cap: 0 })} onMouseLeave={hide}>{ canThrust ? <span>{int(ship.calcSpeed(4, ship.fuelCapacity, 0, false))}{u['m/s']}</span> : <span className='warning'>0 <Warning/></span> }</td>
<td onMouseEnter={termtip.bind(null, boostTooltip, { cap: 0 })} onMouseLeave={hide}>{ canBoost ? <span>{int(ship.calcSpeed(4, ship.fuelCapacity, 0, true))}{u['m/s']}</span> : <span className='warning'>0 <Warning/></span> }</td>
<td><span onMouseEnter={termtip.bind(null, 'TT_SUMMARY_MAX_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{f2(Calc.jumpRange(ship.unladenMass + ship.standard[2].m.getMaxFuelPerJump(), ship.standard[2].m, ship.standard[2].m.getMaxFuelPerJump(), ship))}{u.LY}</span></td>
<td><span onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{f2(Calc.jumpRange(ship.unladenMass + ship.fuelCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</span></td>
<td><span onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{f2(Calc.jumpRange(ship.unladenMass + ship.fuelCapacity + ship.cargoCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</span></td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_TOTAL_JUMP', { cap: 0 })} onMouseLeave={hide}>{f2(Calc.totalJumpRange(ship.unladenMass + ship.fuelCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_TOTAL_JUMP', { cap: 0 })} onMouseLeave={hide}>{f2(Calc.totalJumpRange(ship.unladenMass + ship.fuelCapacity + ship.cargoCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_MAX_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{ canJump ? <span>{ f2(Calc.jumpRange(ship.unladenMass + ship.standard[2].m.getMaxFuelPerJump(), ship.standard[2].m, ship.standard[2].m.getMaxFuelPerJump(), ship))}{u.LY}</span> : <span className='warning'>0 <Warning/></span> }</td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{ canJump ? <span>{f2(Calc.jumpRange(ship.unladenMass + ship.fuelCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</span> : <span className='warning'>0 <Warning/></span> }</td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{ canJump ? <span>{f2(Calc.jumpRange(ship.unladenMass + ship.fuelCapacity + ship.cargoCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</span> : <span className='warning'>0 <Warning/></span> }</td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_TOTAL_JUMP', { cap: 0 })} onMouseLeave={hide}>{ canJump ? <span>{f2(Calc.totalJumpRange(ship.unladenMass + ship.fuelCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</span> : <span className='warning'>0 <Warning/></span> }</td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_TOTAL_JUMP', { cap: 0 })} onMouseLeave={hide}>{ canJump ? <span>{f2(Calc.totalJumpRange(ship.unladenMass + ship.fuelCapacity + ship.cargoCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</span> : <span className='warning'>0 <Warning/></span> }</td>
<td className={sgClassNames} onMouseEnter={termtip.bind(null, sgTooltip, { cap: 0 })} onMouseLeave={hide}>{int(ship.shield)}{u.MJ}</td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_INTEGRITY', { cap: 0 })} onMouseLeave={hide}>{int(ship.armour)}</td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_DPS', { cap: 0 })} onMouseLeave={hide}>{f1(ship.totalDps)}</td>
@@ -159,10 +160,10 @@ export default class ShipSummaryTable extends TranslatedComponent {
<td>{formats.pct1(ship.shieldThermRes)}</td>
<td></td>
<td>{int(ship && ship.shield > 0 ? ship.shield : 0)}{u.MJ}</td>
<td>{int(ship && ship.shield > 0 ? ship.shield * ((1 / (1 - (ship.shieldExplRes)))) : 0)}{u.MJ}</td>
<td>{int(ship && ship.shield > 0 ? ship.shield * ((1 / (1 - (ship.shieldKinRes)))) : 0)}{u.MJ}</td>
<td>{int(ship && ship.shield > 0 ? ship.shield * ((1 / (1 - (ship.shieldThermRes)))) : 0)}{u.MJ}</td>
<td>{int(ship && sgMetrics.summary > 0 ? sgMetrics.summary : 0)}{u.MJ}</td>
<td>{int(ship && sgMetrics.summary > 0 ? sgMetrics.summary / sgMetrics.explosive.base : 0)}{u.MJ}</td>
<td>{int(ship && sgMetrics.summary ? sgMetrics.summary / sgMetrics.kinetic.base : 0)}{u.MJ}</td>
<td>{int(ship && sgMetrics.summary ? sgMetrics.summary / sgMetrics.thermal.base : 0)}{u.MJ}</td>
<td></td>
<td>{sgMetrics && sgMetrics.recover === Math.Inf ? translate('Never') : formats.time(sgMetrics.recover)}</td>
<td>{sgMetrics && sgMetrics.recharge === Math.Inf ? translate('Never') : formats.time(sgMetrics.recharge)}</td>
@@ -197,11 +198,11 @@ export default class ShipSummaryTable extends TranslatedComponent {
<td>{formats.pct1(ship.hullKinRes)}</td>
<td>{formats.pct1(ship.hullThermRes)}</td>
<td>{formats.pct1(ship.hullCausRes)}</td>
<td>{int(ship.armour)}</td>
<td>{int(ship.armour * ((1 / (1 - (ship.hullExplRes)))))}</td>
<td>{int(ship.armour * ((1 / (1 - (ship.hullKinRes)))))}</td>
<td>{int(ship.armour * ((1 / (1 - (ship.hullThermRes)))))}</td>
<td>{int(ship.armour * ((1 / (1 - (ship.hullCausRes)))))}</td>
<td>{int(armourMetrics.total)}</td>
<td>{int(armourMetrics.total / armourMetrics.explosive.total)}</td>
<td>{int(armourMetrics.total/ armourMetrics.kinetic.total)}</td>
<td>{int(armourMetrics.total / armourMetrics.thermal.total)}</td>
<td>{int(armourMetrics.total/ armourMetrics.caustic.total)}</td>
<td>{int(armourMetrics.modulearmour)}</td>
<td>{int(armourMetrics.moduleprotection * 100) + '%'}</td>

View File

@@ -127,8 +127,8 @@ export default class Slot extends TranslatedComponent {
menu = <AvailableModulesMenu
className={this._getClassNames()}
modules={availableModules()}
shipMass={ship.hullMass}
m={m}
ship={ship}
onSelect={onSelect}
warning={warning}
diffDetails={diffDetails.bind(ship, this.context.language)}

View File

@@ -195,6 +195,15 @@ export default class SlotSection extends TranslatedComponent {
if (targetSlot && canMount(this.props.ship, targetSlot, m.grp, m.class)) {
const mCopy = m.clone();
this.props.ship.use(targetSlot, mCopy, false);
let experimentalNum = this.props.ship.hardpoints
.filter(s => s.m && s.m.experimental).length;
// Remove the module on the last slot if we now exceed the number of
// experimentals allowed
if (m.experimental && 4 < experimentalNum) {
this.props.ship.updateStats(originSlot, null, originSlot.m);
originSlot.m = null; // Empty the slot
originSlot.discountedCost = 0;
}
// Copy power info
targetSlot.enabled = originSlot.enabled;
targetSlot.priority = originSlot.priority;

View File

@@ -109,8 +109,8 @@ export default class StandardSlot extends TranslatedComponent {
menu = <AvailableModulesMenu
className='standard'
modules={modules}
shipMass={ModuleUtils.isShieldGenerator(m.grp) ? ship.hullMass : ship.unladenMass}
m={m}
ship={ship}
onSelect={onSelect}
warning={warning}
diffDetails={diffDetails.bind(ship, this.context.language)}

View File

@@ -7,6 +7,7 @@ import * as IT from './it';
import * as RU from './ru';
import * as PL from './pl';
import * as PT from './pt';
import * as CN from './cn';
import * as d3 from 'd3';
let fallbackTerms = EN.terms;
@@ -27,6 +28,7 @@ export function getLanguage(langCode) {
case 'ru': lang = RU; break;
case 'pl': lang = PL; break;
case 'pt': lang = PT; break;
case 'cn': lang = CN; break;
default:
lang = EN;
}
@@ -94,5 +96,6 @@ export const Languages = {
fr: 'Français',
ru: 'ру́сский',
pl: 'polski',
pt: 'português'
pt: 'português',
cn: '中文'
};

16
src/app/i18n/cn.js Normal file
View File

@@ -0,0 +1,16 @@
export const formats = {
decimal: '.',
thousands: ',',
grouping: [3],
currency: ['¥', ''],
dateTime: '%a %b %e %X %Y',
date: '%Y年%m月%d日',
time: '%H:%M:%S',
periods: ['AM', 'PM'],
days: ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'],
shortDays: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
months: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
shortMonths: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月']
};
export { default as terms } from './cn.json';

405
src/app/i18n/cn.json Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -63,7 +63,7 @@
"TT_SUMMARY_SPEED": "With full fuel tank and 4 pips to ENG",
"TT_SUMMARY_SPEED_NONFUNCTIONAL": "Thrusters powered off or over maximum mass with full fuel and cargo loads",
"TT_SUMMARY_BOOST": "With full fuel tank and 4 pips to ENG",
"TT_SUMMARY_BOOST_TIME": "Time between each boost with 4 pips to ENG",
"TT_SUMMARY_BOOST_INTERVAL": "Time between each boost with 4 pips to ENG",
"TT_SUMMARY_BOOST_NONFUNCTIONAL": "Power distributor not able to supply enough power to boost",
"TT_SUMMARY_SHIELDS": "Raw shield strength, including boosters",
"TT_SUMMARY_SHIELDS_SCB": "Raw shield strength, including boosters and SCBs",
@@ -81,6 +81,8 @@
"TT_SUMMARY_UNLADEN_TOTAL_JUMP": "Farthest possible range with no cargo, a full fuel tank, and jumping as far as possible each time",
"TT_SUMMARY_LADEN_TOTAL_JUMP": "Farthest possible range with full cargo, a full fuel tank, and jumping as far as possible each time",
"HELP_MODIFICATIONS_MENU": "Click on a number to enter a new value, or drag along the bar for small changes",
"PHRASE_FAIL_EDENGINEER": "Failed to send to EDEngineer (Launch EDEngineer and make sure the API is started then refresh the page.)",
"PHRASE_FIREFOX_EDENGINEER": "Sending to EDEngineer is not compatible with Firefox's security settings. Please try again with Chrome.",
"am": "Auto Field-Maintenance Unit",
"bh": "Bulkheads",
"bl": "Beam Laser",
@@ -132,6 +134,10 @@
"gfsb": "Guardian Frame Shift Drive Booster",
"ghrp": "Guardian Hull Reinforcement Package",
"gmrp": "Guardian Module Reinforcement Package",
"pwa": "Pulse Wave Analyser",
"abl": "Abrasion Blaster",
"scl": "Seismic Charge Launcher",
"sdm": "Sub-Surface Displacement Missile",
"tbsc": "Shock Cannon",
"gsc": "Guardian Shard Cannon",
"psg": "Prismatic Shield Generator",
@@ -147,6 +153,7 @@
"sfn": "Shutdown Field Neutraliser",
"sg": "Shield Generator",
"ss": "Surface Scanners",
"sua": "Supercruise Assist",
"t": "thrusters",
"tp": "Torpedo Pylon",
"ul": "Burst Laser",
@@ -168,6 +175,7 @@
"ammunition": "Ammo",
"secs": "s",
"rebuildsperbay": "Rebuilds per bay",
"mroll": "Roll",
"worst": "Worst",
"average": "Average",
"random": "Random",
@@ -197,7 +205,7 @@
"internal protection": "Internal protection",
"external protection": "External protection",
"engagement range": "Engagement range",
"boost time": "Boost time",
"boost interval": "Boost intervall",
"total": "Total",
"ammo": "Ammunition maximum",
"boot": "Boot time",
@@ -234,6 +242,7 @@
"rof": "Rate of fire",
"angle": "Scan angle",
"scanrate": "Scan rate",
"proberadius": "Probe Radius",
"scantime": "Scan time",
"shield": "Shield",
"armour": "Armour",
@@ -315,6 +324,7 @@
"never": "never",
"stock": "stock",
"boost": "boost",
"tab_defence": "defence",
"federation rank 1": "Recruit",
"federation rank 2": "Cadet",
"federation rank 3": "Midshipman",

View File

@@ -59,7 +59,7 @@
"empty all": "vide tout",
"Enter Name": "Entrer nom",
"Explorer": "explorateur",
"fastest range": "gamme la plus rapide",
"farthest range": "gamme la plus rapide",
"fuel": "carburant",
"fuel level": "niveau de carburant",
"full tank": "Réservoir plein",

View File

@@ -299,7 +299,7 @@
"edit data": "Редактирование",
"empty all": "пусто все",
"Enter Name": "Введите имя",
"fastest range": "быстрый диапазон",
"farthest range": "быстрый диапазон",
"fuel level": "уровень топлива",
"full tank": "Полный бак",
"internal compartments": "внутренние отсеки",

View File

@@ -103,7 +103,7 @@ export default class AboutPage extends Page {
patreon.com/coriolis_elite
</a>
, which will be used to keep Coriolis up to date and the servers
running. I also run ads, which are also used for development and hosting.
running.
</p>
<form
action="https://www.paypal.com/cgi-bin/webscr"

View File

@@ -224,9 +224,12 @@ export default class OutfittingPage extends Page {
const control = LZString.decompressFromBase64(
Utils.fromUrlSafe(parts[4])
).split('/');
sys = parseFloat(control[0]) || sys;
eng = parseFloat(control[1]) || eng;
wep = parseFloat(control[2]) || wep;
sys = parseFloat(control[0]);
eng = parseFloat(control[1]);
wep = parseFloat(control[2]);
if (sys + eng + wep > 6) {
sys = eng = wep = 2;
}
boost = control[3] == 1 ? true : false;
fuel = parseFloat(control[4]) || fuel;
cargo = parseInt(control[5]) || cargo;

View File

@@ -51,22 +51,16 @@ export default class Page extends React.Component {
}
/**
* Pages are 'pure' components that only render when props, state, or context changes.
* This method performs a shallow comparison to determine change.
*
* @param {Object} np Next/Incoming properties
* @param {Object} ns Next/Incoming state
* @param {Object} nc Next/Incoming context
* @return {Boolean} True if props, state, or context has changed
* Update the window title upon mount
*/
shouldComponentUpdate(np, ns, nc) {
return !shallowEqual(this.props, np) || !shallowEqual(this.state, ns) || !shallowEqual(this.context, nc);
componentWillMount() {
document.title = this.state.title || 'Coriolis';
}
/**
* Update the window title upon mount
*/
componentWillMount() {
componentDidMount() {
document.title = this.state.title || 'Coriolis';
}

View File

@@ -126,12 +126,14 @@ export default class ShipyardPage extends Page {
title: 'Coriolis EDCD Edition - Shipyard',
shipPredicate: 'name',
shipDesc: true,
shipSummaries: ShipyardPage.cachedShipSummaries
shipSummaries: ShipyardPage.cachedShipSummaries,
compare: {},
groupCompared: false,
};
}
/**
* Higlight the current ship in the table
* Higlight the current ship in the table on mouse over
* @param {String} shipId Ship Id
* @param {SyntheticEvent} event Event
*/
@@ -140,6 +142,24 @@ export default class ShipyardPage extends Page {
this.setState({ shipId });
}
/**
* Toggle compare highlighting for ships in the table
* @param {String} shipId Ship Id
*/
_toggleCompare(shipId) {
let compare = this.state.compare;
compare[shipId] = !compare[shipId];
this.setState({ compare });
}
/**
* Toggle grouping of compared ships in the table
* @private
*/
_toggleGroupCompared() {
this.setState({groupCompared: !this.state.groupCompared})
}
/**
* Update state with the specified sort predicates
* @param {String} shipPredicate Sort predicate - property name
@@ -169,10 +189,9 @@ export default class ShipyardPage extends Page {
* @param {Object} u Localized unit map
* @param {Function} fInt Localized integer formatter
* @param {Function} fRound Localized round formatter
* @param {Boolean} highlight Should this row be highlighted
* @return {React.Component} Table Row
*/
_shipRowElement(s, translate, u, fInt, fRound, highlight) {
_shipRowElement(s, translate, u, fInt, fRound) {
let noTouch = this.context.noTouch;
return (
@@ -181,9 +200,10 @@ export default class ShipyardPage extends Page {
style={{ height: '1.5em' }}
className={cn({
highlighted: noTouch && this.state.shipId === s.id,
alt: highlight
comparehighlight: this.state.compare[s.id],
})}
onMouseEnter={noTouch && this._highlightShip.bind(this, s.id)}
onClick={() => this._toggleCompare(s.id)}
>
<td className="ri">{s.manufacturer}</td>
<td className="ri">{fInt(s.retailCost)}</td>
@@ -236,7 +256,7 @@ export default class ShipyardPage extends Page {
let hide = this.context.tooltip.bind(null, null);
let fInt = formats.int;
let fRound = formats.round;
let { shipSummaries, shipPredicate, shipPredicateIndex } = this.state;
let { shipSummaries, shipPredicate, shipPredicateIndex, compare, groupCompared } = this.state;
let sortShips = (predicate, index) =>
this._sortShips.bind(this, predicate, index);
@@ -269,6 +289,15 @@ export default class ShipyardPage extends Page {
valB = val;
}
if (groupCompared) {
if (compare[a.id] && !compare[b.id]) {
return -1;
}
if (!compare[a.id] && compare[b.id]) {
return 1;
}
}
if (valA == valB) {
if (a.name > b.name) {
return 1;
@@ -286,27 +315,13 @@ export default class ShipyardPage extends Page {
let shipRows = new Array(shipSummaries.length);
let detailRows = new Array(shipSummaries.length);
let lastShipSortValue = null;
let backgroundHighlight = false;
for (let s of shipSummaries) {
let shipSortValue = s[shipPredicate];
if (shipPredicateIndex != undefined) {
shipSortValue = shipSortValue[shipPredicateIndex];
}
if (shipSortValue != lastShipSortValue) {
backgroundHighlight = !backgroundHighlight;
lastShipSortValue = shipSortValue;
}
detailRows[i] = this._shipRowElement(
s,
translate,
units,
fInt,
formats.f1,
backgroundHighlight
);
shipRows[i] = (
<tr
@@ -314,9 +329,10 @@ export default class ShipyardPage extends Page {
style={{ height: '1.5em' }}
className={cn({
highlighted: noTouch && this.state.shipId === s.id,
alt: backgroundHighlight
comparehighlight: this.state.compare[s.id],
})}
onMouseEnter={noTouch && this._highlightShip.bind(this, s.id)}
onClick={() => this._toggleCompare(s.id)}
>
<td className="le">
<Link href={'/outfit/' + s.id}>{s.name} {s.beta === true ? '(Beta)' : null}</Link>
@@ -327,18 +343,10 @@ export default class ShipyardPage extends Page {
}
return (
<div className="page" style={{ fontSize: sizeRatio + 'em' }}>
<div
style={{
whiteSpace: 'nowrap',
margin: '0 auto',
fontSize: '0.8em',
position: 'relative',
display: 'inline-block',
maxWidth: '100%'
}}
>
<table style={{ width: '12em', position: 'absolute', zIndex: 1 }}>
<div className="page" style={{fontSize: sizeRatio + 'em'}}>
<div className="content-wrapper">
<div className="shipyard-table-wrapper">
<table style={{width: '12em', position: 'absolute', zIndex: 1}} className="shipyard-table">
<thead>
<tr>
<th className="le rgt">&nbsp;</th>
@@ -356,8 +364,8 @@ export default class ShipyardPage extends Page {
{shipRows}
</tbody>
</table>
<div style={{ overflowX: 'scroll', maxWidth: '100%' }}>
<table style={{ marginLeft: 'calc(12em - 1px)', zIndex: 0 }}>
<div style={{ overflowX: 'auto', maxWidth: '100%' }}>
<table style={{ marginLeft: 'calc(12em - 1px)', zIndex: 0 }} className="shipyard-table">
<thead>
<tr className="main">
<th
@@ -614,6 +622,10 @@ export default class ShipyardPage extends Page {
</table>
</div>
</div>
<div className="table-tools" >
<label><input type="checkbox" checked={this.state.groupCompared} onClick={() => this._toggleGroupCompared()}/>Group highlighted ships</label>
</div>
</div>
</div>
);
}

View File

@@ -15,7 +15,7 @@ export function jumpRange(mass, fsd, fuel, ship) {
let jumpAddition = 0;
if (ship) {
for (const module of ship.internal) {
if (module && module.m && module.m.grp === 'gfsb') {
if (module && module.m && module.m.grp === 'gfsb' && ship.getSlotStatus(module) == 3) {
jumpAddition += module.m.getJumpBoost();
}
}
@@ -400,7 +400,7 @@ export function shieldMetrics(ship, sys) {
let shieldAddition = 0;
if (ship) {
for (const module of ship.internal) {
if (module && module.m && module.m.grp === 'gsrp') {
if (module && module.m && module.m.grp === 'gsrp' && module.enabled) {
shieldAddition += module.m.getShieldAddition();
}
}
@@ -465,6 +465,7 @@ export function shieldMetrics(ship, sys) {
boosters: boostersStrength,
addition: shieldAddition,
cells: ship.shieldCells,
summary: generatorStrength + boostersStrength + shieldAddition,
total: generatorStrength + boostersStrength + ship.shieldCells + shieldAddition,
recover,
recharge,
@@ -573,7 +574,7 @@ export function armourMetrics(ship) {
// };
// Armour from HRPs and module armour from MRPs
for (let slot of ship.internal) {
if (slot.m && (slot.m.grp === 'hr' || slot.m.grp === 'ghrp' || slot.m.grp == 'mahr')) {
if (slot.m && slot.enabled && (slot.m.grp === 'hr' || slot.m.grp === 'ghrp' || slot.m.grp == 'mahr')) {
armourReinforcement += slot.m.getHullReinforcement();
// Hull boost for HRPs is applied against the ship's base armour
armourReinforcement += ship.baseArmour * slot.m.getModValue('hullboost') / 10000;
@@ -585,7 +586,7 @@ export function armourMetrics(ship) {
hullThermDmg = hullThermDmg * (1 - slot.m.getThermalResistance());
hullCausDmg = hullCausDmg * (1 - slot.m.getCausticResistance());
}
if (slot.m && (slot.m.grp == 'mrp' || slot.m.grp == 'gmrp')) {
if (slot.m && slot.enabled && (slot.m.grp == 'mrp' || slot.m.grp == 'gmrp')) {
moduleArmour += slot.m.getIntegrity();
moduleProtection = moduleProtection * (1 - slot.m.getProtection());
}
@@ -1014,7 +1015,10 @@ export function timeToDrainWep(ship, wep) {
*/
export function timeToDeplete(amount, dps, eps, capacity, recharge) {
const drainPerSecond = eps - recharge;
if (drainPerSecond <= 0) {
// If there is nothing to remove, we're don instantly
if (!amount) {
return 0;
} if (drainPerSecond <= 0) {
// Simple result
return amount / dps;
} else {

View File

@@ -57,6 +57,7 @@ export const ModuleGroupToName = {
ghrp: 'Guardian Hull Reinforcement Package',
gmrp: 'Guardian Module Reinforcement Package',
mahr: 'Meta Alloy Hull Reinforcement Package',
sua: 'Supercruise Assist',
// Hard Points
bl: 'Beam Laser',
@@ -94,6 +95,10 @@ export const ModuleGroupToName = {
gsc: 'Guardian Shard Cannon',
tbem: 'Enzyme Missile Rack',
tbrfl: 'Remote Release Flechette Launcher',
pwa: 'Pulse Wave Analyser',
abl: 'Abrasion Blaster',
scl: 'Seismic Charge Launcher',
sdm: 'Sub-Surface Displacement Missile',
};
let GrpNameToCodeMap = {};
@@ -202,7 +207,7 @@ export const ShipFacets = [
i: 9
},
{ // 10
title: 'fastest range',
title: 'farthest range',
props: ['unladenFastestRange', 'ladenFastestRange'],
lbls: ['unladen', 'laden'],
unit: 'LY',

View File

@@ -46,7 +46,7 @@ export default class Module {
if ((!raw) && this.blueprint && this.blueprint.special) {
// This module has a special effect, see if we need to alter our returned value
const modifierActions = Modifications.modifierActions[this.blueprint.special.edname];
const modifierActions = Modifications.modifierActions[this.blueprint.special.key];
if (modifierActions && modifierActions[name]) {
// this special effect modifies our returned value
const modification = Modifications.modifications[name];
@@ -59,14 +59,7 @@ export default class Module {
} else if (modification.method === 'overwrite') {
result = modifierActions[name];
} else {
// rate of fire is special, as it's really burst interval. Handle that here
let mod = null;
if (name === 'rof') {
mod = 1 / (1 + modifierActions[name]) - 1;
} else {
mod = modifierActions[name];
}
result = (((1 + result / multiplier) * (1 + mod)) - 1) * multiplier;
result = (((1 + result / multiplier) * (1 + modifierActions[name])) - 1) * multiplier;
}
}
}
@@ -90,7 +83,7 @@ export default class Module {
}
if (valueiswithspecial && this.blueprint && this.blueprint.special) {
// This module has a special effect, see if we need to alter the stored value
const modifierActions = Modifications.modifierActions[this.blueprint.special.edname];
const modifierActions = Modifications.modifierActions[this.blueprint.special.key];
if (modifierActions && modifierActions[name]) {
// This special effect modifies the value being set, so we need to revert it prior to storing the value
const modification = Modifications.modifications[name];
@@ -105,14 +98,7 @@ export default class Module {
} else if (modification.method === 'overwrite') {
value = null;
} else {
// rate of fire is special, as it's really burst interval. Handle that here
let mod = null;
if (name === 'rof') {
mod = 1 / (1 + modifierActions[name]) - 1;
} else {
mod = modifierActions[name];
}
value = ((value / 10000 + 1) / (1 + mod) - 1) * 10000;
value = ((value / 10000 + 1) / (1 + modifierActions[name]) - 1) * 10000;
}
}
}
@@ -131,6 +117,13 @@ export default class Module {
* @return {Number} The value queried
*/
get(name, modified = true) {
if (name == 'rof' && isNaN(this[name])) {
let fireint = this['fireint'];
if (!isNaN(fireint)) {
this['rof'] = 1 / fireint;
}
}
let val;
if (modified) {
val = this._getModifiedValue(name);
@@ -166,14 +159,20 @@ export default class Module {
modValue = value - baseValue;
} else if (name === 'shieldboost' || name === 'hullboost') {
modValue = (1 + value) / (1 + baseValue) - 1;
} else if (name === 'rof') {
let burst = this.get('burst', true) || 1;
let burstInt = 1 / (this.get('burstrof', true) / 1);
let interval = burst / value;
let newFireint = (interval - (burst - 1) * burstInt);
modValue = newFireint / this['fireint'] - 1;
} else { // multiplicative
modValue = value / baseValue - 1;
modValue = baseValue == 0 ? 0 : value / baseValue - 1;
}
if (modification.type === 'percentage') {
modValue = modValue * 10000;
} else if (modification.type === 'numeric' && name !== 'burst' &&
name !== 'burstrof') {
} else if (modification.type === 'numeric') {
modValue = modValue * 100;
}
@@ -191,7 +190,14 @@ export default class Module {
*/
getPretty(name, modified = true, places = 2) {
const formattingOptions = STATS_FORMATTING[name];
let val = this.get(name, modified) || 0;
let val;
if (formattingOptions && formattingOptions.synthetic) {
val = (this[formattingOptions.synthetic]).call(this, modified);
} else {
val = this.get(name, modified);
}
val = val || 0;
if (formattingOptions && formattingOptions.format.startsWith('pct')) {
return 100 * val;
}
@@ -250,12 +256,17 @@ export default class Module {
} else if (name === 'shieldboost' || name === 'hullboost') {
result = (1 + result) * (1 + modValue) - 1;
} else {
// Rate of fire modifiers are special as they actually are modifiers
// for fire interval. Translate them accordingly here:
if (name == 'rof') {
modValue = 1 / (1 + modValue) - 1;
}
result = result * (1 + modValue);
}
} else if (name === 'burstrof') {
} else if (name === 'burstrof' || name === 'burst') {
// Burst and burst rate of fire are special, as it can not exist but
// have a modification
result = modValue / 100;
result = modValue;
}
}
}
@@ -277,11 +288,7 @@ export default class Module {
formatModifiedValue(name, language, unit, val) {
const formattingOptions = STATS_FORMATTING[name];
if (val === undefined) {
if (formattingOptions && formattingOptions.synthetic) {
val = (this[formattingOptions.synthetic]).call(this, true);
} else {
val = this._getModifiedValue(name);
}
val = this.getPretty(name, true);
}
val = val || 0;
@@ -351,14 +358,18 @@ export default class Module {
if (formattingOptions && formattingOptions.change) {
let changeFormatting = formattingOptions.change;
let baseVal = this[name];
let baseVal = this[name] || 0;
let absVal = this._getModifiedValue(name);
if (changeFormatting === 'additive') {
val = absVal - baseVal;
} else if (changeFormatting === 'multiplicative') {
val = absVal / baseVal - 1;
}
val *= 10000;
if (Modifications.modifications[name].method === 'overwrite') {
val *= 100;
} else {
val *= 10000;
}
}
return val;
}
@@ -572,20 +583,9 @@ export default class Module {
* @return {Number} the falloff of this module
*/
getFalloff(modified = true) {
if (!modified) {
const range = this.getRange(false);
const falloff = this.get('falloff', false);
return (falloff > range ? range : falloff);
}
// Falloff from range is mapped to range
if (this.mods && this.mods['fallofffromrange']) {
if (modified && this.mods && this.mods['fallofffromrange']) {
return this.getRange();
// Need to find out if we have a focused modification, in which case our
// falloff is scaled to range
} else if (this.blueprint && this.blueprint.name === 'Focused') {
const rangeMod = this.getModValue('range') / 10000;
return this.falloff * (1 + rangeMod);
// Standard falloff calculation
} else {
const range = this.getRange();
@@ -639,15 +639,6 @@ export default class Module {
return this.get('protection', modified);
}
/**
* Get the delay for this module
* @param {Boolean} [modified=true] Whether to take modifications into account
* @return {Number} the delay of this module
*/
getDelay(modified = true) {
return this.get('delay', modified);
}
/**
* Get the duration for this module
* @param {Boolean} [modified=true] Whether to take modifications into account
@@ -703,8 +694,7 @@ export default class Module {
let result = 0;
if (this['maxmass']) {
result = this['maxmass'];
// max mass is only modified for non-shield boosters
if (result && modified && this.grp !== 'sg') {
if (result && modified) {
let mult = this.getModValue('optmass') / 10000;
if (mult) { result = result * (1 + mult); }
}
@@ -793,24 +783,6 @@ export default class Module {
return this.get('distdraw', modified);
}
/**
* Get the thermal load for this module
* @param {Boolean} [modified=true] Whether to take modifications into account
* @return {Number} the thermal load of this module
*/
getThermalLoad(modified = true) {
return this.get('thermload', modified);
}
/**
* Get the rounds per shot for this module
* @param {Boolean} [modified=true] Whether to take modifications into account
* @return {Number} the rounds per shot of this module
*/
getRoundsPerShot(modified = true) {
return this.get('roundspershot', modified);
}
/**
* Get the DPS for this module
* @param {Boolean} [modified=true] Whether to take modifications into account
@@ -834,25 +806,39 @@ export default class Module {
return this.getDps(modified) / this.getEps(modified);
}
/**
* Return the factor that gets applied when calculating certain "sustained"
* values, e.g. `SDPS = this.getSustainedFactor() * DPS`.
* @param {Boolean} [modified=true] Whether to take modifications into account
*/
getSustainedFactor(modified = true) {
let clipSize = this.getClip(modified);
if (clipSize) {
// If auto-loader is applied, effective clip size will be nearly doubled
// as you get one reload for every two shots fired.
if (this.blueprint && this.blueprint.special && this.blueprint.special.key === 'special_auto_loader' && modified) {
clipSize += clipSize - 1;
}
let burstSize = this.get('burst', modified) || 1;
let rof = this.getRoF(modified);
// rof averages burstfire + pause until next interval but for sustained
// rof we need to take another burst without pause into account
let burstOverhead = (burstSize - 1) / (this.get('burstrof', modified) || 1);
let srof = clipSize / ((clipSize - burstSize) / rof + burstOverhead + this.getReload(modified));
return srof / rof;
} else {
return 1;
}
}
/**
* Get the SDPS for this module
* @param {Boolean} [modified=true] Whether to take modifications into account
* @return {Number} The SDPS of this module
*/
getSDps(modified = true) {
let dps = this.getDps(modified);
if (this.getClip(modified)) {
let clipSize = this.getClip(modified);
// If auto-loader is applied, effective clip size will be nearly doubled
// as you get one reload for every two shots fired.
if (this.blueprint && this.blueprint.special && this.blueprint.special.edname === 'special_auto_loader' && modified) {
clipSize += clipSize - 1;
}
let timeToDeplete = clipSize / this.getRoF(modified);
return dps * timeToDeplete / (timeToDeplete + this.getReload(modified));
} else {
return dps;
}
return this.getDps(modified) * this.getSustainedFactor(modified);
}
/**
@@ -876,7 +862,7 @@ export default class Module {
*/
getHps(modified = true) {
// HPS is a synthetic value
let heat = this.getThermalLoad(modified);
let heat = this.get('thermload', modified);
// We don't use rpshot here as dist draw is per combined shot
let rof = this.getRoF(modified) || 1;
@@ -913,24 +899,6 @@ export default class Module {
return this.get('reload', modified);
}
/**
* Get the burst size for this module
* @param {Boolean} [modified=true] Whether to take modifications into account
* @return {Number} the burst size of this module
*/
getBurst(modified = true) {
return this.get('burst', modified);
}
/**
* Get the burst rate of fire for this module
* @param {Boolean} [modified=true] Whether to take modifications into account
* @return {Number} the burst rate of fire of this module
*/
getBurstRoF(modified = true) {
return this.get('burstrof', modified);
}
/**
* Get the rate of fire for this module.
* The rate of fire is a combination value, and needs to take in to account
@@ -941,8 +909,8 @@ export default class Module {
* @return {Number} the rate of fire for this module
*/
getRoF(modified = true) {
const burst = this.getBurst(modified) || 1;
const burstRoF = this.getBurstRoF(modified) || 1;
const burst = this.get('burst', modified) || 1;
const burstRoF = this.get('burstrof', modified) || 1;
const intRoF = this.get('rof', modified);
return burst / (((burst - 1) / burstRoF) + 1 / intRoF);
@@ -1073,4 +1041,31 @@ export default class Module {
getHackTime(modified = true) {
return this.get('hacktime', modified);
}
/**
* Get the scan range for this module
* @param {Boolean} [modified=true] Whether to take modifications into account
* @return {string} the time for this module
*/
getScanRange(modified = true) {
return this.get('scanrange', modified);
}
/**
* Get the scan angle for this module
* @param {Boolean} [modified=true] Whether to take modifications into account
* @return {string} the time for this module
*/
getScanAngle(modified = true) {
return this.get('scanangle', modified);
}
/**
* Get the max angle for this module
* @param {Boolean} [modified=true] Whether to take modifications into account
* @return {string} the time for this module
*/
getMaxAngle(modified = true) {
return this.get('maxangle', modified);
}
}

View File

@@ -85,12 +85,12 @@ export function toDetailedBuild(buildName, ship) {
code = ship.toString();
let data = {
$schema: 'https://coriolis.edcd.io/schemas/ship-loadout/4.json#',
$schema: 'https://coriolis.io/schemas/ship-loadout/4.json#',
name: buildName,
ship: ship.name,
references: [{
name: 'Coriolis.io',
url: 'https://coriolis.edcd.io' + outfitURL(ship.id, code, buildName),
url: 'https://coriolis.io' + outfitURL(ship.id, code, buildName),
code,
shipId: ship.id
}],

View File

@@ -10,7 +10,7 @@ import { Ships, Modifications } from 'coriolis-data/dist';
import { chain } from 'lodash';
const zlib = require('zlib');
const UNIQUE_MODULES = ['psg', 'sg', 'bsg', 'rf', 'fs', 'fh', 'gfsb'];
const UNIQUE_MODULES = ['psg', 'sg', 'bsg', 'rf', 'fs', 'fh', 'gfsb', 'dc'];
// Constants for modifications struct
const SLOT_ID_DONE = -1;
@@ -1308,7 +1308,7 @@ export default class Ship {
let fsd = this.standard[2].m; // Frame Shift Drive;
let { unladenMass, fuelCapacity } = this;
this.unladenRange = this.calcUnladenRange(); // Includes fuel weight for jump
this.fullTankRange = Calc.jumpRange(unladenMass + fuelCapacity, fsd, this); // Full Tank
this.fullTankRange = Calc.jumpRange(unladenMass + fuelCapacity, fsd, fuelCapacity, this); // Full Tank
this.ladenRange = this.calcLadenRange(); // Includes full tank and caro
this.unladenFastestRange = Calc.totalJumpRange(unladenMass + this.fuelCapacity, fsd, fuelCapacity, this);
this.ladenFastestRange = Calc.totalJumpRange(unladenMass + this.fuelCapacity + this.cargoCapacity, fsd, fuelCapacity, this);

View File

@@ -26,8 +26,8 @@ export const STATS_FORMATTING = {
'ammo': { 'format': 'int', },
'boot': { 'format': 'int', 'unit': 'secs' },
'brokenregen': { 'format': 'round1', 'unit': 'ps' },
'burst': { 'format': 'int' },
'burstrof': { 'format': 'round1', 'unit': 'ps' },
'burst': { 'format': 'int', 'change': 'additive' },
'burstrof': { 'format': 'round1', 'unit': 'ps', 'change': 'additive' },
'causres': { 'format': 'pct' },
'clip': { 'format': 'int' },
'damage': { 'format': 'round' },
@@ -61,7 +61,7 @@ export const STATS_FORMATTING = {
'ranget': { 'format': 'f1', 'unit': 's' },
'regen': { 'format': 'round1', 'unit': 'ps' },
'reload': { 'format': 'int', 'unit': 's' },
'rof': { 'format': 'round1', 'unit': 'ps' },
'rof': { 'format': 'round1', 'unit': 'ps', 'synthetic': 'getRoF', 'higherbetter': true },
'angle': { 'format': 'round1', 'unit': 'ang' },
'scanrate': { 'format': 'int' },
'scantime': { 'format': 'round1', 'unit': 's' },
@@ -78,5 +78,6 @@ export const STATS_FORMATTING = {
'thermres': { 'format': 'pct' },
'wepcap': { 'format': 'round1', 'unit': 'MJ' },
'weprate': { 'format': 'round1', 'unit': 'MW' },
'jumpboost': { 'format': 'round1', 'unit': 'LY' }
'jumpboost': { 'format': 'round1', 'unit': 'LY' },
'proberadius': { 'format': 'pct1', 'unit': 'pct' },
};

View File

@@ -1,5 +1,6 @@
import React from 'react';
import { Modifications } from 'coriolis-data/dist';
import { STATS_FORMATTING } from '../shipyard/StatsFormatting';
/**
* Generate a tooltip with details of a blueprint's specials
@@ -155,7 +156,7 @@ export function blueprintTooltip(translate, blueprint, engineers, grp, m) {
// We also add in any benefits from specials that aren't covered above
if (m.blueprint && m.blueprint.special) {
for (const feature in Modifications.modifierActions[m.blueprint.special.edname]) {
for (const feature in Modifications.modifierActions[m.blueprint.special.key]) {
if (!blueprint.features[feature] && !m.mods.feature) {
const featureDef = Modifications.modifications[feature];
if (featureDef && !featureDef.hidden) {
@@ -280,6 +281,25 @@ export function isValueBeneficial(feature, value) {
}
}
/**
* Is the change as shown beneficial?
* @param {string} feature The name of the feature
* @param {number} value The value of the feature as percentage change
* @returns True if the value is beneficial
*/
export function isChangeValueBeneficial(feature, value) {
let changeHigherBetter = STATS_FORMATTING[feature].higherbetter;
if (changeHigherBetter === undefined) {
return isValueBeneficial(feature, value);
}
if (changeHigherBetter) {
return value > 0;
} else {
return value < 0;
}
}
/**
* Get a blueprint with a given name and an optional module
* @param {string} name The name of the blueprint
@@ -372,9 +392,7 @@ export function getPercent(m) {
let value = _getValue(m, featureName);
let mult;
if (featureName == 'shieldboost') {
mult = ((1 + value) * (1 + m.shieldboost)) - 1 - m.shieldboost;
} else if (Modifications.modifications[featureName].higherbetter) {
if (Modifications.modifications[featureName].higherbetter) {
// Higher is better, but is this making it better or worse?
if (features[featureName][0] < 0 || (features[featureName][0] === 0 && features[featureName][1] < 0)) {
mult = Math.round((value - features[featureName][1]) / (features[featureName][0] - features[featureName][1]) * 100);

View File

@@ -249,6 +249,13 @@ function _addModifications(module, modifiers, blueprint, grade, specialModificat
if (!modifiers) return;
let special;
if (specialModifications) {
if (specialModifications == 'special_plasma_slug') {
if (module.symbol.match(/PlasmaAccelerator/i)) {
specialModifications = 'special_plasma_slug_pa';
} else {
specialModifications = 'special_plasma_slug_cooled';
}
}
special = Modifications.specials[specialModifications];
}
// Add the blueprint definition, grade and special
@@ -274,6 +281,9 @@ function _addModifications(module, modifiers, blueprint, grade, specialModificat
if (value === Infinity) {
value = modifiers[i].Value * 100;
}
if (modifiers[i].Label.search('DamageFalloffRange') >= 0) {
value = (modifiers[i].Value / module.range - 1) * 100;
}
if (modifiers[i].Label.search('Resistance') >= 0) {
value = (modifiers[i].Value * 100) - (modifiers[i].OriginalValue * 100);
}

View File

@@ -1,6 +1,7 @@
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
@@ -139,20 +140,21 @@ function diff(format, mVal, mmVal) {
export function diffDetails(language, m, mm) {
let { formats, translate, units } = language;
let propDiffs = [];
m = new Module(m);
// Module-specific items
if (m.grp === 'pp') {
let mPowerGeneration = m.pgen || 0;
let mPowerGeneration = m.getPowerGeneration() || 0;
let mmPowerGeneration = mm ? mm.getPowerGeneration() : 0;
if (mPowerGeneration != mmPowerGeneration) propDiffs.push(<div key='pgen'>{translate('pgen')}: <span className={diffClass(mPowerGeneration, mmPowerGeneration)}>{diff(formats.round, mPowerGeneration, mmPowerGeneration)}{units.MJ}</span></div>);
if (mPowerGeneration != mmPowerGeneration) propDiffs.push(<div key='pgen'>{translate('pgen')}: <span className={diffClass(mPowerGeneration, mmPowerGeneration)}>{diff(formats.round, mPowerGeneration, mmPowerGeneration)}{units.MW}</span></div>);
} else {
let mPowerUsage = m.power || 0;
let mPowerUsage = m.getPowerUsage() || 0;
let mmPowerUsage = mm ? mm.getPowerUsage() || 0 : 0;
if (mPowerUsage != mmPowerUsage) propDiffs.push(<div key='power'>{translate('power')}: <span className={diffClass(mPowerUsage, mmPowerUsage, true)}>{diff(formats.round, mPowerUsage, mmPowerUsage)}{units.MJ}</span></div>);
if (mPowerUsage != mmPowerUsage) propDiffs.push(<div key='power'>{translate('power')}: <span className={diffClass(mPowerUsage, mmPowerUsage, true)}>{diff(formats.round, mPowerUsage, mmPowerUsage)}{units.MW}</span></div>);
}
let mDps = m.damage * (m.rpshot || 1) * (m.rof || 1);
let mDps = m.getDps() || 0;
let mmDps = mm ? mm.getDps() || 0 : 0;
if (mDps && mDps != mmDps) propDiffs.push(<div key='dps'>{translate('dps')}: <span className={diffClass(mmDps, mDps, true)}>{diff(formats.round, mDps, mmDps)}</span></div>);
@@ -164,7 +166,7 @@ export function diffDetails(language, m, mm) {
if (mAffectsShield) {
if (m.grp == 'sb') { // Both m and mm must be utility modules if this is true
newShield = this.calcShieldStrengthWith(null, m.shieldboost - (mm ? mm.getShieldBoost() || 0 : 0));
newShield = this.calcShieldStrengthWith(null, m.getShieldBoost() - (mm ? mm.getShieldBoost() || 0 : 0));
} else {
newShield = this.calcShieldStrengthWith(m);
}
@@ -179,7 +181,7 @@ export function diffDetails(language, m, mm) {
}
if (m.grp === 'mrp') {
let mProtection = m.protection;
let mProtection = m.getProtection();
let mmProtection = mm ? mm.getProtection() || 0 : 0;
if (mProtection != mmProtection) {
propDiffs.push(<div key='protection'>{translate('protection')}: <span className={diffClass(mmProtection, mProtection, true)}>{diff(formats.pct, mProtection, mmProtection)}</span></div>);
@@ -187,7 +189,7 @@ export function diffDetails(language, m, mm) {
}
if (m.grp === 'hr') {
let mHullReinforcement = m.hullreinforcement;
let mHullReinforcement = m.getHullReinforcement();
let mmHullReinforcement = mm ? mm.getHullReinforcement() || 0 : 0;
if (mHullReinforcement && mHullReinforcement != mmHullReinforcement) propDiffs.push(<div key='hullreinforcement'>{translate('hullreinforcement')}: <span className={diffClass(mmHullReinforcement, mHullReinforcement, true)}>{diff(formats.round, mHullReinforcement, mmHullReinforcement)}</span></div>);
}
@@ -219,7 +221,7 @@ export function diffDetails(language, m, mm) {
let mmCost = mm ? mm.cost : 0;
if (mCost != mmCost) propDiffs.push(<div key='cost'>{translate('cost')}: <span className={diffClass(mCost, mmCost, true) }>{formats.int(mCost ? Math.round(mCost * (1 - Persist.getModuleDiscount())) : 0)}{units.CR}</span></div>);
let mMass = m.mass || 0;
let mMass = m.getMass() || 0;
let mmMass = mm ? mm.getMass() : 0;
if (mMass != mmMass) propDiffs.push(<div key='mass'>{translate('mass')}: <span className={diffClass(mMass, mmMass, true)}>{diff(formats.round, mMass, mmMass)}{units.T}</span></div>);
@@ -240,7 +242,7 @@ export function diffDetails(language, m, mm) {
}
}
let mIntegrity = m.integrity || 0;
let mIntegrity = m.getIntegrity() || 0;
let mmIntegrity = mm ? mm.getIntegrity() || 0 : 0;
if (mIntegrity != mmIntegrity) {
propDiffs.push(<div key='integrity'>{translate('integrity')}: <span className={diffClass(mmIntegrity, mIntegrity, true)}>{diff(formats.round, mIntegrity, mmIntegrity)}</span></div>);

View File

@@ -32,33 +32,19 @@
window.CORIOLIS_DATE = '<%- htmlWebpackPlugin.options.date.toISOString().slice(0, 10) %>';
window.BUGSNAG_VERSION = '<%- htmlWebpackPlugin.options.version + '-' + htmlWebpackPlugin.options.date.toISOString() %>';
</script>
<% if (htmlWebpackPlugin.options.uaTracking) { %>
<script>
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
ga('create', '<%- htmlWebpackPlugin.options.uaTracking %>', 'auto');
ga('send', 'pageview');
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-55840909-18"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-55840909-18');
</script>
<script async src='https://www.google-analytics.com/analytics.js'></script>
<% } %>
<!-- Piwik -->
<!-- <script type="text/javascript">
var _paq = _paq || [];
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
_paq.push(["setCookieDomain", "*.coriolis.edcd.io"]);
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u="//stats.isadankme.me/";
_paq.push(['setTrackerUrl', u+'piwik.php']);
_paq.push(['setSiteId', '4']);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s);
})();
</script>-->
<!-- End Piwik Code -->
<!-- Bugsnag -->
<!-- Bugsnag -->
<script src="https://d2wy8f7a9ursnm.cloudfront.net/v5.0.0/bugsnag.min.js"></script>
<script>
window.bugsnagClient = bugsnag('ba9fae819372850fb660755341fa6ef5', {appVersion: window.BUGSNAG_VERSION || undefined})

View File

@@ -2,6 +2,8 @@
@bgDarken: 40%;
@disabledDarken: 15%;
@bgTransparency: 10%;
@bgHighlight: 5%;
@fgHighlight: 10%;
// Foreground colors
@fg: #CCC;
@@ -21,9 +23,14 @@
@bgBlack: #000;
@primary-bg: fadeout(darken(@primary, 47%), 15%);
@alt-primary-bg: fadeout(darken(@primary, 42%), 15%); // Lighter brown background
@secondary-bg: fadeout(darken(@secondary, @bgDarken), @bgTransparency); // Brown background
@secondary-bg: fadeout(darken(@secondary, @bgDarken), @bgTransparency); // Blue background
@warning-bg: fadeout(darken(@warning, @bgDarken), @bgTransparency); // Dark Red
@alt-primary-bg-highlighted: lighten(@alt-primary-bg, @bgHighlight);
@fg-highlighted: lighten(@fg, @fgHighlight);
@primary-darker: darken(@primary, 30%);
.fg {
color: @fg;

View File

@@ -54,7 +54,7 @@ textarea {
width:100%;
min-height: 10em;
resize: vertical;
user-select: auto;
user-select: text;
margin:2em 0;
}
}

View File

@@ -49,4 +49,45 @@ a.ship {
font-size: 0.7em;
float: right;
}
}
}
.shipyard-table-wrapper {
white-space: nowrap;
margin: 0 auto;
font-size: 0.8em;
position: relative;
display: inline-block;
max-width: 100%;
}
table.shipyard-table{
tbody tr.comparehighlight{
background-color: @secondary-bg;
color: @fg-highlighted;
}
}
.shipyard-table-wrapper {
border-bottom: 1px solid @primary-darker;
}
.shipyard-table-wrapper div .shipyard-table td:last-child {
border-right: 1px solid @primary-darker;
}
.shipyard-table-wrapper > .shipyard-table td:first-child {
border-left: 1px solid @primary-darker;
}
.content-wrapper{
display: inline-block;
margin: 0 auto;
max-width: 100%;
}
.table-tools{
text-align: left;
color: @primary;
label{
cursor: pointer;
}
}

View File

@@ -52,10 +52,10 @@ tbody tr {
}
.no-touch &.highlight:hover, .no-touch &.highlighted {
background-color: @warning-bg;
background-color: @alt-primary-bg-highlighted; //@warning-bg;
}
&.alt {
&:nth-child(odd){
background-color: @alt-primary-bg;
}
}
@@ -84,3 +84,5 @@ td {
text-align: center;
}
}

View File

@@ -1,6 +1,6 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"id": "https://coriolis.edcd.io/schemas/ship-loadout/1.json#",
"id": "https://coriolis.io/schemas/ship-loadout/1.json#",
"title": "Ship Loadout",
"type": "object",
"description": "The details for a specific ship build/loadout. DEPRECATED in favor of Version 3",

View File

@@ -1,6 +1,6 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"id": "https://coriolis.edcd.io/schemas/ship-loadout/2.json#",
"id": "https://coriolis.io/schemas/ship-loadout/2.json#",
"title": "Ship Loadout",
"type": "object",
"description": "The details for a specific ship build/loadout. DEPRECATED in favor of Version 3",

View File

@@ -1,6 +1,6 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"id": "https://coriolis.edcd.io/schemas/ship-loadout/3.json#",
"id": "https://coriolis.io/schemas/ship-loadout/3.json#",
"title": "Ship Loadout",
"type": "object",
"description": "The details for a specific ship build/loadout",

View File

@@ -1,6 +1,6 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"id": "https://coriolis.edcd.io/schemas/ship-loadout/4.json#",
"id": "https://coriolis.io/schemas/ship-loadout/4.json#",
"title": "Ship Loadout",
"type": "object",
"description": "The details for a specific ship build/loadout",

View File

@@ -7,13 +7,26 @@ if (workbox) {
workbox.routing.registerNavigationRoute('/index.html');
workbox.routing.registerRoute(
new RegExp('/(.*?)'),
workbox.strategies.staleWhileRevalidate()
/\.(?:png|jpg|jpeg|svg|gif)$/,
new workbox.strategies.CacheFirst({
plugins: [
new workbox.cacheableResponse.Plugin({
statuses: [0, 200]
})
]
})
);
workbox.routing.registerRoute(
/\.(?:js|css)$/,
new workbox.strategies.StaleWhileRevalidate({
cacheName: 'static-resources',
})
);
workbox.routing.registerRoute(
new RegExp('https://fonts.(?:googleapis|gstatic).com/(.*)'),
workbox.strategies.cacheFirst({
new workbox.strategies.CacheFirst({
cacheName: 'google-fonts',
plugins: [
new workbox.expiration.Plugin({
@@ -25,12 +38,6 @@ if (workbox) {
]
})
);
try {
workbox.googleAnalytics.initialize();
} catch (e) {
console.log('Probably an ad-blocker');
}
} else {
console.log('Boo! Workbox didn\'t load 😬');
}

View File

@@ -14,7 +14,7 @@ module.exports = {
},
mode: 'development',
entry: {
main: './src/app/index.js'
main: './src/app/index.js',
},
resolve: {
// When requiring, you don't need to add these extensions
@@ -22,13 +22,11 @@ module.exports = {
},
optimization: {
minimize: false,
splitChunks: {
chunks: 'all'
}
usedExports: true
},
output: {
path: path.join(__dirname, 'build'),
filename: 'app.js',
chunkFilename: '[name].bundle.js',
publicPath: '/'
},
plugins: [
@@ -56,7 +54,10 @@ module.exports = {
module: {
rules: [
{ test: /\.css$/, loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader' }) },
{ test: /\.less$/, loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader!less-loader' }) },
{
test: /\.less$/,
loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader!less-loader' })
},
{ test: /\.(js|jsx)$/, loaders: ['babel-loader'], include: path.join(__dirname, 'src') },
{ test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/font-woff' },
{ test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/font-woff' },

View File

@@ -11,26 +11,28 @@ const buildDate = new Date();
module.exports = {
devtool: 'source-map',
entry: {
main: ['./src/app/index.js']
main: './src/app/index.js'
},
resolve: {
extensions: ['.js', '.jsx', '.json', '.less']
},
output: {
path: path.join(__dirname, 'build'),
filename: '[name].[hash].js',
chunkFilename: '[name].bundle.js',
publicPath: '/',
globalObject: 'this'
},
mode: 'production',
optimization: {
minimize: true,
splitChunks: {
chunks: 'all'
}
usedExports: true
},
plugins: [
new CopyWebpackPlugin(['src/.htaccess', { from: 'src/schemas', to: 'schemas' }, {from: 'src/images/logo/*', flatten: true, to: ''}, 'src/iframe.html', 'src/xdLocalStoragePostMessageApi.min.js']),
new CopyWebpackPlugin(['src/.htaccess', { from: 'src/schemas', to: 'schemas' }, {
from: 'src/images/logo/*',
flatten: true,
to: ''
}, 'src/iframe.html', 'src/xdLocalStoragePostMessageApi.min.js']),
// new webpack.optimize.CommonsChunkPlugin({
// name: 'lib',
// filename: 'lib.[chunkhash:6].js'
@@ -48,15 +50,15 @@ module.exports = {
disable: false,
allChunks: true
}),
new BugsnagBuildReporterPlugin({
apiKey: 'ba9fae819372850fb660755341fa6ef5',
appVersion: `${pkgJson.version}-${buildDate.toISOString()}`
}, { /* opts */ }),
new BugsnagSourceMapUploaderPlugin({
apiKey: 'ba9fae819372850fb660755341fa6ef5',
overwrite: true,
appVersion: `${pkgJson.version}-${buildDate.toISOString()}`
}),
// new BugsnagBuildReporterPlugin({
// apiKey: 'ba9fae819372850fb660755341fa6ef5',
// appVersion: `${pkgJson.version}-${buildDate.toISOString()}`
// }, { /* opts */ }),
// new BugsnagSourceMapUploaderPlugin({
// apiKey: 'ba9fae819372850fb660755341fa6ef5',
// overwrite: true,
// appVersion: `${pkgJson.version}-${buildDate.toISOString()}`
// }),
new InjectManifest({
swSrc: './src/sw.js',
importWorkboxFrom: 'cdn',
@@ -66,7 +68,10 @@ module.exports = {
module: {
rules: [
{ test: /\.css$/, loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader' }) },
{ test: /\.less$/, loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader!less-loader' }) },
{
test: /\.less$/,
loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader!less-loader' })
},
{ test: /\.(js|jsx)$/, loader: 'babel-loader?cacheDirectory=true', include: path.join(__dirname, 'src') },
{ test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/font-woff' },
{ test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/font-woff' },