const { sum } = require("lodash"); const ROLE_IDENT = "Harv"; const STATE_INIT = 0; const STATE_GOING_TO_SOURCE = 1; const STATE_HARVESTING = 2; const STATE_GOING_TO_DESTINATION = 3; const STATE_DELIVERING = 4; var roleHarvester = { ROLE_IDENT: ROLE_IDENT, minimum_parts: [WORK, MOVE, CARRY], recommended_parts: [WORK, MOVE, CARRY], min_cost: sum([BODYPART_COST["work"], BODYPART_COST["move"], BODYPART_COST["carry"]]), /** * Orders the given spawn to create a creep fitting for this role according to the recommended_parts and if impossible use minimum_parts instead * @param {spawn} spawn * @param {string} postfix string to postfix to the creep * @param {string} owner_id strind id of who shall be entered as owner of the new creep * * @return {string} name of the new creep */ spawn: function(spawn, postfix, owner_id) { let creep_name = "harvester-"+postfix; if (spawn.spawnCreep(this.recommended_parts, creep_name, {dryRun: true}) === OK) { console.log("roleHarvester: Spawn "+spawn.name+" is creating "+creep_name+" with recommended parts ..."); spawn.spawnCreep(this.recommended_parts, creep_name, {memory: {"owner": owner_id, "role": ROLE_IDENT}}); } else if (spawn.spawnCreep(this.minimum_parts, creep_name, {dryRun: true}) === OK) { console.log("roleHarvester: Spawn "+spawn.name+" is creating "+creep_name+" with minimum parts ..."); spawn.spawnCreep(this.minimum_parts, creep_name, {memory: {"owner": owner_id, "role": ROLE_IDENT}}); } else { console.log("roleHarvester: Spawn \""+spawn.name+"\" couldn't afford a harvester! D: Result: ", spawn.spawnCreep(this.minimum_parts, creep_name, {dryRun: true})); throw "Not enough energy!"; } Memory.creeps[creep_name].harvest_spot = { id: null, resource_type: null, structure_pos: null, goto_pos: null, }; Memory.creeps[creep_name].destination_spot = { id: null, structure_pos: null, goto_pos: null, }; return creep_name; }, /** * Checks if the creep has all the parts necessary to work its role * @param {Creep} creep * @return bool that indicates if the creep can fullfill its role */ can_work: function(creep) { if (creep.getActiveBodyparts(MOVE) >= 1 && creep.getActiveBodyparts(WORK) >= 1 && creep.getActiveBodyparts(CARRY) >= 1) { return true; } return false; }, /** * Checks if the creep has been set to this role. This does not include a check of their body parts. * @param {Creep} creep */ is_harvester: function(creep) { if (creep.memory.role === ROLE_IDENT) { return true; } else { return false; } }, /** * Runs the logic and actions of the harvester * @param {Creep} creep */ run: function(creep) { if (creep.id === undefined) { // id is undefined if the creep was created this tick, should happen only once this.switch_state(creep, STATE_INIT); return } //console.log("roleHarvester ["+creep.name+"]: run() " + creep + " id:", creep.id); // TODO implement state machine here about what the harvester is up to. Currently it only moves to the nearest source. switch (creep.memory.state) { case STATE_INIT: this.init_creep(creep); break; case STATE_GOING_TO_SOURCE: if (!creep.spawning) { this.go_to_source(creep); } break; case STATE_HARVESTING: this.harvest(creep); break; case STATE_GOING_TO_DESTINATION: this.go_to_destination(creep); break; case STATE_DELIVERING: this.delivering(creep); break; default: console.log("roleHarvester ["+creep.name+"]: run() unknown state!"); //this.switch_state(creep, STATE_INIT); }; }, /** * Switches the state of the creep by editing the memory * @param {Creep} creep * @param {int} state State constant as given (e.g. STATE_HARVESTING) */ switch_state: function(creep, state) { console.log("roleHarvester ["+creep.name+"]: Changing state to "+state); //console.log(creep, creep.memory); creep.memory.state = state }, /** * Inits creep and sets the assigned source to be the nearest source. * @param {*} creep */ init_creep: function(creep) { // TODO make source assignment aware of other harvesters to spread them if (creep.memory.harvest_spot.id !== null) { // harvest spot has already been given (by overlord?). Don't have to decide on my own! } else { let room_sources = creep.room.find(FIND_SOURCES); let goals = _.map(room_sources, function(source) { return {pos: source.pos, range: 1}; }); let nearest_source = PathFinder.search(creep.pos, goals); //console.log("roleHarvester ["+creep.name+"]: init_creep() remembering target position: " + nearest_source.path[nearest_source.path.length-1]) for (let source of room_sources) { if (source.pos.isNearTo(nearest_source.path[nearest_source.path.length-1])) { creep.memory.harvest_spot.id = source.id; creep.memory.harvest_spot.structure_pos = source.pos; creep.memory.harvest_spot.goto_pos = nearest_source.path[nearest_source.path.length-1]; creep.memory.harvest_spot.resource_type = RESOURCE_ENERGY; break; } } } if (creep.memory.destination_spot.structure_pos !== null && creep.memory.destination_spot.goto_pos !== null) { // destination spot has already been given (by the overlord?). Don't have to decide on my own! } else if (creep.memory.destination_spot.structure_pos !== null) { // goto_pos === null and we have to find it on our own later // } else { console.log("roleHarvester ["+creep.name+"] init_creep(): Don't have a destination_spot and autonomously finding one is not implemented!"); throw "No Destination spot"; } console.log("roleHarvester ["+creep.name+"] init_creep(): harvest task has been setup and committed to memory!"); this.switch_state(creep, STATE_GOING_TO_SOURCE); }, /** * Moves the creep to its assigned source. The assigned source is a roomPosition in creep.memory.harvest_target_pos * @param {Creep} creep */ go_to_source: function(creep) { if (creep.memory.harvest_spot.goto_pos === null) { // find goto pos, if needed let structure_pos = new RoomPosition(creep.memory.harvest_spot.structure_pos.x, creep.memory.harvest_spot.structure_pos.y, creep.memory.hravest_spot.structure_pos.roomName); let result = PathFinder.search(creep.pos, {pos: structure_pos, range: 1}); creep.memory.harvest_spot.goto_pos = result.path[result.path.length-1]; } let target_pos = new RoomPosition(creep.memory.harvest_spot.goto_pos.x, creep.memory.harvest_spot.goto_pos.y, creep.memory.harvest_spot.goto_pos.roomName); if (creep.pos.isEqualTo(target_pos)) { console.log("roleHarvester ["+creep.name+"]: go_to_source() arrived at target!"); this.switch_state(creep, STATE_HARVESTING); } //console.log("roleHarvester ["+creep.name+"]: go_to_source() moving creep towards " + target_pos); creep.moveTo(target_pos, {visualizePathStyle: {stroke: '#ffaa00'}}); }, /** * Lets the creep harvest its assigned source * @param {Creep} creep */ harvest: function(creep) { if (creep.store.getFreeCapacity(creep.memory.harvest_spot.resource_type) > 0) { let target = Game.getObjectById(creep.memory.harvest_spot.id); let err = creep.harvest(target); if (err != OK) { console.log("roleHarvester [ERROR]["+creep.name+"] harvest(): Failed to harvest at target "+target.pos+" with error: "+err); if (err != ERR_NOT_IN_RANGE) { console.log("roleHarvester ["+creep.name+"] harvest(): Failed to harvest because of wrong goto_pos trying to recalculate and switch back to moving ..."); creep.memory.harvest_spot.structure_pos = target.pos; let result = PathFinder.search(creep.pos, {pos: target.pos, range: 1}); creep.memory.harvest_spot.goto_pos = result.path[result.path.length-1]; this.switch_state(creep, STATE_GOING_TO_SOURCE); } } } else { this.switch_state(creep, STATE_GOING_TO_DESTINATION); } }, /** * Goes to the delivery destination of the creep as dictated by destination_spot in its memory * @param {Creep} creep */ go_to_destination: function(creep) { if (creep.memory.destination_spot.goto_pos === null) { // find goto pos, if needed let structure_pos = new RoomPosition(creep.memory.destination_spot.structure_pos.x, creep.memory.destination_spot.structure_pos.y, creep.memory.destination_spot.structure_pos.roomName); let result = PathFinder.search(creep.pos, {pos: structure_pos, range: 1}); creep.memory.destination_spot.goto_pos = result.path[result.path.length-1]; } let target_pos = new RoomPosition(creep.memory.destination_spot.goto_pos.x, creep.memory.destination_spot.goto_pos.y, creep.memory.destination_spot.goto_pos.roomName); if (creep.pos.isEqualTo(target_pos)) { console.log("roleHarvester ["+creep.name+"]: go_to_destination() arrived at target!"); this.switch_state(creep, STATE_DELIVERING); } creep.moveTo(target_pos, {visualizePathStyle: {stroke: '#ffaa00'}}); }, /** * Lets the creep store its resources into the destination defined by the memory destination_spot * @param {Creep} creep */ delivering: function(creep) { let had_issue = false; let storage = Game.getObjectById(creep.memory.destination_spot.id); for (const resource_type in creep.store) { console.log("roleHarvester ["+creep.name+"] delivering(): "+resource_type); let err = creep.transfer(storage, resource_type); if (err != OK) { had_issue = true; console.log("roleHarvester [ERROR]["+creep.name+"] delivering(): Failed to transfer resources with error: "+err); creep.say("ERROR!"); } } if (!had_issue) { this.switch_state(creep, STATE_GOING_TO_SOURCE); } else { // TODO what to do if delivery failed? (e.g. destination is full) } }, }; module.exports = roleHarvester;