src/role/guardian.ts
/* global FIND_HOSTILE_CREEPS STRUCTURE_RAMPART */
import container from 'utils/container';
import hivemind from 'hivemind';
import Role from 'role/role';
declare global {
interface GuardianCreep extends Creep {
memory: GuardianCreepMemory;
heapMemory: GuardianCreepHeapMemory;
}
interface GuardianCreepMemory extends CreepMemory {
role: 'guardian';
}
interface GuardianCreepHeapMemory extends CreepHeapMemory {
}
}
const filterEnemyCreeps = (c: Creep) => !hivemind.relations.isAlly(c.owner.username) && c.isDangerous();
export default class GuardianRole extends Role {
constructor() {
super();
// Guardians have high priority because of their importance to room defense.
this.stopAt = 0;
this.throttleAt = 0;
}
/**
* Makes a creep behave like a guardian.
*
* @param {Creep} creep
* The creep to run logic for.
*/
run(creep: GuardianCreep) {
const rampart = this.getBestRampartToCover(creep);
if (!rampart) return;
creep.whenInRange(0, rampart, () => {});
this.setAlternatePositions(creep);
this.attackTargetsInRange(creep);
}
setAlternatePositions(creep: GuardianCreep) {
container.get('TrafficManager').setAlternatePositions(creep, this.getAdjacentRampartPositions(creep));
}
getAdjacentRampartPositions(creep: GuardianCreep): RoomPosition[] {
const closestRamparts = _.filter(
creep.room.myStructuresByType[STRUCTURE_RAMPART],
s => {
if (s.pos.getRangeTo(creep.pos) !== 1) return false;
if (!creep.room.roomPlanner.isPlannedLocation(s.pos, 'rampart')) return false;
if (creep.room.roomPlanner.isPlannedLocation(s.pos, 'rampart.ramp')) return false;
return true;
},
);
return _.map(closestRamparts, s => s.pos);
}
getBestRampartToCover(creep: GuardianCreep): StructureRampart {
// @todo Make sure we can find a safe path to the rampart in question.
const targets = creep.room.find(FIND_HOSTILE_CREEPS, {
filter: filterEnemyCreeps,
});
const ramparts: StructureRampart[] = [];
for (const target of targets) {
const closestRampart = _.min(
_.filter(
creep.room.myStructuresByType[STRUCTURE_RAMPART],
s => {
if (!creep.room.roomPlanner.isPlannedLocation(s.pos, 'rampart')) return false;
if (creep.room.roomPlanner.isPlannedLocation(s.pos, 'rampart.ramp')) return false;
// Only target ramparts not occupied by another creep.
const occupyingCreeps = s.pos.lookFor(LOOK_CREEPS);
if (occupyingCreeps.length > 0 && occupyingCreeps[0].id !== creep.id) return false;
return true;
},
),
s => s.pos.getRangeTo(target.pos),
);
if (!ramparts.includes(closestRampart)) ramparts.push(closestRampart);
}
return _.min(ramparts, (s: StructureRampart) => s.pos.getRangeTo(creep.pos) / 2 + s.pos.getRangeTo(s.pos.findClosestByRange(FIND_HOSTILE_CREEPS, {
filter: filterEnemyCreeps,
})));
}
attackTargetsInRange(creep: GuardianCreep) {
// Ask military manager for best target for joint attacks.
creep.room.assertMilitarySituation();
if (creep.getActiveBodyparts(RANGED_ATTACK) > 0) {
const targets = creep.pos.findInRange(FIND_HOSTILE_CREEPS, 3, {
filter: filterEnemyCreeps,
});
if (targets.length === 0) return;
let target = _.max(targets, 'militaryPriority');
if (!target || (typeof target === 'number')) {
if (targets.length > 2 || _.find(targets, c => c.pos.getRangeTo(creep) === 1)) {
creep.rangedMassAttack();
}
else {
target = _.sample(targets);
}
}
creep.rangedAttack(target);
}
if (creep.getActiveBodyparts(ATTACK) > 0) {
const targets = creep.pos.findInRange(FIND_HOSTILE_CREEPS, 1, {
filter: filterEnemyCreeps,
});
if (targets.length === 0) return;
let target = _.max(targets, 'militaryPriority');
if (!target || (typeof target === 'number')) target = _.sample(targets);
creep.attack(target);
}
}
}