package pl.edu.pw.elka.mmarkiew.model;
import java.awt.Point;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Random;
import pl.edu.pw.elka.mmarkiew.model.entities.Entity;
import pl.edu.pw.elka.mmarkiew.model.entities.EntityFactory;
import pl.edu.pw.elka.mmarkiew.model.entities.GameEntities;
import pl.edu.pw.elka.mmarkiew.model.entities.Player;
import pl.edu.pw.elka.mmarkiew.model.entities.bonus.Bonus;
import pl.edu.pw.elka.mmarkiew.model.entities.enemies.Enemy;
import pl.edu.pw.elka.mmarkiew.model.map.BlockFactory;
import pl.edu.pw.elka.mmarkiew.model.map.GameBlock;
/**
* Responsible for loading map
* @author Kajo
*
*/
public class MapLoader {
private Player playerEntity;
private int level;
private final int maxLevel = 10;
/**
* Creates player and set him on 0 level
*/
public MapLoader()
{
this.level = 0;
this.playerEntity = (Player) EntityFactory.createEntity(GameEntities.PLAYER);
}
public void reset()
{
this.level = 0;
this.playerEntity = (Player) EntityFactory.createEntity(GameEntities.PLAYER);
}
/**
* Load next map from file ${++level}.txt
*
* @return Loaded or generated map
* @throws WinGameException - When game is win
*/
public GameMap loadNextMap() throws WinGameException
{
++level;
try
{
if (level > maxLevel)
{
throw new WinGameException();
}
else
{
return loadMap();
}
}
catch (IOException e)
{
return generateMap();
}
catch (NullPointerException e)
{
// If there is no ${level}.txt map generate next map
return generateMap();
}
}
/**
* Load Map from file
*
* @param path - Path to map
* @return Map of game
* @throws NullPointerException - If source not found
* @throws IOException - If IOException
*/
private GameMap loadMap() throws NullPointerException, IOException
{
final ArrayList<String> listOfLines = new ArrayList<String>();
final BufferedReader buffer = new BufferedReader(new InputStreamReader(getClass()
.getResourceAsStream("/" + level + ".txt")));
int width = 0;
int height = 0;
String line;
while( (line = buffer.readLine()) != null)
{
listOfLines.add(line);
width = Math.max(width, line.length());
}
buffer.close();
height = listOfLines.size();
return analyzeMap(listOfLines, width, height);
}
/**
* Analyzes .txt file with map
*
* @param listOfLines - List of map lines
* @param width - Maximum length of line
* @param height - Number of lines
* @return game of map
*/
private GameMap analyzeMap(final ArrayList<String> listOfLines, final int width, final int height)
{
final Iterator<String> it = listOfLines.iterator();
String line;
GameMap tempMap = new GameMap(this.playerEntity, width, height);
int j = 0;
while (it.hasNext())
{
line = it.next();
for (int i = 0; i < width; ++i)
{
if (i < line.length())
{
chooseBlockEntityToCreate(tempMap, line.charAt(i), i, j);
}
else
{
tempMap.setBlock(BlockFactory.createElement(GameBlock.EMPTY), i, j);
}
}
++j;
}
// Sets player Velocity to 0
playerEntity.setXVelocity(0);
playerEntity.setYVelocity(0);
// If there was no specific of player Position sets it on (1,1) tile
if (tempMap.getPlayerStartPosition().equals(new Point(0, 0)))
{
tempMap.setPlayerStartPosition(GameMap.getPositionCenterFromTile(1), GameMap.getPositionCenterFromTile(1));
}
playerEntity.setX((float) tempMap.getPlayerStartPosition().getX());
playerEntity.setY((float) tempMap.getPlayerStartPosition().getY());
return tempMap;
}
/**
* Choosing appropriate entity or/and block to create
*
* @param tempMap - Actual game of map
* @param charAt - Character to analyze
* @param i - Tile x position
* @param j - Tile y position
*/
private void chooseBlockEntityToCreate(final GameMap tempMap, final char charAt, final int i, final int j)
{
// Create appropriate block on this position
tempMap.setBlock(BlockFactory.createElement( GameBlock.getEnumBlock("" + charAt) ), i, j);
// Sets player position if it is Player character
if ( GameEntities.getEnumEntity("" + charAt) == GameEntities.PLAYER)
{
tempMap.setPlayerStartPosition(GameMap.getPositionCenterFromTile(i), GameMap.getPositionCenterFromTile(j));
}
else
{
// Create appropriate entity and possibly hide it behind destructive block
if ( GameEntities.getEnumEntity("" + charAt) != GameEntities.UNDEFINED)
{
Entity e = EntityFactory.createEntity( GameEntities.getEnumEntity("" + charAt),
(int) GameMap.getPositionCenterFromTile(i), (int) GameMap.getPositionCenterFromTile(j));
// If it's enemy add it into map else if it's bonus add it into map and hide it
if (e instanceof Enemy)
{
tempMap.addEnemy(e);
}
else if (e instanceof Bonus)
{
tempMap.addBonus((Bonus) e);
tempMap.setBlock(BlockFactory.createElement(GameBlock.BRICK), i, j);
}
}
}
}
/**
* Generates random map
*
* @return Created random map
* @throws WinGameException - If level > maxLevel => WIN
*/
private GameMap generateMap() throws WinGameException
{
if (level > maxLevel)
{
throw new WinGameException();
}
int dimension = 10 + level;
dimension += (dimension % 2 == 0) ? 1 : 0;
// How often & how much bonuses are generate
final int bonusSpawn = 5;
int bonusAmount = level + 5;
// How often & how much enemies are generate
final int enemySpawn = 150 / level + 2;
int enemyAmount = level * 2;
final ArrayList<String> lines = new ArrayList<String>();
StringBuilder line = new StringBuilder();
final Random rand = new Random();
// Generating map
for (int i = 0; i < dimension; ++i)
{
line.setLength(0);
for (int j = 0; j < dimension; ++j)
{
// Borders & inside blocks
if (i % (dimension - 1) == 0 || j % (dimension - 1) == 0 || i % 2 == 0 && j % 2 == 0)
{
line.append(GameBlock.STONE.getCharacter());
}
else
{
// Left player some spawn space
if (i == 1 && j <= 3 || j == 1 && i <= 3)
{
line.append(GameBlock.EMPTY);
}
else
{
// Rand bonus, enemy or brick
if (bonusAmount > 0 || enemyAmount > 0)
{
if (bonusAmount > 0 && rand.nextInt(bonusSpawn) == 0)
{
line.append(GameEntities.getRandomBonusCharacter());
--bonusAmount;
}
else if (enemyAmount > 0 && rand.nextInt(enemySpawn) == 0)
{
line.append(GameEntities.getRandomEnemyCharacter());
--enemyAmount;
}
else
{
line.append(GameBlock.values()[rand.nextInt(2) + 1].getCharacter());
}
}
else
{
line.append(GameBlock.values()[rand.nextInt(2) + 1].getCharacter());
}
}
}
}
lines.add(line.toString());
}
return analyzeMap(lines, dimension, dimension);
}
Player getPlayer()
{
return playerEntity;
}
int getLevel()
{
return level;
}
}