#region Using Statements
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using ParadigmEngine.DeferredRenderer;
#endregion
namespace ParadigmEngine.TileEngine
{
/// <summary>
/// Defines what part of the map we are drawing
/// </summary>
public enum MapDraw
{
Normal,
Foreground,
}
/// <summary>
/// Defines which texture we are drawing
/// </summary>
public enum MapTexture
{
Color,
Normals,
Depth,
}
public class Map
{
#region Fields
// lists holds each row from map xml
private List<List<string>> layers = new List<List<string>>();
private List<bool> foregroundLayers = new List<bool>();
private List<string> hazardLayer = new List<string>();
private List<string> collisionLayer = new List<string>();
private List<string> terrainLayer = new List<string>();
private List<Tile> tileData = new List<Tile>();
private MaterialResource tilesetTextures = new MaterialResource();
private bool isInitialized = false;
#endregion
#region Serialized Attributes
public string AssetType { get; set; }
public string ID { get; set; }
public Color BgColor { get; set; }
public string TileSet { get; set; }
public int TilesetWidth { get; set; }
public int TilesetHeight { get; set; }
public int Tile_Size { get; set; }
/// <summary>
/// Width in tiles
/// </summary>
public int Width { get; set; }
/// <summary>
/// Height in tiles
/// </summary>
public int Height { get; set; }
public LightParams LightParams { get; set; }
public List<PointLight> LightList { get; set; }
public List<AnimatedTileInfo> AnimatedTileList { get; set; }
public List<MapEntityInfo> SpawnList { get; set; }
public List<ParticleEmitterInfo> ParticleEmitterList { get; set; }
public List<PortalInfo> PortalList { get; set; }
public List<ShaderRegionInfo> ShaderRegionList { get; set; }
public List<TerrainInfo> TerrainList { get; set; }
public List<MapEventInfo> EventList { get; set; }
/// <summary>
/// Map Layers
/// </summary>
public List<List<string>> Layers
{
get { return layers; }
set { layers = value; }
}
/// <summary>
/// Foreground layer indices
/// </summary>
public List<bool> ForegroundLayers
{
get { return foregroundLayers; }
set { foregroundLayers = value; }
}
/// <summary>
/// Hazard layers
/// </summary>
public List<string> HazardLayer
{
get { return hazardLayer; }
set { hazardLayer = value; }
}
/// <summary>
/// Collision layer
/// </summary>
public List<string> CollisionLayer
{
get { return collisionLayer; }
set { collisionLayer = value; }
}
/// <summary>
/// Collision layer
/// </summary>
public List<string> TerrainLayer
{
get { return terrainLayer; }
set { terrainLayer = value; }
}
#endregion
#region Unserialized Attributes
[ContentSerializerIgnore]
public MaterialResource TilesetTextures
{
get { return tilesetTextures; }
}
[ContentSerializerIgnore]
[XmlIgnore()]
public List<Tile> Tiles
{
get { return tileData; }
}
[ContentSerializerIgnore]
[XmlIgnore()]
public int TilesetXCount { get; set; }
[ContentSerializerIgnore]
[XmlIgnore()]
public int TilesetYCount { get; set; }
[ContentSerializerIgnore]
[XmlIgnore()]
public bool IsInitialized
{
get { return isInitialized; }
}
[ContentSerializerIgnore]
public int WidthInPixels
{
get
{
return (Width * Tile_Size);
}
}
[ContentSerializerIgnore]
public int HeightInPixels
{
get
{
return (Height * Tile_Size);
}
}
#endregion
#region Initialize Methods
/// <summary>
/// Initialize map
/// </summary>
public void Initialize(ContentManager content, GraphicsDevice graphicsDevice)
{
Texture2D tileColorMap = null;
Texture2D tileNormalMap = null;
Texture2D tileDepthMap = null;
tileColorMap = content.Load<Texture2D>(TileSet);
tilesetTextures.ColorMap = tileColorMap;
// attempt to load the normals
try
{
tileNormalMap = content.Load<Texture2D>(TileSet + "_Normal");
}
catch (System.Exception)
{
// upon failure, create a transparent texture
tileNormalMap = new Texture2D(graphicsDevice, tileColorMap.Width, tileColorMap.Height,
1, TextureUsage.None, SurfaceFormat.Color);
// set a color to the amount of pixels in the texture
Color[] texture = new Color[tileColorMap.Width * tileColorMap.Height];
Color transparent = new Color(0, 0, 0, 0);
for (int i = 0; i < texture.Length; i++)
{
texture[i] = transparent;
}
tileNormalMap.SetData(texture);
}
finally
{
tilesetTextures.NormalMap = tileNormalMap;
}
// attempt to load the depth
try
{
tileDepthMap = content.Load<Texture2D>(TileSet + "_Depth");
}
catch (System.Exception)
{
// upon failure, create a transparent texture
tileDepthMap = new Texture2D(graphicsDevice, tileColorMap.Width, tileColorMap.Height,
1, TextureUsage.None, SurfaceFormat.Color);
// set a color to the amount of pixels in the texture
Color[] texture = new Color[tileColorMap.Width * tileColorMap.Height];
Color black = new Color(0, 0, 0, 255);
for (int i = 0; i < texture.Length; i++)
{
texture[i] = black;
}
tileDepthMap.SetData(texture);
}
finally
{
tilesetTextures.DepthMap = tileDepthMap;
}
FindTilesetDimensions();
CreateTiles();
LoadAnimatedTiles(content);
isInitialized = true;
}
/// <summary>
/// Initialize map
/// </summary>
public void Initialize()
{
FindTilesetDimensions();
CreateTiles();
isInitialized = true;
}
/// <summary>
/// Manually set tileset color map instead of loading it with initialize method
/// </summary>
public void SetColorTexture(Texture2D texture)
{
this.tilesetTextures.ColorMap = texture;
}
/// <summary>
/// Manually set tileset normal map instead of loading it with initialize method
/// </summary>
public void SetNormalTexture(Texture2D texture)
{
this.tilesetTextures.NormalMap = texture;
}
/// <summary>
/// Manually set tileset depth map instead of loading it with initialize method
/// </summary>
public void SetDepthTexture(Texture2D texture)
{
this.tilesetTextures.DepthMap = texture;
}
#endregion
#region Public Methods
public Map()
{
AnimatedTileList = new List<AnimatedTileInfo>();
SpawnList = new List<MapEntityInfo>();
ParticleEmitterList = new List<ParticleEmitterInfo>();
PortalList = new List<PortalInfo>();
ShaderRegionList = new List<ShaderRegionInfo>();
TerrainList = new List<TerrainInfo>();
EventList = new List<MapEventInfo>();
LightParams = new LightParams();
LightList = new List<PointLight>();
}
/// <summary>
/// Update all animated tiles
/// </summary>
/// <param name="gameTime"></param>
public void Update(GameTime gameTime)
{
foreach (Tile tile in tileData)
tile.Update(gameTime);
}
/// <summary>
/// Draws the map
/// </summary>
public void Draw(GameTime gameTime, SpriteBatch spriteBatch, MapDraw mapDraw, MapTexture mapTexture)
{
foreach (Tile tile in tileData)
tile.Draw(gameTime, spriteBatch, mapDraw, mapTexture);
}
/// <summary>
/// Editor Use: Draws the map, with the option for foreground highlighting and layer occlusions
/// </summary>
/// <param name="spriteBatch"></param>
/// <param name="mapDraw"></param>
/// <param name="foregroundHightlight"></param>
/// <param name="currentLayer"></param>
public void Draw(GameTime gameTime, SpriteBatch spriteBatch, MapDraw mapDraw, MapTexture mapTexture, bool foregroundHightlight, int currentLayer)
{
foreach (Tile tile in tileData)
tile.Draw(gameTime, spriteBatch, mapDraw, mapTexture, foregroundHightlight, currentLayer);
}
/// <summary>
/// Gets the Collision Type of tile at x,y
/// </summary>
public TileCollision GetCollision(int x, int y)
{
// Allow passing through level sides to account for portals
if (x < 0 || x >= Width)
return TileCollision.Passable;
// Allow jumping past the level top and falling through the bottom.
if (y < 0 || y >= Height)
return TileCollision.Passable;
int tileID = (y * Width) + x;
return tileData[tileID].CollisionType;
}
/// <summary>
/// Check and see if a tile causes damage
/// </summary>
public bool GetHazardCollision(int x, int y)
{
if (x < 0 || x >= Width)
return false;
if (y < 0 || y >= Height)
return false;
int tileID = (y * Width) + x;
return tileData[tileID].IsHazard;
}
/// <summary>
/// Return the terrain type associated with a tile
/// </summary>
public int GetTerrainType(int x, int y)
{
if (x < 0 || x >= Width)
return -1;
if (y < 0 || y >= Height)
return -1;
int tileID = (y * Width) + x;
return tileData[tileID].TerrainIndex;
}
/// <summary>
/// Performs a check to see if we have collided with a portal.
/// </summary>
/// <param name="boundingBox">Entity Bounding Box</param>
/// <returns>
/// Returns the index of the portal from PortalInfoList is there is a collision
/// Returns -1 if no collision has occured
/// </returns>
public int GetPortalCollision(Rectangle boundingBox)
{
for (int i = 0; i < PortalList.Count; i++)
{
Rectangle portalBounds = new Rectangle();
PortalInfo portal = PortalList[i];
switch (portal.inDirection)
{
case PortalDirection.Left:
portalBounds.X = portal.boundingBox.X + portal.boundingBox.Width - 1;
portalBounds.Y = portal.boundingBox.Y;
portalBounds.Width = 1;
portalBounds.Height = portal.boundingBox.Height;
if (boundingBox.Intersects(portalBounds))
return i;
break;
case PortalDirection.Right:
portalBounds.X = portal.boundingBox.X + 1;
portalBounds.Y = portal.boundingBox.Y;
portalBounds.Width = 1;
portalBounds.Height = portal.boundingBox.Height;
if (boundingBox.Intersects(portalBounds))
return i;
break;
case PortalDirection.Bottom:
portalBounds.X = portal.boundingBox.X;
portalBounds.Y = portal.boundingBox.Y + 1;
portalBounds.Width = portal.boundingBox.Width;
portalBounds.Height = 1;
if (boundingBox.Intersects(portalBounds))
return i;
break;
case PortalDirection.Top:
portalBounds.X = portal.boundingBox.X;
portalBounds.Y = portal.boundingBox.Y + portal.boundingBox.Height - 1;
portalBounds.Width = portal.boundingBox.Width; ;
portalBounds.Height = 1;
if (boundingBox.Intersects(portalBounds))
return i;
break;
}
}
return -1;
}
/// <summary>
/// Get the bounding box of a portal
/// </summary>
/// <param name="portalIndex">Index of portal</param>
/// <returns>
/// Returns the bounding box of the specified portal, or an empty rectangle if there is an error
/// </returns>
public Rectangle GetPortalBounds(int portalIndex)
{
// if the index if out of range, return an empty rectangle
if (portalIndex >= PortalList.Count)
return Rectangle.Empty;
Rectangle portalBounds = new Rectangle();
PortalInfo portal = PortalList[portalIndex];
switch (portal.inDirection)
{
case PortalDirection.Left:
portalBounds.X = portal.boundingBox.X + portal.boundingBox.Width - 1;
portalBounds.Y = portal.boundingBox.Y;
portalBounds.Width = 1;
portalBounds.Height = portal.boundingBox.Height;
return portalBounds;
case PortalDirection.Right:
portalBounds.X = portal.boundingBox.X + 1;
portalBounds.Y = portal.boundingBox.Y;
portalBounds.Width = 1;
portalBounds.Height = portal.boundingBox.Height;
return portalBounds;
case PortalDirection.Bottom:
portalBounds.X = portal.boundingBox.X;
portalBounds.Y = portal.boundingBox.Y + 1;
portalBounds.Width = portal.boundingBox.Width;
portalBounds.Height = 1;
return portalBounds;
case PortalDirection.Top:
portalBounds.X = portal.boundingBox.X;
portalBounds.Y = portal.boundingBox.Y + portal.boundingBox.Height - 1;
portalBounds.Width = portal.boundingBox.Width; ;
portalBounds.Height = 1;
return portalBounds;
}
return Rectangle.Empty;
}
/// <summary>
/// Get a tiles location in world space
/// </summary>
/// <param name="x">X coord</param>
/// <param name="y">Y coord</param>
/// <returns></returns>
public Vector2 GetTileLocation(int x, int y)
{
int tileID = (y * Width) + x;
return tileData[tileID].Location;
}
/// <summary>
/// Gets the bounding rectangle of a tile in world space.
/// </summary>
public Rectangle GetBounds(int x, int y)
{
return new Rectangle(x * Tile_Size, y * Tile_Size, Tile_Size, Tile_Size);
}
/// <summary>
/// Access a tile directly
/// </summary>
/// <param name="x">X coord</param>
/// <param name="y">Y coord</param>
/// <returns>Tile</returns>
public Tile GetTile(int x, int y)
{
int tileID = (y * Width) + x;
try
{
return tileData[tileID];
}
catch (ArgumentOutOfRangeException)
{
return null;
}
}
#endregion
#region Private Methods
private List<int> ConvertSpawnLayer(List<string> layer)
{
// create a new layer
List<int> newLayer = new List<int>();
foreach (String entry in layer)
{
string[] exploded = entry.Split(new Char[] { ',' });
for (int i = 0; i < exploded.Length; i++)
{
newLayer.Add((int)Convert.ToDouble(exploded[i]));
}
}
return newLayer;
}
/// <summary>
/// This converts comma separated string layer data from XML into a numeric list
/// </summary>
private List<int> ConvertSpriteLayer(List<string> layer)
{
// create a new layer
List<int> newLayer = new List<int>();
foreach (String entry in layer)
{
string[] exploded = entry.Split(new Char[] { ',' });
for (int i = 0; i < exploded.Length; i++)
{
newLayer.Add((int)Convert.ToDouble(exploded[i]));
}
}
return newLayer;
}
/// <summary>
/// This converts comma separated string layer data from xml into a numeric list
/// </summary>
private List<int> ConvertCollisionLayer(List<string> layer)
{
// create a new layer
List<int> newLayer = new List<int>();
foreach (String entry in layer)
{
string[] exploded = entry.Split(new Char[] { ',' });
for (int i = 0; i < exploded.Length; i++)
{
newLayer.Add((int)Convert.ToDouble(exploded[i]));
}
}
return newLayer;
}
/// <summary>
/// Find and set the dimensions of the tileset in tiles
/// </summary>
private void FindTilesetDimensions()
{
TilesetXCount = TilesetWidth / Tile_Size;
TilesetYCount = TilesetHeight / Tile_Size;
}
/// <summary>
/// Create Tile Data for Map
/// </summary>
private void CreateTiles()
{
// two dimensional list of lists for multiple map layers
List<List<int>> mapData = new List<List<int>>();
// iterate through each layer and add to the data list
foreach (List<string> layer in layers)
{
mapData.Add(ConvertSpriteLayer(layer));
}
// add the hazard and collision layers
mapData.Add(ConvertSpriteLayer(hazardLayer));
mapData.Add(ConvertCollisionLayer(collisionLayer));
mapData.Add(ConvertSpriteLayer(terrainLayer));
int totalTiles = Width * Height;
// iterate through every tile
for (int i = 0; i < totalTiles; i++)
{
List<int> layerData = new List<int>();
// iterate through each layer, and add the layer data for that tile to a list
foreach (List<int> layer in mapData)
{
layerData.Add(layer[i]);
}
Tile tile = new Tile(i, layerData, this);
tileData.Add(tile);
}
}
/// <summary>
/// Load all animated tiles from map
/// </summary>
private void LoadAnimatedTiles(ContentManager content)
{
AnimatedTileDesc tileDesc;
Texture2D animationTex;
string tileResource = "";
for (int i = 0; i < AnimatedTileList.Count; i++)
{
AnimatedTile animatedTile;
// get entity directory and filename
tileResource = AnimatedTileList[i].TileResource;
// load tile info from resource
tileDesc = content.Load<AnimatedTileDesc>(@"Animated Tiles\" + tileResource);
// load texture from desc
animationTex = content.Load<Texture2D>(tileDesc.Texture.Substring(1, tileDesc.Texture.Length - 5));
// create new tile and initialize
animatedTile = new AnimatedTile();
animatedTile.ResourcePath = tileResource;
animatedTile.WorldPosition = AnimatedTileList[i].SpawnPoint;
animatedTile.TilePosition = AnimatedTileList[i].SpawnTile;
animatedTile.Layer = AnimatedTileList[i].Layer;
animatedTile.Initialize(animationTex, tileDesc);
tileData[AnimatedTileList[i].ID].AddAnimatedTile(animatedTile);
}
}
/// <summary>
/// Update all animated tiles that need updating
/// </summary>
/// <param name="tileDesc">tileDesc structure containing description of tiles that need updating</param>
/// <param name="animationTex">New tile texture</param>
public void UpdateAnimatedTiles(AnimatedTileDesc tileDesc, Texture2D animationTex)
{
for (int i = 0; i < tileData.Count; i++)
{
tileData[i].UpdateAnimatedTiles(tileDesc, animationTex);
}
}
/// <summary>
/// Editor Use: Manually add an animated tile to the map
/// </summary>
public void AddAnimatedTile(AnimatedTile tile)
{
// add the animated tile to the appropriate tile
tileData[tile.ID].AddAnimatedTile(tile);
}
/// <summary>
/// Update all tiles to use map dimensions. Useful if map dimensions have changed since tiles where created.
/// </summary>
public void UpdateTileRectangles()
{
FindTilesetDimensions();
foreach (Tile tile in tileData)
tile.FindSourceRectangles();
}
#endregion
}
/*
public class MapContentReader : ContentTypeReader<Map>
{
protected override Map Read(
ContentReader input,
Map map)
{
map.AssetType = input.ReadString();
map.ID = input.ReadString();
map.BgColor = input.ReadColor();
map.TileSet = input.ReadString();
map.TilesetWidth = (int)input.ReadDouble();
map.TilesetHeight = (int)input.ReadDouble();
map.Tile_Size = (int)input.ReadDouble();
map.Width = (int)input.ReadDouble();
map.Height = (int)input.ReadDouble();
map.LightParams = input.ReadObject<LightParams>();
map.LightList = input.ReadObject<List<PointLight>>();
map.AnimatedTileList = input.ReadObject<List<AnimatedTileInfo>>();
map.SpawnList = input.ReadObject<List<MapEntityInfo>>();
map.ParticleEmitterList = input.ReadObject<List<ParticleEmitterInfo>>();
map.PortalList = input.ReadObject<List<PortalInfo>>();
map.ShaderRegionList = input.ReadObject<List<ShaderRegionInfo>>();
map.TerrainList = input.ReadObject<List<TerrainInfo>>();
map.EventList = input.ReadObject<List<MapEventInfo>>();
map.Layers = input.ReadObject<List<List<string>>>();
map.ForegroundLayers = input.ReadObject<List<bool>>();
map.HazardLayer = input.ReadObject<List<string>>();
map.CollisionLayer = input.ReadObject<List<string>>();
map.TerrainLayer = input.ReadObject<List<string>>();
return map;
}
}
*/
}