You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

267 lines
11 KiB
JavaScript

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;