Compare commits

..

3 Commits

Author SHA1 Message Date
Unknown
e520208ec6 Some more attempts 2018-01-27 16:15:21 +00:00
Unknown
29020e64f2 Some work 2018-01-27 15:40:43 +00:00
Unknown
16fe3ba9f5 Axios 2018-01-27 14:51:56 +00:00
54 changed files with 13918 additions and 16418 deletions

View File

@@ -1,21 +0,0 @@
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,
"classes": true,
"modules": true
}
},
},
"env": {
"browser": true,
@@ -33,6 +33,7 @@
"ClassDeclaration": true
}
}],
"no-console": 2,
"brace-style": [2, "1tbs", { "allowSingleLine": true }],
"comma-style": [2, "last"],
"indent": [2, 2, { "SwitchCase": 1, "VariableDeclarator": 2 }],

1
.gitignore vendored
View File

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

View File

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

1
devServer.js Executable file → Normal file
View File

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

25080
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,117 +1,114 @@
{
"name": "coriolis_shipyard",
"version": "2.9.16",
"repository": {
"type": "git",
"url": "https://github.com/EDCD/coriolis"
},
"homepage": "https://coriolis.edcd.io",
"bugs": "https://github.com/EDCD/coriolis/issues",
"private": true,
"engine": "node >= 4.8.1",
"license": "MIT",
"scripts": {
"prepublish": "rollup -c && uglifyjs d3.js -c -m -o d3.min.js",
"extract-translations": "grep -hroE \"(translate\\('[^']+'\\))|(tip.bind\\(null, '[^']+')\" src/* | grep -oE \"'[^']+'\" | grep -oE \"[^']+\" | sort -u -f",
"clean": "rimraf build",
"start": "node devServer.js",
"lint": "eslint --ext .js,.jsx src",
"test": "jest",
"prod-serve": "nginx -p $(pwd) -c nginx.conf",
"prod-stop": "kill -QUIT $(cat nginx.pid)",
"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",
"deploy": "npm run lint && npm test && npm run build && npm run rsync"
},
"jest": {
"transform": {
".*": "<rootDir>/node_modules/babel-jest"
},
"testRegex": "(/__tests__/test-.*|\\.(test|spec))\\.js$",
"moduleFileExtensions": [
"js",
"json",
"jsx"
],
"automock": true,
"bail": false,
"unmockedModulePathPatterns": [
"<rootDir>/node_modules/lodash",
"<rootDir>/node_modules/react",
"<rootDir>/node_modules/react-dom",
"<rootDir>/node_modules/react-transition-group",
"<rootDir>/node_modules/react-testutils-additions",
"<rootDir>/node_modules/fbjs",
"<rootDir>/node_modules/fbemitter",
"<rootDir>/node_modules/classnames",
"<rootDir>/node_modules/d3",
"<rootDir>/node_modules/lz-string",
"<rootDir>/node_modules/jsen",
"coriolis-data",
"<rootDir>/src/app/shipyard",
"<rootDir>/src/app/i18n",
"<rootDir>/src/app/utils",
"<rootDir>/src/schemas",
"<rootDir>/__tests__"
]
},
"devDependencies": {
"appcache-webpack-plugin": "^1.3.0",
"babel-core": "*",
"babel-eslint": "*",
"babel-jest": "*",
"babel-loader": "*",
"babel-preset-env": "*",
"babel-preset-react": "*",
"babel-preset-stage-0": "*",
"create-react-class": "^15.6.2",
"css-loader": "^0.28.0",
"cross-env": "^5.1.4",
"d3-selection": "1",
"eslint": "3.19.0",
"eslint-plugin-react": "^6.10.3",
"expose-loader": "^0.7.3",
"express": "^4.15.2",
"extract-text-webpack-plugin": "2.1.0",
"file-loader": "^0.11.1",
"html-webpack-plugin": "^2.28.0",
"jest-cli": "^21.2.1",
"jsen": "^0.6.4",
"json-loader": "^0.5.4",
"less": "^2.7.2",
"less-loader": "^4.0.3",
"react-addons-perf": "^15.4.2",
"react-measure": "^1.4.7",
"react-testutils-additions": "^15.2.0",
"react-transition-group": "^1.1.2",
"rimraf": "^2.6.1",
"rollup": "0.41",
"rollup-plugin-node-resolve": "3",
"style-loader": "^0.16.1",
"uglify-js": "^2.4.11",
"url-loader": "^0.5.8",
"webpack": "^2.4.1",
"webpack-dev-server": "^2.4.4",
"webpack-notifier": "^1.6.0",
"webpack-bugsnag-plugins": "^1.1.1"
},
"dependencies": {
"babel-polyfill": "*",
"browserify-zlib-next": "^1.0.1",
"classnames": "^2.2.5",
"coriolis-data": "../coriolis-data",
"d3": "4.8.0",
"detect-browser": "^1.7.0",
"fbemitter": "^2.1.1",
"lodash": "^4.17.4",
"lz-string": "^1.4.4",
"pako": "^1.0.6",
"prop-types": "^15.5.8",
"react": "^15.5.4",
"react-dom": "^15.5.4",
"react-ga": "^2.5.3",
"react-number-editor": "Athanasius/react-number-editor.git#miggy",
"recharts": "^0.22.3",
"superagent": "^3.5.2"
}
}
{
"name": "coriolis_shipyard",
"version": "2.5.1",
"repository": {
"type": "git",
"url": "https://github.com/EDCD/coriolis"
},
"homepage": "https://coriolis.edcd.io",
"bugs": "https://github.com/EDCD/coriolis/issues",
"private": true,
"engine": "node >= 4.8.1",
"license": "MIT",
"scripts": {
"prepublish": "rollup -c && uglifyjs d3.js -c -m -o d3.min.js",
"extract-translations": "grep -hroE \"(translate\\('[^']+'\\))|(tip.bind\\(null, '[^']+')\" src/* | grep -oE \"'[^']+'\" | grep -oE \"[^']+\" | sort -u -f",
"clean": "rimraf build",
"start": "node devServer.js",
"lint": "eslint --ext .js,.jsx src",
"test": "jest",
"prod-serve": "nginx -p $(pwd) -c nginx.conf",
"prod-stop": "kill -QUIT $(cat nginx.pid)",
"build": "npm run clean && NODE_ENV=production webpack -p --config webpack.config.prod.js",
"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"
},
"jest": {
"transform": {
".*": "<rootDir>/node_modules/babel-jest"
},
"testRegex": "(/__tests__/test-.*|\\.(test|spec))\\.js$",
"moduleFileExtensions": [
"js",
"json",
"jsx"
],
"automock": true,
"bail": false,
"unmockedModulePathPatterns": [
"<rootDir>/node_modules/lodash",
"<rootDir>/node_modules/react",
"<rootDir>/node_modules/react-dom",
"<rootDir>/node_modules/react-transition-group",
"<rootDir>/node_modules/react-testutils-additions",
"<rootDir>/node_modules/fbjs",
"<rootDir>/node_modules/fbemitter",
"<rootDir>/node_modules/classnames",
"<rootDir>/node_modules/d3",
"<rootDir>/node_modules/lz-string",
"<rootDir>/node_modules/jsen",
"coriolis-data",
"<rootDir>/src/app/shipyard",
"<rootDir>/src/app/i18n",
"<rootDir>/src/app/utils",
"<rootDir>/src/schemas",
"<rootDir>/__tests__"
]
},
"devDependencies": {
"appcache-webpack-plugin": "^1.3.0",
"babel-core": "*",
"babel-eslint": "*",
"babel-jest": "*",
"babel-loader": "*",
"babel-preset-env": "*",
"babel-preset-react": "*",
"babel-preset-stage-0": "*",
"create-react-class": "^15.6.2",
"css-loader": "^0.28.0",
"d3-selection": "1",
"eslint": "3.19.0",
"eslint-plugin-react": "^6.10.3",
"expose-loader": "^0.7.3",
"express": "^4.15.2",
"extract-text-webpack-plugin": "2.1.0",
"file-loader": "^0.11.1",
"html-webpack-plugin": "^2.28.0",
"jest-cli": "^21.2.1",
"jsen": "^0.6.4",
"json-loader": "^0.5.4",
"less": "^2.7.2",
"less-loader": "^4.0.3",
"react-addons-perf": "^15.4.2",
"react-measure": "^1.4.7",
"react-testutils-additions": "^15.2.0",
"react-transition-group": "^1.1.2",
"rimraf": "^2.6.1",
"rollup": "0.41",
"rollup-plugin-node-resolve": "3",
"style-loader": "^0.16.1",
"url-loader": "^0.5.8",
"webpack": "^2.4.1",
"webpack-dev-server": "^2.4.4",
"uglify-js": "^2.4.11"
},
"dependencies": {
"axios": "^0.17.1",
"babel-polyfill": "*",
"browserify-zlib-next": "^1.0.1",
"classnames": "^2.2.5",
"coriolis-data": "../coriolis-data",
"d3": "4.8.0",
"detect-browser": "^1.7.0",
"fbemitter": "^2.1.1",
"lodash": "^4.17.4",
"lz-string": "^1.4.4",
"pako": "^1.0.6",
"prop-types": "^15.5.8",
"react": "^15.5.4",
"react-dom": "^15.5.4",
"react-number-editor": "Athanasius/react-number-editor.git#miggy",
"recharts": "^0.22.3",
"superagent": "^3.5.2"
}
}

View File

