UNPKG

11.5 kBJavaScriptView Raw
1const _ = require('lodash');
2const common = require('@screeps/common');
3const config = common.configManager.config;
4const C = config.common.constants;
5const db = common.storage.db;
6const env = common.storage.env;
7const q = require('q');
8const png = require('pngjs').PNG;
9const fs = require('fs');
10const path = require('path');
11
12exports.roomNameFromXY = function(x,y) {
13 if(x < 0) {
14 x = 'W'+(-x-1);
15 }
16 else {
17 x = 'E'+(x);
18 }
19 if(y < 0) {
20 y = 'N'+(-y-1);
21 }
22 else {
23 y = 'S'+(y);
24 }
25 return ""+x+y;
26}
27
28exports.roomNameToXY = function(name) {
29 var [match,hor,x,ver,y] = name.match(/^(\w)(\d+)(\w)(\d+)$/);
30 if(hor == 'W') {
31 x = -x-1;
32 }
33 else {
34 x = +x;
35 //x--;
36 }
37 if(ver == 'N') {
38 y = -y-1;
39 }
40 else {
41 y = +y;
42 //y--;
43 }
44 return [x,y];
45}
46
47exports.translateModulesFromDb = function(modules) {
48 modules = modules || {};
49
50 for(var key in modules) {
51 var newKey = key.replace(/\$DOT\$/g, '.');
52 newKey = newKey.replace(/\$SLASH\$/g, '/');
53 newKey = newKey.replace(/\$BACKSLASH\$/g, '\\');
54 if(newKey != key) {
55 modules[newKey] = modules[key];
56 delete modules[key];
57 }
58 }
59 return modules;
60};
61
62exports.translateModulesToDb = function(modules) {
63 modules = modules || {};
64
65 for(var key in modules) {
66 var newKey = key.replace(/\./g,'$DOT$');
67 newKey = newKey.replace(/\//g,'$SLASH$');
68 newKey = newKey.replace(/\\/g,'$BACKSLASH$');
69
70 if(newKey[0] == '$') {
71 delete modules[key];
72 continue;
73 }
74
75 if(newKey != key) {
76 modules[newKey] = modules[key];
77 delete modules[key];
78 }
79 }
80
81 if(!modules.main) {
82 modules.main = '';
83 }
84
85 return modules;
86};
87
88exports.getUserWorldStatus = function(user) {
89 return db['rooms.objects'].count({user: ""+user._id})
90 .then((objectsCnt) => {
91 if(!objectsCnt) {
92 return {status: 'empty'};
93 }
94 return db['rooms.objects'].find({$and: [
95 {user: ""+user._id},
96 {type: {$in: ['spawn','controller']}}
97 ]}).then((objects) => {
98 var spawns = false;
99 if(objects) {
100 objects.forEach((i) => {
101 if (i.type == 'spawn') {
102 if(!_.any(objects, {type: 'controller', room: i.room, user: i.user})) {
103 return;
104 }
105 spawns = true;
106 }
107 })
108 }
109 return {status: spawns ? 'normal' : 'lost'};
110 })
111 })
112};
113
114
115exports.respawnUser = async function(userId) {
116 return db['users'].findOne({username: C.SYSTEM_USERNAME})
117 .then(async systemUser => {
118 if(!systemUser) {
119 return q.reject('no system user');
120 }
121 const gameTime = await common.getGametime();
122 await db['rooms.objects'].removeWhere({$and: [{user: ""+userId}, {type: {$in: ['creep','powerCreep','constructionSite']}}]});
123 await db['users.power_creeps'].removeWhere({user: ""+userId});
124 const objects = await db['rooms.objects'].find({user: "" + userId, type: {$in: _.keys(C.CONSTRUCTION_COST)}});
125 if(objects.length) {
126 await db['rooms.objects'].insert(objects.map(i => ({
127 type: 'ruin',
128 user: ""+systemUser._id,
129 room: i.room,
130 x: i.x,
131 y: i.y,
132 structure: {
133 id: i._id,
134 type: i.type,
135 hits: 0,
136 hitsMax: i.hitsMax,
137 user: ""+systemUser._id
138 },
139 store: i.store || {},
140 destroyTime: gameTime,
141 decayTime: gameTime + 500000
142 })));
143 }
144 await db['rooms.objects'].update({user: "" + userId, type: 'ruin'}, {$set: {user: ""+systemUser._id}});
145 await db['rooms.objects'].removeWhere({user: "" + userId, type: {$ne: 'controller'}});
146 await db['rooms.flags'].removeWhere({user: ""+userId});
147 const controllers = db['rooms.objects'].find({$and: [{user: ""+userId}, {type: 'controller'}]});
148 for(let i in controllers) {
149 await db.rooms.update({_id: controllers[i].room}, {$set: {status: 'normal'}});
150 }
151 await db['rooms.objects'].update({$and: [{user: "" + userId}, {type: 'controller'}]}, {
152 $set: {
153 level: 0,
154 hits: 0,
155 hitsMax: 0,
156 progress: 0,
157 progressTotal: 0,
158 user: null,
159 downgradeTime: null,
160 safeMode: null,
161 safeModeAvailable: 0,
162 safeModeCooldown: null
163 }
164 });
165 })
166 .then(() => db['users'].update({_id: "" + userId}, {$set: {rooms: []}}));
167};
168
169exports.withHelp = function(array) {
170 var fn = array[1];
171 fn._help = array[0];
172 return fn;
173};
174
175exports.generateCliHelp = function(prefix, container) {
176 return `Available methods:\r\n`+Object.keys(container).filter(i => typeof container[i] == 'function').map(i => ' - ' + prefix + (container[i]._help || i)).join('\r\n');
177};
178
179exports.writePng = function(colors, width, height, filename) {
180
181 var image = new png({width, height});
182
183 for(var y=0; y<height; y++) {
184 for(var x=0; x<width; x++) {
185 var idx = (width*y + x) << 2;
186
187 image.data[idx] = colors[y][x][0];
188 image.data[idx+1] = colors[y][x][1];
189 image.data[idx+2] = colors[y][x][2];
190 image.data[idx+3] = colors[y][x][3] === undefined ? 255 : colors[y][x][3];
191 }
192 }
193
194 var defer = q.defer();
195 image.pack().pipe(fs.createWriteStream(filename)).on('finish', () => defer.resolve());
196 return defer.promise;
197};
198
199exports.createTerrainColorsMap = function(terrain, zoomIn) {
200 var colors = {},
201 width = 50, height = 50;
202
203 for(var y=0; y<height; y++) {
204 if(zoomIn) {
205 colors[y * 3] = {};
206 colors[y * 3 + 1] = {};
207 colors[y * 3 + 2] = {};
208 }
209 else {
210 colors[y] = {};
211 }
212 for(var x=0; x<width; x++) {
213
214 var color;
215 if(common.checkTerrain(terrain,x,y,C.TERRAIN_MASK_WALL)) {
216 color = [0,0,0];
217 }
218 else if(common.checkTerrain(terrain,x,y,C.TERRAIN_MASK_SWAMP)) {
219 color = [35,37,19];
220 }
221 else if(x == 0 || y == 0 || x == 49 || y == 49) {
222 color = [50,50,50];
223 }
224 else {
225 color = [43,43,43];
226 }
227 if(zoomIn) {
228 for (var dx = 0; dx < 3; dx++) {
229 for (var dy = 0; dy < 3; dy++) {
230 colors[y * 3 + dy][x * 3 + dx] = color;
231 }
232 }
233 }
234 else {
235 colors[y][x] = color;
236 }
237 }
238 }
239
240 return colors;
241};
242
243exports.writeTerrainToPng = function(terrain, filename, zoomIn) {
244
245 var colors = exports.createTerrainColorsMap(terrain, zoomIn);
246 return exports.writePng(colors, 50*(zoomIn?3:1), 50*(zoomIn?3:1), filename);
247};
248
249exports.loadBot = function(name) {
250 var dir = config.common.bots[name];
251 if(!dir) {
252 throw new Error(`Bot AI with the name "${name}" doesn't exist`);
253 }
254 var stat = fs.statSync(dir);
255 if (!stat.isDirectory()) {
256 throw new Error(`"${dir}" is not a directory`);
257 }
258 fs.statSync(path.resolve(dir, 'main.js'));
259 var files = fs.readdirSync(dir), modules = {};
260 files.forEach(file => {
261 var m = file.match(/^(.*)\.js$/);
262 if(!m) {
263 return;
264 }
265 modules[m[1]] = fs.readFileSync(path.resolve(dir, file), {encoding: 'utf8'});
266 });
267 return exports.translateModulesToDb(modules);
268};
269
270exports.reloadBotUsers = function(name) {
271
272 return db.users.find({bot: name})
273 .then(users => {
274 if(!users.length) {
275 return 'No bot players found';
276 }
277 var modules = exports.loadBot(name);
278 var timestamp = Date.now();
279
280 return db['users.code'].insert(users.map(i => ({
281 user: i._id,
282 branch: 't'+timestamp,
283 timestamp,
284 activeWorld: true,
285 activeSim: true,
286 modules
287 })))
288 .then(() => db['users.code'].removeWhere({$and: [
289 {user: {$in: users.map(i => i._id)}},
290 {branch: {$ne: 't'+timestamp}}
291 ]}))
292 .then(() => 'Reloaded scripts for users: '+users.map(i => i.username).join(', '));
293 })
294};
295
296exports.isBus = function isBus(coord) {
297 return coord < 0 && (coord+1) % 10 == 0 || coord > 0 && (coord) % 10 == 0 || coord == 0;
298};
299
300exports.isCenter = function isCenter(x,y) {
301 return (x < 0 && Math.abs(x+1)%10 >= 4 && Math.abs(x+1)%10 <= 6 || x >= 0 && Math.abs(x)%10 >= 4 && Math.abs(x)%10 <= 6) &&
302 (y < 0 && Math.abs(y+1)%10 >= 4 && Math.abs(y+1)%10 <= 6 || y >= 0 && Math.abs(y)%10 >= 4 && Math.abs(y)%10 <= 6);
303};
304
305exports.isVeryCenter = function isVeryCenter(x,y) {
306 return (x < 0 && Math.abs(x+1)%10 == 5 || x >= 0 && Math.abs(x)%10 == 5) &&
307 (y < 0 && Math.abs(y+1)%10 == 5 || y >= 0 && Math.abs(y)%10 == 5);
308};
309
310exports.activateRoom = function activateRoom(room) {
311 return env.sadd(env.keys.ACTIVE_ROOMS, room);
312};
313
314exports.getActiveRooms = function getActiveRooms() {
315 return env.smembers(env.keys.ACTIVE_ROOMS);
316};
317
318exports.findFreePos = function findFreePos(roomName, distance, rect, exclude) {
319 if(!rect) {
320 rect = {x1: 4, x2: 45, y1: 4, y2: 45};
321 }
322
323 return q.all([
324 db['rooms.objects'].find({room: roomName}),
325 db['rooms.terrain'].findOne({room: roomName})])
326 .then(([objects, terrain]) => {
327 if (!terrain) {
328 return q.reject();
329 }
330 var x, y, spot, hasObjects, counter = 0;
331 do {
332
333 x = rect.x1 + Math.floor(Math.random() * (rect.x2 - rect.x1));
334 y = rect.y1 + Math.floor(Math.random() * (rect.y2 - rect.y1));
335 if(exclude && exclude.x == x && exclude.y == y) {
336 continue;
337 }
338 spot = true;
339 for (var dx = -distance; dx <= distance; dx++) {
340 for (var dy = -distance; dy <= distance; dy++) {
341 if (common.checkTerrain(terrain.terrain, x + dx, y + dy, C.TERRAIN_MASK_WALL)) {
342 spot = false;
343 }
344 }
345 }
346 hasObjects = _.any(objects, i => Math.abs(i.x - x) <= distance && Math.abs(i.y - y) <= distance &&
347 C.OBSTACLE_OBJECT_TYPES.concat(['rampart','portal']).indexOf(i.type) != -1);
348 counter++;
349 }
350 while ((!spot || hasObjects) && counter < 500);
351
352 if (!spot || hasObjects) {
353 return q.reject();
354 }
355
356 return {x, y};
357 });
358}