Merge branch 'release/2.5.3'

This commit is contained in:
willyb321
2018-04-14 09:47:08 +10:00
12 changed files with 227 additions and 182 deletions

2
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{ {
"name": "coriolis_shipyard", "name": "coriolis_shipyard",
"version": "2.5.1", "version": "2.5.2",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "coriolis_shipyard", "name": "coriolis_shipyard",
"version": "2.5.2", "version": "2.5.3",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/EDCD/coriolis" "url": "https://github.com/EDCD/coriolis"

View File

@@ -53,6 +53,7 @@ export default class ShipSummaryTable extends TranslatedComponent {
<th rowSpan={2}>{translate('TTD')}</th> <th rowSpan={2}>{translate('TTD')}</th>
{/* <th onMouseEnter={termtip.bind(null, 'heat per second')} onMouseLeave={hide} rowSpan={2}>{translate('HPS')}</th> */} {/* <th onMouseEnter={termtip.bind(null, 'heat per second')} onMouseLeave={hide} rowSpan={2}>{translate('HPS')}</th> */}
<th rowSpan={2}>{translate('cargo')}</th> <th rowSpan={2}>{translate('cargo')}</th>
<th rowSpan={2}>{translate('passengers')}</th>
<th rowSpan={2}>{translate('fuel')}</th> <th rowSpan={2}>{translate('fuel')}</th>
<th colSpan={3}>{translate('mass')}</th> <th colSpan={3}>{translate('mass')}</th>
<th onMouseEnter={termtip.bind(null, 'hull hardness', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('hrd')}</th> <th onMouseEnter={termtip.bind(null, 'hull hardness', { cap: 0 })} onMouseLeave={hide} rowSpan={2}>{translate('hrd')}</th>
@@ -86,6 +87,7 @@ export default class ShipSummaryTable extends TranslatedComponent {
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_TTD', { cap: 0 })} onMouseLeave={hide}>{timeToDrain === Infinity ? '∞' : time(timeToDrain)}</td> <td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_TTD', { cap: 0 })} onMouseLeave={hide}>{timeToDrain === Infinity ? '∞' : time(timeToDrain)}</td>
{/* <td>{f1(ship.totalHps)}</td> */} {/* <td>{f1(ship.totalHps)}</td> */}
<td>{round(ship.cargoCapacity)}{u.T}</td> <td>{round(ship.cargoCapacity)}{u.T}</td>
<td>{ship.passengerCapacity}</td>
<td>{round(ship.fuelCapacity)}{u.T}</td> <td>{round(ship.fuelCapacity)}{u.T}</td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_HULL_MASS', { cap: 0 })} onMouseLeave={hide}>{ship.hullMass}{u.T}</td> <td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_HULL_MASS', { cap: 0 })} onMouseLeave={hide}>{ship.hullMass}{u.T}</td>
<td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_MASS', { cap: 0 })} onMouseLeave={hide}>{int(ship.unladenMass)}{u.T}</td> <td onMouseEnter={termtip.bind(null, 'TT_SUMMARY_UNLADEN_MASS', { cap: 0 })} onMouseLeave={hide}>{int(ship.unladenMass)}{u.T}</td>

View File

@@ -235,12 +235,10 @@ export default class StandardSlotSection extends SlotSection {
<ul> <ul>
<li className='lc' onClick={this._multiPurpose.bind(this, false, 0)}>{translate('Multi-purpose')}</li> <li className='lc' onClick={this._multiPurpose.bind(this, false, 0)}>{translate('Multi-purpose')}</li>
<li className='lc' onClick={this._multiPurpose.bind(this, true, 2)}>{translate('Combat')}</li> <li className='lc' onClick={this._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('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='lc' onClick={this._optimizeExplorer.bind(this, false)}>{translate('Explorer')}</li>
<li className={cn('lc', { disabled: planetaryDisabled })} onClick={!planetaryDisabled && this._optimizeExplorer.bind(this, true)}>{translate('Planetary Explorer')}</li> <li className={cn('lc', { disabled: planetaryDisabled })} onClick={!planetaryDisabled && this._optimizeExplorer.bind(this, true)}>{translate('Planetary Explorer')}</li>
<li className='lc' onClick={this._optimizeMiner.bind(this, false)}>{translate('Miner')}</li> <li className='lc' onClick={this._optimizeMiner.bind(this, true)}>{translate('Miner')}</li>
<li className='lc' onClick={this._optimizeMiner.bind(this, true)}>{translate('Shielded Miner')}</li>
<li className='lc' onClick={this._optimizeRacer.bind(this)}>{translate('Racer')}</li> <li className='lc' onClick={this._optimizeRacer.bind(this)}>{translate('Racer')}</li>
</ul> </ul>
</div>; </div>;

View File

@@ -42,7 +42,7 @@ function countInt(slot) {
passSlotType = 'pcq'; passSlotType = 'pcq';
passSlotRating = 'B'; passSlotRating = 'B';
} }
let passengerBay = passSlotType ? ModuleUtils.findInternal(passSlotType, slot.maxClass, passSlotRating) : null; let passengerBay = passSlotType ? ModuleUtils.findMaxInternal(passSlotType, slot.maxClass, passSlotRating) : null;
this.maxPassengers += passengerBay ? passengerBay.passengers : 0; this.maxPassengers += passengerBay ? passengerBay.passengers : 0;
} }

View File

@@ -195,6 +195,29 @@ export function findInternal(groupName, clss, rating, name) {
return null; return null;
} }
/**
* Finds an internal module based on Class, Rating, Group and/or name.
* At least one of Group name or unique module name must be provided.
* will start searching at specified class and proceed lower until a
* module is found or 0 is hit
* Uses findInternal internally
*
* @param {String} groupName [Optional] Full name or abbreviated name for module group
* @param {integer} clss module Class
* @param {String} rating module Rating
* @param {String} name [Optional] Long/unique name for module -e.g. 'Advanced Discover Scanner'
* @return {Object} The module if found, null if not found
*/
export function findMaxInternal(groupName, clss, rating, name) {
let foundModule = null;
let currentClss = clss;
while (currentClss > 0 && foundModule == null) {
foundModule = findInternal(groupName, currentClss, rating, name);
currentClss = currentClss - 1;
}
return foundModule;
}
/** /**
* Finds an internal Module ID based on Class, Rating, Group and/or name. * Finds an internal Module ID based on Class, Rating, Group and/or name.
* At least one ofGroup name or unique module name must be provided * At least one ofGroup name or unique module name must be provided

View File

@@ -569,6 +569,7 @@ export default class Ship {
// Reset Cumulative stats // Reset Cumulative stats
this.fuelCapacity = 0; this.fuelCapacity = 0;
this.cargoCapacity = 0; this.cargoCapacity = 0;
this.passengerCapacity = 0;
this.ladenMass = 0; this.ladenMass = 0;
this.armour = this.baseArmour; this.armour = this.baseArmour;
this.shield = this.baseShieldStrength; this.shield = this.baseShieldStrength;
@@ -1188,6 +1189,7 @@ export default class Ship {
let unladenMass = this.hullMass; let unladenMass = this.hullMass;
let cargoCapacity = 0; let cargoCapacity = 0;
let fuelCapacity = 0; let fuelCapacity = 0;
let passengerCapacity = 0;
unladenMass += this.bulkheads.m.getMass(); unladenMass += this.bulkheads.m.getMass();
@@ -1209,6 +1211,8 @@ export default class Ship {
fuelCapacity += slot.m.fuel; fuelCapacity += slot.m.fuel;
} else if (slot.m.grp === 'cr') { } else if (slot.m.grp === 'cr') {
cargoCapacity += slot.m.cargo; cargoCapacity += slot.m.cargo;
} else if (slot.m.grp.slice(0,2) === 'pc') {
passengerCapacity += slot.m.passengers
} }
} }
} }
@@ -1224,6 +1228,7 @@ export default class Ship {
this.unladenMass = unladenMass; this.unladenMass = unladenMass;
this.cargoCapacity = cargoCapacity; this.cargoCapacity = cargoCapacity;
this.fuelCapacity = fuelCapacity; this.fuelCapacity = fuelCapacity;
this.passengerCapacity = passengerCapacity;
this.ladenMass = unladenMass + fuelCapacity + cargoCapacity; this.ladenMass = unladenMass + fuelCapacity + cargoCapacity;
return this; return this;

View File

@@ -1,5 +1,5 @@
import * as ModuleUtils from './ModuleUtils'; import * as ModuleUtils from './ModuleUtils'
import { canMount } from '../utils/SlotFunctions'; import { canMount } from '../utils/SlotFunctions'
/** /**
* Standard / typical role for multi-purpose or combat (if shielded with better bulkheads) * Standard / typical role for multi-purpose or combat (if shielded with better bulkheads)
@@ -7,20 +7,20 @@ import { canMount } from '../utils/SlotFunctions';
* @param {Boolean} shielded True if shield generator should be included * @param {Boolean} shielded True if shield generator should be included
* @param {integer} bulkheadIndex Bulkhead to use see Constants.BulkheadNames * @param {integer} bulkheadIndex Bulkhead to use see Constants.BulkheadNames
*/ */
export function multiPurpose(ship, shielded, bulkheadIndex) { export function multiPurpose (ship, shielded, bulkheadIndex) {
ship.useStandard('A') ship.useStandard('A')
.use(ship.standard[3], ModuleUtils.standard(3, ship.standard[3].maxClass + 'D')) // D Life Support .use(ship.standard[3], ModuleUtils.standard(3, ship.standard[3].maxClass + 'D')) // D Life Support
.use(ship.standard[5], ModuleUtils.standard(5, ship.standard[5].maxClass + 'D')) // D Sensors .use(ship.standard[5], ModuleUtils.standard(5, ship.standard[5].maxClass + 'D')) // D Sensors
.useBulkhead(bulkheadIndex); .useBulkhead(bulkheadIndex)
if (shielded) { if (shielded) {
ship.internal.some(function(slot) { ship.internal.some(function (slot) {
if (canMount(ship, slot, 'sg')) { // Assuming largest slot can hold an eligible shield if (canMount(ship, slot, 'sg')) { // Assuming largest slot can hold an eligible shield
ship.use(slot, ModuleUtils.findInternal('sg', slot.maxClass, 'A')); ship.use(slot, ModuleUtils.findInternal('sg', slot.maxClass, 'A'))
ship.setSlotEnabled(slot, true); ship.setSlotEnabled(slot, true)
return true; return true
} }
}); })
} }
} }
@@ -30,40 +30,55 @@ export function multiPurpose(ship, shielded, bulkheadIndex) {
* @param {Boolean} shielded True if shield generator should be included * @param {Boolean} shielded True if shield generator should be included
* @param {Object} standardOpts [Optional] Standard module optional overrides * @param {Object} standardOpts [Optional] Standard module optional overrides
*/ */
export function trader(ship, shielded, standardOpts) { export function trader (ship, shielded, standardOpts) {
let usedSlots = [], let usedSlots = []
sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass); let bstCount = 2
let sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass)
ship.useStandard('A')
.use(ship.standard[3], ModuleUtils.standard(3, ship.standard[3].maxClass + 'D')) // D Life Support
.use(ship.standard[1], ModuleUtils.standard(1, ship.standard[1].maxClass + 'D')) // D Life Support
.use(ship.standard[4], ModuleUtils.standard(4, ship.standard[4].maxClass + 'D')) // D Life Support
.use(ship.standard[5], ModuleUtils.standard(5, ship.standard[5].maxClass + 'D')) // D Sensors
// Shield generator if required const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8]
if (shielded) {
const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8];
const shieldInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1) const shieldInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
.filter(a => (!a.eligible) || a.eligible.sg) .filter(a => (!a.eligible) || a.eligible.sg)
.filter(a => a.maxClass >= sg.class) .filter(a => a.maxClass >= sg.class)
.sort((a,b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass)); .sort((a, b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass))
for (let i = 0; i < shieldInternals.length; i++) { shieldInternals.some(function (slot) {
if (canMount(ship, shieldInternals[i], 'sg')) { if (canMount(ship, slot, 'sg')) { // Assuming largest slot can hold an eligible shield
ship.use(shieldInternals[i], sg); const shield = ModuleUtils.findInternal('sg', slot.maxClass, 'A')
usedSlots.push(shieldInternals[i]); if (shield && shield.maxmass > ship.hullMass) {
break; console.log(shield)
} ship.use(slot, shield)
ship.setSlotEnabled(slot, true)
usedSlots.push(slot)
return true
} }
} }
})
// Fill the empty internals with cargo racks // Fill the empty internals with cargo racks
for (let i = ship.internal.length; i--;) { for (let i = ship.internal.length; i--;) {
let slot = ship.internal[i]; let slot = ship.internal[i]
if (usedSlots.indexOf(slot) == -1 && canMount(ship, slot, 'cr')) { if (usedSlots.indexOf(slot) == -1 && canMount(ship, slot, 'cr')) {
ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E')); ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E'))
} }
} }
// Empty the hardpoints // Empty the hardpoints
for (let s of ship.hardpoints) { for (let s of ship.hardpoints) {
ship.use(s, null); ship.use(s, null)
} }
for (let s of ship.hardpoints) {
ship.useLightestStandard(standardOpts); if (s.maxClass == 0 && bstCount) { // Mount up to 2 boosters
ship.use(s, ModuleUtils.hardpoints('04'))
bstCount--
} else {
ship.use(s, null)
}
}
// ship.useLightestStandard(standardOpts);
} }
/** /**
@@ -71,127 +86,127 @@ export function trader(ship, shielded, standardOpts) {
* @param {Ship} ship Ship instance * @param {Ship} ship Ship instance
* @param {Boolean} planetary True if Planetary Vehicle Hangar (PVH) should be included * @param {Boolean} planetary True if Planetary Vehicle Hangar (PVH) should be included
*/ */
export function explorer(ship, planetary) { export function explorer (ship, planetary) {
let standardOpts = { ppRating: 'A' }, let standardOpts = {ppRating: 'A'},
heatSinkCount = 2, // Fit 2 heat sinks if possible heatSinkCount = 2, // Fit 2 heat sinks if possible
usedSlots = [], usedSlots = [],
sgSlot, sgSlot,
fuelScoopSlot, fuelScoopSlot,
sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass); sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass)
if (!planetary) { // Non-planetary explorers don't really need to boost if (!planetary) { // Non-planetary explorers don't really need to boost
standardOpts.pd = '1D'; standardOpts.pd = '1D'
} }
// Cargo hatch can be disabled // Cargo hatch can be disabled
ship.setSlotEnabled(ship.cargoHatch, false); ship.setSlotEnabled(ship.cargoHatch, false)
// Advanced Discovery Scanner - class 1 or higher // Advanced Discovery Scanner - class 1 or higher
const adsOrder = [1, 2, 3, 4, 5, 6, 7, 8]; const adsOrder = [1, 2, 3, 4, 5, 6, 7, 8]
const adsInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1) const adsInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
.filter(a => (!a.eligible) || a.eligible.sc) .filter(a => (!a.eligible) || a.eligible.sc)
.sort((a,b) => adsOrder.indexOf(a.maxClass) - adsOrder.indexOf(b.maxClass)); .sort((a, b) => adsOrder.indexOf(a.maxClass) - adsOrder.indexOf(b.maxClass))
for (let i = 0; i < adsInternals.length; i++) { for (let i = 0; i < adsInternals.length; i++) {
if (canMount(ship, adsInternals[i], 'sc')) { if (canMount(ship, adsInternals[i], 'sc')) {
ship.use(adsInternals[i], ModuleUtils.internal('2f')); ship.use(adsInternals[i], ModuleUtils.internal('2f'))
usedSlots.push(adsInternals[i]); usedSlots.push(adsInternals[i])
break; break
} }
} }
if (planetary) { if (planetary) {
// Planetary Vehicle Hangar - class 2 or higher // Planetary Vehicle Hangar - class 2 or higher
const pvhOrder = [2, 3, 4, 5, 6, 7, 8, 1]; const pvhOrder = [2, 3, 4, 5, 6, 7, 8, 1]
const pvhInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1) const pvhInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
.filter(a => (!a.eligible) || a.eligible.pv) .filter(a => (!a.eligible) || a.eligible.pv)
.sort((a,b) => pvhOrder.indexOf(a.maxClass) - pvhOrder.indexOf(b.maxClass)); .sort((a, b) => pvhOrder.indexOf(a.maxClass) - pvhOrder.indexOf(b.maxClass))
for (let i = 0; i < pvhInternals.length; i++) { for (let i = 0; i < pvhInternals.length; i++) {
if (canMount(ship, pvhInternals[i], 'pv')) { if (canMount(ship, pvhInternals[i], 'pv')) {
// Planetary Vehical Hangar only has even classes // Planetary Vehical Hangar only has even classes
const pvhClass = pvhInternals[i].maxClass % 2 === 1 ? pvhInternals[i].maxClass - 1 : pvhInternals[i].maxClass; const pvhClass = pvhInternals[i].maxClass % 2 === 1 ? pvhInternals[i].maxClass - 1 : pvhInternals[i].maxClass
ship.use(pvhInternals[i], ModuleUtils.findInternal('pv', pvhClass, 'G')); // G is lower mass ship.use(pvhInternals[i], ModuleUtils.findInternal('pv', pvhClass, 'G')) // G is lower mass
ship.setSlotEnabled(pvhInternals[i], false); // Disable power for Planetary Vehical Hangar ship.setSlotEnabled(pvhInternals[i], false) // Disable power for Planetary Vehical Hangar
usedSlots.push(pvhInternals[i]); usedSlots.push(pvhInternals[i])
break; break
} }
} }
} }
// Shield generator // Shield generator
const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8]; const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8]
const shieldInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1) const shieldInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
.filter(a => (!a.eligible) || a.eligible.sg) .filter(a => (!a.eligible) || a.eligible.sg)
.filter(a => a.maxClass >= sg.class) .filter(a => a.maxClass >= sg.class)
.sort((a,b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass)); .sort((a, b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass))
for (let i = 0; i < shieldInternals.length; i++) { for (let i = 0; i < shieldInternals.length; i++) {
if (canMount(ship, shieldInternals[i], 'sg')) { if (canMount(ship, shieldInternals[i], 'sg')) {
ship.use(shieldInternals[i], sg); ship.use(shieldInternals[i], sg)
usedSlots.push(shieldInternals[i]); usedSlots.push(shieldInternals[i])
sgSlot = shieldInternals[i]; sgSlot = shieldInternals[i]
break; break
} }
} }
// Detailed Surface Scanner // Detailed Surface Scanner
const dssOrder = [1, 2, 3, 4, 5, 6, 7, 8]; const dssOrder = [1, 2, 3, 4, 5, 6, 7, 8]
const dssInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1) const dssInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
.filter(a => (!a.eligible) || a.eligible.sc) .filter(a => (!a.eligible) || a.eligible.sc)
.sort((a,b) => dssOrder.indexOf(a.maxClass) - dssOrder.indexOf(b.maxClass)); .sort((a, b) => dssOrder.indexOf(a.maxClass) - dssOrder.indexOf(b.maxClass))
for (let i = 0; i < dssInternals.length; i++) { for (let i = 0; i < dssInternals.length; i++) {
if (canMount(ship, dssInternals[i], 'sc')) { if (canMount(ship, dssInternals[i], 'sc')) {
ship.use(dssInternals[i], ModuleUtils.internal('2i')); ship.use(dssInternals[i], ModuleUtils.internal('2i'))
usedSlots.push(dssInternals[i]); usedSlots.push(dssInternals[i])
break; break
} }
} }
// Fuel scoop - best possible // Fuel scoop - best possible
const fuelScoopOrder = [8, 7, 6, 5, 4, 3, 2, 1]; const fuelScoopOrder = [8, 7, 6, 5, 4, 3, 2, 1]
const fuelScoopInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1) const fuelScoopInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
.filter(a => (!a.eligible) || a.eligible.fs) .filter(a => (!a.eligible) || a.eligible.fs)
.sort((a,b) => fuelScoopOrder.indexOf(a.maxClass) - fuelScoopOrder.indexOf(b.maxClass)); .sort((a, b) => fuelScoopOrder.indexOf(a.maxClass) - fuelScoopOrder.indexOf(b.maxClass))
for (let i = 0; i < fuelScoopInternals.length; i++) { for (let i = 0; i < fuelScoopInternals.length; i++) {
if (canMount(ship, fuelScoopInternals[i], 'fs')) { if (canMount(ship, fuelScoopInternals[i], 'fs')) {
ship.use(fuelScoopInternals[i], ModuleUtils.findInternal('fs', fuelScoopInternals[i].maxClass, 'A')); ship.use(fuelScoopInternals[i], ModuleUtils.findInternal('fs', fuelScoopInternals[i].maxClass, 'A'))
usedSlots.push(fuelScoopInternals[i]); usedSlots.push(fuelScoopInternals[i])
fuelScoopSlot = fuelScoopInternals[i]; fuelScoopSlot = fuelScoopInternals[i]
break; break
} }
} }
// AFMUs - fill as they are 0-weight // AFMUs - fill as they are 0-weight
const afmuOrder = [8, 7, 6, 5, 4, 3, 2, 1]; const afmuOrder = [8, 7, 6, 5, 4, 3, 2, 1]
const afmuInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1) const afmuInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
.filter(a => (!a.eligible) || a.eligible.pc) .filter(a => (!a.eligible) || a.eligible.pc)
.sort((a,b) => afmuOrder.indexOf(a.maxClass) - afmuOrder.indexOf(b.maxClass)); .sort((a, b) => afmuOrder.indexOf(a.maxClass) - afmuOrder.indexOf(b.maxClass))
for (let i = 0; i < afmuInternals.length; i++) { for (let i = 0; i < afmuInternals.length; i++) {
if (canMount(ship, afmuInternals[i], 'am')) { if (canMount(ship, afmuInternals[i], 'am')) {
ship.use(afmuInternals[i], ModuleUtils.findInternal('am', afmuInternals[i].maxClass, 'A')); ship.use(afmuInternals[i], ModuleUtils.findInternal('am', afmuInternals[i].maxClass, 'A'))
usedSlots.push(afmuInternals[i]); usedSlots.push(afmuInternals[i])
ship.setSlotEnabled(afmuInternals[i], false); // Disable power for AFM Unit ship.setSlotEnabled(afmuInternals[i], false) // Disable power for AFM Unit
} }
} }
for (let s of ship.hardpoints) { for (let s of ship.hardpoints) {
if (s.maxClass == 0 && heatSinkCount) { // Mount up to 2 heatsinks if (s.maxClass == 0 && heatSinkCount) { // Mount up to 2 heatsinks
ship.use(s, ModuleUtils.hardpoints('02')); ship.use(s, ModuleUtils.hardpoints('02'))
ship.setSlotEnabled(s, heatSinkCount == 2); // Only enable a single Heatsink ship.setSlotEnabled(s, heatSinkCount == 2) // Only enable a single Heatsink
heatSinkCount--; heatSinkCount--
} else { } else {
ship.use(s, null); ship.use(s, null)
} }
} }
if (sgSlot && fuelScoopSlot) { if (sgSlot && fuelScoopSlot) {
// The SG and Fuel scoop to not need to be powered at the same time // The SG and Fuel scoop to not need to be powered at the same time
if (sgSlot.m.getPowerUsage() > fuelScoopSlot.m.getPowerUsage()) { // The Shield generator uses the most power if (sgSlot.m.getPowerUsage() > fuelScoopSlot.m.getPowerUsage()) { // The Shield generator uses the most power
ship.setSlotEnabled(fuelScoopSlot, false); ship.setSlotEnabled(fuelScoopSlot, false)
} else { // The Fuel scoop uses the most power } else { // The Fuel scoop uses the most power
ship.setSlotEnabled(sgSlot, false); ship.setSlotEnabled(sgSlot, false)
} }
} }
ship.useLightestStandard(standardOpts); ship.useLightestStandard(standardOpts)
} }
/** /**
@@ -199,187 +214,188 @@ export function explorer(ship, planetary) {
* @param {Ship} ship Ship instance * @param {Ship} ship Ship instance
* @param {Boolean} shielded True if shield generator should be included * @param {Boolean} shielded True if shield generator should be included
*/ */
export function miner(ship, shielded) { export function miner (ship, shielded) {
let standardOpts = { ppRating: 'A' }, shielded = true
let standardOpts = {ppRating: 'A'},
miningLaserCount = 2, miningLaserCount = 2,
usedSlots = [], usedSlots = [],
sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass); sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass)
// Cargo hatch should be enabled // Cargo hatch should be enabled
ship.setSlotEnabled(ship.cargoHatch, true); ship.setSlotEnabled(ship.cargoHatch, true)
// Largest possible refinery // Largest possible refinery
const refineryOrder = [4, 5, 6, 7, 8, 3, 2, 1]; const refineryOrder = [4, 5, 6, 7, 8, 3, 2, 1]
const refineryInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1) const refineryInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
.filter(a => (!a.eligible) || a.eligible.rf) .filter(a => (!a.eligible) || a.eligible.rf)
.sort((a,b) => refineryOrder.indexOf(a.maxClass) - refineryOrder.indexOf(b.maxClass)); .sort((a, b) => refineryOrder.indexOf(a.maxClass) - refineryOrder.indexOf(b.maxClass))
for (let i = 0; i < refineryInternals.length; i++) { for (let i = 0; i < refineryInternals.length; i++) {
if (canMount(ship, refineryInternals[i], 'rf')) { if (canMount(ship, refineryInternals[i], 'rf')) {
ship.use(refineryInternals[i], ModuleUtils.findInternal('rf', Math.min(refineryInternals[i].maxClass, 4), 'A')); ship.use(refineryInternals[i], ModuleUtils.findInternal('rf', Math.min(refineryInternals[i].maxClass, 4), 'A'))
usedSlots.push(refineryInternals[i]); usedSlots.push(refineryInternals[i])
break; break
} }
} }
// Prospector limpet controller - 3A if possible // Prospector limpet controller - 3A if possible
const prospectorOrder = [3, 4, 5, 6, 7, 8, 2, 1]; const prospectorOrder = [3, 4, 5, 6, 7, 8, 2, 1]
const prospectorInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1) const prospectorInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
.filter(a => (!a.eligible) || a.eligible.pc) .filter(a => (!a.eligible) || a.eligible.pc)
.sort((a,b) => prospectorOrder.indexOf(a.maxClass) - prospectorOrder.indexOf(b.maxClass)); .sort((a, b) => prospectorOrder.indexOf(a.maxClass) - prospectorOrder.indexOf(b.maxClass))
for (let i = 0; i < prospectorInternals.length; i++) { for (let i = 0; i < prospectorInternals.length; i++) {
if (canMount(ship, prospectorInternals[i], 'pc')) { if (canMount(ship, prospectorInternals[i], 'pc')) {
// Prospector only has odd classes // Prospector only has odd classes
const prospectorClass = prospectorInternals[i].maxClass % 2 === 0 ? prospectorInternals[i].maxClass - 1 : prospectorInternals[i].maxClass; const prospectorClass = prospectorInternals[i].maxClass % 2 === 0 ? prospectorInternals[i].maxClass - 1 : prospectorInternals[i].maxClass
ship.use(prospectorInternals[i], ModuleUtils.findInternal('pc', prospectorClass, 'A')); ship.use(prospectorInternals[i], ModuleUtils.findInternal('pc', prospectorClass, 'A'))
usedSlots.push(prospectorInternals[i]); usedSlots.push(prospectorInternals[i])
break; break
} }
} }
// Shield generator if required // Shield generator if required
if (shielded) { if (shielded) {
const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8]; const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8]
const shieldInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1) const shieldInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
.filter(a => (!a.eligible) || a.eligible.sg) .filter(a => (!a.eligible) || a.eligible.sg)
.filter(a => a.maxClass >= sg.class) .filter(a => a.maxClass >= sg.class)
.sort((a,b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass)); .sort((a, b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass))
for (let i = 0; i < shieldInternals.length; i++) { for (let i = 0; i < shieldInternals.length; i++) {
if (canMount(ship, shieldInternals[i], 'sg')) { if (canMount(ship, shieldInternals[i], 'sg')) {
ship.use(shieldInternals[i], sg); ship.use(shieldInternals[i], sg)
usedSlots.push(shieldInternals[i]); usedSlots.push(shieldInternals[i])
break; break
} }
} }
} }
// Dual mining lasers of highest possible class; remove anything else // Dual mining lasers of highest possible class; remove anything else
const miningLaserOrder = [2, 3, 4, 1, 0]; const miningLaserOrder = [2, 3, 4, 1, 0]
const miningLaserHardpoints = ship.hardpoints.concat().sort(function(a,b) { const miningLaserHardpoints = ship.hardpoints.concat().sort(function (a, b) {
return miningLaserOrder.indexOf(a.maxClass) - miningLaserOrder.indexOf(b.maxClass); return miningLaserOrder.indexOf(a.maxClass) - miningLaserOrder.indexOf(b.maxClass)
}); })
for (let s of miningLaserHardpoints) { for (let s of miningLaserHardpoints) {
if (s.maxClass >= 1 && miningLaserCount) { if (s.maxClass >= 1 && miningLaserCount) {
ship.use(s, ModuleUtils.hardpoints(s.maxClass >= 2 ? '2m' : '2l')); ship.use(s, ModuleUtils.hardpoints(s.maxClass >= 2 ? '2m' : '2l'))
miningLaserCount--; miningLaserCount--
} else { } else {
ship.use(s, null); ship.use(s, null)
} }
} }
// Number of collector limpets required to be active is a function of the size of the ship and the power of the lasers // Number of collector limpets required to be active is a function of the size of the ship and the power of the lasers
const miningLaserDps = ship.hardpoints.filter(h => h.m != null) const miningLaserDps = ship.hardpoints.filter(h => h.m != null)
.reduce(function(a, b) { .reduce(function (a, b) {
return a + b.m.getDps(); return a + b.m.getDps()
}, 0); }, 0)
// Find out how many internal slots we have, and their potential cargo size // Find out how many internal slots we have, and their potential cargo size
const potentialCargo = ship.internal.filter(a => usedSlots.indexOf(a) == -1) const potentialCargo = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
.filter(a => (!a.eligible) || a.eligible.cr) .filter(a => (!a.eligible) || a.eligible.cr)
.map(b => Math.pow(2, b.maxClass)); .map(b => Math.pow(2, b.maxClass))
// One collector for each 1.25 DPS, multiply by 1.25 for medium ships and 1.5 for large ships as they have further to travel // One collector for each 1.25 DPS, multiply by 1.25 for medium ships and 1.5 for large ships as they have further to travel
// 0 if we only have 1 cargo slot, otherwise minium of 1 and maximum of 6 (excluding size modifier) // 0 if we only have 1 cargo slot, otherwise minium of 1 and maximum of 6 (excluding size modifier)
const sizeModifier = ship.class == 2 ? 1.2 : ship.class == 3 ? 1.5 : 1; const sizeModifier = ship.class == 2 ? 1.2 : ship.class == 3 ? 1.5 : 1
let collectorLimpetsRequired = potentialCargo.length == 1 ? 0 : Math.ceil(sizeModifier * Math.min(6, Math.floor(miningLaserDps / 1.25))); let collectorLimpetsRequired = potentialCargo.length == 1 ? 0 : Math.ceil(sizeModifier * Math.min(6, Math.floor(miningLaserDps / 1.25)))
if (collectorLimpetsRequired > 0) { if (collectorLimpetsRequired > 0) {
const collectorOrder = [1, 2, 3, 4, 5, 6, 7, 8]; const collectorOrder = [1, 2, 3, 4, 5, 6, 7, 8]
const collectorInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1) const collectorInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
.filter(a => (!a.eligible) || a.eligible.cc) .filter(a => (!a.eligible) || a.eligible.cc)
.sort((a,b) => collectorOrder.indexOf(a.maxClass) - collectorOrder.indexOf(b.maxClass)); .sort((a, b) => collectorOrder.indexOf(a.maxClass) - collectorOrder.indexOf(b.maxClass))
// Always keep at least 2 slots free for cargo racks (1 for shielded) // Always keep at least 2 slots free for cargo racks (1 for shielded)
for (let i = 0; i < collectorInternals.length - (shielded ? 1 : 2) && collectorLimpetsRequired > 0; i++) { for (let i = 0; i < collectorInternals.length - (shielded ? 1 : 2) && collectorLimpetsRequired > 0; i++) {
if (canMount(ship, collectorInternals[i], 'cc')) { if (canMount(ship, collectorInternals[i], 'cc')) {
// Collector only has odd classes // Collector only has odd classes
const collectorClass = collectorInternals[i].maxClass % 2 === 0 ? collectorInternals[i].maxClass - 1 : collectorInternals[i].maxClass; const collectorClass = collectorInternals[i].maxClass % 2 === 0 ? collectorInternals[i].maxClass - 1 : collectorInternals[i].maxClass
ship.use(collectorInternals[i], ModuleUtils.findInternal('cc', collectorClass, 'D')); ship.use(collectorInternals[i], ModuleUtils.findInternal('cc', collectorClass, 'D'))
usedSlots.push(collectorInternals[i]); usedSlots.push(collectorInternals[i])
collectorLimpetsRequired -= collectorInternals[i].m.maximum; collectorLimpetsRequired -= collectorInternals[i].m.maximum
} }
} }
} }
// Power distributor to power the mining lasers indefinitely // Power distributor to power the mining lasers indefinitely
const wepRateRequired = ship.hardpoints.filter(h => h.m != null) const wepRateRequired = ship.hardpoints.filter(h => h.m != null)
.reduce(function(a, b) { .reduce(function (a, b) {
return a + b.m.getEps(); return a + b.m.getEps()
}, 0); }, 0)
standardOpts.pd = ship.getAvailableModules().matchingPowerDist({ weprate: wepRateRequired }).id; standardOpts.pd = ship.getAvailableModules().matchingPowerDist({weprate: wepRateRequired}).id
// Fill the empty internals with cargo racks // Fill the empty internals with cargo racks
for (let i = ship.internal.length; i--;) { for (let i = ship.internal.length; i--;) {
let slot = ship.internal[i]; let slot = ship.internal[i]
if (usedSlots.indexOf(slot) == -1 && canMount(ship, slot, 'cr')) { if (usedSlots.indexOf(slot) == -1 && canMount(ship, slot, 'cr')) {
ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E')); ship.use(slot, ModuleUtils.findInternal('cr', slot.maxClass, 'E'))
} }
} }
ship.useLightestStandard(standardOpts); ship.useLightestStandard(standardOpts)
} }
/** /**
* Racer Role * Racer Role
* @param {Ship} ship Ship instance * @param {Ship} ship Ship instance
*/ */
export function racer(ship) { export function racer (ship) {
let standardOpts = {}, let standardOpts = {},
usedSlots = [], usedSlots = [],
sgSlot, sgSlot,
sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass); sg = ship.getAvailableModules().lightestShieldGenerator(ship.hullMass)
// Cargo hatch can be disabled // Cargo hatch can be disabled
ship.setSlotEnabled(ship.cargoHatch, false); ship.setSlotEnabled(ship.cargoHatch, false)
// Shield generator // Shield generator
const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8]; const shieldOrder = [1, 2, 3, 4, 5, 6, 7, 8]
const shieldInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1) const shieldInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1)
.filter(a => (!a.eligible) || a.eligible.sg) .filter(a => (!a.eligible) || a.eligible.sg)
.filter(a => a.maxClass >= sg.class) .filter(a => a.maxClass >= sg.class)
.sort((a,b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass)); .sort((a, b) => shieldOrder.indexOf(a.maxClass) - shieldOrder.indexOf(b.maxClass))
for (let i = 0; i < shieldInternals.length; i++) { for (let i = 0; i < shieldInternals.length; i++) {
if (canMount(ship, shieldInternals[i], 'sg')) { if (canMount(ship, shieldInternals[i], 'sg')) {
ship.use(shieldInternals[i], sg); ship.use(shieldInternals[i], sg)
usedSlots.push(shieldInternals[i]); usedSlots.push(shieldInternals[i])
sgSlot = shieldInternals[i]; sgSlot = shieldInternals[i]
break; break
} }
} }
// Empty the hardpoints // Empty the hardpoints
for (let s of ship.hardpoints) { for (let s of ship.hardpoints) {
ship.use(s, null); ship.use(s, null)
} }
// Empty the internals // Empty the internals
for (let i = ship.internal.length; i--;) { for (let i = ship.internal.length; i--;) {
let slot = ship.internal[i]; let slot = ship.internal[i]
if (usedSlots.indexOf(slot) == -1) { if (usedSlots.indexOf(slot) == -1) {
ship.use(slot, null); ship.use(slot, null)
} }
} }
// Best thrusters // Best thrusters
if (ship.standard[1].maxClass === 3) { if (ship.standard[1].maxClass === 3) {
standardOpts.th = 'tz'; standardOpts.th = 'tz'
} else if (ship.standard[1].maxClass === 2) { } else if (ship.standard[1].maxClass === 2) {
standardOpts.th = 'u0'; standardOpts.th = 'u0'
} else { } else {
standardOpts.th = ship.standard[1].maxClass + 'A'; standardOpts.th = ship.standard[1].maxClass + 'A'
} }
// Best power distributor for more boosting // Best power distributor for more boosting
standardOpts.pd = ship.standard[4].maxClass + 'A'; standardOpts.pd = ship.standard[4].maxClass + 'A'
// Smallest possible FSD drive // Smallest possible FSD drive
standardOpts.fsd = '2D'; standardOpts.fsd = '2D'
// Minimal fuel tank // Minimal fuel tank
standardOpts.ft = '1C'; standardOpts.ft = '1C'
// Disable nearly everything // Disable nearly everything
standardOpts.fsdDisabled = true; standardOpts.fsdDisabled = true
standardOpts.sDisabled = true; standardOpts.sDisabled = true
standardOpts.pdDisabled = true; standardOpts.pdDisabled = true
standardOpts.lsDisabled = true; standardOpts.lsDisabled = true
ship.useLightestStandard(standardOpts); ship.useLightestStandard(standardOpts)
// Apply engineering to each module // Apply engineering to each module
// ship.standard[1].m.blueprint = getBlueprint('Engine_Dirty', ship.standard[0]); // ship.standard[1].m.blueprint = getBlueprint('Engine_Dirty', ship.standard[0]);

