Mirroar/hivemind

View on GitHub
src/manager.source.ts

Summary

Maintainability
D
3 days
Test Coverage
/* global Source Mineral StructureKeeperLair LOOK_TERRAIN
FIND_STRUCTURES STRUCTURE_CONTAINER STRUCTURE_LINK STRUCTURE_KEEPER_LAIR */

import cache from 'utils/cache';

declare global {
    interface Source {
        harvesters: HarvesterCreep[];
        getNumHarvestSpots: () => number;
        getNearbyContainer: () => StructureContainer | null;
        getNearbyLink: () => StructureLink | null;
        getNearbyLair: () => StructureKeeperLair | null;
    }

    interface Mineral {
        harvesters: HarvesterCreep[];
        getNumHarvestSpots: () => number;
        getNearbyContainer: () => StructureContainer | null;
        getNearbyLair: () => StructureKeeperLair | null;
    }
}

// Define quick access property source.harvesters.
Object.defineProperty(Source.prototype, 'harvesters', {
    /**
     * Gets a source's assigned harvesters.
     *
     * @return {Creep[]}
     *   Harvesters for this source.
     */
    get(this: Source) {
        return cache.inObject(this, 'harvesters', 1, () => {
            const harvesters = [];
            for (const harvester of _.values<HarvesterCreep>(this.room.creepsByRole.harvester) || []) {
                if (harvester.memory.fixedSource === this.id) {
                    harvesters.push(harvester);
                }
            }

            return harvesters;
        });
    },
    enumerable: false,
    configurable: true,
});

// Define quick access property mineral.harvesters.
Object.defineProperty(Mineral.prototype, 'harvesters', {
    /**
     * Gets a mineral's assigned harvesters.
     *
     * @return {Creep[]}
     *   Harvesters for this mineral.
     */
    get(this: Mineral) {
        return cache.inObject(this, 'harvesters', 1, () => {
            const harvesters = [];
            for (const harvester of _.values<HarvesterCreep>(this.room.creepsByRole.harvester) || []) {
                if (harvester.memory.fixedMineralSource === this.id) {
                    harvesters.push(harvester);
                }
            }

            return harvesters;
        });
    },
    enumerable: false,
    configurable: true,
});

/**
 * Calculates and caches the number of walkable tiles around a source.
 *
 * @return {number}
 *   Maximum number of harvesters on this source.
 */
const getNumberHarvestSpots = function (this: Source | Mineral) {
    return cache.inHeap('numFreeSquares:' + this.id, 5000, () => {
        const terrain = this.room.lookForAtArea(LOOK_TERRAIN, this.pos.y - 1, this.pos.x - 1, this.pos.y + 1, this.pos.x + 1, true);
        const adjacentTerrain = [];
        for (const tile of terrain) {
            if (tile.x === this.pos.x && tile.y === this.pos.y) continue;
            if (tile.terrain === 'plain' || tile.terrain === 'swamp') {
                // @todo Make sure no structures are blocking this tile.
                adjacentTerrain.push(tile);
            }
        }

        return adjacentTerrain.length;
    });
};

/**
 * Calculates and caches the number of walkable tiles around a source.
 *
 * @return {number}
 *   Maximum number of harvesters on this source.
 */
Source.prototype.getNumHarvestSpots = function (this: Source) {
    return getNumberHarvestSpots.call(this);
};

/**
 * Calculates and caches the number of walkable tiles around a source.
 *
 * @return {number}
 *   Maximum number of harvesters on this mineral.
 */
Mineral.prototype.getNumHarvestSpots = function (this: Mineral) {
    return getNumberHarvestSpots.call(this);
};

/**
 * Finds a container in close proximity to this source, for dropping off energy.
 *
 * @return {StructureContainer}
 *   A container close to this source.
 */
const getNearbyContainer = function (this: Source | Mineral) {
    const containerId = cache.inHeap('container:' + this.id, 150, () => {
        // @todo Could use old data and just check if object still exits.
        // Check if there is a container nearby.
        const structures = this.pos.findInRange(FIND_STRUCTURES, 3, {
            filter: structure => structure.structureType === STRUCTURE_CONTAINER,
        });
        if (structures.length > 0) {
            const structure = this.pos.findClosestByRange(structures);
            return structure.id;
        }

        return null;
    });

    if (containerId) {
        return Game.getObjectById<StructureContainer>(containerId);
    }

    return null;
};

/**
 * Finds a container in close proximity to this source, for dropping off energy.
 *
 * @return {StructureContainer}
 *   A container close to this source.
 */
Source.prototype.getNearbyContainer = function (this: Source) {
    return getNearbyContainer.call(this);
};

/**
 * Finds a container in close proximity to this mineral, for dropping off resources.
 *
 * @return {StructureContainer}
 *   A container close to this mineral.
 */
Mineral.prototype.getNearbyContainer = function (this: Mineral) {
    return getNearbyContainer.call(this);
};

/**
 * Finds a link in close proximity to this source, for dropping off energy.
 *
 * @return {StructureLink}
 *   A link close to this source.
 */
Source.prototype.getNearbyLink = function (this: Source) {
    const linkId = cache.inHeap('link:' + this.id, 1000, () => {
        // @todo Could use old data and just check if object still exits.
        // Check if there is a link nearby.
        const structures = this.pos.findInRange(FIND_STRUCTURES, 3, {
            filter: structure => structure.structureType === STRUCTURE_LINK,
        });
        if (structures.length > 0) {
            const structure = this.pos.findClosestByRange(structures);
            return structure.id;
        }

        return null;
    });

    if (linkId) {
        return Game.getObjectById<StructureLink>(linkId);
    }

    return null;
};

/**
 * Finds a source keeper lair in close proximity to this source.
 *
 * @return {StructureKeeperLair}
 *   The lair protecting this source.
 */
const getNearbyLair = function (this: Source | Mineral) {
    const lairId = cache.inHeap('lair:' + this.id, 150_000, () => {
        // @todo Could use old data and just check if object still exits.
        // Check if there is a lair nearby.
        const structures = this.pos.findInRange(FIND_STRUCTURES, 10, {
            filter: structure => structure.structureType === STRUCTURE_KEEPER_LAIR,
        });
        if (structures.length > 0) {
            const structure = this.pos.findClosestByRange(structures);
            return structure.id;
        }

        return null;
    });

    if (lairId) {
        return Game.getObjectById(lairId);
    }

    return null;
};

/**
 * Finds a source keeper lair in close proximity to this source.
 *
 * @return {StructureKeeperLair}
 *   The lair protecting this source.
 */
Source.prototype.getNearbyLair = function (this: Source) {
    return getNearbyLair.call(this);
};

/**
 * Finds a source keeper lair in close proximity to this mineral.
 *
 * @return {StructureKeeperLair}
 *   The lair protecting this mineral.
 */
Mineral.prototype.getNearbyLair = function (this: Mineral) {
    return getNearbyLair.call(this);
};