Compare commits

...

203 Commits

Author SHA1 Message Date
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
willyb321
d218101708 2.9.18 2018-09-23 11:41:16 +10:00
Felix Linker
608ce12156 Undefined variable reference fix 2018-09-17 00:21:13 +02:00
William
80d653483a Merge pull request #386 from EDCD/feature/absolute-mods
Absolute modding
2018-09-17 07:30:44 +10:00
Felix Linker
f9a1e5afd9 Merge branch 'develop' into feature/absolute-mods 2018-09-16 23:29:59 +02:00
Felix Linker
7f5181a9c7 Improved modification menu design 2018-09-15 01:54:17 +02:00
Felix Linker
93d8f15f26 Moved diminishing returns for resistance modding on hrp into getModValue 2018-09-15 01:32:41 +02:00
Felix Linker
06d8930777 Typos fixed 2018-09-15 01:22:22 +02:00
William
23f4024e09 Merge pull request #385 from EDCD/feature/fixes
Fix experimental effect reverse engineering for resistance modding
2018-09-15 09:14:39 +10:00
Felix Linker
a5e9a71037 Clamp modification values to -50k and 50k 2018-09-15 01:12:39 +02:00
willyb321
423d42bc8a remove package-lock 2018-09-15 08:56:08 +10:00
willyb321
4075bab3d0 whoops 2018-09-15 08:54:31 +10:00
willyb321
469f7a0a48 format and request permission to store data
fixes people losing builds
2018-09-15 08:54:06 +10:00
Felix Linker
25d9f8ec61 Fix experimental effect reverse engineering for resistance modding 2018-09-15 00:50:14 +02:00
Felix Linker
1151bd1614 optmul changes are displayed additively 2018-09-15 00:42:39 +02:00
Felix Linker
2456ce330b Change module modding to have absolute values entered 2018-09-15 00:40:49 +02:00
Felix Linker
f86ce62c27 Optimized statistics calculation in shipyard/Ship 2018-09-15 00:38:38 +02:00
Felix Linker
c295a9f4e4 Added percentage as unit 2018-09-15 00:37:50 +02:00
Felix Linker
c4186faa4a Allow to set an absolute value for a module 2018-09-15 00:31:00 +02:00
William
00f4889c93 Merge pull request #381 from EDCD/feature/stats
Modules return null instead of undefined when a value is not given
2018-09-14 09:10:53 +10:00
Felix Linker
b6c83f4aba fix: hullboost mod imports 2018-09-13 23:42:51 +02:00
Felix Linker
3fa040c210 Modules return null instead of undefined when a value is not given 2018-09-13 23:02:35 +02:00
Felix Linker
8c4957c21e Merge pull request #364 from EDCD/feature/stats
Show absolute values next to modification values
2018-09-13 00:56:08 +02:00
Felix Linker
a178c59aa3 Merge branch 'develop' into feature/stats 2018-09-13 00:54:35 +02:00
William
0d9fcd97d7 Merge pull request #377 from EDCD/feature/please-willy
wait for load event before registering service worker
2018-09-12 08:40:39 +10:00
Felix Linker
b02ca117be wait for load event before registering service worker 2018-09-12 00:38:13 +02:00
William
ca2e2a22dd Merge pull request #371 from EDCD/feature/fixes
Various fixes
2018-09-12 08:17:48 +10:00
William
035e217124 Merge pull request #370 from pavchip/feature/update-russian-localization
Update Russian localization
2018-09-12 08:12:47 +10:00
Felix Linker
6424ab283a maxmass is not modified for shield generators now 2018-09-11 21:27:38 +02:00
Felix Linker
f30f10c904 hullboost modified values is calculated like shieldboost now 2018-09-11 20:44:23 +02:00
Felix Linker
8ce09a607f Implemented diminishing returns for hrp resistance modding 2018-09-11 13:09:29 +02:00
Pavel Chistyakov
5b48e30798 Update Russian localization 2018-09-11 13:31:11 +03:00
Felix Linker
0d7e6af2e6 Boost time now considers modifications 2018-09-10 01:03:13 +02:00
Felix Linker
be3ca23aee Consider guardian module reinforcement packages when calculation module protection 2018-09-10 01:00:36 +02:00
Felix Linker
28f464ba5a fix: falloff scaling now only applies for focused modification 2018-09-10 00:45:46 +02:00
Felix Linker
8cedd1b8bc When formating a value for a Module assign it to zero if it is undefined 2018-09-09 17:20:21 +02:00
Felix Linker
56cbc49d04 fix: when querying falloff mods is checked to be not undefined 2018-09-09 16:26:03 +02:00
Felix Linker
5d1ccd2a20 Removed synthetic values from modifications menu 2018-09-09 16:07:01 +02:00
Felix Linker
f898fbc55e Optimized _getModifiedValue 2018-09-09 16:01:54 +02:00
Felix Linker
63acad3aeb Sort mods in modifications menu by editable first 2018-09-09 16:01:54 +02:00
Felix Linker
5b4b0b8dff range and ranget are rounded with fixed decimal places now 2018-09-09 16:01:54 +02:00
Felix Linker
8492dd74f9 falloff and range are formated rounded to two decimal places 2018-09-09 16:01:54 +02:00
Felix Linker
77e652cd34 Allow for SE prefixes when formating module stats 2018-09-09 16:01:53 +02:00
felixlinker
5069d7e464 Modifications menu shows absolute stat values 2018-09-08 11:39:28 +02:00
felixlinker
8b3e9c0f63 Added method to retrieve a formatted stat from a module 2018-09-08 11:34:33 +02:00
felixlinker
99e76e480b Introduced translation for fallofffromrange 2018-09-08 11:33:26 +02:00
felixlinker
c161ed2298 Added units meters and angle 2018-09-08 11:32:56 +02:00
William Blythe
464770f096 Merge branch 'develop' 2018-09-06 09:57:34 +10:00
felixlinker
9fba1be814 Allow to retrieve unmodded vals from module 2018-09-05 17:58:16 +02:00
William
b419b8b104 Merge pull request #356 from EDCD/feature/caustic
Label fix
2018-09-01 09:12:20 +10:00
William
0cae955ca2 Merge pull request #359 from EDCD/feature/measure-charts
Fixed resize observer loop limit exceeded
2018-09-01 08:51:33 +10:00
felixlinker
ec70ad5d29 WeaponDamageChart wrapped in div instead of span 2018-08-31 15:02:44 +02:00
felixlinker
8a386c4ece Use react-container-dimensions instead of react-measure 2018-08-31 15:02:17 +02:00
felixlinker
013460ada4 Ship summary table header fix 2018-08-30 19:14:16 +02:00
William Blythe
d46ad89dc5 fix sw waiting 2018-08-30 08:25:50 +10:00
William
86c53c8e46 Merge pull request #354 from EDCD/feature/caustic
Bug fixes introduced with caustic damage calculation
2018-08-30 08:20:01 +10:00
felixlinker
df14786e79 Fixed bulkheads value for caustic damage 2018-08-29 11:37:28 +02:00
felixlinker
24f206ad82 Fixed ship summary table width 2018-08-29 11:37:14 +02:00
William Blythe
242c3efe45 Merge branch 'develop' of github.com:edcd/coriolis into develop 2018-08-29 09:24:27 +10:00
William
07a848b906 Merge pull request #353 from EDCD/feature/caustic
Added support for armour caustic resistances
2018-08-29 09:24:20 +10:00
felixlinker
1b5402fd2d Added support for caustic armour resistance 2018-08-28 12:38:47 +02:00
felixlinker
903d791549 Fixed tooltip for module protection in ShipSummaryTable 2018-08-28 10:53:40 +02:00
William Blythe
f03c164f1c Merge branch 'feature/alliance-crusader' into develop 2018-08-28 14:15:08 +10:00
William Blythe
107bab0192 Merge branch 'develop' 2018-08-27 08:42:21 +10:00
William Blythe
41f5ebb2f1 Merge remote-tracking branch 'origin/master' 2018-08-27 08:41:35 +10:00
William
266090dc2a Update sw.js 2018-08-27 08:34:10 +10:00
William
83f29f3d62 Merge pull request #351 from EDCD/feature/modules
Added support for research-limpet-controller
2018-08-27 08:30:43 +10:00
Felix Linker
3e6137ca96 Merge branch 'develop' into feature/modules 2018-08-26 20:13:33 +02:00
Felix Linker
58510bbd22 Added support for research-limpet-controller 2018-08-26 20:12:00 +02:00
William Blythe
2f7be75bcf update orbis integration 2018-08-26 09:48:12 +10:00
willyb321
0aecbbf892 [test] switch to service workers
dont merge to live until can be confirmed to work well
2018-08-26 09:44:44 +10:00
William Blythe
9ef2f4179b add alliance crusader 2018-08-25 18:09:58 +10:00
William Blythe
afbfe3ea12 update orbis integration 2018-08-25 13:59:14 +10:00
William Blythe
b145a2da85 update orbis integration 2018-08-25 13:58:55 +10:00
William
a679f7dd98 Merge pull request #348 from EDCD/feature/modules
Added support for new modules
2018-08-24 08:55:28 +10:00
William
f343366ea1 Merge pull request #350 from EDCD/feature/offence-table
Feature/offence table
2018-08-24 08:55:05 +10:00
Felix Linker
1bbea7dda0 Improved docs 2018-08-23 18:21:00 +02:00
Felix Linker
da097e0955 Added piechart for overall damage in offence tab 2018-08-23 18:09:18 +02:00
Felix Linker
1307474755 Added tooltips to offence table summary 2018-08-23 18:08:50 +02:00
Felix Linker
80f5a95297 Added support for new modules 2018-08-23 02:30:39 +02:00
William
e619966679 Merge pull request #346 from EDCD/feature/defence-proportions
Shield defence proportions calculated correctly
2018-08-22 07:04:13 +10:00
William
3f94382925 Merge pull request #345 from EDCD/feature/offence-table
Offence table improved (+Bug fix)
2018-08-22 07:03:57 +10:00
Felix Linker
ea7a6f8872 Added object documentation to shield damage multiplier objects 2018-08-20 03:45:05 +02:00
Felix Linker
4e87b3a0b8 Fixed shield proportion calc in defence panel 2018-08-20 03:44:24 +02:00
Felix Linker
8463dd46f7 Removed unused variable 2018-08-20 03:43:38 +02:00
Felix Linker
3febe465f6 reinforcement/booster contribution to defences now calulcated as multiplier 2018-08-20 02:13:11 +02:00
Felix Linker
45b834c424 Added summary and overall sdps to offence table 2018-08-20 02:12:06 +02:00
William
1ca041097b Merge pull request #343 from felixlinker/summary-shield-regen
Ship summary correctly calculates shield recovery/recharge time
2018-08-20 07:33:04 +10:00
William
587f7acd5b Merge pull request #344 from felixlinker/doc-fixes
doc fixes
2018-08-20 07:32:53 +10:00
Felix Linker
9e64e4a26b doc fixes 2018-08-19 23:26:17 +02:00
Felix Linker
96e09ab36c Ship summary correctly calculates shield recovery/recharge time 2018-08-19 15:41:27 +02:00
willyb321
4268d3f07b Merge branch 'develop' 2018-08-19 08:47:40 +10:00
William
1767a2aed5 Merge pull request #342 from felixlinker/import-shieldboost
Shieldboosters imported correctly now
2018-08-19 07:00:55 +10:00
William
9f061506bb Merge pull request #341 from felixlinker/shotspeed-blueprints
No special treatment for shotspeed modifications anymore
2018-08-19 07:00:19 +10:00
willyb321
0eb9a2048d Fix dev build
Closes #340
2018-08-19 06:59:28 +10:00
Felix Linker
faeafb2402 Shieldboosters imported correctly now 2018-08-18 22:28:13 +02:00
Felix Linker
2a0acfa6cb No special treatment for shotspeed modifications anymore 2018-08-18 21:15:37 +02:00
willyb321
0ac44ac267 (hopefully) fix bugsnag crashing the build 2018-08-17 08:05:04 +10:00
willyb321
b9ae3a4d5a just delete builds that are invalid when importing
temp fix until i figure out a better way
2018-08-17 07:49:49 +10:00
willyb321
b1aefb0003 just delete builds that are invalid when importing
temp fix until i figure out a better way
2018-08-17 07:48:48 +10:00
willyb321
f6a41ec55c make orbis auth checker better 2018-08-10 08:10:38 +10:00
willyb321
39650cc584 add orbis auth status 2018-08-09 16:43:21 +10:00
willyb321
d837287da7 add link to migrate to official domains 2018-08-07 11:46:18 +10:00
willyb321
6e718a39d1 add link to migrate to official domains 2018-08-07 11:46:01 +10:00
willyb321
2e7db02238 fix url 2018-08-07 11:30:23 +10:00
willyb321
dbfd108819 upload all builds to orbis under settings 2018-08-07 11:29:42 +10:00
willyb321
82c0f6fc0f hopefully fix beta.coriolis.edcd.io 2018-08-07 07:47:47 +10:00
willyb321
9e6bbaa67d fix url 2018-08-05 11:55:02 +10:00
willyb321
012a203c4a fix url 2018-08-05 11:54:51 +10:00
willyb321
c7ba129ed7 use different url shortener 2018-08-05 11:52:53 +10:00
willyb321
fbe57d00db use different url shortener 2018-08-05 11:41:53 +10:00
willyb321
dc4fdf215c fix compose again 2018-08-05 11:35:06 +10:00
willyb321
2166ac1584 fix docker-compose file 2018-08-05 10:41:41 +10:00
willyb321
504756de09 update orbis login 2018-08-02 12:56:45 +10:00
willyb321
90f04dba94 add link to orbis data 2018-08-01 16:35:44 +10:00
willyb321
bfd7645fb7 fix localhost uri 2018-08-01 14:27:09 +10:00
willyb321
c37c6983c3 orbis auth 2018-08-01 14:14:30 +10:00
willyb321
736c700aa0 orbis trial 2018-07-31 15:13:25 +10:00
willyb321
de1cb901fc more WIP for orbis 2018-07-28 11:36:44 +10:00
willyb321
3d9d6397f6 fix url shortener 2018-07-28 07:09:16 +10:00
willyb321
dbe836729f WIP for orbis 2018-07-27 11:44:14 +10:00
William Blythe
82fa93e676 Merge branch 'develop' into 'master' 2018-07-26 11:31:08 +10:00
willyb321
90a3392b80 oops i forgot a thing 2018-07-26 11:11:45 +10:00
willyb321
b7f715bd5e fix dockerfile to copy the right config 2018-07-26 08:58:27 +10:00
willyb321
5f88f46770 oops, remove ports and fix branch 2018-07-26 08:39:32 +10:00
willyb321
2244c91a64 Merge remote-tracking branch 'origin/develop' into develop 2018-07-26 08:33:54 +10:00
willyb321
78134404c3 prettify nginx, dockerfile updates 2018-07-26 08:33:05 +10:00
William
c9cd6b175d Merge pull request #333 from felixlinker/autoloader-sdps
SDPS takes autoloader into account
2018-07-26 08:06:09 +10:00
willyb321
5f70d283e0 do reset to branch 2018-07-26 07:25:32 +10:00
willyb321
cd1e27fd11 fix nginx conf 2018-07-25 11:48:01 +10:00
willyb321
0fd1c19514 dont reset 2018-07-25 11:25:21 +10:00
willyb321
a82dffd77d initial docker 2018-07-25 11:22:58 +10:00
Felix Linker
d3eed87077 new module method getSDps that takes autoloader into account 2018-07-25 02:07:53 +02:00
willyb321
d99f8ad7e7 remove preinstall script 2018-07-25 10:05:23 +10:00
William
e933305407 Merge pull request #329 from felixlinker/resistance-stacking
Caps for resistances introduced
2018-07-22 07:36:46 +10:00
felixlinker
690bc5a64a Resistance special effects now applied correctly 2018-07-20 17:54:07 +02:00
felixlinker
d2380a5c9c Armour resistances caps introduced 2018-07-20 14:11:33 +02:00
felixlinker
c2d0a6e9e0 Removed duplicate code on armour calculations 2018-07-20 14:11:33 +02:00
Felix Linker
a8c44fddca Shield resistances caps introduced 2018-07-20 14:11:33 +02:00
willyb321
0e6b306bdc Merge branch 'develop' 2018-07-20 07:31:29 +10:00
William
9b2a47ba0c Merge pull request #327 from felixlinker/sb-fixes
Fixes for shieldboost mods and special effects on shield booster
2018-07-20 07:29:49 +10:00
Felix Linker
3038ebc1c7 sb mod displays percentage change relative to 1+boost; sb special effect applied correctly 2018-07-19 17:37:34 +02:00
willyb321
5a48d5b400 Merge branch 'develop' 2018-07-19 10:26:21 +10:00
willyb321
f36edfe98d Fix guardian HRPs not calculating
Fixes #325
2018-07-19 10:22:42 +10:00
willyb321
3e80d6e13b Merge branch 'develop' 2018-07-18 08:30:07 +10:00
willyb321
c8f0a1ccc6 Fix #137
Recalculate stats after drag and drop
2018-07-18 08:29:08 +10:00
William
79325a3129 Merge pull request #319 from felixlinker/blueprint-special-resets
Resetting special effects for blueprints now re-calculates all stats
2018-07-18 08:04:39 +10:00
felixlinker
1ca0fffe5e Resetting special effects for blueprints now re-calculates all stats 2018-07-17 18:21:10 +02:00
willyb321
f5b9842b2e Merge branch 'develop' 2018-07-14 10:54:46 +10:00
willyb321
72ea3f125e guardian shard cannon 2018-07-14 10:52:28 +10:00
willyb321
f7a2408e44 add shock cannon 2018-07-14 10:32:47 +10:00
willyb321
c05de9a085 Merge branch 'develop' 2018-07-14 10:10:45 +10:00
willyb321
fc657f3b2d add guardian mrps and hrps 2018-07-14 10:09:45 +10:00
willyb321
e388de0364 Merge branch 'develop' 2018-07-11 12:02:30 +10:00
willyb321
26e55afb29 fix #315 2018-07-10 14:38:29 +10:00
willyb321
9b8e1039f6 also hide / disable buttons on firefox 2018-07-09 06:51:15 +10:00
willyb321
082d14ba50 tell user sending to edengineer doesnt work with firefox 2018-07-09 06:44:54 +10:00
willyb321
87ead595c6 possible fix for shopping list 2018-07-06 07:28:23 +10:00
willyb321
fb073acdc3 fix send to edengineer button disabled for some people 2018-07-06 07:09:05 +10:00
willyb321
3dda4e24bf update readme 2018-07-04 08:43:18 +10:00
willyb321
c966bd08ed preinstall clone coriolis-data 2018-07-04 08:37:11 +10:00
willyb321
692c73a6d0 add esdoc 2018-07-04 07:28:39 +10:00
willyb321
849b484b4d register specials in shopping list
ref msarilar/EDEngineer#431
2018-07-03 08:27:12 +10:00
William Blythe
9489baccda update .gitignore 2018-07-02 10:34:24 +10:00
willyb321
62cac15222 a couple fixes on /import 2018-07-02 06:41:01 +10:00
willyb321
0ee9ce2958 fix more args 2018-07-01 13:41:23 +10:00
willyb321
a83007fc6c fix parameter in the wrong function 2018-07-01 13:34:29 +10:00
willyb321
901f266dad dont allow stacking guardian fsd boosters 2018-07-01 13:31:59 +10:00
willyb321
6009426f04 add logic to handle guardian fsd boosters 2018-07-01 12:06:12 +10:00
willyb321
6fb89df4cd fix gsrp not updating shield strength 2018-07-01 11:44:53 +10:00
willyb321
48b99425a3 fix guardian shield reinforcement packages
add shield addition logic
2018-07-01 11:18:20 +10:00
willyb321
9569f9b09b more work on fixing resistances
hopefully getting close
2018-07-01 08:56:07 +10:00
willyb321
7421896902 store selected commander and also the list of commanders 2018-07-01 08:14:55 +10:00
willyb321
b837c8d66c add a cmdr dropdown instead of typing it manually 2018-06-30 11:11:08 +10:00
willyb321
e180f96eab hopefully much more accurate resistance calculation 2018-06-29 10:16:55 +10:00
willyb321
de1f1f790e gsrp 2018-06-29 07:39:59 +10:00
willyb321
fefa74ce0f add krait and challenger to /import 2018-06-29 07:39:37 +10:00
willyb321
a2ceb70aa4 simplify calcPipSpeed
also fix the order of parameters
2018-06-28 06:52:23 +10:00
willyb321
634103f144 update eslintrc to allow es6 2018-06-28 06:48:56 +10:00
willyb321
f3379de81e add formula to calculate pip speed
not used yet, but will be used eventually
2018-06-28 06:48:37 +10:00
willyb321
064ee74c1a fix jsdoc typo 2018-06-28 06:48:16 +10:00
willyb321
0e0c15fe3b lint a tad 2018-06-27 09:11:12 +10:00
willyb321
3c855c608d Merge branch 'develop' 2018-06-27 07:47:44 +10:00
willyb321
414516289c add message if sending to edengineer failed 2018-06-27 07:47:22 +10:00
willyb321
d79fa71337 Merge branch 'develop' 2018-06-27 05:55:18 +10:00
willyb321
39c2ca94c8 add "send to edengineer" button 2018-06-27 05:54:52 +10:00
willyb321
acf1c5ce04 Merge remote-tracking branch 'origin/develop' into develop 2018-06-26 08:02:58 +10:00
willyb321
fd7923155f more prep for gpd 2018-06-26 08:02:18 +10:00
willyb321
2aa6a6daa2 translations update 2018-06-26 08:01:57 +10:00
willyb321
9662621980 fix name of guardian pd 2018-06-26 08:01:46 +10:00
willyb321
37d007d9ab apply module blueprint and special before setting value
Fixes #297
2018-06-26 07:53:14 +10:00
willyb321
a4e48c359a Merge branch 'develop' 2018-06-17 08:24:58 +10:00
willyb321
8ef0101a6e apply module blueprint and special before setting value
Fixes #297
2018-06-17 08:24:24 +10:00
willyb321
e21f109026 Merge branch 'develop' 2018-06-15 10:58:19 +10:00
willyb321
737837eebd Merge remote-tracking branch 'origin/develop' into develop 2018-06-15 10:57:56 +10:00
willyb321
ba09b54409 fix jsdoc 2018-06-15 10:57:48 +10:00
willyb321
417091c648 fix lint 2018-06-15 10:45:00 +10:00
William Blythe
5a02368298 Merge branch 'develop' 2018-06-14 13:02:47 +10:00
William Blythe
da7c739497 add paypal button 2018-06-14 13:01:37 +10:00
willyb321
0842281466 Merge branch 'release/2.9.17' into develop 2018-06-14 07:08:21 +10:00
willyb321
b480f879b1 Merge branch 'release/2.9.17' 2018-06-14 07:08:21 +10:00
56 changed files with 3954 additions and 15151 deletions

2
.docker/.dockerignore Normal file
View File

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

37
.docker/Dockerfile Normal file
View File

@@ -0,0 +1,37 @@
### STAGE 1: Build ###
FROM node:9.11.1-alpine as builder
ARG branch=develop
ENV BRANCH=$branch
WORKDIR /src/app
RUN mkdir -p /src/app/coriolis
RUN mkdir -p /src/app/coriolis-data
COPY ./coriolis/ /src/app/coriolis
COPY ./coriolis-data/ /src/app/coriolis-data
RUN apk update
RUN apk add git
RUN npm i -g npm
# Set up coriolis-data
WORKDIR /src/app/coriolis-data
RUN git fetch --all
RUN git reset --hard origin/$BRANCH
RUN npm install --no-package-lock
RUN npm start
WORKDIR /src/app/coriolis
RUN git fetch --all
RUN git reset --hard origin/$BRANCH
RUN npm install --no-package-lock
RUN npm run build
### STAGE 2: Production Environment ###
FROM nginx:1.13.12-alpine as web
COPY coriolis/.docker/nginx.conf /etc/nginx/nginx.conf
COPY --from=builder /src/app/coriolis/build /usr/share/nginx/html
WORKDIR /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-c", "/etc/nginx/nginx.conf", "-g", "daemon off;"]

View File

@@ -0,0 +1,34 @@
version: '2.2'
services:
coriolis_prod:
image: edcd/coriolis:master
restart: always
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
networks:
- web
labels:
- "traefik.docker.network=web"
- "traefik.enable=true"
- "traefik.basic.frontend.rule=Host:coriolis.io,coriolis.edcd.io"
- "traefik.basic.port=80"
- "traefik.basic.protocol=http"
coriolis_dev:
image: edcd/coriolis:develop
restart: always
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
networks:
- web
labels:
- "traefik.docker.network=web"
- "traefik.enable=true"
- "traefik.basic.frontend.rule=Host:beta.coriolis.io,beta.coriolis.edcd.io"
- "traefik.basic.port=80"
- "traefik.basic.protocol=http"
networks:
web:
external: true

45
.docker/nginx.conf Normal file
View File

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

103
.esdoc.json Normal file
View File

@@ -0,0 +1,103 @@
{
"source": "./src/app",
"includes": ["\\.js$", "\\.jsx$"],
"destination": "./docs",
"index": "./README.md",
"plugins": [
{
"name": "esdoc-standard-plugin",
"option": {
"lint": {
"enable": false
},
"coverage": {
"enable": false
},
"accessor": {
"access": [
"public",
"protected",
"private"
],
"autoPrivate": true
},
"undocumentIdentifier": {
"enable": true
},
"unexportedIdentifier": {
"enable": false
},
"typeInference": {
"enable": true
},
"brand": {
"logo": "./src/images/logo/192x192.png",
"title": "Coriolis",
"description": "Coriolis Shipyard for Elite Dangerous",
"repository": "https://github.com/EDCD/coriolis",
"site": "https://coriolis.edcd.io",
"author": "https://github.com/edcd",
"image": "./src/images/logo/192x192.png"
}
}
},
{
"name": "esdoc-ecmascript-proposal-plugin",
"option": {
"all": true
}
},
{
"name": "esdoc-react-plugin"
},
{
"name": "esdoc-standard-plugin",
"option": {
"lint": {
"enable": false
},
"coverage": {
"enable": false
},
"accessor": {
"access": [
"public",
"protected",
"private"
],
"autoPrivate": true
},
"undocumentIdentifier": {
"enable": true
},
"unexportedIdentifier": {
"enable": false
},
"typeInference": {
"enable": true
},
"brand": {
"logo": "./src/images/logo/192x192.png",
"title": "Coriolis",
"description": "Coriolis Shipyard for Elite Dangerous",
"repository": "https://github.com/EDCD/coriolis",
"site": "https://coriolis.edcd.io",
"author": "https://github.com/edcd",
"image": "./src/images/logo/192x192.png"
}
}
},
{
"name": "esdoc-jsx-plugin",
"option": {
"enable": true
}
},
{
"name": "esdoc-publish-html-plugin",
"option": {
"template": "./node_modules/esdoc-custom-theme/template"
}
}
]
}

View File

