diff --git a/ChangeLog.md b/ChangeLog.md index 4100f9b1..c7ddf17b 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,3 +1,6 @@ +#2.3.6 + * Update miner role to provide better defaults + #2.3.5 * Ensure that hidden blueprint effects are applied when a blueprint is selected * Handle display when summary values show thrusters disabled but current mass keeps them enabled diff --git a/src/app/components/SlotSection.jsx b/src/app/components/SlotSection.jsx index e6f75da4..41dff6e3 100644 --- a/src/app/components/SlotSection.jsx +++ b/src/app/components/SlotSection.jsx @@ -15,6 +15,8 @@ export default class SlotSection extends TranslatedComponent { static propTypes = { ship: PropTypes.object.isRequired, onChange: PropTypes.func.isRequired, + onCargoChange: PropTypes.func.isRequired, + onFuelChange: PropTypes.func.isRequired, code: PropTypes.string.isRequired, togglePwr: PropTypes.func }; diff --git a/src/app/components/StandardSlotSection.jsx b/src/app/components/StandardSlotSection.jsx index 30b4ff66..cba5511f 100644 --- a/src/app/components/StandardSlotSection.jsx +++ b/src/app/components/StandardSlotSection.jsx @@ -28,6 +28,8 @@ export default class StandardSlotSection extends SlotSection { _optimizeStandard() { this.props.ship.useLightestStandard(); this.props.onChange(); + this.props.onCargoChange(this.props.ship.cargoCapacity); + this.props.onFuelChange(this.props.ship.fuelCapacity); this._close(); } @@ -39,6 +41,8 @@ export default class StandardSlotSection extends SlotSection { _multiPurpose(shielded, bulkheadIndex) { ShipRoles.multiPurpose(this.props.ship, shielded, bulkheadIndex); this.props.onChange(); + this.props.onCargoChange(this.props.ship.cargoCapacity); + this.props.onFuelChange(this.props.ship.fuelCapacity); this._close(); } @@ -49,6 +53,8 @@ export default class StandardSlotSection extends SlotSection { _optimizeCargo(shielded) { ShipRoles.trader(this.props.ship, shielded); this.props.onChange(); + this.props.onCargoChange(this.props.ship.cargoCapacity); + this.props.onFuelChange(this.props.ship.fuelCapacity); this._close(); } @@ -59,6 +65,8 @@ export default class StandardSlotSection extends SlotSection { _optimizeMiner(shielded) { ShipRoles.miner(this.props.ship, shielded); this.props.onChange(); + this.props.onCargoChange(this.props.ship.cargoCapacity); + this.props.onFuelChange(this.props.ship.fuelCapacity); this._close(); } @@ -69,6 +77,8 @@ export default class StandardSlotSection extends SlotSection { _optimizeExplorer(planetary) { ShipRoles.explorer(this.props.ship, planetary); this.props.onChange(); + this.props.onCargoChange(this.props.ship.cargoCapacity); + this.props.onFuelChange(this.props.ship.fuelCapacity); this._close(); } @@ -78,6 +88,8 @@ export default class StandardSlotSection extends SlotSection { _optimizeRacer() { ShipRoles.racer(this.props.ship); this.props.onChange(); + this.props.onCargoChange(this.props.ship.cargoCapacity); + this.props.onFuelChange(this.props.ship.fuelCapacity); this._close(); } diff --git a/src/app/pages/OutfittingPage.jsx b/src/app/pages/OutfittingPage.jsx index f61df22c..17af3290 100644 --- a/src/app/pages/OutfittingPage.jsx +++ b/src/app/pages/OutfittingPage.jsx @@ -446,7 +446,10 @@ export default class OutfittingPage extends Page { fuel = ship.fuelCapacity; } const code = this._fullCode(ship, fuel, cargo); - this.setState({ code, cargo, fuel }, () => this._updateRoute(shipId, buildName, code)); + // Only update the state if this really has been updated + if (this.state.code != code || this.state.cargo != cargo || this.state.fuel != fuel) { + this.setState({ code, cargo, fuel }, () => this._updateRoute(shipId, buildName, code)); + } } /** @@ -551,7 +554,7 @@ export default class OutfittingPage extends Page { const internalSlotMarker = `${ship.name}${_iStr}${_pStr}${_mStr}`; const hardpointsSlotMarker = `${ship.name}${_hStr}${_pStr}${_mStr}`; const boostMarker = `${ship.canBoost(cargo, fuel)}`; - const shipSummaryMarker = `${ship.name}${_sStr}${_iStr}${_hStr}${_pStr}${_mStr}${ship.ladenMass}${ship.cargo}${ship.fuel}`; + const shipSummaryMarker = `${ship.name}${_sStr}${_iStr}${_hStr}${_pStr}${_mStr}${ship.ladenMass}${cargo}${fuel}`; return (
@@ -588,10 +591,10 @@ export default class OutfittingPage extends Page { {/* Main tables */} - - - - + + + + {/* Control of ship and opponent */}
diff --git a/src/app/shipyard/ModuleSet.js b/src/app/shipyard/ModuleSet.js index 211df63f..d1bc3c90 100755 --- a/src/app/shipyard/ModuleSet.js +++ b/src/app/shipyard/ModuleSet.js @@ -132,6 +132,20 @@ export default class ModuleSet { return new Module({ template: pd }); }; + /** Find the power distributor that matches the requirements + * @param {Object} requirements The requirements to be met (currently only support 'weprate') + * @return {Object} Power distributor + */ + matchingPowerDist(requirements) { + let pd = this.standard[4][0]; + for (let p of this.standard[4]) { + if (p.weprate >= requirements.weprate || p.weprate >= pd.weprate) { + pd = p; + } + } + return new Module({ template: pd }); + } + /** * Finds the lightest Thruster that can handle the specified tonnage * @param {number} ladenMass Ship laden mass (mass + cargo + fuel) diff --git a/src/app/shipyard/ShipRoles.js b/src/app/shipyard/ShipRoles.js index 84b15f55..73fb132f 100644 --- a/src/app/shipyard/ShipRoles.js +++ b/src/app/shipyard/ShipRoles.js @@ -252,24 +252,6 @@ export function miner(ship, shielded) { } } - // Collector limpet controller if there are enough internals left - let collectorLimpetsRequired = Math.max(ship.internal.filter(a => (!a.eligible) || a.eligible.cr).length - 6, 0); - if (collectorLimpetsRequired > 0) { - const collectorOrder = [1, 2, 3, 4, 5, 6, 7, 8]; - const collectorInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1) - .filter(a => (!a.eligible) || a.eligible.cc) - .sort((a,b) => collectorOrder.indexOf(a.maxClass) - collectorOrder.indexOf(b.maxClass)); - for (let i = 0; i < collectorInternals.length && collectorLimpetsRequired > 0; i++) { - if (canMount(ship, collectorInternals[i], 'cc')) { - // Collector only has odd classes - const collectorClass = collectorInternals[i].maxClass % 2 === 0 ? collectorInternals[i].maxClass - 1 : collectorInternals[i].maxClass; - ship.use(collectorInternals[i], ModuleUtils.findInternal('cc', collectorClass, 'A')); - usedSlots.push(collectorInternals[i]); - collectorLimpetsRequired -= collectorInternals[i].m.maximum; - } - } - } - // Dual mining lasers of highest possible class; remove anything else const miningLaserOrder = [2, 3, 4, 1, 0]; const miningLaserHardpoints = ship.hardpoints.concat().sort(function(a,b) { @@ -284,6 +266,45 @@ export function miner(ship, shielded) { } } + // 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) + .reduce(function(a, b) { + return a + b.m.getDps(); + }, 0); + // Find out how many internal slots we have, and their potential cargo size + const potentialCargo = ship.internal.filter(a => usedSlots.indexOf(a) == -1) + .filter(a => (!a.eligible) || a.eligible.cr) + .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 + // 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; + let collectorLimpetsRequired = potentialCargo.length == 1 ? 0 : Math.ceil(sizeModifier * Math.min(6, Math.floor(miningLaserDps / 1.25))); + console.log(`${collectorLimpetsRequired}`); + + if (collectorLimpetsRequired > 0) { + const collectorOrder = [1, 2, 3, 4, 5, 6, 7, 8]; + const collectorInternals = ship.internal.filter(a => usedSlots.indexOf(a) == -1) + .filter(a => (!a.eligible) || a.eligible.cc) + .sort((a,b) => collectorOrder.indexOf(a.maxClass) - collectorOrder.indexOf(b.maxClass)); + // 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++) { + if (canMount(ship, collectorInternals[i], 'cc')) { + // Collector only has odd classes + const collectorClass = collectorInternals[i].maxClass % 2 === 0 ? collectorInternals[i].maxClass - 1 : collectorInternals[i].maxClass; + ship.use(collectorInternals[i], ModuleUtils.findInternal('cc', collectorClass, 'D')); + usedSlots.push(collectorInternals[i]); + collectorLimpetsRequired -= collectorInternals[i].m.maximum; + } + } + } + + // Power distributor to power the mining lasers indefinitely + const wepRateRequired = ship.hardpoints.filter(h => h.m != null) + .reduce(function(a, b) { + return a + b.m.getEps(); + }, 0); + standardOpts.pd = ship.getAvailableModules().matchingPowerDist({weprate: wepRateRequired}).id; + // Fill the empty internals with cargo racks for (let i = ship.internal.length; i--;) { let slot = ship.internal[i];