package Roguelike.DungeonGeneration.RoomGenerators; import java.util.Random; import Roguelike.Global.Direction; import Roguelike.DungeonGeneration.DungeonFileParser; import Roguelike.DungeonGeneration.Symbol; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.XmlReader.Element; /** * Seperates the room via Binary Space partitioning, then places doors to * connect the branches of the tree. * * @author Philip Collin * */ public class Chambers extends AbstractRoomGenerator { public Chambers() { this.ensuresConnectivity = true; } public static class BSPTree { public int x; public int y; public int width; public int height; public BSPTree( int x, int y, int width, int height ) { this.x = x; this.y = y; this.width = width; this.height = height; } public BSPTree child1; public BSPTree child2; public boolean splitVertically; private static final int minSize = 5; private static final int maxSize = 12; public void partition( Random ran ) { if ( ( width < minSize * 2 && height < minSize * 2 ) || ( width < maxSize && height < maxSize && ran.nextInt( 5 ) == 0 ) ) { } else if ( width < minSize * 2 ) { float split = 0.3f + ran.nextFloat() * 0.4f; int splitheight = (int) ( height * split ); child1 = new BSPTree( x, y, width, splitheight ); child2 = new BSPTree( x, y + splitheight, width, height - splitheight ); splitVertically = true; child1.partition( ran ); child2.partition( ran ); } else if ( height < minSize * 2 ) { float split = 0.3f + ran.nextFloat() * 0.4f; int splitwidth = (int) ( width * split ); child1 = new BSPTree( x, y, splitwidth, height ); child2 = new BSPTree( x + splitwidth, y, width - splitwidth, height ); splitVertically = false; child1.partition( ran ); child2.partition( ran ); } else { boolean vertical = ran.nextBoolean(); if ( vertical ) { float split = 0.3f + ran.nextFloat() * 0.4f; int splitwidth = (int) ( width * split ); child1 = new BSPTree( x, y, splitwidth, height ); child2 = new BSPTree( x + splitwidth, y, width - splitwidth, height ); splitVertically = false; child1.partition( ran ); child2.partition( ran ); } else { float split = 0.3f + ran.nextFloat() * 0.4f; int splitheight = (int) ( height * split ); child1 = new BSPTree( x, y, width, splitheight ); child2 = new BSPTree( x, y + splitheight, width, height - splitheight ); splitVertically = true; child1.partition( ran ); child2.partition( ran ); } } } private void placeDoor( int[][] grid, Random ran ) { int gridWidth = grid.length; int gridHeight = grid[0].length; Array<int[]> possibleDoorTiles = new Array<int[]>(); if ( splitVertically ) { for ( int ix = 0; ix < width; ix++ ) { int tx = x + ix; int ty = child2.y; boolean valid = true; if ( valid ) { int ttx = tx; int tty = ty - 1; if ( tty >= 0 && grid[ttx][tty] != 1 ) { valid = false; } } if ( valid ) { int ttx = tx; int tty = ty + 1; if ( tty < gridHeight && grid[ttx][tty] != 1 ) { valid = false; } } if ( valid ) { possibleDoorTiles.add( new int[] { tx, ty } ); } } } else { for ( int iy = 0; iy < height; iy++ ) { int tx = child2.x; int ty = y + iy; boolean valid = true; if ( valid ) { int ttx = tx - 1; int tty = ty; if ( ttx >= 0 && grid[ttx][tty] != 1 ) { valid = false; } } if ( valid ) { int ttx = tx + 1; int tty = ty; if ( ttx < gridWidth && grid[ttx][tty] != 1 ) { valid = false; } } if ( valid ) { possibleDoorTiles.add( new int[] { tx, ty } ); } } } int[] doorPos = possibleDoorTiles.size > 0 ? possibleDoorTiles.removeIndex( ran.nextInt( possibleDoorTiles.size ) ) : null; if ( doorPos != null ) { grid[doorPos[0]][doorPos[1]] = 2; } } public void dig( int[][] grid, Random ran ) { if ( child1 != null ) { child1.dig( grid, ran ); child2.dig( grid, ran ); placeDoor( grid, ran ); } else { for ( int ix = 1; ix < width; ix++ ) { for ( int iy = 1; iy < height; iy++ ) { grid[x + ix][y + iy] = 1; } } } } } @Override public void process( Symbol[][] grid, Symbol floor, Symbol wall, Random ran, DungeonFileParser dfp ) { Symbol door = dfp.getSymbol( '+' ); int[][] outGrid = null; while ( true ) { BSPTree tree = new BSPTree( 0, 0, grid.length - 1, grid[0].length - 1 ); if (tree.child1 == null) { tree.partition( ran ); } outGrid = new int[grid.length][grid[0].length]; tree.dig( outGrid, ran ); if ( isConnected( outGrid ) ) { break; } System.out.println( "Failed to connect all chambers. Retrying" ); } for ( int x = 0; x < grid.length; x++ ) { for ( int y = 0; y < grid[0].length; y++ ) { if ( outGrid[x][y] == 0 ) { grid[x][y] = wall; } else if ( outGrid[x][y] == 1 ) { grid[x][y] = floor; } else if ( outGrid[x][y] == 2 ) { grid[x][y] = door; } System.out.print( "" + grid[x][y] ); } System.out.print( "\n" ); } } // ---------------------------------------------------------------------- private boolean isConnected( int[][] grid ) { int width = grid.length; int height = grid[0].length; boolean[][] reached = new boolean[width][height]; int x = 0; int y = 0; outer: for ( x = 0; x < width; x++ ) { for ( y = 0; y < height; y++ ) { if ( grid[x][y] >= 1 ) { break outer; } } } Array<int[]> toBeProcessed = new Array<int[]>(); toBeProcessed.add( new int[] { x, y } ); while ( toBeProcessed.size > 0 ) { int[] point = toBeProcessed.pop(); x = point[0]; y = point[1]; if ( reached[x][y] ) { continue; } reached[x][y] = true; for ( Direction dir : Direction.values() ) { int nx = x + dir.getX(); int ny = y + dir.getY(); if ( nx >= 0 && ny >= 0 && nx < width && ny < height && grid[nx][ny] >= 1 ) { toBeProcessed.add( new int[] { nx, ny } ); } } } for ( x = 0; x < width; x++ ) { for ( y = 0; y < height; y++ ) { if ( grid[x][y] >= 1 && !reached[x][y] ) { return false; } } } return true; } @Override public void parse( Element xml ) { } }