TooAngel/screeps

View on GitHub
src/role_carry.js

Summary

Maintainability
D
2 days
Test Coverage
'use strict';

/*
 * carry gets energy and brings it to the storage
 *
 * Moves to the 'targetId', picks up energy from container or dropped,
 * move back to storage, on meeting other creeps the energy is transferred,
 * energy is transferred to other structures, too.
 */

roles.carry = {};

roles.carry.buildRoad = true;
roles.carry.flee = true;

// roles.carry.boostActions = ['capacity'];

roles.carry.settings = {
  param: ['energyCapacityAvailable'],
  prefixString: {
    300: '',
    550: 'MW',
    600: 'W',
  },
  layoutString: 'CM',
  amount: config.carry.sizes,
  maxLayoutAmount: 1,
};

roles.carry.updateSettings = function(room, creep) {
  if (creep.helper) {
    return {
      prefixString: config.buildRoad.buildToOtherMyRoom ? 'W' : '',
      layoutString: 'MC',
      amount: {
        0: [3, 3], // RCL 1
        550: [4, 4], // RCL 2
        800: [6, 6], // RCL 3
        1300: [11, 11], // RCL 4
        1800: [15, 15], // RCL 5
        2300: [21, 21], // RCL 6
      },
    };
  }
};

/**
 * checkHelperEmptyStorage
 *
 * @param {object} creep
 * @return {void}
 */
function checkHelperEmptyStorage(creep) {
  // Fix blocked helpers due to empty structure in the room where we get the energy from
  if (creep.room.name === creep.memory.routing.targetRoom) {
    const targetStructure = Game.getObjectById(creep.memory.routing.targetId);
    if (targetStructure === null) {
      creep.suicide();
      return;
    }

    if (targetStructure.structureType === STRUCTURE_STORAGE) {
      creep.say('storage');
      if (targetStructure.store.energy === 0) {
        creep.log('Suiciding the storage I should get the energy from is empty');
        creep.suicide();
      }
    }
  }
}

/**
 * checkForUniversalSpawn
 *
 * @param {object} creep
 */
function checkForUniversalSpawn(creep) {
  const storage = creep.room.storage;
  if (!(storage && storage.my && storage.isActive())) {
    let resourceAtPosition = 0;
    const resources = creep.pos.lookFor(LOOK_RESOURCES);
    for (const resource of resources) {
      resourceAtPosition += resource.amount;
    }
    let amount = creep.room.getUniversalAmount();
    amount += Math.floor(resourceAtPosition / config.carry.callUniversalPerResources);
    creep.room.checkRoleToSpawn('universal', amount);
  }
}

roles.carry.dismantleStructure = function(creep, directions) {
  const posForward = creep.pos.getAdjacentPosition(directions.direction);
  const structures = posForward.lookFor(LOOK_STRUCTURES);
  for (const structure of structures) {
    if (structure.structureType === STRUCTURE_ROAD) {
      continue;
    }
    if (structure.structureType === STRUCTURE_CONTAINER) {
      continue;
    }
    if (structure.structureType === STRUCTURE_RAMPART && structure.my) {
      continue;
    }
    if (structure.structureType === STRUCTURE_SPAWN && structure.my) {
      continue;
    }
    if (structure.structureType === STRUCTURE_STORAGE && structure.my) {
      continue;
    }
    if (structure.structureType === STRUCTURE_LINK && structure.my) {
      continue;
    }

    creep.dismantle(structure);
    creep.say('dismantle');
    break;
  }
};

const validateDirections = function(creep, directions) {
  // TODO When does this happen? (Not on path?) - Handle better
  if (!directions || Object.keys(directions).length === 0) {
    if (creep.memory.routing.pathPos < 0) {
      return false;
    }
    creep.log(`role_carry.preMove - No directions, why? pathPos: ${creep.memory.routing.pathPos}`);
    creep.say('No directions');
    return false;
  }
  return true;
};


