UNPKG

26.9 kBJavaScriptView Raw
1var express = require('express'),
2 router = express.Router(),
3 q = require('q'),
4 _ = require('lodash'),
5 jsonResponse = require('q-json-response'),
6 utils = require('./utils'),
7 common = require('@screeps/common'),
8 config = common.configManager.config,
9 db = common.storage.db,
10 env = common.storage.env,
11 C = config.common.constants,
12 strongholds = require('./strongholds');
13
14config.cronjobs = {
15 sendNotifications: [60, sendNotifications],
16 roomsForceUpdate: [20, roomsForceUpdate],
17 genPowerBanks: [5*60, genPowerBanks],
18 genInvaders: [5*60, genInvaders],
19 purgeTransactions: [60*60, purgeTransactions],
20 recreateNpcOrders: [5*60, recreateNpcOrders],
21 calcMarketStats: [60*60, calcMarketStats],
22 deletePowerCreeps: [10*60, deletePowerCreeps],
23 genDeposits: [5*60, genDeposits],
24 genStrongholds: [5*60, strongholds.genStrongholds],
25 expandStrongholds: [15*60, strongholds.expandStrongholds]
26};
27
28module.exports.run = function() {
29 _.forEach(config.cronjobs, (i,key) => {
30 if(Date.now() - (i[2]||0) > i[0]*1000) {
31 console.log(`Running cronjob '${key}'`);
32 i[2] = Date.now();
33 i[1]({utils});
34 }
35 });
36};
37
38function recreateNpcOrders() {
39 var gameTime;
40
41 var sellMinerals = ['X','Z','K','L','U','O','O','H','H','Z','K','L','U','O','O','H','H'];
42 var buyMinerals = ['X','Z','K','L','U','O','O','H','H','Z','K','L','U','O','O','H','H'];
43 var sellPrice = {
44 H: 3000,
45 O: 3000,
46 Z: 6000,
47 K: 6000,
48 U: 6000,
49 L: 6000,
50 X: 18000
51 };
52 var buyPrice = 1000;
53
54 var sellMineralAmount = 40, sellEnergyAmount = 40, buyMineralAmount = 20, period = 5000;
55
56 var cnt = 0;
57
58 return common.getGametime()
59 .then(data => gameTime = data)
60 .then(() => db['rooms.objects'].find({$and: [{type: 'terminal'}, {user: {$eq: null}}]}))
61 .then(terminals => common.qSequence(terminals, terminal => {
62 return db.rooms.findOne({_id: terminal.room})
63 .then(room => {
64 if(room.status != 'normal') {
65 return;
66 }
67 if(room.nextNpcMarketOrder && room.nextNpcMarketOrder > gameTime) {
68 return;
69 }
70 const nowTimestamp = new Date().getTime();
71 var sellMineral = sellMinerals[Math.floor(Math.random()*sellMinerals.length)];
72 var buyMineral = buyMinerals[Math.floor(Math.random()*buyMinerals.length)];
73 var orders = [];
74
75 orders.push({
76 created: gameTime,
77 createdTimestamp: nowTimestamp,
78 active: true,
79 type: 'sell',
80 amount: period*sellMineralAmount,
81 remainingAmount: period*sellMineralAmount,
82 totalAmount: period*sellMineralAmount,
83 resourceType: sellMineral,
84 price: sellPrice[sellMineral],
85 roomName: terminal.room
86 });
87
88 if(Math.random() < 0.5) {
89 orders.push({
90 created: gameTime,
91 createdTimestamp: nowTimestamp,
92 active: true,
93 type: 'sell',
94 amount: period*sellEnergyAmount,
95 remainingAmount: period*sellEnergyAmount,
96 totalAmount: period*sellEnergyAmount,
97 resourceType: 'energy',
98 price: 1000,
99 roomName: terminal.room
100 });
101 }
102 if(Math.random() < 0.25) {
103 orders.push({
104 created: gameTime,
105 createdTimestamp: nowTimestamp,
106 active: true,
107 type: 'buy',
108 amount: period*buyMineralAmount,
109 remainingAmount: period*buyMineralAmount,
110 totalAmount: period*buyMineralAmount,
111 resourceType: buyMineral,
112 price: buyPrice,
113 roomName: terminal.room
114 });
115 }
116 cnt++;
117 return db['market.orders'].removeWhere({roomName: room._id})
118 .then(() => db['market.orders'].insert(orders))
119 .then(() => db.rooms.update({_id: room._id}, {$set: {nextNpcMarketOrder: gameTime + Math.round(period*(0.8 + 0.4*Math.random()))}}));
120 })
121 }))
122}
123
124function sendNotifications() {
125
126 var notifications, userIds;
127 var filterDate = new Date();
128 return db['users.notifications'].find({date: {$lt: filterDate.getTime()}})
129 .then((data) => {
130 notifications = data;
131 userIds = _(notifications).pluck('user').uniq(false, (i) => i.toString()).value();
132 })
133 .then(() => db.users.find({_id: {$in: userIds}}))
134 .then((users) => {
135 var promise = q.when();
136 users.forEach((user) => {
137
138 var notificationIdsToRemove = [];
139
140 promise = promise.then(() => {
141
142 var userNotifications = _.filter(notifications, (i) => i.user == user._id);
143
144 if (user.notifyPrefs && (user.notifyPrefs.disabled || !user.email)) {
145 userNotifications.forEach((notification) => {
146 notificationIdsToRemove.push(notification._id);
147 });
148 return;
149 }
150
151 var interval = 60;
152 if (user.notifyPrefs && user.notifyPrefs.interval) {
153 interval = user.notifyPrefs.interval;
154 }
155 interval *= 60 * 1000;
156
157 if (user.lastNotifyDate && (user.lastNotifyDate + interval > Date.now())) {
158 return;
159 }
160
161 userNotifications.forEach((notification) => {
162 notificationIdsToRemove.push(notification._id);
163 });
164
165 config.backend.emit('sendUserNotifications',user,
166 userNotifications.map(i => _.pick(i, ['message','date','count','type'])));
167 })
168 .catch((e) => console.log(`Error sending a message to ${user.username}: ${e}`))
169 .then(() => notificationIdsToRemove.length > 0 && q.all([
170 db['users.notifications'].removeWhere({_id: {$in: notificationIdsToRemove}}),
171 db.users.update({_id: user._id}, {$set: {lastNotifyDate: Date.now()}})
172 ]))
173 });
174 return promise;
175 })
176}
177
178async function roomsForceUpdate() {
179 const gameTime = await common.getGametime();
180 const activeRooms = await utils.getActiveRooms();
181 const rooms = await db.rooms.find({status: {$ne: 'out of borders'}, _id: {$nin: activeRooms}});
182 for(let room of rooms) {
183 if (!room.nextForceUpdateTime || gameTime >= room.nextForceUpdateTime) {
184 await utils.activateRoom(room._id);
185 await db.rooms.update({_id: room._id}, {
186 $set: {
187 nextForceUpdateTime: gameTime + 90 + Math.floor(Math.random() * 20)
188 }
189 });
190 }
191 }
192}
193
194async function genPowerBanks() {
195 const gameTime = await common.getGametime();
196 const rooms = await db.rooms.find({bus: true, status: 'normal'});
197
198 for(const room of rooms) {
199 const respawnTime = Math.round(Math.random() * C.POWER_BANK_RESPAWN_TIME / 2 + C.POWER_BANK_RESPAWN_TIME * 0.75);
200 if (!room.powerBankTime) {
201 room.powerBankTime = gameTime + respawnTime;
202 await db.rooms.update({_id: room._id}, {$set: room});
203 continue;
204 }
205
206 if (gameTime < room.powerBankTime) {
207 continue;
208 }
209
210 room.powerBankTime = gameTime + respawnTime;
211 const data = await db['rooms.terrain'].findOne({room: room._id});
212 let x, y, isWall, hasExit;
213 do {
214 x = Math.floor(Math.random() * 40 + 5);
215 y = Math.floor(Math.random() * 40 + 5);
216 isWall = parseInt(data.terrain.charAt(y * 50 + x)) & 1;
217 hasExit = false;
218 for (let dx = -1; dx <= 1; dx++) {
219 for (let dy = -1; dy <= 1; dy++) {
220 if (!(parseInt(data.terrain.charAt((y + dy) * 50 + x + dx)) & 1)) {
221 hasExit = true;
222 }
223 }
224 }
225 }
226 while (!isWall || !hasExit);
227 let power = Math.floor(Math.random() * (C.POWER_BANK_CAPACITY_MAX - C.POWER_BANK_CAPACITY_MIN) + C.POWER_BANK_CAPACITY_MIN);
228 if (Math.random() < C.POWER_BANK_CAPACITY_CRIT) {
229 power += C.POWER_BANK_CAPACITY_MAX;
230 }
231 await db['rooms.objects'].insert({
232 type: 'powerBank',
233 x, y,
234 room: room._id,
235 store: {power},
236 hits: C.POWER_BANK_HITS,
237 hitsMax: C.POWER_BANK_HITS,
238 decayTime: gameTime + C.POWER_BANK_DECAY
239 });
240 await db.rooms.update({_id: room._id}, {$set: {powerBankTime: room.powerBankTime}});
241 await utils.activateRoom(room._id);
242 }
243}
244
245async function genInvaders() {
246
247 function checkExit(roomName, exit) {
248 var [x,y] = utils.roomNameToXY(roomName);
249 if(exit == 'top') y--;
250 if(exit == 'right') x++;
251 if(exit == 'bottom') y++;
252 if(exit == 'left') x--;
253 var newRoomName = utils.roomNameFromXY(x,y);
254 return db['rooms.objects'].findOne({room: newRoomName, type: 'controller'})
255 .then(controller => {
256 if(controller && (controller.user || controller.reservation)) {
257 return q.reject();
258 }
259 })
260 }
261
262 function createCreep(type, room, square, boosted) {
263
264 var [x,y] = utils.roomNameToXY(room);
265
266 var body = {
267 bigHealer: ['move','move','move','move','move','move','move','move','move','move','move','move','move','move','move','move','move','move','move','move','move','move','move','move','heal','heal','heal','heal','heal','heal','heal','heal','heal','heal','heal','heal','heal','heal','heal','heal','heal','heal','heal','heal','heal','heal','heal','heal','heal','move'],
268 bigRanged: ['tough','tough','tough','tough','tough','tough','move','move','move','move','move','move','move','move','move','move','move','move','move','move','move','move','move','move','move','move','move','move','move','move','ranged_attack','ranged_attack','ranged_attack','ranged_attack','ranged_attack','ranged_attack','ranged_attack','ranged_attack','ranged_attack','ranged_attack','ranged_attack','ranged_attack','ranged_attack','ranged_attack','ranged_attack','ranged_attack','ranged_attack','ranged_attack','work','move'],
269 bigMelee: ['tough','tough','tough','tough','tough','tough','tough','tough','tough','tough','tough','tough','tough','tough','tough','tough','move','move','move','move','move','move','move','move','move','move','move','move','move','move','move','move','move','move','move','move','move','move','move','move','ranged_attack','ranged_attack','ranged_attack','work','work','work','work','attack','attack','move'],
270 smallHealer: ['move','move','move','move','heal','heal','heal','heal','heal','move'],
271 smallRanged: ['tough','tough','move','move','move','move','ranged_attack','ranged_attack','ranged_attack','move'],
272 smallMelee: ['tough','tough','move','move','move','move','ranged_attack','work','attack','move']
273 };
274
275 var creep = {
276 type: 'creep',
277 user: '2',
278 body: _.map(body[type], type => ({type, hits: 100})),
279 hits: body[type].length * 100,
280 hitsMax: body[type].length * 100,
281 ticksToLive: 1500,
282 x: square[0],
283 y: square[1],
284 room,
285 fatigue: 0,
286 store: {},
287 storeCapacity: 0,
288 name: `invader_${room}_${Math.floor(Math.random()*1000)}`
289 };
290
291 if(boosted) {
292 creep.body.forEach(i => {
293 if(i.type == 'heal') {
294 i.boost = utils.isCenter(x,y) ? 'XLHO2' : 'LO';
295 }
296 if(i.type == 'ranged_attack') {
297 i.boost = utils.isCenter(x,y) ? 'XKHO2' : 'KO';
298 }
299 if(i.type == 'work') {
300 i.boost = utils.isCenter(x,y) ? 'XZH2O' : 'ZH';
301 }
302 if(i.type == 'attack') {
303 i.boost = utils.isCenter(x,y) ? 'XUH2O' : 'UH';
304 }
305 if(i.type == 'tough') {
306 i.boost = utils.isCenter(x,y) ? 'XGHO2' : 'GO';
307 }
308 })
309 }
310
311 return db['rooms.objects'].insert(creep);
312 }
313
314 function findClosestExit(square, exits) {
315 var sortedExits = _.sortBy(exits, i => Math.max(Math.abs(i[0] - square[0]), Math.abs(i[1] - square[1])));
316 return sortedExits[0];
317 }
318
319
320 function createRaid(controllerLevel, room, exits) {
321
322 var type = controllerLevel && controllerLevel >= 4 ? 'big' : 'small';
323
324 var max = 1, count = 1, boostChance = 0.5;
325
326 var [x,y] = utils.roomNameToXY(room);
327
328 if(Math.random() > 0.9 || utils.isCenter(x,y)) {
329 max = 2;
330 boostChance = type == 'big' ? 0 : 0.5;
331 if (Math.random() > 0.8 || utils.isCenter(x,y)) {
332 max = 5;
333 if (type == 'big') {
334 if (controllerLevel < 5) {
335 max = 2;
336 }
337 else if (controllerLevel < 6) {
338 max = 2;
339 }
340 else if (controllerLevel < 7) {
341 max = 3;
342 }
343 else if (controllerLevel < 8) {
344 boostChance = 0.4;
345 max = 3;
346 }
347 else {
348 boostChance = 0.4;
349 max = 5;
350 }
351 }
352 }
353
354 count = Math.floor(Math.random()*(max-1)) + 2;
355 count = Math.min(count, exits.length);
356 }
357
358 var promises = [], lastSquare;
359
360 for(var i=0; i<count; i++) {
361 var subtype = i == 0 && !utils.isCenter(x,y) ? 'Melee' :
362 i == 0 || (i == 1 || i == 2 && count == 5) && Math.random() > 0.5 ? 'Ranged' :
363 'Healer';
364
365 var square = lastSquare ? findClosestExit(lastSquare, exits) : exits[Math.floor(Math.random() * exits.length)];
366 if(!square) {
367 break;
368 }
369 promises.push(createCreep(type + subtype, room, square, Math.random() < boostChance));
370 _.pull(exits, square);
371 lastSquare = square;
372 }
373
374 return q.all(promises);
375 }
376
377 const activeRooms = await utils.getActiveRooms();
378 const rooms = await db.rooms.find({status: 'normal', _id: {$nin: activeRooms}});
379 for(const room of rooms) {
380 const objects = await db['rooms.objects'].find({room: room._id, type: {$in: ['source','controller','creep']}});
381 const creeps = _.filter(objects, {type: 'creep', user: '2'});
382 if(creeps.length) {
383 continue;
384 }
385
386 const sources = _.filter(objects, {type: 'source'});
387 const invaderHarvested = _.sum(sources, 'invaderHarvested');
388 const goal = room.invaderGoal || C.INVADERS_ENERGY_GOAL;
389 if(goal != 1 && invaderHarvested < goal) {
390 continue;
391 }
392
393 const sectorRegex = room._id.replace(/^([WE]\d*)\d([NS]\d*)\d$/, (str, p1, p2) => `^${p1}\\d${p2}\\d$`);
394 const invaderCore = await db['rooms.objects'].count({type: 'invaderCore', level: {$gt: 0}, room: {$regex: sectorRegex}});
395 if(!invaderCore) {
396 console.log(`Skip room ${room._id} since there is no invaderCore in sector regex ${sectorRegex}`);
397 continue;
398 }
399
400 const terrain = await db['rooms.terrain'].findOne({room: room._id});
401 let exits = {}, exitSquares = {top: [], left: [], right: [], bottom: []};
402 for(let i=0; i<49; i++) {
403 if(!common.checkTerrain(terrain.terrain, i, 0, C.TERRAIN_MASK_WALL)) {
404 exits.top = true;
405 exitSquares.top.push([i,0]);
406 }
407 if(!common.checkTerrain(terrain.terrain, i, 49, C.TERRAIN_MASK_WALL)) {
408 exits.bottom = true;
409 exitSquares.bottom.push([i,49]);
410 }
411 if(!common.checkTerrain(terrain.terrain, 0, i, C.TERRAIN_MASK_WALL)) {
412 exits.left = true;
413 exitSquares.left.push([0,i]);
414 }
415 if(!common.checkTerrain(terrain.terrain, 49, i, C.TERRAIN_MASK_WALL)) {
416 exits.right = true;
417 exitSquares.right.push([49,i]);
418 }
419 }
420 exits = _.keys(exits);
421
422 await q.all(_.map(exits, exit => {
423 return checkExit(room._id, exit).catch(() => _.pull(exits, exit));
424 }));
425
426 if(!exits.length) {
427 continue;
428 }
429 const exit = exits[Math.floor(Math.random()*exits.length)];
430 const controller = _.find(objects, {type: 'controller'});
431 await createRaid(controller && controller.user && controller.level, room._id, exitSquares[exit]);
432
433 let invaderGoal = Math.floor(C.INVADERS_ENERGY_GOAL * (Math.random()*0.6 + 0.7));
434 if(Math.random() < 0.1) {
435 invaderGoal *= Math.floor( Math.random() > 0.5 ? 2 : 0.5 );
436 }
437 await db.rooms.update({_id: room._id}, {$set: {invaderGoal}})
438 await db['rooms.objects'].update({room: room._id, type: 'source'}, {$set: {invaderHarvested: 0}});
439 await utils.activateRoom(room._id);
440 }
441}
442
443function purgeTransactions() {
444 return db.transactions.find()
445 .then(data => {
446 data = _.sortBy(data, i => -parseInt(i.time));
447
448 var senders = {}, recipients = {}, forDelete = [];
449 data.forEach(i => {
450 var flag1 = true, flag2 = true;
451 senders[i.sender] = senders[i.sender] || [];
452 if(senders[i.sender].length < 100) {
453 senders[i.sender].push(i);
454 flag1 = false;
455 }
456 recipients[i.recipient] = recipients[i.recipient] || [];
457 if(recipients[i.recipient].length < 100) {
458 recipients[i.recipient].push(i);
459 flag2 = false;
460 }
461 if(flag1 && flag2) {
462 forDelete.push(i._id);
463 }
464 });
465
466 if(forDelete.length > 0) {
467 return db.transactions.removeWhere({_id: {$in: forDelete}});
468 }
469 })
470}
471
472function calcMarketStats() {
473 return db['market.stats'].removeWhere({})
474 .then(()=>db['users.money'].find({$and: [{date: {$gt: new Date(Date.now() - 14 * 24 * 3600 * 1000)}},{type: 'market.sell'}]}))
475 .then(data => {
476 const result = {};
477
478 data.forEach(i => {
479 const date = new Date(i.date);
480 i.dateStr = `${date.getFullYear()}-${date.getMonth()<9?'0':''}${date.getMonth()+1}-${date.getDate()<10?'0':''}${date.getDate()}`;
481 const r = i.market.resourceType;
482 if (!result[r]) {
483 result[r] = {};
484 }
485 if (!result[r][i.dateStr]) {
486 result[r][i.dateStr] = {sumPrice: 0, sumAmount: 0, stddev: 0, cnt: 0};
487 }
488 result[r][i.dateStr].sumPrice += i.change;
489 result[r][i.dateStr].sumAmount += i.market.amount;
490 result[r][i.dateStr].cnt++;
491 });
492
493 for (let resourceType in result) {
494 for(let date in result[resourceType]) {
495 result[resourceType][date].avg = result[resourceType][date].sumPrice / result[resourceType][date].sumAmount;
496 }
497 }
498
499 data.forEach(i => {
500 result[i.market.resourceType][i.dateStr].stddev += i.market.amount *
501 Math.pow(i.market.price - result[i.market.resourceType][i.dateStr].avg, 2) /
502 result[i.market.resourceType][i.dateStr].sumAmount;
503 });
504
505 const promises = [];
506
507 for (let resourceType in result) {
508 for (let date in result[resourceType]) {
509 const i = result[resourceType][date];
510 promises.push(db['market.stats'].insert({
511 resourceType,
512 date,
513 transactions: i.cnt,
514 volume: i.sumAmount,
515 avgPrice: +i.avg.toFixed(3),
516 stddevPrice: +Math.sqrt(i.stddev).toFixed(3)
517 }));
518 }
519 }
520
521 return q.all(promises)
522 }).catch(console.log);
523};
524
525function calcPowerLevelBase(level) {
526 return Math.pow(level, C.POWER_LEVEL_POW) * C.POWER_LEVEL_MULTIPLY;
527}
528
529function calcPowerLevel(power) {
530 return Math.floor( Math.pow( (power || 0) / C.POWER_LEVEL_MULTIPLY, 1 / C.POWER_LEVEL_POW ) );
531}
532
533function deletePowerCreeps() {
534 return db['users.power_creeps'].find({deleteTime: {$lt: Date.now()}})
535 .then((data) => _.reduce(data, (promise, creep) => {
536 if(!creep.deleteTime) {
537 return promise;
538 }
539 return promise
540 .then(() => db['users'].findOne({_id: creep.user}))
541 .then(user => {
542 var level = calcPowerLevel(user.power);
543 var basePrev = calcPowerLevelBase(level-1);
544 var baseCurrent = calcPowerLevelBase(level);
545 var baseNext = calcPowerLevelBase(level+1);
546 var change = Math.round(user.power - basePrev -
547 (user.power - baseCurrent) * (baseCurrent - basePrev) / (baseNext - baseCurrent));
548 return q.all([
549 db['users'].update({_id: user._id}, {$inc: {power: -change}}),
550 db['users.power_creeps'].removeWhere({_id: creep._id})
551 ]);
552 })
553 }, q.when()));
554}
555
556async function genDeposits(){
557 function getSectorEdges(centralRoomName) {
558 const result = /^([WE])(\d*)5([NS])(\d*)5$/.exec(centralRoomName);
559 if(!result) {
560 return undefined;
561 }
562 const roomNames = [];
563 const [, we, xx, ns, yy] = result;
564 for(let i = 0; i <= 10; i++) {
565 roomNames.push(`${we}${10*xx+i}${ns}${10*yy}`);
566 roomNames.push(`${we}${10*xx+10}${ns}${10*yy+i}`);
567 roomNames.push(`${we}${10*xx}${ns}${10*yy+i}`);
568 roomNames.push(`${we}${10*xx+i}${ns}${10*yy+10}`);
569 }
570 return _.unique(roomNames);
571 }
572
573 const gameTime = await common.getGametime();
574 const centralRooms = await db['rooms'].find({_id: {$regex: '^[WE]\d*5[NS]\d*5$'}, status: {$ne: 'out of borders'}});
575 const deposits = await db['rooms.objects'].find({type: 'deposit'});
576
577 for(const center of centralRooms) {
578 const sectorEdges = getSectorEdges(center._id);
579 const [cx, cy] = utils.roomNameToXY(center._id); const [cxx, cyy] = [24+50*cx, 24+50*cy];
580 const sectorDeposits = _.filter(deposits, d => {
581 if(!_.includes(sectorEdges, d.room)) {
582 return false;
583 }
584
585 // distance from the center of central room must be half of the sector size (250 tiles)
586 const [xx, yy] = utils.roomNameToXY(d.room);
587 return (Math.abs(50*xx + d.x - cxx) < 250) && (Math.abs(50*yy+d.y - cyy) < 250);
588 });
589 const throughput = _.sum(sectorDeposits, deposit => 20/Math.max(1,(C.DEPOSIT_EXHAUST_MULTIPLY*Math.pow(deposit.harvested||0,C.DEPOSIT_EXHAUST_POW))));
590 if(throughput >= 2.5) {
591 continue;
592 }
593
594 const rooms = await db['rooms'].find({_id: {$in: sectorEdges}, bus: true, status: 'normal', depositType: {$exists: true}});
595 if(!_.some(rooms)) {
596 return q.reject(`No normal bus rooms found for the sector of ${center._id}} (${sectorRegex})`);
597 }
598 const busyRooms = sectorDeposits.map(i => i.room);
599 const freeRooms = _.reject(rooms, r => _.includes(busyRooms, r._id));
600
601 if(!_.some(freeRooms)) {
602 return;
603 }
604
605 const room = _.sample(freeRooms);
606 const terrain = await db['rooms.terrain'].findOne({room: room._id});
607 if(!terrain) {
608 console.log(`${room._id}: no terrain`);
609 return;
610 }
611
612 const objects = await db['rooms.objects'].find({room: room._id});
613 const [xx, yy] = utils.roomNameToXY(room._id);
614 let x, y, isWall, hasExit, inSector, nearObjects, cnt=0;
615 do {
616 cnt++;
617 x = Math.floor(Math.random() * 40 + 5);
618 y = Math.floor(Math.random() * 40 + 5);
619 isWall = parseInt(terrain.terrain.charAt(y * 50 + x)) & 1;
620 hasExit = false;
621 inSector = (Math.abs(50*xx + x - cxx) < 250) && (Math.abs(50*yy+y - cyy) < 250);
622 for (let dx = -1; dx <= 1; dx++) {
623 for (let dy = -1; dy <= 1; dy++) {
624 if (!(parseInt(terrain.terrain.charAt((y + dy) * 50 + x + dx)) & 1)) {
625 hasExit = true;
626 }
627 }
628 }
629 nearObjects = _.any(objects, obj => Math.abs(obj.x-x) <= 2 && Math.abs(obj.y-y) <= 2);
630 }
631 while ((!isWall || !hasExit || nearObjects) && cnt < 1000);
632 if(cnt >= 1000) {
633 console.log(`cannot find location in ${room._id}`);
634 continue;
635 }
636
637 if(room.depositType) {
638 const obj = {type: 'deposit', depositType: room.depositType, x, y, room: room._id, harvested: 0, decayTime: C.DEPOSIT_DECAY_TIME + gameTime};
639 await db['rooms.objects'].insert(obj);
640 await utils.activateRoom(room._id);
641 }
642 }
643}