Mirroar/hivemind

View on GitHub
src/prototype/creep.train.ts

Summary

Maintainability
A
2 hrs
Test Coverage
/* 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 {};