Compare commits

..

6 Commits

Author SHA1 Message Date
felixlinker
59b6945fee Allow testmode for ReactGA 2018-10-21 23:43:56 +01:00
felixlinker
4613e7ca7a Replace local storage variables with class in tests 2018-10-21 23:42:27 +01:00
felixlinker
3315570edf eslint fixes 2018-10-21 23:42:27 +01:00
felixlinker
1fd545ba1f Add new keys to test fixtures 2018-10-21 23:42:27 +01:00
felixlinker
25839de1fe Loop ships outside of test for meaningfull error messages 2018-10-21 23:42:27 +01:00
felixlinker
15feff9344 Don't test or describe fullTankRange because it is not used 2018-10-21 23:42:27 +01:00
57 changed files with 1258 additions and 9093 deletions

2
.docker/.dockerignore Normal file
View File

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

View File

@@ -6,28 +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
RUN apk add --update git COPY ./coriolis/ /src/app/coriolis
COPY ./coriolis-data/ /src/app/coriolis-data
COPY . /src/app/coriolis RUN apk update
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 clone https://github.com/EDCD/coriolis-data.git . RUN git fetch --all
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 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 fholzer/nginx-brotli as web FROM nginx:1.13.12-alpine as web
COPY nginx.conf /etc/nginx/nginx.conf COPY coriolis/.docker/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

View File

@@ -29,23 +29,6 @@ services:
- "traefik.basic.port=80" - "traefik.basic.port=80"
- "traefik.basic.protocol=http" - "traefik.basic.protocol=http"
coriolis_dw2:
image: edcd/coriolis:dw2
restart: always
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
networks:
- web
labels:
- "traefik.docker.network=web"
- "traefik.enable=true"
- "traefik.basic.frontend.rule=Host:dw2.coriolis.io"
- "traefik.basic.port=80"
- "traefik.basic.protocol=http"
networks: networks:
web: web:
external: true external: true

45
.docker/nginx.conf Normal file
View File

@@ -0,0 +1,45 @@
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;
}
}
}

View File

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

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.io", "site": "https://coriolis.edcd.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.io", "site": "https://coriolis.edcd.io",
"author": "https://github.com/edcd", "author": "https://github.com/edcd",
"image": "./src/images/logo/192x192.png" "image": "./src/images/logo/192x192.png"
} }
@@ -100,4 +100,4 @@
} }
} }
] ]
} }

View File

@@ -275,11 +275,11 @@
"totalCost": 882362060, "totalCost": 882362060,
"unladenMass": 1179.2, "unladenMass": 1179.2,
"totalDps": 29, "totalDps": 29,
"passengerCapacity": 0,
"powerAvailable": 36, "powerAvailable": 36,
"powerRetracted": 23.33, "powerRetracted": 23.33,
"powerDeployed": 34.76, "powerDeployed": 34.76,
"unladenRange": 18.49, "unladenRange": 18.49,
"fullTankRange": 18.12,
"ladenRange": 16.39, "ladenRange": 16.39,
"unladenFastestRange": 73.21, "unladenFastestRange": 73.21,
"ladenFastestRange": 66.15, "ladenFastestRange": 66.15,

View File

@@ -287,6 +287,7 @@
"totalThermSDps": 58.64, "totalThermSDps": 58.64,
"baseShieldStrength": 350, "baseShieldStrength": 350,
"baseArmour": 945, "baseArmour": 945,
"hullCausRes": 0,
"hullExplRes": 0.22, "hullExplRes": 0.22,
"hullKinRes": 0.27, "hullKinRes": 0.27,
"hullMass": 400, "hullMass": 400,
@@ -303,13 +304,13 @@
"armour": 2227.5, "armour": 2227.5,
"baseArmour": 525, "baseArmour": 525,
"unladenMass": 1163.2, "unladenMass": 1163.2,
"passengerCapacity": 0,
"powerAvailable": 39.6, "powerAvailable": 39.6,
"powerRetracted": 23.33, "powerRetracted": 23.33,
"powerDeployed": 34.13, "powerDeployed": 34.13,
"roll": 60, "roll": 60,
"unladenRange": 18.74, "unladenRange": 18.74,
"yaw": 10, "yaw": 10,
"fullTankRange": 18.36,
"hardness": 65, "hardness": 65,
"ladenRange": 16.59, "ladenRange": 16.59,
"unladenFastestRange": 74.2, "unladenFastestRange": 74.2,

View File

@@ -237,14 +237,15 @@
"shieldExplRes": 0.5, "shieldExplRes": 0.5,
"shieldKinRes": 0.6, "shieldKinRes": 0.6,
"shieldThermRes": 1.2, "shieldThermRes": 1.2,
"hullCausRes": 0,
"hullExplRes": 1.4, "hullExplRes": 1.4,
"hullKinRes": 1.2, "hullKinRes": 1.2,
"hullThermRes": 1, "hullThermRes": 1,
"passengerCapacity": 0,
"powerAvailable": 20.41, "powerAvailable": 20.41,
"powerRetracted": 11.91, "powerRetracted": 11.91,
"powerDeployed": 11.91, "powerDeployed": 11.91,
"unladenRange": 50.45, "unladenRange": 50.45,
"fullTankRange": 47.03,
"ladenRange": 42.71, "ladenRange": 42.71,
"unladenFastestRange": 317.24, "unladenFastestRange": 317.24,
"ladenFastestRange": 287.02, "ladenFastestRange": 287.02,

View File

@@ -309,14 +309,15 @@
"shieldExplRes": 0.48, "shieldExplRes": 0.48,
"shieldKinRes": 0.55, "shieldKinRes": 0.55,
"shieldThermRes": 1.09, "shieldThermRes": 1.09,
"hullCausRes": 0,
"hullExplRes": 1.4, "hullExplRes": 1.4,
"hullKinRes": 1.2, "hullKinRes": 1.2,
"hullThermRes": 1, "hullThermRes": 1,
"passengerCapacity": 0,
"powerAvailable": 17.24, "powerAvailable": 17.24,
"powerRetracted": 11.3, "powerRetracted": 11.3,
"powerDeployed": 16.41, "powerDeployed": 16.41,
"unladenRange": 25.57, "unladenRange": 25.57,
"fullTankRange": 23.92,
"ladenRange": 22.09, "ladenRange": 22.09,
"unladenFastestRange": 116.23, "unladenFastestRange": 116.23,
"ladenFastestRange": 107, "ladenFastestRange": 107,

View File

@@ -115,7 +115,6 @@
"powerRetracted": 7.21, "powerRetracted": 7.21,
"powerDeployed": 7.21, "powerDeployed": 7.21,
"unladenRange": 32.48, "unladenRange": 32.48,
"fullTankRange": 30.27,
"ladenRange": 19.61, "ladenRange": 19.61,
"unladenFastestRange": 176.71, "unladenFastestRange": 176.71,
"ladenFastestRange": 112.92, "ladenFastestRange": 112.92,
@@ -249,7 +248,6 @@
"powerRetracted": 9.33, "powerRetracted": 9.33,
"powerDeployed": 10.33, "powerDeployed": 10.33,
"unladenRange": 30.24, "unladenRange": 30.24,
"fullTankRange": 28.32,
"ladenRange": 19.8, "ladenRange": 19.8,
"unladenFastestRange": 164.89, "unladenFastestRange": 164.89,
"ladenFastestRange": 114.03, "ladenFastestRange": 114.03,
@@ -379,7 +377,6 @@
"powerRetracted": 7.28, "powerRetracted": 7.28,
"powerDeployed": 8.06, "powerDeployed": 8.06,
"unladenRange": 31.71, "unladenRange": 31.71,
"fullTankRange": 29.61,
"ladenRange": 23.58, "ladenRange": 23.58,
"unladenFastestRange": 172.68, "unladenFastestRange": 172.68,
"ladenFastestRange": 136.46, "ladenFastestRange": 136.46,
@@ -511,7 +508,6 @@
"powerRetracted": 8.81, "powerRetracted": 8.81,
"powerDeployed": 8.81, "powerDeployed": 8.81,
"unladenRange": 26.41, "unladenRange": 26.41,
"fullTankRange": 24.97,
"ladenRange": 17.36, "ladenRange": 17.36,
"unladenFastestRange": 172.04, "unladenFastestRange": 172.04,
"ladenFastestRange": 118.55, "ladenFastestRange": 118.55,
@@ -653,7 +649,6 @@
"powerRetracted": 10.66, "powerRetracted": 10.66,
"powerDeployed": 11.66, "powerDeployed": 11.66,
"unladenRange": 25.77, "unladenRange": 25.77,
"fullTankRange": 24.39,
"ladenRange": 17.98, "ladenRange": 17.98,
"unladenFastestRange": 167.93, "unladenFastestRange": 167.93,
"ladenFastestRange": 122.84, "ladenFastestRange": 122.84,
@@ -791,7 +786,6 @@
"powerRetracted": 8.95, "powerRetracted": 8.95,
"powerDeployed": 9.73, "powerDeployed": 9.73,
"unladenRange": 19.27, "unladenRange": 19.27,
"fullTankRange": 18.95,
"ladenRange": 15.43, "ladenRange": 15.43,
"unladenFastestRange": 67.34, "unladenFastestRange": 67.34,
"ladenFastestRange": 54.75, "ladenFastestRange": 54.75,
@@ -954,7 +948,6 @@
"powerRetracted": 15.49, "powerRetracted": 15.49,
"powerDeployed": 19.43, "powerDeployed": 19.43,
"unladenRange": 27.1, "unladenRange": 27.1,
"fullTankRange": 25.58,
"ladenRange": 21.94, "ladenRange": 21.94,
"unladenFastestRange": 176.39, "unladenFastestRange": 176.39,
"ladenFastestRange": 150.58, "ladenFastestRange": 150.58,
@@ -1096,7 +1089,6 @@
"powerRetracted": 10.38, "powerRetracted": 10.38,
"powerDeployed": 12.58, "powerDeployed": 12.58,
"unladenRange": 26.01, "unladenRange": 26.01,
"fullTankRange": 25.42,
"ladenRange": 17.19, "ladenRange": 17.19,
"unladenFastestRange": 90.67, "unladenFastestRange": 90.67,
"ladenFastestRange": 61.04, "ladenFastestRange": 61.04,
@@ -1260,7 +1252,6 @@
"powerRetracted": 19.35, "powerRetracted": 19.35,
"powerDeployed": 25.31, "powerDeployed": 25.31,
"unladenRange": 15.19, "unladenRange": 15.19,
"fullTankRange": 14.99,
"ladenRange": 14.99, "ladenRange": 14.99,
"unladenFastestRange": 53.15, "unladenFastestRange": 53.15,
"ladenFastestRange": 53.15, "ladenFastestRange": 53.15,
@@ -1360,7 +1351,6 @@
"powerRetracted": 10.7, "powerRetracted": 10.7,
"powerDeployed": 10.7, "powerDeployed": 10.7,
"unladenRange": 24.25, "unladenRange": 24.25,
"fullTankRange": 23.73,
"ladenRange": 23.73, "ladenRange": 23.73,
"unladenFastestRange": 84.56, "unladenFastestRange": 84.56,
"ladenFastestRange": 84.56, "ladenFastestRange": 84.56,
@@ -1499,7 +1489,6 @@
"powerRetracted": 13.13, "powerRetracted": 13.13,
"powerDeployed": 13.13, "powerDeployed": 13.13,
"unladenRange": 19.51, "unladenRange": 19.51,
"fullTankRange": 18.58,
"ladenRange": 13.09, "ladenRange": 13.09,
"unladenFastestRange": 152.32, "unladenFastestRange": 152.32,
"ladenFastestRange": 106.49, "ladenFastestRange": 106.49,
@@ -1637,7 +1626,6 @@
"powerRetracted": 10.25, "powerRetracted": 10.25,
"powerDeployed": 10.25, "powerDeployed": 10.25,
"unladenRange": 28.25, "unladenRange": 28.25,
"fullTankRange": 26.6,
"ladenRange": 16.67, "ladenRange": 16.67,
"unladenFastestRange": 183.67, "unladenFastestRange": 183.67,
"ladenFastestRange": 113.69, "ladenFastestRange": 113.69,
@@ -1808,7 +1796,6 @@
"powerRetracted": 19.34, "powerRetracted": 19.34,
"powerDeployed": 26.18, "powerDeployed": 26.18,
"unladenRange": 20.32, "unladenRange": 20.32,
"fullTankRange": 19.46,
"ladenRange": 14.65, "ladenRange": 14.65,
"unladenFastestRange": 133.17, "unladenFastestRange": 133.17,
"ladenFastestRange": 99.65, "ladenFastestRange": 99.65,
@@ -1987,7 +1974,6 @@
"powerRetracted": 22.61, "powerRetracted": 22.61,
"powerDeployed": 29.63, "powerDeployed": 29.63,
"unladenRange": 14.32, "unladenRange": 14.32,
"fullTankRange": 13.89,
"ladenRange": 13.46, "ladenRange": 13.46,
"unladenFastestRange": 94.42, "unladenFastestRange": 94.42,
"ladenFastestRange": 91.49, "ladenFastestRange": 91.49,
@@ -2217,7 +2203,6 @@
"powerRetracted": 25.88, "powerRetracted": 25.88,
"powerDeployed": 37.51, "powerDeployed": 37.51,
"unladenRange": 16.99, "unladenRange": 16.99,
"fullTankRange": 16.68,
"ladenRange": 15.91, "ladenRange": 15.91,
"unladenFastestRange": 67.35, "unladenFastestRange": 67.35,
"ladenFastestRange": 64.2, "ladenFastestRange": 64.2,
@@ -2372,7 +2357,6 @@
"powerRetracted": 11.92, "powerRetracted": 11.92,
"powerDeployed": 11.92, "powerDeployed": 11.92,
"unladenRange": 39.26, "unladenRange": 39.26,
"fullTankRange": 37.65,
"ladenRange": 21.21, "ladenRange": 21.21,
"unladenFastestRange": 153.79, "unladenFastestRange": 153.79,
"ladenFastestRange": 85.82, "ladenFastestRange": 85.82,
@@ -2528,7 +2512,6 @@
"powerRetracted": 12.49, "powerRetracted": 12.49,
"powerDeployed": 12.49, "powerDeployed": 12.49,
"unladenRange": 38.44, "unladenRange": 38.44,
"fullTankRange": 36.89,
"ladenRange": 21.04, "ladenRange": 21.04,
"unladenFastestRange": 150.62, "unladenFastestRange": 150.62,
"ladenFastestRange": 85.16, "ladenFastestRange": 85.16,
@@ -2696,7 +2679,6 @@
"powerRetracted": 13, "powerRetracted": 13,
"powerDeployed": 12.4, "powerDeployed": 12.4,
"unladenRange": 38.04, "unladenRange": 38.04,
"fullTankRange": 30.11,
"ladenRange": 23.03, "ladenRange": 23.03,
"unladenFastestRange": 675.7, "unladenFastestRange": 675.7,
"ladenFastestRange": 501.97, "ladenFastestRange": 501.97,
@@ -2914,7 +2896,6 @@
"powerRetracted": 23.93, "powerRetracted": 23.93,
"powerDeployed": 35.56, "powerDeployed": 35.56,
"unladenRange": 18.49, "unladenRange": 18.49,
"fullTankRange": 18.12,
"ladenRange": 16.39, "ladenRange": 16.39,
"unladenFastestRange": 73.21, "unladenFastestRange": 73.21,
"ladenFastestRange": 66.15, "ladenFastestRange": 66.15,
@@ -3042,7 +3023,6 @@
"powerRetracted": 8.48, "powerRetracted": 8.48,
"powerDeployed": 7.88, "powerDeployed": 7.88,
"unladenRange": 35.99, "unladenRange": 35.99,
"fullTankRange": 33.36,
"ladenRange": 33.36, "ladenRange": 33.36,
"unladenFastestRange": 232.28, "unladenFastestRange": 232.28,
"ladenFastestRange": 232.28, "ladenFastestRange": 232.28,
@@ -3179,7 +3159,6 @@
"powerRetracted": 14.87, "powerRetracted": 14.87,
"powerDeployed": 17.51, "powerDeployed": 17.51,
"unladenRange": 15.06, "unladenRange": 15.06,
"fullTankRange": 14.86,
"ladenRange": 14.86, "ladenRange": 14.86,
"unladenFastestRange": 42.5, "unladenFastestRange": 42.5,
"ladenFastestRange": 42.5, "ladenFastestRange": 42.5,
@@ -3333,7 +3312,6 @@
"powerRetracted": 17.71, "powerRetracted": 17.71,
"powerDeployed": 23.14, "powerDeployed": 23.14,
"unladenRange": 12.51, "unladenRange": 12.51,
"fullTankRange": 12.38,
"ladenRange": 12.38, "ladenRange": 12.38,
"unladenFastestRange": 35.35, "unladenFastestRange": 35.35,
"ladenFastestRange": 35.35, "ladenFastestRange": 35.35,
@@ -3452,7 +3430,6 @@
"powerRetracted": 9.46, "powerRetracted": 9.46,
"powerDeployed": 11.17, "powerDeployed": 11.17,
"unladenRange": 17.12, "unladenRange": 17.12,
"fullTankRange": 16.71,
"ladenRange": 16.71, "ladenRange": 16.71,
"unladenFastestRange": 42.4, "unladenFastestRange": 42.4,
"ladenFastestRange": 42.4, "ladenFastestRange": 42.4,
@@ -3610,7 +3587,6 @@
"powerRetracted": 9.31, "powerRetracted": 9.31,
"powerDeployed": 13.91, "powerDeployed": 13.91,
"unladenRange": 8.43, "unladenRange": 8.43,
"fullTankRange": 8.09,
"ladenRange": 7.25, "ladenRange": 7.25,
"unladenFastestRange": 81.5, "unladenFastestRange": 81.5,
"ladenFastestRange": 72.9, "ladenFastestRange": 72.9,

View File

