Tweak and refactor build/role preset functions

This commit is contained in:
Colin McLeod
2016-03-03 19:58:47 -08:00
parent 05bc8ebb93
commit 87dd52c043
6 changed files with 158 additions and 85 deletions

View File

@@ -94,6 +94,7 @@ export default class HardpointsSlotSection extends SlotSection {
return <div className='select hardpoint' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}> return <div className='select hardpoint' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
<ul> <ul>
<li className='lc' onClick={this._empty}>{translate('empty all')}</li> <li className='lc' onClick={this._empty}>{translate('empty all')}</li>
<li style={{ textAlign: 'center', marginTop: '1em' }}>{translate('PHRASE_ALT_ALL')}</li>
</ul> </ul>
<div className='select-group cap'>{translate('pl')}</div> <div className='select-group cap'>{translate('pl')}</div>
<ul> <ul>

View File

@@ -138,6 +138,7 @@ export default class InternalSlotSection extends SlotSection {
<li className='lc' onClick={this._fillWithCargo}>{translate('cargo')}</li> <li className='lc' onClick={this._fillWithCargo}>{translate('cargo')}</li>
<li className='lc' onClick={this._fillWithCells}>{translate('scb')}</li> <li className='lc' onClick={this._fillWithCells}>{translate('scb')}</li>
<li className='lc' onClick={this._fillWithArmor}>{translate('hr')}</li> <li className='lc' onClick={this._fillWithArmor}>{translate('hr')}</li>
<li style={{ textAlign: 'center', marginTop: '1em' }}>{translate('PHRASE_ALT_ALL')}</li>
</ul> </ul>
</div>; </div>;
} }

View File

