mirror of
https://github.com/EDCD/coriolis.git
synced 2025-12-09 14:45:35 +00:00
Building components for battle centre
This commit is contained in:
98
src/app/components/BattleCentre.jsx
Normal file
98
src/app/components/BattleCentre.jsx
Normal file
@@ -0,0 +1,98 @@
|
||||
import React from 'react';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import { Ships } from 'coriolis-data/dist';
|
||||
import Slider from '../components/Slider';
|
||||
import Pips from '../components/Pips';
|
||||
import Fuel from '../components/Fuel';
|
||||
import Cargo from '../components/Cargo';
|
||||
import EngagementRange from '../components/EngagementRange';
|
||||
|
||||
/**
|
||||
* Battle centre allows you to pit your current build against another ship,
|
||||
* adjust pips and engagement range, and see a wide variety of information
|
||||
*/
|
||||
export default class BattleCentre extends TranslatedComponent {
|
||||
static PropTypes = {
|
||||
ship: React.PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
static DEFAULT_OPPONENT = { ship: Ships['anaconda'] };
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param {Object} props React Component properties
|
||||
* @param {Object} context React Component context
|
||||
*/
|
||||
constructor(props, context) {
|
||||
super(props);
|
||||
|
||||
const { ship } = this.props;
|
||||
const opponent = BattleCentre.DEFAULT_OPPONENT;
|
||||
|
||||
this.state = { };
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
// Rather than try to keep track of what changes our children require we force an update and let them work it out
|
||||
this.forceUpdate();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggered when pips have been updated
|
||||
*/
|
||||
_pipsUpdated(sys, eng, wep) {
|
||||
console.log('Pips are now ' + sys + '/' + eng + '/' + wep);
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggered when fuel has been updated
|
||||
*/
|
||||
_fuelUpdated(fuel) {
|
||||
console.log('Fuel is now ' + fuel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggered when cargo has been updated
|
||||
*/
|
||||
_cargoUpdated(cargo) {
|
||||
console.log('Cargo is now ' + cargo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggered when engagement range has been updated
|
||||
*/
|
||||
_engagementRangeUpdated(engagementRange) {
|
||||
console.log('Engagement range is now ' + engagementRange);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render
|
||||
* @return {React.Component} contents
|
||||
*/
|
||||
render() {
|
||||
const { language, onWindowResize, sizeRatio, tooltip, termtip } = this.context;
|
||||
const { formats, translate, units } = language;
|
||||
const { against, expanded, maxRange, range, totals } = this.state;
|
||||
const { ship } = this.props;
|
||||
const shipUpdated = this._shipUpdated;
|
||||
const pipsUpdated = this._pipsUpdated;
|
||||
const fuelUpdated = this._fuelUpdated;
|
||||
const cargoUpdated = this._cargoUpdated;
|
||||
const engagementRangeUpdated = this._engagementRangeUpdated;
|
||||
|
||||
return (
|
||||
<span>
|
||||
<h1>{translate('battle centre')}</h1>
|
||||
<div className='group third'>
|
||||
<Pips ship={ship} onChange={pipsUpdated}/>
|
||||
</div>
|
||||
<div className='group twothirds'>
|
||||
<Fuel ship={ship} onChange={fuelUpdated}/>
|
||||
<Cargo ship={ship} onChange={cargoUpdated}/>
|
||||
<EngagementRange ship={ship} onChange={engagementRangeUpdated}/>
|
||||
</div>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
102
src/app/components/Cargo.jsx
Normal file
102
src/app/components/Cargo.jsx
Normal file
@@ -0,0 +1,102 @@
|
||||
import React from 'react';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import { Ships } from 'coriolis-data/dist';
|
||||
import Slider from '../components/Slider';
|
||||
|
||||
/**
|
||||
* Cargo slider
|
||||
* Requires an onChange() function of the form onChange(cargo), providing the cargo in tonnes, which is triggered on cargo level change
|
||||
*/
|
||||
export default class Cargo extends TranslatedComponent {
|
||||
static PropTypes = {
|
||||
ship: React.PropTypes.object.isRequired,
|
||||
onChange: React.PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param {Object} props React Component properties
|
||||
* @param {Object} context React Component context
|
||||
*/
|
||||
constructor(props, context) {
|
||||
super(props);
|
||||
|
||||
const ship = this.props.ship;
|
||||
|
||||
this.state = {
|
||||
cargoCapacity: ship.cargoCapacity,
|
||||
cargoLevel: 1,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the state if our ship changes
|
||||
* @param {Object} nextProps Incoming/Next properties
|
||||
* @return {boolean} Returns true if the component should be rerendered
|
||||
*/
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const { cargoLevel, cargoCapacity } = this.state;
|
||||
const nextCargoCapacity = nextProps.ship.cargoCapacity;
|
||||
|
||||
if (nextCargoCapacity != cargoCapacity) {
|
||||
// We keep the absolute cargo amount the same if possible so recalculate the relative level
|
||||
const nextCargoLevel = Math.min((cargoLevel * cargoCapacity) / nextCargoCapacity, 1);
|
||||
|
||||
this.setState({ cargoLevel: nextCargoLevel, cargoCapacity: nextCargoCapacity });
|
||||
|
||||
// Notify if appropriate
|
||||
if (nextCargoLevel * nextCargoCapacity != cargoLevel * cargoCapacity) {
|
||||
this.props.onChange(Math.round(nextCargoLevel * nextCargoCapacity));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update cargo level
|
||||
* @param {number} cargoLevel percentage level from 0 to 1
|
||||
*/
|
||||
_cargoChange(cargoLevel) {
|
||||
const { cargoCapacity } = this.state;
|
||||
// We round the cargo level to a suitable value given the capacity
|
||||
cargoLevel = Math.round(cargoLevel * cargoCapacity) / cargoCapacity;
|
||||
|
||||
if (cargoLevel != this.state.cargoLevel) {
|
||||
this.setState({ cargoLevel });
|
||||
this.props.onChange(Math.round(cargoLevel * cargoCapacity));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render cargo slider
|
||||
* @return {React.Component} contents
|
||||
*/
|
||||
render() {
|
||||
const { language, onWindowResize, sizeRatio, tooltip, termtip } = this.context;
|
||||
const { formats, translate, units } = language;
|
||||
const { cargoLevel, cargoCapacity } = this.state;
|
||||
|
||||
return (
|
||||
<span>
|
||||
<h3>{translate('cargo carried')}: {formats.int(cargoLevel * cargoCapacity)}{units.T}</h3>
|
||||
<table style={{ width: '100%', lineHeight: '1em', backgroundColor: 'transparent' }}>
|
||||
<tbody >
|
||||
<tr>
|
||||
<td>
|
||||
<Slider
|
||||
axis={true}
|
||||
onChange={this._cargoChange.bind(this)}
|
||||
axisUnit={translate('T')}
|
||||
percent={cargoLevel}
|
||||
max={cargoCapacity}
|
||||
scale={sizeRatio}
|
||||
onResize={onWindowResize}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
123
src/app/components/EngagementRange.jsx
Normal file
123
src/app/components/EngagementRange.jsx
Normal file
@@ -0,0 +1,123 @@
|
||||
import React from 'react';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import { Ships } from 'coriolis-data/dist';
|
||||
import Slider from '../components/Slider';
|
||||
|
||||
/**
|
||||
* Engagement range slider
|
||||
* Requires an onChange() function of the form onChange(range), providing the range in metres, which is triggered on range change
|
||||
*/
|
||||
export default class Range extends TranslatedComponent {
|
||||
static PropTypes = {
|
||||
ship: React.PropTypes.object.isRequired,
|
||||
onChange: React.PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param {Object} props React Component properties
|
||||
* @param {Object} context React Component context
|
||||
*/
|
||||
constructor(props, context) {
|
||||
super(props);
|
||||
|
||||
const ship = this.props.ship;
|
||||
|
||||
const maxRange = this._calcMaxRange(ship);
|
||||
|
||||
this.state = {
|
||||
maxRange: maxRange,
|
||||
rangeLevel: 1,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the state if our ship changes
|
||||
* @param {Object} nextProps Incoming/Next properties
|
||||
* @return {boolean} Returns true if the component should be rerendered
|
||||
*/
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const { rangeLevel, maxRange } = this.state;
|
||||
const nextMaxRange = this._calcMaxRange(nextProps.ship);
|
||||
|
||||
if (nextMaxRange != maxRange) {
|
||||
// We keep the absolute range amount the same if possible so recalculate the relative level
|
||||
const nextRangeLevel = Math.min((rangeLevel * maxRange) / nextMaxRange, 1);
|
||||
|
||||
this.setState({ rangeLevel: nextRangeLevel, maxRange: nextMaxRange });
|
||||
|
||||
// Notify if appropriate
|
||||
if (nextRangeLevel * nextMaxRange != rangeLevel * maxRange) {
|
||||
this.props.onChange(Math.round(nextRangeLevel * nextMaxRange));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the maximum range of a ship's weapons
|
||||
* @param {Object} ship The ship
|
||||
* @returns {int} The maximum range, in metres
|
||||
*/
|
||||
_calcMaxRange(ship) {
|
||||
let maxRange = 1000;
|
||||
for (let i = 0; i < ship.hardpoints.length; i++) {
|
||||
if (ship.hardpoints[i].maxClass > 0 && ship.hardpoints[i].m && ship.hardpoints[i].enabled) {
|
||||
const thisRange = ship.hardpoints[i].m.getRange();
|
||||
if (thisRange > maxRange) {
|
||||
maxRange = thisRange;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return maxRange;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update range
|
||||
* @param {number} range percentage level from 0 to 1
|
||||
*/
|
||||
_rangeChange(rangeLevel) {
|
||||
const { maxRange } = this.state;
|
||||
// We round the range to an integer value
|
||||
rangeLevel = Math.round(rangeLevel * maxRange) / maxRange;
|
||||
|
||||
if (rangeLevel != this.state.rangeLevel) {
|
||||
this.setState({ rangeLevel });
|
||||
this.props.onChange(Math.round(rangeLevel * maxRange));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render range slider
|
||||
* @return {React.Component} contents
|
||||
*/
|
||||
render() {
|
||||
const { language, onWindowResize, sizeRatio, tooltip, termtip } = this.context;
|
||||
const { formats, translate, units } = language;
|
||||
const { rangeLevel, maxRange } = this.state;
|
||||
|
||||
return (
|
||||
<span>
|
||||
<h3>{translate('engagement range')}: {formats.int(rangeLevel * maxRange)}{translate('m')}</h3>
|
||||
<table style={{ width: '100%', lineHeight: '1em', backgroundColor: 'transparent' }}>
|
||||
<tbody >
|
||||
<tr>
|
||||
<td>
|
||||
<Slider
|
||||
axis={true}
|
||||
onChange={this._rangeChange.bind(this)}
|
||||
axisUnit={translate('m')}
|
||||
percent={rangeLevel}
|
||||
max={maxRange}
|
||||
scale={sizeRatio}
|
||||
onResize={onWindowResize}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
96
src/app/components/Fuel.jsx
Normal file
96
src/app/components/Fuel.jsx
Normal file
@@ -0,0 +1,96 @@
|
||||
import React from 'react';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import { Ships } from 'coriolis-data/dist';
|
||||
import Slider from '../components/Slider';
|
||||
|
||||
/**
|
||||
* Fuel slider
|
||||
* Requires an onChange() function of the form onChange(fuel), providing the fuel in tonnes, which is triggered on fuel level change
|
||||
*/
|
||||
export default class Fuel extends TranslatedComponent {
|
||||
static PropTypes = {
|
||||
ship: React.PropTypes.object.isRequired,
|
||||
onChange: React.PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param {Object} props React Component properties
|
||||
* @param {Object} context React Component context
|
||||
*/
|
||||
constructor(props, context) {
|
||||
super(props);
|
||||
|
||||
const ship = this.props.ship;
|
||||
|
||||
this.state = {
|
||||
fuelCapacity: ship.fuelCapacity,
|
||||
fuelLevel: 1,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the state if our ship changes
|
||||
* @param {Object} nextProps Incoming/Next properties
|
||||
* @return {boolean} Returns true if the component should be rerendered
|
||||
*/
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const { fuelLevel, fuelCapacity } = this.state;
|
||||
const nextFuelCapacity = nextProps.ship.fuelCapacity;
|
||||
|
||||
if (nextFuelCapacity != fuelCapacity) {
|
||||
// We keep the absolute fuel amount the same if possible so recalculate the relative level
|
||||
const nextFuelLevel = Math.min((fuelLevel * fuelCapacity) / nextFuelCapacity, 1);
|
||||
|
||||
this.setState({ fuelLevel: nextFuelLevel, fuelCapacity: nextFuelCapacity });
|
||||
|
||||
// Notify if appropriate
|
||||
if (nextFuelLevel * nextFuelCapacity != fuelLevel * fuelCapacity) {
|
||||
this.props.onChange(nextFuelLevel * nextFuelCapacity);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update fuel level
|
||||
* @param {number} fuelLevel percentage level from 0 to 1
|
||||
*/
|
||||
_fuelChange(fuelLevel) {
|
||||
this.setState({ fuelLevel });
|
||||
this.props.onChange(fuelLevel * this.state.fuelCapacity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render fuel slider
|
||||
* @return {React.Component} contents
|
||||
*/
|
||||
render() {
|
||||
const { language, onWindowResize, sizeRatio, tooltip, termtip } = this.context;
|
||||
const { formats, translate, units } = language;
|
||||
const { fuelLevel, fuelCapacity } = this.state;
|
||||
|
||||
return (
|
||||
<span>
|
||||
<h3>{translate('fuel carried')}: {formats.f2(fuelLevel * fuelCapacity)}{units.T}</h3>
|
||||
<table style={{ width: '100%', lineHeight: '1em', backgroundColor: 'transparent' }}>
|
||||
<tbody >
|
||||
<tr>
|
||||
<td>
|
||||
<Slider
|
||||
axis={true}
|
||||
onChange={this._fuelChange.bind(this)}
|
||||
axisUnit={translate('T')}
|
||||
percent={fuelLevel}
|
||||
max={fuelCapacity}
|
||||
scale={sizeRatio}
|
||||
onResize={onWindowResize}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
358
src/app/components/Pips.jsx
Normal file
358
src/app/components/Pips.jsx
Normal file
@@ -0,0 +1,358 @@
|
||||
import React from 'react';
|
||||
import TranslatedComponent from './TranslatedComponent';
|
||||
import { Ships } from 'coriolis-data/dist';
|
||||
import ShipSelector from './ShipSelector';
|
||||
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 = {
|
||||
ship: React.PropTypes.object.isRequired,
|
||||
onChange: React.PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param {Object} props React Component properties
|
||||
* @param {Object} context React Component context
|
||||
*/
|
||||
constructor(props, context) {
|
||||
super(props);
|
||||
const ship = props.ship;
|
||||
const pd = ship.standard[4].m;
|
||||
|
||||
this._keyDown = this._keyDown.bind(this);
|
||||
|
||||
let pipsSvg = this._renderPips(2, 2, 2);
|
||||
this.state = {
|
||||
sys: 2,
|
||||
eng: 2,
|
||||
wep: 2,
|
||||
sysCap: pd.getSystemsCapacity(),
|
||||
engCap: pd.getEnginesCapacity(),
|
||||
wepCap: pd.getWeaponsCapacity(),
|
||||
sysRate: pd.getSystemsRechargeRate(),
|
||||
engRate: pd.getEnginesRechargeRate(),
|
||||
wepRate: pd.getWeaponsRechargeRate(),
|
||||
pipsSvg
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Add listeners after mounting
|
||||
*/
|
||||
componentDidMount() {
|
||||
document.addEventListener('keydown', this._keyDown);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove listeners before unmounting
|
||||
*/
|
||||
componentWillUnmount() {
|
||||
document.removeEventListener('keydown', this._keyDown);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update values if we change ship
|
||||
* @param {Object} nextProps Incoming/Next properties
|
||||
* @returns {boolean} Returns true if the component should be rerendered
|
||||
*/
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const { sysCap, engCap, wepCap, sysRate, engRate, wepRate } = this.state;
|
||||
const ship = nextProps.ship;
|
||||
const pd = ship.standard[4].m;
|
||||
|
||||
const nextSysCap = pd.getSystemsCapacity();
|
||||
const nextEngCap = pd.getEnginesCapacity();
|
||||
const nextWepCap = pd.getWeaponsCapacity();
|
||||
const nextSysRate = pd.getSystemsRechargeRate();
|
||||
const nextEngRate = pd.getEnginesRechargeRate();
|
||||
const nextWepRate = pd.getWeaponsRechargeRate();
|
||||
if (nextSysCap != sysCap ||
|
||||
nextEngCap != engCap ||
|
||||
nextWepCap != wepCap ||
|
||||
nextSysRate != sysRate ||
|
||||
nextEngRate != engRate ||
|
||||
nextWepRate != wepRate) {
|
||||
this.setState({
|
||||
sysCap: nextSysCap,
|
||||
engCap: nextEngCap,
|
||||
wepCap: nextWepCap,
|
||||
sysRate: nextSysRate,
|
||||
engRate: nextEngRate,
|
||||
wepRate: nextWepRate
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Key Down
|
||||
* @param {Event} e Keyboard Event
|
||||
*/
|
||||
_keyDown(e) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the capacitor
|
||||
*/
|
||||
_reset() {
|
||||
let { sys, eng, wep } = this.state;
|
||||
if (sys != 2 || eng != 2 || wep != 2) {
|
||||
sys = eng = wep = 2;
|
||||
this.setState({ sys, eng, wep, pipsSvg: this._renderPips(sys, eng, wep) });
|
||||
this.props.onChange(sys, eng, wep);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the SYS capacitor
|
||||
*/
|
||||
_incSys() {
|
||||
let { sys, eng, wep } = this.state;
|
||||
|
||||
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.setState({ sys, eng, wep, pipsSvg: this._renderPips(sys, eng, wep) });
|
||||
this.props.onChange(sys, eng, wep);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the ENG capacitor
|
||||
*/
|
||||
_incEng() {
|
||||
let { sys, eng, wep } = this.state;
|
||||
|
||||
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.setState({ sys, eng, wep, pipsSvg: this._renderPips(sys, eng, wep) });
|
||||
this.props.onChange(sys, eng, wep);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the WEP capacitor
|
||||
*/
|
||||
_incWep() {
|
||||
let { sys, eng, wep } = this.state;
|
||||
|
||||
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.setState({ sys, eng, wep, pipsSvg: this._renderPips(sys, eng, wep) });
|
||||
this.props.onChange(sys, eng, wep);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a click
|
||||
*/
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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='fullpip' key={i} />);
|
||||
}
|
||||
if (sys > Math.floor(sys)) {
|
||||
pipsSvg['SYS'].push(<Pip className='halfpip' key={'half'} />);
|
||||
}
|
||||
for (let i = Math.floor(sys + 0.5); i < 4; i++) {
|
||||
pipsSvg['SYS'].push(<Pip className='emptypip' key={i} />);
|
||||
}
|
||||
|
||||
// ENG
|
||||
pipsSvg['ENG'] = [];
|
||||
for (let i = 0; i < Math.floor(eng); i++) {
|
||||
pipsSvg['ENG'].push(<Pip className='fullpip' key={i} />);
|
||||
}
|
||||
if (eng > Math.floor(eng)) {
|
||||
pipsSvg['ENG'].push(<Pip className='halfpip' key={'half'} />);
|
||||
}
|
||||
for (let i = Math.floor(eng + 0.5); i < 4; i++) {
|
||||
pipsSvg['ENG'].push(<Pip className='emptypip' key={i} />);
|
||||
}
|
||||
|
||||
// WEP
|
||||
pipsSvg['WEP'] = [];
|
||||
for (let i = 0; i < Math.floor(wep); i++) {
|
||||
pipsSvg['WEP'].push(<Pip className='fullpip' key={i} />);
|
||||
}
|
||||
if (wep > Math.floor(wep)) {
|
||||
pipsSvg['WEP'].push(<Pip className='halfpip' key={'half'} />);
|
||||
}
|
||||
for (let i = Math.floor(wep + 0.5); i < 4; i++) {
|
||||
pipsSvg['WEP'].push(<Pip className='emptypip' key={i} />);
|
||||
}
|
||||
|
||||
return pipsSvg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render pips
|
||||
* @return {React.Component} contents
|
||||
*/
|
||||
render() {
|
||||
const { formats, translate, units } = this.context.language;
|
||||
const { ship } = this.props;
|
||||
const { sys, eng, wep, sysCap, engCap, wepCap, sysRate, engRate, wepRate, pipsSvg } = this.state;
|
||||
|
||||
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');
|
||||
|
||||
return (
|
||||
<table className='pipstable'>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td> </td>
|
||||
<td className = 'pipsclickable' onClick={onEngClicked}>{pipsSvg['ENG']}</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td className = 'pipsclickable' onClick={onSysClicked}>{pipsSvg['SYS']}</td>
|
||||
<td className = 'pipsclickable' onClick={onEngClicked}>{translate('ENG')}</td>
|
||||
<td className = 'pipsclickable' onClick={onWepClicked}>{pipsSvg['WEP']}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td className = 'pipsclickable' onClick={onSysClicked}>{translate('SYS')}</td>
|
||||
<td className = 'pipsclickable' onClick={onRstClicked}>{translate('RST')}</td>
|
||||
<td className = 'pipsclickable' onClick={onWepClicked}>{translate('WEP')}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{translate('capacity')} ({units.MJ})</td>
|
||||
<td>{formats.f1(sysCap)}</td>
|
||||
<td>{formats.f1(engCap)}</td>
|
||||
<td>{formats.f1(wepCap)}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{translate('recharge')} ({units.MW})</td>
|
||||
<td>{formats.f1(sysRate * (sys / 4))}</td>
|
||||
<td>{formats.f1(engRate * (eng / 4))}</td>
|
||||
<td>{formats.f1(wepRate * (wep / 4))}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -708,6 +708,24 @@ export class Switch extends SvgIcon {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pip
|
||||
*/
|
||||
export class Pip extends SvgIcon {
|
||||
/**
|
||||
* Overriden view box
|
||||
* @return {String} view box
|
||||
*/
|
||||
viewBox() { return '0 0 200 200'; }
|
||||
/**
|
||||
* Generate the SVG
|
||||
* @return {React.Component} SVG Contents
|
||||
*/
|
||||
svg() {
|
||||
return <rect x='10' y='10' width='180' height='180'/>;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* In-game Coriolis Station logo
|
||||
*/
|
||||
|
||||
@@ -58,21 +58,21 @@ export function getLanguage(langCode) {
|
||||
},
|
||||
translate,
|
||||
units: {
|
||||
CR: <u> {translate('CR')}</u>, // Credits
|
||||
kg: <u> {translate('kg')}</u>, // Kilograms
|
||||
kgs: <u> {translate('kg/s')}</u>, // Kilograms per second
|
||||
km: <u> {translate('km')}</u>, // Kilometers
|
||||
Ls: <u> {translate('Ls')}</u>, // Light Seconds
|
||||
LY: <u> {translate('LY')}</u>, // Light Years
|
||||
MJ: <u> {translate('MJ')}</u>, // Mega Joules
|
||||
'm/s': <u> {translate('m/s')}</u>, // Meters per second
|
||||
'°/s': <u> {translate('°/s')}</u>, // Degrees per second
|
||||
MW: <u> {translate('MW')}</u>, // Mega Watts (same as Mega Joules per second)
|
||||
CR: <u>{translate('CR')}</u>, // Credits
|
||||
kg: <u>{translate('kg')}</u>, // Kilograms
|
||||
kgs: <u>{translate('kg/s')}</u>, // Kilograms per second
|
||||
km: <u>{translate('km')}</u>, // Kilometers
|
||||
Ls: <u>{translate('Ls')}</u>, // Light Seconds
|
||||
LY: <u>{translate('LY')}</u>, // Light Years
|
||||
MJ: <u>{translate('MJ')}</u>, // Mega Joules
|
||||
'm/s': <u>{translate('m/s')}</u>, // Meters per second
|
||||
'°/s': <u>{translate('°/s')}</u>, // Degrees per second
|
||||
MW: <u>{translate('MW')}</u>, // Mega Watts (same as Mega Joules per second)
|
||||
mps: <u>{translate('m/s')}</u>, // Metres per second
|
||||
ps: <u>{translate('/s')}</u>, // per second
|
||||
pm: <u>{translate('/min')}</u>, // per minute
|
||||
s: <u>{translate('secs')}</u>, // Seconds
|
||||
T: <u> {translate('T')}</u>, // Metric Tons
|
||||
T: <u>{translate('T')}</u>, // Metric Tons
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -20,13 +20,11 @@ import MovementSummary from '../components/MovementSummary';
|
||||
import EngineProfile from '../components/EngineProfile';
|
||||
import FSDProfile from '../components/FSDProfile';
|
||||
import JumpRange from '../components/JumpRange';
|
||||
import DamageDealt from '../components/DamageDealt';
|
||||
import DamageReceived from '../components/DamageReceived';
|
||||
import BattleCentre from '../components/BattleCentre';
|
||||
import PowerManagement from '../components/PowerManagement';
|
||||
import CostSection from '../components/CostSection';
|
||||
import ModalExport from '../components/ModalExport';
|
||||
import ModalPermalink from '../components/ModalPermalink';
|
||||
import Slider from '../components/Slider';
|
||||
|
||||
/**
|
||||
* Document Title Generator
|
||||
@@ -373,11 +371,7 @@ export default class OutfittingPage extends Page {
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<DamageDealt ship={ship} code={code} chartWidth={halfChartWidth} currentMenu={menu}/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<DamageReceived ship={ship} code={code} currentMenu={menu}/>
|
||||
<BattleCentre ship={ship} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
@import 'shipselector';
|
||||
@import 'sortable';
|
||||
@import 'loader';
|
||||
@import 'pips';
|
||||
|
||||
html, body {
|
||||
height: 100%;
|
||||
|
||||
@@ -198,6 +198,14 @@
|
||||
});
|
||||
}
|
||||
|
||||
&.twothirds {
|
||||
width: 67%;
|
||||
|
||||
.smallTablet({
|
||||
width: 100% !important;
|
||||
});
|
||||
}
|
||||
|
||||
.smallScreen({
|
||||
.axis.x {
|
||||
g.tick:nth-child(2n + 1) text {
|
||||
|
||||
29
src/less/pips.less
Executable file
29
src/less/pips.less
Executable file
@@ -0,0 +1,29 @@
|
||||
// The pips table - keep the background black
|
||||
.pipstable {
|
||||
background-color: @bgBlack;
|
||||
color: @primary;
|
||||
}
|
||||
|
||||
// A clickable entity in the pips table
|
||||
.pipsclickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
// A full pip
|
||||
.fullpip {
|
||||
stroke: @primary;
|
||||
fill: @primary;
|
||||
}
|
||||
|
||||
// A half pip
|
||||
.halfpip {
|
||||
stroke: @primary-disabled;
|
||||
fill: @primary-disabled;
|
||||
}
|
||||
|
||||
// An empty pip
|
||||
.emptypip {
|
||||
stroke: @primary-bg;
|
||||
fill: @primary-bg;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user