/** * @class Dungeon generator which tries to fill the space evenly. Generates independent rooms and tries to connect them. * @augments ROT.Map.Dungeon */ ROT.Map.Uniform = function(width, height, options) { ROT.Map.Dungeon.call(this, width, height); this._options = { roomWidth: [3, 9], /* room minimum and maximum width */ roomHeight: [3, 5], /* room minimum and maximum height */ roomDugPercentage: 0.1, /* we stop after this percentage of level area has been dug out by rooms */ timeLimit: 1000 /* we stop after this much time has passed (msec) */ } for (var p in options) { this._options[p] = options[p]; } this._roomAttempts = 20; /* new room is created N-times until is considered as impossible to generate */ this._corridorAttempts = 20; /* corridors are tried N-times until the level is considered as impossible to connect */ this._connected = []; /* list of already connected rooms */ this._unconnected = []; /* list of remaining unconnected rooms */ this._digCallback = this._digCallback.bind(this); this._canBeDugCallback = this._canBeDugCallback.bind(this); this._isWallCallback = this._isWallCallback.bind(this); } ROT.Map.Uniform.extend(ROT.Map.Dungeon); /** * Create a map. If the time limit has been hit, returns null. * @see ROT.Map#create */ ROT.Map.Uniform.prototype.create = function(callback) { var t1 = Date.now(); while (1) { var t2 = Date.now(); if (t2 - t1 > this._options.timeLimit) { return null; } /* time limit! */ this._map = this._fillMap(1); this._dug = 0; this._rooms = []; this._unconnected = []; this._generateRooms(); if (this._generateCorridors()) { break; } } if (callback) { for (var i=0;i this._options.roomDugPercentage) { break; } /* achieved requested amount of free space */ } while (room); /* either enough rooms, or not able to generate more of them :) */ } /** * Try to generate one room */ ROT.Map.Uniform.prototype._generateRoom = function() { var count = 0; while (count < this._roomAttempts) { count++; var room = ROT.Map.Feature.Room.createRandom(this._width, this._height, this._options); if (!room.isValid(this._isWallCallback, this._canBeDugCallback)) { continue; } room.create(this._digCallback); this._rooms.push(room); return room; } /* no room was generated in a given number of attempts */ return null; } /** * Generates connectors beween rooms * @returns {bool} success Was this attempt successfull? */ ROT.Map.Uniform.prototype._generateCorridors = function() { var cnt = 0; while (cnt < this._corridorAttempts) { cnt++; this._corridors = []; /* dig rooms into a clear map */ this._map = this._fillMap(1); for (var i=0;i 0 ? 2 : 0); var dirIndex2 = (dirIndex1 + 2) % 4; var min = room2.getLeft(); var max = room2.getRight(); var index = 0; } else { /* first try connecting east-west walls */ var dirIndex1 = (diffX > 0 ? 1 : 3); var dirIndex2 = (dirIndex1 + 2) % 4; var min = room2.getTop(); var max = room2.getBottom(); var index = 1; } var start = this._placeInWall(room1, dirIndex1); /* corridor will start here */ if (!start) { return false; } if (start[index] >= min && start[index] <= max) { /* possible to connect with straight line (I-like) */ var end = start.slice(); var value = null; switch (dirIndex2) { case 0: value = room2.getTop()-1; break; case 1: value = room2.getRight()+1; break; case 2: value = room2.getBottom()+1; break; case 3: value = room2.getLeft()-1; break; } end[(index+1)%2] = value; this._digLine([start, end]); } else if (start[index] < min-1 || start[index] > max+1) { /* need to switch target wall (L-like) */ var diff = start[index] - center2[index]; switch (dirIndex2) { case 0: case 1: var rotation = (diff < 0 ? 3 : 1); break; case 2: case 3: var rotation = (diff < 0 ? 1 : 3); break; } dirIndex2 = (dirIndex2 + rotation) % 4; var end = this._placeInWall(room2, dirIndex2); if (!end) { return false; } var mid = [0, 0]; mid[index] = start[index]; var index2 = (index+1)%2; mid[index2] = end[index2]; this._digLine([start, mid, end]); } else { /* use current wall pair, but adjust the line in the middle (S-like) */ var index2 = (index+1)%2; var end = this._placeInWall(room2, dirIndex2); if (!end) { return; } var mid = Math.round((end[index2] + start[index2])/2); var mid1 = [0, 0]; var mid2 = [0, 0]; mid1[index] = start[index]; mid1[index2] = mid; mid2[index] = end[index]; mid2[index2] = mid; this._digLine([start, mid1, mid2, end]); } room1.addDoor(start[0], start[1]); room2.addDoor(end[0], end[1]); var index = this._unconnected.indexOf(room1); if (index != -1) { this._unconnected.splice(index, 1); this._connected.push(room1); } var index = this._unconnected.indexOf(room2); if (index != -1) { this._unconnected.splice(index, 1); this._connected.push(room2); } return true; } ROT.Map.Uniform.prototype._placeInWall = function(room, dirIndex) { var start = [0, 0]; var dir = [0, 0]; var length = 0; switch (dirIndex) { case 0: dir = [1, 0]; start = [room.getLeft(), room.getTop()-1]; length = room.getRight()-room.getLeft()+1; break; case 1: dir = [0, 1]; start = [room.getRight()+1, room.getTop()]; length = room.getBottom()-room.getTop()+1; break; case 2: dir = [1, 0]; start = [room.getLeft(), room.getBottom()+1]; length = room.getRight()-room.getLeft()+1; break; case 3: dir = [0, 1]; start = [room.getLeft()-1, room.getTop()]; length = room.getBottom()-room.getTop()+1; break; } var avail = []; var lastBadIndex = -2; for (var i=0;i=0; i--) { if (!avail[i]) { avail.splice(i, 1); } } return (avail.length ? avail.random() : null); } /** * Dig a polyline. */ ROT.Map.Uniform.prototype._digLine = function(points) { for (var i=1;i= this._width || y >= this._height) { return false; } return (this._map[x][y] == 1); } ROT.Map.Uniform.prototype._canBeDugCallback = function(x, y) { if (x < 1 || y < 1 || x+1 >= this._width || y+1 >= this._height) { return false; } return (this._map[x][y] == 1); }