View File

@@ -2,7 +2,7 @@ import Ship from '../shipyard/Ship'
import { HARDPOINT_NUM_TO_CLASS, shipModelFromJson } from './CompanionApiUtils' import { HARDPOINT_NUM_TO_CLASS, shipModelFromJson } from './CompanionApiUtils'
import { Ships } from 'coriolis-data/dist' import { Ships } from 'coriolis-data/dist'
import Module from '../shipyard/Module' import Module from '../shipyard/Module'
import { Modules } from '../../../../coriolis-data/dist' import { Modules } from 'coriolis-data/dist'
/** /**
* Obtain a module given its FD Name * Obtain a module given its FD Name

View File

@@ -57,7 +57,7 @@
<!-- Bugsnag --> <!-- Bugsnag -->
<script <script
src="//d2wy8f7a9ursnm.cloudfront.net/bugsnag-3.min.js" src="//d2wy8f7a9ursnm.cloudfront.net/bugsnag-3.min.js"
data-apikey="2382691c622937f28f8fa82a1bfd797a"></script> data-apikey="ba9fae819372850fb660755341fa6ef5"></script>
</head> </head>
<body style="background-color:#000;"> <body style="background-color:#000;">

View File

@@ -18,7 +18,7 @@ CopyDirPlugin.prototype.apply = function(compiler) {
}; };
module.exports = { module.exports = {
devtool: 'eval', devtool: 'source-map',
devServer: { devServer: {
headers: { "Access-Control-Allow-Origin": "*" } headers: { "Access-Control-Allow-Origin": "*" }
}, },

View File

@@ -23,6 +23,7 @@ module.exports = {
app: ['babel-polyfill', path.resolve(__dirname, 'src/app/index')], app: ['babel-polyfill', path.resolve(__dirname, 'src/app/index')],
lib: ['d3', 'react', 'react-dom', 'classnames', 'fbemitter', 'lz-string'] lib: ['d3', 'react', 'react-dom', 'classnames', 'fbemitter', 'lz-string']
}, },
devtool: 'source-map',
resolve: { resolve: {
extensions: ['.js', '.jsx', '.json', '.less'] extensions: ['.js', '.jsx', '.json', '.less']
}, },