mirror of
https://github.com/paradizelost/screeps.git
synced 2024-11-25 02:14:44 -06:00
584 lines
22 KiB
JavaScript
584 lines
22 KiB
JavaScript
|
/**
|
||
|
* To start using Traveler, require it in main.js:
|
||
|
* Example: var Traveler = require('Traveler.js');
|
||
|
*/
|
||
|
"use strict";
|
||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||
|
class Traveler {
|
||
|
/**
|
||
|
* move creep to destination
|
||
|
* @param creep
|
||
|
* @param destination
|
||
|
* @param options
|
||
|
* @returns {number}
|
||
|
*/
|
||
|
static travelTo(creep, destination, options = {}) {
|
||
|
// uncomment if you would like to register hostile rooms entered
|
||
|
// this.updateRoomStatus(creep.room);
|
||
|
if (!destination) {
|
||
|
return ERR_INVALID_ARGS;
|
||
|
}
|
||
|
if (creep.fatigue > 0) {
|
||
|
Traveler.circle(creep.pos, "aqua", .3);
|
||
|
return ERR_BUSY;
|
||
|
}
|
||
|
destination = this.normalizePos(destination);
|
||
|
// manage case where creep is nearby destination
|
||
|
let rangeToDestination = creep.pos.getRangeTo(destination);
|
||
|
if (options.range && rangeToDestination <= options.range) {
|
||
|
return OK;
|
||
|
}
|
||
|
else if (rangeToDestination <= 1) {
|
||
|
if (rangeToDestination === 1 && !options.range) {
|
||
|
let direction = creep.pos.getDirectionTo(destination);
|
||
|
if (options.returnData) {
|
||
|
options.returnData.nextPos = destination;
|
||
|
options.returnData.path = direction.toString();
|
||
|
}
|
||
|
return creep.move(direction);
|
||
|
}
|
||
|
return OK;
|
||
|
}
|
||
|
// initialize data object
|
||
|
if (!creep.memory._trav) {
|
||
|
delete creep.memory._travel;
|
||
|
creep.memory._trav = {};
|
||
|
}
|
||
|
let travelData = creep.memory._trav;
|
||
|
let state = this.deserializeState(travelData, destination);
|
||
|
// uncomment to visualize destination
|
||
|
// this.circle(destination.pos, "orange");
|
||
|
// check if creep is stuck
|
||
|
if (this.isStuck(creep, state)) {
|
||
|
state.stuckCount++;
|
||
|
Traveler.circle(creep.pos, "magenta", state.stuckCount * .2);
|
||
|
}
|
||
|
else {
|
||
|
state.stuckCount = 0;
|
||
|
}
|
||
|
// handle case where creep is stuck
|
||
|
if (!options.stuckValue) {
|
||
|
options.stuckValue = DEFAULT_STUCK_VALUE;
|
||
|
}
|
||
|
if (state.stuckCount >= options.stuckValue && Math.random() > .5) {
|
||
|
options.ignoreCreeps = false;
|
||
|
options.freshMatrix = true;
|
||
|
delete travelData.path;
|
||
|
}
|
||
|
// TODO:handle case where creep moved by some other function, but destination is still the same
|
||
|
// delete path cache if destination is different
|
||
|
if (!this.samePos(state.destination, destination)) {
|
||
|
if (options.movingTarget && state.destination.isNearTo(destination)) {
|
||
|
travelData.path += state.destination.getDirectionTo(destination);
|
||
|
state.destination = destination;
|
||
|
}
|
||
|
else {
|
||
|
delete travelData.path;
|
||
|
}
|
||
|
}
|
||
|
if (options.repath && Math.random() < options.repath) {
|
||
|
// add some chance that you will find a new path randomly
|
||
|
delete travelData.path;
|
||
|
}
|
||
|
// pathfinding
|
||
|
let newPath = false;
|
||
|
if (!travelData.path) {
|
||
|
newPath = true;
|
||
|
if (creep.spawning) {
|
||
|
return ERR_BUSY;
|
||
|
}
|
||
|
state.destination = destination;
|
||
|
let cpu = Game.cpu.getUsed();
|
||
|
let ret = this.findTravelPath(creep.pos, destination, options);
|
||
|
let cpuUsed = Game.cpu.getUsed() - cpu;
|
||
|
state.cpu = _.round(cpuUsed + state.cpu);
|
||
|
if (state.cpu > REPORT_CPU_THRESHOLD) {
|
||
|
// see note at end of file for more info on this
|
||
|
console.log(`TRAVELER: heavy cpu use: ${creep.name}, cpu: ${state.cpu} origin: ${creep.pos}, dest: ${destination}`);
|
||
|
}
|
||
|
let color = "orange";
|
||
|
if (ret.incomplete) {
|
||
|
// uncommenting this is a great way to diagnose creep behavior issues
|
||
|
// console.log(`TRAVELER: incomplete path for ${creep.name}`);
|
||
|
color = "red";
|
||
|
}
|
||
|
if (options.returnData) {
|
||
|
options.returnData.pathfinderReturn = ret;
|
||
|
}
|
||
|
travelData.path = Traveler.serializePath(creep.pos, ret.path, color);
|
||
|
state.stuckCount = 0;
|
||
|
}
|
||
|
this.serializeState(creep, destination, state, travelData);
|
||
|
if (!travelData.path || travelData.path.length === 0) {
|
||
|
return ERR_NO_PATH;
|
||
|
}
|
||
|
// consume path
|
||
|
if (state.stuckCount === 0 && !newPath) {
|
||
|
travelData.path = travelData.path.substr(1);
|
||
|
}
|
||
|
let nextDirection = parseInt(travelData.path[0], 10);
|
||
|
if (options.returnData) {
|
||
|
if (nextDirection) {
|
||
|
let nextPos = Traveler.positionAtDirection(creep.pos, nextDirection);
|
||
|
if (nextPos) {
|
||
|
options.returnData.nextPos = nextPos;
|
||
|
}
|
||
|
}
|
||
|
options.returnData.state = state;
|
||
|
options.returnData.path = travelData.path;
|
||
|
}
|
||
|
return creep.move(nextDirection);
|
||
|
}
|
||
|
/**
|
||
|
* make position objects consistent so that either can be used as an argument
|
||
|
* @param destination
|
||
|
* @returns {any}
|
||
|
*/
|
||
|
static normalizePos(destination) {
|
||
|
if (!(destination instanceof RoomPosition)) {
|
||
|
return destination.pos;
|
||
|
}
|
||
|
return destination;
|
||
|
}
|
||
|
/**
|
||
|
* check if room should be avoided by findRoute algorithm
|
||
|
* @param roomName
|
||
|
* @returns {RoomMemory|number}
|
||
|
*/
|
||
|
static checkAvoid(roomName) {
|
||
|
return Memory.rooms && Memory.rooms[roomName] && Memory.rooms[roomName].avoid;
|
||
|
}
|
||
|
/**
|
||
|
* check if a position is an exit
|
||
|
* @param pos
|
||
|
* @returns {boolean}
|
||
|
*/
|
||
|
static isExit(pos) {
|
||
|
return pos.x === 0 || pos.y === 0 || pos.x === 49 || pos.y === 49;
|
||
|
}
|
||
|
/**
|
||
|
* check two coordinates match
|
||
|
* @param pos1
|
||
|
* @param pos2
|
||
|
* @returns {boolean}
|
||
|
*/
|
||
|
static sameCoord(pos1, pos2) {
|
||
|
return pos1.x === pos2.x && pos1.y === pos2.y;
|
||
|
}
|
||
|
/**
|
||
|
* check if two positions match
|
||
|
* @param pos1
|
||
|
* @param pos2
|
||
|
* @returns {boolean}
|
||
|
*/
|
||
|
static samePos(pos1, pos2) {
|
||
|
return this.sameCoord(pos1, pos2) && pos1.roomName === pos2.roomName;
|
||
|
}
|
||
|
/**
|
||
|
* draw a circle at position
|
||
|
* @param pos
|
||
|
* @param color
|
||
|
* @param opacity
|
||
|
*/
|
||
|
static circle(pos, color, opacity) {
|
||
|
new RoomVisual(pos.roomName).circle(pos, {
|
||
|
radius: .45, fill: "transparent", stroke: color, strokeWidth: .15, opacity: opacity
|
||
|
});
|
||
|
}
|
||
|
/**
|
||
|
* update memory on whether a room should be avoided based on controller owner
|
||
|
* @param room
|
||
|
*/
|
||
|
static updateRoomStatus(room) {
|
||
|
if (!room) {
|
||
|
return;
|
||
|
}
|
||
|
if (room.controller) {
|
||
|
if (room.controller.owner && !room.controller.my) {
|
||
|
room.memory.avoid = 1;
|
||
|
}
|
||
|
else {
|
||
|
delete room.memory.avoid;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
/**
|
||
|
* find a path from origin to destination
|
||
|
* @param origin
|
||
|
* @param destination
|
||
|
* @param options
|
||
|
* @returns {PathfinderReturn}
|
||
|
*/
|
||
|
static findTravelPath(origin, destination, options = {}) {
|
||
|
_.defaults(options, {
|
||
|
ignoreCreeps: true,
|
||
|
maxOps: DEFAULT_MAXOPS,
|
||
|
range: 1,
|
||
|
});
|
||
|
if (options.movingTarget) {
|
||
|
options.range = 0;
|
||
|
}
|
||
|
origin = this.normalizePos(origin);
|
||
|
destination = this.normalizePos(destination);
|
||
|
let originRoomName = origin.roomName;
|
||
|
let destRoomName = destination.roomName;
|
||
|
// check to see whether findRoute should be used
|
||
|
let roomDistance = Game.map.getRoomLinearDistance(origin.roomName, destination.roomName);
|
||
|
let allowedRooms = options.route;
|
||
|
if (!allowedRooms && (options.useFindRoute || (options.useFindRoute === undefined && roomDistance > 2))) {
|
||
|
let route = this.findRoute(origin.roomName, destination.roomName, options);
|
||
|
if (route) {
|
||
|
allowedRooms = route;
|
||
|
}
|
||
|
}
|
||
|
let roomsSearched = 0;
|
||
|
let callback = (roomName) => {
|
||
|
if (allowedRooms) {
|
||
|
if (!allowedRooms[roomName]) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
else if (!options.allowHostile && Traveler.checkAvoid(roomName)
|
||
|
&& roomName !== destRoomName && roomName !== originRoomName) {
|
||
|
return false;
|
||
|
}
|
||
|
roomsSearched++;
|
||
|
let matrix;
|
||
|
let room = Game.rooms[roomName];
|
||
|
if (room) {
|
||
|
if (options.ignoreStructures) {
|
||
|
matrix = new PathFinder.CostMatrix();
|
||
|
if (!options.ignoreCreeps) {
|
||
|
Traveler.addCreepsToMatrix(room, matrix);
|
||
|
}
|
||
|
}
|
||
|
else if (options.ignoreCreeps || roomName !== originRoomName) {
|
||
|
matrix = this.getStructureMatrix(room, options.freshMatrix);
|
||
|
}
|
||
|
else {
|
||
|
matrix = this.getCreepMatrix(room);
|
||
|
}
|
||
|
if (options.obstacles) {
|
||
|
matrix = matrix.clone();
|
||
|
for (let obstacle of options.obstacles) {
|
||
|
if (obstacle.pos.roomName !== roomName) {
|
||
|
continue;
|
||
|
}
|
||
|
matrix.set(obstacle.pos.x, obstacle.pos.y, 0xff);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (options.roomCallback) {
|
||
|
if (!matrix) {
|
||
|
matrix = new PathFinder.CostMatrix();
|
||
|
}
|
||
|
let outcome = options.roomCallback(roomName, matrix.clone());
|
||
|
if (outcome !== undefined) {
|
||
|
return outcome;
|
||
|
}
|
||
|
}
|
||
|
return matrix;
|
||
|
};
|
||
|
let ret = PathFinder.search(origin, { pos: destination, range: options.range }, {
|
||
|
maxOps: options.maxOps,
|
||
|
maxRooms: options.maxRooms,
|
||
|
plainCost: options.offRoad ? 1 : options.ignoreRoads ? 1 : 2,
|
||
|
swampCost: options.offRoad ? 1 : options.ignoreRoads ? 5 : 10,
|
||
|
roomCallback: callback,
|
||
|
});
|
||
|
if (ret.incomplete && options.ensurePath) {
|
||
|
if (options.useFindRoute === undefined) {
|
||
|
// handle case where pathfinder failed at a short distance due to not using findRoute
|
||
|
// can happen for situations where the creep would have to take an uncommonly indirect path
|
||
|
// options.allowedRooms and options.routeCallback can also be used to handle this situation
|
||
|
if (roomDistance <= 2) {
|
||
|
console.log(`TRAVELER: path failed without findroute, trying with options.useFindRoute = true`);
|
||
|
console.log(`from: ${origin}, destination: ${destination}`);
|
||
|
options.useFindRoute = true;
|
||
|
ret = this.findTravelPath(origin, destination, options);
|
||
|
console.log(`TRAVELER: second attempt was ${ret.incomplete ? "not " : ""}successful`);
|
||
|
return ret;
|
||
|
}
|
||
|
// TODO: handle case where a wall or some other obstacle is blocking the exit assumed by findRoute
|
||
|
}
|
||
|
else {
|
||
|
}
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
/**
|
||
|
* find a viable sequence of rooms that can be used to narrow down pathfinder's search algorithm
|
||
|
* @param origin
|
||
|
* @param destination
|
||
|
* @param options
|
||
|
* @returns {{}}
|
||
|
*/
|
||
|
static findRoute(origin, destination, options = {}) {
|
||
|
let restrictDistance = options.restrictDistance || Game.map.getRoomLinearDistance(origin, destination) + 10;
|
||
|
let allowedRooms = { [origin]: true, [destination]: true };
|
||
|
let highwayBias = 1;
|
||
|
if (options.preferHighway) {
|
||
|
highwayBias = 2.5;
|
||
|
if (options.highwayBias) {
|
||
|
highwayBias = options.highwayBias;
|
||
|
}
|
||
|
}
|
||
|
let ret = Game.map.findRoute(origin, destination, {
|
||
|
routeCallback: (roomName) => {
|
||
|
if (options.routeCallback) {
|
||
|
let outcome = options.routeCallback(roomName);
|
||
|
if (outcome !== undefined) {
|
||
|
return outcome;
|
||
|
}
|
||
|
}
|
||
|
let rangeToRoom = Game.map.getRoomLinearDistance(origin, roomName);
|
||
|
if (rangeToRoom > restrictDistance) {
|
||
|
// room is too far out of the way
|
||
|
return Number.POSITIVE_INFINITY;
|
||
|
}
|
||
|
if (!options.allowHostile && Traveler.checkAvoid(roomName) &&
|
||
|
roomName !== destination && roomName !== origin) {
|
||
|
// room is marked as "avoid" in room memory
|
||
|
return Number.POSITIVE_INFINITY;
|
||
|
}
|
||
|
let parsed;
|
||
|
if (options.preferHighway) {
|
||
|
parsed = /^[WE]([0-9]+)[NS]([0-9]+)$/.exec(roomName);
|
||
|
let isHighway = (parsed[1] % 10 === 0) || (parsed[2] % 10 === 0);
|
||
|
if (isHighway) {
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
// SK rooms are avoided when there is no vision in the room, harvested-from SK rooms are allowed
|
||
|
if (!options.allowSK && !Game.rooms[roomName]) {
|
||
|
if (!parsed) {
|
||
|
parsed = /^[WE]([0-9]+)[NS]([0-9]+)$/.exec(roomName);
|
||
|
}
|
||
|
let fMod = parsed[1] % 10;
|
||
|
let sMod = parsed[2] % 10;
|
||
|
let isSK = !(fMod === 5 && sMod === 5) &&
|
||
|
((fMod >= 4) && (fMod <= 6)) &&
|
||
|
((sMod >= 4) && (sMod <= 6));
|
||
|
if (isSK) {
|
||
|
return 10 * highwayBias;
|
||
|
}
|
||
|
}
|
||
|
return highwayBias;
|
||
|
},
|
||
|
});
|
||
|
if (!_.isArray(ret)) {
|
||
|
console.log(`couldn't findRoute to ${destination}`);
|
||
|
return;
|
||
|
}
|
||
|
for (let value of ret) {
|
||
|
allowedRooms[value.room] = true;
|
||
|
}
|
||
|
return allowedRooms;
|
||
|
}
|
||
|
/**
|
||
|
* check how many rooms were included in a route returned by findRoute
|
||
|
* @param origin
|
||
|
* @param destination
|
||
|
* @returns {number}
|
||
|
*/
|
||
|
static routeDistance(origin, destination) {
|
||
|
let linearDistance = Game.map.getRoomLinearDistance(origin, destination);
|
||
|
if (linearDistance >= 32) {
|
||
|
return linearDistance;
|
||
|
}
|
||
|
let allowedRooms = this.findRoute(origin, destination);
|
||
|
if (allowedRooms) {
|
||
|
return Object.keys(allowedRooms).length;
|
||
|
}
|
||
|
}
|
||
|
/**
|
||
|
* build a cost matrix based on structures in the room. Will be cached for more than one tick. Requires vision.
|
||
|
* @param room
|
||
|
* @param freshMatrix
|
||
|
* @returns {any}
|
||
|
*/
|
||
|
static getStructureMatrix(room, freshMatrix) {
|
||
|
if (!this.structureMatrixCache[room.name] || (freshMatrix && Game.time !== this.structureMatrixTick)) {
|
||
|
this.structureMatrixTick = Game.time;
|
||
|
let matrix = new PathFinder.CostMatrix();
|
||
|
this.structureMatrixCache[room.name] = Traveler.addStructuresToMatrix(room, matrix, 1);
|
||
|
}
|
||
|
return this.structureMatrixCache[room.name];
|
||
|
}
|
||
|
/**
|
||
|
* build a cost matrix based on creeps and structures in the room. Will be cached for one tick. Requires vision.
|
||
|
* @param room
|
||
|
* @returns {any}
|
||
|
*/
|
||
|
static getCreepMatrix(room) {
|
||
|
if (!this.creepMatrixCache[room.name] || Game.time !== this.creepMatrixTick) {
|
||
|
this.creepMatrixTick = Game.time;
|
||
|
this.creepMatrixCache[room.name] = Traveler.addCreepsToMatrix(room, this.getStructureMatrix(room, true).clone());
|
||
|
}
|
||
|
return this.creepMatrixCache[room.name];
|
||
|
}
|
||
|
/**
|
||
|
* add structures to matrix so that impassible structures can be avoided and roads given a lower cost
|
||
|
* @param room
|
||
|
* @param matrix
|
||
|
* @param roadCost
|
||
|
* @returns {CostMatrix}
|
||
|
*/
|
||
|
static addStructuresToMatrix(room, matrix, roadCost) {
|
||
|
let impassibleStructures = [];
|
||
|
for (let structure of room.find(FIND_STRUCTURES)) {
|
||
|
if (structure instanceof StructureRampart) {
|
||
|
if (!structure.my && !structure.isPublic) {
|
||
|
impassibleStructures.push(structure);
|
||
|
}
|
||
|
}
|
||
|
else if (structure instanceof StructureRoad) {
|
||
|
matrix.set(structure.pos.x, structure.pos.y, roadCost);
|
||
|
}
|
||
|
else if (structure instanceof StructureContainer) {
|
||
|
matrix.set(structure.pos.x, structure.pos.y, 5);
|
||
|
}
|
||
|
else {
|
||
|
impassibleStructures.push(structure);
|
||
|
}
|
||
|
}
|
||
|
for (let site of room.find(FIND_MY_CONSTRUCTION_SITES)) {
|
||
|
if (site.structureType === STRUCTURE_CONTAINER || site.structureType === STRUCTURE_ROAD
|
||
|
|| site.structureType === STRUCTURE_RAMPART) {
|
||
|
continue;
|
||
|
}
|
||
|
matrix.set(site.pos.x, site.pos.y, 0xff);
|
||
|
}
|
||
|
for (let structure of impassibleStructures) {
|
||
|
matrix.set(structure.pos.x, structure.pos.y, 0xff);
|
||
|
}
|
||
|
return matrix;
|
||
|
}
|
||
|
/**
|
||
|
* add creeps to matrix so that they will be avoided by other creeps
|
||
|
* @param room
|
||
|
* @param matrix
|
||
|
* @returns {CostMatrix}
|
||
|
*/
|
||
|
static addCreepsToMatrix(room, matrix) {
|
||
|
room.find(FIND_CREEPS).forEach((creep) => matrix.set(creep.pos.x, creep.pos.y, 0xff));
|
||
|
return matrix;
|
||
|
}
|
||
|
/**
|
||
|
* serialize a path, traveler style. Returns a string of directions.
|
||
|
* @param startPos
|
||
|
* @param path
|
||
|
* @param color
|
||
|
* @returns {string}
|
||
|
*/
|
||
|
static serializePath(startPos, path, color = "orange") {
|
||
|
let serializedPath = "";
|
||
|
let lastPosition = startPos;
|
||
|
this.circle(startPos, color);
|
||
|
for (let position of path) {
|
||
|
if (position.roomName === lastPosition.roomName) {
|
||
|
new RoomVisual(position.roomName)
|
||
|
.line(position, lastPosition, { color: color, lineStyle: "dashed" });
|
||
|
serializedPath += lastPosition.getDirectionTo(position);
|
||
|
}
|
||
|
lastPosition = position;
|
||
|
}
|
||
|
return serializedPath;
|
||
|
}
|
||
|
/**
|
||
|
* returns a position at a direction relative to origin
|
||
|
* @param origin
|
||
|
* @param direction
|
||
|
* @returns {RoomPosition}
|
||
|
*/
|
||
|
static positionAtDirection(origin, direction) {
|
||
|
let offsetX = [0, 0, 1, 1, 1, 0, -1, -1, -1];
|
||
|
let offsetY = [0, -1, -1, 0, 1, 1, 1, 0, -1];
|
||
|
let x = origin.x + offsetX[direction];
|
||
|
let y = origin.y + offsetY[direction];
|
||
|
if (x > 49 || x < 0 || y > 49 || y < 0) {
|
||
|
return;
|
||
|
}
|
||
|
return new RoomPosition(x, y, origin.roomName);
|
||
|
}
|
||
|
/**
|
||
|
* convert room avoidance memory from the old pattern to the one currently used
|
||
|
* @param cleanup
|
||
|
*/
|
||
|
static patchMemory(cleanup = false) {
|
||
|
if (!Memory.empire) {
|
||
|
return;
|
||
|
}
|
||
|
if (!Memory.empire.hostileRooms) {
|
||
|
return;
|
||
|
}
|
||
|
let count = 0;
|
||
|
for (let roomName in Memory.empire.hostileRooms) {
|
||
|
if (Memory.empire.hostileRooms[roomName]) {
|
||
|
if (!Memory.rooms[roomName]) {
|
||
|
Memory.rooms[roomName] = {};
|
||
|
}
|
||
|
Memory.rooms[roomName].avoid = 1;
|
||
|
count++;
|
||
|
}
|
||
|
if (cleanup) {
|
||
|
delete Memory.empire.hostileRooms[roomName];
|
||
|
}
|
||
|
}
|
||
|
if (cleanup) {
|
||
|
delete Memory.empire.hostileRooms;
|
||
|
}
|
||
|
console.log(`TRAVELER: room avoidance data patched for ${count} rooms`);
|
||
|
}
|
||
|
static deserializeState(travelData, destination) {
|
||
|
let state = {};
|
||
|
if (travelData.state) {
|
||
|
state.lastCoord = { x: travelData.state[STATE_PREV_X], y: travelData.state[STATE_PREV_Y] };
|
||
|
state.cpu = travelData.state[STATE_CPU];
|
||
|
state.stuckCount = travelData.state[STATE_STUCK];
|
||
|
state.destination = new RoomPosition(travelData.state[STATE_DEST_X], travelData.state[STATE_DEST_Y], travelData.state[STATE_DEST_ROOMNAME]);
|
||
|
}
|
||
|
else {
|
||
|
state.cpu = 0;
|
||
|
state.destination = destination;
|
||
|
}
|
||
|
return state;
|
||
|
}
|
||
|
static serializeState(creep, destination, state, travelData) {
|
||
|
travelData.state = [creep.pos.x, creep.pos.y, state.stuckCount, state.cpu, destination.x, destination.y,
|
||
|
destination.roomName];
|
||
|
}
|
||
|
static isStuck(creep, state) {
|
||
|
let stuck = false;
|
||
|
if (state.lastCoord !== undefined) {
|
||
|
if (this.sameCoord(creep.pos, state.lastCoord)) {
|
||
|
// didn't move
|
||
|
stuck = true;
|
||
|
}
|
||
|
else if (this.isExit(creep.pos) && this.isExit(state.lastCoord)) {
|
||
|
// moved against exit
|
||
|
stuck = true;
|
||
|
}
|
||
|
}
|
||
|
return stuck;
|
||
|
}
|
||
|
}
|
||
|
Traveler.structureMatrixCache = {};
|
||
|
Traveler.creepMatrixCache = {};
|
||
|
exports.Traveler = Traveler;
|
||
|
// this might be higher than you wish, setting it lower is a great way to diagnose creep behavior issues. When creeps
|
||
|
// need to repath to often or they aren't finding valid paths, it can sometimes point to problems elsewhere in your code
|
||
|
const REPORT_CPU_THRESHOLD = 1000;
|
||
|
const DEFAULT_MAXOPS = 20000;
|
||
|
const DEFAULT_STUCK_VALUE = 2;
|
||
|
const STATE_PREV_X = 0;
|
||
|
const STATE_PREV_Y = 1;
|
||
|
const STATE_STUCK = 2;
|
||
|
const STATE_CPU = 3;
|
||
|
const STATE_DEST_X = 4;
|
||
|
const STATE_DEST_Y = 5;
|
||
|
const STATE_DEST_ROOMNAME = 6;
|
||
|
// assigns a function to Creep.prototype: creep.travelTo(destination)
|
||
|
Creep.prototype.travelTo = function (destination, options) {
|
||
|
return Traveler.travelTo(this, destination, options);
|
||
|
};
|