src/prototype/creep.train.ts
/* global Creep */
declare global {
interface Creep {
isPartOfTrain: () => boolean;
isTrainHead: () => boolean;
getTrainId: () => Id<Creep>;
isTrainFullySpawned: () => boolean;
getTrainParts: () => Creep[];
isTrainJoined: () => boolean;
joinTrain: () => void;
}
interface CreepMemory {
train?: {
id: Id<Creep>;
partsToSpawn: number[];
parts: Array<Id<Creep>>;
};
}
}
/**
* Determines if a creep is part of a creep train.
*
* @return {boolean}
* True if the creep is part of a train.
*/
Creep.prototype.isPartOfTrain = function (this: Creep): boolean {
return Boolean(this.memory.train);
};
Creep.prototype.isTrainHead = function (this: Creep): boolean {
// @todo What if the head dies?
return this.getTrainId() === this.id;
};
Creep.prototype.getTrainId = function (this: Creep): Id<Creep> {
return this.memory.train.id || this.id;
};
Creep.prototype.isTrainFullySpawned = function (this: Creep): boolean {
const trainId = this.getTrainId();
const headSegment = Game.getObjectById<Creep>(trainId);
if (headSegment && _.size(headSegment.memory.train.partsToSpawn) > 0) return false;
return true;
};
Creep.prototype.getTrainParts = function (this: Creep): Creep[] {
// @todo Guaruantee stable order of creeps.
const trainId = this.getTrainId();
let segments: Creep[];
if (this.memory.train.parts) {
segments = _.filter(_.map<string, Creep>(this.memory.train.parts, Game.getObjectById));
if (segments.length < this.memory.train.parts.length) {
this.memory.train.parts = _.map(segments, 'id');
}
}
else {
// @todo Order these creeps consistently. We save info redundantly
// for when individual segments die.
segments = _.values(_.filter(Game.creeps, creep => creep.isPartOfTrain() && creep.getTrainId() === trainId));
this.memory.train.parts = _.map(segments, 'id');
}
return segments;
};
Creep.prototype.isTrainJoined = function (this: Creep): boolean {
const segments = this.getTrainParts();
for (let i = 1; i < segments.length; i++) {
// We don't care if a train is disjooined across rooms.
if (segments[i].pos.roomName !== segments[i - 1].pos.roomName) continue;
// Parts need to be adjacent otherwise.
if (segments[i].pos.getRangeTo(segments[i - 1].pos) > 1) return false;
}
return true;
};
Creep.prototype.joinTrain = function (this: Creep) {
const segments = this.getTrainParts();
let canMoveBack = true;
for (let i = 1; i < segments.length; i++) {
let moveForward = false;
let moveBack = false;
if (segments[i].pos.roomName === segments[i - 1].pos.roomName) {
const distance = segments[i].pos.getRangeTo(segments[i - 1].pos);
if (distance > 1) moveForward = true;
if (distance > 2 && canMoveBack) moveBack = true;
}
else {
moveForward = true;
}
if (moveForward) {
// Move toward previous segment.
// @todo Instead of moving to the previous part's current position,
// move to their intended position for next tick for quicker joining.
segments[i].moveToRange(segments[i - 1].pos, 1);
canMoveBack = false;
}
if (moveBack) {
// Move this and all previous segments toward next segment.
segments[i - 1].moveToRange(segments[i].pos, 1);
for (let j = i - 2; j >= 0; j--) {
segments[j].move(segments[j].pos.getDirectionTo(segments[j + 1].pos));
}
canMoveBack = false;
}
}
};
export {};