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
    }
}