@@ -12,7 +12,6 @@ import ModalHelp from './components/ModalHelp';
import ModalImport from './components/ModalImport';
import ModalPermalink from './components/ModalPermalink';
import * as CompanionApiUtils from './utils/CompanionApiUtils';
import * as JournalUtils from './utils/JournalUtils';
import AboutPage from './pages/AboutPage';
import NotFoundPage from './pages/NotFoundPage';
@@ -93,14 +92,7 @@ export default class Coriolis extends React.Component {
// Need to decode and gunzip the data, then build the ship
const data = zlib.inflate(new Buffer(r.params.data, 'base64'), { to: 'string' });
const json = JSON.parse(data);
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);
}
const ship = CompanionApiUtils.shipFromJson(json);
r.params.ship = ship.id;
r.params.code = ship.toString();
this._setPage(OutfittingPage, r);
@@ -134,9 +126,9 @@ export default class Coriolis extends React.Component {
console && console.error && console.error(arguments); // eslint-disable-line no-console
if (errObj) {
if (errObj instanceof Error) {
bugsnagClient.notify(errObj) // eslint-disable-line
Bugsnag.notifyException(errObj) // eslint-disable-line
} else if (errObj instanceof String) {
bugsnagClient.notify(msg, errObj) // eslint-disable-line
Bugsnag.notify(msg, errObj) // eslint-disable-line
}
}
this.setState({
@@ -355,8 +347,6 @@ export default class Coriolis extends React.Component {
<footer>
<div className="right cap">
<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>
</footer>
</div>;

View File

@@ -1,6 +1,5 @@
import Persist from './stores/Persist';
import ReactGA from 'react-ga';
ReactGA.initialize('UA-55840909-18');
let standalone = undefined;
/**
@@ -258,7 +257,9 @@ Route.prototype.match = function(path, params) {
* @param {string} path Path to track
*/
function gaTrack(path) {
ReactGA.pageview(path);
if (window.ga) {
window.ga('send', 'pageview', path);
}
}
/**

View File

@@ -44,7 +44,6 @@ const GRPCAT = {
'rg': 'projectiles',
'mr': 'ordnance',
'axmr': 'experimental',
'rcpl': 'experimental',
'tp': 'ordnance',
'nl': 'ordnance',
'sc': 'scanners',
@@ -57,11 +56,7 @@ const GRPCAT = {
'ch': 'defence',
'po': 'defence',
'ec': 'defence',
'sfn': 'defence',
// Standard
'gpp': 'guardian',
'gpc': 'guardian',
'ggc': 'guardian'
'sfn': 'defence'
};
// Order here is the order in which items will be shown in the modules menu
const CATEGORIES = {
@@ -87,10 +82,7 @@ const CATEGORIES = {
'defence': ['ch', 'po', 'ec'],
'scanners': ['sc', 'ss', 'cs', 'kw', 'ws'], // Overloaded with internal scanners
// Experimental
'experimental': ['axmc', 'axmr', 'rfl', 'xs', 'sfn', 'rcpl'],
// Guardian
'guardian': ['gpp', 'gpc', 'ggc']
'experimental': ['axmc', 'axmr', 'rfl', 'xs', 'sfn']
};
/**
@@ -104,11 +96,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
diffDetails: PropTypes.func,
m: PropTypes.object,
shipMass: PropTypes.number,
warning: PropTypes.func,
firstSlotId: PropTypes.string,
lastSlotId: PropTypes.string,
activeSlotId: PropTypes.string,
slotDiv: PropTypes.object
warning: PropTypes.func
};
static defaultProps = {
@@ -124,7 +112,6 @@ export default class AvailableModulesMenu extends TranslatedComponent {
super(props);
this._hideDiff = this._hideDiff.bind(this);
this.state = this._initState(props, context);
this.slotItems = [];// Array to hold <li> refs.
}
/**
@@ -135,9 +122,8 @@ 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, shipMass, onSelect, modules } = props;
let list, currentGroup;
let buildGroup = this._buildGroup.bind(
this,
translate,
@@ -148,7 +134,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
this._hideDiff(event);
onSelect(m);
}
);
);
if (modules instanceof Array) {
list = buildGroup(modules[0].grp, modules);
@@ -156,10 +142,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
list = [];
// At present time slots with grouped options (Hardpoints and Internal) can be empty
if (m) {
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>);
list.push(<div className='empty-c upp' key='empty' onClick={onSelect.bind(null, null)} >{translate('empty')}</div>);
}
// Need to regroup the modules by our own categorisation
@@ -209,40 +192,37 @@ export default class AvailableModulesMenu extends TranslatedComponent {
}
}
}
let trackingFocus = false;
return { list, currentGroup, trackingFocus };
return { list, currentGroup };
}
/**
* Generate React Components for Module Group
* @param {Function} translate Translate function
* @param {Object} mountedModule Mounted Module
* @param {Function} warningFunc Warning function
* @param {Objecy} mountedModule Mounted Module
* @param {Funciton} 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) {
let prevClass = null, prevRating = null, prevName;
_buildGroup(translate, mountedModule, warningFunc, mass, onSelect, grp, modules) {
let prevClass = null, prevRating = null;
let elems = [];
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
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]));
let itemsOnThisRow = 0;
for (let i = 0; i < sortedModules.length; i++) {
let m = sortedModules[i];
let mount = null;
let disabled = false;
prevName = m.name;
if (ModuleUtils.isShieldGenerator(m.grp)) {
// Shield generators care about maximum hull mass
disabled = mass > m.maxmass;
@@ -258,21 +238,9 @@ export default class AvailableModulesMenu extends TranslatedComponent {
});
let eventHandlers;
if (disabled) {
eventHandlers = {
onKeyDown: this._keyDown.bind(this, null),
onKeyUp: this._keyUp.bind(this, null)
};
if (disabled || active) {
eventHandlers = {};
} 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 select = onSelect.bind(null, m);
@@ -281,9 +249,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
onTouchStart: this._touchStart.bind(this, showDiff),
onTouchEnd: this._touchEnd.bind(this, select),
onMouseLeave: this._hideDiff,
onClick: select,
onKeyDown: this._keyDown.bind(this, select),
onKeyUp: this._keyUp.bind(this, select)
onClick: select
};
}
@@ -292,28 +258,24 @@ export default class AvailableModulesMenu extends TranslatedComponent {
case 'G': mount = <MountGimballed 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)) {
elems.push(<br key={'b' + m.grp + i} />);
itemsOnThisRow = 0;
}
let tbIdx = (classes.indexOf('disabled') < 0) ? 0 : undefined;
elems.push(
<li key={m.id} data-id={m.id} className={classes} {...eventHandlers} tabIndex={tbIdx} ref={slotItem => this.slotItems[m.id] = slotItem}>
<li key={m.id} className={classes} {...eventHandlers}>
{mount}
{(mount ? ' ' : '') + m.class + m.rating + (m.missile ? '/' + m.missile : '') + (m.name ? ' ' + translate(m.name) : '')}
</li>
);
itemsOnThisRow++;
prevClass = m.class;
prevRating = m.rating;
prevName = m.name;
}
return <ul key={'modules' + grp}>{elems}</ul>;
return <ul key={'modules' + grp} >{elems}</ul>;
}
/**
@@ -364,41 +326,6 @@ export default class AvailableModulesMenu extends TranslatedComponent {
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
* @param {SyntheticEvent} event Event
@@ -456,23 +383,6 @@ export default class AvailableModulesMenu extends TranslatedComponent {
if (this.groupElem) { // Scroll to currently selected group
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

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

View File

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

View File

@@ -505,9 +505,6 @@ export default class Header extends TranslatedComponent {
return (
<header>
{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>
<div className='l menu'>

View File

@@ -33,9 +33,6 @@ export default class InternalSlot extends Slot {
let modTT = translate('modified');
if (m && m.blueprint && m.blueprint.name) {
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 = (
<div>
<div>{modTT}</div>
@@ -70,7 +67,6 @@ export default class InternalSlot extends Slot {
{ 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.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.rangeLS ? <div className={'l'}>{translate('range')}: {m.rangeLS}{u.Ls}</div> : null }
{ m.rangeLS === null ? <div className={'l'}>{u.Ls}</div> : null }
@@ -85,7 +81,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.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 && validMods.length > 0 ? <div className='r' tabIndex="0" ref={ modButton => this.modButton = modButton }><button tabIndex="-1" onClick={this._toggleModifications.bind(this)} onContextMenu={stopCtxPropagation} onMouseOver={termtip.bind(null, 'modifications')} onMouseOut={tooltip.bind(null, null)}><ListModifications /></button></div> : null }
{ m && 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 }
</div>
</div>;
} else {

View File

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

View File

@@ -50,7 +50,6 @@ export default class ModalPermalink extends TranslatedComponent {
<h3 >{translate('shortened')}</h3>
<input value={this.state.shortenedUrl} readOnly size={25} onFocus={ (e) => e.target.select() }/>
<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>
</div>;
}

View File

@@ -1,120 +0,0 @@
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,10 +14,7 @@ export default class Modification extends TranslatedComponent {
m: PropTypes.object.isRequired,
name: PropTypes.string.isRequired,
value: PropTypes.number.isRequired,
onChange: PropTypes.func.isRequired,
onKeyDown: PropTypes.func.isRequired,
modItems: PropTypes.array.isRequired,
handleModChange: PropTypes.func.isRequired
onChange: PropTypes.func.isRequired
};
/**
@@ -39,6 +36,7 @@ export default class Modification extends TranslatedComponent {
*/
_updateValue(value) {
const name = this.props.name;
let scaledValue = Math.round(Number(value) * 100);
// Limit to +1000% / -99.99%
if (scaledValue > 100000) {
@@ -59,15 +57,9 @@ export default class Modification extends TranslatedComponent {
/**
* 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() {
if (this.props.value != this.state.value) {
this.props.handleModChange(true);
this.props.onChange();
}
this.props.onChange();
}
/**
@@ -94,9 +86,9 @@ export default class Modification extends TranslatedComponent {
}
return (
<div onBlur={this._updateFinished.bind(this)} className={'cb'} key={name} ref={ modItem => this.props.modItems[name] = modItem }>
<div onBlur={this._updateFinished.bind(this)} className={'cb'} key={name}>
<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)} onKeyDown={ this.props.onKeyDown } />
<NumberEditor className={'cb'} style={{ width: '90%', textAlign: 'center' }} step={0.01} stepModifier={1} decimals={2} value={this.state.value} onValueChange={this._updateValue.bind(this)} />
</div>
);
}

View File

@@ -6,14 +6,7 @@ import { isEmpty, stopCtxPropagation } from '../utils/UtilityFunctions';
import cn from 'classnames';
import { Modifications } from 'coriolis-data/dist';
import Modification from './Modification';
import {
getBlueprint,
blueprintTooltip,
setPercent,
getPercent,
setRandom,
specialToolTip
} from '../utils/BlueprintFunctions';
import { getBlueprint, blueprintTooltip, setWorst, setBest, setExtreme, setRandom } from '../utils/BlueprintFunctions';
/**
* Modifications menu
@@ -24,8 +17,7 @@ export default class ModificationsMenu extends TranslatedComponent {
ship: PropTypes.object.isRequired,
m: PropTypes.object.isRequired,
marker: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
modButton:PropTypes.object
onChange: PropTypes.func.isRequired
};
/**
@@ -38,24 +30,14 @@ export default class ModificationsMenu extends TranslatedComponent {
this._toggleBlueprintsMenu = this._toggleBlueprintsMenu.bind(this);
this._toggleSpecialsMenu = this._toggleSpecialsMenu.bind(this);
this._rollFifty = this._rollFifty.bind(this);
this._rollWorst = this._rollWorst.bind(this);
this._rollRandom = this._rollRandom.bind(this);
this._rollBest = this._rollBest.bind(this);
this._rollWorst = this._rollWorst.bind(this);
this._rollExtreme = this._rollExtreme.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 = {
blueprintMenuOpened: !(props.m.blueprint && props.m.blueprint.name),
blueprintMenuOpened: false,
specialMenuOpened: false
};
}
@@ -70,6 +52,7 @@ export default class ModificationsMenu extends TranslatedComponent {
const { m } = props;
const { language, tooltip, termtip } = context;
const translate = language.translate;
const blueprints = [];
for (const blueprintName in Modifications.modules[m.grp].blueprints) {
const blueprint = getBlueprint(blueprintName, m);
@@ -78,18 +61,14 @@ export default class ModificationsMenu extends TranslatedComponent {
// Grade is a string in the JSON so make it a number
grade = Number(grade);
const classes = cn('c', {
active: m.blueprint && blueprint.id === m.blueprint.id && grade === m.blueprint.grade
active: m.blueprint && blueprint.id === m.blueprint.id && grade === m.blueprint.grade
});
const close = this._blueprintSelected.bind(this, blueprintName, grade);
const key = blueprintName + ':' + grade;
const tooltipContent = blueprintTooltip(translate, blueprint.grades[grade], Modifications.modules[m.grp].blueprints[blueprintName].grades[grade].engineers, m.grp);
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>);
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 (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(<ul key={blueprintName}>{blueprintGrades}</ul>);
}
@@ -97,64 +76,6 @@ export default class ModificationsMenu extends TranslatedComponent {
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
* @param {Object} props React component properties
@@ -165,34 +86,15 @@ export default class ModificationsMenu extends TranslatedComponent {
const { m } = props;
const { language, tooltip, termtip } = context;
const translate = language.translate;
const 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) {
const close = this._specialSelected.bind(this, null);
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>);
specials.push(<div style={{ cursor: 'pointer' }} key={ 'none' } onClick={ close }>{translate('PHRASE_NO_SPECIAL')}</div>);
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);
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>);
}
specials.push(<div style={{ cursor: 'pointer' }} key={ specialName } onClick={ close }>{translate(Modifications.specials[specialName].name)}</div>);
}
}
return specials;
@@ -209,8 +111,7 @@ export default class ModificationsMenu extends TranslatedComponent {
for (const modName of Modifications.modules[m.grp].modifications) {
if (!Modifications.modifications[modName].hidden) {
const key = modName + (m.getModValue(modName) / 100 || 0);
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} />);
modifications.push(<Modification key={ key } ship={ ship } m={ m } name={ modName } value={ m.getModValue(modName) / 100 || 0 } onChange={ onChange }/>);
}
}
return modifications;
@@ -235,9 +136,8 @@ export default class ModificationsMenu extends TranslatedComponent {
const blueprint = getBlueprint(fdname, m);
blueprint.grade = grade;
ship.setModuleBlueprint(m, blueprint);
setPercent(ship, m, 100);
this.setState({ blueprintMenuOpened: false, specialMenuOpened: true });
this.setState({ blueprintMenuOpened: false });
this.props.onChange();
}
@@ -268,15 +168,11 @@ export default class ModificationsMenu extends TranslatedComponent {
}
/**
* Provide a '50%' roll within the information we have
* Provide a 'worst' roll within the information we have
*/
_rollFifty() {
_rollWorst() {
const { m, ship } = this.props;
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);
setWorst(ship, m);
this.props.onChange();
}
@@ -286,10 +182,6 @@ export default class ModificationsMenu extends TranslatedComponent {
_rollRandom() {
const { m, ship } = this.props;
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();
}
@@ -298,22 +190,16 @@ export default class ModificationsMenu extends TranslatedComponent {
*/
_rollBest() {
const { m, ship } = this.props;
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);
setBest(ship, m);
this.props.onChange();
}
/**
* Provide a 'worst' roll within the information we have
* Provide an 'extreme' roll within the information we have
*/
_rollWorst() {
_rollExtreme() {
const { m, ship } = this.props;
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);
setExtreme(ship, m);
this.props.onChange();
}
@@ -324,67 +210,10 @@ export default class ModificationsMenu extends TranslatedComponent {
const { m, ship } = this.props;
ship.clearModifications(m);
ship.clearModuleBlueprint(m);
this.selectedModId = null;
this.selectedSpecialId = null;
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
* @return {React.Component} List
@@ -397,82 +226,66 @@ export default class ModificationsMenu extends TranslatedComponent {
const _toggleBlueprintsMenu = this._toggleBlueprintsMenu;
const _toggleSpecialsMenu = this._toggleSpecialsMenu;
const _rollFull = this._rollBest;
const _rollBest = this._rollBest;
const _rollExtreme = this._rollExtreme;
const _rollWorst = this._rollWorst;
const _rollFifty = this._rollFifty;
const _rollRandom = this._rollRandom;
const _reset = this._reset;
let blueprintLabel;
let haveBlueprint = false;
let blueprintTt;
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]) {
if (m.blueprint && m.blueprint.name) {
blueprintLabel = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
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);
blueprintCv = getPercent(m);
}
let specialLabel;
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);
} else {
specialLabel = translate('PHRASE_SELECT_SPECIAL');
}
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 showSpecial = haveBlueprint && specials.length && !blueprintMenuOpened;
const showSpecialsMenu = specialMenuOpened && specials.length;
const showRolls = haveBlueprint && !blueprintMenuOpened && (!specialMenuOpened || !specials.length);
const showReset = !blueprintMenuOpened && (!specialMenuOpened || !specials.length) && haveBlueprint;
const showMods = !blueprintMenuOpened && (!specialMenuOpened || !specials.length) && haveBlueprint;
if (haveBlueprint) {
this.firstBPLabel = blueprintLabel;
} else {
this.firstBPLabel = 'selectBP';
}
const showSpecialsMenu = specialMenuOpened;
const showRolls = haveBlueprint && !blueprintMenuOpened && !specialMenuOpened;
const showReset = !blueprintMenuOpened && !specialMenuOpened;
const showMods = !blueprintMenuOpened && !specialMenuOpened;
return (
<div
className={cn('select', this.props.className)}
onClick={(e) => e.stopPropagation() }
onContextMenu={stopCtxPropagation}
ref={modItem => this.modItems['modMainDiv'] = modItem}
>
{ showBlueprintsMenu | showSpecialsMenu ? '' : haveBlueprint ?
<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 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 ? '' : 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 className={ cn('section-menu', { selected: blueprintMenuOpened })} style={{ cursor: 'pointer' }} onClick={_toggleBlueprintsMenu}>{translate('PHRASE_SELECT_BLUEPRINT')}</div> }
{ showBlueprintsMenu ? this._renderBlueprints(this.props, this.context) : 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 }
{ showSpecial ? <div className={ cn('section-menu', { selected: specialMenuOpened })} style={{ cursor: 'pointer' }} onClick={_toggleSpecialsMenu}>{specialLabel}</div> : null }
{ showSpecialsMenu ? specials : null }
{ 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 ?
{ showRolls || showReset ?
<table style={{ width: '100%', backgroundColor: 'transparent' }}>
<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: 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>
<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>
<td> { 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 style={{ cursor: 'pointer' }} onClick={_rollBest}onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_BEST')} onMouseOut={tooltip.bind(null, null)}> { translate('best') } </td>
<td style={{ cursor: 'pointer' }} onClick={_rollExtreme}onMouseOver={termtip.bind(null, 'PHRASE_BLUEPRINT_EXTREME')} onMouseOut={tooltip.bind(null, null)}> { translate('extreme') } </td>
<td style={{ cursor: 'pointer' }} onClick={_rollRandom} 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 }
</tbody>
</table> : null }
{ showMods ? <hr /> : null }
{ showMods ?
<span onMouseOver={termtip.bind(null, 'HELP_MODIFICATIONS_MENU')} onMouseOut={tooltip.bind(null, null)} >
{ this._renderModifications(this.props) }

View File

@@ -15,34 +15,22 @@ export default class ShipSummaryTable extends TranslatedComponent {
cargo: PropTypes.number.isRequired,
fuel: PropTypes.number.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
* @return {React.Component} Summary table
*/
render() {
const { ship, cargo, fuel, pips } = this.props;
const { ship, cargo, fuel } = this.props;
let { language, tooltip, termtip } = this.context;
let translate = language.translate;
let u = language.units;
let formats = language.formats;
let { time, int, round, f1, f2 } = formats;
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 sgTooltip = shieldGenerator ? 'TT_SUMMARY_SHIELDS' : 'TT_SUMMARY_SHIELDS_NONFUNCTIONAL';
const timeToDrain = Calc.timeToDrainWep(ship, 4);
@@ -50,20 +38,9 @@ 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 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'>
<table className={'summaryTable'}>
<table id='summaryTable'>
<thead>
<tr className='main'>
<th rowSpan={2} className={ cn({ 'bg-warning-disabled': !canThrust }) }>{translate('speed')}</th>
@@ -76,13 +53,11 @@ export default class ShipSummaryTable extends TranslatedComponent {
<th rowSpan={2}>{translate('TTD')}</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('pax')}</th>
<th rowSpan={2}>{translate('fuel')}</th>
<th colSpan={3}>{translate('mass')}</th>
<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>
</tr>
<tr>
<th className='lft'>{translate('max')}</th>
@@ -111,7 +86,6 @@ export default class ShipSummaryTable extends TranslatedComponent {
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_TTD', { cap: 0 })} onMouseLeave={hide}>{timeToDrain === Infinity ? '∞' : time(timeToDrain)}</td>
{/* <td>{f1(ship.totalHps)}</td> */}
<td>{round(ship.cargoCapacity)}{u.T}</td>
<td>{ship.passengerCapacity}</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_UNLADEN_MASS', { cap: 0 })} onMouseLeave={hide}>{int(ship.unladenMass)}{u.T}</td>
@@ -119,74 +93,9 @@ export default class ShipSummaryTable extends TranslatedComponent {
<td>{int(ship.hardness)}</td>
<td>{ship.crew}</td>
<td>{ship.masslock}</td>
<td>{shipBoost !== 'No Boost' ? formats.time(shipBoost) : 'No Boost'}</td>
</tr>
</tbody>
</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>;
}
}

View File

@@ -1,386 +1,166 @@
import React from 'react';
import PropTypes from 'prop-types';
const MARGIN_LR = 8; // Left/ Right margin
/**
* Horizontal Slider
*/
export default class Slider extends React.Component {
static defaultProps = {
axis: false,
min: 0,
max: 1,
scale: 1 // SVG render scale
};
static propTypes = {
axis: PropTypes.bool,
axisUnit: PropTypes.string,// units (T, M, etc.)
max: PropTypes.number,
min: PropTypes.number,
onChange: PropTypes.func.isRequired,// function which determins percent value
onResize: PropTypes.func,
percent: PropTypes.number.isRequired,// value of slider
scale: PropTypes.number
};
/**
* Constructor
* @param {Object} props React Component properties
*/
constructor(props) {
super(props);
this._down = this._down.bind(this);
this._move = this._move.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._updateDimensions = this._updateDimensions.bind(this);
this.state = { width: 0 };
}
/**
* On Mouse/Touch down handler
* @param {SyntheticEvent} event Event
*/
_down(event) {
let rect = event.currentTarget.getBoundingClientRect();
this.left = rect.left;
this.width = rect.width;
this._move(event);
this.touchStartTimer = setTimeout(() => this.sliderInputBox._setDisplay('block'), 1500);
}
/**
* Update the slider percentage on move
* @param {SyntheticEvent} event Event
*/
_move(event) {
if(this.width !== null && this.left != null) {
let clientX = event.touches ? event.touches[0].clientX : event.clientX;
event.preventDefault();
this._updatePercent(clientX - this.left, this.width);
}
}
/**
* On Mouse/Touch up handler
* @param {Event} event DOM Event
*/
_up(event) {
this.sliderInputBox.sliderVal.focus();
clearTimeout(this.touchStartTimer);
event.preventDefault();
this.left = 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
* @param {SyntheticEvent} event Event
*/
_enter(event) {
if(event.buttons !== 1) {
this.left = null;
this.width = null;
}
}
/**
* Update the slider percentage
* @param {number} pos Slider drag position
* @param {number} width Slider width
* @param {Event} event DOM Event
*/
_updatePercent(pos, width) {
this.props.onChange(Math.min(Math.max(pos / width, 0), 1));
}
/**
* Update dimenions from rendered DOM
*/
_updateDimensions() {
this.setState({
outerWidth: this.node.getBoundingClientRect().width
});
}
/**
* Add listeners when about to mount
*/
componentWillMount() {
if (this.props.onResize) {
this.resizeListener = this.props.onResize(this._updateDimensions);
}
}
/**
* Trigger DOM updates on mount
*/
componentDidMount() {
this._updateDimensions();
}
/**
* Remove listeners on unmount
*/
componentWillUnmount() {
if (this.resizeListener) {
this.resizeListener.remove();
}
}
/**
* Render the slider
* @return {React.Component} The slider
*/
render() {
let outerWidth = this.state.outerWidth;
let { axis, axisUnit, min, max, scale } = this.props;
let style = {
width: '100%',
height: axis ? '2.5em' : '1.5em',
boxSizing: 'border-box'
};
if (!outerWidth) {
return <svg style={style} ref={node => this.node = node} />;
}
let margin = MARGIN_LR * scale;
let width = outerWidth - (margin * 2);
let pctPos = width * this.props.percent;
return <div><svg
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-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} />
<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' }}>
<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='100%' style={{ textAnchor: 'end' }}>{max + axisUnit}</text>
</g>}
</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>;
}
}
import React from 'react';
import PropTypes from 'prop-types';
const MARGIN_LR = 8; // Left/ Right margin
/**
* Horizontal Slider
*/
export default class Slider extends React.Component {
static defaultProps = {
axis: false,
min: 0,
max: 1,
scale: 1 // SVG render scale
};
static propTypes = {
axis: PropTypes.bool,
axisUnit: PropTypes.string,
max: PropTypes.number,
min: PropTypes.number,
onChange: PropTypes.func.isRequired,
onResize: PropTypes.func,
percent: PropTypes.number.isRequired,
scale: PropTypes.number
};
/**
* Constructor
* @param {Object} props React Component properties
*/
constructor(props) {
super(props);
this._down = this._down.bind(this);
this._move = this._move.bind(this);
this._up = this._up.bind(this);
this._updatePercent = this._updatePercent.bind(this);
this._updateDimensions = this._updateDimensions.bind(this);
this.state = { width: 0 };
}
/**
* On Mouse/Touch down handler
* @param {SyntheticEvent} event Event
*/
_down(event) {
let rect = event.currentTarget.getBoundingClientRect();
this.left = rect.left;
this.width = rect.width;
this._move(event);
}
/**
* Update the slider percentage on move
* @param {SyntheticEvent} event Event
*/
_move(event) {
if(this.width !== null && this.left != null) {
let clientX = event.touches ? event.touches[0].clientX : event.clientX;
event.preventDefault();
this._updatePercent(clientX - this.left, this.width);
}
}
/**
* On Mouse/Touch up handler
* @param {Event} event DOM Event
*/
_up(event) {
event.preventDefault();
this.left = null;
this.width = null;
}
/**
* Determine if the user is still dragging
* @param {SyntheticEvent} event Event
*/
_enter(event) {
if(event.buttons !== 1) {
this.left = null;
this.width = null;
}
}
/**
* Update the slider percentage
* @param {number} pos Slider drag position
* @param {number} width Slider width
* @param {Event} event DOM Event
*/
_updatePercent(pos, width) {
this.props.onChange(Math.min(Math.max(pos / width, 0), 1));
}
/**
* Update dimenions from rendered DOM
*/
_updateDimensions() {
this.setState({
outerWidth: this.node.getBoundingClientRect().width
});
}
/**
* Add listeners when about to mount
*/
componentWillMount() {
if (this.props.onResize) {
this.resizeListener = this.props.onResize(this._updateDimensions);
}
}
/**
* Trigger DOM updates on mount
*/
componentDidMount() {
this._updateDimensions();
}
/**
* Remove listeners on unmount
*/
componentWillUnmount() {
if (this.resizeListener) {
this.resizeListener.remove();
}
}
/**
* Render the slider
* @return {React.Component} The slider
*/
render() {
let outerWidth = this.state.outerWidth;
let { axis, axisUnit, min, max, scale } = this.props;
let style = {
width: '100%',
height: axis ? '2.5em' : '1.5em',
boxSizing: 'border-box'
};
if (!outerWidth) {
return <svg style={style} ref={node => this.node = node} />;
}
let margin = MARGIN_LR * scale;
let width = outerWidth - (margin * 2);
let pctPos = width * this.props.percent;
return <svg onMouseUp={this._up} onMouseEnter={this._enter.bind(this)} onMouseMove={this._move} onTouchEnd={this._up} style={style} ref={node => this.node = node}>
<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' />
<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} />
{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='50%' style={{ textAnchor: 'middle' }}>{(min + max / 2) + axisUnit}</text>
<text className='primary-disabled' y='3em' x='100%' style={{ textAnchor: 'end' }}>{max + axisUnit}</text>
</g>}
</svg>;
}
}

View File

@@ -40,8 +40,6 @@ export default class Slot extends TranslatedComponent {
this._contextMenu = wrapCtxMenu(this._contextMenu.bind(this));
this._getMaxClassLabel = this._getMaxClassLabel.bind(this);
this._keyDown = this._keyDown.bind(this);
this.slotDiv = null;
}
// Must be implemented by subclasses:
@@ -75,22 +73,6 @@ export default class Slot extends TranslatedComponent {
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
* @return {React.Component} The slot
@@ -122,7 +104,6 @@ export default class Slot extends TranslatedComponent {
ship={ship}
m={m}
marker={modificationsMarker}
modButton = {this.modButton}
/>;
} else {
menu = <AvailableModulesMenu
@@ -133,7 +114,6 @@ export default class Slot extends TranslatedComponent {
onSelect={onSelect}
warning={warning}
diffDetails={diffDetails.bind(ship, this.context.language)}
slotDiv = {this.slotDiv}
/>;
}
}
@@ -141,7 +121,7 @@ export default class Slot extends TranslatedComponent {
// TODO: implement touch dragging
return (
<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={cn('slot', dropClass, { selected })} onClick={onOpen} onContextMenu={this._contextMenu} onDragOver={dragOver}>
<div className='details-container'>
<div className='sz'>{this._getMaxClassLabel(translate)}</div>
{slotDetails}
@@ -151,7 +131,6 @@ export default class Slot extends TranslatedComponent {
);
}
/**
* Toggle the modifications flag when selecting the modifications icon
*/

View File

@@ -18,8 +18,7 @@ export default class SlotSection extends TranslatedComponent {
onCargoChange: PropTypes.func.isRequired,
onFuelChange: PropTypes.func.isRequired,
code: PropTypes.string.isRequired,
togglePwr: PropTypes.func,
sectionMenuRefs: PropTypes.object
togglePwr: PropTypes.func
};
/**
@@ -33,10 +32,7 @@ export default class SlotSection extends TranslatedComponent {
super(props);
this.sectionId = sectionId;
this.sectionName = sectionName;
this.ssHeadRef = null;
this.sectionRefArr = this.props.sectionMenuRefs[this.sectionId] = [];
this.sectionRefArr['selectedRef'] = null;
this._getSlots = this._getSlots.bind(this);
this._selectModule = this._selectModule.bind(this);
this._getSectionMenu = this._getSectionMenu.bind(this);
@@ -44,8 +40,6 @@ export default class SlotSection extends TranslatedComponent {
this._drop = this._drop.bind(this);
this._dragOverNone = this._dragOverNone.bind(this);
this._close = this._close.bind(this);
this._keyDown = this._keyDown.bind(this);
this._handleSectionFocus = this._handleSectionFocus.bind(this);
this.state = {};
}
@@ -53,59 +47,7 @@ export default class SlotSection extends TranslatedComponent {
// _getSlots()
// _getSectionMenu()
// _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
* @param {string} menu Menu name
@@ -283,7 +225,7 @@ export default class SlotSection extends TranslatedComponent {
return (
<div id={this.sectionId} className={'group'} onDragLeave={this._dragOverNone}>
<div className={cn('section-menu', { selected: sectionMenuOpened })} onClick={open} onContextMenu={ctx}>
<h1 tabIndex="0" onKeyDown={this._keyDown} ref={ssHead => this.sectionRefArr['ssHeadRef'] = ssHead}>{translate(this.sectionName)} <Equalizer/></h1>
<h1>{translate(this.sectionName)} <Equalizer/></h1>
{sectionMenuOpened ? this._getSectionMenu(translate, this.props.ship) : null }
</div>
{this._getSlots()}

View File

@@ -35,21 +35,6 @@ export default class StandardSlot extends TranslatedComponent {
constructor(props) {
super(props);
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);
}
}
/**
@@ -63,10 +48,7 @@ export default class StandardSlot extends TranslatedComponent {
let m = slot.m;
let classRating = m.class + m.rating;
let menu;
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 validMods = m == null ? [] : (Modifications.modules[m.grp].modifications || []);
let showModuleResistances = Persist.showModuleResistances();
let mass = m.getMass() || m.cargo || m.fuel || 0;
@@ -74,9 +56,6 @@ export default class StandardSlot extends TranslatedComponent {
let modTT = translate('modified');
if (m && m.blueprint && m.blueprint.name) {
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 = (
<div>
<div>{modTT}</div>
@@ -100,7 +79,6 @@ export default class StandardSlot extends TranslatedComponent {
ship={ship}
m={m}
marker={modificationsMarker}
modButton = {this.modButton}
/>;
} else {
menu = <AvailableModulesMenu
@@ -111,15 +89,14 @@ export default class StandardSlot extends TranslatedComponent {
onSelect={onSelect}
warning={warning}
diffDetails={diffDetails.bind(ship, this.context.language)}
slotDiv = {this.slotDiv}
/>;
}
}
return (
<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('slot', { selected: this.props.selected })} onClick={this.props.onOpen} onContextMenu={stopCtxPropagation}>
<div className={cn('details-container', { warning: warning && warning(slot.m), disabled: m.grp !== 'bh' && !slot.enabled })}>
<div className={'sz'}>{m.grp == 'bh' ? m.name.charAt(0) : slot.maxClass}</div>
<div className={'sz'}>{slot.maxClass}</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={'r'}>{formats.round(mass)}{units.T}</div>
@@ -141,7 +118,7 @@ export default class StandardSlot extends TranslatedComponent {
{ showModuleResistances && m.getKineticResistance() ? <div className='l'>{translate('kinres')}: {formats.pct(m.getKineticResistance())}</div> : null }
{ showModuleResistances && m.getThermalResistance() ? <div className='l'>{translate('thermres')}: {formats.pct(m.getThermalResistance())}</div> : null }
{ m.getIntegrity() ? <div className='l'>{translate('integrity')}: {formats.int(m.getIntegrity())}</div> : null }
{ 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 }
{ 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 }
</div>
</div>
</div>

View File

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

View File

@@ -228,72 +228,6 @@ 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)
*/

View File

@@ -17,23 +17,12 @@ export default class UtilitySlotSection extends SlotSection {
constructor(props, context) {
super(props, context, 'utility', 'utility mounts');
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() {
this.selectedRefId = this.firstRefId;
this.props.ship.emptyUtility();
this.props.onChange();
this._close();
@@ -47,9 +36,6 @@ export default class UtilitySlotSection extends SlotSection {
* @param {Synthetic} event 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.onChange();
this._close();
@@ -108,28 +94,28 @@ export default class UtilitySlotSection extends SlotSection {
return <div className='select' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
<ul>
<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._empty}>{translate('empty all')}</li>
<li className='optional-hide' style={{ textAlign: 'center', marginTop: '1em' }}>{translate('PHRASE_ALT_ALL')}</li>
</ul>
<div className='select-group cap'>{translate('sb')}</div>
<ul>
<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' tabIndex='0' onClick={_use.bind(this, 'sb', 'B', null)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['sb-B'] = smRef}>B</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' tabIndex='0' onClick={_use.bind(this, 'sb', 'D', null)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['sb-D'] = smRef}>D</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>
<li className='c' onClick={_use.bind(this, 'sb', 'A', null)}>A</li>
<li className='c' onClick={_use.bind(this, 'sb', 'B', null)}>B</li>
<li className='c' onClick={_use.bind(this, 'sb', 'C', null)}>C</li>
<li className='c' onClick={_use.bind(this, 'sb', 'D', null)}>D</li>
<li className='c' onClick={_use.bind(this, 'sb', 'E', null)}>E</li>
</ul>
<div className='select-group cap'>{translate('hs')}</div>
<ul>
<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>
<li className='lc' onClick={_use.bind(this, 'hs', null, 'Heat Sink Launcher')}>{translate('Heat Sink Launcher')}</li>
</ul>
<div className='select-group cap'>{translate('ch')}</div>
<ul>
<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>
<li className='lc' onClick={_use.bind(this, 'ch', null, 'Chaff Launcher')}>{translate('Chaff Launcher')}</li>
</ul>
<div className='select-group cap'>{translate('po')}</div>
<ul>
<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>
<li className='lc' onClick={_use.bind(this, 'po', null, 'Point Defence')}>{translate('Point Defence')}</li>
</ul>
</div>;
}

View File

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

View File

@@ -16,8 +16,6 @@
"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_FIFTY": "50% of full values for this blueprint",
"PHRASE_BLUEPRINT_SEVEN_FIVE": "75% of full 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",
@@ -25,7 +23,6 @@
"PHRASE_SELECT_SPECIAL": "Click to select an experimental effect",
"PHRASE_NO_SPECIAL": "No experimental effect",
"PHRASE_SHOPPING_LIST": "Stations that sell this build",
"PHRASE_SHOPPING_MATS": "Materials needed for 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",
@@ -61,10 +58,8 @@
"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_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",
"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",
@@ -120,10 +115,6 @@
"pl": "Pulse Laser",
"po": "Point Defence",
"pp": "Power Plant",
"gpp": "Guardian Hybrid Power Plant",
"gpd": "Guardian Hybrid Power Distributor",
"gpc": "Guardian Plasma Charger",
"ggc": "Guardian Gauss Cannon",
"psg": "Prismatic Shield Generator",
"pv": "Planetary Vehicle Hangar",
"rf": "Refinery",
@@ -141,7 +132,6 @@
"ul": "Burst Laser",
"ws": "Frame Shift Wake Scanner",
"rpl": "Repair Limpet Controller",
"rcpl": "Recon Limpet Controller",
"xs": "Xeno Scanner",
"emptyrestricted": "empty (restricted)",
"damage dealt to": "Damage dealt to",
@@ -180,11 +170,9 @@
"internal protection": "Internal protection",
"external protection": "External protection",
"engagement range": "Engagement range",
"boost time": "Boost time",
"total": "Total",
"ammo": "Ammunition maximum",
"boot": "Boot time",
"hacktime": "Hack time",
"brokenregen": "Broken regeneration rate",
"burst": "Burst",
"burstrof": "Burst rate of fire",
@@ -219,7 +207,6 @@
"scanrate": "Scan rate",
"scantime": "Scan time",
"shield": "Shield",
"armour": "Armour",
"shieldboost": "Shield boost",
"shieldreinforcement": "Shield reinforcement",
"shotspeed": "Shot speed",

View File

@@ -1,16 +0,0 @@
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';

File diff suppressed because one or more lines are too long

View File

@@ -41,9 +41,6 @@ export default class AboutPage extends Page {
<h3>Chat</h3>
<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>;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -42,7 +42,7 @@ function countInt(slot) {
passSlotType = 'pcq';
passSlotRating = 'B';
}
let passengerBay = passSlotType ? ModuleUtils.findMaxInternal(passSlotType, slot.maxClass, passSlotRating) : null;
let passengerBay = passSlotType ? ModuleUtils.findInternal(passSlotType, slot.maxClass, passSlotRating) : null;
this.maxPassengers += passengerBay ? passengerBay.passengers : 0;
}
@@ -194,7 +194,6 @@ export default class ShipyardPage extends Page {
<td className='cn'>{s.standard[3]}</td>
<td className='cn'>{s.standard[4]}</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[2] })}>{s.hp[2]}</td>
<td className={cn({ disabled: !s.hp[3] })}>{s.hp[3]}</td>
@@ -274,11 +273,11 @@ export default class ShipyardPage extends Page {
for (let s of shipSummaries) {
let shipSortValue = s[shipPredicate];
if(shipPredicateIndex != undefined) {
if( shipPredicateIndex != undefined ) {
shipSortValue = shipSortValue[shipPredicateIndex];
}
if(shipSortValue != lastShipSortValue) {
if( shipSortValue != lastShipSortValue ) {
backgroundHighlight = !backgroundHighlight;
lastShipSortValue = shipSortValue;
}
@@ -330,7 +329,7 @@ export default class ShipyardPage extends Page {
<th>&nbsp;</th>
<th colSpan={4}>{translate('base')}</th>
<th colSpan={5}>{translate('max')}</th>
<th className='lft' colSpan={7}></th>
<th className='lft' colSpan={6}></th>
<th className='lft' colSpan={5}></th>
<th className='lft' colSpan={8}></th>
</tr>
@@ -346,9 +345,9 @@ export default class ShipyardPage extends Page {
<th className='sortable' onClick={sortShips('topBoost')}>{translate('boost')}</th>
<th className='sortable' onClick={sortShips('maxJumpRange')}>{translate('jump')}</th>
<th className='sortable' onClick={sortShips('maxCargo')}>{translate('cargo')}</th>
<th className='sortable' onClick={sortShips('maxPassengers')}>{translate('pax')}</th>
<th className='sortable' onClick={sortShips('maxPassengers')}>{translate('passengers')}</th>
<th className='lft' colSpan={7}>{translate('core module classes')}</th>
<th className='lft' colSpan={6}>{translate('core module classes')}</th>
<th colSpan={5} className='sortable lft' onClick={sortShips('hpCount')}>{translate('hardpoints')}</th>
<th colSpan={8} className='sortable lft' onClick={sortShips('intCount')}>{translate('internal compartments')}</th>
</tr>
@@ -370,7 +369,7 @@ export default class ShipyardPage extends Page {
<th className='sortable' onMouseEnter={termtip.bind(null, 'life support')} onMouseLeave={hide} onClick={sortShips('standard', 3)}>{'ls'}</th>
<th className='sortable' onMouseEnter={termtip.bind(null, 'power distriubtor')} onMouseLeave={hide} onClick={sortShips('standard', 4)}>{'pd'}</th>
<th className='sortable' onMouseEnter={termtip.bind(null, 'sensors')} onMouseLeave={hide} onClick={sortShips('standard', 5)}>{'s'}</th>
<th className='sortable' onMouseEnter={termtip.bind(null, 'fuel tank')} onMouseLeave={hide} onClick={sortShips('standard', 6)}>{'ft'}</th>
<th className='sortable lft' onClick={sortShips('hp',1)}>{translate('S')}</th>
<th className='sortable' onClick={sortShips('hp', 2)}>{translate('M')}</th>
<th className='sortable' onClick={sortShips('hp', 3)}>{translate('L')}</th>

View File

@@ -45,7 +45,7 @@ export function totalJumpRange(mass, fsd, fuel) {
* @param {number} baseShield Base Shield strength MJ for ship
* @param {object} sg The shield generator used
* @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) {
// sg might be a module or a template; handle either here
@@ -55,6 +55,7 @@ export function shieldStrength(mass, baseShield, sg, multiplier) {
let minMul = sg instanceof Module ? sg.getMinMul() : sg.minmul;
let optMul = sg instanceof Module ? sg.getOptMul() : sg.optmul;
let maxMul = sg instanceof Module ? sg.getMaxMul() : sg.maxmul;
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 ynorm = Math.pow(xnorm, exponent);
@@ -452,19 +453,6 @@ export function shieldMetrics(ship, sys) {
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
* @param {Object} ship The ship

View File

@@ -9,16 +9,12 @@ export const StandardArray = [
'pd', // Power Distributor
's', // Sensors
'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
export const ModuleGroupToName = {
// Standard
pp: 'Power Plant',
gpp: 'Guardian Hybrid Power Plant',
gpd: 'Guardian Hybrid Power Distributor',
t: 'Thrusters',
fsd: 'Frame Shift Drive',
ls: 'Life Support',
@@ -79,10 +75,7 @@ export const ModuleGroupToName = {
sb: 'Shield Booster',
tp: 'Torpedo Pylon',
sfn: 'Shutdown Field Neutraliser',
xs: 'Xeno Scanner',
rcpl: 'Recon Limpet Controller',
gpc: 'Guardian Plasma Charger',
ggc: 'Guardian Gauss Cannon',
xs: 'Xeno Scanner'
};
let GrpNameToCodeMap = {};

View File

@@ -64,6 +64,7 @@ export default class Module {
}
}
}
// Sanitise the resultant value to 4dp equivalent
return isNaN(result) ? result : Math.round(result);
}
@@ -78,9 +79,6 @@ export default class Module {
if (!this.mods) {
this.mods = {};
}
if (!this.origVals) {
this.origVals = {};
}
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];
@@ -119,6 +117,7 @@ export default class Module {
_getModifiedValue(name) {
const modification = Modifications.modifications[name];
let result = this[name];
if (!result) {
if (modification && modification.method === 'additive') {
// Additive modifications start at 0 rather than NULL
@@ -722,13 +721,4 @@ export default class Module {
getTime() {
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,6 +63,7 @@ export function standard(type, id) {
if (!isNaN(type)) {
type = StandardArray[type];
}
let s = Modules.standard[type].find(e => e.id == id || (e.class == id.charAt(0) && e.rating == id.charAt(1)));
if (s) {
s = new Module({ template: s });
@@ -195,29 +196,6 @@ export function findInternal(groupName, clss, rating, name) {
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.
* At least one ofGroup name or unique module name must be provided

View File

@@ -437,15 +437,12 @@ export default class Ship {
m.blueprint = bp;
this.clearModifications(m);
// Set any hidden items for the blueprint now
if (m.blueprint.grades[m.blueprint.grade] && m.blueprint.grades[m.blueprint.grade].features) {
const features = m.blueprint.grades[m.blueprint.grade].features;
for (const featureName in features) {
if (Modifications.modifications[featureName].hidden) {
this.setModification(m, featureName, bp.grades[bp.grade].features[featureName][0]);
}
const features = m.blueprint.grades[m.blueprint.grade].features;
for (const featureName in features) {
if (Modifications.modifications[featureName].hidden) {
this.setModification(m, featureName, bp.grades[bp.grade].features[featureName][0]);
}
}
this.updateModificationsString();
}
@@ -467,17 +464,7 @@ export default class Ship {
if (m.blueprint) {
m.blueprint.special = special;
}
this.updatePowerGenerated()
.updatePowerUsed()
.recalculateMass()
.updateJumpStats()
.recalculateShield()
.recalculateShieldCells()
.recalculateArmour()
.recalculateDps()
.recalculateEps()
.recalculateHps()
.updateMovement();
this.recalculateDps().recalculateHps().recalculateEps();
}
/**
@@ -503,6 +490,7 @@ export default class Ship {
// Value passed is invalid; reset it to 0
value = 0;
}
// Handle special cases
if (name === 'pgen') {
// Power generation
@@ -581,7 +569,6 @@ export default class Ship {
// Reset Cumulative stats
this.fuelCapacity = 0;
this.cargoCapacity = 0;
this.passengerCapacity = 0;
this.ladenMass = 0;
this.armour = this.baseArmour;
this.shield = this.baseShieldStrength;
@@ -629,7 +616,7 @@ export default class Ship {
standard[i].cat = 0;
standard[i].priority = priorities && priorities[i + 1] ? priorities[i + 1] * 1 : 0;
standard[i].type = 'SYS';
standard[i].m = null; // Resetting 'old' module if there was one
standard[i].m = null; // Resetting 'old' modul if there was one
standard[i].discountedCost = 0;
if (comps) {
let module = ModuleUtils.standard(i, comps.standard[i]);
@@ -783,7 +770,7 @@ export default class Ship {
// Alter as required due to changes in the (build) code from one version to the next
this.upgradeInternals(internal, 1 + this.standard.length + this.hardpoints.length, priorities, enabled, modifications, blueprints, version);
}
return this.buildWith(
{
bulkheads: code.charAt(0) * 1,
@@ -798,6 +785,14 @@ export default class Ship {
);
};
buildFromLoadout(Modules) {
let internal = this.internal,
standard = this.standard,
hps = this.hardpoints,
cl = standard.length,
i, l;
}
/**
* Empties all hardpoints and utility slots
* @return {this} The current ship instance for chaining
@@ -1201,7 +1196,6 @@ export default class Ship {
let unladenMass = this.hullMass;
let cargoCapacity = 0;
let fuelCapacity = 0;
let passengerCapacity = 0;
unladenMass += this.bulkheads.m.getMass();
@@ -1223,10 +1217,6 @@ export default class Ship {
fuelCapacity += slot.m.fuel;
} else if (slot.m.grp === 'cr') {
cargoCapacity += slot.m.cargo;
} else if (slot.m.grp.slice(0,2) === 'pc') {
if (slot.m.passengers) {
passengerCapacity += slot.m.passengers;
}
}
}
}
@@ -1242,7 +1232,6 @@ export default class Ship {
this.unladenMass = unladenMass;
this.cargoCapacity = cargoCapacity;
this.fuelCapacity = fuelCapacity;
this.passengerCapacity = passengerCapacity;
this.ladenMass = unladenMass + fuelCapacity + cargoCapacity;
return this;

View File

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

View File

@@ -13,7 +13,6 @@ const LS_KEY_STATE = 'state';
const LS_KEY_SIZE_RATIO = 'sizeRatio';
const LS_KEY_TOOLTIPS = 'tooltips';
const LS_KEY_MODULE_RESISTANCES = 'moduleResistances';
const LS_KEY_ROLLS = 'matsPerGrade';
let LS;
@@ -85,7 +84,6 @@ export class Persist extends EventEmitter {
}
let moduleResistances = _get(LS_KEY_MODULE_RESISTANCES);
let matsPerGrade = _get(LS_KEY_ROLLS);
let tips = _get(LS_KEY_TOOLTIPS);
let insurance = _getString(LS_KEY_INSURANCE);
let shipDiscount = _get(LS_KEY_SHIP_DISCOUNT);
@@ -104,13 +102,6 @@ export class Persist extends EventEmitter {
this.outfittingTab = _getString(LS_KEY_OUTFITTING_TAB);
this.state = _get(LS_KEY_STATE);
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.moduleResistancesEnabled = moduleResistances === null ? true : moduleResistances;
@@ -161,10 +152,6 @@ export class Persist extends EventEmitter {
this.moduleResistancesEnabled = !!newValue && newValue.toLowerCase() == 'true';
this.emit('moduleresistances', this.moduleResistancesEnabled);
break;
case LS_KEY_ROLLS:
this.matsPerGrade = JSON.parse(newValue);
this.emit('matsPerGrade', this.matsPerGrade);
break;
}
} catch (e) {
// On JSON.Parse Error - don't sync or do anything
@@ -470,23 +457,6 @@ export class Persist extends EventEmitter {
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
* @param {number} tabName Cost tab name

View File

@@ -1,430 +1,371 @@
import React from 'react';
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
* @param {Object} translate The translate object
* @param {Object} blueprint The blueprint at the required grade
* @param {Array} engineers The engineers supplying this blueprint
* @param {string} grp The group of the module
* @param {Object} m The module to compare with
* @returns {Object} The react components
*/
export function blueprintTooltip(translate, blueprint, engineers, grp, m) {
const effects = [];
if (!blueprint || !blueprint.features) {
return undefined;
}
for (const feature in blueprint.features) {
const featureIsBeneficial = isBeneficial(feature, blueprint.features[feature]);
const featureDef = Modifications.modifications[feature];
if (!featureDef.hidden) {
let symbol = '';
if (feature === 'jitter') {
symbol = '°';
} else if (featureDef.type === 'percentage') {
symbol = '%';
}
let lowerBound = blueprint.features[feature][0];
let upperBound = blueprint.features[feature][1];
if (featureDef.type === 'percentage') {
lowerBound = Math.round(lowerBound * 1000) / 10;
upperBound = Math.round(upperBound * 1000) / 10;
}
const lowerIsBeneficial = isValueBeneficial(feature, lowerBound);
const upperIsBeneficial = isValueBeneficial(feature, upperBound);
if (m) {
// We have a module - add in the current value
let current = m.getModValue(feature);
if (featureDef.type === 'percentage' || featureDef.name === 'burst' || featureDef.name === 'burstrof') {
current = Math.round(current / 10) / 10;
} else if (featureDef.type === 'numeric') {
current /= 100;
}
const currentIsBeneficial = isValueBeneficial(feature, current);
effects.push(
<tr key={feature}>
<td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td>
<td className={lowerBound === 0 ? '' : lowerIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{lowerBound}{symbol}</td>
<td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{current}{symbol}</td>
<td className={upperBound === 0 ? '' : upperIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{upperBound}{symbol}</td>
</tr>
);
} else {
// We do not have a module, no value
effects.push(
<tr key={feature}>
<td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td>
<td className={lowerBound === 0 ? '' : lowerIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{lowerBound}{symbol}</td>
<td className={upperBound === 0 ? '' : upperIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{upperBound}{symbol}</td>
</tr>
);
}
}
}
if (m) {
// Because we have a module add in any benefits that aren't part of the primary blueprint
for (const feature in m.mods) {
if (!blueprint.features[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);
if (featureDef.type === 'percentage' || featureDef.name === 'burst' || featureDef.name === 'burstrof') {
current = Math.round(current / 10) / 10;
} else if (featureDef.type === 'numeric') {
current /= 100;
}
const currentIsBeneficial = isValueBeneficial(feature, current);
effects.push(
<tr key={feature}>
<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>
);
}
}
}
// 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]) {
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);
if (featureDef.type === 'percentage' || featureDef.name === 'burst' || featureDef.name === 'burstrof') {
current = Math.round(current / 10) / 10;
} else if (featureDef.type === 'numeric') {
current /= 100;
}
const currentIsBeneficial = isValueBeneficial(feature, current);
effects.push(
<tr key={feature}>
<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>
);
}
}
}
}
}
let components;
if (!m) {
components = [];
for (const component in blueprint.components) {
components.push(
<tr key={component}>
<td style={{ textAlign: 'left' }}>{translate(component)}</td>
<td style={{ textAlign: 'right' }}>{blueprint.components[component]}</td>
</tr>
);
}
}
let engineersList;
if (engineers) {
engineersList = [];
for (const engineer of engineers) {
engineersList.push(
<tr key={engineer}>
<td style={{ textAlign: 'left' }}>{engineer}</td>
</tr>
);
}
}
return (
<div>
<table width='100%'>
<thead>
<tr>
<td>{translate('feature')}</td>
<td>{translate('worst')}</td>
{m ? <td>{translate('current')}</td> : null }
<td>{translate('best')}</td>
</tr>
</thead>
<tbody>
{effects}
</tbody>
</table>
{ components ? <table width='100%'>
<thead>
<tr>
<td>{translate('component')}</td>
<td>{translate('amount')}</td>
</tr>
</thead>
<tbody>
{components}
</tbody>
</table> : null }
{ engineersList ? <table width='100%'>
<thead>
<tr>
<td>{translate('engineers')}</td>
</tr>
</thead>
<tbody>
{engineersList}
</tbody>
</table> : null }
</div>
);
}
/**
* Is this blueprint feature beneficial?
* @param {string} feature The name of the feature
* @param {array} values The value of the feature
* @returns {boolean} True if this feature is beneficial
*/
export function isBeneficial(feature, values) {
const fact = (values[0] < 0 || (values[0] === 0 && values[1] < 0));
if (Modifications.modifications[feature].higherbetter) {
return !fact;
} else {
return fact;
}
}
/**
* Is this feature value beneficial?
* @param {string} feature The name of the feature
* @param {number} value The value of the feature
* @returns {boolean} True if this value is beneficial
*/
export function isValueBeneficial(feature, value) {
if (Modifications.modifications[feature].higherbetter) {
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
* @param {Object} module The module for which to obtain this blueprint
* @returns {Object} The matching blueprint
*/
export function getBlueprint(name, module) {
// Start with a copy of the blueprint
const findMod = val => Object.keys(Modifications.blueprints).find(elem => elem.toString().toLowerCase().search(val.toString().toLowerCase().replace(/(OutfittingFieldType_|persecond)/igm, '')) >= 0);
const found = Modifications.blueprints[findMod(name)];
if (!found || !found.fdname) {
return {};
}
const blueprint = JSON.parse(JSON.stringify(found));
if (module) {
if (module.grp === 'sb') {
// Shield boosters are treated internally as straight modifiers, so rather than (for example)
// being a 4% boost they are a 104% multiplier. We need to fix the values here so that they look
// accurate as per the information in Elite
for (const grade in blueprint.grades) {
for (const feature in blueprint.grades[grade].features) {
if (feature === 'shieldboost') {
blueprint.grades[grade].features[feature][0] = ((1 + blueprint.grades[grade].features[feature][0]) * (1 + module.shieldboost) - 1) / module.shieldboost - 1;
blueprint.grades[grade].features[feature][1] = ((1 + blueprint.grades[grade].features[feature][1]) * (1 + module.shieldboost) - 1) / module.shieldboost - 1;
}
}
}
}
}
return blueprint;
}
/**
* Provide 'percent' primary modifications
* @param {Object} ship The ship 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 setPercent(ship, m, percent) {
ship.clearModifications(m);
// Pick given value as multiplier
const mult = percent / 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);
}
}
/**
* Provide 'random' 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 setRandom(ship, m) {
// Pick a single value for our randomness
setPercent(ship, m, Math.random() * 100);
}
/**
* Set a modification feature value
* @param {Object} ship The ship for which to perform the modifications
* @param {Object} m The module for which to perform the modifications
* @param {string} featureName The feature being set
* @param {number} value The value being set for the feature
*/
function _setValue(ship, m, featureName, value) {
if (Modifications.modifications[featureName].type == 'percentage') {
ship.setModification(m, featureName, value * 10000);
} else if (Modifications.modifications[featureName].type == 'numeric') {
ship.setModification(m, featureName, value * 100);
} else {
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);
}
}
import React from 'react';
import { Modifications } from 'coriolis-data/dist';
/**
* Generate a tooltip with details of a blueprint's effects
* @param {Object} translate The translate object
* @param {Object} blueprint The blueprint at the required grade
* @param {Array} engineers The engineers supplying this blueprint
* @param {string} grp The group of the module
* @param {Object} m The module to compare with
* @returns {Object} The react components
*/
export function blueprintTooltip(translate, blueprint, engineers, grp, m) {
const effects = [];
for (const feature in blueprint.features) {
const featureIsBeneficial = isBeneficial(feature, blueprint.features[feature]);
const featureDef = Modifications.modifications[feature];
if (!featureDef.hidden) {
let symbol = '';
if (feature === 'jitter') {
symbol = '°';
} else if (featureDef.type === 'percentage') {
symbol = '%';
}
let lowerBound = blueprint.features[feature][0];
let upperBound = blueprint.features[feature][1];
if (featureDef.type === 'percentage') {
lowerBound = Math.round(lowerBound * 1000) / 10;
upperBound = Math.round(upperBound * 1000) / 10;
}
const lowerIsBeneficial = isValueBeneficial(feature, lowerBound);
const upperIsBeneficial = isValueBeneficial(feature, upperBound);
if (m) {
// We have a module - add in the current value
let current = m.getModValue(feature);
if (featureDef.type === 'percentage' || featureDef.name === 'burst' || featureDef.name === 'burstrof') {
current = Math.round(current / 10) / 10;
} else if (featureDef.type === 'numeric') {
current /= 100;
}
const currentIsBeneficial = isValueBeneficial(feature, current);
effects.push(
<tr key={feature}>
<td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td>
<td className={lowerBound === 0 ? '' : lowerIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{lowerBound}{symbol}</td>
<td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{current}{symbol}</td>
<td className={upperBound === 0 ? '' : upperIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{upperBound}{symbol}</td>
</tr>
);
} else {
// We do not have a module, no value
effects.push(
<tr key={feature}>
<td style={{ textAlign: 'left' }}>{translate(feature, grp)}</td>
<td className={lowerBound === 0 ? '' : lowerIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{lowerBound}{symbol}</td>
<td className={upperBound === 0 ? '' : upperIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{upperBound}{symbol}</td>
</tr>
);
}
}
}
if (m) {
// Because we have a module add in any benefits that aren't part of the primary blueprint
for (const feature in m.mods) {
if (!blueprint.features[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);
if (featureDef.type === 'percentage' || featureDef.name === 'burst' || featureDef.name === 'burstrof') {
current = Math.round(current / 10) / 10;
} else if (featureDef.type === 'numeric') {
current /= 100;
}
const currentIsBeneficial = isValueBeneficial(feature, current);
effects.push(
<tr key={feature}>
<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>
);
}
}
}
// 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]) {
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);
if (featureDef.type === 'percentage' || featureDef.name === 'burst' || featureDef.name === 'burstrof') {
current = Math.round(current / 10) / 10;
} else if (featureDef.type === 'numeric') {
current /= 100;
}
const currentIsBeneficial = isValueBeneficial(feature, current);
effects.push(
<tr key={feature}>
<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>
);
}
}
}
}
}
let components;
if (!m) {
components = [];
for (const component in blueprint.components) {
components.push(
<tr key={component}>
<td style={{ textAlign: 'left' }}>{translate(component)}</td>
<td style={{ textAlign: 'right' }}>{blueprint.components[component]}</td>
</tr>
);
}
}
let engineersList;
if (engineers) {
engineersList = [];
for (const engineer of engineers) {
engineersList.push(
<tr key={engineer}>
<td style={{ textAlign: 'left' }}>{engineer}</td>
</tr>
);
}
}
return (
<div>
<table width='100%'>
<thead>
<tr>
<td>{translate('feature')}</td>
<td>{translate('worst')}</td>
{m ? <td>{translate('current')}</td> : null }
<td>{translate('best')}</td>
</tr>
</thead>
<tbody>
{effects}
</tbody>
</table>
{ components ? <table width='100%'>
<thead>
<tr>
<td>{translate('component')}</td>
<td>{translate('amount')}</td>
</tr>
</thead>
<tbody>
{components}
</tbody>
</table> : null }
{ engineersList ? <table width='100%'>
<thead>
<tr>
<td>{translate('engineers')}</td>
</tr>
</thead>
<tbody>
{engineersList}
</tbody>
</table> : null }
</div>
);
}
/**
* Is this blueprint feature beneficial?
* @param {string} feature The name of the feature
* @param {array} values The value of the feature
* @returns {boolean} True if this feature is beneficial
*/
export function isBeneficial(feature, values) {
const fact = (values[0] < 0 || (values[0] === 0 && values[1] < 0));
if (Modifications.modifications[feature].higherbetter) {
return !fact;
} else {
return fact;
}
}
/**
* Is this feature value beneficial?
* @param {string} feature The name of the feature
* @param {number} value The value of the feature
* @returns {boolean} True if this value is beneficial
*/
export function isValueBeneficial(feature, value) {
if (Modifications.modifications[feature].higherbetter) {
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
* @param {Object} module The module for which to obtain this blueprint
* @returns {Object} The matching blueprint
*/
export function getBlueprint(name, module) {
// Start with a copy of the blueprint
const blueprint = JSON.parse(JSON.stringify(Modifications.blueprints[name]));
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') {
// Shield boosters are treated internally as straight modifiers, so rather than (for example)
// being a 4% boost they are a 104% multiplier. We need to fix the values here so that they look
// accurate as per the information in Elite
for (const grade in blueprint.grades) {
for (const feature in blueprint.grades[grade].features) {
if (feature === 'shieldboost') {
blueprint.grades[grade].features[feature][0] = ((1 + blueprint.grades[grade].features[feature][0]) * (1 + module.shieldboost) - 1) / module.shieldboost - 1;
blueprint.grades[grade].features[feature][1] = ((1 + blueprint.grades[grade].features[feature][1]) * (1 + module.shieldboost) - 1) / module.shieldboost - 1;
}
}
}
}
}
return blueprint;
}
/**
* Provide 'worst' 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 setWorst(ship, m) {
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);
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][0];
} else {
value = features[featureName][1];
}
} 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][1];
} else {
value = features[featureName][0];
}
}
_setValue(ship, m, featureName, value);
}
}
/**
* Provide 'random' 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 setRandom(ship, m) {
ship.clearModifications(m);
// Pick a single value for our randomness
const mult = Math.random();
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);
}
}
/**
* Set a modification feature value
* @param {Object} ship The ship for which to perform the modifications
* @param {Object} m The module for which to perform the modifications
* @param {string} featureName The feature being set
* @param {number} value The value being set for the feature
*/
function _setValue(ship, m, featureName, value) {
if (Modifications.modifications[featureName].type == 'percentage') {
ship.setModification(m, featureName, value * 10000);
} else if (Modifications.modifications[featureName].type == 'numeric') {
ship.setModification(m, featureName, value * 100);
} else {
ship.setModification(m, featureName, value);
}
}

View File

@@ -6,7 +6,7 @@ import { getBlueprint } from '../utils/BlueprintFunctions';
import * as ModuleUtils from '../shipyard/ModuleUtils';
// mapping from fd's ship model names to coriolis'
const SHIP_FD_NAME_TO_CORIOLIS_NAME = {
export const SHIP_FD_NAME_TO_CORIOLIS_NAME = {
'Adder': 'adder',
'Anaconda': 'anaconda',
'Asp': 'asp',
@@ -43,7 +43,7 @@ const SHIP_FD_NAME_TO_CORIOLIS_NAME = {
};
// Mapping from hardpoint class to name in companion API
export const HARDPOINT_NUM_TO_CLASS = {
const HARDPOINT_NUM_TO_CLASS = {
0: 'Tiny',
1: 'Small',
2: 'Medium',
@@ -51,7 +51,6 @@ export const HARDPOINT_NUM_TO_CLASS = {
4: 'Huge'
};
/**
* Obtain a module given its ED ID
* @param {Integer} edId the Elite ID of the module
@@ -106,7 +105,7 @@ function _moduleFromEdId(edId) {
* @return {string} the Coriolis model of the ship
*/
function _shipModelFromEDName(edName) {
return SHIP_FD_NAME_TO_CORIOLIS_NAME[Object.keys(SHIP_FD_NAME_TO_CORIOLIS_NAME).find(elem => elem.toLowerCase() === edName.toLowerCase())];
return SHIP_FD_NAME_TO_CORIOLIS_NAME[edName];
}
/**
@@ -115,7 +114,7 @@ function _shipModelFromEDName(edName) {
* @return {string} the Coriolis model of the ship
*/
export function shipModelFromJson(json) {
return _shipModelFromEDName(json.name || json.Ship);
return _shipModelFromEDName(json.name);
}
/**
@@ -145,18 +144,18 @@ export function shipFromJson(json) {
}
let rootModule;
// Add the bulkheads
const armourJson = json.modules.Armour.module;
if (armourJson.name.toLowerCase().endsWith('_armour_grade1')) {
if (armourJson.name.endsWith('_Armour_Grade1')) {
ship.useBulkhead(0, true);
} else if (armourJson.name.toLowerCase().endsWith('_armour_grade2')) {
} else if (armourJson.name.endsWith('_Armour_Grade2')) {
ship.useBulkhead(1, true);
} else if (armourJson.name.toLowerCase().endsWith('_armour_grade3')) {
} else if (armourJson.name.endsWith('_Armour_Grade3')) {
ship.useBulkhead(2, true);
} else if (armourJson.name.toLowerCase().endsWith('_armour_mirrored')) {
} else if (armourJson.name.endsWith('_Armour_Mirrored')) {
ship.useBulkhead(3, true);
} else if (armourJson.name.toLowerCase().endsWith('_armour_reactive')) {
} else if (armourJson.name.endsWith('_Armour_Reactive')) {
ship.useBulkhead(4, true);
} else {
throw 'Unknown bulkheads "' + armourJson.name + '"';
@@ -318,7 +317,7 @@ function _addModifications(module, modifiers, blueprint, grade, specialModificat
if (!modifiers) return;
let special;
if (specialModifications) {
special = Modifications.specials[Object.keys(specialModifications)[0]];
special = Modifications.specials[Object.keys(specialModifications)[0]]
}
for (const i in modifiers) {
// Some special modifications
@@ -345,7 +344,7 @@ function _addModifications(module, modifiers, blueprint, grade, specialModificat
let value;
if (i === 'OutfittingFieldType_DefenceModifierShieldMultiplier') {
value = modifiers[i].value - 1;
} else if (i === 'OutfittingFieldType_DefenceModifierHealthMultiplier' && blueprint.startsWith('Armour_')) {
} 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;

View File

@@ -1,291 +0,0 @@
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
*/
export default function shorternUrl(url, success, error) {
shortenUrlOrbis(url, success, error);
shortenUrlEddp(url, success, error);
}
const SHORTEN_API_GOOGLE = 'https://www.googleapis.com/urlshortener/v1/url?key=';
@@ -64,32 +64,3 @@ function shortenUrlEddp(url, success, error) {
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

@@ -38,7 +38,7 @@
<% } %>
<!-- Piwik -->
<!-- <script type="text/javascript">
<script type="text/javascript">
var _paq = _paq || [];
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
_paq.push(["setCookieDomain", "*.coriolis.edcd.io"]);
@@ -51,16 +51,14 @@
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>-->
</script>
<!-- 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>
<script
src="//d2wy8f7a9ursnm.cloudfront.net/bugsnag-3.min.js"
data-apikey="2382691c622937f28f8fa82a1bfd797a"></script>
</head>
<body style="background-color:#000;">
<section id="coriolis"></section>

View File

@@ -31,50 +31,6 @@ 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 {
margin-left: 0.5em;

View File

@@ -10,8 +10,6 @@
@secondary: #1FB0FF; // Light blue
@warning: #FF3B00; // Dark Orange
@disabled: #555; // Light grey
@success: #71a052; // Green
@purple: #800080; // Purple
@primary-disabled: darken(@primary, @disabledDarken);
@secondary-disabled: darken(@secondary, @disabledDarken);
@warning-disabled: darken(@warning, @disabledDarken);

View File

@@ -12,20 +12,6 @@
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 {
background-color: @bg;
margin: 0;

View File

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

View File

@@ -70,29 +70,10 @@
padding: 0.5em 0.2em;
font-size: 0.9em;
.summaryTable {
#summaryTable {
.user-select-none();
width: 100%;
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,10 +41,6 @@
overflow: hidden;
}
input.cb:focus {
border-color:#fff;
}
.l {
text-transform: capitalize;
margin-right: 0.8em;

View File

@@ -1,30 +1,28 @@
const path = require('path')
const exec = require('child_process').exec
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const WebpackNotifierPlugin = require('webpack-notifier')
const pkgJson = require('./package')
var path = require('path');
var exec = require('child_process').exec;
var webpack = require('webpack');
var pkgJson = require('./package');
var HtmlWebpackPlugin = require("html-webpack-plugin");
var ExtractTextPlugin = require("extract-text-webpack-plugin");
function CopyDirPlugin(source, destination) {
this.source = source
this.destination = destination
}
CopyDirPlugin.prototype.apply = function (compiler) {
compiler.plugin('done', () => {
console.log(compiler.outputPath, this.destination)
exec('cp -r ' + this.source + ' ' + path.join(compiler.outputPath, this.destination))
})
this.source = source;
this.destination = 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 = {
devtool: 'source-map',
devtool: 'eval',
devServer: {
headers: {'Access-Control-Allow-Origin': '*'}
headers: { "Access-Control-Allow-Origin": "*" }
},
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']
},
resolve: {
@@ -39,34 +37,33 @@ module.exports = {
plugins: [
new CopyDirPlugin(path.join(__dirname, 'src/.htaccess'), ''),
new webpack.optimize.CommonsChunkPlugin({
name: 'lib',
filename: 'lib.js'
name: 'lib',
filename: 'lib.js'
}),
new HtmlWebpackPlugin({
inject: false,
template: path.join(__dirname, 'src/index.ejs'),
version: pkgJson.version,
gapiKey: process.env.CORIOLIS_GAPI_KEY || ''
inject: false,
template: path.join(__dirname, "src/index.ejs"),
version: pkgJson.version,
gapiKey: process.env.CORIOLIS_GAPI_KEY || '',
}),
new ExtractTextPlugin({
filename: 'app.css',
disable: false,
allChunks: true
filename: 'app.css',
disable: false,
allChunks: true
}),
new WebpackNotifierPlugin({alwaysNotify: true}),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin()
],
module: {
rules: [
{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: /\.(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: /\.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: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: 'file-loader'},
{test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=image/svg+xml'}
{ 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: /\.(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: /\.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: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: 'file-loader' },
{ test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?limit=10000&mimetype=image/svg+xml' }
]
}
}
};

View File

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