@@ -6,41 +6,70 @@ import TU from 'react-testutils-additions';
let origAddEventListener = window.addEventListener; let origAddEventListener = window.addEventListener;
let storageListener; let storageListener;
let ls = {};
// Implment mock localStorage /**
let localStorage = { * Mock local storage
getItem: function(key) { */
return ls[key]; class LocalStorage {
}, /**
setItem: function(key, value) { * Sets up storage variable
ls[key] = value; */
}, constructor() {
removeItem: function(key) { this.ls = {};
delete ls[key]; }
},
clear: function() { /**
ls = {}; * Retrieves a value from local storage
* @param {String} key Storage key
* @return {*} Storage value
*/
getItem(key) {
return this.ls[key];
}
/**
* Writes a value to local storage
* @param {String} key Storage key
* @param {*} value Storage value
*/
setItem(key, value) {
this.ls[key] = value;
}
/**
* Clears a value from local storage
* @param {String} key Storage key
*/
removeItem(key) {
delete this.ls[key];
}
/**
* Clears local storage
*/
clear() {
this.ls = {};
} }
} }
window.addEventListener = function(eventName, listener) { const LOCAL_STORAGE = new LocalStorage();
window.addEventListener = function(eventName, listener) {
if(eventName == 'storage') { if(eventName == 'storage') {
storageListener = listener; // Keep track of latest storage listener storageListener = listener; // Keep track of latest storage listener
} else { } else {
origAddEventListener.apply(arguments); origAddEventListener.apply(arguments);
} }
} };
describe('Persist', function() { describe('Persist', function() {
const Persist = require('../src/app/stores/Persist').Persist; const Persist = require('../src/app/stores/Persist').Persist;
describe('Multi tab/window', function() { describe('Multi tab/window', function() {
it("syncs builds", function() { it("syncs builds", function() {
window.localStorage = localStorage; window.localStorage = localStorage;
ls = {}; localStorage.clear();
let p = new Persist(); let p = new Persist();
let newBuilds = { let newBuilds = {
anaconda: { test: '1234' } anaconda: { test: '1234' }
@@ -54,7 +83,8 @@ describe('Persist', function() {
describe('General and Settings', function() { describe('General and Settings', function() {
it("has defaults", function() { it("has defaults", function() {
window.localStorage = localStorage; window.localStorage = localStorage;
ls = {}; localStorage.clear();
let p = new Persist(); let p = new Persist();
expect(p.getLangCode()).toBe('en'); expect(p.getLangCode()).toBe('en');
expect(p.showTooltips()).toBe(true); expect(p.showTooltips()).toBe(true);
@@ -65,14 +95,14 @@ describe('Persist', function() {
}); });
it("loads from localStorage correctly", function() { it("loads from localStorage correctly", function() {
window.localStorage = localStorage; window.localStorage = LOCAL_STORAGE;
let savedData = require('./fixtures/valid-backup'); let savedData = require('./fixtures/valid-backup');
ls = {}; LOCAL_STORAGE.clear();
ls.builds = JSON.stringify(savedData.builds); LOCAL_STORAGE.setItem('builds', JSON.stringify(savedData.builds));
ls.NG_TRANSLATE_LANG_KEY = 'de'; LOCAL_STORAGE.setItem('NG_TRANSLATE_LANG_KEY', 'de');
ls.insurance = 'Standard'; LOCAL_STORAGE.setItem('insurance', 'Standard');
ls.shipDiscount = 0.25; LOCAL_STORAGE.setItem('shipDiscount', 0.25);
ls.moduleDiscount = 0.15; LOCAL_STORAGE.setItem('moduleDiscount', 0.15);
let p = new Persist(); let p = new Persist();
expect(p.getInsurance()).toBe('standard'); expect(p.getInsurance()).toBe('standard');
@@ -86,13 +116,13 @@ describe('Persist', function() {
}); });
it("uses defaults from a corrupted localStorage", function() { it("uses defaults from a corrupted localStorage", function() {
window.localStorage = localStorage; window.localStorage = LOCAL_STORAGE;
ls = {}; LOCAL_STORAGE.clear();
ls.builds = "not valid json"; LOCAL_STORAGE.setItem('builds', 'not valid json');
ls.comparisons = "1, 3, 4"; LOCAL_STORAGE.setItem('comparisons', '1, 3, 4');
ls.insurance = 'this insurance does not exist'; LOCAL_STORAGE.setItem('insurance', 'this insurance does not exist');
ls.shipDiscount = 'this is not a number'; LOCAL_STORAGE.setItem('shipDiscount', 'this is not a number');
ls.moduleDiscount = 10; // Way to big LOCAL_STORAGE.setItem('moduleDiscount', 10); // Way to big
let p = new Persist(); let p = new Persist();
expect(p.getLangCode()).toBe('en'); expect(p.getLangCode()).toBe('en');
@@ -122,13 +152,13 @@ describe('Persist', function() {
}); });
it("generates the backup", function() { it("generates the backup", function() {
window.localStorage = localStorage; window.localStorage = LOCAL_STORAGE;
let savedData = require('./fixtures/valid-backup'); let savedData = require('./fixtures/valid-backup');
ls = {}; LOCAL_STORAGE.clear();
ls.builds = JSON.stringify(savedData.builds); LOCAL_STORAGE.setItem('builds', JSON.stringify(savedData.builds));
ls.insurance = 'Beta'; LOCAL_STORAGE.setItem('insurance', 'Beta');
ls.shipDiscount = 0.25; LOCAL_STORAGE.setItem('shipDiscount', 0.25);
ls.moduleDiscount = 0.15; LOCAL_STORAGE.setItem('moduleDiscount', 0.15);
let p = new Persist(); let p = new Persist();
let backup = p.getAll(); let backup = p.getAll();
@@ -140,4 +170,4 @@ describe('Persist', function() {
expect(backup.comparisons).toEqual({}); expect(backup.comparisons).toEqual({});
}); });
}); });
}) });

View File

@@ -3,32 +3,31 @@ import { Ships } from 'coriolis-data/dist';
import * as ModuleUtils from '../src/app/shipyard/ModuleUtils'; import * as ModuleUtils from '../src/app/shipyard/ModuleUtils';
describe("Ship", function() { describe("Ship", function() {
for (let s in Ships) {
it("can build all ships", function() { it("can build " + s, function() {
for (let s in Ships) {
let shipData = Ships[s]; let shipData = Ships[s];
let ship = new Ship(s, shipData.properties, shipData.slots); let ship = new Ship(s, shipData.properties, shipData.slots);
for (let p in shipData.properties) { for (let p in shipData.properties) {
expect(ship[p]).toEqual(shipData.properties[p], s + ' property [' + p + '] does not match when built'); expect(ship[p]).toEqual(shipData.properties[p]);
} }
ship.buildWith(shipData.defaults); ship.buildWith(shipData.defaults);
expect(ship.totalCost).toEqual(shipData.retailCost, s + ' retail cost does not match default build cost'); expect(ship.totalCost).toEqual(shipData.retailCost);
expect(ship.cargoCapacity).toBeDefined(); expect(ship.cargoCapacity).toBeDefined();
expect(ship.priorityBands[0].retracted).toBeGreaterThan(0, s + ' priorityBands'); expect(ship.priorityBands[0].retracted).toBeGreaterThan(0);
expect(ship.powerAvailable).toBeGreaterThan(0, s + ' powerAvailable'); expect(ship.powerAvailable).toBeGreaterThan(0);
expect(ship.unladenRange).toBeGreaterThan(0, s + ' unladenRange'); expect(ship.unladenRange).toBeGreaterThan(0);
expect(ship.ladenRange).toBeGreaterThan(0, s + ' ladenRange'); expect(ship.ladenRange).toBeGreaterThan(0);
expect(ship.fuelCapacity).toBeGreaterThan(0, s + ' fuelCapacity'); expect(ship.fuelCapacity).toBeGreaterThan(0);
expect(ship.unladenFastestRange).toBeGreaterThan(0, s + ' unladenFastestRange'); expect(ship.unladenFastestRange).toBeGreaterThan(0);
expect(ship.ladenFastestRange).toBeGreaterThan(0, s + ' ladenFastestRange'); expect(ship.ladenFastestRange).toBeGreaterThan(0);
expect(ship.shield).toBeGreaterThan(0, s + ' shield'); expect(ship.shield).toBeGreaterThan(0);
expect(ship.armour).toBeGreaterThan(0, s + ' armour'); expect(ship.armour).toBeGreaterThan(0);
expect(ship.topSpeed).toBeGreaterThan(0, s + ' topSpeed'); expect(ship.topSpeed).toBeGreaterThan(0);
} });
}); }
it("resets and rebuilds properly", function() { it("resets and rebuilds properly", function() {
var id = 'cobra_mk_iii'; var id = 'cobra_mk_iii';

6089
d3.js vendored

File diff suppressed because it is too large Load Diff

1
d3.min.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -1,96 +1,59 @@
worker_processes 1; worker_processes 2;
user nobody nobody; error_log ./nginx.error.log;
error_log /tmp/error.log; worker_rlimit_nofile 8192;
pid /tmp/nginx.pid; pid nginx.pid;
events { events {
worker_connections 1024;
worker_connections 1024; multi_accept on;
} }
http { http {
include /etc/nginx/mime.types; access_log off;
default_type application/octet-stream; charset UTF-8;
sendfile on;
client_body_temp_path /tmp/client_body;
fastcgi_temp_path /tmp/fastcgi_temp;
proxy_temp_path /tmp/proxy_temp;
scgi_temp_path /tmp/scgi_temp;
uwsgi_temp_path /tmp/uwsgi_temp;
access_log /tmp/access.log;
error_log /tmp/error.log;
# https://nginx.org/en/docs/http/ngx_http_gzip_module.html types {
# Enable gzip compression. text/html html htm shtml;
# Default: off text/css css;
gzip off; text/xml xml rss;
image/gif gif;
image/jpeg jpeg jpg;
application/x-javascript js;
text/plain txt;
image/png png;
image/svg+xml svg;
image/x-icon ico;
application/pdf pdf;
text/cache-manifest appcache;
}
# Compression level (1-9). gzip on;
# 5 is a perfect compromise between size and CPU usage, offering about gzip_vary on;
# 75% reduction for most ASCII files (almost identical to level 9). gzip_proxied any;
# Default: 1 gzip_comp_level 6;
gzip_comp_level 5; 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;
# Don't compress anything that's already small and unlikely to shrink much server {
# if at all (the default is 20 bytes, which is bad as that usually leads to listen 3301;
# larger files after gzipping). server_name localhost;
# Default: 20 root ./build/;
gzip_min_length 256; index index.html;
# Compress data even for clients that are connecting to us via proxies, location ~* \.(?:manifest|appcache|html?|xml|json|css|js|map|jpg|jpeg|gif|png|ico|svg|eot|ttf|woff|woff2)$ {
# identified by the "Via" header (required for CloudFront). expires -1;
# Default: off add_header Access-Control-Allow-Origin *;
gzip_proxied any;
# Tell proxies to cache both the gzipped and regular version of a resource
# whenever the client's Accept-Encoding capabilities header varies;
# Avoids the issue where a non-gzip capable client (which is extremely rare
# today) would display gibberish if their proxy gave them the gzipped version.
# Default: off
gzip_vary on;
# Compress all output labeled with one of the following MIME-types.
# text/html is always compressed by gzip module.
# Default: text/html
gzip_types *;
brotli on;
# brotli_static on;
brotli_types *;
# This should be turned on if you are going to have pre-compressed copies (.gz) of
# static files available. If not it should be left off as it will cause extra I/O
# for the check. It is best if you enable this in a location{} block for
# a specific directory, or on an individual server{} level.
# gzip_static on;
keepalive_timeout 3000;
server {
listen 80;
listen [::]:80;
index index.html;
server_name localhost;
root /usr/share/nginx/html;
autoindex on;
location ~* \.(?:manifest|appcache|html?|xml|json|css|js|map|jpg|jpeg|gif|png|ico|svg|eot|ttf|woff|woff2)$ {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Credentials true;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
access_log off;
}
location /service-worker.js {
expires -1;
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Credentials true; add_header Access-Control-Allow-Credentials true;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS'; 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'; 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 / {
try_files $uri $uri/ /index.html =404; location / {
} try_files $uri $uri/ /index.html =404;
location /iframe.html { }
try_files $uri $uri/ /iframe.html =404; }
}
}
} }

View File

@@ -5,7 +5,7 @@
"type": "git", "type": "git",
"url": "https://github.com/EDCD/coriolis" "url": "https://github.com/EDCD/coriolis"
}, },
"homepage": "https://coriolis.io", "homepage": "https://coriolis.edcd.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,7 +120,6 @@
"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",
@@ -136,7 +135,6 @@
"react": "^15.5.4", "react": "^15.5.4",
"react-dom": "^15.5.4", "react-dom": "^15.5.4",
"react-extras": "^0.7.1", "react-extras": "^0.7.1",
"react-fuzzy": "^0.5.2",
"react-ga": "^2.5.3", "react-ga": "^2.5.3",
"react-number-editor": "Athanasius/react-number-editor.git#miggy", "react-number-editor": "Athanasius/react-number-editor.git#miggy",
"recharts": "^1.2.0", "recharts": "^1.2.0",

View File

@@ -1,12 +1,11 @@
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';
import Announcement from './components/Announcement';
import Header from './components/Header'; import Header from './components/Header';
import Tooltip from './components/Tooltip'; import Tooltip from './components/Tooltip';
import ModalExport from './components/ModalExport'; import ModalExport from './components/ModalExport';
@@ -15,6 +14,7 @@ import ModalImport from './components/ModalImport';
import ModalPermalink from './components/ModalPermalink'; import ModalPermalink from './components/ModalPermalink';
import * as CompanionApiUtils from './utils/CompanionApiUtils'; import * as CompanionApiUtils from './utils/CompanionApiUtils';
import * as JournalUtils from './utils/JournalUtils'; import * as JournalUtils from './utils/JournalUtils';
import AboutPage from './pages/AboutPage'; import AboutPage from './pages/AboutPage';
import NotFoundPage from './pages/NotFoundPage'; import NotFoundPage from './pages/NotFoundPage';
import OutfittingPage from './pages/OutfittingPage'; import OutfittingPage from './pages/OutfittingPage';
@@ -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,20 +105,18 @@ 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);
} }
} }
async _getAnnouncements() { _getAnnouncements() {
try { return request.get('https://orbis.zone/api/announcement')
const announces = await request.get('https://orbis.zone/api/announcement') .query({showInCoriolis: true})
.query({ showInCoriolis: true }); .then(announces => {
this.setState({ announcements: announces.body }); this.setState({ announcements: announces.body })
} catch (err) { })
console.error(err)
}
} }
/** /**
@@ -346,37 +344,37 @@ export default class Coriolis extends React.Component {
}); });
} }
if ('serviceWorker' in navigator) { if ('serviceWorker' in navigator) {
// Your service-worker.js *must* be located at the top-level directory relative to your site. window.addEventListener('load', () => {
// It won't be able to control pages unless it's located at the same level or higher than them. // Your service-worker.js *must* be located at the top-level directory relative to your site.
// *Don't* register service worker file in, e.g., a scripts/ sub-directory! // It won't be able to control pages unless it's located at the same level or higher than them.
// See https://github.com/slightlyoff/ServiceWorker/issues/468 // *Don't* register service worker file in, e.g., a scripts/ sub-directory!
const self = this; // See https://github.com/slightlyoff/ServiceWorker/issues/468
if (process.env.NODE_ENV === 'production') { const self = this;
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)
} }
}); });
} });
} }
window.onerror = this._onError.bind(this); window.onerror = this._onError.bind(this);
window.addEventListener('resize', () => this.emitter.emit('windowResize')); window.addEventListener('resize', () => this.emitter.emit('windowResize'));
@@ -396,24 +394,20 @@ export default class Coriolis extends React.Component {
let currentMenu = this.state.currentMenu; let currentMenu = this.state.currentMenu;
return <div style={{ minHeight: '100%' }} onClick={this._closeMenu} return <div style={{ minHeight: '100%' }} onClick={this._closeMenu}
className={this.state.noTouch ? 'no-touch' : null}> className={this.state.noTouch ? 'no-touch' : null}>
<Header announcements={this.state.announcements} appCacheUpdate={this.state.appCacheUpdate} <Header announcements={this.state.announcements} appCacheUpdate={this.state.appCacheUpdate} currentMenu={currentMenu}/>
currentMenu={currentMenu}/>
<div className="announcement-container">{this.state.announcements.map(a => <Announcement
text={a.message}/>)}</div>
{this.state.error ? this.state.error : this.state.page ? React.createElement(this.state.page, { currentMenu }) : {this.state.error ? this.state.error : this.state.page ? React.createElement(this.state.page, { currentMenu }) :
<NotFoundPage/>} <NotFoundPage/>}
{this.state.modal} {this.state.modal}
{this.state.tooltip} {this.state.tooltip}
<footer> <footer>
<div className="right cap"> <div className="right cap">
<a href="https://github.com/EDCD/coriolis" target="_blank" rel="noopener noreferrer" <a href="https://github.com/EDCD/coriolis" target="_blank"
title="Coriolis Github Project">{window.CORIOLIS_VERSION} - {window.CORIOLIS_DATE}</a> title="Coriolis Github Project">{window.CORIOLIS_VERSION} - {window.CORIOLIS_DATE}</a>
<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 target="_blank" title={'Coriolis Commits since' + window.CORIOLIS_DATE}>Commits since last release
since last release
({window.CORIOLIS_DATE})</a> ({window.CORIOLIS_DATE})</a>
</div> </div>
</footer> </footer>

View File

@@ -1,7 +1,13 @@
// Check whether a test is run because then we need to
// initialize ReactGA in test mode
let isTest = false;
try {
isTest = process.env.node_env === 'test';
} catch (e) {}
import Persist from './stores/Persist'; import Persist from './stores/Persist';
import ReactGA from 'react-ga'; import ReactGA from 'react-ga';
ReactGA.initialize('UA-55840909-18', { testMode: isTest });
ReactGA.initialize('UA-55840909-18');
let standalone = undefined; let standalone = undefined;
/** /**

View File

@@ -25,7 +25,7 @@ export default class Announcement extends React.Component {
* @return {React.Component} A href element * @return {React.Component} A href element
*/ */
render() { render() {
return <div className="announcement" >{this.props.text}</div>; return <p>{this.props.text}</p>;
} }
} }

View File

@@ -5,7 +5,6 @@ import TranslatedComponent from './TranslatedComponent';
import { stopCtxPropagation } from '../utils/UtilityFunctions'; import { stopCtxPropagation } from '../utils/UtilityFunctions';
import cn from 'classnames'; import cn from 'classnames';
import { MountFixed, MountGimballed, MountTurret } from './SvgIcons'; import { MountFixed, MountGimballed, MountTurret } from './SvgIcons';
import FuzzySearch from 'react-fuzzy';
const PRESS_THRESHOLD = 500; // mouse/touch down threshold const PRESS_THRESHOLD = 500; // mouse/touch down threshold
@@ -40,7 +39,7 @@ const GRPCAT = {
'mc': 'projectiles', 'mc': 'projectiles',
'axmc': 'experimental', 'axmc': 'experimental',
'fc': 'projectiles', 'fc': 'projectiles',
'rfl': 'experimental', 'rfl': 'experimental',
'pa': 'projectiles', 'pa': 'projectiles',
'rg': 'projectiles', 'rg': 'projectiles',
'mr': 'ordnance', 'mr': 'ordnance',
@@ -73,12 +72,7 @@ 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'
}; };
// 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 = {
@@ -95,7 +89,7 @@ const CATEGORIES = {
'structural reinforcement': ['hr', 'mrp'], 'structural reinforcement': ['hr', 'mrp'],
'dc': ['dc'], 'dc': ['dc'],
// Hardpoints // Hardpoints
'lasers': ['pl', 'ul', 'bl'], 'lasers': ['pl', 'ul', 'bl', 'ml'],
'projectiles': ['mc', 'c', 'fc', 'pa', 'rg'], 'projectiles': ['mc', 'c', 'fc', 'pa', 'rg'],
'ordnance': ['mr', 'tp', 'nl'], 'ordnance': ['mr', 'tp', 'nl'],
// Utilities // Utilities
@@ -107,9 +101,7 @@ 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'],
}; };
/** /**
@@ -141,7 +133,6 @@ export default class AvailableModulesMenu extends TranslatedComponent {
constructor(props, context) { constructor(props, context) {
super(props); super(props);
this._hideDiff = this._hideDiff.bind(this); this._hideDiff = this._hideDiff.bind(this);
this._showSearch = this._showSearch.bind(this);
this.state = this._initState(props, context); this.state = this._initState(props, context);
this.slotItems = [];// Array to hold <li> refs. this.slotItems = [];// Array to hold <li> refs.
} }
@@ -168,7 +159,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
onSelect(m); onSelect(m);
} }
); );
let fuzzy = [];
if (modules instanceof Array) { if (modules instanceof Array) {
list = buildGroup(modules[0].grp, modules); list = buildGroup(modules[0].grp, modules);
} else { } else {
@@ -176,11 +167,9 @@ export default class AvailableModulesMenu extends TranslatedComponent {
// At present time slots with grouped options (Hardpoints and Internal) can be empty // At present time slots with grouped options (Hardpoints and Internal) can be empty
if (m) { if (m) {
let emptyId = 'empty'; let emptyId = 'empty';
if (this.firstSlotId == null) this.firstSlotId = emptyId; if(this.firstSlotId == null) this.firstSlotId = emptyId;
let keyDown = this._keyDown.bind(this, onSelect); let keyDown = this._keyDown.bind(this, onSelect);
list.push(<div className='empty-c upp' key={emptyId} data-id={emptyId} onClick={onSelect.bind(null, null)} list.push(<div className='empty-c upp' key={emptyId} data-id={emptyId} onClick={onSelect.bind(null, null)} onKeyDown={keyDown} tabIndex="0" ref={slotItem => this.slotItems[emptyId] = slotItem} >{translate('empty')}</div>);
onKeyDown={keyDown} tabIndex="0"
ref={slotItem => this.slotItems[emptyId] = slotItem}>{translate('empty')}</div>);
} }
// Need to regroup the modules by our own categorisation // Need to regroup the modules by our own categorisation
@@ -208,8 +197,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
if (categories.length === 1) { if (categories.length === 1) {
// Show category header instead of group header // Show category header instead of group header
if (m && grp == m.grp) { if (m && grp == m.grp) {
list.push(<div ref={(elem) => this.groupElem = elem} key={category} list.push(<div ref={(elem) => this.groupElem = elem} key={category} className={'select-category upp'}>{translate(category)}</div>);
className={'select-category upp'}>{translate(category)}</div>);
} else { } else {
list.push(<div key={category} className={'select-category upp'}>{translate(category)}</div>); list.push(<div key={category} className={'select-category upp'}>{translate(category)}</div>);
} }
@@ -220,32 +208,19 @@ export default class AvailableModulesMenu extends TranslatedComponent {
categoryHeader = true; categoryHeader = true;
} }
if (m && grp == m.grp) { if (m && grp == m.grp) {
list.push(<div ref={(elem) => this.groupElem = elem} key={grp} list.push(<div ref={(elem) => this.groupElem = elem} key={grp} className={'select-group cap'}>{translate(grp)}</div>);
className={'select-group cap'}>{translate(grp)}</div>);
} else { } else {
list.push(<div key={grp} className={'select-group cap'}>{translate(grp)}</div>); list.push(<div key={grp} className={'select-group cap'}>{translate(grp)}</div>);
} }
} }
list.push(buildGroup(grp, modules[grp])); list.push(buildGroup(grp, modules[grp]));
for (const i of modules[grp]) {
let mount = '';
if (i.mount === 'F') {
mount = 'Fixed';
} else if (i.mount === 'G') {
mount = 'Gimballed';
} else if (i.mount === 'T') {
mount = 'Turreted';
}
const fuzz = { grp, m: i, name: `${i.class}${i.rating}${mount ? ' ' + mount : ''} ${translate(grp)}` };
fuzzy.push(fuzz);
}
} }
} }
} }
} }
} }
let trackingFocus = false; let trackingFocus = false;
return { list, currentGroup, fuzzy, trackingFocus }; return { list, currentGroup, trackingFocus };
} }
/** /**
@@ -267,11 +242,9 @@ export default class AvailableModulesMenu extends TranslatedComponent {
const sortedModules = modules.sort(this._moduleOrder); const sortedModules = modules.sort(this._moduleOrder);
// Calculate the number of items per class. Used so we don't have long lists with only a few items in each row // Calculate the number of items per class. Used so we don't have long lists with only a few items in each row
const tmp = sortedModules.map((v, i) => v['class']).reduce((count, cls) => { const tmp = sortedModules.map((v, i) => v['class']).reduce((count, cls) => { count[cls] = ++count[cls] || 1; return count; }, {});
count[cls] = ++count[cls] || 1;
return count;
}, {});
const itemsPerClass = Math.max.apply(null, Object.keys(tmp).map(key => tmp[key])); const itemsPerClass = Math.max.apply(null, Object.keys(tmp).map(key => tmp[key]));
let itemsOnThisRow = 0; let itemsOnThisRow = 0;
@@ -324,29 +297,22 @@ export default class AvailableModulesMenu extends TranslatedComponent {
}; };
} }
switch (m.mount) { switch(m.mount) {
case 'F': case 'F': mount = <MountFixed className={'lg'} />; break;
mount = <MountFixed className={'lg'}/>; case 'G': mount = <MountGimballed className={'lg'}/>; break;
break; case 'T': mount = <MountTurret className={'lg'}/>; break;
case 'G':
mount = <MountGimballed className={'lg'}/>;
break;
case 'T':
mount = <MountTurret className={'lg'}/>;
break;
} }
if (m.name && m.name === prevName) { if (m.name && m.name === prevName) {
// elems.push(<br key={'b' + m.grp + i} />); // elems.push(<br key={'b' + m.grp + i} />);
itemsOnThisRow = 0; itemsOnThisRow = 0;
} }
if (itemsOnThisRow == 6 || i > 0 && sortedModules.length > 3 && itemsPerClass > 2 && m.class != prevClass && (m.rating != prevRating || m.mount)) { if (itemsOnThisRow == 6 || i > 0 && sortedModules.length > 3 && itemsPerClass > 2 && m.class != prevClass && (m.rating != prevRating || m.mount)) {
elems.push(<br key={'b' + m.grp + i}/>); elems.push(<br key={'b' + m.grp + i} />);
itemsOnThisRow = 0; itemsOnThisRow = 0;
} }
let tbIdx = (classes.indexOf('disabled') < 0) ? 0 : undefined; let tbIdx = (classes.indexOf('disabled') < 0) ? 0 : undefined;
elems.push( elems.push(
<li key={m.id} data-id={m.id} className={classes} {...eventHandlers} tabIndex={tbIdx} <li key={m.id} data-id={m.id} className={classes} {...eventHandlers} tabIndex={tbIdx} ref={slotItem => this.slotItems[m.id] = slotItem}>
ref={slotItem => this.slotItems[m.id] = slotItem}>
{mount} {mount}
{(mount ? ' ' : '') + m.class + m.rating + (m.missile ? '/' + m.missile : '') + (m.name ? ' ' + translate(m.name) : '')} {(mount ? ' ' : '') + m.class + m.rating + (m.missile ? '/' + m.missile : '') + (m.name ? ' ' + translate(m.name) : '')}
</li> </li>
@@ -374,40 +340,6 @@ export default class AvailableModulesMenu extends TranslatedComponent {
} }
} }
/**
* Generate tooltip content for the difference between the
* mounted module and the hovered modules
*/
_showSearch() {
if (this.props.modules instanceof Array) {
return;
}
return (
<FuzzySearch
list={this.state.fuzzy}
keys={['grp', 'name']}
tokenize={true}
className={'input'}
width={'100%'}
style={{ padding: 0 }}
onSelect={e => this.props.onSelect.bind(null, e.m)()}
resultsTemplate={(props, state, styles, clickHandler) => {
return state.results.map((val, i) => {
return (
<div
key={i}
className={'lc'}
onClick={() => clickHandler(i)}
>
{val.name}
</div>
);
});
}}
/>
);
}
/** /**
* Mouse over diff handler * Mouse over diff handler
* @param {Function} showDiff diff tooltip callback * @param {Function} showDiff diff tooltip callback
@@ -473,7 +405,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
* @param {Function} select Select module callback * @param {Function} select Select module callback
* @param {SytheticEvent} event Event * @param {SytheticEvent} event Event
*/ */
_keyUp(select, event) { _keyUp(select,event) {
// nothing here yet // nothing here yet
} }
@@ -543,13 +475,12 @@ export default class AvailableModulesMenu extends TranslatedComponent {
this.slotItems[this.firstSlotId].focus(); this.slotItems[this.firstSlotId].focus();
} }
} }
/** /**
* Handle focus if the component updates * Handle focus if the component updates
* *
*/ */
componentWillUnmount() { componentWillUnmount() {
if (this.props.slotDiv) { if(this.props.slotDiv) {
this.props.slotDiv.focus(); this.props.slotDiv.focus();
} }
} }
@@ -570,12 +501,11 @@ export default class AvailableModulesMenu extends TranslatedComponent {
render() { render() {
return ( return (
<div ref={node => this.node = node} <div ref={node => this.node = node}
className={cn('select', this.props.className)} className={cn('select', this.props.className)}
onScroll={this._hideDiff} onScroll={this._hideDiff}
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation() }
onContextMenu={stopCtxPropagation} onContextMenu={stopCtxPropagation}
> >
{this._showSearch()}
{this.state.list} {this.state.list}
</div> </div>
); );

View File

@@ -2,21 +2,12 @@ import React from 'react';
import cn from 'classnames'; import cn from 'classnames';
import Slot from './Slot'; import Slot from './Slot';
import Persist from '../stores/Persist'; import Persist from '../stores/Persist';
import { import { DamageAbsolute, DamageKinetic, DamageThermal, DamageExplosive, MountFixed, MountGimballed, MountTurret, ListModifications, Modified } from './SvgIcons';
DamageAbsolute,
DamageKinetic,
DamageThermal,
DamageExplosive,
MountFixed,
MountGimballed,
MountTurret,
ListModifications,
Modified
} from './SvgIcons';
import { Modifications } from 'coriolis-data/dist'; import { Modifications } from 'coriolis-data/dist';
import { stopCtxPropagation } from '../utils/UtilityFunctions'; import { stopCtxPropagation } from '../utils/UtilityFunctions';
import { blueprintTooltip } from '../utils/BlueprintFunctions'; import { blueprintTooltip } from '../utils/BlueprintFunctions';
/** /**
* Hardpoint / Utility Slot * Hardpoint / Utility Slot
*/ */
@@ -36,7 +27,7 @@ export default class HardpointSlot extends Slot {
* @return {string} Label * @return {string} Label
*/ */
_getMaxClassLabel(translate) { _getMaxClassLabel(translate) {
return translate(['U', 'S', 'M', 'L', 'H'][this.props.maxClass]); return translate(['U','S','M','L','H'][this.props.maxClass]);
} }
/** /**
@@ -75,76 +66,42 @@ export default class HardpointSlot extends Slot {
return <div className={className} draggable='true' onDragStart={drag} onDragEnd={drop}> return <div className={className} draggable='true' onDragStart={drag} onDragEnd={drop}>
<div className={'cb'}> <div className={'cb'}>
<div className={'l'}> <div className={'l'}>
{m.mount && m.mount == 'F' ? <span onMouseOver={termtip.bind(null, 'fixed')} {m.mount && m.mount == 'F' ? <span onMouseOver={termtip.bind(null, 'fixed')} onMouseOut={tooltip.bind(null, null)}><MountFixed /></span> : ''}
onMouseOut={tooltip.bind(null, null)}><MountFixed/></span> : ''} {m.mount && m.mount == 'G' ? <span onMouseOver={termtip.bind(null, 'gimballed')} onMouseOut={tooltip.bind(null, null)}><MountGimballed /></span> : ''}
{m.mount && m.mount == 'G' ? <span onMouseOver={termtip.bind(null, 'gimballed')} {m.mount && m.mount == 'T' ? <span onMouseOver={termtip.bind(null, 'turreted')} onMouseOut={tooltip.bind(null, null)}><MountTurret /></span> : ''}
onMouseOut={tooltip.bind(null, null)}><MountGimballed/></span> : ''} {m.getDamageDist() && m.getDamageDist().K ? <span onMouseOver={termtip.bind(null, 'kinetic')} onMouseOut={tooltip.bind(null, null)}><DamageKinetic /></span> : ''}
{m.mount && m.mount == 'T' ? <span onMouseOver={termtip.bind(null, 'turreted')} {m.getDamageDist() && m.getDamageDist().T ? <span onMouseOver={termtip.bind(null, 'thermal')} onMouseOut={tooltip.bind(null, null)}><DamageThermal /></span> : ''}
onMouseOut={tooltip.bind(null, null)}><MountTurret/></span> : ''} {m.getDamageDist() && m.getDamageDist().E ? <span onMouseOver={termtip.bind(null, 'explosive')} onMouseOut={tooltip.bind(null, null)}><DamageExplosive /></span> : ''}
{m.getDamageDist() && m.getDamageDist().K ? <span onMouseOver={termtip.bind(null, 'kinetic')} {m.getDamageDist() && m.getDamageDist().A ? <span onMouseOver={termtip.bind(null, 'absolute')} onMouseOut={tooltip.bind(null, null)}><DamageAbsolute /></span> : ''}
onMouseOut={tooltip.bind(null, null)}><DamageKinetic/></span> : ''} {classRating} {translate(m.name || m.grp)}{ m.mods && Object.keys(m.mods).length > 0 ? <span className='r' onMouseOver={termtip.bind(null, modTT)} onMouseOut={tooltip.bind(null, null)}><Modified /></span> : null }
{m.getDamageDist() && m.getDamageDist().T ? <span onMouseOver={termtip.bind(null, 'thermal')}
onMouseOut={tooltip.bind(null, null)}><DamageThermal/></span> : ''}
{m.getDamageDist() && m.getDamageDist().E ? <span onMouseOver={termtip.bind(null, 'explosive')}
onMouseOut={tooltip.bind(null, null)}><DamageExplosive/></span> : ''}
{m.getDamageDist() && m.getDamageDist().A ? <span onMouseOver={termtip.bind(null, 'absolute')}
onMouseOut={tooltip.bind(null, null)}><DamageAbsolute/></span> : ''}
{classRating} {translate(m.name || m.grp)}{m.mods && Object.keys(m.mods).length > 0 ? <span className='r'
onMouseOver={termtip.bind(null, modTT)}
onMouseOut={tooltip.bind(null, null)}><Modified/></span> : null}
</div> </div>
<div className={'r'}>{formats.round(m.getMass())}{u.T}</div> <div className={'r'}>{formats.round(m.getMass())}{u.T}</div>
</div> </div>
<div className={'cb'}> <div className={'cb'}>
{m.getDps() ? <div className={'l'} onMouseOver={termtip.bind(null, m.getClip() ? 'dpssdps' : 'dps')} { m.getDps() ? <div className={'l'} onMouseOver={termtip.bind(null, m.getClip() ? 'dpssdps' : 'dps')} onMouseOut={tooltip.bind(null, null)}>{translate('DPS')}: {formats.round1(m.getDps())} { m.getClip() ? <span>({formats.round1(m.getSDps()) })</span> : null }</div> : null }
onMouseOut={tooltip.bind(null, null)}>{translate('DPS')}: {formats.round1(m.getDps())} {m.getClip() ? { m.getEps() ? <div className={'l'} onMouseOver={termtip.bind(null, m.getClip() ? 'epsseps' : 'eps')} onMouseOut={tooltip.bind(null, null)}>{translate('EPS')}: {formats.round1(m.getEps())}{u.MW} { m.getClip() ? <span>({formats.round1((m.getClip() * m.getEps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload())) }{u.MW})</span> : null }</div> : null }
<span>({formats.round1(m.getSDps())})</span> : null}</div> : null} { m.getHps() ? <div className={'l'} onMouseOver={termtip.bind(null, m.getClip() ? 'hpsshps' : 'hps')} onMouseOut={tooltip.bind(null, null)}>{translate('HPS')}: {formats.round1(m.getHps())} { m.getClip() ? <span>({formats.round1((m.getClip() * m.getHps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload())) })</span> : null }</div> : null }
{m.getDamage() ? <div className={'l'} onMouseOver={termtip.bind(null, m.getDamage() ? 'shotdmg' : 'shotdmg')} { 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('shotdmg')}: {formats.round1(m.getDamage())}</div> : null} { m.getRoF() ? <div className={'l'} onMouseOver={termtip.bind(null, 'rof')} onMouseOut={tooltip.bind(null, null)}>{translate('ROF')}: {formats.f1(m.getRoF())}{u.ps}</div> : null }
{m.getEps() ? <div className={'l'} onMouseOver={termtip.bind(null, m.getClip() ? 'epsseps' : 'eps')} { m.getRange() ? <div className={'l'}>{translate('range', m.grp)} {formats.f1(m.getRange() / 1000)}{u.km}</div> : null }
onMouseOut={tooltip.bind(null, null)}>{translate('EPS')}: {formats.round1(m.getEps())}{u.MW} {m.getClip() ? { m.getScanTime() ? <div className={'l'}>{translate('scantime')} {formats.f1(m.getScanTime())}{u.s}</div> : null }
<span>({formats.round1((m.getClip() * m.getEps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload()))}{u.MW})</span> : null}</div> : null} { m.getFalloff() ? <div className={'l'}>{translate('falloff')} {formats.round(m.getFalloff() / 1000)}{u.km}</div> : null }
{m.getHps() ? <div className={'l'} onMouseOver={termtip.bind(null, m.getClip() ? 'hpsshps' : 'hps')} { m.getShieldBoost() ? <div className={'l'}>+{formats.pct1(m.getShieldBoost())}</div> : null }
onMouseOut={tooltip.bind(null, null)}>{translate('HPS')}: {formats.round1(m.getHps())} {m.getClip() ? { m.getAmmo() ? <div className={'l'}>{translate('ammunition')}: {formats.int(m.getClip())}/{formats.int(m.getAmmo())}</div> : null }
<span>({formats.round1((m.getClip() * m.getHps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload()))})</span> : null}</div> : null} { m.getReload() ? <div className={'l'}>{translate('reload')}: {formats.round(m.getReload())}{u.s}</div> : null }
{m.getDps() && m.getEps() ? <div className={'l'} onMouseOver={termtip.bind(null, 'dpe')} { m.getShotSpeed() ? <div className={'l'}>{translate('shotspeed')}: {formats.int(m.getShotSpeed())}{u.mps}</div> : null }
onMouseOut={tooltip.bind(null, null)}>{translate('DPE')}: {formats.f1(m.getDps() / m.getEps())}</div> : null} { m.getPiercing() ? <div className={'l'}>{translate('piercing')}: {formats.int(m.getPiercing())}</div> : null }
{m.getRoF() ? <div className={'l'} onMouseOver={termtip.bind(null, 'rof')} { m.getJitter() ? <div className={'l'}>{translate('jitter')}: {formats.f2(m.getJitter())}°</div> : null }
onMouseOut={tooltip.bind(null, null)}>{translate('ROF')}: {formats.f1(m.getRoF())}{u.ps}</div> : null} { showModuleResistances && m.getExplosiveResistance() ? <div className='l'>{translate('explres')}: {formats.pct(m.getExplosiveResistance())}</div> : null }
{m.getRange() ? <div { showModuleResistances && m.getKineticResistance() ? <div className='l'>{translate('kinres')}: {formats.pct(m.getKineticResistance())}</div> : null }
className={'l'}>{translate('range', m.grp)} {formats.f1(m.getRange() / 1000)}{u.km}</div> : null} { showModuleResistances && m.getThermalResistance() ? <div className='l'>{translate('thermres')}: {formats.pct(m.getThermalResistance())}</div> : null }
{m.getScanTime() ? <div { m.getIntegrity() ? <div className='l'>{translate('integrity')}: {formats.int(m.getIntegrity())}</div> : null }
className={'l'}>{translate('scantime')} {formats.f1(m.getScanTime())}{u.s}</div> : null} { m && validMods.length > 0 ? <div className='r' tabIndex="0" ref={ modButton => this.modButton = modButton }><button tabIndex="-1" onClick={this._toggleModifications.bind(this)} onContextMenu={stopCtxPropagation} onMouseOver={termtip.bind(null, 'modifications')} onMouseOut={tooltip.bind(null, null)}><ListModifications /></button></div> : null }
{m.getFalloff() ? <div
className={'l'}>{translate('falloff')} {formats.round(m.getFalloff() / 1000)}{u.km}</div> : null}
{m.getShieldBoost() ? <div className={'l'}>+{formats.pct1(m.getShieldBoost())}</div> : null}
{m.getAmmo() ? <div
className={'l'}>{translate('ammunition')}: {formats.int(m.getClip())}/{formats.int(m.getAmmo())}</div> : null}
{m.getReload() ? <div className={'l'}>{translate('reload')}: {formats.round(m.getReload())}{u.s}</div> : null}
{m.getShotSpeed() ? <div
className={'l'}>{translate('shotspeed')}: {formats.int(m.getShotSpeed())}{u.mps}</div> : null}
{m.getPiercing() ? <div className={'l'}>{translate('piercing')}: {formats.int(m.getPiercing())}</div> : null}
{m.getJitter() ? <div className={'l'}>{translate('jitter')}: {formats.f2(m.getJitter())}°</div> : null}
{m.getScanAngle() ? <div className={'l'}>{translate('scan angle')}: {formats.f2(m.getScanAngle())}°</div> : null}
{m.getScanRange() ? <div className={'l'}>{translate('scan range')}: {formats.int(m.getScanRange())}{u.m}</div> : null}
{m.getMaxAngle() ? <div className={'l'}>{translate('max angle')}: {formats.f2(m.getMaxAngle())}°</div> : null}
{showModuleResistances && m.getExplosiveResistance() ? <div
className='l'>{translate('explres')}: {formats.pct(m.getExplosiveResistance())}</div> : null}
{showModuleResistances && m.getKineticResistance() ? <div
className='l'>{translate('kinres')}: {formats.pct(m.getKineticResistance())}</div> : null}
{showModuleResistances && m.getThermalResistance() ? <div
className='l'>{translate('thermres')}: {formats.pct(m.getThermalResistance())}</div> : null}
{m.getIntegrity() ? <div className='l'>{translate('integrity')}: {formats.int(m.getIntegrity())}</div> : null}
{m && validMods.length > 0 ? <div className='r' tabIndex="0" ref={modButton => this.modButton = modButton}>
<button tabIndex="-1" onClick={this._toggleModifications.bind(this)} onContextMenu={stopCtxPropagation}
onMouseOver={termtip.bind(null, 'modifications')} onMouseOut={tooltip.bind(null, null)}>
<ListModifications/></button>
</div> : null}
</div> </div>
</div>; </div>;
} else { } else {
return <div className={'empty'}>{translate('empty')}</div>; return <div className={'empty'}>{translate('empty')}</div>;
} }
} }

View File

@@ -153,15 +153,6 @@ export default class HardpointSlotSection extends SlotSection {
<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>
<ul>
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'rfl', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['rfl-F'] = smRef}><MountFixed className='lg'/></li>
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'rfl', 'T')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['rfl-T'] = smRef}><MountTurret className='lg'/></li>
</ul>
</div>; </div>;
} }

View File

@@ -23,10 +23,8 @@ export default class ModalOrbis extends TranslatedComponent {
this.state = { this.state = {
orbisCreds: Persist.getOrbisCreds(), orbisCreds: Persist.getOrbisCreds(),
orbisUrl: '...', orbisUrl: '...',
ship: this.props.ship,
authenticatedStatus: 'Checking...' authenticatedStatus: 'Checking...'
}; };
this.orbisCategory = this.orbisCategory.bind(this);
} }
/** /**
@@ -90,17 +88,6 @@ export default class ModalOrbis extends TranslatedComponent {
}); });
} }
/**
* Handler for changing category
* @param {SyntheticEvent} e React Event
*/
orbisCategory(e) {
let ship = this.state.ship;
let cat = e.target.value;
ship.category = cat;
this.setState({ship});
}
/** /**
* Render the modal * Render the modal
* @return {React.Component} Modal Content * @return {React.Component} Modal Content
@@ -119,17 +106,6 @@ export default class ModalOrbis extends TranslatedComponent {
<br/><br/> <br/><br/>
<a className='button' href="https://orbis.zone/api/auth">Log in / signup to Orbis</a> <a className='button' href="https://orbis.zone/api/auth">Log in / signup to Orbis</a>
<br/><br/> <br/><br/>
<h3>Category</h3>
<select onChange={this.orbisCategory}>
<option value="">No Category</option>
<option>Combat</option>
<option>Mining</option>
<option>Trading</option>
<option>Exploration</option>
<option>Passenger Liner</option>
<option>PvP</option>
</select>
<br/><br/>
<h3 >{translate('Orbis link')}</h3> <h3 >{translate('Orbis link')}</h3>
<input value={this.state.orbisUrl} readOnly size={25} onFocus={ (e) => e.target.select() }/> <input value={this.state.orbisUrl} readOnly size={25} onFocus={ (e) => e.target.select() }/>
<br/><br/> <br/><br/>

View File

@@ -52,7 +52,6 @@ export default class ShipSummaryTable extends TranslatedComponent {
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 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 armourMetrics = Calc.armourMetrics(ship); const armourMetrics = Calc.armourMetrics(ship);
let shieldColour = 'blue'; let shieldColour = 'blue';
if (shieldGenerator && shieldGenerator.m.grp === 'psg') { if (shieldGenerator && shieldGenerator.m.grp === 'psg') {
@@ -79,14 +78,13 @@ export default class ShipSummaryTable extends TranslatedComponent {
<th rowSpan={2}>{translate('TTD')}</th> <th rowSpan={2}>{translate('TTD')}</th>
{/* <th onMouseEnter={termtip.bind(null, 'heat per second')} onMouseLeave={hide} rowSpan={2}>{translate('HPS')}</th> */} {/* <th onMouseEnter={termtip.bind(null, 'heat per second')} onMouseLeave={hide} rowSpan={2}>{translate('HPS')}</th> */}
<th rowSpan={2}>{translate('cargo')}</th> <th rowSpan={2}>{translate('cargo')}</th>
<th rowSpan={2} onMouseEnter={termtip.bind(null, 'passenger capacity', { cap: 0 })} onMouseLeave={hide}>{translate('pax')}</th> <th rowSpan={2}>{translate('pax')}</th>
<th rowSpan={2}>{translate('fuel')}</th> <th rowSpan={2}>{translate('fuel')}</th>
<th colSpan={3}>{translate('mass')}</th> <th colSpan={3}>{translate('mass')}</th>
<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_TIME', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('boost time')}</th>
<th rowSpan={2}>{translate('resting heat (Beta)')}</th>
</tr> </tr>
<tr> <tr>
<th className='lft'>{translate('max')}</th> <th className='lft'>{translate('max')}</th>
@@ -124,7 +122,6 @@ export default class ShipSummaryTable extends TranslatedComponent {
<td>{ship.crew}</td> <td>{ship.crew}</td>
<td>{ship.masslock}</td> <td>{ship.masslock}</td>
<td>{shipBoost !== 'No Boost' ? formats.time(shipBoost) : 'No Boost'}</td> <td>{shipBoost !== 'No Boost' ? formats.time(shipBoost) : 'No Boost'}</td>
<td>{formats.pct(restingHeat)}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@@ -20,28 +20,16 @@ export default class StandardSlotSection extends SlotSection {
super(props, context, 'standard', 'core internal'); super(props, context, 'standard', 'core internal');
this._optimizeStandard = this._optimizeStandard.bind(this); this._optimizeStandard = this._optimizeStandard.bind(this);
this._selectBulkhead = this._selectBulkhead.bind(this); this._selectBulkhead = this._selectBulkhead.bind(this);
this._showDW2Menu = this._showDW2Menu.bind(this);
this._dw2 = this._dw2.bind(this);
this.selectedRefId = null; this.selectedRefId = null;
this.firstRefId = 'maxjump'; this.firstRefId = 'maxjump';
this.lastRefId = 'dw2'; this.lastRefId = 'racer';
this.state = {
showDW2: false,
DW2Tier: -1,
DW2Eng: -1,
DW2Role: '',
DW2Gfsb: false,
DW2Gpp: false,
DW2Fighter: false
};
} }
/** /**
* Handle focus if the component updates * Handle focus if the component updates
* @param {Object} prevProps React Component properties * @param {Object} prevProps React Component properties
*/ */
componentDidUpdate(prevProps) { componentDidUpdate(prevProps) {
this._handleSectionFocus(prevProps, this.firstRefId, this.lastRefId); this._handleSectionFocus(prevProps,this.firstRefId, this.lastRefId);
} }
/** /**
@@ -84,114 +72,6 @@ export default class StandardSlotSection extends SlotSection {
this._close(); this._close();
} }
/**
* DW2 Build
*/
_dw2() {
this.selectedRefId = 'dw2';
this.setState({ showDW2: false });
ShipRoles.dw2Build(this.props.ship, this.state.DW2Tier, this.state.DW2Eng, this.state.DW2Role, this.state.DW2Gfsb, this.state.DW2Gpp, this.state.DW2Fighter);
this.props.ship.updateModificationsString();
this.props.onChange();
this.props.onCargoChange(this.props.ship.cargoCapacity);
this.props.onFuelChange(this.props.ship.fuelCapacity);
this._close();
}
_showDW2Menu(translate) {
return (
<div className='select' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
<div className='select-group cap'>{translate('Tier')}</div>
<ul id={'tier'}>
<li className={cn({ active: this.state.DW2Tier === 1 }, 'lc')} tabIndex="0"
onClick={() => this.setState({ DW2Tier: 1 })} onKeyDown={this._keyDown}
>{translate('1 - Max. Jump Range, Unshielded')}</li>
<li className={cn({ active: this.state.DW2Tier === 2 }, 'lc')} tabIndex="0"
onClick={() => this.setState({ DW2Tier: 2 })} onKeyDown={this._keyDown}
>{translate('2 - Max. Jump Range, Minimal Shields')}</li>
<li className={cn({ active: this.state.DW2Tier === 3 }, 'lc')} tabIndex="0"
onClick={() => this.setState({ DW2Tier: 3 })} onKeyDown={this._keyDown}
>{translate('3 - Max. Jump Range, Optimal Shields')}</li>
<li className={cn({ active: this.state.DW2Tier === 4 }, 'lc')} tabIndex="0"
onClick={() => this.setState({ DW2Tier: 4 })} onKeyDown={this._keyDown}
>{translate('4 - Max. Jump Range, Optimal Shields & Thrusters')}</li>
</ul>
<hr/>
<div className='select-group cap'>{translate('Engineering Level')}</div>
<ul id={'engLevel'}>
<li className={cn({ active: this.state.DW2Eng === 1 }, 'lc')} tabIndex="0"
onClick={() => this.setState({ DW2Eng: 1 })} onKeyDown={this._keyDown}
>{translate('No engineering')}</li>
<li className={cn({ active: this.state.DW2Eng === 2 }, 'lc')} tabIndex="0"
onClick={() => this.setState({ DW2Eng: 2 })} onKeyDown={this._keyDown}
>{translate('Only Felicity Farseer and Elvira Martuuk')}</li>
<li className={cn({ active: this.state.DW2Eng === 3 }, 'lc')} tabIndex="0"
onClick={() => this.setState({ DW2Eng: 3 })} onKeyDown={this._keyDown}
>{translate('All exploration engineers')}</li>
</ul>
<hr/>
<div className='select-group cap'>{translate('Role')}</div>
<ul id={'role'}>
<li className={cn({ active: this.state.DW2Role === 'exploration' }, 'lc')} tabIndex="0"
onClick={() => this.setState({ DW2Role: 'exploration' })}
onKeyDown={this._keyDown}
>{translate('Space exploration')}</li>
<li className={cn({ active: this.state.DW2Role === 'surface' }, 'lc')} tabIndex="0"
onClick={() => this.setState({ DW2Role: 'surface' })}
onKeyDown={this._keyDown}
>{translate('Surface exploration')}</li>
<li className={cn({ active: this.state.DW2Role === 'materialProspector' }, 'lc')} tabIndex="0"
onClick={() => this.setState({ DW2Role: 'materialProspector' })}
onKeyDown={this._keyDown}
>{translate('Material prospector')}</li>
<li className={cn({ active: this.state.DW2Role === 'propectorMining' }, 'lc')} tabIndex="0"
onClick={() => this.setState({ DW2Role: 'propectorMining' })}
onKeyDown={this._keyDown}
>{translate('Prospector/Sapper Miner')}</li>
<li className={cn({ active: this.state.DW2Role === 'bigRigMining' }, 'lc')} tabIndex="0"
onClick={() => this.setState({ DW2Role: 'bigRigMining' })}
onKeyDown={this._keyDown}
>{translate('Big Rig, full mining')}</li>
<li className={cn({ active: this.state.DW2Role === 'fuelRat' }, 'lc')} tabIndex="0"
onClick={() => this.setState({ DW2Role: 'fuelRat' })} onKeyDown={this._keyDown}
>{translate('Fuel Rat')}</li>
<li className={cn({ active: this.state.DW2Role === 'mechanic' }, 'lc')} tabIndex="0"
onClick={() => this.setState({ DW2Role: 'mechanic' })} onKeyDown={this._keyDown}
>{translate('Mechanic')}</li>
<li className={cn({ active: this.state.DW2Role === 'trucker' }, 'lc')} tabIndex="0"
onClick={() => this.setState({ DW2Role: 'trucker' })} onKeyDown={this._keyDown}
>{translate('Trucker')}</li>
</ul>
<hr/>
<ul>
<li className={cn({ active: this.state.DW2Gfsb === true }, 'lc')}
onClick={() => this.setState({ DW2Gfsb: this.state.DW2Gfsb !== true })}>
Add Guardian FSD Booster
</li>
</ul>
<ul>
<li className={cn({ active: this.state.DW2Gpp === true }, 'lc')}
onClick={() => this.setState({ DW2Gpp: this.state.DW2Gpp !== true })}>
Add Guardian Power Plant
</li>
</ul>
<ul>
<li className={cn({ active: this.state.DW2Fighter === true }, 'lc')}
onClick={() => this.setState({ DW2Fighter: this.state.DW2Fighter !== true })}>
Add Fighter
</li>
</ul>
<hr/>
<ul>
<li onClick={this._dw2} className={cn('lc')} tabIndex="0"
onKeyDown={this._keyDown}>
<button className="button">Apply</button>
</li>
</ul>
</div>
);
}
/** /**
* Miner Build * Miner Build
* @param {Boolean} shielded True if shield generator should be included * @param {Boolean} shielded True if shield generator should be included
@@ -297,6 +177,7 @@ export default class StandardSlotSection extends SlotSection {
warning={m => m instanceof Module ? m.getMaxMass() < (ship.unladenMass + cargo + fuel - st[1].m.mass + m.mass) : m.maxmass < (ship.unladenMass + cargo + fuel - st[1].m.mass + m.mass)} warning={m => m instanceof Module ? m.getMaxMass() < (ship.unladenMass + cargo + fuel - st[1].m.mass + m.mass) : m.maxmass < (ship.unladenMass + cargo + fuel - st[1].m.mass + m.mass)}
/>; />;
slots[3] = <StandardSlot slots[3] = <StandardSlot
key='fsd' key='fsd'
slot={st[2]} slot={st[2]}
@@ -351,7 +232,7 @@ export default class StandardSlotSection extends SlotSection {
selected={currentMenu == st[6]} selected={currentMenu == st[6]}
onChange={this.props.onChange} onChange={this.props.onChange}
ship={ship} ship={ship}
warning={m => m.fuel < st[2].m.maxfuel} // Show warning when fuel tank is smaller than FSD Max Fuel warning= {m => m.fuel < st[2].m.maxfuel} // Show warning when fuel tank is smaller than FSD Max Fuel
/>; />;
return slots; return slots;
@@ -364,34 +245,19 @@ export default class StandardSlotSection extends SlotSection {
*/ */
_getSectionMenu(translate) { _getSectionMenu(translate) {
let planetaryDisabled = this.props.ship.internal.length < 4; let planetaryDisabled = this.props.ship.internal.length < 4;
if (this.state.showDW2 === true) {
return this._showDW2Menu(translate);
}
return <div className='select' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}> return <div className='select' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
<ul> <ul>
<li className='lc' tabIndex="0" onClick={this._optimizeStandard} onKeyDown={this._keyDown} <li className='lc' tabIndex="0" onClick={this._optimizeStandard} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['maxjump'] = smRef}>{translate('Maximize Jump Range')}</li>
ref={smRef => this.sectionRefArr['maxjump'] = smRef}>{translate('Maximize Jump Range')}</li>
</ul> </ul>
<div className='select-group cap'>{translate('roles')}</div> <div className='select-group cap'>{translate('roles')}</div>
<ul> <ul>
<li className='lc' tabIndex="0" onClick={this._multiPurpose.bind(this, false, 0)} onKeyDown={this._keyDown} <li className='lc' tabIndex="0" onClick={this._multiPurpose.bind(this, false, 0)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['multipurpose'] = smRef}>{translate('Multi-purpose')}</li>
ref={smRef => this.sectionRefArr['multipurpose'] = smRef}>{translate('Multi-purpose')}</li> <li className='lc' tabIndex="0" onClick={this._multiPurpose.bind(this, true, 2)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['combat'] = smRef}>{translate('Combat')}</li>
<li className='lc' tabIndex="0" onClick={this._multiPurpose.bind(this, true, 2)} onKeyDown={this._keyDown} <li className='lc' tabIndex="0" onClick={this._optimizeCargo.bind(this, true)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['trader'] = smRef}>{translate('Trader')}</li>
ref={smRef => this.sectionRefArr['combat'] = smRef}>{translate('Combat')}</li> <li className='lc' tabIndex="0" onClick={this._optimizeExplorer.bind(this, false)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['explorer'] = smRef}>{translate('Explorer')}</li>
<li className='lc' tabIndex="0" onClick={this._optimizeCargo.bind(this, true)} onKeyDown={this._keyDown} <li className={cn('lc', { disabled: planetaryDisabled })} tabIndex={planetaryDisabled ? '' : '0'} onClick={!planetaryDisabled && this._optimizeExplorer.bind(this, true)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['planetary'] = smRef}>{translate('Planetary Explorer')}</li>
ref={smRef => this.sectionRefArr['trader'] = smRef}>{translate('Trader')}</li> <li className='lc' tabIndex="0" onClick={this._optimizeMiner.bind(this, true)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['miner'] = smRef}>{translate('Miner')}</li>
<li className='lc' tabIndex="0" onClick={this._optimizeExplorer.bind(this, false)} onKeyDown={this._keyDown} <li className='lc' tabIndex="0" onClick={this._optimizeRacer.bind(this)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['racer'] = smRef}>{translate('Racer')}</li>
ref={smRef => this.sectionRefArr['explorer'] = smRef}>{translate('Explorer')}</li>
<li className={cn('lc', { disabled: planetaryDisabled })} tabIndex={planetaryDisabled ? '' : '0'}
onClick={!planetaryDisabled && this._optimizeExplorer.bind(this, true)} onKeyDown={this._keyDown}
ref={smRef => this.sectionRefArr['planetary'] = smRef}>{translate('Planetary Explorer')}</li>
<li className='lc' tabIndex="0" onClick={this._optimizeMiner.bind(this, true)} onKeyDown={this._keyDown}
ref={smRef => this.sectionRefArr['miner'] = smRef}>{translate('Miner')}</li>
<li className='lc' tabIndex="0" onClick={this._optimizeRacer.bind(this)} onKeyDown={this._keyDown}
ref={smRef => this.sectionRefArr['racer'] = smRef}>{translate('Racer')}</li>
<li className='lc' tabIndex="0" onClick={() => this.setState({ showDW2: !this.state.showDW2 })}
onKeyDown={this._keyDown}
ref={smRef => this.sectionRefArr['dw2'] = smRef}>{translate('DW2')}</li>
</ul> </ul>
</div>; </div>;
} }

View File

@@ -1,7 +1,7 @@
import TranslatedComponent from './TranslatedComponent'; import TranslatedComponent from './TranslatedComponent';
import React, { PropTypes } from 'react'; import React, { PropTypes } from 'react';
import ContainerDimensions from 'react-container-dimensions'; import ContainerDimensions from 'react-container-dimensions';
import { BarChart, Bar, XAxis, YAxis, LabelList } from 'recharts'; import { BarChart, Bar, XAxis, YAxis } from 'recharts';
const CORIOLIS_COLOURS = ['#FF8C0D', '#1FB0FF', '#71A052', '#D5D54D']; const CORIOLIS_COLOURS = ['#FF8C0D', '#1FB0FF', '#71A052', '#D5D54D'];
const LABEL_COLOUR = '#000000'; const LABEL_COLOUR = '#000000';
@@ -53,9 +53,7 @@ export default class VerticalBarChart extends TranslatedComponent {
<BarChart width={width} height={width * ASPECT} data={this.props.data} margin={{ top: 5, right: 5, left: 5, bottom: 5 }}> <BarChart width={width} height={width * ASPECT} data={this.props.data} margin={{ top: 5, right: 5, left: 5, bottom: 5 }}>
<XAxis interval={0} fontSize='0.8em' stroke={AXIS_COLOUR} dataKey='label' /> <XAxis interval={0} fontSize='0.8em' stroke={AXIS_COLOUR} dataKey='label' />
<YAxis interval={'preserveStart'} tickCount={11} fontSize='0.8em' stroke={AXIS_COLOUR} type='number' domain={[0, localMax]}/> <YAxis interval={'preserveStart'} tickCount={11} fontSize='0.8em' stroke={AXIS_COLOUR} type='number' domain={[0, localMax]}/>
<Bar dataKey='value' fill={CORIOLIS_COLOURS[0]} isAnimationActive={false} onMouseOver={this._termtip} onMouseOut={tooltip.bind(null, null)}> <Bar dataKey='value' label={<ValueLabel />} fill={CORIOLIS_COLOURS[0]} isAnimationActive={false} onMouseOver={this._termtip} onMouseOut={tooltip.bind(null, null)}/>
<LabelList dataKey='value' position='insideTop'/>
</Bar>
</BarChart> </BarChart>
</div> </div>
)} )}
@@ -78,3 +76,29 @@ export default class VerticalBarChart extends TranslatedComponent {
} }
} }
} }
/**
* A label that displays the value within the bar of the chart
*/
class ValueLabel extends React.Component {
static propTypes = {
x: PropTypes.number,
y: PropTypes.number,
payload: PropTypes.object,
value: PropTypes.number
};
/**
* Render offence
* @return {React.Component} contents
*/
render() {
const { x, y, payload, value } = this.props;
const em = value < 1000 ? '1em' : value < 1000 ? '0.8em' : '0.7em';
return (
<text x={x} y={y} fill="#000000" textAnchor="middle" dy={20} style={{ fontSize: em }}>{value}</text>
);
}
};

File diff suppressed because one or more lines are too long

View File

@@ -6,6 +6,7 @@ import { CoriolisLogo, GitHub } from '../components/SvgIcons';
* About Page * About Page
*/ */
export default class AboutPage extends Page { export default class AboutPage extends Page {
/** /**
* Constructor * Constructor
* @param {Object} props React Component properties * @param {Object} props React Component properties
@@ -22,112 +23,33 @@ export default class AboutPage extends Page {
* @return {React.Component} The page contents * @return {React.Component} The page contents
*/ */
renderPage() { renderPage() {
return ( return <div className={'page'} style={{ textAlign: 'left', maxWidth: 800, margin: '0 auto' }}>
<div <h1><CoriolisLogo style={{ marginRight: '0.4em' }} className='xl'/><span className='warning'>Coriolis EDCD Edition</span></h1>
className={'page'}
style={{ textAlign: 'left', maxWidth: 800, margin: '0 auto' }}
>
<h1>
<CoriolisLogo style={{ marginRight: '0.4em' }} className="xl" />
<span className="warning">Coriolis EDCD Edition</span>
</h1>
<p> <p>This is a clone of the Coriolis project, whose original author is currently unable to maintain it. This clone is maintained by the <a href="http://edcd.github.io/">EDCD community</a>.</p>
This is a clone of the Coriolis project, whose original author is <p>To recover your builds, go to <a href='https://coriolis.io/' target='_blank'>https://coriolis.io/</a>, backup your builds (Settings / Backup), copy the text, return here and import (Settings / Import).</p>
currently unable to maintain it. This clone is maintained by the{' '} <p>The Coriolis project was inspired by <a href='http://www.edshipyard.com/' target='_blank'>E:D Shipyard</a> and, of course, <a href='http://www.elitedangerous.com' target='_blank'>Elite Dangerous</a>. The ultimate goal of Coriolis is to provide rich features to support in-game play and planning while engaging the E:D community to support its development.</p>
<a href="http://edcd.github.io/">EDCD community</a>. <p>Coriolis was created using assets and imagery from Elite: Dangerous, with the permission of Frontier Developments plc, for non-commercial purposes. It is not endorsed by nor reflects the views or opinions of Frontier Developments. A number of assets were sourced from <a href='http://edassets.org' target='_blank'>ED Assets</a></p>
</p>
<p>
To recover your builds, go to{' '}
<a href="https://coriolis.io/" target="_blank">
https://coriolis.io/
</a>
, backup your builds (Settings / Backup), copy the text, return here
and import (Settings / Import).
</p>
<p>
The Coriolis project was inspired by{' '}
<a href="http://www.edshipyard.com/" target="_blank">
E:D Shipyard
</a>{' '}
and, of course,{' '}
<a href="http://www.elitedangerous.com" target="_blank">
Elite Dangerous
</a>
. The ultimate goal of Coriolis is to provide rich features to support
in-game play and planning while engaging the E:D community to support
its development.
</p>
<p>
Coriolis was created using assets and imagery from Elite: Dangerous,
with the permission of Frontier Developments plc, for non-commercial
purposes. It is not endorsed by nor reflects the views or opinions of
Frontier Developments. A number of assets were sourced from{' '}
<a href="http://edassets.org" target="_blank">
ED Assets
</a>
</p>
<a <a style={{ display: 'block', textDecoration: 'none' }} href='https://github.com/EDCD/coriolis' target='_blank' title='Coriolis Github Project'>
style={{ display: 'block', textDecoration: 'none' }} <GitHub style={{ margin: '0.4em' }} className='l fg xl'/>
href="https://github.com/EDCD/coriolis" <h2 style={{ margin: 0, textDecoration: 'none' }}>Github</h2>
target="_blank" github.com/EDCD/coriolis
title="Coriolis Github Project" </a>
>
<GitHub style={{ margin: '0.4em' }} className="l fg xl" />
<h2 style={{ margin: 0, textDecoration: 'none' }}>Github</h2>
github.com/EDCD/coriolis
</a>
<p> <p>Coriolis is an open source project. Checkout the list of upcoming features and to-do list on github. Any and all contributions and feedback are welcome. If you encounter any bugs please report them and provide as much detail as possible.</p>
Coriolis is an open source project. Checkout the list of upcoming
features and to-do list on github. Any and all contributions and
feedback are welcome. If you encounter any bugs please report them and
provide as much detail as possible.
</p>
<h3>Chat</h3> <h3>Chat</h3>
<p> <p>You can chat to us on our <a href='https://discord.gg/0uwCh6R62aPRjk9w' target='_blank'>EDCD Discord server</a>.</p>
You can chat to us on our{' '}
<a href="https://discord.gg/0uwCh6R62aPRjk9w" target="_blank">
EDCD Discord server
</a>
.
</p>
<h3>Supporting Coriolis</h3> <h3>Supporting Coriolis</h3>
<p> <p>Coriolis is an open source project, and I work on it in my free time. I have set up a patreon at <a href='https://www.patreon.com/coriolis_elite'>patreon.com/coriolis_elite</a>, which will be used to keep Coriolis up to date and the servers running.</p>
Coriolis is an open source project, and I work on it in my free time. <form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
I have set up a patreon at{' '} <input type="hidden" name="cmd" value="_s-xclick"/>
<a href="https://www.patreon.com/coriolis_elite"> <input type="hidden" name="hosted_button_id" value="SJBKT2SWEEU68" />
patreon.com/coriolis_elite <input type="image" src="https://www.paypalobjects.com/en_AU/i/btn/btn_donate_SM.gif" border="0" name="submit" alt="PayPal The safer, easier way to pay online!" />
</a> <img alt="" border="0" src="https://www.paypalobjects.com/en_AU/i/scr/pixel.gif" width="1" height="1" />
, which will be used to keep Coriolis up to date and the servers
running.
</p>
<form
action="https://www.paypal.com/cgi-bin/webscr"
method="post"
target="_top"
>
<input type="hidden" name="cmd" value="_s-xclick" />
<input type="hidden" name="hosted_button_id" value="SJBKT2SWEEU68" />
<input
type="image"
src="https://www.paypalobjects.com/en_AU/i/btn/btn_donate_SM.gif"
border="0"
name="submit"
alt="PayPal The safer, easier way to pay online!"
/>
<img
alt=""
border="0"
src="https://www.paypalobjects.com/en_AU/i/scr/pixel.gif"
width="1"
height="1"
/>
</form> </form>
</div> </div>;
);
} }
} }

View File

@@ -13,14 +13,7 @@ import ModalCompare from '../components/ModalCompare';
import ModalExport from '../components/ModalExport'; import ModalExport from '../components/ModalExport';
import ModalPermalink from '../components/ModalPermalink'; import ModalPermalink from '../components/ModalPermalink';
import ModalImport from '../components/ModalImport'; import ModalImport from '../components/ModalImport';
import { import { FloppyDisk, Bin, Download, Embed, Rocket, LinkIcon } from '../components/SvgIcons';
FloppyDisk,
Bin,
Download,
Embed,
Rocket,
LinkIcon
} from '../components/SvgIcons';
import ShortenUrl from '../utils/ShortenUrl'; import ShortenUrl from '../utils/ShortenUrl';
import { comparisonBBCode } from '../utils/BBCode'; import { comparisonBBCode } from '../utils/BBCode';
const browser = require('detect-browser'); const browser = require('detect-browser');
@@ -49,6 +42,7 @@ function sortBy(predicate) {
* Comparison Page * Comparison Page
*/ */
export default class ComparisonPage extends Page { export default class ComparisonPage extends Page {
/** /**
* Constructor * Constructor
* @param {Object} props React Component properties * @param {Object} props React Component properties
@@ -87,13 +81,7 @@ export default class ComparisonPage extends Page {
for (let shipId in allBuilds) { for (let shipId in allBuilds) {
for (let buildName in allBuilds[shipId]) { for (let buildName in allBuilds[shipId]) {
if (buildName && allBuilds[shipId][buildName]) { if (buildName && allBuilds[shipId][buildName]) {
builds.push( builds.push(this._createBuild(shipId, buildName, allBuilds[shipId][buildName]));
this._createBuild(
shipId,
buildName,
allBuilds[shipId][buildName]
)
);
} }
} }
} }
@@ -101,9 +89,7 @@ export default class ComparisonPage extends Page {
let comparisonData = Persist.getComparison(name); let comparisonData = Persist.getComparison(name);
if (comparisonData) { if (comparisonData) {
defaultFacets = comparisonData.facets; defaultFacets = comparisonData.facets;
comparisonData.builds.forEach(b => comparisonData.builds.forEach((b) => builds.push(this._createBuild(b.shipId, b.buildName)));
builds.push(this._createBuild(b.shipId, b.buildName))
);
saved = true; saved = true;
newName = name; newName = name;
} }
@@ -115,7 +101,7 @@ export default class ComparisonPage extends Page {
newName = name = comparisonData.n; newName = name = comparisonData.n;
predicate = comparisonData.p; predicate = comparisonData.p;
desc = comparisonData.d; desc = comparisonData.d;
comparisonData.b.forEach(build => { comparisonData.b.forEach((build) => {
builds.push(this._createBuild(build.s, build.n, build.c)); builds.push(this._createBuild(build.s, build.n, build.c));
if (!importObj[build.s]) { if (!importObj[build.s]) {
importObj[build.s] = {}; importObj[build.s] = {};
@@ -132,9 +118,9 @@ export default class ComparisonPage extends Page {
let selectedFacets = new Array(selectedLength); let selectedFacets = new Array(selectedLength);
for (let i = 0; i < ShipFacets.length; i++) { for (let i = 0; i < ShipFacets.length; i++) {
let facet = Object.assign({}, ShipFacets[i]); let facet = Object.assign({ }, ShipFacets[i]);
let defaultIndex = defaultFacets.indexOf(facet.i); let defaultIndex = defaultFacets.indexOf(facet.i);
if (defaultIndex == -1) { if(defaultIndex == -1) {
facets.push(facet); facets.push(facet);
} else { } else {
facet.active = true; facet.active = true;
@@ -169,18 +155,17 @@ export default class ComparisonPage extends Page {
_createBuild(id, name, code) { _createBuild(id, name, code) {
code = code ? code : Persist.getBuild(id, name); // Retrieve build code if not passed code = code ? code : Persist.getBuild(id, name); // Retrieve build code if not passed
if (!code) { if (!code) { // No build found
// No build found
return; return;
} }
let data = Ships[id]; // Get ship properties let data = Ships[id]; // Get ship properties
let b = new Ship(id, data.properties, data.slots); // Create a new Ship instance let b = new Ship(id, data.properties, data.slots); // Create a new Ship instance
b.buildFrom(code); // Populate components from code b.buildFrom(code); // Populate components from code
b.buildName = name; b.buildName = name;
b.applyDiscounts(Persist.getShipDiscount(), Persist.getModuleDiscount()); b.applyDiscounts(Persist.getShipDiscount(), Persist.getModuleDiscount());
return b; return b;
} };
/** /**
* Update state with the specified sort predicates * Update state with the specified sort predicates
@@ -199,18 +184,13 @@ export default class ComparisonPage extends Page {
} }
this.setState({ predicate, desc }); this.setState({ predicate, desc });
} };
/** /**
* Show selected builds modal * Show selected builds modal
*/ */
_selectBuilds() { _selectBuilds() {
this.context.showModal( this.context.showModal(<ModalCompare onSelect={this._buildsSelected} builds={this.state.builds} />);
<ModalCompare
onSelect={this._buildsSelected}
builds={this.state.builds}
/>
);
} }
/** /**
@@ -244,7 +224,7 @@ export default class ComparisonPage extends Page {
_facetDrag(e) { _facetDrag(e) {
this.nodeAfter = false; this.nodeAfter = false;
this.dragged = e.currentTarget; this.dragged = e.currentTarget;
let placeholder = (this.placeholder = document.createElement('li')); let placeholder = this.placeholder = document.createElement('li');
placeholder.style.width = Math.round(this.dragged.offsetWidth) + 'px'; placeholder.style.width = Math.round(this.dragged.offsetWidth) + 'px';
placeholder.className = 'facet-placeholder'; placeholder.className = 'facet-placeholder';
if (!browser || (browser.name !== 'edge' && browser.name !== 'ie')) { if (!browser || (browser.name !== 'edge' && browser.name !== 'ie')) {
@@ -282,7 +262,7 @@ export default class ComparisonPage extends Page {
_facetDragOver(e) { _facetDragOver(e) {
e.preventDefault(); e.preventDefault();
if (e.target.className == 'facet-placeholder') { if(e.target.className == 'facet-placeholder') {
return; return;
} else if (e.target != e.currentTarget) { } else if (e.target != e.currentTarget) {
this.over = e.target; this.over = e.target;
@@ -292,7 +272,7 @@ export default class ComparisonPage extends Page {
let parent = e.target.parentNode; let parent = e.target.parentNode;
if (parent == e.currentTarget) { if (parent == e.currentTarget) {
if (relX > width && this.dragged != e.target) { if(relX > width && this.dragged != e.target) {
this.nodeAfter = true; this.nodeAfter = true;
parent.insertBefore(this.placeholder, e.target.nextElementSibling); parent.insertBefore(this.placeholder, e.target.nextElementSibling);
} else { } else {
@@ -341,7 +321,7 @@ export default class ComparisonPage extends Page {
let { newName, builds, facets } = this.state; let { newName, builds, facets } = this.state;
let selectedFacets = []; let selectedFacets = [];
facets.forEach(f => { facets.forEach((f) => {
if (f.active) { if (f.active) {
selectedFacets.unshift(f.i); selectedFacets.unshift(f.i);
} }
@@ -368,20 +348,14 @@ export default class ComparisonPage extends Page {
let code = fromComparison(name, builds, selectedFacets, predicate, desc); let code = fromComparison(name, builds, selectedFacets, predicate, desc);
let loc = window.location; let loc = window.location;
return ( return loc.protocol + '//' + loc.host + '/comparison?code=' + encodeURIComponent(code);
loc.protocol +
'//' +
loc.host +
'/comparison?code=' +
encodeURIComponent(code)
);
} }
/** /**
* Generates the long permalink URL * Generates the long permalink URL
*/ */
_genPermalink() { _genPermalink() {
this.context.showModal(<ModalPermalink url={this._buildUrl()} />); this.context.showModal(<ModalPermalink url={this._buildUrl()}/>);
} }
/** /**
@@ -391,25 +365,18 @@ export default class ComparisonPage extends Page {
let { translate, formats } = this.context.language; let { translate, formats } = this.context.language;
let { facets, builds } = this.state; let { facets, builds } = this.state;
let generator = callback => { let generator = (callback) => {
let url = this._buildUrl(); let url = this._buildUrl();
ShortenUrl( ShortenUrl(url,
url, (shortenedUrl) => callback(comparisonBBCode(translate, formats, facets, builds, shortenedUrl)),
shortenedUrl => (error) => callback(comparisonBBCode(translate, formats, facets, builds, url))
callback(
comparisonBBCode(translate, formats, facets, builds, shortenedUrl)
),
error =>
callback(comparisonBBCode(translate, formats, facets, builds, url))
); );
}; };
this.context.showModal( this.context.showModal(<ModalExport
<ModalExport title={translate('forum') + ' BBCode'}
title={translate('forum') + ' BBCode'} generator={generator}
generator={generator} />);
/>
);
} }
/** /**
@@ -442,8 +409,7 @@ export default class ComparisonPage extends Page {
* @param {Object} nextContext Incoming/Next conext * @param {Object} nextContext Incoming/Next conext
*/ */
componentWillReceiveProps(nextProps, nextContext) { componentWillReceiveProps(nextProps, nextContext) {
if (this.context.route !== nextContext.route) { if (this.context.route !== nextContext.route) { // Only reinit state if the route has changed
// Only reinit state if the route has changed
this.setState(this._initState(nextContext)); this.setState(this._initState(nextContext));
} }
} }
@@ -453,10 +419,7 @@ export default class ComparisonPage extends Page {
*/ */
componentWillMount() { componentWillMount() {
this.resizeListener = this.context.onWindowResize(this._updateDimensions); this.resizeListener = this.context.onWindowResize(this._updateDimensions);
this.persistListener = Persist.addListener( this.persistListener = Persist.addListener('discounts', this._updateDiscounts);
'discounts',
this._updateDiscounts
);
} }
/** /**
@@ -481,132 +444,65 @@ export default class ComparisonPage extends Page {
renderPage() { renderPage() {
let translate = this.context.language.translate; let translate = this.context.language.translate;
let compareHeader; let compareHeader;
let { let { newName, name, saved, builds, facets, predicate, desc, chartWidth } = this.state;
newName,
name,
saved,
builds,
facets,
predicate,
desc,
chartWidth
} = this.state;
if (this.state.compareMode) { if (this.state.compareMode) {
compareHeader = ( compareHeader = <tr>
<tr> <td className='head'>{translate('comparison')}</td>
<td className="head">{translate('comparison')}</td> <td>
<td> <input value={newName} onChange={this._onNameChange} placeholder={translate('Enter Name')} maxLength='50' />
<input <button onClick={this._save} disabled={!newName || newName == 'all' || saved}>
value={newName} <FloppyDisk className='lg'/><span className='button-lbl'>{translate('save')}</span>
onChange={this._onNameChange} </button>
placeholder={translate('Enter Name')} <button onClick={this._delete} disabled={name == 'all' || !saved}><Bin className='lg warning'/></button>
maxLength="50" <button onClick={this._selectBuilds}>
/> <Rocket className='lg'/><span className='button-lbl'>{translate('builds')}</span>
<button </button>
onClick={this._save} <button className='r' onClick={this._genPermalink} disabled={builds.length == 0}>
disabled={!newName || newName == 'all' || saved} <LinkIcon className='lg'/><span className='button-lbl'>{translate('permalink')}</span>
> </button>
<FloppyDisk className="lg" /> <button className='r' onClick={this._genBBcode} disabled={builds.length == 0}>
<span className="button-lbl">{translate('save')}</span> <Embed className='lg'/><span className='button-lbl'>{translate('forum')}</span>
</button> </button>
<button onClick={this._delete} disabled={name == 'all' || !saved}> </td>
<Bin className="lg warning" /> </tr>;
</button>
<button onClick={this._selectBuilds}>
<Rocket className="lg" />
<span className="button-lbl">{translate('builds')}</span>
</button>
<button
className="r"
onClick={this._genPermalink}
disabled={builds.length == 0}
>
<LinkIcon className="lg" />
<span className="button-lbl">{translate('permalink')}</span>
</button>
<button
className="r"
onClick={this._genBBcode}
disabled={builds.length == 0}
>
<Embed className="lg" />
<span className="button-lbl">{translate('forum')}</span>
</button>
</td>
</tr>
);
} else { } else {
compareHeader = ( compareHeader = <tr>
<tr> <td className='head'>{translate('comparison')}</td>
<td className="head">{translate('comparison')}</td> <td>
<td> <h3>{name}</h3>
<h3>{name}</h3> <button className='r' onClick={this._import}><Download className='lg'/>{translate('import')}</button>
<button className="r" onClick={this._import}> </td>
<Download className="lg" /> </tr>;
{translate('import')}
</button>
</td>
</tr>
);
} }
return ( return (
<div <div className={'page'} style={{ fontSize: this.context.sizeRatio + 'em' }}>
className={'page'} <table id='comparison'>
style={{ fontSize: this.context.sizeRatio + 'em' }}
>
<table id="comparison">
<tbody> <tbody>
{compareHeader} {compareHeader}
<tr key="facets"> <tr key='facets'>
<td className="head">{translate('compare')}</td> <td className='head'>{translate('compare')}</td>
<td> <td>
<ul id="facet-container" onDragOver={this._facetDragOver}> <ul id='facet-container' onDragOver={this._facetDragOver}>
{facets.map((f, i) => ( {facets.map((f, i) =>
<li <li key={f.title} data-i={i} draggable='true' onDragStart={this._facetDrag} onDragEnd={this._facetDrop} className={cn('facet', { active: f.active })} onClick={this._toggleFacet.bind(this, f)}>
key={f.title}
data-i={i}
draggable="true"
onDragStart={this._facetDrag}
onDragEnd={this._facetDrop}
className={cn('facet', { active: f.active })}
onClick={this._toggleFacet.bind(this, f)}
>
{'↔ ' + translate(f.title)} {'↔ ' + translate(f.title)}
</li> </li>
))} )}
</ul> </ul>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
<ComparisonTable <ComparisonTable builds={builds} facets={facets} onSort={this._sortShips} predicate={predicate} desc={desc} />
builds={builds}
facets={facets}
onSort={this._sortShips}
predicate={predicate}
desc={desc}
/>
{!builds.length ? ( {!builds.length ?
<div className="chart" ref={node => (this.chartRef = node)}> <div className='chart' ref={node => this.chartRef = node}>{translate('PHRASE_NO_BUILDS')}</div> :
{translate('PHRASE_NO_BUILDS')} facets.filter((f) => f.active).map((f, i) =>
</div> <div key={f.title} className='chart' ref={ i == 0 ? node => this.chartRef = node : null}>
) : ( <h3 className='ptr' onClick={this._sortShips.bind(this, f.props[0])}>{translate(f.title)}</h3>
facets.filter(f => f.active).map((f, i) => (
<div
key={f.title}
className="chart"
ref={i == 0 ? node => (this.chartRef = node) : null}
>
<h3
className="ptr"
onClick={this._sortShips.bind(this, f.props[0])}
>
{translate(f.title)}
</h3>
<BarChart <BarChart
width={chartWidth} width={chartWidth}
data={builds} data={builds}
@@ -619,8 +515,8 @@ export default class ComparisonPage extends Page {
desc={desc} desc={desc}
/> />
</div> </div>
))
)} )}
</div> </div>
); );
} }

View File

@@ -5,6 +5,7 @@ import Page from './Page';
* 404 Page * 404 Page
*/ */
export default class NotFoundPage extends Page { export default class NotFoundPage extends Page {
/** /**
* Constructor * Constructor
* @param {Object} props React Component properties * @param {Object} props React Component properties
@@ -21,10 +22,6 @@ export default class NotFoundPage extends Page {
* @return {React.Component} The page contents * @return {React.Component} The page contents
*/ */
renderPage() { renderPage() {
return ( return <div className='page' style={{ marginTop: 30 }}>Page <small>{this.context.route.path}</small> Not Found</div>;
<div className="page" style={{ marginTop: 30 }}>
Page <small>{this.context.route.path}</small> Not Found
</div>
);
} }
} }

View File

@@ -53,6 +53,7 @@ function getTitle(shipName, buildName) {
* The Outfitting Page * The Outfitting Page
*/ */
export default class OutfittingPage extends Page { export default class OutfittingPage extends Page {
/** /**
* Constructor * Constructor
* @param {Object} props React Component properties * @param {Object} props React Component properties
@@ -84,38 +85,22 @@ export default class OutfittingPage extends Page {
let shipId = params.ship; let shipId = params.ship;
let code = params.code; let code = params.code;
let buildName = params.bn; let buildName = params.bn;
let data = Ships[shipId]; // Retrieve the basic ship properties, slots and defaults let data = Ships[shipId]; // Retrieve the basic ship properties, slots and defaults
let savedCode = Persist.getBuild(shipId, buildName); let savedCode = Persist.getBuild(shipId, buildName);
if (!data) { if (!data) {
return { error: { message: 'Ship not found: ' + shipId } }; return { error: { message: 'Ship not found: ' + shipId } };
} }
let ship = new Ship(shipId, data.properties, data.slots); // Create a new Ship instance let ship = new Ship(shipId, data.properties, data.slots); // Create a new Ship instance
if (code) { if (code) {
ship.buildFrom(code); // Populate modules from serialized 'code' URL param ship.buildFrom(code); // Populate modules from serialized 'code' URL param
} else { } else {
ship.buildWith(data.defaults); // Populate with default components ship.buildWith(data.defaults); // Populate with default components
} }
this._getTitle = getTitle.bind(this, data.properties.name); this._getTitle = getTitle.bind(this, data.properties.name);
// Obtain ship control from code // Obtain ship control from code
const { const { sys, eng, wep, mcSys, mcEng, mcWep, boost, fuel, cargo, opponent, opponentBuild, opponentSys, opponentEng, opponentWep, engagementRange } = this._obtainControlFromCode(ship, code);
sys,
eng,
wep,
mcSys,
mcEng,
mcWep,
boost,
fuel,
cargo,
opponent,
opponentBuild,
opponentSys,
opponentEng,
opponentWep,
engagementRange
} = this._obtainControlFromCode(ship, code);
return { return {
error: null, error: null,
title: this._getTitle(buildName), title: this._getTitle(buildName),
@@ -154,10 +139,7 @@ export default class OutfittingPage extends Page {
}; };
if (Persist.hasBuild(this.state.shipId, stateChanges.newBuildName)) { if (Persist.hasBuild(this.state.shipId, stateChanges.newBuildName)) {
stateChanges.savedCode = Persist.getBuild( stateChanges.savedCode = Persist.getBuild(this.state.shipId, stateChanges.newBuildName);
this.state.shipId,
stateChanges.newBuildName
);
} else { } else {
stateChanges.savedCode = null; stateChanges.savedCode = null;
} }
@@ -183,9 +165,7 @@ export default class OutfittingPage extends Page {
* @returns {string} the code for this ship * @returns {string} the code for this ship
*/ */
_fullCode(ship, fuel, cargo) { _fullCode(ship, fuel, cargo) {
return `${ship.toString()}.${LZString.compressToBase64( return `${ship.toString()}.${LZString.compressToBase64(this._controlCode(fuel, cargo))}`;
this._controlCode(fuel, cargo)
)}`;
} }
/** /**
@@ -205,11 +185,7 @@ export default class OutfittingPage extends Page {
let boost = false; let boost = false;
let fuel = ship.fuelCapacity; let fuel = ship.fuelCapacity;
let cargo = ship.cargoCapacity; let cargo = ship.cargoCapacity;
let opponent = new Ship( let opponent = new Ship('eagle', Ships['eagle'].properties, Ships['eagle'].slots).buildWith(Ships['eagle'].defaults);
'eagle',
Ships['eagle'].properties,
Ships['eagle'].slots
).buildWith(Ships['eagle'].defaults);
let opponentSys = 2; let opponentSys = 2;
let opponentEng = 2; let opponentEng = 2;
let opponentWep = 2; let opponentWep = 2;
@@ -221,25 +197,16 @@ export default class OutfittingPage extends Page {
const parts = code.split('.'); const parts = code.split('.');
if (parts.length >= 5) { if (parts.length >= 5) {
// We have control information in the code // We have control information in the code
const control = LZString.decompressFromBase64( const control = LZString.decompressFromBase64(Utils.fromUrlSafe(parts[4])).split('/');
Utils.fromUrlSafe(parts[4]) sys = parseFloat(control[0]) || sys;
).split('/'); eng = parseFloat(control[1]) || eng;
sys = parseFloat(control[0]); wep = parseFloat(control[2]) || wep;
eng = parseFloat(control[1]);
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;
if (control[6]) { if (control[6]) {
const shipId = control[6]; const shipId = control[6];
opponent = new Ship( opponent = new Ship(shipId, Ships[shipId].properties, Ships[shipId].slots);
shipId,
Ships[shipId].properties,
Ships[shipId].slots
);
if (control[7] && Persist.getBuild(shipId, control[7])) { if (control[7] && Persist.getBuild(shipId, control[7])) {
// Ship is a particular build // Ship is a particular build
const opponentCode = Persist.getBuild(shipId, control[7]); const opponentCode = Persist.getBuild(shipId, control[7]);
@@ -249,9 +216,7 @@ export default class OutfittingPage extends Page {
// Obtain opponent's sys/eng/wep pips from their code // Obtain opponent's sys/eng/wep pips from their code
const opponentParts = opponentCode.split('.'); const opponentParts = opponentCode.split('.');
if (opponentParts.length >= 5) { if (opponentParts.length >= 5) {
const opponentControl = LZString.decompressFromBase64( const opponentControl = LZString.decompressFromBase64(Utils.fromUrlSafe(opponentParts[4])).split('/');
Utils.fromUrlSafe(opponentParts[4])
).split('/');
opponentSys = parseFloat(opponentControl[0]) || opponentSys; opponentSys = parseFloat(opponentControl[0]) || opponentSys;
opponentEng = parseFloat(opponentControl[1]) || opponentEng; opponentEng = parseFloat(opponentControl[1]) || opponentEng;
opponentWep = parseFloat(opponentControl[2]) || opponentWep; opponentWep = parseFloat(opponentControl[2]) || opponentWep;
@@ -272,23 +237,7 @@ export default class OutfittingPage extends Page {
} }
} }
return { return { sys, eng, wep, mcSys, mcEng, mcWep, boost, fuel, cargo, opponent, opponentBuild, opponentSys, opponentEng, opponentWep, engagementRange };
sys,
eng,
wep,
mcSys,
mcEng,
mcWep,
boost,
fuel,
cargo,
opponent,
opponentBuild,
opponentSys,
opponentEng,
opponentWep,
engagementRange
};
} }
/** /**
@@ -303,9 +252,7 @@ export default class OutfittingPage extends Page {
* @param {number} mcWep WEP pips from multi-crew * @param {number} mcWep WEP pips from multi-crew
*/ */
_pipsUpdated(sys, eng, wep, mcSys, mcEng, mcWep) { _pipsUpdated(sys, eng, wep, mcSys, mcEng, mcWep) {
this.setState({ sys, eng, wep, mcSys, mcEng, mcWep }, () => this.setState({ sys, eng, wep, mcSys, mcEng, mcWep }, () => this._updateRouteOnControlChange());
this._updateRouteOnControlChange()
);
} }
/** /**
@@ -313,7 +260,7 @@ export default class OutfittingPage extends Page {
* @param {boolean} boost true if boosting * @param {boolean} boost true if boosting
*/ */
_boostUpdated(boost) { _boostUpdated(boost) {
this.setState({ boost }, () => this._updateRouteOnControlChange()); this.setState({ boost }, () => this._updateRouteOnControlChange());
} }
/** /**
@@ -321,7 +268,7 @@ export default class OutfittingPage extends Page {
* @param {number} fuel the amount of fuel, in T * @param {number} fuel the amount of fuel, in T
*/ */
_fuelUpdated(fuel) { _fuelUpdated(fuel) {
this.setState({ fuel }, () => this._updateRouteOnControlChange()); this.setState({ fuel }, () => this._updateRouteOnControlChange());
} }
/** /**
@@ -329,7 +276,7 @@ export default class OutfittingPage extends Page {
* @param {number} cargo the amount of cargo, in T * @param {number} cargo the amount of cargo, in T
*/ */
_cargoUpdated(cargo) { _cargoUpdated(cargo) {
this.setState({ cargo }, () => this._updateRouteOnControlChange()); this.setState({ cargo }, () => this._updateRouteOnControlChange());
} }
/** /**
@@ -337,9 +284,7 @@ export default class OutfittingPage extends Page {
* @param {number} engagementRange the engagement range, in m * @param {number} engagementRange the engagement range, in m
*/ */
_engagementRangeUpdated(engagementRange) { _engagementRangeUpdated(engagementRange) {
this.setState({ engagementRange }, () => this.setState({ engagementRange }, () => this._updateRouteOnControlChange());
this._updateRouteOnControlChange()
);
} }
/** /**
@@ -348,11 +293,7 @@ export default class OutfittingPage extends Page {
* @param {string} opponentBuild the name of the opponent's build * @param {string} opponentBuild the name of the opponent's build
*/ */
_opponentUpdated(opponent, opponentBuild) { _opponentUpdated(opponent, opponentBuild) {
const opponentShip = new Ship( const opponentShip = new Ship(opponent, Ships[opponent].properties, Ships[opponent].slots);
opponent,
Ships[opponent].properties,
Ships[opponent].slots
);
let opponentSys = this.state.opponentSys; let opponentSys = this.state.opponentSys;
let opponentEng = this.state.opponentEng; let opponentEng = this.state.opponentEng;
let opponentWep = this.state.opponentWep; let opponentWep = this.state.opponentWep;
@@ -360,13 +301,9 @@ export default class OutfittingPage extends Page {
// Ship is a particular build // Ship is a particular build
opponentShip.buildFrom(Persist.getBuild(opponent, opponentBuild)); opponentShip.buildFrom(Persist.getBuild(opponent, opponentBuild));
// Set pips for opponent // Set pips for opponent
const opponentParts = Persist.getBuild(opponent, opponentBuild).split( const opponentParts = Persist.getBuild(opponent, opponentBuild).split('.');
'.'
);
if (opponentParts.length >= 5) { if (opponentParts.length >= 5) {
const opponentControl = LZString.decompressFromBase64( const opponentControl = LZString.decompressFromBase64(Utils.fromUrlSafe(opponentParts[4])).split('/');
Utils.fromUrlSafe(opponentParts[4])
).split('/');
opponentSys = parseFloat(opponentControl[0]); opponentSys = parseFloat(opponentControl[0]);
opponentEng = parseFloat(opponentControl[1]); opponentEng = parseFloat(opponentControl[1]);
opponentWep = parseFloat(opponentControl[2]); opponentWep = parseFloat(opponentControl[2]);
@@ -379,16 +316,7 @@ export default class OutfittingPage extends Page {
opponentWep = 2; opponentWep = 2;
} }
this.setState( this.setState({ opponent: opponentShip, opponentBuild, opponentSys, opponentEng, opponentWep }, () => this._updateRouteOnControlChange());
{
opponent: opponentShip,
opponentBuild,
opponentSys,
opponentEng,
opponentWep
},
() => this._updateRouteOnControlChange()
);
} }
/** /**
@@ -398,22 +326,8 @@ export default class OutfittingPage extends Page {
* @returns {string} The control code * @returns {string} The control code
*/ */
_controlCode(fuel, cargo) { _controlCode(fuel, cargo) {
const { const { sys, eng, wep, mcSys, mcEng, mcWep, boost, opponent, opponentBuild, engagementRange } = this.state;
sys, const code = `${sys}/${eng}/${wep}/${boost ? 1 : 0}/${fuel || this.state.fuel}/${cargo || this.state.cargo}/${opponent.id}/${opponentBuild ? opponentBuild : ''}/${engagementRange}/${mcSys}/${mcEng}/${mcWep}`;
eng,
wep,
mcSys,
mcEng,
mcWep,
boost,
opponent,
opponentBuild,
engagementRange
} = this.state;
const code = `${sys}/${eng}/${wep}/${boost ? 1 : 0}/${fuel ||
this.state.fuel}/${cargo || this.state.cargo}/${opponent.id}/${
opponentBuild ? opponentBuild : ''
}/${engagementRange}/${mcSys}/${mcEng}/${mcWep}`;
return code; return code;
} }
@@ -424,44 +338,27 @@ export default class OutfittingPage extends Page {
const { ship, buildName, newBuildName, shipId } = this.state; const { ship, buildName, newBuildName, shipId } = this.state;
// If this is a stock ship the code won't be set, so ensure that we have it // If this is a stock ship the code won't be set, so ensure that we have it
const code = this.state.code || ship.toString(); const code = this.state.code || ship.toString();
Persist.saveBuild(shipId, newBuildName, code); Persist.saveBuild(shipId, newBuildName, code);
this._updateRoute(shipId, newBuildName, code); this._updateRoute(shipId, newBuildName, code);
let opponent, opponentBuild, opponentSys, opponentEng, opponentWep; let opponent, opponentBuild, opponentSys, opponentEng, opponentWep;
if ( if (shipId === this.state.opponent.id && buildName === this.state.opponentBuild) {
shipId === this.state.opponent.id &&
buildName === this.state.opponentBuild
) {
// This is a save of our current opponent build; update it // This is a save of our current opponent build; update it
opponentBuild = newBuildName; opponentBuild = newBuildName;
opponent = new Ship( opponent = new Ship(shipId, Ships[shipId].properties, Ships[shipId].slots).buildFrom(code);
shipId,
Ships[shipId].properties,
Ships[shipId].slots
).buildFrom(code);
opponentSys = this.state.sys; opponentSys = this.state.sys;
opponentEng = this.state.eng; opponentEng = this.state.eng;
opponentWep = this.state.wep; opponentWep = this.state.wep;
} else { } else {
opponentBuild = this.state.opponentBuild; opponentBuild = this.state.opponentBuild;
opponent = this.state.opponent; opponent = this.state.opponent;
opponentSys = this.state.opponentSys; opponentSys = this.state.opponentSys;
opponentEng = this.state.opponentEng; opponentEng = this.state.opponentEng;
opponentWep = this.state.opponentWep; opponentWep = this.state.opponentWep;
} }
this.setState({ this.setState({ buildName: newBuildName, code, savedCode: code, opponent, opponentBuild, opponentSys, opponentEng, opponentWep, title: this._getTitle(newBuildName) });
buildName: newBuildName,
code,
savedCode: code,
opponent,
opponentBuild,
opponentSys,
opponentEng,
opponentWep,
title: this._getTitle(newBuildName)
});
} }
/** /**
@@ -473,12 +370,7 @@ export default class OutfittingPage extends Page {
Persist.deleteBuild(shipId, buildName); Persist.deleteBuild(shipId, buildName);
Persist.saveBuild(shipId, newBuildName, code); Persist.saveBuild(shipId, newBuildName, code);
this._updateRoute(shipId, newBuildName, code); this._updateRoute(shipId, newBuildName, code);
this.setState({ this.setState({ buildName: newBuildName, code, savedCode: code, opponentBuild: newBuildName });
buildName: newBuildName,
code,
savedCode: code,
opponentBuild: newBuildName
});
} }
} }
@@ -498,7 +390,9 @@ export default class OutfittingPage extends Page {
ship.buildWith(Ships[shipId].defaults); ship.buildWith(Ships[shipId].defaults);
// Reset controls // Reset controls
const code = ship.toString(); const code = ship.toString();
const { const { sys, eng, wep, mcSys, mcEng, mcWep, boost, fuel, cargo, opponent, opponentBuild, engagementRange } = this._obtainControlFromCode(ship, code);
// Update state, and refresh the ship
this.setState({
sys, sys,
eng, eng,
wep, wep,
@@ -511,25 +405,7 @@ export default class OutfittingPage extends Page {
opponent, opponent,
opponentBuild, opponentBuild,
engagementRange engagementRange
} = this._obtainControlFromCode(ship, code); }, () => this._updateRoute(shipId, buildName, code));
// Update state, and refresh the ship
this.setState(
{
sys,
eng,
wep,
mcSys,
mcEng,
mcWep,
boost,
fuel,
cargo,
opponent,
opponentBuild,
engagementRange
},
() => this._updateRoute(shipId, buildName, code)
);
} }
/** /**
@@ -540,10 +416,7 @@ export default class OutfittingPage extends Page {
Persist.deleteBuild(shipId, buildName); Persist.deleteBuild(shipId, buildName);
let opponentBuild; let opponentBuild;
if ( if (shipId === this.state.opponent.id && buildName === this.state.opponentBuild) {
shipId === this.state.opponent.id &&
buildName === this.state.opponentBuild
) {
// Our current opponent has been deleted; revert to stock // Our current opponent has been deleted; revert to stock
opponentBuild = null; opponentBuild = null;
} else { } else {
@@ -560,13 +433,11 @@ export default class OutfittingPage extends Page {
_exportBuild() { _exportBuild() {
let translate = this.context.language.translate; let translate = this.context.language.translate;
let { buildName, ship } = this.state; let { buildName, ship } = this.state;
this.context.showModal( this.context.showModal(<ModalExport
<ModalExport title={(buildName || ship.name) + ' ' + translate('export')}
title={(buildName || ship.name) + ' ' + translate('export')} description={translate('PHRASE_EXPORT_DESC')}
description={translate('PHRASE_EXPORT_DESC')} data={toDetailedBuild(buildName, ship, ship.toString())}
data={toDetailedBuild(buildName, ship, ship.toString())} />);
/>
);
} }
/** /**
@@ -579,7 +450,9 @@ export default class OutfittingPage extends Page {
this.state.ship.buildFrom(code); this.state.ship.buildFrom(code);
// Obtain controls from the code // Obtain controls from the code
const { const { sys, eng, wep, mcSys, mcEng, mcWep, boost, fuel, cargo, opponent, opponentBuild, engagementRange } = this._obtainControlFromCode(ship, code);
// Update state, and refresh the route when complete
this.setState({
sys, sys,
eng, eng,
wep, wep,
@@ -592,25 +465,7 @@ export default class OutfittingPage extends Page {
opponent, opponent,
opponentBuild, opponentBuild,
engagementRange engagementRange
} = this._obtainControlFromCode(ship, code); }, () => this._updateRoute(shipId, buildName, code));
// Update state, and refresh the route when complete
this.setState(
{
sys,
eng,
wep,
mcSys,
mcEng,
mcWep,
boost,
fuel,
cargo,
opponent,
opponentBuild,
engagementRange
},
() => this._updateRoute(shipId, buildName, code)
);
} }
/** /**
@@ -626,14 +481,8 @@ export default class OutfittingPage extends Page {
} }
const code = this._fullCode(ship, fuel, cargo); const code = this._fullCode(ship, fuel, cargo);
// Only update the state if this really has been updated // Only update the state if this really has been updated
if ( if (this.state.code != code || this.state.cargo != cargo || this.state.fuel != fuel) {
this.state.code != code || this.setState({ code, cargo, fuel }, () => this._updateRoute(shipId, buildName, code));
this.state.cargo != cargo ||
this.state.fuel != fuel
) {
this.setState({ code, cargo, fuel }, () =>
this._updateRoute(shipId, buildName, code)
);
} }
} }
@@ -653,8 +502,7 @@ export default class OutfittingPage extends Page {
* @param {Object} nextContext Incoming/Next conext * @param {Object} nextContext Incoming/Next conext
*/ */
componentWillReceiveProps(nextProps, nextContext) { componentWillReceiveProps(nextProps, nextContext) {
if (this.context.route !== nextContext.route) { if (this.context.route !== nextContext.route) { // Only reinit state if the route has changed
// Only reinit state if the route has changed
this.setState(this._initState(nextProps, nextContext)); this.setState(this._initState(nextProps, nextContext));
} }
} }
@@ -677,7 +525,7 @@ export default class OutfittingPage extends Page {
* Generates the short URL * Generates the short URL
*/ */
_genShortlink() { _genShortlink() {
this.context.showModal(<ModalPermalink url={window.location.href} />); this.context.showModal(<ModalPermalink url={window.location.href}/>);
} }
/** /**
@@ -694,7 +542,7 @@ export default class OutfittingPage extends Page {
data.ShipName = ship.id; data.ShipName = ship.id;
data.Ship = ship.id; data.Ship = ship.id;
console.log(data); console.log(data);
this.context.showModal(<ModalOrbis ship={data} />); this.context.showModal(<ModalOrbis ship={data}/>);
} }
/** /**
@@ -705,23 +553,17 @@ export default class OutfittingPage extends Page {
const shipId = Ships[ship.id].eddbID; const shipId = Ships[ship.id].eddbID;
// Provide unique list of non-PP module EDDB IDs // Provide unique list of non-PP module EDDB IDs
const modIds = ship.internal const modIds = ship.internal.concat(ship.bulkheads, ship.standard, ship.hardpoints).filter(slot => slot !== null && slot.m !== null && !slot.m.pp).map(slot => slot.m.eddbID).filter((v, i, a) => a.indexOf(v) === i);
.concat(ship.bulkheads, ship.standard, ship.hardpoints)
.filter(slot => slot !== null && slot.m !== null && !slot.m.pp)
.map(slot => slot.m.eddbID)
.filter((v, i, a) => a.indexOf(v) === i);
// Open up the relevant URL // Open up the relevant URL
window.open( window.open('https://eddb.io/station?s=' + shipId + '&m=' + modIds.join(','));
'https://eddb.io/station?s=' + shipId + '&m=' + modIds.join(',')
);
} }
/** /**
* Generates the shopping list * Generates the shopping list
*/ */
_genShoppingList() { _genShoppingList() {
this.context.showModal(<ModalShoppingList ship={this.state.ship} />); this.context.showModal(<ModalShoppingList ship={this.state.ship}/>);
} }
/** /**
@@ -731,9 +573,8 @@ export default class OutfittingPage extends Page {
_keyDown(e) { _keyDown(e) {
// .keyCode will eventually be replaced with .key // .keyCode will eventually be replaced with .key
switch (e.keyCode) { switch (e.keyCode) {
case 69: // 'e' case 69: // 'e'
if (e.ctrlKey || e.metaKey) { if (e.ctrlKey || e.metaKey) { // CTRL/CMD + e
// CTRL/CMD + e
e.preventDefault(); e.preventDefault();
this._exportBuild(); this._exportBuild();
} }
@@ -749,28 +590,7 @@ export default class OutfittingPage extends Page {
let state = this.state, let state = this.state,
{ language, termtip, tooltip, sizeRatio, onWindowResize } = this.context, { language, termtip, tooltip, sizeRatio, onWindowResize } = this.context,
{ translate, units, formats } = language, { translate, units, formats } = language,
{ { ship, code, savedCode, buildName, newBuildName, sys, eng, wep, mcSys, mcEng, mcWep, boost, fuel, cargo, opponent, opponentBuild, opponentSys, opponentEng, opponentWep, engagementRange } = state,
ship,
code,
savedCode,
buildName,
newBuildName,
sys,
eng,
wep,
mcSys,
mcEng,
mcWep,
boost,
fuel,
cargo,
opponent,
opponentBuild,
opponentSys,
opponentEng,
opponentWep,
engagementRange
} = state,
hide = tooltip.bind(null, null), hide = tooltip.bind(null, null),
menu = this.props.currentMenu, menu = this.props.currentMenu,
shipUpdated = this._shipUpdated, shipUpdated = this._shipUpdated,
@@ -788,15 +608,11 @@ export default class OutfittingPage extends Page {
const _pStr = `${ship.getPowerEnabledString()}${ship.getPowerPrioritiesString()}`; const _pStr = `${ship.getPowerEnabledString()}${ship.getPowerPrioritiesString()}`;
const _mStr = ship.getModificationsString(); const _mStr = ship.getModificationsString();
const standardSlotMarker = `${ship.name}${_sStr}${_pStr}${_mStr}${ const standardSlotMarker = `${ship.name}${_sStr}${_pStr}${_mStr}${ship.ladenMass}${cargo}${fuel}`;
ship.ladenMass
}${cargo}${fuel}`;
const internalSlotMarker = `${ship.name}${_iStr}${_pStr}${_mStr}`; const internalSlotMarker = `${ship.name}${_iStr}${_pStr}${_mStr}`;
const hardpointsSlotMarker = `${ship.name}${_hStr}${_pStr}${_mStr}`; const hardpointsSlotMarker = `${ship.name}${_hStr}${_pStr}${_mStr}`;
const boostMarker = `${ship.canBoost(cargo, fuel)}`; const boostMarker = `${ship.canBoost(cargo, fuel)}`;
const shipSummaryMarker = `${ const shipSummaryMarker = `${ship.name}${_sStr}${_iStr}${_hStr}${_pStr}${_mStr}${ship.ladenMass}${cargo}${fuel}`;
ship.name
}${_sStr}${_iStr}${_hStr}${_pStr}${_mStr}${ship.ladenMass}${cargo}${fuel}`;
const requirements = Ships[ship.id].requirements; const requirements = Ships[ship.id].requirements;
let requirementElements = []; let requirementElements = [];
@@ -808,275 +624,94 @@ export default class OutfittingPage extends Page {
*/ */
function renderRequirement(className, textKey, tooltipTextKey) { function renderRequirement(className, textKey, tooltipTextKey) {
if (textKey.startsWith('empire') || textKey.startsWith('federation')) { if (textKey.startsWith('empire') || textKey.startsWith('federation')) {
requirementElements.push( requirementElements.push(<div key={textKey} className={className} onMouseEnter={termtip.bind(null, tooltipTextKey)} onMouseLeave={hide}><a href={textKey.startsWith('empire') ? 'http://elite-dangerous.wikia.com/wiki/Empire/Ranks' : 'http://elite-dangerous.wikia.com/wiki/Federation/Ranks'} target="_blank" rel="noopener">{translate(textKey)}</a></div>);
<div
key={textKey}
className={className}
onMouseEnter={termtip.bind(null, tooltipTextKey)}
onMouseLeave={hide}
>
<a
href={
textKey.startsWith('empire') ?
'http://elite-dangerous.wikia.com/wiki/Empire/Ranks' :
'http://elite-dangerous.wikia.com/wiki/Federation/Ranks'
}
target="_blank"
rel="noopener"
>
{translate(textKey)}
</a>
</div>
);
} else { } else {
requirementElements.push( requirementElements.push(<div key={textKey} className={className} onMouseEnter={termtip.bind(null, tooltipTextKey)} onMouseLeave={hide}>{translate(textKey)}</div>);
<div
key={textKey}
className={className}
onMouseEnter={termtip.bind(null, tooltipTextKey)}
onMouseLeave={hide}
>
{translate(textKey)}
</div>
);
} }
} }
if (requirements) { if (requirements) {
requirements.federationRank && requirements.federationRank && renderRequirement('federation', 'federation rank ' + requirements.federationRank, 'federation rank required');
renderRequirement( requirements.empireRank && renderRequirement('empire', 'empire rank ' + requirements.empireRank, 'empire rank required');
'federation', requirements.horizons && renderRequirement('horizons', 'horizons', 'horizons required');
'federation rank ' + requirements.federationRank, requirements.horizonsEarlyAdoption && renderRequirement('horizons', 'horizons early adoption', 'horizons early adoption required');
'federation rank required'
);
requirements.empireRank &&
renderRequirement(
'empire',
'empire rank ' + requirements.empireRank,
'empire rank required'
);
requirements.horizons &&
renderRequirement('horizons', 'horizons', 'horizons required');
requirements.horizonsEarlyAdoption &&
renderRequirement(
'horizons',
'horizons early adoption',
'horizons early adoption required'
);
} }
return ( return (
<div <div id='outfit' className={'page'} style={{ fontSize: (sizeRatio * 0.9) + 'em' }}>
id="outfit" <div id='overview'>
className={'page'}
style={{ fontSize: sizeRatio * 0.9 + 'em' }}
>
<div id="overview">
<h1>{ship.name}</h1> <h1>{ship.name}</h1>
<div id="requirements">{requirementElements}</div> <div id='requirements'>{requirementElements}</div>
<div id="build"> <div id='build'>
<input <input value={newBuildName || ''} onChange={this._buildNameChange} placeholder={translate('Enter Name')} maxLength={50} />
value={newBuildName || ''} <button onClick={canSave && this._saveBuild} disabled={!canSave} onMouseOver={termtip.bind(null, 'save')} onMouseOut={hide}>
onChange={this._buildNameChange} <FloppyDisk className='lg' />
placeholder={translate('Enter Name')}
maxLength={50}
/>
<button
onClick={canSave && this._saveBuild}
disabled={!canSave}
onMouseOver={termtip.bind(null, 'save')}
onMouseOut={hide}
>
<FloppyDisk className="lg" />
</button> </button>
<button <button onClick={canRename && this._renameBuild} disabled={!canRename} onMouseOver={termtip.bind(null, 'rename')} onMouseOut={hide}>
onClick={canRename && this._renameBuild} <span style={{ textTransform: 'none', fontSize: '1.8em' }}>a|</span>
disabled={!canRename}
onMouseOver={termtip.bind(null, 'rename')}
onMouseOut={hide}
>
<span style={{ textTransform: 'none', fontSize: '1.8em' }}>
a|
</span>
</button> </button>
<button <button onClick={canReload && this._reloadBuild} disabled={!canReload} onMouseOver={termtip.bind(null, 'reload')} onMouseOut={hide}>
onClick={canReload && this._reloadBuild} <Reload className='lg'/>
disabled={!canReload}
onMouseOver={termtip.bind(null, 'reload')}
onMouseOut={hide}
>
<Reload className="lg" />
</button> </button>
<button <button className={'danger'} onClick={savedCode && this._deleteBuild} disabled={!savedCode} onMouseOver={termtip.bind(null, 'delete')} onMouseOut={hide}>
className={'danger'} <Bin className='lg'/>
onClick={savedCode && this._deleteBuild}
disabled={!savedCode}
onMouseOver={termtip.bind(null, 'delete')}
onMouseOut={hide}
>
<Bin className="lg" />
</button> </button>
<button <button onClick={code && this._resetBuild} disabled={!code} onMouseOver={termtip.bind(null, 'reset')} onMouseOut={hide}>
onClick={code && this._resetBuild} <Switch className='lg'/>
disabled={!code}
onMouseOver={termtip.bind(null, 'reset')}
onMouseOut={hide}
>
<Switch className="lg" />
</button> </button>
<button <button onClick={buildName && this._exportBuild} disabled={!buildName} onMouseOver={termtip.bind(null, 'export')} onMouseOut={hide}>
onClick={buildName && this._exportBuild} <Download className='lg'/>
disabled={!buildName}
onMouseOver={termtip.bind(null, 'export')}
onMouseOut={hide}
>
<Download className="lg" />
</button> </button>
<button <button onClick={this._eddbShoppingList} onMouseOver={termtip.bind(null, 'PHRASE_SHOPPING_LIST')} onMouseOut={hide}>
onClick={this._eddbShoppingList} <ShoppingIcon className='lg' />
onMouseOver={termtip.bind(null, 'PHRASE_SHOPPING_LIST')}
onMouseOut={hide}
>
<ShoppingIcon className="lg" />
</button> </button>
<button <button onClick={this._genShortlink} onMouseOver={termtip.bind(null, 'shortlink')} onMouseOut={hide}>
onClick={this._genShortlink} <LinkIcon className='lg' />
onMouseOver={termtip.bind(null, 'shortlink')}
onMouseOut={hide}
>
<LinkIcon className="lg" />
</button> </button>
<button <button onClick={this._genOrbis} onMouseOver={termtip.bind(null, 'PHASE_UPLOAD_ORBIS')} onMouseOut={hide}>
onClick={this._genOrbis} <OrbisIcon className='lg' />
onMouseOver={termtip.bind(null, 'PHASE_UPLOAD_ORBIS')}
onMouseOut={hide}
>
<OrbisIcon className="lg" />
</button> </button>
<button <button onClick={this._genShoppingList} onMouseOver={termtip.bind(null, 'PHRASE_SHOPPING_MATS')} onMouseOut={hide}>
onClick={this._genShoppingList} <MatIcon className='lg' />
onMouseOver={termtip.bind(null, 'PHRASE_SHOPPING_MATS')}
onMouseOut={hide}
>
<MatIcon className="lg" />
</button> </button>
</div> </div>
</div> </div>
{/* Main tables */} {/* Main tables */}
<ShipSummaryTable <ShipSummaryTable ship={ship} fuel={fuel} cargo={cargo} marker={shipSummaryMarker} pips={{ sys: this.state.sys, wep: this.state.wep, eng: this.state.eng }} />
ship={ship} <StandardSlotSection ship={ship} fuel={fuel} cargo={cargo} code={standardSlotMarker} onChange={shipUpdated} onCargoChange={this._cargoUpdated} onFuelChange={this._fuelUpdated} currentMenu={menu} sectionMenuRefs={this._sectionMenuRefs}/>
fuel={fuel} <InternalSlotSection ship={ship} code={internalSlotMarker} onChange={shipUpdated} onCargoChange={this._cargoUpdated} onFuelChange={this._fuelUpdated} currentMenu={menu} sectionMenuRefs={this._sectionMenuRefs}/>
cargo={cargo} <HardpointSlotSection ship={ship} code={hardpointsSlotMarker} onChange={shipUpdated} onCargoChange={this._cargoUpdated} onFuelChange={this._fuelUpdated} currentMenu={menu} sectionMenuRefs={this._sectionMenuRefs}/>
marker={shipSummaryMarker} <UtilitySlotSection ship={ship} code={hardpointsSlotMarker} onChange={shipUpdated} onCargoChange={this._cargoUpdated} onFuelChange={this._fuelUpdated} currentMenu={menu} sectionMenuRefs={this._sectionMenuRefs}/>
pips={{
sys: this.state.sys,
wep: this.state.wep,
eng: this.state.eng
}}
/>
<StandardSlotSection
ship={ship}
fuel={fuel}
cargo={cargo}
code={standardSlotMarker}
onChange={shipUpdated}
onCargoChange={this._cargoUpdated}
onFuelChange={this._fuelUpdated}
currentMenu={menu}
sectionMenuRefs={this._sectionMenuRefs}
/>
<InternalSlotSection
ship={ship}
code={internalSlotMarker}
onChange={shipUpdated}
onCargoChange={this._cargoUpdated}
onFuelChange={this._fuelUpdated}
currentMenu={menu}
sectionMenuRefs={this._sectionMenuRefs}
/>
<HardpointSlotSection
ship={ship}
code={hardpointsSlotMarker}
onChange={shipUpdated}
onCargoChange={this._cargoUpdated}
onFuelChange={this._fuelUpdated}
currentMenu={menu}
sectionMenuRefs={this._sectionMenuRefs}
/>
<UtilitySlotSection
ship={ship}
code={hardpointsSlotMarker}
onChange={shipUpdated}
onCargoChange={this._cargoUpdated}
onFuelChange={this._fuelUpdated}
currentMenu={menu}
sectionMenuRefs={this._sectionMenuRefs}
/>
{/* Control of ship and opponent */} {/* Control of ship and opponent */}
<div className="group quarter"> <div className='group quarter'>
<div className="group half"> <div className='group half'>
<h2 style={{ verticalAlign: 'middle', textAlign: 'left' }}> <h2 style={{ verticalAlign: 'middle', textAlign: 'left' }}>{translate('ship control')}</h2>
{translate('ship control')}
</h2>
</div> </div>
<div className="group half"> <div className='group half'>
<Boost <Boost marker={boostMarker} ship={ship} boost={boost} onChange={this._boostUpdated} />
marker={boostMarker}
ship={ship}
boost={boost}
onChange={this._boostUpdated}
/>
</div> </div>
</div> </div>
<div className="group quarter"> <div className='group quarter'>
<Pips <Pips sys={sys} eng={eng} wep={wep} mcSys={mcSys} mcEng={mcEng} mcWep={mcWep} onChange={this._pipsUpdated} />
sys={sys}
eng={eng}
wep={wep}
mcSys={mcSys}
mcEng={mcEng}
mcWep={mcWep}
onChange={this._pipsUpdated}
/>
</div> </div>
<div className="group quarter"> <div className='group quarter'>
<Fuel <Fuel fuelCapacity={ship.fuelCapacity} fuel={fuel} onChange={this._fuelUpdated}/>
fuelCapacity={ship.fuelCapacity}
fuel={fuel}
onChange={this._fuelUpdated}
/>
</div> </div>
<div className="group quarter"> <div className='group quarter'>
{ship.cargoCapacity > 0 ? ( { ship.cargoCapacity > 0 ? <Cargo cargoCapacity={ship.cargoCapacity} cargo={cargo} onChange={this._cargoUpdated}/> : null }
<Cargo
cargoCapacity={ship.cargoCapacity}
cargo={cargo}
onChange={this._cargoUpdated}
/>
) : null}
</div> </div>
<div className="group half"> <div className='group half'>
<div className="group quarter"> <div className='group quarter'>
<h2 style={{ verticalAlign: 'middle', textAlign: 'left' }}> <h2 style={{ verticalAlign: 'middle', textAlign: 'left' }}>{translate('opponent')}</h2>
{translate('opponent')}
</h2>
</div> </div>
<div className="group threequarters"> <div className='group threequarters'>
<ShipPicker <ShipPicker ship={opponent.id} build={opponentBuild} onChange={this._opponentUpdated}/>
ship={opponent.id}
build={opponentBuild}
onChange={this._opponentUpdated}
/>
</div> </div>
</div> </div>
<div className="group half"> <div className='group half'>
<EngagementRange <EngagementRange ship={ship} engagementRange={engagementRange} onChange={this._engagementRangeUpdated}/>
ship={ship}
engagementRange={engagementRange}
onChange={this._engagementRangeUpdated}
/>
</div> </div>
{/* Tabbed subpages */} {/* Tabbed subpages */}

View File

@@ -70,13 +70,6 @@ export default class Page extends React.Component {
document.title = this.state.title || 'Coriolis'; document.title = this.state.title || 'Coriolis';
} }
/**
* Update the window title upon mount
*/
componentDidMount() {
document.title = this.state.title || 'Coriolis';
}
/** /**
* Updates the title upon change * Updates the title upon change
* @param {Object} newProps Incoming properties * @param {Object} newProps Incoming properties

View File

@@ -22,11 +22,9 @@ function countHp(slot) {
*/ */
function countInt(slot) { function countInt(slot) {
let crEligible = !slot.eligible || slot.eligible.cr; let crEligible = !slot.eligible || slot.eligible.cr;
this.int[slot.maxClass - 1]++; // Subtract 1 since there is no Class 0 Internal compartment this.int[slot.maxClass - 1]++; // Subtract 1 since there is no Class 0 Internal compartment
this.intCount++; this.intCount++;
this.maxCargo += crEligible ? this.maxCargo += crEligible ? ModuleUtils.findInternal('cr', slot.maxClass, 'E').cargo : 0;
ModuleUtils.findInternal('cr', slot.maxClass, 'E').cargo :
0;
// if no eligiblity, then assume pce // if no eligiblity, then assume pce
let passSlotType = null; let passSlotType = null;
@@ -44,9 +42,7 @@ function countInt(slot) {
passSlotType = 'pcq'; passSlotType = 'pcq';
passSlotRating = 'B'; passSlotRating = 'B';
} }
let passengerBay = passSlotType ? let passengerBay = passSlotType ? ModuleUtils.findMaxInternal(passSlotType, slot.maxClass, passSlotRating) : null;
ModuleUtils.findMaxInternal(passSlotType, slot.maxClass, passSlotRating) :
null;
this.maxPassengers += passengerBay ? passengerBay.passengers : 0; this.maxPassengers += passengerBay ? passengerBay.passengers : 0;
} }
@@ -61,27 +57,23 @@ function shipSummary(shipId, shipData) {
id: shipId, id: shipId,
hpCount: 0, hpCount: 0,
intCount: 0, intCount: 0,
beta: shipData.beta,
maxCargo: 0, maxCargo: 0,
maxPassengers: 0, maxPassengers: 0,
hp: [0, 0, 0, 0, 0], // Utility, Small, Medium, Large, Huge hp: [0, 0, 0, 0, 0], // Utility, Small, Medium, Large, Huge
int: [0, 0, 0, 0, 0, 0, 0, 0], // Sizes 1 - 8 int: [0, 0, 0, 0, 0, 0, 0, 0], // Sizes 1 - 8
standard: shipData.slots.standard, standard: shipData.slots.standard,
agility: agility: shipData.properties.pitch + shipData.properties.yaw + shipData.properties.roll
shipData.properties.pitch +
shipData.properties.yaw +
shipData.properties.roll
}; };
Object.assign(summary, shipData.properties); Object.assign(summary, shipData.properties);
let ship = new Ship(shipId, shipData.properties, shipData.slots); let ship = new Ship(shipId, shipData.properties, shipData.slots);
// Build Ship // Build Ship
ship.buildWith(shipData.defaults); // Populate with stock/default components ship.buildWith(shipData.defaults); // Populate with stock/default components
ship.hardpoints.forEach(countHp.bind(summary)); // Count Hardpoints by class ship.hardpoints.forEach(countHp.bind(summary)); // Count Hardpoints by class
ship.internal.forEach(countInt.bind(summary)); // Count Internal Compartments by class ship.internal.forEach(countInt.bind(summary)); // Count Internal Compartments by class
summary.retailCost = ship.totalCost; // Record Stock/Default/retail cost summary.retailCost = ship.totalCost; // Record Stock/Default/retail cost
ship.optimizeMass({ pd: '1D' }); // Optimize Mass with 1D PD for maximum possible jump range ship.optimizeMass({ pd: '1D' }); // Optimize Mass with 1D PD for maximum possible jump range
summary.maxJumpRange = ship.unladenRange; // Record Jump Range summary.maxJumpRange = ship.unladenRange; // Record Jump Range
// Best thrusters // Best thrusters
let th; let th;
@@ -105,6 +97,7 @@ function shipSummary(shipId, shipData) {
* The Shipyard summary page * The Shipyard summary page
*/ */
export default class ShipyardPage extends Page { export default class ShipyardPage extends Page {
static cachedShipSummaries = null; static cachedShipSummaries = null;
/** /**
@@ -152,15 +145,12 @@ export default class ShipyardPage extends Page {
shipPredicateIndex = undefined; shipPredicateIndex = undefined;
} }
if ( if (this.state.shipPredicate == shipPredicate && this.state.shipPredicateIndex == shipPredicateIndex) {
this.state.shipPredicate == shipPredicate &&
this.state.shipPredicateIndex == shipPredicateIndex
) {
shipDesc = !shipDesc; shipDesc = !shipDesc;
} }
this.setState({ shipPredicate, shipDesc, shipPredicateIndex }); this.setState({ shipPredicate, shipDesc, shipPredicateIndex });
} };
/** /**
* Generate the table row summary for the ship * Generate the table row summary for the ship
@@ -169,59 +159,56 @@ 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) { _shipRowElement(s, translate, u, fInt, fRound, highlight) {
let noTouch = this.context.noTouch; let noTouch = this.context.noTouch;
return ( return <tr
<tr
key={s.id} key={s.id}
style={{ height: '1.5em' }} style={{ height: '1.5em' }}
className={cn({ className={cn({ highlighted: noTouch && this.state.shipId === s.id, alt: highlight })}
highlighted: noTouch && this.state.shipId === s.id,
})}
onMouseEnter={noTouch && this._highlightShip.bind(this, s.id)} onMouseEnter={noTouch && this._highlightShip.bind(this, 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>
<td className="ri cap">{translate(SizeMap[s.class])}</td> <td className='ri cap'>{translate(SizeMap[s.class])}</td>
<td className="ri">{fInt(s.crew)}</td> <td className='ri'>{fInt(s.crew)}</td>
<td className="ri">{s.masslock}</td> <td className='ri'>{s.masslock}</td>
<td className="ri">{fInt(s.agility)}</td> <td className='ri'>{fInt(s.agility)}</td>
<td className="ri">{fInt(s.hardness)}</td> <td className='ri'>{fInt(s.hardness)}</td>
<td className="ri">{fInt(s.hullMass)}</td> <td className='ri'>{fInt(s.hullMass)}</td>
<td className="ri">{fInt(s.speed)}</td> <td className='ri'>{fInt(s.speed)}</td>
<td className="ri">{fInt(s.boost)}</td> <td className='ri'>{fInt(s.boost)}</td>
<td className="ri">{fInt(s.baseArmour)}</td> <td className='ri'>{fInt(s.baseArmour)}</td>
<td className="ri">{fInt(s.baseShieldStrength)}</td> <td className='ri'>{fInt(s.baseShieldStrength)}</td>
<td className="ri">{fInt(s.topSpeed)}</td> <td className='ri'>{fInt(s.topSpeed)}</td>
<td className="ri">{fInt(s.topBoost)}</td> <td className='ri'>{fInt(s.topBoost)}</td>
<td className="ri">{fRound(s.maxJumpRange)}</td> <td className='ri'>{fRound(s.maxJumpRange)}</td>
<td className="ri">{fInt(s.maxCargo)}</td> <td className='ri'>{fInt(s.maxCargo)}</td>
<td className="ri">{fInt(s.maxPassengers)}</td> <td className='ri'>{fInt(s.maxPassengers)}</td>
<td className="cn">{s.standard[0]}</td> <td className='cn'>{s.standard[0]}</td>
<td className="cn">{s.standard[1]}</td> <td className='cn'>{s.standard[1]}</td>
<td className="cn">{s.standard[2]}</td> <td className='cn'>{s.standard[2]}</td>
<td className="cn">{s.standard[3]}</td> <td className='cn'>{s.standard[3]}</td>
<td className="cn">{s.standard[4]}</td> <td className='cn'>{s.standard[4]}</td>
<td className="cn">{s.standard[5]}</td> <td className='cn'>{s.standard[5]}</td>
<td className="cn">{s.standard[6]}</td> <td className='cn'>{s.standard[6]}</td>
<td className={cn({ disabled: !s.hp[1] })}>{s.hp[1]}</td> <td className={cn({ disabled: !s.hp[1] })}>{s.hp[1]}</td>
<td className={cn({ disabled: !s.hp[2] })}>{s.hp[2]}</td> <td className={cn({ disabled: !s.hp[2] })}>{s.hp[2]}</td>
<td className={cn({ disabled: !s.hp[3] })}>{s.hp[3]}</td> <td className={cn({ disabled: !s.hp[3] })}>{s.hp[3]}</td>
<td className={cn({ disabled: !s.hp[4] })}>{s.hp[4]}</td> <td className={cn({ disabled: !s.hp[4] })}>{s.hp[4]}</td>
<td className={cn({ disabled: !s.hp[0] })}>{s.hp[0]}</td> <td className={cn({ disabled: !s.hp[0] })}>{s.hp[0]}</td>
<td className={cn({ disabled: !s.int[0] })}>{s.int[0]}</td> <td className={cn({ disabled: !s.int[0] })}>{s.int[0]}</td>
<td className={cn({ disabled: !s.int[1] })}>{s.int[1]}</td> <td className={cn({ disabled: !s.int[1] })}>{s.int[1]}</td>
<td className={cn({ disabled: !s.int[2] })}>{s.int[2]}</td> <td className={cn({ disabled: !s.int[2] })}>{s.int[2]}</td>
<td className={cn({ disabled: !s.int[3] })}>{s.int[3]}</td> <td className={cn({ disabled: !s.int[3] })}>{s.int[3]}</td>
<td className={cn({ disabled: !s.int[4] })}>{s.int[4]}</td> <td className={cn({ disabled: !s.int[4] })}>{s.int[4]}</td>
<td className={cn({ disabled: !s.int[5] })}>{s.int[5]}</td> <td className={cn({ disabled: !s.int[5] })}>{s.int[5]}</td>
<td className={cn({ disabled: !s.int[6] })}>{s.int[6]}</td> <td className={cn({ disabled: !s.int[6] })}>{s.int[6]}</td>
<td className={cn({ disabled: !s.int[7] })}>{s.int[7]}</td> <td className={cn({ disabled: !s.int[7] })}>{s.int[7]}</td>
</tr> </tr>;
);
} }
/** /**
@@ -235,8 +222,7 @@ export default class ShipyardPage extends Page {
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 } = this.state;
let sortShips = (predicate, index) => let sortShips = (predicate, index) => this._sortShips.bind(this, predicate, index);
this._sortShips.bind(this, predicate, index);
let filters = { let filters = {
// 'class': { 1: 1, 2: 1} // 'class': { 1: 1, 2: 1}
@@ -253,8 +239,7 @@ export default class ShipyardPage extends Page {
// Sort shipsOverview // Sort shipsOverview
shipSummaries.sort((a, b) => { shipSummaries.sort((a, b) => {
let valA = a[shipPredicate], let valA = a[shipPredicate], valB = b[shipPredicate];
valB = b[shipPredicate];
if (shipPredicateIndex != undefined) { if (shipPredicateIndex != undefined) {
valA = valA[shipPredicateIndex]; valA = valA[shipPredicateIndex];
@@ -267,7 +252,7 @@ export default class ShipyardPage extends Page {
valB = val; valB = val;
} }
if (valA == valB) { if(valA == valB) {
if (a.name > b.name) { if (a.name > b.name) {
return 1; return 1;
} else { } else {
@@ -284,55 +269,47 @@ 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) {
detailRows[i] = this._shipRowElement( let shipSortValue = s[shipPredicate];
s, if(shipPredicateIndex != undefined) {
translate, shipSortValue = shipSortValue[shipPredicateIndex];
units, }
fInt,
formats.f1, if(shipSortValue != lastShipSortValue) {
); backgroundHighlight = !backgroundHighlight;
lastShipSortValue = shipSortValue;
}
detailRows[i] = this._shipRowElement(s, translate, units, fInt, formats.f1, backgroundHighlight);
shipRows[i] = ( shipRows[i] = (
<tr <tr
key={i} key={i}
style={{ height: '1.5em' }} style={{ height: '1.5em' }}
className={cn({ className={cn({ highlighted: noTouch && this.state.shipId === s.id, alt: backgroundHighlight })}
highlighted: noTouch && this.state.shipId === s.id,
})}
onMouseEnter={noTouch && this._highlightShip.bind(this, s.id)} onMouseEnter={noTouch && this._highlightShip.bind(this, s.id)}
> >
<td className="le"> <td className='le'><Link href={'/outfit/' + s.id}>{s.name}</Link></td>
<Link href={'/outfit/' + s.id}>{s.name} {s.beta === true ? '(Beta)' : null}</Link>
</td>
</tr> </tr>
); );
i++; i++;
} }
return ( return (
<div className="page" style={{ fontSize: sizeRatio + 'em' }}> <div className='page' style={{ fontSize: sizeRatio + 'em' }}>
<div <div style={{ whiteSpace: 'nowrap', margin: '0 auto', fontSize: '0.8em', position: 'relative', display: 'inline-block', maxWidth: '100%' }}>
style={{
whiteSpace: 'nowrap',
margin: '0 auto',
fontSize: '0.8em',
position: 'relative',
display: 'inline-block',
maxWidth: '100%'
}}
>
<table style={{ width: '12em', position: 'absolute', zIndex: 1 }}> <table style={{ width: '12em', position: 'absolute', zIndex: 1 }}>
<thead> <thead>
<tr> <tr>
<th className="le rgt">&nbsp;</th> <th className='le rgt'>&nbsp;</th>
</tr> </tr>
<tr className="main"> <tr className='main'>
<th className="sortable le rgt" onClick={sortShips('name')}> <th className='sortable le rgt' onClick={sortShips('name')}>{translate('ship')}</th>
{translate('ship')}
</th>
</tr> </tr>
<tr> <tr>
<th className="le rgt invisible">{units['m/s']}</th> <th className='le rgt invisible'>{units['m/s']}</th>
</tr> </tr>
</thead> </thead>
<tbody onMouseLeave={this._highlightShip.bind(this, null)}> <tbody onMouseLeave={this._highlightShip.bind(this, null)}>
@@ -340,261 +317,80 @@ export default class ShipyardPage extends Page {
</tbody> </tbody>
</table> </table>
<div style={{ overflowX: 'scroll', maxWidth: '100%' }}> <div style={{ overflowX: 'scroll', maxWidth: '100%' }}>
<table style={{ marginLeft: 'calc(12em - 1px)', zIndex: 0 }}> <table style={{ marginLeft: 'calc(12em - 1px)', zIndex: 0 }}>
<thead> <thead>
<tr className="main"> <tr className='main'>
<th <th rowSpan={3} className='sortable' onClick={sortShips('manufacturer')}>{translate('manufacturer')}</th>
rowSpan={3} <th>&nbsp;</th>
className="sortable" <th rowSpan={3} className='sortable' onClick={sortShips('class')}>{translate('size')}</th>
onClick={sortShips('manufacturer')} <th rowSpan={3} className='sortable' onClick={sortShips('crew')}>{translate('crew')}</th>
> <th rowSpan={3} className='sortable' onMouseEnter={termtip.bind(null, 'mass lock factor')} onMouseLeave={hide} onClick={sortShips('masslock')} >{translate('MLF')}</th>
{translate('manufacturer')} <th rowSpan={3} className='sortable' onClick={sortShips('agility')}>{translate('agility')}</th>
</th> <th rowSpan={3} className='sortable' onMouseEnter={termtip.bind(null, 'hardness')} onMouseLeave={hide} onClick={sortShips('hardness')}>{translate('hrd')}</th>
<th>&nbsp;</th> <th>&nbsp;</th>
<th <th colSpan={4}>{translate('base')}</th>
rowSpan={3} <th colSpan={5}>{translate('max')}</th>
className="sortable" <th className='lft' colSpan={7}></th>
onClick={sortShips('class')} <th className='lft' colSpan={5}></th>
> <th className='lft' colSpan={8}></th>
{translate('size')} </tr>
</th> <tr>
<th <th className='sortable lft' onClick={sortShips('retailCost')}>{translate('cost')}</th>
rowSpan={3} <th className='sortable lft' onClick={sortShips('hullMass')}>{translate('hull')}</th>
className="sortable" <th className='sortable lft' onClick={sortShips('speed')}>{translate('speed')}</th>
onClick={sortShips('crew')} <th className='sortable' onClick={sortShips('boost')}>{translate('boost')}</th>
> <th className='sortable' onClick={sortShips('baseArmour')}>{translate('armour')}</th>
{translate('crew')} <th className='sortable' onClick={sortShips('baseShieldStrength')}>{translate('shields')}</th>
</th>
<th
rowSpan={3}
className="sortable"
onMouseEnter={termtip.bind(null, 'mass lock factor')}
onMouseLeave={hide}
onClick={sortShips('masslock')}
>
{translate('MLF')}
</th>
<th
rowSpan={3}
className="sortable"
onClick={sortShips('agility')}
>
{translate('agility')}
</th>
<th
rowSpan={3}
className="sortable"
onMouseEnter={termtip.bind(null, 'hardness')}
onMouseLeave={hide}
onClick={sortShips('hardness')}
>
{translate('hrd')}
</th>
<th>&nbsp;</th>
<th colSpan={4}>{translate('base')}</th>
<th colSpan={5}>{translate('max')}</th>
<th className="lft" colSpan={7} />
<th className="lft" colSpan={5} />
<th className="lft" colSpan={8} />
</tr>
<tr>
<th
className="sortable lft"
onClick={sortShips('retailCost')}
>
{translate('cost')}
</th>
<th className="sortable lft" onClick={sortShips('hullMass')}>
{translate('hull')}
</th>
<th className="sortable lft" onClick={sortShips('speed')}>
{translate('speed')}
</th>
<th className="sortable" onClick={sortShips('boost')}>
{translate('boost')}
</th>
<th className="sortable" onClick={sortShips('baseArmour')}>
{translate('armour')}
</th>
<th
className="sortable"
onClick={sortShips('baseShieldStrength')}
>
{translate('shields')}
</th>
<th className="sortable lft" onClick={sortShips('topSpeed')}> <th className='sortable lft' onClick={sortShips('topSpeed')}>{translate('speed')}</th>
{translate('speed')} <th className='sortable' onClick={sortShips('topBoost')}>{translate('boost')}</th>
</th> <th className='sortable' onClick={sortShips('maxJumpRange')}>{translate('jump')}</th>
<th className="sortable" onClick={sortShips('topBoost')}> <th className='sortable' onClick={sortShips('maxCargo')}>{translate('cargo')}</th>
{translate('boost')} <th className='sortable' onClick={sortShips('maxPassengers')}>{translate('pax')}</th>
</th>
<th className="sortable" onClick={sortShips('maxJumpRange')}>
{translate('jump')}
</th>
<th className="sortable" onClick={sortShips('maxCargo')}>
{translate('cargo')}
</th>
<th className="sortable" onClick={sortShips('maxPassengers')} onMouseEnter={termtip.bind(null, 'passenger capacity')}
onMouseLeave={hide}>
{translate('pax')}
</th>
<th className="lft" colSpan={7}>
{translate('core module classes')}
</th>
<th
colSpan={5}
className="sortable lft"
onClick={sortShips('hpCount')}
>
{translate('hardpoints')}
</th>
<th
colSpan={8}
className="sortable lft"
onClick={sortShips('intCount')}
>
{translate('internal compartments')}
</th>
</tr>
<tr>
<th
className="sortable lft"
onClick={sortShips('retailCost')}
>
{units.CR}
</th>
<th className="sortable lft" onClick={sortShips('hullMass')}>
{units.T}
</th>
<th className="sortable lft" onClick={sortShips('speed')}>
{units['m/s']}
</th>
<th className="sortable" onClick={sortShips('boost')}>
{units['m/s']}
</th>
<th>&nbsp;</th>
<th
className="sortable"
onClick={sortShips('baseShieldStrength')}
>
{units.MJ}
</th>
<th className="sortable lft" onClick={sortShips('topSpeed')}>
{units['m/s']}
</th>
<th className="sortable" onClick={sortShips('topBoost')}>
{units['m/s']}
</th>
<th className="sortable" onClick={sortShips('maxJumpRange')}>
{units.LY}
</th>
<th className="sortable" onClick={sortShips('maxCargo')}>
{units.T}
</th>
<th>&nbsp;</th>
<th
className="sortable lft"
onMouseEnter={termtip.bind(null, 'power plant')}
onMouseLeave={hide}
onClick={sortShips('standard', 0)}
>
{'pp'}
</th>
<th
className="sortable"
onMouseEnter={termtip.bind(null, 'thrusters')}
onMouseLeave={hide}
onClick={sortShips('standard', 1)}
>
{'th'}
</th>
<th
className="sortable"
onMouseEnter={termtip.bind(null, 'frame shift drive')}
onMouseLeave={hide}
onClick={sortShips('standard', 2)}
>
{'fsd'}
</th>
<th
className="sortable"
onMouseEnter={termtip.bind(null, 'life support')}
onMouseLeave={hide}
onClick={sortShips('standard', 3)}
>
{'ls'}
</th>
<th
className="sortable"
onMouseEnter={termtip.bind(null, 'power distriubtor')}
onMouseLeave={hide}
onClick={sortShips('standard', 4)}
>
{'pd'}
</th>
<th
className="sortable"
onMouseEnter={termtip.bind(null, 'sensors')}
onMouseLeave={hide}
onClick={sortShips('standard', 5)}
>
{'s'}
</th>
<th
className="sortable"
onMouseEnter={termtip.bind(null, 'fuel tank')}
onMouseLeave={hide}
onClick={sortShips('standard', 6)}
>
{'ft'}
</th>
<th className="sortable lft" onClick={sortShips('hp', 1)}>
{translate('S')}
</th>
<th className="sortable" onClick={sortShips('hp', 2)}>
{translate('M')}
</th>
<th className="sortable" onClick={sortShips('hp', 3)}>
{translate('L')}
</th>
<th className="sortable" onClick={sortShips('hp', 4)}>
{translate('H')}
</th>
<th className="sortable" onClick={sortShips('hp', 0)}>
{translate('U')}
</th>
<th className="sortable lft" onClick={sortShips('int', 0)}> <th className='lft' colSpan={7}>{translate('core module classes')}</th>
1 <th colSpan={5} className='sortable lft' onClick={sortShips('hpCount')}>{translate('hardpoints')}</th>
</th> <th colSpan={8} className='sortable lft' onClick={sortShips('intCount')}>{translate('internal compartments')}</th>
<th className="sortable" onClick={sortShips('int', 1)}> </tr>
2 <tr>
</th> <th className='sortable lft' onClick={sortShips('retailCost')}>{units.CR}</th>
<th className="sortable" onClick={sortShips('int', 2)}> <th className='sortable lft' onClick={sortShips('hullMass')}>{units.T}</th>
3 <th className='sortable lft' onClick={sortShips('speed')}>{units['m/s']}</th>
</th> <th className='sortable' onClick={sortShips('boost')}>{units['m/s']}</th>
<th className="sortable" onClick={sortShips('int', 3)}> <th>&nbsp;</th>
4 <th className='sortable' onClick={sortShips('baseShieldStrength')}>{units.MJ}</th>
</th> <th className='sortable lft' onClick={sortShips('topSpeed')}>{units['m/s']}</th>
<th className="sortable" onClick={sortShips('int', 4)}> <th className='sortable' onClick={sortShips('topBoost')}>{units['m/s']}</th>
5 <th className='sortable' onClick={sortShips('maxJumpRange')}>{units.LY}</th>
</th> <th className='sortable' onClick={sortShips('maxCargo')}>{units.T}</th>
<th className="sortable" onClick={sortShips('int', 5)}> <th>&nbsp;</th>
6 <th className='sortable lft' onMouseEnter={termtip.bind(null, 'power plant')} onMouseLeave={hide} onClick={sortShips('standard', 0)}>{'pp'}</th>
</th> <th className='sortable' onMouseEnter={termtip.bind(null, 'thrusters')} onMouseLeave={hide} onClick={sortShips('standard', 1)}>{'th'}</th>
<th className="sortable" onClick={sortShips('int', 6)}> <th className='sortable' onMouseEnter={termtip.bind(null, 'frame shift drive')} onMouseLeave={hide} onClick={sortShips('standard', 2)}>{'fsd'}</th>
7 <th className='sortable' onMouseEnter={termtip.bind(null, 'life support')} onMouseLeave={hide} onClick={sortShips('standard', 3)}>{'ls'}</th>
</th> <th className='sortable' onMouseEnter={termtip.bind(null, 'power distriubtor')} onMouseLeave={hide} onClick={sortShips('standard', 4)}>{'pd'}</th>
<th className="sortable" onClick={sortShips('int', 7)}> <th className='sortable' onMouseEnter={termtip.bind(null, 'sensors')} onMouseLeave={hide} onClick={sortShips('standard', 5)}>{'s'}</th>
8 <th className='sortable' onMouseEnter={termtip.bind(null, 'fuel tank')} onMouseLeave={hide} onClick={sortShips('standard', 6)}>{'ft'}</th>
</th> <th className='sortable lft' onClick={sortShips('hp',1)}>{translate('S')}</th>
</tr> <th className='sortable' onClick={sortShips('hp', 2)}>{translate('M')}</th>
</thead> <th className='sortable' onClick={sortShips('hp', 3)}>{translate('L')}</th>
<tbody onMouseLeave={this._highlightShip.bind(this, null)}> <th className='sortable' onClick={sortShips('hp', 4)}>{translate('H')}</th>
{detailRows} <th className='sortable' onClick={sortShips('hp', 0)}>{translate('U')}</th>
</tbody>
</table> <th className='sortable lft' onClick={sortShips('int', 0)} >1</th>
<th className='sortable' onClick={sortShips('int', 1)} >2</th>
<th className='sortable' onClick={sortShips('int', 2)} >3</th>
<th className='sortable' onClick={sortShips('int', 3)} >4</th>
<th className='sortable' onClick={sortShips('int', 4)} >5</th>
<th className='sortable' onClick={sortShips('int', 5)} >6</th>
<th className='sortable' onClick={sortShips('int', 6)} >7</th>
<th className='sortable' onClick={sortShips('int', 7)} >8</th>
</tr>
</thead>
<tbody onMouseLeave={this._highlightShip.bind(this, null)}>
{detailRows}
</tbody>
</table>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -94,10 +94,6 @@ 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 = {};

View File

@@ -41,7 +41,6 @@ export default class Module {
* @return {object} The value of the modification. If it is a numeric value then it is returned as an integer value scaled so that 1.23% == 123 * @return {object} The value of the modification. If it is a numeric value then it is returned as an integer value scaled so that 1.23% == 123
*/ */
getModValue(name, raw) { getModValue(name, raw) {
let baseVal = this[name];
let result = this.mods && this.mods[name] ? this.mods[name] : null; let result = this.mods && this.mods[name] ? this.mods[name] : null;
if ((!raw) && this.blueprint && this.blueprint.special) { if ((!raw) && this.blueprint && this.blueprint.special) {
@@ -52,8 +51,13 @@ export default class Module {
const modification = Modifications.modifications[name]; const modification = Modifications.modifications[name];
const multiplier = modification.type === 'percentage' ? 10000 : 100; const multiplier = modification.type === 'percentage' ? 10000 : 100;
if (name === 'explres' || name === 'kinres' || name === 'thermres' || name === 'causres') { if (name === 'explres' || name === 'kinres' || name === 'thermres' || name === 'causres') {
// Apply resistance modding mechanisms to special effects subsequently // Resistance modifications in itself are additive, however their
result = result + modifierActions[name] * (1 - (this[name] + result / multiplier)) * 100; // special effects are multiplicative. They affect the overall result
// by (special effect resistance) * (damage mult after modification),
// i. e. we need to apply the special effect as a multiplier to the
// overall result and then calculate the difference.
let baseMult = this[name] ? 1 - this[name] : 1;
result = (baseMult - (baseMult - result / multiplier) * (1 - modifierActions[name] / 100)) * multiplier;
} else if (modification.method === 'additive') { } else if (modification.method === 'additive') {
result = result + modifierActions[name] * 100; result = result + modifierActions[name] * 100;
} else if (modification.method === 'overwrite') { } else if (modification.method === 'overwrite') {
@@ -71,6 +75,15 @@ export default class Module {
} }
} }
// Resistance modding for hull reinforcement packages has additional
// diminishing returns implemented. The mod value gets lowered by
// the amount of base resistance the hrp has.
if (!isNaN(result) && this.grp === 'hr' &&
(name === 'kinres' || name === 'thermres' || name === 'explres')) {
let baseRes = this[name];
result = result * (1 - baseRes);
}
// Sanitise the resultant value to 4dp equivalent // Sanitise the resultant value to 4dp equivalent
return isNaN(result) ? result : Math.round(result); return isNaN(result) ? result : Math.round(result);
} }
@@ -95,11 +108,11 @@ export default class Module {
// 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];
if (name === 'explres' || name === 'kinres' || name === 'thermres' || name === 'causres') { if (name === 'explres' || name === 'kinres' || name === 'thermres' || name === 'causres') {
let res = (this[name] ? this[name] : 0) + value / 10000; // Resistance modifications in itself are additive but their
let experimental = modifierActions[name] / 100; // experimentals are applied multiplicatively therefor we must handle
value = (experimental - res) / (experimental - 1) - this[name]; // them differently here (cf. documentation in getModValue).
value *= 10000; let baseMult = (this[name] ? 1 - this[name] : 1);
// value = ((baseMult - value / 10000) / (1 - modifierActions[name] / 100) - baseMult) * -10000; value = ((baseMult - value / 10000) / (1 - modifierActions[name] / 100) - baseMult) * -10000;
} else if (modification.method === 'additive') { } else if (modification.method === 'additive') {
value = value - modifierActions[name]; value = value - modifierActions[name];
} else if (modification.method === 'overwrite') { } else if (modification.method === 'overwrite') {
@@ -164,6 +177,10 @@ export default class Module {
baseValue = 0; baseValue = 0;
} }
modValue = value - baseValue; modValue = value - baseValue;
if (this.grp === 'hr' &&
(name === 'kinres' || name === 'thermres' || name === 'explres')) {
modValue = modValue / (1 - 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 { // multiplicative } else { // multiplicative
@@ -1073,31 +1090,4 @@ 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

@@ -164,13 +164,13 @@ export default class ModuleSet {
/** /**
* Finds the lightest usable Shield Generator * Finds the lightest usable Shield Generator
* @param {number} hullMass Ship hull mass * @param {number} hullMass Ship hull mass
* @param {string} rating The optional rating of the shield * @return {Object} Thruster
* @return {Object} Shield Generator
*/ */
lightestShieldGenerator(hullMass, rating) { lightestShieldGenerator(hullMass) {
let sg = this.internal.sg[0]; let sg = this.internal.sg[0];
for (let s of this.internal.sg) { for (let s of this.internal.sg) {
if ((!rating || rating === s.rating) && s.mass <= sg.mass && s.maxmass > hullMass) { if (s.mass < sg.mass && s.maxmass > hullMass) {
sg = s; sg = s;
} }
} }

View File

@@ -63,10 +63,7 @@ export function standard(type, id) {
if (!isNaN(type)) { if (!isNaN(type)) {
type = StandardArray[type]; type = StandardArray[type];
} }
let s = Modules.standard[type].find(e => e.id === id); let s = Modules.standard[type].find(e => e.id == id || (e.class == id.charAt(0) && e.rating == id.charAt(1)));
if (!s) {
s = Modules.standard[type].find(e => (e.class == id.charAt(0) && e.rating == id.charAt(1)));
}
if (s) { if (s) {
s = new Module({ template: s }); s = new Module({ template: s });
} }

View File

@@ -85,12 +85,12 @@ export function toDetailedBuild(buildName, ship) {
code = ship.toString(); code = ship.toString();
let data = { let data = {
$schema: 'https://coriolis.io/schemas/ship-loadout/4.json#', $schema: 'https://coriolis.edcd.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.io' + outfitURL(ship.id, code, buildName), url: 'https://coriolis.edcd.io' + outfitURL(ship.id, code, buildName),
code, code,
shipId: ship.id shipId: ship.id
}], }],

View File

@@ -505,11 +505,6 @@ export default class Ship {
if (isAbsolute) { if (isAbsolute) {
m.setPretty(name, value, sentfromui); m.setPretty(name, value, sentfromui);
} else { } else {
// Resistance modifiers scale with the base value
if (name == 'kinres' || name == 'thermres' || name == 'causres' || name == 'explres') {
let baseValue = m.get(name, false);
value = (1 - baseValue) * value;
}
m.setModValue(name, value, sentfromui); m.setModValue(name, value, sentfromui);
} }
@@ -1506,7 +1501,7 @@ export default class Ship {
} else { } else {
buffer.writeInt32LE(slotMod.value, curpos); buffer.writeInt32LE(slotMod.value, curpos);
} }
const modification = _.find(Modifications.modifications, function(o) { return o.id === slotMod.id; }); // const modification = _.find(Modifications.modifications, function(o) { return o.id === slotMod.id; });
// console.log('ENCODE Slot ' + i + ': ' + modification.name + ' = ' + slotMod.value); // console.log('ENCODE Slot ' + i + ': ' + modification.name + ' = ' + slotMod.value);
curpos += 4; curpos += 4;
} }
@@ -1519,7 +1514,6 @@ export default class Ship {
} }
this.serialized.modifications = zlib.gzipSync(buffer).toString('base64'); this.serialized.modifications = zlib.gzipSync(buffer).toString('base64');
// console.log(this.serialized.modifications)
} else { } else {
this.serialized.modifications = null; this.serialized.modifications = null;
} }

View File

@@ -1,7 +1,5 @@
import * as ModuleUtils from './ModuleUtils'; import * as ModuleUtils from './ModuleUtils';
import { Modifications } from 'coriolis-data/dist';
import { canMount } from '../utils/SlotFunctions'; import { canMount } from '../utils/SlotFunctions';
import { getBlueprint, setPercent } from '../utils/BlueprintFunctions';
/** /**
* Standard / typical role for multi-purpose or combat (if shielded with better bulkheads) * Standard / typical role for multi-purpose or combat (if shielded with better bulkheads)
@@ -16,7 +14,7 @@ export function multiPurpose(ship, shielded, bulkheadIndex) {
.useBulkhead(bulkheadIndex); .useBulkhead(bulkheadIndex);
if (shielded) { if (shielded) {
ship.internal.some(function (slot) { ship.internal.some(function(slot) {
if (canMount(ship, slot, 'sg')) { // Assuming largest slot can hold an eligible shield if (canMount(ship, slot, 'sg')) { // Assuming largest slot can hold an eligible shield
ship.use(slot, ModuleUtils.findInternal('sg', slot.maxClass, 'A')); ship.use(slot, ModuleUtils.findInternal('sg', slot.maxClass, 'A'));
ship.setSlotEnabled(slot, true); ship.setSlotEnabled(slot, true);
@@ -26,577 +24,6 @@ export function multiPurpose(ship, shielded, bulkheadIndex) {
} }
} }
/**
* Distant Worlds 2 role
* Tiers:
* 1- Max. Jump Range, Unshielded
* 2- Max. Jump Range, Minimal Shields
* 3- Max. Jump Range, Optimal Shields
* 4- Max. Jump Range, Optimal Shields & Thrusters
*
* Engineering level:
* No engineering
* Only Felicity Farseer and Elvira Martuuk
* All exploration related engineers
*
* Role
* Exploration
* Surface exploration
* Big Rig, full mining
* Saper / Prospector mining
* Fuel rat
* Repair rat
* Mechanic
* Trucker
*
* @param ship {Ship} Ship instance
* @param tier {Number}
* @param engineeringLevel {Number}
* @param role {String}
* @param gfsb {Boolean} add Guardian FSD Booster
* @param gpp {Boolean} add Guardian Power Plant
* @param fighter {Boolean} add fighter if supported
*/
export function dw2Build(ship, tier, engineeringLevel, role, gfsb, gpp, fighter) {
ship
.emptyInternal()
.emptyHardpoints()
.emptyUtility();
const fsd = ModuleUtils.findStandard('fsd', ship.standard[2].maxClass, 'A');
ship.use(ship.standard[2], fsd);
ship.use(ship.standard[3], ModuleUtils.findStandard('ls', ship.standard[3].maxClass, 'D'));
ship.use(ship.standard[4], ModuleUtils.findStandard('pd', 1, 'D'));
ship.use(ship.standard[5], ModuleUtils.findStandard('s', ship.standard[5].maxClass, 'D'));
const fuelNeeded = ship.standard[2].m.maxfuel * 2;
const fuelTank = ship.availCS.standard[6]
.filter(e => e.fuel)
.filter(e => e.fuel >= fuelNeeded);
ship.use(ship.standard[6], fuelTank[0]);
ship.useBulkhead(0, false);
if (engineeringLevel === 2) {
const bp = getBlueprint('FSD_LongRange', ship.standard[2]);
bp.grade = 5;
bp.special = Modifications.specials['special_fsd_heavy'];
ship.standard[2].m.blueprint = bp;
setPercent(ship, ship.standard[2].m, 100);
// Sensors G3 LW
const sBP = getBlueprint('Sensor_Sensor_LightWeight', ship.standard[5]);
sBP.grade = 3;
ship.standard[5].m.blueprint = sBP;
setPercent(ship, ship.standard[5].m, 100);
} else if (engineeringLevel === 3) {
// Armour G5 HD + Deep Plating
const armourBP = getBlueprint('Armour_HeavyDuty', ship.bulkheads);
armourBP.grade = 5;
armourBP.special = Modifications.specials['special_armour_chunky'];
ship.bulkheads.m.blueprint = armourBP;
setPercent(ship, ship.bulkheads.m, 100);
// FSD G5 IR + Mass Manager
const fsdBP = getBlueprint('FSD_LongRange', ship.standard[2]);
fsdBP.grade = 5;
fsdBP.special = Modifications.specials['special_fsd_heavy'];
ship.standard[2].m.blueprint = fsdBP;
setPercent(ship, ship.standard[2].m, 100);
// LS G4 LW
const lsBP = getBlueprint('LifeSupport_LightWeight', ship.standard[3]);
lsBP.grade = 4;
ship.standard[3].m.blueprint = lsBP;
setPercent(ship, ship.standard[3].m, 100);
// Sensors G5 LW
const sBP = getBlueprint('Sensor_Sensor_LightWeight', ship.standard[5]);
sBP.grade = 5;
ship.standard[5].m.blueprint = sBP;
setPercent(ship, ship.standard[5].m, 100);
}
if (ship.id === 'imperial_clipper') {
const fs = ModuleUtils.findInternal('fs', 4, 'A');
const slot = ship.internal.filter(a => a.maxClass === 4)[0];
ship.use(slot, fs);
} else if (ship.id === 'imperial_cutter') {
const fs = ModuleUtils.findInternal('fs', 6, 'A');
const slot = ship.internal.filter(a => a.maxClass === 6)[0];
ship.use(slot, fs);
} else if (fsd.class === 2 && fsd.rating === 'A') {
let fs = ModuleUtils.findInternal('fs', 2, 'A');
let slot = ship.internal.filter(a => a.maxClass >= 2).filter(a => a.maxClass >= fs.class)
.sort((a, b) => a.maxClass.toString().localeCompare(b.maxClass.toString()))
[0];
if (slot.m) {
fs = ModuleUtils.findInternal('fs', 1, 'A');
slot = ship.internal.filter(a => a.maxClass === 1)[0];
ship.use(slot, fs);
} else {
ship.use(slot, fs);
}
} else if (fsd.class === 3 && fsd.rating === 'A') {
let fs = ModuleUtils.findInternal('fs', 3, 'B');
let slot = ship.internal.filter(a => a.maxClass >= 3).filter(a => a.maxClass >= fs.class)
.sort((a, b) => a.maxClass.toString().localeCompare(b.maxClass.toString()))
[0];
if (slot.m) {
fs = ModuleUtils.findInternal('fs', 2, 'A');
slot = ship.internal.filter(a => a.maxClass === 2)[0];
ship.use(slot, fs);
} else {
ship.use(slot, fs);
}
} else if (fsd.class === 4 && fsd.rating === 'A') {
let fs = ModuleUtils.findInternal('fs', 4, 'b');
let slot = ship.internal.filter(a => a.maxClass >= 4).filter(a => a.maxClass >= fs.class)
.sort((a, b) => a.maxClass.toString().localeCompare(b.maxClass.toString()))
[0];
if (slot.m) {
fs = ModuleUtils.findInternal('fs', 3, 'A');
slot = ship.internal.filter(a => a.maxClass === 3)[0];
ship.use(slot, fs);
} else {
ship.use(slot, fs);
}
} else if (fsd.class === 5 && fsd.rating === 'A') {
let fs = ModuleUtils.findInternal('fs', 5, 'B');
let slot = ship.internal.filter(a => a.maxClass >= 5).filter(a => a.maxClass >= fs.class)
.sort((a, b) => a.maxClass.toString().localeCompare(b.maxClass.toString()))
[0];
if (slot.m) {
fs = ModuleUtils.findInternal('fs', 4, 'A');
slot = ship.internal.filter(a => a.maxClass === 4).filter(a => a.maxClass >= fs.class)
.sort((a, b) => a.maxClass.toString().localeCompare(b.maxClass.toString()))
[0];
if (fs) {
ship.use(slot, fs);
}
} else {
if (fs) {
ship.use(slot, fs);
}
}
} else if (fsd.class === 6 && fsd.rating === 'A') {
let fs = ModuleUtils.findInternal('fs', 6, 'B');
let slot = ship.internal.filter(a => a.maxClass >= 6)
.filter(a => a.maxClass >= fs.class)
.sort((a, b) => a.maxClass.toString().localeCompare(b.maxClass.toString()))
[0];
if (slot.m) {
fs = ModuleUtils.findInternal('fs', 5, 'A');
slot = ship.internal.filter(a => a.maxClass === 5)[0];
if (fs) {
ship.use(slot, fs);
}
} else {
if (fs) {
ship.use(slot, fs);
}
}
} else if (fsd.class === 7 && fsd.rating === 'A') {
let fs = ModuleUtils.findInternal('fs', 7, 'B');
let slot = ship.internal.filter(a => a.maxClass >= 7).filter(a => a.maxClass >= fs.class)
.sort((a, b) => a.maxClass.toString().localeCompare(b.maxClass.toString()))
[0];
if (slot && slot.m) {
fs = ModuleUtils.findInternal('fs', 6, 'A');
slot = ship.internal.filter(a => a.maxClass === 6)[0];
if (fs) {
ship.use(slot, fs);
}
} else {
if (fs) {
ship.use(slot, fs);
}
}
}
if (tier !== 1) {
const fuelNeeded = ship.standard[2].m.maxfuel * 3;
const fuelTank = ship.availCS.standard[6]
.filter(e => e.fuel)
.filter(e => e.fuel >= fuelNeeded);
if (fuelTank[0]) {
ship.use(ship.standard[6], fuelTank[0]);
}
}
if (tier === 2) {
if (ship.id === 'alliance_chieftain' || ship.id === 'alliance_crusader' || ship.id === 'federal_gunship' || ship.id === 'vulture') {
const hrp = ModuleUtils.findInternal('hrp', 3, 'D');
const slot = ship.internal.filter(e => e.eligible && e.maxClass === 3);
if (hrp) {
ship.use(slot, hrp);
}
} else {
const sg = ship.getAvailableModules().lightestShieldGenerator(ship.ladenMass);
const slot = ship.internal.filter(a => !a.m)
.filter(a => a.maxClass >= sg.class)
.sort((a, b) => a.maxClass.toString().localeCompare(b.maxClass.toString()))
[0];
if (sg) {
ship.use(slot, sg);
}
if (engineeringLevel === 2) {
// ELP G3
const shieldBP = getBlueprint('ShieldGenerator_Optimised', ship.findShieldGenerator());
shieldBP.grade = 3;
ship.findShieldGenerator().blueprint = shieldBP;
setPercent(ship, ship.findShieldGenerator(), 100);
} else if (engineeringLevel === 3) {
// ELP G5
const shieldBP = getBlueprint('ShieldGenerator_Optimised', ship.findShieldGenerator());
shieldBP.grade = 5;
ship.findShieldGenerator().blueprint = shieldBP;
setPercent(ship, ship.findShieldGenerator(), 100);
}
// const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8].reverse();
// const shieldInternals = ship.internal.filter(a => !a.m)
// .filter(a => (!a.eligible) || a.eligible.sg)
// .filter(a => a.maxClass >= sg.class)
// .sort((a, b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass));
// for (let i = 0; i < shieldInternals.length; i++) {
// if (canMount(ship, shieldInternals[i], 'sg')) {
// ship.use(shieldInternals[i], sg);
// break;
// }
// }
}
} else if (tier === 3 || tier === 4) {
const sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass, 'A');
const slot = ship.internal.filter(a => !a.m)
.filter(a => a.maxClass >= sg.class)
.sort((a, b) => a.maxClass.toString().localeCompare(b.maxClass.toString()))
[0];
if (sg) {
ship.use(slot, sg);
}
if (engineeringLevel === 1) {
// ELP G3
const shieldBP = getBlueprint('ShieldGenerator_Optimised', ship.findShieldGenerator());
shieldBP.grade = 3;
shieldBP.special = Modifications.specials['special_shield_lightweight'];
ship.findShieldGenerator().blueprint = shieldBP;
setPercent(ship, ship.findShieldGenerator(), 100);
} else if (engineeringLevel === 2) {
// ELP G5
const shieldBP = getBlueprint('ShieldGenerator_Optimised', ship.findShieldGenerator());
shieldBP.grade = 5;
shieldBP.special = Modifications.specials['special_shield_lightweight'];
ship.findShieldGenerator().blueprint = shieldBP;
setPercent(ship, ship.findShieldGenerator(), 100);
}
}
if (tier === 4) {
let t;
if (canMount(ship, ship.standard[1], 't', ship.standard[1].maxClass - 1)) {
t = ModuleUtils.findStandard('t', ship.standard[1].maxClass - 1, 'A');
} else {
t = ModuleUtils.findStandard('t', ship.standard[1].maxClass, 'A');
}
if (t) {
ship.use(ship.standard[1], t);
}
if (engineeringLevel === 1) {
// DD G3
const tBP = getBlueprint('Engine_Dirty', ship.standard[1]);
tBP.grade = 3;
tBP.special = Modifications.specials['special_engine_lightweight'];
ship.standard[1].m.blueprint = tBP;
setPercent(ship, ship.standard[1].m, 100);
} else if (engineeringLevel === 2) {
// DD G5
const tBP = getBlueprint('Engine_Dirty', ship.standard[1]);
tBP.grade = 5;
tBP.special = Modifications.specials['special_engine_lightweight'];
ship.standard[1].m.blueprint = tBP;
setPercent(ship, ship.standard[1].m, 100);
}
}
if (tier === 4 || tier === 3) {
if (engineeringLevel === 3) {
const pd = ship.availCS.standard[4]
.filter(d => d.rating === 'D')
.filter(d => (d.engcap * 1.728) >= ship.boostEnergy)
.sort((a, b) => a.class.toString().localeCompare(b.class.toString()))[0];
if (pd) {
ship.use(ship.standard[4], pd);
}
// CE G5
const pdBP = getBlueprint('PowerDistributor_HighFrequency', ship.standard[4]);
pdBP.grade = 5;
pdBP.special = Modifications.specials['special_powerdistributor_capacity'];
ship.standard[4].m.blueprint = pdBP;
setPercent(ship, ship.standard[4].m, 100);
} else {
const pd = ship.availCS.standard[4]
.filter(d => d.rating === 'D')
.sort((a, b) => a.engcap > b.engcap)
[0];
if (pd) {
ship.use(ship.standard[4], pd);
}
}
} else if (tier === 4) {
if (engineeringLevel === 3) {
const pd = ship.availCS.standard[4]
.filter(d => d.rating === 'D')
.sort((a, b) => b.class.toString().localeCompare(a.class.toString()))[0];
if (pd) {
ship.use(ship.standard[4], pd);
}
// CE G5
const pdBP = getBlueprint('PowerDistributor_HighFrequency', ship.standard[4]);
pdBP.grade = 5;
pdBP.special = Modifications.specials['special_powerdistributor_capacity'];
ship.standard[4].m.blueprint = pdBP;
setPercent(ship, ship.standard[4].m, 100);
} else {
const pd = ship.availCS.standard[4]
.filter(d => d.rating === 'D')
.sort((a, b) => b.class.toString().localeCompare(a.class.toString()))[0];
if (pd) {
ship.use(ship.standard[4], pd);
}
}
}
if (ship.fighterHangars && fighter) {
const slot = ship.internal.filter(s => s.maxClass >= 5 && !s.m)
.sort((a, b) => a.maxClass.toString().localeCompare(b.maxClass.toString()))
[0];
const mod = ModuleUtils.findInternal('fh', 5, 'D');
if (slot && mod) {
ship.use(slot, mod);
}
}
if (tier === 1) {
const pd = ModuleUtils.findStandard('pd', 1, 'D');
if (pd) {
ship.use(ship.standard[4]);
}
}
let dssPriority = 0;
let srvPriority = 0;
let afmu = true;
let cargo = false;
let miningLaserPriority = 0;
let refinery = false;
let collector = false;
let prospector = false;
let miningTools = false;
let refuelLimpets = false;
let repairLimpets = false;
console.log(role);
if (role === 'exploration') {
dssPriority = 2;
afmu = true;
} else if (role === 'surface') {
dssPriority = 2;
srvPriority = 2;
} else if (role === 'materialProspector') {
miningLaserPriority = 2;
srvPriority = 1;
} else if (role === 'propectorMining') {
dssPriority = 1;
prospector = true;
miningLaserPriority = 1;
cargo = true;
miningTools = true;
} else if (role === 'bigRigMining') {
dssPriority = 1;
miningLaserPriority = 2;
cargo = true;
collector = true;
refinery = true;
miningTools = true;
} else if (role === 'fuelRat') {
refuelLimpets = true;
cargo = true;
srvPriority = 1;
} else if (role === 'mechanic') {
repairLimpets = true;
cargo = true;
srvPriority = 1;
} else if (role === 'trucker') {
cargo = true;
}
if (dssPriority === 2) {
const mod = ModuleUtils.findModule('ss', '2i');
const slot = ship.internal.filter(s => !s.m)
.sort((a, b) => a.maxClass.toString().localeCompare(b.maxClass.toString()))[0];
console.log(slot);
console.log(mod);
ship.use(slot, mod);
}
if (srvPriority === 2) {
let mod;
let slot = ship.internal.filter(s => !s.m)
.filter(s => s.maxClass >= 6)
.sort((a, b) => a.maxClass.toString().localeCompare(b.maxClass.toString()))[0];
if (slot) {
mod = ModuleUtils.findModule('pv', 'v2');
ship.use(slot, mod);
} else if (!slot) {
slot = ship.internal.filter(s => !s.m)
.filter(s => s.maxClass >= 4)
.sort((a, b) => a.maxClass.toString().localeCompare(b.maxClass.toString()))[0];
if (slot) {
mod = ModuleUtils.findModule('pv', 'v4');
ship.use(slot, mod);
} else {
slot = ship.internal.filter(s => !s.m)
.filter(s => s.maxClass >= 2)
.sort((a, b) => a.maxClass.toString().localeCompare(b.maxClass.toString()))[0];
if (slot) {
mod = ModuleUtils.findModule('pv', 'v6');
ship.use(slot, mod);
}
}
}
}
if (cargo === true) {
const slot = ship.internal.filter(s => !s.m)
.sort((a, b) => b.maxClass.toString().localeCompare(a.maxClass.toString()))[0];
const mod = ModuleUtils.findInternal('cr', slot.maxClass, 'E');
ship.use(slot, mod);
}
if (refuelLimpets === true) {
const mod = ModuleUtils.findModule('fx', 'F4');
const slot = ship.internal.filter(s => !s.m)
.sort((a, b) => a.maxClass.toString().localeCompare(b.maxClass.toString()))[0];
ship.use(mod, slot);
}
if (repairLimpets === true) {
const slot = ship.internal.filter(s => !s.m)
.sort((a, b) => a.maxClass.toString().localeCompare(b.maxClass.toString()))[0];
let mod;
if (slot.maxClass >= 3) {
mod = ModuleUtils.findModule('rpl', '9e');
} else {
mod = ModuleUtils.findModule('rpl', '9s');
}
ship.use(mod, slot);
}
if (prospector === true) {
const slot = ship.internal.filter(s => !s.m)
.sort((a, b) => a.maxClass.toString().localeCompare(b.maxClass.toString()))[0];
let mod;
if (slot.maxClass >= 3) {
mod = ModuleUtils.findModule('pc', 'P9');
} else {
mod = ModuleUtils.findModule('pc', 'P4');
}
ship.use(mod, slot);
}
if (collector === true) {
const slots = ship.internal.filter(s => !s.m)
.sort((a, b) => a.maxClass.toString().localeCompare(b.maxClass.toString()));
if (slots.length >= 2) {
let slot = slots.find(s => s.maxClass >= 5);
let mod;
if (slot) {
mod = ModuleUtils.findInternal('cc', slot.maxClass, 'D');
} else if (slots.find(s => s.maxClass <= 4)) {
slot = slots.find(s => s.maxClass <= 4);
mod = ModuleUtils.findInternal('cc', slot.maxClass, 'D');
}
ship.use(slot, mod);
}
}
if (refinery === true) {
const slots = ship.internal.filter(s => !s.m)
.filter(s => s.maxClass >= 4)
.sort((a, b) => a.maxClass.toString().localeCompare(b.maxClass.toString()))[0];
if (slots) {
const mod = ModuleUtils.findInternal('rf', 4, 'A');
ship.use(slots, mod);
} else {
const slot = ship.internal.filter(s => !s.m)
.filter(s => s.maxClass <= 3)
.sort((a, b) => b.maxClass.toString().localeCompare(a.maxClass.toString()))[0];
const mod = ModuleUtils.findInternal('rf', slot.maxClass, 'A');
ship.use(slots, mod);
}
}
if (dssPriority === 1) {
const slot = ship.internal.filter(s => !s.m)
.sort((a, b) => a.maxClass.toString().localeCompare(b.maxClass.toString()))[0];
const dss = ModuleUtils.findInternal('ss', 1, 'C')
if (slot) {
ship.use(slot, dss);
}
}
if (srvPriority === 1) {
const slot = ship.internal.filter(s => !s.m)
.filter(s => s.maxClass >= 2)
.sort((a, b) => a.maxClass.toString().localeCompare(b.maxClass.toString()));
if (slot.find(s => s.maxClass >= 4)) {
const slot = slot.find(s => s.maxClass >= 4);
const srv = ModuleUtils.findInternal('pv', 4, 'G')
ship.use(slot, srv);
} else if (slot.find(s => s.maxClass >= 2)) {
const slot = slot.find(s => s.maxClass >= 2);
const srv = ModuleUtils.findInternal('pv', 2, 'G')
ship.use(slot, srv);
}
}
if (gfsb === true) {
const slots = ship.internal.filter(s => !s.m)
.filter(s => s.maxClass >= 1)
.sort((a, b) => b.maxClass.toString().localeCompare(a.maxClass.toString()));
if (slots.find(s => s.maxClass >= 5)) {
const mod = ModuleUtils.findInternal('gfsb', 5, 'H');
ship.use(slots.find(s => s.maxClass >= 5), mod)
} else if (slots.find(s => s.maxClass >= 4)) {
const mod = ModuleUtils.findInternal('gfsb', 4, 'H');
ship.use(slots.find(s => s.maxClass >= 4), mod)
} else if (slots.find(s => s.maxClass >= 3)) {
const mod = ModuleUtils.findInternal('gfsb', 3, 'H');
ship.use(slots.find(s => s.maxClass >= 3), mod)
} else if (slots.find(s => s.maxClass >= 2)) {
const mod = ModuleUtils.findInternal('gfsb', 2, 'H');
ship.use(slots.find(s => s.maxClass >= 2), mod)
} else if (slots.find(s => s.maxClass >= 1)) {
const mod = ModuleUtils.findInternal('gfsb', 1, 'H');
ship.use(slots.find(s => s.maxClass >= 1), mod)
}
}
// const pp = ship.getAvailableModules().lightestPowerPlant(Math.max(ship.powerRetracted, ship.powerDeployed), 'A');
// const t = ship.getAvailableModules().lightestThruster(ship.ladenMass);
// ship.use(ship.standard[0], pp);
// ship.use(ship.standard[1], t);
// ship.useLightestStandard(standardOpts);
ship.updatePowerGenerated()
.updatePowerUsed()
.recalculateMass()
.updateJumpStats()
.recalculateShield()
.recalculateShieldCells()
.recalculateArmour()
.recalculateDps()
.recalculateEps()
.recalculateHps()
.updateMovement()
.updateModificationsString();
}
/** /**
* Trader Role * Trader Role
* @param {Ship} ship Ship instance * @param {Ship} ship Ship instance
@@ -609,7 +36,7 @@ export function trader(ship, shielded, standardOpts) {
let sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass); let sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass);
ship.useStandard('A') ship.useStandard('A')
.use(ship.standard[3], ModuleUtils.standard(3, ship.standard[3].maxClass + 'D')) // D Life Support .use(ship.standard[3], ModuleUtils.standard(3, ship.standard[3].maxClass + 'D')) // D Life Support
.use(ship.standard[1], ModuleUtils.standard(1, ship.standard[1].maxClass + 'D')) // D Power Plant .use(ship.standard[1], ModuleUtils.standard(1, ship.standard[1].maxClass + 'D')) // D Life Support
.use(ship.standard[4], ModuleUtils.standard(4, ship.standard[4].maxClass + 'D')) // D Life Support .use(ship.standard[4], ModuleUtils.standard(4, ship.standard[4].maxClass + 'D')) // D Life Support
.use(ship.standard[5], ModuleUtils.standard(5, ship.standard[5].maxClass + 'D')); // D Sensors .use(ship.standard[5], ModuleUtils.standard(5, ship.standard[5].maxClass + 'D')); // D Sensors
@@ -618,7 +45,7 @@ export function trader(ship, shielded, standardOpts) {
.filter(a => (!a.eligible) || a.eligible.sg) .filter(a => (!a.eligible) || a.eligible.sg)
.filter(a => a.maxClass >= sg.class) .filter(a => a.maxClass >= sg.class)
.sort((a, b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass)); .sort((a, b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass));
shieldInternals.some(function (slot) { shieldInternals.some(function(slot) {
if (canMount(ship, slot, 'sg')) { // Assuming largest slot can hold an eligible shield if (canMount(ship, slot, 'sg')) { // Assuming largest slot can hold an eligible shield
const shield = ModuleUtils.findInternal('sg', slot.maxClass, 'A'); const shield = ModuleUtils.findInternal('sg', slot.maxClass, 'A');
if (shield && shield.maxmass > ship.hullMass) { if (shield && shield.maxmass > ship.hullMass) {
@@ -660,11 +87,11 @@ export function trader(ship, shielded, standardOpts) {
*/ */
export function explorer(ship, planetary) { export function explorer(ship, planetary) {
let standardOpts = { ppRating: 'A' }, let standardOpts = { ppRating: 'A' },
heatSinkCount = 2, // Fit 2 heat sinks if possible heatSinkCount = 2, // Fit 2 heat sinks if possible
usedSlots = [], usedSlots = [],
sgSlot, sgSlot,
fuelScoopSlot, fuelScoopSlot,
sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass); sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass);
if (!planetary) { // Non-planetary explorers don't really need to boost if (!planetary) { // Non-planetary explorers don't really need to boost
standardOpts.pd = '1D'; standardOpts.pd = '1D';
@@ -789,9 +216,9 @@ export function explorer(ship, planetary) {
export function miner(ship, shielded) { export function miner(ship, shielded) {
shielded = true; shielded = true;
let standardOpts = { ppRating: 'A' }, let standardOpts = { ppRating: 'A' },
miningLaserCount = 2, miningLaserCount = 2,
usedSlots = [], usedSlots = [],
sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass); sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass);
// Cargo hatch should be enabled // Cargo hatch should be enabled
ship.setSlotEnabled(ship.cargoHatch, true); ship.setSlotEnabled(ship.cargoHatch, true);
@@ -842,7 +269,7 @@ export function miner(ship, shielded) {
// Dual mining lasers of highest possible class; remove anything else // Dual mining lasers of highest possible class; remove anything else
const miningLaserOrder = [2, 3, 4, 1, 0]; const miningLaserOrder = [2, 3, 4, 1, 0];
const miningLaserHardpoints = ship.hardpoints.concat().sort(function (a, b) { const miningLaserHardpoints = ship.hardpoints.concat().sort(function(a, b) {
return miningLaserOrder.indexOf(a.maxClass) - miningLaserOrder.indexOf(b.maxClass); return miningLaserOrder.indexOf(a.maxClass) - miningLaserOrder.indexOf(b.maxClass);
}); });
for (let s of miningLaserHardpoints) { for (let s of miningLaserHardpoints) {
@@ -856,7 +283,7 @@ export function miner(ship, shielded) {
// Number of collector limpets required to be active is a function of the size of the ship and the power of the lasers // Number of collector limpets required to be active is a function of the size of the ship and the power of the lasers
const miningLaserDps = ship.hardpoints.filter(h => h.m != null) const miningLaserDps = ship.hardpoints.filter(h => h.m != null)
.reduce(function (a, b) { .reduce(function(a, b) {
return a + b.m.getDps(); return a + b.m.getDps();
}, 0); }, 0);
// Find out how many internal slots we have, and their potential cargo size // Find out how many internal slots we have, and their potential cargo size
@@ -887,7 +314,7 @@ export function miner(ship, shielded) {
// Power distributor to power the mining lasers indefinitely // Power distributor to power the mining lasers indefinitely
const wepRateRequired = ship.hardpoints.filter(h => h.m != null) const wepRateRequired = ship.hardpoints.filter(h => h.m != null)
.reduce(function (a, b) { .reduce(function(a, b) {
return a + b.m.getEps(); return a + b.m.getEps();
}, 0); }, 0);
standardOpts.pd = ship.getAvailableModules().matchingPowerDist({ weprate: wepRateRequired }).id; standardOpts.pd = ship.getAvailableModules().matchingPowerDist({ weprate: wepRateRequired }).id;
@@ -909,9 +336,9 @@ export function miner(ship, shielded) {
*/ */
export function racer(ship) { export function racer(ship) {
let standardOpts = {}, let standardOpts = {},
usedSlots = [], usedSlots = [],
sgSlot, sgSlot,
sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass); sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass);
// Cargo hatch can be disabled // Cargo hatch can be disabled
ship.setSlotEnabled(ship.cargoHatch, false); ship.setSlotEnabled(ship.cargoHatch, false);

View File

@@ -78,6 +78,5 @@ 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,417 +1,417 @@
import React from 'react'; import React from 'react';
import { Modifications } from 'coriolis-data/dist'; import { Modifications } from 'coriolis-data/dist';
/** /**
* Generate a tooltip with details of a blueprint's specials * Generate a tooltip with details of a blueprint's specials
* @param {Object} translate The translate object * @param {Object} translate The translate object
* @param {Object} blueprint The blueprint at the required grade * @param {Object} blueprint The blueprint at the required grade
* @param {string} grp The group of the module * @param {string} grp The group of the module
* @param {Object} m The module to compare with * @param {Object} m The module to compare with
* @param {string} specialName The name of the special * @param {string} specialName The name of the special
* @returns {Object} The react components * @returns {Object} The react components
*/ */
export function specialToolTip(translate, blueprint, grp, m, specialName) { export function specialToolTip(translate, blueprint, grp, m, specialName) {
const effects = []; const effects = [];
if (!blueprint || !blueprint.features) { if (!blueprint || !blueprint.features) {
return undefined; return undefined;
} }
if (m) { if (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) { if (m.blueprint) {
for (const feature in Modifications.modifierActions[specialName]) { for (const feature in Modifications.modifierActions[specialName]) {
// if (!blueprint.features[feature] && !m.mods.feature) { // if (!blueprint.features[feature] && !m.mods.feature) {
const featureDef = Modifications.modifications[feature]; const featureDef = Modifications.modifications[feature];
if (featureDef && !featureDef.hidden) { if (featureDef && !featureDef.hidden) {
let symbol = ''; let symbol = '';
if (feature === 'jitter') { if (feature === 'jitter') {
symbol = '°'; symbol = '°';
} else if (featureDef.type === 'percentage') { } else if (featureDef.type === 'percentage') {
symbol = '%'; symbol = '%';
} }
let current = m.getModValue(feature) - m.getModValue(feature, true); let current = m.getModValue(feature) - m.getModValue(feature, true);
if (featureDef.type === 'percentage') { if (featureDef.type === 'percentage') {
current = Math.round(current / 10) / 10; current = Math.round(current / 10) / 10;
} else if (featureDef.type === 'numeric') { } else if (featureDef.type === 'numeric') {
current /= 100; current /= 100;
} }
const currentIsBeneficial = isValueBeneficial(feature, current); const currentIsBeneficial = isValueBeneficial(feature, current);
effects.push( effects.push(
<tr key={feature + '_specialTT'}> <tr key={feature + '_specialTT'}>
<td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td> <td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td>
<td>&nbsp;</td> <td>&nbsp;</td>
<td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'} <td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'}
style={{ textAlign: 'right' }}>{current}{symbol}</td> style={{ textAlign: 'right' }}>{current}{symbol}</td>
<td>&nbsp;</td> <td>&nbsp;</td>
</tr> </tr>
); );
} }
} }
} }
} }
return ( return (
<div> <div>
<table width='100%'> <table width='100%'>
<tbody> <tbody>
{effects} {effects}
</tbody> </tbody>
</table> </table>
</div> </div>
); );
} }
/** /**
* Generate a tooltip with details of a blueprint's effects * Generate a tooltip with details of a blueprint's effects
* @param {Object} translate The translate object * @param {Object} translate The translate object
* @param {Object} blueprint The blueprint at the required grade * @param {Object} blueprint The blueprint at the required grade
* @param {Array} engineers The engineers supplying this blueprint * @param {Array} engineers The engineers supplying this blueprint
* @param {string} grp The group of the module * @param {string} grp The group of the module
* @param {Object} m The module to compare with * @param {Object} m The module to compare with
* @returns {Object} The react components * @returns {Object} The react components
*/ */
export function blueprintTooltip(translate, blueprint, engineers, grp, m) { export function blueprintTooltip(translate, blueprint, engineers, grp, m) {
const effects = []; const effects = [];
if (!blueprint || !blueprint.features) { if (!blueprint || !blueprint.features) {
return undefined; return undefined;
} }
for (const feature in blueprint.features) { for (const feature in blueprint.features) {
const featureIsBeneficial = isBeneficial(feature, blueprint.features[feature]); const featureIsBeneficial = isBeneficial(feature, blueprint.features[feature]);
const featureDef = Modifications.modifications[feature]; const featureDef = Modifications.modifications[feature];
if (!featureDef.hidden) { if (!featureDef.hidden) {
let symbol = ''; let symbol = '';
if (feature === 'jitter') { if (feature === 'jitter') {
symbol = '°'; symbol = '°';
} else if (featureDef.type === 'percentage') { } else if (featureDef.type === 'percentage') {
symbol = '%'; symbol = '%';
} }
let lowerBound = blueprint.features[feature][0]; let lowerBound = blueprint.features[feature][0];
let upperBound = blueprint.features[feature][1]; let upperBound = blueprint.features[feature][1];
if (featureDef.type === 'percentage') { if (featureDef.type === 'percentage') {
lowerBound = Math.round(lowerBound * 1000) / 10; lowerBound = Math.round(lowerBound * 1000) / 10;
upperBound = Math.round(upperBound * 1000) / 10; upperBound = Math.round(upperBound * 1000) / 10;
} }
const lowerIsBeneficial = isValueBeneficial(feature, lowerBound); const lowerIsBeneficial = isValueBeneficial(feature, lowerBound);
const upperIsBeneficial = isValueBeneficial(feature, upperBound); const upperIsBeneficial = isValueBeneficial(feature, upperBound);
if (m) { if (m) {
// We have a module - add in the current value // We have a module - add in the current value
let current = m.getModValue(feature); let current = m.getModValue(feature);
if (featureDef.type === 'percentage' || featureDef.name === 'burst' || featureDef.name === 'burstrof') { if (featureDef.type === 'percentage' || featureDef.name === 'burst' || featureDef.name === 'burstrof') {
current = Math.round(current / 10) / 10; current = Math.round(current / 10) / 10;
} else if (featureDef.type === 'numeric') { } else if (featureDef.type === 'numeric') {
current /= 100; current /= 100;
} }
const currentIsBeneficial = isValueBeneficial(feature, current); const currentIsBeneficial = isValueBeneficial(feature, current);
effects.push( effects.push(
<tr key={feature}> <tr key={feature}>
<td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td> <td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td>
<td className={lowerBound === 0 ? '' : lowerIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{lowerBound}{symbol}</td> <td className={lowerBound === 0 ? '' : lowerIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{lowerBound}{symbol}</td>
<td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{current}{symbol}</td> <td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{current}{symbol}</td>
<td className={upperBound === 0 ? '' : upperIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{upperBound}{symbol}</td> <td className={upperBound === 0 ? '' : upperIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{upperBound}{symbol}</td>
</tr> </tr>
); );
} else { } else {
// We do not have a module, no value // We do not have a module, no value
effects.push( effects.push(
<tr key={feature}> <tr key={feature}>
<td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td> <td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td>
<td className={lowerBound === 0 ? '' : lowerIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{lowerBound}{symbol}</td> <td className={lowerBound === 0 ? '' : lowerIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{lowerBound}{symbol}</td>
<td className={upperBound === 0 ? '' : upperIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{upperBound}{symbol}</td> <td className={upperBound === 0 ? '' : upperIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{upperBound}{symbol}</td>
</tr> </tr>
); );
} }
} }
} }
if (m) { if (m) {
// Because we have a module add in any benefits that aren't part of the primary blueprint // Because we have a module add in any benefits that aren't part of the primary blueprint
for (const feature in m.mods) { for (const feature in m.mods) {
if (!blueprint.features[feature]) { if (!blueprint.features[feature]) {
const featureDef = Modifications.modifications[feature]; const featureDef = Modifications.modifications[feature];
if (featureDef && !featureDef.hidden) { if (featureDef && !featureDef.hidden) {
let symbol = ''; let symbol = '';
if (feature === 'jitter') { if (feature === 'jitter') {
symbol = '°'; symbol = '°';
} else if (featureDef.type === 'percentage') { } else if (featureDef.type === 'percentage') {
symbol = '%'; symbol = '%';
} }
let current = m.getModValue(feature); let current = m.getModValue(feature);
if (featureDef.type === 'percentage' || featureDef.name === 'burst' || featureDef.name === 'burstrof') { if (featureDef.type === 'percentage' || featureDef.name === 'burst' || featureDef.name === 'burstrof') {
current = Math.round(current / 10) / 10; current = Math.round(current / 10) / 10;
} else if (featureDef.type === 'numeric') { } else if (featureDef.type === 'numeric') {
current /= 100; current /= 100;
} }
const currentIsBeneficial = isValueBeneficial(feature, current); const currentIsBeneficial = isValueBeneficial(feature, current);
effects.push( effects.push(
<tr key={feature}> <tr key={feature}>
<td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td> <td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td>
<td>&nbsp;</td> <td>&nbsp;</td>
<td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{current}{symbol}</td> <td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{current}{symbol}</td>
<td>&nbsp;</td> <td>&nbsp;</td>
</tr> </tr>
); );
} }
} }
} }
// 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.edname]) {
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) {
let symbol = ''; let symbol = '';
if (feature === 'jitter') { if (feature === 'jitter') {
symbol = '°'; symbol = '°';
} else if (featureDef.type === 'percentage') { } else if (featureDef.type === 'percentage') {
symbol = '%'; symbol = '%';
} }
let current = m.getModValue(feature); let current = m.getModValue(feature);
if (featureDef.type === 'percentage' || featureDef.name === 'burst' || featureDef.name === 'burstrof') { if (featureDef.type === 'percentage' || featureDef.name === 'burst' || featureDef.name === 'burstrof') {
current = Math.round(current / 10) / 10; current = Math.round(current / 10) / 10;
} else if (featureDef.type === 'numeric') { } else if (featureDef.type === 'numeric') {
current /= 100; current /= 100;
} }
const currentIsBeneficial = isValueBeneficial(feature, current); const currentIsBeneficial = isValueBeneficial(feature, current);
effects.push( effects.push(
<tr key={feature}> <tr key={feature}>
<td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td> <td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td>
<td>&nbsp;</td> <td>&nbsp;</td>
<td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{current}{symbol}</td> <td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{current}{symbol}</td>
<td>&nbsp;</td> <td>&nbsp;</td>
</tr> </tr>
); );
} }
} }
} }
} }
} }
let components; let components;
if (!m) { if (!m) {
components = []; components = [];
for (const component in blueprint.components) { for (const component in blueprint.components) {
components.push( components.push(
<tr key={component}> <tr key={component}>
<td style={{ textAlign: 'left' }}>{translate(component)}</td> <td style={{ textAlign: 'left' }}>{translate(component)}</td>
<td style={{ textAlign: 'right' }}>{blueprint.components[component]}</td> <td style={{ textAlign: 'right' }}>{blueprint.components[component]}</td>
</tr> </tr>
); );
} }
} }
let engineersList; let engineersList;
if (engineers) { if (engineers) {
engineersList = []; engineersList = [];
for (const engineer of engineers) { for (const engineer of engineers) {
engineersList.push( engineersList.push(
<tr key={engineer}> <tr key={engineer}>
<td style={{ textAlign: 'left' }}>{engineer}</td> <td style={{ textAlign: 'left' }}>{engineer}</td>
</tr> </tr>
); );
} }
} }
return ( return (
<div> <div>
<table width='100%'> <table width='100%'>
<thead> <thead>
<tr> <tr>
<td>{translate('feature')}</td> <td>{translate('feature')}</td>
<td>{translate('worst')}</td> <td>{translate('worst')}</td>
{m ? <td>{translate('current')}</td> : null } {m ? <td>{translate('current')}</td> : null }
<td>{translate('best')}</td> <td>{translate('best')}</td>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{effects} {effects}
</tbody> </tbody>
</table> </table>
{ components ? <table width='100%'> { components ? <table width='100%'>
<thead> <thead>
<tr> <tr>
<td>{translate('component')}</td> <td>{translate('component')}</td>
<td>{translate('amount')}</td> <td>{translate('amount')}</td>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{components} {components}
</tbody> </tbody>
</table> : null } </table> : null }
{ engineersList ? <table width='100%'> { engineersList ? <table width='100%'>
<thead> <thead>
<tr> <tr>
<td>{translate('engineers')}</td> <td>{translate('engineers')}</td>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{engineersList} {engineersList}
</tbody> </tbody>
</table> : null } </table> : null }
</div> </div>
); );
} }
/** /**
* Is this blueprint feature beneficial? * Is this blueprint feature beneficial?
* @param {string} feature The name of the feature * @param {string} feature The name of the feature
* @param {array} values The value of the feature * @param {array} values The value of the feature
* @returns {boolean} True if this feature is beneficial * @returns {boolean} True if this feature is beneficial
*/ */
export function isBeneficial(feature, values) { export function isBeneficial(feature, values) {
const fact = (values[0] < 0 || (values[0] === 0 && values[1] < 0)); const fact = (values[0] < 0 || (values[0] === 0 && values[1] < 0));
if (Modifications.modifications[feature].higherbetter) { if (Modifications.modifications[feature].higherbetter) {
return !fact; return !fact;
} else { } else {
return fact; return fact;
} }
} }
/** /**
* Is this feature value beneficial? * Is this feature value beneficial?
* @param {string} feature The name of the feature * @param {string} feature The name of the feature
* @param {number} value The value of the feature * @param {number} value The value of the feature
* @returns {boolean} True if this value is beneficial * @returns {boolean} True if this value is beneficial
*/ */
export function isValueBeneficial(feature, value) { export function isValueBeneficial(feature, value) {
if (Modifications.modifications[feature].higherbetter) { if (Modifications.modifications[feature].higherbetter) {
return value > 0; return value > 0;
} else { } else {
return value < 0; 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
* @param {Object} module The module for which to obtain this blueprint * @param {Object} module The module for which to obtain this blueprint
* @returns {Object} The matching blueprint * @returns {Object} The matching blueprint
*/ */
export function getBlueprint(name, module) { export function getBlueprint(name, module) {
// Start with a copy of the blueprint // Start with a copy of the blueprint
const findMod = val => Object.keys(Modifications.blueprints).find(elem => elem.toString().toLowerCase().search(val.toString().toLowerCase().replace(/(OutfittingFieldType_|persecond)/igm, '')) >= 0); const findMod = val => Object.keys(Modifications.blueprints).find(elem => elem.toString().toLowerCase().search(val.toString().toLowerCase().replace(/(OutfittingFieldType_|persecond)/igm, '')) >= 0);
const found = Modifications.blueprints[findMod(name)]; const found = Modifications.blueprints[findMod(name)];
if (!found || !found.fdname) { if (!found || !found.fdname) {
return {}; return {};
} }
const blueprint = JSON.parse(JSON.stringify(found)); const blueprint = JSON.parse(JSON.stringify(found));
return blueprint; return blueprint;
} }
/** /**
* Provide 'percent' primary modifications * Provide 'percent' primary modifications
* @param {Object} ship The ship for which to perform the modifications * @param {Object} ship The ship for which to perform the modifications
* @param {Object} m The module for which to perform the modifications * @param {Object} m The module for which to perform the modifications
* @param {Number} percent The percent to set values to of full. * @param {Number} percent The percent to set values to of full.
*/ */
export function setPercent(ship, m, percent) { export function setPercent(ship, m, percent) {
ship.clearModifications(m); ship.clearModifications(m);
// Pick given value as multiplier // Pick given value as multiplier
const mult = percent / 100; const mult = percent / 100;
const features = m.blueprint.grades[m.blueprint.grade].features; const features = m.blueprint.grades[m.blueprint.grade].features;
for (const featureName in features) { for (const featureName in features) {
let value; let value;
if (Modifications.modifications[featureName].higherbetter) { 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)) {
value = features[featureName][1] + ((features[featureName][0] - features[featureName][1]) * mult); value = features[featureName][1] + ((features[featureName][0] - features[featureName][1]) * mult);
} else { } else {
value = features[featureName][0] + ((features[featureName][1] - features[featureName][0]) * mult); value = features[featureName][0] + ((features[featureName][1] - features[featureName][0]) * mult);
} }
} else { } else {
// Higher is worse, but is this making it better or worse? // Higher is worse, 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)) {
value = features[featureName][0] + ((features[featureName][1] - features[featureName][0]) * mult); value = features[featureName][0] + ((features[featureName][1] - features[featureName][0]) * mult);
} else { } else {
value = features[featureName][1] + ((features[featureName][0] - features[featureName][1]) * mult); value = features[featureName][1] + ((features[featureName][0] - features[featureName][1]) * mult);
} }
} }
_setValue(ship, m, featureName, value); _setValue(ship, m, featureName, value);
} }
} }
/** /**
* Provide 'random' primary modifications * Provide 'random' primary modifications
* @param {Object} ship The ship for which to perform the modifications * @param {Object} ship The ship for which to perform the modifications
* @param {Object} m The module for which to perform the modifications * @param {Object} m The module for which to perform the modifications
*/ */
export function setRandom(ship, m) { export function setRandom(ship, m) {
// Pick a single value for our randomness // Pick a single value for our randomness
setPercent(ship, m, Math.random() * 100); setPercent(ship, m, Math.random() * 100);
} }
/** /**
* Set a modification feature value * Set a modification feature value
* @param {Object} ship The ship for which to perform the modifications * @param {Object} ship The ship for which to perform the modifications
* @param {Object} m The module for which to perform the modifications * @param {Object} m The module for which to perform the modifications
* @param {string} featureName The feature being set * @param {string} featureName The feature being set
* @param {number} value The value being set for the feature * @param {number} value The value being set for the feature
*/ */
function _setValue(ship, m, featureName, value) { function _setValue(ship, m, featureName, value) {
if (Modifications.modifications[featureName].type == 'percentage') { if (Modifications.modifications[featureName].type == 'percentage') {
ship.setModification(m, featureName, value * 10000); ship.setModification(m, featureName, value * 10000);
} else if (Modifications.modifications[featureName].type == 'numeric') { } else if (Modifications.modifications[featureName].type == 'numeric') {
ship.setModification(m, featureName, value * 100); ship.setModification(m, featureName, value * 100);
} else { } else {
ship.setModification(m, featureName, value); ship.setModification(m, featureName, value);
} }
} }
/** /**
* Provide 'percent' primary query * Provide 'percent' primary query
* @param {Object} m The module for which to perform the query * @param {Object} m The module for which to perform the query
* @returns {Number} percent The percentage indicator of current applied values. * @returns {Number} percent The percentage indicator of current applied values.
*/ */
export function getPercent(m) { export function getPercent(m) {
let result = null; let result = null;
const features = m.blueprint.grades[m.blueprint.grade].features; const features = m.blueprint.grades[m.blueprint.grade].features;
for (const featureName in features) { for (const featureName in features) {
if (features[featureName][0] === features[featureName][1]) { if (features[featureName][0] === features[featureName][1]) {
continue; continue;
} }
let value = _getValue(m, featureName); let value = _getValue(m, featureName);
let mult; let mult;
if (featureName == 'shieldboost') { if (featureName == 'shieldboost') {
mult = ((1 + value) * (1 + m.shieldboost)) - 1 - m.shieldboost; mult = ((1 + value) * (1 + m.shieldboost)) - 1 - m.shieldboost;
} else if (Modifications.modifications[featureName].higherbetter) { } 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);
} else { } else {
mult = Math.round((value - features[featureName][0]) / (features[featureName][1] - features[featureName][0]) * 100); mult = Math.round((value - features[featureName][0]) / (features[featureName][1] - features[featureName][0]) * 100);
} }
} else { } else {
// Higher is worse, but is this making it better or worse? // Higher is worse, 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][0]) / (features[featureName][1] - features[featureName][0]) * 100); mult = Math.round((value - features[featureName][0]) / (features[featureName][1] - features[featureName][0]) * 100);
} else { } else {
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);
} }
} }
if (result && result != mult) { if (result && result != mult) {
return null; return null;
} else if (result != mult) { } else if (result != mult) {
result = mult; result = mult;
} }
} }
return result; return result;
} }
/** /**
* Query a feature value * Query a feature value
* @param {Object} m The module for which to perform the query * @param {Object} m The module for which to perform the query
* @param {string} featureName The feature being queried * @param {string} featureName The feature being queried
* @returns {number} The value of the modification as a % * @returns {number} The value of the modification as a %
*/ */
function _getValue(m, featureName) { function _getValue(m, featureName) {
if (Modifications.modifications[featureName].type == 'percentage') { if (Modifications.modifications[featureName].type == 'percentage') {
return m.getModValue(featureName, true) / 10000; return m.getModValue(featureName, true) / 10000;
} else if (Modifications.modifications[featureName].type == 'numeric') { } else if (Modifications.modifications[featureName].type == 'numeric') {
return m.getModValue(featureName, true) / 100; return m.getModValue(featureName, true) / 100;
} else { } else {
return m.getModValue(featureName, true); return m.getModValue(featureName, true);
} }
} }

View File

@@ -30,8 +30,6 @@ export const SHIP_FD_NAME_TO_CORIOLIS_NAME = {
'Hauler': 'hauler', 'Hauler': 'hauler',
'Independant_Trader': 'keelback', 'Independant_Trader': 'keelback',
'Krait_MkII': 'krait_mkii', 'Krait_MkII': 'krait_mkii',
'Mamba': 'mamba',
'Krait_Light': 'krait_phantom',
'Orca': 'orca', 'Orca': 'orca',
'Python': 'python', 'Python': 'python',
'SideWinder': 'sidewinder', 'SideWinder': 'sidewinder',

View File

@@ -1,9 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<script src="xdLocalStoragePostMessageApi.min.js"></script>
</head>
<body>
This is the magical iframe
</body>
</html>

View File

@@ -1,19 +1,16 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html manifest="/">
<head> <head>
<title>Coriolis EDCD Edition</title> <title>Coriolis EDCD Edition</title>
<meta charset="UTF-8"> <meta charset="UTF-8">
<link rel="stylesheet" href="<%= htmlWebpackPlugin.files.css[0] %>"> <link rel="stylesheet" href="<%= htmlWebpackPlugin.files.css[0] %>">
<!-- Standard headers --> <!-- Standard headers -->
<meta name="description" content="A ship builder, outfitting and comparison <meta name="description" content="A ship builder, outfitting and comparison tool for Elite Dangerous">
tool for Elite Dangerous">
<meta name="mobile-web-app-capable" content="yes"> <meta name="mobile-web-app-capable" content="yes">
<meta name="viewport" content="width=device-width, initial-scale=1.0, <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
maximum-scale=1.0, user-scalable=0">
<link rel="manifest" href="/manifest.json"> <link rel="manifest" href="/manifest.json">
<link rel="shortcut icon" href=/favicon2.ico> <link rel="shortcut icon" href=/favicon2.ico>
<link rel="icon" sizes="152x152 192x192" type="image/png" <link rel="icon" sizes="152x152 192x192" type="image/png" href="/192x192.png">
href="/192x192.png">
<!-- Apple/iOS headers --> <!-- Apple/iOS headers -->
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes">
@@ -33,16 +30,16 @@
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) { %> <% if (htmlWebpackPlugin.options.uaTracking) { %>
<script> <script>
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date; window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
ga('create', '<%- htmlWebpackPlugin.options.uaTracking %>', 'auto'); ga('create', '<%- htmlWebpackPlugin.options.uaTracking %>', 'auto');
ga('send', 'pageview'); ga('send', 'pageview');
</script> </script>
<script async src='https://www.google-analytics.com/analytics.js'></script> <script async src='https://www.google-analytics.com/analytics.js'></script>
<% } %> <% } %>
<!-- Piwik --> <!-- Piwik -->
<!-- <script type="text/javascript"> <!-- <script type="text/javascript">
var _paq = _paq || []; var _paq = _paq || [];
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */ /* tracker methods like "setCustomDimension" should be called before "trackPageView" */
_paq.push(["setCookieDomain", "*.coriolis.edcd.io"]); _paq.push(["setCookieDomain", "*.coriolis.edcd.io"]);
@@ -56,17 +53,18 @@
g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s); g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s);
})(); })();
</script>--> </script>-->
<!-- End Piwik Code --> <!-- End Piwik Code -->
<!-- Bugsnag --> <!-- Bugsnag -->
<script src="https://d2wy8f7a9ursnm.cloudfront.net/v5.0.0/bugsnag.min.js"></script> <script src="//d2wy8f7a9ursnm.cloudfront.net/v4/bugsnag.min.js"></script>
<script> <script src="//d2wy8f7a9ursnm.cloudfront.net/bugsnag-plugins/v1/bugsnag-react.min.js"></script>
<script>
window.bugsnagClient = bugsnag('ba9fae819372850fb660755341fa6ef5', {appVersion: window.BUGSNAG_VERSION || undefined}) window.bugsnagClient = bugsnag('ba9fae819372850fb660755341fa6ef5', {appVersion: window.BUGSNAG_VERSION || undefined})
window.Bugsnag = window.bugsnagClient window.Bugsnag = window.bugsnagClient
</script> </script>
</head> </head>
<body style="background-color:#000;"> <body style="background-color:#000;">
<section id="coriolis"></section> <section id="coriolis"></section>
</body> </body>
</html> </html>

View File

@@ -171,16 +171,3 @@ footer {
text-align: right; text-align: right;
} }
} }
.announcement-container {
display: flex;
align-items: center;
padding-top: 10px;
justify-content: center;
flex-flow: row wrap;
}
.announcement {
border: 1px @secondary solid;
padding: 10px;
}

View File

@@ -22,15 +22,6 @@ select {
} }
} }
.react-fuzzy-search > * {
padding: 0 !important;
color: @primary;
& > input {
border: 1px solid @primary !important;
color: @primary-bg;
}
}
.cmdr-select { .cmdr-select {
border: 1px solid @primary; border: 1px solid @primary;
padding: 0.5em 0.5em; padding: 0.5em 0.5em;

View File

@@ -55,7 +55,7 @@ tbody tr {
background-color: @warning-bg; background-color: @warning-bg;
} }
&:nth-child(odd){ &.alt {
background-color: @alt-primary-bg; background-color: @alt-primary-bg;
} }
} }

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.io/schemas/ship-loadout/1.json#", "id": "https://coriolis.edcd.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",
@@ -227,11 +227,6 @@
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1
}, },
"fullTankRange": {
"description": "Single Jump range with a full tank (unladenMass + fuel)",
"type": "number",
"minimum": 0
},
"ladenMass": { "ladenMass": {
"description": "Mass of the Ship + fuel + cargo (hull + all components + fuel tank + cargo capacity)", "description": "Mass of the Ship + fuel + cargo (hull + all components + fuel tank + cargo capacity)",
"type": "number", "type": "number",

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.io/schemas/ship-loadout/2.json#", "id": "https://coriolis.edcd.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",
@@ -254,11 +254,6 @@
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1
}, },
"fullTankRange": {
"description": "Single Jump range with a full tank (unladenMass + fuel)",
"type": "number",
"minimum": 0
},
"ladenMass": { "ladenMass": {
"description": "Mass of the Ship + fuel + cargo (hull + all components + fuel tank + cargo capacity)", "description": "Mass of the Ship + fuel + cargo (hull + all components + fuel tank + cargo capacity)",
"type": "number", "type": "number",

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.io/schemas/ship-loadout/3.json#", "id": "https://coriolis.edcd.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",
@@ -258,11 +258,6 @@
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1
}, },
"fullTankRange": {
"description": "Single Jump range with a full tank (unladenMass + fuel)",
"type": "number",
"minimum": 0
},
"ladenMass": { "ladenMass": {
"description": "Mass of the Ship + fuel + cargo (hull + all components + fuel tank + cargo capacity)", "description": "Mass of the Ship + fuel + cargo (hull + all components + fuel tank + cargo capacity)",
"type": "number", "type": "number",

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.io/schemas/ship-loadout/4.json#", "id": "https://coriolis.edcd.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",
@@ -300,11 +300,6 @@
"type": "integer", "type": "integer",
"minimum": 1 "minimum": 1
}, },
"fullTankRange": {
"description": "Single Jump range with a full tank (unladenMass + fuel)",
"type": "number",
"minimum": 0
},
"ladenMass": { "ladenMass": {
"description": "Mass of the Ship + fuel + cargo (hull + all components + fuel tank + cargo capacity)", "description": "Mass of the Ship + fuel + cargo (hull + all components + fuel tank + cargo capacity)",
"type": "number", "type": "number",

View File

@@ -2,21 +2,6 @@ console.log('Hello from sw.js');
if (workbox) { if (workbox) {
console.log('Yay! Workbox is loaded 🎉'); console.log('Yay! Workbox is loaded 🎉');
workbox.precaching.precacheAndRoute(self.__precacheManifest);
workbox.routing.registerNavigationRoute('/index.html');
workbox.routing.registerRoute(
new RegExp('/(.*?)'),
workbox.strategies.staleWhileRevalidate({
plugins: [
new workbox.cacheableResponse.Plugin({
statuses: [0, 200]
})
]
})
);
workbox.routing.registerRoute( workbox.routing.registerRoute(
new RegExp('https://fonts.(?:googleapis|gstatic).com/(.*)'), new RegExp('https://fonts.(?:googleapis|gstatic).com/(.*)'),
workbox.strategies.cacheFirst({ workbox.strategies.cacheFirst({
@@ -31,7 +16,24 @@ if (workbox) {
] ]
}) })
); );
workbox.routing.registerRoute(
/\.(?:png|gif|jpg|jpeg|svg)$/,
workbox.strategies.cacheFirst({
cacheName: 'images',
plugins: [
new workbox.expiration.Plugin({
maxEntries: 60,
maxAgeSeconds: 30 * 24 * 60 * 60 // 30 Days
})
]
})
);
workbox.routing.registerRoute(
/\.(?:js|css)$/,
workbox.strategies.staleWhileRevalidate({
cacheName: 'static-resources'
})
);
try { try {
workbox.googleAnalytics.initialize(); workbox.googleAnalytics.initialize();
} catch (e) { } catch (e) {
@@ -41,17 +43,38 @@ if (workbox) {
console.log('Boo! Workbox didn\'t load 😬'); console.log('Boo! Workbox didn\'t load 😬');
} }
self.addEventListener('message', event => {
if (!event.data) {
return;
}
switch (event.data) { self.addEventListener('message', event => {
case 'skipWaiting': if (!event.data) {
self.skipWaiting(); return;
break; }
default:
// NOOP switch (event.data) {
break; case 'skipWaiting':
} self.skipWaiting();
break;
default:
// NOOP
break;
}
});
self.addEventListener('fetch', function(event) {
console.log('Handling fetch event for', event.request.url);
event.respondWith(
caches.match(event.request).then(function(response) {
if (response) {
return response;
}
return fetch(event.request)
.then(function(response) {
return response;
})
.catch(function(error) {
return caches.match(OFFLINE_URL);
});
})
);
}); });

View File

@@ -1 +0,0 @@
"use strict";window.XdUtils=window.XdUtils||function(){function a(a,b){var c,d=b||{};for(c in a)a.hasOwnProperty(c)&&(d[c]=a[c]);return d}return{extend:a}}(),function(){function a(a,b){var c=XdUtils.extend(b,l);c.id=a,parent.postMessage(JSON.stringify(c),"*")}function b(b,c){a(b,{key:c,value:localStorage.getItem(c)})}function c(b,c,d){localStorage.setItem(c,d),a(b,{success:localStorage.getItem(c)===d})}function d(b,c){localStorage.removeItem(c),a(b,{})}function e(b,c){a(b,{key:localStorage.key(c)})}function f(b){a(b,{size:JSON.stringify(localStorage).length})}function g(b){a(b,{length:localStorage.length})}function h(b){localStorage.clear(),a(b,{})}function i(a){var i;try{i=JSON.parse(a.data)}catch(a){}i&&i.namespace===k&&("set"===i.action?c(i.id,i.key,i.value):"get"===i.action?b(i.id,i.key):"remove"===i.action?d(i.id,i.key):"key"===i.action?e(i.id,i.key):"size"===i.action?f(i.id):"length"===i.action?g(i.id):"clear"===i.action&&h(i.id))}function j(){var a={namespace:k,id:"iframe-ready"};parent.postMessage(JSON.stringify(a),"*")}var k="cross-domain-local-message",l={namespace:k};window.addEventListener?window.addEventListener("message",i,!1):window.attachEvent("onmessage",i),j()}();

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,15 +22,17 @@ module.exports = {
}, },
optimization: { optimization: {
minimize: false, minimize: false,
usedExports: true splitChunks: {
chunks: 'all'
}
}, },
output: { output: {
path: path.join(__dirname, 'build'), path: path.join(__dirname, 'build'),
chunkFilename: '[name].bundle.js', filename: 'app.js',
publicPath: '/' publicPath: '/'
}, },
plugins: [ plugins: [
new CopyWebpackPlugin(['src/.htaccess', 'src/iframe.html', 'src/xdLocalStoragePostMessageApi.min.js']), new CopyWebpackPlugin(['src/.htaccess']),
// new webpack.optimize.CommonsChunkPlugin({ // new webpack.optimize.CommonsChunkPlugin({
// name: 'lib', // name: 'lib',
// filename: 'lib.js' // filename: 'lib.js'
@@ -54,10 +56,7 @@ 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

@@ -4,35 +4,33 @@ const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin'); const ExtractTextPlugin = require('extract-text-webpack-plugin');
const { InjectManifest } = require('workbox-webpack-plugin'); const { InjectManifest } = require('workbox-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin');
const { BugsnagSourceMapUploaderPlugin, BugsnagBuildReporterPlugin } = require('webpack-bugsnag-plugins'); const { BugsnagSourceMapUploaderPlugin } = require('webpack-bugsnag-plugins');
const pkgJson = require('./package'); const pkgJson = require('./package');
const buildDate = new Date(); 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'),
chunkFilename: '[name].bundle.js', filename: '[name].[hash].js',
publicPath: '/', publicPath: '/',
globalObject: 'this' globalObject: 'this'
}, },
mode: 'production', mode: 'production',
optimization: { optimization: {
minimize: true, minimize: true,
usedExports: true splitChunks: {
chunks: 'all'
}
}, },
plugins: [ plugins: [
new CopyWebpackPlugin(['src/.htaccess', { from: 'src/schemas', to: 'schemas' }, { new CopyWebpackPlugin(['src/.htaccess', { from: 'src/schemas', to: 'schemas' }, {from: 'src/images/logo/*', flatten: true, to: ''}]),
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'
@@ -50,13 +48,8 @@ module.exports = {
disable: false, disable: false,
allChunks: true allChunks: true
}), }),
// new BugsnagBuildReporterPlugin({
// apiKey: 'ba9fae819372850fb660755341fa6ef5',
// appVersion: `${pkgJson.version}-${buildDate.toISOString()}`
// }, { /* opts */ }),
// new BugsnagSourceMapUploaderPlugin({ // new BugsnagSourceMapUploaderPlugin({
// apiKey: 'ba9fae819372850fb660755341fa6ef5', // apiKey: 'ba9fae819372850fb660755341fa6ef5',
// overwrite: true,
// appVersion: `${pkgJson.version}-${buildDate.toISOString()}` // appVersion: `${pkgJson.version}-${buildDate.toISOString()}`
// }), // }),
new InjectManifest({ new InjectManifest({
@@ -68,10 +61,7 @@ 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' },