Engine – Tile.cs
#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 } }