Compare commits

...

44 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
William Blythe
a599b1a076 remove d3 rollup that doesnt seem to serve a purpose 2018-10-22 09:21:33 +11:00
willyb321
3a6ac818c2 reload anyway 2018-10-21 10:17:53 +11:00
willyb321
6f077d4c41 use register-service-worker 2018-10-21 10:16:31 +11:00
willyb321
9c767c928c fix icons and manifest
was not flattened
2018-10-21 10:03:01 +11:00
willyb321
515f4ad3da sw work 2018-10-21 09:50:15 +11:00
willyb321
4fcf074595 dont git reset in docker 2018-10-21 08:37:08 +11:00
willyb321
e5f8153a34 add announcements to menu 2018-10-21 08:27:53 +11:00
William
571854a11c Merge pull request #405 from EDCD/feature/code-quality
Cosmetical improvements
2018-10-20 11:00:58 +11:00
William
1f22f249a1 Merge pull request #407 from EDCD/feature/number-inputs
Enhance number editing
2018-10-20 11:00:47 +11:00
felixlinker
718ac0a514 Enhance number editing 2018-10-13 22:29:12 +01:00
willyb321
8f089cb1ee hopefully fix some sw bugs 2018-10-13 09:22:16 +11:00
felixlinker
d19a7276dd Optimized imports 2018-10-05 00:06:57 +01:00
felixlinker
10fffe67fc Code style fixes 2018-10-05 00:06:33 +01:00
William Blythe
f0bf8e8ce2 import babel polyfill 2018-10-03 14:17:56 +10:00
William
598cf8d677 Merge pull request #404 from EDCD/feature/fix-imports
Fix journal import
2018-10-02 10:14:02 +10:00
felixlinker
90f03de3fe Fix journal import 2018-10-01 22:24:18 +01:00
William
e0766f4424 Merge pull request #400 from EDCD/feature/fixes
Various fixes
2018-09-30 06:21:06 +10:00
felixlinker
28a90768e4 Start searching for available slots at 0 because the Type9 starts there 2018-09-29 20:26:40 +01:00
felixlinker
f3d917ccbe Fixed trailing zero check in journal style import 2018-09-29 20:04:40 +01:00
felixlinker
7e5d52385d Fixed loops in journal style import 2018-09-29 20:03:58 +01:00
felixlinker
4368015dc0 Fixed variable assignement that should be a comparison 2018-09-29 17:45:45 +01:00
felixlinker
1201da1811 Differentiate between uneditable and un-highlighted properties in the modifications menu 2018-09-29 16:40:36 +01:00
felixlinker
d195b568b0 eslint indentation fixes 2018-09-29 01:03:00 +01:00
felixlinker
c9866c146b Stop dividing burst two times by 100 2018-09-29 00:55:37 +01:00
felixlinker
5d52809d0d Catch undefined values in ship build parsing 2018-09-28 23:28:46 +01:00
willyb321
8f0cca4fd9 fix 2018-09-29 08:06:15 +10:00
willyb321
e46bb425fe minimize in prod 2018-09-29 07:50:36 +10:00
willyb321
06dc110025 migrate to babel 7, webpack 4, dep updates, lots 2018-09-29 07:49:04 +10:00
felixlinker
e9c34c636a Don't filter values but map falsy ones to zero 2018-09-28 22:35:07 +01:00
felixlinker
59d38cbd33 use react-extras instead of auto-bind 2018-09-26 23:15:09 +01:00
William
51f5188efc Merge pull request #393 from EDCD/feature/mc-pips
Add multicrew pips
2018-09-25 07:15:44 +10:00
felixlinker
be8934da80 Added multi crew pips 2018-09-23 22:30:05 +01:00
felixlinker
18d78b3089 Added auto-bind dependency 2018-09-23 22:29:45 +01:00
willyb321
b1ff4e84f7 Merge branch 'master' into develop 2018-09-23 11:42:45 +10:00
willyb321
bed2ede701 Merge branch 'release/3.0.0' 2018-09-23 11:42:44 +10:00
willyb321
124bd62d2c 3.0.0 2018-09-23 11:42:38 +10:00
willyb321
975846f4ab Merge branch 'master' into develop 2018-09-23 11:41:23 +10:00
willyb321
3f73f9be10 Merge branch 'release/2.9.18' 2018-09-23 11:41:22 +10:00
68 changed files with 916 additions and 8154 deletions

View File

@@ -1,3 +1,34 @@
{ {
"presets": ["env", "react", "stage-0"] "presets": [
["@babel/preset-env", {"modules": "commonjs"}],
"@babel/preset-react"
],
"plugins": [
"@babel/plugin-syntax-dynamic-import",
"@babel/plugin-syntax-import-meta",
["@babel/plugin-proposal-class-properties", { "loose": true }],
"@babel/plugin-proposal-json-strings",
[
"@babel/plugin-proposal-decorators",
{
"legacy": true
}
],
"@babel/plugin-proposal-function-sent",
"@babel/plugin-proposal-export-namespace-from",
"@babel/plugin-proposal-numeric-separator",
"@babel/plugin-proposal-throw-expressions",
"@babel/plugin-proposal-export-default-from",
"@babel/plugin-proposal-logical-assignment-operators",
"@babel/plugin-proposal-optional-chaining",
[
"@babel/plugin-proposal-pipeline-operator",
{
"proposal": "minimal"
}
],
"@babel/plugin-proposal-nullish-coalescing-operator",
"@babel/plugin-proposal-do-expressions",
"@babel/plugin-proposal-function-bind"
]
} }

View File

