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
61 changed files with 1289 additions and 525 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", "title": "Coriolis",
"description": "Coriolis Shipyard for Elite Dangerous", "description": "Coriolis Shipyard for Elite Dangerous",
"repository": "https://github.com/EDCD/coriolis", "repository": "https://github.com/EDCD/coriolis",
"site": "https://coriolis.edcd.io", "site": "https://coriolis.io",
"author": "https://github.com/edcd", "author": "https://github.com/edcd",
"image": "./src/images/logo/192x192.png" "image": "./src/images/logo/192x192.png"
} }
@@ -81,7 +81,7 @@
"title": "Coriolis", "title": "Coriolis",
"description": "Coriolis Shipyard for Elite Dangerous", "description": "Coriolis Shipyard for Elite Dangerous",
"repository": "https://github.com/EDCD/coriolis", "repository": "https://github.com/EDCD/coriolis",
"site": "https://coriolis.edcd.io", "site": "https://coriolis.io",
"author": "https://github.com/edcd", "author": "https://github.com/edcd",
"image": "./src/images/logo/192x192.png" "image": "./src/images/logo/192x192.png"
} }

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
RUN mkdir -p /src/app/coriolis-data RUN mkdir -p /src/app/coriolis-data
COPY ./coriolis/ /src/app/coriolis RUN apk add --update git
COPY ./coriolis-data/ /src/app/coriolis-data
RUN apk update COPY . /src/app/coriolis
RUN apk add git
RUN npm i -g npm RUN npm i -g npm
# Set up coriolis-data # Set up coriolis-data
WORKDIR /src/app/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 install --no-package-lock
RUN npm start RUN npm start
# Set up coriolis
WORKDIR /src/app/coriolis WORKDIR /src/app/coriolis
RUN git fetch --all RUN git checkout ${BRANCH}
RUN npm install --no-package-lock RUN npm install --no-package-lock
RUN npm run build RUN npm run build
### STAGE 2: Production Environment ### ### STAGE 2: Production Environment ###
FROM nginx:1.13.12-alpine as web FROM fholzer/nginx-brotli as web
COPY coriolis/.docker/nginx.conf /etc/nginx/nginx.conf COPY nginx.conf /etc/nginx/nginx.conf
COPY --from=builder /src/app/coriolis/build /usr/share/nginx/html COPY --from=builder /src/app/coriolis/build /usr/share/nginx/html
WORKDIR /usr/share/nginx/html WORKDIR /usr/share/nginx/html
EXPOSE 80 EXPOSE 80

23
Jenkinsfile vendored
View File

@@ -1,23 +0,0 @@
pipeline {
agent any
stages {
stage('Get coriolis-data') {
steps {
sh '''YES=`echo $GIT_BRANCH | awk -F / \'{print $2}\'`
export BRANCH=`git rev-parse --abbrev-ref $YES`
rm -rf $WORKSPACE/../coriolis-data
git clone https://github.com/edcd/coriolis-data.git $WORKSPACE/../coriolis-data
cd ../coriolis-data
git fetch --all
git checkout $BRANCH'''
}
}
stage('Build') {
steps {
sshagent (credentials: ['63c9de04-3213-47c3-8345-2f3442097a5b']) {
sh 'ssh -p 56499 -o StrictHostKeyChecking=no willb@172.17.0.1 echo hi'
}
}
}
}
}

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. 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 ## License

View File

@@ -3,6 +3,10 @@ version: '2.2'
services: services:
coriolis_prod: coriolis_prod:
image: edcd/coriolis:master image: edcd/coriolis:master
build:
dockerfile: Dockerfile
args:
branch: master
restart: always restart: always
volumes: volumes:
- ./nginx.conf:/etc/nginx/nginx.conf - ./nginx.conf:/etc/nginx/nginx.conf
@@ -17,6 +21,10 @@ services:
coriolis_dev: coriolis_dev:
image: edcd/coriolis:develop image: edcd/coriolis:develop
build:
dockerfile: Dockerfile
args:
branch: develop
restart: always restart: always
volumes: volumes:
- ./nginx.conf:/etc/nginx/nginx.conf - ./nginx.conf:/etc/nginx/nginx.conf

View File

