var _ = require('lodash'); var roleBase = require('role.base'); var roleIdle = require('role.idle'); var roleHarvester = require('role.harvester'); var roleHarvesterV2 = require('role.harvester.v2'); var roleRefiller = require('role.refiller'); var roleUpgrader = require('role.upgrader'); var roleBuilder = require('role.builder'); var roleRepairer = require('role.repairer'); var roleWallRepairer = require('role.wallrepairer'); var rolePickup = require('role.pickup'); /* About behaviour levels: Level -1 Special level to make all creeps move to an idle location. Level 1 The starting level, this can be run from the start of the map. All bots will mine the resources they need, then do their task. Level 2 This introduces refillers, overhauls harvesters and changes the behaviour of the rest. Harvesters will deposit mined energy into a container or storage unit placed nearby. Refillers will take energy to structures that need them. Other creeps will take energy from the container/storage instead of mining them themselves. Requirements: Containers placed next to all free spots of the sources in the room. Harvesters will then place themselves between the container and the source. For example: (S = source, H = harvester, C = container, X = wall, _ = empty) __C__ _HHH_ CHSXX _HXXX __XXX */ var behaviourLevel = 2; require('creep.custom')(); // Profiler - https://github.com/screepers/screeps-profiler const profiler = require('screeps-profiler'); // Monkey-patch in the profiler profiler.enable(); module.exports.loop = function () { // Wrap my code in the profiler profiler.wrap(function() { // Setup variables with in-game objects for use in the script var towers = _.filter(Game.structures, {structureType: STRUCTURE_TOWER}); var harvesters = _.filter(Game.creeps, (creep) => creep.memory.role == 'harvester'); var refillers = _.filter(Game.creeps, (creep) => creep.memory.role == 'refiller'); var builders = _.filter(Game.creeps, (creep) => creep.memory.role == 'builder'); var upgraders = _.filter(Game.creeps, (creep) => creep.memory.role == 'upgrader'); var repairers = _.filter(Game.creeps, (creep) => creep.memory.role == 'repairer'); var wallRepairers = _.filter(Game.creeps, (creep) => creep.memory.role == 'wallRepairer'); var pickups = _.filter(Game.creeps, (creep) => creep.memory.role == 'pickup'); // Cleanup memory for(var name in Memory.creeps) { if(!Game.creeps[name]) { roleBase.clearMemory(name); delete Memory.creeps[name]; console.log('Clearing non-existing creep memory:', name); } } if(Memory.rooms == undefined) {Memory.rooms = {};} for (var name in Game.rooms) { if(Memory.rooms[name] == undefined) { Memory.rooms[name] = {}; } if(Memory.rooms[name].repairs == undefined) { Memory.rooms[name].repairs = [] } if(Memory.rooms[name].sources == undefined) { Memory.rooms[name].sources = {}; for(let source of Game.rooms[name].find(FIND_SOURCES)) { Memory.rooms[name].sources[source.id] = 0; } } if(Memory.rooms[name].sources_spots == undefined) { Memory.rooms[name].sources_spots = {}; for(let source of Game.rooms[name].find(FIND_SOURCES)) { var num_spots = 0; for (let x of [-1, 0, 1]) { for (let y of [-1, 0, 1]) { if(!(x == 0 && y == 0)){ var pos = new RoomPosition(source.pos.x + x, source.pos.y + y, source.pos.roomName); if(pos.lookFor(LOOK_TERRAIN) == "plain"){ num_spots += 1; } } } } Memory.rooms[name].sources_spots[source.id] = num_spots; } } } // Tower auto-repair structures nearby for(var tower_id in towers) { var tower = towers[tower_id]; // var closestDamagedStructure = tower.pos.findClosestByRange(FIND_STRUCTURES, { // filter: (structure) => structure.hits < structure.hitsMax // }); // if(closestDamagedStructure) { // tower.repair(closestDamagedStructure); // } var closestHostile = tower.pos.findClosestByRange(FIND_HOSTILE_CREEPS); if(closestHostile) { tower.attack(closestHostile); } } // Auto-spawn new creeps var to_spawn = []; var gameTime = Game.time.toString(); var name_index = gameTime.substring(gameTime.length-4, gameTime.length); if(harvesters.length < 2) { to_spawn.push(['harvester', 'Harvey' + name_index, [WORK, CARRY, MOVE]]); } // Refillers only necessary from behaviour level 2 if(behaviourLevel >= 2) { if(refillers.length < 2) { to_spawn.push(['refiller', 'Reffy' + name_index, [WORK, CARRY, MOVE]]); } } if(builders.length < 1) { to_spawn.push(['builder', 'Bob' + name_index, [WORK, CARRY, MOVE]]); } if(repairers.length < 2) { to_spawn.push(['repairer', 'Reppy' + name_index, [WORK, CARRY, MOVE]]); } if(upgraders.length < 1) { to_spawn.push(['upgrader', 'Uppy' + name_index, [WORK, CARRY, MOVE]]); } if(wallRepairers.length < 1) { to_spawn.push(['wallRepairer', 'Wally' + name_index, [WORK, CARRY, MOVE]]); } if(pickups.length < 1) { to_spawn.push(['pickup', 'Picky' + name_index, [WORK, CARRY, MOVE]]); } if(to_spawn.length > 0) { for(var spawner_id in Game.spawns) { var spawner = Game.spawns[spawner_id]; if(!spawner.spawning){ var entry = to_spawn.shift(); console.log("Spawning " + entry[0]); var spawned = false; var max_available_energy = spawner.store.getCapacity(RESOURCE_ENERGY); for(let extension of _.filter(Game.structures, {structureType: STRUCTURE_EXTENSION, room: spawner.room})) { max_available_energy += extension.store.getCapacity(RESOURCE_ENERGY); } //max_available_energy = 300; var result = spawner.spawnCustomCreep(entry[0], entry[1], max_available_energy, behaviourLevel); if (result == OK) { spawner.room.visual.text('Spawning ' + entry[1] + ' /w ' + max_available_energy + " energy.", spawner.pos.x + 1, spawner.pos.y, {align: 'left', opacity: 0.8}); } // If there are no harvesters any more, and there is not enough energy, spawn a basic harvester. if(harvesters.length == 0 && result == ERR_NOT_ENOUGH_ENERGY) { result = spawner.spawnCreep([WORK, CARRY, MOVE], "BasicHarvester", {memory: {role: 'harvester'}}); if (result == OK) { spawner.room.visual.text('Spawning BasicHarvester.'); } } // If there are no refillers any more, and there is not enough energy, spawn a basic refiller. if(refillers.length == 0 && result == ERR_NOT_ENOUGH_ENERGY) { result = spawner.spawnCreep([WORK, CARRY, MOVE], "BasicRefiller", {memory: {role: 'refiller'}}); if (result == OK) { spawner.room.visual.text('Spawning BasicRefiller.'); } } if (result != OK) { switch (result) { case ERR_BUSY: console.log("Could not spawn " + entry[1] + ", spawner is busy."); break; case ERR_NOT_ENOUGH_ENERGY: console.log("Could not spawn " + entry[1] + ", not enough energy."); break; case ERR_RCL_NOT_ENOUGH: console.log("Could not spawn " + entry[1] + ", RCL not high enough."); break; default: console.log("Could not spawn " + entry[1] + ", error code " + result); break; } } else { spawned = true; } } } if(!spawned){ console.log("Could not spawn, all spawners are busy or errored."); } } // Execute creep ticks for(var name in Game.creeps) { var creep = Game.creeps[name]; var continue_with_role = roleBase.run(creep, behaviourLevel); // If we are at behaviour level -1, move all bots to an idle position if (behaviourLevel == -1) { roleIdle.run(creep, behaviourLevel); } else { // Else, continue with the normal roles if(continue_with_role) { switch(creep.memory.role) { case "harvester": if (behaviourLevel >= 2) { roleHarvesterV2.run(creep, behaviourLevel); } else { roleHarvester.run(creep, behaviourLevel); } break; case "refiller": if (behaviourLevel >= 2) { roleRefiller.run(creep, behaviourLevel); } else { // No defined behaviour for refillers on level 1, just act as a Harvester. roleHarvester.run(creep, behaviourLevel); } break; case "upgrader": roleUpgrader.run(creep, behaviourLevel); break; case "builder": roleBuilder.run(creep, behaviourLevel); break; case "repairer": roleRepairer.run(creep, behaviourLevel); break; case "wallRepairer": roleWallRepairer.run(creep, behaviourLevel); break; case "pickup": rolePickup.run(creep, behaviourLevel); break; default: console.log("Creep with role " + creep.memory.role + " has no defined behaviour."); } } } } // Print used CPU ticks every 10 game ticks if(Game.time % 100 == 0) { if(Game.cpu.limit){ console.log("Used " + Game.cpu.getUsed() + " / " + Game.cpu.limit + "(L) / " + Game.cpu.tickLimit + " (B) CPU this tick. Bucket level: " + Game.cpu.bucket); } } }); }