1 | const common = require('@screeps/common');
|
2 | const C = common.configManager.config.common.constants;
|
3 | const config = common.configManager.config;
|
4 | const db = common.storage.db;
|
5 | const env = common.storage.env;
|
6 | const pubsub = common.storage.pubsub;
|
7 | const utils = require('../utils');
|
8 | const q = require('q');
|
9 | const fs = require('fs');
|
10 | const path = require('path');
|
11 | const _ = require('lodash');
|
12 | const zlib = require('zlib');
|
13 |
|
14 | exports.generateRoom = utils.withHelp([
|
15 | `generateRoom(roomName, [opts]) - Generate a new room at the specified location. 'opts' is an object with the following optional properties:\r
|
16 | * exits - an object with exit coordinates arrays, e.g. {top: [20,21,23], right: [], bottom: [27,28,29,40,41]}, default is random\r
|
17 | * terrainType - the type of generated landscape, a number from 1 to 28, default is random\r
|
18 | * swampType - the type of generated swamp configuration, a number from 0 to 14, default is random\r
|
19 | * sources - the amount of sources in the room, default is random from 1 to 2\r
|
20 | * mineral - the type of the mineral deposit in this room or false if no mineral, default is random type\r
|
21 | * controller - whether this room should have the controller, default is true\r
|
22 | * keeperLairs - whether this room should have source keeper lairs, default is false`,
|
23 | function generateRoom(roomName, opts) {
|
24 | opts = opts || {};
|
25 |
|
26 | opts.exits = opts.exits || {};
|
27 |
|
28 | if(!/^[WE]\d+[NS]\d+$/.test(roomName)) {
|
29 | return q.reject("Invalid room name");
|
30 | }
|
31 |
|
32 | function _exitsArray(terrain, x, y) {
|
33 | var exits = [];
|
34 | for(var i=0;i<50;i++) {
|
35 | if(!common.checkTerrain(terrain, x === undefined ? i : x, y === undefined ? i : y, C.TERRAIN_MASK_WALL)) {
|
36 | exits.push(i);
|
37 | }
|
38 | }
|
39 | return exits;
|
40 | }
|
41 |
|
42 | function _genExit() {
|
43 | var exitLength = Math.floor(Math.random()*43)+1;
|
44 | var intervalsCnt = [0,0,1,1,2][Math.floor(Math.random()*5)];
|
45 | var exit = [],
|
46 | exitStart = Math.floor(Math.random() * (46 - exitLength))+2,
|
47 | intervals = {},
|
48 | curStart = exitStart;
|
49 |
|
50 | for(var j=0; j<intervalsCnt; j++) {
|
51 | curStart += Math.floor(Math.random() * (exitLength/(intervalsCnt*2)))+5;
|
52 | var length = Math.floor(Math.random() * (exitLength/(intervalsCnt*2)))+5;
|
53 | if(length + curStart >= exitStart + exitLength - 5) {
|
54 | length = exitStart + exitLength - curStart - 5;
|
55 | }
|
56 | intervals[j] = {
|
57 | start: curStart,
|
58 | length
|
59 | };
|
60 | curStart += length+1;
|
61 | }
|
62 |
|
63 | for(var x=exitStart; x<=exitStart+exitLength; x++) {
|
64 | if(intervalsCnt > 0) {
|
65 | if(intervals[0].length>0 && x >= intervals[0].start && x <= intervals[0].start+intervals[0].length) {
|
66 | continue;
|
67 | }
|
68 | if(intervalsCnt > 1 && intervals[1].length>0 && x >= intervals[1].start && x <= intervals[1].start+intervals[1].length) {
|
69 | continue;
|
70 | }
|
71 | }
|
72 | if(x < 2 || x > 47) {
|
73 | continue;
|
74 | }
|
75 | exit.push(x);
|
76 | }
|
77 |
|
78 | return exit;
|
79 | }
|
80 |
|
81 | function _matchExitWithNeighbors(exits, dir, neighbor) {
|
82 | var x,y;
|
83 | if(dir == 'top') {
|
84 | y = 49;
|
85 | }
|
86 | if(dir == 'right') {
|
87 | x = 0;
|
88 | }
|
89 | if(dir == 'bottom') {
|
90 | y = 0;
|
91 | }
|
92 | if(dir == 'left') {
|
93 | x = 49;
|
94 | }
|
95 | if(exits[dir]) {
|
96 | if(neighbor) {
|
97 | var neighborExits = _exitsArray(neighbor.terrain, x, y);
|
98 | return neighborExits.length == exits[dir].length && _.intersection(neighborExits, exits[dir]).length == neighborExits.length;
|
99 | }
|
100 | else {
|
101 | return true;
|
102 | }
|
103 | }
|
104 | else {
|
105 | if(neighbor) {
|
106 | exits[dir] = _exitsArray(neighbor.terrain, x, y);
|
107 | }
|
108 | else {
|
109 | exits[dir] = _genExit();
|
110 | }
|
111 | return true;
|
112 | }
|
113 | }
|
114 |
|
115 | function _checkFlood(terrain) {
|
116 |
|
117 | var startX, startY;
|
118 |
|
119 | for(var x=0; x<50; x++) {
|
120 | for(var y=0; y<50; y++) {
|
121 | if(!terrain[y][x].wall) {
|
122 | startX = x;
|
123 | startY = y;
|
124 | break;
|
125 | }
|
126 | }
|
127 | if(startX && startY) {
|
128 | break;
|
129 | }
|
130 | }
|
131 |
|
132 | var visited = {};
|
133 | for(var y=0; y<50; y++) {
|
134 | visited[y] = {};
|
135 | for (var x = 0; x < 50; x++) {
|
136 | visited[y][x] = false;
|
137 | }
|
138 | }
|
139 |
|
140 | var list = [[startX, startY]];
|
141 | do {
|
142 | var i = list.pop(),
|
143 | x = i[0], y = i[1];
|
144 |
|
145 | visited[y][x] = true;
|
146 | for(var dx=-1; dx<=1; dx++) {
|
147 | for(var dy=-1; dy<=1; dy++) {
|
148 | if(!dx && !dy) {
|
149 | continue;
|
150 | }
|
151 | if(x+dx >= 0 && x+dx <= 49 && y+dy >= 0 && y+dy <= 49 && !terrain[y+dy][x+dx].wall && !visited[y+dy][x+dx]) {
|
152 | visited[y+dy][x+dx] = true;
|
153 | list.push([x+dx,y+dy]);
|
154 | }
|
155 | }
|
156 | }
|
157 | }
|
158 | while(list.length > 0);
|
159 |
|
160 | for(var y=0; y<50; y++) {
|
161 | for (var x = 0; x < 50; x++) {
|
162 | if(!terrain[y][x].wall && !visited[y][x]) {
|
163 | return false;
|
164 | }
|
165 | }
|
166 | }
|
167 |
|
168 | return true;
|
169 | }
|
170 |
|
171 | function _smoothTerrain(terrain, factor, key) {
|
172 | var newTerrain = {};
|
173 |
|
174 | for(var y=0; y<50; y++) {
|
175 | newTerrain[y] = {};
|
176 | for(var x=0; x<50; x++) {
|
177 | newTerrain[y][x] = _.clone(terrain[y][x]);
|
178 | var cnt = 0;
|
179 | for(var dx=-1;dx<=1;dx++) {
|
180 | for(var dy=-1;dy<=1;dy++) {
|
181 | if(key == 'wall' && (x+dx < 0 || y+dy < 0 || x+dx > 49 || y+dy > 49) ||
|
182 | x+dx >= 0 && x+dx <= 49 && y+dy >= 0 && y+dy <= 49 && terrain[y+dy][x+dx][key]) {
|
183 | cnt++;
|
184 | }
|
185 | }
|
186 | }
|
187 | newTerrain[y][x][key] = cnt >= factor;
|
188 | if(key == 'wall') {
|
189 | if(x == 0 || x == 49 || y == 0 || y == 49) {
|
190 | newTerrain[y][x].wall = true;
|
191 | }
|
192 | if(terrain[y][x].forceOpen) {
|
193 | newTerrain[y][x].wall = false;
|
194 | }
|
195 | }
|
196 | }
|
197 | }
|
198 |
|
199 | return newTerrain;
|
200 | }
|
201 |
|
202 | function _genTerrain(wallType, swampType, exits, sources, controller, keeperLair) {
|
203 |
|
204 | var types = {
|
205 | 1: {fill: 0.4, smooth: 10, factor: 5},
|
206 | 2: {fill: 0.2, smooth: 20, factor: 4},
|
207 | 3: {fill: 0.2, smooth: 20, factor: 4},
|
208 | 4: {fill: 0.3, smooth: 18, factor: 4},
|
209 | 5: {fill: 0.3, smooth: 10, factor: 4},
|
210 | 6: {fill: 0.3, smooth: 10, factor: 4},
|
211 | 7: {fill: 0.3, smooth: 10, factor: 4},
|
212 | 8: {fill: 0.35, smooth: 15, factor: 4},
|
213 | 9: {fill: 0.3, smooth: 2, factor: 4},
|
214 | 10: {fill: 0.35, smooth: 2, factor: 4},
|
215 | 11: {fill: 0.35, smooth: 5, factor: 4},
|
216 | 12: {fill: 0.35, smooth: 5, factor: 4},
|
217 | 13: {fill: 0.25, smooth: 5, factor: 4},
|
218 |
|
219 | 14: {fill: 0.4, smooth: 3, factor: 5},
|
220 | 15: {fill: 0.5, smooth: 3, factor: 5},
|
221 | 16: {fill: 0.45, smooth: 4, factor: 5},
|
222 | 17: {fill: 0.45, smooth: 6, factor: 5},
|
223 | 18: {fill: 0.45, smooth: 10, factor: 5},
|
224 | 19: {fill: 0.5, smooth: 10, factor: 5},
|
225 |
|
226 | 20: {fill: 0.4, smooth: 3, factor: 5},
|
227 | 21: {fill: 0.5, smooth: 2, factor: 5},
|
228 | 22: {fill: 0.45, smooth: 4, factor: 5},
|
229 | 23: {fill: 0.45, smooth: 6, factor: 5},
|
230 | 24: {fill: 0.45, smooth: 10, factor: 5},
|
231 | 25: {fill: 0.5, smooth: 10, factor: 5},
|
232 |
|
233 | 26: {fill: 0.45, smooth: 10, factor: 5},
|
234 | 27: {fill: 0.45, smooth: 6, factor: 5},
|
235 | 28: {fill: 0.2, smooth: 20, factor: 4},
|
236 | };
|
237 |
|
238 | var swampTypes = {
|
239 | 1: {fill: 0.3, smooth: 3, factor: 5},
|
240 | 2: {fill: 0.35, smooth: 3, factor: 5},
|
241 | 3: {fill: 0.45, smooth: 3, factor: 5},
|
242 | 4: {fill: 0.25, smooth: 1, factor: 5},
|
243 | 5: {fill: 0.25, smooth: 30, factor: 4},
|
244 | 6: {fill: 0.52, smooth: 30, factor: 5},
|
245 | 7: {fill: 0.45, smooth: 3, factor: 5},
|
246 |
|
247 | 8: {fill: 0.3, smooth: 1, factor: 5},
|
248 | 9: {fill: 0.3, smooth: 1, factor: 4},
|
249 | 10: {fill: 0.3, smooth: 3, factor: 5},
|
250 | 11: {fill: 0.3, smooth: 3, factor: 5},
|
251 | 12: {fill: 0.3, smooth: 1, factor: 5},
|
252 | 13: {fill: 0.25, smooth: 1, factor: 5},
|
253 | 14: {fill: 0.35, smooth: 3, factor: 5},
|
254 | };
|
255 |
|
256 | var terrain, tries = 0;
|
257 |
|
258 | do {
|
259 | terrain = {};
|
260 | tries++;
|
261 |
|
262 | if(tries > 100) {
|
263 | wallType = Math.floor(Math.random()*27)+1;
|
264 | tries = 0;
|
265 | }
|
266 |
|
267 | for (var y = 0; y < 50; y++) {
|
268 | terrain[y] = {};
|
269 | for (var x = 0; x < 50; x++) {
|
270 | terrain[y][x] = {};
|
271 | }
|
272 | }
|
273 | for (var y = 0; y < 50; y++) {
|
274 | for (var x = 0; x < 50; x++) {
|
275 | if (y == 0 && _.isArray(exits.top) && _.contains(exits.top, x)) {
|
276 | terrain[y][x].forceOpen = true;
|
277 | terrain[y + 1][x].forceOpen = true;
|
278 | terrain[y][x].exit = true;
|
279 | continue;
|
280 | }
|
281 | if (y == 49 && _.isArray(exits.bottom) && _.contains(exits.bottom, x)) {
|
282 | terrain[y][x].forceOpen = true;
|
283 | terrain[y - 1][x].forceOpen = true;
|
284 | terrain[y][x].exit = true;
|
285 | continue;
|
286 | }
|
287 | if (x == 0 && _.isArray(exits.left) && _.contains(exits.left, y)) {
|
288 | terrain[y][x].forceOpen = true;
|
289 | terrain[y][x + 1].forceOpen = true;
|
290 | terrain[y][x].exit = true;
|
291 | continue;
|
292 | }
|
293 |
|
294 | if (x == 49 && _.isArray(exits.right) && _.contains(exits.right, y)) {
|
295 | terrain[y][x].forceOpen = true;
|
296 | terrain[y][x - 1].forceOpen = true;
|
297 | terrain[y][x].exit = true;
|
298 | continue;
|
299 | }
|
300 | terrain[y][x].wall = Math.random() < types[wallType].fill;
|
301 | terrain[y][x].swamp = swampType ? Math.random() < swampTypes[swampType].fill : false;
|
302 | }
|
303 | }
|
304 |
|
305 | for (var i = 0; i < types[wallType].smooth; i++) {
|
306 | terrain = _smoothTerrain(terrain, types[wallType].factor, 'wall');
|
307 | }
|
308 | }
|
309 | while(!_checkFlood(terrain));
|
310 |
|
311 | if(swampType) {
|
312 | for (var i = 0; i < swampTypes[swampType].smooth; i++) {
|
313 | terrain = _smoothTerrain(terrain, swampTypes[swampType].factor, 'swamp');
|
314 | }
|
315 | }
|
316 |
|
317 | for(var i=0; i<sources; i++) {
|
318 |
|
319 | var x,y;
|
320 | var tries = 0;
|
321 |
|
322 | do {
|
323 |
|
324 | tries++;
|
325 |
|
326 | x = Math.floor(Math.random() * 44) + 3;
|
327 | y = Math.floor(Math.random() * 44) + 3;
|
328 |
|
329 | var passNearby = false;
|
330 | for(var dx=-1; dx<=1; dx++) {
|
331 | for(var dy=-1; dy<=1; dy++) {
|
332 | if(x+dx < 0 || y+dy < 0 || x+dx > 49 || y+dy > 49) {
|
333 | continue;
|
334 | }
|
335 | if(!terrain[y+dy][x+dx].wall) {
|
336 | passNearby = true;
|
337 | break;
|
338 | }
|
339 | }
|
340 | }
|
341 |
|
342 | if(tries > 1000) {
|
343 | return _genTerrain(Math.floor(Math.random() * 27) + 1, swampType, exits, sources, controller, keeperLair);
|
344 | }
|
345 | }
|
346 | while(!terrain[y][x].wall || !passNearby);
|
347 |
|
348 | terrain[y][x].source = true;
|
349 |
|
350 | if(keeperLair) {
|
351 |
|
352 | var lairSpots = [],
|
353 | list = [[x, y]],
|
354 | visited = {[`${x},${y}`]: 0};
|
355 |
|
356 | do {
|
357 | var [cx,cy] = list.pop();
|
358 | for (var dx = -1; dx <= 1; dx++) {
|
359 | for (var dy = -1; dy <= 1; dy++) {
|
360 | if (!dx && !dy || !_.isUndefined(visited[`${cx + dx},${cy + dy}`])) {
|
361 | continue;
|
362 | }
|
363 | if (cx + dx < 0 || cy + dy < 0 || cx + dx > 49 || cy + dy > 49) {
|
364 | continue;
|
365 | }
|
366 | var distance = visited[`${cx},${cy}`] + 1;
|
367 | visited[`${cx + dx},${cy + dy}`] = distance;
|
368 | if (distance >= 3 && distance <= 5 && terrain[cy + dy][cx + dx].wall && !terrain[cy + dy][cx + dx].source &&
|
369 | (cx + dx > 0 && cx + dx < 49 && cy + dy > 0 && cy + dy < 49)) {
|
370 | lairSpots.push([cx + dx, cy + dy]);
|
371 | }
|
372 | if (!terrain[cy + dy][cx + dx].wall && distance < 5) {
|
373 | list.push([cx + dx, cy + dy]);
|
374 | }
|
375 | }
|
376 | }
|
377 | }
|
378 | while (list.length > 0);
|
379 |
|
380 |
|
381 | if (lairSpots.length > 0) {
|
382 | terrain[y][x].source = true;
|
383 | lairSpots = _.shuffle(lairSpots);
|
384 | terrain[lairSpots[0][1]][lairSpots[0][0]].keeperLair = true;
|
385 | }
|
386 | else {
|
387 | return _genTerrain(Math.floor(Math.random() * 27) + 1, swampType, exits, sources, controller, keeperLair);
|
388 | }
|
389 | }
|
390 | }
|
391 |
|
392 | if(controller) {
|
393 | var x,y;
|
394 | do {
|
395 | x = Math.floor(Math.random() * 40) + 5;
|
396 | y = Math.floor(Math.random() * 40) + 5;
|
397 | var passNearby = false;
|
398 | for(var dx=-1; dx<=1; dx++) {
|
399 | for(var dy=-1; dy<=1; dy++) {
|
400 | if(x+dx < 0 || y+dy < 0 || x+dx > 49 || y+dy > 49) {
|
401 | continue;
|
402 | }
|
403 | if(!terrain[y+dy][x+dx].wall) {
|
404 | passNearby = true;
|
405 | break;
|
406 | }
|
407 | }
|
408 | }
|
409 | }
|
410 | while(!terrain[y][x].wall || !passNearby || terrain[y][x].source || terrain[y][x].keeperLair);
|
411 | terrain[y][x].controller = true;
|
412 | }
|
413 |
|
414 | return terrain;
|
415 | }
|
416 |
|
417 | return db.rooms.findOne({_id: roomName})
|
418 | .then(data => data ? q.reject('This room already exists') : undefined)
|
419 | .then(() => {
|
420 | var [x,y] = utils.roomNameToXY(roomName);
|
421 | return q.all([
|
422 | db['rooms.terrain'].findOne({room: utils.roomNameFromXY(x, y - 1)}),
|
423 | db['rooms.terrain'].findOne({room: utils.roomNameFromXY(x + 1, y)}),
|
424 | db['rooms.terrain'].findOne({room: utils.roomNameFromXY(x, y + 1)}),
|
425 | db['rooms.terrain'].findOne({room: utils.roomNameFromXY(x - 1, y)})
|
426 | ]);
|
427 | })
|
428 | .then(neighborRooms => {
|
429 | if(!_matchExitWithNeighbors(opts.exits, 'top', neighborRooms[0])) {
|
430 | return q.reject(`Exits in room ${neighborRooms[0].room} don't match`);
|
431 | }
|
432 | if(!_matchExitWithNeighbors(opts.exits, 'right', neighborRooms[1])) {
|
433 | return q.reject(`Exits in room ${neighborRooms[1].room} don't match`);
|
434 | }
|
435 | if(!_matchExitWithNeighbors(opts.exits, 'bottom', neighborRooms[2])) {
|
436 | return q.reject(`Exits in room ${neighborRooms[2].room} don't match`);
|
437 | }
|
438 | if(!_matchExitWithNeighbors(opts.exits, 'left', neighborRooms[3])) {
|
439 | return q.reject(`Exits in room ${neighborRooms[3].room} don't match`);
|
440 | }
|
441 |
|
442 | opts.exits.top = opts.exits.top || [];
|
443 | opts.exits.left = opts.exits.left || [];
|
444 | opts.exits.bottom = opts.exits.bottom || [];
|
445 | opts.exits.right = opts.exits.right || [];
|
446 |
|
447 | if(opts.terrainType === undefined) {
|
448 | opts.terrainType = Math.floor(Math.random() * 27) + 1;
|
449 | }
|
450 | if(opts.swampType === undefined) {
|
451 | opts.swampType = Math.floor(Math.random() * 14);
|
452 | }
|
453 | if(opts.sources === undefined) {
|
454 | opts.sources = Math.random() > 0.5 ? 1 : 2;
|
455 | }
|
456 | if(opts.controller === undefined) {
|
457 | opts.controller = true;
|
458 | }
|
459 | if(opts.keeperLairs === undefined) {
|
460 | opts.keeperLairs = false;
|
461 | }
|
462 |
|
463 | var roomData = _genTerrain(opts.terrainType, opts.swampType, opts.exits, opts.sources, opts.controller, opts.keeperLairs);
|
464 |
|
465 | var objects = [], terrain = [], x, y, sourceKeepers = false;
|
466 |
|
467 | for (var y in roomData) {
|
468 | y = parseInt(y);
|
469 | for (var x in roomData[y]) {
|
470 | x = parseInt(x);
|
471 | if (roomData[y][x].wall) {
|
472 | terrain.push({type: 'wall', x, y});
|
473 | }
|
474 | if (roomData[y][x].source) {
|
475 | objects.push({
|
476 | room: roomName,
|
477 | type: 'source',
|
478 | x,
|
479 | y,
|
480 | "energy": C.SOURCE_ENERGY_NEUTRAL_CAPACITY,
|
481 | "energyCapacity": C.SOURCE_ENERGY_NEUTRAL_CAPACITY,
|
482 | "ticksToRegeneration": C.ENERGY_REGEN_TIME
|
483 | });
|
484 | }
|
485 | if (roomData[y][x].controller) {
|
486 | objects.push({room: roomName, type: 'controller', x, y, level: 0});
|
487 | }
|
488 | if (roomData[y][x].keeperLair) {
|
489 | objects.push({room: roomName, type: 'keeperLair', x, y});
|
490 | sourceKeepers = true;
|
491 | }
|
492 | if (roomData[y][x].swamp) {
|
493 | var flag = false;
|
494 | for (var dx = -1; dx <= 1; dx++) {
|
495 | for (var dy = -1; dy <= 1; dy++) {
|
496 | if (x + dx >= 0 && y + dy >= 0 && x + dx <= 49 && y + dy <= 49 && !roomData[y + dy][x + dx].wall) {
|
497 | flag = true;
|
498 | break;
|
499 | }
|
500 | }
|
501 | if (flag) {
|
502 | break;
|
503 | }
|
504 | }
|
505 | if (flag) {
|
506 | terrain.push({type: 'swamp', x, y});
|
507 | }
|
508 | }
|
509 | }
|
510 | }
|
511 |
|
512 | terrain = common.encodeTerrain(terrain);
|
513 |
|
514 | if(opts.mineral === undefined) {
|
515 | var types = ['H','H','H','H','H','H', 'O','O','O','O','O','O', 'Z','Z','Z', 'K','K','K', 'U','U','U', 'L','L','L', 'X'];
|
516 | opts.mineral = types[Math.floor(Math.random()*types.length)];
|
517 | }
|
518 |
|
519 | if(opts.mineral) {
|
520 | var mx,my,isWall,hasSpot,hasObjects;
|
521 | do {
|
522 | mx = 4 + Math.floor(Math.random()*42);
|
523 | my = 4 + Math.floor(Math.random()*42);
|
524 | isWall = common.checkTerrain(terrain, mx, my, C.TERRAIN_MASK_WALL);
|
525 | hasSpot = false;
|
526 | for(var dx=-1;dx<=1;dx++) {
|
527 | for(var dy=-1;dy<=1;dy++) {
|
528 | if(!common.checkTerrain(terrain,mx+dx,my+dy, C.TERRAIN_MASK_WALL)) {
|
529 | hasSpot = true;
|
530 | }
|
531 | }
|
532 | }
|
533 | hasObjects = _.any(objects, i => (i.type == 'source' || i.type == 'controller') && Math.abs(i.x - mx) < 5 && Math.abs(i.y - my) < 5);
|
534 | }
|
535 | while(!isWall || !hasSpot || hasObjects);
|
536 |
|
537 | var random = Math.random(), density;
|
538 | for(var d in C.MINERAL_DENSITY_PROBABILITY) {
|
539 | if(random <= C.MINERAL_DENSITY_PROBABILITY[d]) {
|
540 | density = +d;
|
541 | break;
|
542 | }
|
543 | }
|
544 |
|
545 | objects.push({
|
546 | type: 'mineral',
|
547 | mineralType: opts.mineral,
|
548 | density,
|
549 | mineralAmount: C.MINERAL_DENSITY[density],
|
550 | x: mx,
|
551 | y: my,
|
552 | room: roomName
|
553 | });
|
554 | }
|
555 |
|
556 | return q.all([
|
557 | db.rooms.insert({_id: roomName, status: 'normal', active: true, sourceKeepers}),
|
558 | db['rooms.terrain'].insert({room: roomName, terrain}),
|
559 | objects.length ? db['rooms.objects'].insert(objects) : q.when()
|
560 | ]);
|
561 | })
|
562 | .then(() => exports.updateRoomImageAssets(roomName))
|
563 | .then(() => exports.updateTerrainData())
|
564 | .then(() => 'OK');
|
565 | }
|
566 | ]);
|
567 |
|
568 | exports.openRoom = utils.withHelp([
|
569 | "openRoom(roomName, [timestamp]) - Make a room available for use. Specify a timestamp in the future if you want it to be opened later automatically.",
|
570 | function openRoom(roomName, timestamp) {
|
571 |
|
572 | var $set = {status: 'normal'};
|
573 |
|
574 | if(timestamp) {
|
575 | $set.openTime = timestamp;
|
576 | }
|
577 |
|
578 | return db.rooms.update({_id: roomName}, {$set});
|
579 | }
|
580 | ]);
|
581 |
|
582 | exports.closeRoom = utils.withHelp([
|
583 | "closeRoom(roomName) - Make a room not available.",
|
584 | function closeRoom(roomName) {
|
585 | return db.rooms.update({$and: [{_id: roomName}, {status: 'normal'}]}, {$set: {status: 'out of borders'}});
|
586 | }
|
587 | ]);
|
588 |
|
589 | exports.removeRoom = utils.withHelp([
|
590 | "removeRoom(roomName) - Delete the room and all room objects from the database.",
|
591 | function removeRoom(roomName) {
|
592 | return db.rooms.findOne({_id: roomName})
|
593 | .then(data => !data ? q.reject(`The room ${roomName} is not found`) :
|
594 | q.all([
|
595 | db.rooms.removeWhere({_id: roomName}),
|
596 | db['rooms.objects'].removeWhere({room: roomName}),
|
597 | db['rooms.flags'].removeWhere({room: roomName}),
|
598 | db['rooms.terrain'].removeWhere({room: roomName}),
|
599 | db['rooms.intents'].removeWhere({room: roomName}),
|
600 | db['market.stats'].removeWhere({room: roomName}),
|
601 | env.del(env.keys.MAP_VIEW+roomName)
|
602 | ])
|
603 | .then(() => exports.updateTerrainData())
|
604 | .then(() => {
|
605 | fs.unlinkSync(path.resolve(process.env.ASSET_DIR, 'map', roomName+'.png'));
|
606 | })
|
607 | .then(() => 'OK'));
|
608 | }
|
609 | ]);
|
610 |
|
611 | exports.updateRoomImageAssets = utils.withHelp([
|
612 | "updateRoomImageAssets(roomName) - Update images in assets folder for the specified room.",
|
613 | function updateRoomImageAssets(roomName) {
|
614 |
|
615 | return db['rooms.terrain'].findOne({room: roomName})
|
616 | .then(terrainItem => utils.writeTerrainToPng(terrainItem.terrain,
|
617 | path.resolve(process.env.ASSET_DIR, 'map', roomName + '.png'), true))
|
618 | .then(() => {
|
619 | var [x,y] = utils.roomNameToXY(roomName);
|
620 | x = Math.floor(x / 4) * 4;
|
621 | y = Math.floor(y / 4) * 4;
|
622 | var firstRoomName = utils.roomNameFromXY(x,y);
|
623 | var allRoomNames = [];
|
624 | for(var xx=x; xx<=x+4; xx++) {
|
625 | for(var yy=y; yy<=y+4; yy++) {
|
626 | allRoomNames.push(utils.roomNameFromXY(xx,yy));
|
627 | }
|
628 | }
|
629 | return db['rooms.terrain'].find({room: {$in: allRoomNames}})
|
630 | .then(data => {
|
631 | var mergedColors = {};
|
632 | for(var yy=0; yy<200; yy++) {
|
633 | mergedColors[yy] = {};
|
634 | for(var xx=0; xx<200; xx++) {
|
635 | mergedColors[yy][xx] = [0,0,0,0];
|
636 | }
|
637 | }
|
638 | for(var xx=0; xx<4; xx++) {
|
639 | for(var yy=0; yy<4; yy++) {
|
640 |
|
641 | var terrainItem = _.find(data, {room: utils.roomNameFromXY(xx+x,yy+y)});
|
642 | if(!terrainItem) {
|
643 | continue;
|
644 | }
|
645 |
|
646 | var colors = utils.createTerrainColorsMap(terrainItem.terrain, false);
|
647 | for(var cy in colors) {
|
648 | for(var cx in colors[cy]) {
|
649 | mergedColors[parseInt(cy)+yy*50][parseInt(cx)+xx*50] = colors[cy][cx];
|
650 | }
|
651 | }
|
652 | }
|
653 | }
|
654 |
|
655 | return utils.writePng(mergedColors, 200, 200,
|
656 | path.resolve(process.env.ASSET_DIR, 'map/zoom2', firstRoomName + '.png'));
|
657 | })
|
658 | });
|
659 | }]);
|
660 |
|
661 | exports.updateTerrainData = utils.withHelp([
|
662 | "updateTerrainData() - Update cached world terrain data.",
|
663 | function updateTerrainData() {
|
664 |
|
665 | var walled = '';
|
666 | for(var i=0; i<2500; i++) {
|
667 | walled += '1';
|
668 | }
|
669 |
|
670 | return q.all([
|
671 | db.rooms.find(),
|
672 | db['rooms.terrain'].find()
|
673 | ])
|
674 | .then(result => {
|
675 | var [rooms,terrain] = result;
|
676 |
|
677 | rooms.forEach(room => {
|
678 | if(room.status == 'out of borders') {
|
679 | _.find(terrain, {room: room._id}).terrain = walled;
|
680 | }
|
681 | var m = room._id.match(/(W|E)(\d+)(N|S)(\d+)/);
|
682 | var roomH = m[1]+(+m[2]+1)+m[3]+m[4], roomV = m[1]+m[2]+m[3]+(+m[4]+1);
|
683 | if(!_.any(terrain, {room: roomH})) {
|
684 | terrain.push({room: roomH, terrain: walled});
|
685 | }
|
686 | if(!_.any(terrain, {room: roomV})) {
|
687 | terrain.push({room: roomV, terrain: walled});
|
688 | }
|
689 | });
|
690 |
|
691 | return q.ninvoke(zlib, 'deflate', JSON.stringify(terrain));
|
692 | })
|
693 | .then(compressed => env.set(env.keys.TERRAIN_DATA, compressed.toString('base64')))
|
694 | .then(() => 'OK');
|
695 | }]);
|
696 |
|
697 |
|
698 | exports._help = utils.generateCliHelp('map.', exports); |
\ | No newline at end of file |