From a741cd83526ae384891d5bcac1dee8bad8333db5 Mon Sep 17 00:00:00 2001 From: Arne Schauf Date: Wed, 24 Jan 2024 18:02:15 +0100 Subject: [PATCH] initial --- .gitignore | 1 + default/common.js | 46 ++++++++++ default/globals.js | 47 ++++++++++ default/link.js | 25 ++++++ default/main.js | 104 ++++++++++++++++++++++ default/role.builder.js | 107 +++++++++++++++++++++++ default/role.harvester.js | 57 ++++++++++++ default/role.scout.js | 5 ++ default/role.transporter.js | 160 ++++++++++++++++++++++++++++++++++ default/role.upgrader.js | 52 +++++++++++ default/spawn.js | 167 ++++++++++++++++++++++++++++++++++++ 11 files changed, 771 insertions(+) create mode 100644 .gitignore create mode 100644 default/common.js create mode 100644 default/globals.js create mode 100644 default/link.js create mode 100644 default/main.js create mode 100644 default/role.builder.js create mode 100644 default/role.harvester.js create mode 100644 default/role.scout.js create mode 100644 default/role.transporter.js create mode 100644 default/role.upgrader.js create mode 100644 default/spawn.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2dc3750 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.sync diff --git a/default/common.js b/default/common.js new file mode 100644 index 0000000..a1ba1f2 --- /dev/null +++ b/default/common.js @@ -0,0 +1,46 @@ +const consts = { + FINISHED: 1, + HARVESTING: 2, + CONTINUE_ACTIVITY: 3, +} + +module.exports = { + consts, + harvestEnergy (creep) { + if (!creep.memory.harvestingEnergy && creep.store[RESOURCE_ENERGY] > 0) { + return consts.CONTINUE_ACTIVITY + } + creep.memory.harvestingEnergy = creep.store.getFreeCapacity() > 0 + if (!creep.memory.harvestingEnergy) { + return consts.FINISHED + } + // if (creep.store[RESOURCE_ENERGY] === 0) { + // creep.say('🔄 fetch'); + // } + let target + if (!target) { + target = creep.pos.findClosestByRange(FIND_STRUCTURES, { + filter: s => (s.structureType === STRUCTURE_CONTAINER || s.structureType === STRUCTURE_STORAGE) + && s.store[RESOURCE_ENERGY] >= creep.store.getFreeCapacity(RESOURCE_ENERGY) + }); + } + if(target) { + let result = creep.withdraw(target, RESOURCE_ENERGY) + if (result === ERR_NOT_IN_RANGE) { + creep.moveTo(target, {visualizePathStyle: {stroke: '#ffaa00'}}); + } + else if (result === OK) { + return consts.FINISHED + } + } + else { + let target = creep.pos.findClosestByRange(FIND_MY_CREEPS, { + filter: creep => creep.memory.role === 'harvester' && creep.store[RESOURCE_ENERGY] > 0 + }); + if (target) { + creep.moveTo(target, {visualizePathStyle: {stroke: '#ffaa00'}}); + } + } + return consts.HARVESTING + } +} diff --git a/default/globals.js b/default/globals.js new file mode 100644 index 0000000..21fa464 --- /dev/null +++ b/default/globals.js @@ -0,0 +1,47 @@ +const roleHarvester = require('./role.harvester') +const roleTransporter = require('./role.transporter') +const roleBuilder = require('./role.builder') +const roleUpgrader = require('./role.upgrader') + +global.ROLES = { + harvester: { + prio: 3, + count: 3, + module: roleHarvester, + bodies: [ + [WORK, WORK, CARRY, MOVE], + [WORK, WORK, WORK, WORK, WORK, WORK, CARRY, MOVE], + [WORK, WORK, WORK, WORK, WORK, WORK, CARRY, MOVE, MOVE], + [WORK, WORK, WORK, WORK, WORK, WORK, CARRY, MOVE, MOVE, MOVE], + [WORK, WORK, WORK, WORK, WORK, WORK, CARRY, CARRY, MOVE, MOVE, MOVE], + [WORK, WORK, WORK, WORK, WORK, WORK, CARRY, CARRY, MOVE, MOVE, MOVE, MOVE], + ] + }, + transporter: { + prio: 4, + count: 2, + module: roleTransporter, + bodies: [ + [CARRY, CARRY, MOVE], + [CARRY, CARRY, MOVE, CARRY, CARRY, MOVE], + [CARRY, CARRY, MOVE, CARRY, CARRY, MOVE, CARRY, CARRY, MOVE], + [CARRY, CARRY, MOVE, CARRY, CARRY, MOVE, CARRY, CARRY, MOVE, CARRY, CARRY, MOVE], + ] + }, + builder: { + prio: 5, + count: 2, + maxExpands: 4, + module: roleBuilder, + baseBody: [WORK, CARRY, MOVE, MOVE], + expandBody: [WORK, CARRY, MOVE, MOVE], + }, + upgrader: { + prio: 6, + count: 2, + maxExpands: 4, + module: roleUpgrader, + baseBody: [WORK, CARRY, MOVE], + expandBody: [WORK, WORK, MOVE], + }, +} diff --git a/default/link.js b/default/link.js new file mode 100644 index 0000000..fad94b7 --- /dev/null +++ b/default/link.js @@ -0,0 +1,25 @@ +module.exports = { + tickInit () { + for (let room of Object.values(Game.rooms)) { + if (room.memory.controllerLink && !Game.getObjectById(room.memory.controllerLink)) { + delete room.memory.controllerLink + } + } + }, + tick (link) { + if (link.room.memory.controllerLink === link.id) { + return + } + if (link.pos.findInRange(FIND_STRUCTURES, 5, {filter: s => s.structureType === STRUCTURE_CONTROLLER}).length) { + link.room.memory.controllerLink = link.id + return + } + + if (link.room.memory.controllerLink && !link.store.getFreeCapacity(RESOURCE_ENERGY)) { + let targetLink = Game.getObjectById(link.room.memory.controllerLink) + if (targetLink.store.getFreeCapacity(RESOURCE_ENERGY) > 770) { + link.transferEnergy(targetLink) + } + } + } +} diff --git a/default/main.js b/default/main.js new file mode 100644 index 0000000..c5daf81 --- /dev/null +++ b/default/main.js @@ -0,0 +1,104 @@ +const roleHarvester = require('role.harvester') +const roleBuilder = require('role.builder') +const roleTransporter = require('role.transporter') +const roleUpgrader = require('role.upgrader') +const libLink = require('link') +const libSpawn = require('spawn') + +require('globals') + + +module.exports.loop = function () { + // Cleanups + if (!Memory.repairers) { + Memory.repairers = {} + } + if (!Memory.refillers) { + Memory.refillers = {} + } + for (let name in Memory.creeps) { + if (!Game.creeps[name]) { + delete Memory.creeps[name]; + } + } + libLink.tickInit() + libSpawn.tickInit() + + for (let room of Object.values(Game.rooms)) { + // tickInit + for (let role of Object.values(ROLES)) { + role.idling = 0 + if (role.module.tickInit) { + role.module.tickInit(room) + } + } + + for (let role of Object.values(ROLES)) { + if (role.module.cleanup) { + role.module.cleanup() + } + } + + for (let [id, creep] of Object.entries(Memory.refillers)) { + let target = Game.getObjectById(id) + if (!target || target.store.getFreeCapacity(RESOURCE_ENERGY) === 0 || !Game.creeps[creep]) { + delete Memory.refillers[id] + } + } + + let spawn = Game.spawns['Spawn1'] + + // Run modules + for (let link of spawn.room.find(FIND_STRUCTURES, {filter: s => s.structureType === STRUCTURE_LINK})) { + libLink.tick(link) + } + for (let creep of Object.values(Game.creeps)) { + if (creep.store.getFreeCapacity(RESOURCE_ENERGY) > 0) { + for (let target of creep.pos.findInRange(FIND_TOMBSTONES, 1, {filter: t => t.store[RESOURCE_ENERGY] > 0})) { + creep.withdraw(target, RESOURCE_ENERGY) + } + for (let target of creep.pos.findInRange(FIND_RUINS, 1, {filter: t => t.store[RESOURCE_ENERGY] > 0})) { + creep.withdraw(target, RESOURCE_ENERGY) + } + } + if (!creep.memory.role) { + creep.memory.role = 'harvester' + } + if (ROLES[creep.memory.role].module.tick(creep) === 'idle') { + ROLES[creep.memory.role].idling++ + } + } + + libSpawn.tick(spawn) + + let towers = spawn.room.find(FIND_STRUCTURES, {filter: structure => structure.structureType === STRUCTURE_TOWER}) + for (let tower of towers) { + // var closestDamagedStructure = tower.pos.findClosestByRange(FIND_STRUCTURES, { + // filter: (structure) => structure.hits < structure.hitsMax + // }); + // if(closestDamagedStructure) { + // tower.repair(closestDamagedStructure); + // } + + let closestHostile = tower.pos.findClosestByRange(FIND_HOSTILE_CREEPS); + if (closestHostile) { + tower.attack(closestHostile); + } + } + + // for (let [role, values] of Object.entries(ROLES)) { + // if (values.idling > 0) { + // console.log(`${values.idling} ${role}s idling`) + // } + // } + + spawn.room.visual.text( + `${Game.cpu.getUsed().toFixed(1)} / ${Game.cpu.tickLimit} (${Game.cpu.bucket})`, + 1, 1, + {align: 'left', opacity: 0.8}); + } + + if (Game.cpu.generatePixel && Game.cpu.bucket === 10000) { + Game.cpu.generatePixel() + } +} diff --git a/default/role.builder.js b/default/role.builder.js new file mode 100644 index 0000000..a6cd15a --- /dev/null +++ b/default/role.builder.js @@ -0,0 +1,107 @@ +const common = require('common') +let idling = 0 +let maxWallHits = 0 + +module.exports = { + idling, + maxWallHits, + cleanup () { + for(let [id, creep] of Object.entries(Memory.repairers)) { + let repairable = Game.getObjectById(id) + if (!repairable + || repairable.hits === repairable.hitsMax + || !Game.creeps[creep] + || ((repairable.structureType === STRUCTURE_WALL || repairable.structureType === STRUCTURE_RAMPART) + && repairable.hits > maxWallHits) + ) { + delete Memory.repairers[id] + } + } + }, + tickInit (room) { + let walls = room.find(FIND_STRUCTURES, { + filter: structure => (structure.structureType === STRUCTURE_WALL || structure.structureType === STRUCTURE_RAMPART) + }) + let wallAvgHits = 0 + if (walls.length > 0) { + wallAvgHits = walls.reduce((total, el) => total + el.hits, 0) + wallAvgHits = wallAvgHits / walls.length + } + maxWallHits = Math.min(wallAvgHits, 200000) + }, + tick (creep) { + let harvest = common.harvestEnergy(creep) + if (harvest === common.consts.HARVESTING) { + return + } + + if (creep.store.getFreeCapacity(RESOURCE_ENERGY)) { + let dropped = creep.pos.findInRange(FIND_DROPPED_RESOURCES, 1); + if (dropped.length > 0) { + creep.pickup(dropped[0]) + } + + let targets = creep.pos.findInRange(FIND_STRUCTURES, 1, { + filter: s => (s.structureType === STRUCTURE_CONTAINER || s.structureType === STRUCTURE_STORAGE) + && s.store[RESOURCE_ENERGY] > 0 + }) + if (targets.length) { + creep.withdraw(targets[0], RESOURCE_ENERGY) + } + } + + let repairTarget = _.findKey(Memory.repairers, el => el === creep.name) + if (repairTarget) { + repairTarget = Game.getObjectById(repairTarget) + } + + if (!repairTarget) { + repairTarget = creep.pos.findClosestByRange(FIND_STRUCTURES, { + filter: (structure) => { + return ( + structure.structureType !== STRUCTURE_WALL && structure.structureType !== STRUCTURE_RAMPART + ) && ( + structure.hits < structure.hitsMax * 0.5 || + structure.hitsMax - structure.hits >= creep.store.getCapacity(RESOURCE_ENERGY) * 100 + ) && !Memory.repairers[structure.id] + } + }); + } + + if (repairTarget) { + Memory.repairers[repairTarget.id] = creep.name + if(creep.repair(repairTarget) === ERR_NOT_IN_RANGE) { + creep.moveTo(repairTarget, {visualizePathStyle: {stroke: '#1a43cb'}}); + } + return + } + + let target = creep.pos.findClosestByPath(FIND_CONSTRUCTION_SITES, { + filter: (structure) => structure.structureType === STRUCTURE_EXTENSION // || structure.structureType == STRUCTURE_CONTAINER + }) + if (!target) { + target = creep.pos.findClosestByPath(FIND_CONSTRUCTION_SITES); + } + if (target) { + if (creep.build(target) === ERR_NOT_IN_RANGE) { + creep.moveTo(target, {visualizePathStyle: {stroke: '#729075'}}); + } + return + } + + let wallTarget = creep.pos.findClosestByRange(FIND_STRUCTURES, { + filter: structure => structure.hits < structure.hitsMax + && structure.hits <= maxWallHits + && (structure.structureType === STRUCTURE_WALL || structure.structureType === STRUCTURE_RAMPART) + }) + + if (wallTarget) { + if(creep.repair(wallTarget) === ERR_NOT_IN_RANGE) { + creep.moveTo(wallTarget, {visualizePathStyle: {stroke: '#4d4747'}}); + } + return + } + creep.moveTo(creep.pos.findClosestByRange(FIND_FLAGS, {filter: flag => flag.name.startsWith('idle')}), {visualizePathStyle: {stroke: '#ff0000'}}); + return 'idle' + } +} diff --git a/default/role.harvester.js b/default/role.harvester.js new file mode 100644 index 0000000..84d9858 --- /dev/null +++ b/default/role.harvester.js @@ -0,0 +1,57 @@ +let common = require('common') +let idling = 0 + +module.exports = { + idling, + tick (creep) { + let dropped = creep.pos.findInRange(FIND_DROPPED_RESOURCES, 1); + if (dropped.length > 0) { + creep.pickup(dropped[0]) + } + + let targetCreeps = creep.pos.findInRange(FIND_MY_CREEPS, 1, { + filter: tc => tc.store.getFreeCapacity(RESOURCE_ENERGY) && tc.memory.role !== 'harvester' + }); + if (targetCreeps.length > 0) { + let tc = _.sortBy(targetCreeps, el => el.store.getFreeCapacity(RESOURCE_ENERGY))[targetCreeps.length - 1] + creep.transfer(tc, RESOURCE_ENERGY) + } + else { + let targets = creep.pos.findInRange(FIND_STRUCTURES, 1, { + filter: (structure) => structure.store + && structure.store.getFreeCapacity(RESOURCE_ENERGY) > 0 + && structure.structureType === STRUCTURE_EXTENSION + }); + if (targets.length === 0) { + targets = creep.pos.findInRange(FIND_STRUCTURES, 1, { + filter: (structure) => structure.store && structure.store.getFreeCapacity(RESOURCE_ENERGY) > 0 + }); + } + if (targets.length > 0) { + creep.transfer(_.min(targets, el => el.store[RESOURCE_ENERGY]), RESOURCE_ENERGY) + } else { + let targetCreeps = creep.pos.findInRange(FIND_MY_CREEPS, 1, { + filter: tc => tc.store.getFreeCapacity(RESOURCE_ENERGY) + }); + if (targetCreeps.length > 0) { + let tc = targetCreeps[0] + creep.transfer(tc, RESOURCE_ENERGY, tc.memory.role === 'harvester' ? tc.store.getFreeCapacity(RESOURCE_ENERGY) * 0.5 : creep.store[RESOURCE_ENERGY]) + } else { + let buildTargets = creep.pos.findInRange(FIND_MY_CONSTRUCTION_SITES, 1); + if (buildTargets.length > 0 && creep.build(buildTargets[0]) === OK) { + return + } + } + } + } + + let target = Game.getObjectById(creep.memory.source) + let actionResult = creep.harvest(target) + if (actionResult === ERR_NOT_IN_RANGE) { + creep.moveTo(target, {visualizePathStyle: {stroke: '#ffaa00'}}); + } + if (actionResult !== OK) { + return 'idle' + } + } +}; diff --git a/default/role.scout.js b/default/role.scout.js new file mode 100644 index 0000000..6187c06 --- /dev/null +++ b/default/role.scout.js @@ -0,0 +1,5 @@ +module.exports = { + tick (creep) { + + } +} diff --git a/default/role.transporter.js b/default/role.transporter.js new file mode 100644 index 0000000..a5852a3 --- /dev/null +++ b/default/role.transporter.js @@ -0,0 +1,160 @@ +const common = require('common') + +module.exports = { + tick (creep) { + if (creep.store.getFreeCapacity(RESOURCE_ENERGY) === 0) { + creep.memory.loading = false + } + else if (creep.store[RESOURCE_ENERGY] === 0) { + creep.memory.loading = true + } + + if (creep.store[RESOURCE_ENERGY] >= 50) { + let quickfillTargets = creep.pos.findInRange(FIND_STRUCTURES, 2, { + filter: s => { + return s.store + && s.structureType !== STRUCTURE_CONTAINER + && s.structureType !== STRUCTURE_STORAGE + && s.store.getFreeCapacity(RESOURCE_ENERGY) > 0 + } + }) + if (quickfillTargets.length > 0) { + delete creep.memory.idling + let quickfillTarget = creep.pos.findClosestByRange(quickfillTargets) + if (creep.transfer(quickfillTarget, RESOURCE_ENERGY) === ERR_NOT_IN_RANGE) { + creep.moveTo(quickfillTarget, {visualizePathStyle: {stroke: '#729075'}}); + return + } + } + } + + if (creep.store.getFreeCapacity(RESOURCE_ENERGY) > 0) { + let dropped = creep.pos.findInRange(FIND_DROPPED_RESOURCES, 1); + if (dropped.length > 0) { + creep.pickup(dropped[0]) + } + + let quickloadTargets = creep.pos.findInRange(FIND_STRUCTURES, 2, { + filter: s => { + return s.structureType === STRUCTURE_CONTAINER + && s.store[RESOURCE_ENERGY] > creep.store.getFreeCapacity(RESOURCE_ENERGY) + } + }) + if (quickloadTargets.length > 0) { + delete creep.memory.idling + let quickloadTarget = creep.pos.findClosestByPath(quickloadTargets) + if (creep.withdraw(quickloadTarget, RESOURCE_ENERGY) === ERR_NOT_IN_RANGE) { + creep.moveTo(quickloadTarget, {visualizePathStyle: {stroke: '#729075'}}); + return + } + } + } + + if (creep.memory.loading) { + let target = creep.pos.findClosestByPath(FIND_TOMBSTONES, { + filter: (structure) => structure.store[RESOURCE_ENERGY] > 0 + }) + if (!target) { + target = creep.pos.findClosestByPath(FIND_RUINS, { + filter: (structure) => structure.store[RESOURCE_ENERGY] > 0 + }) + } + if (!target) { + target = creep.pos.findClosestByPath(FIND_STRUCTURES, { + filter: s => { + return s.structureType === STRUCTURE_CONTAINER + && s.store[RESOURCE_ENERGY] > 100 + } + }) + } + if (!target) { + target = creep.pos.findClosestByPath(FIND_STRUCTURES, { + filter: s => { + return s.structureType === STRUCTURE_STORAGE + && s.store[RESOURCE_ENERGY] > 100 + } + }) + } + if (target) { + if (creep.withdraw(target, RESOURCE_ENERGY) === ERR_NOT_IN_RANGE) { + creep.moveTo(target, {visualizePathStyle: {stroke: '#f7e180'}}) + } + delete creep.memory.idling + return + } + if (!target) { + target = creep.pos.findClosestByPath(FIND_MY_CREEPS, { + filter: c => { + return c.memory.role === 'harvester' && c.store[RESOURCE_ENERGY] > 0 + } + }) + if (target) { + if (!creep.pos.inRangeTo(target, 1)) { + creep.moveTo(target, {visualizePathStyle: {stroke: '#f7e180'}}) + } + delete creep.memory.idling + return + } + } + } + else { + let target = _.findKey(Memory.refillers, el => el === creep.name) + if (target) { + target = Game.getObjectById(target) + } + if (!target) { + target = creep.pos.findClosestByPath(FIND_STRUCTURES, { + filter: s => ( + s.structureType === STRUCTURE_EXTENSION + || s.structureType === STRUCTURE_SPAWN + || s.structureType === STRUCTURE_TOWER + ) + && s.store.getFreeCapacity(RESOURCE_ENERGY) > 0 + && !Memory.refillers[s.id] + }) + } + if (!target) { + target = creep.pos.findClosestByPath(FIND_STRUCTURES, { + filter: s => { + return s.structureType === STRUCTURE_LINK && s.store.getFreeCapacity(RESOURCE_ENERGY) > Math.min(creep.store[RESOURCE_ENERGY], 400) + } + }) + } + if (!target) { + target = creep.pos.findClosestByPath(FIND_STRUCTURES, { + filter: s => { + return s.structureType === STRUCTURE_STORAGE + && s.store.getFreeCapacity(RESOURCE_ENERGY) > 0 + } + }) + } + if (target) { + if (target.store.getCapacity(RESOURCE_ENERGY) <= creep.store.getCapacity(RESOURCE_ENERGY)) { + Memory.refillers[target.id] = creep.name + } + if (creep.transfer(target, RESOURCE_ENERGY) === ERR_NOT_IN_RANGE) { + creep.moveTo(target, {visualizePathStyle: {stroke: '#00ff0f'}}); + } + delete creep.memory.idling + return + } + } + if (!creep.memory.idling) { + creep.memory.idling = 0 + } + if (creep.memory.idling > 3) { + if (creep.store[RESOURCE_ENERGY] > 10) { + creep.memory.loading = false + } + else if (creep.store.getFreeCapacity(RESOURCE_ENERGY) > 10) { + creep.memory.loading = true + } + creep.moveTo(creep.pos.findClosestByRange(FIND_FLAGS, {filter: flag => flag.name.startsWith('idle')}), {visualizePathStyle: {stroke: '#ff0000'}}); + } + if (creep.memory.idling) { + creep.say('idle ' + creep.memory.idling) + } + creep.memory.idling++ + return 'idle' + } +}; diff --git a/default/role.upgrader.js b/default/role.upgrader.js new file mode 100644 index 0000000..9239f58 --- /dev/null +++ b/default/role.upgrader.js @@ -0,0 +1,52 @@ +const common = require('common') +let idling = 0 + +module.exports = { + idling, + tick (creep) { + if (creep.store.getFreeCapacity(RESOURCE_ENERGY) > 0) { + let dropped = creep.pos.findInRange(FIND_DROPPED_RESOURCES, 1); + if (dropped.length > 0) { + creep.pickup(dropped[0]) + } + + let target + let targets = creep.pos.findInRange(FIND_TOMBSTONES, 1, { + filter: t => t.store[RESOURCE_ENERGY] > 0 + }) + if (targets.length) { + target = targets[0] + } + if (!target) { + targets = creep.pos.findInRange(FIND_STRUCTURES, 1, { + filter: s => s.store && s.store[RESOURCE_ENERGY] > 0 + }) + if (targets.length) { + target = targets[0] + } + } + if (!target) { + target = creep.room.controller.pos.findClosestByRange(FIND_STRUCTURES, { + filter: s => s.store && s.store.getCapacity(RESOURCE_ENERGY) > 0 + }) + } + if (target) { + if (creep.withdraw(target, RESOURCE_ENERGY) === ERR_NOT_IN_RANGE) { + creep.moveTo(target) + return + } + } + } + + if (creep.pos.getRangeTo(creep.room.controller) > 3) { + creep.moveTo(creep.room.controller, {visualizePathStyle: {stroke: '#ffffff'}}) + return + } + let actionResult = creep.upgradeController(creep.room.controller) + if (actionResult === OK) { + return + } + + return 'idle' + } +}; diff --git a/default/spawn.js b/default/spawn.js new file mode 100644 index 0000000..e54b741 --- /dev/null +++ b/default/spawn.js @@ -0,0 +1,167 @@ +require('globals') + +const EMERGENCY_MODE_TIMEOUT = 100 + +function getHarvestingSpots (source) { + if (source.room.memory.harvestingSpots[source.id]) { + return source.room.memory.harvestingSpots[source.id] + } + let spots = [] + let terrain = source.room.getTerrain() + for (let x = source.pos.x; x <= source.pos.x + 1; x++) { + for (let y = source.pos.y; y <= source.pos.y + 1; y++) { + if (terrain.get(x, y) !== TERRAIN_MASK_WALL) { + spots.push([x, y]) + } + } + } + source.room.memory.harvestingSpots[source.id] = spots + return spots +} + +function getBodyCost(body) { + let cost = 0 + for (let part of body) { + cost = cost + BODYPART_COST[part] + } + return cost +} + +module.exports = { + tickInit () { + for (let room of Object.values(Game.rooms)) { + if (!room.memory.harvestingSpots) { + room.memory.harvestingSpots = {} + } + if (!room.memory.spawnIdle) { + room.memory.spawnIdle = 0 + } + if (!room.memory.energyIncreasedAt) { + room.memory.energyIncreasedAt = Game.time + } + if (!room.memory.lastEnergy) { + room.memory.lastEnergy = 0 + } + + if (!room.memory.sourcesData) { + room.memory.sourcesData = {} + } + for (let source of room.find(FIND_SOURCES)) { + if (!room.memory.sourcesData[source.id]) { + room.memory.sourcesData[source.id] = {spawnDistance: 0} + let spawn = source.pos.findClosestByPath(FIND_MY_SPAWNS) + if (spawn) { + room.memory.sourcesData[source.id].spawnDistance = source.pos.findPathTo(spawn.pos, {ignoreCreeps: true}).length + } + } + } + } + }, + tick (spawn) { + let minRoleCount + if (spawn.spawning) { + spawn.room.memory.spawnIdle = 0 + let spawningCreep = Game.creeps[spawn.spawning.name]; + spawn.room.visual.text( + '🛠️' + spawningCreep.memory.role, + spawn.pos.x + 1, + spawn.pos.y, + {align: 'left', opacity: 0.8}); + } + else { + let newCreep, prio + for (let [roleName, role] of Object.entries(ROLES)) { + let roleCreeps = _.filter(Game.creeps, (creep) => creep.memory.role === roleName); + if (minRoleCount === undefined || minRoleCount > roleCreeps.length) { + minRoleCount = roleCreeps.length + } + if (roleCreeps.length > 0 && spawn.room.memory.emergency && spawn.room.energyAvailable < spawn.room.energyCapacityAvailable) { + continue + } + let newName = roleName + Game.time; + let body = [] + let memory = {role: roleName} + if (roleName === 'harvester') { + for (let source of spawn.room.find(FIND_SOURCES)) { + let spots = getHarvestingSpots(source) + let harvestingCapacity = 0 + let sourceHarvesters = _.filter(Game.creeps, {memory: {role: 'harvester', source: source.id}}) + if (sourceHarvesters.length >= spots.length) { + continue + } + for (let harvester of sourceHarvesters) { + if (harvester.ticksToLive > harvester.body.length * 3 + source.room.memory.sourcesData[source.id].spawnDistance) { + harvestingCapacity += harvester.getActiveBodyparts(WORK) * 2 * 300 + } + } + if (harvestingCapacity <= source.energyCapacity) { + for (let b of ROLES.harvester.bodies) { + if (getBodyCost(b) <= (spawn.room.memory.emergency ? spawn.room.energyAvailable : spawn.room.energyCapacityAvailable)) { + body = b + memory.source = source.id + } else { + break + } + } + } + } + } + else if (roleCreeps.length < role.count && role.idling === 0) { + if (Array.isArray(role.bodies)) { + for (let b of role.bodies) { + if (getBodyCost(b) <= (spawn.room.memory.emergency ? spawn.room.energyAvailable : spawn.room.energyCapacityAvailable)) { + body = b + } else { + break + } + } + } else { + body = role.baseBody + while (getBodyCost(body) + getBodyCost(role.expandBody) <= (spawn.room.memory.emergency ? spawn.room.energyAvailable : spawn.room.energyCapacityAvailable)) { + body = body.concat(role.expandBody) + } + } + } + if (body.length && (!prio || role.prio * (roleCreeps.length + 1) < prio)) { + newCreep = [body, newName, {memory}] + prio = role.prio * (roleCreeps.length + 1) + } + } + if (newCreep) { + spawn.spawnCreep(...newCreep) + spawn.room.memory.spawnIdle++ + spawn.room.visual.text( + `Next: ${newCreep[1]}`, + spawn.pos.x + 1, + spawn.pos.y, + {align: 'left', opacity: 0.8, size: 0.3}); + } + + if (spawn.room.memory.lastEnergy !== spawn.room.energyAvailable) { + spawn.room.memory.energyIncreasedAt = Game.time + spawn.room.memory.lastEnergy = spawn.room.energyAvailable + } + spawn.room.memory.emergency = false + if (minRoleCount === 0) { + let timeToEmergency = Math.max( + EMERGENCY_MODE_TIMEOUT - spawn.room.memory.spawnIdle, + EMERGENCY_MODE_TIMEOUT - Game.time - spawn.room.memory.energyIncreasedAt + ) + if (timeToEmergency <= 0) { + spawn.room.memory.emergency = true + spawn.room.visual.text( + `Prio${prio} emergency spawn ${newCreep[1]} (${newCreep[0]})`, + spawn.pos.x + 1, + spawn.pos.y, + {align: 'left', opacity: 0.8}); + } else if (timeToEmergency <= 50) { + spawn.room.visual.text( + 'Emergency in ' + (EMERGENCY_MODE_TIMEOUT - spawn.room.memory.spawnIdle), + spawn.pos.x + 1, + spawn.pos.y, + {align: 'left', opacity: 0.8}); + } + } + } + }, +}