Engine – Map.cs
#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; } } */ }