const checkCreepForTransfer = function(otherCreep, thisCreep) {
  if (!otherCreep.my) {
    return false;
  }
  // Don't transfer to carries if they didn't reached the end once
  if (otherCreep.memory.role === 'carry' && !otherCreep.data.fullyDeployed) {
    return false;
  }

  // Only transfer to creeps with the same base
  if (otherCreep.memory.base !== thisCreep.memory.base) {
    return false;
  }

  // don't transfer to extractor, fixes full terminal with 80% energy?
  if (otherCreep.memory.role === 'extractor') {
    return false;
  }
  // don't transfer to mineral, fixes full terminal with 80% energy?
  if (otherCreep.memory.role === 'mineral') {
    return false;
  }
  if (!thisCreep.store[RESOURCE_ENERGY] && otherCreep.memory.role === 'sourcer') {
    return false;
  }
  // Do we want this?
  if (otherCreep.memory.role === 'powertransporter') {
    return false;
  }
  if (otherCreep.store.getFreeCapacity() === 0) {
    return false;
  }
  return true;
};

/**
 * transferToCreep
 *
 * @param {object} creep
 * @param {object} direction
 * @return {bool}
 */
function transferToCreep(creep, direction) {
  const adjacentPos = creep.pos.getAdjacentPosition(direction);
  if (!adjacentPos.isValid()) {
    return false;
  }

  const creeps = adjacentPos.lookFor('creep');
  for (const otherCreep of creeps) {
    if (!checkCreepForTransfer(otherCreep, creep)) {
      continue;
    }
    for (const resource of Object.keys(creep.store)) {
      const returnCode = creep.transfer(otherCreep, resource);
      if (returnCode === OK) {
        return creep.store.getUsedCapacity() * 0.5 <= otherCreep.store.getCapacity() - otherCreep.store.getUsedCapacity();
      }
    }
  }
  return false;
}

/**
 * transferToCreeps
 *
 * @param {object} creep
 * @param {object} directions
 */
function transferToCreeps(creep, directions) {
  creep.creepLog('Trying to transfer to creep');
  const transferred = transferToCreep(creep, directions.backwardDirection);
  creep.memory.routing.reverse = !transferred;
}

/**
 * getMoveToStorage
 *
 * @param {object} creep
 * @return {bool}
 */
function getMoveToStorage(creep) {
  let moveToStorage = creep.checkCarryEnergyForBringingBackToStorage();
  const fleeFromSourceKeeper = creep.checkForSourceKeeper();
  moveToStorage = moveToStorage || fleeFromSourceKeeper;
  return moveToStorage;
}

/**
 * findCreepWhichCanTransfer
 *
 * @param {object} creep
 * @param {object} adjacentPos
 * @return {bool}
 */
function findCreepWhichCanTransfer(creep, adjacentPos) {
  const creeps = adjacentPos.lookFor(LOOK_CREEPS);
  for (const otherCreep of creeps) {
    if (!Game.creeps[otherCreep.name] || otherCreep.store.getUsedCapacity() < 50 || otherCreep.memory.recycle) {
      continue;
    }

    if (otherCreep.memory.role === 'carry') {
      if (creep.memory.base !== otherCreep.memory.base) {
        continue;
      }
      if (otherCreep.memory.routing.pathPos < 0) {
        continue;
      }
      return creep.checkCarryEnergyForBringingBackToStorage(otherCreep);
    }
    continue;
  }
  return false;
}

/**
 * checkForTransfer
 *
 * @param {object} creep
 * @param {object} direction
 * @return {bool}
 */
function checkForTransfer(creep, direction) {
  if (!creep.data.fullyDeployed) {
    return false;
  }
  if (!direction) {
    creep.creepLog(`checkForTransfer no direction}`);
    return false;
  }

  const adjacentPos = creep.pos.getAdjacentPosition(direction);

  if (adjacentPos.isBorder(-2)) {
    creep.creepLog(`checkForTransfer isBorder}`);
    return false;
  }

  return findCreepWhichCanTransfer(creep, adjacentPos);
}

/**
 * preMoveNotMoveToStorage
 *
 * @param {object} creep
 * @param {object} directions
 * @return {bool}
 */
