mirror of
https://github.com/EDCD/coriolis.git
synced 2025-12-08 22:33:24 +00:00
Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f8a215d790 | ||
|
|
da69f3b2c8 | ||
|
|
fc442c1a42 | ||
|
|
cf59a6b9fd | ||
|
|
34afcd511a | ||
|
|
e2444a2e4e | ||
|
|
692516de9b | ||
|
|
d51009c823 | ||
|
|
2f121bef5e | ||
|
|
61f7d376d2 | ||
|
|
9e253012e6 | ||
|
|
e4e5b1327b | ||
|
|
6ef3b227b8 | ||
|
|
bf657a0945 | ||
|
|
c3dd1886c9 | ||
|
|
32498bb8a7 | ||
|
|
69bb90a0e4 | ||
|
|
8e2988edf0 | ||
|
|
876a352cfd | ||
|
|
84e44cabfa | ||
|
|
36a838d565 | ||
|
|
9ee8693f40 | ||
|
|
6f02965149 | ||
|
|
27ce82de3b | ||
|
|
3d5a9ef220 | ||
|
|
9b81f6efd2 | ||
|
|
3e77e23d71 | ||
|
|
120c032c82 | ||
|
|
46e15b8ecd | ||
|
|
d71d87041b | ||
|
|
124bd58b9f | ||
|
|
257b9b0562 | ||
|
|
b8e15f691d | ||
|
|
2255e3bfc4 | ||
|
|
8797d84605 | ||
|
|
719179a923 | ||
|
|
1d544099f6 | ||
|
|
9b131a762a | ||
|
|
08c5d2256a | ||
|
|
ed6ee4341f | ||
|
|
157c1148fb | ||
|
|
507ea9e09e | ||
|
|
af68cba7be | ||
|
|
224fbe0e8f | ||
|
|
07c936897c |
@@ -5,7 +5,7 @@
|
||||
"jsx": true,
|
||||
"classes": true,
|
||||
"modules": true
|
||||
},
|
||||
}
|
||||
},
|
||||
"env": {
|
||||
"browser": true,
|
||||
@@ -33,7 +33,6 @@
|
||||
"ClassDeclaration": true
|
||||
}
|
||||
}],
|
||||
"no-console": 2,
|
||||
"brace-style": [2, "1tbs", { "allowSingleLine": true }],
|
||||
"comma-style": [2, "last"],
|
||||
"indent": [2, 2, { "SwitchCase": 1, "VariableDeclarator": 2 }],
|
||||
|
||||
@@ -8,7 +8,8 @@ cache:
|
||||
directories:
|
||||
- node_modules
|
||||
|
||||
before_script:
|
||||
before_install:
|
||||
- git clone https://github.com/EDCD/coriolis-data.git ../coriolis-data
|
||||
|
||||
script:
|
||||
- npm run lint
|
||||
|
||||
11
package-lock.json
generated
11
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "coriolis_shipyard",
|
||||
"version": "2.9.14",
|
||||
"version": "2.9.17",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -10760,6 +10760,15 @@
|
||||
"prop-types": "^15.5.10"
|
||||
}
|
||||
},
|
||||
"react-ga": {
|
||||
"version": "2.5.3",
|
||||
"resolved": "https://registry.npmjs.org/react-ga/-/react-ga-2.5.3.tgz",
|
||||
"integrity": "sha512-25wvPv1PVLDLhw1gEYP33h0V2sJHahKMfUCAxhq8JPYmNQwx1fcjJAkJk+WmSqGN93lHLhExDkxy3SQizQnx3A==",
|
||||
"requires": {
|
||||
"prop-types": "^15.6.0",
|
||||
"react": "^15.6.2 || ^16.0"
|
||||
}
|
||||
},
|
||||
"react-measure": {
|
||||
"version": "1.4.7",
|
||||
"resolved": "https://registry.npmjs.org/react-measure/-/react-measure-1.4.7.tgz",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "coriolis_shipyard",
|
||||
"version": "2.9.14",
|
||||
"version": "2.9.17",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/EDCD/coriolis"
|
||||
@@ -109,6 +109,7 @@
|
||||
"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"
|
||||
|
||||
@@ -93,8 +93,8 @@ 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.log('Ship import data: ');
|
||||
console.log(json);
|
||||
console.info('Ship import data: ');
|
||||
console.info(json);
|
||||
let ship;
|
||||
if (json && json.modules) {
|
||||
ship = CompanionApiUtils.shipFromJson(json);
|
||||
@@ -355,6 +355,8 @@ 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>;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import Persist from './stores/Persist';
|
||||
|
||||
import ReactGA from 'react-ga';
|
||||
ReactGA.initialize('UA-55840909-18');
|
||||
let standalone = undefined;
|
||||
|
||||
/**
|
||||
@@ -257,9 +258,16 @@ Route.prototype.match = function(path, params) {
|
||||
* @param {string} path Path to track
|
||||
*/
|
||||
function gaTrack(path) {
|
||||
if (window.ga) {
|
||||
window.ga('send', 'pageview', path);
|
||||
const match = path.match(/\/outfit\/(.*)(\?code=.*)/);
|
||||
if (match) {
|
||||
if (match[1]) {
|
||||
ReactGA.ga('set', 'contentGroup1', match[1]);
|
||||
}
|
||||
if (match[2]) {
|
||||
ReactGA.ga('set', 'contentGroup2', match[2]);
|
||||
}
|
||||
}
|
||||
ReactGA.pageview(path);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -107,6 +107,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
warning: PropTypes.func,
|
||||
firstSlotId: PropTypes.string,
|
||||
lastSlotId: PropTypes.string,
|
||||
activeSlotId: PropTypes.string,
|
||||
slotDiv: PropTypes.object
|
||||
};
|
||||
|
||||
@@ -136,7 +137,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
let translate = context.language.translate;
|
||||
let { m, warning, shipMass, onSelect, modules, firstSlotId, lastSlotId } = props;
|
||||
let list, currentGroup;
|
||||
|
||||
|
||||
let buildGroup = this._buildGroup.bind(
|
||||
this,
|
||||
translate,
|
||||
@@ -148,7 +149,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
onSelect(m);
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
if (modules instanceof Array) {
|
||||
list = buildGroup(modules[0].grp, modules);
|
||||
} else {
|
||||
@@ -209,7 +210,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
}
|
||||
}
|
||||
let trackingFocus = false;
|
||||
return { list, currentGroup, trackingFocus};
|
||||
return { list, currentGroup, trackingFocus };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -221,15 +222,17 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
* @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;
|
||||
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]));
|
||||
@@ -239,7 +242,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
let m = sortedModules[i];
|
||||
let mount = null;
|
||||
let disabled = false;
|
||||
prevName = m.name
|
||||
prevName = m.name;
|
||||
if (ModuleUtils.isShieldGenerator(m.grp)) {
|
||||
// Shield generators care about maximum hull mass
|
||||
disabled = mass > m.maxmass;
|
||||
@@ -255,11 +258,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
});
|
||||
let eventHandlers;
|
||||
|
||||
if (disabled || active) {
|
||||
/**
|
||||
* ToDo: possibly create an "activeSlotId" variable to allow
|
||||
* focus to be set on active slot when slot menu is opened
|
||||
*/
|
||||
if (disabled) {
|
||||
eventHandlers = {
|
||||
onKeyDown: this._keyDown.bind(this, null),
|
||||
onKeyUp: this._keyUp.bind(this, null)
|
||||
@@ -271,6 +270,7 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
* 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);
|
||||
@@ -300,14 +300,14 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
elems.push(<br key={'b' + m.grp + i} />);
|
||||
itemsOnThisRow = 0;
|
||||
}
|
||||
let tbIdx = (classes.indexOf('disabled') < 0 && classes.indexOf('active') < 0) ? 0 : undefined;
|
||||
elems.push(
|
||||
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}>
|
||||
{mount}
|
||||
{(mount ? ' ' : '') + m.class + m.rating + (m.missile ? '/' + m.missile : '') + (m.name ? ' ' + translate(m.name) : '')}
|
||||
</li>
|
||||
);
|
||||
|
||||
|
||||
itemsOnThisRow++;
|
||||
prevClass = m.class;
|
||||
prevRating = m.rating;
|
||||
@@ -369,23 +369,22 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
* @param {Function} select Select module callback
|
||||
* @param {SyntheticEvent} event Event
|
||||
*/
|
||||
|
||||
_keyDown(select, event) {
|
||||
var className = event.currentTarget.attributes['class'].value;
|
||||
let className = event.currentTarget.attributes['class'].value;
|
||||
if (event.key == 'Enter' && className.indexOf('disabled') < 0 && className.indexOf('active') < 0) {
|
||||
select();
|
||||
return
|
||||
return;
|
||||
}
|
||||
var elemId = event.currentTarget.attributes['data-id'].value;
|
||||
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;
|
||||
return;
|
||||
}
|
||||
if (!event.shiftKey && elemId == this.lastSlotId) {
|
||||
event.preventDefault();
|
||||
this.slotItems[this.firstSlotId].focus();
|
||||
this.slotItems[this.firstSlotId].focus();
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -393,10 +392,11 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
|
||||
/**
|
||||
* Key Up
|
||||
*
|
||||
* @param {Function} select Select module callback
|
||||
* @param {SytheticEvent} event Event
|
||||
*/
|
||||
_keyUp(select,event) {
|
||||
//nothing here yet
|
||||
// nothing here yet
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -452,28 +452,26 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
/**
|
||||
* Scroll to mounted (if it exists) module group on mount
|
||||
*/
|
||||
|
||||
componentDidMount() {
|
||||
if (this.groupElem) { // Scroll to currently selected group
|
||||
this.node.scrollTop = this.groupElem.offsetTop;
|
||||
}
|
||||
|
||||
if (this.slotItems) {
|
||||
/**
|
||||
* Set focus on first focusable slot <li> after component mounts. May want to consider
|
||||
* changing this to the Active item instead.
|
||||
*/
|
||||
/**
|
||||
* 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) {
|
||||
console.log("AvailableModulesMenu component will unmount. Set focus to slot");
|
||||
this.props.slotDiv.focus();
|
||||
} else {
|
||||
console.log("AvailableModulesMenu component will unmount. No slotDiv prop present.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -491,7 +489,6 @@ export default class AvailableModulesMenu extends TranslatedComponent {
|
||||
* @return {React.Component} List
|
||||
*/
|
||||
render() {
|
||||
console.log("Tracking focus? " + this.state.trackingFocus);
|
||||
return (
|
||||
<div ref={node => this.node = node}
|
||||
className={cn('select', this.props.className)}
|
||||
|
||||
@@ -17,14 +17,25 @@ 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Empty all slots
|
||||
*/
|
||||
_empty() {
|
||||
this.selectedRefId = 'emptyall';
|
||||
this.props.ship.emptyWeapons();
|
||||
this.props.onChange();
|
||||
this._close();
|
||||
@@ -37,6 +48,7 @@ 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();
|
||||
@@ -95,52 +107,52 @@ export default class HardpointSlotSection extends SlotSection {
|
||||
|
||||
return <div className='select hardpoint' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
|
||||
<ul>
|
||||
<li className='lc' onClick={this._empty}>{translate('empty all')}</li>
|
||||
<li className='lc' tabIndex='0' onClick={this._empty} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['emptyall'] = smRef}>{translate('empty all')}</li>
|
||||
<li className='optional-hide' style={{ textAlign: 'center', marginTop: '1em' }}>{translate('PHRASE_ALT_ALL')}</li>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('pl')}</div>
|
||||
<ul>
|
||||
<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>
|
||||
<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>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('ul')}</div>
|
||||
<ul>
|
||||
<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>
|
||||
<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>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('bl')}</div>
|
||||
<ul>
|
||||
<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>
|
||||
<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>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('mc')}</div>
|
||||
<ul>
|
||||
<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>
|
||||
<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>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('c')}</div>
|
||||
<ul>
|
||||
<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>
|
||||
<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>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('fc')}</div>
|
||||
<ul>
|
||||
<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>
|
||||
<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>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('pa')}</div>
|
||||
<ul>
|
||||
<li className='lc' onClick={_fill.bind(this, 'pa', 'F')}>{translate('pa')}</li>
|
||||
<li className='lc' tabIndex='0' onClick={_fill.bind(this, 'pa', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['pa-F'] = smRef}>{translate('pa')}</li>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('nl')}</div>
|
||||
<ul>
|
||||
<li className='lc' onClick={_fill.bind(this, 'nl', 'F')}>{translate('nl')}</li>
|
||||
<li className='lc' tabIndex='0' onClick={_fill.bind(this, 'nl', 'F')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['nl-F'] = smRef}>{translate('nl')}</li>
|
||||
</ul>
|
||||
</div>;
|
||||
}
|
||||
|
||||
@@ -505,6 +505,9 @@ 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'>
|
||||
|
||||
@@ -21,8 +21,6 @@ export default class InternalSlot extends Slot {
|
||||
* @param {Object} u Localized Units Map
|
||||
* @return {React.Component} Slot contents
|
||||
*/
|
||||
|
||||
|
||||
_getSlotDetails(m, enabled, translate, formats, u) {
|
||||
if (m) {
|
||||
let classRating = m.class + m.rating;
|
||||
|
||||
@@ -18,7 +18,6 @@ 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);
|
||||
@@ -29,12 +28,24 @@ 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();
|
||||
@@ -45,6 +56,7 @@ 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) => {
|
||||
@@ -61,6 +73,7 @@ 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) => {
|
||||
@@ -77,6 +90,7 @@ 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) => {
|
||||
@@ -93,6 +107,7 @@ 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) => {
|
||||
@@ -109,6 +124,7 @@ 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) => {
|
||||
@@ -125,6 +141,7 @@ 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) => {
|
||||
@@ -141,6 +158,7 @@ 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
|
||||
@@ -160,6 +178,7 @@ 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) => {
|
||||
@@ -176,6 +195,7 @@ 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) => {
|
||||
@@ -240,16 +260,16 @@ export default class InternalSlotSection extends SlotSection {
|
||||
_getSectionMenu(translate, ship) {
|
||||
return <div className='select' onClick={e => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
|
||||
<ul>
|
||||
<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='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='optional-hide' style={{ textAlign: 'center', marginTop: '1em' }}>{translate('PHRASE_ALT_ALL')}</li>
|
||||
</ul>
|
||||
</div>;
|
||||
|
||||
@@ -50,6 +50,7 @@ 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>;
|
||||
}
|
||||
|
||||
120
src/app/components/ModalShoppingList.jsx
Normal file
120
src/app/components/ModalShoppingList.jsx
Normal file
@@ -0,0 +1,120 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import ShortenUrl from '../utils/ShortenUrl';
|
||||
import Persist from '../stores/Persist';
|
||||
|
||||
/**
|
||||
* Permalink modal
|
||||
*/
|
||||
export default class ModalShoppingList extends TranslatedComponent {
|
||||
|
||||
static propTypes = {
|
||||
ship: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param {Object} props React Component properties
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
matsList: '',
|
||||
mats: {},
|
||||
matsPerGrade: Persist.getRolls()
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* React component did mount
|
||||
*/
|
||||
componentDidMount() {
|
||||
this.renderMats();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert mats object to string
|
||||
*/
|
||||
renderMats() {
|
||||
const ship = this.props.ship;
|
||||
let mats = {};
|
||||
for (const module of ship.costList) {
|
||||
if (module.type === 'SHIP') {
|
||||
continue;
|
||||
}
|
||||
if (module.m && module.m.blueprint) {
|
||||
if (!module.m.blueprint.grade || !module.m.blueprint.grades) {
|
||||
continue;
|
||||
}
|
||||
for (const g in module.m.blueprint.grades) {
|
||||
if (g > module.m.blueprint.grade) {
|
||||
continue;
|
||||
}
|
||||
for (const i in module.m.blueprint.grades[g].components) {
|
||||
if (mats[i]) {
|
||||
mats[i] += module.m.blueprint.grades[g].components[i] * this.state.matsPerGrade[g];
|
||||
} else {
|
||||
mats[i] = module.m.blueprint.grades[g].components[i] * this.state.matsPerGrade[g];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let matsString = '';
|
||||
for (const i in mats) {
|
||||
if (!mats.hasOwnProperty(i)) {
|
||||
continue;
|
||||
}
|
||||
if (mats[i] === 0) {
|
||||
delete mats[i];
|
||||
continue;
|
||||
}
|
||||
matsString += `${i}: ${mats[i]}\n`;
|
||||
}
|
||||
this.setState({ matsList: matsString, mats });
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for changing roll amounts
|
||||
* @param {SyntheticEvent} e React Event
|
||||
*/
|
||||
changeHandler(e) {
|
||||
let grade = e.target.id;
|
||||
let newState = this.state.matsPerGrade;
|
||||
newState[grade] = parseInt(e.target.value);
|
||||
this.setState({ matsPerGrade: newState });
|
||||
Persist.setRolls(newState);
|
||||
this.renderMats();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the modal
|
||||
* @return {React.Component} Modal Content
|
||||
*/
|
||||
render() {
|
||||
let translate = this.context.language.translate;
|
||||
this.changeHandler = this.changeHandler.bind(this);
|
||||
return <div className='modal' onClick={ (e) => e.stopPropagation() }>
|
||||
<h2>{translate('PHRASE_SHOPPING_MATS')}</h2>
|
||||
<label>Grade 1 rolls </label>
|
||||
<input id={1} type={'number'} min={0} defaultValue={this.state.matsPerGrade[1]} onChange={this.changeHandler} />
|
||||
<br/>
|
||||
<label>Grade 2 rolls </label>
|
||||
<input id={2} type={'number'} min={0} defaultValue={this.state.matsPerGrade[2]} onChange={this.changeHandler} />
|
||||
<br/>
|
||||
<label>Grade 3 rolls </label>
|
||||
<input id={3} type={'number'} min={0} value={this.state.matsPerGrade[3]} onChange={this.changeHandler} />
|
||||
<br/>
|
||||
<label>Grade 4 rolls </label>
|
||||
<input id={4} type={'number'} min={0} value={this.state.matsPerGrade[4]} onChange={this.changeHandler} />
|
||||
<br/>
|
||||
<label>Grade 5 rolls </label>
|
||||
<input id={5} type={'number'} min={0} value={this.state.matsPerGrade[5]} onChange={this.changeHandler} />
|
||||
<div>
|
||||
<textarea className='cb json' readOnly value={this.state.matsList} />
|
||||
</div>
|
||||
<button className={'r dismiss cap'} onClick={this.context.hideModal}>{translate('close')}</button>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
getPercent,
|
||||
setRandom,
|
||||
specialToolTip
|
||||
} from '../utils/BlueprintFunctions'
|
||||
} from '../utils/BlueprintFunctions';
|
||||
|
||||
/**
|
||||
* Modifications menu
|
||||
@@ -43,15 +43,15 @@ export default class ModificationsMenu extends TranslatedComponent {
|
||||
this._rollBest = this._rollBest.bind(this);
|
||||
this._rollWorst = this._rollWorst.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.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.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 = {
|
||||
@@ -78,19 +78,18 @@ 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>);
|
||||
}
|
||||
if (blueprintGrades) {
|
||||
const thisLen = blueprintGrades.length;
|
||||
if (this.firstModId == null) this.firstModId = blueprintGrades[0].key;
|
||||
this.lastModId = blueprintGrades[thisLen-1].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>);
|
||||
}
|
||||
@@ -100,41 +99,38 @@ export default class ModificationsMenu extends TranslatedComponent {
|
||||
|
||||
/**
|
||||
* 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(event) {
|
||||
var className = null;
|
||||
var elemId = null;
|
||||
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
|
||||
return;
|
||||
}
|
||||
if (event.key == 'Tab') {
|
||||
//Shift-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) {
|
||||
} 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) {
|
||||
} 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();
|
||||
@@ -143,9 +139,9 @@ export default class ModificationsMenu extends TranslatedComponent {
|
||||
if (elemId == this.lastModId && elemId != null) {
|
||||
// Initial modification menu
|
||||
event.preventDefault();
|
||||
this.modItems[this.firstModId].focus();
|
||||
this.modItems[this.firstModId].focus();
|
||||
return;
|
||||
} else if (event.currentTarget.className.indexOf("button-inline-menu") >= 0 && event.currentTarget.nextSibling == null && event.currentTarget.nodeName != "TD") {
|
||||
} 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();
|
||||
@@ -154,11 +150,10 @@ export default class ModificationsMenu extends TranslatedComponent {
|
||||
event.preventDefault();
|
||||
this.modItems[this.firstBPLabel].focus();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Render the specials
|
||||
@@ -167,7 +162,6 @@ export default class ModificationsMenu extends TranslatedComponent {
|
||||
* @return {Object} list: Array of React Components
|
||||
*/
|
||||
_renderSpecials(props, context) {
|
||||
|
||||
const { m } = props;
|
||||
const { language, tooltip, termtip } = context;
|
||||
const translate = language.translate;
|
||||
@@ -181,8 +175,9 @@ export default class ModificationsMenu extends TranslatedComponent {
|
||||
continue;
|
||||
}
|
||||
const classes = cn('button-inline-menu', {
|
||||
active: m.blueprint && m.blueprint.special && m.blueprint.special.edname == specialName
|
||||
active: m.blueprint && m.blueprint.special && m.blueprint.special.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 = {};
|
||||
@@ -200,7 +195,6 @@ export default class ModificationsMenu extends TranslatedComponent {
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log("_renderSpecials. specials: %O", specials);
|
||||
return specials;
|
||||
}
|
||||
|
||||
@@ -219,7 +213,6 @@ export default class ModificationsMenu extends TranslatedComponent {
|
||||
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} />);
|
||||
}
|
||||
}
|
||||
console.log("_renderModifications. modItems: %O", this.modItems);
|
||||
return modifications;
|
||||
}
|
||||
|
||||
@@ -280,10 +273,10 @@ export default class ModificationsMenu extends TranslatedComponent {
|
||||
_rollFifty() {
|
||||
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);
|
||||
|
||||
|
||||
this.props.onChange();
|
||||
}
|
||||
|
||||
@@ -293,10 +286,10 @@ 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();
|
||||
}
|
||||
|
||||
@@ -306,10 +299,10 @@ 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);
|
||||
|
||||
|
||||
this.props.onChange();
|
||||
}
|
||||
|
||||
@@ -319,13 +312,9 @@ export default class ModificationsMenu extends TranslatedComponent {
|
||||
_rollWorst() {
|
||||
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);
|
||||
|
||||
this.props.onChange();
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -335,21 +324,24 @@ 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() {
|
||||
/**
|
||||
* Set focus on first element in modifications menu
|
||||
* after it first mounts
|
||||
*/
|
||||
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();
|
||||
@@ -358,14 +350,21 @@ export default class ModificationsMenu extends TranslatedComponent {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set focus on first element in modifications menu
|
||||
* if component updates, unless update is due to value change
|
||||
* in a modification
|
||||
*/
|
||||
componentDidUpdate() {
|
||||
/**
|
||||
* Set focus on first element in modifications menu
|
||||
* if component updates, unless update is due to value change
|
||||
* in a modification
|
||||
*/
|
||||
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();
|
||||
@@ -374,14 +373,15 @@ export default class ModificationsMenu extends TranslatedComponent {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this._handleModChange(false);//Need to reset if component update due to value change
|
||||
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();// set focus to the modification menu icon after mod menu is unmounted.
|
||||
this.props.modButton.focus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -407,6 +407,11 @@ export default class ModificationsMenu extends TranslatedComponent {
|
||||
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]) {
|
||||
blueprintLabel = translate(m.blueprint.name) + ' ' + translate('grade') + ' ' + m.blueprint.grade;
|
||||
haveBlueprint = true;
|
||||
@@ -435,7 +440,7 @@ export default class ModificationsMenu extends TranslatedComponent {
|
||||
const showReset = !blueprintMenuOpened && (!specialMenuOpened || !specials.length) && haveBlueprint;
|
||||
const showMods = !blueprintMenuOpened && (!specialMenuOpened || !specials.length) && haveBlueprint;
|
||||
if (haveBlueprint) {
|
||||
this.firstBPLabel = blueprintLabel
|
||||
this.firstBPLabel = blueprintLabel;
|
||||
} else {
|
||||
this.firstBPLabel = 'selectBP';
|
||||
}
|
||||
@@ -446,21 +451,21 @@ export default class ModificationsMenu extends TranslatedComponent {
|
||||
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> :
|
||||
{ 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 ? 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 }
|
||||
{ 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 ?
|
||||
|
||||
|
||||
<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: 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>
|
||||
|
||||
@@ -18,12 +18,16 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
||||
pips: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
/**
|
||||
* The ShipSummaryTable constructor
|
||||
* @param {Object} props The props
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props)
|
||||
super(props);
|
||||
this.didContextChange = this.didContextChange.bind(this);
|
||||
this.state = {
|
||||
shieldColour: 'blue'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -47,6 +51,7 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
||||
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') {
|
||||
@@ -56,7 +61,7 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
||||
}
|
||||
this.state = {
|
||||
shieldColour
|
||||
}
|
||||
};
|
||||
return <div id='summary'>
|
||||
<table className={'summaryTable'}>
|
||||
<thead>
|
||||
@@ -77,6 +82,7 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
||||
<th onMouseEnter={termtip.bind(null, 'hull hardness', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('hrd')}</th>
|
||||
<th rowSpan={2}>{translate('crew')}</th>
|
||||
<th onMouseEnter={termtip.bind(null, 'mass lock factor', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('MLF')}</th>
|
||||
<th onMouseEnter={termtip.bind(null, 'TT_SUMMARY_BOOST_TIME', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('boost time')}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th className='lft'>{translate('max')}</th>
|
||||
@@ -113,6 +119,7 @@ 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>
|
||||
@@ -140,8 +147,8 @@ export default class ShipSummaryTable extends TranslatedComponent {
|
||||
<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>{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>
|
||||
|
||||
@@ -17,12 +17,12 @@ export default class Slider extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
axis: PropTypes.bool,
|
||||
axisUnit: PropTypes.string,//units (T, M, etc.)
|
||||
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
|
||||
percent: PropTypes.number.isRequired,// value of slider
|
||||
scale: PropTypes.number
|
||||
};
|
||||
|
||||
@@ -51,7 +51,6 @@ export default class Slider extends React.Component {
|
||||
* @param {SyntheticEvent} event Event
|
||||
*/
|
||||
_down(event) {
|
||||
|
||||
let rect = event.currentTarget.getBoundingClientRect();
|
||||
this.left = rect.left;
|
||||
this.width = rect.width;
|
||||
@@ -95,28 +94,26 @@ export default class Slider extends React.Component {
|
||||
case 'Enter':
|
||||
event.preventDefault();
|
||||
this.sliderInputBox._setDisplay('block');
|
||||
//this.enterTimer = setTimeout(() => this.sliderInputBox.sliderVal.focus(), 10);
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* Key down handler
|
||||
* increment slider position by +/- 1 when right/left arrow key is pressed or held
|
||||
* @param {Event} event
|
||||
* @param {Event} event Keyboard even
|
||||
*/
|
||||
_keydown(event) {
|
||||
|
||||
let newVal = this.props.percent * this.props.max;
|
||||
switch (event.key) {
|
||||
case 'ArrowRight':
|
||||
var newVal = this.props.percent*this.props.max + 1;
|
||||
if (newVal <= this.props.max) this.props.onChange(newVal/this.props.max);
|
||||
newVal += 1;
|
||||
if (newVal <= this.props.max) this.props.onChange(newVal / this.props.max);
|
||||
return;
|
||||
case 'ArrowLeft':
|
||||
var newVal = this.props.percent*this.props.max - 1;
|
||||
if (newVal >= 0) this.props.onChange(newVal/this.props.max);
|
||||
newVal -= 1;
|
||||
if (newVal >= 0) this.props.onChange(newVal / this.props.max);
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
@@ -131,7 +128,12 @@ export default class Slider extends React.Component {
|
||||
_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);
|
||||
@@ -181,7 +183,6 @@ export default class Slider extends React.Component {
|
||||
*/
|
||||
componentDidMount() {
|
||||
this._updateDimensions();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -200,21 +201,17 @@ export default class Slider extends React.Component {
|
||||
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' />
|
||||
@@ -233,110 +230,136 @@ export default class Slider extends React.Component {
|
||||
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 {
|
||||
class TextInputBox extends React.Component {
|
||||
static propTypes = {
|
||||
axisUnit: PropTypes.string,//units (T, M, etc.)
|
||||
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
|
||||
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._keydown = this._keydown.bind(this);
|
||||
this._keyup = this._keyup.bind(this);
|
||||
this.state = this._getInitialState();
|
||||
this.percent = this.props.percent;
|
||||
this.max = this.props.max;
|
||||
this.state.inputValue = this.percent * this.max;
|
||||
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 });
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps, nextState) {
|
||||
var 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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
if (prevProps.max !== this.props.max && this.state.inputValue > this.props.max) {
|
||||
// they chose a different module
|
||||
this.setState({ inputValue: this.props.max });
|
||||
}
|
||||
|
||||
_getInitialState() {
|
||||
return {
|
||||
divStyle: {display:'none'},
|
||||
inputStyle: {width:'4em'},
|
||||
labelStyle: {marginLeft: '.1em'},
|
||||
maxLength:5,
|
||||
size:5,
|
||||
min:0,
|
||||
tabIndex:-1,
|
||||
type:'number',
|
||||
readOnly: true
|
||||
}
|
||||
if (this.state.inputValue != prevState.inputValue && prevProps.max == this.props.max) {
|
||||
this.props.onChange(this.state.inputValue / this.props.max);
|
||||
}
|
||||
|
||||
_setDisplay(val) {
|
||||
}
|
||||
/**
|
||||
* 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({
|
||||
divStyle: {display:val}
|
||||
inputValue: this.props.percent * this.props.max
|
||||
});
|
||||
}
|
||||
|
||||
_handleFocus() {
|
||||
this.setState({
|
||||
inputValue:this._getValue()
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 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 });
|
||||
}
|
||||
|
||||
_handleBlur() {
|
||||
this._setDisplay('none');
|
||||
if (this.state.inputValue !== '') {
|
||||
this.props.onChange(this.state.inputValue/this.props.max);
|
||||
} else {
|
||||
this.state.inputValue = this.props.percent * this.props.max;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_getValue() {
|
||||
return this.state.inputValue;
|
||||
}
|
||||
|
||||
_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
|
||||
@@ -350,12 +373,14 @@ export default class Slider extends React.Component {
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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>;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 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>;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -81,17 +81,14 @@ export default class Slot extends TranslatedComponent {
|
||||
* 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') {
|
||||
console.log("Slot: Enter key pressed on mod icon");
|
||||
this._toggleModifications();
|
||||
} else {
|
||||
console.log("Slot: Enter key pressed on: %O", event.target);
|
||||
}
|
||||
this.props.onOpen(event);
|
||||
if(event.target.className == 'r') {
|
||||
this._toggleModifications();
|
||||
}
|
||||
this.props.onOpen(event);
|
||||
}
|
||||
}
|
||||
/**
|
||||
@@ -154,7 +151,7 @@ export default class Slot extends TranslatedComponent {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Toggle the modifications flag when selecting the modifications icon
|
||||
*/
|
||||
|
||||
@@ -18,7 +18,8 @@ export default class SlotSection extends TranslatedComponent {
|
||||
onCargoChange: PropTypes.func.isRequired,
|
||||
onFuelChange: PropTypes.func.isRequired,
|
||||
code: PropTypes.string.isRequired,
|
||||
togglePwr: PropTypes.func
|
||||
togglePwr: PropTypes.func,
|
||||
sectionMenuRefs: PropTypes.object
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -32,7 +33,10 @@ export default class SlotSection extends TranslatedComponent {
|
||||
super(props);
|
||||
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);
|
||||
@@ -40,6 +44,8 @@ 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 = {};
|
||||
}
|
||||
|
||||
@@ -47,7 +53,59 @@ 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
|
||||
@@ -225,7 +283,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>{translate(this.sectionName)} <Equalizer/></h1>
|
||||
<h1 tabIndex="0" onKeyDown={this._keyDown} ref={ssHead => this.sectionRefArr['ssHeadRef'] = ssHead}>{translate(this.sectionName)} <Equalizer/></h1>
|
||||
{sectionMenuOpened ? this._getSectionMenu(translate, this.props.ship) : null }
|
||||
</div>
|
||||
{this._getSlots()}
|
||||
|
||||
@@ -39,13 +39,16 @@ export default class StandardSlot extends TranslatedComponent {
|
||||
this.modButton = null;
|
||||
this.slotDiv = null;
|
||||
}
|
||||
|
||||
_keyDown(event) {
|
||||
/**
|
||||
* 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);
|
||||
if(event.target.className == 'r') {
|
||||
this._toggleModifications();
|
||||
}
|
||||
this.props.onOpen(event);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +64,9 @@ export default class StandardSlot extends TranslatedComponent {
|
||||
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 showModuleResistances = Persist.showModuleResistances();
|
||||
let mass = m.getMass() || m.cargo || m.fuel || 0;
|
||||
|
||||
@@ -148,7 +154,6 @@ export default class StandardSlot extends TranslatedComponent {
|
||||
* Toggle the modifications flag when selecting the modifications icon
|
||||
*/
|
||||
_toggleModifications() {
|
||||
|
||||
this._modificationsSelected = !this._modificationsSelected;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,12 +20,23 @@ 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);
|
||||
@@ -39,6 +50,8 @@ 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);
|
||||
@@ -51,6 +64,7 @@ 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);
|
||||
@@ -63,6 +77,7 @@ 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);
|
||||
@@ -75,6 +90,8 @@ 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);
|
||||
@@ -86,6 +103,7 @@ 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);
|
||||
@@ -229,17 +247,17 @@ 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' onClick={this._optimizeStandard}>{translate('Maximize Jump Range')}</li>
|
||||
<li className='lc' tabIndex="0" onClick={this._optimizeStandard} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['maxjump'] = smRef}>{translate('Maximize Jump Range')}</li>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('roles')}</div>
|
||||
<ul>
|
||||
<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, true)}>{translate('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, true)}>{translate('Miner')}</li>
|
||||
<li className='lc' onClick={this._optimizeRacer.bind(this)}>{translate('Racer')}</li>
|
||||
<li className='lc' tabIndex="0" onClick={this._multiPurpose.bind(this, false, 0)} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['multipurpose'] = smRef}>{translate('Multi-purpose')}</li>
|
||||
<li className='lc' 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>
|
||||
</ul>
|
||||
</div>;
|
||||
}
|
||||
|
||||
@@ -228,6 +228,72 @@ export class LinkIcon extends SvgIcon {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Material
|
||||
*/
|
||||
export class MatIcon extends SvgIcon {
|
||||
/**
|
||||
* Generate the SVG
|
||||
* @return {React.Component} SVG Contents
|
||||
*/
|
||||
svg() {
|
||||
return<g xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="#FF7100" d="M 24.86,4.18
|
||||
C 24.86,4.18 17.17,7.82 17.17,7.82
|
||||
17.17,7.82 15.35,14.55 15.35,14.55
|
||||
15.35,14.55 24.70,9.75 24.70,9.75
|
||||
24.70,9.75 24.86,4.18 24.86,4.18 Z
|
||||
M 32.21,17.45
|
||||
C 32.21,17.45 26.41,11.18 26.41,11.18
|
||||
26.41,11.18 19.51,11.51 19.51,11.51
|
||||
19.51,11.51 26.92,19.01 26.92,19.01
|
||||
26.92,19.01 32.21,17.45 32.21,17.45 Z
|
||||
M 21.99,28.62
|
||||
C 21.99,28.62 26.10,21.10 26.10,21.10
|
||||
26.10,21.10 23.66,14.57 23.66,14.57
|
||||
23.66,14.57 18.89,24.01 18.89,24.01
|
||||
18.89,24.01 21.99,28.62 21.99,28.62 Z
|
||||
M 8.33,22.24
|
||||
C 8.33,22.24 16.67,23.87 16.67,23.87
|
||||
16.67,23.87 22.06,19.51 22.06,19.51
|
||||
22.06,19.51 11.70,17.84 11.70,17.84
|
||||
11.70,17.84 8.33,22.24 8.33,22.24 Z
|
||||
M 10.11,7.14
|
||||
C 10.11,7.14 11.15,15.66 11.15,15.66
|
||||
11.15,15.66 16.92,19.49 16.92,19.49
|
||||
16.92,19.49 15.29,9.02 15.29,9.02
|
||||
15.29,9.02 10.11,7.14 10.11,7.14 Z
|
||||
M 27.69,2.67
|
||||
C 27.69,2.67 35.89,16.00 35.89,16.00
|
||||
35.89,16.00 27.69,29.33 27.69,29.33
|
||||
27.69,29.33 11.31,29.33 11.31,29.33
|
||||
11.31,29.33 3.11,16.00 3.11,16.00
|
||||
3.11,16.00 11.31,2.67 11.31,2.67
|
||||
11.31,2.67 27.67,2.67 27.67,2.67M 29.16,0.00
|
||||
C 29.16,0.00 27.69,0.00 27.69,0.00
|
||||
27.69,0.00 11.31,0.00 11.31,0.00
|
||||
11.31,0.00 9.84,0.00 9.84,0.00
|
||||
9.84,0.00 9.06,1.25 9.06,1.25
|
||||
9.06,1.25 0.87,14.57 0.87,14.57
|
||||
0.87,14.57 0.00,15.98 0.00,15.98
|
||||
0.00,15.98 0.87,17.39 0.87,17.39
|
||||
0.87,17.39 9.06,30.73 9.06,30.73
|
||||
9.06,30.73 9.84,32.00 9.84,32.00
|
||||
9.84,32.00 11.31,32.00 11.31,32.00
|
||||
11.31,32.00 27.69,32.00 27.69,32.00
|
||||
27.69,32.00 29.16,32.00 29.16,32.00
|
||||
29.16,32.00 29.94,30.73 29.94,30.73
|
||||
29.94,30.73 38.13,17.39 38.13,17.39
|
||||
38.13,17.39 39.00,15.98 39.00,15.98
|
||||
39.00,15.98 38.13,14.57 38.13,14.57
|
||||
38.13,14.57 29.94,1.25 29.94,1.25
|
||||
29.94,1.25 29.16,0.00 29.16,0.00
|
||||
29.16,0.00 29.16,0.00 29.16,0.00 Z" />
|
||||
</g>;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Shopping icon (dollar sign)
|
||||
*/
|
||||
|
||||
@@ -17,12 +17,23 @@ 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();
|
||||
@@ -36,6 +47,9 @@ 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();
|
||||
@@ -94,28 +108,28 @@ export default class UtilitySlotSection extends SlotSection {
|
||||
|
||||
return <div className='select' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
|
||||
<ul>
|
||||
<li className='lc' onClick={this._empty}>{translate('empty all')}</li>
|
||||
<li className='lc' tabIndex='0' onClick={this._empty} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['emptyall'] = smRef}>{translate('empty all')}</li>
|
||||
<li className='optional-hide' style={{ textAlign: 'center', marginTop: '1em' }}>{translate('PHRASE_ALT_ALL')}</li>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('sb')}</div>
|
||||
<ul>
|
||||
<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>
|
||||
<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>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('hs')}</div>
|
||||
<ul>
|
||||
<li className='lc' onClick={_use.bind(this, 'hs', null, 'Heat Sink Launcher')}>{translate('Heat Sink Launcher')}</li>
|
||||
<li className='lc' tabIndex='0' onClick={_use.bind(this, 'hs', null, 'Heat Sink Launcher')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['hs'] = smRef}>{translate('Heat Sink Launcher')}</li>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('ch')}</div>
|
||||
<ul>
|
||||
<li className='lc' onClick={_use.bind(this, 'ch', null, 'Chaff Launcher')}>{translate('Chaff Launcher')}</li>
|
||||
<li className='lc' tabIndex='0' onClick={_use.bind(this, 'ch', null, 'Chaff Launcher')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['ch'] = smRef}>{translate('Chaff Launcher')}</li>
|
||||
</ul>
|
||||
<div className='select-group cap'>{translate('po')}</div>
|
||||
<ul>
|
||||
<li className='lc' onClick={_use.bind(this, 'po', null, 'Point Defence')}>{translate('Point Defence')}</li>
|
||||
<li className='lc' tabIndex='0' onClick={_use.bind(this, 'po', null, 'Point Defence')} onKeyDown={this._keyDown} ref={smRef => this.sectionRefArr['po'] = smRef}>{translate('Point Defence')}</li>
|
||||
</ul>
|
||||
</div>;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"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",
|
||||
@@ -60,6 +61,7 @@
|
||||
"TT_SUMMARY_SPEED": "With full fuel tank and 4 pips to ENG",
|
||||
"TT_SUMMARY_SPEED_NONFUNCTIONAL": "Thrusters powered off or over maximum mass with full fuel and cargo loads",
|
||||
"TT_SUMMARY_BOOST": "With full fuel tank and 4 pips to ENG",
|
||||
"TT_SUMMARY_BOOST_TIME": "Time between each boost with 4 pips to ENG",
|
||||
"TT_SUMMARY_BOOST_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",
|
||||
@@ -178,6 +180,7 @@
|
||||
"internal protection": "Internal protection",
|
||||
"external protection": "External protection",
|
||||
"engagement range": "Engagement range",
|
||||
"boost time": "Boost time",
|
||||
"total": "Total",
|
||||
"ammo": "Ammunition maximum",
|
||||
"boot": "Boot time",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -274,11 +274,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;
|
||||
}
|
||||
@@ -396,4 +396,4 @@ export default class ShipyardPage extends Page {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,9 +45,9 @@ 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, ship) {
|
||||
export function shieldStrength(mass, baseShield, sg, multiplier) {
|
||||
// sg might be a module or a template; handle either here
|
||||
let minMass = sg instanceof Module ? sg.getMinMass() : sg.minmass;
|
||||
let optMass = sg instanceof Module ? sg.getOptMass() : sg.optmass;
|
||||
@@ -55,17 +55,6 @@ export function shieldStrength(mass, baseShield, sg, multiplier, ship) {
|
||||
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;
|
||||
if (ship) {
|
||||
for (const i of ship.hardpoints) {
|
||||
if (!i.maxClass) {
|
||||
if (i.grp === 'sb' || (i.m && i.m.grp === 'sb')) {
|
||||
if (!isNaN(i.m.getModValue('optmul'))) {
|
||||
optMul += i.m.getModValue('optmul') / 10000;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
@@ -361,7 +350,7 @@ export function shieldMetrics(ship, sys) {
|
||||
boosterKinDmg = boosterKinDmg > 0.7 ? boosterKinDmg : 0.7 - (0.7 - boosterKinDmg) / 2;
|
||||
boosterThermDmg = boosterThermDmg > 0.7 ? boosterThermDmg : 0.7 - (0.7 - boosterThermDmg) / 2;
|
||||
|
||||
const generatorStrength = this.shieldStrength(ship.hullMass, ship.baseShieldStrength, shieldGenerator, 1, ship);
|
||||
const generatorStrength = this.shieldStrength(ship.hullMass, ship.baseShieldStrength, shieldGenerator, 1);
|
||||
const boostersStrength = generatorStrength * boost;
|
||||
|
||||
// Recover time is the time taken to go from 0 to 50%. It includes a 16-second wait before shields start to recover
|
||||
@@ -463,6 +452,19 @@ 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
|
||||
|
||||
@@ -136,7 +136,7 @@ export default class Module {
|
||||
if (modification.type === 'percentage') {
|
||||
modValue = this.getModValue(name) / 10000;
|
||||
} else if (modification.type === 'numeric') {
|
||||
modValue = this.getModValue(name)/ 100;
|
||||
modValue = this.getModValue(name) / 100;
|
||||
} else {
|
||||
modValue = this.getModValue(name);
|
||||
}
|
||||
|
||||
@@ -241,7 +241,7 @@ export default class Ship {
|
||||
}
|
||||
|
||||
// TODO Not accurate if the ship has modified shield boosters
|
||||
return Calc.shieldStrength(this.hullMass, this.baseShieldStrength, sg, 1 + (multiplierDelta || 0), this);
|
||||
return Calc.shieldStrength(this.hullMass, this.baseShieldStrength, sg, 1 + (multiplierDelta || 0));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -437,12 +437,15 @@ export default class Ship {
|
||||
m.blueprint = bp;
|
||||
this.clearModifications(m);
|
||||
// Set any hidden items for the blueprint now
|
||||
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]);
|
||||
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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.updateModificationsString();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as ModuleUtils from './ModuleUtils'
|
||||
import { canMount } from '../utils/SlotFunctions'
|
||||
import * as ModuleUtils from './ModuleUtils';
|
||||
import { canMount } from '../utils/SlotFunctions';
|
||||
|
||||
/**
|
||||
* Standard / typical role for multi-purpose or combat (if shielded with better bulkheads)
|
||||
@@ -7,20 +7,20 @@ import { canMount } from '../utils/SlotFunctions'
|
||||
* @param {Boolean} shielded True if shield generator should be included
|
||||
* @param {integer} bulkheadIndex Bulkhead to use see Constants.BulkheadNames
|
||||
*/
|
||||
export function multiPurpose (ship, shielded, bulkheadIndex) {
|
||||
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)
|
||||
.useBulkhead(bulkheadIndex);
|
||||
|
||||
if (shielded) {
|
||||
ship.internal.some(function (slot) {
|
||||
ship.internal.some(function(slot) {
|
||||
if (canMount(ship, slot, 'sg')) { // Assuming largest slot can hold an eligible shield
|
||||
ship.use(slot, ModuleUtils.findInternal('sg', slot.maxClass, 'A'))
|
||||
ship.setSlotEnabled(slot, true)
|
||||
return true
|
||||
ship.use(slot, ModuleUtils.findInternal('sg', slot.maxClass, 'A'));
|
||||
ship.setSlotEnabled(slot, true);
|
||||
return true;
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,51 +30,51 @@ export function multiPurpose (ship, shielded, bulkheadIndex) {
|
||||
* @param {Boolean} shielded True if shield generator should be included
|
||||
* @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)
|
||||
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
|
||||
.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 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) {
|
||||
.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')
|
||||
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
|
||||
ship.use(slot, shield);
|
||||
ship.setSlotEnabled(slot, true);
|
||||
usedSlots.push(slot);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// Fill the empty internals with cargo racks
|
||||
for (let i = ship.internal.length; i--;) {
|
||||
let slot = ship.internal[i]
|
||||
let slot = ship.internal[i];
|
||||
if (usedSlots.indexOf(slot) == -1 && canMount(ship, slot, 'cr')) {
|
||||
ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E'))
|
||||
ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E'));
|
||||
}
|
||||
}
|
||||
|
||||
// Empty the hardpoints
|
||||
for (let s of ship.hardpoints) {
|
||||
ship.use(s, null)
|
||||
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--
|
||||
ship.use(s, ModuleUtils.hardpoints('04'));
|
||||
bstCount--;
|
||||
} else {
|
||||
ship.use(s, null)
|
||||
ship.use(s, null);
|
||||
}
|
||||
}
|
||||
// ship.useLightestStandard(standardOpts);
|
||||
@@ -85,127 +85,127 @@ export function trader (ship, shielded, standardOpts) {
|
||||
* @param {Ship} ship Ship instance
|
||||
* @param {Boolean} planetary True if Planetary Vehicle Hangar (PVH) should be included
|
||||
*/
|
||||
export function explorer (ship, planetary) {
|
||||
let standardOpts = {ppRating: 'A'},
|
||||
heatSinkCount = 2, // Fit 2 heat sinks if possible
|
||||
usedSlots = [],
|
||||
sgSlot,
|
||||
fuelScoopSlot,
|
||||
sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass)
|
||||
export function explorer(ship, planetary) {
|
||||
let standardOpts = { ppRating: 'A' },
|
||||
heatSinkCount = 2, // Fit 2 heat sinks if possible
|
||||
usedSlots = [],
|
||||
sgSlot,
|
||||
fuelScoopSlot,
|
||||
sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass);
|
||||
|
||||
if (!planetary) { // Non-planetary explorers don't really need to boost
|
||||
standardOpts.pd = '1D'
|
||||
standardOpts.pd = '1D';
|
||||
}
|
||||
|
||||
// Cargo hatch can be disabled
|
||||
ship.setSlotEnabled(ship.cargoHatch, false)
|
||||
ship.setSlotEnabled(ship.cargoHatch, false);
|
||||
|
||||
// Advanced Discovery Scanner - class 1 or higher
|
||||
const adsOrder = [1, 2, 3, 4, 5, 6, 7, 8]
|
||||
const adsOrder = [1, 2, 3, 4, 5, 6, 7, 8];
|
||||
const adsInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
||||
.filter(a => (!a.eligible) || a.eligible.sc)
|
||||
.sort((a, b) => adsOrder.indexOf(a.maxClass) - adsOrder.indexOf(b.maxClass))
|
||||
.sort((a, b) => adsOrder.indexOf(a.maxClass) - adsOrder.indexOf(b.maxClass));
|
||||
for (let i = 0; i < adsInternals.length; i++) {
|
||||
if (canMount(ship, adsInternals[i], 'sc')) {
|
||||
ship.use(adsInternals[i], ModuleUtils.internal('2f'))
|
||||
usedSlots.push(adsInternals[i])
|
||||
break
|
||||
ship.use(adsInternals[i], ModuleUtils.internal('2f'));
|
||||
usedSlots.push(adsInternals[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (planetary) {
|
||||
// Planetary Vehicle Hangar - class 2 or higher
|
||||
const pvhOrder = [2, 3, 4, 5, 6, 7, 8, 1]
|
||||
const pvhOrder = [2, 3, 4, 5, 6, 7, 8, 1];
|
||||
const pvhInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
||||
.filter(a => (!a.eligible) || a.eligible.pv)
|
||||
.sort((a, b) => pvhOrder.indexOf(a.maxClass) - pvhOrder.indexOf(b.maxClass))
|
||||
.sort((a, b) => pvhOrder.indexOf(a.maxClass) - pvhOrder.indexOf(b.maxClass));
|
||||
for (let i = 0; i < pvhInternals.length; i++) {
|
||||
if (canMount(ship, pvhInternals[i], 'pv')) {
|
||||
// Planetary Vehical Hangar only has even classes
|
||||
const pvhClass = pvhInternals[i].maxClass % 2 === 1 ? pvhInternals[i].maxClass - 1 : pvhInternals[i].maxClass
|
||||
ship.use(pvhInternals[i], ModuleUtils.findInternal('pv', pvhClass, 'G')) // G is lower mass
|
||||
ship.setSlotEnabled(pvhInternals[i], false) // Disable power for Planetary Vehical Hangar
|
||||
usedSlots.push(pvhInternals[i])
|
||||
break
|
||||
const pvhClass = pvhInternals[i].maxClass % 2 === 1 ? pvhInternals[i].maxClass - 1 : pvhInternals[i].maxClass;
|
||||
ship.use(pvhInternals[i], ModuleUtils.findInternal('pv', pvhClass, 'G')); // G is lower mass
|
||||
ship.setSlotEnabled(pvhInternals[i], false); // Disable power for Planetary Vehical Hangar
|
||||
usedSlots.push(pvhInternals[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Shield generator
|
||||
const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8]
|
||||
const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8];
|
||||
const shieldInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
||||
.filter(a => (!a.eligible) || a.eligible.sg)
|
||||
.filter(a => a.maxClass >= sg.class)
|
||||
.sort((a, b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass))
|
||||
.sort((a, b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass));
|
||||
for (let i = 0; i < shieldInternals.length; i++) {
|
||||
if (canMount(ship, shieldInternals[i], 'sg')) {
|
||||
ship.use(shieldInternals[i], sg)
|
||||
usedSlots.push(shieldInternals[i])
|
||||
sgSlot = shieldInternals[i]
|
||||
break
|
||||
ship.use(shieldInternals[i], sg);
|
||||
usedSlots.push(shieldInternals[i]);
|
||||
sgSlot = shieldInternals[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Detailed Surface Scanner
|
||||
const dssOrder = [1, 2, 3, 4, 5, 6, 7, 8]
|
||||
const dssOrder = [1, 2, 3, 4, 5, 6, 7, 8];
|
||||
const dssInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
||||
.filter(a => (!a.eligible) || a.eligible.sc)
|
||||
.sort((a, b) => dssOrder.indexOf(a.maxClass) - dssOrder.indexOf(b.maxClass))
|
||||
.sort((a, b) => dssOrder.indexOf(a.maxClass) - dssOrder.indexOf(b.maxClass));
|
||||
for (let i = 0; i < dssInternals.length; i++) {
|
||||
if (canMount(ship, dssInternals[i], 'sc')) {
|
||||
ship.use(dssInternals[i], ModuleUtils.internal('2i'))
|
||||
usedSlots.push(dssInternals[i])
|
||||
break
|
||||
ship.use(dssInternals[i], ModuleUtils.internal('2i'));
|
||||
usedSlots.push(dssInternals[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Fuel scoop - best possible
|
||||
const fuelScoopOrder = [8, 7, 6, 5, 4, 3, 2, 1]
|
||||
const fuelScoopOrder = [8, 7, 6, 5, 4, 3, 2, 1];
|
||||
const fuelScoopInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
||||
.filter(a => (!a.eligible) || a.eligible.fs)
|
||||
.sort((a, b) => fuelScoopOrder.indexOf(a.maxClass) - fuelScoopOrder.indexOf(b.maxClass))
|
||||
.sort((a, b) => fuelScoopOrder.indexOf(a.maxClass) - fuelScoopOrder.indexOf(b.maxClass));
|
||||
for (let i = 0; i < fuelScoopInternals.length; i++) {
|
||||
if (canMount(ship, fuelScoopInternals[i], 'fs')) {
|
||||
ship.use(fuelScoopInternals[i], ModuleUtils.findInternal('fs', fuelScoopInternals[i].maxClass, 'A'))
|
||||
usedSlots.push(fuelScoopInternals[i])
|
||||
fuelScoopSlot = fuelScoopInternals[i]
|
||||
break
|
||||
ship.use(fuelScoopInternals[i], ModuleUtils.findInternal('fs', fuelScoopInternals[i].maxClass, 'A'));
|
||||
usedSlots.push(fuelScoopInternals[i]);
|
||||
fuelScoopSlot = fuelScoopInternals[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// AFMUs - fill as they are 0-weight
|
||||
const afmuOrder = [8, 7, 6, 5, 4, 3, 2, 1]
|
||||
const afmuOrder = [8, 7, 6, 5, 4, 3, 2, 1];
|
||||
const afmuInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
||||
.filter(a => (!a.eligible) || a.eligible.pc)
|
||||
.sort((a, b) => afmuOrder.indexOf(a.maxClass) - afmuOrder.indexOf(b.maxClass))
|
||||
.sort((a, b) => afmuOrder.indexOf(a.maxClass) - afmuOrder.indexOf(b.maxClass));
|
||||
for (let i = 0; i < afmuInternals.length; i++) {
|
||||
if (canMount(ship, afmuInternals[i], 'am')) {
|
||||
ship.use(afmuInternals[i], ModuleUtils.findInternal('am', afmuInternals[i].maxClass, 'A'))
|
||||
usedSlots.push(afmuInternals[i])
|
||||
ship.setSlotEnabled(afmuInternals[i], false) // Disable power for AFM Unit
|
||||
ship.use(afmuInternals[i], ModuleUtils.findInternal('am', afmuInternals[i].maxClass, 'A'));
|
||||
usedSlots.push(afmuInternals[i]);
|
||||
ship.setSlotEnabled(afmuInternals[i], false); // Disable power for AFM Unit
|
||||
}
|
||||
}
|
||||
|
||||
for (let s of ship.hardpoints) {
|
||||
if (s.maxClass == 0 && heatSinkCount) { // Mount up to 2 heatsinks
|
||||
ship.use(s, ModuleUtils.hardpoints('02'))
|
||||
ship.setSlotEnabled(s, heatSinkCount == 2) // Only enable a single Heatsink
|
||||
heatSinkCount--
|
||||
ship.use(s, ModuleUtils.hardpoints('02'));
|
||||
ship.setSlotEnabled(s, heatSinkCount == 2); // Only enable a single Heatsink
|
||||
heatSinkCount--;
|
||||
} else {
|
||||
ship.use(s, null)
|
||||
ship.use(s, null);
|
||||
}
|
||||
}
|
||||
|
||||
if (sgSlot && fuelScoopSlot) {
|
||||
// The SG and Fuel scoop to not need to be powered at the same time
|
||||
if (sgSlot.m.getPowerUsage() > fuelScoopSlot.m.getPowerUsage()) { // The Shield generator uses the most power
|
||||
ship.setSlotEnabled(fuelScoopSlot, false)
|
||||
ship.setSlotEnabled(fuelScoopSlot, false);
|
||||
} else { // The Fuel scoop uses the most power
|
||||
ship.setSlotEnabled(sgSlot, false)
|
||||
ship.setSlotEnabled(sgSlot, false);
|
||||
}
|
||||
}
|
||||
|
||||
ship.useLightestStandard(standardOpts)
|
||||
ship.useLightestStandard(standardOpts);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -213,188 +213,188 @@ export function explorer (ship, planetary) {
|
||||
* @param {Ship} ship Ship instance
|
||||
* @param {Boolean} shielded True if shield generator should be included
|
||||
*/
|
||||
export function miner (ship, shielded) {
|
||||
shielded = true
|
||||
let standardOpts = {ppRating: 'A'},
|
||||
miningLaserCount = 2,
|
||||
usedSlots = [],
|
||||
sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass)
|
||||
export function miner(ship, shielded) {
|
||||
shielded = true;
|
||||
let standardOpts = { ppRating: 'A' },
|
||||
miningLaserCount = 2,
|
||||
usedSlots = [],
|
||||
sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass);
|
||||
|
||||
// Cargo hatch should be enabled
|
||||
ship.setSlotEnabled(ship.cargoHatch, true)
|
||||
ship.setSlotEnabled(ship.cargoHatch, true);
|
||||
|
||||
// Largest possible refinery
|
||||
const refineryOrder = [4, 5, 6, 7, 8, 3, 2, 1]
|
||||
const refineryOrder = [4, 5, 6, 7, 8, 3, 2, 1];
|
||||
const refineryInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
||||
.filter(a => (!a.eligible) || a.eligible.rf)
|
||||
.sort((a, b) => refineryOrder.indexOf(a.maxClass) - refineryOrder.indexOf(b.maxClass))
|
||||
.sort((a, b) => refineryOrder.indexOf(a.maxClass) - refineryOrder.indexOf(b.maxClass));
|
||||
for (let i = 0; i < refineryInternals.length; i++) {
|
||||
if (canMount(ship, refineryInternals[i], 'rf')) {
|
||||
ship.use(refineryInternals[i], ModuleUtils.findInternal('rf', Math.min(refineryInternals[i].maxClass, 4), 'A'))
|
||||
usedSlots.push(refineryInternals[i])
|
||||
break
|
||||
ship.use(refineryInternals[i], ModuleUtils.findInternal('rf', Math.min(refineryInternals[i].maxClass, 4), 'A'));
|
||||
usedSlots.push(refineryInternals[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Prospector limpet controller - 3A if possible
|
||||
const prospectorOrder = [3, 4, 5, 6, 7, 8, 2, 1]
|
||||
const prospectorOrder = [3, 4, 5, 6, 7, 8, 2, 1];
|
||||
const prospectorInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
||||
.filter(a => (!a.eligible) || a.eligible.pc)
|
||||
.sort((a, b) => prospectorOrder.indexOf(a.maxClass) - prospectorOrder.indexOf(b.maxClass))
|
||||
.sort((a, b) => prospectorOrder.indexOf(a.maxClass) - prospectorOrder.indexOf(b.maxClass));
|
||||
for (let i = 0; i < prospectorInternals.length; i++) {
|
||||
if (canMount(ship, prospectorInternals[i], 'pc')) {
|
||||
// Prospector only has odd classes
|
||||
const prospectorClass = prospectorInternals[i].maxClass % 2 === 0 ? prospectorInternals[i].maxClass - 1 : prospectorInternals[i].maxClass
|
||||
ship.use(prospectorInternals[i], ModuleUtils.findInternal('pc', prospectorClass, 'A'))
|
||||
usedSlots.push(prospectorInternals[i])
|
||||
break
|
||||
const prospectorClass = prospectorInternals[i].maxClass % 2 === 0 ? prospectorInternals[i].maxClass - 1 : prospectorInternals[i].maxClass;
|
||||
ship.use(prospectorInternals[i], ModuleUtils.findInternal('pc', prospectorClass, 'A'));
|
||||
usedSlots.push(prospectorInternals[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Shield generator if required
|
||||
if (shielded) {
|
||||
const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8]
|
||||
const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8];
|
||||
const shieldInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
||||
.filter(a => (!a.eligible) || a.eligible.sg)
|
||||
.filter(a => a.maxClass >= sg.class)
|
||||
.sort((a, b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass))
|
||||
.sort((a, b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass));
|
||||
for (let i = 0; i < shieldInternals.length; i++) {
|
||||
if (canMount(ship, shieldInternals[i], 'sg')) {
|
||||
ship.use(shieldInternals[i], sg)
|
||||
usedSlots.push(shieldInternals[i])
|
||||
break
|
||||
ship.use(shieldInternals[i], sg);
|
||||
usedSlots.push(shieldInternals[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
return miningLaserOrder.indexOf(a.maxClass) - miningLaserOrder.indexOf(b.maxClass)
|
||||
})
|
||||
const miningLaserOrder = [2, 3, 4, 1, 0];
|
||||
const miningLaserHardpoints = ship.hardpoints.concat().sort(function(a, b) {
|
||||
return miningLaserOrder.indexOf(a.maxClass) - miningLaserOrder.indexOf(b.maxClass);
|
||||
});
|
||||
for (let s of miningLaserHardpoints) {
|
||||
if (s.maxClass >= 1 && miningLaserCount) {
|
||||
ship.use(s, ModuleUtils.hardpoints(s.maxClass >= 2 ? '2m' : '2l'))
|
||||
miningLaserCount--
|
||||
ship.use(s, ModuleUtils.hardpoints(s.maxClass >= 2 ? '2m' : '2l'));
|
||||
miningLaserCount--;
|
||||
} else {
|
||||
ship.use(s, null)
|
||||
ship.use(s, null);
|
||||
}
|
||||
}
|
||||
|
||||
// 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))
|
||||
.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)))
|
||||
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 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))
|
||||
.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')) {
|
||||
// Collector only has odd classes
|
||||
const collectorClass = collectorInternals[i].maxClass % 2 === 0 ? collectorInternals[i].maxClass - 1 : collectorInternals[i].maxClass
|
||||
ship.use(collectorInternals[i], ModuleUtils.findInternal('cc', collectorClass, 'D'))
|
||||
usedSlots.push(collectorInternals[i])
|
||||
collectorLimpetsRequired -= collectorInternals[i].m.maximum
|
||||
const collectorClass = collectorInternals[i].maxClass % 2 === 0 ? collectorInternals[i].maxClass - 1 : collectorInternals[i].maxClass;
|
||||
ship.use(collectorInternals[i], ModuleUtils.findInternal('cc', collectorClass, 'D'));
|
||||
usedSlots.push(collectorInternals[i]);
|
||||
collectorLimpetsRequired -= collectorInternals[i].m.maximum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
standardOpts.pd = ship.getAvailableModules().matchingPowerDist({weprate: wepRateRequired}).id
|
||||
.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
|
||||
for (let i = ship.internal.length; i--;) {
|
||||
let slot = ship.internal[i]
|
||||
let slot = ship.internal[i];
|
||||
if (usedSlots.indexOf(slot) == -1 && canMount(ship, slot, 'cr')) {
|
||||
ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E'))
|
||||
ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E'));
|
||||
}
|
||||
}
|
||||
|
||||
ship.useLightestStandard(standardOpts)
|
||||
ship.useLightestStandard(standardOpts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Racer Role
|
||||
* @param {Ship} ship Ship instance
|
||||
*/
|
||||
export function racer (ship) {
|
||||
export function racer(ship) {
|
||||
let standardOpts = {},
|
||||
usedSlots = [],
|
||||
sgSlot,
|
||||
sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass)
|
||||
usedSlots = [],
|
||||
sgSlot,
|
||||
sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass);
|
||||
|
||||
// Cargo hatch can be disabled
|
||||
ship.setSlotEnabled(ship.cargoHatch, false)
|
||||
ship.setSlotEnabled(ship.cargoHatch, false);
|
||||
|
||||
// Shield generator
|
||||
const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8]
|
||||
const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8];
|
||||
const shieldInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
|
||||
.filter(a => (!a.eligible) || a.eligible.sg)
|
||||
.filter(a => a.maxClass >= sg.class)
|
||||
.sort((a, b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass))
|
||||
.sort((a, b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass));
|
||||
for (let i = 0; i < shieldInternals.length; i++) {
|
||||
if (canMount(ship, shieldInternals[i], 'sg')) {
|
||||
ship.use(shieldInternals[i], sg)
|
||||
usedSlots.push(shieldInternals[i])
|
||||
sgSlot = shieldInternals[i]
|
||||
break
|
||||
ship.use(shieldInternals[i], sg);
|
||||
usedSlots.push(shieldInternals[i]);
|
||||
sgSlot = shieldInternals[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Empty the hardpoints
|
||||
for (let s of ship.hardpoints) {
|
||||
ship.use(s, null)
|
||||
ship.use(s, null);
|
||||
}
|
||||
|
||||
// Empty the internals
|
||||
for (let i = ship.internal.length; i--;) {
|
||||
let slot = ship.internal[i]
|
||||
let slot = ship.internal[i];
|
||||
if (usedSlots.indexOf(slot) == -1) {
|
||||
ship.use(slot, null)
|
||||
ship.use(slot, null);
|
||||
}
|
||||
}
|
||||
|
||||
// Best thrusters
|
||||
if (ship.standard[1].maxClass === 3) {
|
||||
standardOpts.th = 'tz'
|
||||
standardOpts.th = 'tz';
|
||||
} else if (ship.standard[1].maxClass === 2) {
|
||||
standardOpts.th = 'u0'
|
||||
standardOpts.th = 'u0';
|
||||
} else {
|
||||
standardOpts.th = ship.standard[1].maxClass + 'A'
|
||||
standardOpts.th = ship.standard[1].maxClass + 'A';
|
||||
}
|
||||
|
||||
// Best power distributor for more boosting
|
||||
standardOpts.pd = ship.standard[4].maxClass + 'A'
|
||||
standardOpts.pd = ship.standard[4].maxClass + 'A';
|
||||
|
||||
// Smallest possible FSD drive
|
||||
standardOpts.fsd = '2D'
|
||||
standardOpts.fsd = '2D';
|
||||
// Minimal fuel tank
|
||||
standardOpts.ft = '1C'
|
||||
standardOpts.ft = '1C';
|
||||
|
||||
// Disable nearly everything
|
||||
standardOpts.fsdDisabled = true
|
||||
standardOpts.sDisabled = true
|
||||
standardOpts.pdDisabled = true
|
||||
standardOpts.lsDisabled = true
|
||||
standardOpts.fsdDisabled = true;
|
||||
standardOpts.sDisabled = true;
|
||||
standardOpts.pdDisabled = true;
|
||||
standardOpts.lsDisabled = true;
|
||||
|
||||
ship.useLightestStandard(standardOpts)
|
||||
ship.useLightestStandard(standardOpts);
|
||||
|
||||
// Apply engineering to each module
|
||||
// ship.standard[1].m.blueprint = getBlueprint('Engine_Dirty', ship.standard[0]);
|
||||
|
||||
@@ -13,6 +13,7 @@ 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;
|
||||
|
||||
@@ -84,6 +85,7 @@ 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);
|
||||
@@ -102,6 +104,13 @@ 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;
|
||||
|
||||
@@ -152,6 +161,10 @@ 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
|
||||
@@ -457,6 +470,23 @@ 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
|
||||
|
||||
@@ -1,431 +1,430 @@
|
||||
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 specialName
|
||||
* @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> </td>
|
||||
<td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'}
|
||||
style={{textAlign: 'right'}}>{current}{symbol}</td>
|
||||
<td> </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> </td>
|
||||
<td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{current}{symbol}</td>
|
||||
<td> </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> </td>
|
||||
<td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{current}{symbol}</td>
|
||||
<td> </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
|
||||
*/
|
||||
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 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> </td>
|
||||
<td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'}
|
||||
style={{ textAlign: 'right' }}>{current}{symbol}</td>
|
||||
<td> </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> </td>
|
||||
<td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{current}{symbol}</td>
|
||||
<td> </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> </td>
|
||||
<td className={current === 0 ? '' : currentIsBeneficial ? 'secondary' : 'warning'} style={{ textAlign: 'right' }}>{current}{symbol}</td>
|
||||
<td> </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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,7 +318,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 +345,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;
|
||||
|
||||
@@ -1,292 +1,291 @@
|
||||
import Ship from '../shipyard/Ship'
|
||||
import { HARDPOINT_NUM_TO_CLASS, shipModelFromJson } from './CompanionApiUtils'
|
||||
import { Ships } from 'coriolis-data/dist'
|
||||
import Module from '../shipyard/Module'
|
||||
import { Modules } from 'coriolis-data/dist'
|
||||
import { Modifications } from 'coriolis-data/dist'
|
||||
import { getBlueprint } from './BlueprintFunctions'
|
||||
|
||||
/**
|
||||
* Obtain a module given its FD Name
|
||||
* @param {string} fdname the FD Name of the module
|
||||
* @return {Module} the module
|
||||
*/
|
||||
function _moduleFromFdName (fdname) {
|
||||
if (!fdname) return null
|
||||
fdname = fdname.toLowerCase()
|
||||
// Check standard modules
|
||||
for (const grp in Modules.standard) {
|
||||
if (Modules.standard.hasOwnProperty(grp)) {
|
||||
for (const i in Modules.standard[grp]) {
|
||||
if (Modules.standard[grp][i].symbol && Modules.standard[grp][i].symbol.toLowerCase() === fdname) {
|
||||
// Found it
|
||||
return new Module({template: Modules.standard[grp][i]})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check hardpoint modules
|
||||
for (const grp in Modules.hardpoints) {
|
||||
if (Modules.hardpoints.hasOwnProperty(grp)) {
|
||||
for (const i in Modules.hardpoints[grp]) {
|
||||
if (Modules.hardpoints[grp][i].symbol && Modules.hardpoints[grp][i].symbol.toLowerCase() === fdname) {
|
||||
// Found it
|
||||
return new Module({template: Modules.hardpoints[grp][i]})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check internal modules
|
||||
for (const grp in Modules.internal) {
|
||||
if (Modules.internal.hasOwnProperty(grp)) {
|
||||
for (const i in Modules.internal[grp]) {
|
||||
if (Modules.internal[grp][i].symbol && Modules.internal[grp][i].symbol.toLowerCase() === fdname) {
|
||||
// Found it
|
||||
return new Module({template: Modules.internal[grp][i]})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Not found
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a ship from the journal Loadout event JSON
|
||||
* @param {object} json the Loadout event JSON
|
||||
* @return {Ship} the built ship
|
||||
*/
|
||||
export function shipFromLoadoutJSON (json) {
|
||||
// Start off building a basic ship
|
||||
const shipModel = shipModelFromJson(json)
|
||||
if (!shipModel) {
|
||||
throw 'No such ship found: "' + json.Ship + '"'
|
||||
}
|
||||
const shipTemplate = Ships[shipModel]
|
||||
|
||||
let ship = new Ship(shipModel, shipTemplate.properties, shipTemplate.slots)
|
||||
ship.buildWith(null)
|
||||
// Initial Ship building, don't do engineering yet.
|
||||
let opts = [];
|
||||
|
||||
for (const module of json.Modules) {
|
||||
switch (module.Slot.toLowerCase()) {
|
||||
// Cargo Hatch.
|
||||
case 'cargohatch':
|
||||
ship.cargoHatch.enabled = module.On
|
||||
ship.cargoHatch.priority = module.Priority
|
||||
break
|
||||
// Add the bulkheads
|
||||
case 'armour':
|
||||
if (module.Item.toLowerCase().endsWith('_armour_grade1')) {
|
||||
ship.useBulkhead(0, true)
|
||||
} else if (module.Item.toLowerCase().endsWith('_armour_grade2')) {
|
||||
ship.useBulkhead(1, true)
|
||||
} else if (module.Item.toLowerCase().endsWith('_armour_grade3')) {
|
||||
ship.useBulkhead(2, true)
|
||||
} else if (module.Item.toLowerCase().endsWith('_armour_mirrored')) {
|
||||
ship.useBulkhead(3, true)
|
||||
} else if (module.Item.toLowerCase().endsWith('_armour_reactive')) {
|
||||
ship.useBulkhead(4, true)
|
||||
} else {
|
||||
throw 'Unknown bulkheads "' + module.Item + '"'
|
||||
}
|
||||
ship.bulkheads.enabled = true
|
||||
if (module.Engineering) _addModifications(ship.bulkheads.m, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect)
|
||||
break
|
||||
case 'powerplant':
|
||||
const powerplant = _moduleFromFdName(module.Item)
|
||||
ship.use(ship.standard[0], powerplant, true)
|
||||
ship.standard[0].enabled = module.On
|
||||
ship.standard[0].priority = module.Priority
|
||||
if (module.Engineering) _addModifications(powerplant, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect)
|
||||
break
|
||||
case 'mainengines':
|
||||
const thrusters = _moduleFromFdName(module.Item)
|
||||
ship.use(ship.standard[1], thrusters, true)
|
||||
ship.standard[1].enabled = module.On
|
||||
ship.standard[1].priority = module.Priority
|
||||
if (module.Engineering) _addModifications(thrusters, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect)
|
||||
break
|
||||
case 'frameshiftdrive':
|
||||
const frameshiftdrive = _moduleFromFdName(module.Item)
|
||||
ship.use(ship.standard[2], frameshiftdrive, true)
|
||||
ship.standard[2].enabled = module.On
|
||||
ship.standard[2].priority = module.Priority
|
||||
if (module.Engineering) _addModifications(frameshiftdrive, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect)
|
||||
break
|
||||
case 'lifesupport':
|
||||
const lifesupport = _moduleFromFdName(module.Item)
|
||||
ship.use(ship.standard[3], lifesupport, true)
|
||||
ship.standard[3].enabled = module.On === true
|
||||
ship.standard[3].priority = module.Priority
|
||||
if (module.Engineering) _addModifications(lifesupport, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect)
|
||||
break
|
||||
case 'powerdistributor':
|
||||
const powerdistributor = _moduleFromFdName(module.Item)
|
||||
ship.use(ship.standard[4], powerdistributor, true)
|
||||
ship.standard[4].enabled = module.On
|
||||
ship.standard[4].priority = module.Priority
|
||||
if (module.Engineering) _addModifications(powerdistributor, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect)
|
||||
break
|
||||
case 'radar':
|
||||
const sensors = _moduleFromFdName(module.Item)
|
||||
ship.use(ship.standard[5], sensors, true)
|
||||
ship.standard[5].enabled = module.On
|
||||
ship.standard[5].priority = module.Priority
|
||||
if (module.Engineering) _addModifications(sensors, module.Engineering.Modifiers, module.Engineering.BlueprintName, module.Engineering.Level, module.Engineering.ExperimentalEffect)
|
||||
break
|
||||
case 'fueltank':
|
||||
const fueltank = _moduleFromFdName(module.Item)
|
||||
ship.use(ship.standard[6], fueltank, true)
|
||||
ship.standard[6].enabled = true
|
||||
ship.standard[6].priority = 0
|
||||
break
|
||||
default:
|
||||
}
|
||||
for (const module of json.Modules) {
|
||||
if (module.Slot.toLowerCase().search(/hardpoint/) !== -1) {
|
||||
// Add hardpoints
|
||||
let hardpoint;
|
||||
let hardpointClassNum = -1
|
||||
let hardpointSlotNum = -1
|
||||
let hardpointArrayNum = 0
|
||||
for (let i in shipTemplate.slots.hardpoints) {
|
||||
if (shipTemplate.slots.hardpoints[i] === hardpointClassNum) {
|
||||
// Another slot of the same class
|
||||
hardpointSlotNum++
|
||||
} else {
|
||||
// The first slot of a new class
|
||||
hardpointClassNum = shipTemplate.slots.hardpoints[i]
|
||||
hardpointSlotNum = 1
|
||||
}
|
||||
|
||||
// Now that we know what we're looking for, find it
|
||||
const hardpointName = HARDPOINT_NUM_TO_CLASS[hardpointClassNum] + 'Hardpoint' + hardpointSlotNum
|
||||
const hardpointSlot = json.Modules.find(elem => elem.Slot.toLowerCase() === hardpointName.toLowerCase())
|
||||
if (!hardpointSlot) {
|
||||
// This can happen with old imports that don't contain new hardpoints
|
||||
} else if (!hardpointSlot) {
|
||||
// No module
|
||||
} else {
|
||||
hardpoint = _moduleFromFdName(hardpointSlot.Item)
|
||||
ship.use(ship.hardpoints[hardpointArrayNum], hardpoint, true)
|
||||
ship.hardpoints[hardpointArrayNum].enabled = hardpointSlot.On
|
||||
ship.hardpoints[hardpointArrayNum].priority = hardpointSlot.Priority
|
||||
opts.push({coriolisMod: hardpoint, json: hardpointSlot});
|
||||
}
|
||||
hardpointArrayNum++
|
||||
}
|
||||
}
|
||||
if (module.Slot.toLowerCase().search(/slot\d/) !== -1) {
|
||||
let internalSlotNum = 1
|
||||
let militarySlotNum = 1
|
||||
for (let i in shipTemplate.slots.internal) {
|
||||
const isMilitary = isNaN(shipTemplate.slots.internal[i]) ? shipTemplate.slots.internal[i].name = 'military' : false
|
||||
|
||||
// The internal slot might be a standard or a military slot. Military slots have a different naming system
|
||||
let internalSlot = null
|
||||
if (isMilitary) {
|
||||
const internalName = 'Military0' + militarySlotNum
|
||||
internalSlot = json.Modules.find(elem => elem.Slot.toLowerCase() === internalName.toLowerCase())
|
||||
militarySlotNum++
|
||||
} else {
|
||||
// Slot numbers are not contiguous so handle skips.
|
||||
while (internalSlot === null && internalSlotNum < 99) {
|
||||
// Slot sizes have no relationship to the actual size, either, so check all possibilities
|
||||
for (let slotsize = 0; slotsize < 9; slotsize++) {
|
||||
const internalName = 'Slot' + (internalSlotNum <= 9 ? '0' : '') + internalSlotNum + '_Size' + slotsize
|
||||
if (json.Modules.find(elem => elem.Slot.toLowerCase() === internalName.toLowerCase())) {
|
||||
internalSlot = json.Modules.find(elem => elem.Slot.toLowerCase() === internalName.toLowerCase());
|
||||
break
|
||||
}
|
||||
}
|
||||
internalSlotNum++
|
||||
}
|
||||
}
|
||||
|
||||
if (!internalSlot) {
|
||||
// This can happen with old imports that don't contain new slots
|
||||
} else if (!internalSlot) {
|
||||
// No module
|
||||
} else {
|
||||
const internalJson = internalSlot
|
||||
const internal = _moduleFromFdName(internalJson.Item)
|
||||
ship.use(ship.internal[i], internal, true)
|
||||
ship.internal[i].enabled = internalJson.On === true
|
||||
ship.internal[i].priority = internalJson.Priority
|
||||
opts.push({coriolisMod: internal, json: internalSlot});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const i of opts) {
|
||||
if (i.json.Engineering) _addModifications(i.coriolisMod, i.json.Engineering.Modifiers, i.json.Engineering.BlueprintName, i.json.Engineering.Level, i.json.Engineering.ExperimentalEffect)
|
||||
}
|
||||
// We don't have any information on it so guess it's priority 5 and disabled
|
||||
if (!ship.cargoHatch) {
|
||||
ship.cargoHatch.enabled = false
|
||||
ship.cargoHatch.priority = 4
|
||||
}
|
||||
console.log(ship)
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import request from 'superagent';
|
||||
* @param {function} error Failure/Error callback
|
||||
*/
|
||||
export default function shorternUrl(url, success, error) {
|
||||
shortenUrlEddp(url, success, error);
|
||||
shortenUrlOrbis(url, success, error);
|
||||
}
|
||||
|
||||
const SHORTEN_API_GOOGLE = 'https://www.googleapis.com/urlshortener/v1/url?key=';
|
||||
@@ -64,3 +64,32 @@ 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');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,7 +51,7 @@
|
||||
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 -->
|
||||
|
||||
@@ -12,6 +12,20 @@
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user