Compare commits

...

103 Commits

Author SHA1 Message Date
felixlinker
934593fe2a Migrate changes of 87fc3b6cbb 2019-10-08 14:00:54 +02:00
willyb321
e4a826592f remove ads, trial didnt work 2019-09-27 07:15:13 +10:00
willyb321
cee4c32551 Merge branch 'master' into develop 2019-09-23 08:47:08 +10:00
willyb321
081d8fb86a google analytics 2019-09-23 08:41:34 +10:00
willyb321
3dfd563d90 testing ads again 2019-09-23 08:39:53 +10:00
William
fd08cd219c Merge pull request #546 from EDCD/fix/bugs
Various bug fixes
2019-09-18 05:59:58 +10:00
sascha-dev
6a15326d31 Wrong translation corrected. 2019-09-15 12:46:41 +02:00
sascha-dev
608ecc51b7 german translation extended 2019-09-15 12:46:41 +02:00
sascha-dev
fcef26ebbb german translation extended. fixed some typos. 2019-09-15 12:46:41 +02:00
Felix Linker
ba6d758ed5 Import plasma slug correctly
References #450
2019-09-13 22:36:56 +02:00
Felix Linker
43aa3e4e79 Merge pull request #541 from monopoint/export_select
bugfix: unable to copy from export textarea (#540)
2019-09-11 22:27:23 +02:00
Unknown
18f0e060a7 fix for scrollbar on demand 2019-09-11 22:16:55 +02:00
EspenMH
c7547e8baf shipyard: group compared, scrollbar on demand, borders 2019-09-11 22:16:55 +02:00
spinmh
ffff242abe userselect: auto does not resove correct if parent has user-select: none (it has)
set explicitly to "text"
2019-09-07 18:42:48 +02:00
Felix Linker
b44c66b986 Merge pull request #536 from monopoint/develop
448: compare highlights in shipyard
2019-08-25 13:32:08 +02:00
Felix Linker
ae77ec6256 Implement blueprint changes to focused weapon 2019-08-24 15:36:51 +02:00
Felix Linker
4f1e32b154 Move sustained factor calculation into own function
Closes #517
2019-08-24 14:16:50 +02:00
Felix Linker
af37c2bfc5 Fix import falloff
References #535
2019-08-24 14:16:50 +02:00
felixlinker
5d4ab6f2ad Disabling guardian shield/module/hull reinforcement packages has an effect now
Closes #523
2019-08-24 14:16:49 +02:00
felixlinker
0c9db53057 Use shield and armour metrics in ship summary table 2019-08-24 14:16:49 +02:00
felixlinker
b689605ac2 Maximum active limpets does not show twice now
Closes #532
2019-08-19 18:59:11 +02:00
felixlinker
baab91e371 Rename "Boost Time" to "Boost Interval"
Closes #537
2019-08-19 18:52:15 +02:00
EspenMH
70e69c7099 448: removed double quotes in js. slight text highlight on compared ships in shipyard 2019-08-15 15:06:37 +02:00
spinmh
f4534fd3eb Remove manual ShouldComponentUpdate eval 2019-08-15 02:19:38 +02:00
spinmh
93594e1a65 Compare Highlight in Shipyard 2019-08-15 02:18:00 +02:00
William
b5e449ea54 Merge pull request #525 from TranslucentSabre/feature/fix_module_discount_import
Fix backup import to properly handle module discount.
2019-07-01 08:18:07 +10:00
William
0ff4b849aa Update Header.jsx 2019-06-29 11:34:58 +10:00
William
b99e38043f Update Header.jsx 2019-06-29 11:34:03 +10:00
Timothy Myers
28e3a59473 Fix backup import to properly handle module discount. 2019-06-24 11:22:10 -05:00
Felix Linker
b20290fb60 Add mention of compiled and hosted coriolis-data-files in README 2019-05-27 21:18:54 +02:00
willyb321
2734beb6f8 Merge remote-tracking branch 'origin/master' 2019-05-23 16:59:02 +10:00
willyb321
345eec528c RIP 2019-05-23 16:58:14 +10:00
Felix Linker
7a17e18a76 Remove undefined variable 2019-05-19 23:22:07 +02:00
willyb321
4697677457 Merge branch 'develop' 2019-05-20 06:36:43 +10:00
felixlinker
7d8a5a1368 Clear original slot if an experimental is copied and exceeds the allowed number
References #367
2019-05-19 16:01:48 +02:00
felixlinker
dd7402bd0e Allow experimental modules to be replaced with such even if 4 are present
References #367
2019-05-19 16:00:16 +02:00
felixlinker
65592b0fc6 Allow any thruster to be applied
Closes #512
2019-05-19 15:59:33 +02:00
felixlinker
0ab66023a6 Update calculation of SHPS/SEPS to match DPS~SDPS
References #504
2019-05-19 01:25:51 +02:00
felixlinker
d6fad098ee Restrict experimental weapons to at 4 at max
Closes #367
2019-05-19 01:12:50 +02:00
felixlinker
1b5730d337 Revert "Max 4 items per row when selecting modules"
This reverts commit 9b6b1d328c.
2019-05-19 00:49:09 +02:00
felixlinker
439b615b1b Revert "Don't allow more than four experimental weapons at once"
This reverts commit ac2e2e4d69.
2019-05-19 00:41:12 +02:00
Felix Linker
a8b30594dc Merge pull request #511 from EDCD/fix/data
Not more than 4 experimentals
2019-05-18 12:48:44 +02:00
felixlinker
9b6b1d328c Max 4 items per row when selecting modules 2019-05-18 12:16:39 +02:00
felixlinker
ac2e2e4d69 Don't allow more than four experimental weapons at once
Closes https://github.com/EDCD/coriolis/issues/367
2019-05-18 12:01:31 +02:00
Felix Linker
3a5fb31860 Merge pull request #508 from EDCD/fix/dps
Fixes to DPS and ROF calculations
2019-05-17 22:57:32 +02:00
felixlinker
c610eb8627 Fix burst(rof) modding and burst sdps calculation
References #504 #388
2019-05-17 21:02:17 +02:00
felixlinker
94980270c4 Fix SDPS calculations
Closes #504
2019-05-17 20:22:57 +02:00
felixlinker
c685e002e3 Fastest range is farthest range
Closes #471
2019-05-17 19:43:12 +02:00
felixlinker
1f665eed9e We don't need any time to remove no shields
Closes #426
2019-05-17 18:51:09 +02:00
felixlinker
0c4fc1fd9a Correctly update jump range for turned off modules
Closes #506
2019-05-17 18:40:20 +02:00
felixlinker
0fc033363e Don't handle inverting rof twice
Closes #500
2019-05-17 17:42:51 +02:00
willyb321
fb6e9538bc change service worker caching 2019-04-29 06:44:03 +10:00
willyb321
95b98fc4ed remove google analytics 2019-04-29 06:33:11 +10:00
Felix Linker
1840dafed0 Fix rate of fire modification (#497)
* Fix absolute setting of burst/burstrof

* Improve change display for stats with overwrite

* Module.getPretty takes synthetic getters into account

* Allow custom higherbetter for changes of stats when formatted

* Fix rof modding
2019-04-29 06:05:43 +10:00
willyb321
1ad82b116c Merge branch 'develop' 2019-04-27 10:31:37 +10:00
Felix Linker
7bdd17504b Fixes (#495)
* Create class-child module for selection when comparing

Closes #198

* Remove broken exception for shieldboost in getPercent

Closes #274

* Remove barely used functions
2019-04-27 10:29:49 +10:00
willyb321
2d820bb5d5 test 2019-04-27 10:22:21 +10:00
willyb321
2e14512ed8 Merge branch 'develop' 2019-04-24 07:33:52 +10:00
willyb321
48ed583c6d Merge branch 'feature/3.4' into develop 2019-04-24 07:32:35 +10:00
willyb321
dd444a17f3 handle journal-style imports
Closes #391
2019-04-17 11:06:48 +10:00
willyb321
2ea63c711e docking computer is now a unique module
you cannot have more than 1 standard dc, or a standard and an advanced dc, nor more than 1 advanced
2019-04-15 07:01:08 +10:00
willyb321
6d6d31db25 rename assists to flight assists 2019-04-15 07:00:48 +10:00
willyb321
e9273dcb9b move dc to assists category, 3.4 prep 2019-04-15 06:43:55 +10:00
willyb321
2bdc4562c6 add rg to autofill 2019-04-05 08:51:37 +11:00
willyb321
9e8a5323e9 Merge branch 'develop' 2019-03-31 10:34:37 +11:00
willyb321
8e001063b3 fix regex 2019-03-31 10:34:22 +11:00
willyb321
dc88fab4c5 Merge branch 'develop' 2019-03-31 10:24:03 +11:00
willyb321
dfca917e50 build image in docker compose 2019-03-31 10:23:41 +11:00
willyb321
ef7dfd6ca1 checkout branch in docker build 2019-03-31 10:17:58 +11:00
Mingming Cui
435c1b6d45 Chinese translation (#481)
* Chinese translation

* Added Chinese translation for UI

* Made some strings translatable.

Changed the translation ID of certain words which were used with different meanings in difference places in order to achieve a more accurate translation.

* fixed en format

* Fixed one capitalization mistake

* removed vs folder
2019-03-05 08:31:29 +11:00
willyb321
c5c9abe588 Merge branch 'master' into develop 2019-02-27 08:24:25 +11:00
opl-
363735d36b Fix ships menu not sorted by name (#477) 2019-02-26 08:00:09 +11:00
William
2741e7701b Update .gitlab-ci.yml 2019-02-18 12:38:40 +11:00
neilser
3be442ea60 Fix wrong units for pgen and power in pop-ups (#468) 2019-01-31 08:14:35 +11:00
Willyb321
34cbeca201 Merge branch 'develop' 2019-01-24 07:40:39 +11:00
Willyb321
b37e73ead6 one build stage 2019-01-23 08:58:12 +11:00
Willyb321
ee775521d6 Merge branch 'develop' of gitlab.willb.info:coriolis/coriolis into develop 2019-01-23 08:57:33 +11:00
Willyb321
5f84aaef1b add redirect from .edcd.io to .io 2019-01-23 08:57:17 +11:00
William
99ac58d999 Update .gitlab-ci.yml 2019-01-22 19:53:07 +11:00
William
f128a1e87d Create .gitlab-ci.yml 2019-01-22 19:45:28 +11:00
William
8c0768b451 Add license file using license from README 2019-01-22 09:30:20 +11:00
Felix Linker
319307136c Remove constraints for modification of maxmass 2019-01-15 19:17:45 +01:00
Felix Linker
a498452943 Multiple bug fixes (#463)
* Don't allow manually modifying multiplicative mods with base value zero

* Add missing argument when calculating fullTankRange

* Use opponent PD when calculating how long shields will hold

Closes #430

* Allow modifying max mass ONLY for shield generators

Closes #453
2019-01-15 10:32:33 +11:00
Willyb321
fb811faf5e Merge branch 'develop' 2019-01-03 08:54:49 +11:00
Willyb321
1cb88115f6 Merge branch 'develop' 2019-01-03 08:46:29 +11:00
William Blythe
94eec120da Merge branch 'develop' 2019-01-02 12:13:46 +11:00
William Blythe
2457c30b94 Merge branch 'develop' 2018-12-31 08:19:07 +11:00
William Blythe
544e5acaef remove snow 2018-12-26 09:14:45 +11:00
William Blythe
9ab35bbaf9 Merge branch 'develop' 2018-12-26 09:07:21 +11:00
William Blythe
01e1609a9f Merge branch 'develop' 2018-12-26 08:30:00 +11:00
William Blythe
8bed35a8ba probably fix scrolling 2018-12-25 09:06:00 +11:00
William Blythe
9f4ae60577 add christmas snow
Credit: https://codepen.io/keithclark/pen/yBcsr
2018-12-25 08:53:50 +11:00
felixlinker
a66fa8e83f Merge branch 'develop' 2018-12-05 20:32:55 +00:00
William Blythe
307886d4ae Merge branch 'develop' 2018-12-04 12:02:02 +11:00
William Blythe
3987c4e681 maybe an actually good service worker for once 2018-11-13 09:23:29 +11:00
William Blythe
e129e1da39 work on sw 2018-11-07 13:08:35 +11:00
William Blythe
8acd32b0fc Merge branch 'develop' 2018-11-05 11:59:25 +11:00
William Blythe
f8f99a5aaa Merge branch 'develop' 2018-11-01 10:12:02 +11:00
William Blythe
70cfa58896 remove halloween 2018-11-01 07:30:07 +11:00
William Blythe
56571f9c1f halloween 2018-10-31 09:20:35 +11:00
William Blythe
4ab376d9ed Fix/no adsense (#412) 2018-10-24 09:08:13 +11:00
William Blythe
2858ef3e93 Merge branch 'develop' 2018-10-23 13:03:58 +11:00
William Blythe
3215b3942d fix sw not registered 2018-10-23 11:56:33 +11:00
44 changed files with 967 additions and 315 deletions

13
.gitlab-ci.yml Normal file
View File

@@ -0,0 +1,13 @@
image: docker:stable
services:
- docker:dind
stages:
- Build image
docker build:
stage: Build image
script:
- img build --build-arg branch=$CI_COMMIT_REF_NAME -t edcd/coriolis:$CI_COMMIT_REF_NAME .
- echo "$REGISTRY_PASSWORD" | img login --username "$REGISTRY_USER" --password-stdin
- img push edcd/coriolis:$CI_COMMIT_REF_NAME

View File

@@ -21,6 +21,7 @@ RUN npm start
# Set up coriolis
WORKDIR /src/app/coriolis
RUN git checkout ${BRANCH}
RUN npm install --no-package-lock
RUN npm run build

24
LICENSE.md Normal file
View File

@@ -0,0 +1,24 @@
All Data and [associated JSON](https://github.com/EDCD/coriolis-data) files are intellectual property and copyright of Frontier Developments plc ('Frontier', 'Frontier Developments') and are subject to their
[terms and conditions](https://www.frontierstore.net/terms-and-conditions/).
The code (Javascript, CSS, HTML, and SVG files only) specificially for Coriolis.io is released under the MIT License.
Copyright (c) 2015 Coriolis.io, Colin McLeod
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software (Javascript, CSS, HTML, and SVG files only), and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -29,6 +29,8 @@ Also see [the documentation site.](https://coriolis.willb.info/)
See the [Data wiki](https://github.com/cmmcleod/coriolis-data/wiki) for details on structure, etc.
You can find hosted and compiled versions of these data-jsons under https://coriolis.io/data/ and https://beta.coriolis.io/data/.
You might want to load these as depedency instead of reyling on the npm-dependency.
## License

View File

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

View File

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

View File

@@ -78,7 +78,11 @@ const GRPCAT = {
// Mining
'scl': 'mining',
'pwa': 'mining',
'sdm': 'mining'
'sdm': 'mining',
// Assists
'dc': 'flight assists',
'sua': 'flight assists',
};
// Order here is the order in which items will be shown in the modules menu
const CATEGORIES = {
@@ -93,7 +97,8 @@ const CATEGORIES = {
'rf': ['rf'],
'shields': ['sg', 'bsg', 'psg', 'scb'],
'structural reinforcement': ['hr', 'mrp'],
'dc': ['dc'],
'flight assists': ['dc', 'sua'],
// Hardpoints
'lasers': ['pl', 'ul', 'bl'],
'projectiles': ['mc', 'c', 'fc', 'pa', 'rg'],
@@ -121,7 +126,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
onSelect: PropTypes.func.isRequired,
diffDetails: PropTypes.func,
m: PropTypes.object,
shipMass: PropTypes.number,
ship: PropTypes.object.isRequired,
warning: PropTypes.func,
firstSlotId: PropTypes.string,
lastSlotId: PropTypes.string,
@@ -129,10 +134,6 @@ export default class AvailableModulesMenu extends TranslatedComponent {
slotDiv: PropTypes.object
};
static defaultProps = {
shipMass: 0
};
/**
* Constructor
* @param {Object} props React Component properties
@@ -154,15 +155,15 @@ export default class AvailableModulesMenu extends TranslatedComponent {
*/
_initState(props, context) {
let translate = context.language.translate;
let { m, warning, shipMass, onSelect, modules, firstSlotId, lastSlotId } = props;
let { m, warning, onSelect, modules, ship } = props;
let list, currentGroup;
let buildGroup = this._buildGroup.bind(
this,
ship,
translate,
m,
warning,
shipMass - (m && m.mass ? m.mass : 0),
(m, event) => {
this._hideDiff(event);
onSelect(m);
@@ -250,18 +251,16 @@ export default class AvailableModulesMenu extends TranslatedComponent {
/**
* Generate React Components for Module Group
* @param {Ship} ship Ship the selection is for
* @param {Function} translate Translate function
* @param {Object} mountedModule Mounted Module
* @param {Function} warningFunc Warning function
* @param {number} mass Mass
* @param {function} onSelect Select/Mount callback
* @param {string} grp Group name
* @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
*/
_buildGroup(translate, mountedModule, warningFunc, mass, onSelect, grp, modules, firstSlotId, lastSlotId) {
_buildGroup(ship, translate, mountedModule, warningFunc, onSelect, grp, modules) {
let prevClass = null, prevRating = null, prevName;
let elems = [];
@@ -282,10 +281,11 @@ export default class AvailableModulesMenu extends TranslatedComponent {
prevName = m.name;
if (ModuleUtils.isShieldGenerator(m.grp)) {
// Shield generators care about maximum hull mass
disabled = mass > m.maxmass;
} else if (m.maxmass) {
// Thrusters care about total mass
disabled = mass + m.mass > m.maxmass;
disabled = ship.hullMass > m.maxmass;
// If the mounted module is experimental as well, we can replace it so
// the maximum does not apply
} else if (m.experimental && (!mountedModule || !mountedModule.experimental)) {
disabled = 4 <= ship.hardpoints.filter(o => o.m && o.m.experimental).length;
}
let active = mountedModule && mountedModule.id === m.id;
let classes = cn(m.name ? 'lc' : 'c', {

View File

@@ -52,12 +52,12 @@ export default class Defence extends TranslatedComponent {
* @return {React.Component} contents
*/
render() {
const { ship, sys, opponentWep } = this.props;
const { opponent, sys, opponentWep } = this.props;
const { language, tooltip, termtip } = this.context;
const { formats, translate, units } = language;
const { shield, armour, shielddamage, armourdamage } = this.state;
const pd = ship.standard[4].m;
const pd = opponent.standard[4].m;
const shieldSourcesData = [];
const effectiveShieldData = [];

View File

@@ -104,10 +104,10 @@ export default class HardpointSlot extends Slot {
onMouseOut={tooltip.bind(null, null)}>{translate('shotdmg')}: {formats.round1(m.getDamage())}</div> : null}
{m.getEps() ? <div className={'l'} onMouseOver={termtip.bind(null, m.getClip() ? 'epsseps' : 'eps')}
onMouseOut={tooltip.bind(null, null)}>{translate('EPS')}: {formats.round1(m.getEps())}{u.MW} {m.getClip() ?
<span>({formats.round1((m.getClip() * m.getEps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload()))}{u.MW})</span> : null}</div> : null}
<span>({formats.round1(m.getEps() * m.getSustainedFactor())}{u.MW})</span> : null}</div> : null}
{m.getHps() ? <div className={'l'} onMouseOver={termtip.bind(null, m.getClip() ? 'hpsshps' : 'hps')}
onMouseOut={tooltip.bind(null, null)}>{translate('HPS')}: {formats.round1(m.getHps())} {m.getClip() ?
<span>({formats.round1((m.getClip() * m.getHps() / m.getRoF()) / ((m.getClip() / m.getRoF()) + m.getReload()))})</span> : null}</div> : null}
<span>({formats.round1(m.getHps() * m.getSustainedFactor())})</span> : null}</div> : null}
{m.getDps() && m.getEps() ? <div className={'l'} onMouseOver={termtip.bind(null, 'dpe')}
onMouseOut={tooltip.bind(null, null)}>{translate('DPE')}: {formats.f1(m.getDps() / m.getEps())}</div> : null}
{m.getRoF() ? <div className={'l'} onMouseOver={termtip.bind(null, 'rof')}
@@ -121,7 +121,7 @@ export default class HardpointSlot extends Slot {
{m.getShieldBoost() ? <div className={'l'}>+{formats.pct1(m.getShieldBoost())}</div> : null}
{m.getAmmo() ? <div
className={'l'}>{translate('ammunition')}: {formats.int(m.getClip())}/{formats.int(m.getAmmo())}</div> : null}
{m.getReload() ? <div className={'l'}>{translate('reload')}: {formats.round(m.getReload())}{u.s}</div> : null}
{m.getReload() ? <div className={'l'}>{translate('wep_reload')}: {formats.round(m.getReload())}{u.s}</div> : null}
{m.getShotSpeed() ? <div
className={'l'}>{translate('shotspeed')}: {formats.int(m.getShotSpeed())}{u.mps}</div> : null}
{m.getPiercing() ? <div className={'l'}>{translate('piercing')}: {formats.int(m.getPiercing())}</div> : null}

View File

@@ -149,14 +149,14 @@ export default class HardpointSlotSection extends SlotSection {
<ul>
<li className='lc' tabIndex='0' onClick={_fill.bind(this, 'pa', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['pa-F'] = smRef}>{translate('pa')}</li>
</ul>
<div className='select-group cap'>{translate('rg')}</div>
<ul>
<li className='lc' tabIndex='0' onClick={_fill.bind(this, 'rg', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['rg-F'] = smRef}>{translate('rg')}</li>
</ul>
<div className='select-group cap'>{translate('nl')}</div>
<ul>
<li className='lc' tabIndex='0' onClick={_fill.bind(this, 'nl', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['nl-F'] = smRef}>{translate('nl')}</li>
</ul>
<div className='select-group cap'>{translate('ggc')}</div>
<ul>
<li className='lc' tabIndex='0' onClick={_fill.bind(this, 'ggc', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['ggc-F'] = smRef}>{translate('ggc')}</li>
</ul>
<div className='select-group cap'>{translate('rfl')}</div>
<ul>
<li className='c' tabIndex='0' onClick={_fill.bind(this, 'rfl', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['rfl-F'] = smRef}><MountFixed className='lg'/></li>

View File

@@ -349,7 +349,7 @@ export default class Header extends TranslatedComponent {
_getShipsMenu() {
let shipList = [];
for (let s in Ships) {
for (let s of this.shipOrder) {
shipList.push(<ActiveLink key={s} href={outfitURL(s)} className='block'>{Ships[s].properties.name}</ActiveLink>);
}

View File

@@ -78,7 +78,6 @@ export default class InternalSlot extends Slot {
{ m.rangeLS ? <div className={'l'}>{translate('range')}: {m.rangeLS}{u.Ls}</div> : null }
{ m.rangeLS === null ? <div className={'l'}>{u.Ls}</div> : null }
{ m.rangeRating ? <div className={'l'}>{translate('range')}: {m.rangeRating}</div> : null }
{ m.maximum ? <div className={'l'}>{translate('max')}: {(m.maximum)}</div> : null }
{ m.passengers ? <div className={'l'}>{translate('passengers')}: {m.passengers}</div> : null }
{ m.getRegenerationRate() ? <div className='l'>{translate('regen')}: {formats.round1(m.getRegenerationRate())}{u.ps}</div> : null }
{ m.getBrokenRegenerationRate() ? <div className='l'>{translate('brokenregen')}: {formats.round1(m.getBrokenRegenerationRate())}{u.ps}</div> : null }

View File

@@ -13,6 +13,8 @@ import { Download } from './SvgIcons';
import { outfitURL } from '../utils/UrlGenerators';
import * as CompanionApiUtils from '../utils/CompanionApiUtils';
const zlib = require('pako');
const textBuildRegex = new RegExp('^\\[([\\w \\-]+)\\]\n');
const lineRegex = new RegExp('^([\\dA-Z]{1,2}): (\\d)([A-I])[/]?([FGT])?([SD])? ([\\w\\- ]+)');
const mountMap = { 'H': 4, 'L': 3, 'M': 2, 'S': 1, 'U': 0 };
@@ -99,6 +101,7 @@ export default class ModalImport extends TranslatedComponent {
this.state = {
builds: props.builds,
canEdit: !props.builds,
loadoutEvent: null,
comparisons: null,
shipDiscount: null,
moduleDiscount: null,
@@ -111,12 +114,28 @@ export default class ModalImport extends TranslatedComponent {
this._process = this._process.bind(this);
this._import = this._import.bind(this);
this._importBackup = this._importBackup.bind(this);
this._importLoadout = this._importLoadout.bind(this);
this._importDetailedArray = this._importDetailedArray.bind(this);
this._importTextBuild = this._importTextBuild.bind(this);
this._importCompanionApiBuild = this._importCompanionApiBuild.bind(this);
this._validateImport = this._validateImport.bind(this);
}
/**
* Import a Loadout event from Elite: Dangerous journal files
* @param {Object} data Loadout event
* @throws {string} If import fails
*/
_importLoadout(data) {
if (data && data.Ship && data.Modules) {
const deflated = zlib.deflate(JSON.stringify(data), { to: 'string' });
let compressed = btoa(deflated);
this.setState({loadoutEvent: compressed});
} else {
throw 'Loadout event must contain Ship and Modules';
}
}
/**
* Import a Coriolis backup
* @param {Object} importData Backup Data
@@ -159,7 +178,7 @@ export default class ModalImport extends TranslatedComponent {
}
// Check for module discount
if (!isNaN(importData.moduleDiscount)) {
this.setState({ shipDiscount: importData.moduleDiscount * 1 });
this.setState({ moduleDiscount: importData.moduleDiscount * 1 });
}
if (typeof importData.insurance == 'string') {
@@ -345,12 +364,14 @@ export default class ModalImport extends TranslatedComponent {
} else if (importData.ship && typeof importData.name !== undefined) { // Using JSON from a single ship build export
this._importDetailedArray([importData]); // Convert to array with singleobject
this.setState({ singleBuild: true });
} else if (importData.Modules != null && importData.Modules[0] != null) {
this._importLoadout(importData);
} else { // Using Backup JSON
this._importBackup(importData);
}
}
} catch (e) {
// console.log(e.stack);
console.log(e);
this.setState({ errorMsg: (typeof e == 'string') ? e : 'Cannot Parse the data!' });
return;
}
@@ -364,6 +385,10 @@ export default class ModalImport extends TranslatedComponent {
_process() {
let builds = null, comparisons = null;
if (this.state.loadoutEvent) {
return Router.go(`/import?data=${this.state.loadoutEvent}`);
}
// If only importing a single build go straight to the outfitting page
if (this.state.singleBuild) {
builds = this.state.builds;
@@ -480,7 +505,7 @@ export default class ModalImport extends TranslatedComponent {
if (!state.processed) {
importStage = (
<div>
<textarea className='cb json' ref={node => this.importField = node} onChange={this._validateImport} defaultValue={this.state.importString} placeholder={translate('PHRASE_IMPORT')} />
<textarea spellCheck={false} className='cb json' ref={node => this.importField = node} onChange={this._validateImport} defaultValue={this.state.importString} placeholder={translate('PHRASE_IMPORT')} />
<button id='proceed' className='l cap' onClick={this._process} disabled={!state.importValid} >{translate('proceed')}</button>
<div className='l warning' style={{ marginLeft:'3em' }}>{state.errorMsg}</div>
</div>

View File

@@ -109,17 +109,18 @@ export default class ModalShoppingList extends TranslatedComponent {
*/
sendToEDEng(event) {
event.preventDefault();
let translate = this.context.language.translate;
const target = event.target;
target.disabled = this.state.blueprints.length > 0;
if (this.state.blueprints.length === 0) {
target.innerText = 'No modded components.';
target.innerText = translate('No modded components.');
target.disabled = true;
setTimeout(() => {
target.innerText = 'Send to EDEngineer';
target.innerText = translate('Send to EDEngineer');
target.disabled = false;
}, 3000);
} else {
target.innerText = 'Sending...';
target.innerText = translate('Sending...');
}
let countSent = 0;
let countTotal = this.state.blueprints.length;
@@ -139,7 +140,7 @@ export default class ModalShoppingList extends TranslatedComponent {
countSent++;
if (countSent === countTotal) {
target.disabled = false;
target.innerText = 'Send to EDEngineer';
target.innerText = translate('Send to EDEngineer');
}
});
}
@@ -230,32 +231,32 @@ export default class ModalShoppingList extends TranslatedComponent {
this.sendToEDEng = this.sendToEDEng.bind(this);
return <div className='modal' onClick={ (e) => e.stopPropagation() }>
<h2>{translate('PHRASE_SHOPPING_MATS')}</h2>
<label>Grade 1 rolls </label>
<label>{translate('Grade 1 rolls ')}</label>
<input id={1} type={'number'} min={0} defaultValue={this.state.matsPerGrade[1]} onChange={this.changeHandler} />
<br/>
<label>Grade 2 rolls </label>
<label>{translate('Grade 2 rolls ')}</label>
<input id={2} type={'number'} min={0} defaultValue={this.state.matsPerGrade[2]} onChange={this.changeHandler} />
<br/>
<label>Grade 3 rolls </label>
<label>{translate('Grade 3 rolls ')}</label>
<input id={3} type={'number'} min={0} value={this.state.matsPerGrade[3]} onChange={this.changeHandler} />
<br/>
<label>Grade 4 rolls </label>
<label>{translate('Grade 4 rolls ')}</label>
<input id={4} type={'number'} min={0} value={this.state.matsPerGrade[4]} onChange={this.changeHandler} />
<br/>
<label>Grade 5 rolls </label>
<label>{translate('Grade 5 rolls ')}</label>
<input id={5} type={'number'} min={0} value={this.state.matsPerGrade[5]} onChange={this.changeHandler} />
<div>
<textarea className='cb json' readOnly value={this.state.matsList} />
</div>
<label hidden={!compatible} className={'l cap'}>CMDR Name </label>
<label hidden={!compatible} className={'l cap'}>{translate('CMDR Name')}</label>
<br/>
<select hidden={!compatible} className={'cmdr-select l cap'} onChange={this.cmdrChangeHandler} defaultValue={this.state.cmdrName}>
{this.state.cmdrs.map(e => <option key={e}>{e}</option>)}
</select>
<br/>
<p hidden={!this.state.failed} id={'failed'} className={'l'}>Failed to send to EDEngineer (Launch EDEngineer and make sure the API is started then refresh the page.)</p>
<p hidden={compatible} id={'browserbad'} className={'l'}>Sending to EDEngineer is not compatible with Firefox's security settings. Please try again with Chrome.</p>
<button className={'l cb dismiss cap'} disabled={!!this.state.failed || !compatible} onClick={this.sendToEDEng}>{translate('Send To EDEngineer')}</button>
<p hidden={!this.state.failed} id={'failed'} className={'l'}>{translate('PHRASE_FAIL_EDENGINEER')}</p>
<p hidden={compatible} id={'browserbad'} className={'l'}>{translate('PHRASE_FIREFOX_EDENGINEER')}</p>
<button className={'l cb dismiss cap'} disabled={!!this.state.failed || !compatible} onClick={this.sendToEDEng}>{translate('Send to EDEngineer')}</button>
<button className={'r dismiss cap'} onClick={this.context.hideModal}>{translate('close')}</button>
</div>;
}

View File

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

View File

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

View File

@@ -174,7 +174,7 @@ export default class OutfittingSubpages extends TranslatedComponent {
<th style={{ width:'25%' }} className={cn({ active: tab == 'power' })} onClick={this._showTab.bind(this, 'power')} >{translate('power and costs')}</th>
<th style={{ width:'25%' }} className={cn({ active: tab == 'profiles' })} onClick={this._showTab.bind(this, 'profiles')} >{translate('profiles')}</th>
<th style={{ width:'25%' }} className={cn({ active: tab == 'offence' })} onClick={this._showTab.bind(this, 'offence')} >{translate('offence')}</th>
<th style={{ width:'25%' }} className={cn({ active: tab == 'defence' })} onClick={this._showTab.bind(this, 'defence')} >{translate('defence')}</th>
<th style={{ width:'25%' }} className={cn({ active: tab == 'defence' })} onClick={this._showTab.bind(this, 'defence')} >{translate('tab_defence')}</th>
</tr>
</thead>
</table>

View File

@@ -50,6 +50,7 @@ export default class ShipSummaryTable extends TranslatedComponent {
const speedTooltip = canThrust ? 'TT_SUMMARY_SPEED' : 'TT_SUMMARY_SPEED_NONFUNCTIONAL';
const canBoost = ship.canBoost(cargo, ship.fuelCapacity);
const boostTooltip = canBoost ? 'TT_SUMMARY_BOOST' : canThrust ? 'TT_SUMMARY_BOOST_NONFUNCTIONAL' : 'TT_SUMMARY_SPEED_NONFUNCTIONAL';
const canJump = ship.getSlotStatus(ship.standard[2]) == 3;
const sgMetrics = Calc.shieldMetrics(ship, pips.sys);
const shipBoost = canBoost ? Calc.calcBoost(ship) : 'No Boost';
const restingHeat = Math.sqrt(((ship.standard[0].m.pgen * ship.standard[0].m.eff) / ship.heatCapacity) / 0.2);
@@ -71,7 +72,7 @@ export default class ShipSummaryTable extends TranslatedComponent {
<tr className='main'>
<th rowSpan={2} className={ cn({ 'bg-warning-disabled': !canThrust }) }>{translate('speed')}</th>
<th rowSpan={2} className={ cn({ 'bg-warning-disabled': !canBoost }) }>{translate('boost')}</th>
<th colSpan={5}>{translate('jump range')}</th>
<th colSpan={5} className={ cn({ 'bg-warning-disabled': !canJump }) }>{translate('jump range')}</th>
<th rowSpan={2}>{translate('shield')}</th>
<th rowSpan={2}>{translate('integrity')}</th>
<th rowSpan={2}>{translate('DPS')}</th>
@@ -85,15 +86,15 @@ export default class ShipSummaryTable extends TranslatedComponent {
<th onMouseEnter={termtip.bind(null, 'hull hardness', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('hrd')}</th>
<th 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, 'TT_SUMMARY_BOOST_TIME', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('boost time')}</th>
<th onMouseEnter={termtip.bind(null, 'TT_SUMMARY_BOOST_INTERVAL', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('boost interval')}</th>
<th rowSpan={2}>{translate('resting heat (Beta)')}</th>
</tr>
<tr>
<th className='lft'>{translate('max')}</th>
<th>{translate('unladen')}</th>
<th>{translate('laden')}</th>
<th>{translate('total unladen')}</th>
<th>{translate('total laden')}</th>
<th className={ cn({ 'lft': true, 'bg-warning-disabled': !canJump }) }>{translate('max')}</th>
<th className={ cn({ 'bg-warning-disabled': !canJump }) }>{translate('unladen')}</th>
<th className={ cn({ 'bg-warning-disabled': !canJump }) }>{translate('laden')}</th>
<th className={ cn({ 'bg-warning-disabled': !canJump }) }>{translate('total unladen')}</th>
<th className={ cn({ 'bg-warning-disabled': !canJump }) }>{translate('total laden')}</th>
<th className='lft'>{translate('hull')}</th>
<th>{translate('unladen')}</th>
<th>{translate('laden')}</th>
@@ -103,11 +104,11 @@ export default class ShipSummaryTable extends TranslatedComponent {
<tr>
<td onMouseEnter={termtip.bind(null, speedTooltip, { cap: 0 })} onMouseLeave={hide}>{ canThrust ? <span>{int(ship.calcSpeed(4, ship.fuelCapacity, 0, false))}{u['m/s']}</span> : <span className='warning'>0 <Warning/></span> }</td>
<td onMouseEnter={termtip.bind(null, boostTooltip, { cap: 0 })} onMouseLeave={hide}>{ canBoost ? <span>{int(ship.calcSpeed(4, ship.fuelCapacity, 0, true))}{u['m/s']}</span> : <span className='warning'>0 <Warning/></span> }</td>
<td><span onMouseEnter={termtip.bind(null, 'TT_SUMMARY_MAX_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{f2(Calc.jumpRange(ship.unladenMass + ship.standard[2].m.getMaxFuelPerJump(), ship.standard[2].m, ship.standard[2].m.getMaxFuelPerJump(), ship))}{u.LY}</span></td>
<td><span onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{f2(Calc.jumpRange(ship.unladenMass + ship.fuelCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</span></td>
<td><span onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{f2(Calc.jumpRange(ship.unladenMass + ship.fuelCapacity + ship.cargoCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</span></td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_TOTAL_JUMP', { cap: 0 })} onMouseLeave={hide}>{f2(Calc.totalJumpRange(ship.unladenMass + ship.fuelCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_TOTAL_JUMP', { cap: 0 })} onMouseLeave={hide}>{f2(Calc.totalJumpRange(ship.unladenMass + ship.fuelCapacity + ship.cargoCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_MAX_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{ canJump ? <span>{ f2(Calc.jumpRange(ship.unladenMass + ship.standard[2].m.getMaxFuelPerJump(), ship.standard[2].m, ship.standard[2].m.getMaxFuelPerJump(), ship))}{u.LY}</span> : <span className='warning'>0 <Warning/></span> }</td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{ canJump ? <span>{f2(Calc.jumpRange(ship.unladenMass + ship.fuelCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</span> : <span className='warning'>0 <Warning/></span> }</td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_SINGLE_JUMP', { cap: 0 })} onMouseLeave={hide}>{ canJump ? <span>{f2(Calc.jumpRange(ship.unladenMass + ship.fuelCapacity + ship.cargoCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</span> : <span className='warning'>0 <Warning/></span> }</td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_TOTAL_JUMP', { cap: 0 })} onMouseLeave={hide}>{ canJump ? <span>{f2(Calc.totalJumpRange(ship.unladenMass + ship.fuelCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</span> : <span className='warning'>0 <Warning/></span> }</td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_LADEN_TOTAL_JUMP', { cap: 0 })} onMouseLeave={hide}>{ canJump ? <span>{f2(Calc.totalJumpRange(ship.unladenMass + ship.fuelCapacity + ship.cargoCapacity, ship.standard[2].m, ship.fuelCapacity, ship))}{u.LY}</span> : <span className='warning'>0 <Warning/></span> }</td>
<td className={sgClassNames} onMouseEnter={termtip.bind(null, sgTooltip, { cap: 0 })} onMouseLeave={hide}>{int(ship.shield)}{u.MJ}</td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_INTEGRITY', { cap: 0 })} onMouseLeave={hide}>{int(ship.armour)}</td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_DPS', { cap: 0 })} onMouseLeave={hide}>{f1(ship.totalDps)}</td>
@@ -159,10 +160,10 @@ export default class ShipSummaryTable extends TranslatedComponent {
<td>{formats.pct1(ship.shieldThermRes)}</td>
<td></td>
<td>{int(ship && ship.shield > 0 ? ship.shield : 0)}{u.MJ}</td>
<td>{int(ship && ship.shield > 0 ? ship.shield * ((1 / (1 - (ship.shieldExplRes)))) : 0)}{u.MJ}</td>
<td>{int(ship && ship.shield > 0 ? ship.shield * ((1 / (1 - (ship.shieldKinRes)))) : 0)}{u.MJ}</td>
<td>{int(ship && ship.shield > 0 ? ship.shield * ((1 / (1 - (ship.shieldThermRes)))) : 0)}{u.MJ}</td>
<td>{int(ship && sgMetrics.summary > 0 ? sgMetrics.summary : 0)}{u.MJ}</td>
<td>{int(ship && sgMetrics.summary > 0 ? sgMetrics.summary / sgMetrics.explosive.base : 0)}{u.MJ}</td>
<td>{int(ship && sgMetrics.summary ? sgMetrics.summary / sgMetrics.kinetic.base : 0)}{u.MJ}</td>
<td>{int(ship && sgMetrics.summary ? sgMetrics.summary / sgMetrics.thermal.base : 0)}{u.MJ}</td>
<td></td>
<td>{sgMetrics && sgMetrics.recover === Math.Inf ? translate('Never') : formats.time(sgMetrics.recover)}</td>
<td>{sgMetrics && sgMetrics.recharge === Math.Inf ? translate('Never') : formats.time(sgMetrics.recharge)}</td>
@@ -197,11 +198,11 @@ export default class ShipSummaryTable extends TranslatedComponent {
<td>{formats.pct1(ship.hullKinRes)}</td>
<td>{formats.pct1(ship.hullThermRes)}</td>
<td>{formats.pct1(ship.hullCausRes)}</td>
<td>{int(ship.armour)}</td>
<td>{int(ship.armour * ((1 / (1 - (ship.hullExplRes)))))}</td>
<td>{int(ship.armour * ((1 / (1 - (ship.hullKinRes)))))}</td>
<td>{int(ship.armour * ((1 / (1 - (ship.hullThermRes)))))}</td>
<td>{int(ship.armour * ((1 / (1 - (ship.hullCausRes)))))}</td>
<td>{int(armourMetrics.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.total/ armourMetrics.caustic.total)}</td>
<td>{int(armourMetrics.modulearmour)}</td>
<td>{int(armourMetrics.moduleprotection * 100) + '%'}</td>

View File

@@ -127,8 +127,8 @@ export default class Slot extends TranslatedComponent {
menu = <AvailableModulesMenu
className={this._getClassNames()}
modules={availableModules()}
shipMass={ship.hullMass}
m={m}
ship={ship}
onSelect={onSelect}
warning={warning}
diffDetails={diffDetails.bind(ship, this.context.language)}

View File

@@ -195,6 +195,15 @@ export default class SlotSection extends TranslatedComponent {
if (targetSlot && canMount(this.props.ship, targetSlot, m.grp, m.class)) {
const mCopy = m.clone();
this.props.ship.use(targetSlot, mCopy, false);
let experimentalNum = this.props.ship.hardpoints
.filter(s => s.m && s.m.experimental).length;
// Remove the module on the last slot if we now exceed the number of
// experimentals allowed
if (m.experimental && 4 < experimentalNum) {
this.props.ship.updateStats(originSlot, null, originSlot.m);
originSlot.m = null; // Empty the slot
originSlot.discountedCost = 0;
}
// Copy power info
targetSlot.enabled = originSlot.enabled;
targetSlot.priority = originSlot.priority;

View File

@@ -109,8 +109,8 @@ export default class StandardSlot extends TranslatedComponent {
menu = <AvailableModulesMenu
className='standard'
modules={modules}
shipMass={ModuleUtils.isShieldGenerator(m.grp) ? ship.hullMass : ship.unladenMass}
m={m}
ship={ship}
onSelect={onSelect}
warning={warning}
diffDetails={diffDetails.bind(ship, this.context.language)}

View File

@@ -7,6 +7,7 @@ import * as IT from './it';
import * as RU from './ru';
import * as PL from './pl';
import * as PT from './pt';
import * as CN from './cn';
import * as d3 from 'd3';
let fallbackTerms = EN.terms;
@@ -27,6 +28,7 @@ export function getLanguage(langCode) {
case 'ru': lang = RU; break;
case 'pl': lang = PL; break;
case 'pt': lang = PT; break;
case 'cn': lang = CN; break;
default:
lang = EN;
}
@@ -94,5 +96,6 @@ export const Languages = {
fr: 'Français',
ru: 'ру́сский',
pl: 'polski',
pt: 'português'
pt: 'português',
cn: '中文'
};

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

@@ -0,0 +1,16 @@
export const formats = {
decimal: '.',
thousands: ',',
grouping: [3],
currency: ['¥', ''],
dateTime: '%a %b %e %X %Y',
date: '%Y年%m月%d日',
time: '%H:%M:%S',
periods: ['AM', 'PM'],
days: ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'],
shortDays: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
months: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
shortMonths: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月']
};
export { default as terms } from './cn.json';

405
src/app/i18n/cn.json Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -63,7 +63,7 @@
"TT_SUMMARY_SPEED": "With full fuel tank and 4 pips to ENG",
"TT_SUMMARY_SPEED_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_TIME": "Time between each boost with 4 pips to ENG",
"TT_SUMMARY_BOOST_INTERVAL": "Time between each boost with 4 pips to ENG",
"TT_SUMMARY_BOOST_NONFUNCTIONAL": "Power distributor not able to supply enough power to boost",
"TT_SUMMARY_SHIELDS": "Raw shield strength, including boosters",
"TT_SUMMARY_SHIELDS_SCB": "Raw shield strength, including boosters and SCBs",
@@ -81,6 +81,8 @@
"TT_SUMMARY_UNLADEN_TOTAL_JUMP": "Farthest possible range with no cargo, a full fuel tank, and jumping as far as possible each time",
"TT_SUMMARY_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",
"PHRASE_FAIL_EDENGINEER": "Failed to send to EDEngineer (Launch EDEngineer and make sure the API is started then refresh the page.)",
"PHRASE_FIREFOX_EDENGINEER": "Sending to EDEngineer is not compatible with Firefox's security settings. Please try again with Chrome.",
"am": "Auto Field-Maintenance Unit",
"bh": "Bulkheads",
"bl": "Beam Laser",
@@ -151,6 +153,7 @@
"sfn": "Shutdown Field Neutraliser",
"sg": "Shield Generator",
"ss": "Surface Scanners",
"sua": "Supercruise Assist",
"t": "thrusters",
"tp": "Torpedo Pylon",
"ul": "Burst Laser",
@@ -172,6 +175,7 @@
"ammunition": "Ammo",
"secs": "s",
"rebuildsperbay": "Rebuilds per bay",
"mroll": "Roll",
"worst": "Worst",
"average": "Average",
"random": "Random",
@@ -201,7 +205,7 @@
"internal protection": "Internal protection",
"external protection": "External protection",
"engagement range": "Engagement range",
"boost time": "Boost time",
"boost interval": "Boost intervall",
"total": "Total",
"ammo": "Ammunition maximum",
"boot": "Boot time",
@@ -320,6 +324,7 @@
"never": "never",
"stock": "stock",
"boost": "boost",
"tab_defence": "defence",
"federation rank 1": "Recruit",
"federation rank 2": "Cadet",
"federation rank 3": "Midshipman",

View File

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

View File

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

View File

@@ -50,19 +50,6 @@ export default class Page extends React.Component {
}
}
/**
* Pages are 'pure' components that only render when props, state, or context changes.
* This method performs a shallow comparison to determine change.
*
* @param {Object} np Next/Incoming properties
* @param {Object} ns Next/Incoming state
* @param {Object} nc Next/Incoming context
* @return {Boolean} True if props, state, or context has changed
*/
shouldComponentUpdate(np, ns, nc) {
return !shallowEqual(this.props, np) || !shallowEqual(this.state, ns) || !shallowEqual(this.context, nc);
}
/**
* Update the window title upon mount
*/

View File

@@ -126,12 +126,14 @@ export default class ShipyardPage extends Page {
title: 'Coriolis EDCD Edition - Shipyard',
shipPredicate: 'name',
shipDesc: true,
shipSummaries: ShipyardPage.cachedShipSummaries
shipSummaries: ShipyardPage.cachedShipSummaries,
compare: {},
groupCompared: false,
};
}
/**
* Higlight the current ship in the table
* Higlight the current ship in the table on mouse over
* @param {String} shipId Ship Id
* @param {SyntheticEvent} event Event
*/
@@ -140,6 +142,24 @@ export default class ShipyardPage extends Page {
this.setState({ shipId });
}
/**
* Toggle compare highlighting for ships in the table
* @param {String} shipId Ship Id
*/
_toggleCompare(shipId) {
let compare = this.state.compare;
compare[shipId] = !compare[shipId];
this.setState({ compare });
}
/**
* Toggle grouping of compared ships in the table
* @private
*/
_toggleGroupCompared() {
this.setState({groupCompared: !this.state.groupCompared})
}
/**
* Update state with the specified sort predicates
* @param {String} shipPredicate Sort predicate - property name
@@ -180,8 +200,10 @@ export default class ShipyardPage extends Page {
style={{ height: '1.5em' }}
className={cn({
highlighted: noTouch && this.state.shipId === s.id,
comparehighlight: this.state.compare[s.id],
})}
onMouseEnter={noTouch && this._highlightShip.bind(this, s.id)}
onClick={() => this._toggleCompare(s.id)}
>
<td className="ri">{s.manufacturer}</td>
<td className="ri">{fInt(s.retailCost)}</td>
@@ -234,7 +256,7 @@ export default class ShipyardPage extends Page {
let hide = this.context.tooltip.bind(null, null);
let fInt = formats.int;
let fRound = formats.round;
let { shipSummaries, shipPredicate, shipPredicateIndex } = this.state;
let { shipSummaries, shipPredicate, shipPredicateIndex, compare, groupCompared } = this.state;
let sortShips = (predicate, index) =>
this._sortShips.bind(this, predicate, index);
@@ -267,6 +289,15 @@ export default class ShipyardPage extends Page {
valB = val;
}
if (groupCompared) {
if (compare[a.id] && !compare[b.id]) {
return -1;
}
if (!compare[a.id] && compare[b.id]) {
return 1;
}
}
if (valA == valB) {
if (a.name > b.name) {
return 1;
@@ -298,8 +329,10 @@ export default class ShipyardPage extends Page {
style={{ height: '1.5em' }}
className={cn({
highlighted: noTouch && this.state.shipId === s.id,
comparehighlight: this.state.compare[s.id],
})}
onMouseEnter={noTouch && this._highlightShip.bind(this, s.id)}
onClick={() => this._toggleCompare(s.id)}
>
<td className="le">
<Link href={'/outfit/' + s.id}>{s.name} {s.beta === true ? '(Beta)' : null}</Link>
@@ -310,18 +343,10 @@ export default class ShipyardPage extends Page {
}
return (
<div className="page" style={{ fontSize: sizeRatio + 'em' }}>
<div
style={{
whiteSpace: 'nowrap',
margin: '0 auto',
fontSize: '0.8em',
position: 'relative',
display: 'inline-block',
maxWidth: '100%'
}}
>
<table style={{ width: '12em', position: 'absolute', zIndex: 1 }}>
<div className="page" style={{fontSize: sizeRatio + 'em'}}>
<div className="content-wrapper">
<div className="shipyard-table-wrapper">
<table style={{width: '12em', position: 'absolute', zIndex: 1}} className="shipyard-table">
<thead>
<tr>
<th className="le rgt">&nbsp;</th>
@@ -339,8 +364,8 @@ export default class ShipyardPage extends Page {
{shipRows}
</tbody>
</table>
<div style={{ overflowX: 'scroll', maxWidth: '100%' }}>
<table style={{ marginLeft: 'calc(12em - 1px)', zIndex: 0 }}>
<div style={{ overflowX: 'auto', maxWidth: '100%' }}>
<table style={{ marginLeft: 'calc(12em - 1px)', zIndex: 0 }} className="shipyard-table">
<thead>
<tr className="main">
<th
@@ -597,6 +622,10 @@ export default class ShipyardPage extends Page {
</table>
</div>
</div>
<div className="table-tools" >
<label><input type="checkbox" checked={this.state.groupCompared} onClick={() => this._toggleGroupCompared()}/>Group highlighted ships</label>
</div>
</div>
</div>
);
}

View File

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

View File

@@ -57,6 +57,7 @@ export const ModuleGroupToName = {
ghrp: 'Guardian Hull Reinforcement Package',
gmrp: 'Guardian Module Reinforcement Package',
mahr: 'Meta Alloy Hull Reinforcement Package',
sua: 'Supercruise Assist',
// Hard Points
bl: 'Beam Laser',
@@ -206,7 +207,7 @@ export const ShipFacets = [
i: 9
},
{ // 10
title: 'fastest range',
title: 'farthest range',
props: ['unladenFastestRange', 'ladenFastestRange'],
lbls: ['unladen', 'laden'],
unit: 'LY',

View File

@@ -46,7 +46,7 @@ export default class Module {
if ((!raw) && this.blueprint && this.blueprint.special) {
// This module has a special effect, see if we need to alter our returned value
const modifierActions = Modifications.modifierActions[this.blueprint.special.edname];
const modifierActions = Modifications.modifierActions[this.blueprint.special.key];
if (modifierActions && modifierActions[name]) {
// this special effect modifies our returned value
const modification = Modifications.modifications[name];
@@ -59,14 +59,7 @@ export default class Module {
} else if (modification.method === 'overwrite') {
result = modifierActions[name];
} else {
// rate of fire is special, as it's really burst interval. Handle that here
let mod = null;
if (name === 'rof') {
mod = 1 / (1 + modifierActions[name]) - 1;
} else {
mod = modifierActions[name];
}
result = (((1 + result / multiplier) * (1 + mod)) - 1) * multiplier;
result = (((1 + result / multiplier) * (1 + modifierActions[name])) - 1) * multiplier;
}
}
}
@@ -90,7 +83,7 @@ export default class Module {
}
if (valueiswithspecial && this.blueprint && this.blueprint.special) {
// This module has a special effect, see if we need to alter the stored value
const modifierActions = Modifications.modifierActions[this.blueprint.special.edname];
const modifierActions = Modifications.modifierActions[this.blueprint.special.key];
if (modifierActions && modifierActions[name]) {
// This special effect modifies the value being set, so we need to revert it prior to storing the value
const modification = Modifications.modifications[name];
@@ -105,14 +98,7 @@ export default class Module {
} else if (modification.method === 'overwrite') {
value = null;
} else {
// rate of fire is special, as it's really burst interval. Handle that here
let mod = null;
if (name === 'rof') {
mod = 1 / (1 + modifierActions[name]) - 1;
} else {
mod = modifierActions[name];
}
value = ((value / 10000 + 1) / (1 + mod) - 1) * 10000;
value = ((value / 10000 + 1) / (1 + modifierActions[name]) - 1) * 10000;
}
}
}
@@ -131,6 +117,13 @@ export default class Module {
* @return {Number} The value queried
*/
get(name, modified = true) {
if (name == 'rof' && isNaN(this[name])) {
let fireint = this['fireint'];
if (!isNaN(fireint)) {
this['rof'] = 1 / fireint;
}
}
let val;
if (modified) {
val = this._getModifiedValue(name);
@@ -166,14 +159,20 @@ export default class Module {
modValue = value - baseValue;
} else if (name === 'shieldboost' || name === 'hullboost') {
modValue = (1 + value) / (1 + baseValue) - 1;
} else if (name === 'rof') {
let burst = this.get('burst', true) || 1;
let burstInt = 1 / (this.get('burstrof', true) / 1);
let interval = burst / value;
let newFireint = (interval - (burst - 1) * burstInt);
modValue = newFireint / this['fireint'] - 1;
} else { // multiplicative
modValue = value / baseValue - 1;
modValue = baseValue == 0 ? 0 : value / baseValue - 1;
}
if (modification.type === 'percentage') {
modValue = modValue * 10000;
} else if (modification.type === 'numeric' && name !== 'burst' &&
name !== 'burstrof') {
} else if (modification.type === 'numeric') {
modValue = modValue * 100;
}
@@ -191,7 +190,14 @@ export default class Module {
*/
getPretty(name, modified = true, places = 2) {
const formattingOptions = STATS_FORMATTING[name];
let val = this.get(name, modified) || 0;
let val;
if (formattingOptions && formattingOptions.synthetic) {
val = (this[formattingOptions.synthetic]).call(this, modified);
} else {
val = this.get(name, modified);
}
val = val || 0;
if (formattingOptions && formattingOptions.format.startsWith('pct')) {
return 100 * val;
}
@@ -250,12 +256,17 @@ export default class Module {
} else if (name === 'shieldboost' || name === 'hullboost') {
result = (1 + result) * (1 + modValue) - 1;
} else {
// Rate of fire modifiers are special as they actually are modifiers
// for fire interval. Translate them accordingly here:
if (name == 'rof') {
modValue = 1 / (1 + modValue) - 1;
}
result = result * (1 + modValue);
}
} else if (name === 'burstrof') {
} else if (name === 'burstrof' || name === 'burst') {
// Burst and burst rate of fire are special, as it can not exist but
// have a modification
result = modValue / 100;
result = modValue;
}
}
}
@@ -277,11 +288,7 @@ export default class Module {
formatModifiedValue(name, language, unit, val) {
const formattingOptions = STATS_FORMATTING[name];
if (val === undefined) {
if (formattingOptions && formattingOptions.synthetic) {
val = (this[formattingOptions.synthetic]).call(this, true);
} else {
val = this._getModifiedValue(name);
}
val = this.getPretty(name, true);
}
val = val || 0;
@@ -351,14 +358,18 @@ export default class Module {
if (formattingOptions && formattingOptions.change) {
let changeFormatting = formattingOptions.change;
let baseVal = this[name];
let baseVal = this[name] || 0;
let absVal = this._getModifiedValue(name);
if (changeFormatting === 'additive') {
val = absVal - baseVal;
} else if (changeFormatting === 'multiplicative') {
val = absVal / baseVal - 1;
}
val *= 10000;
if (Modifications.modifications[name].method === 'overwrite') {
val *= 100;
} else {
val *= 10000;
}
}
return val;
}
@@ -572,20 +583,9 @@ export default class Module {
* @return {Number} the falloff of this module
*/
getFalloff(modified = true) {
if (!modified) {
const range = this.getRange(false);
const falloff = this.get('falloff', false);
return (falloff > range ? range : falloff);
}
// Falloff from range is mapped to range
if (this.mods && this.mods['fallofffromrange']) {
if (modified && this.mods && this.mods['fallofffromrange']) {
return this.getRange();
// Need to find out if we have a focused modification, in which case our
// falloff is scaled to range
} else if (this.blueprint && this.blueprint.name === 'Focused') {
const rangeMod = this.getModValue('range') / 10000;
return this.falloff * (1 + rangeMod);
// Standard falloff calculation
} else {
const range = this.getRange();
@@ -639,15 +639,6 @@ export default class Module {
return this.get('protection', modified);
}
/**
* Get the delay for this module
* @param {Boolean} [modified=true] Whether to take modifications into account
* @return {Number} the delay of this module
*/
getDelay(modified = true) {
return this.get('delay', modified);
}
/**
* Get the duration for this module
* @param {Boolean} [modified=true] Whether to take modifications into account
@@ -703,8 +694,7 @@ export default class Module {
let result = 0;
if (this['maxmass']) {
result = this['maxmass'];
// max mass is only modified for non-shield boosters
if (result && modified && this.grp !== 'sg') {
if (result && modified) {
let mult = this.getModValue('optmass') / 10000;
if (mult) { result = result * (1 + mult); }
}
@@ -793,24 +783,6 @@ export default class Module {
return this.get('distdraw', modified);
}
/**
* Get the thermal load for this module
* @param {Boolean} [modified=true] Whether to take modifications into account
* @return {Number} the thermal load of this module
*/
getThermalLoad(modified = true) {
return this.get('thermload', modified);
}
/**
* Get the rounds per shot for this module
* @param {Boolean} [modified=true] Whether to take modifications into account
* @return {Number} the rounds per shot of this module
*/
getRoundsPerShot(modified = true) {
return this.get('roundspershot', modified);
}
/**
* Get the DPS for this module
* @param {Boolean} [modified=true] Whether to take modifications into account
@@ -834,25 +806,39 @@ export default class Module {
return this.getDps(modified) / this.getEps(modified);
}
/**
* Return the factor that gets applied when calculating certain "sustained"
* values, e.g. `SDPS = this.getSustainedFactor() * DPS`.
* @param {Boolean} [modified=true] Whether to take modifications into account
*/
getSustainedFactor(modified = true) {
let clipSize = this.getClip(modified);
if (clipSize) {
// If auto-loader is applied, effective clip size will be nearly doubled
// as you get one reload for every two shots fired.
if (this.blueprint && this.blueprint.special && this.blueprint.special.key === 'special_auto_loader' && modified) {
clipSize += clipSize - 1;
}
let burstSize = this.get('burst', modified) || 1;
let rof = this.getRoF(modified);
// rof averages burstfire + pause until next interval but for sustained
// rof we need to take another burst without pause into account
let burstOverhead = (burstSize - 1) / (this.get('burstrof', modified) || 1);
let srof = clipSize / ((clipSize - burstSize) / rof + burstOverhead + this.getReload(modified));
return srof / rof;
} else {
return 1;
}
}
/**
* Get the SDPS for this module
* @param {Boolean} [modified=true] Whether to take modifications into account
* @return {Number} The SDPS of this module
*/
getSDps(modified = true) {
let dps = this.getDps(modified);
if (this.getClip(modified)) {
let clipSize = this.getClip(modified);
// If auto-loader is applied, effective clip size will be nearly doubled
// as you get one reload for every two shots fired.
if (this.blueprint && this.blueprint.special && this.blueprint.special.edname === 'special_auto_loader' && modified) {
clipSize += clipSize - 1;
}
let timeToDeplete = clipSize / this.getRoF(modified);
return dps * timeToDeplete / (timeToDeplete + this.getReload(modified));
} else {
return dps;
}
return this.getDps(modified) * this.getSustainedFactor(modified);
}
/**
@@ -876,7 +862,7 @@ export default class Module {
*/
getHps(modified = true) {
// HPS is a synthetic value
let heat = this.getThermalLoad(modified);
let heat = this.get('thermload', modified);
// We don't use rpshot here as dist draw is per combined shot
let rof = this.getRoF(modified) || 1;
@@ -913,24 +899,6 @@ export default class Module {
return this.get('reload', modified);
}
/**
* Get the burst size for this module
* @param {Boolean} [modified=true] Whether to take modifications into account
* @return {Number} the burst size of this module
*/
getBurst(modified = true) {
return this.get('burst', modified);
}
/**
* Get the burst rate of fire for this module
* @param {Boolean} [modified=true] Whether to take modifications into account
* @return {Number} the burst rate of fire of this module
*/
getBurstRoF(modified = true) {
return this.get('burstrof', modified);
}
/**
* Get the rate of fire for this module.
* The rate of fire is a combination value, and needs to take in to account
@@ -941,8 +909,8 @@ export default class Module {
* @return {Number} the rate of fire for this module
*/
getRoF(modified = true) {
const burst = this.getBurst(modified) || 1;
const burstRoF = this.getBurstRoF(modified) || 1;
const burst = this.get('burst', modified) || 1;
const burstRoF = this.get('burstrof', modified) || 1;
const intRoF = this.get('rof', modified);
return burst / (((burst - 1) / burstRoF) + 1 / intRoF);

View File

@@ -10,7 +10,7 @@ import { Ships, Modifications } from 'coriolis-data/dist';
import { chain } from 'lodash';
const zlib = require('zlib');
const UNIQUE_MODULES = ['psg', 'sg', 'bsg', 'rf', 'fs', 'fh', 'gfsb'];
const UNIQUE_MODULES = ['psg', 'sg', 'bsg', 'rf', 'fs', 'fh', 'gfsb', 'dc'];
// Constants for modifications struct
const SLOT_ID_DONE = -1;
@@ -1308,7 +1308,7 @@ export default class Ship {
let fsd = this.standard[2].m; // Frame Shift Drive;
let { unladenMass, fuelCapacity } = this;
this.unladenRange = this.calcUnladenRange(); // Includes fuel weight for jump
this.fullTankRange = Calc.jumpRange(unladenMass + fuelCapacity, fsd, this); // Full Tank
this.fullTankRange = Calc.jumpRange(unladenMass + fuelCapacity, fsd, fuelCapacity, this); // Full Tank
this.ladenRange = this.calcLadenRange(); // Includes full tank and caro
this.unladenFastestRange = Calc.totalJumpRange(unladenMass + this.fuelCapacity, fsd, fuelCapacity, this);
this.ladenFastestRange = Calc.totalJumpRange(unladenMass + this.fuelCapacity + this.cargoCapacity, fsd, fuelCapacity, this);

View File

@@ -26,8 +26,8 @@ export const STATS_FORMATTING = {
'ammo': { 'format': 'int', },
'boot': { 'format': 'int', 'unit': 'secs' },
'brokenregen': { 'format': 'round1', 'unit': 'ps' },
'burst': { 'format': 'int' },
'burstrof': { 'format': 'round1', 'unit': 'ps' },
'burst': { 'format': 'int', 'change': 'additive' },
'burstrof': { 'format': 'round1', 'unit': 'ps', 'change': 'additive' },
'causres': { 'format': 'pct' },
'clip': { 'format': 'int' },
'damage': { 'format': 'round' },
@@ -61,7 +61,7 @@ export const STATS_FORMATTING = {
'ranget': { 'format': 'f1', 'unit': 's' },
'regen': { 'format': 'round1', 'unit': 'ps' },
'reload': { 'format': 'int', 'unit': 's' },
'rof': { 'format': 'round1', 'unit': 'ps' },
'rof': { 'format': 'round1', 'unit': 'ps', 'synthetic': 'getRoF', 'higherbetter': true },
'angle': { 'format': 'round1', 'unit': 'ang' },
'scanrate': { 'format': 'int' },
'scantime': { 'format': 'round1', 'unit': 's' },

View File

@@ -1,5 +1,6 @@
import React from 'react';
import { Modifications } from 'coriolis-data/dist';
import { STATS_FORMATTING } from '../shipyard/StatsFormatting';
/**
* Generate a tooltip with details of a blueprint's specials
@@ -155,7 +156,7 @@ export function blueprintTooltip(translate, blueprint, engineers, grp, m) {
// We also add in any benefits from specials that aren't covered above
if (m.blueprint && m.blueprint.special) {
for (const feature in Modifications.modifierActions[m.blueprint.special.edname]) {
for (const feature in Modifications.modifierActions[m.blueprint.special.key]) {
if (!blueprint.features[feature] && !m.mods.feature) {
const featureDef = Modifications.modifications[feature];
if (featureDef && !featureDef.hidden) {
@@ -280,6 +281,25 @@ export function isValueBeneficial(feature, value) {
}
}
/**
* Is the change as shown beneficial?
* @param {string} feature The name of the feature
* @param {number} value The value of the feature as percentage change
* @returns True if the value is beneficial
*/
export function isChangeValueBeneficial(feature, value) {
let changeHigherBetter = STATS_FORMATTING[feature].higherbetter;
if (changeHigherBetter === undefined) {
return isValueBeneficial(feature, value);
}
if (changeHigherBetter) {
return value > 0;
} else {
return value < 0;
}
}
/**
* Get a blueprint with a given name and an optional module
* @param {string} name The name of the blueprint
@@ -372,9 +392,7 @@ export function getPercent(m) {
let value = _getValue(m, featureName);
let mult;
if (featureName == 'shieldboost') {
mult = ((1 + value) * (1 + m.shieldboost)) - 1 - m.shieldboost;
} else if (Modifications.modifications[featureName].higherbetter) {
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);

View File

@@ -249,6 +249,13 @@ function _addModifications(module, modifiers, blueprint, grade, specialModificat
if (!modifiers) return;
let special;
if (specialModifications) {
if (specialModifications == 'special_plasma_slug') {
if (module.symbol.match(/PlasmaAccelerator/i)) {
specialModifications = 'special_plasma_slug_pa';
} else {
specialModifications = 'special_plasma_slug_cooled';
}
}
special = Modifications.specials[specialModifications];
}
// Add the blueprint definition, grade and special
@@ -274,6 +281,9 @@ function _addModifications(module, modifiers, blueprint, grade, specialModificat
if (value === Infinity) {
value = modifiers[i].Value * 100;
}
if (modifiers[i].Label.search('DamageFalloffRange') >= 0) {
value = (modifiers[i].Value / module.range - 1) * 100;
}
if (modifiers[i].Label.search('Resistance') >= 0) {
value = (modifiers[i].Value * 100) - (modifiers[i].OriginalValue * 100);
}

View File

@@ -1,6 +1,7 @@
import React from 'react';
import Persist from '../stores/Persist';
import * as ModuleUtils from '../shipyard/ModuleUtils';
import Module from '../shipyard/Module';
/**
* Determine if a slot on a ship can mount a module of a particular class and group
@@ -139,20 +140,21 @@ function diff(format, mVal, mmVal) {
export function diffDetails(language, m, mm) {
let { formats, translate, units } = language;
let propDiffs = [];
m = new Module(m);
// Module-specific items
if (m.grp === 'pp') {
let mPowerGeneration = m.pgen || 0;
let mPowerGeneration = m.getPowerGeneration() || 0;
let mmPowerGeneration = mm ? mm.getPowerGeneration() : 0;
if (mPowerGeneration != mmPowerGeneration) propDiffs.push(<div key='pgen'>{translate('pgen')}: <span className={diffClass(mPowerGeneration, mmPowerGeneration)}>{diff(formats.round, mPowerGeneration, mmPowerGeneration)}{units.MJ}</span></div>);
if (mPowerGeneration != mmPowerGeneration) propDiffs.push(<div key='pgen'>{translate('pgen')}: <span className={diffClass(mPowerGeneration, mmPowerGeneration)}>{diff(formats.round, mPowerGeneration, mmPowerGeneration)}{units.MW}</span></div>);
} else {
let mPowerUsage = m.power || 0;
let mPowerUsage = m.getPowerUsage() || 0;
let mmPowerUsage = mm ? mm.getPowerUsage() || 0 : 0;
if (mPowerUsage != mmPowerUsage) propDiffs.push(<div key='power'>{translate('power')}: <span className={diffClass(mPowerUsage, mmPowerUsage, true)}>{diff(formats.round, mPowerUsage, mmPowerUsage)}{units.MJ}</span></div>);
if (mPowerUsage != mmPowerUsage) propDiffs.push(<div key='power'>{translate('power')}: <span className={diffClass(mPowerUsage, mmPowerUsage, true)}>{diff(formats.round, mPowerUsage, mmPowerUsage)}{units.MW}</span></div>);
}
let mDps = m.damage * (m.rpshot || 1) * (m.rof || 1);
let mDps = m.getDps() || 0;
let mmDps = mm ? mm.getDps() || 0 : 0;
if (mDps && mDps != mmDps) propDiffs.push(<div key='dps'>{translate('dps')}: <span className={diffClass(mmDps, mDps, true)}>{diff(formats.round, mDps, mmDps)}</span></div>);
@@ -164,7 +166,7 @@ export function diffDetails(language, m, mm) {
if (mAffectsShield) {
if (m.grp == 'sb') { // Both m and mm must be utility modules if this is true
newShield = this.calcShieldStrengthWith(null, m.shieldboost - (mm ? mm.getShieldBoost() || 0 : 0));
newShield = this.calcShieldStrengthWith(null, m.getShieldBoost() - (mm ? mm.getShieldBoost() || 0 : 0));
} else {
newShield = this.calcShieldStrengthWith(m);
}
@@ -179,7 +181,7 @@ export function diffDetails(language, m, mm) {
}
if (m.grp === 'mrp') {
let mProtection = m.protection;
let mProtection = m.getProtection();
let mmProtection = mm ? mm.getProtection() || 0 : 0;
if (mProtection != mmProtection) {
propDiffs.push(<div key='protection'>{translate('protection')}: <span className={diffClass(mmProtection, mProtection, true)}>{diff(formats.pct, mProtection, mmProtection)}</span></div>);
@@ -187,7 +189,7 @@ export function diffDetails(language, m, mm) {
}
if (m.grp === 'hr') {
let mHullReinforcement = m.hullreinforcement;
let mHullReinforcement = m.getHullReinforcement();
let mmHullReinforcement = mm ? mm.getHullReinforcement() || 0 : 0;
if (mHullReinforcement && mHullReinforcement != mmHullReinforcement) propDiffs.push(<div key='hullreinforcement'>{translate('hullreinforcement')}: <span className={diffClass(mmHullReinforcement, mHullReinforcement, true)}>{diff(formats.round, mHullReinforcement, mmHullReinforcement)}</span></div>);
}
@@ -219,7 +221,7 @@ export function diffDetails(language, m, mm) {
let mmCost = mm ? mm.cost : 0;
if (mCost != mmCost) propDiffs.push(<div key='cost'>{translate('cost')}: <span className={diffClass(mCost, mmCost, true) }>{formats.int(mCost ? Math.round(mCost * (1 - Persist.getModuleDiscount())) : 0)}{units.CR}</span></div>);
let mMass = m.mass || 0;
let mMass = m.getMass() || 0;
let mmMass = mm ? mm.getMass() : 0;
if (mMass != mmMass) propDiffs.push(<div key='mass'>{translate('mass')}: <span className={diffClass(mMass, mmMass, true)}>{diff(formats.round, mMass, mmMass)}{units.T}</span></div>);
@@ -240,7 +242,7 @@ export function diffDetails(language, m, mm) {
}
}
let mIntegrity = m.integrity || 0;
let mIntegrity = m.getIntegrity() || 0;
let mmIntegrity = mm ? mm.getIntegrity() || 0 : 0;
if (mIntegrity != mmIntegrity) {
propDiffs.push(<div key='integrity'>{translate('integrity')}: <span className={diffClass(mmIntegrity, mIntegrity, true)}>{diff(formats.round, mIntegrity, mmIntegrity)}</span></div>);

View File

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

View File

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

View File

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

View File

@@ -49,4 +49,45 @@ a.ship {
font-size: 0.7em;
float: right;
}
}
}
.shipyard-table-wrapper {
white-space: nowrap;
margin: 0 auto;
font-size: 0.8em;
position: relative;
display: inline-block;
max-width: 100%;
}
table.shipyard-table{
tbody tr.comparehighlight{
background-color: @secondary-bg;
color: @fg-highlighted;
}
}
.shipyard-table-wrapper {
border-bottom: 1px solid @primary-darker;
}
.shipyard-table-wrapper div .shipyard-table td:last-child {
border-right: 1px solid @primary-darker;
}
.shipyard-table-wrapper > .shipyard-table td:first-child {
border-left: 1px solid @primary-darker;
}
.content-wrapper{
display: inline-block;
margin: 0 auto;
max-width: 100%;
}
.table-tools{
text-align: left;
color: @primary;
label{
cursor: pointer;
}
}

View File

@@ -52,7 +52,7 @@ tbody tr {
}
.no-touch &.highlight:hover, .no-touch &.highlighted {
background-color: @warning-bg;
background-color: @alt-primary-bg-highlighted; //@warning-bg;
}
&:nth-child(odd){
@@ -84,3 +84,5 @@ td {
text-align: center;
}
}

View File

@@ -7,8 +7,8 @@ if (workbox) {
workbox.routing.registerNavigationRoute('/index.html');
workbox.routing.registerRoute(
new RegExp('/(.*?)'),
workbox.strategies.staleWhileRevalidate({
/\.(?:png|jpg|jpeg|svg|gif)$/,
new workbox.strategies.CacheFirst({
plugins: [
new workbox.cacheableResponse.Plugin({
statuses: [0, 200]
@@ -17,9 +17,16 @@ if (workbox) {
})
);
workbox.routing.registerRoute(
/\.(?:js|css)$/,
new workbox.strategies.StaleWhileRevalidate({
cacheName: 'static-resources',
})
);
workbox.routing.registerRoute(
new RegExp('https://fonts.(?:googleapis|gstatic).com/(.*)'),
workbox.strategies.cacheFirst({
new workbox.strategies.CacheFirst({
cacheName: 'google-fonts',
plugins: [
new workbox.expiration.Plugin({
@@ -31,12 +38,6 @@ if (workbox) {
]
})
);
try {
workbox.googleAnalytics.initialize();
} catch (e) {
console.log('Probably an ad-blocker');
}
} else {
console.log('Boo! Workbox didn\'t load 😬');
}