package vooga.rts.map;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.ObjectInputStream.GetField;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import util.Location;
import vooga.rts.IGameLoop;
import vooga.rts.ai.Path;
import vooga.rts.ai.PathFinder;
import vooga.rts.gamedesign.sprite.gamesprites.GameEntity;
import vooga.rts.gamedesign.sprite.gamesprites.GameSprite;
import vooga.rts.gamedesign.sprite.gamesprites.Resource;
import vooga.rts.gamedesign.sprite.gamesprites.interactive.InteractiveEntity;
import vooga.rts.gamedesign.sprite.map.Terrain;
import vooga.rts.resourcemanager.ResourceManager;
import vooga.rts.util.Camera;
import vooga.rts.util.Location3D;
import vooga.rts.util.TimeIt;
/**
* The GameMap manages all aspects of the map on the screen.
* This includes the underlying tiles, the terrain objects on
* the map and the nodes used for pathfinding.
*
* In addition to this, the map will be responsible for painting
* everything that is in the world. This is to ensure that objects
* are painted in the correct order.
*
* @author Challen Herzberg-Brovold
* @author Jonathan Schmidt
*
*/
public class GameMap implements IGameLoop {
private static final int CLICK_RADIUS = 100;
private NodeMap myNodeMap;
private TileMap myTiles;
private GameSpriteManager<Terrain> myTerrain;
private GameSpriteManager<Resource> myResources;
private Dimension mySize;
private String myMapName;
private String myMapDescription;
private List<Location3D> myPlayerLocations;
/**
* calculates how many nodes there are
*
* @param mapSize This is the size of the map in pixels
*/
public GameMap (Dimension size) {
mySize = size;
NodeFactory factory = new NodeFactory();
myNodeMap = factory.makeMap(Node.NODE_SIZE, size);
myTerrain = new GameSpriteManager<Terrain>();
myResources = new GameSpriteManager<Resource>();
myPlayerLocations = new ArrayList<Location3D>();
Camera.instance().setMapSize(size);
}
public GameMap (Dimension size, boolean random) {
this(size);
Dimension dou = new Dimension((int) size.getWidth(), (int) (size.getHeight() * 2));
randomGenMap(dou);
}
public GameMap (Dimension tileSize, int width, int height) {
this(new Dimension((int) tileSize.getWidth() * width, (int) tileSize.getHeight() * height));
myTiles = new TileMap(tileSize, width, height);
}
/**
* @return The Terrain manager
*/
public GameSpriteManager<Terrain> getTerrain () {
return myTerrain;
}
/**
* @return The Resource manager
*/
public GameSpriteManager<Resource> getResources () {
return myResources;
}
public Path getPath (PathFinder finder, Location3D start, Location3D finish) {
return finder.calculatePath(getNodeMap().getNode(start), getNodeMap().getNode(finish),
myNodeMap);
}
public NodeMap getNodeMap () {
return myNodeMap;
}
/**
* Returns a list of Interactive Entities that are within a specified radius of
* a central point. This can be used by the units to find targets in the area.
*
* @param loc The Location to search from
* @param radius The radius of the circle to search in
* @param type The type of unit to search for. This will compare on the name of the unit.
* @param teamID The team ID.
* @param same Whether to search for things of the same team or of the other teams.
* @return
*/
public <T extends GameEntity> List<T> getInArea (Location3D loc,
double radius,
Class<T> type,
int teamID,
boolean same) {
List<T> inRange = new ArrayList<T>();
List<Node> nodesinArea = myNodeMap.getNodesinArea(loc, radius);
for (Node n : nodesinArea) {
inRange.addAll(n.<T> filterGameSprites(type, teamID, same));
}
return GameMap.sortByDistance(inRange, loc);
}
@Override
public void update (double elapsedTime) {
myTerrain.update(elapsedTime);
myResources.update(elapsedTime);
}
@Override
public void paint (Graphics2D pen) {
myTiles.paint(pen);
myNodeMap.paint(pen);
}
/**
* Returns a Game Entity that is at the specified location.
*
* @param loc The location to search for an interactive entity.
* @return The Game Entity if it exists, otherwise null.
*/
public GameEntity getEntity (Location3D loc) {
List<Node> myNodes = myNodeMap.getNodesinArea(loc, CLICK_RADIUS);
for (Node n : myNodes) {
for (GameSprite gs : n.getContents()) {
if (gs instanceof GameEntity) {
if (gs.intersects(loc)) {
return (GameEntity) gs;
}
}
}
}
return null;
}
private void randomGenMap (Dimension size) {
int tileWidth = 64;
int tileHeight = 64;
int tilesX = (int) size.getWidth() / tileWidth;
int tilesY = (int) size.getHeight() / tileHeight;
myTiles = new TileMap(new Dimension(tileWidth, tileHeight), tilesX, tilesY);
BufferedImage banana =
ResourceManager.getInstance()
.<BufferedImage> getFile("images/tiles/iso-64x64-outside.png",
BufferedImage.class);
myTiles.addTileType(1, banana.getSubimage(6 * tileWidth, 0, tileWidth, tileHeight));
myTiles.addTileType(2, banana.getSubimage(4 * tileWidth, 0, tileWidth, tileHeight));
for (int i = 0; i < tilesX; i++) {
for (int j = 0; j < tilesY; j++) {
if (Math.random() < 0.2) {
myTiles.createTile(2, i, j);
}
else {
myTiles.createTile(1, i, j);
}
}
}
Camera.instance().setMapSize(size);
}
public void setTileMap (TileMap map) {
myTiles = map;
}
public Dimension getSize () {
return mySize;
}
/**
* @return the name of the map
*/
public String getMapName () {
return myMapName;
}
/**
* Set the name of the map
*
* @param mapName the name of the map
*/
public void setMapName (String mapName) {
myMapName = mapName;
}
/**
* @return the description of the map
*/
public String getMapDescription () {
return myMapDescription;
}
/**
* Set the description of the map.
*
* @param mapDescription the description of the map
*/
protected void setMapDescription (String mapDescription) {
myMapDescription = mapDescription;
}
/**
* @return a list of locations that players can start at
*/
public List<Location3D> getPlayerLocations () {
return myPlayerLocations;
}
/**
* Adds a location that a player can start at on the map.
*/
protected void addPlayerLocation (Location3D loc) {
myPlayerLocations.add(loc);
}
private static <T extends GameEntity> List<T> sortByDistance (List<T> items,
Location3D fromPosition) {
Map<Double, T> myDistances = new TreeMap<Double, T>();
List<T> mySorted = new ArrayList<T>();
for (T t : items) {
double distance = t.getWorldLocation().getDistance(fromPosition);
myDistances.put(distance, t);
}
mySorted.addAll(myDistances.values());
return mySorted;
}
}