package Roguelike.DungeonGeneration.RoomGenerators;
import java.util.Random;
import Roguelike.Global;
import Roguelike.Global.Direction;
import Roguelike.DungeonGeneration.DungeonFileParser;
import Roguelike.DungeonGeneration.Symbol;
import Roguelike.Tiles.Point;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.XmlReader.Element;
/*
* Builds a 'burrow' - that is the organic looking patterns
* discovered by Kusigrosz and documented in this thread
* http://groups.google.com/group/rec.games.roguelike.development/browse_thread/thread/4c56271970c253bf
*/
/* Arguments:
* ngb_min, ngb_max: the minimum and maximum number of neighbouring
* floor cells that a wall cell must have to become a floor cell.
* 1 <= ngb_min <= 3; ngb_min <= ngb_max <= 8;
* connchance: the chance (in percent) that a new connection is
* allowed; for ngb_max == 1 this has no effect as any
* connecting cell must have 2 neighbours anyway.
* cellnum: the maximum number of floor cells that will be generated.
* The default values of the arguments are defined below.
*
* Algorithm description:
* The algorithm operates on a rectangular grid. Each cell can be 'wall'
* or 'floor'. A (non-border) cell has 8 neigbours - diagonals count.
* There is also a cell store with two operations: store a given cell on
* top, and pull a cell from the store. The cell to be pulled is selected
* randomly from the store if N_cells_in_store < 125, and from the top
* 25 * cube_root(N_cells_in_store) otherwise. There is no check for
* repetitions, so a given cell can be stored multiple times.
*
* The algorithm starts with most of the map filled with 'wall', with a
* "seed" of some floor cells; their neigbouring wall cells are in store.
* The main loop in delveon() is repeated until the desired number of
* floor cells is achieved, or there is nothing in store:
* 1) Get a cell from the store;
* Check the conditions:
* a) the cell has between ngb_min and ngb_max floor neighbours,
* b) making it a floor cell won't open new connections,
* or the RNG allows it with connchance/100 chance.
* if a) and b) are met, the cell becomes floor, and its wall
* neighbours are put in store in random order.
* There are many variants possible, for example:
* 1) picking the cell in rndpull() always from the whole store makes
* compact patterns;
* 2) storing the neighbours in digcell() clockwise starting from
* a random one, and picking the bottom cell in rndpull() creates
* meandering or spiral patterns.
*/
public class Burrow extends AbstractRoomGenerator
{
private float floorCoverage;
private float connectionChance;
private Array<Point> tempArray = new Array<Point>();
private boolean canPlace( Symbol[][] grid, Symbol floor, Symbol wall, Random ran, Point p )
{
return true;
}
private Point getCellFromStore( Array<Point> cellStore, Random ran )
{
int range = cellStore.size;
if ( cellStore.size > 125 )
{
range = (int) ( 25 * Math.pow( cellStore.size, 1.0f / 3.0f ) );
}
int index = ran.nextInt( range );
Point p = cellStore.removeIndex( cellStore.size - index - 1 );
return p;
}
private void addNeighboursToCellStore( Symbol[][] grid, Symbol wall, Point point, Array<Point> cellStore )
{
for ( Direction dir : Direction.values() )
{
int nx = point.x + dir.getX();
int ny = point.y + dir.getY();
if ( nx < 0 || ny < 0 || nx >= grid.length || ny >= grid[0].length )
{
continue;
}
if ( grid[nx][ny] == wall )
{
cellStore.add( Global.PointPool.obtain().set( nx, ny ) );
}
}
}
private Point placeNewSeed( Symbol[][] grid, Symbol floor, Symbol wall, Random ran )
{
int width = grid.length;
int height = grid[0].length;
for ( int x = 0; x < width; x++ )
{
for ( int y = 0; y < height; y++ )
{
if ( grid[x][y] == wall )
{
Point pos = Global.PointPool.obtain().set( x, y );
tempArray.add( pos );
}
}
}
Point chosen = tempArray.get( ran.nextInt( tempArray.size ) ).copy();
grid[chosen.x][chosen.y] = floor;
Global.PointPool.freeAll( tempArray );
tempArray.clear();
return chosen;
}
@Override
public void process( Symbol[][] grid, Symbol floor, Symbol wall, Random ran, DungeonFileParser dfp )
{
Array<Point> cellStore = new Array<Point>( false, 16 );
int placedCount = 0;
int width = grid.length;
int height = grid[0].length;
int targetTileCount = (int) ( width * height * floorCoverage );
// Place seed tiles
for ( int i = 0; i < 8; i++ )
{
Point seed = placeNewSeed( grid, floor, wall, ran );
placedCount++;
addNeighboursToCellStore( grid, wall, seed, cellStore );
Global.PointPool.free(seed);
}
// place tiles
while ( placedCount < targetTileCount )
{
if ( cellStore.size == 0 )
{
Point seed = placeNewSeed( grid, floor, wall, ran );
placedCount++;
addNeighboursToCellStore( grid, wall, seed, cellStore );
Global.PointPool.free(seed);
}
else
{
Point p = getCellFromStore( cellStore, ran );
if ( canPlace( grid, floor, wall, ran, p ) )
{
grid[p.x][p.y] = floor;
placedCount++;
addNeighboursToCellStore( grid, wall, p, cellStore );
Global.PointPool.free(p);
}
}
}
}
@Override
public void parse( Element xml )
{
floorCoverage = xml.getFloat( "FloorCoverage", 0.6f );
connectionChance = xml.getFloat( "ConnectionChance", 0.2f );
}
}