├── .github └── workflows │ └── npm.yml ├── .gitignore ├── .npmignore ├── LICENSE.txt ├── README.md ├── gulpfile.js ├── package-lock.json ├── package.json ├── spec ├── engine │ ├── game │ │ ├── roomsSpec.js │ │ └── storeSpec.js │ ├── processor │ │ └── intents │ │ │ ├── creeps │ │ │ └── transferSpec.js │ │ │ └── movementSpec.js │ └── utilsSpec.js ├── helpers │ └── mocks │ │ ├── bulk.js │ │ ├── common.js │ │ ├── creeps.js │ │ ├── intents.js │ │ ├── powerCreeps.js │ │ ├── rooms.js │ │ └── users.js └── support │ └── jasmine.json └── src ├── config.js ├── decycle.js ├── game ├── console.js ├── constants.js ├── construction-sites.js ├── creeps.js ├── custom-prototypes.js ├── deposits.js ├── flags.js ├── game.js ├── map.js ├── market.js ├── minerals.js ├── names.js ├── nukes.js ├── path-finder.js ├── path-utils.js ├── power-creeps.js ├── resources.js ├── rooms.js ├── ruins.js ├── sources.js ├── store.js ├── structures.js └── tombstones.js ├── index.js ├── main.js ├── processor.js ├── processor ├── common │ └── fake-runtime.js ├── global-intents │ ├── market.js │ ├── power.js │ └── power │ │ ├── _diePowerCreep.js │ │ ├── createPowerCreep.js │ │ ├── deletePowerCreep.js │ │ ├── renamePowerCreep.js │ │ ├── spawnPowerCreep.js │ │ ├── suicidePowerCreep.js │ │ └── upgradePowerCreep.js ├── global.js └── intents │ ├── _calc_spawns.js │ ├── _create-energy.js │ ├── _damage.js │ ├── constructedWalls │ └── tick.js │ ├── construction-sites │ └── tick.js │ ├── containers │ └── tick.js │ ├── controllers │ ├── activateSafeMode.js │ ├── tick.js │ └── unclaim.js │ ├── creeps │ ├── _add-fatigue.js │ ├── _clear-newbie-walls.js │ ├── _die.js │ ├── _drop-resources-without-space.js │ ├── _recalc-body.js │ ├── attack.js │ ├── attackController.js │ ├── build.js │ ├── claimController.js │ ├── dismantle.js │ ├── drop.js │ ├── generateSafeMode.js │ ├── harvest.js │ ├── heal.js │ ├── intents.js │ ├── invaders │ │ ├── findAttack.js │ │ ├── flee.js │ │ ├── healer.js │ │ ├── pretick.js │ │ └── shootAtWill.js │ ├── keepers │ │ └── pretick.js │ ├── move.js │ ├── pickup.js │ ├── pull.js │ ├── rangedAttack.js │ ├── rangedHeal.js │ ├── rangedMassAttack.js │ ├── repair.js │ ├── reserveController.js │ ├── say.js │ ├── signController.js │ ├── suicide.js │ ├── tick.js │ ├── transfer.js │ ├── upgradeController.js │ └── withdraw.js │ ├── deposits │ └── tick.js │ ├── energy │ └── tick.js │ ├── extensions │ └── tick.js │ ├── extractors │ └── tick.js │ ├── factories │ ├── produce.js │ └── tick.js │ ├── flags │ └── intents.js │ ├── invader-core │ ├── attackController.js │ ├── create-creep.js │ ├── destroy.js │ ├── intents.js │ ├── pretick.js │ ├── reserveController.js │ ├── stronghold │ │ ├── creeps.js │ │ ├── defence.js │ │ └── stronghold.js │ ├── tick.js │ ├── transfer.js │ └── upgradeController.js │ ├── keeper-lairs │ └── tick.js │ ├── labs │ ├── boost-creep.js │ ├── intents.js │ ├── reverse-reaction.js │ ├── run-reaction.js │ ├── tick.js │ └── unboost-creep.js │ ├── links │ ├── intents.js │ ├── tick.js │ └── transfer.js │ ├── minerals │ └── tick.js │ ├── movement.js │ ├── nukers │ └── launch-nuke.js │ ├── nukes │ ├── pretick.js │ └── tick.js │ ├── portals │ └── tick.js │ ├── power-creeps │ ├── drop.js │ ├── enableRoom.js │ ├── intents.js │ ├── move.js │ ├── pickup.js │ ├── renew.js │ ├── say.js │ ├── tick.js │ ├── transfer.js │ ├── usePower.js │ └── withdraw.js │ ├── power-spawns │ ├── intents.js │ └── process-power.js │ ├── ramparts │ ├── set-public.js │ └── tick.js │ ├── roads │ └── tick.js │ ├── room │ ├── create-construction-site.js │ ├── create-flag.js │ ├── destroy-structure.js │ ├── gen-energy.js │ ├── intents.js │ ├── remove-construction-site.js │ └── remove-flag.js │ ├── ruins │ └── tick.js │ ├── sources │ └── tick.js │ ├── spawns │ ├── _born-creep.js │ ├── _charge-energy.js │ ├── cancel-spawning.js │ ├── create-creep.js │ ├── intents.js │ ├── recycle-creep.js │ ├── renew-creep.js │ ├── set-spawn-directions.js │ └── tick.js │ ├── storages │ └── tick.js │ ├── structures │ └── _destroy.js │ ├── terminal │ ├── send.js │ └── tick.js │ ├── tombstones │ └── tick.js │ └── towers │ ├── attack.js │ ├── heal.js │ ├── intents.js │ ├── repair.js │ └── tick.js ├── runner.js └── utils.js /.github/workflows/npm.yml: -------------------------------------------------------------------------------- 1 | name: Build npm package 2 | on: push 3 | jobs: 4 | build: 5 | if: contains(github.event.head_commit.message, '🔖') 6 | runs-on: ubuntu-latest 7 | permissions: 8 | contents: read 9 | packages: write 10 | steps: 11 | - name: ⤵️ Checkout 12 | uses: actions/checkout@v3 13 | # Setup .npmrc file to use GitHub Packages 14 | - name: 🔨 Setup node (github registry) 15 | uses: actions/setup-node@v3 16 | with: 17 | node-version: '10.x' 18 | registry-url: 'https://npm.pkg.github.com' 19 | # Defaults to the user or organization that owns the workflow file 20 | scope: '@screeps' 21 | - name: 👷 Install 22 | run: npm ci 23 | env: 24 | NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 25 | - name: 📦️ Publish package (github registry) 26 | if: github.ref_name == 'master' 27 | run: npm publish 28 | env: 29 | NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 30 | - name: 📦️ Publish tagged package (github registry) 31 | if: github.ref_name != 'master' 32 | run: npm publish --tag ${{ github.ref_name }} 33 | env: 34 | NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 35 | - name: 🔨 Setup node (npmjs) 36 | uses: actions/setup-node@v3 37 | with: 38 | node-version: '10.x' 39 | registry-url: 'https://registry.npmjs.org' 40 | scope: '@screeps' 41 | - name: 📦️ Publish package (npmjs) 42 | if: github.ref_name == 'master' 43 | run: npm publish 44 | env: 45 | NODE_AUTH_TOKEN: ${{ secrets.NPMJS_TOKEN }} 46 | - name: 📦️ Publish tagged package (npmjs) 47 | if: github.ref_name != 'master' 48 | run: npm publish --tag ${{ github.ref_name }} 49 | env: 50 | NODE_AUTH_TOKEN: ${{ secrets.NPMJS_TOKEN }} 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .gitignore support plugin (hsz.mobi) 2 | dist 3 | dist_linux 4 | node_modules 5 | .idea 6 | build/config.gypi 7 | src/local-config.js -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Created by .gitignore support plugin (hsz.mobi) 2 | node_modules 3 | .idea 4 | .run 5 | .github 6 | build/config.gypi 7 | src/local-config.js 8 | dist/core 9 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Artem Chivchalov 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, 4 | provided that the above copyright notice and this permission notice appear in all copies. 5 | 6 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL 7 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 8 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 9 | AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 10 | OF THIS SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a module for Screeps standalone server. See [main repository](https://github.com/screeps/screeps) for more info. -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'), 2 | watch = require('gulp-watch'), 3 | babel = require('gulp-babel'), 4 | path = require('path'), 5 | plumber = require('gulp-plumber'), 6 | sourcemaps = require('gulp-sourcemaps'); 7 | 8 | var paths = { 9 | src: {src: 'src/**/*.js', dest: 'dist'} 10 | }; 11 | 12 | function isAdded(file) { 13 | return file.event === 'added' || file.event === 'deleted' || file.event === 'renamed'; 14 | } 15 | 16 | function handleError(err) { 17 | console.log(err.toString()); 18 | this.emit('end'); 19 | } 20 | 21 | 22 | var escapeContent = function(content) { 23 | return content.replace(/"/g, '\\"').replace(/\r?\n/g, '" +\n "'); 24 | }; 25 | 26 | var run = function (watched, babelOpts) { 27 | 28 | 29 | (watched ? 30 | watch(paths.src.src, {silent: false}) : 31 | gulp.src(paths.src.src)) 32 | .pipe(plumber()) 33 | .pipe(sourcemaps.init()) 34 | .pipe(babel(babelOpts)) 35 | .pipe(sourcemaps.write('./sourcemaps', {includeContent: false, sourceRoot: '/src'})) 36 | .pipe(gulp.dest(paths.src.dest)); 37 | 38 | }; 39 | 40 | gulp.task('watch', function () { 41 | run(false); 42 | run(true); 43 | }); 44 | 45 | gulp.task('default', function () { 46 | run(false); 47 | }); 48 | 49 | gulp.task('frontend', function () { 50 | run(false, {presets: ['es2015']}); 51 | }); 52 | 53 | gulp.task('frontend-watch', function () { 54 | run(false, {presets: ['es2015']}); 55 | run(true, {presets: ['es2015']}); 56 | }); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@screeps/engine", 3 | "version": "4.2.22", 4 | "bin": { 5 | "screeps-engine-main": "dist/main.js", 6 | "screeps-engine-runner": "dist/runner.js", 7 | "screeps-engine-processor": "dist/processor.js" 8 | }, 9 | "main": "dist/index.js", 10 | "scripts": { 11 | "test": "cross-env DRIVER_MODULE=@screeps/driver jasmine", 12 | "prepublish": "gulp" 13 | }, 14 | "dependencies": { 15 | "@screeps/pathfinding": "^0.4.17", 16 | "bulk-require": "^0.2.1", 17 | "cross-env": "^5.2.0", 18 | "lodash": "3.10.1", 19 | "q": "^1.0.1" 20 | }, 21 | "devDependencies": { 22 | "babel-plugin-transform-es2015-destructuring": "^6.23.0", 23 | "babel-plugin-transform-strict-mode": "^6.24.1", 24 | "babel-preset-es2015": "^6.24.1", 25 | "cross-env": "^5.2.0", 26 | "gulp": "^3.9.1", 27 | "gulp-babel": "^6.1.2", 28 | "gulp-plumber": "^1.1.0", 29 | "gulp-sourcemaps": "^2.6.0", 30 | "gulp-traceur": "^0.17.2", 31 | "gulp-watch": "^4.3.11", 32 | "jasmine": "^3.3.0" 33 | }, 34 | "license": "ISC", 35 | "author": "Artem Chivchalov ", 36 | "repository": { 37 | "type": "git", 38 | "url": "https://github.com/screeps/engine.git" 39 | }, 40 | "babel": { 41 | "plugins": [ 42 | "transform-es2015-destructuring", 43 | "transform-strict-mode" 44 | ] 45 | }, 46 | "browser": { 47 | "@screeps/core": "@screeps/simcore" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /spec/engine/game/roomsSpec.js: -------------------------------------------------------------------------------- 1 | const _ =require('lodash'), 2 | utils = require('../../../src/utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants, 5 | rooms = require('../../../src/game/rooms'); 6 | 7 | describe('rooms', () => { 8 | describe('RoomPosition', () => { 9 | let globals = {}; 10 | 11 | beforeEach(()=>{ 12 | const runtimeData = { 13 | staticTerrainData: require('../../helpers/mocks/rooms').terrain 14 | }; 15 | const register = { 16 | wrapFn: function(fn) { return fn } 17 | }; 18 | 19 | rooms.make(runtimeData, {}, register, globals); 20 | rooms.makePos(register); 21 | }); 22 | 23 | it('Exists',()=>{ 24 | expect(globals.RoomPosition).toBeDefined(); 25 | }); 26 | 27 | it('Preserves coordinates', ()=>{ 28 | const pos = new globals.RoomPosition(11,14,'E2S7'); 29 | 30 | expect(pos).toBeDefined(); 31 | expect(pos.x).toBe(11); 32 | expect(pos.y).toBe(14); 33 | expect(pos.roomName).toBe('E2S7'); 34 | }); 35 | 36 | it('Mutates x properly', ()=>{ 37 | const pos = new globals.RoomPosition(11,14,'E2S7'); 38 | 39 | pos.x++; 40 | 41 | expect(pos).toBeDefined(); 42 | expect(pos.x).toBe(12); 43 | expect(pos.y).toBe(14); 44 | expect(pos.roomName).toBe('E2S7'); 45 | }); 46 | 47 | it('Mutates y properly', ()=>{ 48 | const pos = new globals.RoomPosition(11,14,'E2S7'); 49 | 50 | pos.y++; 51 | 52 | expect(pos).toBeDefined(); 53 | expect(pos.x).toBe(11); 54 | expect(pos.y).toBe(15); 55 | expect(pos.roomName).toBe('E2S7'); 56 | }); 57 | }); 58 | }); -------------------------------------------------------------------------------- /spec/engine/processor/intents/creeps/transferSpec.js: -------------------------------------------------------------------------------- 1 | const _ =require('lodash'), 2 | utils = require('../../../../../src/utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants, 5 | movement = require('../../../../../src/processor/intents/movement'), 6 | creepsEnv = require('../../../../helpers/mocks/creeps'), 7 | intents = require('../../../../helpers/mocks/intents'); 8 | 9 | describe("Creep transferring resource", () => { 10 | let lorry1, lorry2; 11 | 12 | beforeEach(()=>{ 13 | lorry1 = creepsEnv.createCreep('lorry', {x: 24, y: 24}); 14 | lorry2 = creepsEnv.createCreep('lorry', {x: 24, y: 25}); 15 | movement.init(); 16 | }); 17 | 18 | it("does not transfer it to a creep being spawned",()=>{ 19 | lorry1.store = {energy: 100}; 20 | lorry2.store = {energy:0}; 21 | lorry2.spawning = true; 22 | lorry1.transfer(lorry2._id, C.RESOURCE_ENERGY, 100); 23 | intents.ticks(); 24 | 25 | expect(lorry1.store.energy).toBe(100); 26 | expect(lorry2.store.energy).toBe(0); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /spec/helpers/mocks/bulk.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'), 2 | common = require('./common'); 3 | 4 | let objects = {}; 5 | 6 | exports.update = (id, data) => { 7 | data = _.cloneDeep(data); 8 | 9 | _.forEach(data, (value, key) => { 10 | if(_.isObject(value)) { 11 | if (!_.isObject(id)) { 12 | throw new Error(`can not update an object diff property '${key}' without object reference`); 13 | } 14 | const originalValue = id[key] || {}; 15 | _.merge(originalValue, value); 16 | data[key] = originalValue; 17 | } 18 | }); 19 | if(_.isObject(id)) { 20 | _.merge(id, data); 21 | } else { 22 | const object = objects[id]; 23 | if(!object) { 24 | throw new Error(`${id} is not registered in bulk fake`); 25 | } 26 | _.merge(object, data); 27 | } 28 | }; 29 | 30 | exports.remove = id => { 31 | if(!objects[id]) { 32 | throw new Error(`${id} is not registered in bulk fake`); 33 | } 34 | delete objects[id]; 35 | }; 36 | 37 | exports.insert = object => { 38 | if(!object._id) { 39 | object._id = common.generateId(); 40 | } 41 | objects[object._id] = object; 42 | }; 43 | 44 | exports.reset = () => {objects = {};}; 45 | -------------------------------------------------------------------------------- /spec/helpers/mocks/common.js: -------------------------------------------------------------------------------- 1 | exports.generateId = ()=>{ 2 | return 'xxxxxxxxxxxxxxxxxxxxxxxx'.replace(/[xy]/g, c => { 3 | const r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8); 4 | return v.toString(16); 5 | }); 6 | }; 7 | -------------------------------------------------------------------------------- /spec/helpers/mocks/creeps.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'), 2 | utils = require('../../../src/utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants, 5 | intents = require('./intents'), 6 | common = require('./common'); 7 | 8 | const commonData = { 9 | type: 'creep', 10 | room: 'E2S7', 11 | fatigue: 0, 12 | actionLog: {}, 13 | toString: function(){return `[creep #${this._id}@${this.x},${this.y}]`}, 14 | move: function(target) { require('../../../src/processor/intents/creeps/move')(this, _.isNumber(target) ? {direction: target} : {id: target}, intents.scope); }, 15 | pull: function(target) { require('../../../src/processor/intents/creeps/pull')(this, {id: target}, intents.scope); }, 16 | transfer: function(target, resourceType, amount) { require('../../../src/processor/intents/creeps/transfer')(this, {id: target, resourceType, amount}, intents.scope); }, 17 | tick: function() { require('../../../src/processor/intents/creeps/tick')(this, intents.scope); } 18 | }; 19 | 20 | const creeps = { 21 | scout: { 22 | name: 'scout', 23 | body: [ 24 | {type: C.MOVE, hits: 100} 25 | ] 26 | }, 27 | noMove: { 28 | name: 'noMove', 29 | body: [ 30 | {type: C.TOUGH, hits: 100} 31 | ] 32 | }, 33 | fullSpeed: { 34 | name: 'fullSpeed', 35 | body: [ 36 | {type: C.TOUGH, hits: 100}, 37 | {type: C.MOVE, hits: 100} 38 | ] 39 | }, 40 | halfSpeed: { 41 | name: 'halfSpeed', 42 | body: [ 43 | {type: C.TOUGH, hits: 100}, 44 | {type: C.TOUGH, hits: 100}, 45 | {type: C.MOVE, hits: 100} 46 | ] 47 | }, 48 | lorry: { 49 | name: 'fullSpeed', 50 | body: [ 51 | {type: C.CARRY, hits: 100}, 52 | {type: C.CARRY, hits: 100}, 53 | {type: C.MOVE, hits: 100} 54 | ], 55 | carryCapacity: 100 56 | }, 57 | halfLocomotive: { 58 | name: 'halfLocomotive', 59 | body: _.times(25, _.constant({ type: C.MOVE, hits: 100 })) 60 | }, 61 | fullLocomotive: { 62 | name: 'fullLocomotive', 63 | body: _.times(50, _.constant({ type: C.MOVE, hits: 100 })) 64 | }, 65 | fullWorker: { 66 | name: 'fullLocomotive', 67 | body: _.times(50, _.constant({ type: C.WORK, hits: 100 })) 68 | }, 69 | }; 70 | 71 | exports.createCreep = function(template, data) { 72 | const copy = _.merge( 73 | _.cloneDeep(commonData), 74 | _.cloneDeep(creeps[template]), 75 | data); 76 | if(!copy._id) { 77 | copy._id = common.generateId(); 78 | } 79 | intents.scope.roomObjects[copy._id] = copy; 80 | intents.scope.bulk.insert(copy); 81 | return copy; 82 | }; 83 | -------------------------------------------------------------------------------- /spec/helpers/mocks/intents.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | 3 | exports.scope = { 4 | roomObjects: {}, 5 | gameTime: 30, 6 | eventLog: [], 7 | bulk: require('./bulk') 8 | }; 9 | 10 | exports.reset = ()=>{ exports.scope.bulk.reset(); exports.scope.roomObjects = {}}; 11 | exports.ticks = ()=> { 12 | _.forEach(exports.scope.roomObjects, (object => { 13 | if(object.type == 'creep' || object.type == 'powerCreep') { 14 | object.tick(); 15 | } 16 | })); 17 | }; 18 | -------------------------------------------------------------------------------- /spec/helpers/mocks/powerCreeps.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'), 2 | utils = require('../../../src/utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants, 5 | intents = require('./intents'), 6 | common = require('./common'); 7 | 8 | const commonData = { 9 | type: 'powerCreep', 10 | class: 'operator', 11 | room: 'E2S7', 12 | actionLog: {}, 13 | level: 1, 14 | powers: {}, 15 | toString: function(){return `[powerCreep #${this._id}@${this.x},${this.y}]`}, 16 | move: function(target) { require('../../../src/processor/intents/power-creeps/move')(this, _.isNumber(target) ? {direction: target} : {id: target}, intents.scope); }, 17 | tick: function() { require('../../../src/processor/intents/power-creeps/tick')(this, intents.scope); } 18 | }; 19 | 20 | const powerCreeps = { 21 | FullOperator: { 22 | name: 'FullOperator', 23 | level: 25, 24 | powers: {1: {level: 5}, 2: {level: 5},4: {level: 1},7: {level: 1}, 13: {level: 4}, 17: {level: 5}}, 25 | hits: 26000, 26 | hitsMax: 26000, 27 | } 28 | }; 29 | 30 | exports.createPowerCreep = function(template, data) { 31 | const copy = _.merge( 32 | _.cloneDeep(commonData), 33 | _.cloneDeep(powerCreeps[template]), 34 | data); 35 | if(!copy._id) { 36 | copy._id = common.generateId(); 37 | } 38 | intents.scope.roomObjects[copy._id] = copy; 39 | intents.scope.bulk.insert(copy); 40 | return copy; 41 | }; 42 | -------------------------------------------------------------------------------- /spec/helpers/mocks/rooms.js: -------------------------------------------------------------------------------- 1 | exports.terrain = { 2 | 'E2S7': '' + 3 | '11111111111111100000000000000000011111111111111111' + 4 | '11000000001111000000000000000000000001111112002111' + 5 | '12002220000022220000000000222002220001111102022211' + 6 | '12222220000022220022000222222000222211111100002211' + 7 | '12200222000002222222000000222002221111111000000011' + 8 | '11200022011111112222200000020000111111222200022111' + 9 | '11110222111111111111110000220001111110222200020111' + 10 | '11111222111111111111111122222011111100222200000011' + 11 | '11111122000220111111111112202020111000222011100021' + 12 | '11111102000220221101111111002220000000220111100021' + 13 | '11111002000000022000111111102000000002200011000001' + 14 | '11110000000000000000211111102000000020000000000001' + 15 | '11200000000000000000000001112000000020000000000001' + 16 | '12220222022200002000000000111000110020000000000001' + 17 | '10000222222200022200000000111111000000000000000221' + 18 | '10000220002200022200000000111111000000000000000001' + 19 | '10000000002000022220000000010002202000000000000001' + 20 | '10000000000020222220000000000002222200000001100001' + 21 | '10000020000220222000000212000022222222200011110001' + 22 | '10002220000000022000000111200000222222200111110001' + 23 | '10002222011000000000002111222000222022000111111011' + 24 | '10000220111120000000002110002200000000022111111011' + 25 | '11000022011120000000002110000220001000222201110001' + 26 | '11000022001120000000000010000222011100222200111011' + 27 | '11000000022000000200000002000022211100022000111111' + 28 | '11000020022200000220000001000222111100000000011111' + 29 | '10000020222000000200000001100011111100000000001111' + 30 | '10000222222200021111110001110111111100000000000111' + 31 | '10000022222000001111111011111111111100000020000011' + 32 | '10020200002000001111111111111111111110000000000011' + 33 | '02220000022000000000111111000000011111111111000001' + 34 | '00220010000000000200211110000000001111111111000001' + 35 | '00221111000000000000011110022110001111111110000001' + 36 | '00011112222200022200001110020111001110000000000001' + 37 | '00001222222202222200000002022110001110222000200001' + 38 | '00022222222222022200000022022220222222220022202001' + 39 | '00222222220000000000000000022200220000020000022001' + 40 | '10222222002200000000000000002000000002000000022001' + 41 | '10220000002000000000000000000000000002220000022011' + 42 | '00000000022000000000000000000000000000022200002011' + 43 | '00000000000000000020111100000011100000022200000011' + 44 | '00000000002200000221111110000011100000001110000001' + 45 | '00011111002222200011122111000011000002111110000001' + 46 | '00111111122001020200000012200002220001111111000001' + 47 | '11111111020011120200000022000000000021122210000221' + 48 | '11000000000001000000000202000000002222000000000221' + 49 | '12022200000000000000100000000000022220000000020221' + 50 | '10000200000000000001110000000000222220000000000001' + 51 | '11000200000000000011111110000000222200000000000011' + 52 | '11111100000000111111111111100000000000000000111111' 53 | }; 54 | -------------------------------------------------------------------------------- /spec/helpers/mocks/users.js: -------------------------------------------------------------------------------- 1 | exports.defaultId = '5a70498ac9e2f700010d0a63'; 2 | -------------------------------------------------------------------------------- /spec/support/jasmine.json: -------------------------------------------------------------------------------- 1 | { 2 | "spec_dir": "spec", 3 | "spec_files": [ 4 | "**/*[sS]pec.js" 5 | ], 6 | "helpers": [ 7 | "helpers/**/*.js" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /src/config.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'); 2 | var result = {}; 3 | 4 | _.merge(result, { 5 | ptr: "true" == process.env.PTR, 6 | redis: { 7 | port: 6379, 8 | host: '127.0.0.1', 9 | options: {} 10 | } 11 | }); 12 | 13 | 14 | module.exports = result; 15 | -------------------------------------------------------------------------------- /src/game/console.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | messages = {}, 3 | commandResults = {}, 4 | visual = {}; 5 | 6 | exports.makeConsole = function(id, sandboxedFunctionWrapper) { 7 | messages[id] = []; 8 | commandResults[id] = []; 9 | visual[id] = {}; 10 | return Object.create(null, { 11 | log: { 12 | writable: true, 13 | configurable: true, 14 | value: sandboxedFunctionWrapper(function() { 15 | 16 | if(typeof self != 'undefined' && self.navigator.userAgent) { 17 | self['console']['log'].apply(console, arguments); 18 | } 19 | 20 | messages[id].push( 21 | _.map(arguments, (i) => { 22 | if(i && i.toString) return i.toString(); 23 | if(typeof i === 'undefined') return 'undefined'; 24 | return JSON.stringify(i); 25 | }).join(' ')); 26 | }) 27 | }, 28 | commandResult: { 29 | value: sandboxedFunctionWrapper(function(message) { 30 | if(typeof self != 'undefined' && self.navigator.userAgent) { 31 | self['console']['log'].call(console, message); 32 | } 33 | commandResults[id].push(String(message)); 34 | }) 35 | }, 36 | addVisual: { 37 | value: sandboxedFunctionWrapper(function(roomName, data) { 38 | roomName = roomName || ""; 39 | if(!data) { 40 | return; 41 | } 42 | const sizeLimit = roomName == "map" ? 1000 : 500; 43 | visual[id][roomName] = visual[id][roomName] || ""; 44 | const dataString = _.isString(data) ? data : JSON.stringify(data)+"\n"; 45 | if((visual[id][roomName].length + dataString.length) > sizeLimit*1024) { 46 | if(roomName == "map") { 47 | throw new Error( `MapVisual size has exceeded ${sizeLimit} KB limit`); 48 | } 49 | throw new Error( `RoomVisual size in room ${roomName} has exceeded ${sizeLimit} KB limit`); 50 | } 51 | 52 | visual[id][roomName] += dataString; 53 | }) 54 | }, 55 | getVisualSize: { 56 | value: sandboxedFunctionWrapper(function(roomName) { 57 | roomName = roomName || ""; 58 | if(!visual[id][roomName]) { 59 | return 0; 60 | } 61 | return visual[id][roomName].length; 62 | }) 63 | }, 64 | clearVisual: { 65 | value: sandboxedFunctionWrapper(function(roomName) { 66 | roomName = roomName || ""; 67 | visual[id][roomName] = ""; 68 | }) 69 | }, 70 | getVisual: { 71 | value: sandboxedFunctionWrapper(function(roomName){ 72 | return visual[id][roomName || ""]; 73 | }) 74 | } 75 | }); 76 | }; 77 | 78 | exports.getMessages = function(id) { 79 | var result = messages[id]; 80 | messages[id] = []; 81 | return result; 82 | }; 83 | 84 | exports.getCommandResults = function(id) { 85 | var result = commandResults[id]; 86 | commandResults[id] = []; 87 | return result; 88 | }; 89 | 90 | exports.getVisual = function(id) { 91 | var result = visual[id]; 92 | visual[id] = []; 93 | return result; 94 | }; 95 | -------------------------------------------------------------------------------- /src/game/constants.js: -------------------------------------------------------------------------------- 1 | var utils = require('../utils'), 2 | driver = utils.getRuntimeDriver(); 3 | 4 | module.exports = driver.constants; -------------------------------------------------------------------------------- /src/game/construction-sites.js: -------------------------------------------------------------------------------- 1 | var utils = require('./../utils'), 2 | rooms = require('./rooms'), 3 | driver = utils.getRuntimeDriver(), 4 | C = driver.constants, 5 | _ = require('lodash'); 6 | 7 | var runtimeData, intents, register, globals; 8 | 9 | exports.make = function(_runtimeData, _intents, _register, _globals) { 10 | 11 | runtimeData = _runtimeData; 12 | intents = _intents; 13 | register = _register; 14 | globals = _globals; 15 | 16 | if(globals.ConstructionSite) { 17 | return; 18 | } 19 | 20 | var data = (id) => { 21 | if(!runtimeData.roomObjects[id]) { 22 | throw new Error("Could not find an object with ID "+id); 23 | } 24 | return runtimeData.roomObjects[id]; 25 | }; 26 | 27 | var ConstructionSite = register.wrapFn(function(id) { 28 | var _data = data(id); 29 | globals.RoomObject.call(this, _data.x, _data.y, _data.room, _data.effects); 30 | this.id = id; 31 | }); 32 | 33 | ConstructionSite.prototype = Object.create(globals.RoomObject.prototype); 34 | ConstructionSite.prototype.constructor = ConstructionSite; 35 | 36 | utils.defineGameObjectProperties(ConstructionSite.prototype, data, { 37 | progress: (o) => o.progress, 38 | progressTotal: (o) => o.progressTotal, 39 | structureType: (o) => o.structureType, 40 | name: (o) => o.name, 41 | owner: (o) => new Object({username: runtimeData.users[o.user].username}), 42 | my: (o) => _.isUndefined(o.user) ? undefined : o.user == runtimeData.user._id 43 | }); 44 | 45 | 46 | ConstructionSite.prototype.toString = register.wrapFn(function() { 47 | return `[construction site (${data(this.id).structureType}) #${this.id}]`; 48 | }); 49 | 50 | ConstructionSite.prototype.remove = register.wrapFn(function() { 51 | 52 | if(!this.my && !(this.room && this.room.controller && this.room.controller.my)) { 53 | return C.ERR_NOT_OWNER; 54 | } 55 | intents.pushByName('room', 'removeConstructionSite', {roomName: data(this.id).room, id: this.id}); 56 | return C.OK; 57 | }); 58 | 59 | Object.defineProperty(globals, 'ConstructionSite', {enumerable: true, value: ConstructionSite}); 60 | }; -------------------------------------------------------------------------------- /src/game/custom-prototypes.js: -------------------------------------------------------------------------------- 1 | var utils = require('./../utils'), 2 | rooms = require('./rooms'), 3 | driver = utils.getRuntimeDriver(), 4 | C = driver.constants; 5 | 6 | var scope = {}; 7 | 8 | module.exports = function(name, parent, properties, prototypeExtender, userOwned) { 9 | return function (_runtimeData, _intents, _register, _globals) { 10 | 11 | scope.runtimeData = _runtimeData; 12 | scope.intents = _intents; 13 | scope.register = _register; 14 | scope.globals = _globals; 15 | 16 | if (scope.globals[name]) { 17 | return; 18 | } 19 | 20 | var data = (id) => { 21 | if (!scope.runtimeData.roomObjects[id]) { 22 | throw new Error("Could not find an object with ID " + id); 23 | } 24 | return scope.runtimeData.roomObjects[id]; 25 | }; 26 | 27 | var _CustomObject = scope.register.wrapFn(function (id) { 28 | var _data = data(id); 29 | if(parent) { 30 | scope.globals[parent].call(this, id); 31 | } 32 | else { 33 | scope.globals.RoomObject.call(this, _data.x, _data.y, _data.room); 34 | } 35 | this.id = id; 36 | }); 37 | 38 | _CustomObject.prototype = Object.create(parent ? scope.globals[parent].prototype : scope.globals.RoomObject.prototype); 39 | _CustomObject.prototype.constructor = _CustomObject; 40 | 41 | if(properties) { 42 | utils.defineGameObjectProperties(_CustomObject.prototype, data, properties); 43 | } 44 | 45 | if(userOwned) { 46 | utils.defineGameObjectProperties(_CustomObject.prototype, data, { 47 | my: (o) => o.user == scope.runtimeData.user._id, 48 | owner: (o) => new Object({username: scope.runtimeData.users[o.user].username}), 49 | }); 50 | } 51 | 52 | if(prototypeExtender) { 53 | prototypeExtender(_CustomObject.prototype, scope, {utils}); 54 | } 55 | 56 | scope.globals[name] = _CustomObject; 57 | } 58 | }; 59 | -------------------------------------------------------------------------------- /src/game/deposits.js: -------------------------------------------------------------------------------- 1 | const utils = require('./../utils'), 2 | driver = utils.getRuntimeDriver(), 3 | C = driver.constants; 4 | 5 | let runtimeData, intents, register, globals; 6 | 7 | exports.make = function(_runtimeData, _intents, _register, _globals) { 8 | runtimeData = _runtimeData; 9 | intents = _intents; 10 | register = _register; 11 | globals = _globals; 12 | 13 | if(globals.Deposit) { 14 | return; 15 | } 16 | 17 | const data = (id) => { 18 | if(!runtimeData.roomObjects[id]) { 19 | throw new Error("Could not find an object with ID "+id); 20 | } 21 | return runtimeData.roomObjects[id]; 22 | }; 23 | 24 | const Deposit = register.wrapFn(function(id) { 25 | const _data = data(id); 26 | globals.RoomObject.call(this, _data.x, _data.y, _data.room, _data.effects); 27 | this.id = id; 28 | }); 29 | 30 | Deposit.prototype = Object.create(globals.RoomObject.prototype); 31 | Deposit.prototype.constructor = Deposit; 32 | 33 | utils.defineGameObjectProperties(Deposit.prototype, data, { 34 | depositType: o => o.depositType, 35 | cooldown: o => o.cooldownTime && o.cooldownTime > runtimeData.time ? o.cooldownTime - runtimeData.time : 0, 36 | lastCooldown: o => Math.ceil(C.DEPOSIT_EXHAUST_MULTIPLY*Math.pow(o.harvested,C.DEPOSIT_EXHAUST_POW)), 37 | ticksToDecay: o => o.decayTime ? o.decayTime - runtimeData.time : undefined 38 | }); 39 | 40 | Deposit.prototype.toString = register.wrapFn(function() { 41 | return `[deposit (${this.depositType}) #${this.id}]`; 42 | }); 43 | 44 | Object.defineProperty(globals, 'Deposit', {enumerable: true, value: Deposit}); 45 | }; 46 | -------------------------------------------------------------------------------- /src/game/minerals.js: -------------------------------------------------------------------------------- 1 | var utils = require('./../utils'), 2 | rooms = require('./rooms'), 3 | driver = utils.getRuntimeDriver(), 4 | C = driver.constants; 5 | 6 | var runtimeData, intents, register, globals; 7 | 8 | exports.make = function(_runtimeData, _intents, _register, _globals) { 9 | 10 | runtimeData = _runtimeData; 11 | intents = _intents; 12 | register = _register; 13 | globals = _globals; 14 | 15 | if(globals.Mineral) { 16 | return; 17 | } 18 | 19 | var data = (id) => { 20 | if(!runtimeData.roomObjects[id]) { 21 | throw new Error("Could not find an object with ID "+id); 22 | } 23 | return runtimeData.roomObjects[id]; 24 | }; 25 | 26 | var Mineral = register.wrapFn(function(id) { 27 | var _data = data(id); 28 | globals.RoomObject.call(this, _data.x, _data.y, _data.room, _data.effects); 29 | this.id = id; 30 | }); 31 | 32 | Mineral.prototype = Object.create(globals.RoomObject.prototype); 33 | Mineral.prototype.constructor = Mineral; 34 | 35 | utils.defineGameObjectProperties(Mineral.prototype, data, { 36 | mineralType: (o) => o.mineralType, 37 | mineralAmount: (o) => o.mineralAmount, 38 | density: o => o.density, 39 | ticksToRegeneration: (o) => o.nextRegenerationTime ? o.nextRegenerationTime - runtimeData.time : undefined 40 | }); 41 | 42 | Mineral.prototype.toString = register.wrapFn(function() { 43 | return `[mineral (${this.mineralType}) #${this.id}]`; 44 | }); 45 | 46 | Object.defineProperty(globals, 'Mineral', {enumerable: true, value: Mineral}); 47 | }; -------------------------------------------------------------------------------- /src/game/names.js: -------------------------------------------------------------------------------- 1 | var boyNames = [ 'Jackson', 'Aiden', 'Liam', 'Lucas', 'Noah', 'Mason', 'Jayden', 'Ethan', 'Jacob', 'Jack', 'Caden', 'Logan', 'Benjamin', 'Michael', 'Caleb', 'Ryan', 'Alexander', 'Elijah', 'James', 'William', 'Oliver', 'Connor', 'Matthew', 'Daniel', 'Luke', 'Brayden', 'Jayce', 'Henry', 'Carter', 'Dylan', 'Gabriel', 'Joshua', 'Nicholas', 'Isaac', 'Owen', 'Nathan', 'Grayson', 'Eli', 'Landon', 'Andrew', 'Max', 'Samuel', 'Gavin', 'Wyatt', 'Christian', 'Hunter', 'Cameron', 'Evan', 'Charlie', 'David', 'Sebastian', 'Joseph', 'Dominic', 'Anthony', 'Colton', 'John', 'Tyler', 'Zachary', 'Thomas', 'Julian', 'Levi', 'Adam', 'Isaiah', 'Alex', 'Aaron', 'Parker', 'Cooper', 'Miles', 'Chase', 'Muhammad', 'Christopher', 'Blake', 'Austin', 'Jordan', 'Leo', 'Jonathan', 'Adrian', 'Colin', 'Hudson', 'Ian', 'Xavier', 'Camden', 'Tristan', 'Carson', 'Jason', 'Nolan', 'Riley', 'Lincoln', 'Brody', 'Bentley', 'Nathaniel', 'Josiah', 'Declan', 'Jake', 'Asher', 'Jeremiah', 'Cole', 'Mateo', 'Micah', 'Elliot' ], 2 | girlNames = [ 'Sophia', 'Emma', 'Olivia', 'Isabella', 'Mia', 'Ava', 'Lily', 'Zoe', 'Emily', 'Chloe', 'Layla', 'Madison', 'Madelyn', 'Abigail', 'Aubrey', 'Charlotte', 'Amelia', 'Ella', 'Kaylee', 'Avery', 'Aaliyah', 'Hailey', 'Hannah', 'Addison', 'Riley', 'Harper', 'Aria', 'Arianna', 'Mackenzie', 'Lila', 'Evelyn', 'Adalyn', 'Grace', 'Brooklyn', 'Ellie', 'Anna', 'Kaitlyn', 'Isabelle', 'Sophie', 'Scarlett', 'Natalie', 'Leah', 'Sarah', 'Nora', 'Mila', 'Elizabeth', 'Lillian', 'Kylie', 'Audrey', 'Lucy', 'Maya', 'Annabelle', 'Makayla', 'Gabriella', 'Elena', 'Victoria', 'Claire', 'Savannah', 'Peyton', 'Maria', 'Alaina', 'Kennedy', 'Stella', 'Liliana', 'Allison', 'Samantha', 'Keira', 'Alyssa', 'Reagan', 'Molly', 'Alexandra', 'Violet', 'Charlie', 'Julia', 'Sadie', 'Ruby', 'Eva', 'Alice', 'Eliana', 'Taylor', 'Callie', 'Penelope', 'Camilla', 'Bailey', 'Kaelyn', 'Alexis', 'Kayla', 'Katherine', 'Sydney', 'Lauren', 'Jasmine', 'London', 'Bella', 'Adeline', 'Caroline', 'Vivian', 'Juliana', 'Gianna', 'Skyler', 'Jordyn' ]; 3 | 4 | exports.getUniqueName = (checkExistingFn) => { 5 | 6 | var c = 0, name, exists; 7 | 8 | do { 9 | var list = Math.random() > 0.5 ? boyNames : girlNames; 10 | name = list[Math.floor(Math.random()*list.length)]; 11 | 12 | if(c > 3) { 13 | name += list[Math.floor(Math.random()*list.length)]; 14 | } 15 | 16 | c++; 17 | 18 | exists = checkExistingFn(name); 19 | } 20 | while(exists); 21 | 22 | return name; 23 | }; -------------------------------------------------------------------------------- /src/game/nukes.js: -------------------------------------------------------------------------------- 1 | var utils = require('./../utils'), 2 | rooms = require('./rooms'), 3 | driver = utils.getRuntimeDriver(), 4 | C = driver.constants; 5 | 6 | var runtimeData, intents, register, globals; 7 | 8 | exports.make = function(_runtimeData, _intents, _register, _globals) { 9 | 10 | runtimeData = _runtimeData; 11 | intents = _intents; 12 | register = _register; 13 | globals = _globals; 14 | 15 | if(globals.Nuke) { 16 | return; 17 | } 18 | 19 | var data = (id) => { 20 | if(!runtimeData.roomObjects[id]) { 21 | throw new Error("Could not find an object with ID "+id); 22 | } 23 | return runtimeData.roomObjects[id]; 24 | }; 25 | 26 | var Nuke = register.wrapFn(function(id) { 27 | var _data = data(id); 28 | globals.RoomObject.call(this, _data.x, _data.y, _data.room, _data.effects); 29 | this.id = id; 30 | }); 31 | 32 | Nuke.prototype = Object.create(globals.RoomObject.prototype); 33 | Nuke.prototype.constructor = Nuke; 34 | 35 | utils.defineGameObjectProperties(Nuke.prototype, data, { 36 | timeToLand: (o) => o.landTime - runtimeData.time, 37 | launchRoomName: o => o.launchRoomName 38 | }); 39 | 40 | Nuke.prototype.toString = register.wrapFn(function() { 41 | return `[nuke #${this.id}]`; 42 | }); 43 | 44 | Object.defineProperty(globals, 'Nuke', {enumerable: true, value: Nuke}); 45 | }; -------------------------------------------------------------------------------- /src/game/path-finder.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | let _ = require('lodash'); 3 | let kObstacle = Infinity; 4 | let utils = require('../utils'); 5 | let driver = utils.getRuntimeDriver(); 6 | let C = driver.constants; 7 | 8 | exports.make = function(runtimeData, intents, register, globals) { 9 | 10 | driver.pathFinder.make(globals); 11 | 12 | if(globals.PathFinder) { 13 | return; 14 | } 15 | 16 | // 17 | // 2d array of costs for pathfinding 18 | var CostMatrix = register.wrapFn(function() { 19 | this._bits = new Uint8Array(2500); 20 | }); 21 | 22 | CostMatrix.prototype.set = register.wrapFn(function(xx, yy, val) { 23 | xx = xx|0; 24 | yy = yy|0; 25 | this._bits[xx * 50 + yy] = Math.min(Math.max(0, val), 255); 26 | }); 27 | 28 | CostMatrix.prototype.get = register.wrapFn(function(xx, yy) { 29 | xx = xx|0; 30 | yy = yy|0; 31 | return this._bits[xx * 50 + yy]; 32 | }); 33 | 34 | CostMatrix.prototype.clone = register.wrapFn(function() { 35 | var newMatrix = new CostMatrix; 36 | newMatrix._bits = new Uint8Array(this._bits); 37 | return newMatrix; 38 | }); 39 | 40 | CostMatrix.prototype.serialize = register.wrapFn(function() { 41 | return Array.prototype.slice.apply(new Uint32Array(this._bits.buffer)); 42 | }); 43 | 44 | CostMatrix.deserialize = register.wrapFn(function(data) { 45 | let instance = Object.create(CostMatrix.prototype); 46 | instance._bits = new Uint8Array(new Uint32Array(data).buffer); 47 | return instance; 48 | }); 49 | 50 | var PathFinder = Object.create(Object.prototype, { 51 | 52 | CostMatrix: { 53 | enumerable: true, 54 | value: CostMatrix 55 | }, 56 | 57 | search: { 58 | enumerable: true, 59 | value: register.wrapFn(function (origin, goal, options) { 60 | if (!goal || Array.isArray(goal) && !goal.length) { 61 | return {path: [], ops: 0}; 62 | } 63 | return driver.pathFinder.search(origin, goal, options); 64 | }) 65 | }, 66 | 67 | use: { 68 | enumerable: true, 69 | value: register.wrapFn(function (isActive) { 70 | if (!isActive) { 71 | register.deprecated('`PathFinder.use` is considered deprecated and will be removed soon.'); 72 | } 73 | register._useNewPathFinder = !!isActive; 74 | }) 75 | } 76 | }); 77 | 78 | Object.defineProperty(globals, 'PathFinder', {enumerable: true, value: PathFinder}); 79 | 80 | }; 81 | -------------------------------------------------------------------------------- /src/game/path-utils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // 4 | // Simple open-closed list 5 | exports.OpenClosed = class OpenClosed { 6 | constructor(size) { 7 | this.list = new Uint8Array(size); 8 | this.marker = 1; 9 | } 10 | 11 | clear() { 12 | if (this.marker >= 253) { 13 | this.list = new Uint8Array(this.list.length); 14 | this.marker = 1; 15 | } else { 16 | this.marker += 2; 17 | } 18 | } 19 | 20 | isOpen(index) { 21 | return this.list[index] === this.marker; 22 | } 23 | 24 | isClosed(index) { 25 | return this.list[index] === this.marker + 1; 26 | } 27 | 28 | open(index) { 29 | this.list[index] = this.marker; 30 | } 31 | 32 | close(index) { 33 | this.list[index] = this.marker + 1; 34 | } 35 | } 36 | 37 | // 38 | // Priority queue implementation w/ support for updating priorities 39 | exports.Heap = class Heap { 40 | constructor(size, ctor) { 41 | this.priorities = new (ctor || Uint16Array)(size + 1); 42 | this.heap = new Uint16Array(size + 1); 43 | this.size_ = 0; 44 | } 45 | 46 | minPriority() { 47 | return this.priorities[this.heap[1]]; 48 | } 49 | 50 | min() { 51 | return this.heap[1]; 52 | } 53 | 54 | size() { 55 | return this.size_; 56 | } 57 | 58 | priority(index) { 59 | return this.priorities[index]; 60 | } 61 | 62 | pop() { 63 | this.heap[1] = this.heap[this.size_]; 64 | --this.size_; 65 | let vv = 1; 66 | do { 67 | let uu = vv; 68 | if ((uu << 1) + 1 <= this.size_) { 69 | if (this.priorities[this.heap[uu]] >= this.priorities[this.heap[uu << 1]]) { 70 | vv = uu << 1; 71 | } 72 | if (this.priorities[this.heap[vv]] >= this.priorities[this.heap[(uu << 1) + 1]]) { 73 | vv = (uu << 1) + 1; 74 | } 75 | } else if (uu << 1 <= this.size_) { 76 | if (this.priorities[this.heap[uu]] >= this.priorities[this.heap[uu << 1]]) { 77 | vv = uu << 1; 78 | } 79 | } 80 | if (uu !== vv) { 81 | let tmp = this.heap[uu]; 82 | this.heap[uu] = this.heap[vv]; 83 | this.heap[vv] = tmp; 84 | } else { 85 | return; 86 | } 87 | } while (true); 88 | } 89 | 90 | push(index, priority) { 91 | this.priorities[index] = priority; 92 | let ii = ++this.size_; 93 | this.heap[ii] = index; 94 | this.bubbleUp(ii); 95 | } 96 | 97 | update(index, priority) { 98 | for (let ii = this.size_; ii > 0; --ii) { 99 | if (this.heap[ii] === index) { 100 | this.priorities[index] = priority; 101 | this.bubbleUp(ii); 102 | return; 103 | } 104 | } 105 | } 106 | 107 | bubbleUp(ii) { 108 | while (ii !== 1) { 109 | if (this.priorities[this.heap[ii]] <= this.priorities[this.heap[ii >>> 1]]) { 110 | let tmp = this.heap[ii]; 111 | this.heap[ii] = this.heap[ii >>> 1]; 112 | this.heap[ii = ii >>> 1] = tmp; 113 | } else { 114 | return; 115 | } 116 | } 117 | } 118 | 119 | clear() { 120 | this.size_ = 0; 121 | } 122 | } 123 | 124 | -------------------------------------------------------------------------------- /src/game/resources.js: -------------------------------------------------------------------------------- 1 | var utils = require('./../utils'), 2 | rooms = require('./rooms'), 3 | driver = utils.getRuntimeDriver(), 4 | C = driver.constants; 5 | 6 | var runtimeData, intents, register, globals; 7 | 8 | exports.make = function(_runtimeData, _intents, _register, _globals) { 9 | 10 | runtimeData = _runtimeData; 11 | intents = _intents; 12 | register = _register; 13 | globals = _globals; 14 | 15 | if(globals.Resource) { 16 | return; 17 | } 18 | 19 | var data = (id) => { 20 | if(!runtimeData.roomObjects[id]) { 21 | throw new Error("Could not find an object with ID "+id); 22 | } 23 | return runtimeData.roomObjects[id]; 24 | }; 25 | 26 | var Resource = register.wrapFn(function(id) { 27 | var _data = data(id); 28 | globals.RoomObject.call(this, _data.x, _data.y, _data.room, _data.effects); 29 | this.id = id; 30 | }); 31 | 32 | Resource.prototype = Object.create(globals.RoomObject.prototype); 33 | Resource.prototype.constructor = Resource; 34 | 35 | utils.defineGameObjectProperties(Resource.prototype, data, { 36 | energy: (o) => o.energy, 37 | amount: (o) => o[o.resourceType || C.RESOURCE_ENERGY], 38 | resourceType: (o) => o.resourceType || C.RESOURCE_ENERGY 39 | }); 40 | 41 | Resource.prototype.toString = register.wrapFn(function() { 42 | return `[resource (${this.resourceType}) #${this.id}]`; 43 | }); 44 | 45 | Object.defineProperty(globals, 'Resource', {enumerable: true, value: Resource}); 46 | Object.defineProperty(globals, 'Energy', {enumerable: true, value: Resource}); 47 | }; -------------------------------------------------------------------------------- /src/game/ruins.js: -------------------------------------------------------------------------------- 1 | var utils = require('./../utils'), 2 | rooms = require('./rooms'), 3 | driver = utils.getRuntimeDriver(), 4 | C = driver.constants; 5 | 6 | var runtimeData, intents, register, globals; 7 | 8 | function _storeGetter(o) { 9 | return new globals.Store(o); 10 | } 11 | 12 | exports.make = function(_runtimeData, _intents, _register, _globals) { 13 | runtimeData = _runtimeData; 14 | intents = _intents; 15 | register = _register; 16 | globals = _globals; 17 | 18 | if(globals.Ruin) { 19 | return; 20 | } 21 | 22 | const data = (id) => { 23 | if(!runtimeData.roomObjects[id]) { 24 | throw new Error("Could not find an object with ID "+id); 25 | } 26 | return runtimeData.roomObjects[id]; 27 | }; 28 | 29 | const Ruin = register.wrapFn(function(id) { 30 | const _data = data(id); 31 | globals.RoomObject.call(this, _data.x, _data.y, _data.room, _data.effects); 32 | this.id = id; 33 | }); 34 | 35 | Ruin.prototype = Object.create(globals.RoomObject.prototype); 36 | Ruin.prototype.constructor = Ruin; 37 | 38 | utils.defineGameObjectProperties(Ruin.prototype, data, { 39 | structureType: o => o.structureType, 40 | destroyTime: o => o.destroyTime, 41 | ticksToDecay: o => o.decayTime - runtimeData.time, 42 | store: _storeGetter, 43 | structure: o => { 44 | if(o.structure.user) { 45 | const structure = new globals.OwnedStructure(); 46 | Object.defineProperties(structure, { 47 | id: { enumerable: true, get() { return o.structure.id; }}, 48 | hits: { enumerable: true, get() { return o.structure.hits; }}, 49 | hitsMax: { enumerable: true, get() { return o.structure.hitsMax; }}, 50 | structureType: { enumerable: true, get() { return o.structure.type }}, 51 | owner: { enumerable: true, get() { return { username: runtimeData.users[o.structure.user].username } }}, 52 | my: { enumerable: true, get() { return o.structure.user == runtimeData.user._id }} 53 | }); 54 | return structure; 55 | } 56 | 57 | const structure = new globals.Structure(); 58 | Object.defineProperties(structure, { 59 | id: { enumerable: true, get() { return o.structure.id; }}, 60 | hits: { enumerable: true, get() { return o.structure.hits }}, 61 | hitsMax: { enumerable: true, get() { return o.structure.hitsMax }}, 62 | structureType: { enumerable: true, get() { return o.structure.type }} 63 | }); 64 | return structure; 65 | } 66 | }); 67 | 68 | Ruin.prototype.toString = register.wrapFn(function() { 69 | return `[ruin (${this.structure.structureType}) #${this.id}]`; 70 | }); 71 | 72 | Object.defineProperty(globals, 'Ruin', {enumerable: true, value: Ruin}); 73 | }; 74 | -------------------------------------------------------------------------------- /src/game/sources.js: -------------------------------------------------------------------------------- 1 | var utils = require('./../utils'), 2 | rooms = require('./rooms'), 3 | driver = utils.getRuntimeDriver(), 4 | C = driver.constants; 5 | 6 | var runtimeData, intents, register, globals; 7 | 8 | exports.make = function(_runtimeData, _intents, _register, _globals) { 9 | 10 | runtimeData = _runtimeData; 11 | intents = _intents; 12 | register = _register; 13 | globals = _globals; 14 | 15 | if(globals.Source) { 16 | return; 17 | } 18 | 19 | var data = (id) => { 20 | if(!runtimeData.roomObjects[id]) { 21 | throw new Error("Could not find an object with ID "+id); 22 | } 23 | return runtimeData.roomObjects[id]; 24 | }; 25 | 26 | var Source = register.wrapFn(function(id) { 27 | var _data = data(id); 28 | globals.RoomObject.call(this, _data.x, _data.y, _data.room, _data.effects); 29 | this.id = id; 30 | }); 31 | 32 | Source.prototype = Object.create(globals.RoomObject.prototype); 33 | Source.prototype.constructor = Source; 34 | 35 | utils.defineGameObjectProperties(Source.prototype, data, { 36 | energy: (o) => o.energy, 37 | energyCapacity: (o) => o.energyCapacity, 38 | ticksToRegeneration: (o) => o.nextRegenerationTime ? o.nextRegenerationTime - runtimeData.time : undefined 39 | }); 40 | 41 | Source.prototype.toString = register.wrapFn(function() { 42 | return `[source #${this.id}]`; 43 | }); 44 | 45 | Object.defineProperty(globals, 'Source', {enumerable: true, value: Source}); 46 | }; -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | exports.game = require('./game/game'); 2 | exports.utils = require('./utils'); 3 | exports.console = require('./game/console'); -------------------------------------------------------------------------------- /src/processor/global-intents/power.js: -------------------------------------------------------------------------------- 1 | var q = require('q'), 2 | _ = require('lodash'), 3 | utils = require('../../utils'), 4 | driver = utils.getDriver(), 5 | path = require('path'), 6 | C = driver.constants; 7 | 8 | const intentTypes = ['spawnPowerCreep','suicidePowerCreep','deletePowerCreep','upgradePowerCreep','createPowerCreep','renamePowerCreep']; 9 | 10 | var modules = require('bulk-require')(path.resolve(__dirname, 'power'), ['*.js']); 11 | 12 | module.exports = function(scope) { 13 | 14 | const {usersById, userIntents, roomObjectsByType, gameTime} = scope; 15 | 16 | if(userIntents) { 17 | userIntents.forEach(iUserIntents => { 18 | var user = usersById[iUserIntents.user]; 19 | 20 | intentTypes.forEach(intentType => { 21 | if(iUserIntents.intents[intentType]) { 22 | iUserIntents.intents[intentType].forEach(intent => { 23 | modules[intentType](intent, user, scope); 24 | }) 25 | } 26 | }); 27 | }) 28 | } 29 | 30 | if(roomObjectsByType.powerCreep) { 31 | roomObjectsByType.powerCreep.forEach(creep => { 32 | if(gameTime >= creep.ageTime-1 || creep.hits <= 0) { 33 | require('./power/_diePowerCreep')(creep, scope); 34 | } 35 | }) 36 | } 37 | }; -------------------------------------------------------------------------------- /src/processor/global-intents/power/_diePowerCreep.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, {roomObjects, bulkObjects, bulkUsersPowerCreeps, gameTime}) { 7 | 8 | let tombstone = { 9 | type: 'tombstone', 10 | room: object.room, 11 | x: object.x, 12 | y: object.y, 13 | user: object.user, 14 | deathTime: gameTime, 15 | decayTime: gameTime + C.TOMBSTONE_DECAY_POWER_CREEP, 16 | store: {}, 17 | powerCreepId: ""+object._id, 18 | powerCreepName: object.name, 19 | powerCreepTicksToLive: object.ageTime - gameTime, 20 | powerCreepClassName: object.className, 21 | powerCreepLevel: object.level, 22 | powerCreepPowers: _.mapValues(object.powers, i => ({level: i.level})), 23 | powerCreepSaying: object.actionLog && object.actionLog.say && object.actionLog.say.isPublic ? object.actionLog.say.message : undefined 24 | }; 25 | 26 | let container = _.find(roomObjects, { type: 'container', x: object.x, y: object.y }); 27 | if(container) { 28 | container.store = container.store || {}; 29 | } 30 | 31 | if(object.store) { 32 | _.forEach(object.store, (amount, resourceType) => { 33 | if(amount <= 0) { 34 | return; 35 | } 36 | if(container && container.hits > 0) { 37 | const targetTotal = utils.calcResources(container); 38 | const toContainerAmount = Math.min(amount, container.storeCapacity - targetTotal); 39 | if(toContainerAmount > 0) { 40 | container.store[resourceType] = (container.store[resourceType] || 0) + toContainerAmount; 41 | bulkObjects.update(container, {store: {[resourceType]: container.store[resourceType]}}); 42 | amount -= toContainerAmount; 43 | } 44 | } 45 | if(amount > 0){ 46 | tombstone.store[resourceType] = (tombstone.store[resourceType] || 0) + amount; 47 | } 48 | }) 49 | } 50 | 51 | bulkObjects.insert(tombstone); 52 | 53 | bulkObjects.remove(object._id); 54 | 55 | bulkUsersPowerCreeps.update(object._id, { 56 | shard: null, 57 | spawnCooldownTime: Date.now() + C.POWER_CREEP_SPAWN_COOLDOWN 58 | }); 59 | }; 60 | -------------------------------------------------------------------------------- /src/processor/global-intents/power/createPowerCreep.js: -------------------------------------------------------------------------------- 1 | var q = require('q'), 2 | _ = require('lodash'), 3 | utils = require('../../../utils'), 4 | driver = utils.getDriver(), 5 | C = driver.constants; 6 | 7 | module.exports = function(intent, user, {userPowerCreeps, bulkUsersPowerCreeps}) { 8 | 9 | var thisUserPowerCreeps = _.filter(userPowerCreeps, i => i.user == user._id); 10 | 11 | var powerLevel = Math.floor(Math.pow((user.power || 0) / C.POWER_LEVEL_MULTIPLY, 1 / C.POWER_LEVEL_POW)); 12 | var used = thisUserPowerCreeps.length + _.sum(thisUserPowerCreeps, 'level') + (user._usedPowerLevels||0); 13 | if(used >= powerLevel) { 14 | return; 15 | } 16 | 17 | if(Object.values(C.POWER_CLASS).indexOf(intent.className) === -1) { 18 | return; 19 | } 20 | 21 | var name = intent.name.substring(0,50); 22 | 23 | if(_.any(thisUserPowerCreeps, {name})) { 24 | return; 25 | } 26 | 27 | bulkUsersPowerCreeps.insert({ 28 | name, 29 | className: intent.className, 30 | user: ""+user._id, 31 | level: 0, 32 | hitsMax: 1000, 33 | store: {}, 34 | storeCapacity: 100, 35 | spawnCooldownTime: 0, 36 | powers: {} 37 | }); 38 | 39 | user._usedPowerLevels = (user._usedPowerLevels||0) + 1; 40 | }; 41 | -------------------------------------------------------------------------------- /src/processor/global-intents/power/deletePowerCreep.js: -------------------------------------------------------------------------------- 1 | var q = require('q'), 2 | _ = require('lodash'), 3 | utils = require('../../../utils'), 4 | driver = utils.getDriver(), 5 | C = driver.constants; 6 | 7 | module.exports = function(intent, user, {userPowerCreeps, bulkObjects, bulkUsersPowerCreeps}) { 8 | 9 | var powerCreep = _.find(userPowerCreeps, i => i.user == user._id && i._id == intent.id); 10 | 11 | if (!powerCreep || powerCreep.spawnCooldownTime === null) { 12 | return; 13 | } 14 | 15 | if(intent.cancel) { 16 | bulkUsersPowerCreeps.update(powerCreep._id, {deleteTime: null}); 17 | } 18 | else { 19 | console.log(user.powerExperimentationTime); 20 | if(user.powerExperimentationTime > Date.now()) { 21 | bulkUsersPowerCreeps.remove(powerCreep._id); 22 | return; 23 | } 24 | if (powerCreep.deleteTime) { 25 | return; 26 | } 27 | bulkUsersPowerCreeps.update(powerCreep._id, {deleteTime: Date.now() + C.POWER_CREEP_DELETE_COOLDOWN}); 28 | } 29 | }; -------------------------------------------------------------------------------- /src/processor/global-intents/power/renamePowerCreep.js: -------------------------------------------------------------------------------- 1 | var q = require('q'), 2 | _ = require('lodash'), 3 | utils = require('../../../utils'), 4 | driver = utils.getDriver(), 5 | C = driver.constants; 6 | 7 | module.exports = function(intent, user, {userPowerCreeps, bulkObjects, bulkUsersPowerCreeps}) { 8 | 9 | var thisUserPowerCreeps = _.filter(userPowerCreeps, i => i.user == user._id); 10 | var powerCreep = _.find(thisUserPowerCreeps, i => i._id == intent.id); 11 | 12 | if (!powerCreep || powerCreep.spawnCooldownTime === null) { 13 | return; 14 | } 15 | 16 | var name = intent.name.substring(0,50); 17 | 18 | if(_.any(thisUserPowerCreeps, {name})) { 19 | return; 20 | } 21 | 22 | bulkUsersPowerCreeps.update(powerCreep._id, {name}); 23 | }; -------------------------------------------------------------------------------- /src/processor/global-intents/power/spawnPowerCreep.js: -------------------------------------------------------------------------------- 1 | var q = require('q'), 2 | _ = require('lodash'), 3 | utils = require('../../../utils'), 4 | driver = utils.getDriver(), 5 | C = driver.constants; 6 | 7 | module.exports = function(intent, user, {roomObjectsByType, userPowerCreeps, bulkObjects, 8 | bulkUsersPowerCreeps, shardName, gameTime}) { 9 | 10 | const powerSpawn = _.find(roomObjectsByType.powerSpawn, i => i._id == intent.id); 11 | if(!powerSpawn || powerSpawn.user != user._id || powerSpawn._justSpawned) 12 | return; 13 | 14 | 15 | var powerCreep = _.find(userPowerCreeps, i => i.user == user._id && i.name == intent.name); 16 | if (!powerCreep || powerCreep.spawnCooldownTime === null || powerCreep.spawnCooldownTime > Date.now()) { 17 | return; 18 | } 19 | 20 | if(_.any(roomObjectsByType.powerCreep, {room: powerSpawn.room, x: powerSpawn.x, y: powerSpawn.y})) { 21 | return; 22 | } 23 | 24 | bulkUsersPowerCreeps.update(powerCreep, { 25 | shard: shardName, 26 | spawnCooldownTime: null, 27 | deleteTime: null 28 | }); 29 | 30 | bulkObjects.insert(Object.assign({}, powerCreep, { 31 | type: 'powerCreep', 32 | room: powerSpawn.room, 33 | x: powerSpawn.x, 34 | y: powerSpawn.y, 35 | hits: powerCreep.hitsMax, 36 | ageTime: gameTime + C.POWER_CREEP_LIFE_TIME, 37 | actionLog: {spawned: true}, 38 | notifyWhenAttacked: true 39 | }), powerCreep._id); 40 | 41 | powerSpawn._justSpawned = true; 42 | }; -------------------------------------------------------------------------------- /src/processor/global-intents/power/suicidePowerCreep.js: -------------------------------------------------------------------------------- 1 | var q = require('q'), 2 | _ = require('lodash'), 3 | utils = require('../../../utils'), 4 | driver = utils.getDriver(), 5 | C = driver.constants; 6 | 7 | module.exports = function(intent, user, scope) { 8 | 9 | const {roomObjectsByType} = scope; 10 | 11 | var powerCreep = _.find(roomObjectsByType.powerCreep, i => i.user == user._id && i._id == intent.id); 12 | if (!powerCreep) { 13 | return; 14 | } 15 | 16 | require('./_diePowerCreep')(powerCreep, scope); 17 | }; -------------------------------------------------------------------------------- /src/processor/global-intents/power/upgradePowerCreep.js: -------------------------------------------------------------------------------- 1 | var q = require('q'), 2 | _ = require('lodash'), 3 | utils = require('../../../utils'), 4 | driver = utils.getDriver(), 5 | C = driver.constants; 6 | 7 | module.exports = function(intent, user, {roomObjectsByType, userPowerCreeps, gameTime, bulkObjects, bulkUsersPowerCreeps}) { 8 | 9 | var thisUserPowerCreeps = _.filter(userPowerCreeps, i => i.user == user._id); 10 | 11 | var powerLevel = Math.floor(Math.pow((user.power || 0) / C.POWER_LEVEL_MULTIPLY, 1 / C.POWER_LEVEL_POW)); 12 | var used = thisUserPowerCreeps.length + _.sum(thisUserPowerCreeps, 'level') + (user._usedPowerLevels||0); 13 | if(used >= powerLevel) { 14 | return; 15 | } 16 | 17 | var powerCreep = _.find(thisUserPowerCreeps, i => i._id == intent.id); 18 | if (!powerCreep) { 19 | return; 20 | } 21 | 22 | if(powerCreep.level >= C.POWER_CREEP_MAX_LEVEL) { 23 | return; 24 | } 25 | var powerInfo = C.POWER_INFO[intent.power]; 26 | if(!powerInfo) { 27 | return; 28 | } 29 | if(powerInfo.className !== powerCreep.className) { 30 | return; 31 | } 32 | 33 | let level = powerCreep.level; 34 | if(!powerCreep.powers[intent.power]) { 35 | powerCreep.powers[intent.power] = {level: 0}; 36 | } 37 | if(powerCreep.powers[intent.power].level == 5) { 38 | return; 39 | } 40 | 41 | if(level < powerInfo.level[powerCreep.powers[intent.power].level]) { 42 | return; 43 | } 44 | 45 | level++; 46 | let storeCapacity = powerCreep.storeCapacity + 100; 47 | let hitsMax = powerCreep.hitsMax + 1000; 48 | powerCreep.powers[intent.power].level++; 49 | 50 | let roomPowerCreep = _.find(roomObjectsByType.powerCreep, i => i._id == intent.id); 51 | if(roomPowerCreep) { 52 | bulkObjects.update(roomPowerCreep, { 53 | level, 54 | hitsMax, 55 | storeCapacity, 56 | powers: powerCreep.powers 57 | }); 58 | } 59 | 60 | bulkUsersPowerCreeps.update(powerCreep, { 61 | level, 62 | hitsMax, 63 | storeCapacity, 64 | powers: powerCreep.powers 65 | }); 66 | }; 67 | -------------------------------------------------------------------------------- /src/processor/global.js: -------------------------------------------------------------------------------- 1 | var q = require('q'), 2 | _ = require('lodash'), 3 | utils = require('../utils'), 4 | driver = utils.getDriver(), 5 | C = driver.constants, 6 | marketProcessor = require('./global-intents/market'), 7 | powerProcessor = require('./global-intents/power'); 8 | 9 | module.exports = () => { 10 | 11 | return driver.getInterRoom().then((data) => { 12 | if(!data) { 13 | return; 14 | } 15 | 16 | var [gameTime,creeps,accessibleRooms,roomObjects,{orders,users,userPowerCreeps,userIntents,shardName}={}] = data; 17 | 18 | var bulkObjects = driver.bulkObjectsWrite(), 19 | bulkRooms = driver.bulkRoomsWrite(), 20 | bulkUsers = driver.bulkUsersWrite(), 21 | bulkTransactions = driver.bulkTransactionsWrite(), 22 | bulkUsersMoney = driver.bulkUsersMoney(), 23 | bulkUsersResources = driver.bulkUsersResources(), 24 | bulkUsersPowerCreeps = driver.bulkUsersPowerCreeps(), 25 | bulkMarketOrders = driver.bulkMarketOrders(), 26 | bulkMarketIntershardOrders = driver.bulkMarketIntershardOrders(), 27 | activateRooms = {}, 28 | usersById = _.indexBy(users, '_id'), 29 | roomObjectsByType = _.groupBy(roomObjects, 'type'); 30 | 31 | // creeps 32 | 33 | creeps.forEach((creep) => { 34 | if(!accessibleRooms[creep.interRoom.room]) { 35 | return; 36 | } 37 | if(!activateRooms[creep.interRoom.room]) { 38 | driver.activateRoom(creep.interRoom.room); 39 | } 40 | activateRooms[creep.interRoom.room] = true; 41 | 42 | bulkObjects.update(creep, {room: creep.interRoom.room, x: creep.interRoom.x, y: creep.interRoom.y, interRoom: null}); 43 | }); 44 | 45 | powerProcessor({userIntents, usersById, roomObjectsByType, userPowerCreeps, gameTime, 46 | bulkObjects, bulkUsers, bulkUsersPowerCreeps, shardName}); 47 | 48 | marketProcessor({orders, userIntents, usersById, gameTime, roomObjectsByType, bulkObjects, bulkUsers, bulkTransactions, 49 | bulkUsersMoney, bulkUsersResources, bulkMarketOrders, bulkMarketIntershardOrders}); 50 | 51 | return q.all([ 52 | bulkObjects.execute(), 53 | bulkRooms.execute(), 54 | bulkUsers.execute(), 55 | bulkMarketOrders.execute(), 56 | bulkMarketIntershardOrders.execute(), 57 | bulkUsersMoney.execute(), 58 | bulkTransactions.execute(), 59 | bulkUsersResources.execute(), 60 | bulkUsersPowerCreeps.execute(), 61 | driver.clearGlobalIntents() 62 | ]); 63 | }); 64 | }; 65 | 66 | -------------------------------------------------------------------------------- /src/processor/intents/_calc_spawns.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(roomSpawns, roomExtensions, {roomController, bulk}) { 7 | var spawns = roomSpawns; 8 | 9 | if(spawns.length > C.CONTROLLER_STRUCTURES.spawn[roomController.level|0]) { 10 | spawns.sort(utils.comparatorDistance(roomController)); 11 | spawns = _.take(spawns, C.CONTROLLER_STRUCTURES.spawn[roomController.level|0]); 12 | roomSpawns.forEach(i => i._off = !_.contains(spawns, i)); 13 | } 14 | else { 15 | roomSpawns.forEach(i => i._off = false); 16 | } 17 | 18 | roomSpawns.forEach(i => { 19 | if(i._off !== i.off) { 20 | bulk.update(i._id, {off: i._off}); 21 | } 22 | }); 23 | 24 | 25 | var extensions = roomExtensions; 26 | 27 | if(extensions.length > C.CONTROLLER_STRUCTURES.extension[roomController.level|0]) { 28 | extensions.sort(utils.comparatorDistance(roomController)); 29 | extensions = _.take(extensions, C.CONTROLLER_STRUCTURES.extension[roomController.level|0]); 30 | roomExtensions.forEach(i => i._off = !_.contains(extensions, i)); 31 | } 32 | else { 33 | roomExtensions.forEach(i => i._off = false); 34 | } 35 | 36 | roomExtensions.forEach(i => { 37 | if(i._off !== i.off) { 38 | bulk.update(i._id, {off: i._off}); 39 | } 40 | }); 41 | }; -------------------------------------------------------------------------------- /src/processor/intents/_create-energy.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(x, y, room, amount, resourceType, scope) { 7 | 8 | const {roomObjects, bulk} = scope; 9 | 10 | 11 | 12 | resourceType = resourceType || 'energy'; 13 | 14 | amount = Math.round(amount); 15 | 16 | if(amount <= 0) { 17 | return; 18 | } 19 | 20 | var container = _.find(roomObjects, {type: 'container', x, y}); 21 | 22 | if(container && container.hits > 0) { 23 | container.store = container.store || {}; 24 | var targetTotal = utils.calcResources(container); 25 | var toContainerAmount = Math.min(amount, container.storeCapacity - targetTotal); 26 | if(toContainerAmount > 0) { 27 | container.store[resourceType] = (container.store[resourceType] || 0) + toContainerAmount; 28 | bulk.update(container, {store: {[resourceType]: container.store[resourceType]}}); 29 | amount -= toContainerAmount; 30 | } 31 | } 32 | 33 | if(amount > 0) { 34 | 35 | var existingDrop = _.find(roomObjects, {type: 'energy', x, y, resourceType}); 36 | if (existingDrop) { 37 | bulk.update(existingDrop, { 38 | [resourceType]: existingDrop[resourceType] + amount 39 | }); 40 | } 41 | else { 42 | const obj = { 43 | type: 'energy', 44 | x, y, 45 | room: room, 46 | [resourceType]: amount, 47 | resourceType 48 | }; 49 | obj._id = bulk.insert(obj); 50 | roomObjects[obj._id] = obj; 51 | } 52 | } 53 | }; 54 | -------------------------------------------------------------------------------- /src/processor/intents/constructedWalls/tick.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, {roomObjects, bulk, roomController, gameTime}) { 7 | 8 | if(!object || object.type != 'constructedWall') return; 9 | 10 | if(roomController && !object.newbieWall) { 11 | var hitsMax = C.CONTROLLER_STRUCTURES.constructedWall[roomController.level] ? C.WALL_HITS_MAX : 0; 12 | if(hitsMax != object.hitsMax) { 13 | bulk.update(object, {hitsMax}); 14 | } 15 | } 16 | 17 | if(object.ticksToLive > 0) { 18 | bulk.update(object, { 19 | decayTime: gameTime + object.ticksToLive, 20 | ticksToLive: null 21 | }); 22 | } 23 | 24 | if(!object.decayTime) { 25 | return; 26 | } 27 | 28 | if(!_.isObject(object.decayTime)) { 29 | if (gameTime >= object.decayTime - 1 || roomController && !roomController.user) { 30 | bulk.remove(object._id); 31 | delete roomObjects[object._id]; 32 | } 33 | 34 | if (object.user && gameTime == object.decayTime - 5000) { 35 | driver.sendNotification(object.user, "Attention! Your room protection will be removed soon.\nLearn how to defend your room against intruders from this article."); 36 | } 37 | } 38 | 39 | if(_.isObject(object.decayTime)) { 40 | if(Date.now() > object.decayTime.timestamp) { 41 | bulk.remove(object._id); 42 | delete roomObjects[object._id]; 43 | } 44 | } 45 | 46 | }; -------------------------------------------------------------------------------- /src/processor/intents/construction-sites/tick.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, {}) { 7 | 8 | if(!object || object.type != 'constructionSite') return; 9 | 10 | 11 | }; -------------------------------------------------------------------------------- /src/processor/intents/containers/tick.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, scope) { 7 | 8 | const {roomObjects, bulk, roomController, gameTime, eventLog} = scope; 9 | 10 | if(!object.nextDecayTime || gameTime >= object.nextDecayTime-1) { 11 | object.hits = object.hits || 0; 12 | object.hits -= C.CONTAINER_DECAY; 13 | if(object.hits <= 0) { 14 | if(object.store) { 15 | _.forEach(object.store, (amount, resourceType) => { 16 | if (amount > 0) { 17 | require('../_create-energy')(object.x, object.y, object.room, amount, resourceType, scope); 18 | } 19 | }); 20 | } 21 | 22 | bulk.remove(object._id); 23 | delete roomObjects[object._id]; 24 | } 25 | else { 26 | object.nextDecayTime = gameTime + (roomController && roomController.level > 0 ? C.CONTAINER_DECAY_TIME_OWNED : C.CONTAINER_DECAY_TIME); 27 | bulk.update(object, { 28 | hits: object.hits, 29 | nextDecayTime: object.nextDecayTime 30 | }); 31 | } 32 | } 33 | 34 | 35 | }; 36 | -------------------------------------------------------------------------------- /src/processor/intents/controllers/activateSafeMode.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, {bulk, gameTime, roomInfo}) { 7 | 8 | if(!object.user || !object.level) { 9 | return; 10 | } 11 | if(!(object.safeModeAvailable > 0)) { 12 | return; 13 | } 14 | if(object.safeModeCooldown >= gameTime) { 15 | return; 16 | } 17 | if(object.upgradeBlocked > gameTime) { 18 | return; 19 | } 20 | if(object.downgradeTime < gameTime + C.CONTROLLER_DOWNGRADE[object.level]/2 - C.CONTROLLER_DOWNGRADE_SAFEMODE_THRESHOLD) { 21 | return; 22 | } 23 | 24 | object._safeModeActivated = 1; 25 | }; 26 | -------------------------------------------------------------------------------- /src/processor/intents/controllers/unclaim.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, {bulk, bulkUsers, gameTime, roomInfo, users}) { 7 | 8 | if(object.type != 'controller') { 9 | return; 10 | } 11 | 12 | if(!object.user || !object.level) { 13 | return; 14 | } 15 | 16 | driver.removeRoomFromUser(object.room, users[object.user], bulkUsers); 17 | 18 | bulk.update(object, { 19 | user: null, 20 | level: 0, 21 | progress: 0, 22 | downgradeTime: null, 23 | safeMode: null, 24 | safeModeAvailable: 0, 25 | safeModeCooldown: roomInfo.novice > Date.now() ? null : gameTime + C.SAFE_MODE_COOLDOWN, 26 | isPowerEnabled: false, 27 | }); 28 | }; -------------------------------------------------------------------------------- /src/processor/intents/creeps/_add-fatigue.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, dFatigue, {roomObjects, bulk}) { 7 | if(_.isUndefined(object._fatigue)) { object._fatigue = object.fatigue } 8 | 9 | if((object._fatigue > 0) && (dFatigue < 0)) { // MOVES contribution removes own fatigue first 10 | const resting = Math.min(object._fatigue, -dFatigue); 11 | object._fatigue -= resting; 12 | dFatigue += resting; 13 | 14 | const fatigue = Math.max(0, object._fatigue); 15 | if(object.fatigue != fatigue) { 16 | bulk.update(object, { fatigue }); 17 | } 18 | 19 | if(dFatigue == 0) { 20 | return; 21 | } 22 | } 23 | 24 | while(!!object._pulled && !!roomObjects[object._pulled]) { 25 | object = roomObjects[object._pulled]; 26 | } 27 | 28 | if(_.isUndefined(object._fatigue)) { object._fatigue = object.fatigue } 29 | object._fatigue += dFatigue; 30 | 31 | const fatigue = Math.max(0, object._fatigue); 32 | if(object.fatigue != fatigue) { 33 | bulk.update(object, { fatigue }); 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /src/processor/intents/creeps/_clear-newbie-walls.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function({roomObjects, bulk}) { 7 | 8 | _.forEach(roomObjects, (i) => { 9 | if(i.type == 'constructedWall' && i.decayTime && i.user) { 10 | bulk.remove(i._id); 11 | delete roomObjects[i._id]; 12 | } 13 | }); 14 | }; -------------------------------------------------------------------------------- /src/processor/intents/creeps/_drop-resources-without-space.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function dropResourcesWithoutSpace(object, scope) { 7 | for(var i=0; i=0; i--) { 11 | object.body[i]._oldHits = object.body[i]._oldHits || object.body[i].hits; 12 | if(hits > 100) 13 | object.body[i].hits = 100; 14 | else 15 | object.body[i].hits = hits; 16 | hits -= 100; 17 | if(hits < 0) hits = 0; 18 | } 19 | 20 | if(!object.noCapacityRecalc) { 21 | object.storeCapacity = utils.calcBodyEffectiveness(object.body, C.CARRY, 'capacity', C.CARRY_CAPACITY, true); 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /src/processor/intents/creeps/attack.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, scope) { 7 | 8 | let {roomObjects, roomController, gameTime} = scope; 9 | 10 | if(object.type != 'creep') { 11 | return; 12 | } 13 | if(object.spawning) { 14 | return; 15 | } 16 | 17 | var target = roomObjects[intent.id]; 18 | if(!target || target == object) { 19 | return; 20 | } 21 | if(Math.abs(target.x - object.x) > 1 || Math.abs(target.y - object.y) > 1) { 22 | return; 23 | } 24 | if(target.type == 'creep' && target.spawning) { 25 | return; 26 | } 27 | if(!target.hits) { 28 | return; 29 | } 30 | if(roomController && roomController.user != object.user && roomController.safeMode > gameTime) { 31 | return; 32 | } 33 | var rampart = _.find(roomObjects, {type: 'rampart', x: target.x, y: target.y}); 34 | if(rampart) { 35 | target = rampart; 36 | } 37 | 38 | var attackPower = utils.calcBodyEffectiveness(object.body, C.ATTACK, 'attack', C.ATTACK_POWER); 39 | 40 | require('../_damage')(object, target, attackPower, C.EVENT_ATTACK_TYPE_MELEE, scope); 41 | 42 | object._attack = true; 43 | 44 | 45 | }; -------------------------------------------------------------------------------- /src/processor/intents/creeps/attackController.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, {roomObjects, bulk, roomController, gameTime, eventLog}) { 7 | 8 | if(object.type != 'creep') { 9 | return; 10 | } 11 | if(object.spawning) { 12 | return; 13 | } 14 | 15 | var target = roomObjects[intent.id]; 16 | if(!target || target.type != 'controller') { 17 | return; 18 | } 19 | if(Math.abs(target.x - object.x) > 1 || Math.abs(target.y - object.y) > 1) { 20 | return; 21 | } 22 | if(!target.user && !target.reservation) { 23 | return; 24 | } 25 | if(roomController && roomController.user != object.user && roomController.safeMode > gameTime || 26 | roomController.upgradeBlocked > gameTime) { 27 | return; 28 | } 29 | if(_.any(target.effects, e => e.effect == C.EFFECT_INVULNERABILITY && e.endTime > gameTime)) { 30 | return; 31 | } 32 | 33 | if(target.reservation) { 34 | var effect = Math.floor(_.filter(object.body, (i) => i.hits > 0 && i.type == C.CLAIM).length * C.CONTROLLER_RESERVE); 35 | if(!effect) { 36 | return; 37 | } 38 | var endTime = target.reservation.endTime - effect; 39 | bulk.update(target, {reservation: {endTime}}); 40 | } 41 | if(target.user) { 42 | var effect = Math.floor(_.filter(object.body, (i) => i.hits > 0 && i.type == C.CLAIM).length * C.CONTROLLER_CLAIM_DOWNGRADE); 43 | if(!effect) { 44 | return; 45 | } 46 | var downgradeTime = target.downgradeTime - effect; 47 | bulk.update(target, {downgradeTime}); 48 | target._upgradeBlocked = gameTime + C.CONTROLLER_ATTACK_BLOCKED_UPGRADE; 49 | } 50 | object.actionLog.attack = {x: target.x, y: target.y}; 51 | 52 | eventLog.push({event: C.EVENT_ATTACK_CONTROLLER, objectId: object._id}) 53 | }; -------------------------------------------------------------------------------- /src/processor/intents/creeps/claimController.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, {roomObjects, bulk, bulkUsers, users}) { 7 | 8 | if(object.type != 'creep') { 9 | return; 10 | } 11 | if(object.spawning) { 12 | return; 13 | } 14 | 15 | var target = roomObjects[intent.id]; 16 | if(!target || target.type != 'controller') { 17 | return; 18 | } 19 | if(Math.abs(target.x - object.x) > 1 || Math.abs(target.y - object.y) > 1) { 20 | return; 21 | } 22 | if(target.bindUser && object.user != target.bindUser) { 23 | return; 24 | } 25 | if(target.level > 0) { 26 | return; 27 | } 28 | if ((_.filter(object.body, (i) => i.hits > 0 && i.type == C.CLAIM).length) === 0) { 29 | return; 30 | } 31 | if(target.reservation && target.reservation.user != object.user) { 32 | return; 33 | } 34 | let user = users[object.user], 35 | claimedRooms = user.rooms ? user.rooms.length : 0; 36 | 37 | if(user.gcl < utils.calcNeededGcl(claimedRooms + 1)) { 38 | return; 39 | } 40 | 41 | var level = 1; 42 | 43 | bulk.update(target, { 44 | user: object.user, 45 | level, 46 | progress: 0, 47 | downgradeTime: null, 48 | reservation: null 49 | }); 50 | 51 | driver.addRoomToUser(object.room, users[object.user], bulkUsers); 52 | }; -------------------------------------------------------------------------------- /src/processor/intents/creeps/dismantle.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, scope) { 7 | 8 | const {roomObjects, roomTerrain, bulk, roomController, gameTime} = scope; 9 | 10 | if(object.type != 'creep') { 11 | return; 12 | } 13 | if(object.spawning) { 14 | return; 15 | } 16 | 17 | var target = roomObjects[intent.id]; 18 | if(!target || !C.CONSTRUCTION_COST[target.type]) { 19 | return; 20 | } 21 | if(Math.abs(target.x - object.x) > 1 || Math.abs(target.y - object.y) > 1) { 22 | return; 23 | } 24 | if(roomController && roomController.user != object.user && roomController.safeMode > gameTime) { 25 | return; 26 | } 27 | var rampart = _.find(roomObjects, {type: 'rampart', x: target.x, y: target.y}); 28 | if(rampart) { 29 | target = rampart; 30 | } 31 | 32 | 33 | var power = utils.calcBodyEffectiveness(object.body, C.WORK, 'dismantle', C.DISMANTLE_POWER), 34 | amount = Math.min(power, target.hits), 35 | energyGain = Math.floor(amount * C.DISMANTLE_COST); 36 | 37 | var effect = _.find(target.effects, e => e.endTime >= gameTime && (e.power == C.PWR_SHIELD || e.power == C.PWR_FORTIFY || e.effect == C.EFFECT_INVULNERABILITY)); 38 | if(effect) { 39 | energyGain = 0; 40 | } 41 | 42 | if(amount) { 43 | object.store = object.store || {}; 44 | object.store.energy += energyGain; 45 | bulk.update(object, {store:{energy: object.store.energy}}); 46 | 47 | const usedSpace = utils.calcResources(object); 48 | if (usedSpace > object.storeCapacity) { 49 | require('./drop')(object, {amount: usedSpace - object.storeCapacity, resourceType: 'energy'}, scope); 50 | } 51 | 52 | require('../_damage')(object, target, amount, C.EVENT_ATTACK_TYPE_DISMANTLE, scope); 53 | } 54 | }; 55 | -------------------------------------------------------------------------------- /src/processor/intents/creeps/drop.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, scope) { 7 | 8 | const {bulk} = scope; 9 | 10 | if(!_.contains(C.RESOURCES_ALL, intent.resourceType)) { 11 | return; 12 | } 13 | if(object.spawning || !object.store || !(object.store[intent.resourceType] >= intent.amount) ) { 14 | return; 15 | } 16 | 17 | if(intent.amount > 0) { 18 | object.store[intent.resourceType] -= intent.amount; 19 | require('../_create-energy')(object.x, object.y, object.room, intent.amount, intent.resourceType, scope); 20 | } 21 | 22 | bulk.update(object, {store:{[intent.resourceType]: object.store[intent.resourceType]}}); 23 | }; 24 | -------------------------------------------------------------------------------- /src/processor/intents/creeps/generateSafeMode.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, {roomObjects, bulk}) { 7 | 8 | if(object.spawning) { 9 | return; 10 | } 11 | 12 | var target = roomObjects[intent.id]; 13 | if(!target || target.type != 'controller') { 14 | return; 15 | } 16 | if(Math.abs(target.x - object.x) > 1 || Math.abs(target.y - object.y) > 1) { 17 | return; 18 | } 19 | if(!object.store || !(object.store[C.RESOURCE_GHODIUM] >= C.SAFE_MODE_COST)) { 20 | return; 21 | } 22 | 23 | bulk.update(target, {safeModeAvailable: (target.safeModeAvailable || 0) + 1}); 24 | bulk.update(object, {store: {[C.RESOURCE_GHODIUM]: object.store[C.RESOURCE_GHODIUM] - C.SAFE_MODE_COST}}); 25 | }; 26 | -------------------------------------------------------------------------------- /src/processor/intents/creeps/heal.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, {roomObjects, roomController, gameTime, eventLog}) { 7 | 8 | if(object.type != 'creep') { 9 | return; 10 | } 11 | if(object.spawning) { 12 | return; 13 | } 14 | 15 | var target = roomObjects[intent.id]; 16 | if(!target || (target.type != 'creep' && target.type != 'powerCreep') || target.spawning) { 17 | return; 18 | } 19 | if(Math.abs(target.x - object.x) > 1 || Math.abs(target.y - object.y) > 1) { 20 | return; 21 | } 22 | if(roomController && roomController.user != object.user && roomController.safeMode > gameTime) { 23 | return; 24 | } 25 | 26 | var healPower = utils.calcBodyEffectiveness(object.body, C.HEAL, 'heal', C.HEAL_POWER); 27 | 28 | target._healToApply = (target._healToApply || 0) + healPower; 29 | 30 | object.actionLog.heal = {x: target.x, y: target.y}; 31 | target.actionLog.healed = {x: object.x, y: object.y}; 32 | 33 | eventLog.push({event: C.EVENT_HEAL, objectId: object._id, data: {targetId: target._id, amount: healPower, healType: C.EVENT_HEAL_TYPE_MELEE}}); 34 | }; -------------------------------------------------------------------------------- /src/processor/intents/creeps/intents.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'); 2 | 3 | var priorities = { 4 | rangedHeal: ['heal'], 5 | attackController: ['rangedHeal', 'heal'], 6 | dismantle: ['attackController','rangedHeal','heal'], 7 | repair: ['dismantle','attackController','rangedHeal','heal'], 8 | build: ['repair','dismantle','attackController','rangedHeal','heal'], 9 | attack: ['build','repair','dismantle','attackController','rangedHeal','heal'], 10 | harvest: ['attack','build','repair','dismantle','attackController','rangedHeal','heal'], 11 | rangedMassAttack: ['build','repair','rangedHeal'], 12 | rangedAttack: ['rangedMassAttack','build','repair','rangedHeal'] 13 | }; 14 | 15 | var creepActions = ['drop','transfer','withdraw','pickup','heal','rangedHeal','dismantle','attack','harvest','move','repair', 16 | 'build','rangedMassAttack','rangedAttack','say','suicide','claimController','upgradeController','reserveController', 17 | 'attackController','generateSafeMode','signController','pull']; 18 | 19 | var modules = require('bulk-require')(__dirname, ['*.js']); 20 | 21 | function checkPriorities(intents, name) { 22 | return intents[name] && (!priorities[name] || !_.any(priorities[name], i => !!intents[i])); 23 | } 24 | 25 | module.exports = function(object, objectIntents, scope) { 26 | creepActions.forEach(name => { 27 | if(checkPriorities(objectIntents, name)) { 28 | modules[name](object, objectIntents[name], scope); 29 | } 30 | }); 31 | }; 32 | -------------------------------------------------------------------------------- /src/processor/intents/creeps/invaders/flee.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'), 2 | utils = require('../../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants, 5 | fakeRuntime = require('../../../common/fake-runtime'); 6 | 7 | module.exports = function(creep, range, context) { 8 | const {scope, intents, hostiles} = context; 9 | 10 | const nearCreeps = _.filter(hostiles, c => utils.dist(creep, c) < range); 11 | if(_.some(nearCreeps)) { 12 | const direction = fakeRuntime.flee(creep, nearCreeps, range, {}, scope); 13 | if(direction) { 14 | intents.set(creep._id, 'move', { direction }); 15 | return true; 16 | } 17 | } 18 | 19 | return false; 20 | }; 21 | -------------------------------------------------------------------------------- /src/processor/intents/creeps/invaders/healer.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'), 2 | utils = require('../../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants, 5 | fakeRuntime = require('../../../common/fake-runtime'), 6 | flee = require('./flee'); 7 | 8 | module.exports = function(creep, context) { 9 | const {scope, intents, invaders} = context; 10 | 11 | let healTargets = _.filter(invaders, c => utils.dist(c, creep) <= 3); 12 | if(_.some(healTargets)) { 13 | const healTarget = _.first(healTargets.sort((a, b) => (b.hitsMax - b.hits) - (a.hitsMax - a.hits))); 14 | if(utils.dist(creep, healTarget) <= 1) { 15 | intents.set(creep._id, 'heal', {id: healTarget._id, x: healTarget.x, y: healTarget.y}); 16 | } else { 17 | intents.set(creep._id, 'rangedHeal', {id: healTarget._id}); 18 | } 19 | } 20 | 21 | if(creep.hits < creep.hitsMax / 2) { 22 | if(!flee(creep, 4, context)) { 23 | const fleeTarget = fakeRuntime.findClosestByPath( 24 | creep, 25 | _.filter(invaders, c => (c != creep) && fakeRuntime.hasActiveBodyparts(c, C.HEAL)), 26 | null, 27 | scope); 28 | 29 | if(fleeTarget) { 30 | const direction = fakeRuntime.moveTo(creep, fleeTarget, {range: 1}, scope); 31 | if(direction) { 32 | intents.set(creep._id, 'move', { direction }); 33 | } 34 | } 35 | } 36 | 37 | return; 38 | } 39 | 40 | let target = fakeRuntime.findClosestByPath(creep, _.filter(invaders, c => c.hits < c.hitsMax), null, scope); 41 | if(!target) { 42 | if(flee(creep, 4, context)) { 43 | return; 44 | } 45 | target = fakeRuntime.findClosestByPath(creep, _.filter(invaders, c => (c != creep) && !fakeRuntime.hasActiveBodyparts(c, C.HEAL)), null, scope); 46 | } 47 | 48 | if(!target) { 49 | intents.set(creep._id, 'suicide', {}); 50 | return; 51 | } 52 | let direction = 0; 53 | if(utils.dist(creep, target) <= 1) { 54 | direction = utils.getDirection(target.x-creep.x,target.y-creep.y); 55 | } else { 56 | direction = fakeRuntime.moveTo(creep, target, {range: 1}, scope); 57 | } 58 | if(direction) { 59 | intents.set(creep._id, 'move', { direction }); 60 | } 61 | }; 62 | -------------------------------------------------------------------------------- /src/processor/intents/creeps/invaders/pretick.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'), 2 | utils = require('../../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants, 5 | fakeRuntime = require('../../../common/fake-runtime'); 6 | 7 | module.exports = function(creep, scope) { 8 | const {roomObjects} = scope; 9 | 10 | const intents = { 11 | list: {}, 12 | set(id, name, data) { 13 | this.list[id] = this.list[id] || {}; 14 | this.list[id][name] = data; 15 | } 16 | }; 17 | 18 | const creeps = [], invaders = [], healers = [], hostiles = [], defenders = [], fortifications = []; 19 | _.forEach(roomObjects, function(object){ 20 | if(!object.spawning && object.type == 'creep' || object.type == 'powerCreep') { 21 | creeps.push(object); 22 | if(creep.user == object.user) { 23 | invaders.push(object); 24 | if(fakeRuntime.hasActiveBodyparts(object, C.HEAL)) { 25 | healers.push(object); 26 | } 27 | } else { 28 | if(object.user != 3) { 29 | hostiles.push(object); 30 | if(_.some(object.body, i => (i.hits > 0) && (i.type == C.ATTACK) || (i.type == C.RANGED_ATTACK))) { 31 | defenders.push(object); 32 | } 33 | } 34 | } 35 | } 36 | if(object.type==C.STRUCTURE_RAMPART || object.type==C.STRUCTURE_WALL) { 37 | fortifications.push(object); 38 | } 39 | }); 40 | 41 | const context = {scope, intents, roomObjects, creeps, invaders, healers, hostiles, defenders, fortifications}; 42 | 43 | if(_.some(creep.body, {type: C.HEAL})) { 44 | require('./healer')(creep, context); 45 | } else { 46 | require('./findAttack')(creep, context); 47 | } 48 | 49 | require('./shootAtWill')(creep, context); 50 | 51 | return intents.list; 52 | }; 53 | -------------------------------------------------------------------------------- /src/processor/intents/creeps/invaders/shootAtWill.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'), 2 | utils = require('../../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants, 5 | fakeRuntime = require('../../../common/fake-runtime'); 6 | 7 | module.exports = function(creep, context) { 8 | if(!fakeRuntime.hasActiveBodyparts(creep, C.RANGED_ATTACK)) { 9 | return; 10 | } 11 | 12 | const { intents, hostiles } = context; 13 | 14 | const targets = _.filter(hostiles, c => utils.dist(creep, c) <= 3); 15 | 16 | if(!_.some(targets)){ 17 | return; 18 | } 19 | 20 | const target = _.min(targets, 'hits'); 21 | intents.set(creep._id, 'rangedAttack', {id: target._id}); 22 | }; 23 | -------------------------------------------------------------------------------- /src/processor/intents/creeps/keepers/pretick.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'), 2 | utils = require('../../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants, 5 | fakeRuntime = require('../../../common/fake-runtime'); 6 | 7 | module.exports = function(creep, scope) { 8 | const {roomObjects, bulk} = scope; 9 | 10 | let source = undefined; 11 | let resources = []; 12 | let hostilesInMeleeRange = []; 13 | let hostilesInRangedRange = []; 14 | 15 | const intents = { 16 | list: {}, 17 | set(id, name, data) { 18 | this.list[id] = this.list[id] || {}; 19 | this.list[id][name] = data; 20 | } 21 | }; 22 | 23 | _.forEach(roomObjects, function(object){ 24 | if((object.type == 'source') || (object.type == 'mineral')) { 25 | resources.push(object); 26 | } 27 | if((object.type == 'creep' || object.type == 'powerCreep') && (object.user != 2) && (object.user != 3)) { 28 | const distance = utils.dist(creep, object); 29 | if(distance <= 1) { hostilesInMeleeRange.push(object); } 30 | if(distance <= 3) { hostilesInRangedRange.push(object); } 31 | } 32 | }); 33 | 34 | if(creep.memory_sourceId && !!roomObjects[creep.memory_sourceId]) { 35 | source = roomObjects[creep.memory_sourceId]; 36 | } 37 | 38 | if(!source) { 39 | source = _.find(resources, o => utils.dist(creep, o) <= 5); 40 | if(source) { 41 | bulk.update(creep, {memory_sourceId: source._id.toString()}); 42 | } 43 | } 44 | 45 | if(source && utils.dist(source, creep) > 1) { 46 | const direction = fakeRuntime.moveTo(creep, source, {range: 1, reusePath: 50}, scope); 47 | if(direction > 0) { 48 | intents.set(creep._id, 'move', { direction }); 49 | } else { 50 | bulk.update(creep, {memory_move: null}); 51 | } 52 | } 53 | 54 | const meleeTarget = _.min(hostilesInMeleeRange, 'hits'); 55 | if(meleeTarget) { 56 | intents.set(creep._id, 'attack', { id: meleeTarget._id, x: meleeTarget.x, y: meleeTarget.y }); 57 | } 58 | 59 | if(_.some(hostilesInRangedRange)) { 60 | const damageByRange = [10, 10, 4, 1]; 61 | const massDamage = _.sum(hostilesInRangedRange, c => damageByRange[utils.dist(creep, c)]); 62 | if(massDamage > 13) { 63 | intents.set(creep._id, 'rangedMassAttack', {}); 64 | } else { 65 | const rangedTarget = _.min(hostilesInRangedRange, 'hits'); 66 | intents.set(creep._id, 'rangedAttack', { id: rangedTarget._id }); 67 | } 68 | } 69 | 70 | return intents.list; 71 | }; 72 | -------------------------------------------------------------------------------- /src/processor/intents/creeps/move.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants, 5 | movement = require('../movement'); 6 | 7 | module.exports = function(object, intent, {roomObjects}) { 8 | 9 | if(object.spawning) { 10 | return; 11 | } 12 | 13 | object._oldFatigue = object.fatigue; 14 | 15 | var d = null; 16 | if(intent.direction) { 17 | d = utils.getOffsetsByDirection(intent.direction); 18 | } 19 | if(intent.id) { 20 | const creep = roomObjects[intent.id]; 21 | if(creep && creep.type == 'creep' && utils.dist(object, creep) == 1) { 22 | d = [creep.x-object.x, creep.y-object.y]; 23 | } 24 | } 25 | 26 | if(!d) { 27 | return; 28 | } 29 | 30 | var [dx,dy] = d; 31 | 32 | if(object.x + dx < 0 || object.x + dx > 49 || object.y + dy < 0 || object.y + dy > 49) { 33 | return; 34 | } 35 | 36 | var targetObjects = _.filter(roomObjects, {x: object.x+dx, y: object.y+dy}); 37 | 38 | if(!_.any(targetObjects, (target) => _.contains(C.OBSTACLE_OBJECT_TYPES, target.type) && 39 | target.type != 'creep' && target.type != 'powerCreep' || 40 | target.type == 'rampart' && !target.isPublic && object.user != target.user || 41 | object.type == 'powerCreep' && target.type == 'portal' && target.destination.shard)) { 42 | 43 | movement.add(object, dx, dy); 44 | } 45 | }; 46 | -------------------------------------------------------------------------------- /src/processor/intents/creeps/pickup.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, {roomObjects, bulk}) { 7 | 8 | object.store = object.store || {}; 9 | var carry = utils.calcResources(object); 10 | 11 | if(object.spawning || carry >= object.storeCapacity) { 12 | return; 13 | } 14 | 15 | var target = roomObjects[intent.id]; 16 | if(!target || target.type != 'energy') { 17 | return; 18 | } 19 | if(Math.abs(target.x - object.x) > 1 || Math.abs(target.y - object.y) > 1) { 20 | return; 21 | } 22 | 23 | var resourceType = target.resourceType || 'energy'; 24 | 25 | var amount = Math.min(object.storeCapacity - carry, target[resourceType]); 26 | 27 | target[resourceType] -= amount; 28 | object.store[resourceType] = (object.store[resourceType] || 0) + amount; 29 | 30 | if(!target[resourceType]) { 31 | bulk.remove(target._id); 32 | delete roomObjects[target._id]; 33 | } 34 | else { 35 | bulk.update(target, {[resourceType]: target[resourceType]}); 36 | } 37 | 38 | bulk.update(object, {store:{[resourceType]: object.store[resourceType]}}); 39 | }; 40 | -------------------------------------------------------------------------------- /src/processor/intents/creeps/pull.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants, 5 | movement = require('../movement'); 6 | 7 | module.exports = function(object, intent, {roomObjects}) { 8 | if(object.type != 'creep' || object.spawning) { 9 | return; 10 | } 11 | 12 | const target = roomObjects[intent.id]; 13 | if(!target || target.type != 'creep' || target.spawning) { 14 | return; 15 | } 16 | 17 | if(Math.abs(target.x - object.x) > 1 || Math.abs(target.y - object.y) > 1) { 18 | return; 19 | } 20 | 21 | movement.addPulling(object, target); 22 | }; 23 | -------------------------------------------------------------------------------- /src/processor/intents/creeps/rangedAttack.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, scope) { 7 | 8 | const {roomObjects, roomController, gameTime} = scope; 9 | 10 | if(object.type != 'creep') { 11 | return; 12 | } 13 | if(object.spawning) { 14 | return; 15 | } 16 | 17 | var target = roomObjects[intent.id]; 18 | if(!target || target == object) { 19 | return; 20 | } 21 | if(Math.abs(target.x - object.x) > 3 || Math.abs(target.y - object.y) > 3) { 22 | return; 23 | } 24 | if(target.type == 'creep' && target.spawning) { 25 | return; 26 | } 27 | if(!target.hits) { 28 | return; 29 | } 30 | if(roomController && roomController.user != object.user && roomController.safeMode > gameTime) { 31 | return; 32 | } 33 | var rampart = _.find(roomObjects, {type: 'rampart', x: target.x, y: target.y}); 34 | if(rampart) { 35 | target = rampart; 36 | } 37 | 38 | 39 | var attackPower = utils.calcBodyEffectiveness(object.body, C.RANGED_ATTACK, 'rangedAttack', C.RANGED_ATTACK_POWER); 40 | 41 | require('../_damage')(object, target, attackPower, C.EVENT_ATTACK_TYPE_RANGED, scope); 42 | 43 | }; -------------------------------------------------------------------------------- /src/processor/intents/creeps/rangedHeal.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, {roomObjects, roomController, gameTime, eventLog}) { 7 | 8 | if(object.type != 'creep') { 9 | return; 10 | } 11 | if(object.spawning) { 12 | return; 13 | } 14 | 15 | var target = roomObjects[intent.id]; 16 | if(!target || (target.type != 'creep' && target.type != 'powerCreep') || target.spawning) { 17 | return; 18 | } 19 | if(Math.abs(target.x - object.x) > 3 || Math.abs(target.y - object.y) > 3) { 20 | return; 21 | } 22 | if(roomController && roomController.user != object.user && roomController.safeMode > gameTime) { 23 | return; 24 | } 25 | 26 | var healPower = utils.calcBodyEffectiveness(object.body, C.HEAL, 'rangedHeal', C.RANGED_HEAL_POWER); 27 | 28 | target._healToApply = (target._healToApply || 0) + healPower; 29 | 30 | object.actionLog.rangedHeal = {x: target.x, y: target.y}; 31 | target.actionLog.healed = {x: object.x, y: object.y}; 32 | 33 | eventLog.push({event: C.EVENT_HEAL, objectId: object._id, data: {targetId: target._id, amount: healPower, healType: C.EVENT_HEAL_TYPE_RANGED}}); 34 | }; -------------------------------------------------------------------------------- /src/processor/intents/creeps/rangedMassAttack.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, scope) { 7 | 8 | const {roomObjects, roomController, gameTime} = scope; 9 | 10 | if(object.type != 'creep') { 11 | return; 12 | } 13 | if(object.spawning) { 14 | return; 15 | } 16 | 17 | var attackPower = utils.calcBodyEffectiveness(object.body, C.RANGED_ATTACK, 'rangedMassAttack', C.RANGED_ATTACK_POWER); 18 | 19 | if(attackPower == 0) { 20 | return; 21 | } 22 | if(roomController && roomController.user != object.user && roomController.safeMode > gameTime) { 23 | return; 24 | } 25 | 26 | var targets = _.filter(roomObjects, (i) => { 27 | return (!_.isUndefined(i.user) || i.type == 'powerBank') && i.user != object.user && 28 | i.x >= object.x - 3 && i.x <= object.x + 3 && 29 | i.y >= object.y - 3 && i.y <= object.y + 3; 30 | }); 31 | 32 | var distanceRate = {0: 1, 1: 1, 2: 0.4, 3: 0.1}; 33 | 34 | for(var i in targets) { 35 | 36 | var target = targets[i]; 37 | 38 | if(target.type != 'rampart' && _.find(roomObjects, {type: 'rampart', x: target.x, y: target.y})) { 39 | continue; 40 | } 41 | if(!target.hits) { 42 | continue; 43 | } 44 | if(target.type == 'creep' && target.spawning) { 45 | continue; 46 | } 47 | if(_.some(target.effects, e => e.endTime >= gameTime && (e.power == C.PWR_FORTIFY || e.effect == C.EFFECT_INVULNERABILITY))) { 48 | continue; 49 | } 50 | 51 | var distance = Math.max(Math.abs(object.x - target.x), Math.abs(object.y - target.y)); 52 | 53 | var targetAttackPower = Math.round(attackPower * distanceRate[distance]); 54 | 55 | require('../_damage')(object, target, targetAttackPower, C.EVENT_ATTACK_TYPE_RANGED_MASS, scope); 56 | } 57 | 58 | object.actionLog.rangedMassAttack = {}; 59 | }; 60 | -------------------------------------------------------------------------------- /src/processor/intents/creeps/repair.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, {roomObjects, bulk, stats, eventLog}) { 7 | 8 | if(object.type != 'creep') { 9 | return; 10 | } 11 | if(object.spawning || !object.store || object.store.energy <= 0) { 12 | return; 13 | } 14 | 15 | var target = roomObjects[intent.id]; 16 | if(!target || !target.hitsMax || !C.CONSTRUCTION_COST[target.type] || target.hits >= target.hitsMax) { 17 | return; 18 | } 19 | if(Math.abs(target.x - object.x) > 3 || Math.abs(target.y - object.y) > 3) { 20 | return; 21 | } 22 | 23 | var repairPower = _.filter(object.body, (i) => (i.hits > 0 || i._oldHits > 0) && i.type == C.WORK).length * C.REPAIR_POWER || 0, 24 | repairEnergyRemaining = object.store.energy / C.REPAIR_COST, 25 | repairHitsMax = target.hitsMax - target.hits, 26 | repairEffect = Math.min(repairPower, repairEnergyRemaining, repairHitsMax), 27 | repairCost = Math.min(object.store.energy, Math.ceil(repairEffect * C.REPAIR_COST)), 28 | boostedParts = _.map(object.body, i => { 29 | if(i.type == C.WORK && i.boost && C.BOOSTS[C.WORK][i.boost].repair > 0) { 30 | return (C.BOOSTS[C.WORK][i.boost].repair-1) * C.REPAIR_POWER; 31 | } 32 | return 0; 33 | }); 34 | 35 | boostedParts.sort((a,b) => b-a); 36 | boostedParts = boostedParts.slice(0,repairEffect); 37 | 38 | var boostedEffect = Math.min(Math.floor(repairEffect + _.sum(boostedParts)), repairHitsMax); 39 | 40 | if(!boostedEffect) { 41 | return; 42 | } 43 | 44 | target.hits += boostedEffect; 45 | object.store.energy -= repairCost; 46 | 47 | stats.inc('energyConstruction', object.user, repairCost); 48 | 49 | if(target.hits > target.hitsMax) { 50 | target.hits = target.hitsMax; 51 | } 52 | 53 | object.actionLog.repair = {x: target.x, y: target.y}; 54 | 55 | bulk.update(target, {hits: target.hits}); 56 | bulk.update(object, {store: {energy: object.store.energy}}); 57 | 58 | eventLog.push({event: C.EVENT_REPAIR, objectId: object._id, data: { 59 | targetId: target._id, amount: boostedEffect, energySpent: repairCost 60 | }}); 61 | 62 | }; 63 | -------------------------------------------------------------------------------- /src/processor/intents/creeps/reserveController.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, {roomObjects, bulk, gameTime, eventLog}) { 7 | 8 | if(object.type != 'creep') { 9 | return; 10 | } 11 | if(object.spawning) { 12 | return; 13 | } 14 | 15 | var target = roomObjects[intent.id]; 16 | if(!target || target.type != 'controller') { 17 | return; 18 | } 19 | if(Math.abs(target.x - object.x) > 1 || Math.abs(target.y - object.y) > 1) { 20 | return; 21 | } 22 | if(target.user || target.reservation && target.reservation.user != object.user) { 23 | return; 24 | } 25 | 26 | var effect = _.filter(object.body, (i) => i.hits > 0 && i.type == C.CLAIM).length * C.CONTROLLER_RESERVE; 27 | if(!effect) { 28 | return; 29 | } 30 | 31 | if(!target.reservation) { 32 | target.reservation = { 33 | user: object.user, 34 | endTime: gameTime+1 35 | }; 36 | } 37 | 38 | 39 | if(target.reservation.endTime + effect > gameTime + C.CONTROLLER_RESERVE_MAX) { 40 | return; 41 | } 42 | 43 | object.actionLog.reserveController = {x: target.x, y: target.y}; 44 | 45 | target.reservation.endTime += effect; 46 | bulk.update(target, {reservation: target.reservation}); 47 | 48 | eventLog.push({event: C.EVENT_RESERVE_CONTROLLER, objectId: object._id, data: {amount: effect}}); 49 | }; 50 | -------------------------------------------------------------------------------- /src/processor/intents/creeps/say.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, {}) { 7 | 8 | if(object.type != 'creep') { 9 | return; 10 | } 11 | if(object.spawning) { 12 | return; 13 | } 14 | if(!_.isString(intent.message)) { 15 | return; 16 | } 17 | 18 | object.actionLog.say = { 19 | message: intent.message.substring(0,10), 20 | isPublic: intent.isPublic 21 | }; 22 | }; -------------------------------------------------------------------------------- /src/processor/intents/creeps/signController.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, {roomObjects, bulk, gameTime}) { 7 | 8 | if(object.type != 'creep') { 9 | return; 10 | } 11 | if(object.spawning) { 12 | return; 13 | } 14 | 15 | var target = roomObjects[intent.id]; 16 | if(!target || target.type != 'controller') { 17 | return; 18 | } 19 | if(Math.abs(target.x - object.x) > 1 || Math.abs(target.y - object.y) > 1) { 20 | return; 21 | } 22 | 23 | bulk.update(target, {sign: intent.sign ? { 24 | user: object.user, 25 | text: intent.sign, 26 | time: gameTime, 27 | datetime: Date.now() 28 | } : null}); 29 | }; -------------------------------------------------------------------------------- /src/processor/intents/creeps/suicide.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, scope) { 7 | 8 | if(object.type != 'creep') { 9 | return; 10 | } 11 | if(object.spawning) { 12 | return; 13 | } 14 | 15 | require('./_die')(object, object.user == '2' ? 0 : undefined, false, scope); 16 | }; -------------------------------------------------------------------------------- /src/processor/intents/creeps/transfer.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, {roomObjects, bulk, eventLog}) { 7 | 8 | const resourceType = intent.resourceType; 9 | if(!_.contains(C.RESOURCES_ALL, resourceType)) { 10 | return; 11 | } 12 | if(!object || object.spawning || !object.store || !(object.store[resourceType] >= intent.amount) || intent.amount < 0) { 13 | return; 14 | } 15 | 16 | var target = roomObjects[intent.id]; 17 | if(!target || target.type == 'creep' && target.spawning) { 18 | return; 19 | } 20 | if(Math.abs(target.x - object.x) > 1 || Math.abs(target.y - object.y) > 1) { 21 | return; 22 | } 23 | 24 | const targetCapacity = utils.capacityForResource(target, resourceType); 25 | 26 | if(!targetCapacity) { 27 | return; 28 | } 29 | 30 | let amount = intent.amount; 31 | 32 | const storedAmount = target.storeCapacityResource ? target.store[resourceType] : utils.calcResources(target); 33 | 34 | if(storedAmount >= targetCapacity) { 35 | return; 36 | } 37 | if(storedAmount + amount > targetCapacity) { 38 | amount = targetCapacity - storedAmount; 39 | } 40 | 41 | if(!amount) { 42 | return; 43 | } 44 | 45 | target.store[resourceType] = (target.store[resourceType] || 0) + amount; 46 | bulk.update(target, {store: {[resourceType]: target.store[resourceType]}}); 47 | 48 | object.store[resourceType] -= amount; 49 | bulk.update(object, {store: {[resourceType]: object.store[resourceType]}}); 50 | 51 | if(target.type == 'lab' && intent.resourceType != 'energy' && !target.storeCapacityResource[resourceType]) { 52 | bulk.update(target, { 53 | storeCapacityResource: {[resourceType]: C.LAB_MINERAL_CAPACITY}, 54 | storeCapacity: null 55 | }); 56 | } 57 | 58 | eventLog.push({event: C.EVENT_TRANSFER, objectId: object._id, data: {targetId: target._id, resourceType: resourceType, amount}}); 59 | 60 | }; 61 | -------------------------------------------------------------------------------- /src/processor/intents/creeps/withdraw.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, {roomObjects, bulk, roomController, gameTime, eventLog}) { 7 | 8 | if(!_.contains(C.RESOURCES_ALL, intent.resourceType)) { 9 | return; 10 | } 11 | 12 | var emptySpace = object.storeCapacity - utils.calcResources(object); 13 | var amount = Math.min(intent.amount, emptySpace); 14 | 15 | if(!object || object.spawning || !object.storeCapacity || amount < 0) { 16 | return; 17 | } 18 | if(roomController && roomController.user != object.user && roomController.safeMode > gameTime) { 19 | return; 20 | } 21 | var target = roomObjects[intent.id]; 22 | if(!target) { 23 | return; 24 | } 25 | if(object.user != target.user && _.any(roomObjects, i => i.type == C.STRUCTURE_RAMPART && i.user != object.user && !i.isPublic && i.x == target.x && i.y == target.y)) { 26 | return; 27 | } 28 | if(Math.abs(target.x - object.x) > 1 || Math.abs(target.y - object.y) > 1) { 29 | return; 30 | } 31 | 32 | if(target.type == 'nuker' || target.type == 'powerBank') { 33 | return; 34 | } 35 | 36 | if(target.type == 'terminal') { 37 | var effect = _.find(target.effects, {power: C.PWR_DISRUPT_TERMINAL}); 38 | if(effect && effect.endTime > gameTime) { 39 | return; 40 | } 41 | } 42 | 43 | if(amount > target.store[intent.resourceType]) { 44 | amount = target.store[intent.resourceType]; 45 | } 46 | 47 | object.store[intent.resourceType] = (object.store[intent.resourceType]||0) + amount; 48 | bulk.update(object, {store:{[intent.resourceType]: object.store[intent.resourceType]}}); 49 | 50 | target.store[intent.resourceType] -= amount; 51 | bulk.update(target, {store:{[intent.resourceType]: target.store[intent.resourceType]}}); 52 | if(target.type == 'lab' && intent.resourceType != 'energy' && !target.store[intent.resourceType]) { 53 | bulk.update(target, { 54 | storeCapacityResource: {[intent.resourceType]: null}, 55 | storeCapacity: C.LAB_ENERGY_CAPACITY + C.LAB_MINERAL_CAPACITY 56 | }); 57 | } 58 | 59 | eventLog.push({event: C.EVENT_TRANSFER, objectId: target._id, data: {targetId: object._id, resourceType: intent.resourceType, amount}}); 60 | 61 | }; 62 | -------------------------------------------------------------------------------- /src/processor/intents/deposits/tick.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, {roomObjects, bulk, gameTime}) { 7 | if(object._cooldown) { 8 | bulk.update(object, { 9 | cooldownTime: gameTime + object._cooldown 10 | }); 11 | } 12 | 13 | if(object.decayTime && gameTime > object.decayTime) { 14 | bulk.remove(object._id); 15 | delete roomObjects[object._id]; 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /src/processor/intents/energy/tick.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, {roomObjects, bulk}) { 7 | 8 | if(!object || object.type != 'energy') return; 9 | 10 | var resourceType = object.resourceType || 'energy'; 11 | 12 | object[resourceType] -= Math.ceil(object[resourceType] / C.ENERGY_DECAY); 13 | 14 | if (object[resourceType] <= 0 || !object[resourceType]) { 15 | if (_.isNaN(object[resourceType])) { 16 | console.log("Energy NaN: dropped"); 17 | } 18 | bulk.remove(object._id); 19 | delete roomObjects[object._id]; 20 | } 21 | else { 22 | bulk.update(object, {[resourceType]: object[resourceType]}); 23 | } 24 | }; -------------------------------------------------------------------------------- /src/processor/intents/extensions/tick.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, {bulk, roomController}) { 7 | 8 | if(!object || object.type != 'extension') return; 9 | 10 | if(roomController) { 11 | const storeCapacity = C.EXTENSION_ENERGY_CAPACITY[roomController.level] || 0; 12 | if(!object.storeCapacityResource || 13 | !object.storeCapacityResource.energy || 14 | storeCapacity != object.storeCapacityResource.energy) { 15 | bulk.update(object, {storeCapacityResource: {energy: storeCapacity}}); 16 | } 17 | } 18 | 19 | 20 | }; 21 | -------------------------------------------------------------------------------- /src/processor/intents/extractors/tick.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants, 5 | movement = require('../movement'); 6 | 7 | module.exports = function(object, {bulk}) { 8 | 9 | if(object.cooldown > 0) { 10 | 11 | object.cooldown--; 12 | 13 | if(object.cooldown < 0) 14 | object.cooldown = 0; 15 | 16 | bulk.update(object, { 17 | cooldown: object.cooldown 18 | }); 19 | } 20 | 21 | if(object._cooldown) { 22 | bulk.update(object, { 23 | cooldown: object._cooldown 24 | }); 25 | } 26 | 27 | }; -------------------------------------------------------------------------------- /src/processor/intents/factories/produce.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, scope) { 7 | const {gameTime, roomObjects, roomController, bulk} = scope; 8 | 9 | if(!object ||!object.store || !C.COMMODITIES[intent.resourceType] || !!C.COMMODITIES[intent.resourceType].level && object.level != C.COMMODITIES[intent.resourceType].level) { 10 | return; 11 | } 12 | 13 | if(!!object.cooldownTime && object.cooldownTime > gameTime) { 14 | return; 15 | } 16 | 17 | if(!utils.checkStructureAgainstController(object, roomObjects, roomController)) { 18 | return; 19 | } 20 | 21 | if(!!C.COMMODITIES[intent.resourceType].level && (object.level > 0) && !_.some(object.effects, e => e.power == C.PWR_OPERATE_FACTORY && e.level == C.COMMODITIES[intent.resourceType].level && e.endTime >= gameTime)) { 22 | return; 23 | } 24 | 25 | if(_.some(_.keys(C.COMMODITIES[intent.resourceType].components), p => (object.store[p]||0) object.storeCapacity) { 32 | return; 33 | } 34 | 35 | for(let part in C.COMMODITIES[intent.resourceType].components) { 36 | object.store[part] = object.store[part] - C.COMMODITIES[intent.resourceType].components[part]; 37 | } 38 | object.store[intent.resourceType] = (object.store[intent.resourceType]||0) + (C.COMMODITIES[intent.resourceType].amount || 1); 39 | bulk.update(object, {store: object.store}); 40 | 41 | object.actionLog.produce = {x: object.x, y: object.y, resourceType: intent.resourceType}; 42 | 43 | bulk.update(object, {cooldownTime: C.COMMODITIES[intent.resourceType].cooldown + gameTime}); 44 | }; 45 | -------------------------------------------------------------------------------- /src/processor/intents/factories/tick.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants;; 5 | 6 | module.exports = function(object, {roomController,bulk}) { 7 | if(roomController) { 8 | const storeCapacity = roomController.level > 0 && roomController.user == object.user && C.CONTROLLER_STRUCTURES.factory[roomController.level] ? C.FACTORY_CAPACITY : 0; 9 | if(storeCapacity != object.storeCapacity) { 10 | bulk.update(object, {storeCapacity}); 11 | } 12 | } 13 | 14 | if(!_.isEqual(object._actionLog, object.actionLog)) { 15 | bulk.update(object, { 16 | actionLog: object.actionLog 17 | }); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /src/processor/intents/flags/intents.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'); 2 | 3 | module.exports = function(object, objectIntents, roomObjects, roomTerrain, bulk, bulkUsers, roomController, stats) { 4 | 5 | if(objectIntents.remove) { 6 | bulk.remove(object._id); 7 | } 8 | 9 | if(objectIntents.setColor) { 10 | if(_.contains(['white','grey','red','purple','blue','cyan','green','yellow','orange','brown'], objectIntents.setColor.color) && 11 | _.contains(['white','grey','red','purple','blue','cyan','green','yellow','orange','brown'], objectIntents.setColor.secondaryColor)) { 12 | bulk.update(object, {color: objectIntents.setColor.color, secondaryColor: objectIntents.setColor.secondaryColor}); 13 | } 14 | } 15 | 16 | if(objectIntents.setPosition) { 17 | var intent = objectIntents.setPosition; 18 | if(intent.x >= 0 && intent.y >= 0 && intent.x <= 49 && intent.y <= 49 && /^(W|E)\d+(S|N)\d+$/.test(intent.roomName)) { 19 | bulk.update(object, {x: intent.x, y: intent.y, room: intent.roomName}); 20 | } 21 | } 22 | 23 | }; -------------------------------------------------------------------------------- /src/processor/intents/invader-core/attackController.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, {roomObjects, bulk, roomController, gameTime, eventLog, roomInfo}) { 7 | 8 | if(object.type != 'invaderCore') { 9 | return; 10 | } 11 | if(object.spawning) { 12 | return; 13 | } 14 | 15 | var target = roomObjects[intent.id]; 16 | if(!target || target.type != 'controller') { 17 | return; 18 | } 19 | if(!target.user && !target.reservation) { 20 | return; 21 | } 22 | if(roomController && roomController.user != object.user && roomController.safeMode > gameTime || 23 | roomController.upgradeBlocked > gameTime) { 24 | return; 25 | } 26 | if(target.reservation) { 27 | var effect = Math.floor(C.INVADER_CORE_CONTROLLER_POWER * C.CONTROLLER_RESERVE); 28 | var endTime = target.reservation.endTime - effect; 29 | bulk.update(target, {reservation: {endTime}}); 30 | } 31 | if(target.user) { 32 | var effect = Math.floor(C.INVADER_CORE_CONTROLLER_POWER * C.CONTROLLER_CLAIM_DOWNGRADE); 33 | var downgradeTime = target.downgradeTime - effect; 34 | bulk.update(target, {downgradeTime}); 35 | target._upgradeBlocked = gameTime + C.CONTROLLER_ATTACK_BLOCKED_UPGRADE; 36 | } 37 | object.actionLog.reserveController = {x: target.x, y: target.y}; 38 | 39 | roomInfo.active = true; 40 | 41 | eventLog.push({event: C.EVENT_ATTACK_CONTROLLER, objectId: object._id}) 42 | }; -------------------------------------------------------------------------------- /src/processor/intents/invader-core/create-creep.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | 7 | module.exports = function(object, intent, scope) { 8 | if(!object || object.spawning || object.type != 'invaderCore') return; 9 | 10 | if(!object.level || !C.INVADER_CORE_CREEP_SPAWN_TIME[object.level]) return; 11 | 12 | const {gameTime, bulk} = scope; 13 | 14 | intent.body = intent.body.slice(0, C.MAX_CREEP_SIZE); 15 | 16 | const body = []; 17 | for(let i = 0; i < intent.body.length; i++) { 18 | const type = intent.body[i]; 19 | if(!_.contains(C.BODYPARTS_ALL, type)) { 20 | continue; 21 | } 22 | if(intent.boosts && (intent.boosts.length >= i) && C.BOOSTS[type] && C.BOOSTS[type][intent.boosts[i]]){ 23 | body.push({ 24 | type, 25 | hits: 100, 26 | boost: intent.boosts[i] 27 | }); 28 | } else { 29 | body.push({ 30 | type, 31 | hits: 100 32 | }); 33 | } 34 | } 35 | 36 | const storeCapacity = utils.calcBodyEffectiveness(body, C.CARRY, 'capacity', C.CARRY_CAPACITY, true); 37 | 38 | const creep = { 39 | strongholdId: object.strongholdId, 40 | type: 'creep', 41 | name: intent.name, 42 | x: object.x, 43 | y: object.y, 44 | body, 45 | store: { energy: 0 }, 46 | storeCapacity, 47 | room: object.room, 48 | user: object.user, 49 | hits: body.length * 100, 50 | hitsMax: body.length * 100, 51 | spawning: true, 52 | fatigue: 0, 53 | notifyWhenAttacked: false, 54 | ageTime: object.decayTime 55 | }; 56 | 57 | bulk.insert(creep); 58 | 59 | bulk.update(object, { 60 | spawning: { 61 | name: intent.name, 62 | needTime: C.INVADER_CORE_CREEP_SPAWN_TIME[object.level] * body.length, 63 | spawnTime: gameTime + C.INVADER_CORE_CREEP_SPAWN_TIME[object.level] * body.length 64 | } 65 | }); 66 | }; 67 | -------------------------------------------------------------------------------- /src/processor/intents/invader-core/destroy.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants, 5 | strongholds = driver.strongholds; 6 | 7 | module.exports = function(object, scope) { 8 | const { templates, coreRewards, coreAmounts, coreDensities } = strongholds; 9 | const { bulk, roomController } = scope; 10 | 11 | if(roomController) { 12 | bulk.update(roomController, { 13 | user: null, 14 | level: 0, 15 | progress: 0, 16 | downgradeTime: null, 17 | safeMode: null, 18 | safeModeAvailable: 0, 19 | safeModeCooldown: null, 20 | isPowerEnabled: false, 21 | effects: null 22 | }); 23 | } 24 | 25 | if(!object || !object.depositType || !coreRewards[object.depositType] || !_.some(coreRewards[object.depositType]) || !object.templateName || !templates[object.templateName]) { 26 | return; 27 | } 28 | 29 | const rewardLevel = templates[object.templateName].rewardLevel; 30 | const rewards = coreRewards[object.depositType].slice(0, 1+rewardLevel).map(r => _.isArray(r) ? _.sample(r) : r); 31 | const rewardDensities = coreDensities.slice(0, 1+rewardLevel); 32 | 33 | const store = utils.calcReward(_.object(rewards, rewardDensities), coreAmounts[rewardLevel]); 34 | 35 | bulk.update(object, { store }); 36 | 37 | driver.config.emit('strongholdDestroyed', object, scope); 38 | }; 39 | -------------------------------------------------------------------------------- /src/processor/intents/invader-core/intents.js: -------------------------------------------------------------------------------- 1 | module.exports = function(object, objectIntents, scope) { 2 | if(objectIntents.transfer) 3 | require('./transfer')(object, objectIntents.transfer, scope); 4 | 5 | if(objectIntents.createCreep) 6 | require('./create-creep')(object, objectIntents.createCreep, scope); 7 | 8 | if(objectIntents.reserveController) 9 | require('./reserveController')(object, objectIntents.reserveController, scope); 10 | 11 | if(objectIntents.attackController) 12 | require('./attackController')(object, objectIntents.attackController, scope); 13 | 14 | if(objectIntents.upgradeController) 15 | require('./upgradeController')(object, objectIntents.upgradeController, scope); 16 | }; 17 | -------------------------------------------------------------------------------- /src/processor/intents/invader-core/pretick.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants, 5 | stronghold = require('./stronghold/stronghold'); 6 | 7 | module.exports = function(object, scope) { 8 | const {gameTime, roomObjects, roomController, bulk} = scope; 9 | const user = object.user; 10 | const intents = { 11 | list: {}, 12 | set(id, name, data) { 13 | this.list[id] = this.list[id] || {}; 14 | this.list[id][name] = data; 15 | } 16 | }; 17 | 18 | const behavior = object.deployTime ? 'deploy' : object.strongholdBehavior || 'default'; 19 | if(!stronghold.behaviors[behavior]) { 20 | return; 21 | } 22 | 23 | const creeps = [], 24 | defenders = [], damagedDefenders=[], 25 | hostiles = [], 26 | towers = [], 27 | ramparts = [], 28 | damagedRoads = []; 29 | _.forEach(roomObjects, o => { 30 | if((o.type == 'creep' || o.type == 'powerCreep') && !o.spawning) { 31 | creeps.push(o); 32 | if(o.user == user) { 33 | defenders.push(o); 34 | if(o.hits < o.hitsMax) { 35 | damagedDefenders.push(o); 36 | } 37 | } else if(o.user != '3') { 38 | hostiles.push(o); 39 | } 40 | return; 41 | } 42 | if(o.type == C.STRUCTURE_TOWER && o.user == user) { 43 | towers.push(o); 44 | return; 45 | } 46 | if(o.type == C.STRUCTURE_RAMPART && o.user == user) { 47 | ramparts.push(o); 48 | return; 49 | } 50 | if(object.strongholdId == o.strongholdId && o.type == C.STRUCTURE_ROAD && o.hits < o.hitsMax) { 51 | damagedRoads.push(o); 52 | } 53 | }); 54 | 55 | const context = {scope, intents, roomObjects, gameTime, bulk, creeps, defenders, damagedDefenders, hostiles, towers, ramparts, damagedRoads, roomController, core: object}; 56 | 57 | stronghold.behaviors[behavior](context); 58 | 59 | return intents.list; 60 | }; 61 | -------------------------------------------------------------------------------- /src/processor/intents/invader-core/reserveController.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, scope) { 7 | const {roomObjects, bulk, gameTime, eventLog} = scope; 8 | 9 | if(object.type != 'invaderCore') { 10 | return; 11 | } 12 | 13 | const target = roomObjects[intent.id]; 14 | if(!target || target.type != 'controller') { 15 | return; 16 | } 17 | 18 | if(target.user || target.reservation && target.reservation.user != object.user) { 19 | return; 20 | } 21 | 22 | if(!target.reservation) { 23 | target.reservation = { 24 | user: object.user, 25 | endTime: gameTime+1 26 | }; 27 | } 28 | 29 | const effect = C.INVADER_CORE_CONTROLLER_POWER * C.CONTROLLER_RESERVE; 30 | if(target.reservation.endTime + effect > gameTime + C.CONTROLLER_RESERVE_MAX) { 31 | return; 32 | } 33 | 34 | object.actionLog.reserveController = {x: target.x, y: target.y}; 35 | 36 | target.reservation.endTime += effect; 37 | bulk.update(target, {reservation: target.reservation}); 38 | 39 | eventLog.push({event: C.EVENT_RESERVE_CONTROLLER, objectId: object._id, data: {amount: effect}}); 40 | }; 41 | -------------------------------------------------------------------------------- /src/processor/intents/invader-core/stronghold/defence.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'), 2 | utils = require('../../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants, 5 | fakeRuntime = require('../../../common/fake-runtime'), 6 | defence = require('./defence'); 7 | 8 | exports.createSafeMatrixCallback = function createSafeMatrixCallback(context) { 9 | const { hostiles, ramparts, roomObjects } = context; 10 | 11 | if(!_.some(hostiles)) { 12 | return; 13 | } 14 | 15 | const safeMatrixCallback = function safeMatrixCallback(room) { 16 | const matrix = new fakeRuntime.CostMatrix(); 17 | for(let i = 0; i < 50; i++) 18 | for(let j = 0; j < 50; j++) 19 | matrix.set(i, j, Infinity); 20 | 21 | for(let rampart of ramparts) { 22 | matrix.set(rampart.x, rampart.y, 1); 23 | } 24 | 25 | _.forEach(roomObjects, object => { 26 | if(object.type != 'creep' && _.includes(C.OBSTACLE_OBJECT_TYPES, object.type)) { 27 | matrix.set(object.x, object.y, Infinity); 28 | } 29 | }); 30 | 31 | return matrix; 32 | }; 33 | 34 | return safeMatrixCallback; 35 | }; 36 | 37 | exports.distribute = function(positions, agents) { 38 | if(!_.some(agents)) { 39 | return {}; 40 | } 41 | if(agents.length > positions.length) { 42 | agents = agents.slice(0, positions.length); 43 | } 44 | 45 | const result = {}, weights = _.map(positions, p => { return {pos: p, weight: 100}}); 46 | while(_.some(agents)) { 47 | const creep = agents.shift(); 48 | const place = _.max(weights, 'weight'); 49 | _.pull(weights, place); 50 | result[50*place.pos.x+place.pos.y] = creep; 51 | _.forEach(weights, w => { 52 | w.weight -= Math.max(0, _.size(weights)-utils.dist(w.pos, place.pos)); 53 | }); 54 | } 55 | return result; 56 | }; 57 | -------------------------------------------------------------------------------- /src/processor/intents/invader-core/tick.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, scope) { 7 | if(!object || object.type != 'invaderCore') return; 8 | 9 | const {roomObjects, roomController, bulk, roomInfo, gameTime} = scope; 10 | 11 | const collapseEffect = _.find(object.effects, {effect: C.EFFECT_COLLAPSE_TIMER}); 12 | if(collapseEffect && collapseEffect.endTime <= gameTime) { 13 | if(roomController) { 14 | bulk.update(roomController, { 15 | user: null, 16 | level: 0, 17 | progress: 0, 18 | downgradeTime: null, 19 | safeMode: null, 20 | safeModeAvailable: 0, 21 | safeModeCooldown: null, 22 | isPowerEnabled: false, 23 | effects: null 24 | }); 25 | } 26 | return; 27 | } 28 | 29 | if(object.spawning) { 30 | if(gameTime >= object.spawning.spawnTime-1) { 31 | const spawningCreep = _.find(roomObjects, {type: 'creep', name: object.spawning.name, x: object.x, y: object.y}); 32 | const bornOk = require('../spawns/_born-creep')(object, spawningCreep, scope); 33 | 34 | if(bornOk) { 35 | bulk.update(object, {spawning: null}); 36 | } 37 | else { 38 | bulk.update(object, {spawning: {spawnTime: 1+gameTime}}); 39 | } 40 | } 41 | } 42 | 43 | if(!_.isEqual(object.actionLog, object._actionLog)) { 44 | roomInfo.active = true; 45 | bulk.update(object, {actionLog: object.actionLog}); 46 | } 47 | }; 48 | -------------------------------------------------------------------------------- /src/processor/intents/invader-core/transfer.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, {roomObjects, bulk, roomController, eventLog}) { 7 | if(object.type != 'invaderCore') { 8 | return; 9 | } 10 | 11 | var target = roomObjects[intent.id]; 12 | if(!target) { 13 | return; 14 | } 15 | 16 | if(!_.contains(['tower','creep'], target.type)) { 17 | return; 18 | } 19 | const targetTotal = target.type == 'creep' ? utils.calcResources(target) : target.store.energy; 20 | const targetCapacity = target.storeCapacity || target.storeCapacityResource.energy; 21 | if(targetTotal == targetCapacity) { 22 | return; 23 | } 24 | 25 | var amount = intent.amount; 26 | if(targetTotal + amount > targetCapacity) { 27 | amount = targetCapacity - targetTotal; 28 | } 29 | 30 | target.store.energy += amount; 31 | 32 | object.actionLog.transferEnergy = {x: target.x, y: target.y}; 33 | 34 | bulk.update(object, {actionLog: object.actionLog}); 35 | bulk.update(target, { store: { energy: target.store.energy }}); 36 | 37 | eventLog.push({event: C.EVENT_TRANSFER, objectId: object._id, data: {targetId: target._id, resourceType: C.RESOURCE_ENERGY, amount}}); 38 | }; 39 | -------------------------------------------------------------------------------- /src/processor/intents/invader-core/upgradeController.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants, 5 | strongholds = driver.strongholds; 6 | 7 | 8 | module.exports = function(object, intent, scope) { 9 | const { roomObjects, bulk, gameTime, eventLog } = scope; 10 | 11 | if(object.type != 'invaderCore') { 12 | return; 13 | } 14 | 15 | const target = roomObjects[intent.id]; 16 | if(!target || target.type != 'controller') { 17 | return; 18 | } 19 | 20 | if(target.level == 0 || target.user != object.user) { 21 | return; 22 | } 23 | if(target.upgradeBlocked && target.upgradeBlocked > gameTime) { 24 | return; 25 | } 26 | 27 | const effect = _.find(target.effects, {effect: C.EFFECT_INVULNERABILITY}); 28 | if(effect) { 29 | effect.endTime = gameTime + C.INVADER_CORE_CONTROLLER_DOWNGRADE; 30 | } else { 31 | target.effects = [{ 32 | effect: C.EFFECT_INVULNERABILITY, 33 | endTime: gameTime + C.INVADER_CORE_CONTROLLER_DOWNGRADE, 34 | duration: C.INVADER_CORE_CONTROLLER_DOWNGRADE 35 | }]; 36 | } 37 | 38 | const upgradePower = 1; 39 | target.downgradeTime = gameTime + C.INVADER_CORE_CONTROLLER_DOWNGRADE; 40 | 41 | target._upgraded += upgradePower; 42 | 43 | object.actionLog.upgradeController = {x: target.x, y: target.y}; 44 | 45 | const effects = target.effects; 46 | bulk.update(target, { effects: null }); 47 | bulk.update(target, { 48 | downgradeTime: target.downgradeTime, 49 | effects 50 | }); 51 | 52 | eventLog.push({event: C.EVENT_UPGRADE_CONTROLLER, objectId: object._id, data: { 53 | amount: upgradePower, energySpent: 0 54 | }}); 55 | }; 56 | -------------------------------------------------------------------------------- /src/processor/intents/keeper-lairs/tick.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, {roomObjects, bulk, gameTime}) { 7 | 8 | if(!object || object.type != 'keeperLair') return; 9 | 10 | 11 | 12 | if(!object.nextSpawnTime) { 13 | var keeper = _.find(roomObjects, (i) => i.type == 'creep' && i.user == '3' && i.name == 'Keeper'+object._id); 14 | if(!keeper || keeper.hits < 5000) { 15 | bulk.update(object, {nextSpawnTime: gameTime + C.ENERGY_REGEN_TIME}); 16 | } 17 | } 18 | 19 | if(object.nextSpawnTime && gameTime >= object.nextSpawnTime-1) { 20 | var keeper = _.find(roomObjects, (i) => i.type == 'creep' && i.user == '3' && i.name == 'Keeper'+object._id); 21 | if(keeper) { 22 | bulk.remove(keeper._id); 23 | } 24 | 25 | var body = []; 26 | 27 | for(var i=0;i<17;i++) { 28 | body.push({ 29 | type: C.TOUGH, 30 | hits: 100 31 | }); 32 | } 33 | for(var i=0;i<13;i++) { 34 | body.push({ 35 | type: C.MOVE, 36 | hits: 100 37 | }); 38 | } 39 | for(var i=0;i<10;i++) { 40 | body.push({ 41 | type: C.ATTACK, 42 | hits: 100 43 | }); 44 | body.push({ 45 | type: C.RANGED_ATTACK, 46 | hits: 100 47 | }); 48 | } 49 | 50 | bulk.insert({ 51 | name: 'Keeper'+object._id, 52 | x: object.x, 53 | y: object.y, 54 | body, 55 | store: { energy: 0 }, 56 | storeCapacity: 0, 57 | type: 'creep', 58 | room: object.room, 59 | user: '3', 60 | hits: 5000, 61 | hitsMax: 5000, 62 | spawning: false, 63 | fatigue: 0 64 | }); 65 | 66 | bulk.update(object, {nextSpawnTime: null}); 67 | } 68 | 69 | 70 | 71 | 72 | }; 73 | -------------------------------------------------------------------------------- /src/processor/intents/labs/boost-creep.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, {roomObjects, bulk}) { 7 | if(!object || !object.store) { 8 | return; 9 | } 10 | 11 | const mineralType = _(object.store).keys().filter(k => k != C.RESOURCE_ENERGY && object.store[k]).first(); 12 | if(!mineralType) { 13 | return; 14 | } 15 | if(object.store[mineralType] < C.LAB_BOOST_MINERAL || object.store.energy < C.LAB_BOOST_ENERGY) { 16 | return; 17 | } 18 | 19 | var target = roomObjects[intent.id]; 20 | if(!target || target.type != 'creep' || target.spawning) { 21 | return; 22 | } 23 | if(Math.abs(target.x - object.x) > 1 || Math.abs(target.y - object.y) > 1) { 24 | return; 25 | } 26 | 27 | var nonBoostedParts = _.filter(target.body, i => !i.boost && C.BOOSTS[i.type] && C.BOOSTS[i.type][mineralType]); 28 | 29 | if(!nonBoostedParts.length) { 30 | return; 31 | } 32 | 33 | if(nonBoostedParts[0].type != C.TOUGH) { 34 | nonBoostedParts.reverse(); 35 | } 36 | 37 | if(intent.bodyPartsCount) { 38 | nonBoostedParts = nonBoostedParts.slice(0,intent.bodyPartsCount); 39 | } 40 | 41 | while(object.store[mineralType] >= C.LAB_BOOST_MINERAL && object.store.energy >= C.LAB_BOOST_ENERGY && nonBoostedParts.length) { 42 | nonBoostedParts[0].boost = mineralType; 43 | object.store[mineralType] -= C.LAB_BOOST_MINERAL; 44 | object.store.energy -= C.LAB_BOOST_ENERGY; 45 | nonBoostedParts.splice(0,1); 46 | } 47 | 48 | if(object.store[mineralType]) { 49 | bulk.update(object, {store:{[mineralType]: object.store[mineralType], energy: object.store.energy}}); 50 | } else { 51 | bulk.update(object, { 52 | store:{[mineralType]: object.store[mineralType], energy: object.store.energy}, 53 | storeCapacityResource:{[mineralType]: null}, 54 | storeCapacity: C.LAB_ENERGY_CAPACITY + C.LAB_MINERAL_CAPACITY 55 | }); 56 | } 57 | 58 | require('../creeps/_recalc-body')(target); 59 | 60 | bulk.update(target, {body: target.body, storeCapacity: target.storeCapacity}); 61 | target.actionLog.healed = {x: object.x, y: object.y}; 62 | }; 63 | -------------------------------------------------------------------------------- /src/processor/intents/labs/intents.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = function(object, objectIntents, scope) { 3 | 4 | if(objectIntents.boostCreep) 5 | require('./boost-creep')(object, objectIntents.boostCreep, scope); 6 | 7 | if(objectIntents.unboostCreep) 8 | require('./unboost-creep')(object, objectIntents.unboostCreep, scope); 9 | 10 | if(objectIntents.runReaction) 11 | require('./run-reaction')(object, objectIntents.runReaction, scope); 12 | else if(objectIntents.reverseReaction) 13 | require('./reverse-reaction')(object, objectIntents.reverseReaction, scope); 14 | }; 15 | -------------------------------------------------------------------------------- /src/processor/intents/labs/run-reaction.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, {roomObjects, bulk, gameTime}) { 7 | 8 | if(!!object.cooldownTime && object.cooldownTime > gameTime) { 9 | return; 10 | } 11 | 12 | let reactionAmount = C.LAB_REACTION_AMOUNT; 13 | const effect = _.find(object.effects, {power: C.PWR_OPERATE_LAB}); 14 | if(effect && effect.endTime > gameTime) { 15 | reactionAmount += C.POWER_INFO[C.PWR_OPERATE_LAB].effect[effect.level-1]; 16 | } 17 | 18 | const lab1 = roomObjects[intent.lab1]; 19 | if(!lab1 || lab1.type != 'lab') { 20 | return; 21 | } 22 | const lab1MineralType = _(lab1.store).keys().filter(k => k != C.RESOURCE_ENERGY && lab1.store[k]).first(); 23 | if(!lab1MineralType || lab1.store[lab1MineralType] < reactionAmount) { 24 | return; 25 | } 26 | if(Math.abs(lab1.x - object.x) > 2 || Math.abs(lab1.y - object.y) > 2) { 27 | return; 28 | } 29 | 30 | const lab2 = roomObjects[intent.lab2]; 31 | if(!lab2 || lab2.type != 'lab') { 32 | return; 33 | } 34 | const lab2MineralType = _(lab2.store).keys().filter(k => k != C.RESOURCE_ENERGY && lab2.store[k]).first(); 35 | if(!lab2MineralType || lab2.store[lab2MineralType] < reactionAmount) { 36 | return; 37 | } 38 | if(Math.abs(lab2.x - object.x) > 2 || Math.abs(lab2.y - object.y) > 2) { 39 | return; 40 | } 41 | 42 | const mineralType = _(object.store).keys().filter(k => k != C.RESOURCE_ENERGY && object.store[k]).first(); 43 | if((object.store[mineralType]||0) + reactionAmount > C.LAB_MINERAL_CAPACITY) { 44 | return; 45 | } 46 | 47 | const product = C.REACTIONS[lab1MineralType][lab2MineralType]; 48 | 49 | if(!product || mineralType && mineralType != product) { 50 | return; 51 | } 52 | 53 | if(object.storeCapacityResource[product]) { 54 | bulk.update(object, { 55 | store: {[product]: (object.store[product]||0) + reactionAmount}, 56 | cooldownTime: gameTime + C.REACTION_TIME[product] 57 | }); 58 | } else { 59 | bulk.update(object, { 60 | store: {[product]: (object.store[product]||0) + reactionAmount}, 61 | cooldownTime: gameTime + C.REACTION_TIME[product], 62 | storeCapacityResource: {[product]: C.LAB_MINERAL_CAPACITY}, 63 | storeCapacity: null 64 | }); 65 | } 66 | 67 | lab1.store[lab1MineralType] -= reactionAmount; 68 | if(lab1.store[lab1MineralType]) { 69 | bulk.update(lab1, {store: {[lab1MineralType]: lab1.store[lab1MineralType]}}); 70 | } else { 71 | bulk.update(lab1, { 72 | store: {[lab1MineralType]: lab1.store[lab1MineralType]}, 73 | storeCapacityResource: {[lab1MineralType]: null}, 74 | storeCapacity: C.LAB_ENERGY_CAPACITY + C.LAB_MINERAL_CAPACITY 75 | }); 76 | } 77 | lab2.store[lab2MineralType] -= reactionAmount; 78 | if(lab2.store[lab2MineralType]) { 79 | bulk.update(lab2, {store: {[lab2MineralType]: lab2.store[lab2MineralType]}}); 80 | } else { 81 | bulk.update(lab2, { 82 | store: {[lab2MineralType]: lab2.store[lab2MineralType]}, 83 | storeCapacityResource: {[lab2MineralType]: null}, 84 | storeCapacity: C.LAB_ENERGY_CAPACITY + C.LAB_MINERAL_CAPACITY 85 | }); 86 | } 87 | 88 | object.actionLog.runReaction = {x1: lab1.x, y1: lab1.y, x2: lab2.x, y2: lab2.y}; 89 | }; 90 | -------------------------------------------------------------------------------- /src/processor/intents/labs/tick.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants, 5 | movement = require('../movement'); 6 | 7 | module.exports = function(object, {bulk}) { 8 | if(!_.isEqual(object._actionLog, object.actionLog)) { 9 | bulk.update(object, { 10 | actionLog: object.actionLog 11 | }); 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /src/processor/intents/labs/unboost-creep.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, scope) { 7 | const {roomObjects, bulk, roomController, gameTime} = scope; 8 | 9 | if(!!object.cooldownTime && object.cooldownTime > gameTime) { 10 | return; 11 | } 12 | 13 | const target = roomObjects[intent.id]; 14 | if(!target || target.type != 'creep' || target.user != object.user) { 15 | return; 16 | } 17 | if(!utils.checkStructureAgainstController(object, roomObjects, roomController)) { 18 | return; 19 | } 20 | if(Math.abs(target.x - object.x) > 1 || Math.abs(target.y - object.y) > 1) { 21 | return; 22 | } 23 | const boostedParts = _.mapValues(_.groupBy(_.filter(target.body,p=>!!p.boost), 'boost'), v=>v.length); 24 | if(!_.some(boostedParts)) { 25 | return; 26 | } 27 | 28 | target.body.forEach(function(p) {p.boost = null;}); 29 | require('../creeps/_recalc-body')(target); 30 | bulk.update(target, {body: target.body, storeCapacity: target.storeCapacity}); 31 | 32 | const cooldown = _.reduce(C.RESOURCES_ALL, function(a, r){ 33 | if(!boostedParts[r]) { 34 | return a; 35 | } 36 | 37 | const energyReturn = boostedParts[r]*C.LAB_UNBOOST_ENERGY; 38 | if(energyReturn>0) { 39 | require('../_create-energy')(target.x, target.y, target.room, energyReturn, C.RESOURCE_ENERGY, scope); 40 | } 41 | 42 | const mineralReturn = boostedParts[r]*C.LAB_UNBOOST_MINERAL; 43 | if(mineralReturn > 0) { 44 | require('../_create-energy')(target.x, target.y, target.room, mineralReturn, r, scope); 45 | } 46 | 47 | return a + boostedParts[r]*utils.calcTotalReactionsTime(r)*C.LAB_UNBOOST_MINERAL/C.LAB_REACTION_AMOUNT; 48 | }, 0); 49 | 50 | if(cooldown > 0) { 51 | bulk.update(object, { cooldownTime: cooldown + gameTime }); 52 | } 53 | }; 54 | -------------------------------------------------------------------------------- /src/processor/intents/links/intents.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = function(object, objectIntents, scope) { 3 | 4 | 5 | if(objectIntents.transfer) 6 | require('./transfer')(object, objectIntents.transfer, scope); 7 | 8 | }; -------------------------------------------------------------------------------- /src/processor/intents/links/tick.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants, 5 | movement = require('../movement'); 6 | 7 | module.exports = function(object, {bulk}) { 8 | 9 | if(!object || object.type != 'link') return; 10 | 11 | if(object.cooldown > 0) { 12 | 13 | object.cooldown--; 14 | 15 | if(object.cooldown < 0) 16 | object.cooldown = 0; 17 | 18 | bulk.update(object, { 19 | cooldown: object.cooldown, 20 | actionLog: object.actionLog 21 | }); 22 | } 23 | else { 24 | if(!_.isEqual(object._actionLog, object.actionLog)) { 25 | bulk.update(object, { 26 | actionLog: object.actionLog 27 | }); 28 | } 29 | } 30 | 31 | }; -------------------------------------------------------------------------------- /src/processor/intents/links/transfer.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, {roomObjects, bulk, roomController, eventLog}) { 7 | 8 | if(!object || object.type != 'link') { 9 | return; 10 | } 11 | 12 | if(!object.store || object.store.energy < intent.amount || intent.amount < 0) { 13 | return; 14 | } 15 | 16 | var target = roomObjects[intent.id]; 17 | if(!target) { 18 | return; 19 | } 20 | 21 | if(target.type != 'link') { 22 | return; 23 | } 24 | var amount = intent.amount; 25 | var targetTotal; 26 | 27 | if(object.cooldown > 0) { 28 | return; 29 | } 30 | if(!utils.checkStructureAgainstController(object, roomObjects, roomController)) { 31 | return; 32 | } 33 | targetTotal = target.store.energy; 34 | 35 | if(!target.storeCapacityResource || !target.storeCapacityResource.energy || targetTotal == target.storeCapacityResource.energy) { 36 | return; 37 | } 38 | 39 | if(targetTotal + amount > target.storeCapacityResource.energy) { 40 | amount = target.storeCapacityResource.energy - targetTotal; 41 | } 42 | target.store.energy += amount; 43 | 44 | object.store.energy -= amount; 45 | 46 | target.store.energy -= Math.ceil(amount * C.LINK_LOSS_RATIO); 47 | object.cooldown += C.LINK_COOLDOWN * Math.max(Math.abs(target.x - object.x), Math.abs(target.y - object.y)); 48 | object.actionLog.transferEnergy = {x: target.x, y: target.y}; 49 | bulk.update(target, {store:{energy: target.store.energy}}); 50 | 51 | bulk.update(object, {store:{energy: object.store.energy}, cooldown: object.cooldown, actionLog: object.actionLog}); 52 | 53 | eventLog.push({event: C.EVENT_TRANSFER, objectId: object._id, data: {targetId: target._id, resourceType: C.RESOURCE_ENERGY, amount}}); 54 | }; 55 | -------------------------------------------------------------------------------- /src/processor/intents/minerals/tick.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, {bulk, gameTime}) { 7 | 8 | if(!object.mineralAmount) { 9 | 10 | if(!object.nextRegenerationTime) { 11 | object.nextRegenerationTime = gameTime + C.MINERAL_REGEN_TIME; 12 | bulk.update(object, {nextRegenerationTime: object.nextRegenerationTime}); 13 | } 14 | if(gameTime >= object.nextRegenerationTime-1) { 15 | var update = { 16 | nextRegenerationTime: null, 17 | mineralAmount: C.MINERAL_DENSITY[object.density] 18 | }; 19 | if(object.density == C.DENSITY_LOW || object.density == C.DENSITY_ULTRA || 20 | Math.random() < C.MINERAL_DENSITY_CHANGE) { 21 | var oldDensity = object.density, newDensity; 22 | do { 23 | var random = Math.random(); 24 | for (var density in C.MINERAL_DENSITY_PROBABILITY) { 25 | if (random <= C.MINERAL_DENSITY_PROBABILITY[density]) { 26 | newDensity = +density; 27 | break; 28 | } 29 | } 30 | } 31 | while(newDensity == oldDensity); 32 | 33 | update.density = object.density = newDensity; 34 | } 35 | bulk.update(object, update); 36 | } 37 | } 38 | 39 | var effect = _.find(object.effects, {power: C.PWR_REGEN_MINERAL}); 40 | if(effect && effect.endTime > gameTime && !object.nextRegenerationTime && object.mineralAmount) { 41 | const powerInfo = C.POWER_INFO[C.PWR_REGEN_MINERAL]; 42 | if(((effect.endTime - gameTime - 1) % powerInfo.period) === 0) { 43 | bulk.update(object, { 44 | mineralAmount: object.mineralAmount + powerInfo.effect[effect.level - 1] 45 | }); 46 | } 47 | } 48 | 49 | }; 50 | -------------------------------------------------------------------------------- /src/processor/intents/nukers/launch-nuke.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | config = require('../../../config'), 3 | utils = require('../../../utils'), 4 | driver = utils.getDriver(), 5 | C = driver.constants; 6 | 7 | 8 | module.exports = function(object, intent, {roomObjects, bulk, roomController, gameTime, roomInfo}) { 9 | 10 | if(!utils.checkStructureAgainstController(object, roomObjects, roomController)) { 11 | return; 12 | } 13 | if(object.store.G < object.storeCapacityResource.G || object.store.energy < object.storeCapacityResource.energy) { 14 | return; 15 | } 16 | if(object.cooldownTime > gameTime) { 17 | return; 18 | } 19 | if(intent.x < 0 || intent.y < 0 || intent.x > 49 || intent.y > 49) { 20 | return; 21 | } 22 | if(roomInfo.novice && roomInfo.novice > Date.now() || roomInfo.respawnArea && roomInfo.respawnArea > Date.now()) { 23 | return; 24 | } 25 | 26 | if(!_.isString(intent.roomName) || !/^(W|E)\d+(S|N)\d+$/.test(intent.roomName)) { 27 | return; 28 | } 29 | 30 | var [tx,ty] = utils.roomNameToXY(intent.roomName); 31 | var [x,y] = utils.roomNameToXY(object.room); 32 | 33 | if(Math.abs(tx-x) > C.NUKE_RANGE || Math.abs(ty-y) > C.NUKE_RANGE) { 34 | return; 35 | } 36 | 37 | bulk.update(object, { 38 | store: {energy: 0, G: 0}, 39 | cooldownTime: gameTime + (config.ptr ? 100 : C.NUKER_COOLDOWN) 40 | }); 41 | 42 | bulk.insert({ 43 | type: 'nuke', 44 | room: intent.roomName, 45 | x: intent.x, 46 | y: intent.y, 47 | landTime: gameTime + (config.ptr ? 100 : C.NUKE_LAND_TIME), 48 | launchRoomName: object.room 49 | }); 50 | 51 | }; 52 | -------------------------------------------------------------------------------- /src/processor/intents/nukes/pretick.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intents, scope) { 7 | const {gameTime} = scope; 8 | if(object.landTime == 1 + gameTime) { 9 | _.forEach(intents.users, userIntents => { 10 | _.forEach(userIntents.objects, i => { 11 | if(!!i.createCreep) { i.createCreep = null; } 12 | }); 13 | }); 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /src/processor/intents/nukes/tick.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, scope) { 7 | 8 | const {roomObjects, bulk, roomController, gameTime, roomInfo} = scope; 9 | 10 | if(roomInfo.novice && roomInfo.novice > Date.now() || roomInfo.respawnArea && roomInfo.respawnArea > Date.now()) { 11 | bulk.remove(object._id); 12 | delete roomObjects[object._id]; 13 | return; 14 | } 15 | 16 | if(gameTime == object.landTime-1) { 17 | 18 | _.forEach(roomObjects, target => { 19 | if(!target) { 20 | return; 21 | } 22 | if (target.type == 'creep') { 23 | require('../creeps/_die')(target, 0, true, scope, C.EVENT_ATTACK_TYPE_NUKE); 24 | } 25 | if (target.type == 'powerCreep') { 26 | bulk.update(target, {hits: 0}); 27 | } 28 | if(target.type == 'constructionSite' || target.type == 'energy' || target.type == 'tombstone' || target.type == 'ruin') { 29 | bulk.remove(target._id); 30 | delete roomObjects[target._id]; 31 | } 32 | if(target.type == 'spawn' && target.spawning != null) { 33 | bulk.update(target, { 34 | spawning: null 35 | }); 36 | } 37 | }); 38 | 39 | for(let dx=-2; dx<=2; dx++) { 40 | for(let dy=-2; dy<=2; dy++) { 41 | let x = object.x+dx, 42 | y = object.y+dy, 43 | range = Math.max(Math.abs(dx), Math.abs(dy)), 44 | damage = range == 0 ? C.NUKE_DAMAGE[0] : C.NUKE_DAMAGE[2]; 45 | 46 | let objects = _.filter(roomObjects, {x,y}); 47 | let rampart = _.find(objects, {type: 'rampart'}); 48 | if(rampart) { 49 | let rampartHits = rampart.hits; 50 | _.pull(objects, rampart); 51 | require('../_damage')(object, rampart, damage, C.EVENT_ATTACK_TYPE_NUKE, scope); 52 | damage -= rampartHits; 53 | } 54 | if(damage > 0) { 55 | objects.forEach(target => { 56 | require('../_damage')(object, target, damage, C.EVENT_ATTACK_TYPE_NUKE, scope); 57 | }); 58 | } 59 | } 60 | } 61 | 62 | if(roomController) { 63 | if(roomController.safeMode > gameTime) { 64 | bulk.update(roomController, { 65 | safeMode: gameTime, 66 | safeModeCooldown: null 67 | }); 68 | } 69 | 70 | if (roomController.user && 71 | !_.some(roomController.effects, e => e.effect == C.EFFECT_INVULNERABILITY && e.endTime > gameTime) && 72 | !roomController.upgradeBlocked || roomController.upgradeBlocked < gameTime) { 73 | bulk.update(roomController, { 74 | upgradeBlocked: gameTime + C.CONTROLLER_NUKE_BLOCKED_UPGRADE 75 | }); 76 | } 77 | } 78 | } 79 | 80 | if(gameTime >= object.landTime) { 81 | bulk.remove(object._id); 82 | delete roomObjects[object._id]; 83 | }; 84 | 85 | 86 | }; 87 | -------------------------------------------------------------------------------- /src/processor/intents/portals/tick.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, {roomObjects, bulk, gameTime}) { 7 | 8 | if(object.unstableDate && Date.now() > object.unstableDate) { 9 | bulk.update(object, { 10 | decayTime: gameTime + C.PORTAL_DECAY, 11 | unstableDate: null 12 | }); 13 | } 14 | 15 | if(object.decayTime && gameTime > object.decayTime) { 16 | bulk.remove(object._id); 17 | delete roomObjects[object._id]; 18 | 19 | var wall = _.find(roomObjects, i => i.type == 'constructedWall' && i.x == object.x+1 && i.y == object.y+1); 20 | if(wall) { 21 | bulk.remove(wall._id); 22 | delete roomObjects[wall._id]; 23 | } 24 | } 25 | 26 | }; -------------------------------------------------------------------------------- /src/processor/intents/power-creeps/drop.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, scope) { 7 | 8 | require('../creeps/drop')(object, intent, scope); 9 | }; 10 | -------------------------------------------------------------------------------- /src/processor/intents/power-creeps/enableRoom.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, {roomObjects, bulk, gameTime}) { 7 | 8 | var target = roomObjects[intent.id]; 9 | if(!target || target.type != 'controller') { 10 | return; 11 | } 12 | if(target.user != object.user && target.safeMode > gameTime) { 13 | return; 14 | } 15 | if(utils.dist(object, target) > 1) { 16 | return; 17 | } 18 | 19 | bulk.update(target, {isPowerEnabled: true}); 20 | 21 | object.actionLog.attack = {x: target.x, y: target.y}; 22 | }; -------------------------------------------------------------------------------- /src/processor/intents/power-creeps/intents.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'); 2 | 3 | var creepActions = ['move','usePower','withdraw','transfer','say','drop','pickup','enableRoom','renew']; 4 | 5 | var modules = require('bulk-require')(__dirname, ['*.js']); 6 | 7 | module.exports = function(object, objectIntents, scope) { 8 | creepActions.forEach(name => { 9 | if(objectIntents[name]) { 10 | modules[name](object, objectIntents[name], scope); 11 | } 12 | }); 13 | }; 14 | -------------------------------------------------------------------------------- /src/processor/intents/power-creeps/move.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants, 5 | movement = require('../movement'); 6 | 7 | module.exports = function(object, intent, scope) { 8 | 9 | require('../creeps/move')(object, intent, scope); 10 | }; 11 | -------------------------------------------------------------------------------- /src/processor/intents/power-creeps/pickup.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, scope) { 7 | 8 | require('../creeps/pickup')(object, intent, scope); 9 | }; 10 | -------------------------------------------------------------------------------- /src/processor/intents/power-creeps/renew.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, {roomObjects, roomController, bulk, gameTime}) { 7 | 8 | var target = roomObjects[intent.id]; 9 | if(!target || target.type != 'powerBank' && target.type != 'powerSpawn') { 10 | return; 11 | } 12 | if(utils.dist(object, target) > 1) { 13 | return; 14 | } 15 | 16 | if(target.type == 'powerSpawn' && !utils.checkStructureAgainstController(target, roomObjects, roomController)) { 17 | return; 18 | } 19 | 20 | bulk.update(object, {ageTime: gameTime + C.POWER_CREEP_LIFE_TIME}); 21 | 22 | object.actionLog.healed = {x: object.x, y: object.y}; 23 | }; 24 | -------------------------------------------------------------------------------- /src/processor/intents/power-creeps/say.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, {}) { 7 | 8 | if(!_.isString(intent.message)) { 9 | return; 10 | } 11 | 12 | object.actionLog.say = { 13 | message: intent.message.substring(0,10), 14 | isPublic: intent.isPublic 15 | }; 16 | }; -------------------------------------------------------------------------------- /src/processor/intents/power-creeps/tick.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants, 5 | movement = require('../movement'); 6 | 7 | module.exports = function(object, scope) { 8 | 9 | const {roomObjects, bulk, roomController, gameTime, eventLog} = scope; 10 | 11 | if(!object || object.type != 'powerCreep') return; 12 | 13 | 14 | movement.execute(object, scope); 15 | 16 | if(utils.isAtEdge(object) && object.user != '2' && object.user != '3') { 17 | var [roomX, roomY] = utils.roomNameToXY(object.room), 18 | x = object.x, 19 | y = object.y, 20 | room = object.room; 21 | 22 | if (object.x == 0) { 23 | x = 49; 24 | room = utils.getRoomNameFromXY(roomX-1, roomY); 25 | } 26 | else if (object.y == 0) { 27 | y = 49; 28 | room = utils.getRoomNameFromXY(roomX, roomY-1); 29 | } 30 | else if (object.x == 49) { 31 | x = 0; 32 | room = utils.getRoomNameFromXY(roomX+1, roomY); 33 | } 34 | else if (object.y == 49) { 35 | y = 0; 36 | room = utils.getRoomNameFromXY(roomX, roomY+1); 37 | } 38 | 39 | bulk.update(object, {interRoom: {room, x, y}}); 40 | 41 | eventLog.push({event: C.EVENT_EXIT, objectId: object._id, data: {room, x, y}}); 42 | } 43 | 44 | var portal = _.find(roomObjects, i => i.type == 'portal' && i.x == object.x && i.y == object.y); 45 | if (portal && !portal.destination.shard) { 46 | bulk.update(object, {interRoom: portal.destination}); 47 | } 48 | 49 | let hits = object.hits; 50 | 51 | if (object._damageToApply) { 52 | hits -= object._damageToApply; 53 | delete object._damageToApply; 54 | } 55 | 56 | if (object._healToApply) { 57 | hits += object._healToApply; 58 | delete object._healToApply; 59 | } 60 | 61 | if(hits > object.hitsMax) { 62 | hits = object.hitsMax; 63 | } 64 | 65 | if(hits != object.hits) { 66 | bulk.update(object, {hits}); 67 | } 68 | 69 | if(!_.isEqual(object.actionLog, object._actionLog)) { 70 | bulk.update(object, {actionLog: object.actionLog}); 71 | } 72 | 73 | }; 74 | -------------------------------------------------------------------------------- /src/processor/intents/power-creeps/transfer.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, scope) { 7 | 8 | require('../creeps/transfer')(object, intent, scope); 9 | }; 10 | -------------------------------------------------------------------------------- /src/processor/intents/power-creeps/withdraw.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, scope) { 7 | require('../creeps/withdraw')(object, intent, scope); 8 | }; 9 | -------------------------------------------------------------------------------- /src/processor/intents/power-spawns/intents.js: -------------------------------------------------------------------------------- 1 | module.exports = function(object, objectIntents, scope) { 2 | 3 | if(objectIntents.processPower) 4 | require('./process-power')(object, objectIntents.processPower, scope); 5 | 6 | }; -------------------------------------------------------------------------------- /src/processor/intents/power-spawns/process-power.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | 7 | module.exports = function(object, intent, {roomObjects, bulk, bulkUsers, roomController, stats, gameTime}) { 8 | 9 | if(object.type != 'powerSpawn' || !object.store) 10 | return; 11 | 12 | if(!utils.checkStructureAgainstController(object, roomObjects, roomController)) { 13 | return; 14 | } 15 | 16 | var amount = 1; 17 | var effect = _.find(object.effects, {power: C.PWR_OPERATE_POWER}); 18 | if(effect && effect.endTime >= gameTime) { 19 | amount = Math.min(object.store.power, amount + C.POWER_INFO[C.PWR_OPERATE_POWER].effect[effect.level-1]); 20 | } 21 | 22 | if(object.store.power < amount || object.store.energy < amount * C.POWER_SPAWN_ENERGY_RATIO) { 23 | return; 24 | } 25 | 26 | object.store.power -= amount; 27 | object.store.energy -= amount * C.POWER_SPAWN_ENERGY_RATIO; 28 | 29 | stats.inc('powerProcessed', object.user, amount); 30 | 31 | bulk.update(object, { store: { 32 | energy: object.store.energy, 33 | power: object.store.power 34 | }}); 35 | 36 | if(bulkUsers.inc) { 37 | bulkUsers.inc(object.user, 'power', amount); 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /src/processor/intents/ramparts/set-public.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, {bulk}) { 7 | 8 | bulk.update(object, { 9 | isPublic: !!intent.isPublic 10 | }); 11 | }; -------------------------------------------------------------------------------- /src/processor/intents/ramparts/tick.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, {roomObjects, bulk, roomController, gameTime}) { 7 | 8 | if(!object || object.type != 'rampart') return; 9 | 10 | var effect = _.find(object.effects, {power: C.PWR_SHIELD}); 11 | if(effect) { 12 | if(effect.endTime <= gameTime) { 13 | bulk.remove(object._id); 14 | delete roomObjects[object._id]; 15 | } 16 | return; 17 | } 18 | 19 | if(roomController && object.user != '2') { 20 | var hitsMax = object.user == roomController.user ? C.RAMPART_HITS_MAX[roomController.level] || 0 : 0; 21 | if(hitsMax != object.hitsMax) { 22 | bulk.update(object, {hitsMax}); 23 | } 24 | } 25 | 26 | if(!object.nextDecayTime || gameTime >= object.nextDecayTime-1) { 27 | object.hits = object.hits || 0; 28 | object.hits -= C.RAMPART_DECAY_AMOUNT; 29 | if(object.hits <= 0) { 30 | bulk.remove(object._id); 31 | delete roomObjects[object._id]; 32 | } 33 | else { 34 | object.nextDecayTime = gameTime + C.RAMPART_DECAY_TIME; 35 | bulk.update(object, { 36 | hits: object.hits, 37 | nextDecayTime: object.nextDecayTime 38 | }); 39 | } 40 | } 41 | 42 | 43 | }; 44 | -------------------------------------------------------------------------------- /src/processor/intents/roads/tick.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, {roomObjects, roomTerrain, bulk, gameTime}) { 7 | 8 | if(!object || object.type != 'road') return; 9 | 10 | if(!object.nextDecayTime || gameTime >= object.nextDecayTime-1) { 11 | var decayAmount = C.ROAD_DECAY_AMOUNT; 12 | if(_.any(roomObjects, (i) => i.x == object.x && i.y == object.y && i.type == 'swamp') || 13 | utils.checkTerrain(roomTerrain, object.x, object.y, C.TERRAIN_MASK_SWAMP)) { 14 | decayAmount *= C.CONSTRUCTION_COST_ROAD_SWAMP_RATIO; 15 | } 16 | if(_.any(roomObjects, (i) => i.x == object.x && i.y == object.y && i.type == 'wall') || 17 | utils.checkTerrain(roomTerrain, object.x, object.y, C.TERRAIN_MASK_WALL)) { 18 | decayAmount *= C.CONSTRUCTION_COST_ROAD_WALL_RATIO; 19 | } 20 | object.hits -= decayAmount; 21 | if(object.hits <= 0) { 22 | bulk.remove(object._id); 23 | delete roomObjects[object._id]; 24 | } 25 | else { 26 | object.nextDecayTime = gameTime + C.ROAD_DECAY_TIME; 27 | bulk.update(object, { 28 | hits: object.hits, 29 | nextDecayTime: object.nextDecayTime 30 | }); 31 | } 32 | } 33 | 34 | 35 | }; -------------------------------------------------------------------------------- /src/processor/intents/room/create-construction-site.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants, 5 | config = require('../../../config'); 6 | 7 | var createdConstructionSiteCounter = 0; 8 | 9 | module.exports = function(userId, intent, {roomObjects, roomTerrain, bulk, roomController}) { 10 | 11 | if(intent.x <= 0 || intent.x >= 49 || intent.y <= 0 || intent.y >= 49) { 12 | return; 13 | } 14 | 15 | if(!C.CONSTRUCTION_COST[intent.structureType]) { 16 | return; 17 | } 18 | 19 | if(/^(W|E)/.test(intent.roomName)) { 20 | 21 | if (roomController && (roomController.user && roomController.user != userId || roomController.reservation && roomController.reservation.user != userId)) { 22 | return; 23 | } 24 | 25 | if (!utils.checkControllerAvailability(intent.structureType, roomObjects, roomController)) { 26 | return; 27 | } 28 | } 29 | 30 | if(!utils.checkConstructionSite(roomObjects, intent.structureType, intent.x, intent.y) || 31 | !utils.checkConstructionSite(roomTerrain, intent.structureType, intent.x, intent.y)) { 32 | return; 33 | } 34 | 35 | var progressTotal = C.CONSTRUCTION_COST[intent.structureType]; 36 | 37 | if(intent.structureType == 'road') { 38 | if(_.any(roomObjects, {x: intent.x, y: intent.y, type: 'swamp'}) || 39 | utils.checkTerrain(roomTerrain, intent.x, intent.y, C.TERRAIN_MASK_SWAMP)) { 40 | progressTotal *= C.CONSTRUCTION_COST_ROAD_SWAMP_RATIO; 41 | } 42 | if(_.any(roomObjects, {x: intent.x, y: intent.y, type: 'wall'}) || 43 | utils.checkTerrain(roomTerrain, intent.x, intent.y, C.TERRAIN_MASK_WALL)) { 44 | progressTotal *= C.CONSTRUCTION_COST_ROAD_WALL_RATIO; 45 | } 46 | } 47 | 48 | if(config.ptr) { 49 | progressTotal = 1; 50 | } 51 | 52 | if(intent.roomName == 'sim' && intent.structureType == 'tower') { 53 | progressTotal = 100; 54 | } 55 | 56 | var obj = { 57 | structureType: intent.structureType, 58 | x: intent.x, 59 | y: intent.y, 60 | type: 'constructionSite', 61 | room: intent.roomName, 62 | user: userId, 63 | progress: 0, 64 | progressTotal 65 | }; 66 | 67 | if(intent.structureType == 'spawn') { 68 | obj.name = intent.name; 69 | } 70 | 71 | bulk.insert(obj); 72 | 73 | roomObjects['_createdConstructionSite'+createdConstructionSiteCounter] = obj; 74 | createdConstructionSiteCounter++; 75 | }; -------------------------------------------------------------------------------- /src/processor/intents/room/create-flag.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(userId, intent, {flags}) { 7 | 8 | var name = intent.name.replace(/\|/g,"$VLINE$").replace(/~/g,"$TILDE$"); 9 | 10 | if(_.any(flags, i => { 11 | return i.user == userId && _.any(i._parsed, j => j[0] == name); 12 | })) { 13 | return; 14 | } 15 | if(!intent.color || !_.contains(C.COLORS_ALL, intent.color)) { 16 | return; 17 | } 18 | if(!intent.secondaryColor || !_.contains(C.COLORS_ALL, intent.secondaryColor)) { 19 | return; 20 | } 21 | 22 | if(intent.x < 0 || intent.x > 49 || intent.y < 0 || intent.y > 49) { 23 | return; 24 | } 25 | 26 | var flagItem = _.find(flags, {user: userId}); 27 | if(!flagItem) { 28 | flagItem = {user: userId, room: intent.roomName, _parsed: []}; 29 | flags.push(flagItem); 30 | } 31 | 32 | flagItem._modified = true; 33 | flagItem._parsed.push([name, intent.color, intent.secondaryColor, intent.x, intent.y]); 34 | }; -------------------------------------------------------------------------------- /src/processor/intents/room/destroy-structure.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(userId, intent, scope) { 7 | 8 | const {roomObjects, roomController} = scope; 9 | 10 | var object = roomObjects[intent.id]; 11 | 12 | if(!object || !C.CONSTRUCTION_COST[object.type]) return; 13 | 14 | if(!roomController || roomController.user != userId) return; 15 | 16 | if(object.type == C.STRUCTURE_WALL && object.decayTime && !object.user) return; 17 | 18 | if(_.any(roomObjects, i => (i.type == 'creep' || i.type == 'powerCreep') && i.user != userId)) return; 19 | 20 | require('../structures/_destroy')(object, scope); 21 | 22 | if(object.type == 'constructedWall' && object.decayTime && object.user) { 23 | require('../creeps/_clear-newbie-walls')(scope); 24 | } 25 | 26 | }; 27 | -------------------------------------------------------------------------------- /src/processor/intents/room/gen-energy.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(userId, intent, {roomObjects, roomTerrain, bulk}) { 7 | 8 | if(userId != '3') { 9 | return; 10 | } 11 | 12 | var x,y; 13 | 14 | do { 15 | x = Math.floor(Math.random() * 48) + 1; 16 | y = Math.floor(Math.random() * 48) + 1; 17 | } 18 | while(_.any(roomObjects, (i) => _.contains(C.OBSTACLE_OBJECT_TYPES, i.type) && i.x == x && i.y == y) || 19 | utils.checkTerrain(roomTerrain, x, y, C.TERRAIN_MASK_WALL)); 20 | 21 | bulk.insert({ 22 | x, 23 | y, 24 | type: 'energy', 25 | energy: 300, 26 | room: intent.roomName 27 | }); 28 | }; -------------------------------------------------------------------------------- /src/processor/intents/room/intents.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'); 2 | 3 | module.exports = function(userId, objectIntents, scope) { 4 | 5 | const {flags, bulkFlags} = scope; 6 | 7 | flags.forEach(i => { 8 | i._parsed = i.data.split("|"); 9 | i._parsed = _.map(i._parsed, j => j.split("~")); 10 | }); 11 | 12 | if(objectIntents.removeFlag) { 13 | _.forEach(objectIntents.removeFlag, (i) => { 14 | require('./remove-flag')(userId, i, scope); 15 | }); 16 | } 17 | if(objectIntents.createFlag) { 18 | _.forEach(objectIntents.createFlag, (i) => { 19 | require('./create-flag')(userId, i, scope); 20 | }); 21 | } 22 | if(objectIntents.createConstructionSite) { 23 | _.forEach(objectIntents.createConstructionSite, (i) => { 24 | require('./create-construction-site')(userId, i, scope); 25 | }); 26 | } 27 | if(objectIntents.removeConstructionSite) { 28 | _.forEach(objectIntents.removeConstructionSite, (i) => { 29 | require('./remove-construction-site')(userId, i, scope); 30 | }); 31 | } 32 | if(objectIntents.destroyStructure) { 33 | _.forEach(objectIntents.destroyStructure, (i) => { 34 | require('./destroy-structure')(userId, i, scope); 35 | }); 36 | } 37 | 38 | if(objectIntents.genEnergy) { 39 | require('./gen-energy')(userId, objectIntents.genEnergy, scope); 40 | } 41 | 42 | flags.forEach(i => { 43 | if(i._modified) { 44 | var data = _.map(i._parsed, j => j.join("~")).join("|"); 45 | 46 | if(i._id) { 47 | bulkFlags.update(i._id, {data}); 48 | } 49 | else { 50 | bulkFlags.insert({data, user: i.user, room: i.room}); 51 | } 52 | } 53 | }); 54 | }; -------------------------------------------------------------------------------- /src/processor/intents/room/remove-construction-site.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(userId, intent, scope) { 7 | 8 | const {roomObjects, bulk, roomController} = scope; 9 | 10 | var object = roomObjects[intent.id]; 11 | 12 | if(!object || object.type != 'constructionSite') return; 13 | 14 | if(object.user != userId && !(roomController && roomController.user == userId)) return; 15 | 16 | bulk.remove(object._id); 17 | if(object.progress > 1) { 18 | require('../_create-energy')(object.x, object.y, object.room, Math.floor(object.progress/2), 'energy', scope); 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /src/processor/intents/room/remove-flag.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(userId, intent, {flags}) { 7 | 8 | var flagItem = _.find(flags, {user: userId}); 9 | if(!flagItem) { 10 | return; 11 | } 12 | 13 | var name = intent.name.replace(/\|/g,"$VLINE$").replace(/~/g,"$TILDE$"); 14 | 15 | if(!_.any(flagItem._parsed, i => i[0] == name)) { 16 | return; 17 | } 18 | flagItem._modified = true; 19 | _.remove(flagItem._parsed, i => i[0] == name); 20 | }; -------------------------------------------------------------------------------- /src/processor/intents/ruins/tick.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function (object, scope) { 7 | const {roomObjects, bulk, gameTime} = scope; 8 | 9 | if (!object.decayTime || gameTime >= object.decayTime - 1) { 10 | if(object.store) { 11 | _.forEach(object.store, (amount, resourceType)=>{ 12 | require('../_create-energy')(object.x, object.y, object.room, amount, resourceType, scope); 13 | }); 14 | } 15 | 16 | bulk.remove(object._id); 17 | delete roomObjects[object._id]; 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /src/processor/intents/sources/tick.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, {bulk, roomController, gameTime}) { 7 | 8 | if(!object || object.type != 'source') return; 9 | 10 | if(object.energy < object.energyCapacity) { 11 | 12 | if(!object.nextRegenerationTime) { 13 | object.nextRegenerationTime = gameTime + C.ENERGY_REGEN_TIME; 14 | bulk.update(object, {nextRegenerationTime: object.nextRegenerationTime}); 15 | } 16 | 17 | var effect = _.find(object.effects, {power: C.PWR_DISRUPT_SOURCE}); 18 | if(effect && effect.endTime > gameTime) { 19 | bulk.update(object, { 20 | nextRegenerationTime: object.nextRegenerationTime+1 21 | }); 22 | } 23 | 24 | if(gameTime >= object.nextRegenerationTime-1) { 25 | bulk.update(object, { 26 | nextRegenerationTime: null, 27 | energy: object.energyCapacity 28 | }); 29 | } 30 | 31 | effect = _.find(object.effects, {power: C.PWR_REGEN_SOURCE}); 32 | if(effect && effect.endTime > gameTime) { 33 | const powerInfo = C.POWER_INFO[C.PWR_REGEN_SOURCE]; 34 | if(((effect.endTime - gameTime - 1) % powerInfo.period) === 0) { 35 | bulk.update(object, { 36 | energy: Math.min(object.energyCapacity, object.energy + powerInfo.effect[effect.level - 1]) 37 | }); 38 | } 39 | } 40 | 41 | 42 | } 43 | 44 | 45 | 46 | if(roomController) { 47 | if (!roomController.user && !roomController.reservation && object.energyCapacity != C.SOURCE_ENERGY_NEUTRAL_CAPACITY) { 48 | bulk.update(object, { 49 | energyCapacity: C.SOURCE_ENERGY_NEUTRAL_CAPACITY, 50 | energy: Math.min(object.energy, C.SOURCE_ENERGY_NEUTRAL_CAPACITY) 51 | }); 52 | } 53 | if ((roomController.user || roomController.reservation) && object.energyCapacity != C.SOURCE_ENERGY_CAPACITY) { 54 | bulk.update(object, {energyCapacity: C.SOURCE_ENERGY_CAPACITY}); 55 | } 56 | } 57 | else if(object.energyCapacity != C.SOURCE_ENERGY_KEEPER_CAPACITY) { 58 | bulk.update(object, {energyCapacity: C.SOURCE_ENERGY_KEEPER_CAPACITY}); 59 | } 60 | }; -------------------------------------------------------------------------------- /src/processor/intents/spawns/_born-creep.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants, 5 | movement = require('../movement'); 6 | 7 | module.exports = function(spawn, creep, scope) { 8 | 9 | const {roomObjects, roomTerrain, bulk} = scope; 10 | 11 | var newX, newY, isOccupied, hostileOccupied; 12 | var checkObstacleFn = (i) => (i.x == newX && i.y == newY) && ( 13 | _.contains(C.OBSTACLE_OBJECT_TYPES, i.type) || // just unwalkable 14 | (i.type == 'constructionSite' && _.contains(C.OBSTACLE_OBJECT_TYPES, i.structureType)) // unwalkable site 15 | ); 16 | 17 | var directions = [1,2,3,4,5,6,7,8]; 18 | if(spawn.spawning && spawn.spawning.directions) { 19 | directions = spawn.spawning.directions; 20 | } 21 | const otherDirections = _.difference([1,2,3,4,5,6,7,8], directions); 22 | // find the first direction where the creep can spawn 23 | for (var direction of directions) { 24 | var [dx,dy] = utils.getOffsetsByDirection(direction); 25 | 26 | newX = spawn.x + dx; 27 | newY = spawn.y + dy; 28 | isOccupied = _.any(roomObjects, checkObstacleFn) || 29 | movement.isTileBusy(newX, newY) || 30 | (utils.checkTerrain(roomTerrain, newX, newY, C.TERRAIN_MASK_WALL) && !_.any(roomObjects, {type: 'road', x: newX, y: newY})); 31 | 32 | if (!isOccupied) { 33 | break; 34 | } 35 | 36 | // remember the first direction where we found a hostile creep 37 | if(!hostileOccupied) { 38 | hostileOccupied = _.find(roomObjects, i => i.x == newX && i.y == newY && i.type == 'creep' && i.user != spawn.user); 39 | } 40 | } 41 | 42 | // if we found a place to spawn, do so 43 | if(!isOccupied) { 44 | bulk.update(creep, { 45 | x: newX, 46 | y: newY, 47 | spawning: false 48 | }); 49 | return true; 50 | } 51 | 52 | // spawn is surrounded, spawnstomp the first hostile we found above, unless... 53 | if(hostileOccupied) { 54 | // bail if there's an opening we could spawn to but chose not to 55 | for (var direction of otherDirections) { 56 | var [dx,dy] = utils.getOffsetsByDirection(direction); 57 | 58 | newX = spawn.x + dx; 59 | newY = spawn.y + dy; 60 | isOccupied = _.any(roomObjects, checkObstacleFn) || 61 | utils.checkTerrain(roomTerrain, newX, newY, C.TERRAIN_MASK_WALL) || 62 | movement.isTileBusy(newX, newY); 63 | 64 | if (!isOccupied) { 65 | return false; 66 | } 67 | } 68 | 69 | require('../creeps/_die')(hostileOccupied, undefined, true, scope); 70 | bulk.update(creep, { 71 | x: hostileOccupied.x, 72 | y: hostileOccupied.y, 73 | spawning: false 74 | }); 75 | return true; 76 | } 77 | 78 | return false; 79 | }; 80 | -------------------------------------------------------------------------------- /src/processor/intents/spawns/_charge-energy.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | function oldEnergyHandling(spawn, cost, {roomObjects, bulk}){ 7 | var spawns = _.filter(roomObjects, i => i.type == 'spawn' && i.user == spawn.user && !i.off); 8 | var extensions = _.filter(roomObjects, i => i.type == 'extension' && i.user == spawn.user && !i.off); 9 | var availableEnergy = _.sum(extensions, 'store.energy') + _.sum(spawns, 'store.energy'); 10 | 11 | if(availableEnergy < cost) { 12 | return false; 13 | } 14 | 15 | spawns.sort(utils.comparatorDistance(spawn)); 16 | spawns.forEach((i) => { 17 | var neededEnergy = Math.min(cost, i.store.energy); 18 | i.store.energy -= neededEnergy; 19 | cost -= neededEnergy; 20 | bulk.update(i, {store:{energy: i.store.energy}}); 21 | }); 22 | 23 | if(cost <= 0) { 24 | return true; 25 | } 26 | 27 | extensions.sort(utils.comparatorDistance(spawn)); 28 | extensions.forEach((extension) => { 29 | if(cost <= 0) { 30 | return; 31 | } 32 | var neededEnergy = Math.min(cost, extension.store.energy); 33 | extension.store.energy -= neededEnergy; 34 | cost -= neededEnergy; 35 | bulk.update(extension, {store:{energy: extension.store.energy}}); 36 | }); 37 | 38 | return true; 39 | } 40 | 41 | function newEnergyHandling(spawn, cost, energyStructures, {roomObjects, bulk}){ 42 | energyStructures = _.filter(energyStructures, id => { 43 | let energyStructure = roomObjects[id]; 44 | 45 | return energyStructure && !energyStructure.off && energyStructure.user === spawn.user && 46 | (energyStructure.type === 'spawn' || energyStructure.type === 'extension'); 47 | }); 48 | 49 | energyStructures = _.uniq(energyStructures); 50 | 51 | let availableEnergy = _.sum(energyStructures, id => roomObjects[id].store.energy); 52 | if(availableEnergy < cost) { 53 | return false; 54 | } 55 | 56 | _.forEach(energyStructures, id => { 57 | let energyStructure = roomObjects[id]; 58 | 59 | let energyChange = Math.min(cost, energyStructure.store.energy); 60 | energyStructure.store.energy -= energyChange; 61 | bulk.update(energyStructure, {store:{energy: energyStructure.store.energy}}); 62 | 63 | cost -= energyChange; 64 | if(cost <= 0) { 65 | return false; 66 | } 67 | }); 68 | 69 | return true; 70 | } 71 | 72 | module.exports = function chargeEnergy(spawn, cost, energyStructures, scope) { 73 | if(energyStructures === undefined) { 74 | return oldEnergyHandling(spawn, cost, scope); 75 | } else { 76 | return newEnergyHandling(spawn, cost, energyStructures, scope); 77 | } 78 | }; 79 | -------------------------------------------------------------------------------- /src/processor/intents/spawns/cancel-spawning.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(spawn, intent, {roomObjects, bulk}) { 7 | if(spawn.type != 'spawn' || !spawn.spawning) 8 | return; 9 | var spawningCreep = _.find(roomObjects, {type: 'creep', name: spawn.spawning.name, x: spawn.x, y: spawn.y}); 10 | bulk.remove(spawningCreep._id); 11 | delete roomObjects[spawningCreep._id]; 12 | bulk.update(spawn, {spawning: null}); 13 | }; -------------------------------------------------------------------------------- /src/processor/intents/spawns/create-creep.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | 7 | module.exports = function(spawn, intent, scope) { 8 | 9 | const {roomObjects, bulk, roomController, stats, gameTime} = scope; 10 | 11 | if(spawn.spawning) { 12 | return; 13 | } 14 | if(spawn.type != 'spawn') 15 | return; 16 | 17 | if(!utils.checkStructureAgainstController(spawn, roomObjects, roomController)) { 18 | return; 19 | } 20 | 21 | let directions = intent.directions; 22 | if(directions !== undefined) { 23 | if(!_.isArray(directions)) { 24 | return; 25 | } 26 | // convert directions to numbers, eliminate duplicates 27 | directions = _.uniq(_.map(directions, e => +e)); 28 | if(directions.length > 0) { 29 | // bail if any numbers are out of bounds or non-integers 30 | if(!_.all(directions, direction => direction >= 1 && direction <= 8 && direction === (direction | 0))) { 31 | return; 32 | } 33 | } 34 | } 35 | 36 | intent.body = intent.body.slice(0, C.MAX_CREEP_SIZE); 37 | 38 | var cost = utils.calcCreepCost(intent.body); 39 | var result = require('./_charge-energy')(spawn, cost, intent.energyStructures, scope); 40 | 41 | if(!result) { 42 | return; 43 | } 44 | 45 | stats.inc('energyCreeps', spawn.user, cost); 46 | 47 | stats.inc('creepsProduced', spawn.user, intent.body.length); 48 | 49 | var needTime = C.CREEP_SPAWN_TIME * intent.body.length; 50 | 51 | var effect = _.find(spawn.effects, {power: C.PWR_OPERATE_SPAWN}); 52 | if(effect && effect.endTime > gameTime) { 53 | needTime = Math.ceil(needTime * C.POWER_INFO[C.PWR_OPERATE_SPAWN].effect[effect.level-1]); 54 | } 55 | 56 | bulk.update(spawn, { 57 | spawning: { 58 | name: intent.name, 59 | needTime, 60 | spawnTime: gameTime + needTime, 61 | directions 62 | } 63 | }); 64 | 65 | var body = [], storeCapacity = 0; 66 | 67 | intent.body.forEach((i) => { 68 | if(_.contains(C.BODYPARTS_ALL, i)) { 69 | body.push({ 70 | type: i, 71 | hits: 100 72 | }); 73 | } 74 | 75 | if(i == C.CARRY) 76 | storeCapacity += C.CARRY_CAPACITY; 77 | }); 78 | 79 | var creep = { 80 | name: intent.name, 81 | x: spawn.x, 82 | y: spawn.y, 83 | body, 84 | store: { energy: 0 }, 85 | storeCapacity, 86 | type: 'creep', 87 | room: spawn.room, 88 | user: spawn.user, 89 | hits: body.length * 100, 90 | hitsMax: body.length * 100, 91 | spawning: true, 92 | fatigue: 0, 93 | notifyWhenAttacked: true 94 | }; 95 | 96 | if(spawn.tutorial) { 97 | creep.tutorial = true; 98 | } 99 | 100 | bulk.insert(creep); 101 | }; 102 | -------------------------------------------------------------------------------- /src/processor/intents/spawns/intents.js: -------------------------------------------------------------------------------- 1 | module.exports = function(object, objectIntents, scope) { 2 | 3 | if(objectIntents.createCreep) 4 | require('./create-creep')(object, objectIntents.createCreep, scope); 5 | 6 | if(objectIntents.renewCreep) 7 | require('./renew-creep')(object, objectIntents.renewCreep, scope); 8 | 9 | if(objectIntents.recycleCreep) 10 | require('./recycle-creep')(object, objectIntents.recycleCreep, scope); 11 | 12 | if(objectIntents.setSpawnDirections) 13 | require('./set-spawn-directions')(object, objectIntents.setSpawnDirections, scope); 14 | 15 | if(objectIntents.cancelSpawning) 16 | require('./cancel-spawning')(object, objectIntents.cancelSpawning, scope); 17 | }; 18 | -------------------------------------------------------------------------------- /src/processor/intents/spawns/recycle-creep.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, scope) { 7 | 8 | const {roomObjects} = scope; 9 | 10 | if(object.type != 'spawn') { 11 | return; 12 | } 13 | 14 | var target = roomObjects[intent.id]; 15 | if(!target || target.type != 'creep' || target.user != object.user || target.spawning) { 16 | return; 17 | } 18 | if(Math.abs(target.x - object.x) > 1 || Math.abs(target.y - object.y) > 1) { 19 | return; 20 | } 21 | 22 | require('../creeps/_die')(target, 1.0, false, scope); 23 | }; -------------------------------------------------------------------------------- /src/processor/intents/spawns/renew-creep.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, scope) { 7 | 8 | const {roomObjects, bulk, stats, gameTime} = scope; 9 | 10 | if(object.type != 'spawn') { 11 | return; 12 | } 13 | if(object.spawning) { 14 | return; 15 | } 16 | 17 | var target = roomObjects[intent.id]; 18 | if(!target || target.type != 'creep' || target.user != object.user || target.spawning) { 19 | return; 20 | } 21 | if(Math.abs(target.x - object.x) > 1 || Math.abs(target.y - object.y) > 1) { 22 | return; 23 | } 24 | if(_.filter(target.body, (i) => i.type == C.CLAIM).length > 0) { 25 | return; 26 | } 27 | 28 | var effect = Math.floor(C.SPAWN_RENEW_RATIO * C.CREEP_LIFE_TIME / C.CREEP_SPAWN_TIME / target.body.length); 29 | if(target.ageTime + effect > gameTime + C.CREEP_LIFE_TIME) { 30 | return; 31 | } 32 | 33 | var cost = Math.ceil(C.SPAWN_RENEW_RATIO * utils.calcCreepCost(target.body) / C.CREEP_SPAWN_TIME / target.body.length); 34 | var result = require('./_charge-energy')(object, cost, undefined, scope); 35 | 36 | if(!result) { 37 | return; 38 | } 39 | 40 | stats.inc('energyCreeps', object.user, cost); 41 | 42 | target.actionLog.healed = {x: object.x, y: object.y}; 43 | bulk.inc(target, 'ageTime', effect); 44 | 45 | if(_.any(target.body, i => !!i.boost)) { 46 | target.body.forEach(i => { 47 | i.boost = null; 48 | }); 49 | require('../creeps/_recalc-body')(target); 50 | // we may not be able to hold all of the resources we could before now. 51 | require('../creeps/_drop-resources-without-space')(target, scope); 52 | bulk.update(target, {body: target.body, storeCapacity: target.storeCapacity}); 53 | } 54 | 55 | }; 56 | -------------------------------------------------------------------------------- /src/processor/intents/spawns/set-spawn-directions.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(spawn, intent, {bulk}) { 7 | if(spawn.type != 'spawn' || !spawn.spawning) 8 | return; 9 | var directions = intent.directions; 10 | if(_.isArray(directions) && directions.length > 0) { 11 | // convert directions to numbers, eliminate duplicates 12 | directions = _.uniq(_.map(directions, e => +e)); 13 | // bail if any numbers are out of bounds or non-integers 14 | if(!_.any(directions, (direction)=>direction < 1 || direction > 8 || direction !== (direction | 0))) { 15 | const spawning = _.clone(spawn.spawning); 16 | spawning.directions = directions; 17 | bulk.update(spawn, {spawning: null}); 18 | bulk.update(spawn, {spawning}); 19 | } 20 | } 21 | }; -------------------------------------------------------------------------------- /src/processor/intents/spawns/tick.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, scope) { 7 | 8 | const {roomObjects, bulk, roomController, energyAvailable, gameTime} = scope; 9 | 10 | if(!object || object.type != 'spawn') return; 11 | 12 | if(object.spawning) { 13 | const effect = _.find(object.effects, {power: C.PWR_DISRUPT_SPAWN}); 14 | if(effect && effect.endTime > gameTime) { 15 | bulk.update(object, {spawning: {spawnTime: 1+object.spawning.spawnTime}}); 16 | } else { 17 | if(gameTime >= object.spawning.spawnTime-1) { 18 | const spawningCreep = _.find(roomObjects, {type: 'creep', name: object.spawning.name, x: object.x, y: object.y}); 19 | 20 | const bornOk = require('./_born-creep')(object, spawningCreep, scope); 21 | 22 | if(bornOk) { 23 | bulk.update(object, {spawning: null}); 24 | } 25 | else { 26 | bulk.update(object, {spawning: {spawnTime: 1+gameTime}}); 27 | } 28 | } 29 | } 30 | } 31 | 32 | if(!roomController || roomController.level < 1 || roomController.user != object.user) { 33 | return; 34 | } 35 | var spawns = _.filter(roomObjects, {type: 'spawn'}); 36 | if(spawns.length > C.CONTROLLER_STRUCTURES.spawn[roomController.level]) { 37 | spawns.sort(utils.comparatorDistance(roomController)); 38 | spawns = _.take(spawns, C.CONTROLLER_STRUCTURES.spawn[roomController.level]); 39 | if(!_.contains(spawns, object)) { 40 | return; 41 | } 42 | } 43 | 44 | if(!object.tutorial && energyAvailable < C.SPAWN_ENERGY_CAPACITY && object.store.energy < C.SPAWN_ENERGY_CAPACITY) { 45 | object.store.energy++; 46 | bulk.update(object, {store:{energy: object.store.energy}}); 47 | } 48 | 49 | }; 50 | -------------------------------------------------------------------------------- /src/processor/intents/storages/tick.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, {bulk, roomController, gameTime}) { 7 | if (roomController) { 8 | var storeCapacity = roomController.level > 0 && roomController.user == object.user && C.CONTROLLER_STRUCTURES.storage[roomController.level] > 0 ? C.STORAGE_CAPACITY : 0; 9 | if(storeCapacity > 0) { 10 | var effect = _.find(object.effects, {power: C.PWR_OPERATE_STORAGE}); 11 | if (effect && effect.endTime > gameTime) { 12 | storeCapacity += C.POWER_INFO[C.PWR_OPERATE_STORAGE].effect[effect.level-1]; 13 | } 14 | } 15 | if (storeCapacity != object.storeCapacity) { 16 | bulk.update(object, {storeCapacity}); 17 | } 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /src/processor/intents/structures/_destroy.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, scope, attackType) { 7 | const {gameTime, bulk, roomObjects} = scope; 8 | 9 | if(object.type == 'spawn' && object.spawning) { 10 | const spawning = _.find(roomObjects, {user: object.user, name: object.spawning.name}); 11 | if(spawning) { 12 | bulk.remove(spawning._id); 13 | delete roomObjects[spawning._id]; 14 | } 15 | } 16 | 17 | if(object.type == 'invaderCore') { 18 | require('../invader-core/destroy')(object, scope); 19 | } 20 | 21 | if(!attackType || attackType != C.EVENT_ATTACK_TYPE_NUKE) { 22 | const ruin = { 23 | type: 'ruin', 24 | room: object.room, 25 | x: object.x, 26 | y: object.y, 27 | structure: { 28 | id: object._id.toString(), 29 | type: object.type, 30 | hits: 0, 31 | hitsMax: object.hitsMax, 32 | user: object.user 33 | }, 34 | destroyTime: gameTime, 35 | decayTime: gameTime + (C.RUIN_DECAY_STRUCTURES[object.type] || C.RUIN_DECAY) 36 | }; 37 | if(object.user) { 38 | ruin.user = object.user 39 | } 40 | ruin.store = object.store || {}; 41 | 42 | if(object.effects) { 43 | const collapseEffect = _.find(object.effects, {effect: C.EFFECT_COLLAPSE_TIMER}); 44 | if(collapseEffect) { 45 | ruin.decayTime = _.max([ruin.decayTime, collapseEffect.endTime]); 46 | } 47 | } 48 | 49 | bulk.insert(ruin); 50 | } 51 | 52 | bulk.remove(object._id); 53 | delete roomObjects[object._id]; 54 | }; 55 | -------------------------------------------------------------------------------- /src/processor/intents/terminal/send.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, {bulk, gameTime}) { 7 | 8 | if(!/^(W|E)\d+(N|S)\d+$/.test(intent.targetRoomName)) { 9 | return; 10 | } 11 | 12 | if(!_.contains(C.RESOURCES_ALL, intent.resourceType)) { 13 | return; 14 | } 15 | if(!intent.amount || !object.store || !(object.store[intent.resourceType] >= intent.amount)) { 16 | return; 17 | } 18 | 19 | var range = utils.calcRoomsDistance(object.room, intent.targetRoomName, true); 20 | var cost = utils.calcTerminalEnergyCost(intent.amount, range); 21 | 22 | var effect = _.find(object.effects, {power: C.PWR_OPERATE_TERMINAL}); 23 | if(effect && effect.endTime >= gameTime) { 24 | cost = Math.ceil(cost * C.POWER_INFO[C.PWR_OPERATE_TERMINAL].effect[effect.level-1]); 25 | } 26 | 27 | if(intent.resourceType != C.RESOURCE_ENERGY && object.store.energy < cost || 28 | intent.resourceType == C.RESOURCE_ENERGY && object.store.energy < intent.amount + cost) { 29 | return; 30 | } 31 | 32 | bulk.update(object, {send: intent}); 33 | }; 34 | -------------------------------------------------------------------------------- /src/processor/intents/terminal/tick.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, {bulk, roomController}) { 7 | 8 | if(roomController) { 9 | var storeCapacity = roomController.level > 0 && roomController.user == object.user && C.CONTROLLER_STRUCTURES.terminal[roomController.level] ? C.TERMINAL_CAPACITY : 0; 10 | if(storeCapacity != object.storeCapacity) { 11 | bulk.update(object, {storeCapacity}); 12 | } 13 | } 14 | 15 | }; 16 | -------------------------------------------------------------------------------- /src/processor/intents/tombstones/tick.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function (object, scope) { 7 | const {roomObjects, bulk, gameTime} = scope; 8 | 9 | if (!object.decayTime || gameTime >= object.decayTime - 1) { 10 | 11 | if(object.store) { 12 | _.forEach(object.store, (amount, resourceType)=>{ 13 | require('../_create-energy')(object.x, object.y, object.room, amount, resourceType, scope); 14 | }); 15 | } 16 | 17 | bulk.remove(object._id); 18 | delete roomObjects[object._id]; 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /src/processor/intents/towers/attack.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, scope) { 7 | 8 | let {roomObjects, bulk, roomController, gameTime} = scope; 9 | 10 | if(!object || object.type != 'tower' || !object.store) { 11 | return; 12 | } 13 | 14 | var target = roomObjects[intent.id]; 15 | if(!target || target == object) { 16 | return; 17 | } 18 | if(target.type == 'creep' && target.spawning) { 19 | return; 20 | } 21 | if(!target.hits) { 22 | return; 23 | } 24 | if(object.store.energy < C.TOWER_ENERGY_COST) { 25 | return; 26 | } 27 | var rampart = _.find(roomObjects, {type: 'rampart', x: target.x, y: target.y}); 28 | if(rampart) { 29 | target = rampart; 30 | } 31 | 32 | var range = Math.max(Math.abs(target.x - object.x), Math.abs(target.y - object.y)); 33 | var amount = C.TOWER_POWER_ATTACK; 34 | if(range > C.TOWER_OPTIMAL_RANGE) { 35 | if(range > C.TOWER_FALLOFF_RANGE) { 36 | range = C.TOWER_FALLOFF_RANGE; 37 | } 38 | amount -= amount * C.TOWER_FALLOFF * (range - C.TOWER_OPTIMAL_RANGE) / (C.TOWER_FALLOFF_RANGE - C.TOWER_OPTIMAL_RANGE); 39 | } 40 | [C.PWR_OPERATE_TOWER, C.PWR_DISRUPT_TOWER].forEach(power => { 41 | var effect = _.find(object.effects, {power}); 42 | if(effect && effect.endTime > gameTime) { 43 | amount *= C.POWER_INFO[power].effect[effect.level-1]; 44 | } 45 | }); 46 | amount = Math.floor(amount); 47 | 48 | if(!amount) { 49 | return; 50 | } 51 | 52 | require('../_damage')(object, target, amount, C.EVENT_ATTACK_TYPE_RANGED, scope); 53 | 54 | object.store.energy -= C.TOWER_ENERGY_COST; 55 | bulk.update(object, {store:{energy: object.store.energy}}); 56 | 57 | 58 | object.actionLog.attack = {x: target.x, y: target.y}; 59 | if(target.actionLog) { 60 | target.actionLog.attacked = {x: object.x, y: object.y}; 61 | } 62 | 63 | if(target.notifyWhenAttacked) { 64 | utils.sendAttackingNotification(target, roomController); 65 | } 66 | 67 | 68 | 69 | 70 | }; 71 | -------------------------------------------------------------------------------- /src/processor/intents/towers/heal.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, {roomObjects, bulk, eventLog, gameTime}) { 7 | 8 | if(!object || object.type != 'tower' || !object.store) { 9 | return; 10 | } 11 | 12 | var target = roomObjects[intent.id]; 13 | if(!target || target.type != 'creep' && target.type !== 'powerCreep') { 14 | return; 15 | } 16 | if(target.spawning) { 17 | return; 18 | } 19 | if(object.store.energy < C.TOWER_ENERGY_COST) { 20 | return; 21 | } 22 | 23 | var range = Math.max(Math.abs(target.x - object.x), Math.abs(target.y - object.y)); 24 | var amount = C.TOWER_POWER_HEAL; 25 | if(range > C.TOWER_OPTIMAL_RANGE) { 26 | if(range > C.TOWER_FALLOFF_RANGE) { 27 | range = C.TOWER_FALLOFF_RANGE; 28 | } 29 | amount -= amount * C.TOWER_FALLOFF * (range - C.TOWER_OPTIMAL_RANGE) / (C.TOWER_FALLOFF_RANGE - C.TOWER_OPTIMAL_RANGE); 30 | } 31 | [C.PWR_OPERATE_TOWER, C.PWR_DISRUPT_TOWER].forEach(power => { 32 | var effect = _.find(object.effects, {power}); 33 | if(effect && effect.endTime > gameTime) { 34 | amount *= C.POWER_INFO[power].effect[effect.level-1]; 35 | } 36 | }); 37 | amount = Math.floor(amount); 38 | 39 | if(!amount) { 40 | return; 41 | } 42 | 43 | target._healToApply = (target._healToApply || 0) + amount; 44 | 45 | object.store.energy -= C.TOWER_ENERGY_COST; 46 | bulk.update(object, {store:{energy: object.store.energy}}); 47 | 48 | object.actionLog.heal = {x: target.x, y: target.y}; 49 | target.actionLog.healed = {x: object.x, y: object.y}; 50 | 51 | eventLog.push({event: C.EVENT_HEAL, objectId: object._id, data: {targetId: target._id, amount: amount, healType: C.EVENT_HEAL_TYPE_RANGED}}); 52 | 53 | }; 54 | -------------------------------------------------------------------------------- /src/processor/intents/towers/intents.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = function(object, objectIntents, scope) { 3 | 4 | if(objectIntents.heal) 5 | require('./heal')(object, objectIntents.heal, scope); 6 | else if(objectIntents.repair) 7 | require('./repair')(object, objectIntents.repair, scope); 8 | else if(objectIntents.attack) 9 | require('./attack')(object, objectIntents.attack, scope); 10 | }; 11 | -------------------------------------------------------------------------------- /src/processor/intents/towers/repair.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants; 5 | 6 | module.exports = function(object, intent, {roomObjects, bulk, stats, eventLog, gameTime}) { 7 | 8 | if(!object || object.type != 'tower' || !object.store) { 9 | return; 10 | } 11 | 12 | var target = roomObjects[intent.id]; 13 | if(!target || !C.CONSTRUCTION_COST[target.type] || target.hits >= target.hitsMax) { 14 | return; 15 | } 16 | if(object.store.energy < C.TOWER_ENERGY_COST) { 17 | return; 18 | } 19 | 20 | var range = Math.max(Math.abs(target.x - object.x), Math.abs(target.y - object.y)); 21 | var amount = C.TOWER_POWER_REPAIR; 22 | if(range > C.TOWER_OPTIMAL_RANGE) { 23 | if(range > C.TOWER_FALLOFF_RANGE) { 24 | range = C.TOWER_FALLOFF_RANGE; 25 | } 26 | amount -= amount * C.TOWER_FALLOFF * (range - C.TOWER_OPTIMAL_RANGE) / (C.TOWER_FALLOFF_RANGE - C.TOWER_OPTIMAL_RANGE); 27 | } 28 | [C.PWR_OPERATE_TOWER, C.PWR_DISRUPT_TOWER].forEach(power => { 29 | var effect = _.find(object.effects, {power}); 30 | if(effect && effect.endTime > gameTime) { 31 | amount *= C.POWER_INFO[power].effect[effect.level-1]; 32 | } 33 | }); 34 | amount = Math.floor(amount); 35 | 36 | if(!amount) { 37 | return; 38 | } 39 | 40 | target.hits += amount; 41 | if(target.hits > target.hitsMax) { 42 | target.hits = target.hitsMax; 43 | } 44 | bulk.update(target, {hits: target.hits}); 45 | 46 | object.store.energy -= C.TOWER_ENERGY_COST; 47 | object.actionLog.repair = {x: target.x, y: target.y}; 48 | bulk.update(object, {store:{energy: object.store.energy}}); 49 | 50 | stats.inc('energyConstruction', object.user, C.TOWER_ENERGY_COST); 51 | 52 | eventLog.push({event: C.EVENT_REPAIR, objectId: object._id, data: { 53 | targetId: target._id, amount: amount, energySpent: C.TOWER_ENERGY_COST 54 | }}); 55 | 56 | }; 57 | -------------------------------------------------------------------------------- /src/processor/intents/towers/tick.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'), 2 | utils = require('../../../utils'), 3 | driver = utils.getDriver(), 4 | C = driver.constants, 5 | movement = require('../movement'); 6 | 7 | module.exports = function(object, {bulk}) { 8 | 9 | if(!object || object.type != 'tower') return; 10 | 11 | if(!_.isEqual(object._actionLog, object.actionLog)) { 12 | bulk.update(object, { 13 | actionLog: object.actionLog 14 | }); 15 | } 16 | 17 | }; -------------------------------------------------------------------------------- /src/runner.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | var q = require('q'), 3 | _ = require('lodash'), 4 | util = require('util'), 5 | utils = require('./utils'), 6 | driver = utils.getDriver(), 7 | C = driver.constants; 8 | 9 | function runUser(userId) { 10 | 11 | driver.config.emit('runnerLoopStage','runUser', userId); 12 | 13 | //driver.influxAccumulator.resetTime(); 14 | 15 | return driver.makeRuntime(userId) 16 | .then(saveResult, saveResult); 17 | 18 | function saveResult(runResult) { 19 | 20 | driver.config.emit('runnerLoopStage','saveResultStart', runResult); 21 | 22 | //driver.influxAccumulator.mark('endMakeRuntime'); 23 | if(runResult.console) { 24 | driver.sendConsoleMessages(userId, runResult.console); 25 | } 26 | if(runResult.error) { 27 | driver.sendConsoleError(userId, runResult.error); 28 | } 29 | 30 | //driver.resetUserRoomVisibility(userId); 31 | 32 | var promises = []; 33 | if(runResult.memory) { 34 | promises.push(driver.saveUserMemory(userId, runResult.memory)); 35 | } 36 | if(runResult.memorySegments) { 37 | promises.push(driver.saveUserMemorySegments(userId, runResult.memorySegments)); 38 | } 39 | if(runResult.interShardSegment) { 40 | promises.push(driver.saveUserMemoryInterShardSegment(userId, runResult.interShardSegment)); 41 | } 42 | if(runResult.intents) { 43 | promises.push(driver.saveUserIntents(userId, runResult.intents)); 44 | } 45 | return q.all(promises) 46 | .then(() => { 47 | driver.config.emit('runnerLoopStage','saveResultFinish', runResult); 48 | //driver.influxAccumulator.mark('saveUser'); 49 | }) 50 | } 51 | } 52 | 53 | driver.connect('runner') 54 | .then(() => driver.queue.create('users', 'read')) 55 | .catch((error) => { 56 | console.error('Error connecting to driver:', error); 57 | process.exit(1); 58 | }) 59 | .then(_usersQueue => { 60 | 61 | var usersQueue = _usersQueue; 62 | 63 | driver.startLoop('runner', function() { 64 | var userId, fetchedUserId; 65 | 66 | driver.config.emit('runnerLoopStage','start'); 67 | 68 | return usersQueue.fetch() 69 | .then((_userId) => { 70 | userId = fetchedUserId = _userId; 71 | return runUser(userId); 72 | }) 73 | .catch((error) => console.error('Error in runner loop:', _.isObject(error) && error.stack || error)) 74 | .then(() => usersQueue.markDone(fetchedUserId)) 75 | .finally(() => driver.config.emit('runnerLoopStage','finish', userId)); 76 | }); 77 | 78 | }); 79 | 80 | 81 | if(typeof self == 'undefined') { 82 | setInterval(() => { 83 | var rejections = q.getUnhandledReasons(); 84 | rejections.forEach((i) => console.error('Unhandled rejection:', i)); 85 | q.resetUnhandledRejections(); 86 | }, 1000); 87 | } 88 | 89 | --------------------------------------------------------------------------------