package squidpony.squidgrid.mapping;
import squidpony.ArrayTools;
import squidpony.squidmath.Coord;
import squidpony.squidmath.CoordPacker;
import squidpony.squidmath.PoissonDisk;
import squidpony.squidmath.RNG;
import java.util.ArrayList;
/**
* Map generator that constructs a large number of overlapping rectangular rooms.
* Meant for the kind of crowded architecture that might fit the dungeon equivalent of urban areas.
* Likely to have many dead-end sections with only one door-like area, similar to hotel rooms or closets depending on
* size, and the distance required to reach a particular room may be... cruel and/or unusual. Whole winding areas of a
* large map may only be accessible from outside by one door, for instance.
* <br>
* An example of what this outputs:
* https://gist.github.com/tommyettinger/3144b56a3a8e5bbe5ee401c1a93989f4
* Created by Tommy Ettinger on 5/4/2016.
*/
public class DenseRoomMapGenerator {
public char[][] map;
public int[][] environment;
public RNG rng;
protected int width, height;
public DenseRoomMapGenerator()
{
this(80, 30, new RNG());
}
public DenseRoomMapGenerator(int width, int height)
{
this(width, height, new RNG());
}
public DenseRoomMapGenerator(int width, int height, RNG rng)
{
this.rng = rng;
this.width = Math.max(3, width);
this.height = Math.max(3, height);
map = ArrayTools.fill('#', this.width, this.height);
environment = new int[this.width][this.height];
}
/**
* Generate a map as a 2D char array using the width and height specified in the constructor.
* Should produce a crowded arrangement of rectangular rooms that overlap with each other.
* @return a 2D char array for the map of densely-packed rectangular rooms.
*/
public char[][] generate()
{
//ArrayList<short[]> regions = new ArrayList<>();
short[] tempPacked;
int ctr = 0, nh, nw, nx, ny, hnw, hnh, maxw = 8 + width / 10, maxh = 8 + height / 10;
ArrayList<Coord> sampled = PoissonDisk.sampleRectangle(Coord.get(1, 1), Coord.get(width - 2, height - 2),
6.5f * width * height / 5000f, width, height, 35, rng);
sampled.addAll(PoissonDisk.sampleRectangle(Coord.get(1, 1), Coord.get(width - 2, height - 2),
8.5f * width * height / 5000f, width, height, 40, rng));
for(Coord center : sampled) {
nw = rng.between(4, maxw);
nh = rng.between(4, maxh);
hnw = (nw + 1) / 2;
hnh = (nh + 1) / 2;
nx = Math.max(0, Math.min(width - 2 - hnw, center.x - hnw));
ny = Math.max(0, Math.min(height - 2 - hnh, center.y - hnh));
if (center.x - hnw != nx)
nw -= Math.abs(center.x - hnw - nx);
if (center.y - hnh != ny)
nh -= Math.abs(center.y - hnh - ny);
if (nw >= 0 && nh >= 0) {
ArrayTools.insert(DungeonUtility.wallWrap(ArrayTools.fill('.', nw, nh)),
map, nx, ny);
//regions.add(CoordPacker.rectangle(nx, ny, nw, nh));
}
}
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
environment[x][y] = (map[x][y] == '.') ? MixedGenerator.ROOM_FLOOR : MixedGenerator.ROOM_WALL;
}
}
tempPacked = CoordPacker.intersectPacked(
CoordPacker.rectangle(1, 1, width - 2, height - 2),
CoordPacker.pack(map, '#'));
Coord[] holes = CoordPacker.randomSeparated(tempPacked, 3, rng);
for(Coord hole : holes) {
if (hole.x > 0 && hole.y > 0 && hole.x < width - 1 && hole.y < height - 1 &&
((map[hole.x - 1][hole.y] == '.' && map[hole.x + 1][hole.y] == '.') ||
(map[hole.x][hole.y - 1] == '.' && map[hole.x][hole.y + 1] == '.'))) {
map[hole.x][hole.y] = '.';
environment[hole.x][hole.y] = MixedGenerator.CORRIDOR_FLOOR;
}
}
/*
regions = rng.shuffle(regions);
while (regions.size() > 1)
{
region = regions.remove(0);
linking = regions.get(0);
start = CoordPacker.singleRandom(region, rng);
end = CoordPacker.singleRandom(linking, rng);
path = OrthoLine.line(start, end);
for(Coord elem : path)
{
if(elem.x < width && elem.y < height) {
if (map[elem.x][elem.y] == '#') {
map[elem.x][elem.y] = '.';
environment[elem.x][elem.y] = MixedGenerator.CORRIDOR_FLOOR;
ctr++;
}
}
}
}
*/
int upperY = height - 1;
int upperX = width - 1;
for (int i = 0; i < width; i++) {
map[i][0] = '#';
map[i][upperY] = '#';
environment[i][0] = MixedGenerator.UNTOUCHED;
environment[i][upperY] = MixedGenerator.UNTOUCHED;
}
for (int i = 0; i < height; i++) {
map[0][i] = '#';
map[upperX][i] = '#';
environment[0][i] = MixedGenerator.UNTOUCHED;
environment[upperX][i] = MixedGenerator.UNTOUCHED;
}
return map;
}
/**
* Gets a 2D array of int constants, each representing a type of environment corresponding to a static field of
* MixedGenerator. This array will have the same size as the last char 2D array produced by generate(); the value
* of this method if called before generate() is undefined, but probably will be a 2D array of all 0 (UNTOUCHED).
* <ul>
* <li>MixedGenerator.UNTOUCHED, equal to 0, is used for any cells that aren't near a floor.</li>
* <li>MixedGenerator.ROOM_FLOOR, equal to 1, is used for floor cells inside wide room areas.</li>
* <li>MixedGenerator.ROOM_WALL, equal to 2, is used for wall cells around wide room areas.</li>
* <li>MixedGenerator.CAVE_FLOOR, equal to 3, is used for floor cells inside rough cave areas.</li>
* <li>MixedGenerator.CAVE_WALL, equal to 4, is used for wall cells around rough cave areas.</li>
* <li>MixedGenerator.CORRIDOR_FLOOR, equal to 5, is used for floor cells inside narrow corridor areas.</li>
* <li>MixedGenerator.CORRIDOR_WALL, equal to 6, is used for wall cells around narrow corridor areas.</li>
* </ul>
* @return a 2D int array where each element is an environment type constant in MixedGenerator
*/
public int[][] getEnvironment()
{
return environment;
}
}