package rts.core.engine.map;
import java.awt.Point;
import java.io.InputStream;
import java.util.ArrayList;
import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.tiled.TiledMap;
import org.newdawn.slick.util.pathfinding.PathFindingContext;
import org.newdawn.slick.util.pathfinding.TileBasedMap;
import rts.core.engine.Engine;
import rts.core.engine.Player;
import rts.core.engine.layers.entities.ActiveEntity;
import rts.core.engine.layers.entities.EData;
import rts.core.engine.layers.entities.EntityGenerator;
import rts.core.engine.layers.entities.MoveableEntity;
import rts.core.engine.layers.entities.effects.Lava;
import rts.core.engine.layers.entities.effects.Swamp;
import rts.core.engine.layers.entities.others.Wall;
import rts.utils.Configuration;
import rts.utils.ResourceManager;
/**
* This class represent a map object.
*
* The map is defined in 5 layers
*
* 0. First decorating layer 1. Second decorating layer (objects) 2. Earth and
* water collision layer 3. Spawn layer 4. Start entities
*
* We only render an image (layer 1 & 2) that represent the map (because it's
* less resources consuming)
*
* @author Vincent PIRAULT
*
*/
public class Map extends TiledMap implements TileBasedMap, Comparable<Map> {
// Debug
private static final boolean ENABLE_FOG = true;
private static final Color FADE_RED = new Color(255, 0, 0, 100);
private boolean[][] blocked;
private boolean[][] water;
private boolean needScroll;
private boolean visibleFow;
private EntityLocation[][] entitiesLocations;
private Wall wallLocations[][];
private ArrayList<Point> spawns;
private ArrayList<Ent> entites;
private Image background;
private String name;
private ArrayList<ActiveEntity> rendererEntities;
public static long startTime;
// FOW
private boolean enableFow;
private Image fowImage;
private boolean[][] visibleLocations;
public Map(String name, InputStream ref, String tilePath) throws SlickException {
super(ref, tilePath);
this.name = name;
this.blocked = new boolean[width][height];
this.water = new boolean[width][height];
this.entitiesLocations = new EntityLocation[width][height];
this.wallLocations = new Wall[width][height];
this.spawns = new ArrayList<Point>();
this.entites = new ArrayList<Ent>();
this.visibleLocations = new boolean[width][height];
this.rendererEntities = new ArrayList<ActiveEntity>();
this.enableFow = ENABLE_FOG;
this.visibleFow = true;
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
entitiesLocations[x][y] = new EntityLocation();
// Collisions
int tileID = this.getTileId(x, y, 2);
String value = this.getTileProperty(tileID, "collision", "false");
blocked[x][y] = value.equals("true");
// Water
value = this.getTileProperty(tileID, "water", "false");
water[x][y] = value.equals("true");
// Spawns
tileID = this.getTileId(x, y, 3);
value = this.getTileProperty(tileID, "spawn", "false");
if (value.equals("true")) {
spawns.add(new Point(x, y));
}
// Entities
tileID = this.getTileId(x, y, 4);
int type = Integer.parseInt(this.getTileProperty(tileID, "type", "-1"));
if (type >= 0) {
Ent ent = new Ent();
ent.type = type;
ent.life = Integer.parseInt(this.getTileProperty(tileID, "life", "100"));
ent.x = x * tileWidth;
ent.y = y * tileHeight;
entites.add(ent);
}
}
}
background = ResourceManager.getImage(name);
fowImage = ResourceManager.getImage("fow");
}
public void init(Engine engine) {
startTime = System.currentTimeMillis();
// Need scroll ?
needScroll = (getWidthInPixel() > engine.getContainer().getWidth() || getHeightInPixel() > engine.getContainer().getHeight());
// Reset ents location
for (int i = 0; i < entitiesLocations.length; i++) {
for (int j = 0; j < entitiesLocations[i].length; j++) {
entitiesLocations[i][j].clear();
}
}
// Reset FOW
for (int i = 0; i < visibleLocations.length; i++) {
for (int j = 0; j < visibleLocations[i].length; j++) {
visibleLocations[i][j] = false;
}
}
if (engine.isNetwork()) {
// Create own builder
Player player = engine.getPlayer();
Point p = spawns.get(player.getSpawn());
engine.getNetworkManager().sendCreateEntity(EData.MOVER_BUILDER, player.getId(), player.getTeamId(), p.x * tileWidth, p.y * tileHeight);
// Center scroll in builder
engine.centerScrollOn(p.x * tileWidth, p.y * tileHeight);
// Add 3 entities to begin
engine.getNetworkManager().sendCreateEntity(EData.MOVER_SOLDIER, player.getId(), player.getTeamId(), (p.x - 3) * tileWidth, p.y * tileHeight);
engine.getNetworkManager().sendCreateEntity(EData.MOVER_SOLDIER, player.getId(), player.getTeamId(), (p.x + 3) * tileWidth, p.y * tileHeight);
engine.getNetworkManager().sendCreateEntity(EData.MOVER_SCOUT, player.getId(), player.getTeamId(), p.x * tileWidth, (p.y + 3) * tileHeight);
if (engine.getNetworkManager().isServer()) {
// Send entity associated to the map
for (int i = 0; i < entites.size(); i++) {
if (!effectEntity(engine, entites.get(i)))
engine.getNetworkManager().sendCreateEntity(entites.get(i).type, -1, -1, entites.get(i).life, entites.get(i).x, entites.get(i).y);
}
}
} else {
for (int i = 0; i < entites.size(); i++) {
if (!effectEntity(engine, entites.get(i))) {
ActiveEntity ae = EntityGenerator.createActiveEntityFromMap(engine, entites.get(i).type, entites.get(i).x, entites.get(i).y);
ae.setLife(entites.get(i).life);
engine.addEntity(ae);
}
}
}
}
private boolean effectEntity(Engine engine, Ent ent) {
if (ent.type == EData.LAVA_EFFECT) {
engine.addEntity(new Lava(engine, ent.x, ent.y));
return true;
} else {
if (ent.type == EData.SWAMP_EFFECT) {
engine.addEntity(new Swamp(engine, ent.x, ent.y));
return true;
}
}
return false;
}
// Walls
public Wall getWall(int x, int y) {
if (x >= 0 && x < width && y >= 0 && y < height)
return wallLocations[x][y];
else
return null;
}
public void setWall(Wall wall, int x, int y) {
if (x >= 0 && x < width && y >= 0 && y < height)
wallLocations[x][y] = wall;
}
public void removeWall(int x, int y) {
if (x >= 0 && x < width && y >= 0 && y < height)
wallLocations[x][y] = null;
}
// PathFinding
public boolean blocked(MoveableEntity e, int tx, int ty) {
if (tx >= 0 && tx < width && ty >= 0 && ty < height) {
if (e.getMoveType() == MoveableEntity.EVERYWHERE)
return entitiesLocations[tx][ty].isBlocked();
else {
if (e.getMoveType() == MoveableEntity.EARTH_ONLY) {
return blocked[tx][ty] || entitiesLocations[tx][ty].isBlocked() || water[tx][ty];
} else {
if (e.getMoveType() == MoveableEntity.WATER_ONLY) {
return blocked[tx][ty] || entitiesLocations[tx][ty].isBlocked() || !water[tx][ty];
}
}
}
}
return true;
}
public boolean isBlocked(int x, int y) {
if (x >= 0 && x < width && y >= 0 && y < height)
return blocked[x][y];
else
return true;
}
@Override
public boolean blocked(PathFindingContext context, int tx, int ty) {
if (context.getMover() instanceof MoveableEntity) {
return blocked((MoveableEntity) context.getMover(), tx, ty);
}
return true;
}
@Override
public float getCost(PathFindingContext context, int tx, int ty) {
// Le cout d'un chemin d�pend du brouillard de guerre
//(visibleLocations[tx][ty]) ? 0 : 10;
return 1;
}
@Override
public void pathFinderVisited(int x, int y) {
}
public void blockWithWater(int x, int y) {
water[x][y] = true;
}
public void freeWithWater(int x, int y) {
water[x][y] = false;
}
public boolean isWater(int x, int y) {
if (x >= 0 && x < width && y >= 0 && y < height)
return water[x][y];
else
return false;
}
public void addEntityLocation(ActiveEntity entity, boolean block, int x, int y) {
entitiesLocations[x][y].addEntity(entity, block);
}
public void removeEntityLocation(ActiveEntity entity, int x, int y) {
entitiesLocations[x][y].removeEntity(entity);
}
public boolean isEntityBlocked(int x, int y) {
if (x >= 0 && x < width && y >= 0 && y < height)
return entitiesLocations[x][y].isBlocked();
else
return true;
}
public boolean isEntityOccupy(int x, int y) {
if (x >= 0 && x < width && y >= 0 && y < height)
return entitiesLocations[x][y].isOccupy();
else
return true;
}
public ActiveEntity getEntityAt(ActiveEntity entity, int x, int y) {
if (x >= 0 && x < width && y >= 0 && y < height) {
return (entitiesLocations[x][y].getLastEntity() == entity) ? null : entitiesLocations[x][y].getLastEntity();
} else
return null;
}
// Rendering
public void render(Graphics g, GameContainer container, int decalX, int decalY) {
if (needScroll) {
g.drawImage(background.getSubImage(decalX, decalY, (decalX > (getWidthInPixel() - container.getWidth())) ? container.getWidth()
- (decalX - (getWidthInPixel() - container.getWidth())) : decalX + container.getWidth(), decalY + container.getHeight()), 0, 0);
} else
g.drawImage(background, 0, 0);
if (Configuration.isDebug()) {
// TODO DEBUG REMOVE
g.translate(-decalX, -decalY);
g.setColor(FADE_RED);
for (int i = 0; i < blocked.length; i++) {
for (int j = 0; j < blocked[i].length; j++) {
if (blocked[i][j] || entitiesLocations[i][j].isBlocked()) {
g.fillRect(i * 20, j * 20, 20, 20);
}
}
}
g.translate(decalX, decalY);
}
}
public void renderFow(Graphics g, GameContainer container, int decalX, int decalY) {
if (enableFow && visibleFow) {
int fx = (decalX / tileWidth - 1 < 0) ? 0 : decalX / tileWidth - 1;
int fy = (decalY / tileHeight - 1 < 0) ? 0 : decalY / tileHeight - 1;
int ex = (fx + (container.getWidth() / tileWidth) + 2 > width) ? width : (fx + (container.getWidth() / tileWidth)) + 2;
int ey = (fy + (container.getHeight() / tileHeight) + 2 > height) ? height : (fy + (container.getHeight() / tileHeight)) + 2;
for (int i = fx; i < ex; i++) {
for (int j = fy; j < ey; j++) {
if (!visibleLocations[i][j])
g.drawImage(fowImage, (i * tileWidth) - 20, (j * tileHeight) - 20);
}
}
}
}
public void renderMiniMap(Graphics g, int x, int y, int width, int height, boolean drawFromMenu) {
g.drawImage(background.getScaledCopy(width, height), x, y);
float tw = ((float) width * tileWidth) / getWidthInPixel();
float th = ((float) height * tileHeight) / getHeightInPixel();
if (drawFromMenu) {
g.setColor(Color.red);
for (int i = 0; i < spawns.size(); i++) {
g.drawString("" + (i + 1), (x + spawns.get(i).x * tw) - 2 * tw, (y + spawns.get(i).y * th) - 2 * th);
}
} else {
rendererEntities.clear();
for (int i = 0; i < visibleLocations.length; i++) {
for (int j = 0; j < visibleLocations[i].length; j++) {
if (!visibleLocations[i][j] && enableFow && visibleFow) {
g.setColor(Color.black);
g.fillRect(x + i * tw, y + j * th, tw, th);
} else {
ActiveEntity ae = entitiesLocations[i][j].getLastEntity();
if (ae != null && !rendererEntities.contains(ae)) {
ae.renderOnMiniMap(g, x + i * tw, y + j * th, tw, th);
rendererEntities.add(ae);
}
}
}
}
}
}
public boolean isNeededScroll() {
return needScroll;
}
public boolean fogOn(int x, int y) {
if (!enableFow || (enableFow && !visibleFow)) {
return false;
} else {
if (x >= 0 && x < width && y >= 0 && y < height) {
return !visibleLocations[x][y];
}
return true;
}
}
public void setVisibleFow(boolean visibleFow) {
this.visibleFow = visibleFow;
}
public void showFow(int x, int y) {
if (x >= 0 && x < width && y >= 0 && y < height)
visibleLocations[x][y] = true;
}
public int getWidthInPixel() {
return width * tileWidth;
}
public String getName() {
return name;
}
public int getHeightInPixel() {
return height * tileHeight;
}
@Override
public int getHeightInTiles() {
return height;
}
@Override
public int getWidthInTiles() {
return width;
}
public boolean isEnableFow() {
return enableFow;
}
public int getNumberOfSpawns() {
return spawns.size();
}
@Override
public String toString() {
return "[" + (spawns.size() - 1) + " Opponents] " + name;
}
private static class Ent {
public int type;
public int life;
public int x;
public int y;
}
private static class EntityLocation {
private ArrayList<Entity> entities;
public EntityLocation() {
entities = new ArrayList<Entity>();
}
private boolean contain(ActiveEntity e) {
for (int i = 0; i < entities.size(); i++) {
if (entities.get(i).entity == e)
return true;
}
return false;
}
public void addEntity(ActiveEntity e, boolean block) {
if (!contain(e))
entities.add(new Entity(e, block));
}
public void removeEntity(ActiveEntity e) {
for (int i = 0; i < entities.size(); i++) {
if (entities.get(i).entity == e) {
entities.remove(i);
break;
}
}
}
public ActiveEntity getLastEntity() {
if (entities.isEmpty())
return null;
else
return entities.get(entities.size() - 1).entity;
}
public boolean isBlocked() {
for (int i = 0; i < entities.size(); i++) {
if (entities.get(i).blocked)
return true;
}
return false;
}
public boolean isOccupy() {
return !entities.isEmpty();
}
public void clear() {
entities.clear();
}
private static class Entity {
private ActiveEntity entity;
private boolean blocked;
public Entity(ActiveEntity entity, boolean blocked) {
super();
this.entity = entity;
this.blocked = blocked;
}
}
}
@Override
public int compareTo(Map map) {
return this.spawns.size() - map.spawns.size();
}
}