Mirroar/hivemind

View on GitHub
src/dispatcher/resource-source/grave.ts

Summary

Maintainability
D
1 day
Test Coverage
import TaskProvider from "dispatcher/task-provider";
import { ENEMY_STRENGTH_NONE } from "room-defense";
import { getDangerMatrix } from "utils/cost-matrix";
import { getResourcesIn } from "utils/store";

interface GraveSourceTask extends ResourceSourceTask {
    type: 'grave';
    target: Id<Tombstone | Ruin>;
}

export default class GraveSource extends TaskProvider<GraveSourceTask, ResourceSourceContext> {
    constructor(readonly room: Room) {
        super();
    }

    getType(): 'grave' {
        return 'grave';
    }

    getHighestPriority() {
        return 5;
    }

    getTasks(context: ResourceSourceContext) {
        const options: GraveSourceTask[] = [];

        this.addGraveResourceOptions(options, context);

        return options;
    }

    private addGraveResourceOptions(options: GraveSourceTask[], context: ResourceSourceContext) {
        const creep = context.creep;

        // Look for tombstones and ruins with resources.
        const targets = (this.room.find(FIND_TOMBSTONES) as Array<Tombstone | Ruin>)
            .concat(this.room.find(FIND_RUINS))
            .filter(target => {
                return target.store.getUsedCapacity() > 10;
            });

        for (const target of targets) {
            // @todo It might be more effective to only create one task per tombstone / ruin and pick up all resources at once.
            for (const resourceType of getResourcesIn(target.store)) {
                if (context.resourceType && resourceType !== context.resourceType) continue;

                const amount = target.store.getUsedCapacity(resourceType);
                const option: GraveSourceTask = {
                    priority: 4,
                    weight: amount / (resourceType === RESOURCE_ENERGY ? 100 : 30), // @todo Also factor in distance.
                    type: this.getType(),
                    target: target.id,
                    resourceType: resourceType,
                };

                if (resourceType === RESOURCE_POWER) {
                    option.priority++;
                }
                else if (resourceType === RESOURCE_ENERGY) {
                    if (amount < 100) option.priority--;
                    if (amount < 200) option.priority--;
    
                    // If spawn / extensions need filling, transporters should not pick up
                    // energy from random targets as readily, instead prioritize storage.
                    if (creep.room.energyAvailable < creep.room.energyCapacityAvailable && creep.room.getCurrentResourceAmount(RESOURCE_ENERGY) > 5000 && creep.memory.role === 'transporter') option.priority -= 2;

                    if (creep.room.storage && creep.room.getFreeStorage() < amount && creep.room.getEffectiveAvailableEnergy() > 20_000) {
                        // If storage is super full, try leaving stuff on the ground.
                        option.priority -= 2;
                    }
                }

                if (creep.room.getFreeStorage() < target.store.getUsedCapacity(resourceType)) {
                    // If storage is super full, try leaving stuff on the ground.
                    continue;
                }

                if (target.store.getUsedCapacity() < creep.store.getCapacity() * 2) {
                    // We only need to limit the number of creeps picking up resources if the amount is small.
                    option.priority -= this.room.getCreepsWithOrder('grave', target.id).length * 2;
                }

                options.push(option);
            }
        }
    }

    isValid(task: GraveSourceTask, context: ResourceSourceContext): boolean {
        const tombstone = Game.getObjectById(task.target);
        if (!tombstone) return false;
        if (tombstone.store.getUsedCapacity(task.resourceType) === 0) return false;
        if (context.creep.store.getFreeCapacity(task.resourceType) === 0) return false;
        if (!this.isSafePosition(context.creep, tombstone.pos)) return false;

        const terminal = this.room.terminal;
        const terminalNeedsSpaceForEnergy = terminal && (terminal.store.getFreeCapacity() + terminal.store.getUsedCapacity(RESOURCE_ENERGY)) < 5000;
        if (task.resourceType !== RESOURCE_ENERGY && terminalNeedsSpaceForEnergy) return false;

        return true;
    }

    isSafePosition(creep: Creep | PowerCreep, pos: RoomPosition): boolean {
        if (!creep.room.isMine()) return true;
        if (creep.room.defense.getEnemyStrength() === ENEMY_STRENGTH_NONE) return true;

        const matrix = getDangerMatrix(creep.room.name);
        if (matrix.get(pos.x, pos.y) > 0) return false;

        return true;
    }

    execute(task: GraveSourceTask, context: ResourceSourceContext): void {
        const creep = context.creep;
        const target = Game.getObjectById(task.target);

        creep.whenInRange(1, target, () => {
            creep.withdraw(target, task.resourceType);
        });
    }
}