@@ -1,48 +1,84 @@
worker_processes 2; worker_processes 1;
error_log ./nginx.error.log; user nobody nobody;
worker_rlimit_nofile 8192; error_log /tmp/error.log;
pid nginx.pid; pid /tmp/nginx.pid;
events { events {
worker_connections 1024; worker_connections 1024;
multi_accept on;
} }
http { http {
access_log off; include /etc/nginx/mime.types;
charset UTF-8; 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 { # https://nginx.org/en/docs/http/ngx_http_gzip_module.html
text/html html htm shtml; # Enable gzip compression.
text/css css; # Default: off
text/xml xml rss; gzip off;
image/gif gif;
image/jpeg jpeg jpg;
application/x-javascript js;
text/plain txt;
image/png png;
image/svg+xml svg;
image/x-icon ico;
application/pdf pdf;
text/cache-manifest appcache;
}
gzip on; # Compression level (1-9).
gzip_vary on; # 5 is a perfect compromise between size and CPU usage, offering about
# 75% reduction for most ASCII files (almost identical to level 9).
# Default: 1
gzip_comp_level 5;
# Don't compress anything that's already small and unlikely to shrink much
# if at all (the default is 20 bytes, which is bad as that usually leads to
# larger files after gzipping).
# Default: 20
gzip_min_length 256;
# Compress data even for clients that are connecting to us via proxies,
# identified by the "Via" header (required for CloudFront).
# Default: off
gzip_proxied any; 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;
# 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 { server {
listen 3301; listen 80;
server_name localhost; listen [::]:80;
root ./build/;
index index.html; 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)$ { 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; expires -1;
add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Credentials true; add_header Access-Control-Allow-Credentials true;
@@ -50,7 +86,6 @@ http {
add_header Access-Control-Allow-Headers 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type'; 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; access_log off;
} }
location / { location / {
try_files $uri $uri/ /index.html =404; try_files $uri $uri/ /index.html =404;
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -52,12 +52,12 @@ export default class Defence extends TranslatedComponent {
* @return {React.Component} contents * @return {React.Component} contents
*/ */
render() { render() {
const { ship, sys, opponentWep } = this.props; const { opponent, sys, opponentWep } = this.props;
const { language, tooltip, termtip } = this.context; const { language, tooltip, termtip } = this.context;
const { formats, translate, units } = language; const { formats, translate, units } = language;
const { shield, armour, shielddamage, armourdamage } = this.state; const { shield, armour, shielddamage, armourdamage } = this.state;
const pd = ship.standard[4].m; const pd = opponent.standard[4].m;
const shieldSourcesData = []; const shieldSourcesData = [];
const effectiveShieldData = []; 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} 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')} {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() ? 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')} {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() ? 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')} {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} 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')} {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.getShieldBoost() ? <div className={'l'}>+{formats.pct1(m.getShieldBoost())}</div> : null}
{m.getAmmo() ? <div {m.getAmmo() ? <div
className={'l'}>{translate('ammunition')}: {formats.int(m.getClip())}/{formats.int(m.getAmmo())}</div> : null} 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 {m.getShotSpeed() ? <div
className={'l'}>{translate('shotspeed')}: {formats.int(m.getShotSpeed())}{u.mps}</div> : null} 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.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.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 {showModuleResistances && m.getExplosiveResistance() ? <div
className='l'>{translate('explres')}: {formats.pct(m.getExplosiveResistance())}</div> : null} className='l'>{translate('explres')}: {formats.pct(m.getExplosiveResistance())}</div> : null}
{showModuleResistances && m.getKineticResistance() ? <div {showModuleResistances && m.getKineticResistance() ? <div

View File

@@ -149,14 +149,14 @@ export default class HardpointSlotSection extends SlotSection {
<ul> <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> <li className='lc' tabIndex='0' onClick={_fill.bind(this, 'pa', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['pa-F'] = smRef}>{translate('pa')}</li>
</ul> </ul>
<div className='select-group cap'>{translate('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> <div className='select-group cap'>{translate('nl')}</div>
<ul> <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> <li className='lc' tabIndex='0' onClick={_fill.bind(this, 'nl', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['nl-F'] = smRef}>{translate('nl')}</li>
</ul> </ul>
<div 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> <div className='select-group cap'>{translate('rfl')}</div>
<ul> <ul>
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'rfl', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['rfl-F'] = smRef}><MountFixed className='lg'/></li> <li className='c' tabIndex='0' onClick={_fill.bind(this, 'rfl', '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() { _getShipsMenu() {
let shipList = []; 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>); 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 ? <div className={'l'}>{translate('range')}: {m.rangeLS}{u.Ls}</div> : null }
{ m.rangeLS === null ? <div className={'l'}>{u.Ls}</div> : null } { m.rangeLS === null ? <div className={'l'}>{u.Ls}</div> : null }
{ m.rangeRating ? <div className={'l'}>{translate('range')}: {m.rangeRating}</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.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.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 } { 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 { outfitURL } from '../utils/UrlGenerators';
import * as CompanionApiUtils from '../utils/CompanionApiUtils'; import * as CompanionApiUtils from '../utils/CompanionApiUtils';
const zlib = require('pako');
const textBuildRegex = new RegExp('^\\[([\\w \\-]+)\\]\n'); const textBuildRegex = new RegExp('^\\[([\\w \\-]+)\\]\n');
const lineRegex = new RegExp('^([\\dA-Z]{1,2}): (\\d)([A-I])[/]?([FGT])?([SD])? ([\\w\\- ]+)'); const lineRegex = new RegExp('^([\\dA-Z]{1,2}): (\\d)([A-I])[/]?([FGT])?([SD])? ([\\w\\- ]+)');
const mountMap = { 'H': 4, 'L': 3, 'M': 2, 'S': 1, 'U': 0 }; const mountMap = { 'H': 4, 'L': 3, 'M': 2, 'S': 1, 'U': 0 };
@@ -99,6 +101,7 @@ export default class ModalImport extends TranslatedComponent {
this.state = { this.state = {
builds: props.builds, builds: props.builds,
canEdit: !props.builds, canEdit: !props.builds,
loadoutEvent: null,
comparisons: null, comparisons: null,
shipDiscount: null, shipDiscount: null,
moduleDiscount: null, moduleDiscount: null,
@@ -111,12 +114,28 @@ export default class ModalImport extends TranslatedComponent {
this._process = this._process.bind(this); this._process = this._process.bind(this);
this._import = this._import.bind(this); this._import = this._import.bind(this);
this._importBackup = this._importBackup.bind(this); this._importBackup = this._importBackup.bind(this);
this._importLoadout = this._importLoadout.bind(this);
this._importDetailedArray = this._importDetailedArray.bind(this); this._importDetailedArray = this._importDetailedArray.bind(this);
this._importTextBuild = this._importTextBuild.bind(this); this._importTextBuild = this._importTextBuild.bind(this);
this._importCompanionApiBuild = this._importCompanionApiBuild.bind(this); this._importCompanionApiBuild = this._importCompanionApiBuild.bind(this);
this._validateImport = this._validateImport.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 * Import a Coriolis backup
* @param {Object} importData Backup Data * @param {Object} importData Backup Data
@@ -159,7 +178,7 @@ export default class ModalImport extends TranslatedComponent {
} }
// Check for module discount // Check for module discount
if (!isNaN(importData.moduleDiscount)) { if (!isNaN(importData.moduleDiscount)) {
this.setState({ shipDiscount: importData.moduleDiscount * 1 }); this.setState({ moduleDiscount: importData.moduleDiscount * 1 });
} }
if (typeof importData.insurance == 'string') { 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 } 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._importDetailedArray([importData]); // Convert to array with singleobject
this.setState({ singleBuild: true }); this.setState({ singleBuild: true });
} else if (importData.Modules != null && importData.Modules[0] != null) {
this._importLoadout(importData);
} else { // Using Backup JSON } else { // Using Backup JSON
this._importBackup(importData); this._importBackup(importData);
} }
} }
} catch (e) { } catch (e) {
// console.log(e.stack); console.log(e);
this.setState({ errorMsg: (typeof e == 'string') ? e : 'Cannot Parse the data!' }); this.setState({ errorMsg: (typeof e == 'string') ? e : 'Cannot Parse the data!' });
return; return;
} }
@@ -364,6 +385,10 @@ export default class ModalImport extends TranslatedComponent {
_process() { _process() {
let builds = null, comparisons = null; 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 only importing a single build go straight to the outfitting page
if (this.state.singleBuild) { if (this.state.singleBuild) {
builds = this.state.builds; builds = this.state.builds;
@@ -480,7 +505,7 @@ export default class ModalImport extends TranslatedComponent {
if (!state.processed) { if (!state.processed) {
importStage = ( importStage = (
<div> <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> <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 className='l warning' style={{ marginLeft:'3em' }}>{state.errorMsg}</div>
</div> </div>

View File

@@ -109,17 +109,18 @@ export default class ModalShoppingList extends TranslatedComponent {
*/ */
sendToEDEng(event) { sendToEDEng(event) {
event.preventDefault(); event.preventDefault();
let translate = this.context.language.translate;
const target = event.target; const target = event.target;
target.disabled = this.state.blueprints.length > 0; target.disabled = this.state.blueprints.length > 0;
if (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; target.disabled = true;
setTimeout(() => { setTimeout(() => {
target.innerText = 'Send to EDEngineer'; target.innerText = translate('Send to EDEngineer');
target.disabled = false; target.disabled = false;
}, 3000); }, 3000);
} else { } else {
target.innerText = 'Sending...'; target.innerText = translate('Sending...');
} }
let countSent = 0; let countSent = 0;
let countTotal = this.state.blueprints.length; let countTotal = this.state.blueprints.length;
@@ -139,7 +140,7 @@ export default class ModalShoppingList extends TranslatedComponent {
countSent++; countSent++;
if (countSent === countTotal) { if (countSent === countTotal) {
target.disabled = false; 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); this.sendToEDEng = this.sendToEDEng.bind(this);
return <div className='modal' onClick={ (e) => e.stopPropagation() }> return <div className='modal' onClick={ (e) => e.stopPropagation() }>
<h2>{translate('PHRASE_SHOPPING_MATS')}</h2> <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} /> <input id={1} type={'number'} min={0} defaultValue={this.state.matsPerGrade[1]} onChange={this.changeHandler} />
<br/> <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} /> <input id={2} type={'number'} min={0} defaultValue={this.state.matsPerGrade[2]} onChange={this.changeHandler} />
<br/> <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} /> <input id={3} type={'number'} min={0} value={this.state.matsPerGrade[3]} onChange={this.changeHandler} />
<br/> <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} /> <input id={4} type={'number'} min={0} value={this.state.matsPerGrade[4]} onChange={this.changeHandler} />
<br/> <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} /> <input id={5} type={'number'} min={0} value={this.state.matsPerGrade[5]} onChange={this.changeHandler} />
<div> <div>
<textarea className='cb json' readOnly value={this.state.matsList} /> <textarea className='cb json' readOnly value={this.state.matsList} />
</div> </div>
<label hidden={!compatible} className={'l cap'}>CMDR Name </label> <label hidden={!compatible} className={'l cap'}>{translate('CMDR Name')}</label>
<br/> <br/>
<select hidden={!compatible} className={'cmdr-select l cap'} onChange={this.cmdrChangeHandler} defaultValue={this.state.cmdrName}> <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>)} {this.state.cmdrs.map(e => <option key={e}>{e}</option>)}
</select> </select>
<br/> <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={!this.state.failed} id={'failed'} className={'l'}>{translate('PHRASE_FAIL_EDENGINEER')}</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> <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={'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> <button className={'r dismiss cap'} onClick={this.context.hideModal}>{translate('close')}</button>
</div>; </div>;
} }

View File

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

View File

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

View File

@@ -50,6 +50,7 @@ export default class ShipSummaryTable extends TranslatedComponent {
const speedTooltip = canThrust ? 'TT_SUMMARY_SPEED' : 'TT_SUMMARY_SPEED_NONFUNCTIONAL'; const speedTooltip = canThrust ? 'TT_SUMMARY_SPEED' : 'TT_SUMMARY_SPEED_NONFUNCTIONAL';
const canBoost = ship.canBoost(cargo, ship.fuelCapacity); const canBoost = ship.canBoost(cargo, ship.fuelCapacity);
const boostTooltip = canBoost ? 'TT_SUMMARY_BOOST' : canThrust ? 'TT_SUMMARY_BOOST_NONFUNCTIONAL' : 'TT_SUMMARY_SPEED_NONFUNCTIONAL'; const boostTooltip = canBoost ? 'TT_SUMMARY_BOOST' : canThrust ? 'TT_SUMMARY_BOOST_NONFUNCTIONAL' : 'TT_SUMMARY_SPEED_NONFUNCTIONAL';
const canJump = ship.getSlotStatus(ship.standard[2]) == 3;
const sgMetrics = Calc.shieldMetrics(ship, pips.sys); const sgMetrics = Calc.shieldMetrics(ship, pips.sys);
const shipBoost = canBoost ? Calc.calcBoost(ship) : 'No Boost'; const shipBoost = canBoost ? Calc.calcBoost(ship) : 'No Boost';
const restingHeat = Math.sqrt(((ship.standard[0].m.pgen * ship.standard[0].m.eff) / ship.heatCapacity) / 0.2); const 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'> <tr className='main'>
<th rowSpan={2} className={ cn({ 'bg-warning-disabled': !canThrust }) }>{translate('speed')}</th> <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 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('shield')}</th>
<th rowSpan={2}>{translate('integrity')}</th> <th rowSpan={2}>{translate('integrity')}</th>
<th rowSpan={2}>{translate('DPS')}</th> <th rowSpan={2}>{translate('DPS')}</th>
@@ -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 onMouseEnter={termtip.bind(null, 'hull hardness', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('hrd')}</th>
<th rowSpan={2}>{translate('crew')}</th> <th rowSpan={2}>{translate('crew')}</th>
<th onMouseEnter={termtip.bind(null, 'mass lock factor', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('MLF')}</th> <th onMouseEnter={termtip.bind(null, 'mass lock factor', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('MLF')}</th>
<th onMouseEnter={termtip.bind(null, 'TT_SUMMARY_BOOST_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> <th rowSpan={2}>{translate('resting heat (Beta)')}</th>
</tr> </tr>
<tr> <tr>
<th className='lft'>{translate('max')}</th> <th className={ cn({ 'lft': true, 'bg-warning-disabled': !canJump }) }>{translate('max')}</th>
<th>{translate('unladen')}</th> <th className={ cn({ 'bg-warning-disabled': !canJump }) }>{translate('unladen')}</th>
<th>{translate('laden')}</th> <th className={ cn({ 'bg-warning-disabled': !canJump }) }>{translate('laden')}</th>
<th>{translate('total unladen')}</th> <th className={ cn({ 'bg-warning-disabled': !canJump }) }>{translate('total unladen')}</th>
<th>{translate('total laden')}</th> <th className={ cn({ 'bg-warning-disabled': !canJump }) }>{translate('total laden')}</th>
<th className='lft'>{translate('hull')}</th> <th className='lft'>{translate('hull')}</th>
<th>{translate('unladen')}</th> <th>{translate('unladen')}</th>
<th>{translate('laden')}</th> <th>{translate('laden')}</th>
@@ -103,11 +104,11 @@ export default class ShipSummaryTable extends TranslatedComponent {
<tr> <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, 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 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 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><span onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{f2(Calc.jumpRange(ship.unladenMass + ship.fuelCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</span></td> <td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_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><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_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}>{f2(Calc.totalJumpRange(ship.unladenMass + ship.fuelCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</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}>{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_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 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_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> <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>{formats.pct1(ship.shieldThermRes)}</td>
<td></td> <td></td>
<td>{int(ship && ship.shield > 0 ? ship.shield : 0)}{u.MJ}</td> <td>{int(ship && sgMetrics.summary > 0 ? sgMetrics.summary : 0)}{u.MJ}</td>
<td>{int(ship && ship.shield > 0 ? ship.shield * ((1 / (1 - (ship.shieldExplRes)))) : 0)}{u.MJ}</td> <td>{int(ship && sgMetrics.summary > 0 ? sgMetrics.summary / sgMetrics.explosive.base : 0)}{u.MJ}</td>
<td>{int(ship && ship.shield > 0 ? ship.shield * ((1 / (1 - (ship.shieldKinRes)))) : 0)}{u.MJ}</td> <td>{int(ship && sgMetrics.summary ? sgMetrics.summary / sgMetrics.kinetic.base : 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 ? sgMetrics.summary / sgMetrics.thermal.base : 0)}{u.MJ}</td>
<td></td> <td></td>
<td>{sgMetrics && sgMetrics.recover === Math.Inf ? translate('Never') : formats.time(sgMetrics.recover)}</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> <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.hullKinRes)}</td>
<td>{formats.pct1(ship.hullThermRes)}</td> <td>{formats.pct1(ship.hullThermRes)}</td>
<td>{formats.pct1(ship.hullCausRes)}</td> <td>{formats.pct1(ship.hullCausRes)}</td>
<td>{int(ship.armour)}</td> <td>{int(armourMetrics.total)}</td>
<td>{int(ship.armour * ((1 / (1 - (ship.hullExplRes)))))}</td> <td>{int(armourMetrics.total / armourMetrics.explosive.total)}</td>
<td>{int(ship.armour * ((1 / (1 - (ship.hullKinRes)))))}</td> <td>{int(armourMetrics.total/ armourMetrics.kinetic.total)}</td>
<td>{int(ship.armour * ((1 / (1 - (ship.hullThermRes)))))}</td> <td>{int(armourMetrics.total / armourMetrics.thermal.total)}</td>
<td>{int(ship.armour * ((1 / (1 - (ship.hullCausRes)))))}</td> <td>{int(armourMetrics.total/ armourMetrics.caustic.total)}</td>
<td>{int(armourMetrics.modulearmour)}</td> <td>{int(armourMetrics.modulearmour)}</td>
<td>{int(armourMetrics.moduleprotection * 100) + '%'}</td> <td>{int(armourMetrics.moduleprotection * 100) + '%'}</td>

View File

@@ -127,8 +127,8 @@ export default class Slot extends TranslatedComponent {
menu = <AvailableModulesMenu menu = <AvailableModulesMenu
className={this._getClassNames()} className={this._getClassNames()}
modules={availableModules()} modules={availableModules()}
shipMass={ship.hullMass}
m={m} m={m}
ship={ship}
onSelect={onSelect} onSelect={onSelect}
warning={warning} warning={warning}
diffDetails={diffDetails.bind(ship, this.context.language)} 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)) { if (targetSlot && canMount(this.props.ship, targetSlot, m.grp, m.class)) {
const mCopy = m.clone(); const mCopy = m.clone();
this.props.ship.use(targetSlot, mCopy, false); 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 // Copy power info
targetSlot.enabled = originSlot.enabled; targetSlot.enabled = originSlot.enabled;
targetSlot.priority = originSlot.priority; targetSlot.priority = originSlot.priority;

View File

@@ -109,8 +109,8 @@ export default class StandardSlot extends TranslatedComponent {
menu = <AvailableModulesMenu menu = <AvailableModulesMenu
className='standard' className='standard'
modules={modules} modules={modules}
shipMass={ModuleUtils.isShieldGenerator(m.grp) ? ship.hullMass : ship.unladenMass}
m={m} m={m}
ship={ship}
onSelect={onSelect} onSelect={onSelect}
warning={warning} warning={warning}
diffDetails={diffDetails.bind(ship, this.context.language)} 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 RU from './ru';
import * as PL from './pl'; import * as PL from './pl';
import * as PT from './pt'; import * as PT from './pt';
import * as CN from './cn';
import * as d3 from 'd3'; import * as d3 from 'd3';
let fallbackTerms = EN.terms; let fallbackTerms = EN.terms;
@@ -27,6 +28,7 @@ export function getLanguage(langCode) {
case 'ru': lang = RU; break; case 'ru': lang = RU; break;
case 'pl': lang = PL; break; case 'pl': lang = PL; break;
case 'pt': lang = PT; break; case 'pt': lang = PT; break;
case 'cn': lang = CN; break;
default: default:
lang = EN; lang = EN;
} }
@@ -94,5 +96,6 @@ export const Languages = {
fr: 'Français', fr: 'Français',
ru: 'ру́сский', ru: 'ру́сский',
pl: 'polski', 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": "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_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": "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_BOOST_NONFUNCTIONAL": "Power distributor not able to supply enough power to boost",
"TT_SUMMARY_SHIELDS": "Raw shield strength, including boosters", "TT_SUMMARY_SHIELDS": "Raw shield strength, including boosters",
"TT_SUMMARY_SHIELDS_SCB": "Raw shield strength, including boosters and SCBs", "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_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", "TT_SUMMARY_LADEN_TOTAL_JUMP": "Farthest possible range with full cargo, a full fuel tank, and jumping as far as possible each time",
"HELP_MODIFICATIONS_MENU": "Click on a number to enter a new value, or drag along the bar for small changes", "HELP_MODIFICATIONS_MENU": "Click on a number to enter a new value, or drag along the bar for small changes",
"PHRASE_FAIL_EDENGINEER": "Failed to send to EDEngineer (Launch EDEngineer and make sure the API is started then refresh the page.)",
"PHRASE_FIREFOX_EDENGINEER": "Sending to EDEngineer is not compatible with Firefox's security settings. Please try again with Chrome.",
"am": "Auto Field-Maintenance Unit", "am": "Auto Field-Maintenance Unit",
"bh": "Bulkheads", "bh": "Bulkheads",
"bl": "Beam Laser", "bl": "Beam Laser",
@@ -132,6 +134,10 @@
"gfsb": "Guardian Frame Shift Drive Booster", "gfsb": "Guardian Frame Shift Drive Booster",
"ghrp": "Guardian Hull Reinforcement Package", "ghrp": "Guardian Hull Reinforcement Package",
"gmrp": "Guardian Module 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", "tbsc": "Shock Cannon",
"gsc": "Guardian Shard Cannon", "gsc": "Guardian Shard Cannon",
"psg": "Prismatic Shield Generator", "psg": "Prismatic Shield Generator",
@@ -147,6 +153,7 @@
"sfn": "Shutdown Field Neutraliser", "sfn": "Shutdown Field Neutraliser",
"sg": "Shield Generator", "sg": "Shield Generator",
"ss": "Surface Scanners", "ss": "Surface Scanners",
"sua": "Supercruise Assist",
"t": "thrusters", "t": "thrusters",
"tp": "Torpedo Pylon", "tp": "Torpedo Pylon",
"ul": "Burst Laser", "ul": "Burst Laser",
@@ -168,6 +175,7 @@
"ammunition": "Ammo", "ammunition": "Ammo",
"secs": "s", "secs": "s",
"rebuildsperbay": "Rebuilds per bay", "rebuildsperbay": "Rebuilds per bay",
"mroll": "Roll",
"worst": "Worst", "worst": "Worst",
"average": "Average", "average": "Average",
"random": "Random", "random": "Random",
@@ -197,7 +205,7 @@
"internal protection": "Internal protection", "internal protection": "Internal protection",
"external protection": "External protection", "external protection": "External protection",
"engagement range": "Engagement range", "engagement range": "Engagement range",
"boost time": "Boost time", "boost interval": "Boost intervall",
"total": "Total", "total": "Total",
"ammo": "Ammunition maximum", "ammo": "Ammunition maximum",
"boot": "Boot time", "boot": "Boot time",
@@ -234,6 +242,7 @@
"rof": "Rate of fire", "rof": "Rate of fire",
"angle": "Scan angle", "angle": "Scan angle",
"scanrate": "Scan rate", "scanrate": "Scan rate",
"proberadius": "Probe Radius",
"scantime": "Scan time", "scantime": "Scan time",
"shield": "Shield", "shield": "Shield",
"armour": "Armour", "armour": "Armour",
@@ -315,6 +324,7 @@
"never": "never", "never": "never",
"stock": "stock", "stock": "stock",
"boost": "boost", "boost": "boost",
"tab_defence": "defence",
"federation rank 1": "Recruit", "federation rank 1": "Recruit",
"federation rank 2": "Cadet", "federation rank 2": "Cadet",
"federation rank 3": "Midshipman", "federation rank 3": "Midshipman",

View File

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

View File

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

View File

@@ -103,7 +103,7 @@ export default class AboutPage extends Page {
patreon.com/coriolis_elite patreon.com/coriolis_elite
</a> </a>
, which will be used to keep Coriolis up to date and the servers , 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> </p>
<form <form
action="https://www.paypal.com/cgi-bin/webscr" action="https://www.paypal.com/cgi-bin/webscr"

View File

@@ -224,9 +224,12 @@ export default class OutfittingPage extends Page {
const control = LZString.decompressFromBase64( const control = LZString.decompressFromBase64(
Utils.fromUrlSafe(parts[4]) Utils.fromUrlSafe(parts[4])
).split('/'); ).split('/');
sys = parseFloat(control[0]) || sys; sys = parseFloat(control[0]);
eng = parseFloat(control[1]) || eng; eng = parseFloat(control[1]);
wep = parseFloat(control[2]) || wep; wep = parseFloat(control[2]);
if (sys + eng + wep > 6) {
sys = eng = wep = 2;
}
boost = control[3] == 1 ? true : false; boost = control[3] == 1 ? true : false;
fuel = parseFloat(control[4]) || fuel; fuel = parseFloat(control[4]) || fuel;
cargo = parseInt(control[5]) || cargo; 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. * Update the window title upon mount
* 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
*/ */
shouldComponentUpdate(np, ns, nc) { componentWillMount() {
return !shallowEqual(this.props, np) || !shallowEqual(this.state, ns) || !shallowEqual(this.context, nc); document.title = this.state.title || 'Coriolis';
} }
/** /**
* Update the window title upon mount * Update the window title upon mount
*/ */
componentWillMount() { componentDidMount() {
document.title = this.state.title || 'Coriolis'; document.title = this.state.title || 'Coriolis';
} }

View File

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

View File

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

View File

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

View File

@@ -46,7 +46,7 @@ export default class Module {
if ((!raw) && this.blueprint && this.blueprint.special) { if ((!raw) && this.blueprint && this.blueprint.special) {
// This module has a special effect, see if we need to alter our returned value // 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]) { if (modifierActions && modifierActions[name]) {
// this special effect modifies our returned value // this special effect modifies our returned value
const modification = Modifications.modifications[name]; const modification = Modifications.modifications[name];
@@ -59,14 +59,7 @@ export default class Module {
} else if (modification.method === 'overwrite') { } else if (modification.method === 'overwrite') {
result = modifierActions[name]; result = modifierActions[name];
} else { } else {
// rate of fire is special, as it's really burst interval. Handle that here result = (((1 + result / multiplier) * (1 + modifierActions[name])) - 1) * multiplier;
let mod = null;
if (name === 'rof') {
mod = 1 / (1 + modifierActions[name]) - 1;
} else {
mod = modifierActions[name];
}
result = (((1 + result / multiplier) * (1 + mod)) - 1) * multiplier;
} }
} }
} }
@@ -90,7 +83,7 @@ export default class Module {
} }
if (valueiswithspecial && this.blueprint && this.blueprint.special) { if (valueiswithspecial && this.blueprint && this.blueprint.special) {
// This module has a special effect, see if we need to alter the stored value // 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]) { if (modifierActions && modifierActions[name]) {
// This special effect modifies the value being set, so we need to revert it prior to storing the value // This special effect modifies the value being set, so we need to revert it prior to storing the value
const modification = Modifications.modifications[name]; const modification = Modifications.modifications[name];
@@ -105,14 +98,7 @@ export default class Module {
} else if (modification.method === 'overwrite') { } else if (modification.method === 'overwrite') {
value = null; value = null;
} else { } else {
// rate of fire is special, as it's really burst interval. Handle that here value = ((value / 10000 + 1) / (1 + modifierActions[name]) - 1) * 10000;
let mod = null;
if (name === 'rof') {
mod = 1 / (1 + modifierActions[name]) - 1;
} else {
mod = modifierActions[name];
}
value = ((value / 10000 + 1) / (1 + mod) - 1) * 10000;
} }
} }
} }
@@ -131,6 +117,13 @@ export default class Module {
* @return {Number} The value queried * @return {Number} The value queried
*/ */
get(name, modified = true) { get(name, modified = true) {
if (name == 'rof' && isNaN(this[name])) {
let fireint = this['fireint'];
if (!isNaN(fireint)) {
this['rof'] = 1 / fireint;
}
}
let val; let val;
if (modified) { if (modified) {
val = this._getModifiedValue(name); val = this._getModifiedValue(name);
@@ -166,14 +159,20 @@ export default class Module {
modValue = value - baseValue; modValue = value - baseValue;
} else if (name === 'shieldboost' || name === 'hullboost') { } else if (name === 'shieldboost' || name === 'hullboost') {
modValue = (1 + value) / (1 + baseValue) - 1; 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 } else { // multiplicative
modValue = value / baseValue - 1; modValue = baseValue == 0 ? 0 : value / baseValue - 1;
} }
if (modification.type === 'percentage') { if (modification.type === 'percentage') {
modValue = modValue * 10000; modValue = modValue * 10000;
} else if (modification.type === 'numeric' && name !== 'burst' && } else if (modification.type === 'numeric') {
name !== 'burstrof') {
modValue = modValue * 100; modValue = modValue * 100;
} }
@@ -191,7 +190,14 @@ export default class Module {
*/ */
getPretty(name, modified = true, places = 2) { getPretty(name, modified = true, places = 2) {
const formattingOptions = STATS_FORMATTING[name]; 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')) { if (formattingOptions && formattingOptions.format.startsWith('pct')) {
return 100 * val; return 100 * val;
} }
@@ -250,12 +256,17 @@ export default class Module {
} else if (name === 'shieldboost' || name === 'hullboost') { } else if (name === 'shieldboost' || name === 'hullboost') {
result = (1 + result) * (1 + modValue) - 1; result = (1 + result) * (1 + modValue) - 1;
} else { } 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); 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 // Burst and burst rate of fire are special, as it can not exist but
// have a modification // have a modification
result = modValue / 100; result = modValue;
} }
} }
} }
@@ -277,11 +288,7 @@ export default class Module {
formatModifiedValue(name, language, unit, val) { formatModifiedValue(name, language, unit, val) {
const formattingOptions = STATS_FORMATTING[name]; const formattingOptions = STATS_FORMATTING[name];
if (val === undefined) { if (val === undefined) {
if (formattingOptions && formattingOptions.synthetic) { val = this.getPretty(name, true);
val = (this[formattingOptions.synthetic]).call(this, true);
} else {
val = this._getModifiedValue(name);
}
} }
val = val || 0; val = val || 0;
@@ -351,15 +358,19 @@ export default class Module {
if (formattingOptions && formattingOptions.change) { if (formattingOptions && formattingOptions.change) {
let changeFormatting = formattingOptions.change; let changeFormatting = formattingOptions.change;
let baseVal = this[name]; let baseVal = this[name] || 0;
let absVal = this._getModifiedValue(name); let absVal = this._getModifiedValue(name);
if (changeFormatting === 'additive') { if (changeFormatting === 'additive') {
val = absVal - baseVal; val = absVal - baseVal;
} else if (changeFormatting === 'multiplicative') { } else if (changeFormatting === 'multiplicative') {
val = absVal / baseVal - 1; val = absVal / baseVal - 1;
} }
if (Modifications.modifications[name].method === 'overwrite') {
val *= 100;
} else {
val *= 10000; val *= 10000;
} }
}
return val; return val;
} }
@@ -572,20 +583,9 @@ export default class Module {
* @return {Number} the falloff of this module * @return {Number} the falloff of this module
*/ */
getFalloff(modified = true) { 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 // Falloff from range is mapped to range
if (this.mods && this.mods['fallofffromrange']) { if (modified && this.mods && this.mods['fallofffromrange']) {
return this.getRange(); 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 // Standard falloff calculation
} else { } else {
const range = this.getRange(); const range = this.getRange();
@@ -639,15 +639,6 @@ export default class Module {
return this.get('protection', modified); 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 * Get the duration for this module
* @param {Boolean} [modified=true] Whether to take modifications into account * @param {Boolean} [modified=true] Whether to take modifications into account
@@ -703,8 +694,7 @@ export default class Module {
let result = 0; let result = 0;
if (this['maxmass']) { if (this['maxmass']) {
result = this['maxmass']; result = this['maxmass'];
// max mass is only modified for non-shield boosters if (result && modified) {
if (result && modified && this.grp !== 'sg') {
let mult = this.getModValue('optmass') / 10000; let mult = this.getModValue('optmass') / 10000;
if (mult) { result = result * (1 + mult); } if (mult) { result = result * (1 + mult); }
} }
@@ -793,24 +783,6 @@ export default class Module {
return this.get('distdraw', modified); 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 * Get the DPS for this module
* @param {Boolean} [modified=true] Whether to take modifications into account * @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 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 * Get the SDPS for this module
* @param {Boolean} [modified=true] Whether to take modifications into account * @param {Boolean} [modified=true] Whether to take modifications into account
* @return {Number} The SDPS of this module * @return {Number} The SDPS of this module
*/ */
getSDps(modified = true) { getSDps(modified = true) {
let dps = this.getDps(modified); return this.getDps(modified) * this.getSustainedFactor(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;
}
} }
/** /**
@@ -876,7 +862,7 @@ export default class Module {
*/ */
getHps(modified = true) { getHps(modified = true) {
// HPS is a synthetic value // 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 // We don't use rpshot here as dist draw is per combined shot
let rof = this.getRoF(modified) || 1; let rof = this.getRoF(modified) || 1;
@@ -913,24 +899,6 @@ export default class Module {
return this.get('reload', modified); 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. * Get the rate of fire for this module.
* The rate of fire is a combination value, and needs to take in to account * 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 * @return {Number} the rate of fire for this module
*/ */
getRoF(modified = true) { getRoF(modified = true) {
const burst = this.getBurst(modified) || 1; const burst = this.get('burst', modified) || 1;
const burstRoF = this.getBurstRoF(modified) || 1; const burstRoF = this.get('burstrof', modified) || 1;
const intRoF = this.get('rof', modified); const intRoF = this.get('rof', modified);
return burst / (((burst - 1) / burstRoF) + 1 / intRoF); return burst / (((burst - 1) / burstRoF) + 1 / intRoF);
@@ -1073,4 +1041,31 @@ export default class Module {
getHackTime(modified = true) { getHackTime(modified = true) {
return this.get('hacktime', modified); 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(); code = ship.toString();
let data = { let data = {
$schema: 'https://coriolis.edcd.io/schemas/ship-loadout/4.json#', $schema: 'https://coriolis.io/schemas/ship-loadout/4.json#',
name: buildName, name: buildName,
ship: ship.name, ship: ship.name,
references: [{ references: [{
name: 'Coriolis.io', name: 'Coriolis.io',
url: 'https://coriolis.edcd.io' + outfitURL(ship.id, code, buildName), url: 'https://coriolis.io' + outfitURL(ship.id, code, buildName),
code, code,
shipId: ship.id shipId: ship.id
}], }],

View File

@@ -10,7 +10,7 @@ import { Ships, Modifications } from 'coriolis-data/dist';
import { chain } from 'lodash'; import { chain } from 'lodash';
const zlib = require('zlib'); 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 // Constants for modifications struct
const SLOT_ID_DONE = -1; const SLOT_ID_DONE = -1;
@@ -1308,7 +1308,7 @@ export default class Ship {
let fsd = this.standard[2].m; // Frame Shift Drive; let fsd = this.standard[2].m; // Frame Shift Drive;
let { unladenMass, fuelCapacity } = this; let { unladenMass, fuelCapacity } = this;
this.unladenRange = this.calcUnladenRange(); // Includes fuel weight for jump this.unladenRange = this.calcUnladenRange(); // Includes fuel weight for jump
this.fullTankRange = Calc.jumpRange(unladenMass + fuelCapacity, fsd, this); // Full Tank this.fullTankRange = Calc.jumpRange(unladenMass + fuelCapacity, fsd, fuelCapacity, this); // Full Tank
this.ladenRange = this.calcLadenRange(); // Includes full tank and caro this.ladenRange = this.calcLadenRange(); // Includes full tank and caro
this.unladenFastestRange = Calc.totalJumpRange(unladenMass + this.fuelCapacity, fsd, fuelCapacity, this); this.unladenFastestRange = Calc.totalJumpRange(unladenMass + this.fuelCapacity, fsd, fuelCapacity, this);
this.ladenFastestRange = Calc.totalJumpRange(unladenMass + this.fuelCapacity + this.cargoCapacity, 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', }, 'ammo': { 'format': 'int', },
'boot': { 'format': 'int', 'unit': 'secs' }, 'boot': { 'format': 'int', 'unit': 'secs' },
'brokenregen': { 'format': 'round1', 'unit': 'ps' }, 'brokenregen': { 'format': 'round1', 'unit': 'ps' },
'burst': { 'format': 'int' }, 'burst': { 'format': 'int', 'change': 'additive' },
'burstrof': { 'format': 'round1', 'unit': 'ps' }, 'burstrof': { 'format': 'round1', 'unit': 'ps', 'change': 'additive' },
'causres': { 'format': 'pct' }, 'causres': { 'format': 'pct' },
'clip': { 'format': 'int' }, 'clip': { 'format': 'int' },
'damage': { 'format': 'round' }, 'damage': { 'format': 'round' },
@@ -61,7 +61,7 @@ export const STATS_FORMATTING = {
'ranget': { 'format': 'f1', 'unit': 's' }, 'ranget': { 'format': 'f1', 'unit': 's' },
'regen': { 'format': 'round1', 'unit': 'ps' }, 'regen': { 'format': 'round1', 'unit': 'ps' },
'reload': { 'format': 'int', 'unit': 's' }, 'reload': { 'format': 'int', 'unit': 's' },
'rof': { 'format': 'round1', 'unit': 'ps' }, 'rof': { 'format': 'round1', 'unit': 'ps', 'synthetic': 'getRoF', 'higherbetter': true },
'angle': { 'format': 'round1', 'unit': 'ang' }, 'angle': { 'format': 'round1', 'unit': 'ang' },
'scanrate': { 'format': 'int' }, 'scanrate': { 'format': 'int' },
'scantime': { 'format': 'round1', 'unit': 's' }, 'scantime': { 'format': 'round1', 'unit': 's' },
@@ -78,5 +78,6 @@ export const STATS_FORMATTING = {
'thermres': { 'format': 'pct' }, 'thermres': { 'format': 'pct' },
'wepcap': { 'format': 'round1', 'unit': 'MJ' }, 'wepcap': { 'format': 'round1', 'unit': 'MJ' },
'weprate': { 'format': 'round1', 'unit': 'MW' }, '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 React from 'react';
import { Modifications } from 'coriolis-data/dist'; import { Modifications } from 'coriolis-data/dist';
import { STATS_FORMATTING } from '../shipyard/StatsFormatting';
/** /**
* Generate a tooltip with details of a blueprint's specials * 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 // We also add in any benefits from specials that aren't covered above
if (m.blueprint && m.blueprint.special) { 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) { if (!blueprint.features[feature] && !m.mods.feature) {
const featureDef = Modifications.modifications[feature]; const featureDef = Modifications.modifications[feature];
if (featureDef && !featureDef.hidden) { 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 * Get a blueprint with a given name and an optional module
* @param {string} name The name of the blueprint * @param {string} name The name of the blueprint
@@ -372,9 +392,7 @@ export function getPercent(m) {
let value = _getValue(m, featureName); let value = _getValue(m, featureName);
let mult; let mult;
if (featureName == 'shieldboost') { if (Modifications.modifications[featureName].higherbetter) {
mult = ((1 + value) * (1 + m.shieldboost)) - 1 - m.shieldboost;
} else if (Modifications.modifications[featureName].higherbetter) {
// Higher is better, but is this making it better or worse? // Higher is better, but is this making it better or worse?
if (features[featureName][0] < 0 || (features[featureName][0] === 0 && features[featureName][1] < 0)) { if (features[featureName][0] < 0 || (features[featureName][0] === 0 && features[featureName][1] < 0)) {
mult = Math.round((value - features[featureName][1]) / (features[featureName][0] - features[featureName][1]) * 100); 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; if (!modifiers) return;
let special; let special;
if (specialModifications) { 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]; special = Modifications.specials[specialModifications];
} }
// Add the blueprint definition, grade and special // Add the blueprint definition, grade and special
@@ -274,6 +281,9 @@ function _addModifications(module, modifiers, blueprint, grade, specialModificat
if (value === Infinity) { if (value === Infinity) {
value = modifiers[i].Value * 100; 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) { if (modifiers[i].Label.search('Resistance') >= 0) {
value = (modifiers[i].Value * 100) - (modifiers[i].OriginalValue * 100); value = (modifiers[i].Value * 100) - (modifiers[i].OriginalValue * 100);
} }

View File

@@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import Persist from '../stores/Persist'; import Persist from '../stores/Persist';
import * as ModuleUtils from '../shipyard/ModuleUtils'; 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 * 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) { export function diffDetails(language, m, mm) {
let { formats, translate, units } = language; let { formats, translate, units } = language;
let propDiffs = []; let propDiffs = [];
m = new Module(m);
// Module-specific items // Module-specific items
if (m.grp === 'pp') { if (m.grp === 'pp') {
let mPowerGeneration = m.pgen || 0; let mPowerGeneration = m.getPowerGeneration() || 0;
let mmPowerGeneration = mm ? mm.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 { } else {
let mPowerUsage = m.power || 0; let mPowerUsage = m.getPowerUsage() || 0;
let mmPowerUsage = mm ? mm.getPowerUsage() || 0 : 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; 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>); 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 (mAffectsShield) {
if (m.grp == 'sb') { // Both m and mm must be utility modules if this is true 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 { } else {
newShield = this.calcShieldStrengthWith(m); newShield = this.calcShieldStrengthWith(m);
} }
@@ -179,7 +181,7 @@ export function diffDetails(language, m, mm) {
} }
if (m.grp === 'mrp') { if (m.grp === 'mrp') {
let mProtection = m.protection; let mProtection = m.getProtection();
let mmProtection = mm ? mm.getProtection() || 0 : 0; let mmProtection = mm ? mm.getProtection() || 0 : 0;
if (mProtection != mmProtection) { if (mProtection != mmProtection) {
propDiffs.push(<div key='protection'>{translate('protection')}: <span className={diffClass(mmProtection, mProtection, true)}>{diff(formats.pct, mProtection, mmProtection)}</span></div>); 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') { if (m.grp === 'hr') {
let mHullReinforcement = m.hullreinforcement; let mHullReinforcement = m.getHullReinforcement();
let mmHullReinforcement = mm ? mm.getHullReinforcement() || 0 : 0; let mmHullReinforcement = mm ? mm.getHullReinforcement() || 0 : 0;
if (mHullReinforcement && mHullReinforcement != mmHullReinforcement) propDiffs.push(<div key='hullreinforcement'>{translate('hullreinforcement')}: <span className={diffClass(mmHullReinforcement, mHullReinforcement, true)}>{diff(formats.round, mHullReinforcement, mmHullReinforcement)}</span></div>); if (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; 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>); 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; 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>); 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; let mmIntegrity = mm ? mm.getIntegrity() || 0 : 0;
if (mIntegrity != mmIntegrity) { if (mIntegrity != mmIntegrity) {
propDiffs.push(<div key='integrity'>{translate('integrity')}: <span className={diffClass(mmIntegrity, mIntegrity, true)}>{diff(formats.round, mIntegrity, mmIntegrity)}</span></div>); propDiffs.push(<div key='integrity'>{translate('integrity')}: <span className={diffClass(mmIntegrity, mIntegrity, true)}>{diff(formats.round, mIntegrity, mmIntegrity)}</span></div>);

View File

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

View File

@@ -2,6 +2,8 @@
@bgDarken: 40%; @bgDarken: 40%;
@disabledDarken: 15%; @disabledDarken: 15%;
@bgTransparency: 10%; @bgTransparency: 10%;
@bgHighlight: 5%;
@fgHighlight: 10%;
// Foreground colors // Foreground colors
@fg: #CCC; @fg: #CCC;
@@ -21,9 +23,14 @@
@bgBlack: #000; @bgBlack: #000;
@primary-bg: fadeout(darken(@primary, 47%), 15%); @primary-bg: fadeout(darken(@primary, 47%), 15%);
@alt-primary-bg: fadeout(darken(@primary, 42%), 15%); // Lighter brown background @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 @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 { .fg {
color: @fg; color: @fg;

View File

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

View File

@@ -50,3 +50,44 @@ a.ship {
float: right; 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 { .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; background-color: @alt-primary-bg;
} }
} }
@@ -84,3 +84,5 @@ td {
text-align: center; text-align: center;
} }
} }

View File

@@ -1,6 +1,6 @@
{ {
"$schema": "http://json-schema.org/draft-04/schema#", "$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", "title": "Ship Loadout",
"type": "object", "type": "object",
"description": "The details for a specific ship build/loadout. DEPRECATED in favor of Version 3", "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#", "$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", "title": "Ship Loadout",
"type": "object", "type": "object",
"description": "The details for a specific ship build/loadout. DEPRECATED in favor of Version 3", "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#", "$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", "title": "Ship Loadout",
"type": "object", "type": "object",
"description": "The details for a specific ship build/loadout", "description": "The details for a specific ship build/loadout",

View File

@@ -1,6 +1,6 @@
{ {
"$schema": "http://json-schema.org/draft-04/schema#", "$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", "title": "Ship Loadout",
"type": "object", "type": "object",
"description": "The details for a specific ship build/loadout", "description": "The details for a specific ship build/loadout",

View File

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

View File

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

View File

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