UNPKG

@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
'use strict'; 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