├── web ├── img │ ├── bed1.png │ ├── bed2.png │ ├── dirt.png │ ├── door.png │ ├── orb.png │ ├── oven.png │ ├── tree.png │ ├── wall.png │ ├── well.png │ ├── barrel.png │ ├── cTable1.png │ ├── cTable2.png │ ├── cTable3.png │ ├── cTable4.png │ ├── chest.png │ ├── clock.png │ ├── column.png │ ├── floor.png │ ├── floor2.png │ ├── grass1.png │ ├── grass2.png │ ├── grill.png │ ├── lChair.png │ ├── lTable.png │ ├── lTorch.png │ ├── locker.png │ ├── mirror.png │ ├── nChair.png │ ├── piano.png │ ├── plant.png │ ├── rChair.png │ ├── rTable.png │ ├── rTorch.png │ ├── sChair.png │ ├── sPiano.png │ ├── shelf.png │ ├── shelf2.png │ ├── throne.png │ ├── water.png │ ├── fakeWall.png │ ├── fireplace.png │ ├── firewall.png │ ├── fountain.png │ ├── jarTable.png │ ├── library1.png │ ├── library2.png │ ├── crossWindow.png │ ├── magicCarpet.png │ ├── smallTable.png │ └── wineBarrel.png ├── index.html └── generator.js ├── src ├── ui │ ├── WebAdapter.js │ └── CanvasRenderer.js ├── Random.js ├── TerrainGenerator.js ├── Arrays.js ├── MapGenerator.js ├── Cells.js ├── CastleStructureGenerator.js ├── RoomBuilder.js └── RoomsGenerator.js └── README.MD /web/img/bed1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/bed1.png -------------------------------------------------------------------------------- /web/img/bed2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/bed2.png -------------------------------------------------------------------------------- /web/img/dirt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/dirt.png -------------------------------------------------------------------------------- /web/img/door.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/door.png -------------------------------------------------------------------------------- /web/img/orb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/orb.png -------------------------------------------------------------------------------- /web/img/oven.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/oven.png -------------------------------------------------------------------------------- /web/img/tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/tree.png -------------------------------------------------------------------------------- /web/img/wall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/wall.png -------------------------------------------------------------------------------- /web/img/well.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/well.png -------------------------------------------------------------------------------- /web/img/barrel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/barrel.png -------------------------------------------------------------------------------- /web/img/cTable1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/cTable1.png -------------------------------------------------------------------------------- /web/img/cTable2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/cTable2.png -------------------------------------------------------------------------------- /web/img/cTable3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/cTable3.png -------------------------------------------------------------------------------- /web/img/cTable4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/cTable4.png -------------------------------------------------------------------------------- /web/img/chest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/chest.png -------------------------------------------------------------------------------- /web/img/clock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/clock.png -------------------------------------------------------------------------------- /web/img/column.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/column.png -------------------------------------------------------------------------------- /web/img/floor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/floor.png -------------------------------------------------------------------------------- /web/img/floor2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/floor2.png -------------------------------------------------------------------------------- /web/img/grass1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/grass1.png -------------------------------------------------------------------------------- /web/img/grass2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/grass2.png -------------------------------------------------------------------------------- /web/img/grill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/grill.png -------------------------------------------------------------------------------- /web/img/lChair.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/lChair.png -------------------------------------------------------------------------------- /web/img/lTable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/lTable.png -------------------------------------------------------------------------------- /web/img/lTorch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/lTorch.png -------------------------------------------------------------------------------- /web/img/locker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/locker.png -------------------------------------------------------------------------------- /web/img/mirror.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/mirror.png -------------------------------------------------------------------------------- /web/img/nChair.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/nChair.png -------------------------------------------------------------------------------- /web/img/piano.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/piano.png -------------------------------------------------------------------------------- /web/img/plant.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/plant.png -------------------------------------------------------------------------------- /web/img/rChair.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/rChair.png -------------------------------------------------------------------------------- /web/img/rTable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/rTable.png -------------------------------------------------------------------------------- /web/img/rTorch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/rTorch.png -------------------------------------------------------------------------------- /web/img/sChair.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/sChair.png -------------------------------------------------------------------------------- /web/img/sPiano.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/sPiano.png -------------------------------------------------------------------------------- /web/img/shelf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/shelf.png -------------------------------------------------------------------------------- /web/img/shelf2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/shelf2.png -------------------------------------------------------------------------------- /web/img/throne.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/throne.png -------------------------------------------------------------------------------- /web/img/water.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/water.png -------------------------------------------------------------------------------- /web/img/fakeWall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/fakeWall.png -------------------------------------------------------------------------------- /web/img/fireplace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/fireplace.png -------------------------------------------------------------------------------- /web/img/firewall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/firewall.png -------------------------------------------------------------------------------- /web/img/fountain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/fountain.png -------------------------------------------------------------------------------- /web/img/jarTable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/jarTable.png -------------------------------------------------------------------------------- /web/img/library1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/library1.png -------------------------------------------------------------------------------- /web/img/library2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/library2.png -------------------------------------------------------------------------------- /web/img/crossWindow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/crossWindow.png -------------------------------------------------------------------------------- /web/img/magicCarpet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/magicCarpet.png -------------------------------------------------------------------------------- /web/img/smallTable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/smallTable.png -------------------------------------------------------------------------------- /web/img/wineBarrel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashman/ultimaCastleGen/HEAD/web/img/wineBarrel.png -------------------------------------------------------------------------------- /src/ui/WebAdapter.js: -------------------------------------------------------------------------------- 1 | window.MapGenerator = require('../MapGenerator'); 2 | window.CanvasRenderer = require('./CanvasRenderer'); -------------------------------------------------------------------------------- /src/Random.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | rand: function(low, hi){ 3 | return Math.floor(Math.random() * (hi - low + 1))+low; 4 | }, 5 | randomElementOf: function(array){ 6 | return array[Math.floor(Math.random()*array.length)]; 7 | }, 8 | chance: function(chance){ 9 | return this.rand(0,100) <= chance; 10 | } 11 | } -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | Generates single-floor layouts of castles inspired by Ultima V. 2 | 3 | - Play online: https://slash.itch.io/ultima-castle-generator 4 | - Read more about this generator in my blog: https://blog.slashie.net/category/ultima-castle-map-generator/ 5 | - The non-itch version of the generator outputs intermediate steps of the generator: https://slashie.net/ultimacastlegen/ 6 | 7 | # Building 8 | 9 | browserify -e .\src\ui\WebAdapter.js -o .\web\generator.js -------------------------------------------------------------------------------- /src/TerrainGenerator.js: -------------------------------------------------------------------------------- 1 | var Random = require('./Random'); 2 | var Cells = require('./Cells'); 3 | 4 | function TerrainGenerator(){ 5 | 6 | }; 7 | 8 | TerrainGenerator.prototype = { 9 | generateTerrain: function(generationParams){ 10 | var map = []; 11 | for (var x = 0; x < generationParams.width; x++){ 12 | map[x] = []; 13 | for (var y = 0; y < generationParams.height; y++){ 14 | if (Random.chance(80)) 15 | map[x][y] = Cells.GRASS_1; 16 | else if (Random.chance(80)) 17 | map[x][y] = Cells.GRASS_2; 18 | else 19 | map[x][y] = Cells.TREE; 20 | } 21 | } 22 | return map; 23 | } 24 | } 25 | 26 | module.exports = TerrainGenerator; -------------------------------------------------------------------------------- /src/Arrays.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | shuffle: function(array) { 3 | var counter = array.length; 4 | 5 | // While there are elements in the array 6 | while (counter > 0) { 7 | // Pick a random index 8 | var index = Math.floor(Math.random() * counter); 9 | 10 | // Decrease counter by 1 11 | counter--; 12 | 13 | // And swap the last element with it 14 | var temp = array[counter]; 15 | array[counter] = array[index]; 16 | array[index] = temp; 17 | } 18 | 19 | return array; 20 | }, 21 | removeObject: function(array, object){ 22 | for(var i = 0; i < array.length; i++) { 23 | if(array[i] === object) { 24 | array.splice(i, 1); 25 | } 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/MapGenerator.js: -------------------------------------------------------------------------------- 1 | var CastleStructureGenerator = require('./CastleStructureGenerator'); 2 | var RoomsGenerator = require('./RoomsGenerator'); 3 | var RoomBuilder = require('./RoomBuilder'); 4 | var TerrainGenerator = require('./TerrainGenerator'); 5 | 6 | function MapGenerator(){ 7 | 8 | }; 9 | 10 | MapGenerator.prototype = { 11 | generateMap: function(generationParams){ 12 | this.generationParams = generationParams; 13 | this.castleStructureGenerator = new CastleStructureGenerator(); 14 | this.roomsGenerator = new RoomsGenerator(); 15 | this.terrainGenerator = new TerrainGenerator(); 16 | this.roomBuilder = new RoomBuilder(); 17 | 18 | var castle = {}; 19 | while (true){ 20 | castle.structure = this.castleStructureGenerator.generateMap(generationParams); 21 | castle.rooms = this.roomsGenerator.generateMap(castle.structure, generationParams); 22 | if (castle.rooms != false) 23 | break; 24 | } 25 | 26 | castle.map = this.terrainGenerator.generateTerrain(generationParams); 27 | this.roomBuilder.buildRooms(castle.map, castle.rooms); 28 | return castle; 29 | } 30 | } 31 | 32 | module.exports = MapGenerator; -------------------------------------------------------------------------------- /src/Cells.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | WALL: 'wall', 3 | FLOOR: 'floor', 4 | FLOOR_2: 'floor2', 5 | GRASS_1: 'grass1', 6 | GRASS_2: 'grass2', 7 | TREE: 'tree', 8 | DOOR: 'door', 9 | CROSS_WINDOW: 'crossWindow', 10 | FOUNTAIN: 'fountain', 11 | WATER: 'water', 12 | WELL: 'well', 13 | DIRT: 'dirt', 14 | FIREPLACE: 'fireplace', 15 | COLUMN: 'column', 16 | R_TORCH: 'rTorch', 17 | L_TORCH: 'lTorch', 18 | THRONE: 'throne', 19 | ORB: 'orb', 20 | SMALL_TABLE: 'smallTable', 21 | MAGIC_CARPET: 'magicCarpet', 22 | BED_1: 'bed1', 23 | BED_2: 'bed2', 24 | LOCKER: 'locker', 25 | JAR_TABLE: 'jarTable', 26 | BARREL: 'barrel', 27 | PLANT: 'plant', 28 | MIRROR: 'mirror', 29 | SHELF: 'shelf', 30 | PIANO: 'piano', 31 | R_CHAIR: 'rChair', 32 | L_CHAIR: 'lChair', 33 | S_CHAIR: 'sChair', 34 | N_CHAIR: 'nChair', 35 | S_PIANO: 'sPiano', 36 | CLOCK: 'clock', 37 | SHELF_2: 'shelf2', 38 | R_TABLE: 'rTable', 39 | L_TABLE: 'lTable', 40 | C_TABLE_1: 'cTable1', 41 | C_TABLE_2: 'cTable2', 42 | C_TABLE_3: 'cTable3', 43 | C_TABLE_4: 'cTable4', 44 | WALL_FIREPLACE: 'firewall', 45 | GRILL: 'grill', 46 | OVEN: 'oven', 47 | WINE_BARREL: 'wineBarrel', 48 | LIBRARY_1: 'library1', 49 | LIBRARY_2: 'library2', 50 | CHEST: 'chest', 51 | FAKE_WALL: 'fakeWall' 52 | }; -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |by Slashie
23 |Read more about this generator in my blog
24 | 25 |26 | 27 | 28 |
29 |
30 | 31 |
32 | 33 | 36 | -------------------------------------------------------------------------------- /src/ui/CanvasRenderer.js: -------------------------------------------------------------------------------- 1 | var Cells = require('../Cells'); 2 | 3 | 4 | 5 | function CanvasRenderer(config){ 6 | this.config = config; 7 | this.tiles = {}; 8 | for (key in Cells){ 9 | var val = Cells[key]; 10 | this.tiles[val] = new Image(); 11 | totalImages++; 12 | this.tiles[val].onload = increaseLoadedCount; 13 | this.tiles[val].src = 'img/'+val+'.png'; 14 | } 15 | setTimeout(checkImagesLoaded, 500); 16 | } 17 | 18 | var totalImages = 0; 19 | var loadedImages = 0; 20 | 21 | function increaseLoadedCount(){ 22 | loadedImages++; 23 | } 24 | 25 | function checkImagesLoaded(){ 26 | if (loadedImages == totalImages){ 27 | imagesLoaded(); 28 | } else { 29 | setTimeout(checkImagesLoaded, 500); 30 | } 31 | } 32 | 33 | CanvasRenderer.prototype = { 34 | drawSketch: function(rooms, canvas, overlay){ 35 | var canvas = document.getElementById(canvas); 36 | var context = canvas.getContext('2d'); 37 | context.font="16px Avatar"; 38 | if (!overlay) 39 | context.clearRect(0, 0, canvas.width, canvas.height); 40 | var zoom = 16; 41 | for (var i = 0; i < rooms.length; i++){ 42 | var area = rooms[i]; 43 | context.beginPath(); 44 | context.rect(area.x * zoom, area.y * zoom, area.width * zoom, area.height * zoom); 45 | if (!overlay){ 46 | if (area.type === 'rooms'){ 47 | context.fillStyle = 'blue'; 48 | context.globalAlpha = 0.5; 49 | } 50 | else{ 51 | context.fillStyle = 'yellow'; 52 | context.globalAlpha = 1; 53 | } 54 | 55 | context.fill(); 56 | } 57 | context.lineWidth = 2; 58 | context.strokeStyle = 'black'; 59 | context.stroke(); 60 | if (area.bridges) for (var j = 0; j < area.bridges.length; j++){ 61 | var bridge = area.bridges[j]; 62 | context.beginPath(); 63 | context.rect((bridge.x) * zoom, (bridge.y) * zoom, zoom, zoom); 64 | context.lineWidth = 2; 65 | context.strokeStyle = 'red'; 66 | context.stroke(); 67 | } 68 | } 69 | context.globalAlpha = 1; 70 | for (var i = 0; i < rooms.length; i++){ 71 | var area = rooms[i]; 72 | context.fillStyle = 'black'; 73 | context.fillText(area.name,(area.x)* zoom + 5,(area.y + area.height/2)* zoom + 10); 74 | } 75 | }, 76 | drawLevel: function(level, canvas){ 77 | 78 | }, 79 | drawLevelWithIcons: function(cells, canvas){ 80 | var canvas = document.getElementById(canvas); 81 | var context = canvas.getContext('2d'); 82 | context.clearRect(0, 0, canvas.width, canvas.height); 83 | var zoom = 8; 84 | for (var x = 0; x < this.config.LEVEL_WIDTH; x++){ 85 | for (var y = 0; y < this.config.LEVEL_HEIGHT; y++){ 86 | var cell = cells[x][y]; 87 | if (cell) 88 | context.drawImage(this.tiles[cell], x * 14, y * 16); 89 | } 90 | } 91 | } 92 | } 93 | 94 | module.exports = CanvasRenderer; -------------------------------------------------------------------------------- /src/CastleStructureGenerator.js: -------------------------------------------------------------------------------- 1 | var Random = require('./Random'); 2 | 3 | function CastleStructureGenerator(){}; 4 | 5 | CastleStructureGenerator.prototype = { 6 | generateMap: function(generationParams){ 7 | this.generationParams = generationParams; 8 | var castle = {}; 9 | this.castle = castle; 10 | castle.general = this.selectGeneral(); 11 | castle.surroundings = this.selectSurroundings(); 12 | castle.towers = this.selectTowers(); 13 | castle.central = this.selectCentral(); 14 | castle.entrances = this.selectEntrances(); 15 | castle.rooms = this.selectRooms(castle); 16 | return castle; 17 | }, 18 | selectGeneral: function(){ 19 | return { 20 | size: Random.chance(80) ? 'big' : 'small', 21 | superSymmetric: Random.chance(20) 22 | } 23 | }, 24 | selectSurroundings: function(){ 25 | return { 26 | hasMoat: Random.chance(50) 27 | } 28 | }, 29 | selectEntrances: function(){ 30 | var entranceStructure = {}; 31 | if (Random.chance(50)){ 32 | entranceStructure.northExit = this.selectEntrance(false); 33 | } 34 | entranceStructure.southExit = this.selectEntrance(true); 35 | return entranceStructure; 36 | }, 37 | selectEntrance: function(mainEntrance){ 38 | var entranceStructure = {}; 39 | entranceStructure.hasFloor = Random.chance(50); 40 | entranceStructure.hasCrossWindows = Random.chance(50); 41 | entranceStructure.lighting = Random.randomElementOf(['none', 'torches', 'firepits']); 42 | entranceStructure.hasBanners = mainEntrance && Random.chance(60); 43 | entranceStructure.isMain = mainEntrance; 44 | entranceStructure.width = this.castle.central.width - Random.rand(3, 6) * 2; 45 | if (entranceStructure.width < 3) 46 | entranceStructure.width = 3; 47 | entranceStructure.openingWidth = Random.rand(1,entranceStructure.width-2); 48 | if (entranceStructure.openingWidth % 2 == 0) 49 | entranceStructure.openingWidth--; 50 | entranceStructure.closingWidth = Random.rand(1,entranceStructure.width-2); 51 | if (entranceStructure.closingWidth % 2 == 0) 52 | entranceStructure.closingWidth--; 53 | return entranceStructure; 54 | }, 55 | selectTowers: function(){ 56 | var towerStructure = {}; 57 | towerStructure.size = 5 + Random.rand(0,1) * 2; 58 | towerStructure.crossWindows = Random.chance(50); 59 | towerStructure.circle = Random.chance(50); 60 | 61 | towerStructure.verticalConnections = Random.chance(50); 62 | towerStructure.horizontalConnections = Random.randomElementOf(['both', 'top', 'bottom']); 63 | towerStructure.connectionCorridors = {}; 64 | if (Random.chance(50)){ 65 | towerStructure.connectionCorridors.type = 'corridor'; 66 | } else { 67 | towerStructure.connectionCorridors.type = 'halls'; 68 | towerStructure.connectionCorridors.hallDecoration = { 69 | torches: Random.chance(50), 70 | plants: Random.chance(50), 71 | columns: Random.chance(50), 72 | fountains: Random.chance(50) 73 | } 74 | towerStructure.connectionCorridors.hallWidth = Random.rand(3,5); 75 | } 76 | return towerStructure; 77 | }, 78 | selectCentral: function(){ 79 | var centralStructure = {}; 80 | if (Random.chance(50)){ 81 | centralStructure.type = 'courtyard'; 82 | switch (Random.rand(0,2)){ 83 | case 0: 84 | if (Random.chance(60)) 85 | centralStructure.centralFeature = 'fountain'; 86 | if (Random.chance(50)){ 87 | centralStructure.additionalFountains = true; 88 | centralStructure.fountainSymmetry = Random.randomElementOf(['x', 'y', 'full']); 89 | } 90 | centralStructure.hasSmallLake = Random.chance(50); 91 | break; 92 | case 1: 93 | centralStructure.centralFeature = 'well'; 94 | break; 95 | case 2: 96 | if (Random.chance(40)) 97 | centralStructure.centralFeature = 'fountain'; 98 | centralStructure.hasSmallLake = true; 99 | break; 100 | } 101 | centralStructure.connectionWithRooms = { 102 | type: Random.randomElementOf(['radial', 'around']), 103 | terrain: Random.randomElementOf(['floor', 'dirt']) 104 | }; 105 | } else { 106 | centralStructure.type = 'mainHall'; 107 | centralStructure.hasSpecialFloor = Random.chance(50); 108 | if (Random.chance(50)){ 109 | centralStructure.centralFeature = 'fountain'; 110 | } 111 | centralStructure.centralFireplace = Random.chance(50); 112 | centralStructure.cornerFireplaces = Random.chance(50); 113 | } 114 | centralStructure.width = 9 + Random.rand(0,3)*2; 115 | var maxWidth = 15; 116 | if (this.castle.towers.verticalConnections) 117 | maxWidth -= 8; 118 | if (this.castle.towers.connectionCorridors.hallWidth > 3) 119 | maxWidth -= 2; 120 | var maxHeight = 15; 121 | if (this.castle.towers.horizontalConnections === 'both') 122 | maxHeight -= 4; 123 | if (centralStructure.width > maxWidth) 124 | centralStructure.width = maxWidth; 125 | if (Random.chance(50)){ 126 | centralStructure.height = 9 + Random.rand(0,3)*2; 127 | if (centralStructure.height > maxHeight) 128 | centralStructure.height = maxHeight; 129 | } else { 130 | centralStructure.height = centralStructure.width; 131 | } 132 | centralStructure.shape = Random.randomElementOf(['square', 'circle', 'cross']); 133 | return centralStructure; 134 | }, 135 | selectRooms: function(castle){ 136 | var numberOfRooms = 0; 137 | if (castle.general.size === 'small'){ 138 | // Only four rooms, one below each "Tower" 139 | numberOfRooms = 4; 140 | } else { 141 | // Number of rooms depends on available space 142 | /** 143 | * Base is 8 rooms 144 | * -1 If North Exit 145 | * +4 If no vertical connections between towers 146 | * +2 If no top connection between towers 147 | * +2 If no bottom connection between towers 148 | * -4 If central feature is too big 149 | */ 150 | numberOfRooms = 8; 151 | if (castle.entrances.northExit) 152 | numberOfRooms--; 153 | if (castle.towers.verticalConnections) 154 | numberOfRooms+= 4; 155 | if (castle.towers.horizontalConnections != 'both') 156 | numberOfRooms+= 2; 157 | if (castle.central.width > 10) 158 | numberOfRooms-= 4; 159 | if (numberOfRooms < 0) 160 | numberOfRooms = 0; 161 | } 162 | var rooms = []; 163 | this.currentRooms = {}; 164 | for (var i = 0; i < numberOfRooms; i++){ 165 | var room = this.selectRoom(rooms); 166 | this.fillRoom(room); 167 | rooms.push(room); 168 | } 169 | return rooms; 170 | }, 171 | fillRoom: function(room){ 172 | switch (room.type){ 173 | case 'livingQuarters': 174 | room.freeSpace = Random.rand(0, 50); 175 | break; 176 | case 'guestRoom': 177 | room.beds = Random.rand(1,2); 178 | room.mirror = Random.chance(50); 179 | room.piano = Random.chance(30); 180 | room.fireplace = Random.chance(50); 181 | break; 182 | case 'storage': 183 | room.filled = Random.rand(60,90); 184 | room.barrels = Random.rand(0,room.filled); 185 | room.boxes = room.filled - room.barrels; 186 | break; 187 | case 'diningRoom': 188 | room.luxury = Random.rand(1,3); 189 | room.fireplace = Random.chance(50); 190 | break; 191 | case 'kitchen': 192 | room.filled = Random.rand(0,20); 193 | room.barrels = Random.rand(0,room.filled); 194 | room.boxes = room.filled - room.barrels; 195 | room.hasOven = Random.chance(50); 196 | room.isNextTo = 'diningRoom'; 197 | break; 198 | case 'throneRoom': 199 | room.hasCarpet = Random.chance(70); 200 | room.hasMagicCarpet = Random.chance(20); 201 | room.linedWithColumns = Random.chance(30); 202 | room.linedWithTorches = Random.chance(70); 203 | room.hasSecondaryThrone = Random.chance(50); 204 | room.hasMagicOrb = Random.chance(50); 205 | room.placeNorth = true; 206 | room.southRoom = 'throneHall'; 207 | room.linkeable = false; 208 | room.isBig = true; 209 | room.level = 2; 210 | break; 211 | case 'lordQuarters': 212 | room.piano = Random.chance(50); 213 | room.clock = Random.chance(50); 214 | room.bookshelf = Random.chance(70); 215 | room.fireplace = Random.chance(80); 216 | room.placeNorth = true; 217 | room.isBig = true; 218 | break; 219 | case 'hall': 220 | room.torches = Random.chance(50); 221 | room.plants = Random.chance(50); 222 | room.columns = Random.chance(50); 223 | room.fountains = Random.chance(50); 224 | break; 225 | } 226 | }, 227 | selectRoom: function(rooms){ 228 | /** 229 | * Throne Room - Required if the castle is Royal 230 | * Castle Lord Room - Required 231 | * Staff Living Quarters - At least one 232 | * Dining rooms - At least one 233 | * Kitchen - At least one 234 | * Forge - Optional, only one 235 | * Prison Cells - Optional 236 | * Dungeon - Optional 237 | * Hall - Optional 238 | * Library - Optional 239 | */ 240 | var room = {}; 241 | if (this.generationParams.royal && !this.currentRooms.throneRoom){ 242 | room.type = 'throneRoom'; 243 | } else if (!this.currentRooms.lordQuarters){ 244 | room.type = 'lordQuarters'; 245 | } else if (!this.currentRooms.diningRoom){ 246 | room.type = 'diningRoom'; 247 | } else if (!this.currentRooms.kitchen){ 248 | room.type = 'kitchen'; 249 | } else if (!this.currentRooms.livingQuarters){ 250 | room.type = 'livingQuarters'; 251 | } else { 252 | var possibleRooms = ['livingQuarters', 'diningRoom', 'kitchen', /*'prison', 'dungeon',*/ 'hall', 'guestRoom', 'library']; 253 | /*if (!this.currentRooms.forge){ 254 | possibleRooms.push('forge'); 255 | }*/ 256 | room.type = Random.randomElementOf(possibleRooms); 257 | } 258 | this.currentRooms[room.type] = true; 259 | return room; 260 | } 261 | } 262 | 263 | module.exports = CastleStructureGenerator; -------------------------------------------------------------------------------- /src/RoomBuilder.js: -------------------------------------------------------------------------------- 1 | var Random = require('./Random'); 2 | var Arrays = require('./Arrays'); 3 | var Cells = require('./Cells'); 4 | 5 | function RoomBuilder(){}; 6 | 7 | RoomBuilder.prototype = { 8 | buildRooms: function(map, rooms){ 9 | this.map = map; 10 | rooms = rooms.sort(function(a,b){var aLevel = a.level ? a.level : 0; var bLevel = b.level ? b.level : 0; return aLevel - bLevel;}); 11 | for (var i = 0; i < rooms.length; i++){ 12 | var buildFunction = this["build_"+rooms[i].type]; 13 | if (buildFunction){ 14 | buildFunction.call(this, rooms[i]); 15 | } else 16 | this.buildRoom(rooms[i]); 17 | } 18 | for (var i = 0; i < rooms.length; i++){ 19 | this.placeDoors(rooms[i]); 20 | } 21 | for (var x = 0; x < 32; x++){ 22 | for (var y = 0; y < 32; y++){ 23 | if (this.map[x][y] === Cells.CHEST){ 24 | // Place secret door nearby 25 | if (x > 16 && Random.chance(90) && this.map[x+1][y] === Cells.WALL){ 26 | this.map[x+1][y] = Cells.FAKE_WALL; 27 | } 28 | if (x < 16 && Random.chance(90) && this.map[x-1][y] === Cells.WALL){ 29 | this.map[x-1][y] = Cells.FAKE_WALL; 30 | } 31 | } 32 | } 33 | } 34 | 35 | }, 36 | buildRoom: function(room){ 37 | for (var x = room.x; x < room.x + room.width; x++){ 38 | for (var y = room.y; y < room.y + room.height; y++){ 39 | if (x == room.x || x == room.x + room.width - 1 || y == room.y || y == room.y + room.height - 1){ 40 | this.map[x][y] = Cells.WALL; 41 | } else { 42 | this.map[x][y] = Cells.FLOOR; 43 | } 44 | } 45 | } 46 | }, 47 | build_rooms: function(room){ // Foundations 48 | for (var x = room.x; x < room.x + room.width; x++){ 49 | for (var y = room.y; y < room.y + room.height; y++){ 50 | if (x == room.x || x == room.x + room.width - 1 || y == room.y || y == room.y + room.height - 1){ 51 | this.map[x][y] = Cells.WALL; 52 | } else { 53 | this.map[x][y] = Random.chance(80) ? Cells.FLOOR : Cells.CHEST; 54 | } 55 | } 56 | } 57 | }, 58 | build_tower: function(room){ 59 | if (room.features.walls.north == 'exit') 60 | room.northDoors = [{position: room.x + Math.floor(room.width/2), cell: Cells.DOOR}]; 61 | if (room.features.walls.south == 'exit') 62 | room.southDoors = [{position: room.x + Math.floor(room.width/2), cell: Cells.DOOR}]; 63 | if (room.features.walls.east == 'exit') 64 | room.eastDoors = [{position: room.y + Math.floor(room.height/2), cell: Cells.DOOR}]; 65 | if (room.features.walls.west == 'exit') 66 | room.westDoors = [{position: room.y + Math.floor(room.height/2), cell: Cells.DOOR}]; 67 | 68 | for (var x = room.x; x < room.x + room.width; x++){ 69 | for (var y = room.y; y < room.y + room.height; y++){ 70 | var wall = false; 71 | if (x == room.x){ 72 | wall = room.features.walls.west; 73 | } else if (x == room.x + room.width - 1){ 74 | wall = room.features.walls.east; 75 | } else if (y == room.y){ 76 | wall = room.features.walls.north; 77 | } else if (y == room.y + room.height - 1){ 78 | wall = room.features.walls.south; 79 | } 80 | if (wall){ 81 | if (wall === 'solid'){ 82 | this.map[x][y] = Cells.WALL; 83 | } else if (wall === 'crossWindows'){ 84 | if (x === room.x + Math.floor(room.width/2) || 85 | y === room.y + Math.floor(room.height/2) ) 86 | this.map[x][y] = Cells.CROSS_WINDOW; 87 | else 88 | this.map[x][y] = Cells.WALL; 89 | } else if (wall === 'exit'){ 90 | this.map[x][y] = Cells.WALL; 91 | } else if (wall === 'open'){ 92 | this.map[x][y] = Cells.FLOOR; 93 | } 94 | } else { 95 | this.map[x][y] = Cells.FLOOR; 96 | } 97 | } 98 | } 99 | }, 100 | build_mainHall: function(room){ 101 | for (var x = room.x; x < room.x + room.width; x++){ 102 | for (var y = room.y; y < room.y + room.height; y++){ 103 | if (x == room.x || x == room.x + room.width - 1 || y == room.y || y == room.y + room.height - 1){ 104 | this.map[x][y] = Cells.WALL; 105 | } else { 106 | if (room.features.hasSpecialFloor) 107 | this.map[x][y] = Cells.FLOOR_2; 108 | else 109 | this.map[x][y] = Cells.FLOOR; 110 | } 111 | } 112 | } 113 | var midx = room.x+Math.floor(room.width/2); 114 | var midy = room.y+Math.floor(room.height/2); 115 | if (room.features.centralFireplace){ 116 | if (room.features.centralFeature){ 117 | if (room.width > 6 && room.height > 6){ 118 | this.map[midx - 1][midy - 1] = Cells.FIREPLACE; 119 | this.map[midx + 1][midy - 1] = Cells.FIREPLACE; 120 | this.map[midx - 1][midy + 1] = Cells.FIREPLACE; 121 | this.map[midx + 1][midy + 1] = Cells.FIREPLACE; 122 | } 123 | } else { 124 | this.map[midx][midy] = Cells.FIREPLACE; 125 | } 126 | } 127 | if (room.features.cornerFireplaces && (room.height > 5 || room.width > 5)){ 128 | if (!room.features.centralFireplace || (room.height > 7 || room.width > 7)){ 129 | this.map[room.x+1][room.y+1] = Cells.FIREPLACE; 130 | this.map[room.x+1][room.y+room.height-2] = Cells.FIREPLACE; 131 | this.map[room.x+room.width-2][room.y+1] = Cells.FIREPLACE; 132 | this.map[room.x+room.width-2][room.y+room.height-2] = Cells.FIREPLACE; 133 | } 134 | } 135 | if (room.features.centralFeature === 'fountain'){ 136 | this.map[midx][midy] = Cells.FOUNTAIN; 137 | } 138 | //TODO: Implement room.features.shape === 'circle' 139 | //TODO: Implement room.features.shape === 'cross' 140 | }, 141 | build_courtyard: function(room){ 142 | for (var x = room.x; x < room.x + room.width; x++){ 143 | for (var y = room.y; y < room.y + room.height; y++){ 144 | if (x == room.x || x == room.x + room.width - 1 || y == room.y || y == room.y + room.height - 1){ 145 | this.map[x][y] = Cells.WALL; 146 | } else { 147 | if (Random.chance(80)) 148 | this.map[x][y] = Cells.GRASS_1; 149 | else if (Random.chance(80)) 150 | this.map[x][y] = Cells.GRASS_2; 151 | else 152 | this.map[x][y] = Cells.TREE; 153 | } 154 | } 155 | } 156 | var midx = room.x+Math.floor(room.width/2); 157 | var midy = room.y+Math.floor(room.height/2); 158 | var connectionTerrain = room.features.connectionWithRooms.terrain; 159 | if (room.features.connectionWithRooms.type === 'radial'){ 160 | if (room.width > 6) for (var x = room.x + 1; x < room.x + room.width - 1; x++){ 161 | this.map[x][midy-1] = connectionTerrain; 162 | this.map[x][midy] = connectionTerrain; 163 | this.map[x][midy+1] = connectionTerrain; 164 | } 165 | if (room.height > 6) for (var y = room.y + 1; y < room.y + room.height - 1; y++){ 166 | this.map[midx-1][y] = connectionTerrain; 167 | this.map[midx][y] = connectionTerrain; 168 | this.map[midx+1][y] = connectionTerrain; 169 | } 170 | if (room.width > 7 && room.width > 7) { 171 | this.map[midx - 2][midy - 2] = connectionTerrain; 172 | this.map[midx - 2][midy + 2] = connectionTerrain; 173 | this.map[midx + 2][midy - 2] = connectionTerrain; 174 | this.map[midx + 2][midy + 2] = connectionTerrain; 175 | } 176 | } else if (room.features.connectionWithRooms.type === 'around'){ 177 | for (var x = room.x + 1; x < room.x + room.width - 1; x++){ 178 | this.map[x][room.y+1] = connectionTerrain; 179 | this.map[x][room.y+room.height - 2] = connectionTerrain; 180 | } 181 | for (var y = room.y + 1; y < room.y + room.height - 1; y++){ 182 | this.map[room.x+1][y] = connectionTerrain; 183 | this.map[room.x+room.width - 2][y] = connectionTerrain; 184 | } 185 | if (room.width > 7 && room.height > 7){ 186 | this.map[room.x+2][room.y+2] = connectionTerrain; 187 | this.map[room.x+2][room.y+room.height-3] = connectionTerrain; 188 | this.map[room.x+room.width-3][room.y+2] = connectionTerrain; 189 | this.map[room.x+room.width-3][room.y+room.height-3] = connectionTerrain; 190 | } 191 | } 192 | if (room.features.hasSmallLake && room.height > 6 && room.width > 6){ 193 | this.map[midx-1][midy] = Cells.WATER; 194 | this.map[midx][midy+1] = Cells.WATER; 195 | this.map[midx][midy] = Cells.WATER; 196 | this.map[midx][midy-1] = Cells.WATER; 197 | this.map[midx+1][midy] = Cells.WATER; 198 | 199 | if (Random.chance(60) && room.height > 7 && room.width > 7){ 200 | this.map[midx-1][midy+1] = Cells.WATER; 201 | this.map[midx-1][midy-1] = Cells.WATER; 202 | this.map[midx+1][midy+1] = Cells.WATER; 203 | this.map[midx+1][midy-1] = Cells.WATER; 204 | } 205 | } 206 | if (room.features.centralFeature === 'fountain'){ 207 | this.map[midx][midy] = Cells.FOUNTAIN; 208 | } else if (room.features.centralFeature === 'well'){ 209 | this.map[midx][midy] = Cells.WELL; 210 | } 211 | if (room.features.additionalFountains){ 212 | if (room.features.fountainSymmetry === 'x' && room.height > 9){ 213 | this.map[midx][midy + 2] = Cells.FOUNTAIN; 214 | this.map[midx][midy - 2] = Cells.FOUNTAIN; 215 | } else if (room.features.fountainSymmetry === 'y' && room.width > 9){ 216 | this.map[midx - 2][midy] = Cells.FOUNTAIN; 217 | this.map[midx + 2][midy] = Cells.FOUNTAIN; 218 | } else if (room.features.fountainSymmetry === 'full' && room.width > 9 && room.height > 9){ 219 | this.map[midx - 2][midy - 2] = Cells.FOUNTAIN; 220 | this.map[midx + 2][midy - 2] = Cells.FOUNTAIN; 221 | this.map[midx - 2][midy + 2] = Cells.FOUNTAIN; 222 | this.map[midx + 2][midy + 2] = Cells.FOUNTAIN; 223 | } 224 | } 225 | }, 226 | build_entranceHall: function(room){ 227 | var halfOpening = Math.floor((room.width - room.features.openingWidth) / 2); 228 | var halfClosing = Math.floor((room.width - room.features.closingWidth) / 2); 229 | for (var x = room.x; x < room.x + room.width; x++){ 230 | for (var y = room.y; y < room.y + room.height; y++){ 231 | if (x == room.x || x == room.x + room.width - 1){ 232 | this.map[x][y] = Cells.WALL; 233 | } else if ((room.features.isMain && y == room.y) || (!room.features.isMain && y == room.y + room.height - 1) ){ 234 | if (x >= room.x+halfOpening && x <= room.x + room.width - halfOpening-1){ 235 | this.map[x][y] = Cells.FLOOR; 236 | } else { 237 | this.map[x][y] = Cells.WALL; 238 | } 239 | } else if ((!room.features.isMain && y == room.y) || (room.features.isMain && y == room.y + room.height - 1) ){ 240 | if (x >= room.x+halfClosing && x <= room.x + room.width - halfClosing-1){ 241 | this.map[x][y] = Cells.FLOOR; 242 | } else { 243 | this.map[x][y] = Cells.WALL; 244 | } 245 | } else { 246 | this.map[x][y] = Cells.FLOOR; 247 | } 248 | } 249 | } 250 | }, 251 | build_entrance: function(room){ 252 | for (var x = room.x; x < room.x + room.width; x++){ 253 | for (var y = room.y; y < room.y + room.height; y++){ 254 | this.map[x][y] = Cells.FLOOR; 255 | } 256 | } 257 | }, 258 | build_throneRoom: function(room){ 259 | // Pad room 260 | var modifiedWidth = false; 261 | if (room.width % 2 == 0){ 262 | room.width--; 263 | for (var y = room.y; y < room.y + room.height; y++){ 264 | this.map[room.x+room.width-1][y] = Cells.WALL; 265 | } 266 | modifiedWidth = true; 267 | } 268 | this.buildRoom(room); 269 | var midx = room.x+Math.floor(room.width/2); 270 | // Place throne 271 | this.map[midx][room.y+1] = Cells.THRONE; 272 | if (room.features.hasSecondaryThrone) 273 | this.map[midx-1][room.y+1] = Cells.SMALL_TABLE; 274 | if (room.features.hasMagicOrb) 275 | this.map[midx+1][room.y+1] = Cells.ORB; 276 | if (room.features.hasMagicCarpet) 277 | this.map[midx][room.y+2] = Cells.MAGIC_CARPET; 278 | 279 | var columnsPosition = Random.rand(2, Math.floor(room.width/2) - 2); 280 | var columnsSpacing = Random.rand(2,3); 281 | var torchesSpacing = Random.rand(2,3); 282 | var placeColumns = room.features.linedWithColumns && room.width >= 9; 283 | var placeTorches = room.features.linedWithTorches && room.width >= 9; 284 | var placeSmallCarpet = true; 285 | var placeBigCarpet = room.width >= 7; 286 | var carpetStart = room.features.hasMagicCarpet ? room.y + 3 : room.y + 2; 287 | 288 | for (var y = room.y+1; y < room.y + room.height - 1; y++){ 289 | // Place carpet 290 | if (room.features.hasCarpet) if (y >= carpetStart){ 291 | if (placeSmallCarpet) 292 | this.map[midx][y] = Cells.FLOOR_2; 293 | if (placeBigCarpet){ 294 | this.map[midx-1][y] = Cells.FLOOR_2; 295 | this.map[midx+1][y] = Cells.FLOOR_2; 296 | } 297 | } 298 | // Place columns 299 | if (placeColumns) if (y % columnsSpacing == 0){ 300 | this.map[room.x+columnsPosition][y] = Cells.COLUMN; 301 | this.map[room.x+room.width-columnsPosition-1][y] = Cells.COLUMN; 302 | } 303 | // Place torches 304 | if (placeTorches) if ((y+1) % torchesSpacing == 0){ 305 | this.map[room.x+1][y] = Cells.L_TORCH; 306 | this.map[room.x+room.width-2][y] = Cells.R_TORCH; 307 | } 308 | } 309 | // Place door 310 | room.southDoors = [{position: midx, cell: Cells.DOOR}]; 311 | if (modifiedWidth){ 312 | room.width++; 313 | } 314 | }, 315 | build_livingQuarters: function(room){ 316 | this.buildLivingQuarters(room, 'simple'); 317 | }, 318 | build_guestRoom: function(room){ 319 | this.buildLivingQuarters(room, 'guestRoom'); 320 | }, 321 | build_lordQuarters: function(room){ 322 | this.buildLivingQuarters(room, 'lord'); 323 | }, 324 | buildLivingQuarters: function(room, quartersType){ 325 | this.buildRoom(room); 326 | var leftSize = room.width >= 6 ? 3 : 2; 327 | var rightSize = room.width >= 8 ? room.width > 8 ? 3 : 2 : 0; 328 | var topSize = room.height >= 6 ? 3 : 2; 329 | var bottomSize = room.height >= 8 ? room.height > 8 ? 3 : 2 : 0; 330 | 331 | if (Random.chance(50)){ 332 | // Flip sides 333 | var temp = rightSize; 334 | rightSize = leftSize; 335 | leftSize = temp; 336 | } 337 | room.placedElements = {}; 338 | // Place furniture 339 | for (var y = room.y+1; y < room.y + room.height - 1; y++){ 340 | if (leftSize){ 341 | var mainBlock = this.selectLivingQuartersBlock(room, y, quartersType); 342 | if (mainBlock) 343 | this.placeBlock(mainBlock, room.x+1, y, leftSize, false, quartersType); 344 | } 345 | if (rightSize){ 346 | var mainBlock = this.selectLivingQuartersBlock(room, y, quartersType); 347 | if (mainBlock) 348 | this.placeBlock(mainBlock, room.x+room.width-rightSize-1, y, rightSize, true, quartersType); 349 | } 350 | } 351 | for (var x = room.x+3; x < room.x + room.width - 3; x++){ 352 | if (topSize){ 353 | var mainBlock = this.selectLivingQuartersVBlock(room, quartersType); 354 | if (mainBlock) 355 | this.placeVBlock(mainBlock, x, room.y+1, topSize, false, quartersType); 356 | } 357 | if (bottomSize){ 358 | var mainBlock = this.selectLivingQuartersVBlock(room, quartersType); 359 | if (mainBlock) 360 | this.placeVBlock(mainBlock, x, room.y+room.height-bottomSize-1, bottomSize, true, quartersType); 361 | } 362 | } 363 | 364 | if (quartersType === 'lord' || quartersType === 'guestRoom'){ 365 | // Place bed(s) for royal and guest quarters 366 | var numberOfBeds = quartersType === 'guestRoom' ? 2 : 1; 367 | for (var i = 0; i < numberOfBeds; i++){ 368 | var y = Random.rand(room.y+1, room.y + room.height - 2 - (quartersType === 'lord' ? 1 : 0)); 369 | if (leftSize){ 370 | this.placeBlock('bed', room.x+1, y, leftSize, false, quartersType); 371 | } else if (rightSize){ 372 | this.placeBlock('bed', room.x+room.width-rightSize-1, y, rightSize, true, quartersType); 373 | } 374 | } 375 | this.addWallFireplace(room); 376 | } 377 | this.addTorchesToRoom(room); 378 | 379 | }, 380 | selectLivingQuartersBlock: function(room, y, quartersType){ 381 | // Beds? 382 | if (quartersType === 'simple' && ((y%2 == 0 && Random.chance(70)) || (y%2 != 0 && Random.chance(30)))){ 383 | return 'bed'; 384 | } 385 | // Nothing? 386 | if (Random.chance(20)){ 387 | return false; 388 | } 389 | // Table with chair 390 | if (!room.placedElements["tableAndChair"] && (quartersType === 'lord' || quartersType === 'guestRoom') && Random.chance(40)){ 391 | room.placedElements["tableAndChair"] = true; 392 | return 'tableAndChair' 393 | } 394 | // Kitchen Table 395 | if (!room.placedElements["diningTable"] && quartersType === 'kitchen' && y > room.y+1 && y < room.y+room.height - 2 && Random.chance(40)){ 396 | room.placedElements["diningTable"] = true; 397 | return 'diningTable' 398 | } 399 | var additionalElements = false; 400 | switch (quartersType){ 401 | case 'simple': 402 | additionalElements = [Cells.BARREL, Cells.LOCKER, Cells.PLANT, Cells.SMALL_TABLE]; 403 | break; 404 | case 'guestRoom': 405 | additionalElements = [Cells.BARREL, Cells.SHELF, Cells.PLANT, Cells.JAR_TABLE]; 406 | break; 407 | case 'lord': 408 | additionalElements = [Cells.SHELF, Cells.PLANT, Cells.JAR_TABLE]; 409 | break; 410 | case 'kitchen': 411 | additionalElements = [Cells.WINE_BARREL, Cells.OVEN, Cells.BARREL, Cells.GRILL, 'tableAndChair']; 412 | break; 413 | case 'library': 414 | additionalElements = ['bookshelf', 'tableAndChair', Cells.PLANT, Cells.SHELF_2]; 415 | break; 416 | case 'hall': 417 | additionalElements = [Cells.PLANT]; 418 | if (!room.placedElements[Cells.FOUNTAIN]) 419 | additionalElements.push(Cells.FOUNTAIN); 420 | break; 421 | 422 | } 423 | var element = Random.randomElementOf(additionalElements); 424 | room.placedElements[element] = true; 425 | return element; 426 | }, 427 | selectLivingQuartersVBlock: function(room, quartersType){ 428 | if (!room.placedElements["tableAndPiano"] && quartersType === 'lord' && Random.chance(40)){ 429 | room.placedElements["tableAndPiano"] = true; 430 | return 'tableAndPiano' 431 | } 432 | if (Random.chance(60)){ 433 | return false; 434 | } 435 | var additionalElements = false; 436 | switch (quartersType){ 437 | case 'simple': 438 | additionalElements = [Cells.BARREL, Cells.LOCKER, Cells.PLANT, Cells.SMALL_TABLE]; 439 | break; 440 | case 'guestRoom': 441 | additionalElements = [Cells.BARREL, Cells.SHELF, Cells.SHELF_2, Cells.PLANT, Cells.JAR_TABLE]; 442 | if (!room.placedElements[Cells.MIRROR]) 443 | additionalElements.push(Cells.MIRROR); 444 | break; 445 | case 'lord': 446 | additionalElements = [Cells.SHELF, Cells.SHELF_2, Cells.PLANT, Cells.JAR_TABLE]; 447 | if (!room.placedElements[Cells.MIRROR]) 448 | additionalElements.push(Cells.MIRROR); 449 | if (!room.placedElements[Cells.CLOCK]) 450 | additionalElements.push(Cells.CLOCK); 451 | break; 452 | case 'kitchen': 453 | additionalElements = [Cells.WINE_BARREL, Cells.OVEN, Cells.BARREL, Cells.GRILL]; 454 | break; 455 | case 'library': 456 | additionalElements = [Cells.PLANT, Cells.SHELF_2]; 457 | if (!room.placedElements[Cells.CLOCK]) 458 | additionalElements.push(Cells.CLOCK); 459 | break; 460 | case 'hall': 461 | additionalElements = [Cells.PLANT]; 462 | if (!room.placedElements[Cells.FOUNTAIN]) 463 | additionalElements.push(Cells.FOUNTAIN); 464 | break; 465 | 466 | } 467 | var element = Random.randomElementOf(additionalElements); 468 | room.placedElements[element] = true; 469 | return element; 470 | }, 471 | placeVBlock: function(type, x,y,size, flip, quartersType){ 472 | switch (type){ 473 | case 'tableAndChair': 474 | if (!flip){ 475 | this.map[x][y] = Cells.SMALL_TABLE; 476 | this.map[x][y+1] = Cells.N_CHAIR; 477 | } else { 478 | this.map[x][y+size-1] = Cells.SMALL_TABLE; 479 | this.map[x][y+size-2] = Cells.S_CHAIR; 480 | } 481 | break; 482 | case 'tableAndPiano': 483 | if (!flip){ 484 | this.map[x][y] = Cells.S_PIANO; 485 | this.map[x][y+1] = Cells.N_CHAIR; 486 | } else { 487 | this.map[x][y+size-1] = Cells.PIANO; 488 | this.map[x][y+size-2] = Cells.S_CHAIR; 489 | } 490 | break; 491 | default: 492 | var y = flip ? y+size-1 : y; 493 | this.map[x][y] = type; 494 | break; 495 | } 496 | }, 497 | placeBlock: function(type, x,y,size, flip, quartersType){ 498 | switch (type){ 499 | case 'bed': 500 | if (!flip){ 501 | this.map[x][y] = Cells.BED_1; 502 | this.map[x+1][y] = Cells.BED_2; 503 | if (size > 2 && quartersType === 'simple'){ 504 | this.map[x+2][y] = Random.chance(70) ? Cells.LOCKER : Cells.SMALL_TABLE; 505 | } 506 | } else { 507 | if (size > 2){ 508 | this.map[x+1][y] = Cells.BED_1; 509 | this.map[x+2][y] = Cells.BED_2; 510 | if (quartersType === 'simple') 511 | this.map[x][y] = Random.chance(70) ? Cells.LOCKER : Cells.SMALL_TABLE; 512 | } else { 513 | this.map[x][y] = Cells.BED_1; 514 | this.map[x+1][y] = Cells.BED_2; 515 | } 516 | } 517 | break; 518 | case 'tableAndChair': 519 | if (!flip){ 520 | this.map[x][y] = Cells.SMALL_TABLE; 521 | this.map[x+1][y] = Cells.L_CHAIR; 522 | } else { 523 | this.map[x+size-1][y] = Cells.SMALL_TABLE; 524 | this.map[x+size-2][y] = Cells.R_CHAIR; 525 | } 526 | break; 527 | case 'tableAndPiano': 528 | var x = flip ? x+size-1 : x; 529 | if (this.map[x][y-1] !== Cells.BED_2 && this.map[x][y-1] !== Cells.BED_2){ 530 | this.map[x][y-1] = Cells.S_CHAIR; 531 | this.map[x][y] = Cells.PIANO; 532 | } 533 | break; 534 | case 'diningTable': 535 | if (size == 1){ 536 | this.map[x][y] = Cells.SMALL_TABLE; 537 | } else { 538 | this.map[x][y] = Cells.L_TABLE; 539 | this.map[x+size-1][y] = Cells.R_TABLE; 540 | for (var i = 0; i < size-2; i++){ 541 | switch (Random.rand(0,3)){ 542 | case 0: this.map[x+1+i][y] = Cells.C_TABLE_1; break; 543 | case 1: this.map[x+1+i][y] = Cells.C_TABLE_2; break; 544 | case 2: this.map[x+1+i][y] = Cells.C_TABLE_3; break; 545 | case 3: this.map[x+1+i][y] = Cells.C_TABLE_4; break; 546 | } 547 | } 548 | } 549 | break; 550 | case 'bookshelf': 551 | if (flip){ 552 | this.map[x+size-2][y] = Cells.LIBRARY_1; 553 | this.map[x+size-1][y] = Cells.LIBRARY_2; 554 | } else { 555 | this.map[x][y] = Cells.LIBRARY_1; 556 | this.map[x+1][y] = Cells.LIBRARY_2; 557 | } 558 | break; 559 | case 'upChairs': 560 | if (size === 1){ 561 | this.map[x][y] = Cells.N_CHAIR; 562 | } else for (var i = x; i < x+size-1; i++){ 563 | if (Random.chance(80)) this.map[i][y] = Cells.N_CHAIR; 564 | } 565 | break; 566 | case 'downChairs': 567 | if (size === 1){ 568 | this.map[x][y] = Cells.S_CHAIR; 569 | } else for (var i = x+1; i < x+size; i++){ 570 | if (Random.chance(80)) this.map[i][y] = Cells.S_CHAIR; 571 | } 572 | break; 573 | default: 574 | var x = flip ? x+size-1 : x; 575 | this.map[x][y] = type; 576 | break; 577 | } 578 | }, 579 | placeDoors: function(room){ 580 | if (room.northDoors) for (var i = 0; i < room.northDoors.length; i++){ 581 | var door = room.northDoors[i]; 582 | if (door.cell === Cells.DOOR){ 583 | this.tryClear(door.position,room.y-1) 584 | this.tryClear(door.position,room.y+1) 585 | } 586 | if (this.map[door.position][room.y] === Cells.WALL) 587 | this.map[door.position][room.y] = door.cell; 588 | } 589 | if (room.southDoors) for (var i = 0; i < room.southDoors.length; i++){ 590 | var door = room.southDoors[i]; 591 | if (door.cell === Cells.DOOR){ 592 | this.tryClear(door.position,room.y+room.height); 593 | this.tryClear(door.position,room.y+room.height-2); 594 | } 595 | if (this.map[door.position][room.y+room.height-1] === Cells.WALL) 596 | this.map[door.position][room.y+room.height-1] = door.cell; 597 | } 598 | if (room.westDoors) for (var i = 0; i < room.westDoors.length; i++){ 599 | var door = room.westDoors[i]; 600 | if (door.cell === Cells.DOOR){ 601 | this.tryClear(room.x-1,door.position); 602 | this.tryClear(room.x+1,door.position); 603 | } 604 | if (this.map[room.x][door.position] === Cells.WALL) 605 | this.map[room.x][door.position] = door.cell; 606 | } 607 | 608 | if (room.eastDoors) for (var i = 0; i < room.eastDoors.length; i++){ 609 | var door = room.eastDoors[i]; 610 | if (door.cell === Cells.DOOR){ 611 | this.tryClear(room.x+room.width,door.position); 612 | this.tryClear(room.x+room.width-2,door.position); 613 | } 614 | if (this.map[room.x+room.width-1][door.position] === Cells.WALL) 615 | this.map[room.x+room.width-1][door.position] = door.cell; 616 | } 617 | }, 618 | tryClear: function(x,y){ 619 | if (!this.isFloor(x,y)){ 620 | if (this.map[x][y] === Cells.BED_2) 621 | this.map[x-1][y] = Cells.FLOOR; 622 | if (this.map[x][y] === Cells.BED_1) 623 | this.map[x+1][y] = Cells.FLOOR; 624 | if (this.map[x][y] === Cells.LIBRARY_2) 625 | this.map[x-1][y] = Cells.FLOOR; 626 | if (this.map[x][y] === Cells.LIBRARY_1) 627 | this.map[x+1][y] = Cells.FLOOR; 628 | if (this.map[x][y] === Cells.R_TABLE){ 629 | if (this.map[x-1][y] === Cells.L_TABLE){ 630 | this.map[x-1][y] = Cells.SMALL_TABLE; 631 | } else { 632 | this.map[x-1][y] = Cells.R_TABLE; 633 | } 634 | } else if (this.map[x][y] === Cells.L_TABLE){ 635 | if (this.map[x+1][y] === Cells.R_TABLE){ 636 | this.map[x+1][y] = Cells.SMALL_TABLE; 637 | } else { 638 | this.map[x+1][y] = Cells.L_TABLE; 639 | } 640 | } 641 | this.map[x][y] = Cells.FLOOR; 642 | } 643 | }, 644 | isFloor: function(x,y){ 645 | return this.map[x][y] === Cells.FLOOR || 646 | this.map[x][y] === Cells.FLOOR_2 || 647 | this.map[x][y] === Cells.DIRT || 648 | this.map[x][y] === Cells.GRASS_1 || 649 | this.map[x][y] === Cells.GRASS_2; 650 | }, 651 | isMultiTile: function(x,y){ 652 | return this.map[x][y] === Cells.L_TABLE || 653 | this.map[x][y] === Cells.R_TABLE || 654 | this.map[x][y] === Cells.C_TABLE_1 || 655 | this.map[x][y] === Cells.C_TABLE_2 || 656 | this.map[x][y] === Cells.C_TABLE_3 || 657 | this.map[x][y] === Cells.C_TABLE_4 || 658 | this.map[x][y] === Cells.LIBRARY_1 || 659 | this.map[x][y] === Cells.LIBRARY_2 || 660 | this.map[x][y] === Cells.BED_1 || 661 | this.map[x][y] === Cells.BED_2; 662 | }, 663 | build_diningRoom: function(room){ 664 | var tableLength = room.width - 3; 665 | this.buildRoom(room); 666 | var leftSize = Random.chance(50); 667 | room.placedElements = {}; 668 | var location = Random.rand(room.x+1, room.x+room.width-tableLength-1); 669 | for (var y = room.y+1; y < room.y + room.height - 1; y++){ 670 | var phase = (y-room.y)%3; 671 | if (phase == 1){ 672 | if ((room.y+room.height)-y<4) 673 | break; 674 | location = Random.rand(room.x+1, room.x+room.width-tableLength-1); 675 | } 676 | // Place tables and chairs 677 | switch(phase){ 678 | case 0: 679 | this.placeBlock('upChairs', location, y, tableLength, false, 'diningRoom'); 680 | break; 681 | case 1: 682 | this.placeBlock('downChairs', location, y, tableLength, false, 'diningRoom'); 683 | break; 684 | case 2: 685 | this.placeBlock('diningTable', location, y, tableLength, false, 'diningRoom'); 686 | break; 687 | } 688 | } 689 | this.addWallFireplace(room); 690 | this.addTorchesToRoom(room); 691 | }, 692 | addTorchesToRoom: function(room){ 693 | for (var y = room.y+1; y < room.y + room.height - 1; y++){ 694 | if (Random.chance(30) && !this.isMultiTile(room.x+1, y)){ 695 | this.map[room.x+1][y] = Cells.L_TORCH; 696 | y++; 697 | } 698 | } 699 | for (var y = room.y+1; y < room.y + room.height - 1; y++){ 700 | if (Random.chance(30) && !this.isMultiTile(room.x+room.width-2, y)){ 701 | this.map[room.x+room.width-2][y] = Cells.R_TORCH; 702 | y++; 703 | } 704 | } 705 | }, 706 | addWallFireplace: function(room){ 707 | var wall = room.northDoors; 708 | if (!wall) { 709 | wall = []; 710 | room.northDoors = wall; 711 | } 712 | wall.push({cell: Cells.WALL_FIREPLACE, position: Random.rand(room.x+1, room.x+room.width - 2)}); 713 | }, 714 | build_kitchen: function(room){ 715 | this.buildLivingQuarters(room, 'kitchen'); 716 | }, 717 | build_library: function(room){ 718 | this.buildLivingQuarters(room, 'library'); 719 | }, 720 | build_throneHall: function(room){ 721 | this.build_hall(room); 722 | }, 723 | build_halls: function(room){ // Wall halls} 724 | this.buildRoom(room); 725 | if (room.width > 3) 726 | this.addTorchesToRoom(room); 727 | }, 728 | build_hall: function(room){ 729 | this.buildLivingQuarters(room, 'hall'); 730 | } 731 | } 732 | 733 | module.exports = RoomBuilder; -------------------------------------------------------------------------------- /src/RoomsGenerator.js: -------------------------------------------------------------------------------- 1 | var Random = require('./Random'); 2 | var Arrays = require('./Arrays'); 3 | var Cells = require('./Cells'); 4 | 5 | function RoomsGenerator(){}; 6 | 7 | RoomsGenerator.prototype = { 8 | generateMap: function(structure, generationParams){ 9 | this.structure = structure; 10 | this.generationParams = generationParams; 11 | this.rooms = []; 12 | this.anchorPoints = {}; 13 | this.placeFoundations(); 14 | this.placeTowers(); 15 | this.placeCentralFeature(); 16 | this.placeEntrances(); 17 | var retries = 0; 18 | while(true){ 19 | var emptyRooms = this.placeRooms(); 20 | if (emptyRooms == false){ 21 | return false; 22 | } 23 | var assigned = this.assignRooms(emptyRooms); 24 | if (!assigned){ 25 | if (retries++ < 50){ 26 | continue; 27 | } else { 28 | return false; 29 | } 30 | } 31 | break; 32 | } 33 | this.linkRooms(); 34 | return this.rooms; 35 | }, 36 | placeFoundations: function(){ 37 | var def = this.structure.towers; 38 | var connectionWidth = def.connectionCorridors.type === 'corridor' ? 3 : def.connectionCorridors.hallWidth; 39 | var x = 3; 40 | if (def.verticalConnections){ 41 | x = 1 + Math.floor(def.size / 2) + Math.floor(connectionWidth/2) - 1; 42 | } 43 | var y = 3; 44 | if (def.horizontalConnections === 'both' || def.horizontalConnections === 'top'){ 45 | y = 1 + Math.floor(def.size / 2) + Math.floor(connectionWidth/2); 46 | } 47 | var yEnd = this.generationParams.height - 5; 48 | if (def.horizontalConnections === 'both' || def.horizontalConnections === 'bottom'){ 49 | yEnd = this.generationParams.height - 3 - Math.floor(def.size / 2) - Math.floor(connectionWidth/2); 50 | } 51 | this.addRoom( 52 | x, 53 | y, 54 | 'rooms', 55 | '', 56 | this.generationParams.width - 2 * x - 1, 57 | yEnd - y + 1, 58 | {}, 59 | -2 60 | ); 61 | }, 62 | 63 | placeTowers: function(){ 64 | var def = this.structure.towers; 65 | //NW 66 | this.addRoom(1, 1,'tower', 'Northwest Tower', def.size, def.size, { 67 | shape: def.circle ? 'circle' : 'square', 68 | walls: { 69 | north: def.crossWindows ? 'crossWindows' : 'solid', 70 | west: def.crossWindows ? 'crossWindows' : 'solid', 71 | south: def.verticalConnections ? 'exit' : 'solid', 72 | east: def.horizontalConnections === 'both' || def.horizontalConnections === 'top' ? 'exit' : 'solid' 73 | } 74 | }, -1); 75 | //NE 76 | this.addRoom(this.generationParams.width - def.size - 2, 1,'tower', 'Northeast Tower', def.size, def.size, { 77 | shape: def.circle ? 'circle' : 'square', 78 | walls: { 79 | north: def.crossWindows ? 'crossWindows' : 'solid', 80 | east: def.crossWindows ? 'crossWindows' : 'solid', 81 | south: def.verticalConnections ? 'exit' : 'solid', 82 | west: def.horizontalConnections === 'both' || def.horizontalConnections === 'top' ? 'exit' : 'solid' 83 | } 84 | }, -1); 85 | //SW 86 | this.addRoom(1, this.generationParams.height - def.size - 2,'tower', 'Southwest Tower', def.size, def.size, { 87 | shape: def.circle ? 'circle' : 'square', 88 | walls: { 89 | south: def.crossWindows ? 'crossWindows' : 'solid', 90 | west: def.crossWindows ? 'crossWindows' : 'solid', 91 | north: def.verticalConnections ? 'exit' : 'solid', 92 | east: def.horizontalConnections === 'both' || def.horizontalConnections === 'bottom' ? 'exit' : 'solid' 93 | } 94 | }, -1); 95 | //SE 96 | this.addRoom(this.generationParams.width - def.size - 2, this.generationParams.height - def.size - 2,'tower', 'Southeast Tower', def.size, def.size, { 97 | shape: def.circle ? 'circle' : 'square', 98 | walls: { 99 | south: def.crossWindows ? 'crossWindows' : 'solid', 100 | east: def.crossWindows ? 'crossWindows' : 'solid', 101 | north: def.verticalConnections ? 'exit' : 'solid', 102 | west: def.horizontalConnections === 'both' || def.horizontalConnections === 'bottom' ? 'exit' : 'solid' 103 | } 104 | }, -1); 105 | var connectionWidth = def.connectionCorridors.type === 'corridor' ? 3 : def.connectionCorridors.hallWidth; 106 | if (def.verticalConnections){ 107 | // West corridor 108 | this.addRoom( 109 | 1 + Math.floor(def.size / 2) - Math.floor(connectionWidth/2), 110 | 1 + def.size - 1, 111 | def.connectionCorridors.type, 'West '+def.connectionCorridors.type, 112 | connectionWidth, 113 | this.generationParams.height - 2 * def.size - 1, 114 | def.hallDecoration, 115 | 0, 116 | true 117 | ); 118 | // East corridor 119 | this.addRoom( 120 | this.generationParams.width - 3 - Math.floor(def.size / 2) - Math.floor(connectionWidth/2) + ((def.size - connectionWidth)%2 != 0 ? 1 : 0), 121 | 1 + def.size - 1, 122 | def.connectionCorridors.type, 'East '+def.connectionCorridors.type, 123 | connectionWidth, 124 | this.generationParams.height - 2 * def.size - 1, 125 | def.hallDecoration, 126 | 0, 127 | true 128 | ); 129 | } 130 | if (def.horizontalConnections === 'both' || def.horizontalConnections === 'top'){ 131 | // North corridor 132 | this.anchorPoints.northBound = 1 + Math.floor(def.size / 2) - Math.floor(connectionWidth/2); 133 | this.addRoom( 134 | 1 + def.size - 1, 135 | this.anchorPoints.northBound, 136 | def.connectionCorridors.type, 'North '+def.connectionCorridors.type, 137 | this.generationParams.width - 2 * def.size - 1, 138 | connectionWidth, 139 | def.hallDecoration, 140 | 0, 141 | true 142 | ); 143 | } else { 144 | this.anchorPoints.northBound = 3; 145 | } 146 | if (def.horizontalConnections === 'both' || def.horizontalConnections === 'bottom'){ 147 | this.anchorPoints.southBound = this.generationParams.height - 3 - Math.floor(def.size / 2) - Math.floor(connectionWidth/2)+ ((def.size - connectionWidth)%2 != 0 ? 1 : 0) + connectionWidth; 148 | // South corridor 149 | this.addRoom( 150 | 1 + def.size - 1, 151 | this.anchorPoints.southBound - connectionWidth, 152 | def.connectionCorridors.type, 'South '+def.connectionCorridors.type, 153 | this.generationParams.width - 2 * def.size - 1, 154 | connectionWidth, 155 | def.hallDecoration, 156 | 0, 157 | true 158 | ); 159 | } else { 160 | this.anchorPoints.southBound = this.generationParams.height - 4; 161 | } 162 | }, 163 | placeCentralFeature: function(){ 164 | var def = this.structure.central; 165 | this.centerRoom = this.addRoom( 166 | Math.floor(this.generationParams.width / 2) - Math.floor(def.width /2) - 1, 167 | Math.floor(this.generationParams.height / 2) - Math.floor(def.height /2) - 1, 168 | def.type, 169 | def.type === 'courtyard' ? 'Courtyard' : 'Main Hall', 170 | def.width, 171 | def.height, 172 | def 173 | ); 174 | }, 175 | placeEntrances: function(){ 176 | var entranceLength = Math.floor(this.generationParams.height / 2) - Math.floor(this.structure.central.height /2) - 1; 177 | 178 | // North entrance 179 | if (this.structure.entrances.northExit){ 180 | var def = this.structure.entrances.northExit; 181 | // Northern Exit 182 | this.addRoom( 183 | Math.floor(this.generationParams.width / 2) - Math.floor(def.width /2) - 1, 184 | 0, 185 | 'entrance', 186 | 'Entrance', 187 | def.width, 188 | this.anchorPoints.northBound, 189 | def, 190 | 1, 191 | false 192 | ); 193 | 194 | // Northern entrance hall 195 | this.addRoom( 196 | Math.floor(this.generationParams.width / 2) - Math.floor(def.width /2) - 1, 197 | this.anchorPoints.northBound, 198 | 'entranceHall', 199 | 'Entrance Hall', 200 | def.width, 201 | entranceLength - this.anchorPoints.northBound + 1, 202 | def, 203 | 1, 204 | false 205 | ); 206 | } 207 | // South Entrance 208 | if (this.structure.entrances.southExit){ 209 | var def = this.structure.entrances.southExit; 210 | this.addRoom( 211 | Math.floor(this.generationParams.width / 2) - Math.floor(def.width /2) - 1, 212 | entranceLength + this.structure.central.height - 1, 213 | 'entranceHall', 214 | 'Entrance Hall', 215 | def.width, 216 | this.anchorPoints.southBound - (entranceLength + this.structure.central.height - 1), 217 | def, 218 | 1, 219 | false 220 | ); 221 | this.addRoom( 222 | Math.floor(this.generationParams.width / 2) - Math.floor(def.width /2) - 1, 223 | this.anchorPoints.southBound, 224 | 'entrance', 225 | 'Entrance', 226 | def.width, 227 | this.generationParams.height - this.anchorPoints.southBound, 228 | def, 229 | 1, 230 | false 231 | ); 232 | } 233 | 234 | }, 235 | placeRooms: function(){ 236 | var def = this.structure.towers; 237 | var connectionWidth = def.connectionCorridors.type === 'corridor' ? 3 : def.connectionCorridors.hallWidth; 238 | var x = 3; 239 | var adjustment = (def.size - connectionWidth)%2 == 0 ? 1 : 0; 240 | adjustment = 0; 241 | if (def.verticalConnections){ 242 | x = 1 + Math.floor(def.size / 2) + Math.floor(connectionWidth/2) + adjustment - 1; 243 | } 244 | var y = 3; 245 | if (def.horizontalConnections === 'both' || def.horizontalConnections === 'top'){ 246 | y = 1 + Math.floor(def.size / 2) + Math.floor(connectionWidth/2) + adjustment; 247 | } 248 | var yEnd = this.generationParams.height - 5; 249 | if (def.horizontalConnections === 'both' || def.horizontalConnections === 'bottom'){ 250 | yEnd = this.generationParams.height - 3 - Math.floor(def.size / 2) - Math.floor(connectionWidth/2) - adjustment; 251 | } 252 | var area = { 253 | x: x, 254 | y: y, 255 | w: Math.floor(this.generationParams.width / 2) - x, 256 | h: yEnd - y + 1 257 | }; 258 | // Brute force! Let's try a lot of times to fit the rooms in the space we have! 259 | var roomsToPlace = Math.ceil(this.structure.rooms.length / 2); 260 | this.roomsArea = area; 261 | var minHeight = 5; 262 | var maxHeight = 7; 263 | var minWidth = 5; 264 | var maxWidth = 7; 265 | var addedRooms = []; 266 | var placedRooms = 0; 267 | var reduceRoomsToPlace = 100; 268 | // Place room connecting with courtyard 269 | addedRooms.push({ 270 | x: this.centerRoom.x - 4, 271 | y: this.centerRoom.y + Math.floor(this.centerRoom.height/2) - 2, 272 | w: 5, 273 | h: 5 274 | }); 275 | out: for (var i = 0; i < 500; i++){ 276 | for (var j = 0; j < 500; j++){ 277 | var room = { 278 | x: Random.rand(area.x, area.x + area.w - 2 - minWidth), 279 | y: Random.rand(area.y, area.y + area.h - 2 - minHeight), 280 | w: Random.rand(minWidth, maxWidth), 281 | h: Random.rand(minHeight, maxHeight), 282 | }; 283 | if (this.validRoom(room, addedRooms)){ 284 | addedRooms.push(room); 285 | placedRooms++; 286 | if (placedRooms == roomsToPlace) 287 | break out; 288 | } 289 | } 290 | if (reduceRoomsToPlace == 0){ 291 | if (roomsToPlace > 4) 292 | roomsToPlace--; 293 | reduceRoomsToPlace = 100; 294 | } else { 295 | reduceRoomsToPlace --; 296 | } 297 | addedRooms = []; 298 | } 299 | if (placedRooms != roomsToPlace) 300 | return false; // Couldn't place enough rooms, sucks! 301 | 302 | // Is the castle super symmetric? if not, we mirror the rooms before making them grow, and extend the play area 303 | if (!this.structure.general.superSymmetric){ 304 | var originalRoomsCount = addedRooms.length; 305 | // Mirror the added rooms over the y axis 306 | for (var i = 0; i < originalRoomsCount; i++){ 307 | var room = addedRooms[i]; 308 | var dist = this.roomsArea.x + this.roomsArea.w - (room.x + room.w); 309 | addedRooms.push({ 310 | x: this.roomsArea.x + this.roomsArea.w + dist - 1, 311 | y: room.y, 312 | w: room.w, 313 | h: room.h 314 | }); 315 | } 316 | area.w = this.generationParams.width - 2 * x - 1; 317 | } 318 | 319 | while(true){ 320 | //Try to make each room bigger in a direction, until it's not possible for any 321 | var grew = false; 322 | for (var i = 0; i < addedRooms.length; i++){ 323 | var room = addedRooms[i]; 324 | //Select a random direction first, if it can't grow in that direction, check in order 325 | var randomDirection = Random.rand(0,3); 326 | if (this.roomCanGrow(room, addedRooms, randomDirection)){ 327 | this.growRoom(room, randomDirection); 328 | grew = true; 329 | } else { 330 | for (var j = 0; j <= 3; j++){ 331 | if (this.roomCanGrow(room, addedRooms, j)){ 332 | this.growRoom(room, j); 333 | grew = true; 334 | break; 335 | } 336 | } 337 | } 338 | } 339 | if (!grew){ 340 | break; 341 | } 342 | } 343 | 344 | // If the castle is super symmetric, mirror them now (after growing) 345 | if (this.structure.general.superSymmetric){ 346 | var originalRoomsCount = addedRooms.length; 347 | // Mirror the added rooms over the y axis 348 | for (var i = 0; i < originalRoomsCount; i++){ 349 | var room = addedRooms[i]; 350 | var dist = this.roomsArea.x + this.roomsArea.w - (room.x + room.w); 351 | addedRooms.push({ 352 | x: this.roomsArea.x + this.roomsArea.w + dist - 1, 353 | y: room.y, 354 | w: room.w, 355 | h: room.h 356 | }); 357 | } 358 | } 359 | 360 | // Now let's fill the spaces and make these rooms grow again 361 | area.w = this.generationParams.width - 2 * x - 1; 362 | while(true){ 363 | var noHoles = true; 364 | hole: for (var x = area.x; x <= area.x + area.w - 2; x++){ 365 | for (var y = area.y; y <= area.y + area.h - 2; y++){ 366 | // Is there a hole here? is it big enough? 367 | if (this.holeAt(addedRooms, x,y)){ 368 | noHoles = false; 369 | // Lets plot a room and make it grow 370 | var fillRoom = { 371 | x: x, 372 | y: y, 373 | w: 5, 374 | h: 5 375 | }; 376 | console.log("Placing a fill room", fillRoom); 377 | addedRooms.push(fillRoom); 378 | while(true){ 379 | var grew = false; 380 | //Select a random direction first, if it can't grow in that direction, check in order 381 | var randomDirection = Random.rand(0,3); 382 | if (this.roomCanGrow(fillRoom, addedRooms, randomDirection)){ 383 | this.growRoom(fillRoom, randomDirection); 384 | grew = true; 385 | } else { 386 | for (var j = 0; j <= 3; j++){ 387 | if (this.roomCanGrow(fillRoom, addedRooms, j)){ 388 | this.growRoom(fillRoom, j); 389 | grew = true; 390 | break; 391 | } 392 | } 393 | } 394 | if (!grew){ 395 | break; 396 | } 397 | } 398 | break hole; 399 | } 400 | } 401 | } 402 | if (noHoles) 403 | break; 404 | } 405 | return addedRooms; 406 | }, 407 | holeAt: function(rooms, x,y){ 408 | for (var dx = 0; dx < 5; dx++){ 409 | for (var dy = 0; dy < 5; dy++){ 410 | if (!this.emptySpace(x+dx, y+dy, rooms)){ 411 | return false; 412 | } 413 | } 414 | } 415 | return true; 416 | }, 417 | assignRooms: function(rooms, force){ 418 | /** We now have a lot of empty rooms, give a function to each based on the super structure 419 | * We will try several times 420 | * Following room attributes are relevant: 421 | * placeNorth: boolean - Room must be placed as north as possible 422 | * isNextTo: string - Room must be placed as close as possible to a room with the given code 423 | * southRoom: string - If there is a room in the southern exit of this room, it has to be one of the given code. Only for "placeNorth" rooms 424 | * isBig: boolean - Pick one of the rooms with greatest area 425 | */ 426 | 427 | // Do we have enough big rooms in the north to satisfy requirements, can we whine? 428 | if (!force){ 429 | var bigHeightThreshold = 8; 430 | var bigNorthRequiredRooms = 0; 431 | var northRequiredRooms = 0; 432 | var bigRequiredRooms = 0; 433 | var bigNorthAvailableRooms = 0; 434 | var northAvailableRooms = 0; 435 | var bigAvailableRooms = 0; 436 | 437 | // Gather requirements 438 | for (var i = 0; i < this.structure.rooms.length; i++){ 439 | var room = this.structure.rooms[i]; 440 | if (room.placeNorth){ 441 | if (room.isBig){ 442 | bigNorthRequiredRooms++; 443 | } else { 444 | northRequiredRooms++; 445 | } 446 | } else if (room.isBig){ 447 | bigRequiredRooms++; 448 | } 449 | } 450 | 451 | // Sum available rooms 452 | for (var i = 0; i < rooms.length; i++){ 453 | var room = rooms[i]; 454 | if (room.y == this.roomsArea.y){ 455 | if (room.h > bigHeightThreshold) 456 | bigNorthAvailableRooms ++; 457 | else 458 | northAvailableRooms ++; 459 | } else if (room.h > bigHeightThreshold) 460 | bigAvailableRooms ++; 461 | } 462 | 463 | // Check if we comply 464 | if (bigNorthAvailableRooms < bigNorthRequiredRooms || 465 | northAvailableRooms < northRequiredRooms || 466 | bigAvailableRooms < bigRequiredRooms) 467 | return false; // Give me better rooms! 468 | } 469 | 470 | // Split the rooms by category 471 | var northAvailableRooms = []; 472 | var northBigAvailableRooms = []; 473 | var bigAvailableRooms = []; 474 | var otherAvailableRooms = []; 475 | for (var i = 0; i < rooms.length; i++){ 476 | var room = rooms[i]; 477 | if (room.y == this.roomsArea.y){ 478 | if (room.h > bigHeightThreshold){ 479 | northBigAvailableRooms.push(room); 480 | } else { 481 | northAvailableRooms.push(room); 482 | } 483 | } else { 484 | if (room.h > bigHeightThreshold){ 485 | bigAvailableRooms.push(room); 486 | } else { 487 | otherAvailableRooms.push(room); 488 | } 489 | } 490 | } 491 | 492 | // Shuffle the rooms 493 | var roomsToAdd = Arrays.shuffle(this.structure.rooms); 494 | // but place rooms with southRoom first 495 | roomsToAdd = roomsToAdd.sort(function(a, b){ 496 | return (a.southRoom && b.southRoom) ? 0 : a.southRoom ? -1 : 1; 497 | }); 498 | for (var i = 0; i < roomsToAdd.length; i++){ 499 | var requiredRoom = roomsToAdd[i]; 500 | var room = false; 501 | if (requiredRoom.placeNorth){ 502 | if (requiredRoom.isBig || northAvailableRooms.length == 0){ 503 | room = Random.randomElementOf(northBigAvailableRooms); 504 | } else { 505 | room = Random.randomElementOf(northAvailableRooms); 506 | } 507 | } else if (requiredRoom.isBig){ 508 | room = Random.randomElementOf(bigAvailableRooms); 509 | } else { 510 | room = Random.randomElementOf(otherAvailableRooms); 511 | } 512 | if (!room){ 513 | // Couldn't pick from preferred? pick from any available!! 514 | if (otherAvailableRooms.length) room = Random.randomElementOf(otherAvailableRooms); 515 | if (northBigAvailableRooms.length) room = Random.randomElementOf(northBigAvailableRooms); 516 | if (northAvailableRooms.length) room = Random.randomElementOf(northAvailableRooms); 517 | if (bigAvailableRooms.length) room = Random.randomElementOf(bigAvailableRooms); 518 | } 519 | // Remove used space 520 | Arrays.removeObject(northBigAvailableRooms, room); 521 | Arrays.removeObject(northAvailableRooms, room); 522 | Arrays.removeObject(bigAvailableRooms, room); 523 | Arrays.removeObject(otherAvailableRooms, room); 524 | if (room){ 525 | var linkeable = requiredRoom.linkeable != undefined ? requiredRoom.linkeable : true; 526 | this.addRoom(room.x, room.y, requiredRoom.type, requiredRoom.type, room.w, room.h, requiredRoom, requiredRoom.level, linkeable); 527 | // Place south rooms 528 | if (requiredRoom.southRoom){ 529 | var southRoom = this.getRoomAt(rooms, room.x + Math.floor(room.w/2), room.y + room.h+2); 530 | if (!southRoom){ 531 | // There's probably a hall, or the central feature which is fine. 532 | } else { 533 | this.addRoom(southRoom.x, southRoom.y, requiredRoom.southRoom, requiredRoom.southRoom, southRoom.w, southRoom.h, requiredRoom, requiredRoom.level, true); 534 | Arrays.removeObject(bigAvailableRooms, southRoom); // Available space used 535 | Arrays.removeObject(otherAvailableRooms, southRoom); // Available space used 536 | } 537 | } 538 | } else { 539 | return false; // Give me better rooms! 540 | } 541 | } 542 | 543 | // Fill unused rooms with living quarters 544 | var remainingRooms = northBigAvailableRooms.concat(northAvailableRooms).concat(bigAvailableRooms).concat(otherAvailableRooms); 545 | for (var i = 0; i < remainingRooms.length; i++){ 546 | var availableRoom = remainingRooms[i]; 547 | this.addRoom(availableRoom.x, availableRoom.y, 'livingQuarters', 'livingQuarters*', availableRoom.w, availableRoom.h, availableRoom, 0, true); 548 | } 549 | return true; 550 | }, 551 | roomCanGrow: function(room, tempRooms, direction){ 552 | var testRoom = { 553 | x: room.x, 554 | y: room.y, 555 | w: room.w, 556 | h: room.h 557 | } 558 | switch(direction){ 559 | case 0: 560 | testRoom.x --; 561 | testRoom.w ++; 562 | break; 563 | case 1: 564 | testRoom.w ++; 565 | break; 566 | case 2: 567 | testRoom.y --; 568 | testRoom.h ++; 569 | break; 570 | case 3: 571 | testRoom.h ++; 572 | break; 573 | } 574 | return this.validRoom(testRoom, tempRooms, room); 575 | }, 576 | growRoom: function(room, direction){ 577 | switch(direction){ 578 | case 0: 579 | room.x --; 580 | room.w ++; 581 | break; 582 | case 1: 583 | room.w ++; 584 | break; 585 | case 2: 586 | room.y --; 587 | room.h ++; 588 | break; 589 | case 3: 590 | room.h ++; 591 | break; 592 | } 593 | }, 594 | getRoomAt: function(rooms,x,y){ 595 | for (var i = 0; i < rooms.length; i++){ 596 | var room = rooms[i]; 597 | if (x >= room.x && x < room.x + room.w && y >= room.y && y < room.y + room.h) 598 | return room; 599 | } 600 | return false; 601 | }, 602 | getRealRoomAt: function(x,y,exclude){ 603 | for (var i = 0; i < this.rooms.length; i++){ 604 | var room = this.rooms[i]; 605 | if (room.type === 'rooms') 606 | continue; 607 | if (room == exclude) 608 | continue; 609 | if (x >= room.x && x < room.x + room.width && y >= room.y && y < room.y + room.height) 610 | return room; 611 | } 612 | return false; 613 | }, 614 | validRoom: function(room, tempRooms, skipRoom){ 615 | // Must be inside the rooms area 616 | if (room.x >= this.roomsArea.x && room.x + room.w <= this.roomsArea.x + this.roomsArea.w && 617 | room.y >= this.roomsArea.y && room.y + room.h <= this.roomsArea.y + this.roomsArea.h) { 618 | // Ok... 619 | } else { 620 | return false; 621 | } 622 | 623 | // Must not collide with other rooms (except the towers) 624 | for (var i = 0; i < this.rooms.length; i++){ 625 | var existingRoom = this.rooms[i]; 626 | if (existingRoom.type === 'tower' || existingRoom.type === 'rooms'){ 627 | continue; 628 | } 629 | if (existingRoom.x < room.x + room.w - 1 && existingRoom.x + existingRoom.width - 1 > room.x && 630 | existingRoom.y < room.y + room.h - 1 && existingRoom.y + existingRoom.height - 1 > room.y) { 631 | return false; 632 | } 633 | } 634 | // Must not collide with other temp rooms 635 | for (var i = 0; i < tempRooms.length; i++){ 636 | var existingRoom = tempRooms[i]; 637 | if (skipRoom && skipRoom == existingRoom){ 638 | continue; 639 | } 640 | if (existingRoom.x < room.x + room.w - 1 && existingRoom.x + existingRoom.w - 1 > room.x && 641 | existingRoom.y < room.y + room.h - 1 && existingRoom.y + existingRoom.h - 1 > room.y) { 642 | return false; 643 | } 644 | } 645 | return true; 646 | }, 647 | emptySpace: function(x, y, tempRooms){ 648 | // Must be inside the rooms area 649 | if (x >= this.roomsArea.x && x <= this.roomsArea.x + this.roomsArea.w && 650 | y >= this.roomsArea.y && y <= this.roomsArea.y + this.roomsArea.h) { 651 | // Ok... 652 | } else { 653 | return false; 654 | } 655 | 656 | // Must not collide with other rooms (except the towers) 657 | for (var i = 0; i < this.rooms.length; i++){ 658 | var existingRoom = this.rooms[i]; 659 | if (existingRoom.type === 'tower' || existingRoom.type === 'rooms'){ 660 | continue; 661 | } 662 | if (existingRoom.x < x && existingRoom.x + existingRoom.width - 1 > x && 663 | existingRoom.y < y && existingRoom.y + existingRoom.height - 1 > y) { 664 | return false; 665 | } 666 | } 667 | // Must not collide with other temp rooms 668 | for (var i = 0; i < tempRooms.length; i++){ 669 | var existingRoom = tempRooms[i]; 670 | if (existingRoom.x < x && existingRoom.x + existingRoom.w - 1 > x && 671 | existingRoom.y < y && existingRoom.y + existingRoom.h - 1 > y) { 672 | return false; 673 | } 674 | } 675 | return true; 676 | }, 677 | addRoom: function(x, y, type, name, width, height, features, level, linkeable){ 678 | var room = { 679 | x: x, 680 | y: y, 681 | type: type, 682 | name: name, 683 | width: width, 684 | height: height, 685 | features: features, 686 | level: level, 687 | linkeable: linkeable 688 | } 689 | this.rooms.push(room); 690 | return room; 691 | }, 692 | linkRooms: function(){ 693 | // Starting from the courtyard or main hall, go into each direction connection rooms if possible 694 | // Go westeros 695 | var westRoom = this.getRealRoomAt(this.centerRoom.x - 1, this.centerRoom.y + Math.floor(this.centerRoom.height/2), this.centerRoom); 696 | if (westRoom){ 697 | this.linkRoom(westRoom); 698 | if (!this.centerRoom.westDoors){ 699 | this.centerRoom.westDoors = []; 700 | } 701 | this.centerRoom.westDoors.push({position: this.centerRoom.y + Math.floor(this.centerRoom.height/2), cell: Cells.DOOR}); 702 | } 703 | 704 | // Go wessos 705 | var eastRoom = this.getRealRoomAt(this.centerRoom.x + this.centerRoom.width, this.centerRoom.y + Math.floor(this.centerRoom.height/2), this.centerRoom); 706 | if (eastRoom){ 707 | this.linkRoom(eastRoom); 708 | if (!this.centerRoom.eastDoors) 709 | this.centerRoom.eastDoors = []; 710 | this.centerRoom.eastDoors.push({position: this.centerRoom.y + Math.floor(this.centerRoom.height/2), cell: Cells.DOOR}); 711 | } 712 | 713 | 714 | // At the end, for unconnected rooms, connect to nearby 715 | }, 716 | linkRoom: function(room){ 717 | //First, get all nearby linkeable rooms, and take note of the segments 718 | room.linkeable = false; 719 | var northRooms = this.getLinkeableRooms(room, 'north'); 720 | var southRooms = this.getLinkeableRooms(room, 'south'); 721 | var westRooms = this.getLinkeableRooms(room, 'west'); 722 | var eastRooms = this.getLinkeableRooms(room, 'east'); 723 | 724 | //Then link using all the segments 725 | for (var i = 0; i < northRooms.length; i++){ 726 | var segment = northRooms[i]; 727 | var x = Random.rand(segment.start, segment.end); 728 | if (!room.northDoors) 729 | room.northDoors = []; 730 | room.northDoors.push({position: x, cell: Cells.DOOR}); 731 | this.linkRoom(segment.room); 732 | } 733 | 734 | 735 | for (var i = 0; i < southRooms.length; i++){ 736 | var segment = southRooms[i]; 737 | var x = Random.rand(segment.start, segment.end); 738 | if (!room.southDoors) 739 | room.southDoors = []; 740 | room.southDoors.push({position: x, cell: Cells.DOOR}); 741 | this.linkRoom(segment.room); 742 | } 743 | 744 | for (var i = 0; i < westRooms.length; i++){ 745 | var segment = westRooms[i]; 746 | var x = Random.rand(segment.start, segment.end); 747 | if (!room.westDoors) 748 | room.westDoors = []; 749 | room.westDoors.push({position: x, cell: Cells.DOOR}); 750 | this.linkRoom(segment.room); 751 | } 752 | 753 | for (var i = 0; i < eastRooms.length; i++){ 754 | var segment = eastRooms[i]; 755 | var x = Random.rand(segment.start, segment.end); 756 | if (!room.eastDoors) 757 | room.eastDoors = []; 758 | room.eastDoors.push({position: x, cell: Cells.DOOR}); 759 | this.linkRoom(segment.room); 760 | } 761 | }, 762 | getLinkeableRooms: function(room, direction){ 763 | var currentSegment = false; 764 | var segments = []; 765 | switch (direction){ 766 | case 'north': case 'south': 767 | start = room.x + 1; 768 | end = room.x + room.width - 2; 769 | break; 770 | case 'west': case 'east': 771 | start = room.y + 1; 772 | end = room.y + room.height - 2; 773 | break; 774 | } 775 | for (var x = start; x <= end; x++){ 776 | var nearRoom = false; 777 | switch (direction){ 778 | case 'north': 779 | nearRoom = this.getRealRoomAt(x, room.y - 1, room); 780 | break; 781 | case 'south': 782 | nearRoom = this.getRealRoomAt(x, room.y + room.height+1, room); 783 | break; 784 | case 'west': 785 | nearRoom = this.getRealRoomAt(room.x - 1, x, room); 786 | break; 787 | case 'east': 788 | nearRoom = this.getRealRoomAt(room.x + room.width +1, x, room); 789 | break; 790 | } 791 | 792 | if (nearRoom){ 793 | if (!currentSegment){ 794 | if (!nearRoom.linkeable) 795 | continue; 796 | currentSegment = { 797 | start: x + 1, 798 | room: nearRoom 799 | } 800 | segments.push(currentSegment); 801 | } 802 | if (nearRoom == currentSegment.room){ 803 | // The segment continues 804 | } else { 805 | currentSegment.end = x - 2; 806 | currentSegment.room.linkeable = false; 807 | if (nearRoom.linkeable){ 808 | // New segment 809 | currentSegment = { 810 | start: x + 1, 811 | room: nearRoom 812 | } 813 | segments.push(currentSegment); 814 | } else { 815 | currentSegment = false; 816 | } 817 | } 818 | } else if (currentSegment){ 819 | // No room, segment ends 820 | currentSegment.end = x - 2; 821 | currentSegment.room.linkeable = false; 822 | currentSegment = false; 823 | } 824 | } 825 | if (currentSegment && !currentSegment.end){ 826 | currentSegment.end = end - 1; 827 | } 828 | // room.segments = segments; 829 | return segments; 830 | } 831 | } 832 | 833 | module.exports = RoomsGenerator; -------------------------------------------------------------------------------- /web/generator.js: -------------------------------------------------------------------------------- 1 | (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o