/*******************************************************************************
* Copyright (c) 2013 Philip Collin.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*
* Contributors:
* Philip Collin - initial API and implementation
******************************************************************************/
package com.lyeeedar.Roguelike3D.Game.Level;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import com.badlogic.gdx.graphics.Camera;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.math.collision.Ray;
import com.badlogic.gdx.utils.Pools;
import com.lyeeedar.Graphics.ParticleEffects.ParticleEmitter;
import com.lyeeedar.Roguelike3D.Game.GameData;
import com.lyeeedar.Roguelike3D.Game.Actor.GameActor;
import com.lyeeedar.Roguelike3D.Game.Actor.Player;
import com.lyeeedar.Roguelike3D.Game.Level.MapGenerator.GeneratorType;
import com.lyeeedar.Roguelike3D.Game.Level.XML.BiomeReader;
import com.lyeeedar.Roguelike3D.Game.Level.XML.MonsterEvolver;
import com.lyeeedar.Roguelike3D.Game.Level.XML.RoomReader;
import com.lyeeedar.Roguelike3D.Game.LevelObjects.LevelObject;
import com.lyeeedar.Roguelike3D.Graphics.Lights.LightManager;
import com.lyeeedar.Roguelike3D.Graphics.Renderers.Renderer;
import com.lyeeedar.Utils.Bag;
public class Level implements Serializable {
private static final long serialVersionUID = 7198101723293369502L;
private static final int VIEW_STEP = 10;
public static final String MONSTER_TYPE = "monster_type";
public final Tile[][] levelArray;
private int bx; private int bz;
private int tx; private int tz;
private float radius2;
private final Tile[][] block = new Tile[3][3];
public final HashMap<Character, String> shortDescs = new HashMap<Character, String>();
public final HashMap<Character, String> longDescs = new HashMap<Character, String>();
public final HashMap<Character, Color> colours = new HashMap<Character, Color>();
public final Bag<Character> opaques = new Bag<Character>();
public final Bag<Character> solids = new Bag<Character>();
private transient final Bag<DungeonRoom> rooms;
public final int width;
public final int height;
public final boolean hasRoof;
public final int depth;
public final GeneratorType gtype;
public Level(int width, int height, GeneratorType gtype, BiomeReader biome, boolean hasRoof, int depth, int up, int down)
{
this.depth = depth;
this.hasRoof = hasRoof;
this.gtype = gtype;
this.width = width;
this.height = height;
solids.add('#');
solids.add(' ');
opaques.add('#');
opaques.add(' ');
colours.put('#', biome.getWallColour());
colours.put('.', biome.getFloorColour());
colours.put(' ', biome.getWallColour());
shortDescs.put('#', biome.getShortDescription('#'));
longDescs.put('#', biome.getLongDescription('#'));
shortDescs.put('.', biome.getShortDescription('.'));
longDescs.put('.', biome.getLongDescription('.'));
shortDescs.put('R', biome.getShortDescription('R'));
longDescs.put('R', biome.getLongDescription('R'));
MapGenerator generator = new MapGenerator(width, height, solids, opaques, colours, gtype, biome, up, down);
levelArray = generator.getLevel();
rooms = generator.getRooms();
for (AbstractObject ao : generator.getObjects())
{
LevelObject lo = LevelObject.checkObject(ao, (ao.x)*10, 0, (ao.z)*10, this, null);
if (lo != null)
{
addLevelObject(lo);
}
}
}
public boolean fillRoom(RoomReader rReader, LevelContainer lc)
{
if (rooms.size == 0)
{
return true;
}
DungeonRoom room = rooms.remove(0);
AbstractRoom aroom = rReader.getRoom(room.roomtype, room.width, room.height, (gtype != GeneratorType.STATIC));
if (aroom == null) {
System.err.println("Failed to place "+room.roomtype);
return false;
}
System.out.println("Placed room "+room.roomtype);
ArrayList<AbstractObject> abstractObjects = new ArrayList<AbstractObject>();
MonsterEvolver evolver = null;
if (aroom.meta.containsKey(MONSTER_TYPE))
{
evolver = lc.getMonsterEvolver(aroom.meta.get(MONSTER_TYPE));
}
for (int i = 0; i < aroom.width; i++)
{
for (int j = 0; j < aroom.height; j++)
{
if (aroom.contents[i][j] == '#')
{
levelArray[room.x+i][room.y+j].character = '#';
levelArray[room.x+i][room.y+j].height = levelArray[room.x+i][room.y+j].roof;
}
else
{
levelArray[room.x+i][room.y+j].character = '.';
levelArray[room.x+i][room.y+j].height = levelArray[room.x+i][room.y+j].floor;
AbstractObject ao = aroom.objects.get(aroom.contents[i][j]);
if (ao == null) continue;
System.out.println("Placed object "+ao.type);
ao = ao.cpy();
ao.x = room.x+i;
ao.z = room.y+j;
ao.y = levelArray[room.x+i][room.y+j].floor;
abstractObjects.add(ao);
}
}
}
for (AbstractObject ao : abstractObjects)
{
LevelObject lo = LevelObject.checkObject(ao, (ao.x)*10, 0, (ao.z)*10, this, evolver);
if (lo != null)
{
lo.shortDesc = ao.shortDesc;
lo.longDesc = ao.longDesc;
addLevelObject(lo);
}
else
{
System.err.println("Failed at creating Object! Char=" + ao.character + " Type=" + ao.type);
}
}
return false;
}
// ----- Add and Remove GameObjects ----- //
public void addLevelObject(LevelObject lo)
{
Tile tile = getTile(lo.position.x, lo.position.z);
if (tile == null) return;
tile.levelObjects.add(lo);
}
public void addGameActor(GameActor ga)
{
Tile tile = getTile(ga.position.x, ga.position.z);
if (tile == null) return;
tile.actors.add(ga);
}
public void removeLevelObject(LevelObject lo)
{
Tile tile = getTile(lo.position.x, lo.position.z);
if (tile == null) return;
tile.removeLevelObject(lo.UID);
}
public void removeGameActor(GameActor ga)
{
Tile tile = getTile(ga.position.x, ga.position.z);
if (tile == null) return;
tile.removeGameActor(ga.UID);
}
// ----- 3D Game Actions (Creation, Destruction, Rendering etc) ----- //
public void update(float delta, Camera cam)
{
for (Tile[] tt : levelArray)
{
for (Tile t : tt)
{
t.update(delta, cam);
}
}
}
public void render(Renderer renderer, Camera cam, ArrayList<ParticleEmitter> visibleEmitters)
{
for (Tile[] tt : levelArray)
{
for (Tile t : tt)
{
t.render(renderer, cam, visibleEmitters);
}
}
}
public Player getPlayer()
{
Player player = null;
for (Tile[] tt : levelArray)
{
for (Tile t : tt)
{
player = t.getPlayer();
if (player != null) return player;
}
}
return null;
}
public void getLights(LightManager lightManager)
{
for (Tile[] tt : levelArray)
{
for (Tile t : tt)
{
t.getLights(lightManager);
}
}
}
public void positionPlayer(Player player, String prevLevel, String currentLevel)
{
for (Tile[] tt : levelArray)
{
for (Tile t : tt)
{
if (t.positionPlayer(player, prevLevel, currentLevel)) return;
}
}
}
public void evaluateUniqueBehaviour(LightManager lightManager)
{
for (Tile[] tt : levelArray)
{
for (Tile t : tt)
{
t.evaluateUniqueBehaviour(this, lightManager);
}
}
}
public void fixReferences()
{
for (Tile[] tt : levelArray)
{
for (Tile t : tt)
{
t.fixReferences();
}
}
}
public void create()
{
for (Tile[] tt : levelArray)
{
for (Tile t : tt)
{
t.create();
}
}
}
public void bakeLights(LightManager lightManager)
{
for (Tile[] tt : levelArray)
{
for (Tile t : tt)
{
t.bakeLights(lightManager);
}
}
}
public void dispose()
{
for (Tile[] tt : levelArray)
{
for (Tile t : tt)
{
t.dispose();
}
}
}
public float getDescription(Ray ray, float view, StringBuilder sB, boolean longDesc)
{
Vector3 pos = Pools.obtain(Vector3.class).set(ray.origin);
Vector3 step = Pools.obtain(Vector3.class).set(ray.direction).mul(VIEW_STEP);
float dist = 0;
for (int i = 0; i < view; i += VIEW_STEP)
{
dist += VIEW_STEP;
if (dist*dist > view) break;
pos.add(step);
Tile t = getTile(pos.x, pos.z);
if (t == null) { dist=view; break; }
if (pos.y < t.height)
{
sB.delete(0, sB.length());
if (longDesc)
{
sB.append(longDescs.get(t.character));
}
else
{
sB.append(shortDescs.get(t.character));
}
break;
}
else if (pos.y > t.roof)
{
if (hasRoof) {
sB.delete(0, sB.length());
if (longDesc)
{
sB.append(longDescs.get('R'));
}
else
{
sB.append(shortDescs.get('R'));
}
}
else
{
}
break;
}
}
Pools.free(pos);
Pools.free(step);
return dist*dist;
}
// ----- Collision ----- //
// public GameActor collideRayActors(Ray ray, float dist2, Vector3 p1, Vector3 p2, String ignoreUID, Vector3 collisionPoint)
// {
// GameActor chosen = null;
// for (GameActor go : GameData.level.actors)
// {
// if (go.UID.equals(ignoreUID)) continue;
//
// if (p1.dst2(go.position) < go.radius*go.radius) return go;
// if (p2.dst2(go.position) < go.radius*go.radius) return go;
//
// if (Intersector.intersectRaySphere(ray, go.getTruePosition(), go.radius, tmpVec))
// {
// tempdist2 = tmpVec.dst2(ray.origin);
// if (tempdist2 > dist2) continue;
// else
// {
// if (collisionPoint != null) collisionPoint.set(tmpVec);
// dist2 = tempdist2;
// chosen = go;
// }
// }
//
// }
// return chosen;
// }
//
// public LevelObject collideRayLevelObjects(Ray ray, float dist2, String ignoreUID, Vector3 collisionPoint)
// {
// LevelObject chosen = null;
// for (LevelObject go : GameData.level.levelObjects)
// {
// if (go.UID.equals(ignoreUID)) continue;
//
// if (Intersector.intersectRaySphere(ray, go.getTruePosition(), go.radius, tmpVec))
// {
// tempdist2 = tmpVec.dst2(ray.origin);
// if (tempdist2 > dist2) continue;
// else
// {
// if (collisionPoint != null) collisionPoint.set(tmpVec);
// dist2 = tempdist2;
// chosen = go;
// }
// }
// }
//
// return chosen;
// }
public boolean collideRayLevel(Ray ray, float view)
{
Vector3 pos = Pools.obtain(Vector3.class).set(ray.origin);
Vector3 step = Pools.obtain(Vector3.class).set(ray.direction).mul(VIEW_STEP);
float dist = 0;
boolean collide = false;
for (int i = 0; i < view; i += VIEW_STEP)
{
dist += VIEW_STEP;
if (dist*dist > view) break;
pos.add(step);
Tile t = getTile(pos.x, pos.z);
if (t == null) { dist=view; break; }
if (pos.y < t.height)
{
collide = true;
break;
}
else if (hasRoof && pos.y > t.roof)
{
collide = true;
break;
}
}
Pools.free(pos);
Pools.free(step);
return collide;
}
public boolean collideSphereAll(float x, float y, float z, float radius, String UID)
{
Tile tile = null;
tile = getTile(x+radius, z);
if (tile != null && checkSolid(tile)) return true;
tile = getTile(x-radius, z);
if (tile != null && checkSolid(tile)) return true;
tile = getTile(x, z+radius);
if (tile != null && checkSolid(tile)) return true;
tile = getTile(x, z-radius);
if (tile != null && checkSolid(tile)) return true;
tile = getTile(x+radius, z+radius);
if (tile != null && checkSolid(tile)) return true;
tile = getTile(x+radius, z-radius);
if (tile != null && checkSolid(tile)) return true;
tile = getTile(x-radius, z+radius);
if (tile != null && checkSolid(tile)) return true;
tile = getTile(x-radius, z-radius);
if (tile != null && checkSolid(tile)) return true;
tile = getTile(x, z);
if (tile != null && checkSolid(tile)) return true;
if (tile == null) return true;
if (y-radius < tile.height)
{
return true;
}
else if (hasRoof && y+radius > tile.roof)
{
return true;
}
if (collideSphereLevelObjectsAll(x, y, z, radius) != null) return true;
if (collideSphereActorsAll(x, y, z, radius, UID) != null) return true;
return false;
}
public LevelObject collideSphereLevelObjectsAll(float x, float y, float z, float radius)
{
tx = (int)((x/10f)+0.5f);
tz = (int)((z/10f)+0.5f);
radius2 = radius * radius;
final Tile[][] block = getBlock(tx, tz);
LevelObject lo = null;
lo = collideSphereLevelObjectsTile(block[0][0], x, y, z, radius2);
if (lo != null) return lo;
lo = collideSphereLevelObjectsTile(block[1][0], x, y, z, radius2);
if (lo != null) return lo;
lo = collideSphereLevelObjectsTile(block[2][0], x, y, z, radius2);
if (lo != null) return lo;
lo = collideSphereLevelObjectsTile(block[0][1], x, y, z, radius2);
if (lo != null) return lo;
lo = collideSphereLevelObjectsTile(block[1][1], x, y, z, radius2);
if (lo != null) return lo;
lo = collideSphereLevelObjectsTile(block[2][1], x, y, z, radius2);
if (lo != null) return lo;
lo = collideSphereLevelObjectsTile(block[0][2], x, y, z, radius2);
if (lo != null) return lo;
lo = collideSphereLevelObjectsTile(block[1][2], x, y, z, radius2);
if (lo != null) return lo;
lo = collideSphereLevelObjectsTile(block[2][2], x, y, z, radius2);
if (lo != null) return lo;
return null;
}
public LevelObject collideSphereLevelObjectsTile(Tile tile, float x, float y, float z, float radius2)
{
for (LevelObject lo : tile.levelObjects)
{
if (!lo.solid) continue;
if (lo.position.dst2(x, y, z) <= radius2+(lo.radius*lo.radius)) return lo;
}
return null;
}
public GameActor collideSphereActorsAll(float x, float y, float z, float radius, String UID)
{
tx = (int)((x/10f)+0.5f);
tz = (int)((z/10f)+0.5f);
radius2 = radius * radius;
final Tile[][] block = getBlock(tx, tz);
GameActor ga = null;
ga = collideSphereActorsTile(block[0][0], x, y, z, radius2, UID);
if (ga != null) return ga;
ga = collideSphereActorsTile(block[1][0], x, y, z, radius2, UID);
if (ga != null) return ga;
ga = collideSphereActorsTile(block[2][0], x, y, z, radius2, UID);
if (ga != null) return ga;
ga = collideSphereActorsTile(block[0][1], x, y, z, radius2, UID);
if (ga != null) return ga;
ga = collideSphereActorsTile(block[1][1], x, y, z, radius2, UID);
if (ga != null) return ga;
ga = collideSphereActorsTile(block[2][1], x, y, z, radius2, UID);
if (ga != null) return ga;
ga = collideSphereActorsTile(block[0][2], x, y, z, radius2, UID);
if (ga != null) return ga;
ga = collideSphereActorsTile(block[1][2], x, y, z, radius2, UID);
if (ga != null) return ga;
ga = collideSphereActorsTile(block[2][2], x, y, z, radius2, UID);
if (ga != null) return ga;
return null;
}
public GameActor collideBoxActorsAll(float x, float y, float z, Vector3 box, String UID)
{
tx = (int)((x/10f)+0.5f);
tz = (int)((z/10f)+0.5f);
final Tile[][] block = getBlock(tx, tz);
GameActor ga = null;
ga = collideBoxActorsTile(block[0][0], x, y, z, box, UID);
if (ga != null) return ga;
ga = collideBoxActorsTile(block[1][0], x, y, z, box, UID);
if (ga != null) return ga;
ga = collideBoxActorsTile(block[2][0], x, y, z, box, UID);
if (ga != null) return ga;
ga = collideBoxActorsTile(block[0][1], x, y, z, box, UID);
if (ga != null) return ga;
ga = collideBoxActorsTile(block[1][1], x, y, z, box, UID);
if (ga != null) return ga;
ga = collideBoxActorsTile(block[2][1], x, y, z, box, UID);
if (ga != null) return ga;
ga = collideBoxActorsTile(block[0][2], x, y, z, box, UID);
if (ga != null) return ga;
ga = collideBoxActorsTile(block[1][2], x, y, z, box, UID);
if (ga != null) return ga;
ga = collideBoxActorsTile(block[2][2], x, y, z, box, UID);
if (ga != null) return ga;
return null;
}
public GameActor collideSphereActorsTile(Tile tile, float x, float y, float z, float radius2, String UID)
{
for (GameActor ga : tile.actors)
{
if (!ga.solid) continue;
if (UID != null)
{
if (ga.UID.equals(UID)) continue;
}
if (ga.position.dst2(x, y, z) <= radius2+(ga.radius*ga.radius)) return ga;
}
return null;
}
public GameActor collideBoxActorsTile(Tile tile, float x, float y, float z, Vector3 box, String UID)
{
for (GameActor ga : tile.actors)
{
if (!ga.solid) continue;
if (UID != null)
{
if (ga.UID.equals(UID)) continue;
}
if (GameData.SphereBoxIntersection(ga.position.x, ga.position.y, ga.position.z, ga.radius, x, y, z, box.x, box.y, box.z)) return ga;
}
return null;
}
public Tile[][] getBlock(int tx, int tz)
{
Tile t = null;
bx = tx-1; bz = tz+1;
if (checkBounds(bx, bz)) t = null;
else t = levelArray[bx][bz];
block[0][0] = t;
bx = tx; bz = tz+1;
if (checkBounds(bx, bz)) t = null;
else t = levelArray[bx][bz];
block[1][0] = t;
bx = tx+1; bz = tz+1;
if (checkBounds(bx, bz)) t = null;
else t = levelArray[bx][bz];
block[2][0] = t;
bx = tx-1; bz = tz;
if (checkBounds(bx, bz)) t = null;
else t = levelArray[bx][bz];
block[0][1] = t;
bx = tx; bz = tz;
if (checkBounds(bx, bz)) t = null;
else t = levelArray[bx][bz];
block[1][1] = t;
bx = tx+1; bz = tz;
if (checkBounds(bx, bz)) t = null;
else t = levelArray[bx][bz];
block[2][1] = t;
bx = tx-1; bz = tz-1;
if (checkBounds(bx, bz)) t = null;
else t = levelArray[bx][bz];
block[0][2] = t;
bx = tx; bz = tz-1;
if (checkBounds(bx, bz)) t = null;
else t = levelArray[bx][bz];
block[1][2] = t;
bx = tx+1; bz = tz-1;
if (checkBounds(bx, bz)) t = null;
else t = levelArray[bx][bz];
block[2][2] = t;
return block;
}
// ----- Check mappings ----- //
public boolean checkBounds(int x, int z)
{
if (x < 0 || x >= width || z < 0 || z >= height) return true;
return false;
}
public Tile getTile(float x, float z)
{
tx = (int)((x/10f)+0.5f);
tz = (int)((z/10f)+0.5f);
if (checkBounds(tx, tz)) return null;
return levelArray[tx][tz];
}
public boolean checkSolid(Tile tile)
{
for (Character c : solids) if (tile.character == c) return true;
return false;
}
public boolean checkOpaque(Tile tile)
{
for (Character c : solids) if (tile.character == c) return true;
return false;
}
}