|
|
|
|
require('globals')
|
|
|
|
|
|
|
|
|
|
const EMERGENCY_MODE_TIMEOUT = 100
|
|
|
|
|
|
|
|
|
|
function getHarvestingSpots (source) {
|
|
|
|
|
let spots = []
|
|
|
|
|
let terrain = source.room.getTerrain()
|
|
|
|
|
for (let x = source.pos.x - 1; x <= source.pos.x + 1; x++) {
|
|
|
|
|
for (let y = source.pos.y - 1; 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)) {
|
|
|
|
|
let spawns = room.find(FIND_MY_SPAWNS)
|
|
|
|
|
room.memory.spawingRoom = spawns.length > 0
|
|
|
|
|
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.energyStructuresPriority || Game.time % 100 === 0) {
|
|
|
|
|
room.memory.energyStructuresPriority = _.sortBy(
|
|
|
|
|
room.find(
|
|
|
|
|
FIND_MY_STRUCTURES,
|
|
|
|
|
{filter: s => s.structureType === STRUCTURE_EXTENSION || s.structureType === STRUCTURE_SPAWN}
|
|
|
|
|
).map(el => {return {structure: el, range: el.pos.getRangeTo(room.storage)}}),
|
|
|
|
|
'range'
|
|
|
|
|
).map(el => el.structure.id)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!room.memory.sourcesData) {
|
|
|
|
|
room.memory.sourcesData = {}
|
|
|
|
|
}
|
|
|
|
|
for (let source of room.find(FIND_SOURCES)) {
|
|
|
|
|
if (!room.memory.harvestingSpots[source.id] || Game.time % 10 === 0) {
|
|
|
|
|
room.memory.harvestingSpots[source.id] = getHarvestingSpots(source)
|
|
|
|
|
}
|
|
|
|
|
if (!room.memory.sourcesData[source.id] || Game.time % 10 === 0) {
|
|
|
|
|
room.memory.sourcesData[source.id] = {spawnDistance: 0, energyCapacity: source.energyCapacity}
|
|
|
|
|
let spawn = source.pos.findClosestByPath(FIND_MY_SPAWNS)
|
|
|
|
|
if (spawn) {
|
|
|
|
|
room.memory.sourcesData[source.id].spawnDistance = source.pos.findPathTo(spawn.pos, {ignoreCreeps: true}).length
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (spawns.length > 0 && (Game.time + 5) % 100 === 0 && room.find(FIND_MY_CONSTRUCTION_SITES).length === 0) {
|
|
|
|
|
let spawn = spawns[0]
|
|
|
|
|
let extensions = room.find(FIND_MY_STRUCTURES, {filter: s => s.structureType === STRUCTURE_EXTENSION})
|
|
|
|
|
if (CONTROLLER_STRUCTURES.extension[room.controller.level] > extensions.length) {
|
|
|
|
|
let ring = 0
|
|
|
|
|
let x = spawn.pos.x
|
|
|
|
|
let y = spawn.pos.y
|
|
|
|
|
while (ring < 10 && room.createConstructionSite(x, y, STRUCTURE_EXTENSION) === ERR_INVALID_TARGET) {
|
|
|
|
|
x += 2
|
|
|
|
|
if (x > spawn.pos.x + ring) {
|
|
|
|
|
x = spawn.pos.x - ring
|
|
|
|
|
y += 2
|
|
|
|
|
}
|
|
|
|
|
if (y > spawn.pos.y + ring) {
|
|
|
|
|
ring++
|
|
|
|
|
x = spawn.pos.x - ring
|
|
|
|
|
y = spawn.pos.y - ring
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
tick (spawn) {
|
|
|
|
|
let minRoleCount
|
|
|
|
|
if (spawn.spawning) {
|
|
|
|
|
spawn.room.memory.spawnIdle = 0
|
|
|
|
|
let spawningCreep = Game.creeps[spawn.spawning.name];
|
|
|
|
|
let room = spawningCreep.memory.room || ''
|
|
|
|
|
spawn.room.visual.text(
|
|
|
|
|
'🛠️' + spawningCreep.memory.role + ' ' + room,
|
|
|
|
|
spawn.pos.x + 1,
|
|
|
|
|
spawn.pos.y,
|
|
|
|
|
{align: 'left', opacity: 0.8});
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
let newCreep, prio
|
|
|
|
|
for (let [roleName, role] of Object.entries(ROLES)) {
|
|
|
|
|
if (role.minRcl > spawn.room.controller.level) {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
let roleCreeps = _.filter(Game.creeps, {memory: {role: roleName, colony: spawn.room.name}});
|
|
|
|
|
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, colony: spawn.room.name}
|
|
|
|
|
if (role.nextSpawn) {
|
|
|
|
|
let nextSpawn = role.nextSpawn(spawn, roleCreeps)
|
|
|
|
|
if (nextSpawn) {
|
|
|
|
|
[body, memory] = nextSpawn
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (roleName === 'harvester') {
|
|
|
|
|
for (let source of spawn.room.find(FIND_SOURCES)) {
|
|
|
|
|
let spots = spawn.room.memory.harvestingSpots[source.id]
|
|
|
|
|
let harvestingCapacity = 0
|
|
|
|
|
let sourceHarvesters = _.filter(roleCreeps, {memory: {source: source.id}})
|
|
|
|
|
if (sourceHarvesters.length >= spots.length) {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
for (let harvester of sourceHarvesters) {
|
|
|
|
|
if (!harvester.ticksToLive || harvester.ticksToLive > harvester.body.length * 3 + source.room.memory.sourcesData[source.id].spawnDistance) {
|
|
|
|
|
harvestingCapacity += _.filter(harvester.body, {type: WORK}).length * 2 * 300
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (harvestingCapacity < source.energyCapacity) {
|
|
|
|
|
for (let b of role.bodies) {
|
|
|
|
|
if (getBodyCost(b) <= (spawn.room.memory.emergency ? spawn.room.energyAvailable : spawn.room.energyCapacityAvailable)) {
|
|
|
|
|
body = b
|
|
|
|
|
memory.source = source.id
|
|
|
|
|
} else {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (roleName === 'remoteHarvester') {
|
|
|
|
|
for (let roomName of activeRemotes) {
|
|
|
|
|
let roomMemory = Memory.rooms[roomName]
|
|
|
|
|
if (roomMemory) {
|
|
|
|
|
for (let [sourceId, sourceData] of Object.entries(roomMemory.sourcesData)) {
|
|
|
|
|
let spots = roomMemory.harvestingSpots[sourceId]
|
|
|
|
|
let harvestingCapacity = 0
|
|
|
|
|
let sourceHarvesters = _.filter(Game.creeps, {memory: {role: 'remoteHarvester', source: sourceId}})
|
|
|
|
|
if (sourceHarvesters.length >= spots.length) {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
for (let harvester of sourceHarvesters) {
|
|
|
|
|
if (!harvester.ticksToLive || harvester.ticksToLive > harvester.body.length * 3 + sourceData.spawnDistance) {
|
|
|
|
|
harvestingCapacity += harvester.getActiveBodyparts(WORK) * 2 * 300
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (harvestingCapacity < sourceData.energyCapacity) {
|
|
|
|
|
for (let b of role.bodies) {
|
|
|
|
|
if (getBodyCost(b) <= spawn.room.energyCapacityAvailable) {
|
|
|
|
|
body = b
|
|
|
|
|
memory.source = sourceId
|
|
|
|
|
memory.room = roomName
|
|
|
|
|
if (_.filter(b, WORK).length * 2 * 300 > sourceData.energyCapacity) {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (roleCreeps.length < (role.getCount ? role.getCount(spawn.room) : 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 >= role.baseBody.length + role.expandBody.length * role.maxExpands) {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
memory.role = roleName
|
|
|
|
|
memory.colony = spawn.room.name
|
|
|
|
|
if (body.length && (!prio || role.prio * (roleCreeps.length + 1) < prio)) {
|
|
|
|
|
newCreep = [body, newName, {memory}]
|
|
|
|
|
prio = role.prio * (roleCreeps.length + 1)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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 (newCreep) {
|
|
|
|
|
newCreep[2].energyStructures = spawn.room.memory.energyStructuresPriority.map(el => Game.getObjectById(el))
|
|
|
|
|
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 (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 ' + timeToEmergency,
|
|
|
|
|
spawn.pos.x + 1,
|
|
|
|
|
spawn.pos.y,
|
|
|
|
|
{align: 'left', opacity: 0.8});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}/* else {
|
|
|
|
|
let renewableCreeps = spawn.pos.findInRange(FIND_MY_CREEPS, 1, {filter: c => c.ticksToLive < 1200})
|
|
|
|
|
if (renewableCreeps.length > 0) {
|
|
|
|
|
spawn.renewCreep(renewableCreeps[0])
|
|
|
|
|
}
|
|
|
|
|
}*/
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
}
|