Compare commits

...

263 Commits

Author SHA1 Message Date
willyb321
f8a215d790 2.9.17 2018-06-14 07:08:17 +10:00
willyb321
da69f3b2c8 New URL Shortener
eddp.co shortened links are considered End-Of-Life from now on
2018-06-14 07:00:05 +10:00
willyb321
fc442c1a42 Merge branch 'master' into develop 2018-06-14 06:50:46 +10:00
willyb321
cf59a6b9fd whoops 2018-06-14 06:50:08 +10:00
willyb321
34afcd511a Merge branch 'develop' 2018-06-14 06:44:13 +10:00
willyb321
e2444a2e4e track ship in ga 2018-06-14 06:43:54 +10:00
willyb321
692516de9b Merge branch 'develop' 2018-06-13 08:37:31 +10:00
willyb321
d51009c823 add time between boosts
Fixes #269
2018-06-13 08:36:56 +10:00
willyb321
2f121bef5e Merge branch 'develop' 2018-06-13 06:46:27 +10:00
willyb321
61f7d376d2 store number of rolls per grade 2018-06-13 06:46:12 +10:00
willyb321
9e253012e6 Merge branch 'develop' 2018-06-13 06:23:57 +10:00
willyb321
e4e5b1327b fix rank link showing on things that dont need a rank link 2018-06-13 06:22:45 +10:00
willyb321
6ef3b227b8 Merge branch 'develop' 2018-06-13 06:16:37 +10:00
willyb321
bf657a0945 fix view changes 2018-06-13 06:16:21 +10:00
willyb321
c3dd1886c9 Merge branch 'develop' 2018-06-12 12:12:29 +10:00
willyb321
32498bb8a7 properly fix super cap 2018-06-12 11:32:37 +10:00
willyb321
69bb90a0e4 Merge branch 'release/2.9.16' 2018-06-12 11:03:18 +10:00
willyb321
8e2988edf0 Merge branch 'release/2.9.16' into develop 2018-06-12 11:03:18 +10:00
willyb321
876a352cfd 2.9.16 2018-06-12 11:03:13 +10:00
willyb321
84e44cabfa mats list modal allow setting # of rolls 2018-06-12 09:49:07 +10:00
willyb321
36a838d565 add material shopping list 2018-06-12 09:09:05 +10:00
willyb321
9ee8693f40 add material icon 2018-06-12 09:08:44 +10:00
willyb321
6f02965149 Merge branch 'develop' 2018-06-12 07:56:19 +10:00
willyb321
27ce82de3b switch to google analytics from piwik 2018-06-12 07:56:01 +10:00
Pat Nellesen
3d5a9ef220 Cleanup for linting issues, and added last bit of focus handling for … (#304)
* Cleanup for linting issues, and added last bit of focus handling for selected modifications and specials

* added correct value for selectedRefId for utility slot section menu when Empty All selected
2018-06-11 08:23:03 +10:00
Pat Nellesen
9b81f6efd2 Feature/#293 header keynav (#303)
Added keydown and focus handlers for Slot Section Menus ("Core Internal", "Optional Internal", etc.)

When focus is on the header, Enter key will open the menu and set focus to either the first option, or else the currently selected option, such as "Planetary Explorer" in Core Internal menu (if one has been previously selected).

While menu is open, Tab and Shift-Tab will move the focus up and down as expected. Shift-tab on first option will move focus to last option in the menu, and Tab on the last option will move focus to the top. Focus will stay inside the menu until menu is closed.

When focus is on a menu options, hitting the Enter key will trigger the onClick function for that option, and will set the option as the currently selected option for that menu.

Esc key will close the menu and set focus to the menu header H1 element.
2018-06-10 09:00:43 +10:00
willyb321
3e77e23d71 Merge remote-tracking branch 'origin/develop' into develop 2018-06-10 06:26:17 +10:00
willyb321
120c032c82 Fix approximately a lot of lint issues 2018-06-10 06:24:55 +10:00
William
46e15b8ecd Merge pull request #301 from BenJuan26/feature/linting
Fix obvious lint issues
2018-06-08 07:15:39 +10:00
William
d71d87041b Merge pull request #300 from BenJuan26/feature/travis-install-coriolis-data
Clone coriolis-data into Travis before npm install
2018-06-08 07:14:54 +10:00
Benjamin Schubert
124bd58b9f Fix obvious linting issues 2018-06-07 10:50:52 -04:00
Benjamin Schubert
257b9b0562 Clone coriolis-data into Travis before npm install 2018-06-07 10:00:46 -04:00
willyb321
b8e15f691d Merge branch 'develop' 2018-06-07 06:57:33 +10:00
willyb321
2255e3bfc4 Temp fix for #273
Don't allow engineering on guardian PP
2018-06-07 06:57:00 +10:00
willyb321
8797d84605 Merge branch 'develop' 2018-06-06 06:27:18 +10:00
willyb321
719179a923 Add link to ranks page
Fixes #298
2018-06-06 06:26:02 +10:00
willyb321
1d544099f6 Merge branch 'release/2.9.15' 2018-05-30 07:43:16 +10:00
willyb321
9b131a762a Merge branch 'release/2.9.15' into develop 2018-05-30 07:43:15 +10:00
willyb321
08c5d2256a 2.9.15 2018-05-30 07:43:09 +10:00
willyb321
ed6ee4341f Fix crash when applying blueprint
thanks bugsnag
2018-05-30 07:42:33 +10:00
willyb321
157c1148fb Implement temporary fix for #280 2018-05-30 07:37:20 +10:00
Pat Nellesen
507ea9e09e Fix to account for cases where there is only one module available for… (#291)
* Fix to account for cases where there is only one module available for a given slot (e.g. Sidewinder fuel tank)

* Fix for issue #289 - set focus to active module section if the slot has an active module

* Added activeSlot ref to set focus to current module when module list is opened

* Comment/console cleanup for PR #291
2018-05-30 07:25:35 +10:00
willyb321
af68cba7be add "view release changes" button below the update available banner 2018-05-29 07:31:58 +10:00
willyb321
224fbe0e8f add a "commits since last release button"
makes it easier for people to see activity
2018-05-29 07:23:20 +10:00
willyb321
49a6c5f2c4 Merge branch 'release/2.9.14' 2018-05-29 07:03:18 +10:00
willyb321
07c936897c Merge branch 'release/2.9.14' into develop 2018-05-29 07:03:18 +10:00
willyb321
3786fb907c 2.9.14 2018-05-29 07:03:11 +10:00
Pat Nellesen
752d9f0c68 Feature/#271 keyboard nav (#288)
* Initial stab at Tab/Enter key handlers for Slots - Added tabIndex="0" attribute for the Slot sections and for the <li> elements inside.

* Set up refs for slot <li> elements to allow focus manipulation.

* Added initial keyDown handlers for Enter and Tab keys.

* Additional update for focus handling inside slot selection <ul>

* Added Tab/Enter/Shift-Tab key handlers for Outfitting section. Needs some fine-tuning but is usable in current state

* Added keydown handler for Empty module selection. ToDo: handlers for engineering sections

* Cleanup

* Update to add focus on Modifications icon and Enter key handler to open modifications menu

* Updates to add keydown handlers for modifications menu, and tab/shift-tab/enter key handlers while mod menus are open.

* Additional updates for keyDown handlers

* Update to add tabindex=0 and Enter Key handling for specials menus. ToDo: keep focus inside specials menu until item is selected with Enter key.

* Further updates for keyDown handler in Modifications menus.

* Added keyDown handlers to full mod menu (when mod menu is opened and a mod has been previously selected)

* Update to add shift-tab and tab focus handling for number editor fields

* Additional tab/shift-tab handlers. Fixed bug with exp. effects being recaculated for every onBlur event.

* Added check to bypass focus reset if change in component was due to change in modification value.

* Additional updates to tab/shift-tab handlers for modifications menu.

* more updates

* "Final" updates and bug fixes - added code for cases where there were no specials.

* Final updates to set focus to appropriate element when slot/mod menus are closed.
2018-05-29 07:00:22 +10:00
Pat Nellesen
baf59aafcb Feature/#248 slider keyboard (#257)
* Added tabIndex=0 to <svg> in Slider to allow keyboard focus

* further tests for mobile keyboard fix

* Proof of concept of way to get mobile keyboard to open for Slider component

* Added CSS to slider input box to make it invisible but still usable

* Update to hide text input and move it up to same position as slider

* Change slider text field type to 'tel" to force numeric-only keyboard on mobile

* Added focus/blur handlers and initial styling to show/hide text field component

* Update to insure text field values are updated when slider moves, and vice versa.

* Added timeout handler for Outfitting sliders to raise keyboard after 1500ms. Doesn't work 100% on iPhone.

* Changed touch/mouse down event handlers to change the display value on the parent div, then use componentDidUpdate to set focus on the text box.

* Added tap/hold mousedown/hold keyboard display - works on desktop, iOS, and Android
2018-05-04 09:01:55 +10:00
willyb321
8787303d2a add a column with what type of shield, update tooltips too 2018-05-04 08:59:57 +10:00
willyb321
298eaa8b4b Merge remote-tracking branch 'origin/develop' 2018-05-03 10:58:46 +10:00
William Blythe
118c80af27 Fix kinetic and thermal res swapped
Closes #265
2018-05-03 10:54:55 +10:00
willyb321
7b87038a8c Merge branch 'develop' 2018-05-03 07:39:08 +10:00
willyb321
d103939e45 add experimentals to core internals 2018-05-03 07:38:54 +10:00
willyb321
fdc9171c69 Merge branch 'release/2.9.13' 2018-05-03 07:14:53 +10:00
willyb321
2a6ade3cab Merge branch 'release/2.9.13' into develop 2018-05-03 07:14:52 +10:00
willyb321
c8c42689f9 2.9.13 2018-05-03 07:14:45 +10:00
willyb321
da2f472f4d hopefully fix #262 2018-05-01 07:13:58 +10:00
William Blythe
3ba878237b fix super cap, nan with no shield 2018-04-30 10:57:42 +10:00
willyb321
7577fb53a2 biweaves are purple 2018-04-28 13:47:46 +10:00
willyb321
f2509f89ee change colour depending on shield 2018-04-28 13:39:17 +10:00
willyb321
9dd1f78330 make shield summary bar blue, change "damage from x" to "x hp" 2018-04-28 13:39:17 +10:00
willyb321
ebb6c2c420 more work on summary 2018-04-28 13:39:17 +10:00
willyb321
9d23dc1763 use set pips to calc shield 2018-04-28 13:39:17 +10:00
willyb321
5fa3f8703e pass pips to summary table 2018-04-28 13:39:17 +10:00
willyb321
797885faea more wip table 2018-04-28 13:39:17 +10:00
willyb321
68e7e9f5b7 fix crash when removing shield 2018-04-28 13:39:17 +10:00
willyb321
d1d165ad51 WIP summary table 2018-04-28 13:39:17 +10:00
willyb321
f43bd100e6 Merge branch 'release/2.9.12' 2018-04-28 12:16:21 +10:00
willyb321
0ebb247666 Merge branch 'release/2.9.12' into develop 2018-04-28 12:16:21 +10:00
willyb321
aa73bc2809 2.9.12 2018-04-28 12:16:11 +10:00
willyb321
c3fcdb918f prep for recon limpet controllers 2018-04-28 12:12:38 +10:00
willyb321
5e3722bcfd hopefully fix a crash 2018-04-28 11:37:24 +10:00
willyb321
6a4fca2eb1 fix .editorconfig 2018-04-26 13:39:52 +10:00
willyb321
fbba0e3ea5 Merge branch 'release/2.9.11' 2018-04-26 09:21:01 +10:00
willyb321
6922cfd047 2.9.11 2018-04-26 09:19:57 +10:00
willyb321
d93fc1d2d0 just update everything when applying a special 2018-04-26 09:06:18 +10:00
willyb321
b15695128f Merge branch 'release/release/v2.9.10' 2018-04-25 20:00:47 +10:00
willyb321
48290b2e75 2.9.10 2018-04-25 20:00:36 +10:00
willyb321
b62abef618 2.9.9 2018-04-25 19:55:05 +10:00
willyb321
e0c0778d82 Lowercase in import 2018-04-25 18:45:24 +10:00
willyb321
b4a82ae7c2 fix crash with not having modifications key 2018-04-25 18:00:01 +10:00
willyb321
fc73102b30 Merge remote-tracking branch 'origin/develop' into develop 2018-04-25 15:07:23 +10:00
willyb321
b14e7473f3 add guardian powerplant, gauss cannon and also plasma charger 2018-04-25 15:07:13 +10:00
willyb321
f4cc9fc722 fix typo 2018-04-25 14:20:45 +10:00
William
8d19ef7783 Merge pull request #256 from ExitCode/develop
Fixed bp preset highlight when specials alter on the same values
2018-04-24 14:49:39 +10:00
willyb321
32be186ec5 remove unused var 2018-04-23 18:15:44 +10:00
willyb321
abb0c7f90d remove debug logs 2018-04-23 18:15:24 +10:00
willyb321
71ddbdfe75 add specials tooltip 2018-04-23 14:51:45 +10:00
ExitCode
38463ad9a6 Fixed bp preset highlight when specials alter on the same values 2018-04-23 01:16:13 +02:00
willyb321
0870b90443 Merge branch 'release/2.9.8' 2018-04-23 07:38:34 +10:00
willyb321
70375f94c8 bump 2018-04-23 07:38:24 +10:00
William
2d4336116a Merge pull request #254 from ExitCode/develop
Value highlights for bp presets
2018-04-23 07:36:12 +10:00
willyb321
f52880765e fix legacy mods showing up 2018-04-23 07:34:52 +10:00
ExitCode
dbfe68decb Implemented active selected blueprint value highlight; updated look and feel 2018-04-22 05:36:19 +02:00
ExitCode
659f337de9 Added a spacer between the mod values 2018-04-22 05:34:25 +02:00
ExitCode
1d36d41da1 Tuned visibility of components in mod menu 2018-04-22 05:33:47 +02:00
ExitCode
0f90efaa54 Implemented a func to determine the current percentage level of a mod 2018-04-22 05:31:16 +02:00
willyb321
48ccab152b Merge branch 'release/2.9.7' 2018-04-22 12:27:20 +10:00
willyb321
4442930a82 bump 2018-04-22 12:26:06 +10:00
William
912a775088 Merge pull request #253 from ExitCode/develop
blue print workflow (easier editing of modules)
2018-04-22 12:08:01 +10:00
ExitCode
24a229d818 Implemented workflow on bp assignment; applied some styles 2018-04-22 03:43:57 +02:00
ExitCode
66afb61494 added new style for inline menu buttons 2018-04-22 03:41:21 +02:00
William
e7511cc05b Merge pull request #252 from ExitCode/develop
(optional) changed roll presets to 0-50-100
2018-04-22 09:09:37 +10:00
ExitCode
926f19a936 added default init 100% on bp selection as the new system makes it obvious to reach 100% 2018-04-22 00:59:44 +02:00
ExitCode
9e6f86b963 (optional) changed roll presets to 0-50-100 2018-04-22 00:28:04 +02:00
William
059de43a9a Merge pull request #251 from ExitCode/develop
reused random functionality as it worked better on percentage rolls between min max
2018-04-22 08:10:30 +10:00
ExitCode
e246b737b2 reused random functionality as it worked better on percentage rolls between min/max 2018-04-21 23:53:02 +02:00
willyb321
f3bc900f16 Merge branch 'release/2.9.6' 2018-04-22 07:00:47 +10:00
willyb321
38842417b0 bump 2018-04-22 06:59:08 +10:00
willyb321
82d485a98e fix preset rolls locked to low value 2018-04-22 06:49:43 +10:00
willyb321
fac71feea7 Lowercase armour check
Fixes #231
2018-04-21 12:56:51 +10:00
willyb321
ec148847a9 Merge branch 'develop' 2018-04-21 11:39:19 +10:00
willyb321
445c63878b add guard for tooltips crashing the import 2018-04-21 11:39:02 +10:00
willyb321
0474af912a Merge branch 'release/2.9.5' 2018-04-21 11:05:29 +10:00
willyb321
7df5953824 bump 2018-04-21 11:05:19 +10:00
willyb321
e391b563fb change preset rolls to 100%, 75%, 50% and random 2018-04-21 11:03:23 +10:00
willyb321
53f62f96d0 Merge branch 'master' into develop 2018-04-21 07:17:11 +10:00
willyb321
18745979a0 update about page 2018-04-20 19:20:47 +10:00
willyb321
c1966a38ff Merge branch 'master' into develop 2018-04-20 12:49:10 +10:00
willyb321
d86973f3b1 specify appVersion for bugsnag 2018-04-20 12:48:46 +10:00
willyb321
56b8d19649 Merge branch 'release/2.9.4' 2018-04-19 07:17:21 +10:00
willyb321
7cb037e0bc bump 2018-04-19 07:17:06 +10:00
willyb321
56e1b3f9e9 Revert "prep for guardian"
This reverts commit 7e67bd80dd.
2018-04-19 07:14:24 +10:00
willyb321
c792323a8a misc bits 2018-04-19 07:14:13 +10:00
willyb321
4d2865de13 fix resistance blueprint values 2018-04-19 07:14:02 +10:00
willyb321
0d360bc367 bugsnag update 2018-04-19 07:13:20 +10:00
willyb321
91c9b46b91 clean up webpack configs 2018-04-19 06:44:02 +10:00
willyb321
7e67bd80dd prep for guardian 2018-04-19 06:26:53 +10:00
willyb321
2f775ea09b Merge branch 'release/2.9.3' 2018-04-18 18:04:42 +10:00
willyb321
0603c55089 bump 2018-04-18 18:04:28 +10:00
willyb321
41f25a44e9 Merge branch 'master' into develop 2018-04-18 10:33:17 +10:00
willyb321
35440b7273 allow specials in internal and standard slots 2018-04-18 10:33:11 +10:00
willyb321
7d68b91018 Merge branch 'release/2.9.2' 2018-04-17 17:33:45 +10:00
willyb321
deaa61b848 bump version 2018-04-17 17:32:56 +10:00
willyb321
2d00cbc41b fix more values (hopefully done)
also, Fixes #247 by changing passengers to pax
2018-04-17 17:31:11 +10:00
willyb321
57c1e83c67 apply new experimentals structure 2018-04-17 15:01:09 +10:00
William
b7079dbd4e Merge pull request #246 from pnellesen/feature/bugfix_245
Fix for issue 245
2018-04-17 12:38:21 +10:00
willyb321
be642a5373 Merge branch 'master' into develop 2018-04-17 07:59:42 +10:00
willyb321
ffc691c1a2 Merge branch 'release/2.9.1' 2018-04-17 07:57:44 +10:00
willyb321
6122d99369 version bump 2018-04-17 07:57:13 +10:00
willyb321
b73a8bcdab hopefully fix some values and crashes
resistances are buggered on shields and armour by the looks of things
2018-04-17 07:55:17 +10:00
willyb321
dd624537b7 fix hardpoints / internals having the wrong mod applied 2018-04-17 07:50:57 +10:00
willyb321
50a67f73fd get rid of some redundant code 2018-04-17 07:50:13 +10:00
willyb321
2262a980d4 Merge remote-tracking branch 'origin/develop' into develop 2018-04-17 07:46:35 +10:00
willyb321
84964ceb5f add source maps to assist with debugging.
also upload them to bugsnag
2018-04-17 07:46:26 +10:00
William Blythe
c4bdb7a66e Merge remote-tracking branch 'origin/master' into develop 2018-04-16 13:11:44 +10:00
Pat
21309e129f Fix for issue 245 - check that the passenger property is defined for a slot before calculating total passenger capacity 2018-04-14 08:50:48 -05:00
willyb321
928e02c718 Merge branch 'release/2.9.0' 2018-04-14 16:53:08 +10:00
willyb321
7f494dd200 bump 2018-04-14 16:52:05 +10:00
William
f38e743e59 Merge pull request #244 from EDCD/feature/beyond-coriolis
Engineer mods.
2018-04-14 15:14:13 +10:00
Willyb321
58b55eb3da Engineers. Are. Back. Probably. 2018-04-14 14:50:37 +10:00
willyb321
8375ad95b3 Merge branch 'master' into feature/beyond-coriolis 2018-04-14 10:16:20 +10:00
willyb321
506d027a2d Merge branch 'develop' 2018-04-14 09:52:03 +10:00
willyb321
fbeb6237cf fix webpack 2018-04-14 09:51:46 +10:00
willyb321
bd95e2c5a5 Merge branch 'master' into develop 2018-04-14 09:48:03 +10:00
willyb321
f3276e557a Merge branch 'release/2.5.3' 2018-04-14 09:47:08 +10:00
willyb321
8b813e0e7f bump 2018-04-14 09:46:41 +10:00
willyb321
d660d2959f more roles fixing
use a smaller shield, add boosters, d rate some internals
2018-04-14 09:44:29 +10:00
willyb321
014ebda7d2 start fixing suggested roles. Thanks to rinzler + others 2018-04-14 08:06:44 +10:00
William
607398d364 Merge pull request #241 from Blackth0rn/fix/#240_incorrect_passenger_counts
Fix/#240 incorrect passenger counts
2018-04-13 16:20:52 +10:00
willyb321
97d141ce2b update lockfile version 2018-04-13 16:17:59 +10:00
willyb321
9f492db9c6 source map 2018-04-13 16:16:42 +10:00
willyb321
6ed82b366c update bugsnag 2018-04-13 16:16:01 +10:00
willyb321
a7ca037f48 loadout event prep 2018-04-13 14:38:22 +10:00
willyb321
6e21d0e74a Work on engineering import 2018-04-13 13:39:33 +10:00
willyb321
e6c75bf2af Merge branch 'develop' into feature/beyond-coriolis 2018-04-13 09:16:23 +10:00
willyb321
270c2f386e Merge branch 'release/2.5.2' 2018-04-13 07:56:38 +10:00
willyb321
83f7880c58 bump version 2018-04-13 07:51:07 +10:00
William
51d29aee38 Merge pull request #235 from pnellesen/feature/fueltank_column
Added fuel tank column to Core Module Classes on Shipyard
2018-04-13 07:38:07 +10:00
William
c7754c0365 Merge pull request #234 from joelgarboden/feature/remote_testing
Disable 'localhost' host header requirement
2018-04-13 07:34:59 +10:00
Greg Matthews
94037cea38 Fix #240: Added passenger count to individual ship page 2018-04-11 21:09:57 -07:00
Greg Matthews
5c1a9d9eea Fix #240: There is no size 7 passenger cabin so use the new findMaxInternal function to get the biggest one that fits in the slot 2018-04-11 21:07:00 -07:00
Greg Matthews
3835c73ffd Add findMaxInternal function to find the biggest module of a given group, rating, slotsize 2018-04-11 21:06:07 -07:00
Pat
b8c1effecb Update for issue #183 - use first letter of bulkhead instead of "Max Rating" value 2018-04-09 23:10:28 -05:00
Pat
dd07241dd9 Added fuel tank column to Core Module Classes on Shipyard 2018-04-07 18:57:54 -05:00
Joel Garboden
5ad828a613 Disable 'localhost' limit
Disale host header so webpack allows more than localhost access
2018-04-07 22:34:50 +00:00
Unknown
9856df5527 Portiguese 2018-03-22 22:00:19 +00:00
Unknown
7b249900ec Portuguese 2018-03-22 21:59:50 +00:00
Unknown
e3b9267c3f Merge remote-tracking branch 'origin/feature/beyond-coriolis' into develop 2018-03-22 21:59:37 +00:00
willyb321
a996b8135a inital engineerless import working 2018-03-23 08:51:48 +11:00
willyb321
5e1237390b Initial work on 3.0 imports.
Decided to split journal imports to a separate file.
2018-03-22 16:22:59 +11:00
Willyb321
83571b4bef add .editorconfig
Makes contributing and not messing up the codestyle easier
2018-03-22 12:07:19 +11:00
Willyb321
c2a0dad9a8 Export textarea select
Fixes #146
2018-03-22 12:06:36 +11:00
Unknown
f6f057689c Merge branch 'feature/chieftain' into develop 2018-01-27 11:47:17 +00:00
willyb321
4378f0020e add chieftain to map
also remove console logs that i forgot to remove
2018-01-27 10:55:37 +11:00
Unknown
2e9e7c4fc6 Merge branch 'release/2.5.1' 2018-01-24 09:18:58 +00:00
Unknown
b7b8ee5580 Merge branch 'release/2.5.1' into develop 2018-01-24 09:18:36 +00:00
Unknown
c630dbbed0 Version Bump 2018-01-24 09:18:21 +00:00
Unknown
fa6703a3b8 Merge remote-tracking branch 'Blackth0rn/feature/#186-passenger-totals' into develop 2018-01-24 08:45:54 +00:00
Unknown
bdcb64c9d1 Merge remote-tracking branch 'willyb321/feature/fix-engineer-values' into develop 2018-01-24 08:45:48 +00:00
Unknown
a1afc869a7 Merge remote-tracking branch 'Ghnuberath/feature/ax-modules' into develop 2018-01-24 08:45:44 +00:00
Unknown
d8ce26c7cf Merge remote-tracking branch 'origin/master' into develop 2018-01-24 08:44:45 +00:00
Sean McIntyre
95c474dc05 Recategorizing experimental modules into an "experimental" category 2018-01-22 16:48:10 -05:00
Sean McIntyre
56ca73b4ad Forgot Remote Release Flak Launcher 2018-01-22 16:43:29 -05:00
Sean McIntyre
d82cd6a89e Removing unnecessary change 2018-01-22 15:21:59 -05:00
Sean McIntyre
bf20d32364 Adding AX modules 2018-01-22 15:20:41 -05:00
willyb321
e968e62fca Fix clip size (hopefully) 2018-01-16 08:14:21 +11:00
Unknown
b9c9ca9fa1 Merge branch 'release/2.5.0' 2018-01-11 21:59:31 +00:00
Unknown
6e965e2e98 Version Bump 2018-01-11 21:59:20 +00:00
Unknown
7d569f9036 Merge branch 'feature/fix-engineers' into develop 2018-01-11 21:51:35 +00:00
Unknown
df5a77199d Merge branch 'develop' 2018-01-11 21:50:28 +00:00
Greg Matthews
3c8dfebfdc Issue #186. Added passenger totals to shipyard screen 2018-01-10 21:20:27 -08:00
Unknown
3cc422596f Something something consistant naming 2017-12-24 09:47:49 +00:00
Unknown
4ed167de22 Add T10 2017-12-24 09:47:33 +00:00
Unknown
b420647501 Something something consistant naming 2017-12-24 09:46:49 +00:00
Unknown
0c318b5e68 Add T10 2017-12-23 12:03:21 +00:00
willyb321
1c627297b8 shorten the terrible _addModifications call 2017-12-19 16:39:42 +11:00
willyb321
f41e2d0552 fix indent and hopefully fix ammo clip
dont know if it was broken, if someone has it DM it to me thanks
2017-12-19 16:28:05 +11:00
willyb321
1a1d539c60 fix engagement range slider and armour integrity
hopefully
2017-12-19 16:18:20 +11:00
willyb321
ba2e46f88f fix rate of fire
probably
2017-12-19 14:49:04 +11:00
willyb321
c2f1fa81af remove console logs 2017-12-17 18:48:24 +11:00
willyb321
752e03fa0f possible armour fix
need more data to confirm however
2017-12-17 18:24:34 +11:00
willyb321
1da69664d7 shields should be accurate now
dont now what the issue with hull is
if you see it let me know
2017-12-17 14:49:51 +11:00
willyb321
9aa986a133 thanks webstorm 2017-12-17 12:48:13 +11:00
willyb321
bdbfb28c4a missed a spot 2017-12-17 12:47:02 +11:00
willyb321
162156bb2b fix random weird indents 2017-12-17 12:46:18 +11:00
willyb321
64c5d542e9 more work on mods 2017-12-17 12:27:04 +11:00
willyb321
31dc789f6e initial (re)implementation of engineer mods
still some kinds to work out.
2017-12-17 11:16:50 +11:00
Unknown
b0e2cfd7db Merge branch 'feature/#112_ship_table_highlights' into develop 2017-12-04 09:41:54 +00:00
Greg Matthews
54ddb0d014 Rename sortValue and tmpSortValue to be more meaningful 2017-11-11 13:29:59 -08:00
Greg Matthews
e19688e96f Add alternate row highlighting for sorted value 2017-11-11 13:25:17 -08:00
Greg Matthews
4f53d75999 Fix whitespace in html 2017-11-11 13:24:57 -08:00
Unknown
7277460060 Merge branch 'release/2.4.2' 2017-11-09 17:23:46 +00:00
Unknown
93c4f6f3c0 Merge branch 'release/2.4.2' into develop 2017-11-09 17:23:32 +00:00
Unknown
c6919a7518 Bumps and changelog 2017-11-09 17:23:15 +00:00
Unknown
ca428e67dc Package Lock and D3 2017-11-09 17:18:55 +00:00
Unknown
c6726cf020 Merge remote-tracking branch 'BenJuan26/feature/fix-tests' into develop 2017-11-09 17:15:06 +00:00
Unknown
56ae1378da Merge branch 'feature/slot-commas' into develop 2017-11-09 17:14:49 +00:00
Unknown
e982ab1a3b Merge branch 'feature/ship-purchase-requirements' into develop 2017-11-09 17:11:32 +00:00
Unknown
0d6aa87e89 Update coriolis to use directory basis 2017-11-09 17:04:23 +00:00
Joshua Campbell
94d06e4025 Show ship purchase requirements on outfitting page 2017-11-07 01:46:58 -05:00
Benjamin Schubert
cee5b297ac Add create-react-class 2017-11-02 12:02:15 -04:00
Benjamin Schubert
c549213ce0 Switch to babel-preset-env 2017-11-02 11:44:53 -04:00
Benjamin Schubert
f3f9112767 Change jest version 2017-11-02 11:25:32 -04:00
Benjamin Schubert
fb325ea3e2 Add commas to slot cost 2017-11-01 15:56:51 -04:00
Unknown
3773f6f7ec Merge branch 'release/2.4.1' into develop 2017-10-31 16:52:51 +00:00
Unknown
b90ab6fe48 Merge branch 'release/2.4.1' 2017-10-31 16:52:34 +00:00
Unknown
cc2e56dc8a bump 2017-10-31 16:52:13 +00:00
William
ae65af7bbf prevent crashing when no modifications are in the data (#160) 2017-10-12 08:33:36 +01:00
William
c8fb513cd1 Fix repair limpet grouping (#161)
* Group repair limpets with the rest

* editor decided to add a random whitespace

* also it was set to tabs
2017-10-12 08:33:22 +01:00
Unknown
7fc3855af4 Merge branch 'release/2.4.0' into develop 2017-10-10 16:47:21 +01:00
Unknown
bcebb26d4a Merge branch 'release/2.4.0' 2017-10-10 16:46:57 +01:00
Unknown
1f23e4cfcc Version Bump 2017-10-10 16:46:34 +01:00
William
65998778fe fix zbuf error (#154)
Add an optional extended description…
2017-10-10 11:59:44 +01:00
Unknown
e53c04a07f Repair Limpet 2017-10-03 20:25:01 +01:00
Unknown
e5a8e106c1 Merge branch 'feature/languages' into develop 2017-09-01 20:56:34 +01:00
Unknown
11d1d80a53 Other languages JSON import 2017-09-01 20:55:11 +01:00
Unknown
c8a3d86a45 JSON Import 2017-09-01 20:52:45 +01:00
Unknown
4484ca226a Polish , Spanish, Italian JSON 2017-09-01 19:50:52 +01:00
Unknown
496c9ba35c Full bugsnag 2017-08-30 20:29:46 +01:00
Unknown
dae2fc9192 Bugsnag 2017-08-30 20:29:29 +01:00
Unknown
07b00e6230 Full bugsnag 2017-08-30 20:28:59 +01:00
Unknown
36526f0824 Merge branch 'feature/bugsnag' into develop 2017-08-30 19:47:52 +01:00
Unknown
f4691939ba Bugsnag 2017-08-30 19:46:36 +01:00
Unknown
534f735b63 Translation Advice 2017-08-30 19:33:42 +01:00
Unknown
bc2c8406a2 Russian JSON Generated 2017-08-30 19:30:27 +01:00
Unknown
032c44f39a German JSON Generated 2017-08-30 19:15:17 +01:00
Unknown
2b43c8e91a French JSON Generated 2017-08-30 19:04:57 +01:00
Unknown
e91b3df31a English JSON Generated 2017-08-30 19:04:49 +01:00
Unknown
3e0597023a Merge remote-tracking branch 'origin/master' 2017-08-29 20:06:30 +01:00
Unknown
1dd2edf742 Merge branch 'master' into develop 2017-08-29 20:05:51 +01:00
strideynet
4ee92b1f3e Update Tracking 2017-08-22 20:59:39 +01:00
78 changed files with 18290 additions and 4580 deletions

View File

@@ -1,3 +1,3 @@
{ {
"presets": ["es2015", "react", "stage-0"] "presets": ["env", "react", "stage-0"]
} }

21
.editorconfig Normal file
View File

@@ -0,0 +1,21 @@
root = true
[*]
# change these settings to your own preference
indent_style = space
indent_size = 2
# we recommend you to keep these unchanged
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[{package,bower}.json]
indent_style = space
indent_size = 2

View File

@@ -5,7 +5,7 @@
"jsx": true, "jsx": true,
"classes": true, "classes": true,
"modules": true "modules": true
}, }
}, },
"env": { "env": {
"browser": true, "browser": true,
@@ -33,7 +33,6 @@
"ClassDeclaration": true "ClassDeclaration": true
} }
}], }],
"no-console": 2,
"brace-style": [2, "1tbs", { "allowSingleLine": true }], "brace-style": [2, "1tbs", { "allowSingleLine": true }],
"comma-style": [2, "last"], "comma-style": [2, "last"],
"indent": [2, 2, { "SwitchCase": 1, "VariableDeclarator": 2 }], "indent": [2, 2, { "SwitchCase": 1, "VariableDeclarator": 2 }],

1
.gitignore vendored
View File

@@ -7,3 +7,4 @@ nginx.pid
/bin /bin
env env
*.swp *.swp
.project

View File

@@ -8,7 +8,8 @@ cache:
directories: directories:
- node_modules - node_modules
before_script: before_install:
- git clone https://github.com/EDCD/coriolis-data.git ../coriolis-data
script: script:
- npm run lint - npm run lint

View File

@@ -1,3 +1,29 @@
#2.5.1
* Passenger count on main page
* AX Modules
* Engineering fixes
* Use coriolis-data 2.5.1
#2.5.0
* willyb321 and myself have conquered engineering. Mainly him though...
* Use coriolis-data 2.5.0
#2.4.2
Lots of kind people have helped out for this release! Check out the PR history!
* Uses coriolis-data update:
* Fixes issues with repair limpets
* Adds requirement data
* Adds requirements panel
* Adds comma formatting to tooltip numbers
#2.4.1
* Small patches and changes
#2.4.0
* Changed compression library to Pako
* Use coriolis-data 2.4.0
* Repair Limpets added
#2.3.7 #2.3.7
* Fixed Travis test issues * Fixed Travis test issues
* Bumped NodeJS version to provide better compatability and support * Bumped NodeJS version to provide better compatability and support

View File

@@ -10,6 +10,11 @@ Coriolis was created using assets and imagery from Elite: Dangerous, with the pe
Please [submit issues](https://github.com/EDCD/coriolis/issues), or better yet [pull requests](https://github.com/EDCD/coriolis/pulls) for any corrections or additions to the database or the code. Please [submit issues](https://github.com/EDCD/coriolis/issues), or better yet [pull requests](https://github.com/EDCD/coriolis/pulls) for any corrections or additions to the database or the code.
### Translations
Please use the OneSky translation site to suggest new translations: http://edcd-coriolis.oneskyapp.com
These will be merged regularly by the project manager.
### Feature Requests, Suggestions & Bugs ### Feature Requests, Suggestions & Bugs
Chat to us on [Discord](https://discord.gg/0uwCh6R62aPRjk9w)! Chat to us on [Discord](https://discord.gg/0uwCh6R62aPRjk9w)!

878
d3.js vendored
View File

@@ -1340,6 +1340,12 @@ Selection.prototype = selection.prototype = {
dispatch: selection_dispatch dispatch: selection_dispatch
}; };
var select = function(selector) {
return typeof selector === "string"
? new Selection([[document.querySelector(selector)]], [document.documentElement])
: new Selection([[selector]], root);
};
var define = function(constructor, factory, prototype) { var define = function(constructor, factory, prototype) {
constructor.prototype = factory.prototype = prototype; constructor.prototype = factory.prototype = prototype;
prototype.constructor = constructor; prototype.constructor = constructor;
@@ -6947,861 +6953,7 @@ var cool = cubehelixLong(cubehelix(260, 0.75, 0.35), cubehelix(80, 1.50, 0.8));
var rainbow = cubehelix(); var rainbow = cubehelix();
var xhtml$1 = "http://www.w3.org/1999/xhtml";
var namespaces$1 = {
svg: "http://www.w3.org/2000/svg",
xhtml: xhtml$1,
xlink: "http://www.w3.org/1999/xlink",
xml: "http://www.w3.org/XML/1998/namespace",
xmlns: "http://www.w3.org/2000/xmlns/"
};
var namespace$1 = function(name) {
var prefix = name += "", i = prefix.indexOf(":");
if (i >= 0 && (prefix = name.slice(0, i)) !== "xmlns") name = name.slice(i + 1);
return namespaces$1.hasOwnProperty(prefix) ? {space: namespaces$1[prefix], local: name} : name;
};
function creatorInherit$1(name) {
return function() {
var document = this.ownerDocument,
uri = this.namespaceURI;
return uri === xhtml$1 && document.documentElement.namespaceURI === xhtml$1
? document.createElement(name)
: document.createElementNS(uri, name);
};
}
function creatorFixed$1(fullname) {
return function() {
return this.ownerDocument.createElementNS(fullname.space, fullname.local);
};
}
var creator$1 = function(name) {
var fullname = namespace$1(name);
return (fullname.local
? creatorFixed$1
: creatorInherit$1)(fullname);
};
var matcher$2 = function(selector) {
return function() {
return this.matches(selector);
};
};
if (typeof document !== "undefined") {
var element$2 = document.documentElement;
if (!element$2.matches) {
var vendorMatches$1 = element$2.webkitMatchesSelector
|| element$2.msMatchesSelector
|| element$2.mozMatchesSelector
|| element$2.oMatchesSelector;
matcher$2 = function(selector) {
return function() {
return vendorMatches$1.call(this, selector);
};
};
}
}
var matcher$3 = matcher$2;
var filterEvents$1 = {};
var event$1 = null;
if (typeof document !== "undefined") {
var element$3 = document.documentElement;
if (!("onmouseenter" in element$3)) {
filterEvents$1 = {mouseenter: "mouseover", mouseleave: "mouseout"};
}
}
function filterContextListener$1(listener, index, group) {
listener = contextListener$1(listener, index, group);
return function(event) {
var related = event.relatedTarget;
if (!related || (related !== this && !(related.compareDocumentPosition(this) & 8))) {
listener.call(this, event);
}
};
}
function contextListener$1(listener, index, group) {
return function(event1) {
var event0 = event$1; // Events can be reentrant (e.g., focus).
event$1 = event1;
try {
listener.call(this, this.__data__, index, group);
} finally {
event$1 = event0;
}
};
}
function parseTypenames$2(typenames) {
return typenames.trim().split(/^|\s+/).map(function(t) {
var name = "", i = t.indexOf(".");
if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);
return {type: t, name: name};
});
}
function onRemove$1(typename) {
return function() {
var on = this.__on;
if (!on) return;
for (var j = 0, i = -1, m = on.length, o; j < m; ++j) {
if (o = on[j], (!typename.type || o.type === typename.type) && o.name === typename.name) {
this.removeEventListener(o.type, o.listener, o.capture);
} else {
on[++i] = o;
}
}
if (++i) on.length = i;
else delete this.__on;
};
}
function onAdd$1(typename, value, capture) {
var wrap = filterEvents$1.hasOwnProperty(typename.type) ? filterContextListener$1 : contextListener$1;
return function(d, i, group) {
var on = this.__on, o, listener = wrap(value, i, group);
if (on) for (var j = 0, m = on.length; j < m; ++j) {
if ((o = on[j]).type === typename.type && o.name === typename.name) {
this.removeEventListener(o.type, o.listener, o.capture);
this.addEventListener(o.type, o.listener = listener, o.capture = capture);
o.value = value;
return;
}
}
this.addEventListener(typename.type, listener, capture);
o = {type: typename.type, name: typename.name, value: value, listener: listener, capture: capture};
if (!on) this.__on = [o];
else on.push(o);
};
}
var selection_on$1 = function(typename, value, capture) {
var typenames = parseTypenames$2(typename + ""), i, n = typenames.length, t;
if (arguments.length < 2) {
var on = this.node().__on;
if (on) for (var j = 0, m = on.length, o; j < m; ++j) {
for (i = 0, o = on[j]; i < n; ++i) {
if ((t = typenames[i]).type === o.type && t.name === o.name) {
return o.value;
}
}
}
return;
}
on = value ? onAdd$1 : onRemove$1;
if (capture == null) capture = false;
for (i = 0; i < n; ++i) this.each(on(typenames[i], value, capture));
return this;
};
function none$1() {}
var selector$1 = function(selector) {
return selector == null ? none$1 : function() {
return this.querySelector(selector);
};
};
var selection_select$1 = function(select) {
if (typeof select !== "function") select = selector$1(select);
for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {
if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {
if ("__data__" in node) subnode.__data__ = node.__data__;
subgroup[i] = subnode;
}
}
}
return new Selection$2(subgroups, this._parents);
};
function empty$2() {
return [];
}
var selectorAll$1 = function(selector) {
return selector == null ? empty$2 : function() {
return this.querySelectorAll(selector);
};
};
var selection_selectAll$1 = function(select) {
if (typeof select !== "function") select = selectorAll$1(select);
for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {
for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
if (node = group[i]) {
subgroups.push(select.call(node, node.__data__, i, group));
parents.push(node);
}
}
}
return new Selection$2(subgroups, parents);
};
var selection_filter$1 = function(match) {
if (typeof match !== "function") match = matcher$3(match);
for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {
if ((node = group[i]) && match.call(node, node.__data__, i, group)) {
subgroup.push(node);
}
}
}
return new Selection$2(subgroups, this._parents);
};
var sparse$1 = function(update) {
return new Array(update.length);
};
var selection_enter$1 = function() {
return new Selection$2(this._enter || this._groups.map(sparse$1), this._parents);
};
function EnterNode$1(parent, datum) {
this.ownerDocument = parent.ownerDocument;
this.namespaceURI = parent.namespaceURI;
this._next = null;
this._parent = parent;
this.__data__ = datum;
}
EnterNode$1.prototype = {
constructor: EnterNode$1,
appendChild: function(child) { return this._parent.insertBefore(child, this._next); },
insertBefore: function(child, next) { return this._parent.insertBefore(child, next); },
querySelector: function(selector) { return this._parent.querySelector(selector); },
querySelectorAll: function(selector) { return this._parent.querySelectorAll(selector); }
};
var constant$10 = function(x) { var constant$10 = function(x) {
return function() {
return x;
};
};
var keyPrefix$2 = "$"; // Protect against keys like “__proto__”.
function bindIndex$1(parent, group, enter, update, exit, data) {
var i = 0,
node,
groupLength = group.length,
dataLength = data.length;
// Put any non-null nodes that fit into update.
// Put any null nodes into enter.
// Put any remaining data into enter.
for (; i < dataLength; ++i) {
if (node = group[i]) {
node.__data__ = data[i];
update[i] = node;
} else {
enter[i] = new EnterNode$1(parent, data[i]);
}
}
// Put any non-null nodes that dont fit into exit.
for (; i < groupLength; ++i) {
if (node = group[i]) {
exit[i] = node;
}
}
}
function bindKey$1(parent, group, enter, update, exit, data, key) {
var i,
node,
nodeByKeyValue = {},
groupLength = group.length,
dataLength = data.length,
keyValues = new Array(groupLength),
keyValue;
// Compute the key for each node.
// If multiple nodes have the same key, the duplicates are added to exit.
for (i = 0; i < groupLength; ++i) {
if (node = group[i]) {
keyValues[i] = keyValue = keyPrefix$2 + key.call(node, node.__data__, i, group);
if (keyValue in nodeByKeyValue) {
exit[i] = node;
} else {
nodeByKeyValue[keyValue] = node;
}
}
}
// Compute the key for each datum.
// If there a node associated with this key, join and add it to update.
// If there is not (or the key is a duplicate), add it to enter.
for (i = 0; i < dataLength; ++i) {
keyValue = keyPrefix$2 + key.call(parent, data[i], i, data);
if (node = nodeByKeyValue[keyValue]) {
update[i] = node;
node.__data__ = data[i];
nodeByKeyValue[keyValue] = null;
} else {
enter[i] = new EnterNode$1(parent, data[i]);
}
}
// Add any remaining nodes that were not bound to data to exit.
for (i = 0; i < groupLength; ++i) {
if ((node = group[i]) && (nodeByKeyValue[keyValues[i]] === node)) {
exit[i] = node;
}
}
}
var selection_data$1 = function(value, key) {
if (!value) {
data = new Array(this.size()), j = -1;
this.each(function(d) { data[++j] = d; });
return data;
}
var bind = key ? bindKey$1 : bindIndex$1,
parents = this._parents,
groups = this._groups;
if (typeof value !== "function") value = constant$10(value);
for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) {
var parent = parents[j],
group = groups[j],
groupLength = group.length,
data = value.call(parent, parent && parent.__data__, j, parents),
dataLength = data.length,
enterGroup = enter[j] = new Array(dataLength),
updateGroup = update[j] = new Array(dataLength),
exitGroup = exit[j] = new Array(groupLength);
bind(parent, group, enterGroup, updateGroup, exitGroup, data, key);
// Now connect the enter nodes to their following update node, such that
// appendChild can insert the materialized enter node before this node,
// rather than at the end of the parent node.
for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) {
if (previous = enterGroup[i0]) {
if (i0 >= i1) i1 = i0 + 1;
while (!(next = updateGroup[i1]) && ++i1 < dataLength);
previous._next = next || null;
}
}
}
update = new Selection$2(update, parents);
update._enter = enter;
update._exit = exit;
return update;
};
var selection_exit$1 = function() {
return new Selection$2(this._exit || this._groups.map(sparse$1), this._parents);
};
var selection_merge$1 = function(selection) {
for (var groups0 = this._groups, groups1 = selection._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) {
for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {
if (node = group0[i] || group1[i]) {
merge[i] = node;
}
}
}
for (; j < m0; ++j) {
merges[j] = groups0[j];
}
return new Selection$2(merges, this._parents);
};
var selection_order$1 = function() {
for (var groups = this._groups, j = -1, m = groups.length; ++j < m;) {
for (var group = groups[j], i = group.length - 1, next = group[i], node; --i >= 0;) {
if (node = group[i]) {
if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next);
next = node;
}
}
}
return this;
};
var selection_sort$1 = function(compare) {
if (!compare) compare = ascending$2;
function compareNode(a, b) {
return a && b ? compare(a.__data__, b.__data__) : !a - !b;
}
for (var groups = this._groups, m = groups.length, sortgroups = new Array(m), j = 0; j < m; ++j) {
for (var group = groups[j], n = group.length, sortgroup = sortgroups[j] = new Array(n), node, i = 0; i < n; ++i) {
if (node = group[i]) {
sortgroup[i] = node;
}
}
sortgroup.sort(compareNode);
}
return new Selection$2(sortgroups, this._parents).order();
};
function ascending$2(a, b) {
return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
}
var selection_call$1 = function() {
var callback = arguments[0];
arguments[0] = this;
callback.apply(null, arguments);
return this;
};
var selection_nodes$1 = function() {
var nodes = new Array(this.size()), i = -1;
this.each(function() { nodes[++i] = this; });
return nodes;
};
var selection_node$1 = function() {
for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {
for (var group = groups[j], i = 0, n = group.length; i < n; ++i) {
var node = group[i];
if (node) return node;
}
}
return null;
};
var selection_size$1 = function() {
var size = 0;
this.each(function() { ++size; });
return size;
};
var selection_empty$1 = function() {
return !this.node();
};
var selection_each$1 = function(callback) {
for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {
for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) {
if (node = group[i]) callback.call(node, node.__data__, i, group);
}
}
return this;
};
function attrRemove$2(name) {
return function() {
this.removeAttribute(name);
};
}
function attrRemoveNS$2(fullname) {
return function() {
this.removeAttributeNS(fullname.space, fullname.local);
};
}
function attrConstant$2(name, value) {
return function() {
this.setAttribute(name, value);
};
}
function attrConstantNS$2(fullname, value) {
return function() {
this.setAttributeNS(fullname.space, fullname.local, value);
};
}
function attrFunction$2(name, value) {
return function() {
var v = value.apply(this, arguments);
if (v == null) this.removeAttribute(name);
else this.setAttribute(name, v);
};
}
function attrFunctionNS$2(fullname, value) {
return function() {
var v = value.apply(this, arguments);
if (v == null) this.removeAttributeNS(fullname.space, fullname.local);
else this.setAttributeNS(fullname.space, fullname.local, v);
};
}
var selection_attr$1 = function(name, value) {
var fullname = namespace$1(name);
if (arguments.length < 2) {
var node = this.node();
return fullname.local
? node.getAttributeNS(fullname.space, fullname.local)
: node.getAttribute(fullname);
}
return this.each((value == null
? (fullname.local ? attrRemoveNS$2 : attrRemove$2) : (typeof value === "function"
? (fullname.local ? attrFunctionNS$2 : attrFunction$2)
: (fullname.local ? attrConstantNS$2 : attrConstant$2)))(fullname, value));
};
var defaultView = function(node) {
return (node.ownerDocument && node.ownerDocument.defaultView) // node is a Node
|| (node.document && node) // node is a Window
|| node.defaultView; // node is a Document
};
function styleRemove$2(name) {
return function() {
this.style.removeProperty(name);
};
}
function styleConstant$2(name, value, priority) {
return function() {
this.style.setProperty(name, value, priority);
};
}
function styleFunction$2(name, value, priority) {
return function() {
var v = value.apply(this, arguments);
if (v == null) this.style.removeProperty(name);
else this.style.setProperty(name, v, priority);
};
}
var selection_style$1 = function(name, value, priority) {
var node;
return arguments.length > 1
? this.each((value == null
? styleRemove$2 : typeof value === "function"
? styleFunction$2
: styleConstant$2)(name, value, priority == null ? "" : priority))
: defaultView(node = this.node())
.getComputedStyle(node, null)
.getPropertyValue(name);
};
function propertyRemove$1(name) {
return function() {
delete this[name];
};
}
function propertyConstant$1(name, value) {
return function() {
this[name] = value;
};
}
function propertyFunction$1(name, value) {
return function() {
var v = value.apply(this, arguments);
if (v == null) delete this[name];
else this[name] = v;
};
}
var selection_property$1 = function(name, value) {
return arguments.length > 1
? this.each((value == null
? propertyRemove$1 : typeof value === "function"
? propertyFunction$1
: propertyConstant$1)(name, value))
: this.node()[name];
};
function classArray$1(string) {
return string.trim().split(/^|\s+/);
}
function classList$1(node) {
return node.classList || new ClassList$1(node);
}
function ClassList$1(node) {
this._node = node;
this._names = classArray$1(node.getAttribute("class") || "");
}
ClassList$1.prototype = {
add: function(name) {
var i = this._names.indexOf(name);
if (i < 0) {
this._names.push(name);
this._node.setAttribute("class", this._names.join(" "));
}
},
remove: function(name) {
var i = this._names.indexOf(name);
if (i >= 0) {
this._names.splice(i, 1);
this._node.setAttribute("class", this._names.join(" "));
}
},
contains: function(name) {
return this._names.indexOf(name) >= 0;
}
};
function classedAdd$1(node, names) {
var list = classList$1(node), i = -1, n = names.length;
while (++i < n) list.add(names[i]);
}
function classedRemove$1(node, names) {
var list = classList$1(node), i = -1, n = names.length;
while (++i < n) list.remove(names[i]);
}
function classedTrue$1(names) {
return function() {
classedAdd$1(this, names);
};
}
function classedFalse$1(names) {
return function() {
classedRemove$1(this, names);
};
}
function classedFunction$1(names, value) {
return function() {
(value.apply(this, arguments) ? classedAdd$1 : classedRemove$1)(this, names);
};
}
var selection_classed$1 = function(name, value) {
var names = classArray$1(name + "");
if (arguments.length < 2) {
var list = classList$1(this.node()), i = -1, n = names.length;
while (++i < n) if (!list.contains(names[i])) return false;
return true;
}
return this.each((typeof value === "function"
? classedFunction$1 : value
? classedTrue$1
: classedFalse$1)(names, value));
};
function textRemove$1() {
this.textContent = "";
}
function textConstant$2(value) {
return function() {
this.textContent = value;
};
}
function textFunction$2(value) {
return function() {
var v = value.apply(this, arguments);
this.textContent = v == null ? "" : v;
};
}
var selection_text$1 = function(value) {
return arguments.length
? this.each(value == null
? textRemove$1 : (typeof value === "function"
? textFunction$2
: textConstant$2)(value))
: this.node().textContent;
};
function htmlRemove$1() {
this.innerHTML = "";
}
function htmlConstant$1(value) {
return function() {
this.innerHTML = value;
};
}
function htmlFunction$1(value) {
return function() {
var v = value.apply(this, arguments);
this.innerHTML = v == null ? "" : v;
};
}
var selection_html$1 = function(value) {
return arguments.length
? this.each(value == null
? htmlRemove$1 : (typeof value === "function"
? htmlFunction$1
: htmlConstant$1)(value))
: this.node().innerHTML;
};
function raise$2() {
if (this.nextSibling) this.parentNode.appendChild(this);
}
var selection_raise$1 = function() {
return this.each(raise$2);
};
function lower$1() {
if (this.previousSibling) this.parentNode.insertBefore(this, this.parentNode.firstChild);
}
var selection_lower$1 = function() {
return this.each(lower$1);
};
var selection_append$1 = function(name) {
var create = typeof name === "function" ? name : creator$1(name);
return this.select(function() {
return this.appendChild(create.apply(this, arguments));
});
};
function constantNull$1() {
return null;
}
var selection_insert$1 = function(name, before) {
var create = typeof name === "function" ? name : creator$1(name),
select = before == null ? constantNull$1 : typeof before === "function" ? before : selector$1(before);
return this.select(function() {
return this.insertBefore(create.apply(this, arguments), select.apply(this, arguments) || null);
});
};
function remove$1() {
var parent = this.parentNode;
if (parent) parent.removeChild(this);
}
var selection_remove$1 = function() {
return this.each(remove$1);
};
var selection_datum$1 = function(value) {
return arguments.length
? this.property("__data__", value)
: this.node().__data__;
};
function dispatchEvent$1(node, type, params) {
var window = defaultView(node),
event = window.CustomEvent;
if (event) {
event = new event(type, params);
} else {
event = window.document.createEvent("Event");
if (params) event.initEvent(type, params.bubbles, params.cancelable), event.detail = params.detail;
else event.initEvent(type, false, false);
}
node.dispatchEvent(event);
}
function dispatchConstant$1(type, params) {
return function() {
return dispatchEvent$1(this, type, params);
};
}
function dispatchFunction$1(type, params) {
return function() {
return dispatchEvent$1(this, type, params.apply(this, arguments));
};
}
var selection_dispatch$1 = function(type, params) {
return this.each((typeof params === "function"
? dispatchFunction$1
: dispatchConstant$1)(type, params));
};
var root$2 = [null];
function Selection$2(groups, parents) {
this._groups = groups;
this._parents = parents;
}
function selection$2() {
return new Selection$2([[document.documentElement]], root$2);
}
Selection$2.prototype = selection$2.prototype = {
constructor: Selection$2,
select: selection_select$1,
selectAll: selection_selectAll$1,
filter: selection_filter$1,
data: selection_data$1,
enter: selection_enter$1,
exit: selection_exit$1,
merge: selection_merge$1,
order: selection_order$1,
sort: selection_sort$1,
call: selection_call$1,
nodes: selection_nodes$1,
node: selection_node$1,
size: selection_size$1,
empty: selection_empty$1,
each: selection_each$1,
attr: selection_attr$1,
style: selection_style$1,
property: selection_property$1,
classed: selection_classed$1,
text: selection_text$1,
html: selection_html$1,
raise: selection_raise$1,
lower: selection_lower$1,
append: selection_append$1,
insert: selection_insert$1,
remove: selection_remove$1,
datum: selection_datum$1,
on: selection_on$1,
dispatch: selection_dispatch$1
};
var select$1 = function(selector) {
return typeof selector === "string"
? new Selection$2([[document.querySelector(selector)]], [document.documentElement])
: new Selection$2([[selector]], root$2);
};
var constant$11 = function(x) {
return function constant() { return function constant() {
return x; return x;
}; };
@@ -7850,7 +7002,7 @@ function y$3(p) {
var line = function() { var line = function() {
var x$$1 = x$3, var x$$1 = x$3,
y$$1 = y$3, y$$1 = y$3,
defined = constant$11(true), defined = constant$10(true),
context = null, context = null,
curve = curveLinear, curve = curveLinear,
output = null; output = null;
@@ -7876,15 +7028,15 @@ var line = function() {
} }
line.x = function(_) { line.x = function(_) {
return arguments.length ? (x$$1 = typeof _ === "function" ? _ : constant$11(+_), line) : x$$1; return arguments.length ? (x$$1 = typeof _ === "function" ? _ : constant$10(+_), line) : x$$1;
}; };
line.y = function(_) { line.y = function(_) {
return arguments.length ? (y$$1 = typeof _ === "function" ? _ : constant$11(+_), line) : y$$1; return arguments.length ? (y$$1 = typeof _ === "function" ? _ : constant$10(+_), line) : y$$1;
}; };
line.defined = function(_) { line.defined = function(_) {
return arguments.length ? (defined = typeof _ === "function" ? _ : constant$11(!!_), line) : defined; return arguments.length ? (defined = typeof _ === "function" ? _ : constant$10(!!_), line) : defined;
}; };
line.curve = function(_) { line.curve = function(_) {
@@ -7924,7 +7076,7 @@ function slope2(that, t) {
// According to https://en.wikipedia.org/wiki/Cubic_Hermite_spline#Representations // According to https://en.wikipedia.org/wiki/Cubic_Hermite_spline#Representations
// "you can express cubic Hermite interpolation in terms of cubic Bézier curves // "you can express cubic Hermite interpolation in terms of cubic Bézier curves
// with respect to the four values p0, p0 + m0 / 3, p1 - m1 / 3, p1". // with respect to the four values p0, p0 + m0 / 3, p1 - m1 / 3, p1".
function point$6(that, t0, t1) { function point$5(that, t0, t1) {
var x0 = that._x0, var x0 = that._x0,
y0 = that._y0, y0 = that._y0,
x1 = that._x1, x1 = that._x1,
@@ -7953,7 +7105,7 @@ MonotoneX.prototype = {
lineEnd: function() { lineEnd: function() {
switch (this._point) { switch (this._point) {
case 2: this._context.lineTo(this._x1, this._y1); break; case 2: this._context.lineTo(this._x1, this._y1); break;
case 3: point$6(this, this._t0, slope2(this, this._t0)); break; case 3: point$5(this, this._t0, slope2(this, this._t0)); break;
} }
if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath(); if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
this._line = 1 - this._line; this._line = 1 - this._line;
@@ -7966,8 +7118,8 @@ MonotoneX.prototype = {
switch (this._point) { switch (this._point) {
case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break; case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
case 1: this._point = 2; break; case 1: this._point = 2; break;
case 2: this._point = 3; point$6(this, slope2(this, t1 = slope3(this, x, y)), t1); break; case 2: this._point = 3; point$5(this, slope2(this, t1 = slope3(this, x, y)), t1); break;
default: point$6(this, this._t0, t1 = slope3(this, x, y)); break; default: point$5(this, this._t0, t1 = slope3(this, x, y)); break;
} }
this._x0 = this._x1, this._x1 = x; this._x0 = this._x1, this._x1 = x;
@@ -8153,7 +7305,7 @@ exports.line = line;
exports.scaleBand = band; exports.scaleBand = band;
exports.scaleLinear = linear$2; exports.scaleLinear = linear$2;
exports.scaleOrdinal = ordinal; exports.scaleOrdinal = ordinal;
exports.select = select$1; exports.select = select;
Object.defineProperty(exports, '__esModule', { value: true }); Object.defineProperty(exports, '__esModule', { value: true });

6
d3.min.js vendored

File diff suppressed because one or more lines are too long

1
devServer.js Normal file → Executable file
View File

@@ -5,6 +5,7 @@ var config = require('./webpack.config.dev');
new WebpackDevServer(webpack(config), { new WebpackDevServer(webpack(config), {
publicPath: config.output.publicPath, publicPath: config.output.publicPath,
hot: true, hot: true,
disableHostCheck: true,
headers: { "Access-Control-Allow-Origin": "*" }, headers: { "Access-Control-Allow-Origin": "*" },
historyApiFallback: { historyApiFallback: {
rewrites: [ rewrites: [

12862
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "coriolis_shipyard", "name": "coriolis_shipyard",
"version": "2.3.7", "version": "2.9.17",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/EDCD/coriolis" "url": "https://github.com/EDCD/coriolis"
@@ -19,12 +19,14 @@
"test": "jest", "test": "jest",
"prod-serve": "nginx -p $(pwd) -c nginx.conf", "prod-serve": "nginx -p $(pwd) -c nginx.conf",
"prod-stop": "kill -QUIT $(cat nginx.pid)", "prod-stop": "kill -QUIT $(cat nginx.pid)",
"build": "npm run clean && NODE_ENV=production webpack -p --config webpack.config.prod.js", "build": "npm run clean && cross-env NODE_ENV=production webpack -p --config webpack.config.prod.js",
"rsync": "rsync -ae \"ssh -i $CORIOLIS_PEM\" --delete build/ $CORIOLIS_USER@$CORIOLIS_HOST:~/wwws", "rsync": "rsync -ae \"ssh -i $CORIOLIS_PEM\" --delete build/ $CORIOLIS_USER@$CORIOLIS_HOST:~/wwws",
"deploy": "npm run lint && npm test && npm run build && npm run rsync" "deploy": "npm run lint && npm test && npm run build && npm run rsync"
}, },
"jest": { "jest": {
"transform": {".*": "<rootDir>/node_modules/babel-jest"}, "transform": {
".*": "<rootDir>/node_modules/babel-jest"
},
"testRegex": "(/__tests__/test-.*|\\.(test|spec))\\.js$", "testRegex": "(/__tests__/test-.*|\\.(test|spec))\\.js$",
"moduleFileExtensions": [ "moduleFileExtensions": [
"js", "js",
@@ -37,6 +39,7 @@
"<rootDir>/node_modules/lodash", "<rootDir>/node_modules/lodash",
"<rootDir>/node_modules/react", "<rootDir>/node_modules/react",
"<rootDir>/node_modules/react-dom", "<rootDir>/node_modules/react-dom",
"<rootDir>/node_modules/react-transition-group",
"<rootDir>/node_modules/react-testutils-additions", "<rootDir>/node_modules/react-testutils-additions",
"<rootDir>/node_modules/fbjs", "<rootDir>/node_modules/fbjs",
"<rootDir>/node_modules/fbemitter", "<rootDir>/node_modules/fbemitter",
@@ -58,10 +61,12 @@
"babel-eslint": "*", "babel-eslint": "*",
"babel-jest": "*", "babel-jest": "*",
"babel-loader": "*", "babel-loader": "*",
"babel-preset-es2015": "*", "babel-preset-env": "*",
"babel-preset-react": "*", "babel-preset-react": "*",
"babel-preset-stage-0": "*", "babel-preset-stage-0": "*",
"create-react-class": "^15.6.2",
"css-loader": "^0.28.0", "css-loader": "^0.28.0",
"cross-env": "^5.1.4",
"d3-selection": "1", "d3-selection": "1",
"eslint": "3.19.0", "eslint": "3.19.0",
"eslint-plugin-react": "^6.10.3", "eslint-plugin-react": "^6.10.3",
@@ -70,7 +75,7 @@
"extract-text-webpack-plugin": "2.1.0", "extract-text-webpack-plugin": "2.1.0",
"file-loader": "^0.11.1", "file-loader": "^0.11.1",
"html-webpack-plugin": "^2.28.0", "html-webpack-plugin": "^2.28.0",
"jest-cli": "^19.0.2", "jest-cli": "^21.2.1",
"jsen": "^0.6.4", "jsen": "^0.6.4",
"json-loader": "^0.5.4", "json-loader": "^0.5.4",
"less": "^2.7.2", "less": "^2.7.2",
@@ -78,28 +83,33 @@
"react-addons-perf": "^15.4.2", "react-addons-perf": "^15.4.2",
"react-measure": "^1.4.7", "react-measure": "^1.4.7",
"react-testutils-additions": "^15.2.0", "react-testutils-additions": "^15.2.0",
"react-transition-group": "^1.1.2",
"rimraf": "^2.6.1", "rimraf": "^2.6.1",
"rollup": "0.41", "rollup": "0.41",
"rollup-plugin-node-resolve": "3", "rollup-plugin-node-resolve": "3",
"style-loader": "^0.16.1", "style-loader": "^0.16.1",
"uglify-js": "^2.4.11",
"url-loader": "^0.5.8", "url-loader": "^0.5.8",
"webpack": "^2.4.1", "webpack": "^2.4.1",
"webpack-dev-server": "^2.4.4", "webpack-dev-server": "^2.4.4",
"uglify-js": "^2.4.11" "webpack-notifier": "^1.6.0",
"webpack-bugsnag-plugins": "^1.1.1"
}, },
"dependencies": { "dependencies": {
"babel-polyfill": "*", "babel-polyfill": "*",
"browserify-zlib-next": "^1.0.1", "browserify-zlib-next": "^1.0.1",
"classnames": "^2.2.5", "classnames": "^2.2.5",
"coriolis-data": "EDCD/coriolis-data", "coriolis-data": "../coriolis-data",
"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.4",
"lz-string": "^1.4.4", "lz-string": "^1.4.4",
"pako": "^1.0.6",
"prop-types": "^15.5.8", "prop-types": "^15.5.8",
"react": "^15.5.4", "react": "^15.5.4",
"react-dom": "^15.5.4", "react-dom": "^15.5.4",
"react-ga": "^2.5.3",
"react-number-editor": "Athanasius/react-number-editor.git#miggy", "react-number-editor": "Athanasius/react-number-editor.git#miggy",
"recharts": "^0.22.3", "recharts": "^0.22.3",
"superagent": "^3.5.2" "superagent": "^3.5.2"

View File

@@ -12,6 +12,7 @@ import ModalHelp from './components/ModalHelp';
import ModalImport from './components/ModalImport'; import ModalImport from './components/ModalImport';
import ModalPermalink from './components/ModalPermalink'; import ModalPermalink from './components/ModalPermalink';
import * as CompanionApiUtils from './utils/CompanionApiUtils'; import * as CompanionApiUtils from './utils/CompanionApiUtils';
import * as JournalUtils from './utils/JournalUtils';
import AboutPage from './pages/AboutPage'; import AboutPage from './pages/AboutPage';
import NotFoundPage from './pages/NotFoundPage'; import NotFoundPage from './pages/NotFoundPage';
@@ -20,7 +21,7 @@ import ComparisonPage from './pages/ComparisonPage';
import ShipyardPage from './pages/ShipyardPage'; import ShipyardPage from './pages/ShipyardPage';
import ErrorDetails from './pages/ErrorDetails'; import ErrorDetails from './pages/ErrorDetails';
const zlib = require('zlib'); const zlib = require('pako');
/** /**
* Coriolis App * Coriolis App
@@ -90,9 +91,16 @@ export default class Coriolis extends React.Component {
_importBuild(r) { _importBuild(r) {
try { try {
// Need to decode and gunzip the data, then build the ship // Need to decode and gunzip the data, then build the ship
const data = zlib.gunzipSync(new Buffer(r.params.data, 'base64')); const data = zlib.inflate(new Buffer(r.params.data, 'base64'), { to: 'string' });
const json = JSON.parse(data); const json = JSON.parse(data);
const ship = CompanionApiUtils.shipFromJson(json); console.info('Ship import data: ');
console.info(json);
let ship;
if (json && json.modules) {
ship = CompanionApiUtils.shipFromJson(json);
} else if (json && json.Modules) {
ship = JournalUtils.shipFromLoadoutJSON(json);
}
r.params.ship = ship.id; r.params.ship = ship.id;
r.params.code = ship.toString(); r.params.code = ship.toString();
this._setPage(OutfittingPage, r); this._setPage(OutfittingPage, r);
@@ -124,6 +132,13 @@ export default class Coriolis extends React.Component {
*/ */
_onError(msg, scriptUrl, line, col, errObj) { _onError(msg, scriptUrl, line, col, errObj) {
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 instanceof Error) {
bugsnagClient.notify(errObj) // eslint-disable-line
} else if (errObj instanceof String) {
bugsnagClient.notify(msg, errObj) // eslint-disable-line
}
}
this.setState({ this.setState({
error: <ErrorDetails error={{ message: msg, details: { scriptUrl, line, col, error: JSON.stringify(errObj) } }}/>, error: <ErrorDetails error={{ message: msg, details: { scriptUrl, line, col, error: JSON.stringify(errObj) } }}/>,
page: null, page: null,
@@ -340,6 +355,8 @@ export default class Coriolis extends React.Component {
<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/>
<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

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

View File

@@ -20,6 +20,7 @@ const GRPCAT = {
'fx': 'limpet controllers', 'fx': 'limpet controllers',
'hb': 'limpet controllers', 'hb': 'limpet controllers',
'pc': 'limpet controllers', 'pc': 'limpet controllers',
'rpl': 'limpet controllers',
'pce': 'passenger cabins', 'pce': 'passenger cabins',
'pci': 'passenger cabins', 'pci': 'passenger cabins',
'pcm': 'passenger cabins', 'pcm': 'passenger cabins',
@@ -36,10 +37,14 @@ const GRPCAT = {
'ml': 'lasers', 'ml': 'lasers',
'c': 'projectiles', 'c': 'projectiles',
'mc': 'projectiles', 'mc': 'projectiles',
'axmc': 'experimental',
'fc': 'projectiles', 'fc': 'projectiles',
'rfl': 'experimental',
'pa': 'projectiles', 'pa': 'projectiles',
'rg': 'projectiles', 'rg': 'projectiles',
'mr': 'ordnance', 'mr': 'ordnance',
'axmr': 'experimental',
'rcpl': 'experimental',
'tp': 'ordnance', 'tp': 'ordnance',
'nl': 'ordnance', 'nl': 'ordnance',
'sc': 'scanners', 'sc': 'scanners',
@@ -48,9 +53,15 @@ const GRPCAT = {
'cs': 'scanners', 'cs': 'scanners',
'kw': 'scanners', 'kw': 'scanners',
'ws': 'scanners', 'ws': 'scanners',
'xs': 'scanners',
'ch': 'defence', 'ch': 'defence',
'po': 'defence', 'po': 'defence',
'ec': 'defence', 'ec': 'defence',
'sfn': 'defence',
// Standard
'gpp': 'guardian',
'gpc': 'guardian',
'ggc': '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 = {
@@ -60,7 +71,7 @@ const CATEGORIES = {
'fi': ['fi'], 'fi': ['fi'],
'fuel': ['ft', 'fs'], 'fuel': ['ft', 'fs'],
'hangars': ['fh', 'pv'], 'hangars': ['fh', 'pv'],
'limpet controllers': ['cc', 'fx', 'hb', 'pc'], 'limpet controllers': ['cc', 'fx', 'hb', 'pc', 'rpl'],
'passenger cabins': ['pce', 'pci', 'pcm', 'pcq'], 'passenger cabins': ['pce', 'pci', 'pcm', 'pcq'],
'rf': ['rf'], 'rf': ['rf'],
'shields': ['sg', 'bsg', 'psg', 'scb'], 'shields': ['sg', 'bsg', 'psg', 'scb'],
@@ -75,6 +86,11 @@ const CATEGORIES = {
'hs': ['hs'], 'hs': ['hs'],
'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': ['axmc', 'axmr', 'rfl', 'xs', 'sfn', 'rcpl'],
// Guardian
'guardian': ['gpp', 'gpc', 'ggc']
}; };
/** /**
@@ -88,7 +104,11 @@ export default class AvailableModulesMenu extends TranslatedComponent {
diffDetails: PropTypes.func, diffDetails: PropTypes.func,
m: PropTypes.object, m: PropTypes.object,
shipMass: PropTypes.number, shipMass: PropTypes.number,
warning: PropTypes.func warning: PropTypes.func,
firstSlotId: PropTypes.string,
lastSlotId: PropTypes.string,
activeSlotId: PropTypes.string,
slotDiv: PropTypes.object
}; };
static defaultProps = { static defaultProps = {
@@ -104,6 +124,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
super(props); super(props);
this._hideDiff = this._hideDiff.bind(this); this._hideDiff = this._hideDiff.bind(this);
this.state = this._initState(props, context); this.state = this._initState(props, context);
this.slotItems = [];// Array to hold <li> refs.
} }
/** /**
@@ -114,8 +135,9 @@ export default class AvailableModulesMenu extends TranslatedComponent {
*/ */
_initState(props, context) { _initState(props, context) {
let translate = context.language.translate; let translate = context.language.translate;
let { m, warning, shipMass, onSelect, modules } = props; let { m, warning, shipMass, onSelect, modules, firstSlotId, lastSlotId } = props;
let list, currentGroup; let list, currentGroup;
let buildGroup = this._buildGroup.bind( let buildGroup = this._buildGroup.bind(
this, this,
translate, translate,
@@ -126,7 +148,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
this._hideDiff(event); this._hideDiff(event);
onSelect(m); onSelect(m);
} }
); );
if (modules instanceof Array) { if (modules instanceof Array) {
list = buildGroup(modules[0].grp, modules); list = buildGroup(modules[0].grp, modules);
@@ -134,7 +156,10 @@ export default class AvailableModulesMenu extends TranslatedComponent {
list = []; list = [];
// At present time slots with grouped options (Hardpoints and Internal) can be empty // At present time slots with grouped options (Hardpoints and Internal) can be empty
if (m) { if (m) {
list.push(<div className='empty-c upp' key='empty' onClick={onSelect.bind(null, null)} >{translate('empty')}</div>); let emptyId = 'empty';
if(this.firstSlotId == null) this.firstSlotId = emptyId;
let keyDown = this._keyDown.bind(this, onSelect);
list.push(<div className='empty-c upp' key={emptyId} data-id={emptyId} onClick={onSelect.bind(null, null)} onKeyDown={keyDown} tabIndex="0" ref={slotItem => this.slotItems[emptyId] = slotItem} >{translate('empty')}</div>);
} }
// Need to regroup the modules by our own categorisation // Need to regroup the modules by our own categorisation
@@ -184,37 +209,40 @@ export default class AvailableModulesMenu extends TranslatedComponent {
} }
} }
} }
let trackingFocus = false;
return { list, currentGroup }; return { list, currentGroup, trackingFocus };
} }
/** /**
* Generate React Components for Module Group * Generate React Components for Module Group
* @param {Function} translate Translate function * @param {Function} translate Translate function
* @param {Objecy} mountedModule Mounted Module * @param {Object} mountedModule Mounted Module
* @param {Funciton} warningFunc Warning function * @param {Function} warningFunc Warning function
* @param {number} mass Mass * @param {number} mass Mass
* @param {function} onSelect Select/Mount callback * @param {function} onSelect Select/Mount callback
* @param {string} grp Group name * @param {string} grp Group name
* @param {Array} modules Available modules * @param {Array} modules Available modules
* @param {string} firstSlotId id of first slot item
* @param {string} lastSlotId id of last slot item
* @return {React.Component} Available Module Group contents * @return {React.Component} Available Module Group contents
*/ */
_buildGroup(translate, mountedModule, warningFunc, mass, onSelect, grp, modules) { _buildGroup(translate, mountedModule, warningFunc, mass, onSelect, grp, modules, firstSlotId, lastSlotId) {
let prevClass = null, prevRating = null; let prevClass = null, prevRating = null, prevName;
let elems = []; let elems = [];
const sortedModules = modules.sort(this._moduleOrder); const sortedModules = modules.sort(this._moduleOrder);
// Calculate the number of items per class. Used so we don't have long lists with only a few items in each row // Calculate the number of items per class. Used so we don't have long lists with only a few items in each row
const tmp = sortedModules.map((v, i) => v['class']).reduce((count, cls) => { count[cls] = ++count[cls] || 1; return count; }, {}); const tmp = sortedModules.map((v, i) => v['class']).reduce((count, cls) => { count[cls] = ++count[cls] || 1; return count; }, {});
const itemsPerClass = Math.max.apply(null, Object.keys(tmp).map(key => tmp[key])); const itemsPerClass = Math.max.apply(null, Object.keys(tmp).map(key => tmp[key]));
let itemsOnThisRow = 0; let itemsOnThisRow = 0;
for (let i = 0; i < sortedModules.length; i++) { for (let i = 0; i < sortedModules.length; i++) {
let m = sortedModules[i]; let m = sortedModules[i];
let mount = null; let mount = null;
let disabled = false; let disabled = false;
prevName = m.name;
if (ModuleUtils.isShieldGenerator(m.grp)) { if (ModuleUtils.isShieldGenerator(m.grp)) {
// Shield generators care about maximum hull mass // Shield generators care about maximum hull mass
disabled = mass > m.maxmass; disabled = mass > m.maxmass;
@@ -230,9 +258,21 @@ export default class AvailableModulesMenu extends TranslatedComponent {
}); });
let eventHandlers; let eventHandlers;
if (disabled || active) { if (disabled) {
eventHandlers = {}; eventHandlers = {
onKeyDown: this._keyDown.bind(this, null),
onKeyUp: this._keyUp.bind(this, null)
};
} else { } else {
/**
* Get the ids of the first and last <li> elements in the <ul> that are focusable (i.e. are not active or disabled)
* Will be used to keep focus inside the <ul> on Tab and Shift-Tab while it is visible
*/
if (this.firstSlotId == null) this.firstSlotId = sortedModules[i].id;
if (active) this.activeSlotId = sortedModules[i].id;
this.lastSlotId = sortedModules[i].id;
let showDiff = this._showDiff.bind(this, mountedModule, m); let showDiff = this._showDiff.bind(this, mountedModule, m);
let select = onSelect.bind(null, m); let select = onSelect.bind(null, m);
@@ -241,7 +281,9 @@ export default class AvailableModulesMenu extends TranslatedComponent {
onTouchStart: this._touchStart.bind(this, showDiff), onTouchStart: this._touchStart.bind(this, showDiff),
onTouchEnd: this._touchEnd.bind(this, select), onTouchEnd: this._touchEnd.bind(this, select),
onMouseLeave: this._hideDiff, onMouseLeave: this._hideDiff,
onClick: select onClick: select,
onKeyDown: this._keyDown.bind(this, select),
onKeyUp: this._keyUp.bind(this, select)
}; };
} }
@@ -250,24 +292,28 @@ export default class AvailableModulesMenu extends TranslatedComponent {
case 'G': mount = <MountGimballed className={'lg'}/>; break; case 'G': mount = <MountGimballed className={'lg'}/>; break;
case 'T': mount = <MountTurret className={'lg'}/>; break; case 'T': mount = <MountTurret className={'lg'}/>; break;
} }
if (m.name && m.name === prevName) {
// elems.push(<br key={'b' + m.grp + i} />);
itemsOnThisRow = 0;
}
if (itemsOnThisRow == 6 || i > 0 && sortedModules.length > 3 && itemsPerClass > 2 && m.class != prevClass && (m.rating != prevRating || m.mount)) { if (itemsOnThisRow == 6 || i > 0 && sortedModules.length > 3 && itemsPerClass > 2 && m.class != prevClass && (m.rating != prevRating || m.mount)) {
elems.push(<br key={'b' + m.grp + i} />); elems.push(<br key={'b' + m.grp + i} />);
itemsOnThisRow = 0; itemsOnThisRow = 0;
} }
let tbIdx = (classes.indexOf('disabled') < 0) ? 0 : undefined;
elems.push( elems.push(
<li key={m.id} className={classes} {...eventHandlers}> <li key={m.id} data-id={m.id} className={classes} {...eventHandlers} tabIndex={tbIdx} ref={slotItem => this.slotItems[m.id] = slotItem}>
{mount} {mount}
{(mount ? ' ' : '') + m.class + m.rating + (m.missile ? '/' + m.missile : '') + (m.name ? ' ' + translate(m.name) : '')} {(mount ? ' ' : '') + m.class + m.rating + (m.missile ? '/' + m.missile : '') + (m.name ? ' ' + translate(m.name) : '')}
</li> </li>
); );
itemsOnThisRow++; itemsOnThisRow++;
prevClass = m.class; prevClass = m.class;
prevRating = m.rating; prevRating = m.rating;
prevName = m.name;
} }
return <ul key={'modules' + grp}>{elems}</ul>;
return <ul key={'modules' + grp} >{elems}</ul>;
} }
/** /**
@@ -318,6 +364,41 @@ export default class AvailableModulesMenu extends TranslatedComponent {
this._hideDiff(); this._hideDiff();
} }
/**
* Key down - select module on Enter key, move to next/previous module on Tab/Shift-Tab, close on Esc
* @param {Function} select Select module callback
* @param {SyntheticEvent} event Event
*/
_keyDown(select, event) {
let className = event.currentTarget.attributes['class'].value;
if (event.key == 'Enter' && className.indexOf('disabled') < 0 && className.indexOf('active') < 0) {
select();
return;
}
let elemId = event.currentTarget.attributes['data-id'].value;
if (className.indexOf('disabled') < 0 && event.key == 'Tab') {
if (event.shiftKey && elemId == this.firstSlotId) {
event.preventDefault();
this.slotItems[this.lastSlotId].focus();
return;
}
if (!event.shiftKey && elemId == this.lastSlotId) {
event.preventDefault();
this.slotItems[this.firstSlotId].focus();
return;
}
}
}
/**
* Key Up
* @param {Function} select Select module callback
* @param {SytheticEvent} event Event
*/
_keyUp(select,event) {
// nothing here yet
}
/** /**
* Hide diff tooltip * Hide diff tooltip
* @param {SyntheticEvent} event Event * @param {SyntheticEvent} event Event
@@ -375,6 +456,23 @@ export default class AvailableModulesMenu extends TranslatedComponent {
if (this.groupElem) { // Scroll to currently selected group if (this.groupElem) { // Scroll to currently selected group
this.node.scrollTop = this.groupElem.offsetTop; this.node.scrollTop = this.groupElem.offsetTop;
} }
/**
* Set focus on active or first slot element, if applicable.
*/
if (this.slotItems[this.activeSlotId]) {
this.slotItems[this.activeSlotId].focus();
} else if (this.slotItems[this.firstSlotId]) {
this.slotItems[this.firstSlotId].focus();
}
}
/**
* Handle focus if the component updates
*
*/
componentWillUnmount() {
if(this.props.slotDiv) {
this.props.slotDiv.focus();
}
} }
/** /**

View File

@@ -25,7 +25,7 @@ export default class EngagementRange extends TranslatedComponent {
const { ship } = props; const { ship } = props;
const maxRange = this._calcMaxRange(ship); const maxRange = Math.round(this._calcMaxRange(ship));
this.state = { this.state = {
maxRange maxRange

View File

@@ -97,11 +97,12 @@ export default class HardpointSlot extends Slot {
{ 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 }
{ 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 }
{ m && validMods.length > 0 ? <div className='r' ><button onClick={this._toggleModifications.bind(this)} onContextMenu={stopCtxPropagation} onMouseOver={termtip.bind(null, 'modifications')} onMouseOut={tooltip.bind(null, null)}><ListModifications /></button></div> : null } { m && validMods.length > 0 ? <div className='r' tabIndex="0" ref={ modButton => this.modButton = modButton }><button tabIndex="-1" onClick={this._toggleModifications.bind(this)} onContextMenu={stopCtxPropagation} onMouseOver={termtip.bind(null, 'modifications')} onMouseOut={tooltip.bind(null, null)}><ListModifications /></button></div> : null }
</div> </div>
</div>; </div>;
} else { } else {
return <div className={'empty'}>{translate('empty')}</div>; return <div className={'empty'}>{translate('empty')}</div>;
} }
} }
} }

View File

@@ -17,14 +17,25 @@ export default class HardpointSlotSection extends SlotSection {
*/ */
constructor(props, context) { constructor(props, context) {
super(props, context, 'hardpoints', 'hardpoints'); super(props, context, 'hardpoints', 'hardpoints');
this._empty = this._empty.bind(this); this._empty = this._empty.bind(this);
this.selectedRefId = null;
this.firstRefId = 'emptyall';
this.lastRefId = 'nl-F';
}
/**
* Handle focus when component updates
* @param {Object} prevProps React Component properties
*/
componentDidUpdate(prevProps) {
this._handleSectionFocus(prevProps,this.firstRefId, this.lastRefId);
} }
/** /**
* Empty all slots * Empty all slots
*/ */
_empty() { _empty() {
this.selectedRefId = 'emptyall';
this.props.ship.emptyWeapons(); this.props.ship.emptyWeapons();
this.props.onChange(); this.props.onChange();
this._close(); this._close();
@@ -37,6 +48,7 @@ export default class HardpointSlotSection extends SlotSection {
* @param {SyntheticEvent} event Event * @param {SyntheticEvent} event Event
*/ */
_fill(group, mount, event) { _fill(group, mount, event) {
this.selectedRefId = group + '-' + mount;
this.props.ship.useWeapon(group, mount, null, event.getModifierState('Alt')); this.props.ship.useWeapon(group, mount, null, event.getModifierState('Alt'));
this.props.onChange(); this.props.onChange();
this._close(); this._close();
@@ -95,52 +107,52 @@ export default class HardpointSlotSection extends SlotSection {
return <div className='select hardpoint' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}> return <div className='select hardpoint' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
<ul> <ul>
<li className='lc' onClick={this._empty}>{translate('empty all')}</li> <li className='lc' tabIndex='0' onClick={this._empty} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['emptyall'] = smRef}>{translate('empty all')}</li>
<li className='optional-hide' style={{ textAlign: 'center', marginTop: '1em' }}>{translate('PHRASE_ALT_ALL')}</li> <li className='optional-hide' style={{ textAlign: 'center', marginTop: '1em' }}>{translate('PHRASE_ALT_ALL')}</li>
</ul> </ul>
<div className='select-group cap'>{translate('pl')}</div> <div className='select-group cap'>{translate('pl')}</div>
<ul> <ul>
<li className='c' onClick={_fill.bind(this, 'pl', 'F')}><MountFixed className='lg'/></li> <li className='c' tabIndex='0' onClick={_fill.bind(this, 'pl', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['pl-F'] = smRef}><MountFixed className='lg'/></li>
<li className='c' onClick={_fill.bind(this, 'pl', 'G')}><MountGimballed className='lg'/></li> <li className='c' tabIndex='0' onClick={_fill.bind(this, 'pl', 'G')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['pl-G'] = smRef}><MountGimballed className='lg'/></li>
<li className='c' onClick={_fill.bind(this, 'pl', 'T')}><MountTurret className='lg'/></li> <li className='c' tabIndex='0' onClick={_fill.bind(this, 'pl', 'T')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['pl-T'] = smRef}><MountTurret className='lg'/></li>
</ul> </ul>
<div className='select-group cap'>{translate('ul')}</div> <div className='select-group cap'>{translate('ul')}</div>
<ul> <ul>
<li className='c' onClick={_fill.bind(this, 'ul', 'F')}><MountFixed className='lg'/></li> <li className='c' tabIndex='0' onClick={_fill.bind(this, 'ul', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['ul-F'] = smRef}><MountFixed className='lg'/></li>
<li className='c' onClick={_fill.bind(this, 'ul', 'G')}><MountGimballed className='lg'/></li> <li className='c' tabIndex='0' onClick={_fill.bind(this, 'ul', 'G')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['ul-G'] = smRef}><MountGimballed className='lg'/></li>
<li className='c' onClick={_fill.bind(this, 'ul', 'T')}><MountTurret className='lg'/></li> <li className='c' tabIndex='0' onClick={_fill.bind(this, 'ul', 'T')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['ul-T'] = smRef}><MountTurret className='lg'/></li>
</ul> </ul>
<div className='select-group cap'>{translate('bl')}</div> <div className='select-group cap'>{translate('bl')}</div>
<ul> <ul>
<li className='c' onClick={_fill.bind(this, 'bl', 'F')}><MountFixed className='lg'/></li> <li className='c' tabIndex='0' onClick={_fill.bind(this, 'bl', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['bl-F'] = smRef}><MountFixed className='lg'/></li>
<li className='c' onClick={_fill.bind(this, 'bl', 'G')}><MountGimballed className='lg'/></li> <li className='c' tabIndex='0' onClick={_fill.bind(this, 'bl', 'G')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['bl-G'] = smRef}><MountGimballed className='lg'/></li>
<li className='c' onClick={_fill.bind(this, 'bl', 'T')}><MountTurret className='lg'/></li> <li className='c' tabIndex='0' onClick={_fill.bind(this, 'bl', 'T')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['bl-T'] = smRef}><MountTurret className='lg'/></li>
</ul> </ul>
<div className='select-group cap'>{translate('mc')}</div> <div className='select-group cap'>{translate('mc')}</div>
<ul> <ul>
<li className='c' onClick={_fill.bind(this, 'mc', 'F')}><MountFixed className='lg'/></li> <li className='c' tabIndex='0' onClick={_fill.bind(this, 'mc', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['mc-F'] = smRef}><MountFixed className='lg'/></li>
<li className='c' onClick={_fill.bind(this, 'mc', 'G')}><MountGimballed className='lg'/></li> <li className='c' tabIndex='0' onClick={_fill.bind(this, 'mc', 'G')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['mc-G'] = smRef}><MountGimballed className='lg'/></li>
<li className='c' onClick={_fill.bind(this, 'mc', 'T')}><MountTurret className='lg'/></li> <li className='c' tabIndex='0' onClick={_fill.bind(this, 'mc', 'T')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['mc-T'] = smRef}><MountTurret className='lg'/></li>
</ul> </ul>
<div className='select-group cap'>{translate('c')}</div> <div className='select-group cap'>{translate('c')}</div>
<ul> <ul>
<li className='c' onClick={_fill.bind(this, 'c', 'F')}><MountFixed className='lg'/></li> <li className='c' tabIndex='0' onClick={_fill.bind(this, 'c', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['c-F'] = smRef}><MountFixed className='lg'/></li>
<li className='c' onClick={_fill.bind(this, 'c', 'G')}><MountGimballed className='lg'/></li> <li className='c' tabIndex='0' onClick={_fill.bind(this, 'c', 'G')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['c-G'] = smRef}><MountGimballed className='lg'/></li>
<li className='c' onClick={_fill.bind(this, 'c', 'T')}><MountTurret className='lg'/></li> <li className='c' tabIndex='0' onClick={_fill.bind(this, 'c', 'T')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['c-T'] = smRef}><MountTurret className='lg'/></li>
</ul> </ul>
<div className='select-group cap'>{translate('fc')}</div> <div className='select-group cap'>{translate('fc')}</div>
<ul> <ul>
<li className='c' onClick={_fill.bind(this, 'fc', 'F')}><MountFixed className='lg'/></li> <li className='c' tabIndex='0' onClick={_fill.bind(this, 'fc', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['fc-F'] = smRef}><MountFixed className='lg'/></li>
<li className='c' onClick={_fill.bind(this, 'fc', 'G')}><MountGimballed className='lg'/></li> <li className='c' tabIndex='0' onClick={_fill.bind(this, 'fc', 'G')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['fc-G'] = smRef}><MountGimballed className='lg'/></li>
<li className='c' onClick={_fill.bind(this, 'fc', 'T')}><MountTurret className='lg'/></li> <li className='c' tabIndex='0' onClick={_fill.bind(this, 'fc', 'T')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['fc-T'] = smRef}><MountTurret className='lg'/></li>
</ul> </ul>
<div className='select-group cap'>{translate('pa')}</div> <div className='select-group cap'>{translate('pa')}</div>
<ul> <ul>
<li className='lc' onClick={_fill.bind(this, 'pa', 'F')}>{translate('pa')}</li> <li className='lc' tabIndex='0' onClick={_fill.bind(this, 'pa', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['pa-F'] = smRef}>{translate('pa')}</li>
</ul> </ul>
<div className='select-group cap'>{translate('nl')}</div> <div className='select-group cap'>{translate('nl')}</div>
<ul> <ul>
<li className='lc' onClick={_fill.bind(this, 'nl', 'F')}>{translate('nl')}</li> <li className='lc' tabIndex='0' onClick={_fill.bind(this, 'nl', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['nl-F'] = smRef}>{translate('nl')}</li>
</ul> </ul>
</div>; </div>;
} }

View File

@@ -505,6 +505,9 @@ export default class Header extends TranslatedComponent {
return ( return (
<header> <header>
{this.props.appCacheUpdate && <div id="app-update" onClick={() => window.location.reload() }>{translate('PHRASE_UPDATE_RDY')}</div>} {this.props.appCacheUpdate && <div id="app-update" onClick={() => window.location.reload() }>{translate('PHRASE_UPDATE_RDY')}</div>}
{this.props.appCacheUpdate ? <a className={'view-changes'} href={'https://github.com/EDCD/coriolis/compare/edcd:develop@{' + window.CORIOLIS_DATE + '}...edcd:develop'} target="_blank">
{'View Release Changes'}
</a> : null}
<Link className='l' href='/' style={{ marginRight: '1em' }} title='Home'><CoriolisLogo className='icon xl' /></Link> <Link className='l' href='/' style={{ marginRight: '1em' }} title='Home'><CoriolisLogo className='icon xl' /></Link>
<div className='l menu'> <div className='l menu'>

View File

@@ -26,13 +26,16 @@ export default class InternalSlot extends Slot {
let classRating = m.class + m.rating; let classRating = m.class + m.rating;
let { drag, drop, ship } = this.props; let { drag, drop, ship } = this.props;
let { termtip, tooltip } = this.context; let { termtip, tooltip } = this.context;
let validMods = Modifications.modules[m.grp].modifications || []; let validMods = (Modifications.modules[m.grp] ? Modifications.modules[m.grp].modifications : []);
let showModuleResistances = Persist.showModuleResistances(); let showModuleResistances = Persist.showModuleResistances();
// Modifications tooltip shows blueprint and grade, if available // Modifications tooltip shows blueprint and grade, if available
let modTT = translate('modified'); let modTT = translate('modified');
if (m && m.blueprint && m.blueprint.name) { if (m && m.blueprint && m.blueprint.name) {
modTT = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade; modTT = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
if (m.blueprint.special && m.blueprint.special.id >= 0) {
modTT += ', ' + translate(m.blueprint.special.name);
}
modTT = ( modTT = (
<div> <div>
<div>{modTT}</div> <div>{modTT}</div>
@@ -67,6 +70,7 @@ export default class InternalSlot extends Slot {
{ m.getRange() ? <div className={'l'}>{translate('range')} {formats.f2(m.getRange())}{u.km}</div> : null } { m.getRange() ? <div className={'l'}>{translate('range')} {formats.f2(m.getRange())}{u.km}</div> : null }
{ m.getRangeT() ? <div className={'l'}>{translate('ranget')} {formats.f1(m.getRangeT())}{u.s}</div> : null } { m.getRangeT() ? <div className={'l'}>{translate('ranget')} {formats.f1(m.getRangeT())}{u.s}</div> : null }
{ m.getTime() ? <div className={'l'}>{translate('time')}: {formats.time(m.getTime())}</div> : null } { m.getTime() ? <div className={'l'}>{translate('time')}: {formats.time(m.getTime())}</div> : null }
{ m.getHackTime() ? <div className={'l'}>{translate('hacktime')}: {formats.time(m.getHackTime())}</div> : null }
{ m.maximum ? <div className={'l'}>{translate('max')}: {(m.maximum)}</div> : null } { m.maximum ? <div className={'l'}>{translate('max')}: {(m.maximum)}</div> : null }
{ m.rangeLS ? <div className={'l'}>{translate('range')}: {m.rangeLS}{u.Ls}</div> : null } { m.rangeLS ? <div className={'l'}>{translate('range')}: {m.rangeLS}{u.Ls}</div> : null }
{ m.rangeLS === null ? <div className={'l'}>{u.Ls}</div> : null } { m.rangeLS === null ? <div className={'l'}>{u.Ls}</div> : null }
@@ -81,7 +85,7 @@ export default class InternalSlot extends Slot {
{ 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 }
{ m && validMods.length > 0 ? <div className='r' ><button onClick={this._toggleModifications.bind(this)} onContextMenu={stopCtxPropagation} onMouseOver={termtip.bind(null, 'modifications')} onMouseOut={tooltip.bind(null, null)}><ListModifications /></button></div> : null } { m && validMods.length > 0 ? <div className='r' tabIndex="0" ref={ modButton => this.modButton = modButton }><button tabIndex="-1" onClick={this._toggleModifications.bind(this)} onContextMenu={stopCtxPropagation} onMouseOver={termtip.bind(null, 'modifications')} onMouseOut={tooltip.bind(null, null)}><ListModifications /></button></div> : null }
</div> </div>
</div>; </div>;
} else { } else {

View File

@@ -18,7 +18,6 @@ export default class InternalSlotSection extends SlotSection {
*/ */
constructor(props, context) { constructor(props, context) {
super(props, context, 'internal', 'optional internal'); super(props, context, 'internal', 'optional internal');
this._empty = this._empty.bind(this); this._empty = this._empty.bind(this);
this._fillWithCargo = this._fillWithCargo.bind(this); this._fillWithCargo = this._fillWithCargo.bind(this);
this._fillWithCells = this._fillWithCells.bind(this); this._fillWithCells = this._fillWithCells.bind(this);
@@ -29,12 +28,24 @@ export default class InternalSlotSection extends SlotSection {
this._fillWithFirstClassCabins = this._fillWithFirstClassCabins.bind(this); this._fillWithFirstClassCabins = this._fillWithFirstClassCabins.bind(this);
this._fillWithBusinessClassCabins = this._fillWithBusinessClassCabins.bind(this); this._fillWithBusinessClassCabins = this._fillWithBusinessClassCabins.bind(this);
this._fillWithEconomyClassCabins = this._fillWithEconomyClassCabins.bind(this); this._fillWithEconomyClassCabins = this._fillWithEconomyClassCabins.bind(this);
this.selectedRefId = null;
this.firstRefId = 'emptyall';
this.lastRefId = this.sectionRefArr['pcq'] ? 'pcq' : 'pcm';
}
/**
* Handle focus when component updates
* @param {Object} prevProps React Component properties
*/
componentDidUpdate(prevProps) {
this._handleSectionFocus(prevProps,this.firstRefId, this.lastRefId);
} }
/** /**
* Empty all slots * Empty all slots
*/ */
_empty() { _empty() {
this.selectedRefId = 'emptyall';
this.props.ship.emptyInternal(); this.props.ship.emptyInternal();
this.props.onChange(); this.props.onChange();
this._close(); this._close();
@@ -45,6 +56,7 @@ export default class InternalSlotSection extends SlotSection {
* @param {SyntheticEvent} event Event * @param {SyntheticEvent} event Event
*/ */
_fillWithCargo(event) { _fillWithCargo(event) {
this.selectedRefId = 'cargo';
let clobber = event.getModifierState('Alt'); let clobber = event.getModifierState('Alt');
let ship = this.props.ship; let ship = this.props.ship;
ship.internal.forEach((slot) => { ship.internal.forEach((slot) => {
@@ -61,6 +73,7 @@ export default class InternalSlotSection extends SlotSection {
* @param {SyntheticEvent} event Event * @param {SyntheticEvent} event Event
*/ */
_fillWithFuelTanks(event) { _fillWithFuelTanks(event) {
this.selectedRefId = 'ft';
let clobber = event.getModifierState('Alt'); let clobber = event.getModifierState('Alt');
let ship = this.props.ship; let ship = this.props.ship;
ship.internal.forEach((slot) => { ship.internal.forEach((slot) => {
@@ -77,6 +90,7 @@ export default class InternalSlotSection extends SlotSection {
* @param {SyntheticEvent} event Event * @param {SyntheticEvent} event Event
*/ */
_fillWithLuxuryCabins(event) { _fillWithLuxuryCabins(event) {
this.selectedRefId = 'pcq';
let clobber = event.getModifierState('Alt'); let clobber = event.getModifierState('Alt');
let ship = this.props.ship; let ship = this.props.ship;
ship.internal.forEach((slot) => { ship.internal.forEach((slot) => {
@@ -93,6 +107,7 @@ export default class InternalSlotSection extends SlotSection {
* @param {SyntheticEvent} event Event * @param {SyntheticEvent} event Event
*/ */
_fillWithFirstClassCabins(event) { _fillWithFirstClassCabins(event) {
this.selectedRefId = 'pcm';
let clobber = event.getModifierState('Alt'); let clobber = event.getModifierState('Alt');
let ship = this.props.ship; let ship = this.props.ship;
ship.internal.forEach((slot) => { ship.internal.forEach((slot) => {
@@ -109,6 +124,7 @@ export default class InternalSlotSection extends SlotSection {
* @param {SyntheticEvent} event Event * @param {SyntheticEvent} event Event
*/ */
_fillWithBusinessClassCabins(event) { _fillWithBusinessClassCabins(event) {
this.selectedRefId = 'pci';
let clobber = event.getModifierState('Alt'); let clobber = event.getModifierState('Alt');
let ship = this.props.ship; let ship = this.props.ship;
ship.internal.forEach((slot) => { ship.internal.forEach((slot) => {
@@ -125,6 +141,7 @@ export default class InternalSlotSection extends SlotSection {
* @param {SyntheticEvent} event Event * @param {SyntheticEvent} event Event
*/ */
_fillWithEconomyClassCabins(event) { _fillWithEconomyClassCabins(event) {
this.selectedRefId = 'pce';
let clobber = event.getModifierState('Alt'); let clobber = event.getModifierState('Alt');
let ship = this.props.ship; let ship = this.props.ship;
ship.internal.forEach((slot) => { ship.internal.forEach((slot) => {
@@ -141,6 +158,7 @@ export default class InternalSlotSection extends SlotSection {
* @param {SyntheticEvent} event Event * @param {SyntheticEvent} event Event
*/ */
_fillWithCells(event) { _fillWithCells(event) {
this.selectedRefId = 'scb';
let clobber = event.getModifierState('Alt'); let clobber = event.getModifierState('Alt');
let ship = this.props.ship; let ship = this.props.ship;
let chargeCap = 0; // Capacity of single activation let chargeCap = 0; // Capacity of single activation
@@ -160,6 +178,7 @@ export default class InternalSlotSection extends SlotSection {
* @param {SyntheticEvent} event Event * @param {SyntheticEvent} event Event
*/ */
_fillWithArmor(event) { _fillWithArmor(event) {
this.selectedRefId = 'hr';
let clobber = event.getModifierState('Alt'); let clobber = event.getModifierState('Alt');
let ship = this.props.ship; let ship = this.props.ship;
ship.internal.forEach((slot) => { ship.internal.forEach((slot) => {
@@ -176,6 +195,7 @@ export default class InternalSlotSection extends SlotSection {
* @param {SyntheticEvent} event Event * @param {SyntheticEvent} event Event
*/ */
_fillWithModuleReinforcementPackages(event) { _fillWithModuleReinforcementPackages(event) {
this.selectedRefId = 'mrp';
let clobber = event.getModifierState('Alt'); let clobber = event.getModifierState('Alt');
let ship = this.props.ship; let ship = this.props.ship;
ship.internal.forEach((slot) => { ship.internal.forEach((slot) => {
@@ -240,16 +260,16 @@ export default class InternalSlotSection extends SlotSection {
_getSectionMenu(translate, ship) { _getSectionMenu(translate, ship) {
return <div className='select' onClick={e => e.stopPropagation()} onContextMenu={stopCtxPropagation}> return <div className='select' onClick={e => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
<ul> <ul>
<li className='lc' onClick={this._empty}>{translate('empty all')}</li> <li className='lc' tabIndex='0' onClick={this._empty} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['emptyall'] = smRef}>{translate('empty all')}</li>
<li className='lc' onClick={this._fillWithCargo}>{translate('cargo')}</li> <li className='lc' tabIndex='0' onClick={this._fillWithCargo} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['cargo'] = smRef}>{translate('cargo')}</li>
<li className='lc' onClick={this._fillWithCells}>{translate('scb')}</li> <li className='lc' tabIndex='0' onClick={this._fillWithCells} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['scb'] = smRef}>{translate('scb')}</li>
<li className='lc' onClick={this._fillWithArmor}>{translate('hr')}</li> <li className='lc' tabIndex='0' onClick={this._fillWithArmor} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['hr'] = smRef}>{translate('hr')}</li>
<li className='lc' onClick={this._fillWithModuleReinforcementPackages}>{translate('mrp')}</li> <li className='lc' tabIndex='0' onClick={this._fillWithModuleReinforcementPackages} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['mrp'] = smRef}>{translate('mrp')}</li>
<li className='lc' onClick={this._fillWithFuelTanks}>{translate('ft')}</li> <li className='lc' tabIndex='0' onClick={this._fillWithFuelTanks} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['ft'] = smRef}>{translate('ft')}</li>
<li className='lc' onClick={this._fillWithEconomyClassCabins}>{translate('pce')}</li> <li className='lc' tabIndex='0' onClick={this._fillWithEconomyClassCabins} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['pce'] = smRef}>{translate('pce')}</li>
<li className='lc' onClick={this._fillWithBusinessClassCabins}>{translate('pci')}</li> <li className='lc' tabIndex='0' onClick={this._fillWithBusinessClassCabins} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['pci'] = smRef}>{translate('pci')}</li>
<li className='lc' onClick={this._fillWithFirstClassCabins}>{translate('pcm')}</li> <li className='lc' tabIndex='0' onClick={this._fillWithFirstClassCabins} onKeyDown={ship.luxuryCabins ? '' : this._keyDown} ref={smRef => this.sectionRefArr['pcm'] = smRef}>{translate('pcm')}</li>
{ ship.luxuryCabins ? <li className='lc' onClick={this._fillWithLuxuryCabins}>{translate('pcq')}</li> : ''} { ship.luxuryCabins ? <li className='lc' tabIndex='0' onClick={this._fillWithLuxuryCabins} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['pcq'] = smRef}>{translate('pcq')}</li> : ''}
<li className='optional-hide' style={{ textAlign: 'center', marginTop: '1em' }}>{translate('PHRASE_ALT_ALL')}</li> <li className='optional-hide' style={{ textAlign: 'center', marginTop: '1em' }}>{translate('PHRASE_ALT_ALL')}</li>
</ul> </ul>
</div>; </div>;

View File

@@ -50,6 +50,7 @@ export default class ModalPermalink extends TranslatedComponent {
<h3 >{translate('shortened')}</h3> <h3 >{translate('shortened')}</h3>
<input value={this.state.shortenedUrl} readOnly size={25} onFocus={ (e) => e.target.select() }/> <input value={this.state.shortenedUrl} readOnly size={25} onFocus={ (e) => e.target.select() }/>
<br/><br/> <br/><br/>
<p>s.orbis.zone is the new URL shortener domain, old eddp.co urls are considered end of life and could go down at any moment. Sorry for any inconvenience.</p>
<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

@@ -0,0 +1,120 @@
import React from 'react';
import PropTypes from 'prop-types';
import TranslatedComponent from './TranslatedComponent';
import ShortenUrl from '../utils/ShortenUrl';
import Persist from '../stores/Persist';
/**
* Permalink modal
*/
export default class ModalShoppingList extends TranslatedComponent {
static propTypes = {
ship: PropTypes.object.isRequired
};
/**
* Constructor
* @param {Object} props React Component properties
*/
constructor(props) {
super(props);
this.state = {
matsList: '',
mats: {},
matsPerGrade: Persist.getRolls()
};
}
/**
* React component did mount
*/
componentDidMount() {
this.renderMats();
}
/**
* Convert mats object to string
*/
renderMats() {
const ship = this.props.ship;
let mats = {};
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;
}
for (const g in module.m.blueprint.grades) {
if (g > module.m.blueprint.grade) {
continue;
}
for (const i in module.m.blueprint.grades[g].components) {
if (mats[i]) {
mats[i] += module.m.blueprint.grades[g].components[i] * this.state.matsPerGrade[g];
} else {
mats[i] = module.m.blueprint.grades[g].components[i] * this.state.matsPerGrade[g];
}
}
}
}
}
let matsString = '';
for (const i in mats) {
if (!mats.hasOwnProperty(i)) {
continue;
}
if (mats[i] === 0) {
delete mats[i];
continue;
}
matsString += `${i}: ${mats[i]}\n`;
}
this.setState({ matsList: matsString, mats });
}
/**
* Handler for changing roll amounts
* @param {SyntheticEvent} e React Event
*/
changeHandler(e) {
let grade = e.target.id;
let newState = this.state.matsPerGrade;
newState[grade] = parseInt(e.target.value);
this.setState({ matsPerGrade: newState });
Persist.setRolls(newState);
this.renderMats();
}
/**
* Render the modal
* @return {React.Component} Modal Content
*/
render() {
let translate = this.context.language.translate;
this.changeHandler = this.changeHandler.bind(this);
return <div className='modal' onClick={ (e) => e.stopPropagation() }>
<h2>{translate('PHRASE_SHOPPING_MATS')}</h2>
<label>Grade 1 rolls </label>
<input id={1} type={'number'} min={0} defaultValue={this.state.matsPerGrade[1]} onChange={this.changeHandler} />
<br/>
<label>Grade 2 rolls </label>
<input id={2} type={'number'} min={0} defaultValue={this.state.matsPerGrade[2]} onChange={this.changeHandler} />
<br/>
<label>Grade 3 rolls </label>
<input id={3} type={'number'} min={0} value={this.state.matsPerGrade[3]} onChange={this.changeHandler} />
<br/>
<label>Grade 4 rolls </label>
<input id={4} type={'number'} min={0} value={this.state.matsPerGrade[4]} onChange={this.changeHandler} />
<br/>
<label>Grade 5 rolls </label>
<input id={5} type={'number'} min={0} value={this.state.matsPerGrade[5]} onChange={this.changeHandler} />
<div>
<textarea className='cb json' readOnly value={this.state.matsList} />
</div>
<button className={'r dismiss cap'} onClick={this.context.hideModal}>{translate('close')}</button>
</div>;
}
}

View File

@@ -14,7 +14,10 @@ export default class Modification extends TranslatedComponent {
m: PropTypes.object.isRequired, m: PropTypes.object.isRequired,
name: PropTypes.string.isRequired, name: PropTypes.string.isRequired,
value: PropTypes.number.isRequired, value: PropTypes.number.isRequired,
onChange: PropTypes.func.isRequired onChange: PropTypes.func.isRequired,
onKeyDown: PropTypes.func.isRequired,
modItems: PropTypes.array.isRequired,
handleModChange: PropTypes.func.isRequired
}; };
/** /**
@@ -36,7 +39,6 @@ export default class Modification extends TranslatedComponent {
*/ */
_updateValue(value) { _updateValue(value) {
const name = this.props.name; const name = this.props.name;
let scaledValue = Math.round(Number(value) * 100); let scaledValue = Math.round(Number(value) * 100);
// Limit to +1000% / -99.99% // Limit to +1000% / -99.99%
if (scaledValue > 100000) { if (scaledValue > 100000) {
@@ -57,9 +59,15 @@ export default class Modification extends TranslatedComponent {
/** /**
* Triggered when an update to slider value is finished i.e. when losing focus * Triggered when an update to slider value is finished i.e. when losing focus
*
* pnellesen (24/05/2018): added value check below - this should prevent experimental effects from being recalculated
* with each onBlur event, even when no change has actually been made to the field.
*/ */
_updateFinished() { _updateFinished() {
this.props.onChange(); if (this.props.value != this.state.value) {
this.props.handleModChange(true);
this.props.onChange();
}
} }
/** /**
@@ -86,9 +94,9 @@ export default class Modification extends TranslatedComponent {
} }
return ( return (
<div onBlur={this._updateFinished.bind(this)} className={'cb'} key={name}> <div onBlur={this._updateFinished.bind(this)} className={'cb'} key={name} ref={ modItem => this.props.modItems[name] = modItem }>
<div className={'cb'}>{translate(name, m.grp)}{symbol}</div> <div className={'cb'}>{translate(name, m.grp)}{symbol}</div>
<NumberEditor className={'cb'} style={{ width: '90%', textAlign: 'center' }} step={0.01} stepModifier={1} decimals={2} value={this.state.value} onValueChange={this._updateValue.bind(this)} /> <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 } />
</div> </div>
); );
} }

View File

@@ -6,7 +6,14 @@ import { isEmpty, stopCtxPropagation } from '../utils/UtilityFunctions';
import cn from 'classnames'; import cn from 'classnames';
import { Modifications } from 'coriolis-data/dist'; import { Modifications } from 'coriolis-data/dist';
import Modification from './Modification'; import Modification from './Modification';
import { getBlueprint, blueprintTooltip, setWorst, setBest, setExtreme, setRandom } from '../utils/BlueprintFunctions'; import {
getBlueprint,
blueprintTooltip,
setPercent,
getPercent,
setRandom,
specialToolTip
} from '../utils/BlueprintFunctions';
/** /**
* Modifications menu * Modifications menu
@@ -17,7 +24,8 @@ export default class ModificationsMenu extends TranslatedComponent {
ship: PropTypes.object.isRequired, ship: PropTypes.object.isRequired,
m: PropTypes.object.isRequired, m: PropTypes.object.isRequired,
marker: PropTypes.string.isRequired, marker: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired onChange: PropTypes.func.isRequired,
modButton:PropTypes.object
}; };
/** /**
@@ -30,14 +38,24 @@ export default class ModificationsMenu extends TranslatedComponent {
this._toggleBlueprintsMenu = this._toggleBlueprintsMenu.bind(this); this._toggleBlueprintsMenu = this._toggleBlueprintsMenu.bind(this);
this._toggleSpecialsMenu = this._toggleSpecialsMenu.bind(this); this._toggleSpecialsMenu = this._toggleSpecialsMenu.bind(this);
this._rollWorst = this._rollWorst.bind(this); this._rollFifty = this._rollFifty.bind(this);
this._rollRandom = this._rollRandom.bind(this); this._rollRandom = this._rollRandom.bind(this);
this._rollBest = this._rollBest.bind(this); this._rollBest = this._rollBest.bind(this);
this._rollExtreme = this._rollExtreme.bind(this); this._rollWorst = this._rollWorst.bind(this);
this._reset = this._reset.bind(this); this._reset = this._reset.bind(this);
this._keyDown = this._keyDown.bind(this);
this.modItems = [];// Array to hold various element refs (<li>, <div>, <ul>, etc.)
this.firstModId = null;
this.firstBPLabel = null;// First item in mod menu
this.lastModId = null;
this.selectedModId = null;
this.selectedSpecialId = null;
this.lastNeId = null;// Last number editor id. Used to set focus to last number editor when shift-tab pressed on first element in mod menu.
this.modValDidChange = false; // used to determine if component update was caused by change in modification value.
this._handleModChange = this._handleModChange.bind(this);
this.state = { this.state = {
blueprintMenuOpened: false, blueprintMenuOpened: !(props.m.blueprint && props.m.blueprint.name),
specialMenuOpened: false specialMenuOpened: false
}; };
} }
@@ -52,7 +70,6 @@ export default class ModificationsMenu extends TranslatedComponent {
const { m } = props; const { m } = props;
const { language, tooltip, termtip } = context; const { language, tooltip, termtip } = context;
const translate = language.translate; const translate = language.translate;
const blueprints = []; const blueprints = [];
for (const blueprintName in Modifications.modules[m.grp].blueprints) { for (const blueprintName in Modifications.modules[m.grp].blueprints) {
const blueprint = getBlueprint(blueprintName, m); const blueprint = getBlueprint(blueprintName, m);
@@ -66,9 +83,13 @@ export default class ModificationsMenu extends TranslatedComponent {
const close = this._blueprintSelected.bind(this, blueprintName, grade); const close = this._blueprintSelected.bind(this, blueprintName, grade);
const key = blueprintName + ':' + grade; const key = blueprintName + ':' + grade;
const tooltipContent = blueprintTooltip(translate, blueprint.grades[grade], Modifications.modules[m.grp].blueprints[blueprintName].grades[grade].engineers, m.grp); const tooltipContent = blueprintTooltip(translate, blueprint.grades[grade], Modifications.modules[m.grp].blueprints[blueprintName].grades[grade].engineers, m.grp);
blueprintGrades.unshift(<li key={key} className={classes} style={{ width: '2em' }} onMouseOver={termtip.bind(null, tooltipContent)} onMouseOut={tooltip.bind(null, null)} onClick={close}>{grade}</li>); if (classes.indexOf('active') >= 0) this.selectedModId = key;
blueprintGrades.unshift(<li key={key} tabIndex="0" data-id={key} className={classes} style={{ width: '2em' }} onMouseOver={termtip.bind(null, tooltipContent)} onMouseOut={tooltip.bind(null, null)} onClick={close} onKeyDown={this._keyDown} ref={modItem => this.modItems[key] = modItem}>{grade}</li>);
} }
if (blueprintGrades) { if (blueprintGrades) {
const thisLen = blueprintGrades.length;
if (this.firstModId == null) this.firstModId = blueprintGrades[0].key;
this.lastModId = blueprintGrades[thisLen - 1].key;
blueprints.push(<div key={blueprint.name} className={'select-group cap'}>{translate(blueprint.name)}</div>); blueprints.push(<div key={blueprint.name} className={'select-group cap'}>{translate(blueprint.name)}</div>);
blueprints.push(<ul key={blueprintName}>{blueprintGrades}</ul>); blueprints.push(<ul key={blueprintName}>{blueprintGrades}</ul>);
} }
@@ -76,6 +97,64 @@ export default class ModificationsMenu extends TranslatedComponent {
return blueprints; return blueprints;
} }
/**
* Key down - select module on Enter key, move to next/previous module on Tab/Shift-Tab, close on Esc
* @param {SyntheticEvent} event Event
*
*/
_keyDown(event) {
let className = null;
let elemId = null;
if (event.currentTarget.attributes['class']) className = event.currentTarget.attributes['class'].value;
if (event.currentTarget.attributes['data-id']) elemId = event.currentTarget.attributes['data-id'].value;
if (event.key == 'Enter' && className.indexOf('disabled') < 0 && className.indexOf('active') < 0) {
event.stopPropagation();
if (elemId != null) {
this.modItems[elemId].click();
} else {
event.currentTarget.click();
}
return;
}
if (event.key == 'Tab') {
// Shift-Tab
if(event.shiftKey) {
if (elemId == this.firstModId && elemId != null) {
// Initial modification menu
event.preventDefault();
this.modItems[this.lastModId].focus();
return;
} else if (event.currentTarget.className.indexOf('button-inline-menu') >= 0 && event.currentTarget.previousElementSibling == null && this.lastNeId != null && this.modItems[this.lastNeId] != null) {
// shift-tab on first element in modifications menu. set focus to last number editor field if open
event.preventDefault();
this.modItems[this.lastNeId].lastChild.focus();
return;
} else if (event.currentTarget.className.indexOf('button-inline-menu') >= 0 && event.currentTarget.previousElementSibling == null) {
// shift-tab on button-inline-menu with no number editor
event.preventDefault();
event.currentTarget.parentElement.lastElementChild.focus();
}
} else {
if (elemId == this.lastModId && elemId != null) {
// Initial modification menu
event.preventDefault();
this.modItems[this.firstModId].focus();
return;
} else if (event.currentTarget.className.indexOf('button-inline-menu') >= 0 && event.currentTarget.nextSibling == null && event.currentTarget.nodeName != 'TD') {
// Experimental menu
event.preventDefault();
event.currentTarget.parentElement.firstElementChild.focus();
return;
} else if (event.currentTarget.className == 'cb' && event.currentTarget.parentElement.nextSibling == null) {
event.preventDefault();
this.modItems[this.firstBPLabel].focus();
}
}
}
}
/** /**
* Render the specials * Render the specials
* @param {Object} props React component properties * @param {Object} props React component properties
@@ -86,15 +165,34 @@ export default class ModificationsMenu extends TranslatedComponent {
const { m } = props; const { m } = props;
const { language, tooltip, termtip } = context; const { language, tooltip, termtip } = context;
const translate = language.translate; const translate = language.translate;
const specials = []; const specials = [];
const specialsId = m.missile && Modifications.modules[m.grp]['specials_' + m.missile] ? 'specials_' + m.missile : 'specials'; const specialsId = m.missile && Modifications.modules[m.grp]['specials_' + m.missile] ? 'specials_' + m.missile : 'specials';
if (Modifications.modules[m.grp][specialsId] && Modifications.modules[m.grp][specialsId].length > 0) { if (Modifications.modules[m.grp][specialsId] && Modifications.modules[m.grp][specialsId].length > 0) {
const close = this._specialSelected.bind(this, null); const close = this._specialSelected.bind(this, null);
specials.push(<div style={{ cursor: 'pointer' }} key={ 'none' } onClick={ close }>{translate('PHRASE_NO_SPECIAL')}</div>); specials.push(<div tabIndex="0" style={{ cursor: 'pointer', fontWeight: 'bold' }} className={ 'button-inline-menu warning' } key={ 'none' } data-id={ 'none' } onClick={ close } onKeyDown={this._keyDown} ref={modItem => this.modItems['none'] = modItem}>{translate('PHRASE_NO_SPECIAL')}</div>);
for (const specialName of Modifications.modules[m.grp][specialsId]) { for (const specialName of Modifications.modules[m.grp][specialsId]) {
if (Modifications.specials[specialName].name.search('Legacy') >= 0) {
continue;
}
const classes = cn('button-inline-menu', {
active: m.blueprint && m.blueprint.special && m.blueprint.special.edname == specialName
});
if (classes.indexOf('active') >= 0) this.selectedSpecialId = specialName;
const close = this._specialSelected.bind(this, specialName); const close = this._specialSelected.bind(this, specialName);
specials.push(<div style={{ cursor: 'pointer' }} key={ specialName } onClick={ close }>{translate(Modifications.specials[specialName].name)}</div>); if (m.blueprint && m.blueprint.name) {
let tmp = {};
if (m.blueprint.special) {
tmp = m.blueprint.special;
} else {
tmp = undefined;
}
m.blueprint.special = Modifications.specials[specialName];
let specialTt = specialToolTip(translate, m.blueprint.grades[m.blueprint.grade], m.grp, m, specialName);
m.blueprint.special = tmp;
specials.push(<div tabIndex="0" style={{ cursor: 'pointer' }} className={classes} key={ specialName } data-id={ specialName } onMouseOver={termtip.bind(null, specialTt)} onMouseOut={tooltip.bind(null, null)} onClick={ close } onKeyDown={this._keyDown} ref={modItem => this.modItems[specialName] = modItem}>{translate(Modifications.specials[specialName].name)}</div>);
} else {
specials.push(<div tabIndex="0" style={{ cursor: 'pointer' }} className={classes} key={ specialName } data-id={ specialName }onClick={ close } onKeyDown={this._keyDown} ref={modItem => this.modItems[specialName] = modItem}>{translate(Modifications.specials[specialName].name)}</div>);
}
} }
} }
return specials; return specials;
@@ -111,7 +209,8 @@ export default class ModificationsMenu extends TranslatedComponent {
for (const modName of Modifications.modules[m.grp].modifications) { for (const modName of Modifications.modules[m.grp].modifications) {
if (!Modifications.modifications[modName].hidden) { if (!Modifications.modifications[modName].hidden) {
const key = modName + (m.getModValue(modName) / 100 || 0); const key = modName + (m.getModValue(modName) / 100 || 0);
modifications.push(<Modification key={ key } ship={ ship } m={ m } name={ modName } value={ m.getModValue(modName) / 100 || 0 } onChange={ onChange }/>); 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} />);
} }
} }
return modifications; return modifications;
@@ -136,8 +235,9 @@ export default class ModificationsMenu extends TranslatedComponent {
const blueprint = getBlueprint(fdname, m); const blueprint = getBlueprint(fdname, m);
blueprint.grade = grade; blueprint.grade = grade;
ship.setModuleBlueprint(m, blueprint); ship.setModuleBlueprint(m, blueprint);
setPercent(ship, m, 100);
this.setState({ blueprintMenuOpened: false }); this.setState({ blueprintMenuOpened: false, specialMenuOpened: true });
this.props.onChange(); this.props.onChange();
} }
@@ -168,11 +268,15 @@ export default class ModificationsMenu extends TranslatedComponent {
} }
/** /**
* Provide a 'worst' roll within the information we have * Provide a '50%' roll within the information we have
*/ */
_rollWorst() { _rollFifty() {
const { m, ship } = this.props; const { m, ship } = this.props;
setWorst(ship, m); setPercent(ship, m, 50);
// this will change the values in the modifications. Set modDidChange to true to prevent focus change when component updates
this._handleModChange(true);
this.props.onChange(); this.props.onChange();
} }
@@ -182,6 +286,10 @@ export default class ModificationsMenu extends TranslatedComponent {
_rollRandom() { _rollRandom() {
const { m, ship } = this.props; const { m, ship } = this.props;
setRandom(ship, m); setRandom(ship, m);
// this will change the values in the modifications. Set modDidChange to true to prevent focus change when component updates
this._handleModChange(true);
this.props.onChange(); this.props.onChange();
} }
@@ -190,16 +298,22 @@ export default class ModificationsMenu extends TranslatedComponent {
*/ */
_rollBest() { _rollBest() {
const { m, ship } = this.props; const { m, ship } = this.props;
setBest(ship, m); setPercent(ship, m, 100);
// this will change the values in the modifications. Set modDidChange to true to prevent focus change when component updates
this._handleModChange(true);
this.props.onChange(); this.props.onChange();
} }
/** /**
* Provide an 'extreme' roll within the information we have * Provide a 'worst' roll within the information we have
*/ */
_rollExtreme() { _rollWorst() {
const { m, ship } = this.props; const { m, ship } = this.props;
setExtreme(ship, m); setPercent(ship, m, 0);
// this will change the values in the modifications. Set modDidChange to true to prevent focus change when component updates
this._handleModChange(true);
this.props.onChange(); this.props.onChange();
} }
@@ -210,10 +324,67 @@ export default class ModificationsMenu extends TranslatedComponent {
const { m, ship } = this.props; const { m, ship } = this.props;
ship.clearModifications(m); ship.clearModifications(m);
ship.clearModuleBlueprint(m); ship.clearModuleBlueprint(m);
this.selectedModId = null;
this.selectedSpecialId = null;
this.props.onChange(); this.props.onChange();
} }
/**
* set mod did change boolean
* @param {boolean} b Boolean to determine if a change has been made to a module
*/
_handleModChange(b) {
this.modValDidChange = b;
}
/**
* Set focus on first element in modifications menu
* after it first mounts
*/
componentDidMount() {
let firstEleCn = this.modItems['modMainDiv'].children.length > 0 ? this.modItems['modMainDiv'].children[0].className : null;
if (firstEleCn.indexOf('select-group cap') >= 0) {
this.modItems['modMainDiv'].children[1].firstElementChild.focus();
} else {
this.modItems['modMainDiv'].firstElementChild.focus();
}
}
/**
* Set focus on first element in modifications menu
* if component updates, unless update is due to value change
* in a modification
*/
componentDidUpdate() {
if (!this.modValDidChange) {
if (this.modItems['modMainDiv'].children.length > 0) {
if (this.modItems[this.selectedModId]) {
this.modItems[this.selectedModId].focus();
return;
} else if (this.modItems[this.selectedSpecialId]) {
this.modItems[this.selectedSpecialId].focus();
return;
}
let firstEleCn = this.modItems['modMainDiv'].children[0].className;
if (firstEleCn.indexOf('button-inline-menu') >= 0) {
this.modItems['modMainDiv'].firstElementChild.focus();
} else if (firstEleCn.indexOf('select-group cap') >= 0) {
this.modItems['modMainDiv'].children[1].firstElementChild.focus();
}
}
} else {
this._handleModChange(false);// Need to reset if component update due to value change
}
}
/**
* set focus to the modification menu icon after mod menu is unmounted.
*/
componentWillUnmount() {
if (this.props.modButton) {
this.props.modButton.focus();
}
}
/** /**
* Render the list * Render the list
* @return {React.Component} List * @return {React.Component} List
@@ -226,66 +397,82 @@ export default class ModificationsMenu extends TranslatedComponent {
const _toggleBlueprintsMenu = this._toggleBlueprintsMenu; const _toggleBlueprintsMenu = this._toggleBlueprintsMenu;
const _toggleSpecialsMenu = this._toggleSpecialsMenu; const _toggleSpecialsMenu = this._toggleSpecialsMenu;
const _rollBest = this._rollBest; const _rollFull = this._rollBest;
const _rollExtreme = this._rollExtreme;
const _rollWorst = this._rollWorst; const _rollWorst = this._rollWorst;
const _rollFifty = this._rollFifty;
const _rollRandom = this._rollRandom; const _rollRandom = this._rollRandom;
const _reset = this._reset; const _reset = this._reset;
let blueprintLabel; let blueprintLabel;
let haveBlueprint = false; let haveBlueprint = false;
let blueprintTt; let blueprintTt;
if (m.blueprint && m.blueprint.name) { let blueprintCv;
// TODO: Fix this to actually find the correct blueprint.
if (!m.blueprint || !m.blueprint.name || !m.blueprint.fdname || !Modifications.modules[m.grp].blueprints || !Modifications.modules[m.grp].blueprints[m.blueprint.fdname]) {
this.props.ship.clearModuleBlueprint(m);
this.props.ship.clearModuleSpecial(m);
}
if (m.blueprint && m.blueprint.name && Modifications.modules[m.grp].blueprints[m.blueprint.fdname].grades[m.blueprint.grade]) {
blueprintLabel = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade; blueprintLabel = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
haveBlueprint = true; haveBlueprint = true;
blueprintTt = blueprintTooltip(translate, m.blueprint.grades[m.blueprint.grade], Modifications.modules[m.grp].blueprints[m.blueprint.fdname].grades[m.blueprint.grade].engineers, m.grp); blueprintTt = blueprintTooltip(translate, m.blueprint.grades[m.blueprint.grade], Modifications.modules[m.grp].blueprints[m.blueprint.fdname].grades[m.blueprint.grade].engineers, m.grp);
blueprintCv = getPercent(m);
} }
let specialLabel; let specialLabel;
let specialTt;
if (m.blueprint && m.blueprint.special) { if (m.blueprint && m.blueprint.special) {
specialLabel = m.blueprint.special.name; specialLabel = m.blueprint.special.name;
specialTt = specialToolTip(translate, m.blueprint.grades[m.blueprint.grade], m.grp, m, m.blueprint.special.edname);
} else { } else {
specialLabel = translate('PHRASE_SELECT_SPECIAL'); specialLabel = translate('PHRASE_SELECT_SPECIAL');
} }
const specials = this._renderSpecials(this.props, this.context); const specials = this._renderSpecials(this.props, this.context);
/**
* pnellesen - 05/28/2018 - added additional checks for specials.length below to ensure menus
* display correctly in cases where there are no specials (ex: AFMUs.)
*/
const showBlueprintsMenu = blueprintMenuOpened; const showBlueprintsMenu = blueprintMenuOpened;
const showSpecial = haveBlueprint && specials.length && !blueprintMenuOpened; const showSpecial = haveBlueprint && specials.length && !blueprintMenuOpened;
const showSpecialsMenu = specialMenuOpened; const showSpecialsMenu = specialMenuOpened && specials.length;
const showRolls = haveBlueprint && !blueprintMenuOpened && !specialMenuOpened; const showRolls = haveBlueprint && !blueprintMenuOpened && (!specialMenuOpened || !specials.length);
const showReset = !blueprintMenuOpened && !specialMenuOpened; const showReset = !blueprintMenuOpened && (!specialMenuOpened || !specials.length) && haveBlueprint;
const showMods = !blueprintMenuOpened && !specialMenuOpened; const showMods = !blueprintMenuOpened && (!specialMenuOpened || !specials.length) && haveBlueprint;
if (haveBlueprint) {
this.firstBPLabel = blueprintLabel;
} else {
this.firstBPLabel = 'selectBP';
}
return ( return (
<div <div
className={cn('select', this.props.className)} className={cn('select', this.props.className)}
onClick={(e) => e.stopPropagation() } onClick={(e) => e.stopPropagation() }
onContextMenu={stopCtxPropagation} onContextMenu={stopCtxPropagation}
ref={modItem => this.modItems['modMainDiv'] = modItem}
> >
{ showBlueprintsMenu ? '' : haveBlueprint ? { showBlueprintsMenu | showSpecialsMenu ? '' : haveBlueprint ?
<div className={ cn('section-menu', { selected: blueprintMenuOpened })} style={{ cursor: 'pointer' }} onMouseOver={termtip.bind(null, blueprintTt)} onMouseOut={tooltip.bind(null, null)} onClick={_toggleBlueprintsMenu}>{blueprintLabel}</div> : <div tabIndex="0" className={ cn('section-menu button-inline-menu', { selected: blueprintMenuOpened })} style={{ cursor: 'pointer' }} onMouseOver={termtip.bind(null, blueprintTt)} onMouseOut={tooltip.bind(null, null)} onClick={_toggleBlueprintsMenu} onKeyDown={ this._keyDown } ref={modItems => this.modItems[this.firstBPLabel] = modItems}>{blueprintLabel}</div> :
<div className={ cn('section-menu', { selected: blueprintMenuOpened })} style={{ cursor: 'pointer' }} onClick={_toggleBlueprintsMenu}>{translate('PHRASE_SELECT_BLUEPRINT')}</div> } <div tabIndex="0" className={ cn('section-menu button-inline-menu', { selected: blueprintMenuOpened })} style={{ cursor: 'pointer' }} onClick={_toggleBlueprintsMenu} onKeyDown={ this._keyDown } ref={modItems => this.modItems[this.firstBPLabel] = modItems}>{translate('PHRASE_SELECT_BLUEPRINT')}</div> }
{ showBlueprintsMenu ? this._renderBlueprints(this.props, this.context) : null } { showBlueprintsMenu ? this._renderBlueprints(this.props, this.context) : null }
{ showSpecial ? <div className={ cn('section-menu', { selected: specialMenuOpened })} style={{ cursor: 'pointer' }} onClick={_toggleSpecialsMenu}>{specialLabel}</div> : null } { showSpecial & !showSpecialsMenu ? <div tabIndex="0" className={ cn('section-menu button-inline-menu', { selected: specialMenuOpened })} style={{ cursor: 'pointer' }} onMouseOver={specialTt ? termtip.bind(null, specialTt) : null} onMouseOut={specialTt ? tooltip.bind(null, null) : null} onClick={_toggleSpecialsMenu} onKeyDown={ this._keyDown }>{specialLabel}</div> : null }
{ showSpecialsMenu ? specials : null } { showSpecialsMenu ? specials : null }
{ showRolls || showReset ? { showReset ? <div tabIndex="0" className={'section-menu button-inline-menu warning'} style={{ cursor: 'pointer' }} onClick={_reset} onKeyDown={ this._keyDown } onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_RESET')} onMouseOut={tooltip.bind(null, null)}> { translate('reset') } </div> : null }
{ showRolls ?
<table style={{ width: '100%', backgroundColor: 'transparent' }}> <table style={{ width: '100%', backgroundColor: 'transparent' }}>
<tbody> <tbody>
{ showRolls ? { showRolls ?
<tr> <tr>
<td> { translate('roll') }: </td> <td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: false }) }> { translate('roll') }: </td>
<td style={{ cursor: 'pointer' }} onClick={_rollWorst} onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_WORST')} onMouseOut={tooltip.bind(null, null)}> { translate('worst') } </td> <td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: blueprintCv === 0 }) } style={{ cursor: 'pointer' }} onClick={_rollWorst} onKeyDown={ this._keyDown } onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_WORST')} onMouseOut={tooltip.bind(null, null)}> { translate('0%') } </td>
<td style={{ cursor: 'pointer' }} onClick={_rollBest}onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_BEST')} onMouseOut={tooltip.bind(null, null)}> { translate('best') } </td> <td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: blueprintCv === 50 })} style={{ cursor: 'pointer' }} onClick={_rollFifty} onKeyDown={ this._keyDown } onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_FIFTY')} onMouseOut={tooltip.bind(null, null)}> { translate('50%') } </td>
<td style={{ cursor: 'pointer' }} onClick={_rollExtreme}onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_EXTREME')} onMouseOut={tooltip.bind(null, null)}> { translate('extreme') } </td> <td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: blueprintCv === 100 })} style={{ cursor: 'pointer' }} onClick={_rollFull} onKeyDown={ this._keyDown } onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_BEST')} onMouseOut={tooltip.bind(null, null)}> { translate('100%') } </td>
<td style={{ cursor: 'pointer' }} onClick={_rollRandom} onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_RANDOM')} onMouseOut={tooltip.bind(null, null)}> { translate('random') } </td> <td tabIndex="0" className={ cn('section-menu button-inline-menu', { active: blueprintCv === null || blueprintCv % 50 != 0 })} style={{ cursor: 'pointer' }} onClick={_rollRandom} onKeyDown={ this._keyDown } onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_RANDOM')} onMouseOut={tooltip.bind(null, null)}> { translate('random') } </td>
</tr> : null }
{ showReset ?
<tr>
<td colSpan={'5'} style={{ cursor: 'pointer' }} onClick={_reset}onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_RESET')} onMouseOut={tooltip.bind(null, null)}> { translate('reset') } </td>
</tr> : null } </tr> : null }
</tbody> </tbody>
</table> : null } </table> : null }
{ showMods ? <hr /> : null }
{ showMods ? { showMods ?
<span onMouseOver={termtip.bind(null, 'HELP_MODIFICATIONS_MENU')} onMouseOut={tooltip.bind(null, null)} > <span onMouseOver={termtip.bind(null, 'HELP_MODIFICATIONS_MENU')} onMouseOut={tooltip.bind(null, null)} >
{ this._renderModifications(this.props) } { this._renderModifications(this.props) }

View File

@@ -15,22 +15,34 @@ export default class ShipSummaryTable extends TranslatedComponent {
cargo: PropTypes.number.isRequired, cargo: PropTypes.number.isRequired,
fuel: PropTypes.number.isRequired, fuel: PropTypes.number.isRequired,
marker: PropTypes.string.isRequired, marker: PropTypes.string.isRequired,
pips: PropTypes.object.isRequired
}; };
/**
* The ShipSummaryTable constructor
* @param {Object} props The props
*/
constructor(props) {
super(props);
this.didContextChange = this.didContextChange.bind(this);
this.state = {
shieldColour: 'blue'
};
}
/** /**
* Render the table * Render the table
* @return {React.Component} Summary table * @return {React.Component} Summary table
*/ */
render() { render() {
const { ship, cargo, fuel } = this.props; const { ship, cargo, fuel, pips } = this.props;
let { language, tooltip, termtip } = this.context; let { language, tooltip, termtip } = this.context;
let translate = language.translate; let translate = language.translate;
let u = language.units; let u = language.units;
let formats = language.formats; let formats = language.formats;
let { time, int, round, f1, f2 } = formats; let { time, int, round, f1, f2 } = formats;
let hide = tooltip.bind(null, null); let hide = tooltip.bind(null, null);
const shieldGenerator = ship.findInternalByGroup('sg') || ship.findInternalByGroup('psg');
const shieldGenerator = ship.findInternalByGroup('sg');
const sgClassNames = cn({ warning: shieldGenerator && !ship.shield, muted: !shieldGenerator }); const sgClassNames = cn({ warning: shieldGenerator && !ship.shield, muted: !shieldGenerator });
const sgTooltip = shieldGenerator ? 'TT_SUMMARY_SHIELDS' : 'TT_SUMMARY_SHIELDS_NONFUNCTIONAL'; const sgTooltip = shieldGenerator ? 'TT_SUMMARY_SHIELDS' : 'TT_SUMMARY_SHIELDS_NONFUNCTIONAL';
const timeToDrain = Calc.timeToDrainWep(ship, 4); const timeToDrain = Calc.timeToDrainWep(ship, 4);
@@ -38,9 +50,20 @@ 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 shipBoost = canBoost ? Calc.calcBoost(ship) : 'No Boost';
const armourMetrics = Calc.armourMetrics(ship);
let shieldColour = 'blue';
if (shieldGenerator && shieldGenerator.m.grp === 'psg') {
shieldColour = 'green';
} else if (shieldGenerator && shieldGenerator.m.grp === 'bsg') {
shieldColour = 'purple';
}
this.state = {
shieldColour
};
return <div id='summary'> return <div id='summary'>
<table id='summaryTable'> <table className={'summaryTable'}>
<thead> <thead>
<tr className='main'> <tr className='main'>
<th rowSpan={2} className={ cn({ 'bg-warning-disabled': !canThrust }) }>{translate('speed')}</th> <th rowSpan={2} className={ cn({ 'bg-warning-disabled': !canThrust }) }>{translate('speed')}</th>
@@ -53,11 +76,13 @@ export default class ShipSummaryTable extends TranslatedComponent {
<th rowSpan={2}>{translate('TTD')}</th> <th rowSpan={2}>{translate('TTD')}</th>
{/* <th onMouseEnter={termtip.bind(null, 'heat per second')} onMouseLeave={hide} rowSpan={2}>{translate('HPS')}</th> */} {/* <th onMouseEnter={termtip.bind(null, 'heat per second')} onMouseLeave={hide} rowSpan={2}>{translate('HPS')}</th> */}
<th rowSpan={2}>{translate('cargo')}</th> <th rowSpan={2}>{translate('cargo')}</th>
<th rowSpan={2}>{translate('pax')}</th>
<th rowSpan={2}>{translate('fuel')}</th> <th rowSpan={2}>{translate('fuel')}</th>
<th colSpan={3}>{translate('mass')}</th> <th colSpan={3}>{translate('mass')}</th>
<th onMouseEnter={termtip.bind(null, 'hull hardness', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('hrd')}</th> <th onMouseEnter={termtip.bind(null, 'hull hardness', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('hrd')}</th>
<th rowSpan={2}>{translate('crew')}</th> <th rowSpan={2}>{translate('crew')}</th>
<th onMouseEnter={termtip.bind(null, 'mass lock factor', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('MLF')}</th> <th onMouseEnter={termtip.bind(null, 'mass lock factor', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('MLF')}</th>
<th onMouseEnter={termtip.bind(null, 'TT_SUMMARY_BOOST_TIME', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('boost time')}</th>
</tr> </tr>
<tr> <tr>
<th className='lft'>{translate('max')}</th> <th className='lft'>{translate('max')}</th>
@@ -86,6 +111,7 @@ export default class ShipSummaryTable extends TranslatedComponent {
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_TTD', { cap: 0 })} onMouseLeave={hide}>{timeToDrain === Infinity ? '∞' : time(timeToDrain)}</td> <td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_TTD', { cap: 0 })} onMouseLeave={hide}>{timeToDrain === Infinity ? '∞' : time(timeToDrain)}</td>
{/* <td>{f1(ship.totalHps)}</td> */} {/* <td>{f1(ship.totalHps)}</td> */}
<td>{round(ship.cargoCapacity)}{u.T}</td> <td>{round(ship.cargoCapacity)}{u.T}</td>
<td>{ship.passengerCapacity}</td>
<td>{round(ship.fuelCapacity)}{u.T}</td> <td>{round(ship.fuelCapacity)}{u.T}</td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_HULL_MASS', { cap: 0 })} onMouseLeave={hide}>{ship.hullMass}{u.T}</td> <td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_HULL_MASS', { cap: 0 })} onMouseLeave={hide}>{ship.hullMass}{u.T}</td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_MASS', { cap: 0 })} onMouseLeave={hide}>{int(ship.unladenMass)}{u.T}</td> <td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_MASS', { cap: 0 })} onMouseLeave={hide}>{int(ship.unladenMass)}{u.T}</td>
@@ -93,9 +119,74 @@ export default class ShipSummaryTable extends TranslatedComponent {
<td>{int(ship.hardness)}</td> <td>{int(ship.hardness)}</td>
<td>{ship.crew}</td> <td>{ship.crew}</td>
<td>{ship.masslock}</td> <td>{ship.masslock}</td>
<td>{shipBoost !== 'No Boost' ? formats.time(shipBoost) : 'No Boost'}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
<table className={'summaryTable'}>
<thead className={this.state.shieldColour}>
<tr>
<th 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 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 onMouseEnter={termtip.bind(null, 'TT_SUMMARY_SHIELDS_SCB', { cap: 0 })} onMouseLeave={hide} className='lft'>{`${translate('explosive')} ${translate('HP')}`}</th>
<th onMouseEnter={termtip.bind(null, 'TT_SUMMARY_SHIELDS_SCB', { cap: 0 })} onMouseLeave={hide} className='lft'>{`${translate('kinetic')} ${translate('HP')}`}</th>
<th onMouseEnter={termtip.bind(null, 'TT_SUMMARY_SHIELDS_SCB', { cap: 0 })} onMouseLeave={hide} className='lft'>{`${translate('thermal')} ${translate('HP')}`}</th>
<th onMouseEnter={termtip.bind(null, 'PHRASE_SG_RECOVER', { cap: 0 })} onMouseLeave={hide} className='lft'>{translate('recovery')}</th>
<th onMouseEnter={termtip.bind(null, 'PHRASE_SG_RECHARGE', { cap: 0 })} onMouseLeave={hide} className='lft'>{translate('recharge')}</th>
</tr>
</thead>
<tbody>
<tr>
<td>{translate(shieldGenerator && shieldGenerator.m.grp || 'No Shield')}</td>
<td>{int(ship.shieldExplRes * 100) + '%'}</td>
<td>{int(ship.shieldKinRes * 100) + '%'}</td>
<td>{int(ship.shieldThermRes * 100) + '%'}</td>
<td>{int(sgMetrics && sgMetrics.generator ? sgMetrics.total / sgMetrics.absolute.total : 0)}</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(sgMetrics && sgMetrics.generator ? sgMetrics.total / sgMetrics.thermal.total : 0)}</td>
<td>{sgMetrics && sgMetrics.recover ? formats.time(sgMetrics.recover) : 0}</td>
<td>{sgMetrics && sgMetrics.recharge ? formats.time(sgMetrics.recharge) : 0}</td>
</tr>
</tbody>
<thead>
<tr>
<th 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 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>
</tr>
</thead>
<tbody>
<tr>
<td>{translate(ship && ship.bulkheads && ship.bulkheads.m && ship.bulkheads.m.name || 'No Armour')}</td>
<td>{int(ship.hullExplRes * 100) + '%'}</td>
<td>{int(ship.hullKinRes * 100) + '%'}</td>
<td>{int(ship.hullThermRes * 100) + '%'}</td>
<td>{int(armourMetrics.total / armourMetrics.absolute.total)}</td>
<td>{int(armourMetrics.total / armourMetrics.explosive.total)}</td>
<td>{int(armourMetrics.total / armourMetrics.kinetic.total)}</td>
<td>{int(armourMetrics.total / armourMetrics.thermal.total)}</td>
<td>{int(armourMetrics.modulearmour)}</td>
<td>{int(armourMetrics.moduleprotection * 100) + '%'}</td>
</tr>
</tbody>
</table>
</div>; </div>;
} }
} }

View File

@@ -17,12 +17,12 @@ export default class Slider extends React.Component {
static propTypes = { static propTypes = {
axis: PropTypes.bool, axis: PropTypes.bool,
axisUnit: PropTypes.string, axisUnit: PropTypes.string,// units (T, M, etc.)
max: PropTypes.number, max: PropTypes.number,
min: PropTypes.number, min: PropTypes.number,
onChange: PropTypes.func.isRequired, onChange: PropTypes.func.isRequired,// function which determins percent value
onResize: PropTypes.func, onResize: PropTypes.func,
percent: PropTypes.number.isRequired, percent: PropTypes.number.isRequired,// value of slider
scale: PropTypes.number scale: PropTypes.number
}; };
@@ -35,6 +35,11 @@ export default class Slider extends React.Component {
this._down = this._down.bind(this); this._down = this._down.bind(this);
this._move = this._move.bind(this); this._move = this._move.bind(this);
this._up = this._up.bind(this); this._up = this._up.bind(this);
this._keyup = this._keyup.bind(this);
this._keydown = this._keydown.bind(this);
this._touchstart = this._touchstart.bind(this);
this._touchend = this._touchend.bind(this);
this._updatePercent = this._updatePercent.bind(this); this._updatePercent = this._updatePercent.bind(this);
this._updateDimensions = this._updateDimensions.bind(this); this._updateDimensions = this._updateDimensions.bind(this);
@@ -50,6 +55,7 @@ export default class Slider extends React.Component {
this.left = rect.left; this.left = rect.left;
this.width = rect.width; this.width = rect.width;
this._move(event); this._move(event);
this.touchStartTimer = setTimeout(() => this.sliderInputBox._setDisplay('block'), 1500);
} }
/** /**
@@ -69,11 +75,70 @@ export default class Slider extends React.Component {
* @param {Event} event DOM Event * @param {Event} event DOM Event
*/ */
_up(event) { _up(event) {
this.sliderInputBox.sliderVal.focus();
clearTimeout(this.touchStartTimer);
event.preventDefault(); event.preventDefault();
this.left = null; this.left = null;
this.width = null; this.width = null;
} }
/**
* Key up handler for keyboard.
* display the number field then set focus to it
* when "Enter" key is pressed
* @param {Event} event Keyboard event
*/
_keyup(event) {
switch (event.key) {
case 'Enter':
event.preventDefault();
this.sliderInputBox._setDisplay('block');
return;
default:
return;
}
}
/**
* Key down handler
* increment slider position by +/- 1 when right/left arrow key is pressed or held
* @param {Event} event Keyboard even
*/
_keydown(event) {
let newVal = this.props.percent * this.props.max;
switch (event.key) {
case 'ArrowRight':
newVal += 1;
if (newVal <= this.props.max) this.props.onChange(newVal / this.props.max);
return;
case 'ArrowLeft':
newVal -= 1;
if (newVal >= 0) this.props.onChange(newVal / this.props.max);
return;
default:
return;
}
}
/**
* Touch start handler
* @param {Event} event DOM Event
*
*/
_touchstart(event) {
this.touchStartTimer = setTimeout(() => this.sliderInputBox._setDisplay('block'), 1500);
}
/**
* Touch end handler
* @param {Event} event DOM Event
*
*/
_touchend(event) {
this.sliderInputBox.sliderVal.focus();
clearTimeout(this.touchStartTimer);
}
/** /**
* Determine if the user is still dragging * Determine if the user is still dragging
* @param {SyntheticEvent} event Event * @param {SyntheticEvent} event Event
@@ -136,31 +201,186 @@ export default class Slider extends React.Component {
render() { render() {
let outerWidth = this.state.outerWidth; let outerWidth = this.state.outerWidth;
let { axis, axisUnit, min, max, scale } = this.props; let { axis, axisUnit, min, max, scale } = this.props;
let style = { let style = {
width: '100%', width: '100%',
height: axis ? '2.5em' : '1.5em', height: axis ? '2.5em' : '1.5em',
boxSizing: 'border-box' boxSizing: 'border-box'
}; };
if (!outerWidth) { if (!outerWidth) {
return <svg style={style} ref={node => this.node = node} />; return <svg style={style} ref={node => this.node = node} />;
} }
let margin = MARGIN_LR * scale; let margin = MARGIN_LR * scale;
let width = outerWidth - (margin * 2); let width = outerWidth - (margin * 2);
let pctPos = width * this.props.percent; let pctPos = width * this.props.percent;
return <div><svg
return <svg onMouseUp={this._up} onMouseEnter={this._enter.bind(this)} onMouseMove={this._move} onTouchEnd={this._up} style={style} ref={node => this.node = node}> onMouseUp={this._up} onMouseEnter={this._enter.bind(this)} onMouseMove={this._move} onKeyUp={this._keyup} onKeyDown={this._keydown} style={style} ref={node => this.node = node} tabIndex="0">
<rect className='primary' style={{ opacity: 0.3 }} x={margin} y='0.25em' rx='0.3em' ry='0.3em' width={width} height='0.7em' /> <rect className='primary' style={{ opacity: 0.3 }} x={margin} y='0.25em' rx='0.3em' ry='0.3em' width={width} height='0.7em' />
<rect className='primary-disabled' x={margin} y='0.45em' rx='0.15em' ry='0.15em' width={pctPos} height='0.3em' /> <rect className='primary-disabled' x={margin} y='0.45em' rx='0.15em' ry='0.15em' width={pctPos} height='0.3em' />
<circle className='primary' r={margin} cy='0.6em' cx={pctPos + margin} /> <circle className='primary' r={margin} cy='0.6em' cx={pctPos + margin} />
<rect x={margin} width={width} height='100%' fillOpacity='0' style={{ cursor: 'col-resize' }} onMouseDown={this._down} onTouchMove={this._move} onTouchStart={this._down} /> <rect x={margin} width={width} height='100%' fillOpacity='0' style={{ cursor: 'col-resize' }} onMouseDown={this._down} onTouchMove={this._move} onTouchStart={this._down} onTouchEnd={this._touchend} />
{axis && <g style={{ fontSize: '.7em' }}> {axis && <g style={{ fontSize: '.7em' }}>
<text className='primary-disabled' y='3em' x={margin} style={{ textAnchor: 'middle' }}>{min + axisUnit}</text> <text className='primary-disabled' y='3em' x={margin} style={{ textAnchor: 'middle' }}>{min + axisUnit}</text>
<text className='primary-disabled' y='3em' x='50%' style={{ textAnchor: 'middle' }}>{(min + max / 2) + axisUnit}</text> <text className='primary-disabled' y='3em' x='50%' style={{ textAnchor: 'middle' }}>{(min + max / 2) + axisUnit}</text>
<text className='primary-disabled' y='3em' x='100%' style={{ textAnchor: 'end' }}>{max + axisUnit}</text> <text className='primary-disabled' y='3em' x='100%' style={{ textAnchor: 'end' }}>{max + axisUnit}</text>
</g>} </g>}
</svg>; </svg>
<TextInputBox ref={(tb) => this.sliderInputBox = tb}
onChange={this.props.onChange}
percent={this.props.percent}
axisUnit={this.props.axisUnit}
scale={this.props.scale}
max={this.props.max}
/>
</div>;
} }
} }
/**
* New component to add keyboard support for sliders - works on all devices (desktop, iOS, Android)
**/
class TextInputBox extends React.Component {
static propTypes = {
axisUnit: PropTypes.string,// units (T, M, etc.)
max: PropTypes.number,
onChange: PropTypes.func.isRequired,// function which determins percent value
percent: PropTypes.number.isRequired,// value of slider
scale: PropTypes.number
};
/**
* Determine if the user is still dragging
* @param {Object} props React Component properties
*/
constructor(props) {
super(props);
this._handleFocus = this._handleFocus.bind(this);
this._handleBlur = this._handleBlur.bind(this);
this._handleChange = this._handleChange.bind(this);
this._keyup = this._keyup.bind(this);
this.state = this._getInitialState();
}
/**
* Update input value if slider changes will change props/state
* @param {Object} nextProps React Component properites
* @param {Object} nextState React Component state values
*/
componentWillReceiveProps(nextProps, nextState) {
let nextValue = nextProps.percent * nextProps.max;
// See https://stackoverflow.com/questions/32414308/updating-state-on-props-change-in-react-form
if (nextValue !== this.state.inputValue && nextValue <= nextProps.max) {
this.setState({ inputValue: nextValue });
}
}
/**
* Update slider textbox visibility/values if changes are made to slider
* @param {Object} prevProps React Component properites
* @param {Object} prevState React Component state values
*/
componentDidUpdate(prevProps, prevState) {
if (prevState.divStyle.display == 'none' && this.state.divStyle.display == 'block') {
this.enterTimer = setTimeout(() => this.sliderVal.focus(), 10);
}
if (prevProps.max !== this.props.max && this.state.inputValue > this.props.max) {
// they chose a different module
this.setState({ inputValue: this.props.max });
}
if (this.state.inputValue != prevState.inputValue && prevProps.max == this.props.max) {
this.props.onChange(this.state.inputValue / this.props.max);
}
}
/**
* Set initial state for the textbox.
* We may want to rethink this to
* try and make it a stateless component
* @returns {object} React state object with initial values set
*/
_getInitialState() {
return {
divStyle: { display:'none' },
inputStyle: { width:'4em' },
labelStyle: { marginLeft: '.1em' },
maxLength:5,
size:5,
min:0,
tabIndex:-1,
type:'number',
readOnly: true,
inputValue: this.props.percent * this.props.max
};
}
/**
*
* @param {string} val block or none
*/
_setDisplay(val) {
this.setState({
divStyle: { display:val }
});
}
/**
* Update the input value
* when textbox gets focus
*/
_handleFocus() {
this.setState({
inputValue:this._getValue()
});
}
/**
* Update inputValue when textbox loses focus
*/
_handleBlur() {
this._setDisplay('none');
if (this.state.inputValue !== '') {
this.props.onChange(this.state.inputValue / this.props.max);
} else {
this.setState({
inputValue: this.props.percent * this.props.max
});
}
}
/**
* Get the value in the text box
* @returns {number} inputValue Value of the input box
*/
_getValue() {
return this.state.inputValue;
}
/**
* Update and set limits on input box
* values depending on what user
* has selected
*
* @param {SyntheticEvent} event ReactJs onChange event
*/
_handleChange(event) {
if (event.target.value < 0) {
this.setState({ inputValue: 0 });
} else if (event.target.value <= this.props.max) {
this.setState({ inputValue: event.target.value });
} else {
this.setState({ inputValue: this.props.max });
}
}
/**
* Key up handler for input field.
* If user hits Enter key, blur/close the input field
* @param {Event} event Keyboard event
*/
_keyup(event) {
switch (event.key) {
case 'Enter':
this.sliderVal.blur();
return;
default:
return;
}
}
/**
* Get the value in the text box
* @return {React.Component} Text Input component for Slider
*/
render() {
let { axisUnit, onChange, percent, scale } = this.props;
return <div style={this.state.divStyle}><input style={this.state.inputStyle} value={this._getValue()} min={this.state.min} max={this.props.max} onChange={this._handleChange} onKeyUp={this._keyup} tabIndex={this.state.tabIndex} maxLength={this.state.maxLength} size={this.state.size} onBlur={() => {this._handleBlur();}} onFocus={() => {this._handleFocus();}} type={this.state.type} ref={(ip) => this.sliderVal = ip}/><text className="primary upp" style={this.state.labelStyle}>{this.props.axisUnit}</text></div>;
}
}

View File

@@ -40,6 +40,8 @@ export default class Slot extends TranslatedComponent {
this._contextMenu = wrapCtxMenu(this._contextMenu.bind(this)); this._contextMenu = wrapCtxMenu(this._contextMenu.bind(this));
this._getMaxClassLabel = this._getMaxClassLabel.bind(this); this._getMaxClassLabel = this._getMaxClassLabel.bind(this);
this._keyDown = this._keyDown.bind(this);
this.slotDiv = null;
} }
// Must be implemented by subclasses: // Must be implemented by subclasses:
@@ -73,6 +75,22 @@ export default class Slot extends TranslatedComponent {
this.props.onSelect(null,null); this.props.onSelect(null,null);
} }
/** Key Down handler
* @param {SyntheticEvent} event Event
* ToDo: see if this can be moved up
* we do more or less the same thing
* in every section when Enter key is pressed
* on a focusable item
*
*/
_keyDown(event) {
if (event.key == 'Enter') {
if(event.target.className == 'r') {
this._toggleModifications();
}
this.props.onOpen(event);
}
}
/** /**
* Render the slot * Render the slot
* @return {React.Component} The slot * @return {React.Component} The slot
@@ -104,6 +122,7 @@ export default class Slot extends TranslatedComponent {
ship={ship} ship={ship}
m={m} m={m}
marker={modificationsMarker} marker={modificationsMarker}
modButton = {this.modButton}
/>; />;
} else { } else {
menu = <AvailableModulesMenu menu = <AvailableModulesMenu
@@ -114,6 +133,7 @@ export default class Slot extends TranslatedComponent {
onSelect={onSelect} onSelect={onSelect}
warning={warning} warning={warning}
diffDetails={diffDetails.bind(ship, this.context.language)} diffDetails={diffDetails.bind(ship, this.context.language)}
slotDiv = {this.slotDiv}
/>; />;
} }
} }
@@ -121,7 +141,7 @@ export default class Slot extends TranslatedComponent {
// TODO: implement touch dragging // TODO: implement touch dragging
return ( return (
<div className={cn('slot', dropClass, { selected })} onClick={onOpen} onContextMenu={this._contextMenu} onDragOver={dragOver}> <div className={cn('slot', dropClass, { selected })} onClick={onOpen} onKeyDown={this._keyDown} onContextMenu={this._contextMenu} onDragOver={dragOver} tabIndex="0" ref={slotDiv => this.slotDiv = slotDiv}>
<div className='details-container'> <div className='details-container'>
<div className='sz'>{this._getMaxClassLabel(translate)}</div> <div className='sz'>{this._getMaxClassLabel(translate)}</div>
{slotDetails} {slotDetails}
@@ -131,6 +151,7 @@ export default class Slot extends TranslatedComponent {
); );
} }
/** /**
* Toggle the modifications flag when selecting the modifications icon * Toggle the modifications flag when selecting the modifications icon
*/ */

View File

@@ -18,7 +18,8 @@ export default class SlotSection extends TranslatedComponent {
onCargoChange: PropTypes.func.isRequired, onCargoChange: PropTypes.func.isRequired,
onFuelChange: PropTypes.func.isRequired, onFuelChange: PropTypes.func.isRequired,
code: PropTypes.string.isRequired, code: PropTypes.string.isRequired,
togglePwr: PropTypes.func togglePwr: PropTypes.func,
sectionMenuRefs: PropTypes.object
}; };
/** /**
@@ -32,7 +33,10 @@ export default class SlotSection extends TranslatedComponent {
super(props); super(props);
this.sectionId = sectionId; this.sectionId = sectionId;
this.sectionName = sectionName; this.sectionName = sectionName;
this.ssHeadRef = null;
this.sectionRefArr = this.props.sectionMenuRefs[this.sectionId] = [];
this.sectionRefArr['selectedRef'] = null;
this._getSlots = this._getSlots.bind(this); this._getSlots = this._getSlots.bind(this);
this._selectModule = this._selectModule.bind(this); this._selectModule = this._selectModule.bind(this);
this._getSectionMenu = this._getSectionMenu.bind(this); this._getSectionMenu = this._getSectionMenu.bind(this);
@@ -40,6 +44,8 @@ export default class SlotSection extends TranslatedComponent {
this._drop = this._drop.bind(this); this._drop = this._drop.bind(this);
this._dragOverNone = this._dragOverNone.bind(this); this._dragOverNone = this._dragOverNone.bind(this);
this._close = this._close.bind(this); this._close = this._close.bind(this);
this._keyDown = this._keyDown.bind(this);
this._handleSectionFocus = this._handleSectionFocus.bind(this);
this.state = {}; this.state = {};
} }
@@ -47,7 +53,59 @@ export default class SlotSection extends TranslatedComponent {
// _getSlots() // _getSlots()
// _getSectionMenu() // _getSectionMenu()
// _contextMenu() // _contextMenu()
// componentDidUpdate(prevProps)
/**
* TODO: May either need to send the function to be triggered when Enter key is pressed, or else
* may need a separate keyDown handler for each subclass (StandardSlotSection, HardpointSlotSection, etc.)
* ex: _keyDown(_keyDownfn, event)
*
* @param {SyntheticEvent} event KeyDown event
*/
_keyDown(event) {
if (event.key == 'Enter') {
event.stopPropagation();
if (event.currentTarget.nodeName === 'H1') {
this._openMenu(this.sectionName, event);
} else {
event.currentTarget.click();
}
return;
}
if (event.key == 'Tab') {
if (event.shiftKey) {
if ((event.currentTarget === this.sectionRefArr[this.firstRefId]) && this.sectionRefArr[this.lastRefId]) {
event.preventDefault();
this.sectionRefArr[this.lastRefId].focus();
}
} else {
if ((event.currentTarget === this.sectionRefArr[this.lastRefId]) && this.sectionRefArr[this.firstRefId]) {
event.preventDefault();
this.sectionRefArr[this.firstRefId].focus();
}
}
}
}
/**
* Set focus on appropriate Slot Section Menu element
* @param {Object} focusPrevProps prevProps for componentDidUpdate() from ...SlotSection.jsx
* @param {String} firstRef id of the first ref in ...SlotSection.jsx
* @param {String} lastRef id of the last ref in ...SlotSection.jsx
*
*/
_handleSectionFocus(focusPrevProps, firstRef, lastRef) {
if (this.selectedRefId !== null && this.sectionRefArr[this.selectedRefId]) {
// set focus on the previously selected option for the currently open section menu
this.sectionRefArr[this.selectedRefId].focus();
} else if (this.sectionRefArr[firstRef] && this.sectionRefArr[firstRef] != null) {
// set focus on the first option in the currently open section menu if none have been selected previously
this.sectionRefArr[firstRef].focus();
} else if (this.props.currentMenu == null && focusPrevProps.currentMenu == this.sectionName && this.sectionRefArr['ssHeadRef']) {
// set focus on the section menu header when section menu is closed
this.sectionRefArr['ssHeadRef'].focus();
}
}
/** /**
* Open a menu * Open a menu
* @param {string} menu Menu name * @param {string} menu Menu name
@@ -225,7 +283,7 @@ export default class SlotSection extends TranslatedComponent {
return ( return (
<div id={this.sectionId} className={'group'} onDragLeave={this._dragOverNone}> <div id={this.sectionId} className={'group'} onDragLeave={this._dragOverNone}>
<div className={cn('section-menu', { selected: sectionMenuOpened })} onClick={open} onContextMenu={ctx}> <div className={cn('section-menu', { selected: sectionMenuOpened })} onClick={open} onContextMenu={ctx}>
<h1>{translate(this.sectionName)} <Equalizer/></h1> <h1 tabIndex="0" onKeyDown={this._keyDown} ref={ssHead => this.sectionRefArr['ssHeadRef'] = ssHead}>{translate(this.sectionName)} <Equalizer/></h1>
{sectionMenuOpened ? this._getSectionMenu(translate, this.props.ship) : null } {sectionMenuOpened ? this._getSectionMenu(translate, this.props.ship) : null }
</div> </div>
{this._getSlots()} {this._getSlots()}

View File

@@ -35,6 +35,21 @@ export default class StandardSlot extends TranslatedComponent {
constructor(props) { constructor(props) {
super(props); super(props);
this._modificationsSelected = false; this._modificationsSelected = false;
this._keyDown = this._keyDown.bind(this);
this.modButton = null;
this.slotDiv = null;
}
/**
* Handle Enter key
* @param {SyntheticEvent} event KeyDown event
*/
_keyDown(event) {
if (event.key == 'Enter') {
if(event.target.className == 'r') {
this._toggleModifications();
}
this.props.onOpen(event);
}
} }
/** /**
@@ -48,7 +63,10 @@ export default class StandardSlot extends TranslatedComponent {
let m = slot.m; let m = slot.m;
let classRating = m.class + m.rating; let classRating = m.class + m.rating;
let menu; let menu;
let validMods = m == null ? [] : (Modifications.modules[m.grp].modifications || []); let validMods = m == null || !Modifications.modules[m.grp] ? [] : (Modifications.modules[m.grp].modifications || []);
if (m && m.name && m.name === 'Guardian Hybrid Power Plant') {
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;
@@ -56,6 +74,9 @@ export default class StandardSlot extends TranslatedComponent {
let modTT = translate('modified'); let modTT = translate('modified');
if (m && m.blueprint && m.blueprint.name) { if (m && m.blueprint && m.blueprint.name) {
modTT = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade; modTT = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
if (m.blueprint.special && m.blueprint.special.id >= 0) {
modTT += ', ' + translate(m.blueprint.special.name);
}
modTT = ( modTT = (
<div> <div>
<div>{modTT}</div> <div>{modTT}</div>
@@ -79,6 +100,7 @@ export default class StandardSlot extends TranslatedComponent {
ship={ship} ship={ship}
m={m} m={m}
marker={modificationsMarker} marker={modificationsMarker}
modButton = {this.modButton}
/>; />;
} else { } else {
menu = <AvailableModulesMenu menu = <AvailableModulesMenu
@@ -89,14 +111,15 @@ export default class StandardSlot extends TranslatedComponent {
onSelect={onSelect} onSelect={onSelect}
warning={warning} warning={warning}
diffDetails={diffDetails.bind(ship, this.context.language)} diffDetails={diffDetails.bind(ship, this.context.language)}
slotDiv = {this.slotDiv}
/>; />;
} }
} }
return ( return (
<div className={cn('slot', { selected: this.props.selected })} onClick={this.props.onOpen} onContextMenu={stopCtxPropagation}> <div className={cn('slot', { selected: this.props.selected })} onClick={this.props.onOpen} onKeyDown={this._keyDown} onContextMenu={stopCtxPropagation} tabIndex="0" ref={ slotDiv => this.slotDiv = slotDiv }>
<div className={cn('details-container', { warning: warning && warning(slot.m), disabled: m.grp !== 'bh' && !slot.enabled })}> <div className={cn('details-container', { warning: warning && warning(slot.m), disabled: m.grp !== 'bh' && !slot.enabled })}>
<div className={'sz'}>{slot.maxClass}</div> <div className={'sz'}>{m.grp == 'bh' ? m.name.charAt(0) : slot.maxClass}</div>
<div> <div>
<div className={'l'}>{classRating} {translate(m.name || m.grp)}{m.mods && Object.keys(m.mods).length > 0 ? <span className='r' onMouseOver={termtip.bind(null, modTT)} onMouseOut={tooltip.bind(null, null)}><Modified /></span> : null }</div> <div className={'l'}>{classRating} {translate(m.name || m.grp)}{m.mods && Object.keys(m.mods).length > 0 ? <span className='r' onMouseOver={termtip.bind(null, modTT)} onMouseOut={tooltip.bind(null, null)}><Modified /></span> : null }</div>
<div className={'r'}>{formats.round(mass)}{units.T}</div> <div className={'r'}>{formats.round(mass)}{units.T}</div>
@@ -118,7 +141,7 @@ export default class StandardSlot extends TranslatedComponent {
{ 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 }
{ 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 }
{ validMods.length > 0 ? <div className='r' ><button onClick={this._toggleModifications.bind(this)} onContextMenu={stopCtxPropagation} onMouseOver={termtip.bind(null, 'modifications')} onMouseOut={tooltip.bind(null, null)}><ListModifications /></button></div> : null } { validMods.length > 0 ? <div className='r' tabIndex="0" ref={ modButton => this.modButton = modButton }><button tabIndex="-1" onClick={this._toggleModifications.bind(this)} onContextMenu={stopCtxPropagation} onMouseOver={termtip.bind(null, 'modifications')} onMouseOut={tooltip.bind(null, null)}><ListModifications /></button></div> : null }
</div> </div>
</div> </div>
</div> </div>

View File

@@ -20,12 +20,23 @@ export default class StandardSlotSection extends SlotSection {
super(props, context, 'standard', 'core internal'); super(props, context, 'standard', 'core internal');
this._optimizeStandard = this._optimizeStandard.bind(this); this._optimizeStandard = this._optimizeStandard.bind(this);
this._selectBulkhead = this._selectBulkhead.bind(this); this._selectBulkhead = this._selectBulkhead.bind(this);
this.selectedRefId = null;
this.firstRefId = 'maxjump';
this.lastRefId = 'racer';
}
/**
* Handle focus if the component updates
* @param {Object} prevProps React Component properties
*/
componentDidUpdate(prevProps) {
this._handleSectionFocus(prevProps,this.firstRefId, this.lastRefId);
} }
/** /**
* Use the lightest/optimal available standard modules * Use the lightest/optimal available standard modules
*/ */
_optimizeStandard() { _optimizeStandard() {
this.selectedRefId = 'maxjump';
this.props.ship.useLightestStandard(); this.props.ship.useLightestStandard();
this.props.onChange(); this.props.onChange();
this.props.onCargoChange(this.props.ship.cargoCapacity); this.props.onCargoChange(this.props.ship.cargoCapacity);
@@ -39,6 +50,8 @@ export default class StandardSlotSection extends SlotSection {
* @param {integer} bulkheadIndex Bulkhead to use see Constants.BulkheadNames * @param {integer} bulkheadIndex Bulkhead to use see Constants.BulkheadNames
*/ */
_multiPurpose(shielded, bulkheadIndex) { _multiPurpose(shielded, bulkheadIndex) {
this.selectedRefId = 'multipurpose';
if (bulkheadIndex === 2) this.selectedRefId = 'combat';
ShipRoles.multiPurpose(this.props.ship, shielded, bulkheadIndex); ShipRoles.multiPurpose(this.props.ship, shielded, bulkheadIndex);
this.props.onChange(); this.props.onChange();
this.props.onCargoChange(this.props.ship.cargoCapacity); this.props.onCargoChange(this.props.ship.cargoCapacity);
@@ -51,6 +64,7 @@ export default class StandardSlotSection extends SlotSection {
* @param {Boolean} shielded True if shield generator should be included * @param {Boolean} shielded True if shield generator should be included
*/ */
_optimizeCargo(shielded) { _optimizeCargo(shielded) {
this.selectedRefId = 'trader';
ShipRoles.trader(this.props.ship, shielded); ShipRoles.trader(this.props.ship, shielded);
this.props.onChange(); this.props.onChange();
this.props.onCargoChange(this.props.ship.cargoCapacity); this.props.onCargoChange(this.props.ship.cargoCapacity);
@@ -63,6 +77,7 @@ export default class StandardSlotSection extends SlotSection {
* @param {Boolean} shielded True if shield generator should be included * @param {Boolean} shielded True if shield generator should be included
*/ */
_optimizeMiner(shielded) { _optimizeMiner(shielded) {
this.selectedRefId = 'miner';
ShipRoles.miner(this.props.ship, shielded); ShipRoles.miner(this.props.ship, shielded);
this.props.onChange(); this.props.onChange();
this.props.onCargoChange(this.props.ship.cargoCapacity); this.props.onCargoChange(this.props.ship.cargoCapacity);
@@ -75,6 +90,8 @@ export default class StandardSlotSection extends SlotSection {
* @param {Boolean} planetary True if Planetary Vehicle Hangar (PVH) should be included * @param {Boolean} planetary True if Planetary Vehicle Hangar (PVH) should be included
*/ */
_optimizeExplorer(planetary) { _optimizeExplorer(planetary) {
this.selectedRefId = 'explorer';
if (planetary) this.selectedRefId = 'planetary';
ShipRoles.explorer(this.props.ship, planetary); ShipRoles.explorer(this.props.ship, planetary);
this.props.onChange(); this.props.onChange();
this.props.onCargoChange(this.props.ship.cargoCapacity); this.props.onCargoChange(this.props.ship.cargoCapacity);
@@ -86,6 +103,7 @@ export default class StandardSlotSection extends SlotSection {
* Racer role * Racer role
*/ */
_optimizeRacer() { _optimizeRacer() {
this.selectedRefId = 'racer';
ShipRoles.racer(this.props.ship); ShipRoles.racer(this.props.ship);
this.props.onChange(); this.props.onChange();
this.props.onCargoChange(this.props.ship.cargoCapacity); this.props.onCargoChange(this.props.ship.cargoCapacity);
@@ -229,19 +247,17 @@ export default class StandardSlotSection extends SlotSection {
let planetaryDisabled = this.props.ship.internal.length < 4; let planetaryDisabled = this.props.ship.internal.length < 4;
return <div className='select' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}> return <div className='select' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
<ul> <ul>
<li className='lc' onClick={this._optimizeStandard}>{translate('Maximize Jump Range')}</li> <li className='lc' tabIndex="0" onClick={this._optimizeStandard} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['maxjump'] = smRef}>{translate('Maximize Jump Range')}</li>
</ul> </ul>
<div className='select-group cap'>{translate('roles')}</div> <div className='select-group cap'>{translate('roles')}</div>
<ul> <ul>
<li className='lc' onClick={this._multiPurpose.bind(this, false, 0)}>{translate('Multi-purpose')}</li> <li className='lc' tabIndex="0" onClick={this._multiPurpose.bind(this, false, 0)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['multipurpose'] = smRef}>{translate('Multi-purpose')}</li>
<li className='lc' onClick={this._multiPurpose.bind(this, true, 2)}>{translate('Combat')}</li> <li className='lc' tabIndex="0" onClick={this._multiPurpose.bind(this, true, 2)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['combat'] = smRef}>{translate('Combat')}</li>
<li className='lc' onClick={this._optimizeCargo.bind(this, false)}>{translate('Trader')}</li> <li className='lc' tabIndex="0" onClick={this._optimizeCargo.bind(this, true)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['trader'] = smRef}>{translate('Trader')}</li>
<li className='lc' onClick={this._optimizeCargo.bind(this, true)}>{translate('Shielded Trader')}</li> <li className='lc' tabIndex="0" onClick={this._optimizeExplorer.bind(this, false)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['explorer'] = smRef}>{translate('Explorer')}</li>
<li className='lc' onClick={this._optimizeExplorer.bind(this, false)}>{translate('Explorer')}</li> <li className={cn('lc', { disabled: planetaryDisabled })} tabIndex={planetaryDisabled ? '' : '0'} onClick={!planetaryDisabled && this._optimizeExplorer.bind(this, true)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['planetary'] = smRef}>{translate('Planetary Explorer')}</li>
<li className={cn('lc', { disabled: planetaryDisabled })} onClick={!planetaryDisabled && this._optimizeExplorer.bind(this, true)}>{translate('Planetary Explorer')}</li> <li className='lc' tabIndex="0" onClick={this._optimizeMiner.bind(this, true)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['miner'] = smRef}>{translate('Miner')}</li>
<li className='lc' onClick={this._optimizeMiner.bind(this, false)}>{translate('Miner')}</li> <li className='lc' tabIndex="0" onClick={this._optimizeRacer.bind(this)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['racer'] = smRef}>{translate('Racer')}</li>
<li className='lc' onClick={this._optimizeMiner.bind(this, true)}>{translate('Shielded Miner')}</li>
<li className='lc' onClick={this._optimizeRacer.bind(this)}>{translate('Racer')}</li>
</ul> </ul>
</div>; </div>;
} }

View File

@@ -228,6 +228,72 @@ export class LinkIcon extends SvgIcon {
} }
} }
/**
* Material
*/
export class MatIcon extends SvgIcon {
/**
* Generate the SVG
* @return {React.Component} SVG Contents
*/
svg() {
return<g xmlns="http://www.w3.org/2000/svg">
<path fill="#FF7100" d="M 24.86,4.18
C 24.86,4.18 17.17,7.82 17.17,7.82
17.17,7.82 15.35,14.55 15.35,14.55
15.35,14.55 24.70,9.75 24.70,9.75
24.70,9.75 24.86,4.18 24.86,4.18 Z
M 32.21,17.45
C 32.21,17.45 26.41,11.18 26.41,11.18
26.41,11.18 19.51,11.51 19.51,11.51
19.51,11.51 26.92,19.01 26.92,19.01
26.92,19.01 32.21,17.45 32.21,17.45 Z
M 21.99,28.62
C 21.99,28.62 26.10,21.10 26.10,21.10
26.10,21.10 23.66,14.57 23.66,14.57
23.66,14.57 18.89,24.01 18.89,24.01
18.89,24.01 21.99,28.62 21.99,28.62 Z
M 8.33,22.24
C 8.33,22.24 16.67,23.87 16.67,23.87
16.67,23.87 22.06,19.51 22.06,19.51
22.06,19.51 11.70,17.84 11.70,17.84
11.70,17.84 8.33,22.24 8.33,22.24 Z
M 10.11,7.14
C 10.11,7.14 11.15,15.66 11.15,15.66
11.15,15.66 16.92,19.49 16.92,19.49
16.92,19.49 15.29,9.02 15.29,9.02
15.29,9.02 10.11,7.14 10.11,7.14 Z
M 27.69,2.67
C 27.69,2.67 35.89,16.00 35.89,16.00
35.89,16.00 27.69,29.33 27.69,29.33
27.69,29.33 11.31,29.33 11.31,29.33
11.31,29.33 3.11,16.00 3.11,16.00
3.11,16.00 11.31,2.67 11.31,2.67
11.31,2.67 27.67,2.67 27.67,2.67M 29.16,0.00
C 29.16,0.00 27.69,0.00 27.69,0.00
27.69,0.00 11.31,0.00 11.31,0.00
11.31,0.00 9.84,0.00 9.84,0.00
9.84,0.00 9.06,1.25 9.06,1.25
9.06,1.25 0.87,14.57 0.87,14.57
0.87,14.57 0.00,15.98 0.00,15.98
0.00,15.98 0.87,17.39 0.87,17.39
0.87,17.39 9.06,30.73 9.06,30.73
9.06,30.73 9.84,32.00 9.84,32.00
9.84,32.00 11.31,32.00 11.31,32.00
11.31,32.00 27.69,32.00 27.69,32.00
27.69,32.00 29.16,32.00 29.16,32.00
29.16,32.00 29.94,30.73 29.94,30.73
29.94,30.73 38.13,17.39 38.13,17.39
38.13,17.39 39.00,15.98 39.00,15.98
39.00,15.98 38.13,14.57 38.13,14.57
38.13,14.57 29.94,1.25 29.94,1.25
29.94,1.25 29.16,0.00 29.16,0.00
29.16,0.00 29.16,0.00 29.16,0.00 Z" />
</g>;
}
}
/** /**
* Shopping icon (dollar sign) * Shopping icon (dollar sign)
*/ */

View File

@@ -17,12 +17,23 @@ export default class UtilitySlotSection extends SlotSection {
constructor(props, context) { constructor(props, context) {
super(props, context, 'utility', 'utility mounts'); super(props, context, 'utility', 'utility mounts');
this._empty = this._empty.bind(this); this._empty = this._empty.bind(this);
this.selectedRefId = null;
this.firstRefId = 'emptyall';
this.lastRefId = 'po';
}
/**
* Handle focus if the component updates
* @param {Object} prevProps React Component properties
*/
componentDidUpdate(prevProps) {
this._handleSectionFocus(prevProps,this.firstRefId, this.lastRefId);
} }
/** /**
* Empty all utility slots and close the menu * Empty all utility slots and close the menu
*/ */
_empty() { _empty() {
this.selectedRefId = this.firstRefId;
this.props.ship.emptyUtility(); this.props.ship.emptyUtility();
this.props.onChange(); this.props.onChange();
this._close(); this._close();
@@ -36,6 +47,9 @@ export default class UtilitySlotSection extends SlotSection {
* @param {Synthetic} event Event * @param {Synthetic} event Event
*/ */
_use(group, rating, name, event) { _use(group, rating, name, event) {
this.selectedRefId = group;
if (rating !== null) this.selectedRefId += '-' + rating;
this.props.ship.useUtility(group, rating, name, event.getModifierState('Alt')); this.props.ship.useUtility(group, rating, name, event.getModifierState('Alt'));
this.props.onChange(); this.props.onChange();
this._close(); this._close();
@@ -94,28 +108,28 @@ export default class UtilitySlotSection extends SlotSection {
return <div className='select' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}> return <div className='select' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
<ul> <ul>
<li className='lc' onClick={this._empty}>{translate('empty all')}</li> <li className='lc' tabIndex='0' onClick={this._empty} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['emptyall'] = smRef}>{translate('empty all')}</li>
<li className='optional-hide' style={{ textAlign: 'center', marginTop: '1em' }}>{translate('PHRASE_ALT_ALL')}</li> <li className='optional-hide' style={{ textAlign: 'center', marginTop: '1em' }}>{translate('PHRASE_ALT_ALL')}</li>
</ul> </ul>
<div className='select-group cap'>{translate('sb')}</div> <div className='select-group cap'>{translate('sb')}</div>
<ul> <ul>
<li className='c' onClick={_use.bind(this, 'sb', 'A', null)}>A</li> <li className='c' tabIndex='0' onClick={_use.bind(this, 'sb', 'A', null)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['sb-A'] = smRef}>A</li>
<li className='c' onClick={_use.bind(this, 'sb', 'B', null)}>B</li> <li className='c' tabIndex='0' onClick={_use.bind(this, 'sb', 'B', null)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['sb-B'] = smRef}>B</li>
<li className='c' onClick={_use.bind(this, 'sb', 'C', null)}>C</li> <li className='c' tabIndex='0' onClick={_use.bind(this, 'sb', 'C', null)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['sb-C'] = smRef}>C</li>
<li className='c' onClick={_use.bind(this, 'sb', 'D', null)}>D</li> <li className='c' tabIndex='0' onClick={_use.bind(this, 'sb', 'D', null)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['sb-D'] = smRef}>D</li>
<li className='c' onClick={_use.bind(this, 'sb', 'E', null)}>E</li> <li className='c' tabIndex='0' onClick={_use.bind(this, 'sb', 'E', null)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['sb-E'] = smRef}>E</li>
</ul> </ul>
<div className='select-group cap'>{translate('hs')}</div> <div className='select-group cap'>{translate('hs')}</div>
<ul> <ul>
<li className='lc' onClick={_use.bind(this, 'hs', null, 'Heat Sink Launcher')}>{translate('Heat Sink Launcher')}</li> <li className='lc' tabIndex='0' onClick={_use.bind(this, 'hs', null, 'Heat Sink Launcher')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['hs'] = smRef}>{translate('Heat Sink Launcher')}</li>
</ul> </ul>
<div className='select-group cap'>{translate('ch')}</div> <div className='select-group cap'>{translate('ch')}</div>
<ul> <ul>
<li className='lc' onClick={_use.bind(this, 'ch', null, 'Chaff Launcher')}>{translate('Chaff Launcher')}</li> <li className='lc' tabIndex='0' onClick={_use.bind(this, 'ch', null, 'Chaff Launcher')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['ch'] = smRef}>{translate('Chaff Launcher')}</li>
</ul> </ul>
<div className='select-group cap'>{translate('po')}</div> <div className='select-group cap'>{translate('po')}</div>
<ul> <ul>
<li className='lc' onClick={_use.bind(this, 'po', null, 'Point Defence')}>{translate('Point Defence')}</li> <li className='lc' tabIndex='0' onClick={_use.bind(this, 'po', null, 'Point Defence')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['po'] = smRef}>{translate('Point Defence')}</li>
</ul> </ul>
</div>; </div>;
} }

View File

@@ -6,6 +6,7 @@ import * as FR from './fr';
import * as IT from './it'; import * as IT from './it';
import * as RU from './ru'; import * as RU from './ru';
import * as PL from './pl'; import * as PL from './pl';
import * as PT from './pt';
import * as d3 from 'd3'; import * as d3 from 'd3';
let fallbackTerms = EN.terms; let fallbackTerms = EN.terms;
@@ -25,6 +26,7 @@ export function getLanguage(langCode) {
case 'it': lang = IT; break; case 'it': lang = IT; break;
case 'ru': lang = RU; break; case 'ru': lang = RU; break;
case 'pl': lang = PL; break; case 'pl': lang = PL; break;
case 'pt': lang = PT; break;
default: default:
lang = EN; lang = EN;
} }
@@ -88,5 +90,6 @@ export const Languages = {
es: 'Español', es: 'Español',
fr: 'Français', fr: 'Français',
ru: 'ру́сский', ru: 'ру́сский',
pl: 'polski' pl: 'polski',
pt: 'português'
}; };

View File

@@ -13,607 +13,4 @@ export const formats = {
shortMonths: ['Jan', 'Feb', 'Mrz', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'] shortMonths: ['Jan', 'Feb', 'Mrz', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez']
}; };
export const terms = { export { default as terms } from './de.json';
PHRASE_ALT_ALL: 'Alt + Klick um alle Plätze zu füllen',
PHRASE_BACKUP_DESC: 'Export aller Coriolis-Daten, um sie zu sichern oder um sie zu einem anderen Browser/Gerät zu übertragen.',
PHRASE_CONFIRMATION: 'Sind Sie sicher?',
PHRASE_EXPORT_DESC: 'Ein detaillierter JSON-Export Ihrer Konfiguration für die Verwendung in anderen Websites und Tools',
PHRASE_FASTEST_RANGE: 'aufeinanderfolgende maximale Reichweite/Sprünge',
PHRASE_IMPORT: 'JSON hier einfügen oder importieren',
PHRASE_LADEN: 'Schiffsmasse + Treibstoff + Fracht',
PHRASE_NO_BUILDS: 'Keine Konfigurationen zum Vergleich ausgewählt!',
PHRASE_NO_RETROCH: 'Keine Umrüständerungen',
PHRASE_SELECT_BUILDS: 'Ausstattung zum Vergleich auswählen',
PHRASE_SG_RECHARGE: 'Zeit von 50% bis 100% der Ladung bei vollem SYS Kondensator',
PHRASE_SG_RECOVER: 'Erneuerung (zu 50%) nach Zusammenbruch bei vollem SYS Kondensator',
PHRASE_UNLADEN: 'Schiffsmasse ohne Treibstoff und Fracht',
PHRASE_UPDATE_RDY: 'Update verfügbar! Klicken zum Aktualisieren',
PHRASE_ENGAGEMENT_RANGE: 'Die Distanz zwischen deinem Schiff und seinem Ziel',
PHRASE_SELECT_BLUEPRINT: 'Klicken um eine Bauplan auszuwählen',
PHRASE_BLUEPRINT_WORST: 'Schlechteste Primärwerte für diesen Bauplan',
PHRASE_BLUEPRINT_RANDOM: 'Zufällige Auswahl an Primärwerten für diesen Bauplan (Schlechteste <> Beste)',
PHRASE_BLUEPRINT_BEST: 'Beste Primärwerte für diesen Bauplan',
PHRASE_BLUEPRINT_EXTREME: 'Beste Positive Werte und schlechteste Negative Werte für diesen Bauplan',
PHRASE_BLUEPRINT_RESET: 'Entferne alle Modifikationen und experimentelle Effekte',
PHRASE_SELECT_SPECIAL: 'Klicken um einen experimentellen Effekt auszuwählen',
PHRASE_NO_SPECIAL: 'Keine Experimentellen Effekte',
PHRASE_SHOPPING_LIST: 'Stationen die diese Schiffskonfiguration verkaufen',
PHRASE_REFIT_SHOPPING_LIST: 'Stationen die die benötigten Module verkaufen',
PHRASE_TOTAL_EFFECTIVE_SHIELD: 'Gesamtschaden der von jeder Schadensart absorbiert werden kann (Bei Nutzung aller Schildzellen)',
PHRASE_TIME_TO_LOSE_SHIELDS: 'Schilde werden halten für',
PHRASE_TIME_TO_RECOVER_SHIELDS: 'Schilde werden sich erholen in',
PHRASE_TIME_TO_RECHARGE_SHIELDS: 'Schilde werden sich wieder aufgeladen haben in',
PHRASE_SHIELD_SOURCES: 'Aufschlüsselung der Schildenergiezusammensetzung',
PHRASE_EFFECTIVE_SHIELD: 'Effektive Schildstärke gegen die Unterschiedlichen Schadensarten',
PHRASE_ARMOUR_SOURCES: 'Aufschlüsselung der Hüllenpanzerungszusammensetzung',
PHRASE_EFFECTIVE_ARMOUR: 'Effektive Hüllenpanzerungsstärke gegen die unterschiedlichen Schadensarten',
PHRASE_DAMAGE_TAKEN: '% des rohen Schadens der unterschiedlichen Schadensarten',
PHRASE_TIME_TO_LOSE_ARMOUR: 'Hüllenpanzerung wird halten für',
PHRASE_MODULE_PROTECTION_EXTERNAL: 'Modulpanzerung der Waffenaufhängung',
PHRASE_MODULE_PROTECTION_INTERNAL: 'Modulpanzerung für alle anderen Module',
PHRASE_SHIELD_DAMAGE: 'Aufschlüsselung des kontinuierlichen SPS gegen Schilde',
PHRASE_ARMOUR_DAMAGE: 'Aufschlüsselung des kontinuierlichen SPS gegen Hüllenpanzerung',
PHRASE_TIME_TO_REMOVE_SHIELDS: 'Schilde werden zusammenbrechen in',
TT_TIME_TO_REMOVE_SHIELDS: 'Mit andauerndem Beschuss durch alle Waffen',
PHRASE_TIME_TO_REMOVE_ARMOUR: 'Hüllenpanzerung wird brechen in',
TT_TIME_TO_REMOVE_ARMOUR: 'Mit andauerndem Beschuss durch alle Waffen',
PHRASE_TIME_TO_DRAIN_WEP: 'Leert WAF Energie in',
TT_TIME_TO_DRAIN_WEP: 'Dauer um WAF Energie aufzubrauchen wenn alle Waffen gefeuert werden',
TT_TIME_TO_LOSE_SHIELDS: 'Gegen andauernden Beschuss durch alle Waffen des Gegners',
TT_TIME_TO_LOSE_ARMOUR: 'Gegen andauernden Beschuss durch alle Waffen des Gegners',
TT_MODULE_ARMOUR: 'ModulPanzerung für den Schutz interner Subsysteme (Module)',
TT_MODULE_PROTECTION_EXTERNAL: 'Prozensatz des Schadens der von den Waffenaufhängungen zu den Modulverstärkungen umgeleitet wird',
TT_MODULE_PROTECTION_INTERNAL: 'Prozensatz des Schadens der von den Subsystem (außer Waffen) zu den Modulverstärkungen umgeleitet wird',
TT_EFFECTIVE_SDPS_SHIELDS: 'Effektiver SPS solange die Waffenenergie nicht aufgebraucht wurde',
TT_EFFECTIVENESS_SHIELDS: 'Effektivität im Vergleich zu einem Ziel ohne Widerstände mit 0 PIPS in SYS bei 0m',
TT_EFFECTIVE_SDPS_ARMOUR: 'Effektiver kontinuierlicher Schaden solange der WAF Kondensator nicht aufgebraucht wurde',
TT_EFFECTIVENESS_ARMOUR: 'Effektivität im Vergleich zu einem Ziel ohne Widerstände bei 0m',
PHRASE_EFFECTIVE_SDPS_SHIELDS: 'Effektiver kontinuierlicher SPS gegen Schilde',
PHRASE_EFFECTIVE_SDPS_ARMOUR: 'Effektiver kontinuierlicher SPS gegen Hüllenpanzerung',
TT_SUMMARY_SPEED: 'Mit vollem Tank und 4 PIPS in WAF',
TT_SUMMARY_SPEED_NONFUNCTIONAL: 'Schubdüsen deaktiviert oder maximale Masse überschritten',
TT_SUMMARY_BOOST: 'Mit vollem Tank und 4 PIPS in ANT',
TT_SUMMARY_BOOST_NONFUNCTIONAL: 'Energieverteiler kann nicht genügend Energie für den Boost liefern',
TT_SUMMARY_SHIELDS: 'Rohe Schildstärke, inklusive Schildverstärker',
TT_SUMMARY_SHIELDS_NONFUNCTIONAL: 'Keine Schildgenerator oder Schilde deaktiviert',
TT_SUMMARY_INTEGRITY: 'Schiffsintegrität, einschließlich Hüllenpanzerung und Rumpfhüllenverstärkung',
TT_SUMMARY_HULL_MASS: 'Hüllenmasse, bevor jegliche Module installiert wurde',
TT_SUMMARY_UNLADEN_MASS: 'Hüllenmasse ohne Ladung und Treibstoff',
TT_SUMMARY_LADEN_MASS: 'Hüllenmasse, einschließlich Treibstoff und Ladung',
TT_SUMMARY_DPS: 'Schaden pro Sekunde wenn alle Waffen feuern',
TT_SUMMARY_EPS: 'WAF Kondensator Verbrauch pro Sekunde wenn alle Waffen feuern',
TT_SUMMARY_TTD: 'Zeit um den WAF Kondensator aufzubrauchen wenn alle Waffen feuern und 4 PIPS auf dem WAF Kondesator',
TT_SUMMARY_MAX_SINGLE_JUMP: 'Weitest mögliche Sprungreichweite ohne Ladung und nur genügend Treibstoff für den Sprung selbst',
TT_SUMMARY_UNLADEN_SINGLE_JUMP: 'Weitest mögliche Sprungreichweite ohne Ladung und einem vollen Tank',
TT_SUMMARY_LADEN_SINGLE_JUMP: 'Weitest mögliche Sprungreichweite mit voller Ladung und einem vollen Tank',
TT_SUMMARY_UNLADEN_TOTAL_JUMP: 'Weitest mögliche Sprungreichweite ohne Ladung, einem vollen Tank und der weitest möglichen Sprungreichweite bei jedem Sprung',
TT_SUMMARY_LADEN_TOTAL_JUMP: 'Weitest mögliche Sprungreichweite mit maximaler Ladung, einem vollen Tank und der weitest möglichen Sprungreichweite bei jedem Sprung',
HELP_MODIFICATIONS_MENU: 'Klicke auf eine Zahl um einen neuen Wert einzutragen oder bewege den Regler',
// Other languages fallback to these values
// Only Translate to other languages if the name is different in-game
am: 'Automatische Feldwartung',
bh: 'Hüllenpanzerung',
bl: 'Strahlenlaser',
bsg: 'Bizellengenerator',
c: 'Kanone',
cc: 'Sammeldrohnensteuerung',
ch: 'Düppelwerfer',
cr: 'Laderaum',
cs: 'Ladungssensor',
dc: 'Landecomputer',
ec: 'Elektronische Gegenmaßnahme',
fc: ' Fragmentkanone',
fh: 'Jägerhangar',
fi: 'Frameshift Unterbrecher',
fs: 'Treibstoffsammler',
fsd: 'Frameshiftantrieb',
ft: 'Treibstofftank',
fx: 'Treibstoffdrohnencontroller',
hb: 'Ladelukenbrecherdrohnencontroller',
hr: 'Rumpfhüllenverstärkung',
hs: 'Kühlkörperwerfer',
kw: 'Tötungsbefehlscanner',
ls: 'Lebenserhaltung',
mc: 'Mehrzweckgeschütz ',
ml: 'Erzabbaulaser',
mr: 'Raketengestell',
mrp: 'Modulverstärkung',
nl: 'Minenwerfer',
pa: 'Plasmabeschleuniger',
pas: 'Planetare Annäherungseinheit',
pc: 'Erzsuchersteuerung',
pce: 'Touristen Passagierkabine',
pci: 'Business Klasse Passagierkabine',
pcm: 'Erste Klasse Passagierkabine',
pcq: 'Luxus Passagierkabine',
pd: 'Energieverteiler',
pl: 'Pulslaser',
po: 'Punktverteidigung',
pp: 'Kraftwerk',
psg: 'Prismatischer Schildgenerator',
pv: 'Planetarer Fahrzeughangar',
rf: 'Raffinerie',
rg: 'Schienenkanone',
s: 'Sensoren',
sb: 'Schildverstärker',
sc: 'Himmelskörperscanner',
scb: 'Schildzellenbatterie',
sg: 'Schildgenerator',
ss: 'Oberflächensensor',
t: 'Schubdüsen',
tp: 'Torpedopylone',
ul: 'Salvenlaser',
ws: 'Frameshiftwolkenscanner',
hrd: 'Hüllenhärte',
// Items on the outfitting page
// Notification of restricted slot
emptyrestricted: 'leer (eingeschränkt)',
'damage dealt to': 'Schaden gegen',
'damage received from': 'Schaden durch',
'against shields': 'Gegen Schilde',
'against hull': 'Gegen Hülle',
'total effective shield': 'Effektiver Schild (kombiniert)',
// 'ammo' was overloaded for outfitting page and modul info, so changed to ammunition for outfitting page
ammunition: 'Munition',
// Unit for seconds
secs: 's',
rebuildsperbay: 'Jäger pro Stellpaltz',
// Blueprint rolls
worst: 'Schlecht',
average: 'Durchschnitt',
random: 'Zufall',
best: 'Sehr gut',
extreme: 'Extrem',
reset: 'Zurücksetzen',
// Weapon, offence, defence and movement
dpe: 'Damage per MJ of energy',
dps: 'Schaden pro Sekunde',
sdps: 'Kontinuierlicher Schaden pro Sekunde',
dpssdps: 'Schaden pro Sekunde (kontinuierlicher Schaden pro Sekunde)',
eps: 'Energie pro Sekunde',
epsseps: 'Energie pro Sekunde (kontinuierliche Energie pro Sekunde)',
hps: 'Hitze pro Sekunde',
hpsshps: 'Hitze pro Sekunde (kontinuierliche Hitze pro Sekunde)',
'damage by': 'Schaden von',
'damage from': 'Schaden von',
'shield cells': 'Schildbatterien',
'recovery': 'Erholung',
'recharge': 'Auflaung',
'engine pips': 'Schubpriorität',
'4b': '4 PIPS und Boost',
'speed': 'Tempo',
'pitch': 'Kippen',
'roll': 'Rollen',
'yaw': 'Gieren',
'internal protection': 'Interner Schutz',
'external protection': 'Externer Schutz',
'engagement range': 'Gefechtsreichweite',
'total': 'Insg.',
// Modifications
ammo: 'Maximale Munition',
boot: 'Startzeit',
brokenregen: 'Regenrationsrate (Gebrochene Schilde)',
burst: 'Salve',
burstrof: 'Salven Feuerrate',
clip: 'Munnitionsmagazin',
damage: 'Schaden',
distdraw: 'Energieverteilerverbrauch',
duration: 'Dauer',
eff: 'Effizienz',
engcap: 'Antriebskapazität',
engrate: 'Antrieb Ladungsrate',
explres: 'Explosionswiderstand',
facinglimit: 'Facing limit',
hullboost: 'Hüllenboost',
hullreinforcement: 'Hüllenverstärkung',
integrity: 'Integrität',
jitter: 'Schwankungsbreite',
kinres: 'Kinetischer Widerstand',
maxfuel: 'Maximaler Treibstoff pro Sprung',
mass: 'Masse',
optmass: 'Optimale Masse',
optmul: 'Optimalmultiplikator',
pgen: 'Energiegewinnung',
piercing: 'Durchdringung',
power: 'Energieverbrauch',
protection: 'Schutz',
range: 'Reichweite',
ranget: 'Reichweite/s', // Range in time (for FSD interdictor)
regen: 'Wiederaufladungsrate',
reload: 'Wiederaufladung',
rof: 'Feuerrate',
angle: 'Abtastwinkel',
scanrate: 'Abtastrate',
scantime: 'Abtastzeit',
shield: 'Schild',
shieldboost: 'Schildverstärkung',
shieldreinforcement: 'Schildverstärkung',
shotspeed: 'Schussgeschwidndigkeit',
spinup: 'Aufwärmphase',
syscap: 'Systemkapazität',
sysrate: 'System Ladungsrate',
thermload: 'Thermische Last',
thermres: 'Thermischer Widerstand',
wepcap: 'Waffenkapazität',
weprate: 'Waffen Ladungsrate',
// Shield generators use a different terminology
minmass_sg: 'Minimale Hüllenmasse',
optmass_sg: 'Optimale Hüllenmasse',
maxmass_sg: 'Maximum Hüllenmasse',
minmul_sg: 'Minimale Stärke',
optmul_sg: 'Optimale Stärke',
maxmul_sg: 'Maximale Stärke',
minmass_psg: 'Minimale Hüllenmasse',
optmass_psg: 'Optimale Hüllenmasse',
maxmass_psg: 'Maximum Hüllenmasse',
minmul_psg: 'Minimale Stärke',
optmul_psg: 'Optimale Stärke',
maxmul_psg: 'Maximale Stärke',
minmass_bsg: 'Minimale Hüllenmasse',
optmass_bsg: 'Optimale Hüllenmasse',
maxmass_bsg: 'Maximum Hüllenmasse',
minmul_bsg: 'Minimale Stärke',
optmul_bsg: 'Optimale Stärke',
maxmul_bsg: 'Maximale Stärke',
range_s: 'Typische Emissionsreichweite',
// Damage types
absolute: 'Insgesamt',
explosive: 'Explosiv',
kinetic: 'Kinetisch',
thermal: 'Thermisch',
// Shield sources
generator: 'Generator',
boosters: 'Verstärker',
cells: 'Batterien',
// Armour sources
bulkheads: 'Hüllenpanzerung',
reinforcement: 'Hüllenverstärkung',
// Optional module groups (only these, that are not in the list with the short terms)
'hangars': 'Hangars',
'limpet controllers': 'Drohnensteuerung',
'passenger cabins': 'Passagierkabinen',
'structural reinforcement': 'Strukturverstärkungen',
// Hardpoint module groups
'lasers': 'Laser',
'projectiles': 'Projektilwaffen',
'ordnance': 'Artillerie',
// Armour modules
'Lightweight Alloy': 'leichte Legierung',
'Reinforced Alloy': 'verstärkte Legierung',
'Military Grade Composite': 'Militär-Komposit',
'Mirrored Surface Composite': 'Gespiegelte-Oberfläche-Komposit',
'Reactive Surface Composite': 'Reaktive-Oberfläche-Komposit',
// Scanner modules
'scanners': 'Scanner',
'Basic Discovery Scanner': 'Aufklärungsscanner (einf.)',
'Advanced Discovery Scanner': 'Aufklärungsscanner (fortgeschr.)',
'Detailed Surface Scanner': 'Detail-Oberflächenscanner',
'Intermedia Discovery Scanner': 'Intermedia Discovery Scanner',
// Docking modules
'Standard Docking Computer': 'Standard-Landecomputer',
// Point defence modules
'Point Defence': 'Punktverteidigung',
// Chaff launcher modules
'Chaff Launcher': 'Düppel-Werfer',
// Heat sink launcher modules
'Heat Sink Launcher': 'Kühlkörperwerfer',
// Panel headings and subheadings
'power and costs': 'Energie und Kosten',
'costs': 'Kosten',
'retrofit costs': 'Umrüstkosten',
'reload costs': 'Nachladekosten',
'profiles': 'Profile',
'engine profile': 'Antriebsprofil',
'fsd profile': 'Frameshit Antriebsprofil',
'movement profile': 'Bewegungsprofil',
'damage to opponent\'s shields': 'Gegnerischer Schildschaden',
'damage to opponent\'s hull': 'Gegnerischer Hüllenschaden',
'offence': 'Offensiv',
'defence': 'Defensiv',
'shield metrics': 'Schildwerte',
'raw shield strength': 'Pure Schildstärke',
'shield sources': 'Schildzusammensetzung',
'damage taken': 'Erhaltener Schaden',
'effective shield': 'Effektiver Schildwert',
'armour metrics': 'Panzerungswerte',
'raw armour strength': 'Pure Panzerungsstärke',
'armour sources': 'Panzerungszusammensetzung',
'raw module armour': 'Pure Modulpanzerung',
'effective armour': 'Effektive Panzerung',
'offence metrics': 'Offensivwerte',
'defence metrics': 'Defensivwerte',
// internal module panel header
'Maximize Jump Range': 'Sprungreichweite maximieren',
'roles': 'Rollen',
'Multi-purpose': 'Allrounder',
'Combat': 'Kampf',
'Trader': 'Handel',
'Shielded Trader': 'Handel (mit Schild)',
'Explorer': 'Entdecker',
'Planetary Explorer': 'Planetenentdecker',
'Miner': 'Erzabbau',
'Shielded Miner': 'Erzabbau (mit Schild)',
'Racer': 'Geschwindigkeit',
// Misc items
'fuel carried': 'geladener Treibstoff',
'cargo carried': 'geladene Fracht',
'ship control': 'Energieverteilung',
'opponent': 'Gegner',
'opponent\'s shields': 'Gegnerische Schilde',
'opponent\'s armour': 'Gegenerische Panzerung',
'shield damage sources': 'Schadensquellen (Schild)',
'armour damage sources': 'Schadensquellen (Panzerung)',
'never': 'Niemals',
'stock': 'Standard',
'boost': 'Boost',
'ship': 'Schiff',
'laden': 'Beladen',
'unladen': 'Leer',
'jump range': 'Sprungreichweite',
'total laden': 'gesamt beladen',
'total unladen': 'gesamt leer',
'cargo': 'Fracht',
'hull': 'Hülle',
'Enter Name': 'Name eingeben',
'fuel': 'Tank',
// Items on the ship list page
// Ship list
'manufacturer': 'Hersteller',
'cost': 'Preis',
'size': 'Größe',
'small': 'Klein',
'medium': 'Mittel',
'large': 'Groß',
'agility': 'MNV',
'base': 'Basis',
'armour': 'Panzerung',
'shields': 'Schilde',
'jump': 'Sprung',
'core module classes': 'Basismodulklassen',
'Power Plant': 'Kraftwerk',
th: 'Schubdüsen',
fsd: 'Frameshiftantrieb',
ls: 'Lebenserhaltung',
pd: 'Energieverteiler',
s: 'Sensoren',
'hardpoints': 'Aufhängungen',
'internal compartments': 'Optionale Modulklassen',
// Menu items
'ships': 'Schiffe',
'builds': 'eigene builds',
'compare': 'Vergleiche',
'compare all': 'Alle vergleichen',
'create new': 'Neu anlegen',
'none created': 'Keine angelegt',
'insurance': 'Versicherung',
'discount': 'Nachlass',
'settings': 'Einstellungen',
'module resistances': 'Modulwiderstände',
'comparisons': 'Vergleiche',
'backup': 'Sichern',
'detailed export': 'Detaillierter Export',
'import': 'Importieren',
'delete all': 'Alles löschen',
'about': 'Über',
// Items on the outfitting page
'core internal': 'Intern (basis)',
'optional internal': 'Intern (optional)',
'utility mounts': 'Werkzeug-Steckplätze',
'empty': 'leer',
'empty all': 'Alles leeren',
// Help text
HELP_TEXT: `
<h1>Einführung</h1>
Coriolis ist ein Schiffskonfigurator für Elite:Dangerous. Diese Hilfedatei versorgt dich mit allen Informationen die du benötigst um Coriolis zu benutzen.
<h1>Importiere dein Schiff nach Coriolis</h1>
Often, you will want to start with your existing ship in Coriolis and see how particular changes might affect it, for example upgrading your FSD. There are a number of tools that can be used to import your ship without you having to create it manually. This has the added benefit of copying over any engineering modifications that have taken place as well. </p>
<h2>Importiere dein Schiff von EDDI</h2>
To import your ship from EDDI first ensure that your connection to the Frontier servers&apos; companion API is working. To do this check the &apos;Companion App&apos; tab where you should see "Your connection to the companion app is operational". If not then follow the instructions in the companion app tab in EDDI to connect to the Frontier servers.</p>
Once you have a working companion API connection go to the &apos;Shipyard&apos; tab. At the right-hand side of each ship is an &apos;Export to Coriolis&apos; button that will open your default web browser in Coriolis with the ship&apos;s build. </p>
Note that Internet Explorer and Edge might not import correctly, due to their internal restrictions on URL length. If you find that this is the case then please change your default browser to Chrome. </p>
Also, the imported information does not provide any data on the power priority or enabled status of your cargo hatch. Coriolis sets this item to have a power priority of "5" and to be disabled by default. You can change this after import in the Power Management section. </p>
<h2>Importiere dein Schiff von EDMC</h2>
To import your ship from EDMC once your connection to the Frontier servers&apos; companion API is working go to &apos;Settings -&gt;Configuration&apos; and set the &apos;Preferred Shipyard&apos; to &apos;Coriolis&apos;. Once this is set up clicking on your ship in the main window will open your default web browser in Coriolis with the ship&apos;s build.</p>
Note that Internet Explorer and Edge might not import correctly, due to their internal restrictions on URL length. If you find that this is the case then please change your default browser to Chrome. </p>
<h1>Understanding And Using The Outfitting Panels</h1>
The outfitting page is where you will spend most of your time, and contains the information for your ship. Information on each of the panels is provided below. </p>
<h2>Schlüsselwerte</h2>
Along the top of the screen are some of the key values for your build. This is a handy reference for the values, but more information is provided for the values in the further panels. </p>
Here, along with most places in Coriolis, acronyms will have tooltips explaining what they mean. Hover over the acronym to obtain more detail, or look in the glossary at the end of this help.</p>
All values are the highest possible, assuming that you an optimal setup for that particular value (maximum pips in ENG for speed, minimum fuel for jump range, etc.). This means that these values will not be affected by changes to pip settings. Details of the specific setup for each value are listed in the associated tootip.</p>
<h2>Module</h2>
The next set of panels laid out horizontally across the screen contain the modules you have put in your build. From left to right these are the core modules, the internal modules, the hardpoints and the utility mounts. These represent the available slots in your ship and cannot be altered. Each slot has a class, or size, and in general any module up to a given size can fit in a given slot (exceptions being bulkheads, life support and sensors in core modules and restricted internal slots, which can only take a subset of module depending on their restrictions). </p>
To add a module to a slot left-click on the slot and select the required module. Only the modules capable of fitting in the selected slot will be shown. </p>
To remove a module from a slot right-click on the module. </p>
To move a module from one slot to another drag it. If you instead want to copy the module drag it whilst holding down the &apos;Alt&apos; key. </p>
Clicking on the headings for each set of modules gives you the ability to either select an overall role for your ship (when clicking the core internal header) or a specific module with which you want to fill all applicable slots (when clicking the other headers). </p>
<h2>Schiffskontrolle</h2>
The ship controls allow you to set your pips, boost, and amount of fuel and cargo that your build carries. The changes made here will effect the information supplied in the subsequent panels, giving you a clearer view of what effect different changing these items will have. </p>
Ship control settings are saved as part of a build. </p>
<h2>Gegner</h2>
The opponet selection allows you to choose your opponent. The opponent can be either a stock build of a ship or one of your own saved builds. You can also set the engagement range between you and your opponent. Your selection here will effect the information supplied in the subsequent panels, specifically the Offence and Defence panels. </p>
Opponent settings are saved as part of a build. </p>
<h2>Energieverbrauch und Kosten Untermenü</h2>
<h3>Energie</h3>
The power management panel provides information about power usage and priorities. It allows you to enable and disable individual modules, as well as set power priorities for each module. Disabled modules will not be included in the build&apos;s statistics, with the exception of Shield Cell Banks as they are usually disabled when not in use and only enabled when required. </p>
<h3>Kosten</h3>
The costs panel provides information about the costs for each of your modules, and the total cost and insurance for your build. By default Coriolis uses the standard costs, however discounts for your ship, modules and insurance can be altered in the &apos;Settings&apos; at the top-right of the page.</p>
The retrofit costs provides information about the costs of changing the base build for your ship, or your saved build, to the current build.</p>
The reload costs provides information about the costs of reloading your current build.</p>
<h2>Profile</h2>
Profiles provide graphs that show the general performance of modules in your build
<h3>Antriebsprofil</h3>
The engine profile panel provides information about the capabilities of your current thrusters. The graph shows you how the maximum speed alters with the overall mass of your build. The vertical dashed line on the graph shows your current mass. Your engine profile can be altered by obtaining different thrusters or engineering your existing thrusters, and you can increase your maximum speed by adding pips to the ENG capacitor as well as reducing the amount of fuel and cargo you are carrying as well as reducing the overall weight of the build. You can also temporarily increase your speed by hitting the boost button. </p>
<h3>FSA Profil</h3>
The FSD profile panel provides information about the capabilities of your current frame shift drive. The graph shows you how the maximum jump range alters with the overall mass of your build. The vertical dashed line on the graph shows your current maximum single jump range. Your FSD profile can be altered by obtaining a different FSD or engineering your existing FSD, and you can increase your maximum jump range by reducing the amount of fuel and cargo you are carrying as well as reducing the overall weight of the build, </p>
<h3>Bewegungsprofil</h3>
The movement profile panel provides information about the capabilities of your current thrusters with your current overall mass and ENG pips settings. The diagram shows your ability to move and rotate in the different axes:
<dl>
<dt>Geschwindigkeit</dt><dd>The fastest the ship can move, in metres per second</dd>
<dt>Kippen</dt><dd>The fastest the ship can raise or lower its nose, in degrees per second</dd>
<dt>Rollen</dt><dd>The fastest the ship can roll its body, in degrees per second</dd>
<dt>Gieren</dt><dd>The fastest the ship can turn its nose left or right, in degrees per second</dd>
</dl>
Your movement profile can be altered by obtaining different thrusters or engineering your existing thrusters, and you can increase your movement values by adding pips to the ENG capacitor as well as reducing the amount of fuel and cargo you are carrying as well as reducing the overall weight of the build. You can also temporarily increase your movement profile by hitting the boost button. </p>
<h3>Schadensprofil</h3>
The damage profile provides two graphs showing how the the build&apos;s damage to the opponent&apos;s shields and hull change with engagement range. The vertical dashed line on the graph shows your current engagement range. This combines information about the build&apos;s weapons with the opponent&apos;s shields and hull to provide an accurate picture of sustained damage that can be inflicted on the opponent. </p>
<h2>Offensive</h2>
<h3>Zusammenfassung</h3>
The offence summary provides per-weapon information about sustained damage per second inflicted to shields and hull, along with a measure of effectiveness of that weapon. The effectiveness value has a tooltip that provides a breakdown of the effectiveness, and can include reductions or increases due to range, resistance, and either power distributor (for shields) or hardness (for hull). The final effectiveness value is calculated by multiplying these percentages together. </p>
<h3>Offensivwerte</h3>
The offence metrics panel provides information about your offence. </p>
Time to drain is a measure of how quickly your WEP capacitor will drain when firing all weapons. It is affected by the number of pips you have in your WEP capacitor, with more pips resulting in a higher WEP recharge rate and hence a longer time to drain. </p>
The next value is the time it will take you to remove your opponent&apos;s shields. This assumes that you have 100% time on target and that your engagement range stays constant. Note that if your time to remove shields is longer than your time to drain this assumes that you continue firing throughout, inflicting lower damage due to the reduced energy in your WEP capacitor. </p>
The next value is the time it will take you to remove your opponent&apos;s armour. This follows the same logic as the time to remove shields. </p>
<h3>Schadensquellen (Schilde)</h3>
The shield damage sources provides information about the sources of damage to your opponent by damage type. For each applicable type of damage (absolute explosive, kinetic, thermal) a sustained damage per second value is provided. </p>
<h3>Schadensquelle (Hülle)</h3>
The hull damage sources provides information about the sources of damage to your opponent by damage type. For each applicable type of damage (absolute explosive, kinetic, thermal) a sustained damage per second value is provided. </p>
<h2>Defensive</h2>
<h3>Schildwerte</h3>
The shield metrics provides information about your shield defence. </p>
Raw shield strength is the sum of the shield from your generator, boosters and shield cell banks. A tooltip provides a breakdown of these values. </p>
The time the shields will hold for is the time it will take your opponent&apos; to remove your shields. This assumes that they have 100% time on target and that the engagement range stays constant. It also assumes that you fire all of your shield cell banks prior to your shields being lost. </p>
The time the shields will recover in is the time it will take your shields to go from collapsed (0%) to recovered (50%). This is affected by the number of pips you have in your SYS capacitor. </p>
The time the shields will recharge in is the time it will take your shields to go from recovered (50%) to full (100%). This is affected by the number of pips you have in your SYS capacitor. </p>
</h3>Schildstärke (Zusammensetzung)</h3>
This chart provides information about the sources of your shields. For each applicable source of shields (generator, boosters, shield cell banks) a value is provided. </p>
</h3>Schadensauswirkung</h3>
This graph shows how the initial damage from the weapons of each type are reduced before their damage is applied to the shields. For each type of damage (absolute, explosive, kinetic, thermal) a percentage of the initial damage is provided. A tooltip provides a breakdown of these values. </p>
</h3>Effektiver Schildwert</h3>
This graph shows the effective shield for each damage type, found by dividing the raw shield value by the damage taken for that type. </p>
<h3>Panzerungswerte</h3>
The armour metrics provides information about your armour defence. </p>
Raw armour strength is the sum of the armour from your bulkheads and hull reinforcement packages. A tooltip provides a breakdown of these values. </p>
The time the armour will hold for is the time it will take your opponent&apos; to take your armour to 0. This assumes that they have 100% time on target, the engagement range stays constant, and that all damage is dealt to the armour rather than modules. </p>
Raw module armour is the sum of the protection from your module reinforcement packages. </p>
Protection for hardpoints is the amount of protection that your module reinforcement packages provide to hardpoints. This percentage of damage to the hardpoints will be diverted to the module reinforcement packages. </p>
Protection for all other modules is the amount of protection that your module reinforcement packages provide to everything other than hardpoints. This percentage of damage to the modules will be diverted to the module reinforcement packages. </p>
</h3>Hüllenpanzerung (Zusammensetzung)</h3>
This chart provides information about the sources of your armour. For each applicable source of shields (bulkheads, hull reinforcement packages) a value is provided. </p>
</h3>Schadensauswirkung</h3>
This graph shows how the initial damage from the weapons of each type are reduced before their damage is applied to the armour. For each type of damage (absolute, explosive, kinetic, thermal) a percentage of the initial damage is provided. A tooltip provides a breakdown of these values. </p>
</h3>Effektive Hüllenpanzerung</h3>
This graph shows the effective armour for each damage type, found by dividing the raw armour value by the damage taken for that type. </p>
<h1>Tastaturkommandos</h1>
<dl>
<dt>Ctrl-b</dt><dd>Boost an-/auschalten</dd>
<dt>Ctrl-e</dt><dd>open Export Dialog (ausschließlich Ausstattungsseite)</dd>
<dt>Ctrl-h</dt><dd>Öffne den Hilfedialog</dd>
<dt>Ctrl-i</dt><dd>Öffne den Importdialog</dd>
<dt>Ctrl-o</dt><dd>Öffne den Kurzlinkdialog</dd>
<dt>Ctrl-left-arrow</dt><dd>Erhöhe den SYS Kondensator</dd>
<dt>Ctrl-up-arrow</dt><dd>Erhöhe den ANT Kondensator</dd>
<dt>Ctrl-right-arrow</dt><dd>Erhöhe den WAF Kondensator</dd>
<dt>Ctrl-down-arrow</dt><dd>Setze den Energieverteiler zurück</dd>
<dt>Esc</dt><dd>Schließe jeden offenen Dialog</dd>
</dl>
<h1>Glossar</h1>
<dl>
<dt>Absoluter Schaden</dt><dd>A type of damage, without any protection. Absolute damage is always dealt at 100% regardless of if the damage is to shields, hull or modules, and irrespective of resistances</dd>
<dt>SPS</dt><dd>Schaden pro Sekunde; the amount of damage that a weapon or a ship can deal per second to a target under optimum conditions</dd>
<dt>EPS</dt><dd>Energie pro Sekunde; the amount of energy that a weapon or a ship drains from the weapons capacitor per second when firing</dd>
<dt>HPS</dt><dd>Hitze pro Sekunde; the amount of heat that a weapon or a ship generates per second when firing</dd>
<dt>Effektivität</dt><dd>A comparison of the maximum DPS of a given weapon to the actual DPS of the given weapon in a specific situation. DPS can be reduced by range to the target, the target&apos;s hull and shield resistances, and the target&apos;s hardness</dd>
<dt>Explosiver Schaden</dt><dd>A type of damage, protected against by explosive resistance</dd>
<dt>Hüllenhärte</dt><dd>The inherent resistance to damage of a ship&apos;s hull. Hardness is defined on a per-ship basis and there is currently nothing that can be done to change it. Hardness of a ship&apos;s hull is compared to the piercing of weapons: if piercing is higher than hardness the weapon does 100% damage, otherwise it does a fraction of its damage calculated as piercing/hardness</dd>
<dt>Schadensabfall</dt><dd>The distance at which a weapons starts to do less damage than its stated DPS</dd>
<dt>Kinetischer Schaden</dt><dd>A type of damage, protected against by kinetic resistance</dd>
<dt>KSPS</dt><dd>Kontinuierlicher Schaden pro Sekunde; the amount of damage that a weapon or a ship can deal per second to a target, taking in to account ammunition reload</dd>
<dt>KEPS</dt><dd>Kontinuierliche Energie pro Sekunde; the amount of energy that a weapon or a ship drains from the weapons capacitor per second when firing, taking in to account ammunition reload</dd>
<dt>KHPS</dt><dd>Kontinuierliche Hitze pro Sekunde; the amount of heat that a weapon or a ship generates per second when firing, taking in to account ammunition reload</dd>
<dt>Thermischer Schaden</dt><dd>A type of damage, protected against by thermal resistance</dd>
</dl>
`,
};

358
src/app/i18n/de.json Normal file

File diff suppressed because one or more lines are too long

View File

@@ -13,496 +13,4 @@ export const formats = {
shortMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] shortMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
}; };
export const terms = { export { default as terms } from './en.json';
PHRASE_ALT_ALL: 'Alt + Click to fill all slots',
PHRASE_BACKUP_DESC: 'Backup of all Coriolis data to save or transfer to another browser/device',
PHRASE_CONFIRMATION: 'Are you sure?',
PHRASE_EXPORT_DESC: 'A detailed JSON export of your build for use in other sites and tools',
PHRASE_FASTEST_RANGE: 'Consecutive max range jumps',
PHRASE_IMPORT: 'Paste JSON or import here',
PHRASE_LADEN: 'Ship mass + fuel + cargo',
PHRASE_NO_BUILDS: 'No builds added to comparison!',
PHRASE_NO_RETROCH: 'No Retrofitting changes',
PHRASE_SELECT_BUILDS: 'Select builds to compare',
PHRASE_SG_RECHARGE: 'Time from 50% to 100% charge, assuming full SYS capacitor to start with',
PHRASE_SG_RECOVER: 'Time from 0% to 50% charge, assuming full SYS capacitor to start with',
PHRASE_UNLADEN: 'Ship mass excluding fuel and cargo',
PHRASE_UPDATE_RDY: 'Update Available! Click to refresh',
PHRASE_ENGAGEMENT_RANGE: 'The distance between your ship and its target',
PHRASE_SELECT_BLUEPRINT: 'Click to select a blueprint',
PHRASE_BLUEPRINT_WORST: 'Worst primary values for this blueprint',
PHRASE_BLUEPRINT_RANDOM: 'Random selection between worst and best primary values for this blueprint',
PHRASE_BLUEPRINT_BEST: 'Best primary values for this blueprint',
PHRASE_BLUEPRINT_EXTREME: 'Best beneficial and worst detrimental primary values for this blueprint',
PHRASE_BLUEPRINT_RESET: 'Remove all modifications and blueprint',
PHRASE_SELECT_SPECIAL: 'Click to select an experimental effect',
PHRASE_NO_SPECIAL: 'No experimental effect',
PHRASE_SHOPPING_LIST: 'Stations that sell this build',
PHRASE_REFIT_SHOPPING_LIST: 'Stations that sell required modules',
PHRASE_TOTAL_EFFECTIVE_SHIELD: 'Total amount of damage that can be taken from each damage type, if using all shield cells',
PHRASE_TIME_TO_LOSE_SHIELDS: 'Shields will hold for',
PHRASE_TIME_TO_RECOVER_SHIELDS: 'Shields will recover in',
PHRASE_TIME_TO_RECHARGE_SHIELDS: 'Shields will recharge in',
PHRASE_SHIELD_SOURCES: 'Breakdown of the supply of shield energy',
PHRASE_EFFECTIVE_SHIELD: 'Effective shield strength against different damage types',
PHRASE_ARMOUR_SOURCES: 'Breakdown of the supply of armour',
PHRASE_EFFECTIVE_ARMOUR: 'Effective armour strength against different damage types',
PHRASE_DAMAGE_TAKEN: '% of raw damage taken for different damage types',
PHRASE_TIME_TO_LOSE_ARMOUR: 'Armour will hold for',
PHRASE_MODULE_PROTECTION_EXTERNAL: 'Protection for hardpoints',
PHRASE_MODULE_PROTECTION_INTERNAL: 'Protection for all other modules',
PHRASE_SHIELD_DAMAGE: 'Breakdown of sources for sustained DPS against shields',
PHRASE_ARMOUR_DAMAGE: 'Breakdown of sources for sustained DPS against armour',
PHRASE_TIME_TO_REMOVE_SHIELDS: 'Will remove shields in',
TT_TIME_TO_REMOVE_SHIELDS: 'With sustained fire by all weapons',
PHRASE_TIME_TO_REMOVE_ARMOUR: 'Will remove armour in',
TT_TIME_TO_REMOVE_ARMOUR: 'With sustained fire by all weapons',
PHRASE_TIME_TO_DRAIN_WEP: 'Will drain WEP in',
TT_TIME_TO_DRAIN_WEP: 'Time to drain WEP capacitor with all weapons firing',
TT_TIME_TO_LOSE_SHIELDS: 'Against sustained fire from all opponent\'s weapons',
TT_TIME_TO_LOSE_ARMOUR: 'Against sustained fire from all opponent\'s weapons',
TT_MODULE_ARMOUR: 'Armour protecting against module damage',
TT_MODULE_PROTECTION_EXTERNAL: 'Percentage of damage diverted from hardpoints to module reinforcement packages',
TT_MODULE_PROTECTION_INTERNAL: 'Percentage of damage diverted from non-hardpoint modules to module reinforcement packages',
TT_EFFECTIVE_SDPS_SHIELDS: 'Actual sustained DPS whilst WEP capacitor is not empty',
TT_EFFECTIVENESS_SHIELDS: 'Effectivness compared to hitting a 0-resistance target with 0 pips to SYS at 0m',
TT_EFFECTIVE_SDPS_ARMOUR: 'Actual sustained DPS whilst WEP capacitor is not empty',
TT_EFFECTIVENESS_ARMOUR: 'Effectivness compared to hitting a 0-resistance target at 0m',
PHRASE_EFFECTIVE_SDPS_SHIELDS: 'SDPS against shields',
PHRASE_EFFECTIVE_SDPS_ARMOUR: 'SDPS against armour',
TT_SUMMARY_SPEED: 'With full fuel tank and 4 pips to ENG',
TT_SUMMARY_SPEED_NONFUNCTIONAL: 'Thrusters powered off or over maximum mass with full fuel and cargo loads',
TT_SUMMARY_BOOST: 'With full fuel tank and 4 pips to ENG',
TT_SUMMARY_BOOST_NONFUNCTIONAL: 'Power distributor not able to supply enough power to boost',
TT_SUMMARY_SHIELDS: 'Raw shield strength, including boosters',
TT_SUMMARY_SHIELDS_NONFUNCTIONAL: 'No shield generator or shield generator powered off',
TT_SUMMARY_INTEGRITY: 'Ship integrity, including bulkheads and hull reinforcement packages',
TT_SUMMARY_HULL_MASS: 'Mass of the hull prior to any modules being installed',
TT_SUMMARY_UNLADEN_MASS: 'Mass of the hull and modules prior to any fuel or cargo',
TT_SUMMARY_LADEN_MASS: 'Mass of the hull and modules with full fuel and cargo',
TT_SUMMARY_DPS: 'Damage per second with all weapons firing',
TT_SUMMARY_EPS: 'WEP capacitor consumed per second with all weapons firing',
TT_SUMMARY_TTD: 'Time to drain WEP capacitor with all weapons firing and 4 pips to WEP',
TT_SUMMARY_MAX_SINGLE_JUMP: 'Farthest possible jump range with no cargo and only enough fuel for the jump itself',
TT_SUMMARY_UNLADEN_SINGLE_JUMP: 'Farthest possible jump range with no cargo and a full fuel tank',
TT_SUMMARY_LADEN_SINGLE_JUMP: 'Farthest possible jump range with full cargo and a full fuel tank',
TT_SUMMARY_UNLADEN_TOTAL_JUMP: 'Farthest possible range with no cargo, a full fuel tank, and jumping as far as possible each time',
TT_SUMMARY_LADEN_TOTAL_JUMP: 'Farthest possible range with full cargo, a full fuel tank, and jumping as far as possible each time',
HELP_MODIFICATIONS_MENU: 'Click on a number to enter a new value, or drag along the bar for small changes',
// Other languages fallback to these values
// Only Translate to other languages if the name is different in-game
am: 'Auto Field-Maintenance Unit',
bh: 'Bulkheads',
bl: 'Beam Laser',
bsg: 'Bi-Weave Shield Generator',
c: 'Cannon',
cc: 'Collector Limpet Controller',
ch: 'Chaff Launcher',
cr: 'Cargo Rack',
cs: 'Manifest Scanner',
dc: 'Docking Computer',
ec: 'Electronic Countermeasure',
fc: 'Fragment Cannon',
fh: 'Fighter Hangar',
fi: 'FSD Interdictor',
fs: 'Fuel Scoop',
fsd: 'Frame Shift Drive',
ft: 'Fuel Tank',
fx: 'Fuel Transfer Limpet Controller',
hb: 'Hatch Breaker Limpet Controller',
hr: 'Hull Reinforcement Package',
hs: 'Heat Sink Launcher',
kw: 'Kill Warrant Scanner',
ls: 'Life Support',
mc: 'Multi-cannon',
ml: 'Mining Laser',
mr: 'Missile Rack',
mrp: 'Module Reinforcement Package',
nl: 'Mine Launcher',
pa: 'Plasma Accelerator',
pas: 'Planetary Approach Suite',
pc: 'Prospector Limpet Controller',
pce: 'Economy Class Passenger Cabin',
pci: 'Business Class Passenger Cabin',
pcm: 'First Class Passenger Cabin',
pcq: 'Luxury Passenger Cabin',
pd: 'power distributor',
pl: 'Pulse Laser',
po: 'Point Defence',
pp: 'Power Plant',
psg: 'Prismatic Shield Generator',
pv: 'Planetary Vehicle Hangar',
rf: 'Refinery',
rg: 'Rail Gun',
s: 'Sensors',
sb: 'Shield Booster',
sc: 'Stellar Scanners',
scb: 'Shield Cell Bank',
sg: 'Shield Generator',
ss: 'Surface Scanners',
t: 'thrusters',
tp: 'Torpedo Pylon',
ul: 'Burst Laser',
ws: 'Frame Shift Wake Scanner',
// Items on the outfitting page
// Notification of restricted slot
emptyrestricted: 'empty (restricted)',
'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',
// 'ammo' was overloaded for outfitting page and modul info, so changed to ammunition for outfitting page
ammunition: 'Ammo',
// Unit for seconds
secs: 's',
rebuildsperbay: 'Rebuilds per bay',
// Blueprint rolls
worst: 'Worst',
average: 'Average',
random: 'Random',
best: 'Best',
extreme: 'Extreme',
reset: 'Reset',
// Weapon, offence, defence and movement
dpe: 'Damage per MJ of energy',
dps: 'Damage per second',
sdps: 'Sustained damage per second',
dpssdps: 'Damage per second (sustained damage per second)',
eps: 'Energy per second',
epsseps: 'Energy per second (sustained energy per second)',
hps: 'Heat per second',
hpsshps: 'Heat per second (sustained heat per second)',
'damage by': 'Damage by',
'damage from': 'Damage from',
'shield cells': 'Shield cells',
'recovery': 'Recovery',
'recharge': 'Recharge',
'engine pips': 'Engine Pips',
'4b': '4 pips and boost',
'speed': 'Speed',
'pitch': 'Pitch',
'roll': 'Roll',
'yaw': 'Yaw',
'internal protection': 'Internal protection',
'external protection': 'External protection',
'engagement range': 'Engagement range',
'total': 'Total',
// Modifications
ammo: 'Ammunition maximum',
boot: 'Boot time',
brokenregen: 'Broken regeneration rate',
burst: 'Burst',
burstrof: 'Burst rate of fire',
clip: 'Ammunition clip',
damage: 'Damage',
distdraw: 'Distributor draw',
duration: 'Duration',
eff: 'Efficiency',
engcap: 'Engines capacity',
engrate: 'Engines recharge rate',
explres: 'Explosive resistance',
facinglimit: 'Facing limit',
hullboost: 'Hull boost',
hullreinforcement: 'Hull reinforcement',
integrity: 'Integrity',
jitter: 'Jitter',
kinres: 'Kinetic resistance',
maxfuel: 'Maximum fuel per jump',
mass: 'Mass',
optmass: 'Optimal mass',
optmul: 'Optimal multiplier',
pgen: 'Power generation',
piercing: 'Piercing',
power: 'Power draw',
protection: 'Protection',
range: 'Range',
ranget: 'Range', // Range in time (for FSD interdictor)
regen: 'Regeneration rate',
reload: 'Reload',
rof: 'Rate of fire',
angle: 'Scan angle',
scanrate: 'Scan rate',
scantime: 'Scan time',
shield: 'Shield',
shieldboost: 'Shield boost',
shieldreinforcement: 'Shield reinforcement',
shotspeed: 'Shot speed',
spinup: 'Spin up time',
syscap: 'Systems capacity',
sysrate: 'Systems recharge rate',
thermload: 'Thermal load',
thermres: 'Thermal resistance',
wepcap: 'Weapons capacity',
weprate: 'Weapons recharge rate',
// Shield generators use a different terminology
minmass_sg: 'Minimum hull mass',
optmass_sg: 'Optimal hull mass',
maxmass_sg: 'Maximum hull mass',
minmul_sg: 'Minimum strength',
optmul_sg: 'Optimal strength',
maxmul_sg: 'Minimum strength',
minmass_psg: 'Minimum hull mass',
optmass_psg: 'Optimal hull mass',
maxmass_psg: 'Maximum hull mass',
minmul_psg: 'Minimum strength',
optmul_psg: 'Optimal strength',
maxmul_psg: 'Minimum strength',
minmass_bsg: 'Minimum hull mass',
optmass_bsg: 'Optimal hull mass',
maxmass_bsg: 'Maximum hull mass',
minmul_bsg: 'Minimum strength',
optmul_bsg: 'Optimal strength',
maxmul_bsg: 'Minimum strength',
range_s: 'Typical emission range',
// Damage types
absolute: 'Absolute',
explosive: 'Explosive',
kinetic: 'Kinetic',
thermal: 'Thermal',
// Shield sources
generator: 'Generator',
boosters: 'Boosters',
cells: 'Cells',
// Armour sources
bulkheads: 'Bulkheads',
reinforcement: 'Reinforcement',
// Panel headings and subheadings
'power and costs': 'power and costs',
'costs': 'costs',
'retrofit costs': 'retrofit costs',
'reload costs': 'reload costs',
'profiles': 'profiles',
'engine profile': 'engine profile',
'fsd profile': 'fsd profile',
'movement profile': 'movement profile',
'damage to opponent\'s shields': 'damage to opponent\'s shields',
'damage to opponent\'s hull': 'damage to opponent\'s hull',
'offence': 'offence',
'defence': 'defence',
'shield metrics': 'shield metrics',
'raw shield strength': 'raw shield strength',
'shield sources': 'shield sources',
'damage taken': 'damage taken',
'effective shield': 'effective shield',
'armour metrics': 'armour metrics',
'raw armour strength': 'raw armour strength',
'armour sources': 'armour sources',
'raw module armour': 'raw module armour',
'effective armour': 'effective armour',
'offence metrics': 'offence metrics',
'defence metrics': 'defence metrics',
// Misc items
'fuel carried': 'fuel carried',
'cargo carried': 'cargo carried',
'ship control': 'ship control',
'opponent': 'opponent',
'opponent\'s shields': 'opponent\'s shields',
'opponent\'s armour': 'opponent\'s armour',
'shield damage sources': 'shield damage sources',
'armour damage sources': 'armour damage sources',
'never': 'never',
'stock': 'stock',
'boost': 'boost',
// Help text
HELP_TEXT: `
<h1>Introduction</h1>
Coriolis is a ship builder for Elite: Dangerous. This help file provides you with the information you need to use Coriolis.
<h1>Importing Your Ship Into Coriolis</h1>
Often, you will want to start with your existing ship in Coriolis and see how particular changes might affect it, for example upgrading your FSD. There are a number of tools that can be used to import your ship without you having to create it manually. This has the added benefit of copying over any engineering modifications that have taken place as well. </p>
<h2>Importing Your Ship From EDDI</h2>
To import your ship from EDDI first ensure that your connection to the Frontier servers&apos; companion API is working. To do this check the &apos;Companion App&apos; tab where you should see "Your connection to the companion app is operational". If not then follow the instructions in the companion app tab in EDDI to connect to the Frontier servers.</p>
Once you have a working companion API connection go to the &apos;Shipyard&apos; tab. At the right-hand side of each ship is an &apos;Export to Coriolis&apos; button that will open your default web browser in Coriolis with the ship&apos;s build. </p>
Note that Internet Explorer and Edge might not import correctly, due to their internal restrictions on URL length. If you find that this is the case then please change your default browser to Chrome. </p>
Also, the imported information does not provide any data on the power priority or enabled status of your cargo hatch. Coriolis sets this item to have a power priority of "5" and to be disabled by default. You can change this after import in the Power Management section. </p>
<h2>Importing Your Ship From EDMC</h2>
To import your ship from EDMC once your connection to the Frontier servers&apos; companion API is working go to &apos;Settings -&gt;Configuration&apos; and set the &apos;Preferred Shipyard&apos; to &apos;Coriolis&apos;. Once this is set up clicking on your ship in the main window will open your default web browser in Coriolis with the ship&apos;s build.</p>
Note that Internet Explorer and Edge might not import correctly, due to their internal restrictions on URL length. If you find that this is the case then please change your default browser to Chrome. </p>
<h1>Understanding And Using The Outfitting Panels</h1>
The outfitting page is where you will spend most of your time, and contains the information for your ship. Information on each of the panels is provided below. </p>
<h2>Key Values</h2>
Along the top of the screen are some of the key values for your build. This is a handy reference for the values, but more information is provided for the values in the further panels. </p>
Here, along with most places in Coriolis, acronyms will have tooltips explaining what they mean. Hover over the acronym to obtain more detail, or look in the glossary at the end of this help.</p>
All values are the highest possible, assuming that you an optimal setup for that particular value (maximum pips in ENG for speed, minimum fuel for jump range, etc.). This means that these values will not be affected by changes to pip settings. Details of the specific setup for each value are listed in the associated tootip.</p>
<h2>Modules</h2>
The next set of panels laid out horizontally across the screen contain the modules you have put in your build. From left to right these are the core modules, the internal modules, the hardpoints and the utility mounts. These represent the available slots in your ship and cannot be altered. Each slot has a class, or size, and in general any module up to a given size can fit in a given slot (exceptions being bulkheads, life support and sensors in core modules and restricted internal slots, which can only take a subset of module depending on their restrictions). </p>
To add a module to a slot left-click on the slot and select the required module. Only the modules capable of fitting in the selected slot will be shown. </p>
To remove a module from a slot right-click on the module. </p>
To move a module from one slot to another drag it. If you instead want to copy the module drag it whilst holding down the &apos;Alt&apos; key. </p>
Clicking on the headings for each set of modules gives you the ability to either select an overall role for your ship (when clicking the core internal header) or a specific module with which you want to fill all applicable slots (when clicking the other headers). </p>
<h2>Ship Controls</h2>
The ship controls allow you to set your pips, boost, and amount of fuel and cargo that your build carries. The changes made here will effect the information supplied in the subsequent panels, giving you a clearer view of what effect different changing these items will have. </p>
Ship control settings are saved as part of a build. </p>
<h2>Opponent</h2>
The opponet selection allows you to choose your opponent. The opponent can be either a stock build of a ship or one of your own saved builds. You can also set the engagement range between you and your opponent. Your selection here will effect the information supplied in the subsequent panels, specifically the Offence and Defence panels. </p>
Opponent settings are saved as part of a build. </p>
<h2>Power and Costs Sub-panels</h2>
<h3>Power</h3>
The power management panel provides information about power usage and priorities. It allows you to enable and disable individual modules, as well as set power priorities for each module. Disabled modules will not be included in the build&apos;s statistics, with the exception of Shield Cell Banks as they are usually disabled when not in use and only enabled when required. </p>
<h3>Costs</h3>
The costs panel provides information about the costs for each of your modules, and the total cost and insurance for your build. By default Coriolis uses the standard costs, however discounts for your ship, modules and insurance can be altered in the &apos;Settings&apos; at the top-right of the page.</p>
The retrofit costs provides information about the costs of changing the base build for your ship, or your saved build, to the current build.</p>
The reload costs provides information about the costs of reloading your current build.</p>
<h2>Profiles</h2>
Profiles provide graphs that show the general performance of modules in your build
<h3>Engine Profile</h3>
The engine profile panel provides information about the capabilities of your current thrusters. The graph shows you how the maximum speed alters with the overall mass of your build. The vertical dashed line on the graph shows your current mass. Your engine profile can be altered by obtaining different thrusters or engineering your existing thrusters, and you can increase your maximum speed by adding pips to the ENG capacitor as well as reducing the amount of fuel and cargo you are carrying as well as reducing the overall weight of the build. You can also temporarily increase your speed by hitting the boost button. </p>
<h3>FSD Profile</h3>
The FSD profile panel provides information about the capabilities of your current frame shift drive. The graph shows you how the maximum jump range alters with the overall mass of your build. The vertical dashed line on the graph shows your current maximum single jump range. Your FSD profile can be altered by obtaining a different FSD or engineering your existing FSD, and you can increase your maximum jump range by reducing the amount of fuel and cargo you are carrying as well as reducing the overall weight of the build, </p>
<h3>Movement Profile</h3>
The movement profile panel provides information about the capabilities of your current thrusters with your current overall mass and ENG pips settings. The diagram shows your ability to move and rotate in the different axes:
<dl>
<dt>Speed</dt><dd>The fastest the ship can move, in metres per second</dd>
<dt>Pitch</dt><dd>The fastest the ship can raise or lower its nose, in degrees per second</dd>
<dt>Roll</dt><dd>The fastest the ship can roll its body, in degrees per second</dd>
<dt>Yaw</dt><dd>The fastest the ship can turn its nose left or right, in degrees per second</dd>
</dl>
Your movement profile can be altered by obtaining different thrusters or engineering your existing thrusters, and you can increase your movement values by adding pips to the ENG capacitor as well as reducing the amount of fuel and cargo you are carrying as well as reducing the overall weight of the build. You can also temporarily increase your movement profile by hitting the boost button. </p>
<h3>Damage Profile</h3>
The damage profile provides two graphs showing how the the build&apos;s damage to the opponent&apos;s shields and hull change with engagement range. The vertical dashed line on the graph shows your current engagement range. This combines information about the build&apos;s weapons with the opponent&apos;s shields and hull to provide an accurate picture of sustained damage that can be inflicted on the opponent. </p>
<h2>Offence</h2>
<h3>Summary</h3>
The offence summary provides per-weapon information about sustained damage per second inflicted to shields and hull, along with a measure of effectiveness of that weapon. The effectiveness value has a tooltip that provides a breakdown of the effectiveness, and can include reductions or increases due to range, resistance, and either power distributor (for shields) or hardness (for hull). The final effectiveness value is calculated by multiplying these percentages together. </p>
<h3>Offence Metrics</h3>
The offence metrics panel provides information about your offence. </p>
Time to drain is a measure of how quickly your WEP capacitor will drain when firing all weapons. It is affected by the number of pips you have in your WEP capacitor, with more pips resulting in a higher WEP recharge rate and hence a longer time to drain. </p>
The next value is the time it will take you to remove your opponent&apos;s shields. This assumes that you have 100% time on target and that your engagement range stays constant. Note that if your time to remove shields is longer than your time to drain this assumes that you continue firing throughout, inflicting lower damage due to the reduced energy in your WEP capacitor. </p>
The next value is the time it will take you to remove your opponent&apos;s armour. This follows the same logic as the time to remove shields. </p>
<h3>Shield Damage Sources</h3>
The shield damage sources provides information about the sources of damage to your opponent by damage type. For each applicable type of damage (absolute explosive, kinetic, thermal) a sustained damage per second value is provided. </p>
<h3>Hull Damage Sources</h3>
The hull damage sources provides information about the sources of damage to your opponent by damage type. For each applicable type of damage (absolute explosive, kinetic, thermal) a sustained damage per second value is provided. </p>
<h2>Defence</h2>
<h3>Shield Metrics</h3>
The shield metrics provides information about your shield defence. </p>
Raw shield strength is the sum of the shield from your generator, boosters and shield cell banks. A tooltip provides a breakdown of these values. </p>
The time the shields will hold for is the time it will take your opponent&apos; to remove your shields. This assumes that they have 100% time on target and that the engagement range stays constant. It also assumes that you fire all of your shield cell banks prior to your shields being lost. </p>
The time the shields will recover in is the time it will take your shields to go from collapsed (0%) to recovered (50%). This is affected by the number of pips you have in your SYS capacitor. </p>
The time the shields will recharge in is the time it will take your shields to go from recovered (50%) to full (100%). This is affected by the number of pips you have in your SYS capacitor. </p>
</h3>Shield Sources</h3>
This chart provides information about the sources of your shields. For each applicable source of shields (generator, boosters, shield cell banks) a value is provided. </p>
</h3>Damage Taken</h3>
This graph shows how the initial damage from the weapons of each type are reduced before their damage is applied to the shields. For each type of damage (absolute, explosive, kinetic, thermal) a percentage of the initial damage is provided. A tooltip provides a breakdown of these values. </p>
</h3>Effective Shield</h3>
This graph shows the effective shield for each damage type, found by dividing the raw shield value by the damage taken for that type. </p>
<h3>Amour Metrics</h3>
The armour metrics provides information about your armour defence. </p>
Raw armour strength is the sum of the armour from your bulkheads and hull reinforcement packages. A tooltip provides a breakdown of these values. </p>
The time the armour will hold for is the time it will take your opponent&apos; to take your armour to 0. This assumes that they have 100% time on target, the engagement range stays constant, and that all damage is dealt to the armour rather than modules. </p>
Raw module armour is the sum of the protection from your module reinforcement packages. </p>
Protection for hardpoints is the amount of protection that your module reinforcement packages provide to hardpoints. This percentage of damage to the hardpoints will be diverted to the module reinforcement packages. </p>
Protection for all other modules is the amount of protection that your module reinforcement packages provide to everything other than hardpoints. This percentage of damage to the modules will be diverted to the module reinforcement packages. </p>
</h3>Armour Sources</h3>
This chart provides information about the sources of your armour. For each applicable source of shields (bulkheads, hull reinforcement packages) a value is provided. </p>
</h3>Damage Taken</h3>
This graph shows how the initial damage from the weapons of each type are reduced before their damage is applied to the armour. For each type of damage (absolute, explosive, kinetic, thermal) a percentage of the initial damage is provided. A tooltip provides a breakdown of these values. </p>
</h3>Effective Armour</h3>
This graph shows the effective armour for each damage type, found by dividing the raw armour value by the damage taken for that type. </p>
<h1>Keyboard Shortcuts</h1>
<dl>
<dt>Ctrl-b</dt><dd>toggle boost</dd>
<dt>Ctrl-e</dt><dd>open export dialogue (outfitting page only)</dd>
<dt>Ctrl-h</dt><dd>open help dialogue</dd>
<dt>Ctrl-i</dt><dd>open import dialogue</dd>
<dt>Ctrl-o</dt><dd>open shortlink dialogue</dd>
<dt>Ctrl-left-arrow</dt><dd>increase SYS capacitor</dd>
<dt>Ctrl-up-arrow</dt><dd>increase ENG capacitor</dd>
<dt>Ctrl-right-arrow</dt><dd>increase WEP capacitor</dd>
<dt>Ctrl-down-arrow</dt><dd>reset power distributor</dd>
<dt>Esc</dt><dd>close any open dialogue</dd>
</dl>
<h1>Glossary</h1>
<dl>
<dt>Absolute damage</dt><dd>A type of damage, without any protection. Absolute damage is always dealt at 100% regardless of if the damage is to shields, hull or modules, and irrespective of resistances</dd>
<dt>DPS</dt><dd>Damage per second; the amount of damage that a weapon or a ship can deal per second to a target under optimum conditions</dd>
<dt>EPS</dt><dd>Energy per second; the amount of energy that a weapon or a ship drains from the weapons capacitor per second when firing</dd>
<dt>HPS</dt><dd>Heat per second; the amount of heat that a weapon or a ship generates per second when firing</dd>
<dt>Effectivness</dt><dd>A comparison of the maximum DPS of a given weapon to the actual DPS of the given weapon in a specific situation. DPS can be reduced by range to the target, the target&apos;s hull and shield resistances, and the target&apos;s hardness</dd>
<dt>Explosive damage</dt><dd>A type of damage, protected against by explosive resistance</dd>
<dt>Hardness</dt><dd>The inherent resistance to damage of a ship&apos;s hull. Hardness is defined on a per-ship basis and there is currently nothing that can be done to change it. Hardness of a ship&apos;s hull is compared to the piercing of weapons: if piercing is higher than hardness the weapon does 100% damage, otherwise it does a fraction of its damage calculated as piercing/hardness</dd>
<dt>Falloff</dt><dd>The distance at which a weapons starts to do less damage than its stated DPS</dd>
<dt>Kinetic damage</dt><dd>A type of damage, protected against by kinetic resistance</dd>
<dt>SDPS</dt><dd>Sustained damage per second; the amount of damage that a weapon or a ship can deal per second to a target, taking in to account ammunition reload</dd>
<dt>SEPS</dt><dd>Sustained energy per second; the amount of energy that a weapon or a ship drains from the weapons capacitor per second when firing, taking in to account ammunition reload</dd>
<dt>SHPS</dt><dd>Sustained heat per second; the amount of heat that a weapon or a ship generates per second when firing, taking in to account ammunition reload</dd>
<dt>Thermal damage</dt><dd>A type of damage, protected against by thermal resistance</dd>
</dl>
`,
};

331
src/app/i18n/en.json Normal file

File diff suppressed because one or more lines are too long

View File

@@ -13,196 +13,4 @@ export const formats = {
shortMonths: ['ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul', 'ago', 'sep', 'oct', 'nov', 'dic'] shortMonths: ['ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul', 'ago', 'sep', 'oct', 'nov', 'dic']
}; };
export const terms = { export { default as terms } from './es.json';
'PHRASE_EXPORT_DESC': 'Una detallada exportaci\u00f3n JSON de tu construcci\u00f3n para usarlo en otros sitios web y herramientas',
'A-Rated': 'Calidad-A',
'about': 'Acerca',
'action': 'Acci\u00f3n',
'added': 'A\u00f1adido',
'Advanced Discovery Scanner': 'Esc\u00e1ner de exploraci\u00f3n avanzado',
maneuverability: 'Maniobrabilidad',
'alpha': 'Alfa',
'ammo': 'Munici\u00f3n',
'PHRASE_CONFIRMATION': '\u00bfEst\u00e1s seguro?',
'armour': 'Blindaje',
'am': 'Unidad de auto-reparaciones',
'available': 'Disponible',
'backup': 'Reserva',
'Basic Discovery Scanner': 'Esc\u00e1ner de exploraci\u00f3n b\u00e1sico',
'bl': 'L\u00e1ser de haz',
'bins': 'contenedores',
'boost': 'incrementar',
'build': 'Construcci\u00f3n',
'build name': 'Nombre de la construcci\u00f3n',
'builds': 'Construcciones',
'bh': 'mamparos',
'ul': 'Laser de r\u00e1fagas',
'buy': 'Comprar',
'cancel': 'Cancelar',
'c': 'Ca\u00f1\u00f3n',
'capital': 'capital',
'cargo': 'Carga',
'Cargo Hatch': 'Compuerta de carga',
'cr': 'Compartimento de carga',
'cs': 'Esc\u00e1ner de carga',
'cells': 'celdas',
'Chaff Launcher': 'Lanzador de virutas',
'close': 'Cerrar',
'cc': 'Controlador de Drones de Recogida',
'compare': 'Comparar',
'compare all': 'comparar todas',
'comparison': 'Comparativa',
'comparisons': 'Comparativas',
'component': 'Componente',
'cost': 'Coste',
'costs': 'Costes',
'cm': 'Contramedidas',
'create': 'Crear',
'create new': 'Crear nuevo',
'credits': 'Cr\u00e9ditos',
'damage': 'Da\u00f1o',
'delete': 'Borrar',
'delete all': 'Borrar todo',
'dep': 'desp',
'deployed': 'Desplegado',
'detailed export': 'Exportacion detallada',
'Detailed Surface Scanner': 'Escaner de exploraci\u00f3n detallada',
'disabled': 'Desactivado',
'discount': 'Descuento',
'dc': 'Ordenador de aterrizaje',
'done': 'Hecho',
'DPS': 'DPS (Da\u00f1o Por Segundo)',
'edit data': 'Editar datos',
'efficiency': 'Eficiencia',
'Electronic Countermeasure': 'Contramedidas electr\u00f3nicas',
'empty': 'Vac\u00edo',
'ENG': 'MOT',
'enter name': 'Introduce el nombre',
'export': 'exportar',
'fixed': 'fijo',
'forum': 'Foro',
'fc': 'Ca\u00f1\u00f3n de fragmentaci\u00f3n',
'fsd': 'Motor de salto',
'ws': 'Esc\u00e1ner de Salto',
'fi': 'Interdictor FSD',
'fuel': 'Combustible',
'fs': 'Recolector de Combustible',
'ft': 'Tanque de combustible',
'fx': 'Sistema de Transferencia de Combustilble',
'full tank': 'Tanque lleno',
'Gimballed': 'Card\u00e1n',
'H': 'E',
'hardpoints': 'Montura de armas',
'hb': 'Controlador de Apertura de Bah\u00eda de Carga',
'Heat Sink Launcher': 'Eyector de Acumulador de Calor',
'huge': 'enorme',
'hull': 'Casco',
'hr': 'Sistema de Casco Reforzado',
'import': 'Importar',
'import all': 'Importar todo',
'insurance': 'Seguro',
'Intermediate Discovery Scanner': 'Esc\u00e1ner de exploraci\u00f3n media',
'internal compartments': 'Compartimentos internos',
'jump range': 'Rango de salto',
'jumps': 'Saltos',
'kw': 'Esc\u00e1ner Detector de Recompensas',
'L': 'G',
'laden': 'Cargada',
'language': 'Idioma',
'large': 'Grande',
'ls': 'Soporte vital',
'Lightweight Alloy': 'Aleaci\u00f3n ligera',
'limpets': 'Drones',
'lock factor': 'factor de bloqueo',
'mass': 'Masa',
'max': 'm\u00e1x',
'max mass': 'Masa m\u00e1xima',
'medium': 'medio',
'Military Grade Composite': 'Blindaje Militar',
'nl': 'Lanzaminas',
'Mining Lance': 'Lanza de miner\u00eda',
'ml': 'L\u00e1ser de miner\u00eda',
'Mirrored Surface Composite': 'Blindaje Reflectante',
'mr': 'Bah\u00eda de Misiles',
'mc': 'Ca\u00f1\u00f3n m\u00faltiple',
'net cost': 'Coste neto',
'PHRASE_NO_BUILDS': '\u00a1No se a\u00f1adieron plantillas para comparaci\u00f3n!',
'PHRASE_NO_RETROCH': 'No hay cambios en los ajutes',
'none': 'Nada',
'none created': 'Nada creado',
'off': 'apagado',
'on': 'encendido',
'optimal': '\u00f3ptimo',
'optimal mass': 'masa \u00f3ptima',
'optimize mass': 'optimizar masa',
'overwrite': 'Sobreescribir',
'PHRASE_IMPORT': 'Pega el JSON o imp\u00f3rtalo aqu\u00ed',
'penetration': 'penetraci\u00f3n',
'permalink': 'enlace permanente',
'pa': 'Acelerador de Plasma',
'Point Defence': 'Punto de Defensa',
'power': 'energ\u00eda',
'pd': 'distribuidor de energ\u00eda',
'pp': 'Planta de Energ\u00eda',
'priority': 'prioridad',
'proceed': 'Proceder',
'pc': 'Controlador de drones de prospecci\u00f3n',
'pl': 'L\u00e1ser de Pulso',
'PWR': 'POT',
'rg': 'Ca\u00f1\u00f3n de Riel',
'range': 'rango',
'rate': 'ratio',
'Reactive Surface Composite': 'Blindaje Reactivo',
'recharge': 'recargar',
'rf': 'Refineria',
'refuel time': 'Tiempo para repostar',
'Reinforced Alloy': 'Armadura reforzada',
'reload': 'Recargar',
'rename': 'Renombrar',
'repair': 'Reparar',
'reset': 'Reiniciar',
'ret': 'PLE',
'retracted': 'plegadas',
'retrofit costs': 'costes de equipamiento',
'retrofit from': 'equipamiento desde',
'ROF': 'RDF',
'S': 'P',
'save': 'guardar',
'sc': 'sc\u00e1ner',
'PHRASE_SELECT_BUILDS': 'Selecciona equipamientos para comparar',
'sell': 'Vender',
's': 'Sensores',
'settings': 'Configuraci\u00f3n',
'sb': 'Potenciador de Escudos',
'scb': 'C\u00e9lula de Energ\u00eda de Escudos',
'sg': 'Generador de escudos',
'shields': 'Escudos',
'ship': 'Nave ',
'ships': 'Naves',
'shortened': 'Abreviado',
'size': 'Tama\u00f1o',
'skip': 'omitir',
'small': 'Peque\u00f1o',
'speed': 'velocidad',
'standard': 'est\u00e1ndar',
'Standard Docking Computer': 'Computador de Atraque Est\u00e1ndar',
'Stock': 'De serie',
'SYS': 'SIS',
'T_LOAD': 'c-t\u00e9rmica',
't': 'Propulsores',
'time': 'Tiempo',
'tp': 'Anclaje de torpedo',
'total': 'Total',
'total range': 'Rango total',
'turret': 'torreta',
'type': 'Tipo',
'unladen': 'Sin carga',
'PHRASE_UPDATE_RDY': 'Actualizacion disponible! Haz click para recargar',
'URL': 'Enlace',
'utility': 'utilidad',
'utility mounts': 'monturas de utilidad',
'version': 'Versi\u00f3n',
'WEP': 'ARM',
'yes': 'si',
'PHRASE_BACKUP_DESC': 'Copia de seguridad de todos los datos de Coriolis para guardarlos o transferirlos a otro navegador\/dispositivo'
};

193
src/app/i18n/es.json Normal file
View File

@@ -0,0 +1,193 @@
{
"PHRASE_EXPORT_DESC": "Una detallada exportación JSON de tu construcción para usarlo en otros sitios web y herramientas",
"A-Rated": "Calidad-A",
"about": "Acerca",
"action": "Acción",
"added": "Añadido",
"Advanced Discovery Scanner": "Escáner de exploración avanzado",
"maneuverability": "Maniobrabilidad",
"alpha": "Alfa",
"ammo": "Munición",
"PHRASE_CONFIRMATION": "¿Estás seguro?",
"armour": "Blindaje",
"am": "Unidad de auto-reparaciones",
"available": "Disponible",
"backup": "Reserva",
"Basic Discovery Scanner": "Escáner de exploración básico",
"bl": "Láser de haz",
"bins": "contenedores",
"boost": "incrementar",
"build": "Construcción",
"build name": "Nombre de la construcción",
"builds": "Construcciones",
"bh": "mamparos",
"ul": "Laser de ráfagas",
"buy": "Comprar",
"cancel": "Cancelar",
"c": "Cañón",
"capital": "capital",
"cargo": "Carga",
"Cargo Hatch": "Compuerta de carga",
"cr": "Compartimento de carga",
"cs": "Escáner de carga",
"cells": "celdas",
"Chaff Launcher": "Lanzador de virutas",
"close": "Cerrar",
"cc": "Controlador de Drones de Recogida",
"compare": "Comparar",
"compare all": "comparar todas",
"comparison": "Comparativa",
"comparisons": "Comparativas",
"component": "Componente",
"cost": "Coste",
"costs": "Costes",
"cm": "Contramedidas",
"create": "Crear",
"create new": "Crear nuevo",
"credits": "Créditos",
"damage": "Daño",
"delete": "Borrar",
"delete all": "Borrar todo",
"dep": "desp",
"deployed": "Desplegado",
"detailed export": "Exportacion detallada",
"Detailed Surface Scanner": "Escaner de exploración detallada",
"disabled": "Desactivado",
"discount": "Descuento",
"dc": "Ordenador de aterrizaje",
"done": "Hecho",
"DPS": "DPS (Daño Por Segundo)",
"edit data": "Editar datos",
"efficiency": "Eficiencia",
"Electronic Countermeasure": "Contramedidas electrónicas",
"empty": "Vacío",
"ENG": "MOT",
"enter name": "Introduce el nombre",
"export": "exportar",
"fixed": "fijo",
"forum": "Foro",
"fc": "Cañón de fragmentación",
"fsd": "Motor de salto",
"ws": "Escáner de Salto",
"fi": "Interdictor FSD",
"fuel": "Combustible",
"fs": "Recolector de Combustible",
"ft": "Tanque de combustible",
"fx": "Sistema de Transferencia de Combustilble",
"full tank": "Tanque lleno",
"Gimballed": "Cardán",
"H": "E",
"hardpoints": "Montura de armas",
"hb": "Controlador de Apertura de Bahía de Carga",
"Heat Sink Launcher": "Eyector de Acumulador de Calor",
"huge": "enorme",
"hull": "Casco",
"hr": "Sistema de Casco Reforzado",
"import": "Importar",
"import all": "Importar todo",
"insurance": "Seguro",
"Intermediate Discovery Scanner": "Escáner de exploración media",
"internal compartments": "Compartimentos internos",
"jump range": "Rango de salto",
"jumps": "Saltos",
"kw": "Escáner Detector de Recompensas",
"L": "G",
"laden": "Cargada",
"language": "Idioma",
"large": "Grande",
"ls": "Soporte vital",
"Lightweight Alloy": "Aleación ligera",
"limpets": "Drones",
"lock factor": "factor de bloqueo",
"mass": "Masa",
"max": "máx",
"max mass": "Masa máxima",
"medium": "medio",
"Military Grade Composite": "Blindaje Militar",
"nl": "Lanzaminas",
"Mining Lance": "Lanza de minería",
"ml": "Láser de minería",
"Mirrored Surface Composite": "Blindaje Reflectante",
"mr": "Bahía de Misiles",
"mc": "Cañón múltiple",
"net cost": "Coste neto",
"PHRASE_NO_BUILDS": "¡No se añadieron plantillas para comparación!",
"PHRASE_NO_RETROCH": "No hay cambios en los ajutes",
"none": "Nada",
"none created": "Nada creado",
"off": "apagado",
"on": "encendido",
"optimal": "óptimo",
"optimal mass": "masa óptima",
"optimize mass": "optimizar masa",
"overwrite": "Sobreescribir",
"PHRASE_IMPORT": "Pega el JSON o impórtalo aquí",
"penetration": "penetración",
"permalink": "enlace permanente",
"pa": "Acelerador de Plasma",
"Point Defence": "Punto de Defensa",
"power": "energía",
"pd": "distribuidor de energía",
"pp": "Planta de Energía",
"priority": "prioridad",
"proceed": "Proceder",
"pc": "Controlador de drones de prospección",
"pl": "Láser de Pulso",
"PWR": "POT",
"rg": "Cañón de Riel",
"range": "rango",
"rate": "ratio",
"Reactive Surface Composite": "Blindaje Reactivo",
"recharge": "recargar",
"rf": "Refineria",
"refuel time": "Tiempo para repostar",
"Reinforced Alloy": "Armadura reforzada",
"reload": "Recargar",
"rename": "Renombrar",
"repair": "Reparar",
"reset": "Reiniciar",
"ret": "PLE",
"retracted": "plegadas",
"retrofit costs": "costes de equipamiento",
"retrofit from": "equipamiento desde",
"ROF": "RDF",
"S": "P",
"save": "guardar",
"sc": "scáner",
"PHRASE_SELECT_BUILDS": "Selecciona equipamientos para comparar",
"sell": "Vender",
"s": "Sensores",
"settings": "Configuración",
"sb": "Potenciador de Escudos",
"scb": "Célula de Energía de Escudos",
"sg": "Generador de escudos",
"shields": "Escudos",
"ship": "Nave ",
"ships": "Naves",
"shortened": "Abreviado",
"size": "Tamaño",
"skip": "omitir",
"small": "Pequeño",
"speed": "velocidad",
"standard": "estándar",
"Standard Docking Computer": "Computador de Atraque Estándar",
"Stock": "De serie",
"SYS": "SIS",
"T_LOAD": "c-térmica",
"t": "Propulsores",
"time": "Tiempo",
"tp": "Anclaje de torpedo",
"total": "Total",
"total range": "Rango total",
"turret": "torreta",
"type": "Tipo",
"unladen": "Sin carga",
"PHRASE_UPDATE_RDY": "Actualizacion disponible! Haz click para recargar",
"URL": "Enlace",
"utility": "utilidad",
"utility mounts": "monturas de utilidad",
"version": "Versión",
"WEP": "ARM",
"yes": "si",
"PHRASE_BACKUP_DESC": "Copia de seguridad de todos los datos de Coriolis para guardarlos o transferirlos a otro navegador/dispositivo"
}

View File

@@ -13,143 +13,4 @@ export const formats = {
shortMonths: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.', 'août', 'sept.', 'oct.', 'nov.', 'déc.'] shortMonths: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.', 'août', 'sept.', 'oct.', 'nov.', 'déc.']
}; };
export const terms = { export { default as terms } from './fr.json';
// Phrases
PHRASE_BACKUP_DESC: 'Copie des données Coriolis pour l\'utilisation dans d\'autres sites et outils', // Backup of all Coriolis data to save or transfer to another browser/device
PHRASE_CONFIRMATION: 'Êtes-vous sûr?', // Are You Sure?
PHRASE_EXPORT_DESC: 'Un export détaillé en JSON de votre configuration pour l\'utilisation dans d\'autres sites et outils', // A detailed JSON export of your build for use in other sites and tools
PHRASE_FASTEST_RANGE: 'Portée maximale en cas de sauts successifs', // Consecutive max range jumps
PHRASE_IMPORT: 'Coller JSON ou importer ici', // Paste JSON or import here
PHRASE_LADEN: 'Masse du Vaisseau + Carburant + Cargo', // Ship Mass + Fuel + Cargo
PHRASE_NO_BUILDS: 'Aucune configuration ajoutée pour la comparaison', // No builds added to comparison!
PHRASE_NO_RETROCH: 'Aucun changement de configuration', // No Retrofitting changes
PHRASE_SELECT_BUILDS: 'Sélectionner les configurations à comparer', // Select Builds to Compare
PHRASE_SG_RECHARGE: 'Temps de charge de 50% à 100 %', // Time from 50% to 100% Charge
PHRASE_SG_RECOVER: 'Temps de redémarrage après perte du bouclier', // Recovery (to 50%) after collapse
PHRASE_UNLADEN: 'Masse du Vaisseau hors Carburant et Cargo', // Ship Mass excluding Fuel and Cargo
PHRASE_UPDATE_RDY: 'Mise à jour disponible ! Cliquez pour rafraichir', // Update Available! Click to Refresh
// Units / Metrics
Ls: 'SL', // Light seconds
LY: 'AL', // Light Years
// Sizes
L: 'G', // Large Hardpoint size (single character)
large: 'grand', // Large Ship Size
medium: 'moyen', // Medium ship size
S: 'P', // Small Hardpoint (single Character)
small: 'petit', // Small ship size
// Terms
about: 'à propos', // Link to about page / about Coriolis.io
added: 'ajouté',
ammo: 'munition', // Ammunition
armour: 'Armure',
available: 'Disponibilité', // Available options
backup: 'sauvegarde',
bays: 'baies',
bins: 'bacs', // Number of Mining Refinery bins
build: 'Configuration', // Shorthand for the build/configuration/design name
'build name': 'Nom de la configuration', // Ship build/configuration/design name
builds: 'Configurations', // Ship build/configuration/design names
buy: 'Acheter',
cancel: 'Annuler',
cargo: 'Soute',
cells: 'Cellule', // Number of cells in a shield cell bank
close: 'fermer',
compare: 'comparer',
'compare all': 'tout comparer',
comparison: 'comparaison',
comparisons: 'comparaisons',
cost: 'coût', // Cost / price of a module or price of a ship
costs: 'coûts', // Costs / prices of a modules or prices of ships
create: 'Créer',
'create new': 'Créer nouveau',
credits: 'crédits',
damage: 'dégât',
'damage per second': 'dégât par seconde',
delete: 'supprimer',
'delete all': 'tout supprimer',
dep: 'depl', // Weapons/Hardpoints Deployed abbreviation
deployed: 'déployé', // Weapons/Hardpoints Deployed
'detailed export': 'export détaillé',
disabled: 'désactivé',
discount: 'ristourne',
'edit data': 'Editer donnée',
efficiency: 'rendement', // Power Plant efficiency
empty: 'Vide',
'empty all': 'vide tout',
'Enter Name': 'Entrer nom',
Explorer: 'explorateur',
'fastest range': 'gamme la plus rapide', // Fastet totaljump range - sum of succesive jumps
fuel: 'carburant',
'fuel level': 'niveau de carburant', // Percent of fuel (T) in the tank
'full tank': 'Réservoir plein',
hardpoints: 'Points d\'emport',
hull: 'Coque', // Ships hull
import: 'Importer',
insurance: 'Assurance',
'internal compartments': 'compartiments internes',
jump: 'saut', // Single jump range
'jump range': 'Distance de saut',
jumps: 'Sauts',
laden: 'chargé',
language: 'Langage',
maneuverability: 'maniabilité',
manufacturer: 'fabricant',
mass: 'Masse',
'mass lock factor': 'facteur inhibition de masse',
'max mass': 'masse max',
MLF: 'FIM', // Mass Lock Factor Abbreviation
'net cost': 'coûts nets',
no: 'non',
'none created': 'Rien de créé',
ok: 'D\'accord',
'optimal mass': 'masse optimale', // Lowest weight / best weight for jump distance, etc
optimize: 'optimiser',
pen: 'pén.', // Armour peneration abbreviation
permalink: 'lien durable',
power: 'énergie', // Power = Energy / second. Power generated by the power plant, or power consumed (MW / Mega Watts). Used in the power plant section
proceed: 'continuer',
PWR: 'P', // Power Abbreviation. See Power
qty: 'quantité', // Quantity abbreviation
range: 'portée',
rate: 'cadence',
recharge: 'recharger', // Shield Recharge time from 50% -> 100%
recovery: 'récupération', // Shield recovery time (after losing shields/turning on -> 50%)
'refuel time': 'Temps de remplissage', // Time to refuel the tank when scooping
reload: 'recharger', // Reload weapon/hardpoint
'reload costs': 'recharger coûts',
rename: 'renommer',
repair: 'réparer',
reset: 'Réinitialisation',
ret: 'esc', // Retracted abbreviation
retracted: 'escamoté', // Weapons/Hardpoints retracted
'retrofit costs': 'Valeur de rachat', // The cost difference when upgrading / downgrading a component
'retrofit from': 'Racheter de', // Retrofit from Build A against build B
ROF: 'cadence', // Rate of Fire abbreviation
roles: 'rôles', // Commander/Ship build roles - e.g. Trader, Bounty-Hunter, Explorer, etc
save: 'sauvegarder',
sell: 'vendre',
settings: 'paramètres', // Coriolis application settings
shields: 'boucliers',
ship: 'vaisseau',
ships: 'vaisseaux',
shortened: 'raccourci', // Standard/Stock build of a ship when purchased new
size: 'taille',
skip: 'Suivant', // Skip past something / ignore it
speed: 'vitesse',
Stock: 'de base', // Thermal-load abbreviation
strength: 'force', // Strength in reference to Shield Strength
subtotal: 'Sous-Total',
'T-Load': 'degrés', // Thermal load abbreviation
time: 'temps', // time it takes to complete something
tooltips: 'infobulles', // Tooltips setting - show/hide
'total range': 'plage totale',
Trader: 'commerçant', // Trader role
'unit cost': 'coût unitaire',
unladen: 'Non chargé', // No cargo or fuel
'utility mounts': 'Support utilitaire',
WEP: 'ARM', // Abbreviation - Weapon recharge rate for power distributor
yes: 'oui'
};

133
src/app/i18n/fr.json Normal file
View File

@@ -0,0 +1,133 @@
{
"PHRASE_BACKUP_DESC": "Copie des données Coriolis pour l'utilisation dans d'autres sites et outils",
"PHRASE_CONFIRMATION": "Êtes-vous sûr?",
"PHRASE_EXPORT_DESC": "Un export détaillé en JSON de votre configuration pour l'utilisation dans d'autres sites et outils",
"PHRASE_FASTEST_RANGE": "Portée maximale en cas de sauts successifs",
"PHRASE_IMPORT": "Coller JSON ou importer ici",
"PHRASE_LADEN": "Masse du Vaisseau + Carburant + Cargo",
"PHRASE_NO_BUILDS": "Aucune configuration ajoutée pour la comparaison",
"PHRASE_NO_RETROCH": "Aucun changement de configuration",
"PHRASE_SELECT_BUILDS": "Sélectionner les configurations à comparer",
"PHRASE_SG_RECHARGE": "Temps de charge de 50% à 100 %",
"PHRASE_SG_RECOVER": "Temps de redémarrage après perte du bouclier",
"PHRASE_UNLADEN": "Masse du Vaisseau hors Carburant et Cargo",
"PHRASE_UPDATE_RDY": "Mise à jour disponible ! Cliquez pour rafraichir",
"Ls": "SL",
"LY": "AL",
"L": "G",
"large": "grand",
"medium": "moyen",
"S": "P",
"small": "petit",
"about": "à propos",
"added": "ajouté",
"ammo": "munition",
"armour": "Armure",
"available": "Disponibilité",
"backup": "sauvegarde",
"bays": "baies",
"bins": "bacs",
"build": "Configuration",
"build name": "Nom de la configuration",
"builds": "Configurations",
"buy": "Acheter",
"cancel": "Annuler",
"cargo": "Soute",
"cells": "Cellule",
"close": "fermer",
"compare": "comparer",
"compare all": "tout comparer",
"comparison": "comparaison",
"comparisons": "comparaisons",
"cost": "coût",
"costs": "coûts",
"create": "Créer",
"create new": "Créer nouveau",
"credits": "crédits",
"damage": "dégât",
"damage per second": "dégât par seconde",
"delete": "supprimer",
"delete all": "tout supprimer",
"dep": "depl",
"deployed": "déployé",
"detailed export": "export détaillé",
"disabled": "désactivé",
"discount": "ristourne",
"edit data": "Editer donnée",
"efficiency": "rendement",
"empty": "Vide",
"empty all": "vide tout",
"Enter Name": "Entrer nom",
"Explorer": "explorateur",
"fastest range": "gamme la plus rapide",
"fuel": "carburant",
"fuel level": "niveau de carburant",
"full tank": "Réservoir plein",
"hardpoints": "Points d'emport",
"hull": "Coque",
"import": "Importer",
"insurance": "Assurance",
"internal compartments": "compartiments internes",
"jump": "saut",
"jump range": "Distance de saut",
"jumps": "Sauts",
"laden": "chargé",
"language": "Langage",
"maneuverability": "maniabilité",
"manufacturer": "fabricant",
"mass": "Masse",
"mass lock factor": "facteur inhibition de masse",
"max mass": "masse max",
"MLF": "FIM",
"net cost": "coûts nets",
"no": "non",
"none created": "Rien de créé",
"ok": "D'accord",
"optimal mass": "masse optimale",
"optimize": "optimiser",
"pen": "pén.",
"permalink": "lien durable",
"power": "énergie",
"proceed": "continuer",
"PWR": "P",
"qty": "quantité",
"range": "portée",
"rate": "cadence",
"recharge": "recharger",
"recovery": "récupération",
"refuel time": "Temps de remplissage",
"reload": "recharger",
"reload costs": "recharger coûts",
"rename": "renommer",
"repair": "réparer",
"reset": "Réinitialisation",
"ret": "esc",
"retracted": "escamoté",
"retrofit costs": "Valeur de rachat",
"retrofit from": "Racheter de",
"ROF": "cadence",
"roles": "rôles",
"save": "sauvegarder",
"sell": "vendre",
"settings": "paramètres",
"shields": "boucliers",
"ship": "vaisseau",
"ships": "vaisseaux",
"shortened": "raccourci",
"size": "taille",
"skip": "Suivant",
"speed": "vitesse",
"Stock": "de base",
"strength": "force",
"subtotal": "Sous-Total",
"T-Load": "degrés",
"time": "temps",
"tooltips": "infobulles",
"total range": "plage totale",
"Trader": "commerçant",
"unit cost": "coût unitaire",
"unladen": "Non chargé",
"utility mounts": "Support utilitaire",
"WEP": "ARM",
"yes": "oui"
}

View File

@@ -13,117 +13,4 @@ export const formats = {
shortMonths: ['Gen', 'Feb', 'Mar', 'Apr', 'Mag', 'Giu', 'Lug', 'Ago', 'Set', 'Ott', 'Nov', 'Dic'] shortMonths: ['Gen', 'Feb', 'Mar', 'Apr', 'Mag', 'Giu', 'Lug', 'Ago', 'Set', 'Ott', 'Nov', 'Dic']
}; };
export const terms = { export { default as terms } from './it.json';
PHRASE_EXPORT_DESC: 'Un export dettagliato in formato JSON della tua configurazione per essere usato in altri siti o tools',
'A-Rated': 'Classe A',
about: 'Info su Coriolis',
action: 'azione',
added: 'aggiunto',
Advanced: 'Avanzato',
maneuverability: 'agilità',
ammo: 'munizioni',
PHRASE_CONFIRMATION: 'Sei sicuro ?',
armour: 'armatura',
available: 'disponibile',
bins: 'contenitore',
build: 'configurazione',
'build name': 'Nome Configurazione',
builds: 'configurazioni',
buy: 'compra',
cancel: 'cancella',
cells: 'celle',
close: 'chiudi',
compare: 'confronta',
'compare all': 'confronta tutti',
comparison: 'comparazione',
comparisons: 'comparazioni',
component: 'componente',
cost: 'costo',
costs: 'costi',
cm: 'Contromisure',
create: 'crea',
'create new': 'crea nuovo',
credits: 'crediti',
damage: 'danno',
delete: 'elimina',
'delete all': 'elimina tutto',
dep: 'dep',
deployed: 'deployed',
'detailed export': 'esportazione dettagliata',
disabled: 'disabilita',
discount: 'sconto',
done: 'fatto',
'edit data': 'modifica i dati',
efficiency: 'efficenza',
empty: 'vuoto',
Enforcer: 'Rinforzatore',
'enter name': 'Inserisci un nome',
export: 'esporta',
fixed: 'fissi',
fuel: 'carburante',
'full tank': 'Serbatoio Pieno',
huge: 'enorme',
hull: 'corazza',
import: 'importa',
'import all': 'importa tutto',
insurance: 'assicurazione',
'internal compartments': 'compartimenti interni',
'jump range': 'distanza di salto',
jumps: 'salti',
laden: 'carico',
language: 'lingua',
large: 'largo',
mass: 'massa',
max: 'massimo',
'max mass': 'massa massimale',
medium: 'medio',
'net cost': 'costo netto',
PHRASE_NO_BUILDS: 'nessuna configurazione è stata aggiunta per la comparazione!',
PHRASE_NO_RETROCH: 'Nessun cambiamento di Retrofitting',
none: 'nessuno',
'none created': 'nessuno creato',
optimal: 'ottimale',
'optimal mass': 'massa ottimale',
'optimize mass': 'ottimizza la massa',
overwrite: 'sovrasscrivi',
PHRASE_IMPORT: 'Incolla un JSON o importalo qua',
penetration: 'penetrazione',
power: 'potenza',
priority: 'priorità',
proceed: 'procedi',
range: 'distanza',
rate: 'rateo',
recharge: 'ricarica',
reload: 'ricarica',
rename: 'rinomina',
repair: 'ripara',
reset: 'resetta',
retracted: 'retratti',
'retrofit costs': 'costi di retrofit',
'retrofit from': 'retrofit da',
save: 'salva',
sell: 'vendi',
settings: 'impostazioni',
shields: 'scudi',
ship: 'nave',
ships: 'navi',
shortened: 'accorciato',
size: 'grandezza',
skip: 'salta',
small: 'piccolo',
speed: 'velocità',
Stock: 'appena comprata',
t: 'thrusters',
time: 'tempo',
total: 'totale',
'total range': 'distanza totale',
turret: 'torrette',
type: 'tipo',
unladen: 'scarico',
PHRASE_UPDATE_RDY: 'Aggiornamenti disponibili ! Clicca per Aggiornare',
utility: 'supporti',
'utility mounts': 'supporti di utilità',
version: 'versione',
yes: 'sì',
PHRASE_BACKUP_DESC: 'Esportazione di tutti i dati su Coriolis per salvarli o trasferirli in un altro Browser/dispositivo'
};

114
src/app/i18n/it.json Normal file
View File

@@ -0,0 +1,114 @@
{
"PHRASE_EXPORT_DESC": "Un export dettagliato in formato JSON della tua configurazione per essere usato in altri siti o tools",
"A-Rated": "Classe A",
"about": "Info su Coriolis",
"action": "azione",
"added": "aggiunto",
"Advanced": "Avanzato",
"maneuverability": "agilità",
"ammo": "munizioni",
"PHRASE_CONFIRMATION": "Sei sicuro ?",
"armour": "armatura",
"available": "disponibile",
"bins": "contenitore",
"build": "configurazione",
"build name": "Nome Configurazione",
"builds": "configurazioni",
"buy": "compra",
"cancel": "cancella",
"cells": "celle",
"close": "chiudi",
"compare": "confronta",
"compare all": "confronta tutti",
"comparison": "comparazione",
"comparisons": "comparazioni",
"component": "componente",
"cost": "costo",
"costs": "costi",
"cm": "Contromisure",
"create": "crea",
"create new": "crea nuovo",
"credits": "crediti",
"damage": "danno",
"delete": "elimina",
"delete all": "elimina tutto",
"dep": "dep",
"deployed": "deployed",
"detailed export": "esportazione dettagliata",
"disabled": "disabilita",
"discount": "sconto",
"done": "fatto",
"edit data": "modifica i dati",
"efficiency": "efficenza",
"empty": "vuoto",
"Enforcer": "Rinforzatore",
"enter name": "Inserisci un nome",
"export": "esporta",
"fixed": "fissi",
"fuel": "carburante",
"full tank": "Serbatoio Pieno",
"huge": "enorme",
"hull": "corazza",
"import": "importa",
"import all": "importa tutto",
"insurance": "assicurazione",
"internal compartments": "compartimenti interni",
"jump range": "distanza di salto",
"jumps": "salti",
"laden": "carico",
"language": "lingua",
"large": "largo",
"mass": "massa",
"max": "massimo",
"max mass": "massa massimale",
"medium": "medio",
"net cost": "costo netto",
"PHRASE_NO_BUILDS": "nessuna configurazione è stata aggiunta per la comparazione!",
"PHRASE_NO_RETROCH": "Nessun cambiamento di Retrofitting",
"none": "nessuno",
"none created": "nessuno creato",
"optimal": "ottimale",
"optimal mass": "massa ottimale",
"optimize mass": "ottimizza la massa",
"overwrite": "sovrasscrivi",
"PHRASE_IMPORT": "Incolla un JSON o importalo qua",
"penetration": "penetrazione",
"power": "potenza",
"priority": "priorità",
"proceed": "procedi",
"range": "distanza",
"rate": "rateo",
"recharge": "ricarica",
"reload": "ricarica",
"rename": "rinomina",
"repair": "ripara",
"reset": "resetta",
"retracted": "retratti",
"retrofit costs": "costi di retrofit",
"retrofit from": "retrofit da",
"save": "salva",
"sell": "vendi",
"settings": "impostazioni",
"shields": "scudi",
"ship": "nave",
"ships": "navi",
"shortened": "accorciato",
"size": "grandezza",
"skip": "salta",
"small": "piccolo",
"speed": "velocità",
"Stock": "appena comprata",
"t": "thrusters",
"time": "tempo",
"total": "totale",
"total range": "distanza totale",
"turret": "torrette",
"type": "tipo",
"unladen": "scarico",
"PHRASE_UPDATE_RDY": "Aggiornamenti disponibili ! Clicca per Aggiornare",
"utility": "supporti",
"utility mounts": "supporti di utilità",
"version": "versione",
"yes": "sì",
"PHRASE_BACKUP_DESC": "Esportazione di tutti i dati su Coriolis per salvarli o trasferirli in un altro Browser/dispositivo"
}

View File

@@ -13,65 +13,4 @@ export const formats = {
shortMonths: ['Sty', 'Lut', 'Mar', 'Kwi', 'Maj', 'Cze', 'Lip', 'Sie', 'Wrz', 'Paź', 'Lis', 'Gru'] shortMonths: ['Sty', 'Lut', 'Mar', 'Kwi', 'Maj', 'Cze', 'Lip', 'Sie', 'Wrz', 'Paź', 'Lis', 'Gru']
}; };
export const terms = { export { default as terms } from './pl.json';
PHRASE_ALT_ALL: 'Alt + kliknięcie by wypełnić wszystkie sloty',
PHRASE_BACKUP_DESC: 'Kopia zapasowa wszystkich danych Coriolis w celu zapisu lub przeniesienia na inne urządzenie/przeglądarkę',
PHRASE_CONFIRMATION: 'Czy jesteś pewien?',
PHRASE_EXPORT_DESC: 'Szczegółowy eksport schematu w formacie JSON w celu użycia na innych stronach i narzędziach',
PHRASE_FASTEST_RANGE: 'Maksymalna ilość skoków na najwyższym zasięgu',
PHRASE_IMPORT: 'Wklej tu JSON lub importuj',
PHRASE_LADEN: 'Masa statku + paliwo + ładunek',
PHRASE_NO_BUILDS: 'Nie dodano schematu do porównania!',
PHRASE_NO_RETROCH: 'Brak zmian retrofit',
PHRASE_SELECT_BUILDS: 'Wybierz schematy do porównania',
PHRASE_SG_RECHARGE: 'Czas od 50% do 100% naładowania',
PHRASE_SG_RECOVER: 'Odnowienie (do 50%) po upadku',
PHRASE_UNLADEN: 'Masa statku z wyłączeniem paliwa i ładunku',
PHRASE_UPDATE_RDY: 'Dostępna aktualizacja! Naciśnij by odświeżyć',
// Other languages fallback to these values
// Only Translate to other languages if the name is different in-game
am: 'Auto Field-Maintenance Unit',
bh: 'Bulkheads',
bl: 'Beam Laser',
bsg: 'Bi-Weave Shield Generator',
c: 'Cannon',
cc: 'Collector Limpet Controller',
cm: 'Countermeasure',
cr: 'Cargo Rack',
cs: 'Cargo Scanner',
dc: 'Docking Computer',
fc: 'Fragment Cannon',
fi: 'FSD Interdictor',
fs: 'Fuel Scoop',
fsd: 'Frame Shift Drive',
ft: 'Fuel Tank',
fx: 'Fuel Transfer Limpet Controller',
hb: 'Hatch Breaker Limpet Controller',
hr: 'Hull Reinforcement Package',
kw: 'Kill Warrant Scanner',
ls: 'Life Support',
mc: 'Multi-cannon',
ml: 'Mining Laser',
mr: 'Missile Rack',
nl: 'Mine Launcher',
pa: 'Plasma Accelerator',
pas: 'Planetary Approach Suite',
pc: 'Prospector Limpet Controller',
pd: 'power distributor',
pl: 'Pulse Laser',
pp: 'Power Plant',
psg: 'Prismatic Shield Generator',
pv: 'Planetary Vehicle Hangar',
rf: 'Refinery',
rg: 'Rail Gun',
s: 'Sensors',
sb: 'Shield Booster',
sc: 'Scanner',
scb: 'Shield Cell Bank',
sg: 'Shield Generator',
t: 'thrusters',
tp: 'Torpedo Pylon',
ul: 'Burst Laser',
ws: 'Frame Shift Wake Scanner'
};

59
src/app/i18n/pl.json Normal file
View File

@@ -0,0 +1,59 @@
{
"PHRASE_ALT_ALL": "Alt + kliknięcie by wypełnić wszystkie sloty",
"PHRASE_BACKUP_DESC": "Kopia zapasowa wszystkich danych Coriolis w celu zapisu lub przeniesienia na inne urządzenie/przeglądarkę",
"PHRASE_CONFIRMATION": "Czy jesteś pewien?",
"PHRASE_EXPORT_DESC": "Szczegółowy eksport schematu w formacie JSON w celu użycia na innych stronach i narzędziach",
"PHRASE_FASTEST_RANGE": "Maksymalna ilość skoków na najwyższym zasięgu",
"PHRASE_IMPORT": "Wklej tu JSON lub importuj",
"PHRASE_LADEN": "Masa statku + paliwo + ładunek",
"PHRASE_NO_BUILDS": "Nie dodano schematu do porównania!",
"PHRASE_NO_RETROCH": "Brak zmian retrofit",
"PHRASE_SELECT_BUILDS": "Wybierz schematy do porównania",
"PHRASE_SG_RECHARGE": "Czas od 50% do 100% naładowania",
"PHRASE_SG_RECOVER": "Odnowienie (do 50%) po upadku",
"PHRASE_UNLADEN": "Masa statku z wyłączeniem paliwa i ładunku",
"PHRASE_UPDATE_RDY": "Dostępna aktualizacja! Naciśnij by odświeżyć",
"am": "Auto Field-Maintenance Unit",
"bh": "Bulkheads",
"bl": "Beam Laser",
"bsg": "Bi-Weave Shield Generator",
"c": "Cannon",
"cc": "Collector Limpet Controller",
"cm": "Countermeasure",
"cr": "Cargo Rack",
"cs": "Cargo Scanner",
"dc": "Docking Computer",
"fc": "Fragment Cannon",
"fi": "FSD Interdictor",
"fs": "Fuel Scoop",
"fsd": "Frame Shift Drive",
"ft": "Fuel Tank",
"fx": "Fuel Transfer Limpet Controller",
"hb": "Hatch Breaker Limpet Controller",
"hr": "Hull Reinforcement Package",
"kw": "Kill Warrant Scanner",
"ls": "Life Support",
"mc": "Multi-cannon",
"ml": "Mining Laser",
"mr": "Missile Rack",
"nl": "Mine Launcher",
"pa": "Plasma Accelerator",
"pas": "Planetary Approach Suite",
"pc": "Prospector Limpet Controller",
"pd": "power distributor",
"pl": "Pulse Laser",
"pp": "Power Plant",
"psg": "Prismatic Shield Generator",
"pv": "Planetary Vehicle Hangar",
"rf": "Refinery",
"rg": "Rail Gun",
"s": "Sensors",
"sb": "Shield Booster",
"sc": "Scanner",
"scb": "Shield Cell Bank",
"sg": "Shield Generator",
"t": "thrusters",
"tp": "Torpedo Pylon",
"ul": "Burst Laser",
"ws": "Frame Shift Wake Scanner"
}

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

@@ -0,0 +1,16 @@
export const formats = {
decimal: ',',
thousands: '.',
grouping: [3],
currency: ['', ' €'],
dateTime: '%A, %e de %B de %Y, %X',
date: '%d/%m/%Y',
time: '%H:%M:%S',
periods: ['AM', 'PM'],
days: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes', 'sábado'],
shortDays: ['dom', 'lun', 'mar', 'mié', 'jue', 'vie', 'sáb'],
months: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio', 'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre'],
shortMonths: ['ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul', 'ago', 'sep', 'oct', 'nov', 'dic']
};
export { default as terms } from './pt.json';

278
src/app/i18n/pt.json Normal file

File diff suppressed because one or more lines are too long

View File

@@ -13,447 +13,4 @@ export const formats = {
shortMonths: ['янв', 'фев', 'мар', 'апр', 'май', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек'] shortMonths: ['янв', 'фев', 'мар', 'апр', 'май', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек']
}; };
export const terms = { export { default as terms } from './ru.json';
PHRASE_ALT_ALL: 'Alt + Нажатие для заполнения всех слотов',
PHRASE_BACKUP_DESC: 'Сохраните все данные перед переносом в другой браузер или устройство',
PHRASE_CONFIRMATION: 'Вы уверены?',
PHRASE_EXPORT_DESC: 'Детальный JSON-экспорт вашей сборки для использования в других местах и инструментах',
PHRASE_FASTEST_RANGE: 'Последовательные прыжки максимальной дальности',
PHRASE_IMPORT: 'Для импорта вставьте код в эту форму',
PHRASE_LADEN: 'Масса корабля с учётом топлива и грузов',
PHRASE_NO_BUILDS: 'Нечего сравнивать',
PHRASE_NO_RETROCH: 'Нет ранних версий сборки',
PHRASE_SELECT_BUILDS: 'Выберите конфигурацию для сравнения',
PHRASE_SG_RECHARGE: 'Восстановление с 50% до 100% объема щита, учитывая полный аккумулятор СИС в начале',
PHRASE_SG_RECOVER: 'Восстановление с 0% до 50% объема щита, учитывая полный аккумулятор СИС в начале',
PHRASE_UNLADEN: 'Масса корабля без учета топлива и грузов',
PHRASE_UPDATE_RDY: 'Доступна новая версия. Нажмите для обновления.',
PHRASE_ENGAGEMENT_RANGE: 'Дистанция между кораблём и целью',
PHRASE_SELECT_BLUEPRINT: 'Нажмите чтобы выбрать чертёж',
PHRASE_BLUEPRINT_WORST: 'Худшие основные значения для чертежа',
PHRASE_BLUEPRINT_RANDOM: 'Случайный выбор между худшими и лучшими значениями для этого чертежа',
PHRASE_BLUEPRINT_BEST: 'Лучшие основные значения для чертежа',
PHRASE_BLUEPRINT_EXTREME: 'Лучшие положительные и худшие отрицательные основные значения для чертежа',
PHRASE_BLUEPRINT_RESET: 'Убрать все изменения и чертёж',
PHRASE_SELECT_SPECIAL: 'Нажмите чтобы выбрать экспериментальный эффект',
PHRASE_NO_SPECIAL: 'Без экспериментального эффекта',
PHRASE_SHOPPING_LIST: 'Станции что продают эту сборку',
PHRASE_REFIT_SHOPPING_LIST: 'Станции что продают необходимые модули',
PHRASE_TOTAL_EFFECTIVE_SHIELD: 'Общий урон что может быть нанесён в каждым типе, если используются все щитонакопители',
PHRASE_TIME_TO_LOSE_SHIELDS: 'Щиты продержатся',
PHRASE_TIME_TO_RECOVER_SHIELDS: 'Щиты восстановятся за',
PHRASE_TIME_TO_RECHARGE_SHIELDS: 'Щиты будут заряжены за',
PHRASE_SHIELD_SOURCES: 'Подробности энергии щита',
PHRASE_EFFECTIVE_SHIELD: 'Эффективная сила щита против разных типов урона',
PHRASE_ARMOUR_SOURCES: 'Подробности состава брони',
PHRASE_EFFECTIVE_ARMOUR: 'Эффективная сила брони против разных типов урона',
PHRASE_DAMAGE_TAKEN: '% общих повреждений полученных в разных типах урона',
PHRASE_TIME_TO_LOSE_ARMOUR: 'Броня продержится',
PHRASE_MODULE_PROTECTION_EXTERNAL: 'Защита гнёзд',
PHRASE_MODULE_PROTECTION_INTERNAL: 'Защита всех остальных модулей',
PHRASE_SHIELD_DAMAGE: 'Подробности источников поддерживаемого ДПС против щитов',
PHRASE_ARMOUR_DAMAGE: 'Подробности источников поддерживаемого ДПС против брони',
PHRASE_TIME_TO_REMOVE_SHIELDS: 'Снимет щиты за',
TT_TIME_TO_REMOVE_SHIELDS: 'Непрерывным огнём из всех орудий',
PHRASE_TIME_TO_REMOVE_ARMOUR: 'Снимет броню за',
TT_TIME_TO_REMOVE_ARMOUR: 'Непрерывным огнём из всех орудий',
PHRASE_TIME_TO_DRAIN_WEP: 'Опустошит ОРУЖ за',
TT_TIME_TO_DRAIN_WEP: 'Время за которое опустошится аккумулятор ОРУЖ при стрельбе из всех орудий',
TT_TIME_TO_LOSE_SHIELDS: 'Против поддерживаемой стрельбы из всех орудий противника',
TT_TIME_TO_LOSE_ARMOUR: 'Против поддерживаемой стрельбы из всех орудий противника',
TT_MODULE_ARMOUR: 'Броня защищаюшае модули от урона',
TT_MODULE_PROTECTION_EXTERNAL: 'Процент урона перенаправленного от гнёзд на наборы для усиления модулей',
TT_MODULE_PROTECTION_INTERNAL: 'Процент урона перенаправленного от модулей вне гнёзд на наборы для усиления модулей',
TT_EFFECTIVE_SDPS_SHIELDS: 'Реальный поддерживаемый ДПС пока аккумулятор ОРУЖ не пуст',
TT_EFFECTIVENESS_SHIELDS: 'Эффективность в сравнении с попаданием по цели с 0-сопротивляемостью без пунктов в СИС на 0 метрах',
TT_EFFECTIVE_SDPS_ARMOUR: 'Реальный поддерживаемый ДПС пока аккумулятор ОРУЖ не пуст',
TT_EFFECTIVENESS_ARMOUR: 'Эффективность в сравнении с попаданием по цели с 0-сопротивляемостью на 0 метрах',
PHRASE_EFFECTIVE_SDPS_SHIELDS: 'ПДПС против щитов',
PHRASE_EFFECTIVE_SDPS_ARMOUR: 'ПДПС против брони',
TT_SUMMARY_SPEED: 'С полным топливным баком и 4 пунктами в ДВИ',
TT_SUMMARY_SPEED_NONFUNCTIONAL: 'маневровые двигатели выключены или превышена максимальная масса с топливом и грузом',
TT_SUMMARY_BOOST: 'С полным топливным баком и 4 пунктами в ДВИ',
TT_SUMMARY_BOOST_NONFUNCTIONAL: 'Распределитель питания не может обеспечить достаточно энергии для форсажа',
TT_SUMMARY_SHIELDS: 'Чистая сила щита, включая усилители',
TT_SUMMARY_SHIELDS_NONFUNCTIONAL: 'Шитогенератор отсутствует или выключен',
TT_SUMMARY_INTEGRITY: 'Целостность корабля, включая переборки и наборы для усиления корпуса',
TT_SUMMARY_HULL_MASS: 'Масса корпуса без каких-либо модулей',
TT_SUMMARY_UNLADEN_MASS: 'Масса корпуса и модулей без топлива и груза',
TT_SUMMARY_LADEN_MASS: 'Масса корпуса и модулей с топливом и грузом',
TT_SUMMARY_DPS: 'Урон в секунду при стрельбе из всех орудий',
TT_SUMMARY_EPS: 'Расход аккумулятора ОРУЖ в секунду при стрельбе из всех орудий',
TT_SUMMARY_TTD: 'Время расхода аккумулятора ОРУЖ при стрельбе из всех орудий и с 4 пунктами в ОРУЖ',
TT_SUMMARY_MAX_SINGLE_JUMP: 'Самый дальний возможный прыжок без груза и с топливом достаточным только на сам прыжок',
TT_SUMMARY_UNLADEN_SINGLE_JUMP: 'Самый дальний возможный прыжок без груза и с полным топливным баком',
TT_SUMMARY_LADEN_SINGLE_JUMP: 'Самый дальний возможный прыжок с полным грузовым отсеком и с полным топливным баком',
TT_SUMMARY_UNLADEN_TOTAL_JUMP: 'Самая дальняя общая дистанция без груза, с полным топливным баком и при прыжках на максимальное расстояние',
TT_SUMMARY_LADEN_TOTAL_JUMP: 'Самая дальняя общая дистанция с полным грузовым отсеком, с полным топливным баком и при прыжках на максимальное расстояние',
HELP_MODIFICATIONS_MENU: 'Ткните на номер чтобы ввести новое значение, или потяните вдоль полосы для малых изменений',
// Other languages fallback to these values
// Only Translate to other languages if the name is different in-game
am: 'Блок Автом. Полевого Ремонта',
bh: 'Переборки',
bl: 'Пучковый Лазер',
bsg: 'Двухпоточный Щитогенератор',
c: 'Орудие',
cc: 'Контроллер магнитного снаряда для сбора',
ch: 'Разбрасыватель дипольных отражателей',
cr: 'Грузовой стеллаж',
cs: 'Сканер содержимого',
dc: 'Стыковочный компьютер',
ec: 'Электр. противодействие',
fc: 'Залповое орудие',
fh: 'Ангар для истребителя',
fi: 'FSD-перехватчик',
fs: 'Топливозаборник',
fsd: 'Рамочно Сместительный двигатель',
ft: 'Топливный бак',
fx: 'Контроллер магнитного снаряда для топлива',
hb: 'Контроллер магнитного снаряда для взлома трюма',
hr: 'Набор для усиления корпуса',
hs: 'Теплоотводная катапульта',
kw: 'Сканер преступников',
ls: 'Система жизнеобеспечения',
mc: 'Многоствольное орудие',
ml: 'Проходочный лазер',
mr: 'Ракетный лоток',
mrp: 'Набор для усиления модуля',
nl: 'Мины',
pa: 'Ускоритель плазмы',
pas: 'Комплект для сближения с планетой',
pc: 'Контроллер магнитного снаряда для геологоразведки',
pce: 'Каюта пассажира эконом-класса',
pci: 'Каюта пассажира бизнес-класса',
pcm: 'Каюта пассажира первого класса',
pcq: 'Каюта пассажира класса люкс',
pd: 'Распределитель питания',
pl: 'Ипмульсный лазер',
po: 'Точечная оборона',
pp: 'Силовая установка',
psg: 'Призматический щитогенератор',
pv: 'Гараж для планетарного транспорта',
rf: 'Устройство переработки',
rg: 'Электромагнитная пушка',
s: 'Сенсоры',
sb: 'Усилитель щита',
sc: 'Сканер обнаружения',
scb: 'Щитонакопитель',
sg: 'Щитогенератор',
ss: 'Сканер Поверхностей',
t: 'Маневровые двигатели',
tp: 'Торпедная стойка',
ul: 'Пульсирующие лазеры',
ws: 'Сканер следа FSD',
// Items on the outfitting page
// Notification of restricted slot
emptyrestricted: 'пусто (ограниченно)',
'damage dealt to': 'Урон нанесён',
'damage received from': 'Урон получен от',
'against shields': 'Против шитов',
'against hull': 'Против корпуса',
'total effective shield': 'Общие эффективные щиты',
// 'ammo' was overloaded for outfitting page and modul info, so changed to ammunition for outfitting page
ammunition: 'Припасы',
// Unit for seconds
secs: 'с',
rebuildsperbay: 'Построек за полосу',
// Blueprint rolls
worst: 'Худшее',
average: 'Среднее',
random: 'Случайное',
best: 'Лучшее',
extreme: 'Экстремальное',
reset: 'Обнулить',
// Weapon, offence, defence and movement
dpe: 'Урон на МДж энергии',
dps: 'Урон в Секунду',
sdps: 'Поддерживаемый урон в секунду',
dpssdps: 'Урон в секунду (поддерживаемый урон в секунду)',
eps: 'Энергия в секунду',
epsseps: 'Энергия в секунду (поддерживаемая энергия в секунду)',
hps: 'Нагрев в секунду',
hpsshps: 'Heat per second (sustained heat per second)',
'damage by': 'Урон',
'damage from': 'Урон от',
'shield cells': 'Щитонакопители',
'recovery': 'Восстановление',
'recharge': 'Перезарядка',
'engine pips': 'Пункты в двигателе',
'4b': '4 пункта и Форсаж',
'speed': 'Скорость',
'pitch': 'Тангаж',
'roll': 'Крен',
'yaw': 'Рыскание',
'internal protection': 'Внутренняя защита',
'external protection': 'Внешняя защита',
'engagement range': 'Боевое расстояние',
'total': 'Общее',
// Modifications
ammo: 'Макс. боекомплект',
boot: 'Время загрузки',
brokenregen: 'Скорость восстановления при пробое',
burst: 'Длина очереди',
burstrof: 'Скорострельность очереди',
clip: 'Боекомплект',
damage: 'Урон',
distdraw: 'Тяга распределителя',
duration: 'Продолжительность',
eff: 'Эффективность',
engcap: 'Ресурс двигателей',
engrate: 'Перезарядка двигателей',
explres: 'Сопротивление взрывам',
facinglimit: 'Ограничение по направлению',
hullboost: 'Увеличение корпуса',
hullreinforcement: 'Укрепление корпуса',
integrity: 'Целостность',
jitter: 'Дрожание',
kinres: 'Сопротивление китетическому урону',
maxfuel: 'Макс. топлива на прыжок',
mass: 'Масса',
optmass: 'Оптимизированная масса',
optmul: 'Оптимальный усилитель',
pgen: 'Мощность',
piercing: 'Бронебойность',
power: 'Энергопотребление',
protection: 'Защита от повреждений',
range: 'Дальность',
ranget: 'Дальность', // Range in time (for FSD interdictor)
regen: 'Скорость восстановления',
reload: 'Время перезарядки',
rof: 'Скорострельность',
angle: 'Угол сканера',
scanrate: 'Скорость сканера',
scantime: 'Время сканирования',
shield: 'Щит',
shieldboost: 'Усиление щитов',
shieldreinforcement: 'Усилитель щита',
shotspeed: 'Скорость выстрела',
spinup: 'Время раскрутки',
syscap: 'Ресурс систем',
sysrate: 'Перезарядка систем',
thermload: 'Тепловая нагрузка',
thermres: 'Сопротивление термическому урону',
wepcap: 'Орудийный ресурс',
weprate: 'Перезарядка оружия',
// Shield generators use a different terminology
minmass_sg: 'Мин. масса корпуса',
optmass_sg: 'Опт. масса корпуса',
maxmass_sg: 'Макс. масса корпуса',
minmul_sg: 'Минимальная прочность',
optmul_sg: 'Оптимальная прочность',
maxmul_sg: 'Максимальная прочность',
minmass_psg: 'Мин. масса корпуса',
optmass_psg: 'Опт. масса корпуса',
maxmass_psg: 'Макс. масса корпуса',
minmul_psg: 'Минимальная прочность',
optmul_psg: 'Оптимальная прочность',
maxmul_psg: 'Максимальная прочность',
minmass_bsg: 'Мин. масса корпуса',
optmass_bsg: 'Опт. масса корпуса',
maxmass_bsg: 'Макс. масса корпуса',
minmul_bsg: 'Минимальная прочность',
optmul_bsg: 'Оптимальная прочность',
maxmul_bsg: 'Максимальная прочность',
range_s: 'Типовой диапозон выброса',
// Damage types
absolute: 'Общий',
explosive: 'Взрывч.',
kinetic: 'Механич.',
thermal: 'Тепл.',
// Shield sources
generator: 'Генератор',
boosters: 'Усилители',
cells: 'Накопители',
// Armour sources
bulkheads: 'Переборки',
reinforcement: 'Усилители',
// Panel headings and subheadings
'power and costs': 'Энергия и стоимость',
'costs': 'Цены',
'retrofit costs': 'Стоимость модификации',
'reload costs': 'Стоимость перезарядки',
'profiles': 'Графики',
'engine profile': 'Двигатели',
'fsd profile': 'FSD',
'movement profile': 'Движение',
'damage to opponent\'s shields': 'Урон щиту противника',
'damage to opponent\'s hull': 'Урон корпусу противника',
'offence': 'Нападение',
'defence': 'Оборона',
'shield metrics': 'Данные щита',
'raw shield strength': 'Чистая мощность щита',
'shield sources': 'Ресурсы щита',
'damage taken': 'Полученный урон',
'effective shield': 'Эффективный щит',
'armour metrics': 'Данные брони',
'raw armour strength': 'Чистая мощность брони',
'armour sources': 'Ресурсы брони',
'raw module armour': 'Чистая броня модулей',
'effective armour': 'Эффективная броня',
'offence metrics': 'Данные нападения',
'defence metrics': 'Данные обороны',
// Misc items
'fuel carried': 'Топливо на борту',
'cargo carried': 'Груз на борту',
'ship control': 'Управление кораблём',
'opponent': 'Противник',
'opponent\'s shields': 'Щит противника',
'opponent\'s armour': 'Броня противника',
'shield damage sources': 'источники урона по щиту',
'armour damage sources': 'источники урона по броне',
'never': 'Никогда',
'stock': 'базовый',
'boost': 'Форсаж',
// Units / Metrics
'/s': '/с', // Per second
'm/s': 'м/с', // Meters / Second
Ls: 'Св.сек', // Light seconds
LY: 'Св.лет', // Light Years
CR: 'кр.', // Credits abbreviation
// Sizes
S: 'M', // Small Hardpoint (single Character)
M: 'С', // Medium Hardpoint size (single character)
L: 'б', // Large Hardpoint size (single character)
H: 'O', // Huge Hardpoint size (single character)
U: 'B', // Utility Hardpoint size (single character) - Kill warrant scanner, etc
small: 'Малый', // Small ship size
medium: 'Средний', // Medium ship size
large: 'большой', // Large Ship Size
// Insurance
alpha: 'Альфа', // Alpha backer insurance level
beta: 'Бета', // Beta back insurance level
standard: 'Стандартный', // Standard insurance level
// Terms
'build name': 'название сборки', // Ship build/configuration/design name
'compare all': 'сравнить все',
'create new': 'Создать новый',
'damage per second': 'урон в секунду',
'delete all': 'Удалить все',
'detailed export': 'Подробный экспорт',
'edit data': 'Редактирование',
'empty all': 'пусто все',
'Enter Name': 'Введите имя',
'fastest range': 'быстрый диапазон', // Fastet totaljump range - sum of succesive jumps
'fuel level': 'уровень топлива', // Percent of fuel (T) in the tank
'full tank': 'Полный бак',
'internal compartments': 'внутренние отсеки',
'jump range': 'Дальность прыжка',
'mass lock factor': 'Масс. блок',
'max mass': 'Максимальная масса',
'net cost': 'разница в цене',
'none created': 'не создано',
'refuel time': 'Время дозаправки', // Time to refuel the tank when scooping
'retrofit costs': 'цена модификации', // The cost difference when upgrading / downgrading a component
'retrofit from': 'модификация от', // Retrofit from Build A against build B
'T-Load': 'Тепл.', // Thermal load abbreviation
'utility mounts': 'Вспомогательное оборудование',
about: 'О ...', // Link to about page / about Coriolis.io
action: 'Действие',
added: 'Добавлено',
ammo: 'Боекомплект', // Ammunition
armour: 'Броня',
available: 'доступно', // Available options
backup: 'Резервная копия',
bins: 'контейнеры', // Number of Mining Refinery bins
boost: 'форсаж',
build: 'cборка', // Shorthand for the build/configuration/design name
builds: 'cборки', // Ship build/configuration/design names
buy: 'купить',
cancel: 'отменить',
cargo: 'Груз',
cells: 'Ячейки', // Number of cells in a shield cell bank
close: 'закрыть',
compare: 'сравнить ',
comparison: 'сравнение',
comparisons: 'сравнения',
cost: 'Стоимость', // Cost / price of a module or price of a ship
costs: 'Расходы', // Costs / prices of a modules or prices of ships
create: 'создать',
credits: 'Кредиты',
damage: 'Урон',
delete: 'Удалить',
dep: 'Вып', // Weapons/Hardpoints Deployed abbreviation
deployed: 'Открыты', // Weapons/Hardpoints Deployed
disabled: 'Отключено',
discount: 'Скидка',
DPS: 'УВС', // Damage per second abbreviation
efficiency: 'Эффективность', // Power Plant efficiency
empty: 'пусто',
ENG: 'ДВИ', // Abbreviation - Engine recharge rate for power distributor
export: 'Экспорт',
forum: 'Форум',
fuel: 'Топливо',
hardpoints: 'Орудийные порты',
hull: 'Корпус', // Ships hull
import: 'импортировать ',
insurance: 'Страховка',
jumps: 'Прыжков',
laden: 'Груженый',
language: 'Язык',
maneuverability: 'Маневренность',
mass: 'Масса',
max: 'Макс',
no: 'Нет',
pen: 'ПБ', // Armour peneration abbreviation
permalink: 'Постоянная ссылка',
power: 'Мощность', // Power = Energy / second. Power generated by the power plant, or power consumed (MW / Mega Watts). Used in the power plant section
pri: 'Осн', // Priority abbreviation for power management
proceed: 'продолжить',
PWR: 'Эн', // Power Abbreviation. See Power
range: 'Дальность',
rate: 'скорость',
recharge: 'перезарядка', // Shield Recharge time from 50% -> 100%
recovery: 'включение', // Shield recovery time (after losing shields/turning on -> 50%)
reload: 'Перезагрузить', // Reload weapon/hardpoint
rename: 'Переименовать',
repair: 'Починка',
reset: 'Сброс',
ret: 'Убр.', // Retracted abbreviation
retracted: 'Убрано', // Weapons/Hardpoints retracted
ROF: 'В/сек', // Rate of Fire abbreviation
save: 'Сохранить',
sell: 'Продать',
settings: 'Настройки', // Coriolis application settings
shields: 'Щиты',
ship: 'Корабль',
ships: 'Корабли',
shortened: 'Укороченный', // Standard/Stock build of a ship when purchased new
size: 'размер',
skip: 'пропустить', // Skip past something / ignore it
speed: 'скорость',
standard: 'Стандартный', // Standard / Common modules (FSD, power plant, life support, etc)
Stock: 'Стандартная комплектация', // Thermal-load abbreviation
SYS: 'СИС', // Abbreviation - System recharge rate for power distributor
time: 'Время', // time it takes to complete something
total: 'Всего',
type: 'Тип',
unladen: 'Пустой', // No cargo or fuel
URL: 'Ссылка', // Link, Uniform Resource Locator
WEP: 'ОРУЖ', // Abbreviation - Weapon recharge rate for power distributor
yes: 'Да'
};

384
src/app/i18n/ru.json Normal file
View File

@@ -0,0 +1,384 @@
{
"PHRASE_ALT_ALL": "Alt + Нажатие для заполнения всех слотов",
"PHRASE_BACKUP_DESC": "Сохраните все данные перед переносом в другой браузер или устройство",
"PHRASE_CONFIRMATION": "Вы уверены?",
"PHRASE_EXPORT_DESC": "Детальный JSON-экспорт вашей сборки для использования в других местах и инструментах",
"PHRASE_FASTEST_RANGE": "Последовательные прыжки максимальной дальности",
"PHRASE_IMPORT": "Для импорта вставьте код в эту форму",
"PHRASE_LADEN": "Масса корабля с учётом топлива и грузов",
"PHRASE_NO_BUILDS": "Нечего сравнивать",
"PHRASE_NO_RETROCH": "Нет ранних версий сборки",
"PHRASE_SELECT_BUILDS": "Выберите конфигурацию для сравнения",
"PHRASE_SG_RECHARGE": "Восстановление с 50% до 100% объема щита, учитывая полный аккумулятор СИС в начале",
"PHRASE_SG_RECOVER": "Восстановление с 0% до 50% объема щита, учитывая полный аккумулятор СИС в начале",
"PHRASE_UNLADEN": "Масса корабля без учета топлива и грузов",
"PHRASE_UPDATE_RDY": "Доступна новая версия. Нажмите для обновления.",
"PHRASE_ENGAGEMENT_RANGE": "Дистанция между кораблём и целью",
"PHRASE_SELECT_BLUEPRINT": "Нажмите чтобы выбрать чертёж",
"PHRASE_BLUEPRINT_WORST": "Худшие основные значения для чертежа",
"PHRASE_BLUEPRINT_RANDOM": "Случайный выбор между худшими и лучшими значениями для этого чертежа",
"PHRASE_BLUEPRINT_BEST": "Лучшие основные значения для чертежа",
"PHRASE_BLUEPRINT_EXTREME": "Лучшие положительные и худшие отрицательные основные значения для чертежа",
"PHRASE_BLUEPRINT_RESET": "Убрать все изменения и чертёж",
"PHRASE_SELECT_SPECIAL": "Нажмите чтобы выбрать экспериментальный эффект",
"PHRASE_NO_SPECIAL": "Без экспериментального эффекта",
"PHRASE_SHOPPING_LIST": "Станции что продают эту сборку",
"PHRASE_REFIT_SHOPPING_LIST": "Станции что продают необходимые модули",
"PHRASE_TOTAL_EFFECTIVE_SHIELD": "Общий урон что может быть нанесён в каждым типе, если используются все щитонакопители",
"PHRASE_TIME_TO_LOSE_SHIELDS": "Щиты продержатся",
"PHRASE_TIME_TO_RECOVER_SHIELDS": "Щиты восстановятся за",
"PHRASE_TIME_TO_RECHARGE_SHIELDS": "Щиты будут заряжены за",
"PHRASE_SHIELD_SOURCES": "Подробности энергии щита",
"PHRASE_EFFECTIVE_SHIELD": "Эффективная сила щита против разных типов урона",
"PHRASE_ARMOUR_SOURCES": "Подробности состава брони",
"PHRASE_EFFECTIVE_ARMOUR": "Эффективная сила брони против разных типов урона",
"PHRASE_DAMAGE_TAKEN": "% общих повреждений полученных в разных типах урона",
"PHRASE_TIME_TO_LOSE_ARMOUR": "Броня продержится",
"PHRASE_MODULE_PROTECTION_EXTERNAL": "Защита гнёзд",
"PHRASE_MODULE_PROTECTION_INTERNAL": "Защита всех остальных модулей",
"PHRASE_SHIELD_DAMAGE": "Подробности источников поддерживаемого ДПС против щитов",
"PHRASE_ARMOUR_DAMAGE": "Подробности источников поддерживаемого ДПС против брони",
"PHRASE_TIME_TO_REMOVE_SHIELDS": "Снимет щиты за",
"TT_TIME_TO_REMOVE_SHIELDS": "Непрерывным огнём из всех орудий",
"PHRASE_TIME_TO_REMOVE_ARMOUR": "Снимет броню за",
"TT_TIME_TO_REMOVE_ARMOUR": "Непрерывным огнём из всех орудий",
"PHRASE_TIME_TO_DRAIN_WEP": "Опустошит ОРУЖ за",
"TT_TIME_TO_DRAIN_WEP": "Время за которое опустошится аккумулятор ОРУЖ при стрельбе из всех орудий",
"TT_TIME_TO_LOSE_SHIELDS": "Против поддерживаемой стрельбы из всех орудий противника",
"TT_TIME_TO_LOSE_ARMOUR": "Против поддерживаемой стрельбы из всех орудий противника",
"TT_MODULE_ARMOUR": "Броня защищаюшае модули от урона",
"TT_MODULE_PROTECTION_EXTERNAL": "Процент урона перенаправленного от гнёзд на наборы для усиления модулей",
"TT_MODULE_PROTECTION_INTERNAL": "Процент урона перенаправленного от модулей вне гнёзд на наборы для усиления модулей",
"TT_EFFECTIVE_SDPS_SHIELDS": "Реальный поддерживаемый ДПС пока аккумулятор ОРУЖ не пуст",
"TT_EFFECTIVENESS_SHIELDS": "Эффективность в сравнении с попаданием по цели с 0-сопротивляемостью без пунктов в СИС на 0 метрах",
"TT_EFFECTIVE_SDPS_ARMOUR": "Реальный поддерживаемый ДПС пока аккумулятор ОРУЖ не пуст",
"TT_EFFECTIVENESS_ARMOUR": "Эффективность в сравнении с попаданием по цели с 0-сопротивляемостью на 0 метрах",
"PHRASE_EFFECTIVE_SDPS_SHIELDS": "ПДПС против щитов",
"PHRASE_EFFECTIVE_SDPS_ARMOUR": "ПДПС против брони",
"TT_SUMMARY_SPEED": "С полным топливным баком и 4 пунктами в ДВИ",
"TT_SUMMARY_SPEED_NONFUNCTIONAL": "маневровые двигатели выключены или превышена максимальная масса с топливом и грузом",
"TT_SUMMARY_BOOST": "С полным топливным баком и 4 пунктами в ДВИ",
"TT_SUMMARY_BOOST_NONFUNCTIONAL": "Распределитель питания не может обеспечить достаточно энергии для форсажа",
"TT_SUMMARY_SHIELDS": "Чистая сила щита, включая усилители",
"TT_SUMMARY_SHIELDS_NONFUNCTIONAL": "Шитогенератор отсутствует или выключен",
"TT_SUMMARY_INTEGRITY": "Целостность корабля, включая переборки и наборы для усиления корпуса",
"TT_SUMMARY_HULL_MASS": "Масса корпуса без каких-либо модулей",
"TT_SUMMARY_UNLADEN_MASS": "Масса корпуса и модулей без топлива и груза",
"TT_SUMMARY_LADEN_MASS": "Масса корпуса и модулей с топливом и грузом",
"TT_SUMMARY_DPS": "Урон в секунду при стрельбе из всех орудий",
"TT_SUMMARY_EPS": "Расход аккумулятора ОРУЖ в секунду при стрельбе из всех орудий",
"TT_SUMMARY_TTD": "Время расхода аккумулятора ОРУЖ при стрельбе из всех орудий и с 4 пунктами в ОРУЖ",
"TT_SUMMARY_MAX_SINGLE_JUMP": "Самый дальний возможный прыжок без груза и с топливом достаточным только на сам прыжок",
"TT_SUMMARY_UNLADEN_SINGLE_JUMP": "Самый дальний возможный прыжок без груза и с полным топливным баком",
"TT_SUMMARY_LADEN_SINGLE_JUMP": "Самый дальний возможный прыжок с полным грузовым отсеком и с полным топливным баком",
"TT_SUMMARY_UNLADEN_TOTAL_JUMP": "Самая дальняя общая дистанция без груза, с полным топливным баком и при прыжках на максимальное расстояние",
"TT_SUMMARY_LADEN_TOTAL_JUMP": "Самая дальняя общая дистанция с полным грузовым отсеком, с полным топливным баком и при прыжках на максимальное расстояние",
"HELP_MODIFICATIONS_MENU": "Ткните на номер чтобы ввести новое значение, или потяните вдоль полосы для малых изменений",
"am": "Блок Автом. Полевого Ремонта",
"bh": "Переборки",
"bl": "Пучковый Лазер",
"bsg": "Двухпоточный Щитогенератор",
"c": "Орудие",
"cc": "Контроллер магнитного снаряда для сбора",
"ch": "Разбрасыватель дипольных отражателей",
"cr": "Грузовой стеллаж",
"cs": "Сканер содержимого",
"dc": "Стыковочный компьютер",
"ec": "Электр. противодействие",
"fc": "Залповое орудие",
"fh": "Ангар для истребителя",
"fi": "FSD-перехватчик",
"fs": "Топливозаборник",
"fsd": "Рамочно Сместительный двигатель",
"ft": "Топливный бак",
"fx": "Контроллер магнитного снаряда для топлива",
"hb": "Контроллер магнитного снаряда для взлома трюма",
"hr": "Набор для усиления корпуса",
"hs": "Теплоотводная катапульта",
"kw": "Сканер преступников",
"ls": "Система жизнеобеспечения",
"mc": "Многоствольное орудие",
"ml": "Проходочный лазер",
"mr": "Ракетный лоток",
"mrp": "Набор для усиления модуля",
"nl": "Мины",
"pa": "Ускоритель плазмы",
"pas": "Комплект для сближения с планетой",
"pc": "Контроллер магнитного снаряда для геологоразведки",
"pce": "Каюта пассажира эконом-класса",
"pci": "Каюта пассажира бизнес-класса",
"pcm": "Каюта пассажира первого класса",
"pcq": "Каюта пассажира класса люкс",
"pd": "Распределитель питания",
"pl": "Ипмульсный лазер",
"po": "Точечная оборона",
"pp": "Силовая установка",
"psg": "Призматический щитогенератор",
"pv": "Гараж для планетарного транспорта",
"rf": "Устройство переработки",
"rg": "Электромагнитная пушка",
"s": "Сенсоры",
"sb": "Усилитель щита",
"sc": "Сканер обнаружения",
"scb": "Щитонакопитель",
"sg": "Щитогенератор",
"ss": "Сканер Поверхностей",
"t": "Маневровые двигатели",
"tp": "Торпедная стойка",
"ul": "Пульсирующие лазеры",
"ws": "Сканер следа FSD",
"emptyrestricted": "пусто (ограниченно)",
"damage dealt to": "Урон нанесён",
"damage received from": "Урон получен от",
"against shields": "Против шитов",
"against hull": "Против корпуса",
"total effective shield": "Общие эффективные щиты",
"ammunition": "Припасы",
"secs": "с",
"rebuildsperbay": "Построек за полосу",
"worst": "Худшее",
"average": "Среднее",
"random": "Случайное",
"best": "Лучшее",
"extreme": "Экстремальное",
"reset": "Сброс",
"dpe": "Урон на МДж энергии",
"dps": "Урон в Секунду",
"sdps": "Поддерживаемый урон в секунду",
"dpssdps": "Урон в секунду (поддерживаемый урон в секунду)",
"eps": "Энергия в секунду",
"epsseps": "Энергия в секунду (поддерживаемая энергия в секунду)",
"hps": "Нагрев в секунду",
"hpsshps": "Heat per second (sustained heat per second)",
"damage by": "Урон",
"damage from": "Урон от",
"shield cells": "Щитонакопители",
"recovery": "включение",
"recharge": "перезарядка",
"engine pips": "Пункты в двигателе",
"4b": "4 пункта и Форсаж",
"speed": "скорость",
"pitch": "Тангаж",
"roll": "Крен",
"yaw": "Рыскание",
"internal protection": "Внутренняя защита",
"external protection": "Внешняя защита",
"engagement range": "Боевое расстояние",
"total": "Всего",
"ammo": "Боекомплект",
"boot": "Время загрузки",
"brokenregen": "Скорость восстановления при пробое",
"burst": "Длина очереди",
"burstrof": "Скорострельность очереди",
"clip": "Боекомплект",
"damage": "Урон",
"distdraw": "Тяга распределителя",
"duration": "Продолжительность",
"eff": "Эффективность",
"engcap": "Ресурс двигателей",
"engrate": "Перезарядка двигателей",
"explres": "Сопротивление взрывам",
"facinglimit": "Ограничение по направлению",
"hullboost": "Увеличение корпуса",
"hullreinforcement": "Укрепление корпуса",
"integrity": "Целостность",
"jitter": "Дрожание",
"kinres": "Сопротивление китетическому урону",
"maxfuel": "Макс. топлива на прыжок",
"mass": "Масса",
"optmass": "Оптимизированная масса",
"optmul": "Оптимальный усилитель",
"pgen": "Мощность",
"piercing": "Бронебойность",
"power": "Мощность",
"protection": "Защита от повреждений",
"range": "Дальность",
"ranget": "Дальность",
"regen": "Скорость восстановления",
"reload": "Перезагрузить",
"rof": "Скорострельность",
"angle": "Угол сканера",
"scanrate": "Скорость сканера",
"scantime": "Время сканирования",
"shield": "Щит",
"shieldboost": "Усиление щитов",
"shieldreinforcement": "Усилитель щита",
"shotspeed": "Скорость выстрела",
"spinup": "Время раскрутки",
"syscap": "Ресурс систем",
"sysrate": "Перезарядка систем",
"thermload": "Тепловая нагрузка",
"thermres": "Сопротивление термическому урону",
"wepcap": "Орудийный ресурс",
"weprate": "Перезарядка оружия",
"minmass_sg": "Мин. масса корпуса",
"optmass_sg": "Опт. масса корпуса",
"maxmass_sg": "Макс. масса корпуса",
"minmul_sg": "Минимальная прочность",
"optmul_sg": "Оптимальная прочность",
"maxmul_sg": "Максимальная прочность",
"minmass_psg": "Мин. масса корпуса",
"optmass_psg": "Опт. масса корпуса",
"maxmass_psg": "Макс. масса корпуса",
"minmul_psg": "Минимальная прочность",
"optmul_psg": "Оптимальная прочность",
"maxmul_psg": "Максимальная прочность",
"minmass_bsg": "Мин. масса корпуса",
"optmass_bsg": "Опт. масса корпуса",
"maxmass_bsg": "Макс. масса корпуса",
"minmul_bsg": "Минимальная прочность",
"optmul_bsg": "Оптимальная прочность",
"maxmul_bsg": "Максимальная прочность",
"range_s": "Типовой диапозон выброса",
"absolute": "Общий",
"explosive": "Взрывч.",
"kinetic": "Механич.",
"thermal": "Тепл.",
"generator": "Генератор",
"boosters": "Усилители",
"cells": "Ячейки",
"bulkheads": "Переборки",
"reinforcement": "Усилители",
"power and costs": "Энергия и стоимость",
"costs": "Расходы",
"retrofit costs": "цена модификации",
"reload costs": "Стоимость перезарядки",
"profiles": "Графики",
"engine profile": "Двигатели",
"fsd profile": "FSD",
"movement profile": "Движение",
"damage to opponent's shields": "Урон щиту противника",
"damage to opponent's hull": "Урон корпусу противника",
"offence": "Нападение",
"defence": "Оборона",
"shield metrics": "Данные щита",
"raw shield strength": "Чистая мощность щита",
"shield sources": "Ресурсы щита",
"damage taken": "Полученный урон",
"effective shield": "Эффективный щит",
"armour metrics": "Данные брони",
"raw armour strength": "Чистая мощность брони",
"armour sources": "Ресурсы брони",
"raw module armour": "Чистая броня модулей",
"effective armour": "Эффективная броня",
"offence metrics": "Данные нападения",
"defence metrics": "Данные обороны",
"fuel carried": "Топливо на борту",
"cargo carried": "Груз на борту",
"ship control": "Управление кораблём",
"opponent": "Противник",
"opponent's shields": "Щит противника",
"opponent's armour": "Броня противника",
"shield damage sources": "источники урона по щиту",
"armour damage sources": "источники урона по броне",
"never": "Никогда",
"stock": "базовый",
"boost": "форсаж",
"/s": "/с",
"m/s": "м/с",
"Ls": "Св.сек",
"LY": "Св.лет",
"CR": "кр.",
"S": "M",
"M": "С",
"L": "б",
"H": "O",
"U": "B",
"small": "Малый",
"medium": "Средний",
"large": "большой",
"alpha": "Альфа",
"beta": "Бета",
"standard": "Стандартный",
"build name": "название сборки",
"compare all": "сравнить все",
"create new": "Создать новый",
"damage per second": "урон в секунду",
"delete all": "Удалить все",
"detailed export": "Подробный экспорт",
"edit data": "Редактирование",
"empty all": "пусто все",
"Enter Name": "Введите имя",
"fastest range": "быстрый диапазон",
"fuel level": "уровень топлива",
"full tank": "Полный бак",
"internal compartments": "внутренние отсеки",
"jump range": "Дальность прыжка",
"mass lock factor": "Масс. блок",
"max mass": "Максимальная масса",
"net cost": "разница в цене",
"none created": "не создано",
"refuel time": "Время дозаправки",
"retrofit from": "модификация от",
"T-Load": "Тепл.",
"utility mounts": "Вспомогательное оборудование",
"about": "О ...",
"action": "Действие",
"added": "Добавлено",
"armour": "Броня",
"available": "доступно",
"backup": "Резервная копия",
"bins": "контейнеры",
"build": "cборка",
"builds": "cборки",
"buy": "купить",
"cancel": "отменить",
"cargo": "Груз",
"close": "закрыть",
"compare": "сравнить ",
"comparison": "сравнение",
"comparisons": "сравнения",
"cost": "Стоимость",
"create": "создать",
"credits": "Кредиты",
"delete": "Удалить",
"dep": "Вып",
"deployed": "Открыты",
"disabled": "Отключено",
"discount": "Скидка",
"DPS": "УВС",
"efficiency": "Эффективность",
"empty": "пусто",
"ENG": "ДВИ",
"export": "Экспорт",
"forum": "Форум",
"fuel": "Топливо",
"hardpoints": "Орудийные порты",
"hull": "Корпус",
"import": "импортировать ",
"insurance": "Страховка",
"jumps": "Прыжков",
"laden": "Груженый",
"language": "Язык",
"maneuverability": "Маневренность",
"max": "Макс",
"no": "Нет",
"pen": "ПБ",
"permalink": "Постоянная ссылка",
"pri": "Осн",
"proceed": "продолжить",
"PWR": "Эн",
"rate": "скорость",
"rename": "Переименовать",
"repair": "Починка",
"ret": "Убр.",
"retracted": "Убрано",
"ROF": "В/сек",
"save": "Сохранить",
"sell": "Продать",
"settings": "Настройки",
"shields": "Щиты",
"ship": "Корабль",
"ships": "Корабли",
"shortened": "Укороченный",
"size": "размер",
"skip": "пропустить",
"Stock": "Стандартная комплектация",
"SYS": "СИС",
"time": "Время",
"type": "Тип",
"unladen": "Пустой",
"URL": "Ссылка",
"WEP": "ОРУЖ",
"yes": "Да"
}

View File

@@ -41,6 +41,9 @@ export default class AboutPage extends Page {
<h3>Chat</h3> <h3>Chat</h3>
<p>You can chat to us on our <a href='https://discord.gg/0uwCh6R62aPRjk9w' target='_blank'>EDCD Discord server</a>.</p> <p>You can chat to us on our <a href='https://discord.gg/0uwCh6R62aPRjk9w' target='_blank'>EDCD Discord server</a>.</p>
<h3>Supporting Coriolis</h3>
<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>
</div>; </div>;
} }
} }

View File

@@ -9,7 +9,7 @@ import * as Utils from '../utils/UtilityFunctions';
import Ship from '../shipyard/Ship'; import Ship from '../shipyard/Ship';
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 } from '../components/SvgIcons'; import { FloppyDisk, Bin, Switch, Download, Reload, LinkIcon, ShoppingIcon, MatIcon } 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';
@@ -25,6 +25,7 @@ import EngagementRange from '../components/EngagementRange';
import OutfittingSubpages from '../components/OutfittingSubpages'; 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';
/** /**
* Document Title Generator * Document Title Generator
@@ -58,6 +59,7 @@ export default class OutfittingPage extends Page {
this._fuelUpdated = this._fuelUpdated.bind(this); this._fuelUpdated = this._fuelUpdated.bind(this);
this._opponentUpdated = this._opponentUpdated.bind(this); this._opponentUpdated = this._opponentUpdated.bind(this);
this._engagementRangeUpdated = this._engagementRangeUpdated.bind(this); this._engagementRangeUpdated = this._engagementRangeUpdated.bind(this);
this._sectionMenuRefs = {};
} }
/** /**
@@ -73,13 +75,10 @@ export default class OutfittingPage extends Page {
let buildName = params.bn; let buildName = params.bn;
let data = Ships[shipId]; // Retrieve the basic ship properties, slots and defaults let data = Ships[shipId]; // Retrieve the basic ship properties, slots and defaults
let savedCode = Persist.getBuild(shipId, buildName); let savedCode = Persist.getBuild(shipId, buildName);
if (!data) { if (!data) {
return { error: { message: 'Ship not found: ' + shipId } }; return { error: { message: 'Ship not found: ' + shipId } };
} }
let ship = new Ship(shipId, data.properties, data.slots); // Create a new Ship instance let ship = new Ship(shipId, data.properties, data.slots); // Create a new Ship instance
if (code) { if (code) {
ship.buildFrom(code); // Populate modules from serialized 'code' URL param ship.buildFrom(code); // Populate modules from serialized 'code' URL param
} else { } else {
@@ -508,6 +507,13 @@ export default class OutfittingPage extends Page {
window.open('https://eddb.io/station?s=' + shipId + '&m=' + modIds.join(',')); window.open('https://eddb.io/station?s=' + shipId + '&m=' + modIds.join(','));
} }
/**
* Generates the shopping list
*/
_genShoppingList() {
this.context.showModal(<ModalShoppingList ship={this.state.ship}/>);
}
/** /**
* Handle Key Down * Handle Key Down
* @param {Event} e Keyboard Event * @param {Event} e Keyboard Event
@@ -556,10 +562,34 @@ export default class OutfittingPage extends Page {
const boostMarker = `${ship.canBoost(cargo, fuel)}`; const boostMarker = `${ship.canBoost(cargo, fuel)}`;
const shipSummaryMarker = `${ship.name}${_sStr}${_iStr}${_hStr}${_pStr}${_mStr}${ship.ladenMass}${cargo}${fuel}`; const shipSummaryMarker = `${ship.name}${_sStr}${_iStr}${_hStr}${_pStr}${_mStr}${ship.ladenMass}${cargo}${fuel}`;
const requirements = Ships[ship.id].requirements;
let requirementElements = [];
/**
* Render the requirements for a ship / etc
* @param {string} className Class names
* @param {string} textKey The key for translating
* @param {String} tooltipTextKey Tooltip key
*/
function renderRequirement(className, textKey, tooltipTextKey) {
if (textKey.startsWith('empire') || textKey.startsWith('federation')) {
requirementElements.push(<div key={textKey} className={className} onMouseEnter={termtip.bind(null, tooltipTextKey)} onMouseLeave={hide}><a href={textKey.startsWith('empire') ? 'http://elite-dangerous.wikia.com/wiki/Empire/Ranks' : 'http://elite-dangerous.wikia.com/wiki/Federation/Ranks'} target="_blank" rel="noopener">{translate(textKey)}</a></div>);
} else {
requirementElements.push(<div key={textKey} className={className} onMouseEnter={termtip.bind(null, tooltipTextKey)} onMouseLeave={hide}>{translate(textKey)}</div>);
}
}
if (requirements) {
requirements.federationRank && renderRequirement('federation', 'federation rank ' + requirements.federationRank, 'federation rank required');
requirements.empireRank && renderRequirement('empire', 'empire rank ' + requirements.empireRank, 'empire rank required');
requirements.horizons && renderRequirement('horizons', 'horizons', 'horizons required');
requirements.horizonsEarlyAdoption && renderRequirement('horizons', 'horizons early adoption', 'horizons early adoption required');
}
return ( return (
<div id='outfit' className={'page'} style={{ fontSize: (sizeRatio * 0.9) + 'em' }}> <div id='outfit' className={'page'} style={{ fontSize: (sizeRatio * 0.9) + 'em' }}>
<div id='overview'> <div id='overview'>
<h1>{ship.name}</h1> <h1>{ship.name}</h1>
<div id='requirements'>{requirementElements}</div>
<div id='build'> <div id='build'>
<input value={newBuildName || ''} onChange={this._buildNameChange} placeholder={translate('Enter Name')} maxLength={50} /> <input value={newBuildName || ''} onChange={this._buildNameChange} placeholder={translate('Enter Name')} maxLength={50} />
<button onClick={canSave && this._saveBuild} disabled={!canSave} onMouseOver={termtip.bind(null, 'save')} onMouseOut={hide}> <button onClick={canSave && this._saveBuild} disabled={!canSave} onMouseOver={termtip.bind(null, 'save')} onMouseOut={hide}>
@@ -586,15 +616,18 @@ 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._genShoppingList} onMouseOver={termtip.bind(null, 'PHRASE_SHOPPING_MATS')} onMouseOut={hide}>
<MatIcon className='lg' />
</button>
</div> </div>
</div> </div>
{/* Main tables */} {/* Main tables */}
<ShipSummaryTable ship={ship} fuel={fuel} cargo={cargo} marker={shipSummaryMarker} /> <ShipSummaryTable ship={ship} fuel={fuel} cargo={cargo} marker={shipSummaryMarker} pips={{ sys: this.state.sys, wep: this.state.wep, eng: this.state.eng }} />
<StandardSlotSection ship={ship} fuel={fuel} cargo={cargo} code={standardSlotMarker} onChange={shipUpdated} onCargoChange={this._cargoUpdated} onFuelChange={this._fuelUpdated} currentMenu={menu} /> <StandardSlotSection ship={ship} fuel={fuel} cargo={cargo} code={standardSlotMarker} onChange={shipUpdated} onCargoChange={this._cargoUpdated} onFuelChange={this._fuelUpdated} currentMenu={menu} sectionMenuRefs={this._sectionMenuRefs}/>
<InternalSlotSection ship={ship} code={internalSlotMarker} onChange={shipUpdated} onCargoChange={this._cargoUpdated} onFuelChange={this._fuelUpdated} currentMenu={menu} /> <InternalSlotSection ship={ship} code={internalSlotMarker} onChange={shipUpdated} onCargoChange={this._cargoUpdated} onFuelChange={this._fuelUpdated} currentMenu={menu} sectionMenuRefs={this._sectionMenuRefs}/>
<HardpointSlotSection ship={ship} code={hardpointsSlotMarker} onChange={shipUpdated} onCargoChange={this._cargoUpdated} onFuelChange={this._fuelUpdated} currentMenu={menu} /> <HardpointSlotSection ship={ship} code={hardpointsSlotMarker} onChange={shipUpdated} onCargoChange={this._cargoUpdated} onFuelChange={this._fuelUpdated} currentMenu={menu} sectionMenuRefs={this._sectionMenuRefs}/>
<UtilitySlotSection ship={ship} code={hardpointsSlotMarker} onChange={shipUpdated} onCargoChange={this._cargoUpdated} onFuelChange={this._fuelUpdated} currentMenu={menu} /> <UtilitySlotSection ship={ship} code={hardpointsSlotMarker} onChange={shipUpdated} onCargoChange={this._cargoUpdated} onFuelChange={this._fuelUpdated} currentMenu={menu} sectionMenuRefs={this._sectionMenuRefs}/>
{/* Control of ship and opponent */} {/* Control of ship and opponent */}
<div className='group quarter'> <div className='group quarter'>

View File

@@ -25,6 +25,25 @@ function countInt(slot) {
this.int[slot.maxClass - 1]++; // Subtract 1 since there is no Class 0 Internal compartment this.int[slot.maxClass - 1]++; // Subtract 1 since there is no Class 0 Internal compartment
this.intCount++; this.intCount++;
this.maxCargo += crEligible ? ModuleUtils.findInternal('cr', slot.maxClass, 'E').cargo : 0; this.maxCargo += crEligible ? ModuleUtils.findInternal('cr', slot.maxClass, 'E').cargo : 0;
// if no eligiblity, then assume pce
let passSlotType = null;
let passSlotRating = null;
if (!slot.eligible || slot.eligible.pce) {
passSlotType = 'pce';
passSlotRating = 'E';
} else if (slot.eligible.pci) {
passSlotType = 'pci';
passSlotRating = 'D';
} else if (slot.eligible.pcm) {
passSlotType = 'pcm';
passSlotRating = 'C';
} else if (slot.eligible.pcq) {
passSlotType = 'pcq';
passSlotRating = 'B';
}
let passengerBay = passSlotType ? ModuleUtils.findMaxInternal(passSlotType, slot.maxClass, passSlotRating) : null;
this.maxPassengers += passengerBay ? passengerBay.passengers : 0;
} }
/** /**
@@ -39,6 +58,7 @@ function shipSummary(shipId, shipData) {
hpCount: 0, hpCount: 0,
intCount: 0, intCount: 0,
maxCargo: 0, maxCargo: 0,
maxPassengers: 0,
hp: [0, 0, 0, 0, 0], // Utility, Small, Medium, Large, Huge hp: [0, 0, 0, 0, 0], // Utility, Small, Medium, Large, Huge
int: [0, 0, 0, 0, 0, 0, 0, 0], // Sizes 1 - 8 int: [0, 0, 0, 0, 0, 0, 0, 0], // Sizes 1 - 8
standard: shipData.slots.standard, standard: shipData.slots.standard,
@@ -139,15 +159,16 @@ export default class ShipyardPage extends Page {
* @param {Object} u Localized unit map * @param {Object} u Localized unit map
* @param {Function} fInt Localized integer formatter * @param {Function} fInt Localized integer formatter
* @param {Function} fRound Localized round formatter * @param {Function} fRound Localized round formatter
* @param {Boolean} highlight Should this row be highlighted
* @return {React.Component} Table Row * @return {React.Component} Table Row
*/ */
_shipRowElement(s, translate, u, fInt, fRound) { _shipRowElement(s, translate, u, fInt, fRound, highlight) {
let noTouch = this.context.noTouch; let noTouch = this.context.noTouch;
return <tr return <tr
key={s.id} key={s.id}
style={{ height: '1.5em' }} style={{ height: '1.5em' }}
className={cn({ highlighted: noTouch && this.state.shipId === s.id })} className={cn({ highlighted: noTouch && this.state.shipId === s.id, alt: highlight })}
onMouseEnter={noTouch && this._highlightShip.bind(this, s.id)} onMouseEnter={noTouch && this._highlightShip.bind(this, s.id)}
> >
<td className='ri'>{s.manufacturer}</td> <td className='ri'>{s.manufacturer}</td>
@@ -166,12 +187,14 @@ export default class ShipyardPage extends Page {
<td className='ri'>{fInt(s.topBoost)}</td> <td className='ri'>{fInt(s.topBoost)}</td>
<td className='ri'>{fRound(s.maxJumpRange)}</td> <td className='ri'>{fRound(s.maxJumpRange)}</td>
<td className='ri'>{fInt(s.maxCargo)}</td> <td className='ri'>{fInt(s.maxCargo)}</td>
<td className='ri'>{fInt(s.maxPassengers)}</td>
<td className='cn'>{s.standard[0]}</td> <td className='cn'>{s.standard[0]}</td>
<td className='cn'>{s.standard[1]}</td> <td className='cn'>{s.standard[1]}</td>
<td className='cn'>{s.standard[2]}</td> <td className='cn'>{s.standard[2]}</td>
<td className='cn'>{s.standard[3]}</td> <td className='cn'>{s.standard[3]}</td>
<td className='cn'>{s.standard[4]}</td> <td className='cn'>{s.standard[4]}</td>
<td className='cn'>{s.standard[5]}</td> <td className='cn'>{s.standard[5]}</td>
<td className='cn'>{s.standard[6]}</td>
<td className={cn({ disabled: !s.hp[1] })}>{s.hp[1]}</td> <td className={cn({ disabled: !s.hp[1] })}>{s.hp[1]}</td>
<td className={cn({ disabled: !s.hp[2] })}>{s.hp[2]}</td> <td className={cn({ disabled: !s.hp[2] })}>{s.hp[2]}</td>
<td className={cn({ disabled: !s.hp[3] })}>{s.hp[3]}</td> <td className={cn({ disabled: !s.hp[3] })}>{s.hp[3]}</td>
@@ -246,13 +269,26 @@ export default class ShipyardPage extends Page {
let shipRows = new Array(shipSummaries.length); let shipRows = new Array(shipSummaries.length);
let detailRows = new Array(shipSummaries.length); let detailRows = new Array(shipSummaries.length);
let lastShipSortValue = null;
let backgroundHighlight = false;
for (let s of shipSummaries) { for (let s of shipSummaries) {
detailRows[i] = this._shipRowElement(s, translate, units, fInt, formats.f1); let shipSortValue = s[shipPredicate];
if(shipPredicateIndex != undefined) {
shipSortValue = shipSortValue[shipPredicateIndex];
}
if(shipSortValue != lastShipSortValue) {
backgroundHighlight = !backgroundHighlight;
lastShipSortValue = shipSortValue;
}
detailRows[i] = this._shipRowElement(s, translate, units, fInt, formats.f1, backgroundHighlight);
shipRows[i] = ( shipRows[i] = (
<tr <tr
key={i} key={i}
style={{ height: '1.5em' }} style={{ height: '1.5em' }}
className={cn({ highlighted: noTouch && this.state.shipId === s.id })} className={cn({ highlighted: noTouch && this.state.shipId === s.id, alt: backgroundHighlight })}
onMouseEnter={noTouch && this._highlightShip.bind(this, s.id)} onMouseEnter={noTouch && this._highlightShip.bind(this, s.id)}
> >
<td className='le'><Link href={'/outfit/' + s.id}>{s.name}</Link></td> <td className='le'><Link href={'/outfit/' + s.id}>{s.name}</Link></td>
@@ -293,8 +329,8 @@ export default class ShipyardPage extends Page {
<th rowSpan={3} className='sortable' onMouseEnter={termtip.bind(null, 'hardness')} onMouseLeave={hide} onClick={sortShips('hardness')}>{translate('hrd')}</th> <th rowSpan={3} className='sortable' onMouseEnter={termtip.bind(null, 'hardness')} onMouseLeave={hide} onClick={sortShips('hardness')}>{translate('hrd')}</th>
<th>&nbsp;</th> <th>&nbsp;</th>
<th colSpan={4}>{translate('base')}</th> <th colSpan={4}>{translate('base')}</th>
<th colSpan={4}>{translate('max')}</th> <th colSpan={5}>{translate('max')}</th>
<th className='lft' colSpan={6}></th> <th className='lft' colSpan={7}></th>
<th className='lft' colSpan={5}></th> <th className='lft' colSpan={5}></th>
<th className='lft' colSpan={8}></th> <th className='lft' colSpan={8}></th>
</tr> </tr>
@@ -310,29 +346,31 @@ export default class ShipyardPage extends Page {
<th className='sortable' onClick={sortShips('topBoost')}>{translate('boost')}</th> <th className='sortable' onClick={sortShips('topBoost')}>{translate('boost')}</th>
<th className='sortable' onClick={sortShips('maxJumpRange')}>{translate('jump')}</th> <th className='sortable' onClick={sortShips('maxJumpRange')}>{translate('jump')}</th>
<th className='sortable' onClick={sortShips('maxCargo')}>{translate('cargo')}</th> <th className='sortable' onClick={sortShips('maxCargo')}>{translate('cargo')}</th>
<th className='sortable' onClick={sortShips('maxPassengers')}>{translate('pax')}</th>
<th className='lft' colSpan={6}>{translate('core module classes')}</th> <th className='lft' colSpan={7}>{translate('core module classes')}</th>
<th colSpan={5} className='sortable lft' onClick={sortShips('hpCount')}>{translate('hardpoints')}</th> <th colSpan={5} className='sortable lft' onClick={sortShips('hpCount')}>{translate('hardpoints')}</th>
<th colSpan={8} className='sortable lft' onClick={sortShips('intCount')}>{translate('internal compartments')}</th> <th colSpan={8} className='sortable lft' onClick={sortShips('intCount')}>{translate('internal compartments')}</th>
</tr> </tr>
<tr> <tr>
<th className='sortable lft' onClick={sortShips('retailCost')}>{units.CR}</th> <th className='sortable lft' onClick={sortShips('retailCost')}>{units.CR}</th>
<th className='sortable lft' onClick={sortShips('hullMass')}>{units.T}</th> <th className='sortable lft' onClick={sortShips('hullMass')}>{units.T}</th>
<th className='sortable lft' onClick={sortShips('speed')}>{units['m/s']}</th> <th className='sortable lft' onClick={sortShips('speed')}>{units['m/s']}</th>
<th className='sortable' onClick={sortShips('boost')}>{units['m/s']}</th> <th className='sortable' onClick={sortShips('boost')}>{units['m/s']}</th>
<th>&nbsp;</th> <th>&nbsp;</th>
<th className='sortable' onClick={sortShips('baseShieldStrength')}>{units.MJ}</th> <th className='sortable' onClick={sortShips('baseShieldStrength')}>{units.MJ}</th>
<th className='sortable lft' onClick={sortShips('topSpeed')}>{units['m/s']}</th> <th className='sortable lft' onClick={sortShips('topSpeed')}>{units['m/s']}</th>
<th className='sortable' onClick={sortShips('topBoost')}>{units['m/s']}</th> <th className='sortable' onClick={sortShips('topBoost')}>{units['m/s']}</th>
<th className='sortable' onClick={sortShips('maxJumpRange')}>{units.LY}</th> <th className='sortable' onClick={sortShips('maxJumpRange')}>{units.LY}</th>
<th className='sortable' onClick={sortShips('maxCargo')}>{units.T}</th> <th className='sortable' onClick={sortShips('maxCargo')}>{units.T}</th>
<th>&nbsp;</th>
<th className='sortable lft' onMouseEnter={termtip.bind(null, 'power plant')} onMouseLeave={hide} onClick={sortShips('standard', 0)}>{'pp'}</th> <th className='sortable lft' onMouseEnter={termtip.bind(null, 'power plant')} onMouseLeave={hide} onClick={sortShips('standard', 0)}>{'pp'}</th>
<th className='sortable' onMouseEnter={termtip.bind(null, 'thrusters')} onMouseLeave={hide} onClick={sortShips('standard', 1)}>{'th'}</th> <th className='sortable' onMouseEnter={termtip.bind(null, 'thrusters')} onMouseLeave={hide} onClick={sortShips('standard', 1)}>{'th'}</th>
<th className='sortable' onMouseEnter={termtip.bind(null, 'frame shift drive')} onMouseLeave={hide} onClick={sortShips('standard', 2)}>{'fsd'}</th> <th className='sortable' onMouseEnter={termtip.bind(null, 'frame shift drive')} onMouseLeave={hide} onClick={sortShips('standard', 2)}>{'fsd'}</th>
<th className='sortable' onMouseEnter={termtip.bind(null, 'life support')} onMouseLeave={hide} onClick={sortShips('standard', 3)}>{'ls'}</th> <th className='sortable' onMouseEnter={termtip.bind(null, 'life support')} onMouseLeave={hide} onClick={sortShips('standard', 3)}>{'ls'}</th>
<th className='sortable' onMouseEnter={termtip.bind(null, 'power distriubtor')} onMouseLeave={hide} onClick={sortShips('standard', 4)}>{'pd'}</th> <th className='sortable' onMouseEnter={termtip.bind(null, 'power distriubtor')} onMouseLeave={hide} onClick={sortShips('standard', 4)}>{'pd'}</th>
<th className='sortable' onMouseEnter={termtip.bind(null, 'sensors')} onMouseLeave={hide} onClick={sortShips('standard', 5)}>{'s'}</th> <th className='sortable' onMouseEnter={termtip.bind(null, 'sensors')} onMouseLeave={hide} onClick={sortShips('standard', 5)}>{'s'}</th>
<th className='sortable' onMouseEnter={termtip.bind(null, 'fuel tank')} onMouseLeave={hide} onClick={sortShips('standard', 6)}>{'ft'}</th>
<th className='sortable lft' onClick={sortShips('hp',1)}>{translate('S')}</th> <th className='sortable lft' onClick={sortShips('hp',1)}>{translate('S')}</th>
<th className='sortable' onClick={sortShips('hp', 2)}>{translate('M')}</th> <th className='sortable' onClick={sortShips('hp', 2)}>{translate('M')}</th>
<th className='sortable' onClick={sortShips('hp', 3)}>{translate('L')}</th> <th className='sortable' onClick={sortShips('hp', 3)}>{translate('L')}</th>

View File

@@ -45,7 +45,7 @@ export function totalJumpRange(mass, fsd, fuel) {
* @param {number} baseShield Base Shield strength MJ for ship * @param {number} baseShield Base Shield strength MJ for ship
* @param {object} sg The shield generator used * @param {object} sg The shield generator used
* @param {number} multiplier Shield multiplier for ship (1 + shield boosters if any) * @param {number} multiplier Shield multiplier for ship (1 + shield boosters if any)
* @return {number} Approximate shield strengh in MJ * @return {number} Approximate shield strengh in MJ
*/ */
export function shieldStrength(mass, baseShield, sg, multiplier) { export function shieldStrength(mass, baseShield, sg, multiplier) {
// sg might be a module or a template; handle either here // sg might be a module or a template; handle either here
@@ -55,7 +55,6 @@ export function shieldStrength(mass, baseShield, sg, multiplier) {
let minMul = sg instanceof Module ? sg.getMinMul() : sg.minmul; let minMul = sg instanceof Module ? sg.getMinMul() : sg.minmul;
let optMul = sg instanceof Module ? sg.getOptMul() : sg.optmul; let optMul = sg instanceof Module ? sg.getOptMul() : sg.optmul;
let maxMul = sg instanceof Module ? sg.getMaxMul() : sg.maxmul; let maxMul = sg instanceof Module ? sg.getMaxMul() : sg.maxmul;
let xnorm = Math.min(1, (maxMass - mass) / (maxMass - minMass)); let xnorm = Math.min(1, (maxMass - mass) / (maxMass - minMass));
let exponent = Math.log((optMul - minMul) / (maxMul - minMul)) / Math.log(Math.min(1, (maxMass - optMass) / (maxMass - minMass))); let exponent = Math.log((optMul - minMul) / (maxMul - minMul)) / Math.log(Math.min(1, (maxMass - optMass) / (maxMass - minMass)));
let ynorm = Math.pow(xnorm, exponent); let ynorm = Math.pow(xnorm, exponent);
@@ -453,6 +452,19 @@ export function shieldMetrics(ship, sys) {
return shield; return shield;
} }
/**
* Calculate time from one boost to another
* @return {number} Boost frequency in seconds
* @param ship
*/
export function calcBoost(ship) {
if (!ship.boostEnergy || !ship.standard[4] || !ship.standard[4].m) {
return undefined;
}
return ship.boostEnergy / ship.standard[4].m.engrate;
}
/** /**
* Calculate armour metrics * Calculate armour metrics
* @param {Object} ship The ship * @param {Object} ship The ship

View File

@@ -9,12 +9,16 @@ export const StandardArray = [
'pd', // Power Distributor 'pd', // Power Distributor
's', // Sensors 's', // Sensors
'ft', // Fuel Tank 'ft', // Fuel Tank
'gpp', // Guardian Hybrid Power Plant
'gpd' // Guardian Hybrid Power Distributor
]; ];
// Map to lookup group labels/names for component grp, used for JSON Serialization // Map to lookup group labels/names for component grp, used for JSON Serialization
export const ModuleGroupToName = { export const ModuleGroupToName = {
// Standard // Standard
pp: 'Power Plant', pp: 'Power Plant',
gpp: 'Guardian Hybrid Power Plant',
gpd: 'Guardian Hybrid Power Distributor',
t: 'Thrusters', t: 'Thrusters',
fsd: 'Frame Shift Drive', fsd: 'Frame Shift Drive',
ls: 'Life Support', ls: 'Life Support',
@@ -58,19 +62,27 @@ export const ModuleGroupToName = {
cm: 'Countermeasure', cm: 'Countermeasure',
ec: 'Electronic Countermeasure', ec: 'Electronic Countermeasure',
fc: 'Fragment Cannon', fc: 'Fragment Cannon',
rfl: 'Remote Release Flak Launcher',
hs: 'Heat Sink Launcher', hs: 'Heat Sink Launcher',
ws: 'Frame Shift Wake Scanner', ws: 'Frame Shift Wake Scanner',
kw: 'Kill Warrant Scanner', kw: 'Kill Warrant Scanner',
nl: 'Mine Launcher', nl: 'Mine Launcher',
ml: 'Mining Laser', ml: 'Mining Laser',
mr: 'Missile Rack', mr: 'Missile Rack',
axmr: 'AX Missile Rack',
pa: 'Plasma Accelerator', pa: 'Plasma Accelerator',
po: 'Point Defence', po: 'Point Defence',
mc: 'Multi-cannon', mc: 'Multi-cannon',
axmc: 'AX Multi-cannon',
pl: 'Pulse Laser', pl: 'Pulse Laser',
rg: 'Rail Gun', rg: 'Rail Gun',
sb: 'Shield Booster', sb: 'Shield Booster',
tp: 'Torpedo Pylon' tp: 'Torpedo Pylon',
sfn: 'Shutdown Field Neutraliser',
xs: 'Xeno Scanner',
rcpl: 'Recon Limpet Controller',
gpc: 'Guardian Plasma Charger',
ggc: 'Guardian Gauss Cannon',
}; };
let GrpNameToCodeMap = {}; let GrpNameToCodeMap = {};

View File

@@ -64,7 +64,6 @@ export default class Module {
} }
} }
} }
// Sanitise the resultant value to 4dp equivalent // Sanitise the resultant value to 4dp equivalent
return isNaN(result) ? result : Math.round(result); return isNaN(result) ? result : Math.round(result);
} }
@@ -79,6 +78,9 @@ export default class Module {
if (!this.mods) { if (!this.mods) {
this.mods = {}; this.mods = {};
} }
if (!this.origVals) {
this.origVals = {};
}
if (valueiswithspecial && this.blueprint && this.blueprint.special) { if (valueiswithspecial && this.blueprint && this.blueprint.special) {
// This module has a special effect, see if we need to alter the stored value // This module has a special effect, see if we need to alter the stored value
const modifierActions = Modifications.modifierActions[this.blueprint.special.edname]; const modifierActions = Modifications.modifierActions[this.blueprint.special.edname];
@@ -117,7 +119,6 @@ export default class Module {
_getModifiedValue(name) { _getModifiedValue(name) {
const modification = Modifications.modifications[name]; const modification = Modifications.modifications[name];
let result = this[name]; let result = this[name];
if (!result) { if (!result) {
if (modification && modification.method === 'additive') { if (modification && modification.method === 'additive') {
// Additive modifications start at 0 rather than NULL // Additive modifications start at 0 rather than NULL
@@ -721,4 +722,13 @@ export default class Module {
getTime() { getTime() {
return this._getModifiedValue('time'); return this._getModifiedValue('time');
} }
/**
* Get the hack time for this module, taking in to account modifications
* @return {string} the time for this module
*/
getHackTime() {
return this._getModifiedValue('hacktime');
}
} }

View File

@@ -63,7 +63,6 @@ export function standard(type, id) {
if (!isNaN(type)) { if (!isNaN(type)) {
type = StandardArray[type]; type = StandardArray[type];
} }
let s = Modules.standard[type].find(e => e.id == id || (e.class == id.charAt(0) && e.rating == id.charAt(1))); let s = Modules.standard[type].find(e => e.id == id || (e.class == id.charAt(0) && e.rating == id.charAt(1)));
if (s) { if (s) {
s = new Module({ template: s }); s = new Module({ template: s });
@@ -196,6 +195,29 @@ export function findInternal(groupName, clss, rating, name) {
return null; return null;
} }
/**
* Finds an internal module based on Class, Rating, Group and/or name.
* At least one of Group name or unique module name must be provided.
* will start searching at specified class and proceed lower until a
* module is found or 0 is hit
* Uses findInternal internally
*
* @param {String} groupName [Optional] Full name or abbreviated name for module group
* @param {integer} clss module Class
* @param {String} rating module Rating
* @param {String} name [Optional] Long/unique name for module -e.g. 'Advanced Discover Scanner'
* @return {Object} The module if found, null if not found
*/
export function findMaxInternal(groupName, clss, rating, name) {
let foundModule = null;
let currentClss = clss;
while (currentClss > 0 && foundModule == null) {
foundModule = findInternal(groupName, currentClss, rating, name);
currentClss = currentClss - 1;
}
return foundModule;
}
/** /**
* Finds an internal Module ID based on Class, Rating, Group and/or name. * Finds an internal Module ID based on Class, Rating, Group and/or name.
* At least one ofGroup name or unique module name must be provided * At least one ofGroup name or unique module name must be provided

View File

@@ -437,12 +437,15 @@ export default class Ship {
m.blueprint = bp; m.blueprint = bp;
this.clearModifications(m); this.clearModifications(m);
// Set any hidden items for the blueprint now // Set any hidden items for the blueprint now
const features = m.blueprint.grades[m.blueprint.grade].features; if (m.blueprint.grades[m.blueprint.grade] && m.blueprint.grades[m.blueprint.grade].features) {
for (const featureName in features) { const features = m.blueprint.grades[m.blueprint.grade].features;
if (Modifications.modifications[featureName].hidden) { for (const featureName in features) {
this.setModification(m, featureName, bp.grades[bp.grade].features[featureName][0]); if (Modifications.modifications[featureName].hidden) {
this.setModification(m, featureName, bp.grades[bp.grade].features[featureName][0]);
}
} }
} }
this.updateModificationsString(); this.updateModificationsString();
} }
@@ -464,7 +467,17 @@ export default class Ship {
if (m.blueprint) { if (m.blueprint) {
m.blueprint.special = special; m.blueprint.special = special;
} }
this.recalculateDps().recalculateHps().recalculateEps(); this.updatePowerGenerated()
.updatePowerUsed()
.recalculateMass()
.updateJumpStats()
.recalculateShield()
.recalculateShieldCells()
.recalculateArmour()
.recalculateDps()
.recalculateEps()
.recalculateHps()
.updateMovement();
} }
/** /**
@@ -490,7 +503,6 @@ export default class Ship {
// Value passed is invalid; reset it to 0 // Value passed is invalid; reset it to 0
value = 0; value = 0;
} }
// Handle special cases // Handle special cases
if (name === 'pgen') { if (name === 'pgen') {
// Power generation // Power generation
@@ -569,6 +581,7 @@ export default class Ship {
// Reset Cumulative stats // Reset Cumulative stats
this.fuelCapacity = 0; this.fuelCapacity = 0;
this.cargoCapacity = 0; this.cargoCapacity = 0;
this.passengerCapacity = 0;
this.ladenMass = 0; this.ladenMass = 0;
this.armour = this.baseArmour; this.armour = this.baseArmour;
this.shield = this.baseShieldStrength; this.shield = this.baseShieldStrength;
@@ -616,7 +629,7 @@ export default class Ship {
standard[i].cat = 0; standard[i].cat = 0;
standard[i].priority = priorities && priorities[i + 1] ? priorities[i + 1] * 1 : 0; standard[i].priority = priorities && priorities[i + 1] ? priorities[i + 1] * 1 : 0;
standard[i].type = 'SYS'; standard[i].type = 'SYS';
standard[i].m = null; // Resetting 'old' modul if there was one standard[i].m = null; // Resetting 'old' module if there was one
standard[i].discountedCost = 0; standard[i].discountedCost = 0;
if (comps) { if (comps) {
let module = ModuleUtils.standard(i, comps.standard[i]); let module = ModuleUtils.standard(i, comps.standard[i]);
@@ -1188,6 +1201,7 @@ export default class Ship {
let unladenMass = this.hullMass; let unladenMass = this.hullMass;
let cargoCapacity = 0; let cargoCapacity = 0;
let fuelCapacity = 0; let fuelCapacity = 0;
let passengerCapacity = 0;
unladenMass += this.bulkheads.m.getMass(); unladenMass += this.bulkheads.m.getMass();
@@ -1209,6 +1223,10 @@ export default class Ship {
fuelCapacity += slot.m.fuel; fuelCapacity += slot.m.fuel;
} else if (slot.m.grp === 'cr') { } else if (slot.m.grp === 'cr') {
cargoCapacity += slot.m.cargo; cargoCapacity += slot.m.cargo;
} else if (slot.m.grp.slice(0,2) === 'pc') {
if (slot.m.passengers) {
passengerCapacity += slot.m.passengers;
}
} }
} }
} }
@@ -1224,6 +1242,7 @@ export default class Ship {
this.unladenMass = unladenMass; this.unladenMass = unladenMass;
this.cargoCapacity = cargoCapacity; this.cargoCapacity = cargoCapacity;
this.fuelCapacity = fuelCapacity; this.fuelCapacity = fuelCapacity;
this.passengerCapacity = passengerCapacity;
this.ladenMass = unladenMass + fuelCapacity + cargoCapacity; this.ladenMass = unladenMass + fuelCapacity + cargoCapacity;
return this; return this;

View File

@@ -9,9 +9,9 @@ import { canMount } from '../utils/SlotFunctions';
*/ */
export function multiPurpose(ship, shielded, bulkheadIndex) { export function multiPurpose(ship, shielded, bulkheadIndex) {
ship.useStandard('A') ship.useStandard('A')
.use(ship.standard[3], ModuleUtils.standard(3, ship.standard[3].maxClass + 'D')) // D Life Support .use(ship.standard[3], ModuleUtils.standard(3, ship.standard[3].maxClass + 'D')) // D Life Support
.use(ship.standard[5], ModuleUtils.standard(5, ship.standard[5].maxClass + 'D')) // D Sensors .use(ship.standard[5], ModuleUtils.standard(5, ship.standard[5].maxClass + 'D')) // D Sensors
.useBulkhead(bulkheadIndex); .useBulkhead(bulkheadIndex);
if (shielded) { if (shielded) {
ship.internal.some(function(slot) { ship.internal.some(function(slot) {
@@ -31,24 +31,31 @@ export function multiPurpose(ship, shielded, bulkheadIndex) {
* @param {Object} standardOpts [Optional] Standard module optional overrides * @param {Object} standardOpts [Optional] Standard module optional overrides
*/ */
export function trader(ship, shielded, standardOpts) { export function trader(ship, shielded, standardOpts) {
let usedSlots = [], let usedSlots = [];
sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass); let bstCount = 2;
let sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass);
ship.useStandard('A')
.use(ship.standard[3], ModuleUtils.standard(3, ship.standard[3].maxClass + 'D')) // D Life Support
.use(ship.standard[1], ModuleUtils.standard(1, ship.standard[1].maxClass + 'D')) // D Life Support
.use(ship.standard[4], ModuleUtils.standard(4, ship.standard[4].maxClass + 'D')) // D Life Support
.use(ship.standard[5], ModuleUtils.standard(5, ship.standard[5].maxClass + 'D')); // D Sensors
// Shield generator if required const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8];
if (shielded) { const shieldInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8]; .filter(a => (!a.eligible) || a.eligible.sg)
const shieldInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1) .filter(a => a.maxClass >= sg.class)
.filter(a => (!a.eligible) || a.eligible.sg) .sort((a, b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass));
.filter(a => a.maxClass >= sg.class) shieldInternals.some(function(slot) {
.sort((a,b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass)); if (canMount(ship, slot, 'sg')) { // Assuming largest slot can hold an eligible shield
for (let i = 0; i < shieldInternals.length; i++) { const shield = ModuleUtils.findInternal('sg', slot.maxClass, 'A');
if (canMount(ship, shieldInternals[i], 'sg')) { if (shield && shield.maxmass > ship.hullMass) {
ship.use(shieldInternals[i], sg); ship.use(slot, shield);
usedSlots.push(shieldInternals[i]); ship.setSlotEnabled(slot, true);
break; usedSlots.push(slot);
return true;
} }
} }
} });
// Fill the empty internals with cargo racks // Fill the empty internals with cargo racks
for (let i = ship.internal.length; i--;) { for (let i = ship.internal.length; i--;) {
@@ -62,8 +69,15 @@ export function trader(ship, shielded, standardOpts) {
for (let s of ship.hardpoints) { for (let s of ship.hardpoints) {
ship.use(s, null); ship.use(s, null);
} }
for (let s of ship.hardpoints) {
ship.useLightestStandard(standardOpts); if (s.maxClass == 0 && bstCount) { // Mount up to 2 boosters
ship.use(s, ModuleUtils.hardpoints('04'));
bstCount--;
} else {
ship.use(s, null);
}
}
// ship.useLightestStandard(standardOpts);
} }
/** /**
@@ -89,8 +103,8 @@ export function explorer(ship, planetary) {
// Advanced Discovery Scanner - class 1 or higher // Advanced Discovery Scanner - class 1 or higher
const adsOrder = [1, 2, 3, 4, 5, 6, 7, 8]; const adsOrder = [1, 2, 3, 4, 5, 6, 7, 8];
const adsInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1) const adsInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
.filter(a => (!a.eligible) || a.eligible.sc) .filter(a => (!a.eligible) || a.eligible.sc)
.sort((a,b) => adsOrder.indexOf(a.maxClass) - adsOrder.indexOf(b.maxClass)); .sort((a, b) => adsOrder.indexOf(a.maxClass) - adsOrder.indexOf(b.maxClass));
for (let i = 0; i < adsInternals.length; i++) { for (let i = 0; i < adsInternals.length; i++) {
if (canMount(ship, adsInternals[i], 'sc')) { if (canMount(ship, adsInternals[i], 'sc')) {
ship.use(adsInternals[i], ModuleUtils.internal('2f')); ship.use(adsInternals[i], ModuleUtils.internal('2f'));
@@ -103,8 +117,8 @@ export function explorer(ship, planetary) {
// Planetary Vehicle Hangar - class 2 or higher // Planetary Vehicle Hangar - class 2 or higher
const pvhOrder = [2, 3, 4, 5, 6, 7, 8, 1]; const pvhOrder = [2, 3, 4, 5, 6, 7, 8, 1];
const pvhInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1) const pvhInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
.filter(a => (!a.eligible) || a.eligible.pv) .filter(a => (!a.eligible) || a.eligible.pv)
.sort((a,b) => pvhOrder.indexOf(a.maxClass) - pvhOrder.indexOf(b.maxClass)); .sort((a, b) => pvhOrder.indexOf(a.maxClass) - pvhOrder.indexOf(b.maxClass));
for (let i = 0; i < pvhInternals.length; i++) { for (let i = 0; i < pvhInternals.length; i++) {
if (canMount(ship, pvhInternals[i], 'pv')) { if (canMount(ship, pvhInternals[i], 'pv')) {
// Planetary Vehical Hangar only has even classes // Planetary Vehical Hangar only has even classes
@@ -120,9 +134,9 @@ export function explorer(ship, planetary) {
// Shield generator // Shield generator
const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8]; const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8];
const shieldInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1) const shieldInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
.filter(a => (!a.eligible) || a.eligible.sg) .filter(a => (!a.eligible) || a.eligible.sg)
.filter(a => a.maxClass >= sg.class) .filter(a => a.maxClass >= sg.class)
.sort((a,b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass)); .sort((a, b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass));
for (let i = 0; i < shieldInternals.length; i++) { for (let i = 0; i < shieldInternals.length; i++) {
if (canMount(ship, shieldInternals[i], 'sg')) { if (canMount(ship, shieldInternals[i], 'sg')) {
ship.use(shieldInternals[i], sg); ship.use(shieldInternals[i], sg);
@@ -135,8 +149,8 @@ export function explorer(ship, planetary) {
// Detailed Surface Scanner // Detailed Surface Scanner
const dssOrder = [1, 2, 3, 4, 5, 6, 7, 8]; const dssOrder = [1, 2, 3, 4, 5, 6, 7, 8];
const dssInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1) const dssInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
.filter(a => (!a.eligible) || a.eligible.sc) .filter(a => (!a.eligible) || a.eligible.sc)
.sort((a,b) => dssOrder.indexOf(a.maxClass) - dssOrder.indexOf(b.maxClass)); .sort((a, b) => dssOrder.indexOf(a.maxClass) - dssOrder.indexOf(b.maxClass));
for (let i = 0; i < dssInternals.length; i++) { for (let i = 0; i < dssInternals.length; i++) {
if (canMount(ship, dssInternals[i], 'sc')) { if (canMount(ship, dssInternals[i], 'sc')) {
ship.use(dssInternals[i], ModuleUtils.internal('2i')); ship.use(dssInternals[i], ModuleUtils.internal('2i'));
@@ -148,8 +162,8 @@ export function explorer(ship, planetary) {
// Fuel scoop - best possible // Fuel scoop - best possible
const fuelScoopOrder = [8, 7, 6, 5, 4, 3, 2, 1]; const fuelScoopOrder = [8, 7, 6, 5, 4, 3, 2, 1];
const fuelScoopInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1) const fuelScoopInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
.filter(a => (!a.eligible) || a.eligible.fs) .filter(a => (!a.eligible) || a.eligible.fs)
.sort((a,b) => fuelScoopOrder.indexOf(a.maxClass) - fuelScoopOrder.indexOf(b.maxClass)); .sort((a, b) => fuelScoopOrder.indexOf(a.maxClass) - fuelScoopOrder.indexOf(b.maxClass));
for (let i = 0; i < fuelScoopInternals.length; i++) { for (let i = 0; i < fuelScoopInternals.length; i++) {
if (canMount(ship, fuelScoopInternals[i], 'fs')) { if (canMount(ship, fuelScoopInternals[i], 'fs')) {
ship.use(fuelScoopInternals[i], ModuleUtils.findInternal('fs', fuelScoopInternals[i].maxClass, 'A')); ship.use(fuelScoopInternals[i], ModuleUtils.findInternal('fs', fuelScoopInternals[i].maxClass, 'A'));
@@ -162,8 +176,8 @@ export function explorer(ship, planetary) {
// AFMUs - fill as they are 0-weight // AFMUs - fill as they are 0-weight
const afmuOrder = [8, 7, 6, 5, 4, 3, 2, 1]; const afmuOrder = [8, 7, 6, 5, 4, 3, 2, 1];
const afmuInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1) const afmuInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
.filter(a => (!a.eligible) || a.eligible.pc) .filter(a => (!a.eligible) || a.eligible.pc)
.sort((a,b) => afmuOrder.indexOf(a.maxClass) - afmuOrder.indexOf(b.maxClass)); .sort((a, b) => afmuOrder.indexOf(a.maxClass) - afmuOrder.indexOf(b.maxClass));
for (let i = 0; i < afmuInternals.length; i++) { for (let i = 0; i < afmuInternals.length; i++) {
if (canMount(ship, afmuInternals[i], 'am')) { if (canMount(ship, afmuInternals[i], 'am')) {
ship.use(afmuInternals[i], ModuleUtils.findInternal('am', afmuInternals[i].maxClass, 'A')); ship.use(afmuInternals[i], ModuleUtils.findInternal('am', afmuInternals[i].maxClass, 'A'));
@@ -200,6 +214,7 @@ export function explorer(ship, planetary) {
* @param {Boolean} shielded True if shield generator should be included * @param {Boolean} shielded True if shield generator should be included
*/ */
export function miner(ship, shielded) { export function miner(ship, shielded) {
shielded = true;
let standardOpts = { ppRating: 'A' }, let standardOpts = { ppRating: 'A' },
miningLaserCount = 2, miningLaserCount = 2,
usedSlots = [], usedSlots = [],
@@ -211,8 +226,8 @@ export function miner(ship, shielded) {
// Largest possible refinery // Largest possible refinery
const refineryOrder = [4, 5, 6, 7, 8, 3, 2, 1]; const refineryOrder = [4, 5, 6, 7, 8, 3, 2, 1];
const refineryInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1) const refineryInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
.filter(a => (!a.eligible) || a.eligible.rf) .filter(a => (!a.eligible) || a.eligible.rf)
.sort((a,b) => refineryOrder.indexOf(a.maxClass) - refineryOrder.indexOf(b.maxClass)); .sort((a, b) => refineryOrder.indexOf(a.maxClass) - refineryOrder.indexOf(b.maxClass));
for (let i = 0; i < refineryInternals.length; i++) { for (let i = 0; i < refineryInternals.length; i++) {
if (canMount(ship, refineryInternals[i], 'rf')) { if (canMount(ship, refineryInternals[i], 'rf')) {
ship.use(refineryInternals[i], ModuleUtils.findInternal('rf', Math.min(refineryInternals[i].maxClass, 4), 'A')); ship.use(refineryInternals[i], ModuleUtils.findInternal('rf', Math.min(refineryInternals[i].maxClass, 4), 'A'));
@@ -224,8 +239,8 @@ export function miner(ship, shielded) {
// Prospector limpet controller - 3A if possible // Prospector limpet controller - 3A if possible
const prospectorOrder = [3, 4, 5, 6, 7, 8, 2, 1]; const prospectorOrder = [3, 4, 5, 6, 7, 8, 2, 1];
const prospectorInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1) const prospectorInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
.filter(a => (!a.eligible) || a.eligible.pc) .filter(a => (!a.eligible) || a.eligible.pc)
.sort((a,b) => prospectorOrder.indexOf(a.maxClass) - prospectorOrder.indexOf(b.maxClass)); .sort((a, b) => prospectorOrder.indexOf(a.maxClass) - prospectorOrder.indexOf(b.maxClass));
for (let i = 0; i < prospectorInternals.length; i++) { for (let i = 0; i < prospectorInternals.length; i++) {
if (canMount(ship, prospectorInternals[i], 'pc')) { if (canMount(ship, prospectorInternals[i], 'pc')) {
// Prospector only has odd classes // Prospector only has odd classes
@@ -240,9 +255,9 @@ export function miner(ship, shielded) {
if (shielded) { if (shielded) {
const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8]; const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8];
const shieldInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1) const shieldInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
.filter(a => (!a.eligible) || a.eligible.sg) .filter(a => (!a.eligible) || a.eligible.sg)
.filter(a => a.maxClass >= sg.class) .filter(a => a.maxClass >= sg.class)
.sort((a,b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass)); .sort((a, b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass));
for (let i = 0; i < shieldInternals.length; i++) { for (let i = 0; i < shieldInternals.length; i++) {
if (canMount(ship, shieldInternals[i], 'sg')) { if (canMount(ship, shieldInternals[i], 'sg')) {
ship.use(shieldInternals[i], sg); ship.use(shieldInternals[i], sg);
@@ -254,7 +269,7 @@ export function miner(ship, shielded) {
// Dual mining lasers of highest possible class; remove anything else // Dual mining lasers of highest possible class; remove anything else
const miningLaserOrder = [2, 3, 4, 1, 0]; const miningLaserOrder = [2, 3, 4, 1, 0];
const miningLaserHardpoints = ship.hardpoints.concat().sort(function(a,b) { const miningLaserHardpoints = ship.hardpoints.concat().sort(function(a, b) {
return miningLaserOrder.indexOf(a.maxClass) - miningLaserOrder.indexOf(b.maxClass); return miningLaserOrder.indexOf(a.maxClass) - miningLaserOrder.indexOf(b.maxClass);
}); });
for (let s of miningLaserHardpoints) { for (let s of miningLaserHardpoints) {
@@ -268,13 +283,13 @@ export function miner(ship, shielded) {
// Number of collector limpets required to be active is a function of the size of the ship and the power of the lasers // Number of collector limpets required to be active is a function of the size of the ship and the power of the lasers
const miningLaserDps = ship.hardpoints.filter(h => h.m != null) const miningLaserDps = ship.hardpoints.filter(h => h.m != null)
.reduce(function(a, b) { .reduce(function(a, b) {
return a + b.m.getDps(); return a + b.m.getDps();
}, 0); }, 0);
// Find out how many internal slots we have, and their potential cargo size // Find out how many internal slots we have, and their potential cargo size
const potentialCargo = ship.internal.filter(a => usedSlots.indexOf(a) == -1) const potentialCargo = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
.filter(a => (!a.eligible) || a.eligible.cr) .filter(a => (!a.eligible) || a.eligible.cr)
.map(b => Math.pow(2, b.maxClass)); .map(b => Math.pow(2, b.maxClass));
// One collector for each 1.25 DPS, multiply by 1.25 for medium ships and 1.5 for large ships as they have further to travel // One collector for each 1.25 DPS, multiply by 1.25 for medium ships and 1.5 for large ships as they have further to travel
// 0 if we only have 1 cargo slot, otherwise minium of 1 and maximum of 6 (excluding size modifier) // 0 if we only have 1 cargo slot, otherwise minium of 1 and maximum of 6 (excluding size modifier)
const sizeModifier = ship.class == 2 ? 1.2 : ship.class == 3 ? 1.5 : 1; const sizeModifier = ship.class == 2 ? 1.2 : ship.class == 3 ? 1.5 : 1;
@@ -283,8 +298,8 @@ export function miner(ship, shielded) {
if (collectorLimpetsRequired > 0) { if (collectorLimpetsRequired > 0) {
const collectorOrder = [1, 2, 3, 4, 5, 6, 7, 8]; const collectorOrder = [1, 2, 3, 4, 5, 6, 7, 8];
const collectorInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1) const collectorInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
.filter(a => (!a.eligible) || a.eligible.cc) .filter(a => (!a.eligible) || a.eligible.cc)
.sort((a,b) => collectorOrder.indexOf(a.maxClass) - collectorOrder.indexOf(b.maxClass)); .sort((a, b) => collectorOrder.indexOf(a.maxClass) - collectorOrder.indexOf(b.maxClass));
// Always keep at least 2 slots free for cargo racks (1 for shielded) // Always keep at least 2 slots free for cargo racks (1 for shielded)
for (let i = 0; i < collectorInternals.length - (shielded ? 1 : 2) && collectorLimpetsRequired > 0; i++) { for (let i = 0; i < collectorInternals.length - (shielded ? 1 : 2) && collectorLimpetsRequired > 0; i++) {
if (canMount(ship, collectorInternals[i], 'cc')) { if (canMount(ship, collectorInternals[i], 'cc')) {
@@ -299,9 +314,9 @@ export function miner(ship, shielded) {
// Power distributor to power the mining lasers indefinitely // Power distributor to power the mining lasers indefinitely
const wepRateRequired = ship.hardpoints.filter(h => h.m != null) const wepRateRequired = ship.hardpoints.filter(h => h.m != null)
.reduce(function(a, b) { .reduce(function(a, b) {
return a + b.m.getEps(); return a + b.m.getEps();
}, 0); }, 0);
standardOpts.pd = ship.getAvailableModules().matchingPowerDist({ weprate: wepRateRequired }).id; standardOpts.pd = ship.getAvailableModules().matchingPowerDist({ weprate: wepRateRequired }).id;
// Fill the empty internals with cargo racks // Fill the empty internals with cargo racks
@@ -331,9 +346,9 @@ export function racer(ship) {
// Shield generator // Shield generator
const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8]; const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8];
const shieldInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1) const shieldInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
.filter(a => (!a.eligible) || a.eligible.sg) .filter(a => (!a.eligible) || a.eligible.sg)
.filter(a => a.maxClass >= sg.class) .filter(a => a.maxClass >= sg.class)
.sort((a,b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass)); .sort((a, b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass));
for (let i = 0; i < shieldInternals.length; i++) { for (let i = 0; i < shieldInternals.length; i++) {
if (canMount(ship, shieldInternals[i], 'sg')) { if (canMount(ship, shieldInternals[i], 'sg')) {
ship.use(shieldInternals[i], sg); ship.use(shieldInternals[i], sg);
@@ -398,4 +413,3 @@ export function racer(ship) {
// ship.standard[5].m.blueprint.grade = 5; // ship.standard[5].m.blueprint.grade = 5;
// setBest(ship, ship.standard[5].m); // setBest(ship, ship.standard[5].m);
} }

View File

@@ -13,6 +13,7 @@ const LS_KEY_STATE = 'state';
const LS_KEY_SIZE_RATIO = 'sizeRatio'; 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';
let LS; let LS;
@@ -84,6 +85,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 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);
@@ -102,6 +104,13 @@ export class Persist extends EventEmitter {
this.outfittingTab = _getString(LS_KEY_OUTFITTING_TAB); this.outfittingTab = _getString(LS_KEY_OUTFITTING_TAB);
this.state = _get(LS_KEY_STATE); this.state = _get(LS_KEY_STATE);
this.sizeRatio = _get(LS_KEY_SIZE_RATIO) || 1; this.sizeRatio = _get(LS_KEY_SIZE_RATIO) || 1;
this.matsPerGrade = matsPerGrade || {
1: 2,
2: 2,
3: 4,
4: 4,
5: 10
};
this.tooltipsEnabled = tips === null ? true : tips; this.tooltipsEnabled = tips === null ? true : tips;
this.moduleResistancesEnabled = moduleResistances === null ? true : moduleResistances; this.moduleResistancesEnabled = moduleResistances === null ? true : moduleResistances;
@@ -152,6 +161,10 @@ export class Persist extends EventEmitter {
this.moduleResistancesEnabled = !!newValue && newValue.toLowerCase() == 'true'; this.moduleResistancesEnabled = !!newValue && newValue.toLowerCase() == 'true';
this.emit('moduleresistances', this.moduleResistancesEnabled); this.emit('moduleresistances', this.moduleResistancesEnabled);
break; break;
case LS_KEY_ROLLS:
this.matsPerGrade = JSON.parse(newValue);
this.emit('matsPerGrade', this.matsPerGrade);
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
@@ -457,6 +470,23 @@ export class Persist extends EventEmitter {
return this.moduleDiscount; return this.moduleDiscount;
} }
/**
* Get the saved ship discount
* @param {Object} matsPerGrade # of rolls per grade
*/
setRolls(matsPerGrade) {
this.matsPerGrade = matsPerGrade;
_put(LS_KEY_ROLLS, this.matsPerGrade);
this.emit('matsPerGrade');
}
/**
* Get the saved Mats per grade
* @return {Object} # of rolls per grade
*/
getRolls() {
return this.matsPerGrade;
}
/** /**
* Persist selected cost tab * Persist selected cost tab
* @param {number} tabName Cost tab name * @param {number} tabName Cost tab name

View File

@@ -1,6 +1,66 @@
import React from 'react'; import React from 'react';
import { Modifications } from 'coriolis-data/dist'; import { Modifications } from 'coriolis-data/dist';
/**
* Generate a tooltip with details of a blueprint's specials
* @param {Object} translate The translate object
* @param {Object} blueprint The blueprint at the required grade
* @param {string} grp The group of the module
* @param {Object} m The module to compare with
* @param {string} specialName The name of the special
* @returns {Object} The react components
*/
export function specialToolTip(translate, blueprint, grp, m, specialName) {
const effects = [];
if (!blueprint || !blueprint.features) {
return undefined;
}
if (m) {
// We also add in any benefits from specials that aren't covered above
if (m.blueprint) {
for (const feature in Modifications.modifierActions[specialName]) {
// if (!blueprint.features[feature] && !m.mods.feature) {
const featureDef = Modifications.modifications[feature];
if (featureDef && !featureDef.hidden) {
let symbol = '';
if (feature === 'jitter') {
symbol = '°';
} else if (featureDef.type === 'percentage') {
symbol = '%';
}
let current = m.getModValue(feature) - m.getModValue(feature, true);
if (featureDef.type === 'percentage') {
current = Math.round(current / 10) / 10;
} else if (featureDef.type === 'numeric') {
current /= 100;
}
const currentIsBeneficial = isValueBeneficial(feature, current);
effects.push(
<tr key={feature + '_specialTT'}>
<td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td>
<td>&nbsp;</td>
<td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'}
style={{ textAlign: 'right' }}>{current}{symbol}</td>
<td>&nbsp;</td>
</tr>
);
}
}
}
}
return (
<div>
<table width='100%'>
<tbody>
{effects}
</tbody>
</table>
</div>
);
}
/** /**
* Generate a tooltip with details of a blueprint's effects * Generate a tooltip with details of a blueprint's effects
* @param {Object} translate The translate object * @param {Object} translate The translate object
@@ -12,6 +72,9 @@ import { Modifications } from 'coriolis-data/dist';
*/ */
export function blueprintTooltip(translate, blueprint, engineers, grp, m) { export function blueprintTooltip(translate, blueprint, engineers, grp, m) {
const effects = []; const effects = [];
if (!blueprint || !blueprint.features) {
return undefined;
}
for (const feature in blueprint.features) { for (const feature in blueprint.features) {
const featureIsBeneficial = isBeneficial(feature, blueprint.features[feature]); const featureIsBeneficial = isBeneficial(feature, blueprint.features[feature]);
const featureDef = Modifications.modifications[feature]; const featureDef = Modifications.modifications[feature];
@@ -225,27 +288,13 @@ export function isValueBeneficial(feature, value) {
*/ */
export function getBlueprint(name, module) { export function getBlueprint(name, module) {
// Start with a copy of the blueprint // Start with a copy of the blueprint
const blueprint = JSON.parse(JSON.stringify(Modifications.blueprints[name])); const findMod = val => Object.keys(Modifications.blueprints).find(elem => elem.toString().toLowerCase().search(val.toString().toLowerCase().replace(/(OutfittingFieldType_|persecond)/igm, '')) >= 0);
const found = Modifications.blueprints[findMod(name)];
if (!found || !found.fdname) {
return {};
}
const blueprint = JSON.parse(JSON.stringify(found));
if (module) { if (module) {
if (module.grp === 'bh' || module.grp === 'hr' || module.grp === 'sg' || module.grp === 'psg' || module.grp === 'bsg') {
// Bulkheads, hull reinforcements and shield generators need to have their resistances altered by the base values
for (const grade in blueprint.grades) {
for (const feature in blueprint.grades[grade].features) {
if (feature === 'explres') {
blueprint.grades[grade].features[feature][0] *= (1 - module.explres);
blueprint.grades[grade].features[feature][1] *= (1 - module.explres);
}
if (feature === 'kinres') {
blueprint.grades[grade].features[feature][0] *= (1 - module.kinres);
blueprint.grades[grade].features[feature][1] *= (1 - module.kinres);
}
if (feature === 'thermres') {
blueprint.grades[grade].features[feature][0] *= (1 - module.thermres);
blueprint.grades[grade].features[feature][1] *= (1 - module.thermres);
}
}
}
}
if (module.grp === 'sb') { if (module.grp === 'sb') {
// Shield boosters are treated internally as straight modifiers, so rather than (for example) // 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 // being a 4% boost they are a 104% multiplier. We need to fix the values here so that they look
@@ -264,56 +313,31 @@ export function getBlueprint(name, module) {
} }
/** /**
* Provide 'worst' primary modifications * Provide 'percent' primary modifications
* @param {Object} ship The ship for which to perform the modifications * @param {Object} ship The ship for which to perform the modifications
* @param {Object} m The module for which to perform the modifications * @param {Object} m The module for which to perform the modifications
* @param {Number} percent The percent to set values to of full.
*/ */
export function setWorst(ship, m) { export function setPercent(ship, m, percent) {
ship.clearModifications(m);
const features = m.blueprint.grades[m.blueprint.grade].features;
for (const featureName in features) {
const value = features[featureName][0];
_setValue(ship, m, featureName, value);
}
}
/**
* Provide 'best' primary modifications
* @param {Object} ship The ship for which to perform the modifications
* @param {Object} m The module for which to perform the modifications
*/
export function setBest(ship, m) {
ship.clearModifications(m);
const features = m.blueprint.grades[m.blueprint.grade].features;
for (const featureName in features) {
const value = features[featureName][1];
_setValue(ship, m, featureName, value);
}
}
/**
* Provide 'extreme' primary modifications
* @param {Object} ship The ship for which to perform the modifications
* @param {Object} m The module for which to perform the modifications
*/
export function setExtreme(ship, m) {
ship.clearModifications(m); ship.clearModifications(m);
// Pick given value as multiplier
const mult = percent / 100;
const features = m.blueprint.grades[m.blueprint.grade].features; const features = m.blueprint.grades[m.blueprint.grade].features;
for (const featureName in features) { for (const featureName in features) {
let value; let value;
if (Modifications.modifications[featureName].higherbetter) { if (Modifications.modifications[featureName].higherbetter) {
// Higher is better, but is this making it better or worse? // Higher is better, but is this making it better or worse?
if (features[featureName][0] < 0 || (features[featureName][0] === 0 && features[featureName][1] < 0)) { if (features[featureName][0] < 0 || (features[featureName][0] === 0 && features[featureName][1] < 0)) {
value = features[featureName][0]; value = features[featureName][1] + ((features[featureName][0] - features[featureName][1]) * mult);
} else { } else {
value = features[featureName][1]; value = features[featureName][0] + ((features[featureName][1] - features[featureName][0]) * mult);
} }
} else { } else {
// Higher is worse, but is this making it better or worse? // Higher is worse, but is this making it better or worse?
if (features[featureName][0] < 0 || (features[featureName][0] === 0 && features[featureName][1] < 0)) { if (features[featureName][0] < 0 || (features[featureName][0] === 0 && features[featureName][1] < 0)) {
value = features[featureName][1]; value = features[featureName][0] + ((features[featureName][1] - features[featureName][0]) * mult);
} else { } else {
value = features[featureName][0]; value = features[featureName][1] + ((features[featureName][0] - features[featureName][1]) * mult);
} }
} }
@@ -327,30 +351,8 @@ export function setExtreme(ship, m) {
* @param {Object} m The module for which to perform the modifications * @param {Object} m The module for which to perform the modifications
*/ */
export function setRandom(ship, m) { export function setRandom(ship, m) {
ship.clearModifications(m);
// Pick a single value for our randomness // Pick a single value for our randomness
const mult = Math.random(); setPercent(ship, m, Math.random() * 100);
const features = m.blueprint.grades[m.blueprint.grade].features;
for (const featureName in features) {
let value;
if (Modifications.modifications[featureName].higherbetter) {
// Higher is better, but is this making it better or worse?
if (features[featureName][0] < 0 || (features[featureName][0] === 0 && features[featureName][1] < 0)) {
value = features[featureName][1] + ((features[featureName][0] - features[featureName][1]) * mult);
} else {
value = features[featureName][0] + ((features[featureName][1] - features[featureName][0]) * mult);
}
} else {
// Higher is worse, but is this making it better or worse?
if (features[featureName][0] < 0 || (features[featureName][0] === 0 && features[featureName][1] < 0)) {
value = features[featureName][0] + ((features[featureName][1] - features[featureName][0]) * mult);
} else {
value = features[featureName][1] + ((features[featureName][0] - features[featureName][1]) * mult);
}
}
_setValue(ship, m, featureName, value);
}
} }
/** /**
@@ -369,3 +371,60 @@ function _setValue(ship, m, featureName, value) {
ship.setModification(m, featureName, value); ship.setModification(m, featureName, value);
} }
} }
/**
* Provide 'percent' primary query
* @param {Object} m The module for which to perform the query
* @returns {Number} percent The percentage indicator of current applied values.
*/
export function getPercent(m) {
let result = null;
const features = m.blueprint.grades[m.blueprint.grade].features;
for (const featureName in features) {
if (features[featureName][0] === features[featureName][1]) {
continue;
}
let value = _getValue(m, featureName);
let mult;
if (Modifications.modifications[featureName].higherbetter) {
// Higher is better, but is this making it better or worse?
if (features[featureName][0] < 0 || (features[featureName][0] === 0 && features[featureName][1] < 0)) {
mult = Math.round((value - features[featureName][1]) / (features[featureName][0] - features[featureName][1]) * 100);
} else {
mult = Math.round((value - features[featureName][0]) / (features[featureName][1] - features[featureName][0]) * 100);
}
} else {
// Higher is worse, but is this making it better or worse?
if (features[featureName][0] < 0 || (features[featureName][0] === 0 && features[featureName][1] < 0)) {
mult = Math.round((value - features[featureName][0]) / (features[featureName][1] - features[featureName][0]) * 100);
} else {
mult = Math.round((value - features[featureName][1]) / (features[featureName][0] - features[featureName][1]) * 100);
}
}
if (result && result != mult) {
return null;
} else if (result != mult) {
result = mult;
}
}
return result;
}
/**
* Query a feature value
* @param {Object} m The module for which to perform the query
* @param {string} featureName The feature being queried
* @returns {number} The value of the modification as a %
*/
function _getValue(m, featureName) {
if (Modifications.modifications[featureName].type == 'percentage') {
return m.getModValue(featureName, true) / 10000;
} else if (Modifications.modifications[featureName].type == 'numeric') {
return m.getModValue(featureName, true) / 100;
} else {
return m.getModValue(featureName, true);
}
}

View File

@@ -35,13 +35,15 @@ const SHIP_FD_NAME_TO_CORIOLIS_NAME = {
'Type6': 'type_6_transporter', 'Type6': 'type_6_transporter',
'Type7': 'type_7_transport', 'Type7': 'type_7_transport',
'Type9': 'type_9_heavy', 'Type9': 'type_9_heavy',
'Type9_Military': 'type_10_defender',
'TypeX': 'alliance_chieftain',
'Viper': 'viper', 'Viper': 'viper',
'Viper_MkIV': 'viper_mk_iv', 'Viper_MkIV': 'viper_mk_iv',
'Vulture': 'vulture' 'Vulture': 'vulture'
}; };
// Mapping from hardpoint class to name in companion API // Mapping from hardpoint class to name in companion API
const HARDPOINT_NUM_TO_CLASS = { export const HARDPOINT_NUM_TO_CLASS = {
0: 'Tiny', 0: 'Tiny',
1: 'Small', 1: 'Small',
2: 'Medium', 2: 'Medium',
@@ -104,7 +106,7 @@ function _moduleFromEdId(edId) {
* @return {string} the Coriolis model of the ship * @return {string} the Coriolis model of the ship
*/ */
function _shipModelFromEDName(edName) { function _shipModelFromEDName(edName) {
return SHIP_FD_NAME_TO_CORIOLIS_NAME[edName]; return SHIP_FD_NAME_TO_CORIOLIS_NAME[Object.keys(SHIP_FD_NAME_TO_CORIOLIS_NAME).find(elem => elem.toLowerCase() === edName.toLowerCase())];
} }
/** /**
@@ -113,7 +115,7 @@ function _shipModelFromEDName(edName) {
* @return {string} the Coriolis model of the ship * @return {string} the Coriolis model of the ship
*/ */
export function shipModelFromJson(json) { export function shipModelFromJson(json) {
return _shipModelFromEDName(json.name); return _shipModelFromEDName(json.name || json.Ship);
} }
/** /**
@@ -142,29 +144,33 @@ export function shipFromJson(json) {
ship.cargoHatch.priority = 4; ship.cargoHatch.priority = 4;
} }
let rootModule;
// Add the bulkheads // Add the bulkheads
const armourJson = json.modules.Armour.module; const armourJson = json.modules.Armour.module;
if (armourJson.name.endsWith('_Armour_Grade1')) { if (armourJson.name.toLowerCase().endsWith('_armour_grade1')) {
ship.useBulkhead(0, true); ship.useBulkhead(0, true);
} else if (armourJson.name.endsWith('_Armour_Grade2')) { } else if (armourJson.name.toLowerCase().endsWith('_armour_grade2')) {
ship.useBulkhead(1, true); ship.useBulkhead(1, true);
} else if (armourJson.name.endsWith('_Armour_Grade3')) { } else if (armourJson.name.toLowerCase().endsWith('_armour_grade3')) {
ship.useBulkhead(2, true); ship.useBulkhead(2, true);
} else if (armourJson.name.endsWith('_Armour_Mirrored')) { } else if (armourJson.name.toLowerCase().endsWith('_armour_mirrored')) {
ship.useBulkhead(3, true); ship.useBulkhead(3, true);
} else if (armourJson.name.endsWith('_Armour_Reactive')) { } else if (armourJson.name.toLowerCase().endsWith('_armour_reactive')) {
ship.useBulkhead(4, true); ship.useBulkhead(4, true);
} else { } else {
throw 'Unknown bulkheads "' + armourJson.name + '"'; throw 'Unknown bulkheads "' + armourJson.name + '"';
} }
ship.bulkheads.enabled = true; ship.bulkheads.enabled = true;
if (armourJson.modifiers) _addModifications(ship.bulkheads.m, armourJson.modifiers, armourJson.recipeName, armourJson.recipeLevel); rootModule = json.modules.Armour;
if (rootModule.WorkInProgress_modifications) _addModifications(ship.bulkheads.m, rootModule.WorkInProgress_modifications, rootModule.engineer.recipeName, rootModule.engineer.recipeLevel);
// Add the standard modules // Add the standard modules
// Power plant // Power plant
const powerplantJson = json.modules.PowerPlant.module; const powerplantJson = json.modules.PowerPlant.module;
const powerplant = _moduleFromEdId(powerplantJson.id); const powerplant = _moduleFromEdId(powerplantJson.id);
if (powerplantJson.modifiers) _addModifications(powerplant, powerplantJson.modifiers, powerplantJson.recipeName, powerplantJson.recipeLevel); rootModule = json.modules.PowerPlant;
if (rootModule.WorkInProgress_modifications) _addModifications(powerplant, rootModule.WorkInProgress_modifications, rootModule.engineer.recipeName, rootModule.engineer.recipeLevel);
ship.use(ship.standard[0], powerplant, true); ship.use(ship.standard[0], powerplant, true);
ship.standard[0].enabled = powerplantJson.on === true; ship.standard[0].enabled = powerplantJson.on === true;
ship.standard[0].priority = powerplantJson.priority; ship.standard[0].priority = powerplantJson.priority;
@@ -172,7 +178,8 @@ export function shipFromJson(json) {
// Thrusters // Thrusters
const thrustersJson = json.modules.MainEngines.module; const thrustersJson = json.modules.MainEngines.module;
const thrusters = _moduleFromEdId(thrustersJson.id); const thrusters = _moduleFromEdId(thrustersJson.id);
if (thrustersJson.modifiers) _addModifications(thrusters, thrustersJson.modifiers, thrustersJson.recipeName, thrustersJson.recipeLevel); rootModule = json.modules.MainEngines;
if (rootModule.WorkInProgress_modifications) _addModifications(thrusters, rootModule.WorkInProgress_modifications, rootModule.engineer.recipeName, rootModule.engineer.recipeLevel);
ship.use(ship.standard[1], thrusters, true); ship.use(ship.standard[1], thrusters, true);
ship.standard[1].enabled = thrustersJson.on === true; ship.standard[1].enabled = thrustersJson.on === true;
ship.standard[1].priority = thrustersJson.priority; ship.standard[1].priority = thrustersJson.priority;
@@ -180,7 +187,8 @@ export function shipFromJson(json) {
// FSD // FSD
const frameshiftdriveJson = json.modules.FrameShiftDrive.module; const frameshiftdriveJson = json.modules.FrameShiftDrive.module;
const frameshiftdrive = _moduleFromEdId(frameshiftdriveJson.id); const frameshiftdrive = _moduleFromEdId(frameshiftdriveJson.id);
if (frameshiftdriveJson.modifiers) _addModifications(frameshiftdrive, frameshiftdriveJson.modifiers, frameshiftdriveJson.recipeName, frameshiftdriveJson.recipeLevel); rootModule = json.modules.FrameShiftDrive;
if (rootModule.WorkInProgress_modifications) _addModifications(frameshiftdrive, rootModule.WorkInProgress_modifications, rootModule.engineer.recipeName, rootModule.engineer.recipeLevel);
ship.use(ship.standard[2], frameshiftdrive, true); ship.use(ship.standard[2], frameshiftdrive, true);
ship.standard[2].enabled = frameshiftdriveJson.on === true; ship.standard[2].enabled = frameshiftdriveJson.on === true;
ship.standard[2].priority = frameshiftdriveJson.priority; ship.standard[2].priority = frameshiftdriveJson.priority;
@@ -188,7 +196,8 @@ export function shipFromJson(json) {
// Life support // Life support
const lifesupportJson = json.modules.LifeSupport.module; const lifesupportJson = json.modules.LifeSupport.module;
const lifesupport = _moduleFromEdId(lifesupportJson.id); const lifesupport = _moduleFromEdId(lifesupportJson.id);
if (lifesupportJson.modifiers)_addModifications(lifesupport, lifesupportJson.modifiers, lifesupportJson.recipeName, lifesupportJson.recipeLevel); rootModule = json.modules.LifeSupport;
if (rootModule.WorkInProgress_modifications) _addModifications(lifesupport, rootModule.WorkInProgress_modifications, rootModule.engineer.recipeName, rootModule.engineer.recipeLevel);
ship.use(ship.standard[3], lifesupport, true); ship.use(ship.standard[3], lifesupport, true);
ship.standard[3].enabled = lifesupportJson.on === true; ship.standard[3].enabled = lifesupportJson.on === true;
ship.standard[3].priority = lifesupportJson.priority; ship.standard[3].priority = lifesupportJson.priority;
@@ -196,7 +205,8 @@ export function shipFromJson(json) {
// Power distributor // Power distributor
const powerdistributorJson = json.modules.PowerDistributor.module; const powerdistributorJson = json.modules.PowerDistributor.module;
const powerdistributor = _moduleFromEdId(powerdistributorJson.id); const powerdistributor = _moduleFromEdId(powerdistributorJson.id);
if (powerdistributorJson.modifiers) _addModifications(powerdistributor, powerdistributorJson.modifiers, powerdistributorJson.recipeName, powerdistributorJson.recipeLevel); rootModule = json.modules.PowerDistributor;
if (rootModule.WorkInProgress_modifications) _addModifications(powerdistributor, rootModule.WorkInProgress_modifications, rootModule.engineer.recipeName, rootModule.engineer.recipeLevel);
ship.use(ship.standard[4], powerdistributor, true); ship.use(ship.standard[4], powerdistributor, true);
ship.standard[4].enabled = powerdistributorJson.on === true; ship.standard[4].enabled = powerdistributorJson.on === true;
ship.standard[4].priority = powerdistributorJson.priority; ship.standard[4].priority = powerdistributorJson.priority;
@@ -204,7 +214,8 @@ export function shipFromJson(json) {
// Sensors // Sensors
const sensorsJson = json.modules.Radar.module; const sensorsJson = json.modules.Radar.module;
const sensors = _moduleFromEdId(sensorsJson.id); const sensors = _moduleFromEdId(sensorsJson.id);
if (sensorsJson.modifiers) _addModifications(sensors, sensorsJson.modifiers, sensorsJson.recipeName, sensorsJson.recipeLevel); rootModule = json.modules.Radar;
if (rootModule.WorkInProgress_modifications) _addModifications(sensors, rootModule.WorkInProgress_modifications, rootModule.engineer.recipeName, rootModule.engineer.recipeLevel);
ship.use(ship.standard[5], sensors, true); ship.use(ship.standard[5], sensors, true);
ship.standard[5].enabled = sensorsJson.on === true; ship.standard[5].enabled = sensorsJson.on === true;
ship.standard[5].priority = sensorsJson.priority; ship.standard[5].priority = sensorsJson.priority;
@@ -240,7 +251,8 @@ export function shipFromJson(json) {
} else { } else {
const hardpointJson = hardpointSlot.module; const hardpointJson = hardpointSlot.module;
const hardpoint = _moduleFromEdId(hardpointJson.id); const hardpoint = _moduleFromEdId(hardpointJson.id);
if (hardpointJson.modifiers) _addModifications(hardpoint, hardpointJson.modifiers, hardpointJson.recipeName, hardpointJson.recipeLevel); rootModule = hardpointSlot;
if (rootModule.WorkInProgress_modifications) _addModifications(hardpoint, rootModule.WorkInProgress_modifications, rootModule.engineer.recipeName, rootModule.engineer.recipeLevel, rootModule.specialModifications);
ship.use(ship.hardpoints[hardpointArrayNum], hardpoint, true); ship.use(ship.hardpoints[hardpointArrayNum], hardpoint, true);
ship.hardpoints[hardpointArrayNum].enabled = hardpointJson.on === true; ship.hardpoints[hardpointArrayNum].enabled = hardpointJson.on === true;
ship.hardpoints[hardpointArrayNum].priority = hardpointJson.priority; ship.hardpoints[hardpointArrayNum].priority = hardpointJson.priority;
@@ -282,7 +294,8 @@ export function shipFromJson(json) {
} else { } else {
const internalJson = internalSlot.module; const internalJson = internalSlot.module;
const internal = _moduleFromEdId(internalJson.id); const internal = _moduleFromEdId(internalJson.id);
if (internalJson.modifiers) _addModifications(internal, internalJson.modifiers, internalJson.recipeName, internalJson.recipeLevel); rootModule = internalSlot;
if (rootModule.WorkInProgress_modifications) _addModifications(internal, rootModule.WorkInProgress_modifications, rootModule.engineer.recipeName, rootModule.engineer.recipeLevel);
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;
@@ -299,35 +312,48 @@ export function shipFromJson(json) {
* @param {Object} modifiers the modifiers * @param {Object} modifiers the modifiers
* @param {Object} blueprint the blueprint of the modification * @param {Object} blueprint the blueprint of the modification
* @param {Object} grade the grade of the modification * @param {Object} grade the grade of the modification
* @param {Object} specialModifications special modification
*/ */
function _addModifications(module, modifiers, blueprint, grade) { function _addModifications(module, modifiers, blueprint, grade, specialModifications) {
if (!modifiers || !modifiers.modifiers) return; if (!modifiers) return;
let special; let special;
for (const i in modifiers.modifiers) { if (specialModifications) {
special = Modifications.specials[Object.keys(specialModifications)[0]];
}
for (const i in modifiers) {
// Some special modifications // Some special modifications
if (modifiers.modifiers[i].name === 'mod_weapon_clip_size_override') { if (modifiers[i].name === 'mod_weapon_clip_size_override') {
// This is a numeric addition to the clip size, but we need to work it out in terms of being a percentage so // This is a numeric addition to the clip size, but we need to work it out in terms of being a percentage so
// that it works the same as other modifications // that it works the same as other modifications
const origClip = module.clip || 1; const origClip = module.clip || 1;
module.setModValue('clip', ((modifiers.modifiers[i].value - origClip) / origClip) * 10000); module.setModValue('clip', ((modifiers[i].value - origClip) / origClip) * 10000);
} else if (modifiers.modifiers[i].name === 'mod_weapon_burst_size') { } else if (modifiers[i].name === 'mod_weapon_burst_size') {
// This is an absolute number that acts as an override // This is an absolute number that acts as an override
module.setModValue('burst', modifiers.modifiers[i].value * 100); module.setModValue('burst', modifiers[i].value * 100);
} else if (modifiers.modifiers[i].name === 'mod_weapon_burst_rof') { } else if (modifiers[i].name === 'mod_weapon_burst_rof') {
// This is an absolute number that acts as an override // This is an absolute number that acts as an override
module.setModValue('burstrof', modifiers.modifiers[i].value * 100); module.setModValue('burstrof', modifiers[i].value * 100);
} else if (modifiers.modifiers[i].name === 'mod_weapon_falloffrange_from_range') { } else if (modifiers[i].name === 'mod_weapon_falloffrange_from_range') {
// Obtain the falloff value directly from the range // Obtain the falloff value directly from the range
module.setModValue('fallofffromrange', 1); module.setModValue('fallofffromrange', 1);
} else if (modifiers.modifiers[i].name && modifiers.modifiers[i].name.startsWith('special_')) { } else if (modifiers[i].name && modifiers[i].name.startsWith('special_')) {
// We don't add special effects directly, but keep a note of them so they can be added when fetching values // We don't add special effects directly, but keep a note of them so they can be added when fetching values
special = Modifications.specials[modifiers.modifiers[i].name]; special = Modifications.specials[modifiers[i].name];
} else { } else {
// Look up the modifiers to find what we need to do // Look up the modifiers to find what we need to do
const modifierActions = Modifications.modifierActions[modifiers.modifiers[i].name]; const modifierActions = Modifications.modifierActions[i];
const value = modifiers.modifiers[i].value; let value;
if (i === 'OutfittingFieldType_DefenceModifierShieldMultiplier') {
value = modifiers[i].value - 1;
} else if (i === 'OutfittingFieldType_DefenceModifierHealthMultiplier' && blueprint.startsWith('Armour_')) {
value = (modifiers[i].value - module.hullboost) / module.hullboost;
} else if (i === 'OutfittingFieldType_DefenceModifierHealthMultiplier') {
value = modifiers[i].value / module.hullboost;
} else if (i === 'OutfittingFieldType_RateOfFire') {
value = (1 / Math.abs(modifiers[i].value));
} else {
value = modifiers[i].value - 1;
}
// 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])) {
@@ -423,7 +449,7 @@ function _addModifications(module, modifiers, blueprint, grade) {
// Bulkhead boost is based off the inherent boost of the module // Bulkhead boost is based off the inherent boost of the module
if (module.grp == 'bh') { if (module.grp == 'bh') {
const alteredBoost = (1 + module.hullboost) * (1 + module.getModValue('hullboost') / 10000) - 1; const alteredBoost = (1 + module.hullboost) * (1 + module.getModValue('hullboost') / 10000) - 1;
module.setModValue('hullboost', (alteredBoost / module.hullboost - 1) * 10000); module.setModValue('hullboost', (alteredBoost / module.hullboost - 1) * 1000);
} }
// Jitter is an absolute number, so we need to divide it by 100 // Jitter is an absolute number, so we need to divide it by 100
@@ -431,11 +457,6 @@ function _addModifications(module, modifiers, blueprint, grade) {
module.setModValue('jitter', module.getModValue('jitter') / 100); module.setModValue('jitter', module.getModValue('jitter') / 100);
} }
// FD uses interval between bursts internally, so we need to translate this to a real rate of fire
if (module.getModValue('rof')) {
module.setModValue('rof', ((1 / (1 + module.getModValue('rof') / 10000)) - 1) * 10000);
}
// Clip size is rounded up so that the result is a whole number // Clip size is rounded up so that the result is a whole number
if (module.getModValue('clip')) { if (module.getModValue('clip')) {
const individual = 1 / (module.clip || 1); const individual = 1 / (module.clip || 1);

View File

@@ -0,0 +1,291 @@
import Ship from '../shipyard/Ship';
import { HARDPOINT_NUM_TO_CLASS, shipModelFromJson } from './CompanionApiUtils';
import { Ships } from 'coriolis-data/dist';
import Module from '../shipyard/Module';
import { Modules } from 'coriolis-data/dist';
import { Modifications } from 'coriolis-data/dist';
import { getBlueprint } from './BlueprintFunctions';
/**
* Obtain a module given its FD Name
* @param {string} fdname the FD Name of the module
* @return {Module} the module
*/
function _moduleFromFdName(fdname) {
if (!fdname) return null;
fdname = fdname.toLowerCase();
// Check standard modules
for (const grp in Modules.standard) {
if (Modules.standard.hasOwnProperty(grp)) {
for (const i in Modules.standard[grp]) {
if (Modules.standard[grp][i].symbol && Modules.standard[grp][i].symbol.toLowerCase() === fdname) {
// Found it
return new Module({ template: Modules.standard[grp][i] });
}
}
}
}
// Check hardpoint modules
for (const grp in Modules.hardpoints) {
if (Modules.hardpoints.hasOwnProperty(grp)) {
for (const i in Modules.hardpoints[grp]) {
if (Modules.hardpoints[grp][i].symbol && Modules.hardpoints[grp][i].symbol.toLowerCase() === fdname) {
// Found it
return new Module({ template: Modules.hardpoints[grp][i] });
}
}
}
}
// Check internal modules
for (const grp in Modules.internal) {
if (Modules.internal.hasOwnProperty(grp)) {
for (const i in Modules.internal[grp]) {
if (Modules.internal[grp][i].symbol && Modules.internal[grp][i].symbol.toLowerCase() === fdname) {
// Found it
return new Module({ template: Modules.internal[grp][i] });
}
}
}
}
// Not found
return null;
}
/**
* Build a ship from the journal Loadout event JSON
* @param {object} json the Loadout event JSON
* @return {Ship} the built ship
*/
export function shipFromLoadoutJSON(json) {
// Start off building a basic ship
const shipModel = shipModelFromJson(json);
if (!shipModel) {
throw 'No such ship found: "' + json.Ship + '"';
}
const shipTemplate = Ships[shipModel];
let ship = new Ship(shipModel, shipTemplate.properties, shipTemplate.slots);
ship.buildWith(null);
// Initial Ship building, don't do engineering yet.
let opts = [];
for (const module of json.Modules) {
switch (module.Slot.toLowerCase()) {
// Cargo Hatch.
case 'cargohatch':
ship.cargoHatch.enabled = module.On;
ship.cargoHatch.priority = module.Priority;
break;
// Add the bulkheads
case 'armour':
if (module.Item.toLowerCase().endsWith('_armour_grade1')) {
ship.useBulkhead(0, true);
} else if (module.Item.toLowerCase().endsWith('_armour_grade2')) {
ship.useBulkhead(1, true);
} else if (module.Item.toLowerCase().endsWith('_armour_grade3')) {
ship.useBulkhead(2, true);
} else if (module.Item.toLowerCase().endsWith('_armour_mirrored')) {
ship.useBulkhead(3, true);
} else if (module.Item.toLowerCase().endsWith('_armour_reactive')) {
ship.useBulkhead(4, true);
} else {
throw 'Unknown bulkheads "' + module.Item + '"';
}
ship.bulkheads.enabled = true;
if (module.Engineering) _addModifications(ship.bulkheads.m, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect);
break;
case 'powerplant':
const powerplant = _moduleFromFdName(module.Item);
ship.use(ship.standard[0], powerplant, true);
ship.standard[0].enabled = module.On;
ship.standard[0].priority = module.Priority;
if (module.Engineering) _addModifications(powerplant, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect);
break;
case 'mainengines':
const thrusters = _moduleFromFdName(module.Item);
ship.use(ship.standard[1], thrusters, true);
ship.standard[1].enabled = module.On;
ship.standard[1].priority = module.Priority;
if (module.Engineering) _addModifications(thrusters, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect);
break;
case 'frameshiftdrive':
const frameshiftdrive = _moduleFromFdName(module.Item);
ship.use(ship.standard[2], frameshiftdrive, true);
ship.standard[2].enabled = module.On;
ship.standard[2].priority = module.Priority;
if (module.Engineering) _addModifications(frameshiftdrive, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect);
break;
case 'lifesupport':
const lifesupport = _moduleFromFdName(module.Item);
ship.use(ship.standard[3], lifesupport, true);
ship.standard[3].enabled = module.On === true;
ship.standard[3].priority = module.Priority;
if (module.Engineering) _addModifications(lifesupport, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect);
break;
case 'powerdistributor':
const powerdistributor = _moduleFromFdName(module.Item);
ship.use(ship.standard[4], powerdistributor, true);
ship.standard[4].enabled = module.On;
ship.standard[4].priority = module.Priority;
if (module.Engineering) _addModifications(powerdistributor, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect);
break;
case 'radar':
const sensors = _moduleFromFdName(module.Item);
ship.use(ship.standard[5], sensors, true);
ship.standard[5].enabled = module.On;
ship.standard[5].priority = module.Priority;
if (module.Engineering) _addModifications(sensors, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect);
break;
case 'fueltank':
const fueltank = _moduleFromFdName(module.Item);
ship.use(ship.standard[6], fueltank, true);
ship.standard[6].enabled = true;
ship.standard[6].priority = 0;
break;
default:
}
for (const module of json.Modules) {
if (module.Slot.toLowerCase().search(/hardpoint/) !== -1) {
// Add hardpoints
let hardpoint;
let hardpointClassNum = -1;
let hardpointSlotNum = -1;
let hardpointArrayNum = 0;
for (let i in shipTemplate.slots.hardpoints) {
if (shipTemplate.slots.hardpoints[i] === hardpointClassNum) {
// Another slot of the same class
hardpointSlotNum++;
} else {
// The first slot of a new class
hardpointClassNum = shipTemplate.slots.hardpoints[i];
hardpointSlotNum = 1;
}
// Now that we know what we're looking for, find it
const hardpointName = HARDPOINT_NUM_TO_CLASS[hardpointClassNum] + 'Hardpoint' + hardpointSlotNum;
const hardpointSlot = json.Modules.find(elem => elem.Slot.toLowerCase() === hardpointName.toLowerCase());
if (!hardpointSlot) {
// This can happen with old imports that don't contain new hardpoints
} else if (!hardpointSlot) {
// No module
} else {
hardpoint = _moduleFromFdName(hardpointSlot.Item);
ship.use(ship.hardpoints[hardpointArrayNum], hardpoint, true);
ship.hardpoints[hardpointArrayNum].enabled = hardpointSlot.On;
ship.hardpoints[hardpointArrayNum].priority = hardpointSlot.Priority;
opts.push({ coriolisMod: hardpoint, json: hardpointSlot });
}
hardpointArrayNum++;
}
}
if (module.Slot.toLowerCase().search(/slot\d/) !== -1) {
let internalSlotNum = 1;
let militarySlotNum = 1;
for (let i in shipTemplate.slots.internal) {
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
let internalSlot = null;
if (isMilitary) {
const internalName = 'Military0' + militarySlotNum;
internalSlot = json.Modules.find(elem => elem.Slot.toLowerCase() === internalName.toLowerCase());
militarySlotNum++;
} else {
// Slot numbers are not contiguous so handle skips.
while (internalSlot === null && internalSlotNum < 99) {
// Slot sizes have no relationship to the actual size, either, so check all possibilities
for (let slotsize = 0; slotsize < 9; slotsize++) {
const internalName = 'Slot' + (internalSlotNum <= 9 ? '0' : '') + internalSlotNum + '_Size' + slotsize;
if (json.Modules.find(elem => elem.Slot.toLowerCase() === internalName.toLowerCase())) {
internalSlot = json.Modules.find(elem => elem.Slot.toLowerCase() === internalName.toLowerCase());
break;
}
}
internalSlotNum++;
}
}
if (!internalSlot) {
// This can happen with old imports that don't contain new slots
} else if (!internalSlot) {
// No module
} else {
const internalJson = internalSlot;
const internal = _moduleFromFdName(internalJson.Item);
ship.use(ship.internal[i], internal, true);
ship.internal[i].enabled = internalJson.On === true;
ship.internal[i].priority = internalJson.Priority;
opts.push({ coriolisMod: internal, json: internalSlot });
}
}
}
}
}
for (const i of opts) {
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
if (!ship.cargoHatch) {
ship.cargoHatch.enabled = false;
ship.cargoHatch.priority = 4;
}
// Now update the ship's codes before returning it
return ship.updatePowerPrioritesString().updatePowerEnabledString().updateModificationsString();
}
/**
* Add the modifications for a module
* @param {Module} module the module
* @param {Object} modifiers the modifiers
* @param {Object} blueprint the blueprint of the modification
* @param {Object} grade the grade of the modification
* @param {Object} specialModifications special modification
*/
function _addModifications(module, modifiers, blueprint, grade, specialModifications) {
if (!modifiers) return;
let special;
if (specialModifications) {
special = Modifications.specials[specialModifications];
}
for (const i in modifiers) {
// Some special modifications
// Look up the modifiers to find what we need to do
const findMod = val => Object.keys(Modifications.modifierActions).find(elem => elem.toString().toLowerCase().replace(/(outfittingfieldtype_|persecond)/igm, '') === val.toString().toLowerCase().replace(/(outfittingfieldtype_|persecond)/igm, ''));
const modifierActions = Modifications.modifierActions[findMod(modifiers[i].Label)];
// TODO: Figure out how to scale this value.
if (!!modifiers[i].LessIsGood) {
}
let value = (modifiers[i].Value / modifiers[i].OriginalValue * 100 - 100) * 100;
if (value === Infinity) {
value = modifiers[i].Value * 100;
}
if (modifiers[i].Label.search('Resistance') >= 0) {
value = (modifiers[i].Value * 100) - (modifiers[i].OriginalValue * 100);
}
// Carry out the required changes
for (const action in modifierActions) {
if (isNaN(modifierActions[action])) {
module.setModValue(action, modifierActions[action]);
} else {
module.setModValue(action, value);
}
}
}
// 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

@@ -8,7 +8,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) {
shortenUrlEddp(url, success, error); shortenUrlOrbis(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=';
@@ -64,3 +64,32 @@ function shortenUrlEddp(url, success, error) {
error('Not Online'); error('Not Online');
} }
} }
const SHORTEN_API_ORBIS = 'https://s.orbis.zone/a';
/**
* Shorten a URL using Orbis's URL shortener API
* @param {string} url The URL to shorten
* @param {function} success Success callback
* @param {function} error Failure/Error callback
*/
function shortenUrlOrbis(url, success, error) {
if (window.navigator.onLine) {
try {
request.post(SHORTEN_API_ORBIS)
.field('lsturl', url)
.field('format', 'json')
.end(function(err, response) {
if (err) {
error('Bad Request');
} else {
success(response.body.short);
}
});
} catch (e) {
console.log(e)
error(e.message ? e.message : e);
}
} else {
error('Not Online');
}
}

View File

@@ -220,7 +220,7 @@ export function diffDetails(language, m, mm) {
let mCost = m.cost || 0; let mCost = m.cost || 0;
let mmCost = mm ? mm.cost : 0; let mmCost = mm ? mm.cost : 0;
if (mCost != mmCost) propDiffs.push(<div key='cost'>{translate('cost')}: <span className={diffClass(mCost, mmCost, true) }>{mCost ? Math.round(mCost * (1 - Persist.getModuleDiscount())) : 0}{units.CR}</span></div>); if (mCost != mmCost) propDiffs.push(<div key='cost'>{translate('cost')}: <span className={diffClass(mCost, mmCost, true) }>{formats.int(mCost ? Math.round(mCost * (1 - Persist.getModuleDiscount())) : 0)}{units.CR}</span></div>);
let mMass = m.mass || 0; let mMass = m.mass || 0;
let mmMass = mm ? mm.getMass() : 0; let mmMass = mm ? mm.getMass() : 0;

6
src/images/Empire.svg Normal file
View File

@@ -0,0 +1,6 @@
<svg width="1718.2px" height="999.8px" viewBox="0 0 1718.2 999.8" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs/>
<g>
<path stroke="none" fill="#FF8C0D" d="M1717.55 -0.05 L1717.65 -0.25 1718.2 -0.1 1718.6 0.35 1719.2 1.55 1719.3 2.25 1718.95 2.85 1709.75 11.35 1010.8 687.7 1008.05 690.5 1005.45 693.4 1003 696.35 1000.75 699.45 998.65 702.65 996.7 705.95 994.9 709.4 993.25 713.05 934.9 850.7 876.4 988.25 871.55 998.95 871.3 999.3 870.9 999.5 870.6 999.55 870 999.4 869.5 999.05 868.3 997.95 867.35 996.7 866.6 995.25 864.7 989.85 762.3 730.55 754.8 711.85 751.1 702.45 746.6 690.65 745.5 688.4 744.25 686.3 742.9 684.35 741.4 682.45 739.75 680.6 737.95 678.85 736 677.15 8.1 61.2 5.75 59.15 3.65 57.1 1.6 54.6 1.1 53.7 1.1 53.25 1.25 52.9 1.5 52.65 1.8 52.45 2.15 52.4 2.55 52.5 9.75 55.7 713.05 398.5 780.95 431.7 848.75 464.95 851.7 466.25 854.4 467.15 856.95 467.6 859.45 467.65 861.9 467.25 864.4 466.45 866.95 465.2 869.6 463.5 1416.95 79.15 1419.7 77.35 1422.45 75.7 1425.25 74.2 1428.1 72.85 1431 71.6 1434 70.5 1437.05 69.55 1440.2 68.7 1574.85 35.15 1709.5 1.5 1713.75 0.6 1717.55 -0.05 M903.9 129.8 L904 129.65 930.9 129.75 934.3 129.85 937.55 130.15 940.65 130.65 943.6 131.35 946.4 132.2 949.2 133.3 951.95 134.6 954.55 136.05 957 137.7 959.35 139.5 961.6 141.5 963.75 143.65 965.8 145.95 967.75 148.4 969.6 151 971.35 153.8 974.25 159 976.8 164.3 979.05 169.7 981 175.2 982.7 180.8 984.2 186.45 985.55 192.15 986.75 197.9 987.85 203.75 988.8 209.65 990.4 221.45 991.6 233.25 992.55 245.1 995 280.65 996 292.5 996.2 295.1 996.25 297.55 996.15 299.85 995.9 302 995.5 304.1 994.95 306.1 994.25 308 993.45 309.7 992.4 311.5 991.25 313.15 989.95 314.75 988.45 316.3 986.8 317.8 985 319.25 980.85 322.15 946.35 344.45 912.05 367.15 843.7 413.05 841.45 414.45 839.25 415.55 836.9 416.35 834.85 416.75 832.6 416.85 830.15 416.5 827.75 415.8 825.25 414.7 812.45 408.55 799.4 402.55 773.35 390.75 773 390.5 772.8 390.15 772.75 389.75 772.85 389.35 794.5 343.45 801.35 328.15 811 305.8 820.45 283.4 839 238.45 841 233 842.7 227.35 844.1 221.65 845.25 215.95 845.7 213.05 845.8 210.65 845.55 208.55 845.3 207.75 844.9 206.9 843.85 205.55 842.3 204.4 840.2 203.4 837.55 202.55 830.2 200.55 822.85 198.85 819.15 198.15 815.5 197.6 811.85 197.2 808.2 196.95 804.55 196.9 800.9 197.05 797.25 197.4 793.6 198 789.9 198.9 786.25 200.05 782.6 201.55 778.55 203.5 777 203.65 776.65 203.6 776.1 203.15 775.95 202.8 775.5 200.55 775.25 197.65 775.3 194.75 775.65 191.9 776.25 189.05 777.05 186.3 778 183.6 779.05 180.9 780.5 177.65 782.1 174.5 783.8 171.5 785.65 168.6 787.6 165.85 789.7 163.2 791.9 160.7 794.25 158.3 796.7 156 799.3 153.85 802 151.8 804.8 149.9 807.7 148.1 810.75 146.4 813.9 144.85 817.15 143.4 824.4 140.6 831.75 138.2 839.15 136.15 846.65 134.45 854.2 133.05 861.8 131.95 869.45 131.1 877.1 130.5 883.8 130.15 890.5 129.95 903.9 129.8"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 30 KiB

View File

@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="631.072px" height="713.591px" viewBox="0 0 631.072 713.591" enable-background="new 0 0 631.072 713.591"
xml:space="preserve">
<g id="Layer_2">
<g>
<rect x="460.344" y="668.509" transform="matrix(0.7399 0.6727 -0.6727 0.7399 586.8318 -155.8918)" fill="#FF8C0D" width="69.376" height="24.999"/>
</g>
<g>
<rect x="499.816" y="630.511" transform="matrix(0.8742 0.4856 -0.4856 0.8742 379.9255 -180.1477)" fill="#FF8C0D" width="75.501" height="24.999"/>
</g>
<g>
<rect x="537.078" y="590.363" transform="matrix(0.9477 0.319 -0.319 0.9477 222.2531 -151.1744)" fill="#FF8C0D" width="71.047" height="25"/>
</g>
<g>
<rect x="561.364" y="544.916" transform="matrix(0.9741 0.2263 -0.2263 0.9741 141.5892 -120.2454)" fill="#FF8C0D" width="67.758" height="25"/>
</g>
<g>
<rect x="113.454" y="633.977" transform="matrix(0.6727 0.7399 -0.7399 0.6727 536.0002 125.674)" fill="#FF8C0D" width="24.999" height="69.375"/>
</g>
<g>
<rect x="72.787" y="594.581" transform="matrix(0.4857 0.8741 -0.8741 0.4857 596.6155 250.6765)" fill="#FF8C0D" width="25" height="75.501"/>
</g>
<g>
<rect x="43.454" y="554.475" transform="matrix(0.319 0.9477 -0.9477 0.319 597.2715 348.7429)" fill="#FF8C0D" width="25" height="71.046"/>
</g>
<g>
<rect x="23.329" y="507.12" transform="matrix(0.2263 0.9741 -0.9741 0.2263 554.6862 383.6775)" fill="#FF8C0D" width="25" height="67.758"/>
</g>
<path fill="#FF8C0D" d="M556.439,425.748c-13.466-24.188-32.583-45.79-56.821-64.205c-30.562-23.22-67.327-39.855-107.331-48.986
v25.737c93.31,23.32,160.681,90.053,160.681,168.752c0,98.096-104.668,177.619-233.785,177.619
c-129.115,0-233.784-79.523-233.784-177.619c0-75.918,62.696-140.699,150.889-166.118v-25.964
c-36.209,9.508-69.489,25.266-97.539,46.578c-24.238,18.415-43.355,40.017-56.821,64.205
c-14.286,25.66-21.529,53.014-21.529,81.299c0,28.284,7.243,55.637,21.529,81.298c13.465,24.188,32.583,45.789,56.821,64.204
c48.479,36.832,112.559,57.117,180.435,57.117c67.876,0,131.956-20.285,180.436-57.117c24.238-18.415,43.355-40.017,56.821-64.204
c14.285-25.661,21.528-53.014,21.528-81.298C577.968,478.761,570.725,451.408,556.439,425.748z"/>
<path fill="#FF8C0D" d="M473.97,427.082c-8.958-16.091-21.621-30.42-37.64-42.591c-13.129-9.975-27.996-18.079-44.043-24.152
v27.053c42.591,19.056,71.178,54.248,71.178,94.519c0,60.541-64.597,109.619-144.282,109.619
c-79.684,0-144.281-49.078-144.281-109.619c0-37.101,24.263-69.892,61.386-89.727v-27.819c-12.323,5.48-23.835,12.212-34.25,20.125
c-16.019,12.171-28.683,26.5-37.64,42.591c-9.618,17.276-14.495,35.724-14.495,54.829c0,19.104,4.877,37.552,14.495,54.829
c8.958,16.091,21.622,30.42,37.64,42.59c31.574,23.989,73.178,37.2,117.146,37.2c43.969,0,85.572-13.211,117.147-37.2
c16.019-12.17,28.682-26.499,37.64-42.59c9.618-17.277,14.495-35.725,14.495-54.829
C488.465,462.805,483.588,444.358,473.97,427.082z"/>
</g>
<g id="Layer_3">
<path fill="#FF8C0D" d="M253.551,232.333c-0.157-0.981-0.264-1.978-0.264-3c0-10.844,9.173-19.667,20.448-19.667
s20.448,8.822,20.448,19.667c0,1.022-0.106,2.019-0.264,3h25.15c0.067-0.993,0.113-1.991,0.113-3
c0-24.668-20.348-44.667-45.448-44.667s-45.448,19.998-45.448,44.667c0,1.009,0.046,2.007,0.113,3H253.551z"/>
<path fill="#FF8C0D" d="M302.831,48c-0.136-0.669-0.21-1.337-0.21-2c0-9.913,15.539-21,36.333-21s36.333,11.087,36.333,21
c0,0.663-0.074,1.331-0.21,2h25.143c0.038-0.664,0.067-1.329,0.067-2c0-25.405-27.46-46-61.333-46
c-33.874,0-61.333,20.595-61.333,46c0,0.671,0.03,1.336,0.067,2H302.831z"/>
<ellipse fill="#FF8C0D" cx="338.954" cy="68.499" rx="36.333" ry="6.75"/>
<path fill="#FF8C0D" d="M375.287,46c0,2.208-16.267,3.999-36.333,3.999S302.621,48.208,302.621,46h-25v158.499h25v-136h72.667
V473.51c-4.165,3.092-16.463,8.535-36.333,8.535c-7.763,0-14.353-0.836-19.771-2.033l-10.749,23.051
c8.99,2.529,19.405,3.982,30.52,3.982c33.873,0,61.333-13.432,61.333-30V46H375.287z"/>
<ellipse fill="#FF8C0D" cx="273.735" cy="253.132" rx="20.448" ry="3.916"/>
<path fill="#FF8C0D" d="M294.184,229.333c0,1.151-9.155,2.084-20.448,2.084s-20.448-0.933-20.448-2.084h-25v257.666h0.128
c1.667,14.529,21.313,26,45.32,26s43.653-11.471,45.32-26h0.128V229.333H294.184z M294.184,254.333v228.201
c-2.473,2.008-9.464,5.465-20.448,5.465s-17.975-3.456-20.448-5.465V253.132h40.896V254.333z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

@@ -38,7 +38,7 @@
<% } %> <% } %>
<!-- Piwik --> <!-- Piwik -->
<script type="text/javascript"> <!-- <script type="text/javascript">
var _paq = _paq || []; var _paq = _paq || [];
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */ /* tracker methods like "setCustomDimension" should be called before "trackPageView" */
_paq.push(["setCookieDomain", "*.coriolis.edcd.io"]); _paq.push(["setCookieDomain", "*.coriolis.edcd.io"]);
@@ -51,8 +51,16 @@
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s); g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s);
})(); })();
</script> </script>-->
<!-- End Piwik Code --> <!-- End Piwik Code -->
<!-- Bugsnag -->
<script src="//d2wy8f7a9ursnm.cloudfront.net/v4/bugsnag.min.js"></script>
<script src="//d2wy8f7a9ursnm.cloudfront.net/bugsnag-plugins/v1/bugsnag-react.min.js"></script>
<script>
window.bugsnagClient = bugsnag('ba9fae819372850fb660755341fa6ef5', {appVersion: window.CORIOLIS_VERSION || undefined})
window.Bugsnag = window.bugsnagClient
</script>
</head> </head>
<body style="background-color:#000;"> <body style="background-color:#000;">
<section id="coriolis"></section> <section id="coriolis"></section>

View File

@@ -31,6 +31,50 @@ button {
} }
} }
.button-inline-menu {
white-space: nowrap;
line-height: 1.5em;
text-align: center;
margin: 0.5em 0;
padding-left: 5px;
border-top: 1px solid @primary-disabled;
border-bottom: 1px solid @primary-disabled;
overflow: hidden;
text-overflow: ellipsis;
background: @primary-bg;
&.warning {
border-color: @warning-disabled;
color: @warning-disabled;
stroke: @warning-disabled;
.no-touch &:hover {
border-color: @warning;
color: @warning;
stroke: @warning;
}
}
&.disabled, &.disabled:hover {
cursor: not-allowed;
border-color: @disabled;
color: @disabled;
stroke: @disabled;
}
&.active {
border-color: @secondary;
color: @secondary;
stroke: @secondary;
}
&:hover {
border-color: @primary;
color: @primary;
stroke: @primary;
}
}
.button-lbl { .button-lbl {
margin-left: 0.5em; margin-left: 0.5em;

View File

@@ -10,6 +10,8 @@
@secondary: #1FB0FF; // Light blue @secondary: #1FB0FF; // Light blue
@warning: #FF3B00; // Dark Orange @warning: #FF3B00; // Dark Orange
@disabled: #555; // Light grey @disabled: #555; // Light grey
@success: #71a052; // Green
@purple: #800080; // Purple
@primary-disabled: darken(@primary, @disabledDarken); @primary-disabled: darken(@primary, @disabledDarken);
@secondary-disabled: darken(@secondary, @disabledDarken); @secondary-disabled: darken(@secondary, @disabledDarken);
@warning-disabled: darken(@warning, @disabledDarken); @warning-disabled: darken(@warning, @disabledDarken);
@@ -18,6 +20,7 @@
@bg: rgba(30,30,30,1); @bg: rgba(30,30,30,1);
@bgBlack: #000; @bgBlack: #000;
@primary-bg: fadeout(darken(@primary, 47%), 15%); @primary-bg: fadeout(darken(@primary, 47%), 15%);
@alt-primary-bg: fadeout(darken(@primary, 42%), 15%); // Lighter brown background
@secondary-bg: fadeout(darken(@secondary, @bgDarken), @bgTransparency); // Brown background @secondary-bg: fadeout(darken(@secondary, @bgDarken), @bgTransparency); // Brown background
@warning-bg: fadeout(darken(@warning, @bgDarken), @bgTransparency); // Dark Red @warning-bg: fadeout(darken(@warning, @bgDarken), @bgTransparency); // Dark Red

View File

@@ -12,6 +12,20 @@
cursor: pointer; cursor: pointer;
} }
.view-changes {
position: fixed;
top: 3em;
left: 0;
right: 0;
height: 3em;
z-index: 3;
line-height: 3em;
text-align: center;
background-color: @bg;
color: @warning;
cursor: pointer;
}
header { header {
background-color: @bg; background-color: @bg;
margin: 0; margin: 0;

View File

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

View File

@@ -21,13 +21,47 @@
#overview { #overview {
h1 { h1 {
margin: 0; margin: 0 1em 0.2em 0;
float: left; float: left;
} }
overflow: hidden; overflow: hidden;
margin: 0.5em; margin: 0.5em;
} }
#requirements {
margin: 0;
float: left;
text-align: left;
div {
display: inline-block;
margin: 0 1em 0.5em 0;
height: 1.6em;
line-height: 1.6em;
text-align: left;
vertical-align: middle;
cursor: default;
background-repeat: no-repeat;
background-position: left center;
background-size: contain;
}
.federation {
background-image: url('../images/Federation.svg');
padding-left: 1.985em + 0.5em;
}
.empire {
background-image: url('../images/Empire.svg');
padding-left: 2.750em + 0.5em;
}
.horizons {
background-image: url('../images/surface_port_pm.svg');
padding-left: 1.415em + 0.5em;
}
}
#summary { #summary {
overflow-x: auto; overflow-x: auto;
-webkit-overflow-scrolling: touch; -webkit-overflow-scrolling: touch;
@@ -36,10 +70,29 @@
padding: 0.5em 0.2em; padding: 0.5em 0.2em;
font-size: 0.9em; font-size: 0.9em;
#summaryTable { .summaryTable {
.user-select-none(); .user-select-none();
width: 100%; width: 100%;
border-collapse: collapse; border-collapse: collapse;
& > thead.blue {
background-color: @secondary;
border-left: 1px solid @primary-bg;
color: @primary-bg;
}
& > thead.green {
background-color: @success;
border-left: 1px solid @primary-bg;
color: @primary-bg;
}
& > thead.purple {
background-color: @purple;
border-left: 1px solid @primary-bg;
color: @primary-bg;
}
} }
} }

View File

@@ -41,6 +41,10 @@
overflow: hidden; overflow: hidden;
} }
input.cb:focus {
border-color:#fff;
}
.l { .l {
text-transform: capitalize; text-transform: capitalize;
margin-right: 0.8em; margin-right: 0.8em;

View File

@@ -54,6 +54,10 @@ tbody tr {
.no-touch &.highlight:hover, .no-touch &.highlighted { .no-touch &.highlight:hover, .no-touch &.highlighted {
background-color: @warning-bg; background-color: @warning-bg;
} }
&.alt {
background-color: @alt-primary-bg;
}
} }
td { td {

View File

@@ -1,28 +1,30 @@
var path = require('path'); const path = require('path')
var exec = require('child_process').exec; const exec = require('child_process').exec
var webpack = require('webpack'); const webpack = require('webpack')
var pkgJson = require('./package'); const HtmlWebpackPlugin = require('html-webpack-plugin')
var HtmlWebpackPlugin = require("html-webpack-plugin"); const ExtractTextPlugin = require('extract-text-webpack-plugin')
var ExtractTextPlugin = require("extract-text-webpack-plugin"); const WebpackNotifierPlugin = require('webpack-notifier')
const pkgJson = require('./package')
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) {
compiler.plugin('done', () => {
console.log(compiler.outputPath, this.destination)
exec('cp -r ' + this.source + ' ' + path.join(compiler.outputPath, this.destination))
})
} }
CopyDirPlugin.prototype.apply = function(compiler) {
compiler.plugin('done', function() {
console.log(compiler.outputPath, this.destination);
exec('cp -r ' + this.source + ' ' + path.join(compiler.outputPath, this.destination));
}.bind(this));
};
module.exports = { module.exports = {
devtool: 'eval', 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')],
lib: ['d3', 'react', 'react-dom', 'classnames', 'fbemitter', 'lz-string'] lib: ['d3', 'react', 'react-dom', 'classnames', 'fbemitter', 'lz-string']
}, },
resolve: { resolve: {
@@ -37,33 +39,34 @@ module.exports = {
plugins: [ plugins: [
new CopyDirPlugin(path.join(__dirname, 'src/.htaccess'), ''), new CopyDirPlugin(path.join(__dirname, 'src/.htaccess'), ''),
new webpack.optimize.CommonsChunkPlugin({ new webpack.optimize.CommonsChunkPlugin({
name: 'lib', name: 'lib',
filename: 'lib.js' filename: 'lib.js'
}), }),
new HtmlWebpackPlugin({ new HtmlWebpackPlugin({
inject: false, inject: false,
template: path.join(__dirname, "src/index.ejs"), template: path.join(__dirname, 'src/index.ejs'),
version: pkgJson.version, version: pkgJson.version,
gapiKey: process.env.CORIOLIS_GAPI_KEY || '', gapiKey: process.env.CORIOLIS_GAPI_KEY || ''
}), }),
new ExtractTextPlugin({ new ExtractTextPlugin({
filename: 'app.css', filename: 'app.css',
disable: false, disable: false,
allChunks: true allChunks: 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,24 +1,27 @@
var path = require('path'); const path = require('path')
var exec = require('child_process').exec; const exec = require('child_process').exec
var webpack = require('webpack'); const webpack = require('webpack')
var pkgJson = require('./package'); const HtmlWebpackPlugin = require('html-webpack-plugin')
var HtmlWebpackPlugin = require("html-webpack-plugin"); const ExtractTextPlugin = require('extract-text-webpack-plugin')
var ExtractTextPlugin = require("extract-text-webpack-plugin"); const AppCachePlugin = require('appcache-webpack-plugin')
var AppCachePlugin = require('appcache-webpack-plugin'); const {BugsnagSourceMapUploaderPlugin} = require('webpack-bugsnag-plugins')
const pkgJson = require('./package')
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) {
compiler.plugin('done', () => {
console.log(compiler.outputPath, this.destination)
exec('cp -r ' + this.source + ' ' + path.join(compiler.outputPath, this.destination))
})
} }
CopyDirPlugin.prototype.apply = function(compiler) {
compiler.plugin('done', function() {
console.log(compiler.outputPath, this.destination);
exec('cp -r ' + this.source + ' ' + path.join(compiler.outputPath, this.destination));
}.bind(this));
};
module.exports = { module.exports = {
cache: true, cache: true,
devtool: 'source-map',
entry: { entry: {
app: ['babel-polyfill', path.resolve(__dirname, 'src/app/index')], app: ['babel-polyfill', path.resolve(__dirname, 'src/app/index')],
lib: ['d3', 'react', 'react-dom', 'classnames', 'fbemitter', 'lz-string'] lib: ['d3', 'react', 'react-dom', 'classnames', 'fbemitter', 'lz-string']
@@ -34,34 +37,39 @@ module.exports = {
}, },
plugins: [ plugins: [
new webpack.optimize.UglifyJsPlugin({ new webpack.optimize.UglifyJsPlugin({
'screw-ie8': true 'screw-ie8': true,
sourceMap: true
}), }),
//new webpack.optimize.CommonsChunkPlugin({ //new webpack.optimize.CommonsChunkPlugin({
// name: 'lib', // name: 'lib',
// filename: 'lib.[chunkhash:6].js' // filename: 'lib.[chunkhash:6].js'
//}), //}),
new HtmlWebpackPlugin({ new HtmlWebpackPlugin({
inject: false, inject: false,
appCache: 'coriolis.appcache', appCache: 'coriolis.appcache',
minify: { minify: {
collapseBooleanAttributes: true, collapseBooleanAttributes: true,
collapseWhitespace: true, collapseWhitespace: true,
removeAttributeQuotes: true, removeAttributeQuotes: true,
removeComments: true, removeComments: true,
removeEmptyAttributes: true, removeEmptyAttributes: true,
removeRedundantAttributes: true, removeRedundantAttributes: true,
removeScriptTypeAttributes: true, removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true removeStyleLinkTypeAttributes: true
}, },
template: path.join(__dirname, "src/index.ejs"), template: path.join(__dirname, 'src/index.ejs'),
uaTracking: process.env.CORIOLIS_UA_TRACKING || '', uaTracking: process.env.CORIOLIS_UA_TRACKING || '',
gapiKey: process.env.CORIOLIS_GAPI_KEY || '', gapiKey: process.env.CORIOLIS_GAPI_KEY || '',
version: pkgJson.version version: pkgJson.version
}), }),
new ExtractTextPlugin({ new ExtractTextPlugin({
filename: '[contenthash:6].css', filename: '[contenthash:6].css',
disable: false, disable: false,
allChunks: true allChunks: true
}),
new BugsnagSourceMapUploaderPlugin({
apiKey: 'ba9fae819372850fb660755341fa6ef5',
appVersion: pkgJson.version
}), }),
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/*'), ''),
@@ -75,14 +83,14 @@ module.exports = {
], ],
module: { module: {
rules: [ rules: [
{ test: /\.css$/, loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader'}) }, {test: /\.css$/, loader: ExtractTextPlugin.extract({fallback: 'style-loader', use: 'css-loader'})},
{ test: /\.less$/, loader: ExtractTextPlugin.extract({ fallback: 'style-loader',use: 'css-loader!less-loader'}) }, {test: /\.less$/, loader: ExtractTextPlugin.extract({fallback: 'style-loader', use: 'css-loader!less-loader'})},
{ test: /\.(js|jsx)$/, loader: 'babel-loader?cacheDirectory=true', include: path.join(__dirname, 'src') }, {test: /\.(js|jsx)$/, loader: 'babel-loader?cacheDirectory=true', include: path.join(__dirname, 'src')},
{ test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/font-woff' }, {test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/font-woff'},
{ test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/font-woff' }, {test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=application/font-woff'},
{ 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'}
] ]
} }
}; }