import _ from 'lodash' import { lacuna, call, findPlanets, eachPlanet, findBuilding, log, util, } from '@tlecommunity/super-core' import { buildRoom, distance, scanOutward } from './starmap' class SendProbes { constructor(options) { this.options = options this.probesSent = 0 } validateOptions() { return new Promise((resolve, reject) => { if (!this.options.planet) { reject('please specify a planet') } else { resolve(true) } }) } async buildToFill(colony, sy, toBuild) { let quantity = await buildRoom(sy, toBuild) if (quantity <= 0) { log.info(`No room to build more probes on ${colony.name}`) return } if (quantity < toBuild) { log.info(`Only room for ${quantity} of ${toBuild} new probes on ${colony.name}`) } let plural = util.handlePlurality(quantity, 'probe') log.info(`Building ${quantity} ${plural} on ${colony.name}`) if (!this.options.dryRun) { await call(() => lacuna.shipyard.buildShips({ options: { autoselect: 'all', body_id: colony.id, building_id: sy.id, quantity, type: 'probe', }, }) ) } } async handleColony(colony, buildings) { let obs = await findBuilding(buildings, 'Observatory') let sy = await findBuilding(buildings, 'Shipyard') let sp = await findBuilding(buildings, 'Space Port') if (!obs || !sy || !sp) { log.info(`${colony.name} needs an Observatory, Shipyard and Space Port - skipping`) return } let probed = await call(() => lacuna.observatory.getProbedStars({ building_id: obs.id, page_number: 1 }) ) let controlled = util.int(probed.star_count) let inTransit = util.int(probed.travelling) let max = util.int(probed.max_probes) let ships = await call(() => lacuna.spacePort.viewAllShips({ building_id: sp.id, paging: { no_paging: 1 }, filter: { type: 'probe', task: 'Docked' }, }) ) let dockedProbes = ships.ships || [] let docked = dockedProbes.length let freeCap = max - controlled - inTransit if (freeCap <= 0) { log.info( `Observatory on ${colony.name} is at capacity (${controlled}/${max}, ${inTransit} inbound)` ) return } let toBuild = freeCap - docked if (toBuild > 0) { await this.buildToFill(colony, sy, toBuild) } let sendCount = Math.min(freeCap, docked) if (sendCount <= 0) { log.info(`No probes ready to send from ${colony.name} (${freeCap} slot(s) free)`) return } let status = await call(() => lacuna.body.getStatus({ body_id: colony.id })) let origin = { x: util.int(status.body.x), y: util.int(status.body.y) } let candidates = [] await scanOutward(origin, { maxRing: 2, enough: (n) => n >= sendCount * 4, collect: (star) => { if (star.bodies || star.station) { return false } candidates.push({ id: star.id, name: star.name, x: util.int(star.x), y: util.int(star.y) }) return true }, }) candidates = _.sortBy(candidates, (s) => distance(origin, s)) let chosen = [] for (const star of candidates) { if (chosen.length >= sendCount) { break } let incoming = await call(() => lacuna.map.checkStarForIncomingProbe({ star_id: star.id })) // The client coerces numeric strings to numbers, so compare numerically. if (incoming.incoming_probe && util.int(incoming.incoming_probe) !== 0) { continue } chosen.push(star) } if (chosen.length === 0) { log.info(`No unprobed stars in range of ${colony.name}`) return } for (const star of chosen) { let dist = distance(origin, star).toFixed(1) if (this.options.dryRun) { log.info(`Would send a probe from ${colony.name} to ${star.name} (${dist} away)`) this.probesSent++ continue } let ship = dockedProbes.shift() if (!ship) { break } try { await call(() => lacuna.spacePort.sendShip({ ship_id: ship.id, target: { star_id: star.id } }) ) this.probesSent++ log.info(`Sent a probe from ${colony.name} to ${star.name} (${dist} away)`) } catch (err) { log.error(`Couldn't send a probe to ${star.name}: ${err}`) dockedProbes.unshift(ship) } } if (chosen.length < sendCount) { log.info( `Only found ${chosen.length} of ${sendCount} unprobed stars within range of ${colony.name}` ) } } run() { return new Promise((resolve, reject) => { findPlanets(this.options.planet) .then((colonies) => { return eachPlanet(colonies, _.bind(this.handleColony, this), { force: true, }) }) .then(() => { if (this.probesSent > 0) { let plural = util.handlePlurality(this.probesSent, 'probe') resolve(`Sent ${this.probesSent} ${plural}`) } else { resolve(`Didn't send any probes`) } }) .catch(reject) }) } } export default SendProbes