function preMoveNotMoveToStorage(creep, directions) {
  creep.creepLog(`preMove not moveToStorage`);
  let moveToStorage = false;
  if (creep.memory.routing.pathPos > 1) {
    // TODO these two methods seems pretty similar, should be unified
    moveToStorage = creep.pickupEnergy();
    moveToStorage = moveToStorage || creep.pickupWhileMoving();
  }
  const energyFromCreep = checkForTransfer(creep, directions.forwardDirection);
  moveToStorage = moveToStorage || energyFromCreep;
  return moveToStorage;
}

/**
 * handlePathPos0
 *
 * @param {object} creep
 * @return {boolean}
 */
function handlePathPos0(creep) {
  creep.drop(RESOURCE_ENERGY);
  checkForUniversalSpawn(creep);
  creep.memory.routing.reverse = false;
  return false;
}

roles.carry.preMove = function(creep, directions) {
  if (!validateDirections(creep, directions)) {
    return false;
  }

  checkHelperEmptyStorage(creep);

  let moveToStorage = getMoveToStorage(creep);
  if (moveToStorage) {
    if (creep.inBase()) {
      const transferred = creep.transferToStructures();
      if (transferred) {
        const energyLeft = creep.carry.energy - transferred.transferred;
        if (energyLeft > 0) { // Better use `checkCarryEnergyForBringingBackToStorage` to be accurate
          if (transferred.moreStructures) {
            return true;
          }
        } else {
          creep.memory.routing.reverse = false;
          return false;
        }
      }

      if (creep.memory.routing.pathPos === 0) {
        return handlePathPos0(creep);
      }
    }

    if (directions.backwardDirection && directions.backwardDirection !== null) {
      transferToCreeps(creep, directions);
      return false;
    }
  } else {
    moveToStorage = preMoveNotMoveToStorage(creep, directions);
  }

  moveToStorage = moveToStorage && creep.memory.routing.pathPos !== 0;

  creep.memory.routing.reverse = moveToStorage;
  if (moveToStorage) {
    directions.direction = directions.backwardDirection;
    creep.data.fullyDeployed = true;
  } else {
    directions.direction = directions.forwardDirection;
  }
  if (!directions.direction) {
    return false;
  }

  roles.carry.dismantleStructure(creep, directions);
  return false;
};

/**
 * handleSourceKeeperRoom
 *
 * @param {object} creep
 */
function handleSourceKeeperRoom(creep) {
  const target = creep.findClosestSourceKeeper();
  if (target !== null) {
    const range = creep.pos.getRangeTo(target);
    if (range < 5) {
      delete creep.memory.routing.reached;
      creep.memory.routing.reverse = true;
    }
  }
}

roles.carry.action = function(creep) {
  // TODO log when this happens, carry is getting energy from the source
  creep.creepLog('ACTION');
  const source = Game.getObjectById(creep.memory.routing.targetId);
  if (source === null) {
    creep.creepLog('sfener');
    creep.memory.routing.reached = false;
    creep.memory.routing.reverse = true;

    const sources = creep.pos.findInRange(FIND_SOURCES, 3);
    if (sources.length > 0) {
      creep.memory.routing.targetId = sources[0].id;
      return true;
    }

    const resource = creep.pos.findClosestByRange(FIND_DROPPED_RESOURCES);
    if (resource !== null) {
      creep.memory.routing.targetId = resource.id;
      // TODO Use pathfinder
      creep.moveTo(resource);
      creep.pickup(resource, resource.amount - 1);
      return true;
    }
  }
  // TODO this should be last position => reverse - In preMove make sure a reverse stays if it is set here
  const reverse = creep.pickupWhileMoving();

  if (!reverse) {
    creep.harvest(source);
  }

  if (!creep.room.controller) {
    handleSourceKeeperRoom(creep);
  }

  creep.memory.routing.reached = false;
  creep.memory.routing.reverse = true;

  // End of path, can't harvest, suicide (otherwise the sourcer gets stuck)
  if (!reverse && creep.body.filter((part) => part.type === WORK).length === 0) {
    // creep.log('Suiciding because end of path, no energy, do not want to get in the way of the sourcer (better recycle?)');
    creep.memory.killed = true;
    creep.suicide();
  }

  return true;
};