@@ -17,13 +17,11 @@ RUN npm i -g npm
# Set up coriolis-data # Set up coriolis-data
WORKDIR /src/app/coriolis-data WORKDIR /src/app/coriolis-data
RUN git fetch --all RUN git fetch --all
RUN git reset --hard origin/$BRANCH
RUN npm install --no-package-lock RUN npm install --no-package-lock
RUN npm start RUN npm start
WORKDIR /src/app/coriolis WORKDIR /src/app/coriolis
RUN git fetch --all RUN git fetch --all
RUN git reset --hard origin/$BRANCH
RUN npm install --no-package-lock RUN npm install --no-package-lock
RUN npm run build RUN npm run build

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 = {};
} }
} }
const LOCAL_STORAGE = new LocalStorage();
window.addEventListener = function(eventName, listener) { 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() {
it("can build all ships", function() {
for (let s in Ships) { for (let s in Ships) {
it("can build " + s, function() {
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';

11
d3-funcs.js vendored
View File

@@ -1,11 +0,0 @@
export {
axisBottom,
axisLeft,
axisTop,
formatLocale,
line,
scaleBand,
scaleLinear,
scaleOrdinal,
select
} from 'd3';

7312
d3.js vendored

File diff suppressed because it is too large Load Diff

3
d3.min.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,6 @@
{ {
"name": "coriolis_shipyard", "name": "coriolis_shipyard",
"version": "2.9.18", "version": "3.0.0",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/EDCD/coriolis" "url": "https://github.com/EDCD/coriolis"
@@ -11,7 +11,6 @@
"engine": "node >= 4.8.1", "engine": "node >= 4.8.1",
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"prepublish": "rollup -c && uglifyjs d3.js -c -m -o d3.min.js",
"extract-translations": "grep -hroE \"(translate\\('[^']+'\\))|(tip.bind\\(null, '[^']+')\" src/* | grep -oE \"'[^']+'\" | grep -oE \"[^']+\" | sort -u -f", "extract-translations": "grep -hroE \"(translate\\('[^']+'\\))|(tip.bind\\(null, '[^']+')\" src/* | grep -oE \"'[^']+'\" | grep -oE \"[^']+\" | sort -u -f",
"clean": "rimraf build", "clean": "rimraf build",
"start": "node devServer.js", "start": "node devServer.js",
@@ -56,18 +55,35 @@
] ]
}, },
"devDependencies": { "devDependencies": {
"appcache-webpack-plugin": "^1.3.0", "@babel/core": "^7.0.0",
"babel-core": "*", "@babel/plugin-proposal-class-properties": "^7.0.0",
"babel-eslint": "*", "@babel/plugin-proposal-decorators": "^7.0.0",
"babel-jest": "*", "@babel/plugin-proposal-do-expressions": "^7.0.0",
"babel-loader": "*", "@babel/plugin-proposal-export-default-from": "^7.0.0",
"babel-preset-env": "*", "@babel/plugin-proposal-export-namespace-from": "^7.0.0",
"babel-preset-react": "*", "@babel/plugin-proposal-function-bind": "^7.0.0",
"babel-preset-stage-0": "*", "@babel/plugin-proposal-function-sent": "^7.0.0",
"create-react-class": "^15.6.2", "@babel/plugin-proposal-json-strings": "^7.0.0",
"cross-env": "^5.1.4", "@babel/plugin-proposal-logical-assignment-operators": "^7.0.0",
"css-loader": "^0.28.0", "@babel/plugin-proposal-nullish-coalescing-operator": "^7.0.0",
"d3-selection": "1", "@babel/plugin-proposal-numeric-separator": "^7.0.0",
"@babel/plugin-proposal-optional-chaining": "^7.0.0",
"@babel/plugin-proposal-pipeline-operator": "^7.0.0",
"@babel/plugin-proposal-throw-expressions": "^7.0.0",
"@babel/plugin-syntax-dynamic-import": "^7.0.0",
"@babel/plugin-syntax-import-meta": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"@babel/preset-react": "^7.0.0",
"appcache-webpack-plugin": "^1.4.0",
"babel-core": "^7.0.0-bridge.0",
"babel-eslint": "^10.0.1",
"babel-jest": "^23.6.0",
"babel-loader": "^8.0.0",
"copy-webpack-plugin": "^4.5.2",
"create-react-class": "^15.6.3",
"cross-env": "^5.2.0",
"css-loader": "^1.0.0",
"d3-selection": "^1.3.2",
"esdoc": "^1.1.0", "esdoc": "^1.1.0",
"esdoc-custom-theme": "^1.4.2", "esdoc-custom-theme": "^1.4.2",
"esdoc-ecmascript-proposal-plugin": "^1.0.0", "esdoc-ecmascript-proposal-plugin": "^1.0.0",
@@ -75,51 +91,54 @@
"esdoc-publish-html-plugin": "^1.1.2", "esdoc-publish-html-plugin": "^1.1.2",
"esdoc-react-plugin": "^1.0.1", "esdoc-react-plugin": "^1.0.1",
"esdoc-standard-plugin": "^1.0.0", "esdoc-standard-plugin": "^1.0.0",
"eslint": "3.19.0", "eslint": "^5.6.0",
"eslint-plugin-react": "^6.10.3", "eslint-plugin-react": "^7.11.1",
"expose-loader": "^0.7.3", "expose-loader": "^0.7.5",
"express": "^4.15.2", "express": "^4.16.3",
"extract-text-webpack-plugin": "2.1.0", "extract-text-webpack-plugin": "^4.0.0-beta.0",
"file-loader": "^0.11.1", "file-loader": "^2.0.0",
"html-webpack-plugin": "^2.28.0", "html-webpack-plugin": "^3.0.7",
"jest-cli": "^21.2.1", "jest-cli": "^23.6.0",
"jsen": "^0.6.4", "jsen": "^0.6.4",
"json-loader": "^0.5.4", "json-loader": "^0.5.4",
"less": "^2.7.2", "less": "^3.8.1",
"less-loader": "^4.0.3", "less-loader": "^4.1.0",
"react-addons-perf": "^15.4.2", "react-addons-perf": "^15.4.2",
"react-container-dimensions": "^1.4.1", "react-container-dimensions": "^1.4.1",
"react-testutils-additions": "^15.2.0", "react-testutils-additions": "^16.0.0",
"react-transition-group": "^1.1.2", "react-transition-group": "^2.5.0",
"rimraf": "^2.6.1", "rimraf": "^2.6.1",
"rollup": "0.41", "rollup": "^0.66.2",
"rollup-plugin-node-resolve": "3", "rollup-plugin-node-resolve": "^3.4.0",
"style-loader": "^0.16.1", "style-loader": "^0.23.0",
"uglify-js": "^2.4.11", "uglify-js": "^3.4.9",
"url-loader": "^0.5.8", "url-loader": "^1.1.1",
"webpack": "^2.4.1", "webpack": "^4.20.2",
"webpack-bugsnag-plugins": "^1.1.1", "webpack-bugsnag-plugins": "^1.2.2",
"webpack-dev-server": "^2.4.4", "webpack-cli": "^3.1.1",
"webpack-dev-server": "^3.1.9",
"webpack-notifier": "^1.6.0", "webpack-notifier": "^1.6.0",
"workbox-webpack-plugin": "^3.4.1" "workbox-webpack-plugin": "^3.6.1"
}, },
"dependencies": { "dependencies": {
"babel-polyfill": "*", "@babel/polyfill": "^7.0.0",
"browserify-zlib-next": "^1.0.1", "browserify-zlib-next": "^1.0.1",
"classnames": "^2.2.5", "classnames": "^2.2.6",
"coriolis-data": "../coriolis-data", "coriolis-data": "../coriolis-data",
"d3": "4.8.0", "d3": "^5.7.0",
"detect-browser": "^1.7.0", "detect-browser": "^3.0.1",
"fbemitter": "^2.1.1", "fbemitter": "^2.1.1",
"lodash": "^4.17.10", "lodash": "^4.17.11",
"lz-string": "^1.4.4", "lz-string": "^1.4.4",
"pako": "^1.0.6", "pako": "^1.0.6",
"prop-types": "^15.5.8", "prop-types": "^15.6.2",
"react": "^15.5.4", "react": "^15.5.4",
"react-dom": "^15.5.4", "react-dom": "^15.5.4",
"react-extras": "^0.7.1",
"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": "^0.22.3", "recharts": "^1.2.0",
"superagent": "^3.5.2" "register-service-worker": "^1.5.2",
"superagent": "^3.8.3"
} }
} }

View File

@@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Router from './Router'; import Router from './Router';
import { register } from 'register-service-worker'
import { 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';
@@ -22,12 +23,12 @@ import ShipyardPage from './pages/ShipyardPage';
import ErrorDetails from './pages/ErrorDetails'; import ErrorDetails from './pages/ErrorDetails';
const zlib = require('pako'); const zlib = require('pako');
const request = require('superagent');
/** /**
* Coriolis App * Coriolis App
*/ */
export default class Coriolis extends React.Component { export default class Coriolis extends React.Component {
static childContextTypes = { static childContextTypes = {
closeMenu: PropTypes.func.isRequired, closeMenu: PropTypes.func.isRequired,
hideModal: PropTypes.func.isRequired, hideModal: PropTypes.func.isRequired,
@@ -66,11 +67,12 @@ export default class Coriolis extends React.Component {
this.state = { this.state = {
noTouch: !('ontouchstart' in window || navigator.msMaxTouchPoints || navigator.maxTouchPoints), noTouch: !('ontouchstart' in window || navigator.msMaxTouchPoints || navigator.maxTouchPoints),
page: null, page: null,
announcements: [],
language: getLanguage(Persist.getLangCode()), language: getLanguage(Persist.getLangCode()),
route: {}, route: {},
sizeRatio: Persist.getSizeRatio() sizeRatio: Persist.getSizeRatio()
}; };
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));
@@ -109,6 +111,14 @@ export default class Coriolis extends React.Component {
} }
} }
_getAnnouncements() {
return request.get('https://orbis.zone/api/announcement')
.query({showInCoriolis: true})
.then(announces => {
this.setState({ announcements: announces.body })
})
}
/** /**
* Updates / Sets the page and route context * Updates / Sets the page and route context
* @param {[type]} page The page to be shown * @param {[type]} page The page to be shown
@@ -340,39 +350,29 @@ export default class Coriolis extends React.Component {
// *Don't* register service worker file in, e.g., a scripts/ sub-directory! // *Don't* register service worker file in, e.g., a scripts/ sub-directory!
// See https://github.com/slightlyoff/ServiceWorker/issues/468 // See https://github.com/slightlyoff/ServiceWorker/issues/468
const self = this; const self = this;
navigator.serviceWorker.register('/service-worker.js').then(function(reg) { register('/service-worker.js', {
// updatefound is fired if service-worker.js changes. ready (registration) {
reg.onupdatefound = function() { console.log('Service worker is active.')
// The updatefound event implies that reg.installing is set; see },
// https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#service-worker-container-updatefound-event registered (registration) {
var installingWorker = reg.installing; console.log('Service worker has been registered.')
},
installingWorker.onstatechange = function() { cached (registration) {
switch (installingWorker.state) { console.log('Content has been cached for offline use.')
case 'installed': },
if (navigator.serviceWorker.controller) { updatefound (registration) {
// At this point, the old content will have been purged and the fresh content will console.log('New content is downloading.')
// have been added to the cache. },
// It's the perfect time to display a "New content is available; please refresh." updated (registration) {
// message in the page's interface. self.setState({ appCacheUpdate: true });
console.log('New or updated content is available.'); console.log('New content is available; please refresh.')
self.setState({ appCacheUpdate: true }); // Browser downloaded a new app cache. },
} else { offline () {
// At this point, everything has been precached. console.log('No internet connection found. App is running in offline mode.')
// It's the perfect time to display a "Content is cached for offline use." message. },
console.log('Content is now available offline!'); error (error) {
self.setState({ appCacheUpdate: true }); // Browser downloaded a new app cache. console.error('Error during service worker registration:', error)
} }
break;
case 'redundant':
console.error('The installing service worker became redundant.');
break;
}
};
};
}).catch(function(e) {
console.error('Error during service worker registration:', e);
}); });
}); });
} }
@@ -395,7 +395,7 @@ export default class Coriolis extends React.Component {
return <div style={{ minHeight: '100%' }} onClick={this._closeMenu} return <div style={{ minHeight: '100%' }} onClick={this._closeMenu}
className={this.state.noTouch ? 'no-touch' : null}> className={this.state.noTouch ? 'no-touch' : null}>
<Header appCacheUpdate={this.state.appCacheUpdate} currentMenu={currentMenu}/> <Header announcements={this.state.announcements} appCacheUpdate={this.state.appCacheUpdate} currentMenu={currentMenu}/>
{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}

View File

@@ -1,6 +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'); ReactGA.initialize('UA-55840909-18', { testMode: isTest });
let standalone = undefined; let standalone = undefined;
/** /**

View File

@@ -16,7 +16,6 @@ function isActive(href) {
* Active Link - Highlighted when URL matches window location * Active Link - Highlighted when URL matches window location
*/ */
export default class ActiveLink extends Link { export default class ActiveLink extends Link {
/** /**
* Renders the component * Renders the component
* @return {React.Component} The active link * @return {React.Component} The active link
@@ -29,5 +28,4 @@ export default class ActiveLink extends Link {
return <a {...this.props} className={className} onClick={this.handler}>{this.props.children}</a>; return <a {...this.props} className={className} onClick={this.handler}>{this.props.children}</a>;
} }
} }

View File

@@ -0,0 +1,31 @@
import React from 'react';
import PropTypes from 'prop-types';
import { autoBind } from 'react-extras';
/**
* Announcement component
*/
export default class Announcement extends React.Component {
static propTypes = {
text: PropTypes.string
};
/**
* Constructor
* @param {Object} props React Component properties
*/
constructor(props) {
super(props);
autoBind(this);
}
/**
* Renders the announcement
* @return {React.Component} A href element
*/
render() {
return <p>{this.props.text}</p>;
}
}

View File

@@ -108,7 +108,6 @@ const CATEGORIES = {
* Available modules menu * Available modules menu
*/ */
export default class AvailableModulesMenu extends TranslatedComponent { export default class AvailableModulesMenu extends TranslatedComponent {
static propTypes = { static propTypes = {
modules: PropTypes.oneOfType([PropTypes.object, PropTypes.array]).isRequired, modules: PropTypes.oneOfType([PropTypes.object, PropTypes.array]).isRequired,
onSelect: PropTypes.func.isRequired, onSelect: PropTypes.func.isRequired,
@@ -511,5 +510,4 @@ export default class AvailableModulesMenu extends TranslatedComponent {
</div> </div>
); );
} }
} }

View File

@@ -38,7 +38,6 @@ function insertLinebreaks(d) {
* Bar Chart * Bar Chart
*/ */
export default class BarChart extends TranslatedComponent { export default class BarChart extends TranslatedComponent {
static defaultProps = { static defaultProps = {
colors: ['#7b6888', '#6b486b', '#3182bd', '#a05d56', '#d0743c'], colors: ['#7b6888', '#6b486b', '#3182bd', '#a05d56', '#d0743c'],
labels: null, labels: null,

View File

@@ -1,13 +1,6 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import TranslatedComponent from './TranslatedComponent'; import TranslatedComponent from './TranslatedComponent';
import { Ships } from 'coriolis-data/dist';
import { nameComparator } from '../utils/SlotFunctions';
import { Pip } from './SvgIcons';
import LineChart from '../components/LineChart';
import Slider from '../components/Slider';
import * as ModuleUtils from '../shipyard/ModuleUtils';
import Module from '../shipyard/Module';
/** /**
* Boost displays a boost button that toggles bosot * Boost displays a boost button that toggles bosot

View File

@@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import TranslatedComponent from './TranslatedComponent'; import TranslatedComponent from './TranslatedComponent';
import { Ships } from 'coriolis-data/dist';
import Slider from '../components/Slider'; import Slider from '../components/Slider';
/** /**

View File

@@ -10,7 +10,6 @@ import { outfitURL } from '../utils/UrlGenerators';
* Comparison Table * Comparison Table
*/ */
export default class ComparisonTable extends TranslatedComponent { export default class ComparisonTable extends TranslatedComponent {
static propTypes = { static propTypes = {
facets: PropTypes.array.isRequired, facets: PropTypes.array.isRequired,
builds: PropTypes.array.isRequired, builds: PropTypes.array.isRequired,

View File

@@ -13,7 +13,6 @@ import { ShoppingIcon } from '../components/SvgIcons';
* Cost Section * Cost Section
*/ */
export default class CostSection extends TranslatedComponent { export default class CostSection extends TranslatedComponent {
static propTypes = { static propTypes = {
ship: PropTypes.object.isRequired, ship: PropTypes.object.isRequired,
code: PropTypes.string.isRequired, code: PropTypes.string.isRequired,

View File

@@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import TranslatedComponent from './TranslatedComponent'; import TranslatedComponent from './TranslatedComponent';
import { Ships } from 'coriolis-data/dist';
import Slider from '../components/Slider'; import Slider from '../components/Slider';
/** /**

View File

@@ -1,12 +1,7 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import TranslatedComponent from './TranslatedComponent'; import TranslatedComponent from './TranslatedComponent';
import { Ships } from 'coriolis-data/dist';
import { nameComparator } from '../utils/SlotFunctions';
import LineChart from '../components/LineChart'; import LineChart from '../components/LineChart';
import Slider from '../components/Slider';
import * as ModuleUtils from '../shipyard/ModuleUtils';
import Module from '../shipyard/Module';
import * as Calc from '../shipyard/Calculations'; import * as Calc from '../shipyard/Calculations';
/** /**

View File

@@ -1,12 +1,7 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import TranslatedComponent from './TranslatedComponent'; import TranslatedComponent from './TranslatedComponent';
import { Ships } from 'coriolis-data/dist';
import { nameComparator } from '../utils/SlotFunctions';
import LineChart from '../components/LineChart'; import LineChart from '../components/LineChart';
import Slider from '../components/Slider';
import * as ModuleUtils from '../shipyard/ModuleUtils';
import Module from '../shipyard/Module';
import * as Calc from '../shipyard/Calculations'; import * as Calc from '../shipyard/Calculations';
/** /**

View File

@@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import TranslatedComponent from './TranslatedComponent'; import TranslatedComponent from './TranslatedComponent';
import { Ships } from 'coriolis-data/dist';
import Slider from '../components/Slider'; import Slider from '../components/Slider';
/** /**

View File

@@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import SlotSection from './SlotSection'; import SlotSection from './SlotSection';
import HardpointSlot from './HardpointSlot'; import HardpointSlot from './HardpointSlot';
import cn from 'classnames';
import { MountFixed, MountGimballed, MountTurret } from '../components/SvgIcons'; import { MountFixed, MountGimballed, MountTurret } from '../components/SvgIcons';
import { stopCtxPropagation } from '../utils/UtilityFunctions'; import { stopCtxPropagation } from '../utils/UtilityFunctions';

View File

@@ -16,6 +16,7 @@ import ModalExport from './ModalExport';
import ModalHelp from './ModalHelp'; import ModalHelp from './ModalHelp';
import ModalImport from './ModalImport'; import ModalImport from './ModalImport';
import Slider from './Slider'; import Slider from './Slider';
import Announcement from './Announcement';
import { outfitURL } from '../utils/UrlGenerators'; import { outfitURL } from '../utils/UrlGenerators';
const SIZE_MIN = 0.65; const SIZE_MIN = 0.65;
@@ -76,8 +77,11 @@ export default class Header extends TranslatedComponent {
this._openShips = this._openMenu.bind(this, 's'); this._openShips = this._openMenu.bind(this, 's');
this._openBuilds = this._openMenu.bind(this, 'b'); this._openBuilds = this._openMenu.bind(this, 'b');
this._openComp = this._openMenu.bind(this, 'comp'); this._openComp = this._openMenu.bind(this, 'comp');
this._openAnnounce = this._openMenu.bind(this, 'announce');
this._getAnnouncementsMenu = this._getAnnouncementsMenu.bind(this);
this._openSettings = this._openMenu.bind(this, 'settings'); this._openSettings = this._openMenu.bind(this, 'settings');
this._showHelp = this._showHelp.bind(this); this._showHelp = this._showHelp.bind(this);
this.update = this.update.bind(this);
this.languageOptions = []; this.languageOptions = [];
this.insuranceOptions = []; this.insuranceOptions = [];
this.state = { this.state = {
@@ -411,6 +415,29 @@ export default class Header extends TranslatedComponent {
); );
} }
/**
* Generate the announcement menu
* @return {React.Component} Menu
*/
_getAnnouncementsMenu() {
let announcements;
let translate = this.context.language.translate;
if (this.props.announcements) {
announcements = [];
for (let announce of this.props.announcements) {
announcements.push(<Announcement text={announce.message} />);
announcements.push(<hr/>);
}
}
return (
<div className='menu-list' onClick={ (e) => e.stopPropagation() } style={{ whiteSpace: 'nowrap' }}>
{announcements}
<hr />
</div>
);
}
/** /**
* Generate the settings menu * Generate the settings menu
* @return {React.Component} Menu * @return {React.Component} Menu
@@ -534,6 +561,15 @@ export default class Header extends TranslatedComponent {
} }
} }
async update() {
const reg = await navigator.serviceWorker.getRegistration();
if (!reg || !reg.waiting) {
return window.location.reload();
}
reg.waiting.postMessage('skipWaiting');
window.location.reload();
}
/** /**
* Render the header * Render the header
* @return {React.Component} Header * @return {React.Component} Header
@@ -544,7 +580,7 @@ export default class Header extends TranslatedComponent {
let hasBuilds = Persist.hasBuilds(); let hasBuilds = Persist.hasBuilds();
return ( return (
<header> <header>
{this.props.appCacheUpdate && <div id="app-update" onClick={() => window.location.reload() }>{translate('PHRASE_UPDATE_RDY')}</div>} {this.props.appCacheUpdate && <div id="app-update" onClick={this.update}>{translate('PHRASE_UPDATE_RDY')}</div>}
{this.props.appCacheUpdate ? <a className={'view-changes'} href={'https://github.com/EDCD/coriolis/compare/edcd:develop@{' + window.CORIOLIS_DATE + '}...edcd:develop'} target="_blank"> {this.props.appCacheUpdate ? <a className={'view-changes'} href={'https://github.com/EDCD/coriolis/compare/edcd:develop@{' + window.CORIOLIS_DATE + '}...edcd:develop'} target="_blank">
{'View Release Changes'} {'View Release Changes'}
</a> : null} </a> : null}
@@ -571,6 +607,13 @@ export default class Header extends TranslatedComponent {
{openedMenu == 'comp' ? this._getComparisonsMenu() : null} {openedMenu == 'comp' ? this._getComparisonsMenu() : null}
</div> </div>
<div className='l menu'>
<div className={cn('menu-header', { selected: openedMenu == 'announce', disabled: this.props.announcements.length === 0})} onClick={this.props.announcements.length !== 0 && this._openAnnounce}>
<span className='menu-item-label'>{translate('announcements')}</span>
</div>
{openedMenu == 'announce' ? this._getAnnouncementsMenu() : null}
</div>
{window.location.origin.search('.edcd.io') >= 0 ? {window.location.origin.search('.edcd.io') >= 0 ?
<div className='l menu'> <div className='l menu'>
<a href="https://youtu.be/4SvnLcefhtI" target="_blank"> <a href="https://youtu.be/4SvnLcefhtI" target="_blank">

View File

@@ -1,5 +1,4 @@
import React from 'react'; import React from 'react';
import cn from 'classnames';
import SlotSection from './SlotSection'; import SlotSection from './SlotSection';
import InternalSlot from './InternalSlot'; import InternalSlot from './InternalSlot';
import * as ModuleUtils from '../shipyard/ModuleUtils'; import * as ModuleUtils from '../shipyard/ModuleUtils';

View File

@@ -1,12 +1,8 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import TranslatedComponent from './TranslatedComponent'; import TranslatedComponent from './TranslatedComponent';
import { Ships } from 'coriolis-data/dist';
import { nameComparator } from '../utils/SlotFunctions';
import LineChart from '../components/LineChart'; import LineChart from '../components/LineChart';
import Slider from '../components/Slider'; import Slider from '../components/Slider';
import * as ModuleUtils from '../shipyard/ModuleUtils';
import Module from '../shipyard/Module';
import * as Calc from '../shipyard/Calculations'; import * as Calc from '../shipyard/Calculations';
/** /**

View File

@@ -6,7 +6,6 @@ import Persist from '../stores/Persist';
* Delete All saved data modal * Delete All saved data modal
*/ */
export default class ModalDeleteAll extends TranslatedComponent { export default class ModalDeleteAll extends TranslatedComponent {
/** /**
* Delete everything and hide the modal * Delete everything and hide the modal
*/ */

View File

@@ -9,7 +9,6 @@ import { isValueBeneficial } from '../utils/BlueprintFunctions';
* Modification * Modification
*/ */
export default class Modification extends TranslatedComponent { export default class Modification extends TranslatedComponent {
static propTypes = { static propTypes = {
ship: PropTypes.object.isRequired, ship: PropTypes.object.isRequired,
m: PropTypes.object.isRequired, m: PropTypes.object.isRequired,
@@ -39,10 +38,24 @@ export default class Modification extends TranslatedComponent {
* in a value by hand * in a value by hand
*/ */
_updateValue(value) { _updateValue(value) {
this.setState({ value });
let reCast = String(Number(value));
if (reCast.endsWith(value) || reCast.startsWith(value)) {
let { m, name, ship } = this.props; let { m, name, ship } = this.props;
value = Math.max(Math.min(value, 50000), -50000); value = Math.max(Math.min(value, 50000), -50000);
ship.setModification(m, name, value, true, true); ship.setModification(m, name, value, true, true);
this.setState({ value }); }
}
/**
* Triggered when a key is pressed down with focus on the number editor.
* @param {SyntheticEvent} event Key down event
*/
_keyDown(event) {
if (event.key == 'Enter') {
this._updateFinished();
}
this.props.onKeyDown(event);
} }
/** /**
@@ -72,6 +85,11 @@ export default class Modification extends TranslatedComponent {
return null; return null;
} }
let inputClassNames = {
'cb': true,
'greyed-out': !this.props.highlight
};
return ( return (
<div onBlur={this._updateFinished.bind(this)} key={name} <div onBlur={this._updateFinished.bind(this)} key={name}
className={cn('cb', 'modification-container')} className={cn('cb', 'modification-container')}
@@ -84,12 +102,12 @@ export default class Modification extends TranslatedComponent {
<td className={'input-container'}> <td className={'input-container'}>
<span> <span>
{this.props.editable ? {this.props.editable ?
<NumberEditor className={'cb'} value={this.state.value} <NumberEditor className={cn(inputClassNames)} value={this.state.value}
decimals={2} style={{ textAlign: 'right' }} step={0.01} decimals={2} style={{ textAlign: 'right' }} step={0.01}
stepModifier={1} onKeyDown={ this.props.onKeyDown } stepModifier={1} onKeyDown={this._keyDown.bind(this)}
onValueChange={this._updateValue.bind(this)} /> : onValueChange={this._updateValue.bind(this)} /> :
<input type="text" value={formats.f2(this.state.value)} <input type="text" value={formats.f2(this.state.value)}
disabled className={'number-editor'} disabled className={cn('number-editor', 'greyed-out')}
style={{ textAlign: 'right', cursor: 'inherit' }}/> style={{ textAlign: 'right', cursor: 'inherit' }}/>
} }
<span className={'unit-container'}> <span className={'unit-container'}>

View File

@@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import * as _ from 'lodash'; import * as _ from 'lodash';
import TranslatedComponent from './TranslatedComponent'; import TranslatedComponent from './TranslatedComponent';
import { isEmpty, stopCtxPropagation } from '../utils/UtilityFunctions'; import { stopCtxPropagation } from '../utils/UtilityFunctions';
import cn from 'classnames'; import cn from 'classnames';
import { Modifications } from 'coriolis-data/dist'; import { Modifications } from 'coriolis-data/dist';
import Modification from './Modification'; import Modification from './Modification';
@@ -23,7 +23,6 @@ const MODIFICATIONS_COMPARATOR = (mod1, mod2) => {
* Modifications menu * Modifications menu
*/ */
export default class ModificationsMenu extends TranslatedComponent { export default class ModificationsMenu extends TranslatedComponent {
static propTypes = { static propTypes = {
ship: PropTypes.object.isRequired, ship: PropTypes.object.isRequired,
m: PropTypes.object.isRequired, m: PropTypes.object.isRequired,
@@ -214,11 +213,11 @@ export default class ModificationsMenu extends TranslatedComponent {
for (const modName of Modifications.modules[m.grp].modifications) { for (const modName of Modifications.modules[m.grp].modifications) {
if (!Modifications.modifications[modName].hidden) { if (!Modifications.modifications[modName].hidden) {
const key = modName + (m.getModValue(modName) / 100 || 0); const key = modName + (m.getModValue(modName) / 100 || 0);
const editable = modName !== 'fallofffromrange' && const editable = modName !== 'fallofffromrange';
m.blueprint.grades[m.blueprint.grade].features[modName]; const highlight = m.blueprint.grades[m.blueprint.grade].features[modName];
this.lastNeId = modName; this.lastNeId = modName;
(editable ? modifiableModifications : modifications).push( (editable && highlight ? modifiableModifications : modifications).push(
<Modification key={ key } ship={ ship } m={ m } <Modification key={ key } ship={ ship } m={ m } highlight={highlight}
value={m.getPretty(modName) || 0} modItems={this.modItems} value={m.getPretty(modName) || 0} modItems={this.modItems}
onChange={onChange} onKeyDown={this._keyDown} name={modName} onChange={onChange} onKeyDown={this._keyDown} name={modName}
editable={editable} handleModChange = {this._handleModChange} /> editable={editable} handleModChange = {this._handleModChange} />

View File

@@ -35,13 +35,13 @@ export default class Movement extends TranslatedComponent {
return ( return (
<span id='movement'> <span id='movement'>
<svg viewBox='0 0 600 600' fillRule="evenodd" clipRule="evenodd"> <svg viewBox='0 0 600 600' fillRule="evenodd" clipRule="evenodd">
// Axes {/* Axes */}
<path d="M150 250v300" strokeWidth='1'/> <path d="M150 250v300" strokeWidth='1'/>
<path d="M150 250l236 236" strokeWidth='1'/> <path d="M150 250l236 236" strokeWidth='1'/>
<path d="M150 250l350 -200" strokeWidth='1'/> <path d="M150 250l350 -200" strokeWidth='1'/>
// End Arrow {/* End Arrow */}
<path d="M508 43.3L487 67l-10-17.3 31-6.4z"/> <path d="M508 43.3L487 67l-10-17.3 31-6.4z"/>
// Axes arcs and arrows {/* Axes arcs and arrows */}
<path d="M71.7 251.7C64.2 259.2 60 269.4 60 280c0 22 18 40 40 40s40-18 40-40c0-10.6-4.2-20.8-11.7-28.3 7.5 7.5 11.7 17.7 11.7 28.3 0 22-18 40-40 40s-40-18-40-40c0-10.6 4.2-20.8 11.7-28.3z" strokeWidth='4' transform="matrix(.6 0 0 .3 87.5 376.3)"/> <path d="M71.7 251.7C64.2 259.2 60 269.4 60 280c0 22 18 40 40 40s40-18 40-40c0-10.6-4.2-20.8-11.7-28.3 7.5 7.5 11.7 17.7 11.7 28.3 0 22-18 40-40 40s-40-18-40-40c0-10.6 4.2-20.8 11.7-28.3z" strokeWidth='4' transform="matrix(.6 0 0 .3 87.5 376.3)"/>
<path d="M142.8 453l-13.2 8.7-2.6-9.7 15.8 1z"/> <path d="M142.8 453l-13.2 8.7-2.6-9.7 15.8 1z"/>
<path d="M144.7 451.6l.5 1.6-16.2 10.6h-.4l-3.5-13 .7-.4 19.3 1.2zm-14.2 7.7l7.7-5-9.2-.7 1.5 5.7zm25.7-6.3l15.8-1-2.6 9.7-13.2-8.8z"/> <path d="M144.7 451.6l.5 1.6-16.2 10.6h-.4l-3.5-13 .7-.4 19.3 1.2zm-14.2 7.7l7.7-5-9.2-.7 1.5 5.7zm25.7-6.3l15.8-1-2.6 9.7-13.2-8.8z"/>
@@ -57,13 +57,13 @@ export default class Movement extends TranslatedComponent {
<path d="M359.5 422.4l-1.2 19.3-1.6.4-10.7-16 .2-.2 13-3.4.3.4zm-9 5l5.2 7.8.6-9.3-5.7 1.2zm-10.5 24l-13.2 8.6-2.6-9.7 15.8 1z"/> <path d="M359.5 422.4l-1.2 19.3-1.6.4-10.7-16 .2-.2 13-3.4.3.4zm-9 5l5.2 7.8.6-9.3-5.7 1.2zm-10.5 24l-13.2 8.6-2.6-9.7 15.8 1z"/>
<path d="M342 450l.4 1.5-16.2 10.7-.4-.2-3.5-13 .3-.3L342 450zm-14.3 7.6l7.7-5-9.2-.6 1.5 5.6z"/> <path d="M342 450l.4 1.5-16.2 10.7-.4-.2-3.5-13 .3-.3L342 450zm-14.3 7.6l7.7-5-9.2-.6 1.5 5.6z"/>
// Speed {/* Speed */}
<text x="470" y="30" strokeWidth='0'>{ship.canThrust(cargo, fuel) ? formats.int(ship.calcSpeed(eng, fuel, cargo, boost)) + 'm/s' : '-'}</text> <text x="470" y="30" strokeWidth='0'>{ship.canThrust(cargo, fuel) ? formats.int(ship.calcSpeed(eng, fuel, cargo, boost)) + 'm/s' : '-'}</text>
// Pitch {/* Pitch */}
<text x="355" y="410" strokeWidth='0'>{ship.canThrust(cargo, fuel) ? formats.int(ship.calcPitch(eng, fuel, cargo, boost)) + '°/s' : '-'}</text> <text x="355" y="410" strokeWidth='0'>{ship.canThrust(cargo, fuel) ? formats.int(ship.calcPitch(eng, fuel, cargo, boost)) + '°/s' : '-'}</text>
// Roll {/* Roll */}
<text x="450" y="110" strokeWidth='0'>{ship.canThrust(cargo, fuel) ? formats.int(ship.calcRoll(eng, fuel, cargo, boost)) + '°/s' : '-'}</text> <text x="450" y="110" strokeWidth='0'>{ship.canThrust(cargo, fuel) ? formats.int(ship.calcRoll(eng, fuel, cargo, boost)) + '°/s' : '-'}</text>
// Yaw {/* Yaw */}
<text x="160" y="430" strokeWidth='0'>{ship.canThrust(cargo, fuel) ? formats.int(ship.calcYaw(eng, fuel, cargo, boost)) + '°/s' : '-'}</text> <text x="160" y="430" strokeWidth='0'>{ship.canThrust(cargo, fuel) ? formats.int(ship.calcYaw(eng, fuel, cargo, boost)) + '°/s' : '-'}</text>
</svg> </svg>
</span>); </span>);

View File

@@ -5,7 +5,6 @@ import * as Calc from '../shipyard/Calculations';
import PieChart from './PieChart'; import PieChart from './PieChart';
import { nameComparator } from '../utils/SlotFunctions'; import { nameComparator } from '../utils/SlotFunctions';
import { MountFixed, MountGimballed, MountTurret } from './SvgIcons'; import { MountFixed, MountGimballed, MountTurret } from './SvgIcons';
import VerticalBarChart from './VerticalBarChart';
/** /**
* Generates an internationalization friendly weapon comparator that will * Generates an internationalization friendly weapon comparator that will

View File

@@ -1,8 +1,6 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import cn from 'classnames'; import cn from 'classnames';
import { Ships } from 'coriolis-data/dist';
import Ship from '../shipyard/Ship';
import Persist from '../stores/Persist'; import Persist from '../stores/Persist';
import TranslatedComponent from './TranslatedComponent'; import TranslatedComponent from './TranslatedComponent';
import PowerManagement from './PowerManagement'; import PowerManagement from './PowerManagement';

View File

@@ -1,13 +1,8 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import TranslatedComponent from './TranslatedComponent'; import TranslatedComponent from './TranslatedComponent';
import { Ships } from 'coriolis-data/dist';
import { nameComparator } from '../utils/SlotFunctions';
import { Pip } from './SvgIcons'; import { Pip } from './SvgIcons';
import LineChart from '../components/LineChart'; import { autoBind } from 'react-extras';
import Slider from '../components/Slider';
import * as ModuleUtils from '../shipyard/ModuleUtils';
import Module from '../shipyard/Module';
/** /**
* Pips displays SYS/ENG/WEP pips and allows users to change them with key presses by clicking on the relevant area. * Pips displays SYS/ENG/WEP pips and allows users to change them with key presses by clicking on the relevant area.
@@ -18,6 +13,9 @@ export default class Pips extends TranslatedComponent {
sys: PropTypes.number.isRequired, sys: PropTypes.number.isRequired,
eng: PropTypes.number.isRequired, eng: PropTypes.number.isRequired,
wep: PropTypes.number.isRequired, wep: PropTypes.number.isRequired,
mcSys: PropTypes.number.isRequired,
mcEng: PropTypes.number.isRequired,
mcWep: PropTypes.number.isRequired,
onChange: PropTypes.func.isRequired onChange: PropTypes.func.isRequired
}; };
@@ -28,9 +26,7 @@ export default class Pips extends TranslatedComponent {
*/ */
constructor(props, context) { constructor(props, context) {
super(props); super(props);
const { sys, eng, wep } = props; autoBind(this);
this._keyDown = this._keyDown.bind(this);
} }
/** /**
@@ -74,30 +70,21 @@ export default class Pips extends TranslatedComponent {
} }
} }
/**
* Handle a click
* @param {string} which Which item was clicked
*/
onClick(which) {
if (which == 'SYS') {
this._incSys();
} else if (which == 'ENG') {
this._incEng();
} else if (which == 'WEP') {
this._incWep();
} else if (which == 'RST') {
this._reset();
}
}
/** /**
* Reset the capacitor * Reset the capacitor
*/ */
_reset() { _reset(isMc) {
let { sys, eng, wep } = this.props; let { sys, eng, wep, mcSys, mcEng, mcWep } = this.props;
if (sys != 2 || eng != 2 || wep != 2) { if (isMc) {
if (mcSys || mcEng || mcWep) {
sys -= mcSys;
eng -= mcEng;
wep -= mcWep;
this.props.onChange(sys, eng, wep, 0, 0, 0);
}
} else if (sys != 2 || eng != 2 || wep != 2) {
sys = eng = wep = 2; sys = eng = wep = 2;
this.props.onChange(sys, eng, wep); this.props.onChange(sys + mcSys, eng + mcEng, wep + mcWep, mcSys, mcEng, mcWep);
} }
} }
@@ -105,151 +92,133 @@ export default class Pips extends TranslatedComponent {
* Increment the SYS capacitor * Increment the SYS capacitor
*/ */
_incSys() { _incSys() {
let { sys, eng, wep } = this.props; this._inc('sys', false);
const required = Math.min(1, 4 - sys);
if (required > 0) {
if (required == 0.5) {
// Take from whichever is larger
if (eng > wep) {
eng -= 0.5;
sys += 0.5;
} else {
wep -= 0.5;
sys += 0.5;
}
} else {
// Required is 1 - take from both if possible
if (eng == 0) {
wep -= 1;
sys += 1;
} else if (wep == 0) {
eng -= 1;
sys += 1;
} else {
eng -= 0.5;
wep -= 0.5;
sys += 1;
}
}
this.props.onChange(sys, eng, wep);
}
} }
/** /**
* Increment the ENG capacitor * Increment the ENG capacitor
*/ */
_incEng() { _incEng() {
let { sys, eng, wep } = this.props; this._inc('eng', false);
const required = Math.min(1, 4 - eng);
if (required > 0) {
if (required == 0.5) {
// Take from whichever is larger
if (sys > wep) {
sys -= 0.5;
eng += 0.5;
} else {
wep -= 0.5;
eng += 0.5;
}
} else {
// Required is 1 - take from both if possible
if (sys == 0) {
wep -= 1;
eng += 1;
} else if (wep == 0) {
sys -= 1;
eng += 1;
} else {
sys -= 0.5;
wep -= 0.5;
eng += 1;
}
}
this.props.onChange(sys, eng, wep);
}
} }
/** /**
* Increment the WEP capacitor * Increment the WEP capacitor
*/ */
_incWep() { _incWep() {
let { sys, eng, wep } = this.props; this._inc('wep', false);
}
const required = Math.min(1, 4 - wep); _wrapMcClick(key) {
if (required > 0) { return (event) => {
event.stopPropagation();
event.preventDefault();
if (key == 'rst') {
this._reset(true);
} else {
this._inc(key, true);
}
};
}
/**
* Increases a given capacitor
* @param {String} key Pip name to increase (one of 'sys', 'eng', 'wep')
* @param {Boolean} isMc True when increase is by multi crew
*/
_inc(key, isMc) {
if (!['sys', 'eng', 'wep'].includes(key)) {
return;
}
let { sys, eng, wep, mcSys, mcEng, mcWep } = this.props;
let mc = key == 'sys' ? mcSys : (key == 'eng' ? mcEng : mcWep);
let pips = this.props[key] - mc;
let other1 = key == 'sys' ? eng - mcEng : sys - mcSys;
let other2 = key == 'wep' ? eng - mcEng : wep - mcWep;
const required = Math.min(1, 4 - mc - pips);
if (isMc) {
// We can only set full pips in multi-crew also we can only set two pips
if (required > 0.5 && mcSys + mcEng + mcWep < 2) {
if (key == 'sys') {
mcSys += 1;
} else if (key == 'eng') {
mcEng += 1;
} else {
mcWep += 1;
}
}
} else if (required > 0) {
if (required == 0.5) { if (required == 0.5) {
// Take from whichever is larger // Take from whichever is larger
if (sys > eng) { if (other1 > other2) {
sys -= 0.5; other1 -= 0.5;
wep += 0.5;
} else { } else {
eng -= 0.5; other2 -= 0.5;
wep += 0.5;
} }
pips += 0.5;
} else { } else {
// Required is 1 - take from both if possible // Required is 1 - take from both if possible
if (sys == 0) { if (other1 == 0) {
eng -= 1; other2 -= 1;
wep += 1; } else if (other2 == 0) {
} else if (eng == 0) { other1 -= 1;
sys -= 1;
wep += 1;
} else { } else {
sys -= 0.5; other1 -= 0.5;
eng -= 0.5; other2 -= 0.5;
wep += 1; }
pips += 1;
} }
} }
this.props.onChange(sys, eng, wep);
} sys = mcSys + (key == 'sys' ? pips : other1);
eng = mcEng + (key == 'eng' ? pips : (key == 'sys' ? other1 : other2));
wep = mcWep + (key == 'wep' ? pips : other2);
this.props.onChange(sys, eng, wep, mcSys, mcEng, mcWep);
} }
/** /**
* Set up the rendering for pips * Set up the rendering for pips
* @param {int} sys the SYS pips * @param {Number} sys the SYS pips
* @param {int} eng the ENG pips * @param {Number} eng the ENG pips
* @param {int} wep the WEP pips * @param {Number} wep the WEP pips
* @param {Number} mcSys SYS pips from multi-crew
* @param {Number} mcEng ENG pips from multi-crew
* @param {Number} mcWep WEP pips from multi-crew
* @returns {Object} Object containing the rendering for the pips * @returns {Object} Object containing the rendering for the pips
*/ */
_renderPips(sys, eng, wep) { _renderPips(sys, eng, wep, mcSys, mcEng, mcWep) {
const pipsSvg = {}; const pipsSvg = {
SYS: [],
ENG: [],
WEP: [],
};
// SYS // Multi-crew pipsSettings actually are included in the overall pip count therefore
pipsSvg['SYS'] = []; // we can consider [0, sys - mcSys] as normal pipsSettings whilst [sys - mcSys, sys]
for (let i = 0; i < Math.floor(sys); i++) { // are the multi-crew pipsSettings in what follows.
pipsSvg['SYS'].push(<Pip className='full' key={i} />);
}
if (sys > Math.floor(sys)) {
pipsSvg['SYS'].push(<Pip className='half' key={'half'} />);
}
for (let i = Math.floor(sys + 0.5); i < 4; i++) {
pipsSvg['SYS'].push(<Pip className='empty' key={i} />);
}
// ENG let pipsSettings = {
pipsSvg['ENG'] = []; SYS: [sys, mcSys],
for (let i = 0; i < Math.floor(eng); i++) { ENG: [eng, mcEng],
pipsSvg['ENG'].push(<Pip className='full' key={i} />); WEP: [wep, mcWep],
} };
if (eng > Math.floor(eng)) {
pipsSvg['ENG'].push(<Pip className='half' key={'half'} />);
}
for (let i = Math.floor(eng + 0.5); i < 4; i++) {
pipsSvg['ENG'].push(<Pip className='empty' key={i} />);
}
// WEP for (let pipName in pipsSettings) {
pipsSvg['WEP'] = []; let [pips, mcPips] = pipsSettings[pipName];
for (let i = 0; i < Math.floor(wep); i++) { for (let i = 0; i < Math.floor(pips - mcPips); i++) {
pipsSvg['WEP'].push(<Pip className='full' key={i} />); pipsSvg[pipName].push(<Pip key={i} className='full' />);
} }
if (wep > Math.floor(wep)) { if (pips > Math.floor(pips)) {
pipsSvg['WEP'].push(<Pip className='half' key={'half'} />); pipsSvg[pipName].push(<Pip className='half' key={'half'} />);
}
for (let i = pips - mcPips; i < Math.floor(pips); i++) {
pipsSvg[pipName].push(<Pip key={i} className='mc' />);
}
for (let i = Math.floor(pips + 0.5); i < 4; i++) {
pipsSvg[pipName].push(<Pip className='empty' key={i} />);
} }
for (let i = Math.floor(wep + 0.5); i < 4; i++) {
pipsSvg['WEP'].push(<Pip className='empty' key={i} />);
} }
return pipsSvg; return pipsSvg;
@@ -260,15 +229,11 @@ export default class Pips extends TranslatedComponent {
* @return {React.Component} contents * @return {React.Component} contents
*/ */
render() { render() {
const { tooltip, termtip } = this.context;
const { formats, translate, units } = this.context.language; const { formats, translate, units } = this.context.language;
const { sys, eng, wep } = this.props; const { sys, eng, wep, mcSys, mcEng, mcWep } = this.props;
const onSysClicked = this.onClick.bind(this, 'SYS'); const pipsSvg = this._renderPips(sys, eng, wep, mcSys, mcEng, mcWep);
const onEngClicked = this.onClick.bind(this, 'ENG');
const onWepClicked = this.onClick.bind(this, 'WEP');
const onRstClicked = this.onClick.bind(this, 'RST');
const pipsSvg = this._renderPips(sys, eng, wep);
return ( return (
<span id='pips'> <span id='pips'>
<table> <table>
@@ -276,20 +241,38 @@ export default class Pips extends TranslatedComponent {
<tr> <tr>
<td>&nbsp;</td> <td>&nbsp;</td>
<td>&nbsp;</td> <td>&nbsp;</td>
<td className='clickable' onClick={onEngClicked}>{pipsSvg['ENG']}</td> <td className='clickable' onClick={() => this._inc('eng')}
onContextMenu={this._wrapMcClick('eng')}>{pipsSvg['ENG']}</td>
<td>&nbsp;</td> <td>&nbsp;</td>
</tr> </tr>
<tr> <tr>
<td>&nbsp;</td> <td>&nbsp;</td>
<td className='clickable' onClick={onSysClicked}>{pipsSvg['SYS']}</td> <td className='clickable' onClick={this._incSys}
<td className='clickable' onClick={onEngClicked}>{translate('ENG')}</td> onContextMenu={this._wrapMcClick('sys')}>{pipsSvg['SYS']}</td>
<td className='clickable' onClick={onWepClicked}>{pipsSvg['WEP']}</td> <td className='clickable' onClick={this._incEng}
onContextMenu={this._wrapMcClick('eng')}>{translate('ENG')}</td>
<td className='clickable' onClick={this._incWep}
onContextMenu={this._wrapMcClick('wep')}>{pipsSvg['WEP']}</td>
</tr> </tr>
<tr> <tr>
<td>&nbsp;</td> <td>&nbsp;</td>
<td className='clickable' onClick={onSysClicked}>{translate('SYS')}</td> <td className='clickable' onClick={this._incSys}
<td className='clickable' onClick={onRstClicked}>{translate('RST')}</td> onContextMenu={this._wrapMcClick('sys')}>{translate('SYS')}</td>
<td className='clickable' onClick={onWepClicked}>{translate('WEP')}</td> <td className='clickable' onClick={this._reset.bind(this, false)}>
{translate('RST')}
</td>
<td className='clickable' onClick={this._incWep}
onContextMenu={this._wrapMcClick('wep')}>{translate('WEP')}</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td className='clickable secondary' onClick={this._wrapMcClick('rst')}
onMouseEnter={termtip.bind(null, 'PHRASE_MULTI_CREW_CAPACITOR_POINTS')}
onMouseLeave={tooltip.bind(null, null)}>
{translate('RST')}
</td>
<td>&nbsp;</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@@ -32,7 +32,6 @@ function bandText(val, index, wattScale) {
* Renders the SVG to simulate in-game power bands * Renders the SVG to simulate in-game power bands
*/ */
export default class PowerBands extends TranslatedComponent { export default class PowerBands extends TranslatedComponent {
static propTypes = { static propTypes = {
bands: PropTypes.array.isRequired, bands: PropTypes.array.isRequired,
available: PropTypes.number.isRequired, available: PropTypes.number.isRequired,

View File

@@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import TranslatedComponent from './TranslatedComponent'; import TranslatedComponent from './TranslatedComponent';
import Ship from '../shipyard/Ship';
import { Ships } from 'coriolis-data/dist'; import { Ships } from 'coriolis-data/dist';
import { Rocket } from './SvgIcons'; import { Rocket } from './SvgIcons';
import Persist from '../stores/Persist'; import Persist from '../stores/Persist';

View File

@@ -6,7 +6,6 @@ import AvailableModulesMenu from './AvailableModulesMenu';
import ModificationsMenu from './ModificationsMenu'; import ModificationsMenu from './ModificationsMenu';
import { diffDetails } from '../utils/SlotFunctions'; import { diffDetails } from '../utils/SlotFunctions';
import { wrapCtxMenu } from '../utils/UtilityFunctions'; import { wrapCtxMenu } from '../utils/UtilityFunctions';
import { stopCtxPropagation } from '../utils/UtilityFunctions';
/** /**
* Abstract Slot * Abstract Slot

View File

@@ -1,5 +1,4 @@
import React from 'react'; import React from 'react';
import cn from 'classnames';
import SlotSection from './SlotSection'; import SlotSection from './SlotSection';
import HardpointSlot from './HardpointSlot'; import HardpointSlot from './HardpointSlot';
import { stopCtxPropagation } from '../utils/UtilityFunctions'; import { stopCtxPropagation } from '../utils/UtilityFunctions';
@@ -8,7 +7,6 @@ import { stopCtxPropagation } from '../utils/UtilityFunctions';
* Utility Slot Section * Utility Slot Section
*/ */
export default class UtilitySlotSection extends SlotSection { export default class UtilitySlotSection extends SlotSection {
/** /**
* Constructor * Constructor
* @param {Object} props React Component properties * @param {Object} props React Component properties
@@ -133,5 +131,4 @@ export default class UtilitySlotSection extends SlotSection {
</ul> </ul>
</div>; </div>;
} }
} }

View File

@@ -17,7 +17,6 @@ const merge = function(one, two) {
* A vertical bar chart * A vertical bar chart
*/ */
export default class VerticalBarChart extends TranslatedComponent { export default class VerticalBarChart extends TranslatedComponent {
static propTypes = { static propTypes = {
data : PropTypes.array.isRequired, data : PropTypes.array.isRequired,
yMax : PropTypes.number yMax : PropTypes.number

View File

@@ -1,13 +1,8 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import TranslatedComponent from './TranslatedComponent'; import TranslatedComponent from './TranslatedComponent';
import { Ships } from 'coriolis-data/dist';
import { nameComparator } from '../utils/SlotFunctions';
import { CollapseSection, ExpandSection, MountFixed, MountGimballed, MountTurret } from './SvgIcons';
import LineChart from '../components/LineChart'; import LineChart from '../components/LineChart';
import Slider from '../components/Slider';
import * as Calc from '../shipyard/Calculations'; import * as Calc from '../shipyard/Calculations';
import Module from '../shipyard/Module';
const DAMAGE_DEALT_COLORS = ['#FFFFFF', '#FF0000', '#00FF00', '#7777FF', '#FFFF00', '#FF00FF', '#00FFFF', '#777777']; const DAMAGE_DEALT_COLORS = ['#FFFFFF', '#FF0000', '#00FF00', '#7777FF', '#FFFF00', '#FF00FF', '#00FFFF', '#777777'];

View File

@@ -43,6 +43,7 @@
"PHRASE_SHIELD_DAMAGE": "Breakdown of sources for sustained DPS against shields", "PHRASE_SHIELD_DAMAGE": "Breakdown of sources for sustained DPS against shields",
"PHRASE_ARMOUR_DAMAGE": "Breakdown of sources for sustained DPS against armour", "PHRASE_ARMOUR_DAMAGE": "Breakdown of sources for sustained DPS against armour",
"PHRASE_TIME_TO_REMOVE_SHIELDS": "Will remove shields in", "PHRASE_TIME_TO_REMOVE_SHIELDS": "Will remove shields in",
"PHRASE_MULTI_CREW_CAPACITOR_POINTS": "Right click a capacitor to assign multi-crew capacitor points.",
"TT_TIME_TO_REMOVE_SHIELDS": "With sustained fire by all weapons", "TT_TIME_TO_REMOVE_SHIELDS": "With sustained fire by all weapons",
"PHRASE_TIME_TO_REMOVE_ARMOUR": "Will remove armour in", "PHRASE_TIME_TO_REMOVE_ARMOUR": "Will remove armour in",
"TT_TIME_TO_REMOVE_ARMOUR": "With sustained fire by all weapons", "TT_TIME_TO_REMOVE_ARMOUR": "With sustained fire by all weapons",

View File

@@ -1,4 +1,4 @@
import 'babel-polyfill'; import '@babel/polyfill';
import React from 'react'; import React from 'react';
import { render } from 'react-dom'; import { render } from 'react-dom';
import '../less/app.less'; import '../less/app.less';

View File

@@ -100,7 +100,7 @@ export default class OutfittingPage extends Page {
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 { sys, eng, wep, boost, fuel, cargo, opponent, opponentBuild, opponentSys, opponentEng, opponentWep, engagementRange } = this._obtainControlFromCode(ship, code); const { 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),
@@ -114,6 +114,9 @@ export default class OutfittingPage extends Page {
sys, sys,
eng, eng,
wep, wep,
mcSys,
mcEng,
mcWep,
boost, boost,
fuel, fuel,
cargo, cargo,
@@ -176,6 +179,9 @@ export default class OutfittingPage extends Page {
let sys = 2; let sys = 2;
let eng = 2; let eng = 2;
let wep = 2; let wep = 2;
let mcSys = 0;
let mcEng = 0;
let mcWep = 0;
let boost = false; let boost = false;
let fuel = ship.fuelCapacity; let fuel = ship.fuelCapacity;
let cargo = ship.cargoCapacity; let cargo = ship.cargoCapacity;
@@ -192,12 +198,12 @@ export default class OutfittingPage extends Page {
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(Utils.fromUrlSafe(parts[4])).split('/'); const control = LZString.decompressFromBase64(Utils.fromUrlSafe(parts[4])).split('/');
sys = parseFloat(control[0]); sys = parseFloat(control[0]) || sys;
eng = parseFloat(control[1]); eng = parseFloat(control[1]) || eng;
wep = parseFloat(control[2]); wep = parseFloat(control[2]) || wep;
boost = control[3] == 1 ? true : false; boost = control[3] == 1 ? true : false;
fuel = parseFloat(control[4]); fuel = parseFloat(control[4]) || fuel;
cargo = parseInt(control[5]); cargo = parseInt(control[5]) || cargo;
if (control[6]) { if (control[6]) {
const shipId = control[6]; const shipId = control[6];
opponent = new Ship(shipId, Ships[shipId].properties, Ships[shipId].slots); opponent = new Ship(shipId, Ships[shipId].properties, Ships[shipId].slots);
@@ -211,9 +217,9 @@ export default class OutfittingPage extends Page {
const opponentParts = opponentCode.split('.'); const opponentParts = opponentCode.split('.');
if (opponentParts.length >= 5) { if (opponentParts.length >= 5) {
const opponentControl = LZString.decompressFromBase64(Utils.fromUrlSafe(opponentParts[4])).split('/'); const opponentControl = LZString.decompressFromBase64(Utils.fromUrlSafe(opponentParts[4])).split('/');
opponentSys = parseFloat(opponentControl[0]); opponentSys = parseFloat(opponentControl[0]) || opponentSys;
opponentEng = parseFloat(opponentControl[1]); opponentEng = parseFloat(opponentControl[1]) || opponentEng;
opponentWep = parseFloat(opponentControl[2]); opponentWep = parseFloat(opponentControl[2]) || opponentWep;
} }
} }
} else { } else {
@@ -221,21 +227,32 @@ export default class OutfittingPage extends Page {
opponent.buildWith(Ships[shipId].defaults); opponent.buildWith(Ships[shipId].defaults);
} }
} }
engagementRange = parseInt(control[8]); engagementRange = parseInt(control[8]) || engagementRange;
// Multi-crew pips were introduced later on so assign default values
// because those values might not be present.
mcSys = parseInt(control[9]) || mcSys;
mcEng = parseInt(control[10]) || mcEng;
mcWep = parseInt(control[11]) || mcWep;
} }
} }
return { sys, eng, wep, boost, fuel, cargo, opponent, opponentBuild, opponentSys, opponentEng, opponentWep, engagementRange }; return { sys, eng, wep, mcSys, mcEng, mcWep, boost, fuel, cargo, opponent, opponentBuild, opponentSys, opponentEng, opponentWep, engagementRange };
} }
/** /**
* Triggered when pips have been updated * Triggered when pips have been updated. Multi-crew pips are already included
* in sys, eng and wep but mcSys, mcEng and mcWep make clear where each pip
* comes from.
* @param {number} sys SYS pips * @param {number} sys SYS pips
* @param {number} eng ENG pips * @param {number} eng ENG pips
* @param {number} wep WEP pips * @param {number} wep WEP pips
* @param {number} mcSys SYS pips from multi-crew
* @param {number} mcEng ENG pips from multi-crew
* @param {number} mcWep WEP pips from multi-crew
*/ */
_pipsUpdated(sys, eng, wep) { _pipsUpdated(sys, eng, wep, mcSys, mcEng, mcWep) {
this.setState({ sys, eng, wep }, () => this._updateRouteOnControlChange()); this.setState({ sys, eng, wep, mcSys, mcEng, mcWep }, () => this._updateRouteOnControlChange());
} }
/** /**
@@ -309,8 +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 { sys, eng, wep, boost, opponent, opponentBuild, engagementRange } = this.state; const { sys, 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}`; 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;
} }
@@ -373,12 +390,15 @@ 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 { sys, eng, wep, boost, fuel, cargo, opponent, opponentBuild, engagementRange } = this._obtainControlFromCode(ship, code); const { sys, eng, wep, mcSys, mcEng, mcWep, boost, fuel, cargo, opponent, opponentBuild, engagementRange } = this._obtainControlFromCode(ship, code);
// Update state, and refresh the ship // Update state, and refresh the ship
this.setState({ this.setState({
sys, sys,
eng, eng,
wep, wep,
mcSys,
mcEng,
mcWep,
boost, boost,
fuel, fuel,
cargo, cargo,
@@ -430,12 +450,15 @@ 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 { sys, eng, wep, boost, fuel, cargo, opponent, opponentBuild, engagementRange } = this._obtainControlFromCode(ship, code); 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 // Update state, and refresh the route when complete
this.setState({ this.setState({
sys, sys,
eng, eng,
wep, wep,
mcSys,
mcEng,
mcWep,
boost, boost,
fuel, fuel,
cargo, cargo,
@@ -567,7 +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, 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,
@@ -671,7 +694,7 @@ export default class OutfittingPage extends Page {
</div> </div>
</div> </div>
<div className='group quarter'> <div className='group quarter'>
<Pips sys={sys} eng={eng} wep={wep} onChange={this._pipsUpdated} /> <Pips 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 fuelCapacity={ship.fuelCapacity} fuel={fuel} onChange={this._fuelUpdated}/> <Fuel fuelCapacity={ship.fuelCapacity} fuel={fuel} onChange={this._fuelUpdated}/>

View File

@@ -2,7 +2,6 @@
* Modification - a modification and its value * Modification - a modification and its value
*/ */
export default class Modification { export default class Modification {
/** /**
* @param {String} id Unique modification ID * @param {String} id Unique modification ID
* @param {Number} value Value of the modification * @param {Number} value Value of the modification
@@ -11,5 +10,4 @@ export default class Modification {
this.id = id; this.id = id;
this.value = value; this.value = value;
} }
} }

View File

@@ -7,7 +7,6 @@ import { STATS_FORMATTING, SI_PREFIXES } from './StatsFormatting';
* Module - active module in a ship's buildout * Module - active module in a ship's buildout
*/ */
export default class Module { export default class Module {
/** /**
* Construct a new module * Construct a new module
* @param {Object} params Module parameters. Either grp/id or template * @param {Object} params Module parameters. Either grp/id or template
@@ -270,7 +269,7 @@ export default class Module {
} else { } else {
result = result * (1 + modValue); result = result * (1 + modValue);
} }
} else if (name === 'burst' || name === 'burstrof') { } else if (name === 'burstrof') {
// Burst and burst rate of fire are special, as it can not exist but // Burst and burst rate of fire are special, as it can not exist but
// have a modification // have a modification
result = modValue / 100; result = modValue / 100;
@@ -1091,5 +1090,4 @@ export default class Module {
getHackTime(modified = true) { getHackTime(modified = true) {
return this.get('hacktime', modified); return this.get('hacktime', modified);
} }
} }

View File

@@ -17,7 +17,6 @@ function filter(arr, maxClass, minClass, mass) {
* The available module set for a specific ship * The available module set for a specific ship
*/ */
export default class ModuleSet { export default class ModuleSet {
/** /**
* Instantiate the module set * Instantiate the module set
* @param {Object} modules All Modules * @param {Object} modules All Modules

View File

@@ -71,7 +71,6 @@ function reduceToIDs(idArray, slot, slotIndex) {
* Ship Model - Encapsulates and models in-game ship behavior * Ship Model - Encapsulates and models in-game ship behavior
*/ */
export default class Ship { export default class Ship {
/** /**
* @param {String} id Unique ship Id / Key * @param {String} id Unique ship Id / Key
* @param {Object} properties Basic ship properties such as name, manufacturer, mass, etc * @param {Object} properties Basic ship properties such as name, manufacturer, mass, etc
@@ -1187,28 +1186,28 @@ export default class Ship {
// handle unladen mass // handle unladen mass
unladenMass += chain(slots) unladenMass += chain(slots)
.map(slot => slot.m ? slot.m.get('mass') : null) .map(slot => slot.m ? slot.m.get('mass') : null)
.filter() .map(mass => mass || 0)
.reduce((sum, mass) => sum + mass) .reduce((sum, mass) => sum + mass)
.value(); .value();
// handle fuel capacity // handle fuel capacity
fuelCapacity += chain(slots) fuelCapacity += chain(slots)
.map(slot => slot.m ? slot.m.get('fuel') : null) .map(slot => slot.m ? slot.m.get('fuel') : null)
.filter() .map(fuel => fuel || 0)
.reduce((sum, fuel) => sum + fuel) .reduce((sum, fuel) => sum + fuel)
.value(); .value();
// handle cargo capacity // handle cargo capacity
cargoCapacity += chain(slots) cargoCapacity += chain(slots)
.map(slot => slot.m ? slot.m.get('cargo') : null) .map(slot => slot.m ? slot.m.get('cargo') : null)
.filter() .map(cargo => cargo || 0)
.reduce((sum, cargo) => sum + cargo) .reduce((sum, cargo) => sum + cargo)
.value(); .value();
// handle passenger capacity // handle passenger capacity
passengerCapacity += chain(slots) passengerCapacity += chain(slots)
.map(slot => slot.m ? slot.m.get('passengers') : null) .map(slot => slot.m ? slot.m.get('passengers') : null)
.filter() .map(passengers => passengers || 0)
.reduce((sum, passengers) => sum + passengers) .reduce((sum, passengers) => sum + passengers)
.value(); .value();

View File

@@ -70,7 +70,6 @@ function _delete(key) {
* export is an instance (see end of this file). * export is an instance (see end of this file).
*/ */
export class Persist extends EventEmitter { export class Persist extends EventEmitter {
/** /**
* Create an instance * Create an instance
*/ */

View File

@@ -147,7 +147,6 @@ export function shipFromLoadoutJSON(json) {
break; break;
default: default:
} }
for (const module of json.Modules) {
if (module.Slot.toLowerCase().search(/hardpoint/) !== -1) { if (module.Slot.toLowerCase().search(/hardpoint/) !== -1) {
// Add hardpoints // Add hardpoints
let hardpoint; let hardpoint;
@@ -181,14 +180,15 @@ export function shipFromLoadoutJSON(json) {
hardpointArrayNum++; hardpointArrayNum++;
} }
} }
if (module.Slot.toLowerCase().search(/slot\d/) !== -1) { }
let internalSlotNum = 1;
let internalSlotNum = 0;
let militarySlotNum = 1; let militarySlotNum = 1;
for (let i in shipTemplate.slots.internal) { for (let i in shipTemplate.slots.internal) {
if (!shipTemplate.slots.internal.hasOwnProperty(i)) { if (!shipTemplate.slots.internal.hasOwnProperty(i)) {
continue; continue;
} }
const isMilitary = isNaN(shipTemplate.slots.internal[i]) ? shipTemplate.slots.internal[i].name = 'military' : false; const isMilitary = isNaN(shipTemplate.slots.internal[i]) ? shipTemplate.slots.internal[i].name == 'Military' : false;
// The internal slot might be a standard or a military slot. Military slots have a different naming system // The internal slot might be a standard or a military slot. Military slots have a different naming system
let internalSlot = null; let internalSlot = null;
@@ -198,16 +198,15 @@ export function shipFromLoadoutJSON(json) {
militarySlotNum++; militarySlotNum++;
} else { } else {
// Slot numbers are not contiguous so handle skips. // Slot numbers are not contiguous so handle skips.
while (internalSlot === null && internalSlotNum < 99) { for (; internalSlot === null && internalSlotNum < 99; internalSlotNum++) {
// Slot sizes have no relationship to the actual size, either, so check all possibilities // Slot sizes have no relationship to the actual size, either, so check all possibilities
for (let slotsize = 0; slotsize < 9; slotsize++) { for (let slotsize = 0; slotsize < 9; slotsize++) {
const internalName = 'Slot' + (internalSlotNum <= 9 ? '0' : '0') + internalSlotNum + '_Size' + slotsize; const internalName = 'Slot' + (internalSlotNum <= 9 ? '0' : '') + internalSlotNum + '_Size' + slotsize;
if (json.Modules.find(elem => elem.Slot.toLowerCase() === internalName.toLowerCase())) { if (json.Modules.find(elem => elem.Slot.toLowerCase() === internalName.toLowerCase())) {
internalSlot = json.Modules.find(elem => elem.Slot.toLowerCase() === internalName.toLowerCase()); internalSlot = json.Modules.find(elem => elem.Slot.toLowerCase() === internalName.toLowerCase());
break; break;
} }
} }
internalSlotNum++;
} }
} }
@@ -222,9 +221,6 @@ export function shipFromLoadoutJSON(json) {
modsToAdd.push({ coriolisMod: internal, json: internalSlot }); modsToAdd.push({ coriolisMod: internal, json: internalSlot });
} }
} }
}
}
}
for (const i of modsToAdd) { for (const i of modsToAdd) {
if (i.json.Engineering) { if (i.json.Engineering) {

View File

@@ -1,7 +1,4 @@
import React from 'react'; import React from 'react';
import cn from 'classnames';
import Module from '../shipyard/Module';
import { Infinite } from '../components/SvgIcons';
import Persist from '../stores/Persist'; import Persist from '../stores/Persist';
import * as ModuleUtils from '../shipyard/ModuleUtils'; import * as ModuleUtils from '../shipyard/ModuleUtils';

View File

@@ -27,7 +27,7 @@
"density": "4.0" "density": "4.0"
} }
], ],
"start_url": "https:\/\/edcd.coriolis.io", "start_url": "https:\/\/coriolis.io",
"display": "standalone", "display": "standalone",
"orientation": "portrait" "orientation": "portrait"
} }

View File

@@ -65,7 +65,6 @@
</head> </head>
<body style="background-color:#000;"> <body style="background-color:#000;">
<section id="coriolis"></section> <section id="coriolis"></section>
<script src="<%= htmlWebpackPlugin.files.chunks.lib.entry %>" charset="utf-8" crossorigin="anonymous"></script>
<script src="<%= htmlWebpackPlugin.files.chunks.app.entry %>" charset="utf-8" crossorigin="anonymous"></script>
</body> </body>
</html> </html>

View File

@@ -12,6 +12,12 @@
cursor: pointer; cursor: pointer;
} }
// A multi-crew pip
.mc {
stroke: @secondary;
fill: @secondary;
}
// A full pip // A full pip
.full { .full {
stroke: @primary; stroke: @primary;

View File

@@ -74,7 +74,7 @@
border-color:#fff; border-color:#fff;
} }
input:disabled { input.greyed-out {
border-color: #888; border-color: #888;
color: #888; color: #888;
} }

View File

@@ -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

@@ -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

@@ -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

@@ -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

@@ -1,8 +1,6 @@
console.log('Hello from sw.js'); console.log('Hello from sw.js');
if (workbox) { if (workbox) {
workbox.skipWaiting();
workbox.clientsClaim();
console.log('Yay! Workbox is loaded 🎉'); console.log('Yay! Workbox is loaded 🎉');
workbox.routing.registerRoute( workbox.routing.registerRoute(
new RegExp('https://fonts.(?:googleapis|gstatic).com/(.*)'), new RegExp('https://fonts.(?:googleapis|gstatic).com/(.*)'),
@@ -44,3 +42,39 @@ if (workbox) {
} else { } else {
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) {
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,49 +1,44 @@
const path = require('path'); const path = require('path');
const exec = require('child_process').exec;
const webpack = require('webpack'); const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin'); const ExtractTextPlugin = require('extract-text-webpack-plugin');
const WebpackNotifierPlugin = require('webpack-notifier'); const WebpackNotifierPlugin = require('webpack-notifier');
const pkgJson = require('./package'); const pkgJson = require('./package');
const buildDate = new Date(); const buildDate = new Date();
function CopyDirPlugin(source, destination) { const CopyWebpackPlugin = require('copy-webpack-plugin');
this.source = source;
this.destination = destination;
}
CopyDirPlugin.prototype.apply = function(compiler) {
compiler.plugin('done', () => {
console.log(compiler.outputPath, this.destination);
exec('cp -r ' + this.source + ' ' + path.join(compiler.outputPath, this.destination));
});
};
module.exports = { module.exports = {
devtool: 'source-map', devtool: 'source-map',
devServer: { devServer: {
headers: { 'Access-Control-Allow-Origin': '*' } headers: { 'Access-Control-Allow-Origin': '*' }
}, },
mode: 'development',
entry: { entry: {
app: ['webpack-dev-server/client?http://0.0.0.0:3300', 'webpack/hot/only-dev-server', path.join(__dirname, 'src/app/index.js')], main: './src/app/index.js'
lib: ['d3', 'react', 'react-dom', 'classnames', 'fbemitter', 'lz-string']
}, },
resolve: { resolve: {
// When requiring, you don't need to add these extensions // When requiring, you don't need to add these extensions
extensions: ['.js', '.jsx', '.json', '.less'] extensions: ['.js', '.jsx', '.json', '.less']
}, },
optimization: {
minimize: false,
splitChunks: {
chunks: 'all'
}
},
output: { output: {
path: path.join(__dirname, 'build'), path: path.join(__dirname, 'build'),
filename: 'app.js', filename: 'app.js',
publicPath: '/' publicPath: '/'
}, },
plugins: [ plugins: [
new CopyDirPlugin(path.join(__dirname, 'src/.htaccess'), ''), new CopyWebpackPlugin(['src/.htaccess']),
new webpack.optimize.CommonsChunkPlugin({ // new webpack.optimize.CommonsChunkPlugin({
name: 'lib', // name: 'lib',
filename: 'lib.js' // filename: 'lib.js'
}), // }),
new HtmlWebpackPlugin({ new HtmlWebpackPlugin({
inject: false, inject: true,
template: path.join(__dirname, 'src/index.ejs'), template: path.join(__dirname, 'src/index.ejs'),
version: pkgJson.version, version: pkgJson.version,
date: buildDate, date: buildDate,

View File

@@ -1,63 +1,42 @@
const path = require('path'); const path = require('path');
const exec = require('child_process').exec;
const webpack = require('webpack'); const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin'); 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 { BugsnagSourceMapUploaderPlugin } = 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();
function CopyDirPlugin(source, destination) {
this.source = source;
this.destination = destination;
}
CopyDirPlugin.prototype.apply = function(compiler) {
compiler.plugin('done', () => {
console.log(compiler.outputPath, this.destination);
exec('cp -r ' + this.source + ' ' + path.join(compiler.outputPath, this.destination));
});
};
module.exports = { module.exports = {
cache: true,
devtool: 'source-map', devtool: 'source-map',
entry: { entry: {
app: ['babel-polyfill', path.resolve(__dirname, 'src/app/index')], main: ['./src/app/index.js']
lib: ['d3', 'react', 'react-dom', 'classnames', 'fbemitter', 'lz-string']
}, },
resolve: { resolve: {
extensions: ['.js', '.jsx', '.json', '.less'] extensions: ['.js', '.jsx', '.json', '.less']
}, },
output: { output: {
path: path.join(__dirname, 'build'), path: path.join(__dirname, 'build'),
filename: '[name].[chunkhash:6].js', filename: '[name].[hash].js',
chunkFilename: '[name].[chunkhash:6]', publicPath: '/',
publicPath: '/' globalObject: 'this'
},
mode: 'production',
optimization: {
minimize: true,
splitChunks: {
chunks: 'all'
}
}, },
plugins: [ plugins: [
new webpack.optimize.UglifyJsPlugin({ new CopyWebpackPlugin(['src/.htaccess', { from: 'src/schemas', to: 'schemas' }, {from: 'src/images/logo/*', flatten: true, to: ''}]),
'screw-ie8': true,
sourceMap: true
}),
// new webpack.optimize.CommonsChunkPlugin({ // new webpack.optimize.CommonsChunkPlugin({
// name: 'lib', // name: 'lib',
// filename: 'lib.[chunkhash:6].js' // filename: 'lib.[chunkhash:6].js'
// }), // }),
new HtmlWebpackPlugin({ new HtmlWebpackPlugin({
inject: false, inject: true,
appCache: 'coriolis.appcache',
minify: {
collapseBooleanAttributes: true,
collapseWhitespace: true,
removeAttributeQuotes: true,
removeComments: true,
removeEmptyAttributes: true,
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true
},
template: path.join(__dirname, 'src/index.ejs'), template: path.join(__dirname, 'src/index.ejs'),
uaTracking: process.env.CORIOLIS_UA_TRACKING || '', uaTracking: process.env.CORIOLIS_UA_TRACKING || '',
gapiKey: process.env.CORIOLIS_GAPI_KEY || '', gapiKey: process.env.CORIOLIS_GAPI_KEY || '',
@@ -65,17 +44,14 @@ module.exports = {
version: pkgJson.version version: pkgJson.version
}), }),
new ExtractTextPlugin({ new ExtractTextPlugin({
filename: '[contenthash:6].css', filename: '[hash:6].css',
disable: false, disable: false,
allChunks: true allChunks: true
}), }),
new BugsnagSourceMapUploaderPlugin({ // new BugsnagSourceMapUploaderPlugin({
apiKey: 'ba9fae819372850fb660755341fa6ef5', // apiKey: 'ba9fae819372850fb660755341fa6ef5',
appVersion: `${pkgJson.version}-${buildDate.toISOString()}` // appVersion: `${pkgJson.version}-${buildDate.toISOString()}`
}), // }),
new CopyDirPlugin(path.join(__dirname, 'src/schemas'), 'schemas'),
new CopyDirPlugin(path.join(__dirname, 'src/images/logo/*'), ''),
new CopyDirPlugin(path.join(__dirname, 'src/.htaccess'), ''),
new InjectManifest({ new InjectManifest({
swSrc: './src/sw.js', swSrc: './src/sw.js',
importWorkboxFrom: 'cdn', importWorkboxFrom: 'cdn',