mirror of
https://github.com/EDCD/coriolis.git
synced 2025-12-09 14:45:35 +00:00
300 lines
7.6 KiB
JavaScript
300 lines
7.6 KiB
JavaScript
import React from 'react';
|
|
import PropTypes from 'prop-types';
|
|
import TranslatedComponent from './TranslatedComponent';
|
|
import { Ships } from 'coriolis-data/dist';
|
|
import { nameComparator } from '../utils/SlotFunctions';
|
|
import { Pip } from './SvgIcons';
|
|
import LineChart from '../components/LineChart';
|
|
import Slider from '../components/Slider';
|
|
import * as ModuleUtils from '../shipyard/ModuleUtils';
|
|
import Module from '../shipyard/Module';
|
|
|
|
/**
|
|
* Pips displays SYS/ENG/WEP pips and allows users to change them with key presses by clicking on the relevant area.
|
|
* Requires an onChange() function of the form onChange(sys, eng, wep) which is triggered whenever the pips change.
|
|
*/
|
|
export default class Pips extends TranslatedComponent {
|
|
static propTypes = {
|
|
sys: PropTypes.number.isRequired,
|
|
eng: PropTypes.number.isRequired,
|
|
wep: PropTypes.number.isRequired,
|
|
onChange: PropTypes.func.isRequired
|
|
};
|
|
|
|
/**
|
|
* Constructor
|
|
* @param {Object} props React Component properties
|
|
* @param {Object} context React Component context
|
|
*/
|
|
constructor(props, context) {
|
|
super(props);
|
|
const { sys, eng, wep } = props;
|
|
|
|
this._keyDown = this._keyDown.bind(this);
|
|
}
|
|
|
|
/**
|
|
* Add listeners after mounting
|
|
*/
|
|
componentDidMount() {
|
|
document.addEventListener('keydown', this._keyDown);
|
|
}
|
|
|
|
/**
|
|
* Remove listeners before unmounting
|
|
*/
|
|
componentWillUnmount() {
|
|
document.removeEventListener('keydown', this._keyDown);
|
|
}
|
|
|
|
/**
|
|
* Handle Key Down
|
|
* @param {Event} e Keyboard Event
|
|
*/
|
|
_keyDown(e) {
|
|
if (e.ctrlKey || e.metaKey) { // CTRL/CMD
|
|
switch (e.keyCode) {
|
|
case 37: // Left arrow == increase SYS
|
|
e.preventDefault();
|
|
this._incSys();
|
|
break;
|
|
case 38: // Up arrow == increase ENG
|
|
e.preventDefault();
|
|
this._incEng();
|
|
break;
|
|
case 39: // Right arrow == increase WEP
|
|
e.preventDefault();
|
|
this._incWep();
|
|
break;
|
|
case 40: // Down arrow == reset
|
|
e.preventDefault();
|
|
this._reset();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle a click
|
|
* @param {string} which Which item was clicked
|
|
*/
|
|
onClick(which) {
|
|
if (which == 'SYS') {
|
|
this._incSys();
|
|
} else if (which == 'ENG') {
|
|
this._incEng();
|
|
} else if (which == 'WEP') {
|
|
this._incWep();
|
|
} else if (which == 'RST') {
|
|
this._reset();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reset the capacitor
|
|
*/
|
|
_reset() {
|
|
let { sys, eng, wep } = this.props;
|
|
if (sys != 2 || eng != 2 || wep != 2) {
|
|
sys = eng = wep = 2;
|
|
this.props.onChange(sys, eng, wep);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Increment the SYS capacitor
|
|
*/
|
|
_incSys() {
|
|
let { sys, eng, wep } = this.props;
|
|
|
|
const required = Math.min(1, 4 - sys);
|
|
if (required > 0) {
|
|
if (required == 0.5) {
|
|
// Take from whichever is larger
|
|
if (eng > wep) {
|
|
eng -= 0.5;
|
|
sys += 0.5;
|
|
} else {
|
|
wep -= 0.5;
|
|
sys += 0.5;
|
|
}
|
|
} else {
|
|
// Required is 1 - take from both if possible
|
|
if (eng == 0) {
|
|
wep -= 1;
|
|
sys += 1;
|
|
} else if (wep == 0) {
|
|
eng -= 1;
|
|
sys += 1;
|
|
} else {
|
|
eng -= 0.5;
|
|
wep -= 0.5;
|
|
sys += 1;
|
|
}
|
|
}
|
|
this.props.onChange(sys, eng, wep);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Increment the ENG capacitor
|
|
*/
|
|
_incEng() {
|
|
let { sys, eng, wep } = this.props;
|
|
|
|
const required = Math.min(1, 4 - eng);
|
|
if (required > 0) {
|
|
if (required == 0.5) {
|
|
// Take from whichever is larger
|
|
if (sys > wep) {
|
|
sys -= 0.5;
|
|
eng += 0.5;
|
|
} else {
|
|
wep -= 0.5;
|
|
eng += 0.5;
|
|
}
|
|
} else {
|
|
// Required is 1 - take from both if possible
|
|
if (sys == 0) {
|
|
wep -= 1;
|
|
eng += 1;
|
|
} else if (wep == 0) {
|
|
sys -= 1;
|
|
eng += 1;
|
|
} else {
|
|
sys -= 0.5;
|
|
wep -= 0.5;
|
|
eng += 1;
|
|
}
|
|
}
|
|
this.props.onChange(sys, eng, wep);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Increment the WEP capacitor
|
|
*/
|
|
_incWep() {
|
|
let { sys, eng, wep } = this.props;
|
|
|
|
const required = Math.min(1, 4 - wep);
|
|
if (required > 0) {
|
|
if (required == 0.5) {
|
|
// Take from whichever is larger
|
|
if (sys > eng) {
|
|
sys -= 0.5;
|
|
wep += 0.5;
|
|
} else {
|
|
eng -= 0.5;
|
|
wep += 0.5;
|
|
}
|
|
} else {
|
|
// Required is 1 - take from both if possible
|
|
if (sys == 0) {
|
|
eng -= 1;
|
|
wep += 1;
|
|
} else if (eng == 0) {
|
|
sys -= 1;
|
|
wep += 1;
|
|
} else {
|
|
sys -= 0.5;
|
|
eng -= 0.5;
|
|
wep += 1;
|
|
}
|
|
}
|
|
this.props.onChange(sys, eng, wep);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set up the rendering for pips
|
|
* @param {int} sys the SYS pips
|
|
* @param {int} eng the ENG pips
|
|
* @param {int} wep the WEP pips
|
|
* @returns {Object} Object containing the rendering for the pips
|
|
*/
|
|
_renderPips(sys, eng, wep) {
|
|
const pipsSvg = {};
|
|
|
|
// SYS
|
|
pipsSvg['SYS'] = [];
|
|
for (let i = 0; i < Math.floor(sys); i++) {
|
|
pipsSvg['SYS'].push(<Pip className='full' key={i} />);
|
|
}
|
|
if (sys > Math.floor(sys)) {
|
|
pipsSvg['SYS'].push(<Pip className='half' key={'half'} />);
|
|
}
|
|
for (let i = Math.floor(sys + 0.5); i < 4; i++) {
|
|
pipsSvg['SYS'].push(<Pip className='empty' key={i} />);
|
|
}
|
|
|
|
// ENG
|
|
pipsSvg['ENG'] = [];
|
|
for (let i = 0; i < Math.floor(eng); i++) {
|
|
pipsSvg['ENG'].push(<Pip className='full' key={i} />);
|
|
}
|
|
if (eng > Math.floor(eng)) {
|
|
pipsSvg['ENG'].push(<Pip className='half' key={'half'} />);
|
|
}
|
|
for (let i = Math.floor(eng + 0.5); i < 4; i++) {
|
|
pipsSvg['ENG'].push(<Pip className='empty' key={i} />);
|
|
}
|
|
|
|
// WEP
|
|
pipsSvg['WEP'] = [];
|
|
for (let i = 0; i < Math.floor(wep); i++) {
|
|
pipsSvg['WEP'].push(<Pip className='full' key={i} />);
|
|
}
|
|
if (wep > Math.floor(wep)) {
|
|
pipsSvg['WEP'].push(<Pip className='half' key={'half'} />);
|
|
}
|
|
for (let i = Math.floor(wep + 0.5); i < 4; i++) {
|
|
pipsSvg['WEP'].push(<Pip className='empty' key={i} />);
|
|
}
|
|
|
|
return pipsSvg;
|
|
}
|
|
|
|
/**
|
|
* Render pips
|
|
* @return {React.Component} contents
|
|
*/
|
|
render() {
|
|
const { formats, translate, units } = this.context.language;
|
|
const { sys, eng, wep } = this.props;
|
|
|
|
const onSysClicked = this.onClick.bind(this, 'SYS');
|
|
const onEngClicked = this.onClick.bind(this, 'ENG');
|
|
const onWepClicked = this.onClick.bind(this, 'WEP');
|
|
const onRstClicked = this.onClick.bind(this, 'RST');
|
|
|
|
const pipsSvg = this._renderPips(sys, eng, wep);
|
|
return (
|
|
<span id='pips'>
|
|
<table>
|
|
<tbody>
|
|
<tr>
|
|
<td> </td>
|
|
<td> </td>
|
|
<td className='clickable' onClick={onEngClicked}>{pipsSvg['ENG']}</td>
|
|
<td> </td>
|
|
</tr>
|
|
<tr>
|
|
<td> </td>
|
|
<td className='clickable' onClick={onSysClicked}>{pipsSvg['SYS']}</td>
|
|
<td className='clickable' onClick={onEngClicked}>{translate('ENG')}</td>
|
|
<td className='clickable' onClick={onWepClicked}>{pipsSvg['WEP']}</td>
|
|
</tr>
|
|
<tr>
|
|
<td> </td>
|
|
<td className='clickable' onClick={onSysClicked}>{translate('SYS')}</td>
|
|
<td className='clickable' onClick={onRstClicked}>{translate('RST')}</td>
|
|
<td className='clickable' onClick={onWepClicked}>{translate('WEP')}</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</span>
|
|
);
|
|
}
|
|
}
|