001package squidpony.squidgrid.mapping; 002 003import squidpony.ArrayTools; 004import squidpony.squidmath.Coord; 005import squidpony.squidmath.GWTRNG; 006import squidpony.squidmath.GreasedRegion; 007import squidpony.squidmath.IRNG; 008 009/** 010 * Map generator that constructs a large number of overlapping rectangular rooms. 011 * Meant for the kind of crowded architecture that might fit the dungeon equivalent of urban areas. 012 * Likely to have many dead-end sections with only one door-like area, similar to hotel rooms or closets depending on 013 * size, and the distance required to reach a particular room may be... cruel and/or unusual. Whole winding areas of a 014 * large map may only be accessible from outside by one door, for instance. 015 * <br> 016 * An example of what this outputs: 017 * https://gist.github.com/tommyettinger/3144b56a3a8e5bbe5ee401c1a93989f4 018 * Created by Tommy Ettinger on 5/4/2016. 019 */ 020public class DenseRoomMapGenerator implements IDungeonGenerator { 021 public char[][] map; 022 public int[][] environment; 023 public IRNG rng; 024 protected int width, height; 025 public DenseRoomMapGenerator() 026 { 027 this(80, 30, new GWTRNG()); 028 } 029 public DenseRoomMapGenerator(int width, int height) 030 { 031 this(width, height, new GWTRNG()); 032 } 033 public DenseRoomMapGenerator(int width, int height, IRNG rng) 034 { 035 this.rng = rng; 036 this.width = Math.max(3, width); 037 this.height = Math.max(3, height); 038 map = ArrayTools.fill('#', this.width, this.height); 039 environment = new int[this.width][this.height]; 040 } 041 042 public char[][] getDungeon() { 043 return map; 044 } 045 /** 046 * Generate a map as a 2D char array using the width and height specified in the constructor. 047 * Should produce a crowded arrangement of rectangular rooms that overlap with each other. 048 * @return a 2D char array for the map of densely-packed rectangular rooms. 049 */ 050 public char[][] generate() 051 { 052 //ArrayList<short[]> regions = new ArrayList<>(); 053 short[] tempPacked; 054 int nh, nw, nx, ny, hnw, hnh; 055// Collection<Coord> sampled = PoissonDisk.sampleRectangle(Coord.get(1, 1), Coord.get(width - 2, height - 2), 056// 6f, width, height, 35, rng); 057// sampled.addAll(PoissonDisk.sampleRectangle(Coord.get(1, 1), Coord.get(width - 2, height - 2), 058// 9, width, height, 40, rng)); 059 GreasedRegion sampled = new GreasedRegion(width, height).allOn().removeEdges(); 060 sampled.remake(sampled.copy().randomScatter(rng, 6).or(sampled.randomScatter(rng,8))); 061 062 for(Coord center : sampled) { 063 nw = rng.between(4, 16); 064 nh = rng.between(4, 16); 065 hnw = (nw + 1) / 2; 066 hnh = (nh + 1) / 2; 067 nx = Math.max(0, Math.min(width - 2 - hnw, center.x - hnw)); 068 ny = Math.max(0, Math.min(height - 2 - hnh, center.y - hnh)); 069 if (center.x - hnw != nx) 070 nw -= Math.abs(center.x - hnw - nx); 071 if (center.y - hnh != ny) 072 nh -= Math.abs(center.y - hnh - ny); 073 if (nw >= 0 && nh >= 0) { 074 ArrayTools.insert(DungeonUtility.wallWrap(ArrayTools.fill('.', nw, nh)), 075 map, nx, ny); 076 //regions.add(CoordPacker.rectangle(nx, ny, nw, nh)); 077 } 078 } 079 for (int x = 0; x < width; x++) { 080 for (int y = 0; y < height; y++) { 081 environment[x][y] = (map[x][y] == '.') ? DungeonUtility.ROOM_FLOOR : DungeonUtility.ROOM_WALL; 082 } 083 } 084// tempPacked = CoordPacker.intersectPacked( 085// CoordPacker.rectangle(1, 1, width - 2, height - 2), 086// CoordPacker.pack(map, '#')); 087// Coord[] holes = CoordPacker.randomSeparated(tempPacked, 3, rng); 088 for(Coord hole : new GreasedRegion(map, '.').fringe().removeEdges().mixedRandomSeparated(0.25, -1, rng.nextLong())) { 089 if (((map[hole.x - 1][hole.y] == '.' && map[hole.x + 1][hole.y] == '.') || 090 (map[hole.x][hole.y - 1] == '.' && map[hole.x][hole.y + 1] == '.'))) { 091 map[hole.x][hole.y] = '.'; 092 environment[hole.x][hole.y] = DungeonUtility.CORRIDOR_FLOOR; 093 } 094 } 095 096 /* 097 regions = rng.shuffle(regions); 098 while (regions.size() > 1) 099 { 100 101 region = regions.remove(0); 102 linking = regions.get(0); 103 start = CoordPacker.singleRandom(region, rng); 104 end = CoordPacker.singleRandom(linking, rng); 105 path = OrthoLine.line(start, end); 106 for(Coord elem : path) 107 { 108 if(elem.x < width && elem.y < height) { 109 if (map[elem.x][elem.y] == '#') { 110 map[elem.x][elem.y] = '.'; 111 environment[elem.x][elem.y] = MixedGenerator.CORRIDOR_FLOOR; 112 ctr++; 113 } 114 } 115 } 116 } 117 */ 118 119 int upperY = height - 1; 120 int upperX = width - 1; 121 for (int i = 0; i < width; i++) { 122 map[i][0] = '#'; 123 map[i][upperY] = '#'; 124 environment[i][0] = DungeonUtility.UNTOUCHED; 125 environment[i][upperY] = DungeonUtility.UNTOUCHED; 126 } 127 for (int i = 0; i < height; i++) { 128 map[0][i] = '#'; 129 map[upperX][i] = '#'; 130 environment[0][i] = DungeonUtility.UNTOUCHED; 131 environment[upperX][i] = DungeonUtility.UNTOUCHED; 132 } 133 134 return map; 135 } 136 137 138 /** 139 * Gets a 2D array of int constants, each representing a type of environment corresponding to a static field of 140 * MixedGenerator. This array will have the same size as the last char 2D array produced by generate(); the value 141 * of this method if called before generate() is undefined, but probably will be a 2D array of all 0 (UNTOUCHED). 142 * <ul> 143 * <li>MixedGenerator.UNTOUCHED, equal to 0, is used for any cells that aren't near a floor.</li> 144 * <li>MixedGenerator.ROOM_FLOOR, equal to 1, is used for floor cells inside wide room areas.</li> 145 * <li>MixedGenerator.ROOM_WALL, equal to 2, is used for wall cells around wide room areas.</li> 146 * <li>MixedGenerator.CAVE_FLOOR, equal to 3, is used for floor cells inside rough cave areas.</li> 147 * <li>MixedGenerator.CAVE_WALL, equal to 4, is used for wall cells around rough cave areas.</li> 148 * <li>MixedGenerator.CORRIDOR_FLOOR, equal to 5, is used for floor cells inside narrow corridor areas.</li> 149 * <li>MixedGenerator.CORRIDOR_WALL, equal to 6, is used for wall cells around narrow corridor areas.</li> 150 * </ul> 151 * @return a 2D int array where each element is an environment type constant in MixedGenerator 152 */ 153 public int[][] getEnvironment() 154 { 155 return environment; 156 } 157}