Make various components stateless

This commit is contained in:
Cmdr McDonald
2017-03-20 13:52:24 +00:00
parent 2f5d123f02
commit 73a75c69a3
12 changed files with 286 additions and 326 deletions

View File

@@ -320,7 +320,6 @@
"shieldExplRes": 0.5, "shieldExplRes": 0.5,
"shieldKinRes": 0.4, "shieldKinRes": 0.4,
"shieldThermRes": -0.2, "shieldThermRes": -0.2,
"timeToDrain": 7.04,
"crew": 3 "crew": 3
} }
} }

View File

@@ -76,6 +76,7 @@
"json-loader": "^0.5.3", "json-loader": "^0.5.3",
"less": "^2.5.3", "less": "^2.5.3",
"less-loader": "^2.2.1", "less-loader": "^2.2.1",
"react-addons-perf": "^15.4.2",
"react-addons-test-utils": "^15.0.1", "react-addons-test-utils": "^15.0.1",
"react-measure": "^1.4.6", "react-measure": "^1.4.6",
"react-testutils-additions": "^15.1.0", "react-testutils-additions": "^15.1.0",

View File

@@ -17,6 +17,7 @@ export default class Boost extends TranslatedComponent {
static propTypes = { static propTypes = {
marker: React.PropTypes.string.isRequired, marker: React.PropTypes.string.isRequired,
ship: React.PropTypes.object.isRequired, ship: React.PropTypes.object.isRequired,
boost: React.PropTypes.bool.isRequired,
onChange: React.PropTypes.func.isRequired onChange: React.PropTypes.func.isRequired
}; };
@@ -27,14 +28,10 @@ export default class Boost extends TranslatedComponent {
*/ */
constructor(props, context) { constructor(props, context) {
super(props); super(props);
const ship = props.ship; const { ship, boost } = props;
this._keyDown = this._keyDown.bind(this); this._keyDown = this._keyDown.bind(this);
this._toggleBoost = this._toggleBoost.bind(this); this._toggleBoost = this._toggleBoost.bind(this);
this.state = {
boost: false
};
} }
/** /**
@@ -51,25 +48,6 @@ export default class Boost extends TranslatedComponent {
document.removeEventListener('keydown', this._keyDown); 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 { boost } = this.state;
const nextShip = nextProps.ship;
const nextBoost = nextShip.canBoost() ? boost : false;
if (nextBoost != boost) {
this.setState({
boost: nextBoost
});
}
return true;
}
/** /**
* Handle Key Down * Handle Key Down
* @param {Event} e Keyboard Event * @param {Event} e Keyboard Event
@@ -91,10 +69,7 @@ export default class Boost extends TranslatedComponent {
* Toggle the boost feature * Toggle the boost feature
*/ */
_toggleBoost() { _toggleBoost() {
let { boost } = this.state; this.props.onChange(!this.props.boost);
boost = !boost;
this.setState({ boost });
this.props.onChange(boost);
} }
/** /**
@@ -103,8 +78,7 @@ export default class Boost extends TranslatedComponent {
*/ */
render() { render() {
const { formats, translate, units } = this.context.language; const { formats, translate, units } = this.context.language;
const { ship } = this.props; const { ship, boost } = this.props;
const { boost } = this.state;
// TODO disable if ship cannot boost // TODO disable if ship cannot boost
return ( return (

View File

@@ -9,7 +9,8 @@ import Slider from '../components/Slider';
*/ */
export default class Cargo extends TranslatedComponent { export default class Cargo extends TranslatedComponent {
static propTypes = { static propTypes = {
ship: React.PropTypes.object.isRequired, cargo: React.PropTypes.number.isRequired,
cargoCapacity: React.PropTypes.number.isRequired,
onChange: React.PropTypes.func.isRequired onChange: React.PropTypes.func.isRequired
}; };
@@ -21,35 +22,7 @@ export default class Cargo extends TranslatedComponent {
constructor(props, context) { constructor(props, context) {
super(props); super(props);
const ship = this.props.ship; this._cargoChange = this._cargoChange.bind(this);
this.state = {
cargoCapacity: ship.cargoCapacity,
cargoLevel: 0,
};
}
/**
* 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;
} }
/** /**
@@ -57,14 +30,12 @@ export default class Cargo extends TranslatedComponent {
* @param {number} cargoLevel percentage level from 0 to 1 * @param {number} cargoLevel percentage level from 0 to 1
*/ */
_cargoChange(cargoLevel) { _cargoChange(cargoLevel) {
const { cargoCapacity } = this.state; const { cargo, cargoCapacity } = this.props;
if (cargoCapacity > 0) { if (cargoCapacity > 0) {
// We round the cargo level to a suitable value given the capacity // We round the cargo to whole number of tonnes
cargoLevel = Math.round(cargoLevel * cargoCapacity) / cargoCapacity; const newCargo = Math.round(cargoLevel * cargoCapacity);
if (newCargo != cargo) {
if (cargoLevel != this.state.cargoLevel) { this.props.onChange(newCargo);
this.setState({ cargoLevel });
this.props.onChange(Math.round(cargoLevel * cargoCapacity));
} }
} }
} }
@@ -76,20 +47,20 @@ export default class Cargo extends TranslatedComponent {
render() { render() {
const { language, onWindowResize, sizeRatio, tooltip, termtip } = this.context; const { language, onWindowResize, sizeRatio, tooltip, termtip } = this.context;
const { formats, translate, units } = language; const { formats, translate, units } = language;
const { cargoLevel, cargoCapacity } = this.state; const { cargo, cargoCapacity } = this.props;
return ( return (
<span> <span>
<h3>{translate('cargo carried')}: {formats.int(cargoLevel * cargoCapacity)}{units.T}</h3> <h3>{translate('cargo carried')}: {formats.int(cargo)}{units.T}</h3>
<table style={{ width: '100%', lineHeight: '1em', backgroundColor: 'transparent' }}> <table style={{ width: '100%', lineHeight: '1em', backgroundColor: 'transparent' }}>
<tbody > <tbody >
<tr> <tr>
<td> <td>
<Slider <Slider
axis={true} axis={true}
onChange={this._cargoChange.bind(this)} onChange={this._cargoChange}
axisUnit={translate('T')} axisUnit={translate('T')}
percent={cargoLevel} percent={cargo / cargoCapacity}
max={cargoCapacity} max={cargoCapacity}
scale={sizeRatio} scale={sizeRatio}
onResize={onWindowResize} onResize={onWindowResize}

View File

@@ -7,9 +7,10 @@ import Slider from '../components/Slider';
* Engagement range slider * Engagement range slider
* Requires an onChange() function of the form onChange(range), providing the range in metres, which is triggered on range change * 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 { export default class EngagementRange extends TranslatedComponent {
static propTypes = { static propTypes = {
ship: React.PropTypes.object.isRequired, ship: React.PropTypes.object.isRequired,
engagementRange: React.PropTypes.number.isRequired,
onChange: React.PropTypes.func.isRequired onChange: React.PropTypes.func.isRequired
}; };
@@ -21,47 +22,15 @@ export default class Range extends TranslatedComponent {
constructor(props, context) { constructor(props, context) {
super(props); super(props);
const ship = this.props.ship; const { ship } = props;
const maxRange = this._calcMaxRange(ship); const maxRange = this._calcMaxRange(ship);
this.state = { this.state = {
maxRange, maxRange
rangeLevel: 1000 / maxRange,
}; };
} }
/**
*
*/
componentWillMount() {
// Pass initial state
this.props.onChange(this.state.maxRange * this.state.rangeLevel);
}
/**
* 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 * Calculate the maximum range of a ship's weapons
* @param {Object} ship The ship * @param {Object} ship The ship
@@ -87,12 +56,12 @@ export default class Range extends TranslatedComponent {
*/ */
_rangeChange(rangeLevel) { _rangeChange(rangeLevel) {
const { maxRange } = this.state; const { maxRange } = this.state;
// We round the range to an integer value
rangeLevel = Math.round(rangeLevel * maxRange) / maxRange;
if (rangeLevel != this.state.rangeLevel) { // We round the range to an integer value
this.setState({ rangeLevel }); const range = Math.round(rangeLevel * maxRange);
this.props.onChange(Math.round(rangeLevel * maxRange));
if (range !== this.props.engagementRange) {
this.props.onChange(range);
} }
} }
@@ -103,11 +72,12 @@ export default class Range extends TranslatedComponent {
render() { render() {
const { language, onWindowResize, sizeRatio, tooltip, termtip } = this.context; const { language, onWindowResize, sizeRatio, tooltip, termtip } = this.context;
const { formats, translate, units } = language; const { formats, translate, units } = language;
const { rangeLevel, maxRange } = this.state; const { engagementRange } = this.props;
const { maxRange } = this.state;
return ( return (
<span> <span>
<h3>{translate('engagement range')}: {formats.int(rangeLevel * maxRange)}{translate('m')}</h3> <h3>{translate('engagement range')}: {formats.int(engagementRange)}{translate('m')}</h3>
<table style={{ width: '100%', lineHeight: '1em', backgroundColor: 'transparent' }}> <table style={{ width: '100%', lineHeight: '1em', backgroundColor: 'transparent' }}>
<tbody > <tbody >
<tr> <tr>
@@ -116,7 +86,7 @@ export default class Range extends TranslatedComponent {
axis={true} axis={true}
onChange={this._rangeChange.bind(this)} onChange={this._rangeChange.bind(this)}
axisUnit={translate('m')} axisUnit={translate('m')}
percent={rangeLevel} percent={engagementRange / maxRange}
max={maxRange} max={maxRange}
scale={sizeRatio} scale={sizeRatio}
onResize={onWindowResize} onResize={onWindowResize}

View File

@@ -9,7 +9,8 @@ import Slider from '../components/Slider';
*/ */
export default class Fuel extends TranslatedComponent { export default class Fuel extends TranslatedComponent {
static propTypes = { static propTypes = {
ship: React.PropTypes.object.isRequired, fuel: React.PropTypes.number.isRequired,
fuelCapacity: React.PropTypes.number.isRequired,
onChange: React.PropTypes.func.isRequired onChange: React.PropTypes.func.isRequired
}; };
@@ -21,35 +22,7 @@ export default class Fuel extends TranslatedComponent {
constructor(props, context) { constructor(props, context) {
super(props); super(props);
const ship = this.props.ship; this._fuelChange = this._fuelChange.bind(this);
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;
} }
/** /**
@@ -57,8 +30,13 @@ export default class Fuel extends TranslatedComponent {
* @param {number} fuelLevel percentage level from 0 to 1 * @param {number} fuelLevel percentage level from 0 to 1
*/ */
_fuelChange(fuelLevel) { _fuelChange(fuelLevel) {
this.setState({ fuelLevel }); const { fuel, fuelCapacity } = this.props;
this.props.onChange(fuelLevel * this.state.fuelCapacity);
const newFuel = fuelLevel * fuelCapacity;
// Only send an update if the fuel has changed significantly
if (Math.round(fuel * 10) != Math.round(newFuel * 10)) {
this.props.onChange(Math.round(newFuel * 10) / 10);
}
} }
/** /**
@@ -68,20 +46,20 @@ export default class Fuel extends TranslatedComponent {
render() { render() {
const { language, onWindowResize, sizeRatio, tooltip, termtip } = this.context; const { language, onWindowResize, sizeRatio, tooltip, termtip } = this.context;
const { formats, translate, units } = language; const { formats, translate, units } = language;
const { fuelLevel, fuelCapacity } = this.state; const { fuel, fuelCapacity } = this.props;
return ( return (
<span> <span>
<h3>{translate('fuel carried')}: {formats.f2(fuelLevel * fuelCapacity)}{units.T}</h3> <h3>{translate('fuel carried')}: {formats.f1(fuel)}{units.T}</h3>
<table style={{ width: '100%', lineHeight: '1em', backgroundColor: 'transparent' }}> <table style={{ width: '100%', lineHeight: '1em', backgroundColor: 'transparent' }}>
<tbody > <tbody >
<tr> <tr>
<td> <td>
<Slider <Slider
axis={true} axis={true}
onChange={this._fuelChange.bind(this)} onChange={this._fuelChange}
axisUnit={translate('T')} axisUnit={translate('T')}
percent={fuelLevel} percent={fuel / fuelCapacity}
max={fuelCapacity} max={fuelCapacity}
scale={sizeRatio} scale={sizeRatio}
onResize={onWindowResize} onResize={onWindowResize}

View File

@@ -66,9 +66,12 @@ export default class OutfittingSubpages extends TranslatedComponent {
let { ship, buildName, code, onChange } = this.props; let { ship, buildName, code, onChange } = this.props;
Persist.setOutfittingTab('power'); Persist.setOutfittingTab('power');
const powerMarker = `${ship.toString()}`;
const costMarker = `${ship.toString().split('.')[0]}`;
return <div> return <div>
<PowerManagement ship={ship} code={code} onChange={onChange} /> <PowerManagement ship={ship} code={powerMarker} onChange={onChange} />
<CostSection ship={ship} buildName={buildName} code={code} /> <CostSection ship={ship} buildName={buildName} code={costMarker} />
</div>; </div>;
} }

View File

@@ -15,7 +15,9 @@ import Module from '../shipyard/Module';
*/ */
export default class Pips extends TranslatedComponent { export default class Pips extends TranslatedComponent {
static propTypes = { static propTypes = {
ship: React.PropTypes.object.isRequired, sys: React.PropTypes.number.isRequired,
eng: React.PropTypes.number.isRequired,
wep: React.PropTypes.number.isRequired,
onChange: React.PropTypes.func.isRequired onChange: React.PropTypes.func.isRequired
}; };
@@ -26,24 +28,9 @@ export default class Pips extends TranslatedComponent {
*/ */
constructor(props, context) { constructor(props, context) {
super(props); super(props);
const ship = props.ship; const { sys, eng, wep } = props;
const pd = ship.standard[4].m;
this._keyDown = this._keyDown.bind(this); 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
};
} }
/** /**
@@ -60,41 +47,6 @@ export default class Pips extends TranslatedComponent {
document.removeEventListener('keydown', this._keyDown); 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 nextShip = nextProps.ship;
const pd = nextShip.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 * Handle Key Down
* @param {Event} e Keyboard Event * @param {Event} e Keyboard Event
@@ -142,10 +94,9 @@ export default class Pips extends TranslatedComponent {
* Reset the capacitor * Reset the capacitor
*/ */
_reset() { _reset() {
let { sys, eng, wep } = this.state; let { sys, eng, wep } = this.props;
if (sys != 2 || eng != 2 || wep != 2) { if (sys != 2 || eng != 2 || wep != 2) {
sys = eng = wep = 2; sys = eng = wep = 2;
this.setState({ sys, eng, wep, pipsSvg: this._renderPips(sys, eng, wep) });
this.props.onChange(sys, eng, wep); this.props.onChange(sys, eng, wep);
} }
} }
@@ -154,7 +105,7 @@ export default class Pips extends TranslatedComponent {
* Increment the SYS capacitor * Increment the SYS capacitor
*/ */
_incSys() { _incSys() {
let { sys, eng, wep } = this.state; let { sys, eng, wep } = this.props;
const required = Math.min(1, 4 - sys); const required = Math.min(1, 4 - sys);
if (required > 0) { if (required > 0) {
@@ -181,7 +132,6 @@ export default class Pips extends TranslatedComponent {
sys += 1; sys += 1;
} }
} }
this.setState({ sys, eng, wep, pipsSvg: this._renderPips(sys, eng, wep) });
this.props.onChange(sys, eng, wep); this.props.onChange(sys, eng, wep);
} }
} }
@@ -190,7 +140,7 @@ export default class Pips extends TranslatedComponent {
* Increment the ENG capacitor * Increment the ENG capacitor
*/ */
_incEng() { _incEng() {
let { sys, eng, wep } = this.state; let { sys, eng, wep } = this.props;
const required = Math.min(1, 4 - eng); const required = Math.min(1, 4 - eng);
if (required > 0) { if (required > 0) {
@@ -217,7 +167,6 @@ export default class Pips extends TranslatedComponent {
eng += 1; eng += 1;
} }
} }
this.setState({ sys, eng, wep, pipsSvg: this._renderPips(sys, eng, wep) });
this.props.onChange(sys, eng, wep); this.props.onChange(sys, eng, wep);
} }
} }
@@ -226,7 +175,7 @@ export default class Pips extends TranslatedComponent {
* Increment the WEP capacitor * Increment the WEP capacitor
*/ */
_incWep() { _incWep() {
let { sys, eng, wep } = this.state; let { sys, eng, wep } = this.props;
const required = Math.min(1, 4 - wep); const required = Math.min(1, 4 - wep);
if (required > 0) { if (required > 0) {
@@ -253,7 +202,6 @@ export default class Pips extends TranslatedComponent {
wep += 1; wep += 1;
} }
} }
this.setState({ sys, eng, wep, pipsSvg: this._renderPips(sys, eng, wep) });
this.props.onChange(sys, eng, wep); this.props.onChange(sys, eng, wep);
} }
} }
@@ -313,14 +261,14 @@ export default class Pips extends TranslatedComponent {
*/ */
render() { render() {
const { formats, translate, units } = this.context.language; const { formats, translate, units } = this.context.language;
const { ship } = this.props; const { sys, eng, wep } = this.props;
const { sys, eng, wep, sysCap, engCap, wepCap, sysRate, engRate, wepRate, pipsSvg } = this.state;
const onSysClicked = this.onClick.bind(this, 'SYS'); const onSysClicked = this.onClick.bind(this, 'SYS');
const onEngClicked = this.onClick.bind(this, 'ENG'); const onEngClicked = this.onClick.bind(this, 'ENG');
const onWepClicked = this.onClick.bind(this, 'WEP'); const onWepClicked = this.onClick.bind(this, 'WEP');
const onRstClicked = this.onClick.bind(this, 'RST'); const onRstClicked = this.onClick.bind(this, 'RST');
const pipsSvg = this._renderPips(sys, eng, wep);
return ( return (
<span id='pips'> <span id='pips'>
<table> <table>
@@ -349,15 +297,3 @@ export default class Pips extends TranslatedComponent {
); );
} }
} }
// <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>

View File

@@ -13,12 +13,12 @@ import cn from 'classnames';
export default class ShipPicker extends TranslatedComponent { export default class ShipPicker extends TranslatedComponent {
static propTypes = { static propTypes = {
onChange: React.PropTypes.func.isRequired, onChange: React.PropTypes.func.isRequired,
ship: React.PropTypes.object, ship: React.PropTypes.string.isRequired,
build: React.PropTypes.string build: React.PropTypes.string
}; };
static defaultProps = { static defaultProps = {
ship: new Ship('anaconda', Ships['anaconda'].properties, Ships['anaconda'].slots).buildWith(Ships['anaconda'].defaults) ship: 'eagle'
} }
/** /**
@@ -33,44 +33,21 @@ export default class ShipPicker extends TranslatedComponent {
this._toggleMenu = this._toggleMenu.bind(this); this._toggleMenu = this._toggleMenu.bind(this);
this._closeMenu = this._closeMenu.bind(this); this._closeMenu = this._closeMenu.bind(this);
this.state = { this.state = { menuOpen: false };
ship: props.ship,
build: props.build
};
}
/**
* 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 { ship, build } = this.state;
const { nextShip, nextBuild } = nextProps;
if (nextShip != undefined && nextShip != ship && nextBuild != build) {
this.setState({ ship: nextShip, build: nextBuild });
}
return true;
} }
/** /**
* Update ship * Update ship
* @param {object} shipId the ship * @param {object} ship the ship
* @param {string} build the build, if present * @param {string} build the build, if present
*/ */
_shipChange(shipId, build) { _shipChange(ship, build) {
const ship = new Ship(shipId, Ships[shipId].properties, Ships[shipId].slots);
if (build) {
// Ship is a particular build
ship.buildFrom(Persist.getBuild(shipId, build));
} else {
// Ship is a stock build
ship.buildWith(Ships[shipId].defaults);
}
this._closeMenu(); this._closeMenu();
this.setState({ ship, build });
this.props.onChange(ship, build); // Ensure that the ship has changed
if (ship !== this.props.ship || this.build !== this.props.build) {
this.props.onChange(ship, build);
}
} }
/** /**
@@ -78,7 +55,7 @@ export default class ShipPicker extends TranslatedComponent {
* @returns {object} the picker menu * @returns {object} the picker menu
*/ */
_renderPickerMenu() { _renderPickerMenu() {
const { ship, build } = this.state; const { ship, build } = this.props;
const _shipChange = this._shipChange; const _shipChange = this._shipChange;
const builds = Persist.getBuilds(); const builds = Persist.getBuilds();
@@ -86,7 +63,7 @@ export default class ShipPicker extends TranslatedComponent {
for (let shipId of this.shipOrder) { for (let shipId of this.shipOrder) {
const shipBuilds = []; const shipBuilds = [];
// Add stock build // Add stock build
const stockSelected = (ship.id == shipId && !build); const stockSelected = (ship == shipId && !build);
shipBuilds.push(<li key={shipId} className={ cn({ 'selected': stockSelected })} onClick={_shipChange.bind(this, shipId, null)}>Stock</li>); shipBuilds.push(<li key={shipId} className={ cn({ 'selected': stockSelected })} onClick={_shipChange.bind(this, shipId, null)}>Stock</li>);
if (builds[shipId]) { if (builds[shipId]) {
let buildNameOrder = Object.keys(builds[shipId]).sort(); let buildNameOrder = Object.keys(builds[shipId]).sort();
@@ -126,9 +103,10 @@ export default class ShipPicker extends TranslatedComponent {
render() { render() {
const { language, onWindowResize, sizeRatio, tooltip, termtip } = this.context; const { language, onWindowResize, sizeRatio, tooltip, termtip } = this.context;
const { formats, translate, units } = language; const { formats, translate, units } = language;
const { menuOpen, ship, build } = this.state; const { ship, build } = this.props;
const { menuOpen } = this.state;
const shipString = ship.name + ': ' + (build ? build : translate('stock')); const shipString = ship + ': ' + (build ? build : translate('stock'));
return ( return (
<div className='shippicker' onClick={ (e) => e.stopPropagation() }> <div className='shippicker' onClick={ (e) => e.stopPropagation() }>
<div className='menu'> <div className='menu'>

View File

@@ -30,24 +30,20 @@ export default class ShipSummaryTable extends TranslatedComponent {
let u = language.units; let u = language.units;
let formats = language.formats; let formats = language.formats;
let { time, int, round, f1, f2 } = formats; let { time, int, round, f1, f2 } = formats;
let sgClassNames = cn({ warning: ship.findInternalByGroup('sg') && !ship.shield, muted: !ship.findInternalByGroup('sg') });
let sgRecover = '-';
let sgRecharge = '-';
let hide = tooltip.bind(null, null); let hide = tooltip.bind(null, null);
if (ship.shield) { const shieldGenerator = ship.findInternalByGroup('sg');
sgRecover = time(ship.calcShieldRecovery()); const sgClassNames = cn({ warning: shieldGenerator && !ship.shield, muted: !shieldGenerator });
sgRecharge = time(ship.calcShieldRecharge());
}
const timeToDrain = Calc.timeToDrainWep(ship, wep); const timeToDrain = Calc.timeToDrainWep(ship, wep);
const canThrust = ship.canThrust();
const canBoost = ship.canBoost();
return <div id='summary'> return <div id='summary'>
<table id='summaryTable'> <table id='summaryTable'>
<thead> <thead>
<tr className='main'> <tr className='main'>
<th rowSpan={2} className={ cn({ 'bg-warning-disabled': !ship.canThrust() }) }>{translate('speed')}</th> <th rowSpan={2} className={ cn({ 'bg-warning-disabled': !canThrust }) }>{translate('speed')}</th>
<th rowSpan={2} className={ cn({ 'bg-warning-disabled': !ship.canBoost() }) }>{translate('boost')}</th> <th rowSpan={2} className={ cn({ 'bg-warning-disabled': !canBoost }) }>{translate('boost')}</th>
<th onMouseEnter={termtip.bind(null, 'damage per second')} onMouseLeave={hide} rowSpan={2}>{translate('DPS')}</th> <th onMouseEnter={termtip.bind(null, 'damage per second')} onMouseLeave={hide} rowSpan={2}>{translate('DPS')}</th>
<th onMouseEnter={termtip.bind(null, 'energy per second')} onMouseLeave={hide} rowSpan={2}>{translate('EPS')}</th> <th onMouseEnter={termtip.bind(null, 'energy per second')} onMouseLeave={hide} rowSpan={2}>{translate('EPS')}</th>
<th onMouseEnter={termtip.bind(null, 'time to drain WEP capacitor')} onMouseLeave={hide} rowSpan={2}>{translate('TTD')}</th> <th onMouseEnter={termtip.bind(null, 'time to drain WEP capacitor')} onMouseLeave={hide} rowSpan={2}>{translate('TTD')}</th>
@@ -72,8 +68,8 @@ export default class ShipSummaryTable extends TranslatedComponent {
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<td>{ ship.canThrust() ? <span>{int(ship.calcSpeed(eng, fuel, cargo, false))}{u['m/s']}</span> : <span className='warning'>0 <Warning/></span> }</td> <td>{ canThrust ? <span>{int(ship.calcSpeed(eng, fuel, cargo, false))}{u['m/s']}</span> : <span className='warning'>0 <Warning/></span> }</td>
<td>{ ship.canBoost() ? <span>{int(ship.calcSpeed(eng, fuel, cargo, true))}{u['m/s']}</span> : <span className='warning'>0 <Warning/></span> }</td> <td>{ canBoost ? <span>{int(ship.calcSpeed(eng, fuel, cargo, true))}{u['m/s']}</span> : <span className='warning'>0 <Warning/></span> }</td>
<td>{f1(ship.totalDps)}</td> <td>{f1(ship.totalDps)}</td>
<td>{f1(ship.totalEps)}</td> <td>{f1(ship.totalEps)}</td>
<td>{timeToDrain === Infinity ? '∞' : time(timeToDrain)}</td> <td>{timeToDrain === Infinity ? '∞' : time(timeToDrain)}</td>

View File

@@ -1,14 +1,17 @@
import React from 'react'; import React from 'react';
// import Perf from 'react-addons-perf';
import { findDOMNode } from 'react-dom'; import { findDOMNode } from 'react-dom';
import { Ships } from 'coriolis-data/dist'; import { Ships } from 'coriolis-data/dist';
import cn from 'classnames'; import cn from 'classnames';
import Page from './Page'; import Page from './Page';
import Router from '../Router'; import Router from '../Router';
import Persist from '../stores/Persist'; import Persist from '../stores/Persist';
import * as Utils from '../utils/UtilityFunctions';
import Ship from '../shipyard/Ship'; import Ship from '../shipyard/Ship';
import { toDetailedBuild } from '../shipyard/Serializer'; import { toDetailedBuild } from '../shipyard/Serializer';
import { outfitURL } from '../utils/UrlGenerators'; import { outfitURL } from '../utils/UrlGenerators';
import { FloppyDisk, Bin, Switch, Download, Reload, LinkIcon, ShoppingIcon } from '../components/SvgIcons'; import { FloppyDisk, Bin, Switch, Download, Reload, LinkIcon, ShoppingIcon } from '../components/SvgIcons';
import LZString from 'lz-string';
import ShipSummaryTable from '../components/ShipSummaryTable'; import ShipSummaryTable from '../components/ShipSummaryTable';
import StandardSlotSection from '../components/StandardSlotSection'; import StandardSlotSection from '../components/StandardSlotSection';
import HardpointsSlotSection from '../components/HardpointsSlotSection'; import HardpointsSlotSection from '../components/HardpointsSlotSection';
@@ -46,7 +49,8 @@ export default class OutfittingPage extends Page {
*/ */
constructor(props, context) { constructor(props, context) {
super(props, context); super(props, context);
this.state = this._initState(context); // window.Perf = Perf;
this.state = this._initState(props, context);
this._keyDown = this._keyDown.bind(this); this._keyDown = this._keyDown.bind(this);
this._exportBuild = this._exportBuild.bind(this); this._exportBuild = this._exportBuild.bind(this);
this._pipsUpdated = this._pipsUpdated.bind(this); this._pipsUpdated = this._pipsUpdated.bind(this);
@@ -59,10 +63,11 @@ export default class OutfittingPage extends Page {
/** /**
* [Re]Create initial state from context * [Re]Create initial state from context
* @param {Object} props React component properties
* @param {context} context React component context * @param {context} context React component context
* @return {Object} New state object * @return {Object} New state object
*/ */
_initState(context) { _initState(props, context) {
let params = context.route.params; let params = context.route.params;
let shipId = params.ship; let shipId = params.ship;
let code = params.code; let code = params.code;
@@ -84,6 +89,8 @@ export default class OutfittingPage extends Page {
this._getTitle = getTitle.bind(this, data.properties.name); this._getTitle = getTitle.bind(this, data.properties.name);
// Obtain ship control from code
const { sys, eng, wep, boost, fuel, cargo, opponent, opponentBuild, engagementRange } = this._obtainControlFromCode(ship, code);
return { return {
error: null, error: null,
title: this._getTitle(buildName), title: this._getTitle(buildName),
@@ -94,14 +101,15 @@ export default class OutfittingPage extends Page {
ship, ship,
code, code,
savedCode, savedCode,
sys: 2, sys,
eng: 2, eng,
wep: 2, wep,
fuel: ship.fuelCapacity, boost,
cargo: 0, fuel,
boost: false, cargo,
engagementRange: 1000, opponent,
opponent: new Ship('anaconda', Ships['anaconda'].properties, Ships['anaconda'].slots).buildWith(Ships['anaconda'].defaults) opponentBuild,
engagementRange
}; };
} }
@@ -123,6 +131,76 @@ export default class OutfittingPage extends Page {
this.setState(stateChanges); this.setState(stateChanges);
} }
/**
* Update the control part of the route
*/
_updateRouteOnControlChange() {
const { ship, shipId, buildName } = this.state;
const code = this._fullCode(ship);
this._updateRoute(shipId, buildName, code);
this.setState({ code });
}
/**
* Provide a full code for this ship, including any additions due to the outfitting page
* @param {Object} ship the ship
* @param {number} fuel the fuel carried by the ship (if different from that in state)
* @param {number} cargo the cargo carried by the ship (if different from that in state)
* @returns {string} the code for this ship
*/
_fullCode(ship, fuel, cargo) {
return `${ship.toString()}.${LZString.compressToBase64(this._controlCode(fuel, cargo))}`;
}
/**
* Obtain the control information from the build code
* @param {Object} ship The ship
* @param {string} code The build code
* @returns {Object} The control information
*/
_obtainControlFromCode(ship, code) {
// Defaults
let sys = 2;
let eng = 2;
let wep = 2;
let boost = false;
let fuel = ship.fuelCapacity;
let cargo = ship.cargoCapacity;
let opponent = new Ship('eagle', Ships['eagle'].properties, Ships['eagle'].slots).buildWith(Ships['eagle'].defaults);
let opponentBuild = undefined;
let engagementRange = 1000;
// Obtain updates from code, if available
if (code) {
const parts = code.split('.');
if (parts.length >= 5) {
// We have control information in the code
const control = LZString.decompressFromBase64(Utils.fromUrlSafe(parts[4])).split('/');
sys = parseFloat(control[0]);
eng = parseFloat(control[1]);
wep = parseFloat(control[2]);
boost = control[3] == 1 ? true : false;
fuel = parseFloat(control[4]);
cargo = parseInt(control[5]);
if (control[6]) {
const shipId = control[6];
opponent = new Ship(shipId, Ships[shipId].properties, Ships[shipId].slots);
if (control[7] && Persist.getBuild(shipId, control[7])) {
// Ship is a particular build
opponent.buildFrom(Persist.getBuild(shipId, control[7]));
opponentBuild = control[7];
} else {
// Ship is a stock build
opponent.buildWith(Ships[shipId].defaults);
}
}
engagementRange = parseInt(control[8]);
}
}
return { sys, eng, wep, boost, fuel, cargo, opponent, opponentBuild, engagementRange };
}
/** /**
* Triggered when pips have been updated * Triggered when pips have been updated
* @param {number} sys SYS pips * @param {number} sys SYS pips
@@ -130,7 +208,7 @@ export default class OutfittingPage extends Page {
* @param {number} wep WEP pips * @param {number} wep WEP pips
*/ */
_pipsUpdated(sys, eng, wep) { _pipsUpdated(sys, eng, wep) {
this.setState({ sys, eng, wep }); this.setState({ sys, eng, wep }, () => this._updateRouteOnControlChange());
} }
/** /**
@@ -138,7 +216,7 @@ export default class OutfittingPage extends Page {
* @param {boolean} boost true if boosting * @param {boolean} boost true if boosting
*/ */
_boostUpdated(boost) { _boostUpdated(boost) {
this.setState({ boost }); this.setState({ boost }, () => this._updateRouteOnControlChange());
} }
/** /**
@@ -146,7 +224,7 @@ export default class OutfittingPage extends Page {
* @param {number} fuel the amount of fuel, in T * @param {number} fuel the amount of fuel, in T
*/ */
_fuelUpdated(fuel) { _fuelUpdated(fuel) {
this.setState({ fuel }); this.setState({ fuel }, () => this._updateRouteOnControlChange());
} }
/** /**
@@ -154,7 +232,7 @@ export default class OutfittingPage extends Page {
* @param {number} cargo the amount of cargo, in T * @param {number} cargo the amount of cargo, in T
*/ */
_cargoUpdated(cargo) { _cargoUpdated(cargo) {
this.setState({ cargo }); this.setState({ cargo }, () => this._updateRouteOnControlChange());
} }
/** /**
@@ -162,24 +240,44 @@ export default class OutfittingPage extends Page {
* @param {number} engagementRange the engagement range, in m * @param {number} engagementRange the engagement range, in m
*/ */
_engagementRangeUpdated(engagementRange) { _engagementRangeUpdated(engagementRange) {
this.setState({ engagementRange }); this.setState({ engagementRange }, () => this._updateRouteOnControlChange());
} }
/** /**
* Triggered when target ship has been updated * Triggered when target ship has been updated
* @param {object} opponent the opponent's ship * @param {string} opponent the opponent's ship model
* @param {string} opponentBuild the name of the opponent's build * @param {string} opponentBuild the name of the opponent's build
*/ */
_opponentUpdated(opponent, opponentBuild) { _opponentUpdated(opponent, opponentBuild) {
this.setState({ opponent, opponentBuild }); const opponentShip = new Ship(opponent, Ships[opponent].properties, Ships[opponent].slots);
if (opponentBuild && Persist.getBuild(opponent, opponentBuild)) {
// Ship is a particular build
opponentShip.buildFrom(Persist.getBuild(opponent, opponentBuild));
} else {
// Ship is a stock build
opponentShip.buildWith(Ships[opponent].defaults);
}
this.setState({ opponent: opponentShip, opponentBuild }, () => this._updateRouteOnControlChange());
}
/**
* Set the control code for this outfitting page
* @param {number} fuel the fuel carried by the ship (if different from that in state)
* @param {number} cargo the cargo carried by the ship (if different from that in state)
* @returns {string} The control code
*/
_controlCode(fuel, cargo) {
const { sys, eng, wep, boost, opponent, opponentBuild, engagementRange } = this.state;
const code = `${sys}/${eng}/${wep}/${boost ? 1 : 0}/${fuel || this.state.fuel}/${cargo || this.state.cargo}/${opponent.id}/${opponentBuild ? opponentBuild : ''}/${engagementRange}`;
return code;
} }
/** /**
* Save the current build * Save the current build
*/ */
_saveBuild() { _saveBuild() {
let code = this.state.ship.toString(); const { code, buildName, newBuildName, shipId } = this.state;
let { buildName, newBuildName, shipId } = this.state;
if (buildName === newBuildName) { if (buildName === newBuildName) {
Persist.saveBuild(shipId, buildName, code); Persist.saveBuild(shipId, buildName, code);
@@ -196,9 +294,8 @@ export default class OutfittingPage extends Page {
* Rename the current build * Rename the current build
*/ */
_renameBuild() { _renameBuild() {
let { buildName, newBuildName, shipId, ship } = this.state; const { code, buildName, newBuildName, shipId, ship } = this.state;
if (buildName != newBuildName && newBuildName.length) { if (buildName != newBuildName && newBuildName.length) {
let code = ship.toString();
Persist.deleteBuild(shipId, buildName); Persist.deleteBuild(shipId, buildName);
Persist.saveBuild(shipId, newBuildName, code); Persist.saveBuild(shipId, newBuildName, code);
this._updateRoute(shipId, newBuildName, code); this._updateRoute(shipId, newBuildName, code);
@@ -210,16 +307,31 @@ export default class OutfittingPage extends Page {
* Reload build from last save * Reload build from last save
*/ */
_reloadBuild() { _reloadBuild() {
this.state.ship.buildFrom(this.state.savedCode); this.setState({ code: this.state.savedCode }, () => this._codeUpdated());
this._shipUpdated();
} }
/** /**
* Reset build to Stock/Factory defaults * Reset build to Stock/Factory defaults
*/ */
_resetBuild() { _resetBuild() {
this.state.ship.buildWith(Ships[this.state.shipId].defaults); const { ship, shipId, buildName } = this.state;
this._shipUpdated(); // Rebuild ship
ship.buildWith(Ships[shipId].defaults);
// Reset controls
const code = ship.toString();
const { sys, eng, wep, boost, fuel, cargo, opponent, opponentBuild, engagementRange } = this._obtainControlFromCode(ship, code);
// Update state, and refresh the ship
this.setState({
sys,
eng,
wep,
boost,
fuel,
cargo,
opponent,
opponentBuild,
engagementRange
}, () => this._updateRoute(shipId, buildName, code));
} }
/** /**
@@ -244,14 +356,43 @@ export default class OutfittingPage extends Page {
} }
/** /**
* Trigger render on ship model change * Called when the code for the ship has been updated, to synchronise the rest of the data
*/
_codeUpdated() {
const { code, ship, shipId, buildName } = this.state;
// Rebuild ship from the code
this.state.ship.buildFrom(code);
// Obtain controls from the code
const { sys, eng, wep, boost, fuel, cargo, opponent, opponentBuild, engagementRange } = this._obtainControlFromCode(ship, code);
// Update state, and refresh the route when complete
this.setState({
sys,
eng,
wep,
boost,
fuel,
cargo,
opponent,
opponentBuild,
engagementRange
}, () => this._updateRoute(shipId, buildName, code));
}
/**
* Called when the ship has been updated, to set the code and then update accordingly
*/ */
_shipUpdated() { _shipUpdated() {
let { shipId, buildName, ship } = this.state; let { ship, shipId, buildName, cargo, fuel } = this.state;
let code = ship.toString(); if (cargo > ship.cargoCapacity) {
cargo = ship.cargoCapacity;
this._updateRoute(shipId, buildName, code); }
this.setState({ code }); if (fuel > ship.fuelCapacity) {
fuel = ship.fuelCapacity;
}
const code = this._fullCode(ship, fuel, cargo);
this.setState({ code, cargo, fuel }, () => this._updateRoute(shipId, buildName, code));
} }
/** /**
@@ -271,7 +412,7 @@ export default class OutfittingPage extends Page {
*/ */
componentWillReceiveProps(nextProps, nextContext) { componentWillReceiveProps(nextProps, nextContext) {
if (this.context.route !== nextContext.route) { // Only reinit state if the route has changed if (this.context.route !== nextContext.route) { // Only reinit state if the route has changed
this.setState(this._initState(nextContext)); this.setState(this._initState(nextProps, nextContext));
} }
} }
@@ -340,16 +481,23 @@ export default class OutfittingPage extends Page {
shipUpdated = this._shipUpdated, shipUpdated = this._shipUpdated,
canSave = (newBuildName || buildName) && code !== savedCode, canSave = (newBuildName || buildName) && code !== savedCode,
canRename = buildName && newBuildName && buildName != newBuildName, canRename = buildName && newBuildName && buildName != newBuildName,
canReload = savedCode && canSave, canReload = savedCode && canSave;
hStr = ship.getHardpointsString() + '.' + ship.getModificationsString(),
iStr = ship.getInternalString() + '.' + ship.getModificationsString();
// Code can be blank for a default loadout. Prefix it with the ship name to ensure that changes in default ships is picked up // Code can be blank for a default loadout. Prefix it with the ship name to ensure that changes in default ships is picked up
code = ship.name + (code || ''); code = ship.name + (code || '');
// Markers are used to propagate state changes without requiring a deep comparison of the ship, as that takes a long time // Markers are used to propagate state changes without requiring a deep comparison of the ship, as that takes a long time
const _sStr = ship.getStandardString();
const _iStr = ship.getInternalString();
const _hStr = ship.getHardpointsString();
const _pStr = `${ship.getPowerEnabledString()}${ship.getPowerPrioritiesString()}`;
const _mStr = ship.getModificationsString();
const standardSlotMarker = `${ship.name}${_sStr}${_pStr}${_mStr}`;
const internalSlotMarker = `${ship.name}${_iStr}${_pStr}${_mStr}`;
const hardpointsSlotMarker = `${ship.name}${_hStr}${_pStr}${_mStr}`;
const boostMarker = `${ship.canBoost()}`; const boostMarker = `${ship.canBoost()}`;
const shipSummaryMarker = `${ship.toString()}:${eng}:${fuel}:${cargo}`; const shipSummaryMarker = `${ship.toString()}${eng}${fuel}${cargo}`;
return ( return (
<div id='outfit' className={'page'} style={{ fontSize: (sizeRatio * 0.9) + 'em' }}> <div id='outfit' className={'page'} style={{ fontSize: (sizeRatio * 0.9) + 'em' }}>
@@ -386,10 +534,10 @@ export default class OutfittingPage extends Page {
{/* Main tables */} {/* Main tables */}
<ShipSummaryTable ship={ship} marker={shipSummaryMarker} eng={eng} sys={sys} wep={wep} cargo={cargo} fuel={fuel}/> <ShipSummaryTable ship={ship} marker={shipSummaryMarker} eng={eng} sys={sys} wep={wep} cargo={cargo} fuel={fuel}/>
<StandardSlotSection ship={ship} code={code} onChange={shipUpdated} currentMenu={menu} /> <StandardSlotSection ship={ship} code={standardSlotMarker} onChange={shipUpdated} currentMenu={menu} />
<InternalSlotSection ship={ship} code={iStr} onChange={shipUpdated} currentMenu={menu} /> <InternalSlotSection ship={ship} code={internalSlotMarker} onChange={shipUpdated} currentMenu={menu} />
<HardpointsSlotSection ship={ship} code={hStr || ''} onChange={shipUpdated} currentMenu={menu} /> <HardpointsSlotSection ship={ship} code={hardpointsSlotMarker} onChange={shipUpdated} currentMenu={menu} />
<UtilitySlotSection ship={ship} code={hStr || ''} onChange={shipUpdated} currentMenu={menu} /> <UtilitySlotSection ship={ship} code={hardpointsSlotMarker} onChange={shipUpdated} currentMenu={menu} />
{/* Control of ship and opponent */} {/* Control of ship and opponent */}
<div className='group quarter'> <div className='group quarter'>
@@ -397,28 +545,28 @@ export default class OutfittingPage extends Page {
<h2 style={{ verticalAlign: 'middle', textAlign: 'left' }}>{translate('ship control')}</h2> <h2 style={{ verticalAlign: 'middle', textAlign: 'left' }}>{translate('ship control')}</h2>
</div> </div>
<div className='group half'> <div className='group half'>
<Boost marker={boostMarker} ship={ship} onChange={this._boostUpdated} /> <Boost marker={boostMarker} ship={ship} boost={boost} onChange={this._boostUpdated} />
</div> </div>
</div> </div>
<div className='group quarter'> <div className='group quarter'>
<Pips ship={ship} onChange={this._pipsUpdated} /> <Pips sys={sys} eng={eng} wep={wep} onChange={this._pipsUpdated} />
</div> </div>
<div className='group quarter'> <div className='group quarter'>
<Fuel ship={ship} onChange={this._fuelUpdated}/> <Fuel fuelCapacity={ship.fuelCapacity} fuel={fuel} onChange={this._fuelUpdated}/>
</div> </div>
<div className='group quarter'> <div className='group quarter'>
{ ship.cargoCapacity > 0 ? <Cargo ship={ship} onChange={this._cargoUpdated}/> : null } { ship.cargoCapacity > 0 ? <Cargo cargoCapacity={ship.cargoCapacity} cargo={cargo} onChange={this._cargoUpdated}/> : null }
</div> </div>
<div className='group half'> <div className='group half'>
<div className='group quarter'> <div className='group quarter'>
<h2 style={{ verticalAlign: 'middle', textAlign: 'left' }}>{translate('opponent')}</h2> <h2 style={{ verticalAlign: 'middle', textAlign: 'left' }}>{translate('opponent')}</h2>
</div> </div>
<div className='group threequarters'> <div className='group threequarters'>
<ShipPicker onChange={this._opponentUpdated}/> <ShipPicker ship={opponent.id} build={opponentBuild} onChange={this._opponentUpdated}/>
</div> </div>
</div> </div>
<div className='group half'> <div className='group half'>
<EngagementRange ship={ship} onChange={this._engagementRangeUpdated}/> <EngagementRange ship={ship} engagementRange={engagementRange} onChange={this._engagementRangeUpdated}/>
</div> </div>
{/* Tabbed subpages */} {/* Tabbed subpages */}

View File

@@ -808,9 +808,9 @@ export function _weaponSustainedDps(m, opponent, opponentShields, opponentArmour
/** /**
* Calculate time to drain WEP capacitor * Calculate time to drain WEP capacitor
* @param {object} ship The ship * @param {object} ship The ship
* @param {number} wep Pips to WEP * @param {number} wep Pips to WEP
* @return The time to drain the WEP capacitor, in seconds * @returns {number} The time to drain the WEP capacitor, in seconds
*/ */
export function timeToDrainWep(ship, wep) { export function timeToDrainWep(ship, wep) {
let totalSEps = 0; let totalSEps = 0;
@@ -835,6 +835,12 @@ export function timeToDrainWep(ship, wep) {
/** /**
* Calculate the time to deplete an amount of shields or armour * Calculate the time to deplete an amount of shields or armour
* @param {number} amount The amount to be depleted
* @param {number} dps The depletion per second
* @param {number} eps The energy drained per second
* @param {number} capacity The initial energy capacity
* @param {number} recharge The energy recharged per second
* @returns {number} The number of seconds to deplete to 0
*/ */
export function timeToDeplete(amount, dps, eps, capacity, recharge) { export function timeToDeplete(amount, dps, eps, capacity, recharge) {
const drainPerSecond = eps - recharge; const drainPerSecond = eps - recharge;