UNPKG

11.8 kBJavaScriptView Raw
1const _ = require('lodash'),
2 q = require('q'),
3 common = require('@screeps/common'),
4 C = common.configManager.config.common.constants,
5 strongholds = require('@screeps/common/lib/strongholds'),
6 db = common.storage.db,
7 env = common.storage.env,
8 util = require('util'),
9 zlib = require('zlib'),
10 utils = require('./utils');
11
12const strongholdDeployTime = 5000;
13
14async function spawnStronghold(roomName, opts = {}) {
15
16 if(!opts.deployTime) {
17 opts.deployTime = 1;
18 }
19
20 if(!roomName) {
21 return q.reject('invalid room');
22 }
23
24 if(opts.templateName && !strongholds.templates[opts.templateName]) {
25 q.reject('invalid template');
26 }
27 const user = opts.user || "2";
28
29 const gameTime = await common.getGametime();
30
31 const [{terrain}, objects] = await q.all([
32 db['rooms.terrain'].findOne({room: roomName}),
33 db['rooms.objects'].find({room: roomName, type: {$in: [C.STRUCTURE_RAMPART, ...C.OBSTACLE_OBJECT_TYPES]}})
34 ]);
35
36 const templateNames = opts.templateName ? [opts.templateName] : _.keys(strongholds.templates);
37
38 const controller = _(objects).filter({type: C.STRUCTURE_CONTROLLER}).first();
39 if(controller && (controller.user || (controller.reservation && controller.reservation.user != user))) {
40 return q.reject('room occupied');
41 }
42
43 const spots = {};
44 if(!_.isUndefined(opts.templateName) && !_.isUndefined(opts.x) && !_.isUndefined(opts.y)){
45 return opts;
46 } else {
47 for(let x = 4; x < 45; x++)
48 for(let y = 4; y < 45; y++)
49 for(let t of templateNames)
50 if(_.reduce(strongholds.templates[t].structures, (r,s) => r && !common.checkTerrain(terrain, x+s.dx, y+s.dy, C.TERRAIN_MASK_WALL) && !_.some(objects, {x: x+s.dx, y: y+s.dy}), true)) {
51 if(_.isUndefined(spots[t])) {
52 spots[t] = [];
53 }
54 spots[t].push({x,y});
55 }
56 }
57
58 if(!_.some(spots)) {
59 return q.reject('No spots');
60 }
61 const selectedTemplateName = _.sample(_.keys(spots));
62 const selectedTemplate = strongholds.templates[selectedTemplateName];
63 const spot = _.sample(spots[selectedTemplateName]);
64
65 const depositType = _.sample(_.keys(strongholds.coreRewards));
66
67 const deployTime = gameTime + opts.deployTime;
68 const strongholdId = `${""+roomName}_${gameTime}`;
69 const structures = _.reduce(selectedTemplate.structures, (acc, i) => {
70 if(i.type == C.STRUCTURE_INVADER_CORE) {
71 const core = _.merge({}, i, {
72 room: roomName,
73 x: 0+spot.x+i.dx,
74 y: 0+spot.y+i.dy,
75 user,
76 templateName: selectedTemplateName,
77 hits: C.INVADER_CORE_HITS,
78 hitsMax: C.INVADER_CORE_HITS,
79 nextExpandTime: deployTime + C.INVADER_CORE_EXPAND_TIME[i.level],
80 depositType,
81 deployTime,
82 strongholdId,
83 effects: [{
84 effect: C.EFFECT_INVULNERABILITY,
85 power: C.EFFECT_INVULNERABILITY,
86 endTime: deployTime,
87 duration: strongholdDeployTime
88 }]
89 });
90 delete core.dx;
91 delete core.dy;
92 acc.push(core);
93 }
94 if(i.type == C.STRUCTURE_RAMPART) {
95 acc.push({
96 type: C.STRUCTURE_RAMPART,
97 room: roomName,
98 x: 0+spot.x+i.dx,
99 y: 0+spot.y+i.dy,
100 user,
101 hits: 1,
102 hitsMax: 1,
103 isPublic: true,
104 strongholdId,
105 nextDecayTime: deployTime
106 });
107 }
108 return acc;
109 }, []);
110
111 if(controller) {
112 await db['rooms.objects'].update({_id: controller._id}, {$set:
113 {
114 user,
115 level: 8,
116 progress: 0,
117 downgradeTime: deployTime,
118 effects: [{
119 effect: C.EFFECT_INVULNERABILITY,
120 power: C.EFFECT_INVULNERABILITY,
121 endTime: deployTime,
122 duration: strongholdDeployTime
123 }]}});
124 }
125
126 return db['rooms.objects'].insert(structures)
127 .then(()=> utils.activateRoom(roomName));
128};
129
130async function selectRoom(sectorCenter) {
131
132 const sectorRegex = sectorCenter._id.replace(/^([WE]\d*)5([NS]\d*)5$/, (str, p1, p2) => `^${p1}\\d${p2}\\d$`);
133 return q.all([
134 db['rooms'].find({_id: {$regex: sectorRegex}}),
135 db['rooms.objects'].find({type: {$in: ['creep', 'controller']}, room: {$regex: sectorRegex}})
136 ])
137 .then(([sectorRooms, sectorObjects]) => {
138
139 const possibleRooms = _.filter(
140 sectorRooms,
141 r => {
142 const [x,y] = common.roomNameToXY(r._id);
143 if(r.bus || !utils.isCenter(x,y) || utils.isVeryCenter(x, y) || r._id.match('0$') || r._id.match('0[NS]')) {
144 return false;
145 }
146 const controller = _(sectorObjects).filter({type: 'controller', room: r._id}).first();
147 if(!controller) {
148 return true;
149 }
150 if(!!controller.user || !!controller.reservation) {
151 return false;
152 }
153
154 return !_.some(sectorObjects, o => o.room==r._id && o.type == 'creep' && _.some(o.body, {type: 'claim'}));
155 });
156 if(_.some(possibleRooms)) {
157 const room = _.sample(possibleRooms);
158 return spawnStronghold(room._id, {deployTime: strongholdDeployTime});
159 }
160 });
161};
162
163async function buildMapGrid() {
164
165 const compressedTerrainData = await env.get(env.keys.TERRAIN_DATA);
166
167 const buf = Buffer.from(compressedTerrainData, 'base64');
168 const terrainData = JSON.parse(await util.promisify(zlib.inflate)(buf));
169
170 const accessibleRooms = JSON.parse(await env.get(env.keys.ACCESSIBLE_ROOMS));
171
172 if (!accessibleRooms) return {};
173 const dirs = {
174 t: {
175 startx: 0,
176 starty: 0,
177 dx: 1,
178 dy: 0,
179 },
180 r: {
181 startx: 49,
182 starty: 0,
183 dx: 0,
184 dy: 1,
185 },
186 b: {
187 startx: 0,
188 starty: 49,
189 dx: 1,
190 dy: 0,
191 },
192 l: {
193 startx: 0,
194 starty: 0,
195 dx: 0,
196 dy: 1,
197 },
198 };
199 let gridData = {};
200 for (let roomName of accessibleRooms) {
201 let [x, y] = common.roomNameToXY(roomName);
202 let terrain = _.find(terrainData, {room:roomName}).terrain;
203 let roomData = {};
204 for (let dirName in dirs) {
205 let {startx, starty, dx, dy} = dirs[dirName];
206 let curx = startx;
207 let cury = starty;
208 let numExits = 0;
209 for (let i = 0; i < 50; ++i) {
210 if (terrain[cury * 50 + curx] == 0) {
211 numExits++;
212 }
213 curx += dx;
214 cury += dy;
215 }
216 if (numExits > 0) {
217 roomData[dirName] = numExits;
218 }
219 }
220 gridData[`${x},${y}`] = roomData;
221 }
222 return gridData;
223}
224
225async function genStrongholds() {
226 const [rooms, objects] = await q.all([
227 db['rooms'].find({$and:[{_id: {$regex: '^[WE]\\d*5[NS]\\d*5$'}}, {'status': {$ne: 'out of borders'}}]}),
228 db['rooms.objects'].find({$or: [{type: 'invaderCore'},{$and: [{type: 'ruin'}, {'structure.type': 'invaderCore'}]}]})
229 ]);
230
231 const sectorsWithoutCores = _.reject(rooms, r => _.some(objects, c => r._id == c.room.replace(/^([WE]\d*)\d([NS]\d*)\d$/, (str, p1, p2) => `${p1}5${p2}5`)));
232
233 for(let s of sectorsWithoutCores) {
234 await selectRoom(s);
235 }
236}
237
238async function expandStronghold(invaderCore, {gameTime, mapGrid} = {}) {
239
240 if(!mapGrid) {
241 mapGrid = await buildMapGrid();
242 }
243 if(!gameTime) {
244 gameTime = await common.getGametime();
245 }
246
247 let openList = [invaderCore.room], closedList = [], found;
248
249 let roomInfo = await db['rooms'].findOne({_id: invaderCore.room});
250 if(roomInfo.novice > Date.now() || roomInfo.respawnArea > Date.now()) {
251 return false;
252 }
253
254 await db['rooms.objects'].update({_id: invaderCore._id},
255 {$set: {nextExpandTime: gameTime + C.INVADER_CORE_EXPAND_TIME[invaderCore.level]}});
256
257 do {
258 let room = openList.shift();
259 closedList.push(room);
260 let [x, y] = common.roomNameToXY(room);
261 let exits = _.shuffle(Object.keys(mapGrid[`${x},${y}`]));
262 for (let dir of exits) {
263 let dx = 0, dy = 0;
264 if (dir == 't') {
265 dy = -1;
266 }
267 if (dir == 'b') {
268 dy = 1;
269 }
270 if (dir == 'l') {
271 dx = -1;
272 }
273 if (dir == 'r') {
274 dx = 1;
275 }
276 if (!mapGrid[`${x + dx},${y + dy}`]) {
277 continue;
278 }
279 let nextRoom = common.getRoomNameFromXY(x + dx, y + dy);
280 if(/(W|E)\d*0/.test(nextRoom) || /(N|S)\d*0/.test(nextRoom)) {
281 continue;
282 }
283 let nextRoomInfo = await db['rooms'].findOne({_id: nextRoom});
284 if(nextRoomInfo.novice > Date.now() || nextRoomInfo.respawnArea > Date.now()) {
285 continue;
286 }
287 let controller = await db['rooms.objects'].findOne({type: 'controller', room: nextRoom});
288 let hasCore = await db['rooms.objects'].count({type: 'invaderCore', room: nextRoom});
289 if(controller && !controller.user && !hasCore) {
290 found = {room: nextRoom, controller};
291 break;
292 }
293 if ((!controller || hasCore) && openList.indexOf(nextRoom) === -1 && closedList.indexOf(nextRoom) === -1) {
294 openList.push(nextRoom);
295 }
296 }
297 }
298 while(!found && openList.length > 0);
299
300 if(!found) {
301 return false;
302 }
303
304 try {
305 var {x, y} = await utils.findFreePos(found.room, 1, {
306 x1: Math.max(1, found.controller.x - 5),
307 x2: Math.min(48, found.controller.x + 5),
308 y1: Math.max(1, found.controller.y - 5),
309 y2: Math.min(48, found.controller.y + 5)
310 });
311 }
312 catch(e) {
313 console.log('no pos',e);
314 return false;
315 }
316
317 await db['rooms.objects'].insert({
318 type: 'invaderCore',
319 level: 0,
320 room: found.room,
321 x, y,
322 user: '2',
323 hits: C.INVADER_CORE_HITS,
324 hitsMax: C.INVADER_CORE_HITS,
325 strongholdId: invaderCore.strongholdId,
326 effects: invaderCore.effects
327 });
328 await utils.activateRoom(found.room);
329
330 return true;
331}
332
333async function expandStrongholds() {
334
335 let mapGrid = await buildMapGrid();
336
337 let gameTime = await common.getGametime();
338 let invaderCores = await db['rooms.objects'].find({
339 type: 'invaderCore',
340 deployTime: null,
341 level: {$gt: 0},
342 nextExpandTime: {$lt: gameTime}
343 });
344
345
346 for(let invaderCore of invaderCores) {
347 await expandStronghold(invaderCore, {gameTime, mapGrid});
348 }
349}
350
351
352exports.spawnStronghold = spawnStronghold;
353exports.selectRoom = selectRoom;
354exports.genStrongholds = genStrongholds;
355exports.expandStronghold = expandStronghold;
356exports.expandStrongholds = expandStrongholds;
357exports.templates = strongholds.templates;