@@ -4,6 +4,7 @@ import SlotSection from './SlotSection';
import StandardSlot from './StandardSlot'; import StandardSlot from './StandardSlot';
import { diffDetails } from '../utils/SlotFunctions'; import { diffDetails } from '../utils/SlotFunctions';
import * as ModuleUtils from '../shipyard/ModuleUtils'; import * as ModuleUtils from '../shipyard/ModuleUtils';
import * as ShipRoles from '../shipyard/ShipRoles';
import { stopCtxPropagation } from '../utils/UtilityFunctions'; import { stopCtxPropagation } from '../utils/UtilityFunctions';
/** /**
@@ -18,23 +19,10 @@ export default class StandardSlotSection extends SlotSection {
*/ */
constructor(props, context) { constructor(props, context) {
super(props, context, 'standard', 'standard'); super(props, context, 'standard', 'standard');
this._optimizeStandard = this._optimizeStandard.bind(this); this._optimizeStandard = this._optimizeStandard.bind(this);
this._optimizeCargo = this._optimizeCargo.bind(this);
this._optimizeExplorer = this._optimizeExplorer.bind(this);
this._hideDiff = this._hideDiff.bind(this); this._hideDiff = this._hideDiff.bind(this);
} }
/**
* Fill all standard slots with the specificed rating (using max class)
* @param {String} rating [A-E]
*/
_fill(rating) {
this.props.ship.useStandard(rating);
this.props.onChange();
this._close();
}
/** /**
* Use the lightest/optimal available standard modules * Use the lightest/optimal available standard modules
*/ */
@@ -45,75 +33,32 @@ export default class StandardSlotSection extends SlotSection {
} }
/** /**
* Trader build * Fill all standard slots with the specificed rating (using max class)
* @param {Boolean} shielded True if shield generator should be included
* @param {integer} bulkheadIndex Bulkhead to use see Constants.BulkheadNames
*/ */
_optimizeCargo() { _multiPurpose(shielded, bulkheadIndex) {
let ship = this.props.ship; ShipRoles.multiPurpose(this.props.ship, shielded, bulkheadIndex);
ship.internal.forEach((slot) => ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E')));
ship.useLightestStandard();
this.props.onChange(); this.props.onChange();
this._close(); this._close();
} }
/** /**
* Explorer build * Trader Build
* @param {Boolean} shielded True if shield generator should be included
*/ */
_optimizeExplorer() { _optimizeCargo(shielded) {
let ship = this.props.ship, ShipRoles.trader(this.props.ship, shielded);
intLength = ship.internal.length, this.props.onChange();
heatSinkCount = 2, // Fit 2 heat sinks if possible this._close();
afmUnitCount = 2, // Fit 2 AFM Units if possible }
sgSlot,
fuelScoopSlot,
sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass);
ship.setSlotEnabled(ship.cargoHatch, false) /**
.use(ship.internal[--intLength], ModuleUtils.internal('2f')) // Advanced Discovery Scanner * Explorer role
.use(ship.internal[--intLength], ModuleUtils.internal('2i')); // Detailed Surface Scanner * @param {Boolean} planetary True if Planetary Vehicle Hangar (PVH) should be included
*/
for (let i = 0; i < intLength; i++) { _optimizeExplorer(planetary) {
let slot = ship.internal[i]; ShipRoles.explorer(this.props.ship, planetary);
let nextSlot = (i + 1) < intLength ? ship.internal[i + 1] : null;
if (!fuelScoopSlot && (!slot.eligible || slot.eligible.fs)) { // Fit best possible Fuel Scoop
fuelScoopSlot = slot;
ship.use(fuelScoopSlot, ModuleUtils.findInternal('fs', slot.maxClass, 'A'));
ship.setSlotEnabled(fuelScoopSlot, true);
// Mount a Shield generator if possible AND an AFM Unit has been mounted already (Guarantees at least 1 AFM Unit)
} else if (!sgSlot && afmUnitCount < 2 && sg.class <= slot.maxClass && (!slot.eligible || slot.eligible.sg) && (!nextSlot || nextSlot.maxClass < sg.class)) {
sgSlot = slot;
ship.use(sgSlot, sg);
ship.setSlotEnabled(sgSlot, true);
} else if (afmUnitCount > 0 && (!slot.eligible || slot.eligible.am)) {
afmUnitCount--;
let am = ModuleUtils.findInternal('am', slot.maxClass, afmUnitCount ? 'B' : 'A');
ship.use(slot, am);
ship.setSlotEnabled(slot, false); // Disabled power for AFM Unit
} else {
ship.use(slot, null);
}
}
ship.hardpoints.forEach((s) => {
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--;
} else {
ship.use(s, null);
}
});
if (sgSlot) {
// The SG and Fuel scoop to not need to be powered at the same time
if (sgSlot.m.power > fuelScoopSlot.m.power) { // The Shield generator uses the most power
ship.setSlotEnabled(fuelScoopSlot, false);
} else { // The Fuel scoop uses the most power
ship.setSlotEnabled(sgSlot, false);
}
}
ship.useLightestStandard({ pd: '1D', ppRating: 'A' });
this.props.onChange(); this.props.onChange();
this._close(); this._close();
} }
@@ -161,7 +106,7 @@ export default class StandardSlotSection extends SlotSection {
* @return {Array} Array of Slots * @return {Array} Array of Slots
*/ */
_getSlots() { _getSlots() {
let { translate, units } = this.context.language; let { translate, units, formats } = this.context.language;
let { ship, currentMenu } = this.props; let { ship, currentMenu } = this.props;
let slots = new Array(8); let slots = new Array(8);
let open = this._openMenu; let open = this._openMenu;
@@ -292,21 +237,19 @@ export default class StandardSlotSection extends SlotSection {
* @return {React.Component} Section menu * @return {React.Component} Section menu
*/ */
_getSectionMenu(translate) { _getSectionMenu(translate) {
let _fill = this._fill; let planetaryDisabled = this.props.ship.internal.length < 4;
return <div className='select' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}> return <div className='select' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
<ul> <ul>
<li className='lc' onClick={this._optimizeStandard}>{translate('optimize')}</li> <li className='lc' onClick={this._optimizeStandard}>{translate('Maximize Jump Range')}</li>
<li className='c' onClick={_fill.bind(this, 'E')}>E</li>
<li className='c' onClick={_fill.bind(this, 'D')}>D</li>
<li className='c' onClick={_fill.bind(this, 'C')}>C</li>
<li className='c' onClick={_fill.bind(this, 'B')}>B</li>
<li className='c' onClick={_fill.bind(this, 'A')}>A</li>
</ul> </ul>
<div className='select-group cap'>{translate('roles')}</div> <div className='select-group cap'>{translate('roles')}</div>
<ul> <ul>
<li className='lc' onClick={this._optimizeCargo}>{translate('Trader')}</li> <li className='lc' onClick={this._multiPurpose.bind(this, false, 0)}>{translate('Multi-purpose')}</li>
<li className='lc' onClick={this._optimizeExplorer}>{translate('Explorer')}</li> <li className='lc' onClick={this._multiPurpose.bind(this, true, 2)}>{translate('Combat')}</li>
<li className='lc' onClick={this._optimizeCargo.bind(this, false)}>{translate('Trader')}</li>
<li className='lc' onClick={this._optimizeCargo.bind(this, true)}>{translate('Shielded Trader')}</li>
<li className='lc' onClick={this._optimizeExplorer.bind(this, false)}>{translate('Explorer')}</li>
<li className={cn('lc', { disabled: planetaryDisabled })} onClick={!planetaryDisabled && this._optimizeExplorer.bind(this, true)}>{translate('Planetary Explorer')}</li>
</ul> </ul>
</div>; </div>;
} }

View File

@@ -94,6 +94,7 @@ export default class UtilitySlotSection extends SlotSection {
return <div className='select' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}> return <div className='select' onClick={(e) => e.stopPropagation()} onContextMenu={stopCtxPropagation}>
<ul> <ul>
<li className='lc' onClick={this._empty}>{translate('empty all')}</li> <li className='lc' onClick={this._empty}>{translate('empty all')}</li>
<li style={{ textAlign: 'center', marginTop: '1em' }}>{translate('PHRASE_ALT_ALL')}</li>
</ul> </ul>
<div className='select-group cap'>{translate('sb')}</div> <div className='select-group cap'>{translate('sb')}</div>
<ul> <ul>

View File

@@ -14,6 +14,7 @@ export const formats = {
}; };
export const terms = { export const terms = {
PHRASE_ALT_ALL: 'Hold ALT to fill all slots',
PHRASE_BACKUP_DESC: 'Backup of all Coriolis data to save or transfer to another browser/device', PHRASE_BACKUP_DESC: 'Backup of all Coriolis data to save or transfer to another browser/device',
PHRASE_CONFIRMATION: 'Are you sure?', PHRASE_CONFIRMATION: 'Are you sure?',
PHRASE_EXPORT_DESC: 'A detailed JSON export of your build for use in other sites and tools', PHRASE_EXPORT_DESC: 'A detailed JSON export of your build for use in other sites and tools',

View File

@@ -0,0 +1,126 @@
import * as ModuleUtils from './ModuleUtils';
import { canMount } from '../utils/SlotFunctions';
/**
* Standard / typical role for multi-purpose or combat (if shielded with better bulkheads)
* @param {Ship} ship Ship instance
* @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) {
ship.useStandard('A')
.use(ship.standard[3], ModuleUtils.standard(3, ship.standard[3].maxClass + 'D')) // D Life Support
.use(ship.standard[5], ModuleUtils.standard(3, ship.standard[5].maxClass + 'D')) // D Sensors
.useBulkhead(bulkheadIndex);
if (shielded) {
ship.internal.some(function(slot) {
if (canMount(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;
}
});
}
}
/**
* Trader Role
* @param {Ship} ship Ship instance
* @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 sg = shielded ? ship.getAvailableModules().lightestShieldGenerator(ship.hullMass) : null;
for (let i = ship.internal.length; i--;) {
let slot = ship.internal[i];
if (sg && canMount(slot, 'sg', sg.class)) {
ship.use(slot, sg);
sg = null;
} else {
ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E'));
}
}
ship.useLightestStandard(standardOpts);
}
/**
* Explorer Role
* @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' },
intLength = ship.internal.length,
heatSinkCount = 2, // Fit 2 heat sinks if possible
afmUnitCount = 2, // Fit 2 AFM Units if possible
shieldNext = planetary,
sgSlot,
fuelScoopSlot,
pvhSlot,
sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass);
if (!planetary) { // Non-planetary explorers don't really need to boost
standardOpts.pd = '1D';
}
ship.setSlotEnabled(ship.cargoHatch, false)
.use(ship.internal[--intLength], ModuleUtils.internal('2f')); // Advanced Discovery Scanner
if (!planetary || intLength > 3) { // Don't mount a DDS on planetary explorer ships too small for both a PVH and DDS
ship.use(ship.internal[--intLength], ModuleUtils.internal('2i')); // Detailed Surface Scanner
}
for (let i = 0; i < intLength; i++) {
let slot = ship.internal[i];
let nextSlot = (i + 1) < intLength ? ship.internal[i + 1] : null;
// Fit best possible Fuel Scoop
if (!fuelScoopSlot && canMount(slot, 'fs')) {
fuelScoopSlot = slot;
ship.use(slot, ModuleUtils.findInternal('fs', slot.maxClass, 'A'));
ship.setSlotEnabled(slot, true);
// Mount a Shield generator if possible AND an AFM Unit has been mounted already (Guarantees at least 1 AFM Unit)
} else if (!sgSlot && shieldNext && canMount(slot, 'sg', sg.class) && !canMount(nextSlot, 'sg', sg.class)) {
sgSlot = slot;
shieldNext = false;
ship.use(slot, sg);
ship.setSlotEnabled(slot, true);
// if planetary explorer and the next slot cannot mount a PVH or the next modul to mount is a SG
} else if (planetary && !pvhSlot && canMount(slot, 'pv') && (shieldNext || !canMount(nextSlot, 'pv', 2))) {
pvhSlot = slot;
ship.use(slot, ModuleUtils.findInternal('pv', Math.min(Math.floor(pvhSlot.maxClass / 2) * 2, 6), 'G'));
ship.setSlotEnabled(slot, false); // Disabled power for PVH
shieldNext = !sgSlot;
} else if (afmUnitCount > 0 && canMount(slot, 'am')) {
afmUnitCount--;
ship.use(slot, ModuleUtils.findInternal('am', slot.maxClass, 'A'));
ship.setSlotEnabled(slot, false); // Disabled power for AFM Unit
shieldNext = !sgSlot;
} else {
ship.use(slot, null);
}
}
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--;
} else {
ship.use(s, null);
}
}
if (sgSlot) {
// The SG and Fuel scoop to not need to be powered at the same time
if (sgSlot.m.power > fuelScoopSlot.m.power) { // The Shield generator uses the most power
ship.setSlotEnabled(fuelScoopSlot, false);
} else { // The Fuel scoop uses the most power
ship.setSlotEnabled(sgSlot, false);
}
}
ship.useLightestStandard(standardOpts);
}