@screeps/engine
Version:
This is a module for Screeps standalone server. See [main repository](https://github.com/screeps/screeps) for more info.
705 lines (613 loc) • 22.2 kB
JavaScript
;
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
var _ = require('lodash');
var driver,
C,
offsetsByDirection = [, [0, -1], [1, -1], [1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [-1, -1]];
function loadDriver() {
C = driver.constants;
}
try {
driver = require('~runtime-driver');
loadDriver();
} catch (e) {}
exports.getDriver = function getDriver() {
driver = typeof process != 'undefined' && process.env.DRIVER_MODULE ? require(process.env.DRIVER_MODULE) : require('@screeps/core');
loadDriver();
return driver;
};
exports.getRuntimeDriver = function getRuntimeDriver() {
try {
driver = require('~runtime-driver');
loadDriver();
return driver;
} catch (e) {
return exports.getDriver();
}
};
exports.fetchXYArguments = function (firstArg, secondArg, globals) {
var x, y, roomName;
if (_.isUndefined(secondArg) || !_.isNumber(secondArg)) {
if (!_.isObject(firstArg)) {
return [undefined, undefined, undefined];
}
if (firstArg instanceof globals.RoomPosition) {
x = firstArg.x;
y = firstArg.y;
roomName = firstArg.roomName;
}
if (firstArg.pos && firstArg.pos instanceof globals.RoomPosition) {
x = firstArg.pos.x;
y = firstArg.pos.y;
roomName = firstArg.pos.roomName;
}
} else {
x = firstArg;
y = secondArg;
}
if (_.isNaN(x)) {
x = undefined;
}
if (_.isNaN(y)) {
y = undefined;
}
return [x, y, roomName];
};
exports.getDirection = function (dx, dy) {
var adx = Math.abs(dx),
ady = Math.abs(dy);
if (adx > ady * 2) {
if (dx > 0) {
return C.RIGHT;
} else {
return C.LEFT;
}
} else if (ady > adx * 2) {
if (dy > 0) {
return C.BOTTOM;
} else {
return C.TOP;
}
} else {
if (dx > 0 && dy > 0) {
return C.BOTTOM_RIGHT;
}
if (dx > 0 && dy < 0) {
return C.TOP_RIGHT;
}
if (dx < 0 && dy > 0) {
return C.BOTTOM_LEFT;
}
if (dx < 0 && dy < 0) {
return C.TOP_LEFT;
}
}
};
exports.getOffsetsByDirection = function (direction) {
if (!offsetsByDirection[direction]) {
try {
throw new Error();
} catch (e) {
console.error('Wrong move direction', JSON.stringify(direction), JSON.stringify(offsetsByDirection), e.stack);
}
}
return offsetsByDirection[direction];
};
exports.calcCreepCost = function (body) {
let result = 0;
body.forEach(i => {
const partType = _.isObject(i) ? i.type : i;
if (!C.BODYPART_COST[partType]) {
throw new Error(`Invalid body part ${partType}`);
}
result += C.BODYPART_COST[partType];
});
return result;
};
exports.checkConstructionSite = function (objects, structureType, x, y) {
var borderTiles;
if (structureType != 'road' && structureType != 'container' && (x == 1 || x == 48 || y == 1 || y == 48)) {
if (x == 1) borderTiles = [[0, y - 1], [0, y], [0, y + 1]];
if (x == 48) borderTiles = [[49, y - 1], [49, y], [49, y + 1]];
if (y == 1) borderTiles = [[x - 1, 0], [x, 0], [x + 1, 0]];
if (y == 48) borderTiles = [[x - 1, 49], [x, 49], [x + 1, 49]];
}
if (_.isString(objects) || objects instanceof Uint8Array) {
if (borderTiles) {
for (var i in borderTiles) {
if (!exports.checkTerrain(objects, borderTiles[i][0], borderTiles[i][1], C.TERRAIN_MASK_WALL)) {
return false;
}
}
}
if (structureType == 'extractor') {
return true;
}
if (structureType != 'road' && exports.checkTerrain(objects, x, y, C.TERRAIN_MASK_WALL)) {
return false;
}
return true;
}
if (objects && _.isArray(objects[0]) && _.isString(objects[0][0])) {
if (borderTiles) {
for (var i in borderTiles) {
if (!(objects[borderTiles[i][1]][borderTiles[i][0]] & C.TERRAIN_MASK_WALL)) {
return false;
}
}
}
if (structureType == 'extractor') {
return true;
}
if (structureType != 'road' && objects[y][x] & C.TERRAIN_MASK_WALL) {
return false;
}
return true;
}
if (_.any(objects, { x, y, type: structureType })) {
return false;
}
if (_.any(objects, { x, y, type: 'constructionSite' })) {
return false;
}
if (structureType == 'extractor') {
return _.any(objects, { x, y, type: 'mineral' }) && !_.any(objects, { x, y, type: 'extractor' });
}
if (structureType != 'rampart' && structureType != 'road' && _.any(objects, i => i.x == x && i.y == y && i.type != 'rampart' && i.type != 'road' && C.CONSTRUCTION_COST[i.type])) {
return false;
}
if (x <= 0 || y <= 0 || x >= 49 || y >= 49) {
return false;
}
return true;
};
exports.getDiff = function (oldData, newData) {
function getIndex(data) {
var index = {};
_.forEach(data, obj => index[obj._id] = obj);
return index;
}
var result = {},
oldIndex = getIndex(oldData),
newIndex = getIndex(newData);
_.forEach(oldData, obj => {
if (newIndex[obj._id]) {
var newObj = newIndex[obj._id];
var objDiff = result[obj._id] = {};
for (var key in obj) {
if (key == '_id') {
continue;
}
if (_.isUndefined(newObj[key])) {
objDiff[key] = null;
} else if (typeof obj[key] != typeof newObj[key] || obj[key] && !newObj[key]) {
objDiff[key] = newObj[key];
} else if (_.isObject(obj[key])) {
objDiff[key] = {};
for (var subkey in obj[key]) {
if (!_.isEqual(obj[key][subkey], newObj[key][subkey])) {
objDiff[key][subkey] = newObj[key][subkey];
}
}
for (var subkey in newObj[key]) {
if (_.isUndefined(obj[key][subkey])) {
objDiff[key][subkey] = newObj[key][subkey];
}
}
if (!_.size(objDiff[key])) {
delete result[obj._id][key];
}
} else if (!_.isEqual(obj[key], newObj[key])) {
objDiff[key] = newObj[key];
}
}
for (var key in newObj) {
if (_.isUndefined(obj[key])) {
objDiff[key] = newObj[key];
}
}
if (!_.size(objDiff)) {
delete result[obj._id];
}
} else {
result[obj._id] = null;
}
});
_.forEach(newData, obj => {
if (!oldIndex[obj._id]) {
result[obj._id] = obj;
}
});
return result;
};
exports.encodeTerrain = function (terrain) {
var result = '';
for (var y = 0; y < 50; y++) {
for (var x = 0; x < 50; x++) {
var objects = _.filter(terrain, { x, y }),
code = 0;
if (_.any(objects, { type: 'wall' })) {
code = code | C.TERRAIN_MASK_WALL;
}
if (_.any(objects, { type: 'swamp' })) {
code = code | C.TERRAIN_MASK_SWAMP;
}
result = result + code;
}
}
return result;
};
exports.decodeTerrain = function (items) {
var result = [];
for (var i in items) {
if (items[i].type != 'terrain') {
continue;
}
for (var y = 0; y < 50; y++) {
for (var x = 0; x < 50; x++) {
var code = items[i].terrain.charAt(y * 50 + x);
if (code & C.TERRAIN_MASK_WALL) {
result.push({ room: items[i].room, x, y, type: 'wall' });
}
if (code & C.TERRAIN_MASK_SWAMP) {
result.push({ room: items[i].room, x, y, type: 'swamp' });
}
}
}
}
return result;
};
exports.decodeTerrainByRoom = function (items) {
var result = {
spatial: {}
};
for (var i in items) {
if (items[i].type != 'terrain') {
continue;
}
result[items[i].room] = result[items[i].room] || [];
result.spatial[items[i].room] = new Array(50);
for (var y = 0; y < 50; y++) {
result.spatial[items[i].room][y] = new Array(50);
for (var x = 0; x < 50; x++) {
var code = items[i].terrain.charAt(y * 50 + x);
/*if (code & C.TERRAIN_MASK_WALL) {
result[items[i].room].push({x, y, type: 'wall'});
}
if (code & C.TERRAIN_MASK_SWAMP) {
result[items[i].room].push({x, y, type: 'swamp'});
}*/
result.spatial[items[i].room][y][x] = code;
}
}
}
return result;
};
exports.checkTerrain = function (terrain, x, y, mask) {
var code = terrain instanceof Uint8Array ? terrain[y * 50 + x] : Number(terrain.charAt(y * 50 + x));
return (code & mask) > 0;
};
exports.checkControllerAvailability = function (type, roomObjects, roomController, offset) {
var rcl = 0;
if (_.isObject(roomController) && roomController.level && (roomController.user || roomController.owner)) {
rcl = roomController.level;
}
if (_.isNumber(roomController)) {
rcl = roomController;
}
offset = offset || 0;
var structuresCnt = _(roomObjects).filter(i => i.type == type || i.type == 'constructionSite' && i.structureType == type).size();
var availableCnt = C.CONTROLLER_STRUCTURES[type][rcl] + offset;
return structuresCnt < availableCnt;
};
// Note that game/rooms.js will swap this function out for a faster version, but may call back to
// this function.
exports.getRoomNameFromXY = function (x, y) {
if (x < 0) {
x = 'W' + (-x - 1);
} else {
x = 'E' + x;
}
if (y < 0) {
y = 'N' + (-y - 1);
} else {
y = 'S' + y;
}
return "" + x + y;
};
exports.roomNameToXY = function (name) {
let xx = parseInt(name.substr(1), 10);
let verticalPos = 2;
if (xx >= 100) {
verticalPos = 4;
} else if (xx >= 10) {
verticalPos = 3;
}
let yy = parseInt(name.substr(verticalPos + 1), 10);
let horizontalDir = name.charAt(0);
let verticalDir = name.charAt(verticalPos);
if (horizontalDir === 'W' || horizontalDir === 'w') {
xx = -xx - 1;
}
if (verticalDir === 'N' || verticalDir === 'n') {
yy = -yy - 1;
}
return [xx, yy];
};
exports.comparatorDistance = function (target) {
if (target.pos) target = target.pos;
return function (a, b) {
if (a.pos) a = a.pos;
if (b.pos) b = b.pos;
var da = Math.max(Math.abs(a.x - target.x), Math.abs(a.y - target.y));
var db = Math.max(Math.abs(b.x - target.x), Math.abs(b.y - target.y));
return da - db;
};
};
exports.storeIntents = function (userId, userIntents, userRuntimeData, customIntentTypes) {
const system = driver ? driver.system : exports.getDriver().system;
var intents = {};
for (var i in userIntents) {
if (i == 'notify') {
intents.notify = system.sanitizeUserIntents({ notify: userIntents.notify }).notify;
continue;
}
if (i == 'room') {
system.sanitizeUserRoomIntents(userIntents.room, intents, customIntentTypes);
continue;
}
if (i == 'global') {
intents.global = system.sanitizeUserIntents(userIntents.global, customIntentTypes);
continue;
}
const object = userRuntimeData.userObjects[i] || userRuntimeData.roomObjects[i];
if (!object) {
continue;
}
intents[object.room] = intents[object.room] || {};
intents[object.room][i] = system.sanitizeUserIntents(userIntents[i], customIntentTypes);
}
return intents;
};
exports.sendAttackingNotification = function (target, roomController) {
var driver = exports.getDriver();
var labelText;
if (target.type == 'creep') {
labelText = 'creep ' + target.name;
} else if (target.type == 'spawn') {
labelText = 'spawn ' + target.name;
} else {
labelText = `${target.type} #${target._id}`;
}
var user = target.user ? target.user : roomController ? roomController.user : null;
if (user) {
driver.sendNotification(user, `Your ${labelText} in room ${target.room} is under attack!`);
}
};
exports.checkStructureAgainstController = function (object, roomObjects, roomController) {
// owner-less objects are always active
if (!object.user) {
return true;
}
// eliminate some other easy cases
if (!roomController || roomController.level < 1 || roomController.user !== object.user) {
return false;
}
let allowedRemaining = C.CONTROLLER_STRUCTURES[object.type][roomController.level];
if (allowedRemaining === 0) {
return false;
}
// if only one object ever allowed, this is it
if (C.CONTROLLER_STRUCTURES[object.type][8] === 1) {
return allowedRemaining !== 0;
}
// Scan through the room objects of the same type and count how many are closer.
let foundSelf = false;
let objectDist = Math.max(Math.abs(object.x - roomController.x), Math.abs(object.y - roomController.y));
let objectIds = _.keys(roomObjects);
for (let i = 0; i < objectIds.length; i++) {
let compareObj = roomObjects[objectIds[i]];
if (compareObj.type === object.type && compareObj.user === object.user) {
let compareDist = Math.max(Math.abs(compareObj.x - roomController.x), Math.abs(compareObj.y - roomController.y));
if (compareDist < objectDist) {
allowedRemaining--;
if (allowedRemaining === 0) {
return false;
}
} else if (!foundSelf && compareDist === objectDist) {
// Objects of equal distance that are discovered before we scan over the selected object are considered closer
if (object === compareObj) {
foundSelf = true;
} else {
allowedRemaining--;
if (allowedRemaining === 0) {
return false;
}
}
}
}
}
return true;
};
exports.defineGameObjectProperties = function (obj, dataFn, properties, opts) {
var propertiesInfo = {};
opts = opts || {};
if (opts.enumerable === undefined) {
opts.enumerable = true;
}
for (var name in properties) {
eval(`
propertiesInfo['${name}'] = {
configurable: !!opts.configurable,
enumerable: !!opts.enumerable,
get() {
if(!this['_${name}']) {
this['_${name}'] = properties['${name}'](dataFn(this.id), this.id);
}
return this['_${name}'];
},
set: opts.canSet ? function(value) {
this['_${name}'] = value;
} : undefined
}`);
}
Object.defineProperties(obj, propertiesInfo);
obj.toJSON = function () {
var result = {};
for (var i in this) {
if (i[0] == '_' || _.contains(['toJSON', 'toString'], i)) {
continue;
}
result[i] = this[i];
}
return result;
};
};
exports.isAtEdge = function (object) {
if (object.pos) {
object = object.pos;
}
return object.x == 0 || object.x == 49 || object.y == 0 || object.y == 49;
};
exports.serializePath = function (path) {
if (!_.isArray(path)) {
throw new Error('path is not an array');
}
var result = '';
if (!path.length) {
return result;
}
if (path[0].x < 0 || path[0].y < 0) {
throw new Error('path coordinates cannot be negative');
}
result += path[0].x > 9 ? path[0].x : '0' + path[0].x;
result += path[0].y > 9 ? path[0].y : '0' + path[0].y;
for (var i = 0; i < path.length; i++) {
result += path[i].direction;
}
return result;
};
exports.deserializePath = function (path) {
if (!_.isString(path)) {
throw new Error('`path` is not a string');
}
var result = [];
if (!path.length) {
return result;
}
var x, y, direction, dx, dy;
x = parseInt(path.substring(0, 2));
y = parseInt(path.substring(2, 4));
if (_.isNaN(x) || _.isNaN(y)) {
throw new Error('`path` is not a valid serialized path string');
}
for (var i = 4; i < path.length; i++) {
direction = parseInt(path.charAt(i));
if (!offsetsByDirection[direction]) {
throw new Error('`path` is not a valid serialized path string');
}
dx = offsetsByDirection[direction][0];
dy = offsetsByDirection[direction][1];
if (i > 4) {
x += dx;
y += dy;
}
result.push({
x, y,
dx, dy,
direction
});
}
return result;
};
exports.calcResources = function (object) {
if (object.store) {
return _.sum(object.store);
}
return _.sum(C.RESOURCES_ALL, i => typeof object[i] == 'object' ? object[i].amount : object[i] || 0);
};
exports.calcBodyEffectiveness = function (body, bodyPartType, methodName, basePower, withoutOldHits) {
var power = 0;
body.forEach(i => {
if (!(i.hits || !withoutOldHits && i._oldHits) || i.type != bodyPartType) {
return;
}
var iPower = basePower;
if (i.boost && C.BOOSTS[bodyPartType][i.boost] && C.BOOSTS[bodyPartType][i.boost][methodName]) {
iPower *= C.BOOSTS[bodyPartType][i.boost][methodName];
}
power += iPower;
});
return power;
};
exports.dist = function (a, b) {
if (a.pos) a = a.pos;
if (b.pos) b = b.pos;
return Math.max(Math.abs(a.x - b.x), Math.abs(a.y - b.y));
};
exports.calcRoomsDistance = function (room1, room2, continuous) {
var _exports$roomNameToXY = exports.roomNameToXY(room1),
_exports$roomNameToXY2 = _slicedToArray(_exports$roomNameToXY, 2),
x1 = _exports$roomNameToXY2[0],
y1 = _exports$roomNameToXY2[1];
var _exports$roomNameToXY3 = exports.roomNameToXY(room2),
_exports$roomNameToXY4 = _slicedToArray(_exports$roomNameToXY3, 2),
x2 = _exports$roomNameToXY4[0],
y2 = _exports$roomNameToXY4[1];
var dx = Math.abs(x2 - x1);
var dy = Math.abs(y2 - y1);
if (continuous) {
var worldSize = driver.getWorldSize();
dx = Math.min(worldSize - dx, dx);
dy = Math.min(worldSize - dy, dy);
}
return Math.max(dx, dy);
};
exports.calcTerminalEnergyCost = function (amount, range) {
return Math.ceil(amount * (1 - Math.exp(-range / 30)));
};
exports.calcNeededGcl = function (gclLevel) {
return C.GCL_MULTIPLY * Math.pow(gclLevel - 1, C.GCL_POW);
};
exports.calcTotalReactionsTime = function (mineral) {
const reagents = _.reduce(C.REACTIONS, (a, n, j) => {
_.forEach(n, (k, v) => a[k] = [v, j]);return a;
}, {});
const calcStep = m => !!C.REACTION_TIME[m] ? C.REACTION_TIME[m] + calcStep(reagents[m][0]) + calcStep(reagents[m][1]) : 0;
return calcStep(mineral);
};
exports.capacityForResource = function (object, resourceType) {
return object.storeCapacityResource && object.storeCapacityResource[resourceType] || Math.max(0, (object.storeCapacity || 0) - _.sum(object.storeCapacityResource));
};
exports.calcReward = function (resourceDensities, targetDensity, itemsLimit) {
let resources = [];
let densities = [];
_.forEach(resourceDensities, (density, resource) => {
resources.push(resource);
densities.push(density);
});
let order = _.shuffle(_.range(resources.length));
if (itemsLimit) {
order = order.slice(0, itemsLimit);
}
let result = _.times(order.length, 0);
let currentDensity = 0;
for (let i = 0; i < order.length - 1; i++) {
result[i] = Math.max(0, Math.round(Math.random() * (targetDensity - currentDensity) / densities[order[i]]));
currentDensity += result[i] * densities[order[i]];
}
result[order.length - 1] = Math.max(0, Math.round((targetDensity - currentDensity) / densities[order.length - 1]));
return _.object(order.map(i => resources[i]), result);
};
exports.getReactionVariants = function getReactionVarients(compound) {
const result = [];
for (let r1 in C.REACTIONS) {
for (let r2 in C.REACTIONS[r1]) {
if (C.REACTIONS[r1][r2] == compound) {
result.push([r1, r2]);
}
}
}
return result;
};
//# sourceMappingURL=sourcemaps/utils.js.map