#region Using Statements
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
#endregion
namespace ParadigmEngine.TileEngine
{
/// <summary>
/// The collision flag associated with a tile
/// </summary>
public enum TileCollision
{
/// <summary>
/// A passable tile is one which does not hinder player motion at all.
/// </summary>
Passable = 1,
/// <summary>
/// An impassable tile is one which does not allow the player to move through
/// it at all. It is completely solid.
/// </summary>
Impassable = 0,
/// <summary>
/// A platform tile is one which behaves like a passable tile except when the
/// player is above it. A player can jump up through a platform as well as move
/// past it to the left and right, but can not fall down through the top of it.
/// </summary>
Platform = 2,
/// <summary>
/// An impassible tile that slopes downwards at 45 degrees
/// </summary>
SlopeDown = 3,
/// <summary>
/// An impassible tile that slopes upwards at 45 degrees
/// </summary>
SlopeUp = 4,
/// <summary>
/// An impassible tile with abnormal shape.
/// </summary>
Abnormal = 5,
/// <summary>
/// Collision volumes used for NPCs only
/// </summary>
NPC = 6,
}
public class Tile
{
#region Fields
/// <summary>
/// Source rectangles for static tiles
/// </summary>
private List<Rectangle> sourceRects = new List<Rectangle>();
/// <summary>
/// Animated tiles
/// </summary>
private List<AnimatedTile> animatedTiles = new List<AnimatedTile>();
#endregion
#region Attributes
public int ID { get; set; }
/// <summary>
/// World Coordinate
/// </summary>
public Vector2 Coordinate { get; set; }
/// <summary>
/// Tile Location
/// </summary>
public Vector2 Location { get; set; }
/// <summary>
/// Layer data
/// </summary>
public List<int> Layers { get; set; }
/// <summary>
/// Linked map
/// </summary>
public Map Map { get; set; }
/// <summary>
/// Collision type
/// </summary>
public TileCollision CollisionType { get; set; }
/// <summary>
/// Is tile a hazard
/// </summary>
public bool IsHazard { get; set; }
/// <summary>
/// Terrain index
/// </summary>
public int TerrainIndex { get; set; }
/// <summary>
/// Source rectangles
/// </summary>
public List<Rectangle> SourceRectangles
{
get { return sourceRects; }
set { sourceRects = value; }
}
/// <summary>
/// Animated tiles
/// </summary>
public List<AnimatedTile> AnimatedTiles
{
get { return animatedTiles; }
}
/// <summary>
/// Rectangle specifing this tile on the map
/// </summary>
public Rectangle MapRectangle
{
get
{
return new Rectangle(
(int)(Location.X),
(int)(Location.Y),
Map.Tile_Size, Map.Tile_Size);
}
}
#endregion
#region Constuctors
public Tile(int id, List<int> layers, Map map)
{
ID = id;
Layers = layers;
Map = map;
IsHazard = false;
// calculate initial location by using the tile id
Location = new Vector2(
(float)((ID % Map.Width) * Map.Tile_Size),
(float)((ID / Map.Width) * Map.Tile_Size)
);
// calculate coordinates in world
Coordinate = new Vector2(
Location.X / Map.Tile_Size,
Location.Y / Map.Tile_Size
);
// calculate source rectangles for the tile
FindSourceRectangles();
// update collision information
UpdateCollisionInformation();
// update hazard information
UpdateHazardInformation();
// update terrain information
UpdateTerrainInformation();
}
#endregion
#region Public Methods
/// <summary>
/// Add an animated tile to this tile
/// </summary>
/// <param name="animatedTile">The tile to add</param>
public void AddAnimatedTile(AnimatedTile newTile)
{
// if an animated tile already exists with the same layer, remove it
for (int i = 0; i < animatedTiles.Count; i++)
{
if (animatedTiles[i].Layer == newTile.Layer)
animatedTiles.RemoveAt(i);
}
// add the new tile
animatedTiles.Add(newTile);
// if we have more than one item in our animated tile list, we want to sort them by layer
if (animatedTiles.Count > 1)
{
animatedTiles.Sort(delegate(AnimatedTile tile1, AnimatedTile tile2)
{
return tile1.Layer.CompareTo(tile2.Layer);
});
}
}
/// <summary>
/// Remove animated tile at layer specified
/// </summary>
/// <param name="layer">The layer at which to the animated tile to remove resides</param>
public void RemoveAnimatedTileAt(int layer)
{
// if an animated tile already exists with the same layer, remove it
for (int i = 0; i < animatedTiles.Count; i++)
{
if (animatedTiles[i].Layer == layer)
animatedTiles.RemoveAt(i);
}
// if we have more than one item in our animated tile list, we want to sort them by layer
if (animatedTiles.Count > 1)
{
animatedTiles.Sort(delegate(AnimatedTile tile1, AnimatedTile tile2)
{
return tile1.Layer.CompareTo(tile2.Layer);
});
}
}
/// <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 < animatedTiles.Count; i++)
{
animatedTiles[i].Update(tileDesc, animationTex);
}
}
/// <summary>
/// Update animated tiles
/// </summary>
/// <param name="gameTime"></param>
public void Update(GameTime gameTime)
{
foreach (AnimatedTile tile in animatedTiles)
tile.AdvanceFrame(gameTime);
}
/// <summary>
/// Draw the tile
/// </summary>
/// <param name="gameTime">GameTime</param>
/// <param name="spriteBatch">SpriteBatch</param>
/// <param name="mapDraw">How are we drawing this tile?</param>
public void Draw(GameTime gameTime, SpriteBatch spriteBatch, MapDraw mapDraw, MapTexture mapTexture)
{
Texture2D drawTexture = null;
// decide which textures we are drawing
switch (mapTexture)
{
case MapTexture.Color:
drawTexture = Map.TilesetTextures.ColorMap;
break;
case MapTexture.Normals:
drawTexture = Map.TilesetTextures.NormalMap;
break;
case MapTexture.Depth:
drawTexture = Map.TilesetTextures.DepthMap;
break;
}
switch (mapDraw)
{
case MapDraw.Normal:
DrawNormal(gameTime, spriteBatch, drawTexture);
break;
case MapDraw.Foreground:
DrawForeground(gameTime, spriteBatch, drawTexture);
break;
}
}
/// <summary>
/// Editor Use: Draw the tile
/// </summary>
/// <param name="gameTime">GameTime</param>
/// <param name="spriteBatch">SpriteBatch</param>
/// <param name="mapDraw">How are we drawing this tile?</param>
/// <param name="foregroundHighlight">Is the foreground highlighted?</param>
/// <param name="currentLayer">The current layer we have selected</param>
public void Draw(GameTime gameTime, SpriteBatch spriteBatch, MapDraw mapDraw, MapTexture mapTexture, bool foregroundHighlight, int currentLayer)
{
Texture2D drawTexture = null;
// decide which textures we are drawing
switch (mapTexture)
{
case MapTexture.Color:
drawTexture = Map.TilesetTextures.ColorMap;
break;
case MapTexture.Normals:
drawTexture = Map.TilesetTextures.NormalMap;
break;
case MapTexture.Depth:
drawTexture = Map.TilesetTextures.DepthMap;
break;
}
switch (mapDraw)
{
case MapDraw.Normal:
DrawNormal(gameTime, spriteBatch, drawTexture, currentLayer);
break;
case MapDraw.Foreground:
DrawForeground(gameTime, spriteBatch, drawTexture, foregroundHighlight, currentLayer);
break;
}
}
/// <summary>
/// Generate source texture rectangles from map tile set
/// </summary>
public void FindSourceRectangles()
{
sourceRects.Clear();
// iterate through all but the final 2 layers in Layers list,
// the 2nd to last layer is hazard info, the final layer is Collision
for (int i = 0; i < Layers.Count - 3; i++)
{
// create source rectangle from tile data and linked map data
Rectangle source = new Rectangle(
((int)(Layers[i] % Map.TilesetXCount) * (int)Map.Tile_Size),
((int)(Layers[i] / Map.TilesetXCount) * (int)Map.Tile_Size),
(int)Map.Tile_Size,
(int)Map.Tile_Size);
sourceRects.Add(source);
}
}
#endregion
#region Private Methods
/// <summary>
/// Draw the tiles not flagged as foreground tiles
/// </summary>
private void DrawNormal(GameTime gameTime, SpriteBatch spriteBatch, Texture2D drawTexture)
{
// iterate through each layer
for (int layer = 0; layer < Layers.Count - 3; layer++)
{
// if this layer is not a foreground layer and it is not a null tile
if (Map.ForegroundLayers[layer] == false)
{
if (Layers[layer] != -1)
{
spriteBatch.Draw(drawTexture,
Location,
sourceRects[layer],
Color.White);
}
// iterate through animated tiles associated with this tile
for (int j = 0; j < animatedTiles.Count; j++)
{
// draw the animated tile if it is supposed to be on this layer
if (animatedTiles[j].Layer == layer)
{
animatedTiles[j].Draw(gameTime, spriteBatch, false);
}
}
}
}
}
/// <summary>
/// Editor Use: Draw the tiles not flagged as foreground tiles with layer coloring and transparency
/// </summary>
/// <param name="currentLayer">The current layer. Layers will be shaded in relation to this value. -1 indicates no layer shading.</param>
private void DrawNormal(GameTime gameTime, SpriteBatch spriteBatch, Texture2D drawTexture, int currentLayer)
{
// iterate through each layer
for (int layer = 0; layer < Layers.Count - 3; layer++)
{
// if this layer is not a foreground layer and it is not a null tile
if ((Map.ForegroundLayers[layer] == false))
{
if (currentLayer == -1)
{
if ((Layers[layer] != -1))
{
spriteBatch.Draw(drawTexture,
Location,
sourceRects[layer],
Color.White);
}
// iterate through animated tiles associated with this tile
for (int j = 0; j < animatedTiles.Count; j++)
{
// draw the animated tile if it is supposed to be on this layer
if (animatedTiles[j].Layer == layer)
{
animatedTiles[j].Draw(gameTime, spriteBatch, false);
}
}
}
else
{
if (layer < currentLayer) // if the layer is under the current layer, shade it
{
if ((Layers[layer] != -1))
{
spriteBatch.Draw(drawTexture,
Location,
sourceRects[layer],
Color.Wheat);
}
// iterate through animated tiles associated with this tile
for (int j = 0; j < animatedTiles.Count; j++)
{
// draw the animated tile if it is supposed to be on this layer
if (animatedTiles[j].Layer == layer)
{
animatedTiles[j].Draw(gameTime, spriteBatch, Color.Wheat);
}
}
}
else if (layer > currentLayer) // if the layer if above the current layer, make it transparent
{
if ((Layers[layer] != -1))
{
spriteBatch.Draw(drawTexture,
Location,
sourceRects[layer],
new Color(255, 255, 255, 100));
}
// iterate through animated tiles associated with this tile
for (int j = 0; j < animatedTiles.Count; j++)
{
// draw the animated tile if it is supposed to be on this layer
if (animatedTiles[j].Layer == layer)
{
animatedTiles[j].Draw(gameTime, spriteBatch, true);
}
}
}
else // if the layer is the current layer, just draw it
{
if ((Layers[layer] != -1))
{
spriteBatch.Draw(drawTexture,
Location,
sourceRects[layer],
Color.White);
}
// iterate through animated tiles associated with this tile
for (int j = 0; j < animatedTiles.Count; j++)
{
// draw the animated tile if it is supposed to be on this layer
if (animatedTiles[j].Layer == layer)
{
animatedTiles[j].Draw(gameTime, spriteBatch, false);
}
}
}
}
}
}
}
/// <summary>
/// Draw foreground tiles
/// </summary>
private void DrawForeground(GameTime gameTime, SpriteBatch spriteBatch, Texture2D drawTexture)
{
// iterate through each layer
for (int layer = 0; layer < Layers.Count - 3; layer++)
{
// if this layer is a foreground layer and it is not a null tile
if (Map.ForegroundLayers[layer] == true)
{
if (Layers[layer] != -1)
{
spriteBatch.Draw(drawTexture,
Location,
sourceRects[layer],
Color.White);
}
// iterate through animated tiles associated with this tile
for (int j = 0; j < animatedTiles.Count; j++)
{
// draw the animated tile if it is supposed to be on this layer
if (animatedTiles[j].Layer == layer)
{
animatedTiles[j].Draw(gameTime, spriteBatch, false);
}
}
}
}
}
/// <summary>
/// Editor Use: Draw foreground tiles with the option to highlight, and have a selected layer
/// </summary>
/// <param name="currentLayer">The current layer. Layers will be shaded in relation to this value. -1 indicates no layer shading.</param>
private void DrawForeground(GameTime gameTime, SpriteBatch spriteBatch, Texture2D drawTexture, bool highlighted, int currentLayer)
{
// if we are highlighting
if (highlighted)
{
// iterate through each layer
for (int layer = 0; layer < Layers.Count - 3; layer++)
{
// if this layer is a foreground layer and it is not a null tile
if (Map.ForegroundLayers[layer] == true)
{
// if current layer is null, it implies we don't want to shade the layers, so we wont
if (currentLayer == -1)
{
if (Layers[layer] != -1)
{
spriteBatch.Draw(drawTexture,
Location,
sourceRects[layer],
Color.Yellow);
}
// iterate through animated tiles associated with this tile
for (int j = 0; j < animatedTiles.Count; j++)
{
// draw the animated tile if it is supposed to be on this layer
if (animatedTiles[j].Layer == layer)
{
animatedTiles[j].Draw(gameTime, spriteBatch, Color.Yellow);
}
}
}
else
{
if (layer < currentLayer) // if the layer is under the current layer, shade it
{
if (Layers[layer] != -1)
{
spriteBatch.Draw(drawTexture,
Location,
sourceRects[layer],
Color.Wheat);
}
// iterate through animated tiles associated with this tile
for (int j = 0; j < animatedTiles.Count; j++)
{
// draw the animated tile if it is supposed to be on this layer
if (animatedTiles[j].Layer == layer)
{
animatedTiles[j].Draw(gameTime, spriteBatch, Color.Wheat);
}
}
}
else if (layer > currentLayer) // if the layer if above the current layer, make it transparent
{
if (Layers[layer] != -1)
{
spriteBatch.Draw(drawTexture,
Location,
sourceRects[layer],
new Color(255, 255, 0, 100));
}
// iterate through animated tiles associated with this tile
for (int j = 0; j < animatedTiles.Count; j++)
{
// draw the animated tile if it is supposed to be on this layer
if (animatedTiles[j].Layer == layer)
{
animatedTiles[j].Draw(gameTime, spriteBatch, true);
}
}
}
else // if the layer is the current layer, just draw it
{
if (Layers[layer] != -1)
{
spriteBatch.Draw(drawTexture,
Location,
sourceRects[layer],
Color.Yellow);
}
// iterate through animated tiles associated with this tile
for (int j = 0; j < animatedTiles.Count; j++)
{
// draw the animated tile if it is supposed to be on this layer
if (animatedTiles[j].Layer == layer)
{
animatedTiles[j].Draw(gameTime, spriteBatch, Color.Yellow);
}
}
}
}
}
}
}
else // no highlighting
{
// iterate through each layer
for (int layer = 0; layer < Layers.Count - 3; layer++)
{
// if this layer is a foreground layer and it is not a null tile
if (Map.ForegroundLayers[layer] == true)
{
if (currentLayer == -1)
{
if (Layers[layer] != -1)
{
spriteBatch.Draw(drawTexture,
Location,
sourceRects[layer],
Color.White);
}
// iterate through animated tiles associated with this tile
for (int j = 0; j < animatedTiles.Count; j++)
{
// draw the animated tile if it is supposed to be on this layer
if (animatedTiles[j].Layer == layer)
{
animatedTiles[j].Draw(gameTime, spriteBatch, false);
}
}
}
else
{
if (layer < currentLayer) // if the layer is under the current layer, shade it
{
if (Layers[layer] != -1)
{
spriteBatch.Draw(drawTexture,
Location,
sourceRects[layer],
Color.Wheat);
}
// iterate through animated tiles associated with this tile
for (int j = 0; j < animatedTiles.Count; j++)
{
// draw the animated tile if it is supposed to be on this layer
if (animatedTiles[j].Layer == layer)
{
animatedTiles[j].Draw(gameTime, spriteBatch, Color.Wheat);
}
}
}
else if (layer > currentLayer) // if the layer if above the current layer, make it transparent
{
if (Layers[layer] != -1)
{
spriteBatch.Draw(drawTexture,
Location,
sourceRects[layer],
new Color(255, 255, 255, 100));
}
// iterate through animated tiles associated with this tile
for (int j = 0; j < animatedTiles.Count; j++)
{
// draw the animated tile if it is supposed to be on this layer
if (animatedTiles[j].Layer == layer)
{
animatedTiles[j].Draw(gameTime, spriteBatch, true);
}
}
}
else // if the layer is the current layer, just draw it
{
if (Layers[layer] != -1)
{
spriteBatch.Draw(drawTexture,
Location,
sourceRects[layer],
Color.White);
}
// iterate through animated tiles associated with this tile
for (int j = 0; j < animatedTiles.Count; j++)
{
// draw the animated tile if it is supposed to be on this layer
if (animatedTiles[j].Layer == layer)
{
animatedTiles[j].Draw(gameTime, spriteBatch, false);
}
}
}
}
}
}
}
}
/// <summary>
/// Update collision information for this tile.
/// </summary>
private void UpdateCollisionInformation()
{
// final element in Layer list contains collision information
CollisionType = (TileCollision)((int)Layers[Layers.Count - 2]);
}
/// <summary>
/// Update hazard information for this tile.
/// </summary>
private void UpdateHazardInformation()
{
// the second to last layer contains objects that cause death
if (Layers[Layers.Count - 3] == -1)
IsHazard = false;
else
IsHazard = true;
}
/// <summary>
/// Update terrain information for this tile.
/// </summary>
private void UpdateTerrainInformation()
{
TerrainIndex = (int)Layers[Layers.Count - 1];
}
#endregion
}
}