package Roguelike.DungeonGeneration;
import java.util.*;
import PaulChew.Pnt;
import PaulChew.Triangle;
import PaulChew.Triangulation;
import Roguelike.Global;
import Roguelike.Global.Direction;
import Roguelike.Global.Passability;
import Roguelike.DungeonGeneration.DungeonFileParser.CorridorFeature.PlacementMode;
import Roguelike.DungeonGeneration.DungeonFileParser.CorridorStyle.PathStyle;
import Roguelike.DungeonGeneration.Room.RoomDoor;
import Roguelike.Pathfinding.AStarPathfind;
import Roguelike.Pathfinding.PathfindingTile;
import Roguelike.Save.SaveLevel;
import Roguelike.Tiles.Point;
import Roguelike.Util.EnumBitflag;
import Roguelike.Util.ImageUtils;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.utils.Array;
public class RecursiveDockGenerator extends AbstractDungeonGenerator
{
// ####################################################################//
// region Constructor
// ----------------------------------------------------------------------
public RecursiveDockGenerator()
{
}
// endregion Constructor
// ####################################################################//
// region Public Methods
// ----------------------------------------------------------------------
// Designed to be called until returns true
@Override
public boolean generate()
{
if ( generationIndex == 0 )
{
selectRooms();
generationIndex++;
generationText = "Partitioning Grid";
}
else if ( generationIndex == 1 )
{
toBePlaced.clear();
placedRooms.clear();
toBePlaced.addAll( requiredRooms );
for ( Room room : toBePlaced )
{
room.revertChanges( ran, dfp );
}
fillGridBase();
partition( );
if ( toBePlaced.size == 0 )
{
generationIndex++;
generationText = "Finding Doors";
}
else
{
width += 10;
height += 10;
System.out.println( "Failed to place all rooms. Increasing size to " + width + "," + height + "and retrying" );
}
}
else if ( generationIndex == 2 )
{
for ( Room room : placedRooms )
{
room.findDoors( ran, dfp );
}
generationIndex++;
generationText = "Marking Rooms";
}
else if ( generationIndex == 3 )
{
markRooms();
generationIndex++;
generationText = "Filling empty space";
}
else if ( generationIndex == 4 )
{
identifyAndFillEmptySpaces();
generationIndex++;
generationText = "Connecting Rooms";
}
else if ( generationIndex == 5 )
{
connectRooms();
generationIndex++;
generationText = "Placing Factions";
if ( DEBUG_OUTPUT )
{
Symbol[][] symbolGrid = new Symbol[width][height];
for ( int x = 0; x < width; x++ )
{
for ( int y = 0; y < height; y++ )
{
symbolGrid[x][y] = tiles[x][y].symbol;
}
}
if ( !Global.ANDROID )
{
ImageUtils.writeSymbolGridToFile( symbolGrid, "beforeFeatures.png", DEBUG_SIZE );
}
}
}
else if ( generationIndex == 6 )
{
placeFactions();
generationIndex++;
generationText = "Marking Rooms Again?";
}
else if ( generationIndex == 7 )
{
markRooms();
generationIndex++;
generationText = "Building level";
}
else if ( generationIndex == 8 )
{
// flatten to symbol grid
Symbol[][] symbolGrid = new Symbol[width][height];
for ( int x = 0; x < width; x++ )
{
for ( int y = 0; y < height; y++ )
{
symbolGrid[x][y] = tiles[x][y].symbol;
}
}
level = createLevel( symbolGrid, dfp.getSymbol( '#' ) );
generationIndex++;
generationText = "Completed";
}
percent = (int) ( ( 100.0f / 8.0f ) * generationIndex );
if ( generationIndex < 9 )
{
return false;
}
else
{
return true;
}
}
// ----------------------------------------------------------------------
@Override
public void setup( SaveLevel level, DungeonFileParser dfp )
{
this.saveLevel = level;
this.dfp = dfp;
width = dfp.minWidth;
height = dfp.minHeight;
minPadding = ( dfp.corridorStyle.width / 2 ) + 1;
maxPadding += minPadding;
paddedMinRoom = minRoomSize + minPadding * 2;
ran = new Random( level.seed );
}
// endregion Public Methods
// ####################################################################//
// region Private Methods
// ----------------------------------------------------------------------
protected void fillGridBase()
{
Symbol floor = dfp.sharedSymbolMap.get( '.' );
Symbol wall = dfp.sharedSymbolMap.get( '#' );
floor.resolveExtends( dfp.sharedSymbolMap );
wall.resolveExtends( dfp.sharedSymbolMap );
this.tiles = new GenerationTile[width][height];
for ( int x = 0; x < width; x++ )
{
for ( int y = 0; y < height; y++ )
{
tiles[x][y] = new GenerationTile( x, y );
if ( dfp.corridorStyle.pathStyle == PathStyle.STRAIGHT )
{
tiles[x][y].influence = width + height;
}
else if ( dfp.corridorStyle.pathStyle == PathStyle.WANDERING )
{
tiles[x][y].influence = ran.nextInt( ( width + height ) / 2 ) + ( width + height ) / 2;
}
if ( x == 0 || y == 0 || x == width - 1 || y == height - 1 )
{
tiles[x][y].passable = false;
}
else
{
tiles[x][y].passable = true;
}
tiles[x][y].symbol = wall;
}
}
if ( dfp.preprocessor != null )
{
Symbol[][] symbolGrid = new Symbol[width][height];
for ( int x = 0; x < width; x++ )
{
for ( int y = 0; y < height; y++ )
{
symbolGrid[x][y] = tiles[x][y].symbol;
}
}
dfp.preprocessor.generator.process( symbolGrid, floor, wall, ran, dfp );
for ( int x = 0; x < width; x++ )
{
for ( int y = 0; y < height; y++ )
{
if ( x == 0 || x == width - 1 || y == 0 || y == height - 1 )
{
symbolGrid[x][y] = wall;
}
}
}
for ( int x = 0; x < width; x++ )
{
for ( int y = 0; y < height; y++ )
{
tiles[x][y].symbol = symbolGrid[x][y];
if (dfp.preprocessor.generator.ensuresConnectivity)
{
if (tiles[x][y].symbol.character == '#')
{
tiles[x][y].passable = false;
}
}
}
}
}
}
// ----------------------------------------------------------------------
protected void partition()
{
int bx = 0;
int by = 0;
int bwidth = width;
int bheight = height;
// First place the rooms with a Placement
Array<Room> northRooms = new Array<Room>( );
Array<Room> southRooms = new Array<Room>( );
Array<Room> eastRooms = new Array<Room>( );
Array<Room> westRooms = new Array<Room>( );
Iterator<Room> itr = toBePlaced.iterator();
while ( itr.hasNext() )
{
Room room = itr.next();
if ( room.roomData.placement != DungeonFileParser.DFPRoom.Placement.CENTRE )
{
if ( room.roomData.placement == DungeonFileParser.DFPRoom.Placement.NORTH )
{
northRooms.add( room );
room.flipVertical();
}
else if ( room.roomData.placement == DungeonFileParser.DFPRoom.Placement.SOUTH )
{
southRooms.add( room );
room.flipVertical();
}
else if ( room.roomData.placement == DungeonFileParser.DFPRoom.Placement.EAST )
{
eastRooms.add( room );
}
else if ( room.roomData.placement == DungeonFileParser.DFPRoom.Placement.WEST )
{
westRooms.add( room );
}
itr.remove();
}
}
// Place north
if (northRooms.size > 0)
{
int totalSize = 0;
for ( Room room : northRooms )
{
if ( room.height + minPadding > bheight )
{
// Not enough space
return;
}
totalSize += room.width;
}
if (totalSize > bwidth)
{
// Not enough space
return;
}
int spacing = (int)Math.floor( (float)(bwidth - totalSize) / ((float)northRooms.size + 1) );
int maxheight = 0;
int cx = ran.nextInt(spacing * 2);
int extra = spacing * 2 - cx;
for ( Room room : northRooms )
{
room.x = cx;
room.y = by + bheight - room.height;
if (room.height > maxheight)
{
maxheight = room.height;
}
placedRooms.add( room );
int offset = ran.nextInt(spacing + extra);
extra = (spacing + extra ) - offset;
cx += room.width + offset;
}
bheight -= maxheight;
}
// Place south
if (southRooms.size > 0)
{
int totalSize = 0;
for ( Room room : southRooms )
{
if ( room.height + minPadding > bheight )
{
// Not enough space
return;
}
totalSize += room.width;
}
if (totalSize > bwidth)
{
// Not enough space
return;
}
int spacing = (int)Math.floor( (float)(bwidth - totalSize) / ((float)southRooms.size + 1) );
int maxheight = 0;
int cx = ran.nextInt(spacing * 2);
int extra = spacing * 2 - cx;
for ( Room room : southRooms )
{
room.x = cx;
room.y = by;
if (room.height > maxheight)
{
maxheight = room.height;
}
placedRooms.add( room );
int offset = ran.nextInt(spacing + extra);
extra = (spacing + extra ) - offset;
cx += room.width + offset;
}
bheight -= maxheight;
by += maxheight;
}
// Place east
if (eastRooms.size > 0)
{
int totalSize = 0;
for ( Room room : eastRooms )
{
if ( room.width + minPadding > bwidth )
{
// Not enough space
return;
}
totalSize += room.height;
}
if (totalSize > bheight)
{
// Not enough space
return;
}
int spacing = (int)Math.floor( (float)(bheight - totalSize) / ((float)eastRooms.size + 1) );
int maxwidth = 0;
int cy = ran.nextInt(spacing * 2);
int extra = spacing * 2 - cy;
for ( Room room : eastRooms )
{
room.x = bx + bwidth - room.width;
room.y = cy;
if (room.width > maxwidth)
{
maxwidth = room.width;
}
placedRooms.add( room );
int offset = ran.nextInt(spacing + extra);
extra = (spacing + extra ) - offset;
cy += room.height + offset;
}
bwidth -= maxwidth;
}
// Place west
if (westRooms.size > 0)
{
int totalSize = 0;
for ( Room room : westRooms )
{
if ( room.width + minPadding > bwidth )
{
// Not enough space
return;
}
totalSize += room.height;
}
if (totalSize > bheight)
{
// Not enough space
return;
}
int spacing = (int)Math.floor( (float)(bheight - totalSize) / ((float)westRooms.size + 1) );
int maxwidth = 0;
int cy = ran.nextInt(spacing * 2);
int extra = spacing * 2 - cy;
for ( Room room : westRooms )
{
room.x = bx;
room.y = cy;
if (room.width > maxwidth)
{
maxwidth = room.width;
}
placedRooms.add( room );
int offset = ran.nextInt(spacing + extra);
extra = (spacing + extra ) - offset;
cy += room.height + offset;
}
bwidth -= maxwidth;
bx += maxwidth;
}
if (DEBUG_OUTPUT)
{
markRooms();
Symbol[][] symbolGrid = new Symbol[ width ][ height ];
for ( int x = 0; x < width; x++ )
{
for ( int y = 0; y < height; y++ )
{
symbolGrid[ x ][ y ] = tiles[ x ][ y ].symbol;
}
}
DEBUG_printGrid( symbolGrid );
}
// Then partition the rest
if ( bwidth >= paddedMinRoom && bheight >= paddedMinRoom )
{
partitionRecursive( bx + minPadding, by + minPadding, bwidth - minPadding2, bheight - minPadding2 );
}
if (DEBUG_OUTPUT)
{
markRooms();
Symbol[][] symbolGrid = new Symbol[ width ][ height ];
for ( int x = 0; x < width; x++ )
{
for ( int y = 0; y < height; y++ )
{
symbolGrid[ x ][ y ] = tiles[ x ][ y ].symbol;
}
}
DEBUG_printGrid( symbolGrid );
}
}
// ----------------------------------------------------------------------
protected void partitionRecursive( int x, int y, int width, int height )
{
int padX = Math.min( ran.nextInt( maxPadding - minPadding ) + minPadding, ( width - minRoomSize ) / 2 );
int padY = Math.min( ran.nextInt( maxPadding - minPadding ) + minPadding, ( height - minRoomSize ) / 2 );
int padX2 = padX * 2;
int padY2 = padY * 2;
// get the room to be placed
Room room = null;
// if the predefined rooms array has items, then try to pick one from it
if ( toBePlaced.size > 0 )
{
// Array of indexes to be tried, stops duplicate work
Array<Integer> indexes = new Array<Integer>();
for ( int i = 0; i < toBePlaced.size; i++ )
{
indexes.add( i );
}
while ( room == null && indexes.size > 0 )
{
int index = indexes.removeIndex( ran.nextInt( indexes.size ) );
Room testRoom = toBePlaced.get( index );
boolean fits = false;
boolean rotate = false;
boolean flipVert = false;
boolean flipHori = false;
boolean fitsVertical = testRoom.width + padX2 <= width && testRoom.height + padY2 <= height;
boolean fitsHorizontal = testRoom.height + padX2 <= width && testRoom.width + padY2 <= height;
if ( testRoom.roomData.lockRotation )
{
if ( fitsVertical )
{
fits = true;
flipVert = true;
if ( ran.nextBoolean() )
{
flipHori = true;
}
}
}
else
{
if ( fitsVertical || fitsHorizontal )
{
fits = true;
// randomly flip
if ( ran.nextBoolean() )
{
flipVert = true;
}
if ( ran.nextBoolean() )
{
flipHori = true;
}
// if it fits on both directions, randomly pick one
if ( fitsVertical && fitsHorizontal )
{
if ( ran.nextBoolean() )
{
rotate = true;
}
}
else if ( fitsHorizontal )
{
rotate = true;
}
}
}
// If it fits then place the room and rotate/flip as neccesary
if ( fits )
{
room = testRoom;
toBePlaced.removeIndex( index );
if ( flipVert )
{
room.flipVertical();
}
if ( flipHori )
{
room.flipHorizontal();
}
if ( rotate )
{
room.rotate();
}
if ( flipVert && rotate )
{
room.orientation = Direction.WEST;
}
else if ( flipVert )
{
room.orientation = Direction.SOUTH;
}
else if ( rotate )
{
room.orientation = Direction.EAST;
}
else
{
room.orientation = Direction.NORTH;
}
}
}
}
// failed to find a suitable predefined room, so create a new one
if ( room == null && dfp.roomGenerators.size > 0 )
{
int roomWidth = Math.min( ran.nextInt( maxRoomSize - minRoomSize ) + minRoomSize, width - padX2 );
int roomHeight = Math.min( ran.nextInt( maxRoomSize - minRoomSize ) + minRoomSize, height - padY2 );
room = new Room();
room.width = roomWidth;
room.height = roomHeight;
room.generateRoomContents( ran, dfp );
}
if ( room == null ) { return; }
placedRooms.add( room );
// pick corner
// possible sides:
// 0 1
// 2 3
int side = ran.nextInt( 4 );
// Position room at side
if ( side == 0 )
{
room.x = x + padX;
room.y = y + padY;
}
else if ( side == 1 )
{
room.x = ( x + width ) - ( room.width + padX );
room.y = y + padY;
}
else if ( side == 2 )
{
room.x = x + padX;
room.y = ( y + height ) - ( room.height + padY );
}
else
{
room.x = ( x + width ) - ( room.width + padX );
room.y = ( y + height ) - ( room.height + padY );
}
// split into 2 remaining rectangles and recurse
if ( side == 0 )
{
// r1
// 22
{
int nx = room.x + room.width + padX;
int ny = y;
int nwidth = x + width - nx;
int nheight = room.height + padY2;
if ( nwidth >= paddedMinRoom && nheight >= paddedMinRoom )
{
partitionRecursive( nx, ny, nwidth, nheight );
}
}
{
int nx = x;
int ny = room.y + room.height + padY;
int nwidth = width;
int nheight = y + height - ny;
if ( nwidth >= paddedMinRoom && nheight >= paddedMinRoom )
{
partitionRecursive( nx, ny, nwidth, nheight );
}
}
}
else if ( side == 1 )
{
// 1r
// 12
{
int nx = x;
int ny = y;
int nwidth = width - ( room.width + padX2 );
int nheight = height;
if ( nwidth >= paddedMinRoom && nheight >= paddedMinRoom )
{
partitionRecursive( nx, ny, nwidth, nheight );
}
}
{
int nx = room.x - padX;
int ny = room.y + room.height + padY;
int nwidth = room.width + padX2;
int nheight = ( y + height ) - ny;
if ( nwidth >= paddedMinRoom && nheight >= paddedMinRoom )
{
partitionRecursive( nx, ny, nwidth, nheight );
}
}
}
else if ( side == 2 )
{
// 12
// r2
{
int nx = x;
int ny = y;
int nwidth = room.width + padX2;
int nheight = height - ( room.height + padY2 );
if ( nwidth >= paddedMinRoom && nheight >= paddedMinRoom )
{
partitionRecursive( nx, ny, nwidth, nheight );
}
}
{
int nx = x + room.width + padX2;
int ny = y;
int nwidth = ( x + width ) - nx;
int nheight = height;
if ( nwidth >= paddedMinRoom && nheight >= paddedMinRoom )
{
partitionRecursive( nx, ny, nwidth, nheight );
}
}
}
else
{
// 22
// 1r
{
int nx = x;
int ny = room.y - padY;
int nwidth = width - ( room.width + padX2 );
int nheight = ( y + height ) - ny;
if ( nwidth >= paddedMinRoom && nheight >= paddedMinRoom )
{
partitionRecursive( nx, ny, nwidth, nheight );
}
}
{
int nx = x;
int ny = y;
int nwidth = width;
int nheight = height - ( room.height + padY2 );
if ( nwidth >= paddedMinRoom && nheight >= paddedMinRoom )
{
partitionRecursive( nx, ny, nwidth, nheight );
}
}
}
}
// ----------------------------------------------------------------------
protected void connectRooms()
{
Array<Pnt> roomPnts = new Array<Pnt>();
for ( Room room : placedRooms )
{
for ( RoomDoor door : room.doors )
{
int x = door.pos.x + room.x;
int y = door.pos.y + room.y;
if ( door.side == Direction.WEST )
{
x -= dfp.corridorStyle.width - 1;
}
else if ( door.side == Direction.NORTH )
{
y -= dfp.corridorStyle.width - 1;
}
if (x >= 1 && y >= 1 && x < width-1 && y < height-1)
{
Pnt p = new Pnt( x, y );
roomPnts.add( p );
}
}
}
Triangle initialTriangle = new Triangle( new Pnt( -10000, -10000 ), new Pnt( 10000, -10000 ), new Pnt( 0, 10000 ) );
Triangulation dt = new Triangulation( initialTriangle );
for ( Pnt p : roomPnts )
{
dt.delaunayPlace( p );
}
Array<Pnt[]> ignoredPaths = new Array<Pnt[]>();
Array<Pnt[]> addedPaths = new Array<Pnt[]>();
Array<Pnt[]> paths = new Array<Pnt[]>();
Array<Triangle> tris = new Array<Triangle>();
for ( Triangle tri : dt )
{
tris.add( tri );
}
tris.sort( new Comparator<Triangle>()
{
@Override
public int compare( Triangle arg0, Triangle arg1 )
{
return arg0.compareTo( arg1 );
}
} );
for ( Triangle tri : tris )
{
calculatePaths( paths, tri, ignoredPaths, addedPaths );
}
for ( Pnt room : roomPnts )
{
int rx = (int) room.coord( 0 );
int ry = (int) room.coord( 1 );
Pnt closest = null;
int closestDist = Integer.MAX_VALUE;
boolean found = false;
outer:
for ( Pnt[] path : paths )
{
for ( Pnt p : path )
{
if ( p == null )
{
break;
}
int px = (int) p.coord( 0 );
int py = (int) p.coord( 1 );
if ( rx == px && ry == py )
{
found = true;
break outer;
}
int tempDist = Math.max( Math.abs( px - rx ), Math.abs( py - ry ) );
if ( tempDist < closestDist )
{
closestDist = tempDist;
closest = p;
}
}
}
if ( !found )
{
paths.add( new Pnt[] { room, closest } );
}
}
for ( Pnt[] p : paths )
{
if ( p[0] == null || p[1] == null )
{
continue;
}
int x1 = (int) p[0].coord( 0 );
int y1 = (int) p[0].coord( 1 );
int x2 = (int) p[1].coord( 0 );
int y2 = (int) p[1].coord( 1 );
AStarPathfind pathFind = new AStarPathfind( tiles, x1, y1, x2, y2, false, true, dfp.corridorStyle.width, GeneratorPassability, null );
Array<Point> path = pathFind.getPath();
if (path == null)
{
Symbol[][] symbolGrid = new Symbol[width][height];
for ( int x = 0; x < width; x++ )
{
for ( int y = 0; y < height; y++ )
{
symbolGrid[x][y] = tiles[x][y].symbol.copy();
if (x == x1 && y == y1)
{
symbolGrid[x][y].character = '-';
}
if (x == x2 && y == y2)
{
symbolGrid[x][y].character = '=';
}
}
}
DEBUG_printGrid( symbolGrid );
}
else
{
carveCorridor( path );
Global.PointPool.freeAll( path );
}
}
}
// ----------------------------------------------------------------------
protected void carveCorridor( Array<Point> path )
{
int centralCount = 0;
int sideCount = 0;
boolean placementAlternator = true;
int width = dfp.corridorStyle.width;
for ( int i = 0; i < path.size; i++ )
{
Point pos = path.get( i );
GenerationTile t = tiles[pos.x][pos.y];
t.isCorridor = true;
for ( int x = 0; x < width; x++ )
{
for ( int y = 0; y < width; y++ )
{
t = tiles[pos.x + x][pos.y + y];
if ( t.symbol.character == '#' )
{
t.symbol = dfp.sharedSymbolMap.get( '.' );
t.symbol.resolveExtends( dfp.sharedSymbolMap );
}
// Wipe out all features not placed by this path
if ( !t.isRoom && t.placerHashCode != path.hashCode() )
{
t.symbol = t.symbol.copy();
t.symbol.environmentData = null;
}
// Wipe out all features in the central square
if ( !t.isRoom && x > 0 && x < width - 1 && y > 0 && y < width - 1 )
{
t.symbol = t.symbol.copy();
t.symbol.environmentData = null;
}
}
}
if ( dfp.corridorStyle.centralConstant != null )
{
t = tiles[pos.x + width / 2][pos.y + width / 2];
if ( t.symbol.shouldPlaceCorridorFeatures() )
{
t.symbol = dfp.corridorStyle.centralConstant.getAsSymbol( t.symbol, dfp );
t.placerHashCode = path.hashCode();
}
}
if ( dfp.corridorStyle.centralRecurring != null )
{
centralCount++;
if ( centralCount == dfp.corridorStyle.centralRecurring.interval )
{
t = tiles[pos.x + width / 2][pos.y + width / 2];
if ( t.symbol.shouldPlaceCorridorFeatures() )
{
t.symbol = dfp.corridorStyle.centralRecurring.getAsSymbol( t.symbol, dfp );
t.placerHashCode = path.hashCode();
}
centralCount = 0;
}
}
if ( dfp.corridorStyle.sideRecurring != null )
{
sideCount++;
if ( sideCount == dfp.corridorStyle.sideRecurring.interval && i > 0 )
{
boolean placeTop = dfp.corridorStyle.sideRecurring.placementMode == PlacementMode.BOTH
|| dfp.corridorStyle.sideRecurring.placementMode == PlacementMode.TOP
|| ( dfp.corridorStyle.sideRecurring.placementMode == PlacementMode.ALTERNATE && placementAlternator );
boolean placeBottom = dfp.corridorStyle.sideRecurring.placementMode == PlacementMode.BOTH
|| dfp.corridorStyle.sideRecurring.placementMode == PlacementMode.BOTTOM
|| ( dfp.corridorStyle.sideRecurring.placementMode == PlacementMode.ALTERNATE && !placementAlternator );
if ( path.get( i - 1 ).x != pos.x )
{
if ( dfp.corridorStyle.width == 1 )
{
if ( placeTop && isEmpty( tiles[pos.x + width / 2][pos.y - 1] ) )
{
t = tiles[pos.x + width / 2][pos.y - 1];
if ( t.symbol.shouldPlaceCorridorFeatures() )
{
t.symbol = dfp.corridorStyle.sideRecurring.getAsSymbol( t.symbol, dfp );
t.symbol.attachLocation = Direction.NORTH;
t.placerHashCode = path.hashCode();
}
}
if ( placeBottom && isEmpty( tiles[pos.x + width / 2][pos.y + width] ) )
{
t = tiles[pos.x + width / 2][pos.y + width];
if ( t.symbol.shouldPlaceCorridorFeatures() )
{
t.symbol = dfp.corridorStyle.sideRecurring.getAsSymbol( t.symbol, dfp );
t.symbol.attachLocation = Direction.SOUTH;
t.placerHashCode = path.hashCode();
}
}
}
else
{
if ( placeTop && tiles[pos.x + width / 2][pos.y - 1].symbol.character == '#' )
{
t = tiles[pos.x + width / 2][pos.y];
if ( t.symbol.shouldPlaceCorridorFeatures() )
{
t.symbol = dfp.corridorStyle.sideRecurring.getAsSymbol( t.symbol, dfp );
t.symbol.attachLocation = Direction.NORTH;
t.placerHashCode = path.hashCode();
}
}
if ( placeBottom && tiles[pos.x + width / 2][pos.y + width].symbol.character == '#' )
{
t = tiles[pos.x + width / 2][pos.y + width - 1];
if ( t.symbol.shouldPlaceCorridorFeatures() )
{
t.symbol = dfp.corridorStyle.sideRecurring.getAsSymbol( t.symbol, dfp );
t.symbol.attachLocation = Direction.SOUTH;
t.placerHashCode = path.hashCode();
}
}
}
}
else
{
if ( dfp.corridorStyle.width == 1 )
{
if ( placeTop && isEmpty( tiles[pos.x - 1][pos.y + width / 2] ) )
{
t = tiles[pos.x - 1][pos.y + width / 2];
if ( t.symbol.shouldPlaceCorridorFeatures() )
{
t.symbol = dfp.corridorStyle.sideRecurring.getAsSymbol( t.symbol, dfp );
t.symbol.attachLocation = Direction.EAST;
t.placerHashCode = path.hashCode();
}
}
if ( placeBottom && isEmpty( tiles[pos.x + width][pos.y + width / 2] ) )
{
t = tiles[pos.x + width][pos.y + width / 2];
if ( t.symbol.shouldPlaceCorridorFeatures() )
{
t.symbol = dfp.corridorStyle.sideRecurring.getAsSymbol( t.symbol, dfp );
t.symbol.attachLocation = Direction.WEST;
t.placerHashCode = path.hashCode();
}
}
}
else
{
if ( placeTop && tiles[pos.x - 1][pos.y + width / 2].symbol.character == '#' )
{
t = tiles[pos.x][pos.y + width / 2];
if ( t.symbol.shouldPlaceCorridorFeatures() )
{
t.symbol = dfp.corridorStyle.sideRecurring.getAsSymbol( t.symbol, dfp );
t.symbol.attachLocation = Direction.EAST;
t.placerHashCode = path.hashCode();
}
}
if ( placeBottom && tiles[pos.x + width][pos.y + width / 2].symbol.character == '#' )
{
t = tiles[pos.x + width - 1][pos.y + width / 2];
if ( t.symbol.shouldPlaceCorridorFeatures() )
{
t.symbol = dfp.corridorStyle.sideRecurring.getAsSymbol( t.symbol, dfp );
t.symbol.attachLocation = Direction.WEST;
t.placerHashCode = path.hashCode();
}
}
}
}
sideCount = 0;
placementAlternator = !placementAlternator;
}
}
}
}
// ----------------------------------------------------------------------
protected void calculatePaths( Array<Pnt[]> paths, Triangle triangle, Array<Pnt[]> ignoredPaths, Array<Pnt[]> addedPaths )
{
Pnt[] vertices = triangle.toArray( new Pnt[0] );
int ignore = 0;
double dist = 0;
dist = Math.pow( 2, vertices[0].coord( 0 ) - vertices[1].coord( 0 ) ) + Math.pow( 2, vertices[0].coord( 1 ) - vertices[1].coord( 1 ) );
double temp = Math.pow( 2, vertices[0].coord( 0 ) - vertices[2].coord( 0 ) ) + Math.pow( 2, vertices[0].coord( 1 ) - vertices[2].coord( 1 ) );
if ( dist < temp )
{
dist = temp;
ignore = 1;
}
temp = Math.pow( 2, vertices[1].coord( 0 ) - vertices[2].coord( 0 ) ) + Math.pow( 2, vertices[1].coord( 1 ) - vertices[2].coord( 1 ) );
if ( dist < temp )
{
dist = temp;
ignore = 2;
}
if ( ignore != 0 && !checkIgnored( vertices[0], vertices[1], ignoredPaths ) && !checkAdded( vertices[0], vertices[1], addedPaths ) )
{
addPath( vertices[0], vertices[1], paths, ignoredPaths, addedPaths );
}
else
{
ignoredPaths.add( new Pnt[] { vertices[0], vertices[1] } );
}
if ( ignore != 1 && !checkIgnored( vertices[0], vertices[2], ignoredPaths ) && !checkAdded( vertices[0], vertices[2], addedPaths ) )
{
addPath( vertices[0], vertices[2], paths, ignoredPaths, addedPaths );
}
else
{
ignoredPaths.add( new Pnt[] { vertices[0], vertices[2] } );
}
if ( ignore != 2 && !checkIgnored( vertices[1], vertices[2], ignoredPaths ) && !checkAdded( vertices[1], vertices[2], addedPaths ) )
{
addPath( vertices[1], vertices[2], paths, ignoredPaths, addedPaths );
}
else
{
ignoredPaths.add( new Pnt[] { vertices[1], vertices[2] } );
}
}
// ----------------------------------------------------------------------
protected void addPath( Pnt p1, Pnt p2, Array<Pnt[]> paths, Array<Pnt[]> ignoredPaths, Array<Pnt[]> addedPaths )
{
if ( p1.coord( 0 ) < 0
|| p1.coord( 1 ) < 0
|| p1.coord( 0 ) >= width - 1
|| p1.coord( 1 ) >= height - 1
|| p2.coord( 0 ) < 0
|| p2.coord( 1 ) < 0
|| p2.coord( 0 ) >= width - 1
|| p2.coord( 1 ) >= height - 1 )
{
ignoredPaths.add( new Pnt[] { p1, p2 } );
}
else
{
addedPaths.add( new Pnt[] { p1, p2 } );
paths.add( new Pnt[] { p1, p2 } );
}
}
// ----------------------------------------------------------------------
protected boolean checkIgnored( Pnt p1, Pnt p2, Array<Pnt[]> ignoredPaths )
{
for ( Pnt[] p : ignoredPaths )
{
if ( p[0].equals( p1 ) && p[1].equals( p2 ) )
{
return true;
}
else if ( p[0].equals( p2 ) && p[1].equals( p1 ) ) { return true; }
}
return false;
}
// ----------------------------------------------------------------------
protected boolean checkAdded( Pnt p1, Pnt p2, Array<Pnt[]> addedPaths )
{
for ( Pnt[] p : addedPaths )
{
if ( p[0].equals( p1 ) && p[1].equals( p2 ) )
{
return true;
}
else if ( p[0].equals( p2 ) && p[1].equals( p1 ) ) { return true; }
}
return false;
}
// ----------------------------------------------------------------------
protected void markRooms()
{
for ( Room room : placedRooms )
{
for ( int x = 0; x < room.width; x++ )
{
for ( int y = 0; y < room.height; y++ )
{
GenerationTile tile = tiles[room.x + x][room.y + y];
Symbol symbol = room.roomContents[x][y];
if ( room.fromEmptySpace && symbol.character == '#' )
{
// Skip placing
}
else if ( room.fromEmptySpace && symbol.character == '.' )
{
// skip also
}
else
{
symbol.containingRoom = room;
tile.passable = symbol.isPassable( GeneratorPassability );
tile.symbol = symbol;
tile.isRoom = !room.fromEmptySpace;
}
}
}
}
}
// ----------------------------------------------------------------------
protected void identifyAndFillEmptySpaces()
{
Symbol wall = dfp.sharedSymbolMap.get( '#' );
wall.resolveExtends( dfp.sharedSymbolMap );
for ( int x = 0; x < width; x++ )
{
for ( int y = 0; y < height; y++ )
{
if ( isEmpty( tiles[x][y] ) )
{
HashSet<GenerationTile> output = new HashSet<GenerationTile>();
floodFillEmptySpace( x, y, output );
if ( output.size() > 8 )
{
// convert into a room
Room room = new Room();
// find min/max
int minx = Integer.MAX_VALUE;
int miny = Integer.MAX_VALUE;
int maxx = 0;
int maxy = 0;
for ( GenerationTile tile : output )
{
if ( tile.x < minx )
{
minx = tile.x;
}
if ( tile.y < miny )
{
miny = tile.y;
}
if ( tile.x > maxx )
{
maxx = tile.x;
}
if ( tile.y > maxy )
{
maxy = tile.y;
}
}
minx--;
miny--;
maxx += 2;
maxy += 2;
room.x = minx;
room.y = miny;
room.width = ( maxx - minx );
room.height = ( maxy - miny );
// Copy contents into room
room.roomContents = new Symbol[room.width][room.height];
for ( int rx = 0; rx < room.width; rx++ )
{
for ( int ry = 0; ry < room.height; ry++ )
{
GenerationTile tile = tiles[minx + rx][miny + ry];
if (rx == 0)
{
room.roomContents[rx][ry] =
tile.getPassable( GeneratorPassability, null ) &&
output.contains( tiles[minx + 1][miny + ry] ) ?
tile.symbol : wall;
}
else if (ry == 0)
{
room.roomContents[rx][ry] =
tile.getPassable( GeneratorPassability, null ) &&
output.contains( tiles[minx + rx][miny + 1] ) ?
tile.symbol : wall;
}
else if (rx == width - 1)
{
room.roomContents[rx][ry] =
tile.getPassable( GeneratorPassability, null ) &&
output.contains( tiles[minx + (width - 2)][miny + ry] ) ?
tile.symbol : wall;
}
else if (ry == height - 1)
{
room.roomContents[rx][ry] =
tile.getPassable( GeneratorPassability, null ) &&
output.contains( tiles[minx + rx][miny + (height - 2)] ) ?
tile.symbol : wall;
}
else
{
if ( output.contains( tile ) )
{
room.roomContents[rx][ry] = tile.symbol;
}
else
{
room.roomContents[rx][ry] = wall;
}
}
}
}
room.resolveExtends(dfp);
// Identify doors
room.findDoors( ran, dfp );
if (room.doors.size == 0)
{
Symbol floor = dfp.getSymbol( '.' );
floor.resolveExtends( dfp.sharedSymbolMap );
room.carveDoors( dfp, ran, floor, true );
// fill in edges
if (room.x == 0)
{
for (int i = 0; i < room.height; i++)
{
room.roomContents[0][i] = wall;
}
}
if (room.y == 0)
{
for (int i = 0; i < room.width; i++)
{
room.roomContents[i][0] = wall;
}
}
if (room.x + room.width == width-1)
{
for (int i = 0; i < room.height; i++)
{
room.roomContents[room.width-1][i] = wall;
}
}
if (room.y + room.height == height-1)
{
for (int i = 0; i < room.width; i++)
{
room.roomContents[i][room.height-1] = wall;
}
}
room.findDoors( ran, dfp );
}
if (room.doors.size > 0)
{
room.fromEmptySpace = true;
System.out.println("Free Space:");
room.print();
placedRooms.add( room );
}
}
// Mark the area as empty space to indicate we already
// filled this
for ( GenerationTile tile : output )
{
tile.isEmptySpace = true;
}
}
}
}
}
// ----------------------------------------------------------------------
protected void floodFillEmptySpace( int sx, int sy, HashSet<GenerationTile> output )
{
Array<Point> toBeProcessed = new Array<Point>();
toBeProcessed.add( Global.PointPool.obtain().set( sx, sy ) );
while ( toBeProcessed.size > 0 )
{
Point point = toBeProcessed.pop();
int x = point.x;
int y = point.y;
Global.PointPool.free( point );
if ( output.contains( tiles[x][y] ) )
{
continue;
}
output.add( tiles[x][y] );
for ( Direction dir : Direction.values() )
{
if ( dir.isCardinal() )
{
if ( isEmpty( x, y, dir ) )
{
int nx = x + dir.getX();
int ny = y + dir.getY();
boolean found = false;
if ( dir == Direction.NORTH || dir == Direction.SOUTH )
{
found = isEmpty( nx, ny, Direction.EAST ) || isEmpty( nx, ny, Direction.WEST );
}
else
{
found = isEmpty( nx, ny, Direction.NORTH ) || isEmpty( nx, ny, Direction.SOUTH );
}
if (found)
{
toBeProcessed.add( Global.PointPool.obtain().set( nx, ny ) );
}
}
}
}
}
}
// ---------------------------------------------------------------------
protected boolean isEmpty( int x, int y, Direction dir )
{
int nx = x + dir.getX();
int ny = y + dir.getY();
if (nx < 0 || ny < 0 || nx >= tiles.length || ny >= tiles[0].length)
{
return false;
}
GenerationTile tile = tiles[nx][ny];
return isEmpty( tile );
}
// ----------------------------------------------------------------------
protected boolean isEmpty( GenerationTile tile )
{
return !tile.isCorridor && !tile.isRoom && !tile.isEmptySpace && tile.symbol.character == '.';
}
// endregion Private Methods
// ####################################################################//
// region Data
// ----------------------------------------------------------------------
public static final EnumBitflag<Passability> GeneratorPassability = new EnumBitflag<Passability>( Passability.WALK );
private static final int DEBUG_SIZE = 16;
private GenerationTile[][] tiles;
private int minPadding = 1;
private int maxPadding = 3;
private int minPadding2 = minPadding * 2;
private int minRoomSize = 7;
private int maxRoomSize = 25;
private int paddedMinRoom;
// endregion Data
// ####################################################################//
// region Classes
// ----------------------------------------------------------------------
public static class GenerationTile implements PathfindingTile
{
public Symbol symbol;
public int influence;
public long placerHashCode;
public boolean passable;
public boolean isCorridor = false;
public boolean isRoom = false;
public boolean isEmptySpace = false;
public int x;
public int y;
public GenerationTile( int x, int y )
{
this.x = x;
this.y = y;
}
// ----------------------------------------------------------------------
@Override
public boolean getPassable( EnumBitflag<Passability> travelType, Object self )
{
return passable;
}
// ----------------------------------------------------------------------
@Override
public int getInfluence( EnumBitflag<Passability> travelType, Object self )
{
if ( isCorridor )
{
return 0;
}
else
{
return influence;
}
}
@Override
public String toString()
{
return "" + symbol.character;
}
}
// endregion Classes
// ####################################################################//
}