001package squidpony.squidgrid.mapping; 002 003import squidpony.ArrayTools; 004import squidpony.annotation.Beta; 005import squidpony.squidmath.Coord; 006import squidpony.squidmath.CoordPacker; 007import squidpony.squidmath.GWTRNG; 008import squidpony.squidmath.IRNG; 009import squidpony.squidmath.IStatefulRNG; 010import squidpony.squidmath.OrderedMap; 011import squidpony.squidmath.OrthoLine; 012import squidpony.squidmath.RegionMap; 013import squidpony.squidmath.StatefulRNG; 014 015import java.util.*; 016 017/** 018 * Generator for maps of high-tech areas like space stations or starships, with repeated modules laid out in random ways. 019 * Different from traditional fantasy dungeon generation in that it should seem generally less chaotic in how it's laid 020 * out, and repeated elements with minor tweaks should be especially common. May also be useful in fantasy games for 021 * regimented areas built by well-organized military forces. 022 * <br> 023 * This is marked Beta because both {@link MapModule} and ModularMapGenerator need improvement to be actually usable, 024 * but it might be a while before there's a clear pathway towards how they can be improved. 025 * <br> 026 * Preview: https://gist.github.com/tommyettinger/c711f8fc83fa9919245d88092444bf7f 027 * Created by Tommy Ettinger on 4/2/2016. 028 */ 029@Beta 030public class ModularMapGenerator implements IDungeonGenerator { 031 public DungeonUtility utility; 032 protected int height, width; 033 public IStatefulRNG rng; 034 protected long rebuildSeed; 035 036 protected char[][] map; 037 protected int[][] environment; 038 //public RegionMap<MapModule> layout, modules, inverseModules; 039 public RegionMap<MapModule> layout; 040 public OrderedMap<Integer, ArrayList<MapModule>> modules; 041 public OrderedMap<Coord, MapModule> displacement; 042 043 private void putModule(short[] module) { 044 char[][] unp = CoordPacker.unpackChar(module, '.', '#'); 045 MapModule mm = new MapModule(ArrayTools.insert(unp, 046 ArrayTools.fill('#', unp.length + 2, unp[0].length + 2), 1, 1)); 047 //short[] b = CoordPacker.rectangle(1 + mm.max.x, 1 + mm.max.y); 048 //modules.put(b, mm); 049 //inverseModules.put(CoordPacker.negatePacked(b), mm); 050 ArrayList<MapModule> mms = modules.get(mm.category); 051 if (mms == null) { 052 mms = new ArrayList<>(16); 053 mms.add(mm); 054 modules.put(mm.category, mms); 055 } else 056 mms.add(mm); 057 } 058 059 private void putRectangle(int width, int height, float multiplier) { 060 putModule(CoordPacker.rectangle(Math.round(width * multiplier), Math.round(height * multiplier))); 061 } 062 063 private void putCircle(int radius, float multiplier) { 064 putModule(CoordPacker.circle(Coord.get(Math.round(radius * multiplier + 1), Math.round(radius * multiplier + 1)), 065 Math.round(radius * multiplier), 066 Math.round((radius + 1) * 2 * multiplier + 1), Math.round((radius + 1) * 2 * multiplier + 1))); 067 } 068 069 private void initModules() { 070 layout = new RegionMap<>(64); 071 //modules = new RegionMap<>(64); 072 //inverseModules = new RegionMap<>(64); 073 modules = new OrderedMap<>(64); 074 for (int i = 1; i <= 64; i <<= 1) { 075 ArrayList<MapModule> mms = new ArrayList<>(16); 076 modules.put(i, mms); 077 } 078 displacement = new OrderedMap<>(64); 079 float multiplier = 1;//(float) Math.sqrt(Math.max(1f, Math.min(width, height) / 24f)); 080 putRectangle(2, 2, multiplier); 081 putRectangle(3, 3, multiplier); 082 putRectangle(4, 4, multiplier); 083 putRectangle(4, 2, multiplier); 084 putRectangle(2, 4, multiplier); 085 putRectangle(6, 6, multiplier); 086 putRectangle(6, 3, multiplier); 087 putRectangle(3, 6, multiplier); 088 putCircle(2, multiplier); 089 090 putRectangle(8, 8, multiplier); 091 putRectangle(6, 12, multiplier); 092 putRectangle(12, 6, multiplier); 093 putCircle(4, multiplier); 094 095 putRectangle(14, 14, multiplier); 096 putRectangle(9, 18, multiplier); 097 putRectangle(18, 9, multiplier); 098 putRectangle(14, 18, multiplier); 099 putRectangle(18, 14, multiplier); 100 putCircle(7, multiplier); 101 } 102 103 /** 104 * Make a ModularMapGenerator with a GWTRNG using a random seed, height 30, and width 60. 105 */ 106 public ModularMapGenerator() { 107 this(60, 30); 108 } 109 110 /** 111 * Make a ModularMapGenerator with the given height and width; the RNG used for generating a dungeon and 112 * adding features will be a GWTRNG using a random seed. 113 * 114 * @param width The width of the dungeon in cells 115 * @param height The height of the dungeon in cells 116 */ 117 public ModularMapGenerator(int width, int height) { 118 this(width, height, new GWTRNG()); 119 } 120 121 /** 122 * Make a ModularMapGenerator with the given height, width, and RNG. Use this if you want to seed the RNG. 123 * 124 * @param width The width of the dungeon in cells 125 * @param height The height of the dungeon in cells 126 * @param rng The RNG to use for all purposes in this class; if it is any kind of IStatefulRNG, then it will be 127 * used as-is; otherwise, a new GWTRNG will be used, randomly seeded by this parameter 128 */ 129 public ModularMapGenerator(int width, int height, IRNG rng) { 130 CoordPacker.init(); 131 this.rng = (rng instanceof IStatefulRNG) ? (IStatefulRNG) rng : new GWTRNG(rng.nextLong()); 132 utility = new DungeonUtility(this.rng); 133 rebuildSeed = this.rng.getState(); 134 this.height = height; 135 this.width = width; 136 map = new char[width][height]; 137 environment = new int[width][height]; 138 for (int x = 0; x < this.width; x++) { 139 Arrays.fill(map[x], '#'); 140 } 141 initModules(); 142 } 143 144 /** 145 * Copies all fields from copying and makes a new DungeonGenerator. 146 * 147 * @param copying the DungeonGenerator to copy 148 */ 149 public ModularMapGenerator(ModularMapGenerator copying) { 150 CoordPacker.init(); 151 rng = new StatefulRNG(copying.rng.getState()); 152 utility = new DungeonUtility(rng); 153 rebuildSeed = rng.getState(); 154 height = copying.height; 155 width = copying.width; 156 map = ArrayTools.copy(copying.map); 157 environment = ArrayTools.copy(copying.environment); 158 layout = new RegionMap<>(copying.layout); 159 modules = new OrderedMap<>(copying.modules); 160 displacement = new OrderedMap<>(copying.displacement); 161 } 162 163 /** 164 * Get the most recently generated char[][] map out of this class. The 165 * map may be null if generate() or setMap() have not been called. 166 * 167 * @return a char[][] map, or null. 168 */ 169 public char[][] getMap() { 170 return map; 171 } 172 /** 173 * Get the most recently generated char[][] map out of this class. The 174 * map may be null if generate() or setMap() have not been called. 175 * 176 * @return a char[][] map, or null. 177 */ 178 public char[][] getDungeon() { 179 return map; 180 } 181 182 /** 183 * Get the most recently generated char[][] map out of this class without any chars other than '#' or '.', for 184 * walls and floors respectively. The map may be null if generate() or setMap() have not been called. 185 * 186 * @return a char[][] map with only '#' for walls and '.' for floors, or null. 187 */ 188 public char[][] getBareMap() { 189 return DungeonUtility.simplifyDungeon(map); 190 } 191 192 public char[][] generate() { 193 MapModule mm, mm2; 194 int xPos, yPos, categorySize = 32, alteredSize = (categorySize * 3) >>> 1, bits = 5, ctr; 195 Coord[][] grid = new Coord[1][1]; 196 // find biggest category and drop down as many modules as we can fit 197 for (; categorySize >= 4; categorySize >>= 1, alteredSize = (categorySize * 3) >>> 1, bits--) { 198 if (width / alteredSize <= 0 || height / alteredSize <= 0) 199 continue; 200 grid = new Coord[width / alteredSize][height / alteredSize]; 201 ctr = 0; 202 for (int xLimit = alteredSize - 1, x = 0; xLimit < width; xLimit += alteredSize, x += alteredSize) { 203 for (int yLimit = alteredSize - 1, y = 0; yLimit < height; yLimit += alteredSize, y += alteredSize) { 204 if (layout.allAt(x + alteredSize / 2, y + alteredSize / 2).isEmpty()) // && (bits <= 3 || rng.nextInt(5) < bits) 205 { 206 if (rng.between(2, grid.length * grid[0].length + 3) == ctr++) 207 continue; 208 mm = rng.getRandomElement(modules.get(categorySize)); 209 if (mm == null) break; 210 mm = mm.rotate(rng.nextInt(4)); 211 xPos = rng.nextInt(3) << (bits - 2); 212 yPos = rng.nextInt(3) << (bits - 2); 213 for (int px = 0; px <= mm.max.x; px++) { 214 System.arraycopy(mm.map[px], 0, map[px + x + xPos], y + yPos, mm.max.y + 1); 215 System.arraycopy(mm.environment[px], 0, environment[px + x + xPos], y + yPos, mm.max.y + 1); 216 } 217 layout.put(CoordPacker.rectangle(x + xPos, y + yPos, categorySize, categorySize), mm); 218 displacement.put(Coord.get(x + xPos, y + yPos), mm); 219 grid[x / alteredSize][y / alteredSize] = Coord.get(x + xPos, y + yPos); 220 } 221 } 222 } 223 if (!layout.isEmpty()) 224 break; 225 } 226 Coord a, b; 227 int gw = grid.length; 228 if (gw > 0) { 229 int gh = grid[0].length; 230 for (int w = 0; w < gw; w++) { 231 for (int h = 0; h < gh; h++) { 232 a = grid[w][h]; 233 if (a == null) 234 continue; 235 int connectors = rng.nextInt(16) | rng.nextInt(16); 236 if ((connectors & 1) == 1 && w > 0 && grid[w - 1][h] != null) { 237 b = grid[w - 1][h]; 238 connectLeftRight(displacement.get(b), b.x, b.y, displacement.get(a), a.x, a.y); 239 } 240 if ((connectors & 2) == 2 && w < gw - 1 && grid[w + 1][h] != null) { 241 b = grid[w + 1][h]; 242 connectLeftRight(displacement.get(a), a.x, a.y, displacement.get(b), b.x, b.y); 243 } 244 if ((connectors & 4) == 4 && h > 0 && grid[w][h - 1] != null) { 245 b = grid[w][h - 1]; 246 connectTopBottom(displacement.get(b), b.x, b.y, displacement.get(a), a.x, a.y); 247 } 248 if ((connectors & 8) == 8 && h < gh - 1 && grid[w][h + 1] != null) { 249 b = grid[w][h + 1]; 250 connectTopBottom(displacement.get(a), a.x, a.y, displacement.get(b), b.x, b.y); 251 } 252 } 253 } 254 } 255 Coord begin; 256 short[] packed; 257 for (Map.Entry<Coord, MapModule> dmm : displacement.entrySet()) { 258 begin = dmm.getKey(); 259 mm = dmm.getValue(); 260 //int newCat = mm.category; 261 //if(newCat >= 16) newCat >>>= 1; 262 //if(newCat >= 8) newCat >>>= 1; 263 //mm2 = rng.getRandomElement(modules.get(newCat)); 264 int shiftsX = begin.x - (mm.category * 3 / 2) * ((begin.x * 2) / (3 * mm.category)), 265 shiftsY = begin.y - (mm.category * 3 / 2) * ((begin.y * 2) / (3 * mm.category)), 266 leftSize = Integer.highestOneBit(shiftsX), 267 rightSize = Integer.highestOneBit((mm.category >>> 1) - shiftsX), 268 topSize = Integer.highestOneBit(shiftsY), 269 bottomSize = Integer.highestOneBit((mm.category >>> 1) - shiftsY); 270 if (leftSize >= 4 && !mm.leftDoors.isEmpty()) { 271 mm2 = rng.getRandomElement(modules.get(leftSize)); 272 if (mm2 == null) continue; 273 if (mm2.rightDoors.isEmpty()) { 274 if (!mm2.topDoors.isEmpty()) 275 mm2 = mm2.rotate(1); 276 else if (!mm2.leftDoors.isEmpty()) 277 mm2 = mm2.flip(true, false); 278 else if (!mm2.bottomDoors.isEmpty()) 279 mm2 = mm2.rotate(3); 280 else continue; 281 } 282 for (int i = 0; i < 4; i++) { 283 packed = CoordPacker.rectangle(begin.x - shiftsX, begin.y + i * mm.category / 4, leftSize, leftSize); 284 if (layout.containsRegion(packed)) 285 continue; 286 for (int px = 0; px <= mm2.max.x; px++) { 287 System.arraycopy(mm2.map[px], 0, map[px + begin.x - shiftsX], begin.y + i * mm.category / 4, mm2.max.y + 1); 288 System.arraycopy(mm2.environment[px], 0, environment[px + begin.x - shiftsX], begin.y + i * mm.category / 4, mm2.max.y + 1); 289 } 290 layout.put(packed, mm2); 291 connectLeftRight(mm2, begin.x - shiftsX, begin.y + i * mm.category / 4, mm, begin.x, begin.y); 292 } 293 } 294 if (rightSize >= 4 && !mm.rightDoors.isEmpty()) { 295 mm2 = rng.getRandomElement(modules.get(rightSize)); 296 if (mm2 == null) continue; 297 if (mm2.leftDoors.isEmpty()) { 298 if (!mm2.bottomDoors.isEmpty()) 299 mm2 = mm2.rotate(1); 300 else if (!mm2.rightDoors.isEmpty()) 301 mm2 = mm2.flip(true, false); 302 else if (!mm2.topDoors.isEmpty()) 303 mm2 = mm2.rotate(3); 304 else continue; 305 } 306 for (int i = 0; i < 4; i++) { 307 packed = CoordPacker.rectangle(begin.x + mm.category, begin.y + i * mm.category / 4, rightSize, rightSize); 308 if (layout.containsRegion(packed)) 309 continue; 310 for (int px = 0; px <= mm2.max.x; px++) { 311 System.arraycopy(mm2.map[px], 0, map[px + begin.x + mm.category], begin.y + i * mm.category / 4, mm2.max.y + 1); 312 System.arraycopy(mm2.environment[px], 0, environment[px + begin.x + mm.category], begin.y + i * mm.category / 4, mm2.max.y + 1); 313 } 314 layout.put(packed, mm2); 315 connectLeftRight(mm, begin.x, begin.y, mm2, begin.x + mm.category, begin.y + i * mm.category / 4); 316 } 317 } 318 if (topSize >= 4 && !mm.topDoors.isEmpty()) { 319 mm2 = rng.getRandomElement(modules.get(topSize)); 320 if (mm2 == null) continue; 321 if (mm2.bottomDoors.isEmpty()) { 322 if (!mm2.leftDoors.isEmpty()) 323 mm2 = mm2.rotate(3); 324 else if (!mm2.topDoors.isEmpty()) 325 mm2 = mm2.flip(false, true); 326 else if (!mm2.rightDoors.isEmpty()) 327 mm2 = mm2.rotate(1); 328 else continue; 329 } 330 for (int i = 0; i < 4; i++) { 331 packed = CoordPacker.rectangle(begin.x + i * mm.category / 4, begin.y - shiftsY, topSize, topSize); 332 if (layout.containsRegion(packed)) 333 continue; 334 for (int px = 0; px <= mm2.max.x; px++) { 335 System.arraycopy(mm2.map[px], 0, map[px + begin.x + i * mm.category / 4], begin.y - shiftsY, mm2.max.y + 1); 336 System.arraycopy(mm2.environment[px], 0, environment[px + begin.x + i * mm.category / 4], begin.y - shiftsY, mm2.max.y + 1); 337 } 338 layout.put(packed, mm2); 339 connectTopBottom(mm2, begin.x + i * mm.category / 4, begin.y - shiftsY, mm, begin.x, begin.y); 340 } 341 } 342 if (bottomSize >= 4 && !mm.bottomDoors.isEmpty()) { 343 mm2 = rng.getRandomElement(modules.get(bottomSize)); 344 if (mm2 == null) continue; 345 if (mm2.topDoors.isEmpty()) { 346 if (!mm2.rightDoors.isEmpty()) 347 mm2 = mm2.rotate(1); 348 else if (!mm2.leftDoors.isEmpty()) 349 mm2 = mm2.rotate(3); 350 else continue; 351 } 352 for (int i = 0; i < 4; i++) { 353 packed = CoordPacker.rectangle(begin.x + i * mm.category / 4, begin.y + mm.category, bottomSize, bottomSize); 354 if (layout.containsRegion(packed)) 355 continue; 356 for (int px = 0; px <= mm2.max.x; px++) { 357 System.arraycopy(mm2.map[px], 0, map[px + begin.x + i * mm.category / 4], begin.y + mm.category, mm2.max.y + 1); 358 System.arraycopy(mm2.environment[px], 0, environment[px + begin.x + i * mm.category / 4], begin.y + mm.category, mm2.max.y + 1); 359 } 360 layout.put(packed, mm2); 361 connectTopBottom(mm, begin.x, begin.y, mm2, begin.x + i * mm.category / 4, begin.y + mm.category); 362 } 363 } 364 } 365 return map; 366 } 367 368 /** 369 * Change the underlying char[][]; only affects the toString method, and of course getMap 370 * 371 * @param map a char[][], probably produced by an earlier call to this class and then modified. 372 */ 373 public void setMap(char[][] map) { 374 this.map = map; 375 if (map == null) { 376 width = 0; 377 height = 0; 378 return; 379 } 380 width = map.length; 381 if (width > 0) 382 height = map[0].length; 383 } 384 385 /** 386 * Height of the map in cells. 387 * 388 * @return Height of the map in cells. 389 */ 390 public int getHeight() { 391 return height; 392 } 393 394 /** 395 * Width of the map in cells. 396 * 397 * @return Width of the map in cells. 398 */ 399 public int getWidth() { 400 return width; 401 } 402 403 /** 404 * Gets the environment int 2D array for use with classes like RoomFinder. 405 * 406 * @return the environment int 2D array 407 */ 408 public int[][] getEnvironment() { 409 return environment; 410 } 411 412 /** 413 * Sets the environment int 2D array. 414 * 415 * @param environment a 2D array of int, where each int corresponds to a constant in MixedGenerator. 416 */ 417 public void setEnvironment(int[][] environment) { 418 this.environment = environment; 419 } 420 421 private void connectLeftRight(MapModule left, int leftX, int leftY, MapModule right, int rightX, int rightY) { 422 if (left.rightDoors == null || left.rightDoors.isEmpty() 423 || right.leftDoors == null || right.leftDoors.isEmpty()) 424 return; 425 List<Coord> line = new ArrayList<>(1), temp; 426 int best = 1024; 427 Coord tl, tr, twl, twr, wl = null, wr = null; 428 for (Coord l : left.rightDoors) { 429 tl = twl = l.translate(leftX, leftY); 430 if (tl.x > 0 && tl.x < width - 1 && map[tl.x - 1][tl.y] != '#') 431 tl = Coord.get(tl.x + 1, tl.y); 432 else if (tl.x > 0 && tl.x < width - 1 && map[tl.x + 1][tl.y] != '#') 433 tl = Coord.get(tl.x - 1, tl.y); 434 else if (tl.y > 0 && tl.y < height - 1 && map[tl.x][tl.y - 1] != '#') 435 tl = Coord.get(tl.x, tl.y + 1); 436 else if (tl.y > 0 && tl.y < height - 1 && map[tl.x][tl.y + 1] != '#') 437 tl = Coord.get(tl.x, tl.y - 1); 438 else 439 continue; 440 441 for (Coord r : right.leftDoors) { 442 tr = twr = r.translate(rightX, rightY); 443 if (tr.x > 0 && tr.x < width - 1 && map[tr.x - 1][tr.y] != '#') 444 tr = Coord.get(tr.x + 1, tr.y); 445 else if (tr.x > 0 && tr.x < width - 1 && map[tr.x + 1][tr.y] != '#') 446 tr = Coord.get(tr.x - 1, tr.y); 447 else if (tr.y > 0 && tr.y < height - 1 && map[tr.x][tr.y - 1] != '#') 448 tr = Coord.get(tr.x, tr.y + 1); 449 else if (tr.y > 0 && tr.y < height - 1 && map[tr.x][tr.y + 1] != '#') 450 tr = Coord.get(tr.x, tr.y - 1); 451 else 452 continue; 453 temp = OrthoLine.line(tl, tr); 454 if (temp.size() < best) { 455 line = temp; 456 best = line.size(); 457 wl = twl; 458 wr = twr; 459 } 460 } 461 } 462 temp = new ArrayList<>(line.size()); 463 for (Coord c : line) { 464 if (map[c.x][c.y] == '#') { 465 map[c.x][c.y] = '.'; 466 environment[c.x][c.y] = DungeonUtility.CORRIDOR_FLOOR; 467 temp.add(c); 468 } 469 } 470 if (wl != null && map[wl.x][wl.y] == '#') { 471 //if(line.isEmpty()) 472 map[wl.x][wl.y] = '.'; 473 environment[wl.x][wl.y] = DungeonUtility.ROOM_FLOOR; 474 //else 475 // map[wl.x][wl.y] = '+'; 476 } 477 if (wr != null && map[wr.x][wr.y] == '#') { 478 //if(line.isEmpty()) 479 map[wr.x][wr.y] = '.'; 480 environment[wr.x][wr.y] = DungeonUtility.ROOM_FLOOR; 481 //else 482 // map[wr.x][wr.y] = '+'; 483 } 484 layout.put(CoordPacker.packSeveral(temp), null); 485 486 } 487 488 private void connectTopBottom(MapModule top, int topX, int topY, MapModule bottom, int bottomX, int bottomY) { 489 if (top.bottomDoors == null || top.bottomDoors.isEmpty() 490 || bottom.topDoors == null || bottom.topDoors.isEmpty()) 491 return; 492 List<Coord> line = new ArrayList<>(1), temp; 493 int best = 1024; 494 Coord tt, tb, twt, twb, wt = null, wb = null; 495 for (Coord l : top.bottomDoors) { 496 tt = twt = l.translate(topX, topY); 497 if (tt.y > 0 && tt.y < height - 1 && map[tt.x][tt.y - 1] != '#') 498 tt = Coord.get(tt.x, tt.y + 1); 499 else if (tt.y > 0 && tt.y < height - 1 && map[tt.x][tt.y + 1] != '#') 500 tt = Coord.get(tt.x, tt.y - 1); 501 else if (tt.x > 0 && tt.x < width - 1 && map[tt.x - 1][tt.y] != '#') 502 tt = Coord.get(tt.x + 1, tt.y); 503 else if (tt.x > 0 && tt.x < width - 1 && map[tt.x + 1][tt.y] != '#') 504 tt = Coord.get(tt.x - 1, tt.y); 505 else 506 continue; 507 508 for (Coord r : bottom.topDoors) { 509 tb = twb = r.translate(bottomX, bottomY); 510 if (tb.y > 0 && tb.y < height - 1 && map[tb.x][tb.y - 1] != '#') 511 tb = Coord.get(tb.x, tb.y + 1); 512 else if (tb.y > 0 && tb.y < height - 1 && map[tb.x][tb.y + 1] != '#') 513 tb = Coord.get(tb.x, tb.y - 1); 514 else if (tb.x > 0 && tb.x < width - 1 && map[tb.x - 1][tb.y] != '#') 515 tb = Coord.get(tb.x + 1, tb.y); 516 else if (tb.x > 0 && tb.x < width - 1 && map[tb.x + 1][tb.y] != '#') 517 tb = Coord.get(tb.x - 1, tb.y); 518 else 519 continue; 520 temp = OrthoLine.line(tt, tb); 521 if (temp.size() < best) { 522 line = temp; 523 best = line.size(); 524 wt = twt; 525 wb = twb; 526 } 527 } 528 } 529 temp = new ArrayList<>(line.size()); 530 for (Coord c : line) { 531 if (map[c.x][c.y] == '#') { 532 map[c.x][c.y] = '.'; 533 environment[c.x][c.y] = DungeonUtility.CORRIDOR_FLOOR; 534 temp.add(c); 535 } 536 } 537 if (wt != null && map[wt.x][wt.y] == '#') { 538 //if(line.isEmpty()) 539 map[wt.x][wt.y] = '.'; 540 environment[wt.x][wt.y] = DungeonUtility.ROOM_FLOOR; 541 //else 542 // map[wl.x][wl.y] = '+'; 543 544 } 545 if (wb != null && map[wb.x][wb.y] == '#') { 546 //if(line.isEmpty()) 547 map[wb.x][wb.y] = '.'; 548 environment[wb.x][wb.y] = DungeonUtility.ROOM_FLOOR; 549 //else 550 // map[wb.x][wb.y] = '+'; 551 } 552 layout.put(CoordPacker.packSeveral(temp), null); 553 } 554}