package Roguelike.DungeonGeneration; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Random; import Roguelike.Dialogue.DialogueManager; import Roguelike.Fields.Field; import Roguelike.Global; import Roguelike.Global.Direction; import Roguelike.DungeonGeneration.DungeonFileParser.DFPRoom; import Roguelike.Entity.EnvironmentEntity; import Roguelike.Entity.GameEntity; import Roguelike.Levels.Level; import Roguelike.Levels.LevelManager; import Roguelike.Save.SaveLevel; import Roguelike.Tiles.GameTile; import Roguelike.Tiles.Point; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.XmlReader; import com.badlogic.gdx.utils.reflect.ClassReflection; import com.badlogic.gdx.utils.reflect.ReflectionException; public abstract class AbstractDungeonGenerator { public int percent; public String generationText = "Selecting Rooms"; protected int generationIndex = 0; protected SaveLevel saveLevel; protected DungeonFileParser dfp; protected Array<DFPRoom> additionalRooms = new Array<DFPRoom>(); protected Array<Room> requiredRooms = new Array<Room>(); protected Array<Room> toBePlaced = new Array<Room>(); protected Array<Room> placedRooms = new Array<Room>(); protected FactionParser majorFaction; protected Array<FactionParser> minorFactions = new Array<FactionParser>(); private HashMap<FactionParser, Point> factions = new HashMap<FactionParser, Point>(); protected Level level; protected Random ran; protected int width; protected int height; protected static final boolean DEBUG_OUTPUT = false; // ---------------------------------------------------------------------- public abstract void setup( SaveLevel level, DungeonFileParser dfp ); // ---------------------------------------------------------------------- public abstract boolean generate(); // ---------------------------------------------------------------------- public Level getLevel() { return level; } // ---------------------------------------------------------------------- protected void selectFactions() { if (dfp.majorFactions.size == 0) { return; } String majorFactionName = dfp.getMajorFaction( ran ); majorFaction = FactionParser.load( majorFactionName ); int numMinor = 2;//ran.nextInt( 2 ) + 1; // 1 - 3 minor for (int i = 0; i < numMinor; i++) { String minorFactionName = dfp.getMinorFaction( ran ); boolean added = false; if ( majorFactionName.equals( minorFactionName ) ) { added = true; } if (!added) { for ( FactionParser fp : minorFactions ) { if ( fp.name.equals( minorFactionName ) ) { added = true; break; } } } if (!added) { minorFactions.add( FactionParser.load( minorFactionName ) ); } } } // ---------------------------------------------------------------------- protected void selectRooms() { selectFactions(); for ( DFPRoom r : dfp.getRooms( saveLevel.depth, ran, saveLevel.isBossLevel, majorFaction, minorFactions ) ) { Room room = new Room(); r.fillRoom( room, ran, dfp ); requiredRooms.add( room ); } additionalRooms.addAll( saveLevel.requiredRooms ); for ( DFPRoom r : additionalRooms ) { Room room = new Room(); r.fillRoom( room, ran, dfp ); requiredRooms.add( room ); } requiredRooms.sort( new Comparator<Room>() { @Override public int compare( Room arg0, Room arg1 ) { return arg0.comparisonString().compareTo( arg1.comparisonString() ); } } ); } // ---------------------------------------------------------------------- public static Symbol[][] minimiseGrid( Symbol[][] grid, Symbol wall ) { int width = grid.length; int height = grid[0].length; int minx = -1; int miny = -1; int maxx = -1; int maxy = -1; boolean complete = false; // find min x for ( int x = 0; x < width; x++ ) { for ( int y = 0; y < height; y++ ) { Symbol s = grid[x][y]; if ( s.character != wall.character ) { minx = x - 1; complete = true; break; } } if ( complete ) { break; } } if ( minx == -1 || minx >= grid.length ) { return grid; } // find min y complete = false; for ( int y = 0; y < height; y++ ) { for ( int x = minx; x < width; x++ ) { Symbol s = grid[x][y]; if ( s.character != wall.character ) { miny = y - 1; complete = true; break; } } if ( complete ) { break; } } if ( miny == -1 || miny >= grid[0].length ) { return grid; } // find max x complete = false; for ( int x = width - 1; x >= minx; x-- ) { for ( int y = miny; y < height; y++ ) { Symbol s = grid[x][y]; if ( s.character != wall.character ) { maxx = x + 2; complete = true; break; } } if ( complete ) { break; } } if ( maxx == -1 || maxx >= grid.length ) { return grid; } // find max y complete = false; for ( int y = height - 1; y >= miny; y-- ) { for ( int x = minx; x < maxx; x++ ) { Symbol s = grid[x][y]; if ( s.character != wall.character ) { maxy = y + 2; complete = true; break; } } if ( complete ) { break; } } if ( maxy == -1 || maxy >= grid[0].length ) { return grid; } // minimise room int newwidth = Math.min( width, maxx - minx ); int newheight = Math.min( height, maxy - miny ); Symbol[][] newgrid = new Symbol[newwidth][newheight]; for ( int x = 0; x < newwidth; x++ ) { for ( int y = 0; y < newheight; y++ ) { newgrid[x][y] = grid[minx + x][miny + y]; } } return newgrid; } // ---------------------------------------------------------------------- protected void DEBUG_printGrid( Symbol[][] symbolGrid ) { for ( int y = height - 1; y >= 0; y-- ) { for ( int x = 0; x < width; x++ ) { System.out.print( symbolGrid[x][y].character ); } System.out.print( "\n" ); } System.out.println( "\n" ); } // ---------------------------------------------------------------------- protected void placeFactions() { // place factions // get largest room int max = 0; Room largest = null; for ( Room room : placedRooms ) { int size = room.width * room.height; if ( size > max ) { max = size; largest = room; } } Array<FactionParser> unplacedMinorFactions = new Array<FactionParser>( minorFactions ); factions.put( majorFaction, new Point( largest.x + largest.width / 2, largest.y + largest.height / 2 ) ); class Pair implements Comparable<Pair> { int dist; Room room; public Pair( int dist, Room room ) { this.dist = dist; this.room = room; } @Override public int compareTo( Pair arg0 ) { return ( (Integer) dist ).compareTo( arg0.dist ); } } Array<Pair> unassignedRooms = new Array<Pair>( ); Array<Pair> sortedRooms = new Array<Pair>(); for ( Room room : placedRooms ) { if ( room.faction == null ) { int dist = Math.abs( room.x - largest.x ) + Math.abs( room.y - largest.y ); Pair pair = new Pair( dist, room ); sortedRooms.add( pair ); unassignedRooms.add( pair ); } else if ( !room.faction.equalsIgnoreCase( "none" ) ) { FactionParser fp = null; if (room.faction.equals( majorFaction.name )) { fp = majorFaction; } else { for (FactionParser minor : minorFactions) { if ( minor.name.equals( room.faction ) ) { fp = minor; unplacedMinorFactions.removeValue( minor, true ); break; } } } if (fp == null) { fp = FactionParser.load( room.faction ); } if ( fp != null && fp != majorFaction ) { int influence = ran.nextInt( 50 ) + 30; room.addFeatures( ran, dfp, fp, influence, false ); factions.put( fp, new Point( room.x + room.width / 2, room.y + room.height / 2 ) ); } else { int dist = Math.abs( room.x - largest.x ) + Math.abs( room.y - largest.y ); sortedRooms.add( new Pair( dist, room ) ); } } } sortedRooms.sort(); while (unplacedMinorFactions.size > 0 && unassignedRooms.size > 0) { Pair pair = unassignedRooms.removeIndex( unassignedRooms.size - 1 ); sortedRooms.removeValue( pair, true ); int influence = ran.nextInt( 80 ) + 10; FactionParser fp = unplacedMinorFactions.removeIndex( 0 ); pair.room.addFeatures( ran, dfp, fp, influence, false ); factions.put( fp, new Point( pair.room.x + pair.room.width / 2, pair.room.y + pair.room.height / 2 ) ); } for (FactionParser fp : unplacedMinorFactions) { int x = ran.nextInt( width ); int y = ran.nextInt( height ); factions.put( fp, new Point( x, y ) ); } // Add features boolean spawnMiniboss = !saveLevel.isBossLevel; for ( int i = 0; i < sortedRooms.size; i++ ) { Pair pair = sortedRooms.get( i ); int influence = pair.dist; if ( influence > 0 ) { float fract = influence / (float) ( width + height ); influence = (int) ( fract * 100 ); } influence = 100 - influence; System.out.println(influence); boolean spawnedMiniboss = pair.room.addFeatures( ran, dfp, majorFaction, influence, spawnMiniboss ); if (spawnedMiniboss) { spawnMiniboss = false; } } } // ---------------------------------------------------------------------- protected Level createLevel( Symbol[][] symbolGrid, Symbol outerWall ) { FactionParser fp = majorFaction; if ( DEBUG_OUTPUT ) { DEBUG_printGrid( symbolGrid ); } // minimise symbolGrid = minimiseGrid( symbolGrid, outerWall ); width = symbolGrid.length; height = symbolGrid[0].length; //if ( DEBUG_OUTPUT ) { DEBUG_printGrid( symbolGrid ); } LevelManager.LevelData levelData = Global.LevelManager.getLevel( Global.LevelManager.current, saveLevel.fileName ); GameTile[][] actualTiles = new GameTile[width][height]; Level level = new Level( actualTiles ); level.Ambient = dfp.ambient; // level.affectedByDayNight = dfp.affectedByDayNight; level.bgmName = dfp.BGM; level.ambientSounds.addAll( dfp.ambientSounds ); level.depth = saveLevel.depth; level.fileName = saveLevel.fileName; level.seed = saveLevel.seed; level.requiredRooms = additionalRooms; level.background = dfp.background; level.isVisionRestricted = dfp.visionRestricted; for ( int x = 0; x < width; x++ ) { for ( int y = 0; y < height; y++ ) { Symbol symbol = symbolGrid[x][y]; GameTile newTile = new GameTile( x, y, level, symbol.getTileData(), ran.nextFloat() ); newTile.metaValue = symbol.metaValue; newTile.metaValue = symbol.metaValue; actualTiles[x][y] = newTile; // System.out.print(symbol.character); } // System.out.print("\n"); } for ( int x = 0; x < width; x++ ) { for ( int y = 0; y < height; y++ ) { Symbol symbol = symbolGrid[x][y]; if ( !saveLevel.created && symbol.hasEnvironmentEntity() ) { GameTile newTile = actualTiles[x][y]; EnvironmentEntity entity = symbol.getEnvironmentEntity( levelData ); if ( entity.attachToWall ) { Direction location = Direction.CENTER; if ( symbol.attachLocation != null ) { location = symbol.attachLocation; } else { // get direction HashSet<Direction> validDirections = new HashSet<Direction>(); for ( Direction dir : Direction.values() ) { boolean passable = symbolGrid[x + dir.getX()][y + dir.getY()].getTileData().passableBy.getBitFlag() != 0; if ( !passable ) { validDirections.add( dir ); } } if ( validDirections.size() > 0 ) { if ( location == Direction.CENTER ) { for ( Direction dir : Direction.values() ) { if ( dir.isCardinal() ) { if ( validDirections.contains( dir ) ) { location = dir; break; } } } } // look for direction with full surround for ( Direction dir : Direction.values() ) { boolean acwvalid = validDirections.contains( dir.getAnticlockwise() ); boolean valid = validDirections.contains( dir ); boolean cwvalid = validDirections.contains( dir.getClockwise() ); if ( acwvalid && valid && cwvalid ) { location = dir; break; } } // If that failed then just try the cardinal // directions // else pick random if ( location == Direction.CENTER ) { location = validDirections.toArray( new Direction[validDirections.size()] )[ran.nextInt( validDirections.size() )]; } location = location.getOpposite(); } } entity.location = location; // entity.sprite.rotation = location.getAngle(); } else { if ( symbol.containingRoom != null && symbol.containingRoom.orientation != Direction.CENTER && symbol.environmentData.getBoolean( "MatchRoomRotation", false ) ) { entity.sprite.rotation = symbol.containingRoom.orientation.getAngle(); } } newTile.addEnvironmentEntity( entity ); } if ( !saveLevel.created && symbol.fieldData != null ) { GameTile newTile = actualTiles[x][y]; Field field = Field.load( symbol.fieldData.getText() ); field.stacks = symbol.fieldData.getIntAttribute( "Stacks", 1 ); newTile.addField( field ); } if ( !saveLevel.created && symbol.hasGameEntity() ) { GameTile newTile = actualTiles[x][y]; GameEntity e = null; if (symbol.entityData instanceof String) { String entityPath = (String)symbol.entityData; if ( entityPath.equals( "Boss" ) ) { e = GameEntity.load( fp.bosses.get( ran.nextInt( fp.bosses.size ) ) ); } else if ( Global.isNumber( entityPath ) ) { int index = Integer.parseInt( entityPath ); index = (int) ( ( index / 9.0f ) * fp.creatures.size ); e = GameEntity.load( fp.creatures.get( index ).entityName ); } else { e = GameEntity.load( entityPath ); } } else { Array<XmlReader.Element> data = new Array<XmlReader.Element>( ); data.add( ( XmlReader.Element)symbol.entityData ); e = GameEntity.load( data ); } if ( e != null ) { newTile.addGameEntity( e ); e.spawnPos = new Point( newTile ); if (Global.LevelManager != null && Global.LevelManager.totalDepth > 0) { // Add stat scaling e.applyDepthScaling(); e.isVariableMapDirty = true; e.HP = e.getMaxHP(); } } } } } saveLevel.addSavedLevelContents( level ); level.depth = saveLevel.depth; level.UID = saveLevel.UID; level.calculateAmbient(); return level; } // ---------------------------------------------------------------------- public static AbstractDungeonGenerator load( SaveLevel level ) { DungeonFileParser dfp = DungeonFileParser.load( level.fileName + "/" + level.fileName ); Class<AbstractDungeonGenerator> c = ClassMap.get( dfp.generator.toUpperCase() ); AbstractDungeonGenerator type = null; try { type = ClassReflection.newInstance( c ); } catch ( Exception e ) { e.printStackTrace(); } type.setup( level, dfp ); return type; } // ---------------------------------------------------------------------- protected static HashMap<String, Class> ClassMap = new HashMap<String, Class>(); // ---------------------------------------------------------------------- static { ClassMap.put( "RECURSIVEDOCK", RecursiveDockGenerator.class ); ClassMap.put( "STATIC", StaticLevelGenerator.class ); } }