@@ -9,7 +9,8 @@
}, },
"env": { "env": {
"browser": true, "browser": true,
"node": true "node": true,
"es6": true
}, },
"plugins": [ "plugins": [
"react" "react"

3
.gitignore vendored
View File

@@ -8,3 +8,6 @@ nginx.pid
env env
*.swp *.swp
.project .project
.vscode/
docs/
package-lock.json

1
.npmrc Normal file
View File

@@ -0,0 +1 @@
package-lock=false

View File

@@ -23,6 +23,7 @@ Chat to us on [Discord](https://discord.gg/0uwCh6R62aPRjk9w)!
See the [Developer's Guide](https://github.com/EDCD/coriolis/wiki/Developing-for-Coriolis) in the wiki. See the [Developer's Guide](https://github.com/EDCD/coriolis/wiki/Developing-for-Coriolis) in the wiki.
Also see [the documentation site.](https://coriolis.willb.info/)
### Ship and Module Database ### Ship and Module Database

12862
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "coriolis_shipyard", "name": "coriolis_shipyard",
"version": "2.9.17", "version": "3.0.0",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/EDCD/coriolis" "url": "https://github.com/EDCD/coriolis"
@@ -65,9 +65,16 @@
"babel-preset-react": "*", "babel-preset-react": "*",
"babel-preset-stage-0": "*", "babel-preset-stage-0": "*",
"create-react-class": "^15.6.2", "create-react-class": "^15.6.2",
"css-loader": "^0.28.0",
"cross-env": "^5.1.4", "cross-env": "^5.1.4",
"css-loader": "^0.28.0",
"d3-selection": "1", "d3-selection": "1",
"esdoc": "^1.1.0",
"esdoc-custom-theme": "^1.4.2",
"esdoc-ecmascript-proposal-plugin": "^1.0.0",
"esdoc-jsx-plugin": "^1.0.0",
"esdoc-publish-html-plugin": "^1.1.2",
"esdoc-react-plugin": "^1.0.1",
"esdoc-standard-plugin": "^1.0.0",
"eslint": "3.19.0", "eslint": "3.19.0",
"eslint-plugin-react": "^6.10.3", "eslint-plugin-react": "^6.10.3",
"expose-loader": "^0.7.3", "expose-loader": "^0.7.3",
@@ -81,7 +88,7 @@
"less": "^2.7.2", "less": "^2.7.2",
"less-loader": "^4.0.3", "less-loader": "^4.0.3",
"react-addons-perf": "^15.4.2", "react-addons-perf": "^15.4.2",
"react-measure": "^1.4.7", "react-container-dimensions": "^1.4.1",
"react-testutils-additions": "^15.2.0", "react-testutils-additions": "^15.2.0",
"react-transition-group": "^1.1.2", "react-transition-group": "^1.1.2",
"rimraf": "^2.6.1", "rimraf": "^2.6.1",
@@ -91,9 +98,10 @@
"uglify-js": "^2.4.11", "uglify-js": "^2.4.11",
"url-loader": "^0.5.8", "url-loader": "^0.5.8",
"webpack": "^2.4.1", "webpack": "^2.4.1",
"webpack-bugsnag-plugins": "^1.1.1",
"webpack-dev-server": "^2.4.4", "webpack-dev-server": "^2.4.4",
"webpack-notifier": "^1.6.0", "webpack-notifier": "^1.6.0",
"webpack-bugsnag-plugins": "^1.1.1" "workbox-webpack-plugin": "^3.4.1"
}, },
"dependencies": { "dependencies": {
"babel-polyfill": "*", "babel-polyfill": "*",
@@ -103,7 +111,7 @@
"d3": "4.8.0", "d3": "4.8.0",
"detect-browser": "^1.7.0", "detect-browser": "^1.7.0",
"fbemitter": "^2.1.1", "fbemitter": "^2.1.1",
"lodash": "^4.17.4", "lodash": "^4.17.10",
"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.5.8",

View File

@@ -134,9 +134,9 @@ export default class Coriolis extends React.Component {
console && console.error && console.error(arguments); // eslint-disable-line no-console console && console.error && console.error(arguments); // eslint-disable-line no-console
if (errObj) { if (errObj) {
if (errObj instanceof Error) { if (errObj instanceof Error) {
bugsnagClient.notify(errObj) // eslint-disable-line bugsnagClient.notify(errObj); // eslint-disable-line
} else if (errObj instanceof String) { } else if (errObj instanceof String) {
bugsnagClient.notify(msg, errObj) // eslint-disable-line bugsnagClient.notify(msg, errObj); // eslint-disable-line
} }
} }
this.setState({ this.setState({
@@ -180,13 +180,13 @@ export default class Coriolis extends React.Component {
case 72: // 'h' case 72: // 'h'
if (e.ctrlKey || e.metaKey) { // CTRL/CMD + h if (e.ctrlKey || e.metaKey) { // CTRL/CMD + h
e.preventDefault(); e.preventDefault();
this._showModal(<ModalHelp />); this._showModal(<ModalHelp/>);
} }
break; break;
case 73: // 'i' case 73: // 'i'
if (e.ctrlKey || e.metaKey) { // CTRL/CMD + i if (e.ctrlKey || e.metaKey) { // CTRL/CMD + i
e.preventDefault(); e.preventDefault();
this._showModal(<ModalImport />); this._showModal(<ModalImport/>);
} }
break; break;
case 79: // 'o' case 79: // 'o'
@@ -208,7 +208,7 @@ export default class Coriolis extends React.Component {
* @param {React.Component} content Modal Content * @param {React.Component} content Modal Content
*/ */
_showModal(content) { _showModal(content) {
let modal = <div className='modal-bg' onClick={(e) => this._hideModal() }>{content}</div>; let modal = <div className='modal-bg' onClick={(e) => this._hideModal()}>{content}</div>;
this.setState({ modal }); this.setState({ modal });
} }
@@ -322,14 +322,60 @@ export default class Coriolis extends React.Component {
*/ */
componentWillMount() { componentWillMount() {
// Listen for appcache updated event, present refresh to update view // Listen for appcache updated event, present refresh to update view
if (window.applicationCache) { // Check that service workers are registered
window.applicationCache.addEventListener('updateready', () => { if (navigator.storage && navigator.storage.persist) {
if (window.applicationCache.status == window.applicationCache.UPDATEREADY) { window.addEventListener('load', () => {
this.setState({ appCacheUpdate: true }); // Browser downloaded a new app cache. navigator.storage.persist().then(granted => {
} if (granted)
console.log('Storage will not be cleared except by explicit user action');
else
console.log('Storage may be cleared by the UA under storage pressure.');
});
}); });
} }
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
// Your service-worker.js *must* be located at the top-level directory relative to your site.
// It won't be able to control pages unless it's located at the same level or higher than them.
// *Don't* register service worker file in, e.g., a scripts/ sub-directory!
// See https://github.com/slightlyoff/ServiceWorker/issues/468
const self = this;
navigator.serviceWorker.register('/service-worker.js').then(function(reg) {
// updatefound is fired if service-worker.js changes.
reg.onupdatefound = function() {
// 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
var installingWorker = reg.installing;
installingWorker.onstatechange = function() {
switch (installingWorker.state) {
case 'installed':
if (navigator.serviceWorker.controller) {
// At this point, the old content will have been purged and the fresh content will
// have been added to the cache.
// It's the perfect time to display a "New content is available; please refresh."
// message in the page's interface.
console.log('New or updated content is available.');
self.setState({ appCacheUpdate: true }); // Browser downloaded a new app cache.
} else {
// At this point, everything has been precached.
// It's the perfect time to display a "Content is cached for offline use." message.
console.log('Content is now available offline!');
self.setState({ appCacheUpdate: true }); // Browser downloaded a new app cache.
}
break;
case 'redundant':
console.error('The installing service worker became redundant.');
break;
}
};
};
}).catch(function(e) {
console.error('Error during service worker registration:', e);
});
});
}
window.onerror = this._onError.bind(this); window.onerror = this._onError.bind(this);
window.addEventListener('resize', () => this.emitter.emit('windowResize')); window.addEventListener('resize', () => this.emitter.emit('windowResize'));
document.getElementById('coriolis').addEventListener('scroll', () => this._tooltip()); document.getElementById('coriolis').addEventListener('scroll', () => this._tooltip());
@@ -347,16 +393,22 @@ export default class Coriolis extends React.Component {
render() { render() {
let currentMenu = this.state.currentMenu; let currentMenu = this.state.currentMenu;
return <div style={{ minHeight: '100%' }} onClick={this._closeMenu} className={ this.state.noTouch ? 'no-touch' : null }> return <div style={{ minHeight: '100%' }} onClick={this._closeMenu}
<Header appCacheUpdate={this.state.appCacheUpdate} currentMenu={currentMenu} /> className={this.state.noTouch ? 'no-touch' : null}>
{ this.state.error ? this.state.error : this.state.page ? React.createElement(this.state.page, { currentMenu }) : <NotFoundPage/> } <Header appCacheUpdate={this.state.appCacheUpdate} currentMenu={currentMenu}/>
{ this.state.modal } {this.state.error ? this.state.error : this.state.page ? React.createElement(this.state.page, { currentMenu }) :
{ this.state.tooltip } <NotFoundPage/>}
{this.state.modal}
{this.state.tooltip}
<footer> <footer>
<div className="right cap"> <div className="right cap">
<a href="https://github.com/EDCD/coriolis" target="_blank" title="Coriolis Github Project">{window.CORIOLIS_VERSION} - {window.CORIOLIS_DATE}</a> <a href="https://github.com/EDCD/coriolis" target="_blank"
title="Coriolis Github Project">{window.CORIOLIS_VERSION} - {window.CORIOLIS_DATE}</a>
<br/> <br/>
<a href={'https://github.com/EDCD/coriolis/compare/edcd:develop@{' + window.CORIOLIS_DATE + '}...edcd:develop'} target="_blank" title={'Coriolis Commits since' + window.CORIOLIS_DATE}>Commits since last release ({window.CORIOLIS_DATE})</a> <a
href={'https://github.com/EDCD/coriolis/compare/edcd:develop@{' + window.CORIOLIS_DATE + '}...edcd:develop'}
target="_blank" title={'Coriolis Commits since' + window.CORIOLIS_DATE}>Commits since last release
({window.CORIOLIS_DATE})</a>
</div> </div>
</footer> </footer>
</div>; </div>;

View File

@@ -45,6 +45,12 @@ const GRPCAT = {
'mr': 'ordnance', 'mr': 'ordnance',
'axmr': 'experimental', 'axmr': 'experimental',
'rcpl': 'experimental', 'rcpl': 'experimental',
'dtl': 'experimental',
'tbsc': 'experimental',
'tbem': 'experimental',
'tbrfl': 'experimental',
'mahr': 'experimental',
'rsl': 'experimental',
'tp': 'ordnance', 'tp': 'ordnance',
'nl': 'ordnance', 'nl': 'ordnance',
'sc': 'scanners', 'sc': 'scanners',
@@ -58,10 +64,15 @@ const GRPCAT = {
'po': 'defence', 'po': 'defence',
'ec': 'defence', 'ec': 'defence',
'sfn': 'defence', 'sfn': 'defence',
// Standard // Guardian
'gpp': 'guardian', 'gpp': 'guardian',
'gpc': 'guardian', 'gpc': 'guardian',
'ggc': 'guardian' 'gsrp': 'guardian',
'ggc': 'guardian',
'gfsb': 'guardian',
'gmrp': 'guardian',
'gsc': 'guardian',
'ghrp': 'guardian'
}; };
// Order here is the order in which items will be shown in the modules menu // Order here is the order in which items will be shown in the modules menu
const CATEGORIES = { const CATEGORIES = {
@@ -87,10 +98,10 @@ const CATEGORIES = {
'defence': ['ch', 'po', 'ec'], 'defence': ['ch', 'po', 'ec'],
'scanners': ['sc', 'ss', 'cs', 'kw', 'ws'], // Overloaded with internal scanners 'scanners': ['sc', 'ss', 'cs', 'kw', 'ws'], // Overloaded with internal scanners
// Experimental // Experimental
'experimental': ['axmc', 'axmr', 'rfl', 'xs', 'sfn', 'rcpl'], 'experimental': ['axmc', 'axmr', 'rfl', 'tbrfl', 'tbsc', 'tbem', 'xs', 'sfn', 'rcpl', 'dtl', 'rsl', 'mahr', ],
// Guardian // Guardian
'guardian': ['gpp', 'gpc', 'ggc'] 'guardian': ['gpp', 'gpd', 'gpc', 'ggc', 'gsrp', 'gfsb', 'ghrp', 'gmrp', 'gsc']
}; };
/** /**

View File

@@ -76,6 +76,7 @@ export default class Defence extends TranslatedComponent {
shieldSourcesData.push({ value: Math.round(shield.generator), label: translate('generator') }); shieldSourcesData.push({ value: Math.round(shield.generator), label: translate('generator') });
shieldSourcesData.push({ value: Math.round(shield.boosters), label: translate('boosters') }); shieldSourcesData.push({ value: Math.round(shield.boosters), label: translate('boosters') });
shieldSourcesData.push({ value: Math.round(shield.cells), label: translate('cells') }); shieldSourcesData.push({ value: Math.round(shield.cells), label: translate('cells') });
shieldSourcesData.push({ value: Math.round(shield.addition), label: translate('shield addition') });
if (shield.generator > 0) { if (shield.generator > 0) {
shieldSourcesTt.push(<div key='generator'>{translate('generator') + ' ' + formats.int(shield.generator)}{units.MJ}</div>); shieldSourcesTt.push(<div key='generator'>{translate('generator') + ' ' + formats.int(shield.generator)}{units.MJ}</div>);
@@ -101,19 +102,19 @@ export default class Defence extends TranslatedComponent {
// Add effective shield from resistances // Add effective shield from resistances
const rawMj = shield.generator + shield.boosters + shield.cells; const rawMj = shield.generator + shield.boosters + shield.cells;
const explosiveMj = rawMj / (shield.explosive.generator * shield.explosive.boosters) - rawMj; const explosiveMj = rawMj / (shield.explosive.base) - rawMj;
if (explosiveMj != 0) effectiveShieldExplosiveTt.push(<div key='resistance'>{translate('resistance') + ' ' + formats.int(explosiveMj)}{units.MJ}</div>); if (explosiveMj != 0) effectiveShieldExplosiveTt.push(<div key='resistance'>{translate('resistance') + ' ' + formats.int(explosiveMj)}{units.MJ}</div>);
const kineticMj = rawMj / (shield.kinetic.generator * shield.kinetic.boosters) - rawMj; const kineticMj = rawMj / (shield.kinetic.base) - rawMj;
if (kineticMj != 0) effectiveShieldKineticTt.push(<div key='resistance'>{translate('resistance') + ' ' + formats.int(kineticMj)}{units.MJ}</div>); if (kineticMj != 0) effectiveShieldKineticTt.push(<div key='resistance'>{translate('resistance') + ' ' + formats.int(kineticMj)}{units.MJ}</div>);
const thermalMj = rawMj / (shield.thermal.generator * shield.thermal.boosters) - rawMj; const thermalMj = rawMj / (shield.thermal.base) - rawMj;
if (thermalMj != 0) effectiveShieldThermalTt.push(<div key='resistance'>{translate('resistance') + ' ' + formats.int(thermalMj)}{units.MJ}</div>); if (thermalMj != 0) effectiveShieldThermalTt.push(<div key='resistance'>{translate('resistance') + ' ' + formats.int(thermalMj)}{units.MJ}</div>);
// Add effective shield from power distributor SYS pips // Add effective shield from power distributor SYS pips
if (shield.absolute.sys != 1) { if (shield.absolute.sys != 1) {
effectiveShieldAbsoluteTt.push(<div key='power distributor'>{translate('power distributor') + ' ' + formats.int(rawMj / shield.absolute.sys - rawMj)}{units.MJ}</div>); effectiveShieldAbsoluteTt.push(<div key='power distributor'>{translate('power distributor') + ' ' + formats.int(rawMj / shield.absolute.total - rawMj)}{units.MJ}</div>);
effectiveShieldExplosiveTt.push(<div key='power distributor'>{translate('power distributor') + ' ' + formats.int(rawMj / shield.explosive.sys - rawMj)}{units.MJ}</div>); effectiveShieldExplosiveTt.push(<div key='power distributor'>{translate('power distributor') + ' ' + formats.int(rawMj / shield.explosive.total - rawMj / shield.explosive.base)}{units.MJ}</div>);
effectiveShieldKineticTt.push(<div key='power distributor'>{translate('power distributor') + ' ' + formats.int(rawMj / shield.kinetic.sys - rawMj)}{units.MJ}</div>); effectiveShieldKineticTt.push(<div key='power distributor'>{translate('power distributor') + ' ' + formats.int(rawMj / shield.kinetic.total - rawMj / shield.kinetic.base)}{units.MJ}</div>);
effectiveShieldThermalTt.push(<div key='power distributor'>{translate('power distributor') + ' ' + formats.int(rawMj / shield.thermal.sys - rawMj)}{units.MJ}</div>); effectiveShieldThermalTt.push(<div key='power distributor'>{translate('power distributor') + ' ' + formats.int(rawMj / shield.thermal.total - rawMj / shield.thermal.base)}{units.MJ}</div>);
} }
} }
@@ -159,18 +160,21 @@ export default class Defence extends TranslatedComponent {
const effectiveArmourExplosiveTt = []; const effectiveArmourExplosiveTt = [];
const effectiveArmourKineticTt = []; const effectiveArmourKineticTt = [];
const effectiveArmourThermalTt = []; const effectiveArmourThermalTt = [];
const effectiveArmourCausticTt = [];
if (armour.bulkheads > 0) { if (armour.bulkheads > 0) {
armourSourcesTt.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.int(armour.bulkheads)}</div>); armourSourcesTt.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.int(armour.bulkheads)}</div>);
effectiveArmourAbsoluteTt.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.int(armour.bulkheads)}</div>); effectiveArmourAbsoluteTt.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.int(armour.bulkheads)}</div>);
effectiveArmourExplosiveTt.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.int(armour.bulkheads)}</div>); effectiveArmourExplosiveTt.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.int(armour.bulkheads)}</div>);
effectiveArmourKineticTt.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.int(armour.bulkheads)}</div>); effectiveArmourKineticTt.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.int(armour.bulkheads)}</div>);
effectiveArmourThermalTt.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.int(armour.bulkheads)}</div>); effectiveArmourThermalTt.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.int(armour.bulkheads)}</div>);
effectiveArmourCausticTt.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.int(armour.bulkheads)}</div>);
if (armour.reinforcement > 0) { if (armour.reinforcement > 0) {
armourSourcesTt.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.int(armour.reinforcement)}</div>); armourSourcesTt.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.int(armour.reinforcement)}</div>);
effectiveArmourAbsoluteTt.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.int(armour.reinforcement)}</div>); effectiveArmourAbsoluteTt.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.int(armour.reinforcement)}</div>);
effectiveArmourExplosiveTt.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.int(armour.reinforcement)}</div>); effectiveArmourExplosiveTt.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.int(armour.reinforcement)}</div>);
effectiveArmourKineticTt.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.int(armour.reinforcement)}</div>); effectiveArmourKineticTt.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.int(armour.reinforcement)}</div>);
effectiveArmourThermalTt.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.int(armour.reinforcement)}</div>); effectiveArmourThermalTt.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.int(armour.reinforcement)}</div>);
effectiveArmourCausticTt.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.int(armour.reinforcement)}</div>);
} }
} }
@@ -183,17 +187,22 @@ export default class Defence extends TranslatedComponent {
const armourDamageTakenExplosiveTt = []; const armourDamageTakenExplosiveTt = [];
armourDamageTakenExplosiveTt.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.pct1(armour.explosive.bulkheads)}</div>); armourDamageTakenExplosiveTt.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.pct1(armour.explosive.bulkheads)}</div>);
armourDamageTakenExplosiveTt.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.pct1(armour.explosive.reinforcement)}</div>); armourDamageTakenExplosiveTt.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.pct1(armour.explosive.reinforcement)}</div>);
if (armour.explosive.bulkheads * armour.explosive.reinforcement != 1) effectiveArmourExplosiveTt.push(<div key='resistance'>{translate('resistance') + ' ' + formats.int(rawArmour / (armour.explosive.bulkheads * armour.explosive.reinforcement) - rawArmour)}</div>); if (armour.explosive.total != 1) effectiveArmourExplosiveTt.push(<div key='resistance'>{translate('resistance') + ' ' + formats.int(rawArmour / armour.explosive.total - rawArmour)}</div>);
const armourDamageTakenKineticTt = []; const armourDamageTakenKineticTt = [];
armourDamageTakenKineticTt.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.pct1(armour.kinetic.bulkheads)}</div>); armourDamageTakenKineticTt.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.pct1(armour.kinetic.bulkheads)}</div>);
armourDamageTakenKineticTt.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.pct1(armour.kinetic.reinforcement)}</div>); armourDamageTakenKineticTt.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.pct1(armour.kinetic.reinforcement)}</div>);
if (armour.kinetic.bulkheads * armour.kinetic.reinforcement != 1) effectiveArmourKineticTt.push(<div key='resistance'>{translate('resistance') + ' ' + formats.int(rawArmour / (armour.kinetic.bulkheads * armour.kinetic.reinforcement) - rawArmour)}</div>); if (armour.kinetic.total != 1) effectiveArmourKineticTt.push(<div key='resistance'>{translate('resistance') + ' ' + formats.int(rawArmour / armour.kinetic.total - rawArmour)}</div>);
const armourDamageTakenThermalTt = []; const armourDamageTakenThermalTt = [];
armourDamageTakenThermalTt.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.pct1(armour.thermal.bulkheads)}</div>); armourDamageTakenThermalTt.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.pct1(armour.thermal.bulkheads)}</div>);
armourDamageTakenThermalTt.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.pct1(armour.thermal.reinforcement)}</div>); armourDamageTakenThermalTt.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.pct1(armour.thermal.reinforcement)}</div>);
if (armour.thermal.bulkheads * armour.thermal.reinforcement != 1) effectiveArmourThermalTt.push(<div key='resistance'>{translate('resistance') + ' ' + formats.int(rawArmour / (armour.thermal.bulkheads * armour.thermal.reinforcement) - rawArmour)}</div>); if (armour.thermal.total != 1) effectiveArmourThermalTt.push(<div key='resistance'>{translate('resistance') + ' ' + formats.int(rawArmour / armour.thermal.total - rawArmour)}</div>);
const armourDamageTakenCausticTt = [];
armourDamageTakenCausticTt.push(<div key='bulkheads'>{translate('bulkheads') + ' ' + formats.pct1(armour.caustic.bulkheads)}</div>);
armourDamageTakenCausticTt.push(<div key='reinforcement'>{translate('reinforcement') + ' ' + formats.pct1(armour.caustic.reinforcement)}</div>);
if (armour.thermal.total != 1) effectiveArmourCausticTt.push(<div key='resistance'>{translate('resistance') + ' ' + formats.int(rawArmour / armour.caustic.total - rawArmour)}</div>);
const effectiveArmourData = []; const effectiveArmourData = [];
const effectiveAbsoluteArmour = armour.total / armour.absolute.total; const effectiveAbsoluteArmour = armour.total / armour.absolute.total;
@@ -204,12 +213,15 @@ export default class Defence extends TranslatedComponent {
effectiveArmourData.push({ value: Math.round(effectiveKineticArmour), label: translate('kinetic'), tooltip: effectiveArmourKineticTt }); effectiveArmourData.push({ value: Math.round(effectiveKineticArmour), label: translate('kinetic'), tooltip: effectiveArmourKineticTt });
const effectiveThermalArmour = armour.total / armour.thermal.total; const effectiveThermalArmour = armour.total / armour.thermal.total;
effectiveArmourData.push({ value: Math.round(effectiveThermalArmour), label: translate('thermal'), tooltip: effectiveArmourThermalTt }); effectiveArmourData.push({ value: Math.round(effectiveThermalArmour), label: translate('thermal'), tooltip: effectiveArmourThermalTt });
const effectiveCausticArmour = armour.total / armour.caustic.total;
effectiveArmourData.push({ value: Math.round(effectiveCausticArmour), label: translate('caustic'), tooltip: effectiveArmourCausticTt });
const armourDamageTakenData = []; const armourDamageTakenData = [];
armourDamageTakenData.push({ value: Math.round(armour.absolute.total * 100), label: translate('absolute'), tooltip: armourDamageTakenTt }); armourDamageTakenData.push({ value: Math.round(armour.absolute.total * 100), label: translate('absolute'), tooltip: armourDamageTakenTt });
armourDamageTakenData.push({ value: Math.round(armour.explosive.total * 100), label: translate('explosive'), tooltip: armourDamageTakenExplosiveTt }); armourDamageTakenData.push({ value: Math.round(armour.explosive.total * 100), label: translate('explosive'), tooltip: armourDamageTakenExplosiveTt });
armourDamageTakenData.push({ value: Math.round(armour.kinetic.total * 100), label: translate('kinetic'), tooltip: armourDamageTakenKineticTt }); armourDamageTakenData.push({ value: Math.round(armour.kinetic.total * 100), label: translate('kinetic'), tooltip: armourDamageTakenKineticTt });
armourDamageTakenData.push({ value: Math.round(armour.thermal.total * 100), label: translate('thermal'), tooltip: armourDamageTakenThermalTt }); armourDamageTakenData.push({ value: Math.round(armour.thermal.total * 100), label: translate('thermal'), tooltip: armourDamageTakenThermalTt });
armourDamageTakenData.push({ value: Math.round(armour.caustic.total * 100), label: translate('caustic'), tooltip: armourDamageTakenCausticTt });
return ( return (
<span id='defence'> <span id='defence'>

View File

@@ -57,7 +57,7 @@ export default class FSDProfile extends TranslatedComponent {
*/ */
_calcMaxRange(ship, fuel, mass) { _calcMaxRange(ship, fuel, mass) {
// Obtain the maximum range // Obtain the maximum range
return Calc.jumpRange(mass, ship.standard[2].m, Math.min(fuel, ship.standard[2].m.getMaxFuelPerJump())); return Calc.jumpRange(mass, ship.standard[2].m, Math.min(fuel, ship.standard[2].m.getMaxFuelPerJump()), ship);
} }
/** /**
@@ -77,7 +77,7 @@ export default class FSDProfile extends TranslatedComponent {
const maxMass = thrusters.getMaxMass(); const maxMass = thrusters.getMaxMass();
const mass = ship.unladenMass + fuel + cargo; const mass = ship.unladenMass + fuel + cargo;
const minRange = 0; const minRange = 0;
const maxRange = Calc.jumpRange(minMass + fsd.getMaxFuelPerJump(), fsd, fsd.getMaxFuelPerJump()); const maxRange = Calc.jumpRange(minMass + fsd.getMaxFuelPerJump(), fsd, fsd.getMaxFuelPerJump(), ship);
// Add a mark at our current mass // Add a mark at our current mass
const mark = Math.min(mass, maxMass); const mark = Math.min(mass, maxMass);

View File

@@ -79,7 +79,7 @@ export default class HardpointSlot extends Slot {
<div className={'r'}>{formats.round(m.getMass())}{u.T}</div> <div className={'r'}>{formats.round(m.getMass())}{u.T}</div>
</div> </div>
<div className={'cb'}> <div className={'cb'}>
{ m.getDps() ? <div className={'l'} onMouseOver={termtip.bind(null, m.getClip() ? 'dpssdps' : 'dps')} onMouseOut={tooltip.bind(null, null)}>{translate('DPS')}: {formats.round1(m.getDps())} { m.getClip() ? <span>({formats.round1((m.getClip() * m.getDps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload())) })</span> : null }</div> : null } { m.getDps() ? <div className={'l'} onMouseOver={termtip.bind(null, m.getClip() ? 'dpssdps' : 'dps')} onMouseOut={tooltip.bind(null, null)}>{translate('DPS')}: {formats.round1(m.getDps())} { m.getClip() ? <span>({formats.round1(m.getSDps()) })</span> : null }</div> : null }
{ m.getEps() ? <div className={'l'} onMouseOver={termtip.bind(null, m.getClip() ? 'epsseps' : 'eps')} onMouseOut={tooltip.bind(null, null)}>{translate('EPS')}: {formats.round1(m.getEps())}{u.MW} { m.getClip() ? <span>({formats.round1((m.getClip() * m.getEps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload())) }{u.MW})</span> : null }</div> : null } { m.getEps() ? <div className={'l'} onMouseOver={termtip.bind(null, m.getClip() ? 'epsseps' : 'eps')} onMouseOut={tooltip.bind(null, null)}>{translate('EPS')}: {formats.round1(m.getEps())}{u.MW} { m.getClip() ? <span>({formats.round1((m.getClip() * m.getEps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload())) }{u.MW})</span> : null }</div> : null }
{ m.getHps() ? <div className={'l'} onMouseOver={termtip.bind(null, m.getClip() ? 'hpsshps' : 'hps')} onMouseOut={tooltip.bind(null, null)}>{translate('HPS')}: {formats.round1(m.getHps())} { m.getClip() ? <span>({formats.round1((m.getClip() * m.getHps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload())) })</span> : null }</div> : null } { m.getHps() ? <div className={'l'} onMouseOver={termtip.bind(null, m.getClip() ? 'hpsshps' : 'hps')} onMouseOut={tooltip.bind(null, null)}>{translate('HPS')}: {formats.round1(m.getHps())} { m.getClip() ? <span>({formats.round1((m.getClip() * m.getHps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload())) })</span> : null }</div> : null }
{ m.getDps() && m.getEps() ? <div className={'l'} onMouseOver={termtip.bind(null, 'dpe')} onMouseOut={tooltip.bind(null, null)}>{translate('DPE')}: {formats.f1(m.getDps() / m.getEps())}</div> : null } { m.getDps() && m.getEps() ? <div className={'l'} onMouseOver={termtip.bind(null, 'dpe')} onMouseOut={tooltip.bind(null, null)}>{translate('DPE')}: {formats.f1(m.getDps() / m.getEps())}</div> : null }

View File

@@ -9,6 +9,8 @@ import { Cogs, CoriolisLogo, Hammer, Help, Rocket, StatsBars } from './SvgIcons'
import { Ships } from 'coriolis-data/dist'; import { Ships } from 'coriolis-data/dist';
import Persist from '../stores/Persist'; import Persist from '../stores/Persist';
import { toDetailedExport } from '../shipyard/Serializer'; import { toDetailedExport } from '../shipyard/Serializer';
import Ship from '../shipyard/Ship';
import ModalBatchOrbis from './ModalBatchOrbis';
import ModalDeleteAll from './ModalDeleteAll'; import ModalDeleteAll from './ModalDeleteAll';
import ModalExport from './ModalExport'; import ModalExport from './ModalExport';
import ModalHelp from './ModalHelp'; import ModalHelp from './ModalHelp';
@@ -235,6 +237,43 @@ export default class Header extends TranslatedComponent {
/>); />);
}; };
/**
* Uploads all ship-builds to orbis
* @param {e} e Event
*/
_uploadAllBuildsToOrbis(e) {
e.preventDefault();
const data = Persist.getBuilds();
let postObject = [];
for (const ship in data) {
for (const code in data[ship]) {
const shipModel = ship;
if (!shipModel) {
throw 'No such ship found: "' + ship + '"';
}
const shipTemplate = Ships[shipModel];
const shipPostObject = {};
let shipInstance = new Ship(shipModel, shipTemplate.properties, shipTemplate.slots);
shipInstance.buildWith(null);
shipInstance.buildFrom(data[ship][code]);
shipPostObject.coriolisId = shipInstance.id;
shipPostObject.coriolisShip = shipInstance;
shipPostObject.coriolisShip.url = window.location.origin + outfitURL(shipModel, data[ship][code], code);
shipPostObject.title = code || shipInstance.id;
shipPostObject.description = code || shipInstance.id;
shipPostObject.ShipName = shipInstance.id;
shipPostObject.Ship = shipInstance.id;
postObject.push(shipPostObject);
}
}
console.log(postObject);
this.context.showModal(<ModalBatchOrbis
ships={postObject}
/>);
}
/** /**
* Show export modal with detailed export * Show export modal with detailed export
* @param {SyntheticEvent} e Event * @param {SyntheticEvent} e Event
@@ -430,6 +469,7 @@ export default class Header extends TranslatedComponent {
{translate('builds')} & {translate('comparisons')} {translate('builds')} & {translate('comparisons')}
<li><Link href="#" className='block' onClick={this._showBackup.bind(this)}>{translate('backup')}</Link></li> <li><Link href="#" className='block' onClick={this._showBackup.bind(this)}>{translate('backup')}</Link></li>
<li><Link href="#" className='block' onClick={this._showDetailedExport.bind(this)}>{translate('detailed export')}</Link></li> <li><Link href="#" className='block' onClick={this._showDetailedExport.bind(this)}>{translate('detailed export')}</Link></li>
<li><Link href="#" className='block' onClick={this._uploadAllBuildsToOrbis.bind(this)}>{translate('upload all builds to orbis')}</Link></li>
<li><Link href="#" className='block' onClick={this._showImport.bind(this)}>{translate('import')}</Link></li> <li><Link href="#" className='block' onClick={this._showImport.bind(this)}>{translate('import')}</Link></li>
<li><Link href="#" className='block' onClick={this._showDeleteAll.bind(this)}>{translate('delete all')}</Link></li> <li><Link href="#" className='block' onClick={this._showDeleteAll.bind(this)}>{translate('delete all')}</Link></li>
</ul> </ul>
@@ -531,6 +571,16 @@ export default class Header extends TranslatedComponent {
{openedMenu == 'comp' ? this._getComparisonsMenu() : null} {openedMenu == 'comp' ? this._getComparisonsMenu() : null}
</div> </div>
{window.location.origin.search('.edcd.io') >= 0 ?
<div className='l menu'>
<a href="https://youtu.be/4SvnLcefhtI" target="_blank">
<div className={cn('menu-header')}>
<Rocket className='warning'/><span className='menu-item-label'>{translate('please migrate to coriolis.io')}</span>
</div>
</a>
</div> : null
}
<div className='r menu'> <div className='r menu'>
<div className={cn('menu-header', { selected: openedMenu == 'settings' })} onClick={this._openSettings}> <div className={cn('menu-header', { selected: openedMenu == 'settings' })} onClick={this._openSettings}>
<Cogs className='xl warning'/><span className='menu-item-label'>{translate('settings')}</span> <Cogs className='xl warning'/><span className='menu-item-label'>{translate('settings')}</span>

View File

@@ -63,6 +63,9 @@ export default class InternalSlot extends Slot {
{ m.getSpinup() ? <div className={'l'}>{translate('spinup')}: {formats.f1(m.getSpinup())}{u.s}</div> : null } { m.getSpinup() ? <div className={'l'}>{translate('spinup')}: {formats.f1(m.getSpinup())}{u.s}</div> : null }
{ m.getDuration() ? <div className={'l'}>{translate('duration')}: {formats.f1(m.getDuration())}{u.s}</div> : null } { m.getDuration() ? <div className={'l'}>{translate('duration')}: {formats.f1(m.getDuration())}{u.s}</div> : null }
{ m.grp === 'scb' ? <div className={'l'}>{translate('cells')}: {formats.int(m.getAmmo() + 1)}</div> : null } { m.grp === 'scb' ? <div className={'l'}>{translate('cells')}: {formats.int(m.getAmmo() + 1)}</div> : null }
{ m.grp === 'gsrp' ? <div className={'l'}>{translate('shield addition')}: {formats.f1(m.getShieldAddition())}{u.MJ}</div> : null }
{ m.grp === 'gfsb' ? <div className={'l'}>{translate('jump addition')}: {formats.f1(m.getJumpBoost())}{u.LY}</div> : null }
{ m.grp === 'gs' ? <div className={'l'}>{translate('shield addition')}: {formats.f1(m.getShieldAddition())}{u.MJ}</div> : null }
{ m.getShieldReinforcement() ? <div className={'l'}>{translate('shieldreinforcement')}: {formats.f1(m.getDuration() * m.getShieldReinforcement())}{u.MJ}</div> : null } { m.getShieldReinforcement() ? <div className={'l'}>{translate('shieldreinforcement')}: {formats.f1(m.getDuration() * m.getShieldReinforcement())}{u.MJ}</div> : null }
{ m.getShieldReinforcement() ? <div className={'l'}>{translate('total')}: {formats.int((m.getAmmo() + 1) * (m.getDuration() * m.getShieldReinforcement()))}{u.MJ}</div> : null } { m.getShieldReinforcement() ? <div className={'l'}>{translate('total')}: {formats.int((m.getAmmo() + 1) * (m.getDuration() * m.getShieldReinforcement()))}{u.MJ}</div> : null }
{ m.repair ? <div className={'l'}>{translate('repair')}: {m.repair}</div> : null } { m.repair ? <div className={'l'}>{translate('repair')}: {m.repair}</div> : null }
@@ -82,6 +85,7 @@ export default class InternalSlot extends Slot {
{ showModuleResistances && m.getExplosiveResistance() ? <div className='l'>{translate('explres')}: {formats.pct(m.getExplosiveResistance())}</div> : null } { showModuleResistances && m.getExplosiveResistance() ? <div className='l'>{translate('explres')}: {formats.pct(m.getExplosiveResistance())}</div> : null }
{ showModuleResistances && m.getKineticResistance() ? <div className='l'>{translate('kinres')}: {formats.pct(m.getKineticResistance())}</div> : null } { showModuleResistances && m.getKineticResistance() ? <div className='l'>{translate('kinres')}: {formats.pct(m.getKineticResistance())}</div> : null }
{ showModuleResistances && m.getThermalResistance() ? <div className='l'>{translate('thermres')}: {formats.pct(m.getThermalResistance())}</div> : null } { showModuleResistances && m.getThermalResistance() ? <div className='l'>{translate('thermres')}: {formats.pct(m.getThermalResistance())}</div> : null }
{ showModuleResistances && m.getCausticResistance() ? <div className='l'>{translate('causres')}: {formats.pct(m.getCausticResistance())}</div> : null }
{ m.getHullReinforcement() ? <div className='l'>{translate('armour')}: {formats.int(m.getHullReinforcement() + ship.baseArmour * m.getModValue('hullboost') / 10000)}</div> : null } { m.getHullReinforcement() ? <div className='l'>{translate('armour')}: {formats.int(m.getHullReinforcement() + ship.baseArmour * m.getModValue('hullboost') / 10000)}</div> : null }
{ m.getProtection() ? <div className='l'>{translate('protection')}: {formats.rPct(m.getProtection())}</div> : null } { m.getProtection() ? <div className='l'>{translate('protection')}: {formats.rPct(m.getProtection())}</div> : null }
{ m.getIntegrity() ? <div className='l'>{translate('integrity')}: {formats.int(m.getIntegrity())}</div> : null } { m.getIntegrity() ? <div className='l'>{translate('integrity')}: {formats.int(m.getIntegrity())}</div> : null }

View File

@@ -61,7 +61,7 @@ export default class JumpRange extends TranslatedComponent {
const fuel = this.state.fuelLevel * ship.fuelCapacity; const fuel = this.state.fuelLevel * ship.fuelCapacity;
// Obtain the jump range // Obtain the jump range
return Calc.jumpRange(ship.unladenMass + fuel + cargo, fsd, fuel); return Calc.jumpRange(ship.unladenMass + fuel + cargo, fsd, fuel, ship);
} }
/** /**

View File

@@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Measure from 'react-measure'; import ContainerDimensions from 'react-container-dimensions';
import * as d3 from 'd3'; import * as d3 from 'd3';
import TranslatedComponent from './TranslatedComponent'; import TranslatedComponent from './TranslatedComponent';
@@ -67,21 +67,17 @@ export default class LineChart extends TranslatedComponent {
xAxisScale, xAxisScale,
yScale, yScale,
tipHeight: 2 + (1.2 * (series ? series.length : 0.8)), tipHeight: 2 + (1.2 * (series ? series.length : 0.8)),
dimensions: {
width: 100,
height: 100
}
}; };
} }
/** /**
* Update tooltip content * Update tooltip content
* @param {number} xPos x coordinate * @param {number} xPos x coordinate
* @param {number} width current container width
*/ */
_tooltip(xPos) { _tooltip(xPos, width) {
let { xLabel, yLabel, xUnit, yUnit, func, series } = this.props; let { xLabel, yLabel, xUnit, yUnit, func, series } = this.props;
let { xScale, yScale } = this.state; let { xScale, yScale } = this.state;
let { width } = this.state.dimensions;
let { formats, translate } = this.context.language; let { formats, translate } = this.context.language;
let x0 = xScale.invert(xPos), let x0 = xScale.invert(xPos),
y0 = func(x0), y0 = func(x0),
@@ -120,11 +116,11 @@ export default class LineChart extends TranslatedComponent {
* Update dimensions based on properties and scale * Update dimensions based on properties and scale
* @param {Object} props React Component properties * @param {Object} props React Component properties
* @param {number} scale size ratio / scale * @param {number} scale size ratio / scale
* @param {number} width current width of the container
* @returns {Object} calculated dimensions * @returns {Object} calculated dimensions
*/ */
_updateDimensions(props, scale) { _updateDimensions(props, scale, width) {
const { xMax, xMin, yMin, yMax } = props; const { xMax, xMin, yMin, yMax } = props;
const { width, height } = this.state.dimensions;
const innerWidth = width - MARGIN.left - MARGIN.right; const innerWidth = width - MARGIN.left - MARGIN.right;
const outerHeight = Math.round(width * props.aspect); const outerHeight = Math.round(width * props.aspect);
const innerHeight = outerHeight - MARGIN.top - MARGIN.bottom; const innerHeight = outerHeight - MARGIN.top - MARGIN.bottom;
@@ -149,10 +145,11 @@ export default class LineChart extends TranslatedComponent {
/** /**
* Move and update tooltip * Move and update tooltip
* @param {SyntheticEvent} e Event * @param {SyntheticEvent} e Event
* @param {number} width current container width
*/ */
_moveTip(e) { _moveTip(e, width) {
let clientX = e.touches ? e.touches[0].clientX : e.clientX; let clientX = e.touches ? e.touches[0].clientX : e.clientX;
this._tooltip(Math.round(clientX - e.currentTarget.getBoundingClientRect().left)); this._tooltip(Math.round(clientX - e.currentTarget.getBoundingClientRect().left), width);
} }
/** /**
@@ -227,18 +224,17 @@ export default class LineChart extends TranslatedComponent {
* @return {React.Component} Chart SVG * @return {React.Component} Chart SVG
*/ */
render() { render() {
const { innerWidth, outerHeight, innerHeight } = this._updateDimensions(this.props, this.context.sizeRatio); return (
const { width, height } = this.state.dimensions; <ContainerDimensions>
{ ({ width, height }) => {
const { innerWidth, outerHeight, innerHeight } = this._updateDimensions(this.props, this.context.sizeRatio, width, height);
const { xMin, xMax, xLabel, yLabel, xUnit, yUnit, xMark, colors } = this.props; const { xMin, xMax, xLabel, yLabel, xUnit, yUnit, xMark, colors } = this.props;
const { tipHeight, detailElems, markerElems, seriesData, seriesLines } = this.state; const { tipHeight, detailElems, markerElems, seriesData, seriesLines } = this.state;
const line = this.line;
const lines = seriesLines.map((line, i) => <path key={i} className='line' fill='none' stroke={colors[i]} strokeWidth='1' d={line(seriesData)} />).reverse(); const lines = seriesLines.map((line, i) => <path key={i} className='line' fill='none' stroke={colors[i]} strokeWidth='1' d={line(seriesData)} />).reverse();
const markX = xMark ? innerWidth * (xMark - xMin) / (xMax - xMin) : 0; const markX = xMark ? innerWidth * (xMark - xMin) / (xMax - xMin) : 0;
const xmark = xMark ? <path key={'mark'} className='line' fill='none' strokeDasharray='5,5' stroke={'#ff8c0d'} strokeWidth='1' d={'M ' + markX + ' ' + innerHeight + ' L ' + markX + ' 0'} /> : ''; const xmark = xMark ? <path key={'mark'} className='line' fill='none' strokeDasharray='5,5' stroke={'#ff8c0d'} strokeWidth='1' d={'M ' + markX + ' ' + innerHeight + ' L ' + markX + ' 0'} /> : '';
return ( return (
<Measure width='100%' whitelist={['width', 'top']} onMeasure={ (dimensions) => { this.setState({ dimensions }); }}>
<div width={width} height={height}> <div width={width} height={height}>
<svg style={{ width: '100%', height: outerHeight }}> <svg style={{ width: '100%', height: outerHeight }}>
<g transform={`translate(${MARGIN.left},${MARGIN.top})`}> <g transform={`translate(${MARGIN.left},${MARGIN.top})`}>
@@ -271,13 +267,15 @@ export default class LineChart extends TranslatedComponent {
onTouchStart={this._showTip} onTouchStart={this._showTip}
onMouseLeave={this._hideTip} onMouseLeave={this._hideTip}
onTouchEnd={this._hideTip} onTouchEnd={this._hideTip}
onMouseMove={this._moveTip} onMouseMove={e => this._moveTip(e, width)}
onTouchMove={this._moveTip} onTouchMove={e => this._moveTip(e, width)}
/> />
</g> </g>
</svg> </svg>
</div> </div>
</Measure> );
}}
</ContainerDimensions>
); );
} }
} }

View File

@@ -0,0 +1,93 @@
import React from 'react';
import PropTypes from 'prop-types';
import request from 'superagent';
import TranslatedComponent from './TranslatedComponent';
import { orbisUpload } from '../utils/ShortenUrl';
import Persist from '../stores/Persist';
/**
* Permalink modal
*/
export default class ModalBatchOrbis extends TranslatedComponent {
static propTypes = {
ships: PropTypes.any.isRequired
};
/**
* Constructor
* @param {Object} props React Component properties
*/
constructor(props) {
super(props);
this.state = {
orbisCreds: Persist.getOrbisCreds(),
resp: ''
};
}
/**
* Send ship to Orbis.zone
* @param {SyntheticEvent} e React Event
* @return {Promise} Promise sending post request to orbis
*/
sendToOrbis(e) {
let agent;
try {
agent = request.agent(); // apparently this crashes somehow
} catch (e) {
console.error(e);
}
if (!agent) {
agent = request;
}
const API_ORBIS = 'https://orbis.zone/api/builds/add/batch';
return new Promise((resolve, reject) => {
try {
agent
.post(API_ORBIS)
.withCredentials()
.redirects(0)
.set('Content-Type', 'application/json')
.send(this.props.ships)
.end((err, response) => {
console.log(response);
if (err) {
console.error(err);
this.setState({ resp: response.text });
reject('Bad Request');
} else {
this.setState({ resp: 'All builds uploaded. Check https://orbis.zone' });
resolve('All builds uploaded. Check https://orbis.zone');
}
});
} catch (e) {
console.log(e);
reject(e.message ? e.message : e);
}
});
}
/**
* Render the modal
* @return {React.Component} Modal Content
*/
render() {
let translate = this.context.language.translate;
this.sendToOrbis = this.sendToOrbis.bind(this);
return <div className='modal' onClick={ (e) => e.stopPropagation() }>
<h2>{translate('permalink')}</h2>
<br/>
<a className='button' href="https://orbis.zone/api/auth">Log in / signup to Orbis</a>
<br/><br/>
<h3 >{translate('success')}</h3>
<input value={this.state.resp} readOnly size={25} onFocus={ (e) => e.target.select() }/>
<br/><br/>
<p>Orbis.zone is currently in a trial period, and may be wiped at any time as development progresses. Some elements are also still placeholders.</p>
<button className={'l cb dismiss cap'} disabled={!!this.state.failed} onClick={this.sendToOrbis}>{translate('PHASE_UPLOAD_ORBIS')}</button>
<button className={'r dismiss cap'} onClick={this.context.hideModal}>{translate('close')}</button>
</div>;
}
}

View File

@@ -126,7 +126,11 @@ export default class ModalImport extends TranslatedComponent {
if (importData.builds && typeof importData.builds == 'object') { if (importData.builds && typeof importData.builds == 'object') {
for (let shipId in importData.builds) { for (let shipId in importData.builds) {
for (let buildName in importData.builds[shipId]) { for (let buildName in importData.builds[shipId]) {
try {
validateBuild(shipId, importData.builds[shipId][buildName], buildName); validateBuild(shipId, importData.builds[shipId][buildName], buildName);
} catch (err) {
delete importData.builds[shipId][buildName];
}
} }
} }
this.setState({ builds: importData.builds }); this.setState({ builds: importData.builds });

View File

@@ -0,0 +1,117 @@
import React from 'react';
import PropTypes from 'prop-types';
import TranslatedComponent from './TranslatedComponent';
import { orbisUpload } from '../utils/ShortenUrl';
import Persist from '../stores/Persist';
/**
* Permalink modal
*/
export default class ModalOrbis extends TranslatedComponent {
static propTypes = {
ship: PropTypes.any.isRequired
};
/**
* Constructor
* @param {Object} props React Component properties
*/
constructor(props) {
super(props);
this.state = {
orbisCreds: Persist.getOrbisCreds(),
orbisUrl: '...',
authenticatedStatus: 'Checking...'
};
}
/**
* Send ship to Orbis.zone
* @param {SyntheticEvent} e React Event
*/
sendToOrbis(e) {
const target = e.target;
target.disabled = true;
this.setState({ orbisUrl: 'Sending...' }, () => {
orbisUpload(this.props.ship, this.state.orbisCreds)
.then(orbisUrl => {
target.disabled = false;
this.setState({ orbisUrl });
})
.catch(err => {
target.disabled = false;
this.setState({ orbisUrl: 'Error - ' + err });
});
});
}
/**
* Get Orbis.zone auth status
* @returns {Object} auth status
*/
getOrbisAuthStatus() {
return fetch('https://orbis.zone/api/checkauth', {
credentials: 'include',
mode: 'cors'
})
.then(data => data.json())
.then(res => {
this.setState({ authenticatedStatus: res.status || res.error });
})
.catch(err => {
console.error(err);
this.setState({ authenticatedStatus: err.message });
});
}
/**
* Handler for changing cmdr name
* @param {SyntheticEvent} e React Event
*/
orbisPasswordHandler(e) {
let password = e.target.value;
this.setState({ orbisCreds: { email: this.state.orbisCreds.email, password } }, () => {
Persist.setOrbisCreds(this.state.orbisCreds);
});
}
/**
* Handler for changing cmdr name
* @param {SyntheticEvent} e React Event
*/
orbisUsername(e) {
let orbisUsername = e.target.value;
this.setState({ orbisCreds: { email: orbisUsername, password: this.state.orbisCreds.password } }, () => {
Persist.setOrbisCreds(this.state.orbisCreds);
});
}
/**
* Render the modal
* @return {React.Component} Modal Content
*/
render() {
let translate = this.context.language.translate;
this.orbisPasswordHandler = this.orbisPasswordHandler.bind(this);
this.orbisUsername = this.orbisUsername.bind(this);
this.sendToOrbis = this.sendToOrbis.bind(this);
this.getOrbisAuthStatus();
return <div className='modal' onClick={ (e) => e.stopPropagation() }>
<h2>{translate('upload to orbis')}</h2>
<br/>
<label>Orbis auth status: </label>
<input value={this.state.authenticatedStatus} readOnly size={25} onFocus={ (e) => e.target.select() }/>
<br/><br/>
<a className='button' href="https://orbis.zone/api/auth">Log in / signup to Orbis</a>
<br/><br/>
<h3 >{translate('Orbis link')}</h3>
<input value={this.state.orbisUrl} readOnly size={25} onFocus={ (e) => e.target.select() }/>
<br/><br/>
<p>Orbis.zone is currently in a trial period, and may be wiped at any time as development progresses. Some elements are also still placeholders.</p>
<button className={'l cb dismiss cap'} disabled={!!this.state.failed} onClick={this.sendToOrbis}>{translate('PHASE_UPLOAD_ORBIS')}</button>
<button className={'r dismiss cap'} onClick={this.context.hideModal}>{translate('close')}</button>
</div>;
}
}

View File

@@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import TranslatedComponent from './TranslatedComponent'; import TranslatedComponent from './TranslatedComponent';
import ShortenUrl from '../utils/ShortenUrl'; import request from 'superagent';
import Persist from '../stores/Persist'; import Persist from '../stores/Persist';
/** /**
@@ -22,7 +22,11 @@ export default class ModalShoppingList extends TranslatedComponent {
this.state = { this.state = {
matsList: '', matsList: '',
mats: {}, mats: {},
matsPerGrade: Persist.getRolls() failed: false,
cmdrName: Persist.getCmdr().selected,
cmdrs: Persist.getCmdr().cmdrs,
matsPerGrade: Persist.getRolls(),
blueprints: []
}; };
} }
@@ -31,6 +35,114 @@ export default class ModalShoppingList extends TranslatedComponent {
*/ */
componentDidMount() { componentDidMount() {
this.renderMats(); this.renderMats();
if (this.checkBrowserIsCompatible()) {
this.getCommanders();
this.registerBPs();
}
}
/**
* Find all blueprints needed to make a build.
*/
registerBPs() {
const ship = this.props.ship;
let blueprints = [];
for (const module of ship.costList) {
if (module.type === 'SHIP') {
continue;
}
if (module.m && module.m.blueprint) {
if (!module.m.blueprint.grade || !module.m.blueprint.grades) {
continue;
}
if (module.m.blueprint.special) {
console.log(module.m.blueprint.special);
blueprints.push({ uuid: module.m.blueprint.special.uuid, number: 1 });
}
for (const g in module.m.blueprint.grades) {
if (!module.m.blueprint.grades.hasOwnProperty(g)) {
continue;
}
if (g > module.m.blueprint.grade) {
continue;
}
blueprints.push({ uuid: module.m.blueprint.grades[g].uuid, number: this.state.matsPerGrade[g] });
}
}
}
this.setState({ blueprints });
}
/**
* Check browser isn't firefox.
* @return {boolean} true if compatible, false if not.
*/
checkBrowserIsCompatible() {
// Firefox 1.0+
return typeof InstallTrigger === 'undefined';
}
/**
* Get a list of commanders from EDEngineer.
*/
getCommanders() {
request
.get('http://localhost:44405/commanders')
.end((err, res) => {
if (err) {
console.log(err);
return this.setState({ failed: true });
}
const cmdrs = JSON.parse(res.text);
if (!this.state.cmdrName) {
this.setState({ cmdrName: cmdrs[0] });
}
this.setState({ cmdrs }, () => {
Persist.setCmdr({ selected: this.state.cmdrName, cmdrs });
});
});
}
/**
* Send all blueprints to ED Engineer
* @param {Event} event React event
*/
sendToEDEng(event) {
event.preventDefault();
const target = event.target;
target.disabled = this.state.blueprints.length > 0;
if (this.state.blueprints.length === 0) {
target.innerText = 'No modded components.';
target.disabled = true;
setTimeout(() => {
target.innerText = 'Send to EDEngineer';
target.disabled = false;
}, 3000);
} else {
target.innerText = 'Sending...';
}
let countSent = 0;
let countTotal = this.state.blueprints.length;
for (const i of this.state.blueprints) {
request
.patch(`http://localhost:44405/${this.state.cmdrName}/shopping-list`)
.field('uuid', i.uuid)
.field('size', i.number)
.end(err => {
if (err) {
console.log(err);
if (err.message !== 'Bad Request') {
this.setState({ failed: true });
}
}
countSent++;
if (countSent === countTotal) {
target.disabled = false;
target.innerText = 'Send to EDEngineer';
}
});
}
} }
/** /**
@@ -48,10 +160,16 @@ export default class ModalShoppingList extends TranslatedComponent {
continue; continue;
} }
for (const g in module.m.blueprint.grades) { for (const g in module.m.blueprint.grades) {
if (!module.m.blueprint.grades.hasOwnProperty(g)) {
continue;
}
if (g > module.m.blueprint.grade) { if (g > module.m.blueprint.grade) {
continue; continue;
} }
for (const i in module.m.blueprint.grades[g].components) { for (const i in module.m.blueprint.grades[g].components) {
if (!module.m.blueprint.grades[g].components.hasOwnProperty(i)) {
continue;
}
if (mats[i]) { if (mats[i]) {
mats[i] += module.m.blueprint.grades[g].components[i] * this.state.matsPerGrade[g]; mats[i] += module.m.blueprint.grades[g].components[i] * this.state.matsPerGrade[g];
} else { } else {
@@ -86,6 +204,18 @@ export default class ModalShoppingList extends TranslatedComponent {
this.setState({ matsPerGrade: newState }); this.setState({ matsPerGrade: newState });
Persist.setRolls(newState); Persist.setRolls(newState);
this.renderMats(); this.renderMats();
this.registerBPs();
}
/**
* Handler for changing cmdr name
* @param {SyntheticEvent} e React Event
*/
cmdrChangeHandler(e) {
let cmdrName = e.target.value;
this.setState({ cmdrName }, () => {
Persist.setCmdr({ selected: this.state.cmdrName, cmdrs: this.state.cmdrs });
});
} }
/** /**
@@ -95,6 +225,9 @@ export default class ModalShoppingList extends TranslatedComponent {
render() { render() {
let translate = this.context.language.translate; let translate = this.context.language.translate;
this.changeHandler = this.changeHandler.bind(this); this.changeHandler = this.changeHandler.bind(this);
const compatible = this.checkBrowserIsCompatible();
this.cmdrChangeHandler = this.cmdrChangeHandler.bind(this);
this.sendToEDEng = this.sendToEDEng.bind(this);
return <div className='modal' onClick={ (e) => e.stopPropagation() }> return <div className='modal' onClick={ (e) => e.stopPropagation() }>
<h2>{translate('PHRASE_SHOPPING_MATS')}</h2> <h2>{translate('PHRASE_SHOPPING_MATS')}</h2>
<label>Grade 1 rolls </label> <label>Grade 1 rolls </label>
@@ -114,6 +247,15 @@ export default class ModalShoppingList extends TranslatedComponent {
<div> <div>
<textarea className='cb json' readOnly value={this.state.matsList} /> <textarea className='cb json' readOnly value={this.state.matsList} />
</div> </div>
<label hidden={!compatible} className={'l cap'}>CMDR Name </label>
<br/>
<select hidden={!compatible} className={'cmdr-select l cap'} onChange={this.cmdrChangeHandler} defaultValue={this.state.cmdrName}>
{this.state.cmdrs.map(e => <option key={e}>{e}</option>)}
</select>
<br/>
<p hidden={!this.state.failed} id={'failed'} className={'l'}>Failed to send to EDEngineer (Launch EDEngineer and make sure the API is started then refresh the page.)</p>
<p hidden={compatible} id={'browserbad'} className={'l'}>Sending to EDEngineer is not compatible with Firefox's security settings. Please try again with Chrome.</p>
<button className={'l cb dismiss cap'} disabled={!!this.state.failed || !compatible} onClick={this.sendToEDEng}>{translate('Send To EDEngineer')}</button>
<button className={'r dismiss cap'} onClick={this.context.hideModal}>{translate('close')}</button> <button className={'r dismiss cap'} onClick={this.context.hideModal}>{translate('close')}</button>
</div>; </div>;
} }

View File

@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import TranslatedComponent from './TranslatedComponent'; import TranslatedComponent from './TranslatedComponent';
import cn from 'classnames'; import cn from 'classnames';
import NumberEditor from 'react-number-editor'; import NumberEditor from 'react-number-editor';
import { isValueBeneficial } from '../utils/BlueprintFunctions';
/** /**
* Modification * Modification
@@ -38,22 +39,9 @@ export default class Modification extends TranslatedComponent {
* in a value by hand * in a value by hand
*/ */
_updateValue(value) { _updateValue(value) {
const name = this.props.name; let { m, name, ship } = this.props;
let scaledValue = Math.round(Number(value) * 100); value = Math.max(Math.min(value, 50000), -50000);
// Limit to +1000% / -99.99% ship.setModification(m, name, value, true, true);
if (scaledValue > 100000) {
scaledValue = 100000;
value = 1000;
}
if (scaledValue < -9999) {
scaledValue = -9999;
value = -99.99;
}
let m = this.props.m;
let ship = this.props.ship;
ship.setModification(m, name, scaledValue, true);
this.setState({ value }); this.setState({ value });
} }
@@ -75,28 +63,50 @@ export default class Modification extends TranslatedComponent {
* @return {React.Component} modification * @return {React.Component} modification
*/ */
render() { render() {
let translate = this.context.language.translate; let { translate, formats, units } = this.context.language;
let { m, name } = this.props; let { m, name } = this.props;
let modValue = m.getChange(name);
if (name === 'damagedist') { if (name === 'damagedist') {
// We don't show damage distribution // We don't show damage distribution
return null; return null;
} }
let symbol;
if (name === 'jitter') {
symbol = '°';
} else if (name !== 'burst' && name != 'burstrof') {
symbol = '%';
}
if (symbol) {
symbol = ' (' + symbol + ')';
}
return ( return (
<div onBlur={this._updateFinished.bind(this)} className={'cb'} key={name} ref={ modItem => this.props.modItems[name] = modItem }> <div onBlur={this._updateFinished.bind(this)} key={name}
<div className={'cb'}>{translate(name, m.grp)}{symbol}</div> className={cn('cb', 'modification-container')}
<NumberEditor className={'cb'} style={{ width: '90%', textAlign: 'center' }} step={0.01} stepModifier={1} decimals={2} value={this.state.value} onValueChange={this._updateValue.bind(this)} onKeyDown={ this.props.onKeyDown } /> ref={ modItem => this.props.modItems[name] = modItem }>
<span className={'cb'}>{translate(name, m.grp)}</span>
<span className={'header-adjuster'}></span>
<table style={{ width: '100%' }}>
<tbody>
<tr>
<td className={'input-container'}>
<span>
{this.props.editable ?
<NumberEditor className={'cb'} value={this.state.value}
decimals={2} style={{ textAlign: 'right' }} step={0.01}
stepModifier={1} onKeyDown={ this.props.onKeyDown }
onValueChange={this._updateValue.bind(this)} /> :
<input type="text" value={formats.f2(this.state.value)}
disabled className={'number-editor'}
style={{ textAlign: 'right', cursor: 'inherit' }}/>
}
<span className={'unit-container'}>
{units[m.getStoredUnitFor(name)]}
</span>
</span>
</td>
<td style={{ textAlign: 'center' }} className={
modValue ?
isValueBeneficial(name, modValue) ? 'secondary': 'warning':
''
}>
{formats.f2(modValue / 100) || 0}%
</td>
</tr>
</tbody>
</table>
</div> </div>
); );
} }

View File

@@ -15,6 +15,10 @@ import {
specialToolTip specialToolTip
} from '../utils/BlueprintFunctions'; } from '../utils/BlueprintFunctions';
const MODIFICATIONS_COMPARATOR = (mod1, mod2) => {
return mod1.props.name.localeCompare(mod2.props.name);
};
/** /**
* Modifications menu * Modifications menu
*/ */
@@ -205,15 +209,26 @@ export default class ModificationsMenu extends TranslatedComponent {
*/ */
_renderModifications(props) { _renderModifications(props) {
const { m, onChange, ship } = props; const { m, onChange, ship } = props;
const modifiableModifications = [];
const modifications = []; const modifications = [];
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' &&
m.blueprint.grades[m.blueprint.grade].features[modName];
this.lastNeId = modName; this.lastNeId = modName;
modifications.push(<Modification key={ key } ship={ ship } m={ m } name={ modName } value={ m.getModValue(modName) / 100 || 0 } onChange={ onChange } onKeyDown={ this._keyDown } modItems={ this.modItems } handleModChange = {this._handleModChange} />); (editable ? modifiableModifications : modifications).push(
<Modification key={ key } ship={ ship } m={ m }
value={m.getPretty(modName) || 0} modItems={this.modItems}
onChange={onChange} onKeyDown={this._keyDown} name={modName}
editable={editable} handleModChange = {this._handleModChange} />
);
} }
} }
return modifications;
modifiableModifications.sort(MODIFICATIONS_COMPARATOR);
modifications.sort(MODIFICATIONS_COMPARATOR);
return modifiableModifications.concat(modifications);
} }
/** /**

View File

@@ -42,6 +42,63 @@ export function weaponComparator(translate, propComparator, desc) {
}; };
} }
/**
* Creates a tooltip that shows damage by type.
* @param {function} translate Translation function
* @param {Object} formats Object that holds format functions
* @param {Calc.SDps} sdpsObject Object that holds sdps split up by type
* @returns {Array} Tooltip
*/
function getSDpsTooltip(translate, formats, sdpsObject) {
return Object.keys(sdpsObject).filter(key => sdpsObject[key])
.map(key => {
return (
<div key={key}>
{translate(key) + ' ' + formats.f1(sdpsObject[key])}
</div>
);
});
}
/**
* Returns a data object used by {@link PieChart} that shows damage by type.
* @param {function} translate Translation function
* @param {Calc.SDps} sdpsObject Object that holds sdps split up by type
* @returns {Object} Data object
*/
function getSDpsData(translate, sdpsObject) {
return Object.keys(sdpsObject).map(key => {
return {
value: Math.round(sdpsObject[key]),
label: translate(key)
};
});
}
/**
* Adds all damage of `add` onto `addOn`.
* @param {Calc.SDps} addOn Object that holds sdps split up by type (will be mutated)
* @param {Calc.SDps} add Object that holds sdps split up by type
*/
function addSDps(addOn, add) {
Object.keys(addOn).map(k => addOn[k] += (add[k] ? add[k] : 0));
}
/**
* Calculates the overall sdps of an sdps object.
* @param {Calc.SDps} sdpsObject Object that holds sdps spluit up by type
*/
function sumSDps(sdpsObject) {
if (sdpsObject.total) {
return sdpsObject.total;
}
return Object.keys(sdpsObject).reduce(
(acc, k) => acc + (sdpsObject[k] ? sdpsObject[k] : 0),
0
);
}
/** /**
* Offence information * Offence information
* Offence information consists of four panels: * Offence information consists of four panels:
@@ -144,51 +201,36 @@ export default class Offence extends TranslatedComponent {
const timeToDrain = Calc.timeToDrainWep(ship, wep); const timeToDrain = Calc.timeToDrainWep(ship, wep);
let absoluteShieldsSDps = 0;
let explosiveShieldsSDps = 0;
let kineticShieldsSDps = 0;
let thermalShieldsSDps = 0;
let absoluteArmourSDps = 0;
let explosiveArmourSDps = 0;
let kineticArmourSDps = 0;
let thermalArmourSDps = 0;
let totalSEps = 0; let totalSEps = 0;
let totalSDpsObject = {'absolute': 0, 'explosive': 0, 'kinetic': 0, 'thermal': 0};
let shieldsSDpsObject = {'absolute': 0, 'explosive': 0, 'kinetic': 0, 'thermal': 0};
let armourSDpsObject = {'absolute': 0, 'explosive': 0, 'kinetic': 0, 'thermal': 0};
const rows = []; const rows = [];
for (let i = 0; i < damage.length; i++) { for (let i = 0; i < damage.length; i++) {
const weapon = damage[i]; const weapon = damage[i];
totalSEps += weapon.seps; totalSEps += weapon.seps;
absoluteShieldsSDps += weapon.sdps.shields.absolute; addSDps(totalSDpsObject, weapon.sdps.base);
explosiveShieldsSDps += weapon.sdps.shields.explosive; addSDps(shieldsSDpsObject, weapon.sdps.shields);
kineticShieldsSDps += weapon.sdps.shields.kinetic; addSDps(armourSDpsObject, weapon.sdps.armour);
thermalShieldsSDps += weapon.sdps.shields.thermal;
absoluteArmourSDps += weapon.sdps.armour.absolute; const baseSDpsTooltipDetails = getSDpsTooltip(translate, formats, weapon.sdps.base);
explosiveArmourSDps += weapon.sdps.armour.explosive;
kineticArmourSDps += weapon.sdps.armour.kinetic;
thermalArmourSDps += weapon.sdps.armour.thermal;
const effectivenessShieldsTooltipDetails = []; const effectivenessShieldsTooltipDetails = [];
effectivenessShieldsTooltipDetails.push(<div key='range'>{translate('range') + ' ' + formats.pct1(weapon.effectiveness.shields.range)}</div>); effectivenessShieldsTooltipDetails.push(<div key='range'>{translate('range') + ' ' + formats.pct1(weapon.effectiveness.shields.range)}</div>);
effectivenessShieldsTooltipDetails.push(<div key='resistance'>{translate('resistance') + ' ' + formats.pct1(weapon.effectiveness.shields.resistance)}</div>); effectivenessShieldsTooltipDetails.push(<div key='resistance'>{translate('resistance') + ' ' + formats.pct1(weapon.effectiveness.shields.resistance)}</div>);
effectivenessShieldsTooltipDetails.push(<div key='power distributor'>{translate('power distributor') + ' ' + formats.pct1(weapon.effectiveness.shields.sys)}</div>); effectivenessShieldsTooltipDetails.push(<div key='power distributor'>{translate('power distributor') + ' ' + formats.pct1(weapon.effectiveness.shields.sys)}</div>);
const effectiveShieldsSDpsTooltipDetails = []; const effectiveShieldsSDpsTooltipDetails = getSDpsTooltip(translate, formats, weapon.sdps.armour);
if (weapon.sdps.shields.absolute) effectiveShieldsSDpsTooltipDetails.push(<div key='absolute'>{translate('absolute') + ' ' + formats.f1(weapon.sdps.shields.absolute)}</div>);
if (weapon.sdps.shields.explosive) effectiveShieldsSDpsTooltipDetails.push(<div key='explosive'>{translate('explosive') + ' ' + formats.f1(weapon.sdps.shields.explosive)}</div>);
if (weapon.sdps.shields.kinetic) effectiveShieldsSDpsTooltipDetails.push(<div key='kinetic'>{translate('kinetic') + ' ' + formats.f1(weapon.sdps.shields.kinetic)}</div>);
if (weapon.sdps.shields.thermal) effectiveShieldsSDpsTooltipDetails.push(<div key='thermal'>{translate('thermal') + ' ' + formats.f1(weapon.sdps.shields.thermal)}</div>);
const effectivenessArmourTooltipDetails = []; const effectivenessArmourTooltipDetails = [];
effectivenessArmourTooltipDetails.push(<div key='range'>{translate('range') + ' ' + formats.pct1(weapon.effectiveness.armour.range)}</div>); effectivenessArmourTooltipDetails.push(<div key='range'>{translate('range') + ' ' + formats.pct1(weapon.effectiveness.armour.range)}</div>);
effectivenessArmourTooltipDetails.push(<div key='resistance'>{translate('resistance') + ' ' + formats.pct1(weapon.effectiveness.armour.resistance)}</div>); effectivenessArmourTooltipDetails.push(<div key='resistance'>{translate('resistance') + ' ' + formats.pct1(weapon.effectiveness.armour.resistance)}</div>);
effectivenessArmourTooltipDetails.push(<div key='hardness'>{translate('hardness') + ' ' + formats.pct1(weapon.effectiveness.armour.hardness)}</div>); effectivenessArmourTooltipDetails.push(<div key='hardness'>{translate('hardness') + ' ' + formats.pct1(weapon.effectiveness.armour.hardness)}</div>);
const effectiveArmourSDpsTooltipDetails = [];
if (weapon.sdps.armour.absolute) effectiveArmourSDpsTooltipDetails.push(<div key='absolute'>{translate('absolute') + ' ' + formats.f1(weapon.sdps.armour.absolute)}</div>); const effectiveArmourSDpsTooltipDetails = getSDpsTooltip(translate, formats, weapon.sdps.armour);
if (weapon.sdps.armour.explosive) effectiveArmourSDpsTooltipDetails.push(<div key='explosive'>{translate('explosive') + ' ' + formats.f1(weapon.sdps.armour.explosive)}</div>);
if (weapon.sdps.armour.kinetic) effectiveArmourSDpsTooltipDetails.push(<div key='kinetic'>{translate('kinetic') + ' ' + formats.f1(weapon.sdps.armour.kinetic)}</div>);
if (weapon.sdps.armour.thermal) effectiveArmourSDpsTooltipDetails.push(<div key='thermal'>{translate('thermal') + ' ' + formats.f1(weapon.sdps.armour.thermal)}</div>);
rows.push( rows.push(
<tr key={weapon.id}> <tr key={weapon.id}>
@@ -199,6 +241,7 @@ export default class Offence extends TranslatedComponent {
{weapon.classRating} {translate(weapon.name)} {weapon.classRating} {translate(weapon.name)}
{weapon.engineering ? ' (' + weapon.engineering + ')' : null } {weapon.engineering ? ' (' + weapon.engineering + ')' : null }
</td> </td>
<td className='ri'><span onMouseOver={termtip.bind(null, baseSDpsTooltipDetails)} onMouseOut={tooltip.bind(null, null)}>{formats.f1(weapon.sdps.base.total)}</span></td>
<td className='ri'><span onMouseOver={termtip.bind(null, effectiveShieldsSDpsTooltipDetails)} onMouseOut={tooltip.bind(null, null)}>{formats.f1(weapon.sdps.shields.total)}</span></td> <td className='ri'><span onMouseOver={termtip.bind(null, effectiveShieldsSDpsTooltipDetails)} onMouseOut={tooltip.bind(null, null)}>{formats.f1(weapon.sdps.shields.total)}</span></td>
<td className='ri'><span onMouseOver={termtip.bind(null, effectivenessShieldsTooltipDetails)} onMouseOut={tooltip.bind(null, null)}>{formats.pct1(weapon.effectiveness.shields.total)}</span></td> <td className='ri'><span onMouseOver={termtip.bind(null, effectivenessShieldsTooltipDetails)} onMouseOut={tooltip.bind(null, null)}>{formats.pct1(weapon.effectiveness.shields.total)}</span></td>
<td className='ri'><span onMouseOver={termtip.bind(null, effectiveArmourSDpsTooltipDetails)} onMouseOut={tooltip.bind(null, null)}>{formats.f1(weapon.sdps.armour.total)}</span></td> <td className='ri'><span onMouseOver={termtip.bind(null, effectiveArmourSDpsTooltipDetails)} onMouseOut={tooltip.bind(null, null)}>{formats.f1(weapon.sdps.armour.total)}</span></td>
@@ -206,20 +249,17 @@ export default class Offence extends TranslatedComponent {
</tr>); </tr>);
} }
const totalShieldsSDps = absoluteShieldsSDps + explosiveShieldsSDps + kineticShieldsSDps + thermalShieldsSDps; const totalSDps = sumSDps(totalSDpsObject);
const totalArmourSDps = absoluteArmourSDps + explosiveArmourSDps + kineticArmourSDps + thermalArmourSDps; const totalSDpsTooltipDetails = getSDpsTooltip(translate, formats, totalSDpsObject);
const totalSDpsData = getSDpsData(translate, totalSDpsObject);
const shieldsSDpsData = []; const totalShieldsSDps = sumSDps(shieldsSDpsObject);
shieldsSDpsData.push({ value: Math.round(absoluteShieldsSDps), label: translate('absolute') }); const totalShieldsSDpsTooltipDetails = getSDpsTooltip(translate, formats, shieldsSDpsObject);
shieldsSDpsData.push({ value: Math.round(explosiveShieldsSDps), label: translate('explosive') }); const shieldsSDpsData = getSDpsData(translate, shieldsSDpsObject);
shieldsSDpsData.push({ value: Math.round(kineticShieldsSDps), label: translate('kinetic') });
shieldsSDpsData.push({ value: Math.round(thermalShieldsSDps), label: translate('thermal') });
const armourSDpsData = []; const totalArmourSDps = sumSDps(armourSDpsObject);
armourSDpsData.push({ value: Math.round(absoluteArmourSDps), label: translate('absolute') }); const totalArmourSDpsTooltipDetails = getSDpsTooltip(translate, formats, armourSDpsObject);
armourSDpsData.push({ value: Math.round(explosiveArmourSDps), label: translate('explosive') }); const armourSDpsData = getSDpsData(translate, armourSDpsObject);
armourSDpsData.push({ value: Math.round(kineticArmourSDps), label: translate('kinetic') });
armourSDpsData.push({ value: Math.round(thermalArmourSDps), label: translate('thermal') });
const timeToDepleteShields = Calc.timeToDeplete(opponentShields.total, totalShieldsSDps, totalSEps, pd.getWeaponsCapacity(), pd.getWeaponsRechargeRate() * (wep / 4)); const timeToDepleteShields = Calc.timeToDeplete(opponentShields.total, totalShieldsSDps, totalSEps, pd.getWeaponsCapacity(), pd.getWeaponsRechargeRate() * (wep / 4));
const timeToDepleteArmour = Calc.timeToDeplete(opponentArmour.total, totalArmourSDps, totalSEps, pd.getWeaponsCapacity(), pd.getWeaponsRechargeRate() * (wep / 4)); const timeToDepleteArmour = Calc.timeToDeplete(opponentArmour.total, totalArmourSDps, totalSEps, pd.getWeaponsCapacity(), pd.getWeaponsRechargeRate() * (wep / 4));
@@ -231,10 +271,12 @@ export default class Offence extends TranslatedComponent {
<thead> <thead>
<tr className='main'> <tr className='main'>
<th rowSpan='2' className='sortable' onClick={sortOrder.bind(this, 'n')}>{translate('weapon')}</th> <th rowSpan='2' className='sortable' onClick={sortOrder.bind(this, 'n')}>{translate('weapon')}</th>
<th colSpan='1'>{translate('overall')}</th>
<th colSpan='2'>{translate('opponent\'s shields')}</th> <th colSpan='2'>{translate('opponent\'s shields')}</th>
<th colSpan='2'>{translate('opponent\'s armour')}</th> <th colSpan='2'>{translate('opponent\'s armour')}</th>
</tr> </tr>
<tr> <tr>
<th className='lft sortable' onMouseOver={termtip.bind(null, 'TT_EFFECTIVE_SDPS_SHIELDS')} onMouseOut={tooltip.bind(null, null)} onClick={sortOrder.bind(this, 'esdpss')}>{'sdps'}</th>
<th className='lft sortable' onMouseOver={termtip.bind(null, 'TT_EFFECTIVE_SDPS_SHIELDS')} onMouseOut={tooltip.bind(null, null)} onClick={sortOrder.bind(this, 'esdpss')}>{'sdps'}</th> <th className='lft sortable' onMouseOver={termtip.bind(null, 'TT_EFFECTIVE_SDPS_SHIELDS')} onMouseOut={tooltip.bind(null, null)} onClick={sortOrder.bind(this, 'esdpss')}>{'sdps'}</th>
<th className='sortable' onMouseOver={termtip.bind(null, 'TT_EFFECTIVENESS_SHIELDS')} onMouseOut={tooltip.bind(null, null)}onClick={sortOrder.bind(this, 'es')}>{'eft'}</th> <th className='sortable' onMouseOver={termtip.bind(null, 'TT_EFFECTIVENESS_SHIELDS')} onMouseOut={tooltip.bind(null, null)}onClick={sortOrder.bind(this, 'es')}>{'eft'}</th>
<th className='lft sortable' onMouseOver={termtip.bind(null, 'TT_EFFECTIVE_SDPS_ARMOUR')} onMouseOut={tooltip.bind(null, null)}onClick={sortOrder.bind(this, 'esdpsh')}>{'sdps'}</th> <th className='lft sortable' onMouseOver={termtip.bind(null, 'TT_EFFECTIVE_SDPS_ARMOUR')} onMouseOut={tooltip.bind(null, null)}onClick={sortOrder.bind(this, 'esdpsh')}>{'sdps'}</th>
@@ -243,6 +285,16 @@ export default class Offence extends TranslatedComponent {
</thead> </thead>
<tbody> <tbody>
{rows} {rows}
{rows.length > 0 &&
<tr>
<td></td>
<td className='ri'><span onMouseOver={termtip.bind(null, totalSDpsTooltipDetails)} onMouseOut={tooltip.bind(null, null)}>={formats.f1(totalSDps)}</span></td>
<td className='ri'><span onMouseOver={termtip.bind(null, totalShieldsSDpsTooltipDetails)} onMouseOut={tooltip.bind(null, null)}>={formats.f1(totalShieldsSDps)}</span></td>
<td></td>
<td className='ri'><span onMouseOver={termtip.bind(null, totalArmourSDpsTooltipDetails)} onMouseOut={tooltip.bind(null, null)}>={formats.f1(totalArmourSDps)}</span></td>
<td></td>
</tr>
}
</tbody> </tbody>
</table> </table>
</div> </div>
@@ -254,6 +306,10 @@ export default class Offence extends TranslatedComponent {
<h2 onMouseOver={termtip.bind(null, translate('TT_EFFECTIVE_SDPS_ARMOUR'))} onMouseOut={tooltip.bind(null, null)}>{translate('PHRASE_EFFECTIVE_SDPS_ARMOUR')}<br/>{formats.f1(totalArmourSDps)}</h2> <h2 onMouseOver={termtip.bind(null, translate('TT_EFFECTIVE_SDPS_ARMOUR'))} onMouseOut={tooltip.bind(null, null)}>{translate('PHRASE_EFFECTIVE_SDPS_ARMOUR')}<br/>{formats.f1(totalArmourSDps)}</h2>
<h2 onMouseOver={termtip.bind(null, translate('TT_TIME_TO_REMOVE_ARMOUR'))} onMouseOut={tooltip.bind(null, null)}>{translate('PHRASE_TIME_TO_REMOVE_ARMOUR')}<br/>{timeToDepleteArmour === Infinity ? translate('never') : formats.time(timeToDepleteArmour)}</h2> <h2 onMouseOver={termtip.bind(null, translate('TT_TIME_TO_REMOVE_ARMOUR'))} onMouseOut={tooltip.bind(null, null)}>{translate('PHRASE_TIME_TO_REMOVE_ARMOUR')}<br/>{timeToDepleteArmour === Infinity ? translate('never') : formats.time(timeToDepleteArmour)}</h2>
</div> </div>
<div className='group quarter'>
<h2 onMouseOver={termtip.bind(null, translate('PHRASE_OVERALL_DAMAGE'))} onMouseOut={tooltip.bind(null, null)}>{translate('overall damage')}</h2>
<PieChart data={totalSDpsData} />
</div>
<div className='group quarter'> <div className='group quarter'>
<h2 onMouseOver={termtip.bind(null, translate('PHRASE_SHIELD_DAMAGE'))} onMouseOut={tooltip.bind(null, null)}>{translate('shield damage sources')}</h2> <h2 onMouseOver={termtip.bind(null, translate('PHRASE_SHIELD_DAMAGE'))} onMouseOut={tooltip.bind(null, null)}>{translate('shield damage sources')}</h2>
<PieChart data={shieldsSDpsData} /> <PieChart data={shieldsSDpsData} />

View File

@@ -1,6 +1,6 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Measure from 'react-measure'; import ContainerDimensions from 'react-container-dimensions';
import * as d3 from 'd3'; import * as d3 from 'd3';
const CORIOLIS_COLOURS = ['#FF8C0D', '#1FB0FF', '#71A052', '#D5D54D']; const CORIOLIS_COLOURS = ['#FF8C0D', '#1FB0FF', '#71A052', '#D5D54D'];
@@ -27,13 +27,6 @@ export default class PieChart extends Component {
this.colors = CORIOLIS_COLOURS; this.colors = CORIOLIS_COLOURS;
this.arc = d3.arc(); this.arc = d3.arc();
this.arc.innerRadius(0); this.arc.innerRadius(0);
this.state = {
dimensions: {
width: 100,
height: 100
}
};
} }
@@ -41,15 +34,15 @@ export default class PieChart extends Component {
* Generate a slice of the pie chart * Generate a slice of the pie chart
* @param {Object} d the data for this slice * @param {Object} d the data for this slice
* @param {number} i the index of this slice * @param {number} i the index of this slice
* @param {number} width the current width of the parent container
* @returns {Object} the SVG for the slice * @returns {Object} the SVG for the slice
*/ */
sliceGenerator(d, i) { sliceGenerator(d, i, width) {
if (!d || d.value == 0) { if (!d || d.value == 0) {
// Ignore 0 values // Ignore 0 values
return null; return null;
} }
const { width, height } = this.state.dimensions;
const { data } = this.props; const { data } = this.props;
// Push the labels further out from the centre of the slice // Push the labels further out from the centre of the slice
@@ -76,22 +69,24 @@ export default class PieChart extends Component {
* @returns {object} Markup * @returns {object} Markup
*/ */
render() { render() {
const { width, height } = this.state.dimensions; return (
<ContainerDimensions>
{ ({ width }) => {
const pie = this.pie(this.props.data), const pie = this.pie(this.props.data),
translate = `translate(${width / 2}, ${width * 0.4})`; translate = `translate(${width / 2}, ${width * 0.4})`;
this.arc.outerRadius(width * 0.4); this.arc.outerRadius(width * 0.4);
return ( return (
<Measure width='100%' whitelist={['width', 'top']} onMeasure={ (dimensions) => { this.setState({ dimensions }); }}>
<div width={width} height={width}> <div width={width} height={width}>
<svg style={{ stroke: 'None' }} width={width} height={width * 0.9}> <svg style={{ stroke: 'None' }} width={width} height={width * 0.9}>
<g transform={translate}> <g transform={translate}>
{pie.map((d, i) => this.sliceGenerator(d, i))} {pie.map((d, i) => this.sliceGenerator(d, i, width))}
</g> </g>
</svg> </svg>
</div> </div>
</Measure> );
}}
</ContainerDimensions>
); );
} }
} }

View File

@@ -50,7 +50,7 @@ export default class ShipSummaryTable extends TranslatedComponent {
const speedTooltip = canThrust ? 'TT_SUMMARY_SPEED' : 'TT_SUMMARY_SPEED_NONFUNCTIONAL'; const speedTooltip = canThrust ? 'TT_SUMMARY_SPEED' : 'TT_SUMMARY_SPEED_NONFUNCTIONAL';
const canBoost = ship.canBoost(cargo, ship.fuelCapacity); const canBoost = ship.canBoost(cargo, ship.fuelCapacity);
const boostTooltip = canBoost ? 'TT_SUMMARY_BOOST' : canThrust ? 'TT_SUMMARY_BOOST_NONFUNCTIONAL' : 'TT_SUMMARY_SPEED_NONFUNCTIONAL'; const boostTooltip = canBoost ? 'TT_SUMMARY_BOOST' : canThrust ? 'TT_SUMMARY_BOOST_NONFUNCTIONAL' : 'TT_SUMMARY_SPEED_NONFUNCTIONAL';
const sgMetrics = Calc.shieldMetrics(ship, pips.sys || 2); const sgMetrics = Calc.shieldMetrics(ship, pips.sys);
const shipBoost = canBoost ? Calc.calcBoost(ship) : 'No Boost'; const shipBoost = canBoost ? Calc.calcBoost(ship) : 'No Boost';
const armourMetrics = Calc.armourMetrics(ship); const armourMetrics = Calc.armourMetrics(ship);
let shieldColour = 'blue'; let shieldColour = 'blue';
@@ -63,6 +63,8 @@ export default class ShipSummaryTable extends TranslatedComponent {
shieldColour shieldColour
}; };
return <div id='summary'> return <div id='summary'>
<div style={{display: "table", width: "100%"}}>
<div style={{display: "table-row"}}>
<table className={'summaryTable'}> <table className={'summaryTable'}>
<thead> <thead>
<tr className='main'> <tr className='main'>
@@ -99,11 +101,11 @@ export default class ShipSummaryTable extends TranslatedComponent {
<tr> <tr>
<td onMouseEnter={termtip.bind(null, speedTooltip, { cap: 0 })} onMouseLeave={hide}>{ canThrust ? <span>{int(ship.calcSpeed(4, ship.fuelCapacity, 0, false))}{u['m/s']}</span> : <span className='warning'>0 <Warning/></span> }</td> <td onMouseEnter={termtip.bind(null, speedTooltip, { cap: 0 })} onMouseLeave={hide}>{ canThrust ? <span>{int(ship.calcSpeed(4, ship.fuelCapacity, 0, false))}{u['m/s']}</span> : <span className='warning'>0 <Warning/></span> }</td>
<td onMouseEnter={termtip.bind(null, boostTooltip, { cap: 0 })} onMouseLeave={hide}>{ canBoost ? <span>{int(ship.calcSpeed(4, ship.fuelCapacity, 0, true))}{u['m/s']}</span> : <span className='warning'>0 <Warning/></span> }</td> <td onMouseEnter={termtip.bind(null, boostTooltip, { cap: 0 })} onMouseLeave={hide}>{ canBoost ? <span>{int(ship.calcSpeed(4, ship.fuelCapacity, 0, true))}{u['m/s']}</span> : <span className='warning'>0 <Warning/></span> }</td>
<td><span onMouseEnter={termtip.bind(null, 'TT_SUMMARY_MAX_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{f2(Calc.jumpRange(ship.unladenMass + ship.standard[2].m.getMaxFuelPerJump(), ship.standard[2].m, ship.standard[2].m.getMaxFuelPerJump()))}{u.LY}</span></td> <td><span onMouseEnter={termtip.bind(null, 'TT_SUMMARY_MAX_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{f2(Calc.jumpRange(ship.unladenMass + ship.standard[2].m.getMaxFuelPerJump(), ship.standard[2].m, ship.standard[2].m.getMaxFuelPerJump(), ship))}{u.LY}</span></td>
<td><span onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{f2(Calc.jumpRange(ship.unladenMass + ship.fuelCapacity, ship.standard[2].m, ship.fuelCapacity))}{u.LY}</span></td> <td><span onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{f2(Calc.jumpRange(ship.unladenMass + ship.fuelCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</span></td>
<td><span onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{f2(Calc.jumpRange(ship.unladenMass + ship.fuelCapacity + ship.cargoCapacity, ship.standard[2].m, ship.fuelCapacity))}{u.LY}</span></td> <td><span onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{f2(Calc.jumpRange(ship.unladenMass + ship.fuelCapacity + ship.cargoCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</span></td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_TOTAL_JUMP', { cap: 0 })} onMouseLeave={hide}>{f2(Calc.totalJumpRange(ship.unladenMass + ship.fuelCapacity, ship.standard[2].m, ship.fuelCapacity))}{u.LY}</td> <td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_TOTAL_JUMP', { cap: 0 })} onMouseLeave={hide}>{f2(Calc.totalJumpRange(ship.unladenMass + ship.fuelCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_TOTAL_JUMP', { cap: 0 })} onMouseLeave={hide}>{f2(Calc.totalJumpRange(ship.unladenMass + ship.fuelCapacity + ship.cargoCapacity, ship.standard[2].m, ship.fuelCapacity))}{u.LY}</td> <td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_TOTAL_JUMP', { cap: 0 })} onMouseLeave={hide}>{f2(Calc.totalJumpRange(ship.unladenMass + ship.fuelCapacity + ship.cargoCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</td>
<td className={sgClassNames} onMouseEnter={termtip.bind(null, sgTooltip, { cap: 0 })} onMouseLeave={hide}>{int(ship.shield)}{u.MJ}</td> <td className={sgClassNames} onMouseEnter={termtip.bind(null, sgTooltip, { cap: 0 })} onMouseLeave={hide}>{int(ship.shield)}{u.MJ}</td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_INTEGRITY', { cap: 0 })} onMouseLeave={hide}>{int(ship.armour)}</td> <td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_INTEGRITY', { cap: 0 })} onMouseLeave={hide}>{int(ship.armour)}</td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_DPS', { cap: 0 })} onMouseLeave={hide}>{f1(ship.totalDps)}</td> <td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_DPS', { cap: 0 })} onMouseLeave={hide}>{f1(ship.totalDps)}</td>
@@ -126,67 +128,85 @@ export default class ShipSummaryTable extends TranslatedComponent {
<table className={'summaryTable'}> <table className={'summaryTable'}>
<thead className={this.state.shieldColour}> <thead className={this.state.shieldColour}>
<tr> <tr>
<th onMouseEnter={termtip.bind(null, 'shield', { cap: 0 })} onMouseLeave={hide} className='lft'>{translate('shield')}</th> <th rowSpan={2} onMouseEnter={termtip.bind(null, 'shield', { cap: 0 })} onMouseLeave={hide} className='lft'>{translate('shield')}</th>
<th onMouseEnter={termtip.bind(null, 'explres', { cap: 0 })} onMouseLeave={hide} className='lft'>{translate('explres')}</th> <th colSpan={4} className='lft'>{translate('resistance')}</th>
<th onMouseEnter={termtip.bind(null, 'kinres', { cap: 0 })} onMouseLeave={hide} className='lft'>{translate('kinres')}</th>
<th onMouseEnter={termtip.bind(null, 'thermres', { cap: 0 })} onMouseLeave={hide} className='lft'>{translate('thermres')}</th>
<th onMouseEnter={termtip.bind(null, 'TT_SUMMARY_SHIELDS_SCB', { cap: 0 })} onMouseLeave={hide} className='lft'>{`${translate('absolute')} ${translate('HP')}`}</th> <th colSpan={5} onMouseEnter={termtip.bind(null, 'TT_SUMMARY_SHIELDS_SCB', { cap: 0 })} onMouseLeave={hide} className='lft'>{`${translate('HP')}`}</th>
<th onMouseEnter={termtip.bind(null, 'TT_SUMMARY_SHIELDS_SCB', { cap: 0 })} onMouseLeave={hide} className='lft'>{`${translate('explosive')} ${translate('HP')}`}</th> <th rowSpan={2} onMouseEnter={termtip.bind(null, 'PHRASE_SG_RECOVER', { cap: 0 })} onMouseLeave={hide} className='lft'>{translate('recovery')}</th>
<th onMouseEnter={termtip.bind(null, 'TT_SUMMARY_SHIELDS_SCB', { cap: 0 })} onMouseLeave={hide} className='lft'>{`${translate('kinetic')} ${translate('HP')}`}</th> <th rowSpan={2} onMouseEnter={termtip.bind(null, 'PHRASE_SG_RECHARGE', { cap: 0 })} onMouseLeave={hide} className='lft'>{translate('recharge')}</th>
<th onMouseEnter={termtip.bind(null, 'TT_SUMMARY_SHIELDS_SCB', { cap: 0 })} onMouseLeave={hide} className='lft'>{`${translate('thermal')} ${translate('HP')}`}</th> </tr>
<th onMouseEnter={termtip.bind(null, 'PHRASE_SG_RECOVER', { cap: 0 })} onMouseLeave={hide} className='lft'>{translate('recovery')}</th> <tr>
<th onMouseEnter={termtip.bind(null, 'PHRASE_SG_RECHARGE', { cap: 0 })} onMouseLeave={hide} className='lft'>{translate('recharge')}</th> <th>{`${translate('explosive')}`}</th>
<th>{`${translate('kinetic')}`}</th>
<th>{`${translate('thermal')}`}</th>
<th></th>
<th className={'bordered'}>{`${translate('absolute')}`}</th>
<th>{`${translate('explosive')}`}</th>
<th>{`${translate('kinetic')}`}</th>
<th>{`${translate('thermal')}`}</th>
<th></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<td>{translate(shieldGenerator && shieldGenerator.m.grp || 'No Shield')}</td> <td>{translate(shieldGenerator && shieldGenerator.m.grp || 'No Shield')}</td>
<td>{int(ship.shieldExplRes * 100) + '%'}</td> <td>{formats.pct1(ship.shieldExplRes)}</td>
<td>{int(ship.shieldKinRes * 100) + '%'}</td> <td>{formats.pct1(ship.shieldKinRes)}</td>
<td>{int(ship.shieldThermRes * 100) + '%'}</td> <td>{formats.pct1(ship.shieldThermRes)}</td>
<td>{int(sgMetrics && sgMetrics.generator ? sgMetrics.total / sgMetrics.absolute.total : 0)}</td> <td></td>
<td>{int(sgMetrics && sgMetrics.generator ? sgMetrics.total / sgMetrics.explosive.total : 0)}</td>
<td>{int(sgMetrics && sgMetrics.generator ? sgMetrics.total / sgMetrics.kinetic.total : 0)}</td> <td>{int(ship && ship.shield > 0 ? ship.shield : 0)}{u.MJ}</td>
<td>{int(sgMetrics && sgMetrics.generator ? sgMetrics.total / sgMetrics.thermal.total : 0)}</td> <td>{int(ship && ship.shield > 0 ? ship.shield * ((1 / (1 - (ship.shieldExplRes)))) : 0)}{u.MJ}</td>
<td>{sgMetrics && sgMetrics.recover ? formats.time(sgMetrics.recover) : 0}</td> <td>{int(ship && ship.shield > 0 ? ship.shield * ((1 / (1 - (ship.shieldKinRes)))) : 0)}{u.MJ}</td>
<td>{sgMetrics && sgMetrics.recharge ? formats.time(sgMetrics.recharge) : 0}</td> <td>{int(ship && ship.shield > 0 ? ship.shield * ((1 / (1 - (ship.shieldThermRes)))) : 0)}{u.MJ}</td>
<td></td>
<td>{sgMetrics && sgMetrics.recover === Math.Inf ? translate('Never') : formats.time(sgMetrics.recover)}</td>
<td>{sgMetrics && sgMetrics.recharge === Math.Inf ? translate('Never') : formats.time(sgMetrics.recharge)}</td>
</tr> </tr>
</tbody> </tbody>
<thead> <thead>
<tr> <tr>
<th onMouseEnter={termtip.bind(null, 'armour', { cap: 0 })} onMouseLeave={hide} className='lft'>{translate('armour')}</th> <th rowSpan={2} onMouseEnter={termtip.bind(null, 'armour', { cap: 0 })} onMouseLeave={hide} className='lft'>{translate('armour')}</th>
<th onMouseEnter={termtip.bind(null, 'explres', { cap: 0 })} onMouseLeave={hide} className='lft'>{translate('explres')}</th> <th colSpan={4} className='lft'>{translate('resistance')}</th>
<th onMouseEnter={termtip.bind(null, 'kinres', { cap: 0 })} onMouseLeave={hide} className='lft'>{translate('kinres')}</th>
<th onMouseEnter={termtip.bind(null, 'thermres', { cap: 0 })} onMouseLeave={hide} className='lft'>{translate('thermres')}</th>
<th onMouseEnter={termtip.bind(null, 'PHRASE_EFFECTIVE_ARMOUR', { cap: 0 })} onMouseLeave={hide} className='lft'>{`${translate('absolute')} ${translate('HP')}`}</th>
<th onMouseEnter={termtip.bind(null, 'PHRASE_EFFECTIVE_ARMOUR', { cap: 0 })} onMouseLeave={hide} className='lft'>{`${translate('explosive')} ${translate('HP')}`}</th>
<th onMouseEnter={termtip.bind(null, 'PHRASE_EFFECTIVE_ARMOUR', { cap: 0 })} onMouseLeave={hide} className='lft'>{`${translate('kinetic')} ${translate('HP')}`}</th>
<th onMouseEnter={termtip.bind(null, 'PHRASE_EFFECTIVE_ARMOUR', { cap: 0 })} onMouseLeave={hide} className='lft'>{`${translate('thermal')} ${translate('HP')}`}</th>
<th onMouseEnter={termtip.bind(null, 'TT_MODULE_ARMOUR', { cap: 0 })} onMouseLeave={hide} className='lft'>{translate('raw module armour')}</th>
<th onMouseEnter={termtip.bind(null, 'armour', { cap: 0 })} onMouseLeave={hide} className='lft'>{translate('internal protection')}</th>
<th colSpan={5} onMouseEnter={termtip.bind(null, 'PHRASE_EFFECTIVE_ARMOUR', { cap: 0 })} onMouseLeave={hide} className='lft'>{`${translate('HP')}`}</th>
<th rowSpan={2} onMouseEnter={termtip.bind(null, 'TT_MODULE_ARMOUR', { cap: 0 })} onMouseLeave={hide} className='lft'>{translate('raw module armour')}</th>
<th rowSpan={2} onMouseEnter={termtip.bind(null, 'TT_MODULE_PROTECTION_INTERNAL', { cap: 0 })} onMouseLeave={hide} className='lft'>{translate('internal protection')}</th>
</tr>
<tr>
<th>{`${translate('explosive')}`}</th>
<th>{`${translate('kinetic')}`}</th>
<th>{`${translate('thermal')}`}</th>
<th>{`${translate('caustic')}`}</th>
<th className={'bordered'}>{`${translate('absolute')}`}</th>
<th>{`${translate('explosive')}`}</th>
<th>{`${translate('kinetic')}`}</th>
<th>{`${translate('thermal')}`}</th>
<th>{`${translate('caustic')}`}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<td>{translate(ship && ship.bulkheads && ship.bulkheads.m && ship.bulkheads.m.name || 'No Armour')}</td> <td>{translate(ship && ship.bulkheads && ship.bulkheads.m && ship.bulkheads.m.name || 'No Armour')}</td>
<td>{int(ship.hullExplRes * 100) + '%'}</td> <td>{formats.pct1(ship.hullExplRes)}</td>
<td>{int(ship.hullKinRes * 100) + '%'}</td> <td>{formats.pct1(ship.hullKinRes)}</td>
<td>{int(ship.hullThermRes * 100) + '%'}</td> <td>{formats.pct1(ship.hullThermRes)}</td>
<td>{int(armourMetrics.total / armourMetrics.absolute.total)}</td> <td>{formats.pct1(ship.hullCausRes)}</td>
<td>{int(armourMetrics.total / armourMetrics.explosive.total)}</td> <td>{int(ship.armour)}</td>
<td>{int(armourMetrics.total / armourMetrics.kinetic.total)}</td> <td>{int(ship.armour * ((1 / (1 - (ship.hullExplRes)))))}</td>
<td>{int(armourMetrics.total / armourMetrics.thermal.total)}</td> <td>{int(ship.armour * ((1 / (1 - (ship.hullKinRes)))))}</td>
<td>{int(ship.armour * ((1 / (1 - (ship.hullThermRes)))))}</td>
<td>{int(ship.armour * ((1 / (1 - (ship.hullCausRes)))))}</td>
<td>{int(armourMetrics.modulearmour)}</td> <td>{int(armourMetrics.modulearmour)}</td>
<td>{int(armourMetrics.moduleprotection * 100) + '%'}</td> <td>{int(armourMetrics.moduleprotection * 100) + '%'}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div>
</div>
</div>; </div>;
} }
} }

View File

@@ -228,6 +228,18 @@ export default class SlotSection extends TranslatedComponent {
targetSlot.priority = targetPriority; targetSlot.priority = targetPriority;
} }
this.props.onChange(); this.props.onChange();
this.props.ship
.updatePowerGenerated()
.updatePowerUsed()
.recalculateMass()
.updateJumpStats()
.recalculateShield()
.recalculateShieldCells()
.recalculateArmour()
.recalculateDps()
.recalculateEps()
.recalculateHps()
.updateMovement();
} }
} }
} }

View File

@@ -67,6 +67,9 @@ export default class StandardSlot extends TranslatedComponent {
if (m && m.name && m.name === 'Guardian Hybrid Power Plant') { if (m && m.name && m.name === 'Guardian Hybrid Power Plant') {
validMods = []; validMods = [];
} }
if (m && m.name && m.name === 'Guardian Power Distributor') {
validMods = [];
}
let showModuleResistances = Persist.showModuleResistances(); let showModuleResistances = Persist.showModuleResistances();
let mass = m.getMass() || m.cargo || m.fuel || 0; let mass = m.getMass() || m.cargo || m.fuel || 0;

View File

@@ -228,6 +228,30 @@ export class LinkIcon extends SvgIcon {
} }
} }
/**
* Link / Permalink / Chain
*/
export class OrbisIcon extends SvgIcon {
/**
* Generate the SVG
* @return {React.Component} SVG Contents
*/
svg() {
return (
<g transform="scale(.037296)">
<path d="m429 319c60.75 0 110 49.248 110 110 0 60.75-49.247 110-110 110s-110-49.247-110-110c0-60.749 49.248-110 110-110m0-20c-34.724 0-67.369 13.522-91.922 38.075s-38.075 57.198-38.075 91.922 13.522 67.369 38.075 91.922c24.553 24.554 57.198 38.075 91.922 38.075s67.369-13.521 91.922-38.075c24.554-24.553 38.075-57.198 38.075-91.922s-13.521-67.369-38.075-91.922c-24.553-24.553-57.198-38.075-91.922-38.075z" />
<path d="m429 235c107.14 0 194 86.855 194 194s-86.855 194-194 194-194-86.855-194-194 86.855-194 194-194m0-20c-28.881 0-56.908 5.661-83.304 16.825-25.485 10.779-48.368 26.207-68.016 45.853-19.646 19.647-35.074 42.53-45.853 68.015-11.163 26.396-16.824 54.423-16.824 83.304s5.661 56.908 16.825 83.304c10.779 25.484 26.207 48.368 45.853 68.016 19.647 19.646 42.53 35.073 68.015 45.854 26.396 11.164 54.423 16.825 83.304 16.825s56.908-5.661 83.304-16.825c25.484-10.779 48.368-26.206 68.016-45.854 19.646-19.646 35.073-42.53 45.854-68.016 11.164-26.396 16.825-54.423 16.825-83.304s-5.661-56.908-16.825-83.304c-10.779-25.485-26.206-48.368-45.854-68.015-19.646-19.646-42.53-35.074-68.016-45.853-26.396-11.164-54.423-16.825-83.304-16.825z" />
<path d="m429 63c202.14 0 366 163.86 366 366s-163.86 366-366 366-366-163.86-366-366 163.86-366 366-366m0-20c-52.101 0-102.65 10.208-150.25 30.342-45.966 19.442-87.244 47.271-122.69 82.714s-63.272 76.721-82.714 122.69c-20.134 47.601-30.342 98.153-30.342 150.25s10.208 102.65 30.342 150.25c19.442 45.967 47.271 87.244 82.714 122.69 35.443 35.442 76.721 63.271 122.69 82.715 47.601 20.133 98.153 30.342 150.25 30.342s102.65-10.209 150.25-30.342c45.967-19.442 87.244-47.271 122.69-82.715 35.441-35.442 63.271-76.721 82.714-122.69 20.133-47.601 30.342-98.153 30.342-150.25s-10.209-102.65-30.342-150.25c-19.442-45.966-47.271-87.244-82.714-122.69s-76.722-63.272-122.69-82.714c-47.601-20.134-98.153-30.342-150.25-30.342z"/>
<path d="m429 63c202.14 0 366 163.86 366 366s-163.86 366-366 366-366-163.86-366-366 163.86-366 366-366m0-20c-52.101 0-102.65 10.208-150.25 30.342-45.966 19.442-87.244 47.271-122.69 82.714s-63.272 76.721-82.714 122.69c-20.134 47.601-30.342 98.153-30.342 150.25s10.208 102.65 30.342 150.25c19.442 45.967 47.271 87.244 82.714 122.69 35.443 35.442 76.721 63.271 122.69 82.715 47.601 20.133 98.153 30.342 150.25 30.342s102.65-10.209 150.25-30.342c45.967-19.442 87.244-47.271 122.69-82.715 35.441-35.442 63.271-76.721 82.714-122.69 20.133-47.601 30.342-98.153 30.342-150.25s-10.209-102.65-30.342-150.25c-19.442-45.966-47.271-87.244-82.714-122.69s-76.722-63.272-122.69-82.714c-47.601-20.134-98.153-30.342-150.25-30.342z" />
<path d="m429 20c225.88 0 409 183.11 409 409s-183.11 409-409 409-409-183.11-409-409 183.11-409 409-409m0-20c-57.905 0-114.09 11.345-166.99 33.721-51.087 21.608-96.963 52.538-136.36 91.93s-70.321 85.269-91.93 136.36c-22.376 52.902-33.721 109.09-33.721 166.99s11.345 114.09 33.721 166.99c21.608 51.087 52.538 96.964 91.93 136.35 39.392 39.392 85.269 70.321 136.36 91.931 52.902 22.375 109.09 33.721 166.99 33.721s114.09-11.346 166.99-33.721c51.087-21.608 96.964-52.538 136.35-91.931 39.392-39.392 70.321-85.269 91.931-136.35 22.375-52.902 33.721-109.09 33.721-166.99s-11.346-114.09-33.721-166.99c-21.608-51.087-52.538-96.963-91.931-136.36-39.392-39.392-85.269-70.321-136.35-91.93-52.902-22.376-109.09-33.721-166.99-33.721z"/>
<path d="m155.34 679.12 173.25-190.21-15.626-13.721-170.9 190.4zm31.01 31.714 202.41-169.1-16.418-14.417-198.76 170.43z"/>
<path d="m702.66 178.87-173.25 190.21 15.625 13.721 170.9-190.4zm-31.01-31.714-202.41 169.1 16.418 14.417 198.76-170.43z" />
<rect transform="matrix(-.7071 -.7071 .7071 -.7071 429.34 1036.2)" x="387.09" y="420.77" width="84.379" height="16.859" />
</g>);
}
}
/** /**
* Material * Material
*/ */

View File

@@ -1,6 +1,6 @@
import TranslatedComponent from './TranslatedComponent'; import TranslatedComponent from './TranslatedComponent';
import React, { PropTypes } from 'react'; import React, { PropTypes } from 'react';
import Measure from 'react-measure'; import ContainerDimensions from 'react-container-dimensions';
import { BarChart, Bar, XAxis, YAxis } from 'recharts'; import { BarChart, Bar, XAxis, YAxis } from 'recharts';
const CORIOLIS_COLOURS = ['#FF8C0D', '#1FB0FF', '#71A052', '#D5D54D']; const CORIOLIS_COLOURS = ['#FF8C0D', '#1FB0FF', '#71A052', '#D5D54D'];
@@ -32,13 +32,6 @@ export default class VerticalBarChart extends TranslatedComponent {
super(props); super(props);
this._termtip = this._termtip.bind(this); this._termtip = this._termtip.bind(this);
this.state = {
dimensions: {
width: 300,
height: 300
}
};
} }
/** /**
@@ -46,7 +39,6 @@ export default class VerticalBarChart extends TranslatedComponent {
* @returns {Object} the markup * @returns {Object} the markup
*/ */
render() { render() {
const { width, height } = this.state.dimensions;
const { tooltip, termtip } = this.context; const { tooltip, termtip } = this.context;
// Calculate maximum for Y // Calculate maximum for Y
@@ -56,7 +48,8 @@ export default class VerticalBarChart extends TranslatedComponent {
const localMax = Math.max(dataMax, yMax); const localMax = Math.max(dataMax, yMax);
return ( return (
<Measure whitelist={['width', 'top']} onMeasure={ (dimensions) => this.setState({ dimensions }) }> <ContainerDimensions>
{ ({ width }) => (
<div width='100%'> <div width='100%'>
<BarChart width={width} height={width * ASPECT} data={this.props.data} margin={{ top: 5, right: 5, left: 5, bottom: 5 }}> <BarChart width={width} height={width * ASPECT} data={this.props.data} margin={{ top: 5, right: 5, left: 5, bottom: 5 }}>
<XAxis interval={0} fontSize='0.8em' stroke={AXIS_COLOUR} dataKey='label' /> <XAxis interval={0} fontSize='0.8em' stroke={AXIS_COLOUR} dataKey='label' />
@@ -64,7 +57,8 @@ export default class VerticalBarChart extends TranslatedComponent {
<Bar dataKey='value' label={<ValueLabel />} fill={CORIOLIS_COLOURS[0]} isAnimationActive={false} onMouseOver={this._termtip} onMouseOut={tooltip.bind(null, null)}/> <Bar dataKey='value' label={<ValueLabel />} fill={CORIOLIS_COLOURS[0]} isAnimationActive={false} onMouseOver={this._termtip} onMouseOut={tooltip.bind(null, null)}/>
</BarChart> </BarChart>
</div> </div>
</Measure> )}
</ContainerDimensions>
); );
} }

View File

@@ -184,7 +184,7 @@ export default class WeaponDamageChart extends TranslatedComponent {
const code = `${ship.toString()}:${opponent.toString()}`; const code = `${ship.toString()}:${opponent.toString()}`;
return ( return (
<span> <div>
<LineChart <LineChart
xMax={maxRange} xMax={maxRange}
yMax={this.state.maxDps} yMax={this.state.maxDps}
@@ -198,7 +198,7 @@ export default class WeaponDamageChart extends TranslatedComponent {
points={200} points={200}
code={code} code={code}
/> />
</span> </div>
); );
} }
} }

View File

@@ -60,17 +60,20 @@ export function getLanguage(langCode) {
}, },
translate, translate,
units: { units: {
ang: '°', // Angle
CR: <u>{translate('CR')}</u>, // Credits CR: <u>{translate('CR')}</u>, // Credits
kg: <u>{translate('kg')}</u>, // Kilograms kg: <u>{translate('kg')}</u>, // Kilograms
kgs: <u>{translate('kg/s')}</u>, // Kilograms per second kgs: <u>{translate('kg/s')}</u>, // Kilograms per second
km: <u>{translate('km')}</u>, // Kilometers km: <u>{translate('km')}</u>, // Kilometers
Ls: <u>{translate('Ls')}</u>, // Light Seconds Ls: <u>{translate('Ls')}</u>, // Light Seconds
LY: <u>{translate('LY')}</u>, // Light Years LY: <u>{translate('LY')}</u>, // Light Years
m: <u>{translate('m')}</u>, // Meters
MJ: <u>{translate('MJ')}</u>, // Mega Joules MJ: <u>{translate('MJ')}</u>, // Mega Joules
'm/s': <u>{translate('m/s')}</u>, // Meters per second 'm/s': <u>{translate('m/s')}</u>, // Meters per second
'°/s': <u>{translate('°/s')}</u>, // Degrees per second '°/s': <u>{translate('°/s')}</u>, // Degrees per second
MW: <u>{translate('MW')}</u>, // Mega Watts (same as Mega Joules per second) MW: <u>{translate('MW')}</u>, // Mega Watts (same as Mega Joules per second)
mps: <u>{translate('m/s')}</u>, // Metres per second mps: <u>{translate('m/s')}</u>, // Metres per second
pct: '%', // Percent
ps: <u>{translate('/s')}</u>, // per second ps: <u>{translate('/s')}</u>, // per second
pm: <u>{translate('/min')}</u>, // per minute pm: <u>{translate('/min')}</u>, // per minute
s: <u>{translate('secs')}</u>, // Seconds s: <u>{translate('secs')}</u>, // Seconds

File diff suppressed because one or more lines are too long

View File

@@ -20,11 +20,11 @@
"PHRASE_BLUEPRINT_BEST": "Лучшие основные значения для чертежа", "PHRASE_BLUEPRINT_BEST": "Лучшие основные значения для чертежа",
"PHRASE_BLUEPRINT_EXTREME": "Лучшие положительные и худшие отрицательные основные значения для чертежа", "PHRASE_BLUEPRINT_EXTREME": "Лучшие положительные и худшие отрицательные основные значения для чертежа",
"PHRASE_BLUEPRINT_RESET": "Убрать все изменения и чертёж", "PHRASE_BLUEPRINT_RESET": "Убрать все изменения и чертёж",
"PHRASE_SELECT_SPECIAL": "Нажмите чтобы выбрать экспериментальный эффект", "PHRASE_SELECT_SPECIAL": "Нажмите, чтобы выбрать экспериментальный эффект",
"PHRASE_NO_SPECIAL": "Без экспериментального эффекта", "PHRASE_NO_SPECIAL": "Без экспериментального эффекта",
"PHRASE_SHOPPING_LIST": "Станции что продают эту сборку", "PHRASE_SHOPPING_LIST": "Станции, что продают эту сборку",
"PHRASE_REFIT_SHOPPING_LIST": "Станции что продают необходимые модули", "PHRASE_REFIT_SHOPPING_LIST": "Станции, что продают необходимые модули",
"PHRASE_TOTAL_EFFECTIVE_SHIELD": "Общий урон что может быть нанесён в каждым типе, если используются все щитонакопители", "PHRASE_TOTAL_EFFECTIVE_SHIELD": "Общий урон, что может быть нанесён в каждым типе, если используются все щитонакопители",
"PHRASE_TIME_TO_LOSE_SHIELDS": "Щиты продержатся", "PHRASE_TIME_TO_LOSE_SHIELDS": "Щиты продержатся",
"PHRASE_TIME_TO_RECOVER_SHIELDS": "Щиты восстановятся за", "PHRASE_TIME_TO_RECOVER_SHIELDS": "Щиты восстановятся за",
"PHRASE_TIME_TO_RECHARGE_SHIELDS": "Щиты будут заряжены за", "PHRASE_TIME_TO_RECHARGE_SHIELDS": "Щиты будут заряжены за",
@@ -43,12 +43,12 @@
"PHRASE_TIME_TO_REMOVE_ARMOUR": "Снимет броню за", "PHRASE_TIME_TO_REMOVE_ARMOUR": "Снимет броню за",
"TT_TIME_TO_REMOVE_ARMOUR": "Непрерывным огнём из всех орудий", "TT_TIME_TO_REMOVE_ARMOUR": "Непрерывным огнём из всех орудий",
"PHRASE_TIME_TO_DRAIN_WEP": "Опустошит ОРУЖ за", "PHRASE_TIME_TO_DRAIN_WEP": "Опустошит ОРУЖ за",
"TT_TIME_TO_DRAIN_WEP": "Время за которое опустошится аккумулятор ОРУЖ при стрельбе из всех орудий", "TT_TIME_TO_DRAIN_WEP": "Время, за которое опустошится аккумулятор ОРУЖ при стрельбе из всех орудий",
"TT_TIME_TO_LOSE_SHIELDS": "Против поддерживаемой стрельбы из всех орудий противника", "TT_TIME_TO_LOSE_SHIELDS": "Против поддерживаемой стрельбы из всех орудий противника",
"TT_TIME_TO_LOSE_ARMOUR": "Против поддерживаемой стрельбы из всех орудий противника", "TT_TIME_TO_LOSE_ARMOUR": "Против поддерживаемой стрельбы из всех орудий противника",
"TT_MODULE_ARMOUR": "Броня защищаюшае модули от урона", "TT_MODULE_ARMOUR": "Броня, защищающая модули от урона",
"TT_MODULE_PROTECTION_EXTERNAL": "Процент урона перенаправленного от гнёзд на наборы для усиления модулей", "TT_MODULE_PROTECTION_EXTERNAL": "Процент урона, перенаправленного от гнёзд на наборы для усиления модулей",
"TT_MODULE_PROTECTION_INTERNAL": "Процент урона перенаправленного от модулей вне гнёзд на наборы для усиления модулей", "TT_MODULE_PROTECTION_INTERNAL": "Процент урона, перенаправленного от модулей вне гнёзд на наборы для усиления модулей",
"TT_EFFECTIVE_SDPS_SHIELDS": "Реальный поддерживаемый ДПС пока аккумулятор ОРУЖ не пуст", "TT_EFFECTIVE_SDPS_SHIELDS": "Реальный поддерживаемый ДПС пока аккумулятор ОРУЖ не пуст",
"TT_EFFECTIVENESS_SHIELDS": "Эффективность в сравнении с попаданием по цели с 0-сопротивляемостью без пунктов в СИС на 0 метрах", "TT_EFFECTIVENESS_SHIELDS": "Эффективность в сравнении с попаданием по цели с 0-сопротивляемостью без пунктов в СИС на 0 метрах",
"TT_EFFECTIVE_SDPS_ARMOUR": "Реальный поддерживаемый ДПС пока аккумулятор ОРУЖ не пуст", "TT_EFFECTIVE_SDPS_ARMOUR": "Реальный поддерживаемый ДПС пока аккумулятор ОРУЖ не пуст",
@@ -56,7 +56,7 @@
"PHRASE_EFFECTIVE_SDPS_SHIELDS": "ПДПС против щитов", "PHRASE_EFFECTIVE_SDPS_SHIELDS": "ПДПС против щитов",
"PHRASE_EFFECTIVE_SDPS_ARMOUR": "ПДПС против брони", "PHRASE_EFFECTIVE_SDPS_ARMOUR": "ПДПС против брони",
"TT_SUMMARY_SPEED": "С полным топливным баком и 4 пунктами в ДВИ", "TT_SUMMARY_SPEED": "С полным топливным баком и 4 пунктами в ДВИ",
"TT_SUMMARY_SPEED_NONFUNCTIONAL": "маневровые двигатели выключены или превышена максимальная масса с топливом и грузом", "TT_SUMMARY_SPEED_NONFUNCTIONAL": "Маневровые двигатели выключены или превышена максимальная масса с топливом и грузом",
"TT_SUMMARY_BOOST": "С полным топливным баком и 4 пунктами в ДВИ", "TT_SUMMARY_BOOST": "С полным топливным баком и 4 пунктами в ДВИ",
"TT_SUMMARY_BOOST_NONFUNCTIONAL": "Распределитель питания не может обеспечить достаточно энергии для форсажа", "TT_SUMMARY_BOOST_NONFUNCTIONAL": "Распределитель питания не может обеспечить достаточно энергии для форсажа",
"TT_SUMMARY_SHIELDS": "Чистая сила щита, включая усилители", "TT_SUMMARY_SHIELDS": "Чистая сила щита, включая усилители",
@@ -68,16 +68,16 @@
"TT_SUMMARY_DPS": "Урон в секунду при стрельбе из всех орудий", "TT_SUMMARY_DPS": "Урон в секунду при стрельбе из всех орудий",
"TT_SUMMARY_EPS": "Расход аккумулятора ОРУЖ в секунду при стрельбе из всех орудий", "TT_SUMMARY_EPS": "Расход аккумулятора ОРУЖ в секунду при стрельбе из всех орудий",
"TT_SUMMARY_TTD": "Время расхода аккумулятора ОРУЖ при стрельбе из всех орудий и с 4 пунктами в ОРУЖ", "TT_SUMMARY_TTD": "Время расхода аккумулятора ОРУЖ при стрельбе из всех орудий и с 4 пунктами в ОРУЖ",
"TT_SUMMARY_MAX_SINGLE_JUMP": "Самый дальний возможный прыжок без груза и с топливом достаточным только на сам прыжок", "TT_SUMMARY_MAX_SINGLE_JUMP": "Самый дальний возможный прыжок без груза и с топливом, достаточным только на сам прыжок",
"TT_SUMMARY_UNLADEN_SINGLE_JUMP": "Самый дальний возможный прыжок без груза и с полным топливным баком", "TT_SUMMARY_UNLADEN_SINGLE_JUMP": "Самый дальний возможный прыжок без груза и с полным топливным баком",
"TT_SUMMARY_LADEN_SINGLE_JUMP": "Самый дальний возможный прыжок с полным грузовым отсеком и с полным топливным баком", "TT_SUMMARY_LADEN_SINGLE_JUMP": "Самый дальний возможный прыжок с полным грузовым отсеком и с полным топливным баком",
"TT_SUMMARY_UNLADEN_TOTAL_JUMP": "Самая дальняя общая дистанция без груза, с полным топливным баком и при прыжках на максимальное расстояние", "TT_SUMMARY_UNLADEN_TOTAL_JUMP": "Самая дальняя общая дистанция без груза, с полным топливным баком и при прыжках на максимальное расстояние",
"TT_SUMMARY_LADEN_TOTAL_JUMP": "Самая дальняя общая дистанция с полным грузовым отсеком, с полным топливным баком и при прыжках на максимальное расстояние", "TT_SUMMARY_LADEN_TOTAL_JUMP": "Самая дальняя общая дистанция с полным грузовым отсеком, с полным топливным баком и при прыжках на максимальное расстояние",
"HELP_MODIFICATIONS_MENU": "Ткните на номер чтобы ввести новое значение, или потяните вдоль полосы для малых изменений", "HELP_MODIFICATIONS_MENU": "Нажмите на номер, чтобы ввести новое значение, или потяните вдоль полосы для малых изменений",
"am": "Блок Автом. Полевого Ремонта", "am": "Блок Автом. Полевого Ремонта",
"bh": "Переборки", "bh": "Переборки",
"bl": "Пучковый Лазер", "bl": "Пучковый лазер",
"bsg": "Двухпоточный Щитогенератор", "bsg": "Двухпоточный щитогенератор",
"c": "Орудие", "c": "Орудие",
"cc": "Контроллер магнитного снаряда для сбора", "cc": "Контроллер магнитного снаряда для сбора",
"ch": "Разбрасыватель дипольных отражателей", "ch": "Разбрасыватель дипольных отражателей",
@@ -89,7 +89,7 @@
"fh": "Ангар для истребителя", "fh": "Ангар для истребителя",
"fi": "FSD-перехватчик", "fi": "FSD-перехватчик",
"fs": "Топливозаборник", "fs": "Топливозаборник",
"fsd": "Рамочно Сместительный двигатель", "fsd": "Рамочно-сместительный двигатель",
"ft": "Топливный бак", "ft": "Топливный бак",
"fx": "Контроллер магнитного снаряда для топлива", "fx": "Контроллер магнитного снаряда для топлива",
"hb": "Контроллер магнитного снаряда для взлома трюма", "hb": "Контроллер магнитного снаряда для взлома трюма",
@@ -110,7 +110,7 @@
"pcm": "Каюта пассажира первого класса", "pcm": "Каюта пассажира первого класса",
"pcq": "Каюта пассажира класса люкс", "pcq": "Каюта пассажира класса люкс",
"pd": "Распределитель питания", "pd": "Распределитель питания",
"pl": пмульсный лазер", "pl": "Импульсный лазер",
"po": "Точечная оборона", "po": "Точечная оборона",
"pp": "Силовая установка", "pp": "Силовая установка",
"psg": "Призматический щитогенератор", "psg": "Призматический щитогенератор",
@@ -122,7 +122,7 @@
"sc": "Сканер обнаружения", "sc": "Сканер обнаружения",
"scb": "Щитонакопитель", "scb": "Щитонакопитель",
"sg": "Щитогенератор", "sg": "Щитогенератор",
"ss": "Сканер Поверхностей", "ss": "Сканер поверхностей",
"t": "Маневровые двигатели", "t": "Маневровые двигатели",
"tp": "Торпедная стойка", "tp": "Торпедная стойка",
"ul": "Пульсирующие лазеры", "ul": "Пульсирующие лазеры",
@@ -130,7 +130,7 @@
"emptyrestricted": "пусто (ограниченно)", "emptyrestricted": "пусто (ограниченно)",
"damage dealt to": "Урон нанесён", "damage dealt to": "Урон нанесён",
"damage received from": "Урон получен от", "damage received from": "Урон получен от",
"against shields": "Против шитов", "against shields": "Против щитов",
"against hull": "Против корпуса", "against hull": "Против корпуса",
"total effective shield": "Общие эффективные щиты", "total effective shield": "Общие эффективные щиты",
"ammunition": "Припасы", "ammunition": "Припасы",
@@ -149,7 +149,7 @@
"eps": "Энергия в секунду", "eps": "Энергия в секунду",
"epsseps": "Энергия в секунду (поддерживаемая энергия в секунду)", "epsseps": "Энергия в секунду (поддерживаемая энергия в секунду)",
"hps": "Нагрев в секунду", "hps": "Нагрев в секунду",
"hpsshps": "Heat per second (sustained heat per second)", "hpsshps": "Нагрев в секунду (поддерживаемый нагрев в секунду)",
"damage by": "Урон", "damage by": "Урон",
"damage from": "Урон от", "damage from": "Урон от",
"shield cells": "Щитонакопители", "shield cells": "Щитонакопители",
@@ -183,7 +183,7 @@
"hullreinforcement": "Укрепление корпуса", "hullreinforcement": "Укрепление корпуса",
"integrity": "Целостность", "integrity": "Целостность",
"jitter": "Дрожание", "jitter": "Дрожание",
"kinres": "Сопротивление китетическому урону", "kinres": "Сопротивление кинетическому урону",
"maxfuel": "Макс. топлива на прыжок", "maxfuel": "Макс. топлива на прыжок",
"mass": "Масса", "mass": "Масса",
"optmass": "Оптимизированная масса", "optmass": "Оптимизированная масса",

View File

@@ -44,6 +44,12 @@ export default class AboutPage extends Page {
<h3>Supporting Coriolis</h3> <h3>Supporting Coriolis</h3>
<p>Coriolis is an open source project, and I work on it in my free time. I have set up a patreon at <a href='https://www.patreon.com/coriolis_elite'>patreon.com/coriolis_elite</a>, which will be used to keep Coriolis up to date and the servers running.</p> <p>Coriolis is an open source project, and I work on it in my free time. I have set up a patreon at <a href='https://www.patreon.com/coriolis_elite'>patreon.com/coriolis_elite</a>, which will be used to keep Coriolis up to date and the servers running.</p>
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
<input type="hidden" name="cmd" value="_s-xclick"/>
<input type="hidden" name="hosted_button_id" value="SJBKT2SWEEU68" />
<input type="image" src="https://www.paypalobjects.com/en_AU/i/btn/btn_donate_SM.gif" border="0" name="submit" alt="PayPal The safer, easier way to pay online!" />
<img alt="" border="0" src="https://www.paypalobjects.com/en_AU/i/scr/pixel.gif" width="1" height="1" />
</form>
</div>; </div>;
} }
} }

View File

@@ -7,9 +7,20 @@ import Router from '../Router';
import Persist from '../stores/Persist'; import Persist from '../stores/Persist';
import * as Utils from '../utils/UtilityFunctions'; import * as Utils from '../utils/UtilityFunctions';
import Ship from '../shipyard/Ship'; import Ship from '../shipyard/Ship';
import * as _ from 'lodash';
import { toDetailedBuild } from '../shipyard/Serializer'; import { toDetailedBuild } from '../shipyard/Serializer';
import { outfitURL } from '../utils/UrlGenerators'; import { outfitURL } from '../utils/UrlGenerators';
import { FloppyDisk, Bin, Switch, Download, Reload, LinkIcon, ShoppingIcon, MatIcon } from '../components/SvgIcons'; import {
FloppyDisk,
Bin,
Switch,
Download,
Reload,
LinkIcon,
ShoppingIcon,
MatIcon,
OrbisIcon
} from '../components/SvgIcons';
import LZString from 'lz-string'; import LZString from 'lz-string';
import ShipSummaryTable from '../components/ShipSummaryTable'; import ShipSummaryTable from '../components/ShipSummaryTable';
import StandardSlotSection from '../components/StandardSlotSection'; import StandardSlotSection from '../components/StandardSlotSection';
@@ -26,6 +37,7 @@ import OutfittingSubpages from '../components/OutfittingSubpages';
import ModalExport from '../components/ModalExport'; import ModalExport from '../components/ModalExport';
import ModalPermalink from '../components/ModalPermalink'; import ModalPermalink from '../components/ModalPermalink';
import ModalShoppingList from '../components/ModalShoppingList'; import ModalShoppingList from '../components/ModalShoppingList';
import ModalOrbis from '../components/ModalOrbis';
/** /**
* Document Title Generator * Document Title Generator
@@ -493,6 +505,23 @@ export default class OutfittingPage extends Page {
this.context.showModal(<ModalPermalink url={window.location.href}/>); this.context.showModal(<ModalPermalink url={window.location.href}/>);
} }
/**
* Generate Orbis link
*/
_genOrbis() {
const data = {};
const ship = this.state.ship;
ship.coriolisId = ship.id;
data.coriolisShip = ship;
data.url = window.location.href;
data.title = this.state.buildName || ship.id;
data.description = this.state.buildName || ship.id;
data.ShipName = ship.id;
data.Ship = ship.id;
console.log(data);
this.context.showModal(<ModalOrbis ship={data}/>);
}
/** /**
* Open up a window for EDDB with a shopping list of our components * Open up a window for EDDB with a shopping list of our components
*/ */
@@ -616,6 +645,9 @@ export default class OutfittingPage extends Page {
<button onClick={this._genShortlink} onMouseOver={termtip.bind(null, 'shortlink')} onMouseOut={hide}> <button onClick={this._genShortlink} onMouseOver={termtip.bind(null, 'shortlink')} onMouseOut={hide}>
<LinkIcon className='lg' /> <LinkIcon className='lg' />
</button> </button>
<button onClick={this._genOrbis} onMouseOver={termtip.bind(null, 'PHASE_UPLOAD_ORBIS')} onMouseOut={hide}>
<OrbisIcon className='lg' />
</button>
<button onClick={this._genShoppingList} onMouseOver={termtip.bind(null, 'PHRASE_SHOPPING_MATS')} onMouseOut={hide}> <button onClick={this._genShoppingList} onMouseOver={termtip.bind(null, 'PHRASE_SHOPPING_MATS')} onMouseOut={hide}>
<MatIcon className='lg' /> <MatIcon className='lg' />
</button> </button>

View File

@@ -7,11 +7,20 @@ import Module from './Module';
* @param {object} fsd The FDS object/component with maxfuel, fuelmul, fuelpower, optmass * @param {object} fsd The FDS object/component with maxfuel, fuelmul, fuelpower, optmass
* @param {number} fuel Optional - The fuel consumed during the jump * @param {number} fuel Optional - The fuel consumed during the jump
* @return {number} Distance in Light Years * @return {number} Distance in Light Years
* @param {object} ship Ship instance
*/ */
export function jumpRange(mass, fsd, fuel) { export function jumpRange(mass, fsd, fuel, ship) {
const fsdMaxFuelPerJump = fsd instanceof Module ? fsd.getMaxFuelPerJump() : fsd.maxfuel; const fsdMaxFuelPerJump = fsd instanceof Module ? fsd.getMaxFuelPerJump() : fsd.maxfuel;
const fsdOptimalMass = fsd instanceof Module ? fsd.getOptMass() : fsd.optmass; const fsdOptimalMass = fsd instanceof Module ? fsd.getOptMass() : fsd.optmass;
return Math.pow(Math.min(fuel === undefined ? fsdMaxFuelPerJump : fuel, fsdMaxFuelPerJump) / fsd.fuelmul, 1 / fsd.fuelpower) * fsdOptimalMass / mass; let jumpAddition = 0;
if (ship) {
for (const module of ship.internal) {
if (module && module.m && module.m.grp === 'gfsb') {
jumpAddition += module.m.getJumpBoost();
}
}
}
return (Math.pow(Math.min(fuel === undefined ? fsdMaxFuelPerJump : fuel, fsdMaxFuelPerJump) / fsd.fuelmul, 1 / fsd.fuelpower) * fsdOptimalMass / mass) + jumpAddition;
} }
/** /**
@@ -21,8 +30,9 @@ export function jumpRange(mass, fsd, fuel) {
* @param {object} fsd The FDS object/component with maxfuel, fuelmul, fuelpower, optmass * @param {object} fsd The FDS object/component with maxfuel, fuelmul, fuelpower, optmass
* @param {number} fuel The total fuel available * @param {number} fuel The total fuel available
* @return {number} Distance in Light Years * @return {number} Distance in Light Years
* @param {object} ship Ship instance
*/ */
export function totalJumpRange(mass, fsd, fuel) { export function totalJumpRange(mass, fsd, fuel, ship) {
const fsdMaxFuelPerJump = fsd instanceof Module ? fsd.getMaxFuelPerJump() : fsd.maxfuel; const fsdMaxFuelPerJump = fsd instanceof Module ? fsd.getMaxFuelPerJump() : fsd.maxfuel;
const fsdOptimalMass = fsd instanceof Module ? fsd.getOptMass() : fsd.optmass; const fsdOptimalMass = fsd instanceof Module ? fsd.getOptMass() : fsd.optmass;
@@ -30,7 +40,7 @@ export function totalJumpRange(mass, fsd, fuel) {
let totalRange = 0; let totalRange = 0;
while (fuelRemaining > 0) { while (fuelRemaining > 0) {
const fuelForThisJump = Math.min(fuelRemaining, fsdMaxFuelPerJump); const fuelForThisJump = Math.min(fuelRemaining, fsdMaxFuelPerJump);
totalRange += this.jumpRange(mass, fsd, fuelForThisJump); totalRange += this.jumpRange(mass, fsd, fuelForThisJump, ship);
// Mass is reduced // Mass is reduced
mass -= fuelForThisJump; mass -= fuelForThisJump;
fuelRemaining -= fuelForThisJump; fuelRemaining -= fuelForThisJump;
@@ -60,7 +70,7 @@ export function shieldStrength(mass, baseShield, sg, multiplier) {
let ynorm = Math.pow(xnorm, exponent); let ynorm = Math.pow(xnorm, exponent);
let mul = minMul + ynorm * (maxMul - minMul); let mul = minMul + ynorm * (maxMul - minMul);
return baseShield * mul * multiplier; return (baseShield * mul * multiplier);
} }
/** /**
@@ -86,6 +96,16 @@ export function speed(mass, baseSpeed, thrusters, engpip) {
return results; return results;
} }
/**
* Calculate pip multiplier for speed.
* @param {number} baseSpeed The base speed of ship in data
* @param {number} topSpeed The top speed of ship in data
* @return {number} The multiplier that pips affect speed.
*/
export function calcPipSpeed(baseSpeed, topSpeed) {
return (topSpeed - baseSpeed) / (4 * topSpeed);
}
/** /**
* Calculate pitch of a ship based on mass and thrusters * Calculate pitch of a ship based on mass and thrusters
* @param {number} mass the mass of the ship * @param {number} mass the mass of the ship
@@ -201,7 +221,7 @@ function calcValue(minMass, optMass, maxMass, minMul, optMul, maxMul, mass, base
* Calculate speed for a given setup * Calculate speed for a given setup
* @param {number} mass the mass of the ship * @param {number} mass the mass of the ship
* @param {number} baseSpeed the base speed of the ship * @param {number} baseSpeed the base speed of the ship
* @param {ojbect} thrusters the thrusters of the ship * @param {object} thrusters the thrusters of the ship
* @param {number} engpip the multiplier per pip to engines * @param {number} engpip the multiplier per pip to engines
* @param {number} eng the pips to engines * @param {number} eng the pips to engines
* @param {number} boostFactor the boost factor for ths ship * @param {number} boostFactor the boost factor for ths ship
@@ -229,7 +249,7 @@ export function calcSpeed(mass, baseSpeed, thrusters, engpip, eng, boostFactor,
* Calculate pitch for a given setup * Calculate pitch for a given setup
* @param {number} mass the mass of the ship * @param {number} mass the mass of the ship
* @param {number} basePitch the base pitch of the ship * @param {number} basePitch the base pitch of the ship
* @param {ojbect} thrusters the thrusters of the ship * @param {object} thrusters the thrusters of the ship
* @param {number} engpip the multiplier per pip to engines * @param {number} engpip the multiplier per pip to engines
* @param {number} eng the pips to engines * @param {number} eng the pips to engines
* @param {number} boostFactor the boost factor for ths ship * @param {number} boostFactor the boost factor for ths ship
@@ -320,41 +340,76 @@ export function shieldMetrics(ship, sys) {
const maxSysResistance = this.sysResistance(4); const maxSysResistance = this.sysResistance(4);
let shield = {}; let shield = {};
const dimReturnLine = (res) => 1 - (1 - res) * 0.7;
const shieldGeneratorSlot = ship.findInternalByGroup('sg'); const shieldGeneratorSlot = ship.findInternalByGroup('sg');
if (shieldGeneratorSlot && shieldGeneratorSlot.enabled && shieldGeneratorSlot.m) { if (shieldGeneratorSlot && shieldGeneratorSlot.enabled && shieldGeneratorSlot.m) {
const shieldGenerator = shieldGeneratorSlot.m; const shieldGenerator = shieldGeneratorSlot.m;
let res = {
kin: shieldGenerator.kinres,
therm: shieldGenerator.thermres,
expl: shieldGenerator.explres
};
// Boosters // Boosters
let boost = 1; let boost = 1;
let boosterExplDmg = 1; let boosterExplDmg = 1;
let boosterKinDmg = 1; let boosterKinDmg = 1;
let boosterThermDmg = 1; let boosterThermDmg = 1;
// const explDim = dimReturnLine(shieldGenerator.explres);
// const thermDim = dimReturnLine(shieldGenerator.thermres);
// const kinDim = dimReturnLine(shieldGenerator.kinres);
for (let slot of ship.hardpoints) { for (let slot of ship.hardpoints) {
if (slot.enabled && slot.m && slot.m.grp == 'sb') { if (slot.enabled && slot.m && slot.m.grp == 'sb') {
boost += slot.m.getShieldBoost(); boost += slot.m.getShieldBoost();
res.expl += slot.m.getExplosiveResistance();
res.kin += slot.m.getKineticResistance();
res.therm += slot.m.getThermalResistance();
boosterExplDmg = boosterExplDmg * (1 - slot.m.getExplosiveResistance()); boosterExplDmg = boosterExplDmg * (1 - slot.m.getExplosiveResistance());
boosterKinDmg = boosterKinDmg * (1 - slot.m.getKineticResistance()); boosterKinDmg = boosterKinDmg * (1 - slot.m.getKineticResistance());
boosterThermDmg = boosterThermDmg * (1 - slot.m.getThermalResistance()); boosterThermDmg = boosterThermDmg * (1 - slot.m.getThermalResistance());
} }
} if (slot.m && slot.m.grp == 'gsrp') {
}
}
// Calculate diminishing returns for boosters // Calculate diminishing returns for boosters
// Diminishing returns not currently in-game // Diminishing returns not currently in-game
// boost = Math.min(boost, (1 - Math.pow(Math.E, -0.7 * boost)) * 2.5); // boost = Math.min(boost, (1 - Math.pow(Math.E, -0.7 * boost)) * 2.5);
// Remove base shield generator strength // Remove base shield generator strength
boost -= 1; boost -= 1;
// Apply diminishing returns
boosterExplDmg = boosterExplDmg > 0.7 ? boosterExplDmg : 0.7 - (0.7 - boosterExplDmg) / 2;
boosterKinDmg = boosterKinDmg > 0.7 ? boosterKinDmg : 0.7 - (0.7 - boosterKinDmg) / 2;
boosterThermDmg = boosterThermDmg > 0.7 ? boosterThermDmg : 0.7 - (0.7 - boosterThermDmg) / 2;
const generatorStrength = this.shieldStrength(ship.hullMass, ship.baseShieldStrength, shieldGenerator, 1); // if (res.expl > explDim) {
// const overage = (res.expl - explDim) * 0.5;
// res.expl = explDim + overage;
// boosterExplDmg = explDim + overage;
// }
//
// if (res.therm > thermDim) {
// const overage = (res.therm - thermDim) * 0.5;
// res.therm = thermDim + overage;
// boosterThermDmg = thermDim + overage;
// }
//
// if (res.kin > kinDim) {
// const overage = (res.kin - kinDim) * 0.5;
// res.kin = kinDim + overage;
// boosterKinDmg = kinDim + overage;
// }
let shieldAddition = 0;
if (ship) {
for (const module of ship.internal) {
if (module && module.m && module.m.grp === 'gsrp') {
shieldAddition += module.m.getShieldAddition();
}
}
}
let generatorStrength = this.shieldStrength(ship.hullMass, ship.baseShieldStrength, shieldGenerator, 1);
const boostersStrength = generatorStrength * boost; const boostersStrength = generatorStrength * boost;
// Recover time is the time taken to go from 0 to 50%. It includes a 16-second wait before shields start to recover // Recover time is the time taken to go from 0 to 50%. It includes a 16-second wait before shields start to recover
const shieldToRecover = (generatorStrength + boostersStrength) / 2; const shieldToRecover = (generatorStrength + boostersStrength + shieldAddition) / 2;
const powerDistributor = ship.standard[4].m; const powerDistributor = ship.standard[4].m;
const sysRechargeRate = this.sysRechargeRate(powerDistributor, sys); const sysRechargeRate = this.sysRechargeRate(powerDistributor, sys);
@@ -373,7 +428,7 @@ export function shieldMetrics(ship, sys) {
const remainingShieldToRecover = shieldToRecover - capacitorLifetime * shieldGenerator.getBrokenRegenerationRate(); const remainingShieldToRecover = shieldToRecover - capacitorLifetime * shieldGenerator.getBrokenRegenerationRate();
if (sys === 0) { if (sys === 0) {
// No system pips so will never recover shields // No system pips so will never recover shields
recover = Math.Inf; recover = Math.Infinity;
} else { } else {
// Recover remaining shields at the rate of the power distributor's recharge // Recover remaining shields at the rate of the power distributor's recharge
recover += remainingShieldToRecover / (sysRechargeRate / 0.6); recover += remainingShieldToRecover / (sysRechargeRate / 0.6);
@@ -381,7 +436,7 @@ export function shieldMetrics(ship, sys) {
} }
// Recharge time is the time taken to go from 50% to 100% // Recharge time is the time taken to go from 50% to 100%
const shieldToRecharge = (generatorStrength + boostersStrength) / 2; const shieldToRecharge = (generatorStrength + boostersStrength + shieldAddition) / 2;
// Our initial regeneration comes from the SYS capacitor store, which is replenished as it goes // Our initial regeneration comes from the SYS capacitor store, which is replenished as it goes
// 0.6 is a magic number from FD: each 0.6 MW of energy from the power distributor recharges 1 MJ/s of regeneration // 0.6 is a magic number from FD: each 0.6 MW of energy from the power distributor recharges 1 MJ/s of regeneration
@@ -408,8 +463,9 @@ export function shieldMetrics(ship, sys) {
shield = { shield = {
generator: generatorStrength, generator: generatorStrength,
boosters: boostersStrength, boosters: boostersStrength,
addition: shieldAddition,
cells: ship.shieldCells, cells: ship.shieldCells,
total: generatorStrength + boostersStrength + ship.shieldCells, total: generatorStrength + boostersStrength + ship.shieldCells + shieldAddition,
recover, recover,
recharge, recharge,
}; };
@@ -424,44 +480,71 @@ export function shieldMetrics(ship, sys) {
max: 1 - maxSysResistance max: 1 - maxSysResistance
}; };
/**
* An object that stores a selection of difference damage multipliers that
* deal with a ship's shield strength.
* @typedef {Object} ShieldDamageMults
* @property {number} generator Base damage multiplier of the shield
* contributing it's base resistance.
* @property {number} boosters Damage multiplier contributed by all
* boosters, i.e. `rawMj / (generator * boosters)` equals shield strength
* with 0 pips to sys.
* @property {number} sys Damage multiplier contributed by pips to sys.
* @property {number} base Damage multiplier with 0 pips to sys; just
* boosters and shield generator. Equals `generator * boosters`.
* @property {number} total Damage multiplier with current pip settings.
* @property {number} max Damage multiplier with 4 pips to sys.
*/
let sgExplosiveDmg = 1 - shieldGenerator.getExplosiveResistance();
let sgSbExplosiveDmg = diminishDamageMult(sgExplosiveDmg * 0.7, (1 - shieldGenerator.getExplosiveResistance()) * boosterExplDmg);
/** @type {ShieldDamageMults} */
shield.explosive = { shield.explosive = {
generator: 1 - shieldGenerator.getExplosiveResistance(), generator: sgExplosiveDmg,
boosters: boosterExplDmg, boosters: sgSbExplosiveDmg / sgExplosiveDmg,
sys: (1 - sysResistance), sys: (1 - sysResistance),
total: (1 - shieldGenerator.getExplosiveResistance()) * boosterExplDmg * (1 - sysResistance), base: sgSbExplosiveDmg,
max: (1 - shieldGenerator.getExplosiveResistance()) * boosterExplDmg * (1 - maxSysResistance) total: sgSbExplosiveDmg * (1 - sysResistance),
max: sgSbExplosiveDmg * (1 - maxSysResistance),
}; };
let sgKineticDmg = 1 - shieldGenerator.getKineticResistance();
let sgSbKineticDmg = diminishDamageMult(sgKineticDmg * 0.7, (1 - shieldGenerator.getKineticResistance()) * boosterKinDmg);
/** @type {ShieldDamageMults} */
shield.kinetic = { shield.kinetic = {
generator: 1 - shieldGenerator.getKineticResistance(), generator: sgKineticDmg,
boosters: boosterKinDmg, boosters: sgSbKineticDmg / sgKineticDmg,
sys: (1 - sysResistance), sys: (1 - sysResistance),
total: (1 - shieldGenerator.getKineticResistance()) * boosterKinDmg * (1 - sysResistance), base: sgSbKineticDmg,
max: (1 - shieldGenerator.getKineticResistance()) * boosterKinDmg * (1 - maxSysResistance) total: sgSbKineticDmg * (1 - sysResistance),
max: sgSbKineticDmg * (1 - maxSysResistance),
}; };
let sgThermalDmg = 1 - shieldGenerator.getThermalResistance();
let sgSbThermalDmg = diminishDamageMult(sgThermalDmg * 0.7, (1 - shieldGenerator.getThermalResistance()) * boosterThermDmg);
/** @type {ShieldDamageMults} */
shield.thermal = { shield.thermal = {
generator: 1 - shieldGenerator.getThermalResistance(), generator: sgThermalDmg,
boosters: boosterThermDmg, boosters: sgSbThermalDmg / sgThermalDmg,
sys: (1 - sysResistance), sys: (1 - sysResistance),
total: (1 - shieldGenerator.getThermalResistance()) * boosterThermDmg * (1 - sysResistance), base: sgSbThermalDmg,
max: (1 - shieldGenerator.getThermalResistance()) * boosterThermDmg * (1 - maxSysResistance) total: sgSbThermalDmg * (1 - sysResistance),
max: sgSbThermalDmg * (1 - maxSysResistance),
}; };
} }
return shield; return shield;
} }
/** /**
* Calculate time from one boost to another * Calculate time from one boost to another
* @return {number} Boost frequency in seconds * @return {number} Boost frequency in seconds
* @param ship * @param {Ship} ship Ship object
*/ */
export function calcBoost(ship) { export function calcBoost(ship) {
if (!ship.boostEnergy || !ship.standard[4] || !ship.standard[4].m) { if (!ship.boostEnergy || !ship.standard[4] || !ship.standard[4].m) {
return undefined; return undefined;
} }
return ship.boostEnergy / ship.standard[4].m.engrate; return ship.boostEnergy / ship.standard[4].m.getEnginesRechargeRate();
} }
@@ -477,33 +560,58 @@ export function armourMetrics(ship) {
let moduleArmour = 0; let moduleArmour = 0;
let moduleProtection = 1; let moduleProtection = 1;
const bulkheads = ship.bulkheads.m;
let hullExplDmg = 1; let hullExplDmg = 1;
let hullKinDmg = 1; let hullKinDmg = 1;
let hullThermDmg = 1; let hullThermDmg = 1;
let hullCausDmg = 1;
// const dimReturnLine = (res) => 1 - (1 - res) * 0.7;
// let res = {
// kin: 0,
// therm: 0,
// expl: 0
// };
// Armour from HRPs and module armour from MRPs // Armour from HRPs and module armour from MRPs
for (let slot of ship.internal) { for (let slot of ship.internal) {
if (slot.m && slot.m.grp == 'hr') { if (slot.m && (slot.m.grp === 'hr' || slot.m.grp === 'ghrp' || slot.m.grp == 'mahr')) {
armourReinforcement += slot.m.getHullReinforcement(); armourReinforcement += slot.m.getHullReinforcement();
// Hull boost for HRPs is applied against the ship's base armour // Hull boost for HRPs is applied against the ship's base armour
armourReinforcement += ship.baseArmour * slot.m.getModValue('hullboost') / 10000; armourReinforcement += ship.baseArmour * slot.m.getModValue('hullboost') / 10000;
// res.expl += slot.m.getExplosiveResistance();
// res.kin += slot.m.getKineticResistance();
// res.therm += slot.m.getThermalResistance();
hullExplDmg = hullExplDmg * (1 - slot.m.getExplosiveResistance()); hullExplDmg = hullExplDmg * (1 - slot.m.getExplosiveResistance());
hullKinDmg = hullKinDmg * (1 - slot.m.getKineticResistance()); hullKinDmg = hullKinDmg * (1 - slot.m.getKineticResistance());
hullThermDmg = hullThermDmg * (1 - slot.m.getThermalResistance()); hullThermDmg = hullThermDmg * (1 - slot.m.getThermalResistance());
hullCausDmg = hullCausDmg * (1 - slot.m.getCausticResistance());
} }
if (slot.m && slot.m.grp == 'mrp') { if (slot.m && (slot.m.grp == 'mrp' || slot.m.grp == 'gmrp')) {
moduleArmour += slot.m.getIntegrity(); moduleArmour += slot.m.getIntegrity();
moduleProtection = moduleProtection * (1 - slot.m.getProtection()); moduleProtection = moduleProtection * (1 - slot.m.getProtection());
} }
} }
moduleProtection = 1 - moduleProtection; moduleProtection = 1 - moduleProtection;
// Apply diminishing returns // const explDim = dimReturnLine(bulkheads.explres);
hullExplDmg = hullExplDmg > 0.7 ? hullExplDmg : 0.7 - (0.7 - hullExplDmg) / 2; // const thermDim = dimReturnLine(bulkheads.thermres);
hullKinDmg = hullKinDmg > 0.7 ? hullKinDmg : 0.7 - (0.7 - hullKinDmg) / 2; // const kinDim = dimReturnLine(bulkheads.kinres);
hullThermDmg = hullThermDmg > 0.7 ? hullThermDmg : 0.7 - (0.7 - hullThermDmg) / 2; // if (res.expl > explDim) {
// const overage = (res.expl - explDim) * 0.5;
// res.expl = explDim + overage;
// hullExplDmg = explDim + overage;
// }
//
// if (res.therm > thermDim) {
// const overage = (res.therm - thermDim) * 0.5;
// res.therm = thermDim + overage;
// hullThermDmg = thermDim + overage;
// }
//
// if (res.kin > kinDim) {
// const overage = (res.kin - kinDim) * 0.5;
// res.kin = kinDim + overage;
// hullKinDmg = kinDim + overage;
// }
const armour = { const armour = {
bulkheads: armourBulkheads, bulkheads: armourBulkheads,
@@ -521,24 +629,41 @@ export function armourMetrics(ship) {
total: 1 total: 1
}; };
let armourExplDmg = diminishDamageMult(0.7, 1 - ship.bulkheads.m.getExplosiveResistance());
let armourReinforcedExplDmg = diminishDamageMult(0.7, (1 - ship.bulkheads.m.getExplosiveResistance()) * hullExplDmg);
armour.explosive = { armour.explosive = {
bulkheads: 1 - ship.bulkheads.m.getExplosiveResistance(), bulkheads: armourExplDmg,
reinforcement: hullExplDmg, reinforcement: armourReinforcedExplDmg / armourExplDmg,
total: (1 - ship.bulkheads.m.getExplosiveResistance()) * hullExplDmg total: armourReinforcedExplDmg,
res: 1 - armourReinforcedExplDmg
}; };
let armourKinDmg = diminishDamageMult(0.7, 1 - ship.bulkheads.m.getKineticResistance());
let armourReinforcedKinDmg = diminishDamageMult(0.7, (1 - ship.bulkheads.m.getKineticResistance()) * hullKinDmg);
armour.kinetic = { armour.kinetic = {
bulkheads: 1 - ship.bulkheads.m.getKineticResistance(), bulkheads: armourKinDmg,
reinforcement: hullKinDmg, reinforcement: armourReinforcedKinDmg / armourKinDmg,
total: (1 - ship.bulkheads.m.getKineticResistance()) * hullKinDmg total: armourReinforcedKinDmg,
res: 1 - armourReinforcedKinDmg
}; };
let armourThermDmg = diminishDamageMult(0.7, 1 - ship.bulkheads.m.getThermalResistance());
let armourReinforcedThermDmg = diminishDamageMult(0.7, (1 - ship.bulkheads.m.getThermalResistance()) * hullThermDmg);
armour.thermal = { armour.thermal = {
bulkheads: 1 - ship.bulkheads.m.getThermalResistance(), bulkheads: armourThermDmg,
reinforcement: hullThermDmg, reinforcement: armourReinforcedThermDmg / armourThermDmg,
total: (1 - ship.bulkheads.m.getThermalResistance()) * hullThermDmg total: armourReinforcedThermDmg,
res: 1 - armourReinforcedThermDmg
}; };
let armourCausDmg = diminishDamageMult(0.7, 1 - ship.bulkheads.m.getCausticResistance());
let armourReinforcedCausDmg = diminishDamageMult(0.7, (1 - ship.bulkheads.m.getCausticResistance()) * hullCausDmg);
armour.caustic = {
bulkheads: armourCausDmg,
reinforcement: armourReinforcedCausDmg / armourCausDmg,
total: armourReinforcedCausDmg,
res: 1 - armourReinforcedCausDmg,
};
return armour; return armour;
} }
@@ -710,6 +835,34 @@ export function _sustainedDps(ship, opponent, opponentShields, opponentArmour, e
return { shieldsdps, armoursdps, eps }; return { shieldsdps, armoursdps, eps };
} }
/**
* Stores SDPS split up by type.
* @typedef {Object} SDps
* @property {number} absolute Damage of type absolute
* @property {number} explosive Damage of type explosive
* @property {number} kinetic Damage of type kinetic
* @property {number} thermal Damage of type thermal
* @property {number} [total] Sum of all damage types
*/
/**
* An object that holds information about SDPS for a given weapon and opponent.
* @typedef {Object} WeaponDamage
* @property {number} eps Energy per second
* @property {Object} damage An object that stores damage inflicted by
* the weapon.
* @property {Object} effectiveness An object that stores the effectiveness of
* the weapon against the opponent given.
*/
/**
* Stores overall SDPS and against a given opponent's shields and armour.
* @typedef {Object} WeaponDamage~damage
* @property {SDps} base Overall SDPS.
* @property {SDps} shields SDPS against the given opponent's shields.
* @property {SDps} armour SDPS against the given opponent's armour.
*/
/** /**
* Calculate the sustained DPS for a weapon at a given range * Calculate the sustained DPS for a weapon at a given range
* @param {Object} m The weapon * @param {Object} m The weapon
@@ -717,13 +870,20 @@ export function _sustainedDps(ship, opponent, opponentShields, opponentArmour, e
* @param {Object} opponentShields The opponent's shield resistances * @param {Object} opponentShields The opponent's shield resistances
* @param {Object} opponentArmour The opponent's armour resistances * @param {Object} opponentArmour The opponent's armour resistances
* @param {int} engagementrange The range between the ship and opponent * @param {int} engagementrange The range between the ship and opponent
* @returns {Object} Sustained DPS for shield and armour * @returns {WeaponDamage} Sustained DPS for shield and armour
*/ */
export function _weaponSustainedDps(m, opponent, opponentShields, opponentArmour, engagementrange) { export function _weaponSustainedDps(m, opponent, opponentShields, opponentArmour, engagementrange) {
const opponentHasShields = opponentShields.generator ? true : false; const opponentHasShields = opponentShields.generator ? true : false;
const weapon = { const weapon = {
eps: 0, eps: 0,
damage: { damage: {
base: {
absolute: 0,
explosive: 0,
kinetic: 0,
thermal: 0,
total: 0,
},
shields: { shields: {
absolute: 0, absolute: 0,
explosive: 0, explosive: 0,
@@ -757,7 +917,7 @@ export function _weaponSustainedDps(m, opponent, opponentShields, opponentArmour
weapon.eps = m.getClip() ? (m.getClip() * m.getEps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload()) : m.getEps(); weapon.eps = m.getClip() ? (m.getClip() * m.getEps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload()) : m.getEps();
// Initial sustained DPS // Initial sustained DPS
let sDps = m.getClip() ? (m.getClip() * m.getDps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload()) : m.getDps(); let sDps = m.getSDps();
// Take fall-off in to account // Take fall-off in to account
const falloff = m.getFalloff(); const falloff = m.getFalloff();
@@ -768,6 +928,12 @@ export function _weaponSustainedDps(m, opponent, opponentShields, opponentArmour
sDps *= dropoff; sDps *= dropoff;
} }
weapon.damage.base.absolute = sDps * m.getDamageDist().A;
weapon.damage.base.explosive = sDps * m.getDamageDist().E;
weapon.damage.base.kinetic = sDps * m.getDamageDist().K;
weapon.damage.base.thermal = sDps * m.getDamageDist().T;
weapon.damage.base.total = sDps;
// Piercing/hardness modifier (for armour only) // Piercing/hardness modifier (for armour only)
const armourMultiple = m.getPiercing() >= opponent.hardness ? 1 : m.getPiercing() / opponent.hardness; const armourMultiple = m.getPiercing() >= opponent.hardness ? 1 : m.getPiercing() / opponent.hardness;
weapon.effectiveness.armour.hardness = armourMultiple; weapon.effectiveness.armour.hardness = armourMultiple;
@@ -865,3 +1031,17 @@ export function timeToDeplete(amount, dps, eps, capacity, recharge) {
} }
} }
} }
/**
* Applies diminishing returns to resistances.
* @param {number} diminishFrom The base resistance up to which no diminishing returns are applied.
* @param {number} damageMult Resistance as damage multiplier
* @returns {number} Actual damage multiplier
*/
export function diminishDamageMult(diminishFrom, damageMult) {
if (damageMult > diminishFrom) {
return damageMult;
} else {
return (diminishFrom / 2) + 0.5 * damageMult;
}
}

View File

@@ -18,7 +18,7 @@ export const ModuleGroupToName = {
// Standard // Standard
pp: 'Power Plant', pp: 'Power Plant',
gpp: 'Guardian Hybrid Power Plant', gpp: 'Guardian Hybrid Power Plant',
gpd: 'Guardian Hybrid Power Distributor', gpd: 'Guardian Power Distributor',
t: 'Thrusters', t: 'Thrusters',
fsd: 'Frame Shift Drive', fsd: 'Frame Shift Drive',
ls: 'Life Support', ls: 'Life Support',
@@ -52,6 +52,11 @@ export const ModuleGroupToName = {
pcq: 'Luxury Passenger Cabin', pcq: 'Luxury Passenger Cabin',
cc: 'Collector Limpet Controller', cc: 'Collector Limpet Controller',
ss: 'Surface Scanner', ss: 'Surface Scanner',
gsrp: 'Guardian Shield Reinforcement Packages',
gfsb: 'Guardian Frame Shift Drive Booster',
ghrp: 'Guardian Hull Reinforcement Package',
gmrp: 'Guardian Module Reinforcement Package',
mahr: 'Meta Alloy Hull Reinforcement Package',
// Hard Points // Hard Points
bl: 'Beam Laser', bl: 'Beam Laser',
@@ -81,8 +86,14 @@ export const ModuleGroupToName = {
sfn: 'Shutdown Field Neutraliser', sfn: 'Shutdown Field Neutraliser',
xs: 'Xeno Scanner', xs: 'Xeno Scanner',
rcpl: 'Recon Limpet Controller', rcpl: 'Recon Limpet Controller',
rsl: 'Research Limpet Controller',
dtl: 'Decontamination Limpet Controller',
gpc: 'Guardian Plasma Charger', gpc: 'Guardian Plasma Charger',
ggc: 'Guardian Gauss Cannon', ggc: 'Guardian Gauss Cannon',
tbsc: 'Shock Cannon',
gsc: 'Guardian Shard Cannon',
tbem: 'Enzyme Missile Rack',
tbrfl: 'Remote Release Flechette Launcher',
}; };
let GrpNameToCodeMap = {}; let GrpNameToCodeMap = {};

File diff suppressed because it is too large Load Diff

View File

@@ -7,9 +7,10 @@ import LZString from 'lz-string';
import * as _ from 'lodash'; import * as _ from 'lodash';
import isEqual from 'lodash/lang'; import isEqual from 'lodash/lang';
import { Ships, Modifications } from 'coriolis-data/dist'; import { Ships, Modifications } from 'coriolis-data/dist';
import { chain } from 'lodash';
const zlib = require('zlib'); const zlib = require('zlib');
const UNIQUE_MODULES = ['psg', 'sg', 'bsg', 'rf', 'fs', 'fh']; const UNIQUE_MODULES = ['psg', 'sg', 'bsg', 'rf', 'fs', 'fh', 'gfsb'];
// Constants for modifications struct // Constants for modifications struct
const SLOT_ID_DONE = -1; const SLOT_ID_DONE = -1;
@@ -151,7 +152,7 @@ export default class Ship {
* @return {Number} Jump range in Light Years * @return {Number} Jump range in Light Years
*/ */
calcLadenRange(massDelta, fuel, fsd) { calcLadenRange(massDelta, fuel, fsd) {
return Calc.jumpRange(this.ladenMass + (massDelta || 0), fsd || this.standard[2].m, fuel); return Calc.jumpRange(this.ladenMass + (massDelta || 0), fsd || this.standard[2].m, fuel, this);
} }
/** /**
@@ -164,7 +165,7 @@ export default class Ship {
calcUnladenRange(massDelta, fuel, fsd) { calcUnladenRange(massDelta, fuel, fsd) {
fsd = fsd || this.standard[2].m; fsd = fsd || this.standard[2].m;
let fsdMaxFuelPerJump = fsd instanceof Module ? fsd.getMaxFuelPerJump() : fsd.maxfuel; let fsdMaxFuelPerJump = fsd instanceof Module ? fsd.getMaxFuelPerJump() : fsd.maxfuel;
return Calc.jumpRange(this.unladenMass + (massDelta || 0) + Math.min(fsdMaxFuelPerJump, fuel || this.fuelCapacity), fsd || this.standard[2].m, fuel); return Calc.jumpRange(this.unladenMass + (massDelta || 0) + Math.min(fsdMaxFuelPerJump, fuel || this.fuelCapacity), fsd || this.standard[2].m, fuel, this);
} }
/** /**
@@ -239,7 +240,6 @@ export default class Ship {
} }
sg = sgSlot.m; sg = sgSlot.m;
} }
// TODO Not accurate if the ship has modified shield boosters // TODO Not accurate if the ship has modified shield boosters
return Calc.shieldStrength(this.hullMass, this.baseShieldStrength, sg, 1 + (multiplierDelta || 0)); return Calc.shieldStrength(this.hullMass, this.baseShieldStrength, sg, 1 + (multiplierDelta || 0));
} }
@@ -485,10 +485,7 @@ export default class Ship {
* @param {Object} m The module for which to clear the blueprint * @param {Object} m The module for which to clear the blueprint
*/ */
clearModuleSpecial(m) { clearModuleSpecial(m) {
if (m.blueprint) { this.setModuleSpecial(m, null);
m.blueprint.special = null;
}
this.recalculateDps().recalculateHps().recalculateEps();
} }
/** /**
@@ -497,68 +494,62 @@ export default class Ship {
* @param {Object} name The name of the modification to change * @param {Object} name The name of the modification to change
* @param {Number} value The new value of the modification. The value of the modification is scaled to provide two decimal places of precision in an integer. For example 1.23% is stored as 123 * @param {Number} value The new value of the modification. The value of the modification is scaled to provide two decimal places of precision in an integer. For example 1.23% is stored as 123
* @param {bool} sentfromui True if this update was sent from the UI * @param {bool} sentfromui True if this update was sent from the UI
* @param {bool} isAbsolute True if value is an absolute value and not a
* modification value
*/ */
setModification(m, name, value, sentfromui) { setModification(m, name, value, sentfromui, isAbsolute) {
if (isNaN(value)) { if (isNaN(value)) {
// Value passed is invalid; reset it to 0 // Value passed is invalid; reset it to 0
value = 0; value = 0;
} }
if (isAbsolute) {
m.setPretty(name, value, sentfromui);
} else {
m.setModValue(name, value, sentfromui);
}
// Handle special cases // Handle special cases
if (name === 'pgen') { if (name === 'pgen') {
// Power generation // Power generation
m.setModValue(name, value, sentfromui);
this.updatePowerGenerated(); this.updatePowerGenerated();
} else if (name === 'power') { } else if (name === 'power') {
// Power usage // Power usage
m.setModValue(name, value, sentfromui);
this.updatePowerUsed(); this.updatePowerUsed();
} else if (name === 'mass') { } else if (name === 'mass') {
// Mass // Mass
m.setModValue(name, value, sentfromui);
this.recalculateMass(); this.recalculateMass();
this.updateMovement(); this.updateMovement();
this.updateJumpStats(); this.updateJumpStats();
} else if (name === 'maxfuel') { } else if (name === 'maxfuel') {
m.setModValue(name, value, sentfromui);
this.updateJumpStats(); this.updateJumpStats();
} else if (name === 'optmass') { } else if (name === 'optmass') {
m.setModValue(name, value, sentfromui);
// Could be for any of thrusters, FSD or shield // Could be for any of thrusters, FSD or shield
this.updateMovement(); this.updateMovement();
this.updateJumpStats(); this.updateJumpStats();
this.recalculateShield(); this.recalculateShield();
} else if (name === 'optmul') { } else if (name === 'optmul') {
m.setModValue(name, value, sentfromui);
// Could be for any of thrusters, FSD or shield // Could be for any of thrusters, FSD or shield
this.updateMovement(); this.updateMovement();
this.updateJumpStats(); this.updateJumpStats();
this.recalculateShield(); this.recalculateShield();
} else if (name === 'shieldboost') { } else if (name === 'shieldboost') {
m.setModValue(name, value, sentfromui);
this.recalculateShield(); this.recalculateShield();
} else if (name === 'hullboost' || name === 'hullreinforcement' || name === 'modulereinforcement') { } else if (name === 'hullboost' || name === 'hullreinforcement' || name === 'modulereinforcement') {
m.setModValue(name, value, sentfromui);
this.recalculateArmour(); this.recalculateArmour();
} else if (name === 'shieldreinforcement') { } else if (name === 'shieldreinforcement') {
m.setModValue(name, value, sentfromui);
this.recalculateShieldCells(); this.recalculateShieldCells();
} else if (name === 'burst' || name == 'burstrof' || name === 'clip' || name === 'damage' || name === 'distdraw' || name === 'jitter' || name === 'piercing' || name === 'range' || name === 'reload' || name === 'rof' || name === 'thermload') { } else if (name === 'burst' || name == 'burstrof' || name === 'clip' || name === 'damage' || name === 'distdraw' || name === 'jitter' || name === 'piercing' || name === 'range' || name === 'reload' || name === 'rof' || name === 'thermload') {
m.setModValue(name, value, sentfromui);
this.recalculateDps(); this.recalculateDps();
this.recalculateHps(); this.recalculateHps();
this.recalculateEps(); this.recalculateEps();
} else if (name === 'explres' || name === 'kinres' || name === 'thermres') { } else if (name === 'explres' || name === 'kinres' || name === 'thermres') {
m.setModValue(name, value, sentfromui);
// Could be for shields or armour // Could be for shields or armour
this.recalculateArmour(); this.recalculateArmour();
this.recalculateShield(); this.recalculateShield();
} else if (name === 'engcap') { } else if (name === 'engcap') {
m.setModValue(name, value, sentfromui);
// Might have resulted in a change in boostability // Might have resulted in a change in boostability
this.updateMovement(); this.updateMovement();
} else {
// Generic
m.setModValue(name, value, sentfromui);
} }
} }
@@ -941,9 +932,14 @@ export default class Ship {
let epsChanged = n && n.getEps() || old && old.getEps(); let epsChanged = n && n.getEps() || old && old.getEps();
let hpsChanged = n && n.getHps() || old && old.getHps(); let hpsChanged = n && n.getHps() || old && old.getHps();
let armourChange = (slot === this.bulkheads) || (n && n.grp === 'hr') || (old && old.grp === 'hr') || (n && n.grp === 'mrp') || (old && old.grp === 'mrp'); let armourChange = (slot === this.bulkheads) ||
(n && n.grp === 'hr') || (old && old.grp === 'hr') ||
(n && n.grp === 'ghrp') || (old && old.grp === 'ghrp') ||
(n && n.grp == 'mahr') || (old && old.grp == 'mahr') ||
(n && n.grp === 'mrp') || (old && old.grp === 'mrp') ||
(n && n.grp === 'gmrp') || (old && old.grp == 'gmrp');
let shieldChange = (n && n.grp === 'bsg') || (old && old.grp === 'bsg') || (n && n.grp === 'psg') || (old && old.grp === 'psg') || (n && n.grp === 'sg') || (old && old.grp === 'sg') || (n && n.grp === 'sb') || (old && old.grp === 'sb'); let shieldChange = (n && n.grp === 'bsg') || (old && old.grp === 'bsg') || (n && n.grp === 'psg') || (old && old.grp === 'psg') || (n && n.grp === 'sg') || (old && old.grp === 'sg') || (n && n.grp === 'sb') || (old && old.grp === 'sb') || (old && old.grp === 'gsrp') || (n && n.grp === 'gsrp');
let shieldCellsChange = (n && n.grp === 'scb') || (old && old.grp === 'scb'); let shieldCellsChange = (n && n.grp === 'scb') || (old && old.grp === 'scb');
@@ -1000,25 +996,6 @@ export default class Ship {
return this; return this;
} }
/**
* Calculate diminishing returns value, where values below a given limit are returned
* as-is, and values between the lower and upper limit of the diminishing returns are
* given at half value.
* Commonly used for resistances.
* @param {Number} val The value
* @param {Number} drll The lower limit for diminishing returns
* @param {Number} drul The upper limit for diminishing returns
* @return {this} The ship instance (for chaining operations)
*/
diminishingReturns(val, drll, drul) {
if (val < drll) {
val = drll;
} else if (val < drul) {
val = drul - (drul - val) / 2;
}
return val;
}
/** /**
* Calculate damage per second and related items for weapons * Calculate damage per second and related items for weapons
* @return {this} The ship instance (for chaining operations) * @return {this} The ship instance (for chaining operations)
@@ -1205,38 +1182,35 @@ export default class Ship {
unladenMass += this.bulkheads.m.getMass(); unladenMass += this.bulkheads.m.getMass();
for (let slotNum in this.standard) { let slots = this.standard.concat(this.internal, this.hardpoints);
const slot = this.standard[slotNum]; // TODO: create class for slot and also add slot.get
if (slot.m) { // handle unladen mass
unladenMass += slot.m.getMass(); unladenMass += chain(slots)
if (slot.m.grp === 'ft') { .map(slot => slot.m ? slot.m.get('mass') : null)
fuelCapacity += slot.m.fuel; .filter()
} .reduce((sum, mass) => sum + mass)
} .value();
}
for (let slotNum in this.internal) { // handle fuel capacity
const slot = this.internal[slotNum]; fuelCapacity += chain(slots)
if (slot.m) { .map(slot => slot.m ? slot.m.get('fuel') : null)
unladenMass += slot.m.getMass(); .filter()
if (slot.m.grp === 'ft') { .reduce((sum, fuel) => sum + fuel)
fuelCapacity += slot.m.fuel; .value();
} else if (slot.m.grp === 'cr') {
cargoCapacity += slot.m.cargo;
} else if (slot.m.grp.slice(0,2) === 'pc') {
if (slot.m.passengers) {
passengerCapacity += slot.m.passengers;
}
}
}
}
for (let slotNum in this.hardpoints) { // handle cargo capacity
const slot = this.hardpoints[slotNum]; cargoCapacity += chain(slots)
if (slot.m) { .map(slot => slot.m ? slot.m.get('cargo') : null)
unladenMass += slot.m.getMass(); .filter()
} .reduce((sum, cargo) => sum + cargo)
} .value();
// handle passenger capacity
passengerCapacity += chain(slots)
.map(slot => slot.m ? slot.m.get('passengers') : null)
.filter()
.reduce((sum, passengers) => sum + passengers)
.value();
// Update global stats // Update global stats
this.unladenMass = unladenMass; this.unladenMass = unladenMass;
@@ -1277,7 +1251,7 @@ export default class Ship {
// Obtain shield metrics with 0 pips to sys (parts affected by SYS aren't used here) // Obtain shield metrics with 0 pips to sys (parts affected by SYS aren't used here)
const metrics = Calc.shieldMetrics(this, 0); const metrics = Calc.shieldMetrics(this, 0);
this.shield = metrics.generator ? metrics.generator + metrics.boosters : 0; this.shield = metrics.generator ? metrics.generator + metrics.boosters + metrics.addition : 0;
this.shieldExplRes = this.shield > 0 ? 1 - metrics.explosive.total : null; this.shieldExplRes = this.shield > 0 ? 1 - metrics.explosive.total : null;
this.shieldKinRes = this.shield > 0 ? 1 - metrics.kinetic.total : null; this.shieldKinRes = this.shield > 0 ? 1 - metrics.kinetic.total : null;
this.shieldThermRes = this.shield > 0 ? 1 - metrics.thermal.total : null; this.shieldThermRes = this.shield > 0 ? 1 - metrics.thermal.total : null;
@@ -1310,45 +1284,15 @@ export default class Ship {
*/ */
recalculateArmour() { recalculateArmour() {
// Armour from bulkheads // Armour from bulkheads
let bulkhead = this.bulkheads.m; let metrics = Calc.armourMetrics(this);
let armour = this.baseArmour + (this.baseArmour * bulkhead.getHullBoost());
let modulearmour = 0;
let moduleprotection = 1;
let hullExplRes = 1 - bulkhead.getExplosiveResistance();
const hullExplResDRStart = hullExplRes * 0.7;
const hullExplResDREnd = hullExplRes * 0;
let hullKinRes = 1 - bulkhead.getKineticResistance();
const hullKinResDRStart = hullKinRes * 0.7;
const hullKinResDREnd = hullKinRes * 0;
let hullThermRes = 1 - bulkhead.getThermalResistance();
const hullThermResDRStart = hullThermRes * 0.7;
const hullThermResDREnd = hullThermRes * 0;
// Armour from HRPs and module armour from MRPs
for (let slot of this.internal) {
if (slot.m && slot.m.grp == 'hr') {
armour += slot.m.getHullReinforcement();
// Hull boost for HRPs is applied against the ship's base armour
armour += this.baseArmour * slot.m.getModValue('hullboost') / 10000;
hullExplRes *= (1 - slot.m.getExplosiveResistance());
hullKinRes *= (1 - slot.m.getKineticResistance());
hullThermRes *= (1 - slot.m.getThermalResistance());
}
if (slot.m && slot.m.grp == 'mrp') {
modulearmour += slot.m.getIntegrity();
moduleprotection = moduleprotection * (1 - slot.m.getProtection());
}
}
moduleprotection = 1 - moduleprotection;
this.armour = armour;
this.modulearmour = modulearmour;
this.moduleprotection = moduleprotection;
this.hullExplRes = 1 - this.diminishingReturns(hullExplRes, hullExplResDREnd, hullExplResDRStart);
this.hullKinRes = 1 - this.diminishingReturns(hullKinRes, hullKinResDREnd, hullKinResDRStart);
this.hullThermRes = 1 - this.diminishingReturns(hullThermRes, hullThermResDREnd, hullThermResDRStart);
this.armour = metrics.total ? metrics.total : 0;
this.modulearmour = metrics.modulearmour;
this.moduleprotection = metrics.moduleprotection;
this.hullExplRes = 1 - metrics.explosive.total;
this.hullKinRes = 1 - metrics.kinetic.total;
this.hullThermRes = 1 - metrics.thermal.total;
this.hullCausRes = 1 - metrics.caustic.total;
return this; return this;
} }
@@ -1360,10 +1304,10 @@ export default class Ship {
let fsd = this.standard[2].m; // Frame Shift Drive; let fsd = this.standard[2].m; // Frame Shift Drive;
let { unladenMass, fuelCapacity } = this; let { unladenMass, fuelCapacity } = this;
this.unladenRange = this.calcUnladenRange(); // Includes fuel weight for jump this.unladenRange = this.calcUnladenRange(); // Includes fuel weight for jump
this.fullTankRange = Calc.jumpRange(unladenMass + fuelCapacity, fsd); // Full Tank this.fullTankRange = Calc.jumpRange(unladenMass + fuelCapacity, fsd, this); // Full Tank
this.ladenRange = this.calcLadenRange(); // Includes full tank and caro this.ladenRange = this.calcLadenRange(); // Includes full tank and caro
this.unladenFastestRange = Calc.totalJumpRange(unladenMass + this.fuelCapacity, fsd, fuelCapacity); this.unladenFastestRange = Calc.totalJumpRange(unladenMass + this.fuelCapacity, fsd, fuelCapacity, this);
this.ladenFastestRange = Calc.totalJumpRange(unladenMass + this.fuelCapacity + this.cargoCapacity, fsd, fuelCapacity); this.ladenFastestRange = Calc.totalJumpRange(unladenMass + this.fuelCapacity + this.cargoCapacity, fsd, fuelCapacity, this);
this.maxJumpCount = Math.ceil(fuelCapacity / fsd.getMaxFuelPerJump()); this.maxJumpCount = Math.ceil(fuelCapacity / fsd.getMaxFuelPerJump());
return this; return this;
} }

View File

@@ -0,0 +1,82 @@
export const SI_PREFIXES = {
'Y': 1e+24, // Yotta
'Z': 1e+21, // Zetta
'E': 1e+18, // Peta
'P': 1e+15, // Peta
'T': 1e+12, // Tera
'G': 1e+9, // Giga
'M': 1e+6, // Mega
'k': 1e+3, // Kilo
'h': 1e+2, // Hekto
'da': 1e+1, // Deka
'': 1,
'd': 1e-1, // Dezi
'c': 1e-2, // Zenti
'm': 1e-3, // Milli
'μ': 1e-6, // mikro not supported due to charset
'n': 10e-9, // Nano
'p': 1e-12, // Nano
'f': 1e-15, // Femto
'a': 1e-18, // Atto
'z': 1e-21, // Zepto
'y': 1e-24 // Yokto
};
export const STATS_FORMATTING = {
'ammo': { 'format': 'int', },
'boot': { 'format': 'int', 'unit': 'secs' },
'brokenregen': { 'format': 'round1', 'unit': 'ps' },
'burst': { 'format': 'int' },
'burstrof': { 'format': 'round1', 'unit': 'ps' },
'causres': { 'format': 'pct' },
'clip': { 'format': 'int' },
'damage': { 'format': 'round' },
'dps': { 'format': 'round', 'units': 'ps', 'synthetic': 'getDps' },
'dpe': { 'format': 'round', 'units': 'ps', 'synthetic': 'getDpe' },
'distdraw': { 'format': 'round', 'unit': 'MW' },
'duration': { 'format': 'round1', 'unit': 's' },
'eff': { 'format': 'round2' },
'engcap': { 'format': 'round1', 'unit': 'MJ' },
'engrate': { 'format': 'round1', 'unit': 'MW' },
'eps': { 'format': 'round', 'units': 'ps', 'synthetic': 'getEps' },
'explres': { 'format': 'pct' },
'facinglimit': { 'format': 'round1', 'unit': 'ang' },
'falloff': { 'format': 'round', 'unit': 'km', 'storedUnit': 'm' },
'fallofffromrange': { 'format': 'round', 'unit': 'km', 'storedUnit': 'm', 'synthetic': 'getFalloff' },
'hps': { 'format': 'round', 'units': 'ps', 'synthetic': 'getHps' },
'hullboost': { 'format': 'pct1', 'change': 'additive' },
'hullreinforcement': { 'format': 'int' },
'integrity': { 'format': 'round1' },
'jitter': { 'format': 'round', 'unit': 'ang' },
'kinres': { 'format': 'pct' },
'mass': { 'format': 'round1', 'unit': 'T' },
'maxfuel': { 'format': 'round1', 'unit': 'T' },
'optmass': { 'format': 'int', 'unit': 'T' },
'optmul': { 'format': 'pct', 'change': 'additive' },
'pgen': { 'format': 'round1', 'unit': 'MW' },
'piercing': { 'format': 'int' },
'power': { 'format': 'round', 'unit': 'MW' },
'protection': { 'format': 'pct' },
'range': { 'format': 'f2', 'unit': 'km', 'storedUnit': 'm' },
'ranget': { 'format': 'f1', 'unit': 's' },
'regen': { 'format': 'round1', 'unit': 'ps' },
'reload': { 'format': 'int', 'unit': 's' },
'rof': { 'format': 'round1', 'unit': 'ps' },
'angle': { 'format': 'round1', 'unit': 'ang' },
'scanrate': { 'format': 'int' },
'scantime': { 'format': 'round1', 'unit': 's' },
'sdps': { 'format': 'round1', 'units': 'ps', 'synthetic': 'getSDps' },
'shield': { 'format': 'int', 'unit': 'MJ' },
'shieldaddition': { 'format': 'round1', 'unit': 'MJ' },
'shieldboost': { 'format': 'pct1', 'change': 'additive' },
'shieldreinforcement': { 'format': 'round1', 'unit': 'MJ' },
'shotspeed': { 'format': 'int', 'unit': 'm/s' },
'spinup': { 'format': 'round1', 'unit': 's' },
'syscap': { 'format': 'round1', 'unit': 'MJ' },
'sysrate': { 'format': 'round1', 'unit': 'MW' },
'thermload': { 'format': 'round1' },
'thermres': { 'format': 'pct' },
'wepcap': { 'format': 'round1', 'unit': 'MJ' },
'weprate': { 'format': 'round1', 'unit': 'MW' },
'jumpboost': { 'format': 'round1', 'unit': 'LY' }
};

View File

@@ -5,6 +5,7 @@ const LS_KEY_BUILDS = 'builds';
const LS_KEY_COMPARISONS = 'comparisons'; const LS_KEY_COMPARISONS = 'comparisons';
const LS_KEY_LANG = 'NG_TRANSLATE_LANG_KEY'; const LS_KEY_LANG = 'NG_TRANSLATE_LANG_KEY';
const LS_KEY_COST_TAB = 'costTab'; const LS_KEY_COST_TAB = 'costTab';
const LS_KEY_CMDR_NAME = 'cmdrName';
const LS_KEY_OUTFITTING_TAB = 'outfittingTab'; const LS_KEY_OUTFITTING_TAB = 'outfittingTab';
const LS_KEY_INSURANCE = 'insurance'; const LS_KEY_INSURANCE = 'insurance';
const LS_KEY_SHIP_DISCOUNT = 'shipDiscount'; const LS_KEY_SHIP_DISCOUNT = 'shipDiscount';
@@ -14,6 +15,7 @@ const LS_KEY_SIZE_RATIO = 'sizeRatio';
const LS_KEY_TOOLTIPS = 'tooltips'; const LS_KEY_TOOLTIPS = 'tooltips';
const LS_KEY_MODULE_RESISTANCES = 'moduleResistances'; const LS_KEY_MODULE_RESISTANCES = 'moduleResistances';
const LS_KEY_ROLLS = 'matsPerGrade'; const LS_KEY_ROLLS = 'matsPerGrade';
const LS_KEY_ORBIS = 'orbis';
let LS; let LS;
@@ -86,6 +88,7 @@ export class Persist extends EventEmitter {
let moduleResistances = _get(LS_KEY_MODULE_RESISTANCES); let moduleResistances = _get(LS_KEY_MODULE_RESISTANCES);
let matsPerGrade = _get(LS_KEY_ROLLS); let matsPerGrade = _get(LS_KEY_ROLLS);
let cmdrName = _get(LS_KEY_CMDR_NAME);
let tips = _get(LS_KEY_TOOLTIPS); let tips = _get(LS_KEY_TOOLTIPS);
let insurance = _getString(LS_KEY_INSURANCE); let insurance = _getString(LS_KEY_INSURANCE);
let shipDiscount = _get(LS_KEY_SHIP_DISCOUNT); let shipDiscount = _get(LS_KEY_SHIP_DISCOUNT);
@@ -93,6 +96,7 @@ export class Persist extends EventEmitter {
let buildJson = _get(LS_KEY_BUILDS); let buildJson = _get(LS_KEY_BUILDS);
let comparisonJson = _get(LS_KEY_COMPARISONS); let comparisonJson = _get(LS_KEY_COMPARISONS);
this.orbisCreds = _get(LS_KEY_ORBIS) || { email: '', password: '' };
this.onStorageChange = this.onStorageChange.bind(this); this.onStorageChange = this.onStorageChange.bind(this);
this.langCode = _getString(LS_KEY_LANG) || 'en'; this.langCode = _getString(LS_KEY_LANG) || 'en';
this.insurance = insurance && Insurance[insurance.toLowerCase()] !== undefined ? insurance : 'standard'; this.insurance = insurance && Insurance[insurance.toLowerCase()] !== undefined ? insurance : 'standard';
@@ -111,6 +115,7 @@ export class Persist extends EventEmitter {
4: 4, 4: 4,
5: 10 5: 10
}; };
this.cmdrName = cmdrName || { selected: '', cmdrs: [] };
this.tooltipsEnabled = tips === null ? true : tips; this.tooltipsEnabled = tips === null ? true : tips;
this.moduleResistancesEnabled = moduleResistances === null ? true : moduleResistances; this.moduleResistancesEnabled = moduleResistances === null ? true : moduleResistances;
@@ -165,6 +170,10 @@ export class Persist extends EventEmitter {
this.matsPerGrade = JSON.parse(newValue); this.matsPerGrade = JSON.parse(newValue);
this.emit('matsPerGrade', this.matsPerGrade); this.emit('matsPerGrade', this.matsPerGrade);
break; break;
case LS_KEY_ORBIS:
this.orbisCreds = JSON.parse(newValue);
this.emit('orbis', this.orbisCreds);
break;
} }
} catch (e) { } catch (e) {
// On JSON.Parse Error - don't sync or do anything // On JSON.Parse Error - don't sync or do anything
@@ -190,6 +199,24 @@ export class Persist extends EventEmitter {
this.emit('language', langCode); this.emit('language', langCode);
} }
/**
* Get the current orbis.zone credentials
* @return {String} language code
*/
getOrbisCreds() {
return this.orbisCreds;
};
/**
* Update and save the orbis.zone credentials
* @param {Object} creds object with username and password properties.
*/
setOrbisCreds(creds) {
this.langCode = creds;
_put(LS_KEY_ORBIS, creds);
this.emit('orbis', creds);
}
/** /**
* Show tooltips setting * Show tooltips setting
* @param {boolean} show Optional - update setting * @param {boolean} show Optional - update setting
@@ -487,6 +514,14 @@ export class Persist extends EventEmitter {
return this.matsPerGrade; return this.matsPerGrade;
} }
/**
* Get the saved Mats per grade
* @return {Object} # of rolls per grade
*/
getCmdr() {
return this.cmdrName;
}
/** /**
* Persist selected cost tab * Persist selected cost tab
* @param {number} tabName Cost tab name * @param {number} tabName Cost tab name
@@ -496,6 +531,16 @@ export class Persist extends EventEmitter {
_put(LS_KEY_COST_TAB, tabName); _put(LS_KEY_COST_TAB, tabName);
} }
/**
* Persist cmdr name
* @param {Object} cmdrName Commander name for EDEngineer
*/
setCmdr(cmdrName) {
this.cmdrName = cmdrName;
_put(LS_KEY_CMDR_NAME, cmdrName);
this.emit('cmdr');
}
/** /**
* Get the saved discount * Get the saved discount
* @return {number} val Discount value/amount * @return {number} val Discount value/amount

View File

@@ -294,21 +294,6 @@ export function getBlueprint(name, module) {
return {}; return {};
} }
const blueprint = JSON.parse(JSON.stringify(found)); const blueprint = JSON.parse(JSON.stringify(found));
if (module) {
if (module.grp === 'sb') {
// Shield boosters are treated internally as straight modifiers, so rather than (for example)
// being a 4% boost they are a 104% multiplier. We need to fix the values here so that they look
// accurate as per the information in Elite
for (const grade in blueprint.grades) {
for (const feature in blueprint.grades[grade].features) {
if (feature === 'shieldboost') {
blueprint.grades[grade].features[feature][0] = ((1 + blueprint.grades[grade].features[feature][0]) * (1 + module.shieldboost) - 1) / module.shieldboost - 1;
blueprint.grades[grade].features[feature][1] = ((1 + blueprint.grades[grade].features[feature][1]) * (1 + module.shieldboost) - 1) / module.shieldboost - 1;
}
}
}
}
}
return blueprint; return blueprint;
} }
@@ -387,7 +372,9 @@ export function getPercent(m) {
let value = _getValue(m, featureName); let value = _getValue(m, featureName);
let mult; let mult;
if (Modifications.modifications[featureName].higherbetter) { if (featureName == 'shieldboost') {
mult = ((1 + value) * (1 + m.shieldboost)) - 1 - m.shieldboost;
} else if (Modifications.modifications[featureName].higherbetter) {
// Higher is better, but is this making it better or worse? // Higher is better, but is this making it better or worse?
if (features[featureName][0] < 0 || (features[featureName][0] === 0 && features[featureName][1] < 0)) { if (features[featureName][0] < 0 || (features[featureName][0] === 0 && features[featureName][1] < 0)) {
mult = Math.round((value - features[featureName][1]) / (features[featureName][0] - features[featureName][1]) * 100); mult = Math.round((value - features[featureName][1]) / (features[featureName][0] - features[featureName][1]) * 100);

View File

@@ -6,7 +6,7 @@ import { getBlueprint } from '../utils/BlueprintFunctions';
import * as ModuleUtils from '../shipyard/ModuleUtils'; import * as ModuleUtils from '../shipyard/ModuleUtils';
// mapping from fd's ship model names to coriolis' // mapping from fd's ship model names to coriolis'
const SHIP_FD_NAME_TO_CORIOLIS_NAME = { export const SHIP_FD_NAME_TO_CORIOLIS_NAME = {
'Adder': 'adder', 'Adder': 'adder',
'Anaconda': 'anaconda', 'Anaconda': 'anaconda',
'Asp': 'asp', 'Asp': 'asp',
@@ -29,6 +29,7 @@ const SHIP_FD_NAME_TO_CORIOLIS_NAME = {
'FerDeLance': 'fer_de_lance', 'FerDeLance': 'fer_de_lance',
'Hauler': 'hauler', 'Hauler': 'hauler',
'Independant_Trader': 'keelback', 'Independant_Trader': 'keelback',
'Krait_MkII': 'krait_mkii',
'Orca': 'orca', 'Orca': 'orca',
'Python': 'python', 'Python': 'python',
'SideWinder': 'sidewinder', 'SideWinder': 'sidewinder',
@@ -37,6 +38,8 @@ const SHIP_FD_NAME_TO_CORIOLIS_NAME = {
'Type9': 'type_9_heavy', 'Type9': 'type_9_heavy',
'Type9_Military': 'type_10_defender', 'Type9_Military': 'type_10_defender',
'TypeX': 'alliance_chieftain', 'TypeX': 'alliance_chieftain',
'TypeX_2': 'alliance_crusader',
'TypeX_3': 'alliance_challenger',
'Viper': 'viper', 'Viper': 'viper',
'Viper_MkIV': 'viper_mk_iv', 'Viper_MkIV': 'viper_mk_iv',
'Vulture': 'vulture' 'Vulture': 'vulture'

View File

@@ -70,7 +70,7 @@ export function shipFromLoadoutJSON(json) {
let ship = new Ship(shipModel, shipTemplate.properties, shipTemplate.slots); let ship = new Ship(shipModel, shipTemplate.properties, shipTemplate.slots);
ship.buildWith(null); ship.buildWith(null);
// Initial Ship building, don't do engineering yet. // Initial Ship building, don't do engineering yet.
let opts = []; let modsToAdd = [];
for (const module of json.Modules) { for (const module of json.Modules) {
switch (module.Slot.toLowerCase()) { switch (module.Slot.toLowerCase()) {
@@ -176,7 +176,7 @@ export function shipFromLoadoutJSON(json) {
ship.use(ship.hardpoints[hardpointArrayNum], hardpoint, true); ship.use(ship.hardpoints[hardpointArrayNum], hardpoint, true);
ship.hardpoints[hardpointArrayNum].enabled = hardpointSlot.On; ship.hardpoints[hardpointArrayNum].enabled = hardpointSlot.On;
ship.hardpoints[hardpointArrayNum].priority = hardpointSlot.Priority; ship.hardpoints[hardpointArrayNum].priority = hardpointSlot.Priority;
opts.push({ coriolisMod: hardpoint, json: hardpointSlot }); modsToAdd.push({ coriolisMod: hardpoint, json: hardpointSlot });
} }
hardpointArrayNum++; hardpointArrayNum++;
} }
@@ -185,6 +185,9 @@ export function shipFromLoadoutJSON(json) {
let internalSlotNum = 1; let internalSlotNum = 1;
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)) {
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
@@ -198,7 +201,7 @@ export function shipFromLoadoutJSON(json) {
while (internalSlot === null && internalSlotNum < 99) { while (internalSlot === null && internalSlotNum < 99) {
// 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' : '') + internalSlotNum + '_Size' + slotsize; const internalName = 'Slot' + (internalSlotNum <= 9 ? '0' : '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;
@@ -210,23 +213,23 @@ export function shipFromLoadoutJSON(json) {
if (!internalSlot) { if (!internalSlot) {
// This can happen with old imports that don't contain new slots // This can happen with old imports that don't contain new slots
} else if (!internalSlot) {
// No module
} else { } else {
const internalJson = internalSlot; const internalJson = internalSlot;
const internal = _moduleFromFdName(internalJson.Item); const internal = _moduleFromFdName(internalJson.Item);
ship.use(ship.internal[i], internal, true); ship.use(ship.internal[i], internal, true);
ship.internal[i].enabled = internalJson.On === true; ship.internal[i].enabled = internalJson.On === true;
ship.internal[i].priority = internalJson.Priority; ship.internal[i].priority = internalJson.Priority;
opts.push({ coriolisMod: internal, json: internalSlot }); modsToAdd.push({ coriolisMod: internal, json: internalSlot });
} }
} }
} }
} }
} }
for (const i of opts) { for (const i of modsToAdd) {
if (i.json.Engineering) _addModifications(i.coriolisMod, i.json.Engineering.Modifiers, i.json.Engineering.BlueprintName, i.json.Engineering.Level, i.json.Engineering.ExperimentalEffect); if (i.json.Engineering) {
_addModifications(i.coriolisMod, i.json.Engineering.Modifiers, i.json.Engineering.BlueprintName, i.json.Engineering.Level, i.json.Engineering.ExperimentalEffect);
}
} }
// We don't have any information on it so guess it's priority 5 and disabled // We don't have any information on it so guess it's priority 5 and disabled
if (!ship.cargoHatch) { if (!ship.cargoHatch) {
@@ -252,6 +255,16 @@ function _addModifications(module, modifiers, blueprint, grade, specialModificat
if (specialModifications) { if (specialModifications) {
special = Modifications.specials[specialModifications]; special = Modifications.specials[specialModifications];
} }
// Add the blueprint definition, grade and special
if (blueprint) {
module.blueprint = getBlueprint(blueprint, module);
if (grade) {
module.blueprint.grade = Number(grade);
}
if (special) {
module.blueprint.special = special;
}
}
for (const i in modifiers) { for (const i in modifiers) {
// Some special modifications // Some special modifications
// Look up the modifiers to find what we need to do // Look up the modifiers to find what we need to do
@@ -268,24 +281,17 @@ function _addModifications(module, modifiers, blueprint, grade, specialModificat
if (modifiers[i].Label.search('Resistance') >= 0) { if (modifiers[i].Label.search('Resistance') >= 0) {
value = (modifiers[i].Value * 100) - (modifiers[i].OriginalValue * 100); value = (modifiers[i].Value * 100) - (modifiers[i].OriginalValue * 100);
} }
if (modifiers[i].Label.search('ShieldMultiplier') >= 0 || modifiers[i].Label.search('DefenceModifierHealthMultiplier') >= 0) {
value = ((100 + modifiers[i].Value) / (100 + modifiers[i].OriginalValue) * 100 - 100) * 100;
}
// Carry out the required changes // Carry out the required changes
for (const action in modifierActions) { for (const action in modifierActions) {
if (isNaN(modifierActions[action])) { if (isNaN(modifierActions[action])) {
module.setModValue(action, modifierActions[action]); module.setModValue(action, modifierActions[action]);
} else { } else {
module.setModValue(action, value); module.setModValue(action, value, true);
} }
} }
} }
// Add the blueprint definition, grade and special
if (blueprint) {
module.blueprint = getBlueprint(blueprint, module);
if (grade) {
module.blueprint.grade = Number(grade);
}
if (special) {
module.blueprint.special = special;
}
}
} }

View File

@@ -1,5 +1,14 @@
import request from 'superagent'; import request from 'superagent';
let agent;
try {
agent = request.agent(); // apparently this crashes somehow
} catch (e) {
console.error(e);
}
if (!agent) {
agent = request;
}
/** /**
* Shorten a URL * Shorten a URL
@@ -8,7 +17,7 @@ import request from 'superagent';
* @param {function} error Failure/Error callback * @param {function} error Failure/Error callback
*/ */
export default function shorternUrl(url, success, error) { export default function shorternUrl(url, success, error) {
shortenUrlOrbis(url, success, error); orbisShorten(url, success, error);
} }
const SHORTEN_API_GOOGLE = 'https://www.googleapis.com/urlshortener/v1/url?key='; const SHORTEN_API_GOOGLE = 'https://www.googleapis.com/urlshortener/v1/url?key=';
@@ -65,31 +74,67 @@ function shortenUrlEddp(url, success, error) {
} }
} }
const SHORTEN_API_ORBIS = 'https://s.orbis.zone/a'; const SHORTEN_API_ORBIS = 'https://s.orbis.zone/api.php';
/** /**
* Shorten a URL using Orbis's URL shortener API * Shorten a URL using Orbis's URL shortener API
* @param {string} url The URL to shorten * @param {string} url The URL to shorten
* @param {function} success Success callback * @param {function} success Success callback
* @param {function} error Failure/Error callback * @param {function} error Failure/Error callback
*/ */
function shortenUrlOrbis(url, success, error) { function orbisShorten(url, success, error) {
if (window.navigator.onLine) { if (window.navigator.onLine) {
try { try {
request.post(SHORTEN_API_ORBIS) request.post(SHORTEN_API_ORBIS)
.field('lsturl', url) .field('action', 'shorturl')
.field('url', url)
.field('format', 'json') .field('format', 'json')
.end(function(err, response) { .end(function(err, response) {
if (err) { if (err) {
console.error(err);
error('Bad Request'); error('Bad Request');
} else { } else {
success(response.body.short); success(response.body.shorturl);
} }
}); });
} catch (e) { } catch (e) {
console.log(e) console.log(e);
error(e.message ? e.message : e); error(e.message ? e.message : e);
} }
} else { } else {
error('Not Online'); error('Not Online');
} }
} }
const API_ORBIS = 'https://orbis.zone/api/builds/add';
/**
* Upload to Orbis
* @param {object} ship The URL to shorten
* @param {object} creds Orbis credentials
* @return {Promise<any>} Either a URL or error message.
*/
export function orbisUpload(ship, creds) {
return new Promise(async (resolve, reject) => {
if (window.navigator.onLine) {
try {
agent
.post(API_ORBIS)
.withCredentials()
.redirects(0)
.set('Content-Type', 'application/json')
.send(ship)
.end(function(err, response) {
if (err) {
reject('Bad Request');
} else {
resolve(response.body.link);
}
});
} catch (e) {
console.log(e);
reject(e.message ? e.message : e);
}
} else {
reject('Not Online');
}
});
}

View File

@@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html <%= htmlWebpackPlugin.options.appCache ? 'manifest=/' + htmlWebpackPlugin.options.appCache : '' %> > <html manifest="/">
<head> <head>
<title>Coriolis EDCD Edition</title> <title>Coriolis EDCD Edition</title>
<meta charset="UTF-8"> <meta charset="UTF-8">
@@ -26,7 +26,8 @@
<script> <script>
window.CORIOLIS_GAPI_KEY = '<%- htmlWebpackPlugin.options.gapiKey %>'; window.CORIOLIS_GAPI_KEY = '<%- htmlWebpackPlugin.options.gapiKey %>';
window.CORIOLIS_VERSION = '<%- htmlWebpackPlugin.options.version %>'; window.CORIOLIS_VERSION = '<%- htmlWebpackPlugin.options.version %>';
window.CORIOLIS_DATE = '<%- new Date().toISOString().slice(0, 10) %>'; window.CORIOLIS_DATE = '<%- htmlWebpackPlugin.options.date.toISOString().slice(0, 10) %>';
window.BUGSNAG_VERSION = '<%- htmlWebpackPlugin.options.version + '-' + htmlWebpackPlugin.options.date.toISOString() %>';
</script> </script>
<% if (htmlWebpackPlugin.options.uaTracking) { %> <% if (htmlWebpackPlugin.options.uaTracking) { %>
<script> <script>
@@ -58,7 +59,7 @@
<script src="//d2wy8f7a9ursnm.cloudfront.net/v4/bugsnag.min.js"></script> <script src="//d2wy8f7a9ursnm.cloudfront.net/v4/bugsnag.min.js"></script>
<script src="//d2wy8f7a9ursnm.cloudfront.net/bugsnag-plugins/v1/bugsnag-react.min.js"></script> <script src="//d2wy8f7a9ursnm.cloudfront.net/bugsnag-plugins/v1/bugsnag-react.min.js"></script>
<script> <script>
window.bugsnagClient = bugsnag('ba9fae819372850fb660755341fa6ef5', {appVersion: window.CORIOLIS_VERSION || undefined}) window.bugsnagClient = bugsnag('ba9fae819372850fb660755341fa6ef5', {appVersion: window.BUGSNAG_VERSION || undefined})
window.Bugsnag = window.bugsnagClient window.Bugsnag = window.bugsnagClient
</script> </script>
</head> </head>

View File

@@ -93,6 +93,9 @@
color: @primary-bg; color: @primary-bg;
} }
& thead th.bordered {
border-left: 1px solid @primary-bg;
}
} }
} }

View File

@@ -22,6 +22,11 @@ select {
} }
} }
.cmdr-select {
border: 1px solid @primary;
padding: 0.5em 0.5em;
}
.select { .select {
color: @primary-disabled; color: @primary-disabled;
position: absolute; position: absolute;

View File

@@ -37,6 +37,35 @@
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.modification-container {
@input-container-width: 75%;
td {
width: 100% - @input-container-width;
text-align: center;
}
.input-container {
width: @input-container-width;
text-align: right;
}
input {
width: 80%;
}
.unit-container {
width: 30px;
padding: 3px;
text-align: left;
display: inline-block;
}
.header-adjuster {
width: 100% - @input-container-width;
display: inline-block;
}
}
.cb { .cb {
overflow: hidden; overflow: hidden;
} }
@@ -45,6 +74,11 @@
border-color:#fff; border-color:#fff;
} }
input:disabled {
border-color: #888;
color: #888;
}
.l { .l {
text-transform: capitalize; text-transform: capitalize;
margin-right: 0.8em; margin-right: 0.8em;

46
src/sw.js Normal file
View File

@@ -0,0 +1,46 @@
console.log('Hello from sw.js');
if (workbox) {
workbox.skipWaiting();
workbox.clientsClaim();
console.log('Yay! Workbox is loaded 🎉');
workbox.routing.registerRoute(
new RegExp('https://fonts.(?:googleapis|gstatic).com/(.*)'),
workbox.strategies.cacheFirst({
cacheName: 'google-fonts',
plugins: [
new workbox.expiration.Plugin({
maxEntries: 30
}),
new workbox.cacheableResponse.Plugin({
statuses: [0, 200]
})
]
})
);
workbox.routing.registerRoute(
/\.(?:png|gif|jpg|jpeg|svg)$/,
workbox.strategies.cacheFirst({
cacheName: 'images',
plugins: [
new workbox.expiration.Plugin({
maxEntries: 60,
maxAgeSeconds: 30 * 24 * 60 * 60 // 30 Days
})
]
})
);
workbox.routing.registerRoute(
/\.(?:js|css)$/,
workbox.strategies.staleWhileRevalidate({
cacheName: 'static-resources'
})
);
try {
workbox.googleAnalytics.initialize();
} catch (e) {
console.log('Probably an ad-blocker');
}
} else {
console.log('Boo! Workbox didn\'t load 😬');
}

View File

@@ -1,27 +1,27 @@
const path = require('path') const path = require('path');
const exec = require('child_process').exec 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();
function CopyDirPlugin(source, destination) { function CopyDirPlugin(source, destination) {
this.source = source this.source = source;
this.destination = destination this.destination = destination;
} }
CopyDirPlugin.prototype.apply = function (compiler) { CopyDirPlugin.prototype.apply = function(compiler) {
compiler.plugin('done', () => { compiler.plugin('done', () => {
console.log(compiler.outputPath, this.destination) console.log(compiler.outputPath, this.destination);
exec('cp -r ' + this.source + ' ' + path.join(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': '*' }
}, },
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')], app: ['webpack-dev-server/client?http://0.0.0.0:3300', 'webpack/hot/only-dev-server', path.join(__dirname, 'src/app/index.js')],
@@ -46,6 +46,7 @@ module.exports = {
inject: false, inject: false,
template: path.join(__dirname, 'src/index.ejs'), template: path.join(__dirname, 'src/index.ejs'),
version: pkgJson.version, version: pkgJson.version,
date: buildDate,
gapiKey: process.env.CORIOLIS_GAPI_KEY || '' gapiKey: process.env.CORIOLIS_GAPI_KEY || ''
}), }),
new ExtractTextPlugin({ new ExtractTextPlugin({
@@ -53,20 +54,20 @@ module.exports = {
disable: false, disable: false,
allChunks: true allChunks: true
}), }),
new WebpackNotifierPlugin({alwaysNotify: true}), new WebpackNotifierPlugin({ alwaysNotify: true }),
new webpack.HotModuleReplacementPlugin(), new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin() new webpack.NoEmitOnErrorsPlugin()
], ],
module: { module: {
rules: [ rules: [
{test: /\.css$/, loader: ExtractTextPlugin.extract({fallback: 'style-loader', use: 'css-loader'})}, { test: /\.css$/, loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader' }) },
{test: /\.less$/, loader: ExtractTextPlugin.extract({fallback: 'style-loader', use: 'css-loader!less-loader'})}, { test: /\.less$/, loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader!less-loader' }) },
{test: /\.(js|jsx)$/, loaders: ['babel-loader'], include: path.join(__dirname, 'src')}, { test: /\.(js|jsx)$/, loaders: ['babel-loader'], include: path.join(__dirname, 'src') },
{test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/font-woff'}, { test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/font-woff' },
{test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/font-woff'}, { test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/font-woff' },
{test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/octet-stream'}, { test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/octet-stream' },
{test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: 'file-loader'}, { test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: 'file-loader' },
{test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=image/svg+xml'} { test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=image/svg+xml' }
] ]
} }
} };

View File

@@ -1,23 +1,24 @@
const path = require('path') const path = require('path');
const exec = require('child_process').exec 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 AppCachePlugin = require('appcache-webpack-plugin') const { InjectManifest } = require('workbox-webpack-plugin');
const {BugsnagSourceMapUploaderPlugin} = require('webpack-bugsnag-plugins')
const pkgJson = require('./package')
function CopyDirPlugin (source, destination) { const { BugsnagSourceMapUploaderPlugin } = require('webpack-bugsnag-plugins');
this.source = source const pkgJson = require('./package');
this.destination = destination const buildDate = new Date();
function CopyDirPlugin(source, destination) {
this.source = source;
this.destination = destination;
} }
CopyDirPlugin.prototype.apply = function (compiler) { CopyDirPlugin.prototype.apply = function(compiler) {
compiler.plugin('done', () => { compiler.plugin('done', () => {
console.log(compiler.outputPath, this.destination) console.log(compiler.outputPath, this.destination);
exec('cp -r ' + this.source + ' ' + path.join(compiler.outputPath, this.destination)) exec('cp -r ' + this.source + ' ' + path.join(compiler.outputPath, this.destination));
}) });
} };
module.exports = { module.exports = {
cache: true, cache: true,
@@ -40,10 +41,10 @@ module.exports = {
'screw-ie8': true, 'screw-ie8': true,
sourceMap: 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: false,
appCache: 'coriolis.appcache', appCache: 'coriolis.appcache',
@@ -60,6 +61,7 @@ module.exports = {
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 || '',
date: buildDate,
version: pkgJson.version version: pkgJson.version
}), }),
new ExtractTextPlugin({ new ExtractTextPlugin({
@@ -69,28 +71,27 @@ module.exports = {
}), }),
new BugsnagSourceMapUploaderPlugin({ new BugsnagSourceMapUploaderPlugin({
apiKey: 'ba9fae819372850fb660755341fa6ef5', apiKey: 'ba9fae819372850fb660755341fa6ef5',
appVersion: pkgJson.version appVersion: `${pkgJson.version}-${buildDate.toISOString()}`
}), }),
new CopyDirPlugin(path.join(__dirname, 'src/schemas'), 'schemas'), new CopyDirPlugin(path.join(__dirname, 'src/schemas'), 'schemas'),
new CopyDirPlugin(path.join(__dirname, 'src/images/logo/*'), ''), new CopyDirPlugin(path.join(__dirname, 'src/images/logo/*'), ''),
new CopyDirPlugin(path.join(__dirname, 'src/.htaccess'), ''), new CopyDirPlugin(path.join(__dirname, 'src/.htaccess'), ''),
new AppCachePlugin({ new InjectManifest({
network: ['*'], swSrc: './src/sw.js',
settings: ['prefer-online'], importWorkboxFrom: 'cdn',
exclude: ['index.html', /.*\.map$/], swDest: 'service-worker.js'
output: 'coriolis.appcache' }),
})
], ],
module: { module: {
rules: [ rules: [
{test: /\.css$/, loader: ExtractTextPlugin.extract({fallback: 'style-loader', use: 'css-loader'})}, { test: /\.css$/, loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader' }) },
{test: /\.less$/, loader: ExtractTextPlugin.extract({fallback: 'style-loader', use: 'css-loader!less-loader'})}, { test: /\.less$/, loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader!less-loader' }) },
{test: /\.(js|jsx)$/, loader: 'babel-loader?cacheDirectory=true', include: path.join(__dirname, 'src')}, { test: /\.(js|jsx)$/, loader: 'babel-loader?cacheDirectory=true', include: path.join(__dirname, 'src') },
{test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/font-woff'}, { test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/font-woff' },
{test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/font-woff'}, { test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/font-woff' },
{test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/octet-stream'}, { test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/octet-stream' },
{test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: 'file-loader'}, { test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: 'file-loader' },
{test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=image/svg+xml'} { test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=image/svg+xml' }
] ]
} }
} };