#region File Description //----------------------------------------------------------------------------- // Map.cs // // Microsoft XNA Community Game Platform // Copyright (C) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- #endregion #region Using Statements using System; using System.Collections.Generic; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Content; #endregion namespace RolePlayingGameData { /// /// One section of the world, and all of the data in it. /// public class Map : ContentObject #if WINDOWS , ICloneable #endif { #region Description /// /// The name of this section of the world. /// private string name; /// /// The name of this section of the world. /// public string Name { get { return name; } set { name = value; } } #endregion #region Dimensions /// /// The dimensions of the map, in tiles. /// private Point mapDimensions; /// /// The dimensions of the map, in tiles. /// public Point MapDimensions { get { return mapDimensions; } set { mapDimensions = value; } } /// /// The size of the tiles in this map, in pixels. /// private Point tileSize; /// /// The size of the tiles in this map, in pixels. /// public Point TileSize { get { return tileSize; } set { tileSize = value; } } /// /// The number of tiles in a row of the map texture. /// /// /// Used to determine the source rectangle from the map layer value. /// private int tilesPerRow; /// /// The number of tiles in a row of the map texture. /// /// /// Used to determine the source rectangle from the map layer value. /// [ContentSerializerIgnore] public int TilesPerRow { get { return tilesPerRow; } } #endregion #region Spawning /// /// A valid spawn position for this map. /// private Point spawnMapPosition; /// /// A valid spawn position for this map. /// public Point SpawnMapPosition { get { return spawnMapPosition; } set { spawnMapPosition = value; } } #endregion #region Graphics Data /// /// The content name of the texture that contains the tiles for this map. /// private string textureName; /// /// The content name of the texture that contains the tiles for this map. /// public string TextureName { get { return textureName; } set { textureName = value; } } /// /// The texture that contains the tiles for this map. /// private Texture2D texture; /// /// The texture that contains the tiles for this map. /// [ContentSerializerIgnore] public Texture2D Texture { get { return texture; } } /// /// The content name of the texture that contains the background for combats /// that occur while traveling on this map. /// private string combatTextureName; /// /// The content name of the texture that contains the background for combats /// that occur while traveling on this map. /// public string CombatTextureName { get { return combatTextureName; } set { combatTextureName = value; } } /// /// The texture that contains the background for combats /// that occur while traveling on this map. /// private Texture2D combatTexture; /// /// The texture that contains the background for combats /// that occur while traveling on this map. /// [ContentSerializerIgnore] public Texture2D CombatTexture { get { return combatTexture; } } #endregion #region Music /// /// The name of the music cue for this map. /// private string musicCueName; /// /// The name of the music cue for this map. /// public string MusicCueName { get { return musicCueName; } set { musicCueName = value; } } /// /// The name of the music cue for combats that occur while traveling on this map. /// private string combatMusicCueName; /// /// The name of the music cue for combats that occur while traveling on this map. /// public string CombatMusicCueName { get { return combatMusicCueName; } set { combatMusicCueName = value; } } #endregion #region Map Layers #region Base Layer /// /// Spatial array for the ground tiles for this map. /// private int[] baseLayer; /// /// Spatial array for the ground tiles for this map. /// public int[] BaseLayer { get { return baseLayer; } set { baseLayer = value; } } /// /// Retrieves the base layer value for the given map position. /// public int GetBaseLayerValue(Point mapPosition) { // check the parameter if ((mapPosition.X < 0) || (mapPosition.X >= mapDimensions.X) || (mapPosition.Y < 0) || (mapPosition.Y >= mapDimensions.Y)) { throw new ArgumentOutOfRangeException("mapPosition"); } return baseLayer[mapPosition.Y * mapDimensions.X + mapPosition.X]; } /// /// Retrieves the source rectangle for the tile in the given position /// in the base layer. /// /// This method allows out-of-bound (blocked) positions. public Rectangle GetBaseLayerSourceRectangle(Point mapPosition) { // check the parameter, but out-of-bounds is nonfatal if ((mapPosition.X < 0) || (mapPosition.X >= mapDimensions.X) || (mapPosition.Y < 0) || (mapPosition.Y >= mapDimensions.Y)) { return Rectangle.Empty; } int baseLayerValue = GetBaseLayerValue(mapPosition); if (baseLayerValue < 0) { return Rectangle.Empty; } return new Rectangle( (baseLayerValue % tilesPerRow) * tileSize.X, (baseLayerValue / tilesPerRow) * tileSize.Y, tileSize.X, tileSize.Y); } #endregion #region Fringe Layer /// /// Spatial array for the fringe tiles for this map. /// private int[] fringeLayer; /// /// Spatial array for the fringe tiles for this map. /// public int[] FringeLayer { get { return fringeLayer; } set { fringeLayer = value; } } /// /// Retrieves the fringe layer value for the given map position. /// public int GetFringeLayerValue(Point mapPosition) { // check the parameter if ((mapPosition.X < 0) || (mapPosition.X >= mapDimensions.X) || (mapPosition.Y < 0) || (mapPosition.Y >= mapDimensions.Y)) { throw new ArgumentOutOfRangeException("mapPosition"); } return fringeLayer[mapPosition.Y * mapDimensions.X + mapPosition.X]; } /// /// Retrieves the source rectangle for the tile in the given position /// in the fringe layer. /// /// This method allows out-of-bound (blocked) positions. public Rectangle GetFringeLayerSourceRectangle(Point mapPosition) { // check the parameter, but out-of-bounds is nonfatal if ((mapPosition.X < 0) || (mapPosition.X >= mapDimensions.X) || (mapPosition.Y < 0) || (mapPosition.Y >= mapDimensions.Y)) { return Rectangle.Empty; } int fringeLayerValue = GetFringeLayerValue(mapPosition); if (fringeLayerValue < 0) { return Rectangle.Empty; } return new Rectangle( (fringeLayerValue % tilesPerRow) * tileSize.X, (fringeLayerValue / tilesPerRow) * tileSize.Y, tileSize.X, tileSize.Y); } #endregion #region Object Layer /// /// Spatial array for the object images on this map. /// private int[] objectLayer; /// /// Spatial array for the object images on this map. /// public int[] ObjectLayer { get { return objectLayer; } set { objectLayer = value; } } /// /// Retrieves the object layer value for the given map position. /// public int GetObjectLayerValue(Point mapPosition) { // check the parameter if ((mapPosition.X < 0) || (mapPosition.X >= mapDimensions.X) || (mapPosition.Y < 0) || (mapPosition.Y >= mapDimensions.Y)) { throw new ArgumentOutOfRangeException("mapPosition"); } return objectLayer[mapPosition.Y * mapDimensions.X + mapPosition.X]; } /// /// Retrieves the source rectangle for the tile in the given position /// in the object layer. /// /// This method allows out-of-bound (blocked) positions. public Rectangle GetObjectLayerSourceRectangle(Point mapPosition) { // check the parameter, but out-of-bounds is nonfatal if ((mapPosition.X < 0) || (mapPosition.X >= mapDimensions.X) || (mapPosition.Y < 0) || (mapPosition.Y >= mapDimensions.Y)) { return Rectangle.Empty; } int objectLayerValue = GetObjectLayerValue(mapPosition); if (objectLayerValue < 0) { return Rectangle.Empty; } return new Rectangle( (objectLayerValue % tilesPerRow) * tileSize.X, (objectLayerValue / tilesPerRow) * tileSize.Y, tileSize.X, tileSize.Y); } #endregion #region Collision Layer /// /// Spatial array for the collision properties of this map. /// private int[] collisionLayer; /// /// Spatial array for the collision properties of this map. /// public int[] CollisionLayer { get { return collisionLayer; } set { collisionLayer = value; } } /// /// Retrieves the collision layer value for the given map position. /// public int GetCollisionLayerValue(Point mapPosition) { // check the parameter if ((mapPosition.X < 0) || (mapPosition.X >= mapDimensions.X) || (mapPosition.Y < 0) || (mapPosition.Y >= mapDimensions.Y)) { throw new ArgumentOutOfRangeException("mapPosition"); } return collisionLayer[mapPosition.Y * mapDimensions.X + mapPosition.X]; } /// /// Returns true if the given map position is blocked. /// /// This method allows out-of-bound (blocked) positions. public bool IsBlocked(Point mapPosition) { // check the parameter, but out-of-bounds is nonfatal if ((mapPosition.X < 0) || (mapPosition.X >= mapDimensions.X) || (mapPosition.Y < 0) || (mapPosition.Y >= mapDimensions.Y)) { return true; } return (GetCollisionLayerValue(mapPosition) != 0); } #endregion #endregion #region Portals /// /// Portals to other maps. /// private List portals = new List(); /// /// Portals to other maps. /// public List Portals { get { return portals; } set { portals = value; } } #endregion #region Map Contents /// /// The content names and positions of the portals on this map. /// private List> portalEntries = new List>(); /// /// The content names and positions of the portals on this map. /// public List> PortalEntries { get { return portalEntries; } set { portalEntries = value; } } /// /// Find a portal on this map based on the given portal name. /// public MapEntry FindPortal(string name) { // check the parameter if (String.IsNullOrEmpty(name)) { throw new ArgumentNullException("name"); } return portalEntries.Find(delegate(MapEntry portalEntry) { return (portalEntry.ContentName == name); }); } /// /// The content names and positions of the treasure chests on this map. /// private List> chestEntries = new List>(); /// /// The content names and positions of the treasure chests on this map. /// public List> ChestEntries { get { return chestEntries; } set { chestEntries = value; } } /// /// The content name, positions, and orientations of the /// fixed combat encounters on this map. /// private List> fixedCombatEntries = new List>(); /// /// The content name, positions, and orientations of the /// fixed combat encounters on this map. /// public List> FixedCombatEntries { get { return fixedCombatEntries; } set { fixedCombatEntries = value; } } /// /// The random combat definition for this map. /// private RandomCombat randomCombat; /// /// The random combat definition for this map. /// public RandomCombat RandomCombat { get { return randomCombat; } set { randomCombat = value; } } /// /// The content names, positions, and orientations of quest Npcs on this map. /// private List> questNpcEntries = new List>(); /// /// The content names, positions, and orientations of quest Npcs on this map. /// public List> QuestNpcEntries { get { return questNpcEntries; } set { questNpcEntries = value; } } /// /// The content names, positions, and orientations of player Npcs on this map. /// private List> playerNpcEntries = new List>(); /// /// The content names, positions, and orientations of player Npcs on this map. /// public List> PlayerNpcEntries { get { return playerNpcEntries; } set { playerNpcEntries = value; } } /// /// The content names, positions, and orientations of the inns on this map. /// private List> innEntries = new List>(); /// /// The content names, positions, and orientations of the inns on this map. /// public List> InnEntries { get { return innEntries; } set { innEntries = value; } } /// /// The content names, positions, and orientations of the stores on this map. /// private List> storeEntries = new List>(); /// /// The content names, positions, and orientations of the stores on this map. /// public List> StoreEntries { get { return storeEntries; } set { storeEntries = value; } } #endregion #region ICloneable Members public object Clone() { Map map = new Map(); map.AssetName = AssetName; map.baseLayer = BaseLayer.Clone() as int[]; foreach (MapEntry chestEntry in chestEntries) { MapEntry mapEntry = new MapEntry(); mapEntry.Content = chestEntry.Content.Clone() as Chest; mapEntry.ContentName = chestEntry.ContentName; mapEntry.Count = chestEntry.Count; mapEntry.Direction = chestEntry.Direction; mapEntry.MapPosition = chestEntry.MapPosition; map.chestEntries.Add(mapEntry); } map.chestEntries.AddRange(ChestEntries); map.collisionLayer = CollisionLayer.Clone() as int[]; map.combatMusicCueName = CombatMusicCueName; map.combatTexture = CombatTexture; map.combatTextureName = CombatTextureName; map.fixedCombatEntries.AddRange(FixedCombatEntries); map.fringeLayer = FringeLayer.Clone() as int[]; map.innEntries.AddRange(InnEntries); map.mapDimensions = MapDimensions; map.musicCueName = MusicCueName; map.name = Name; map.objectLayer = ObjectLayer.Clone() as int[]; map.playerNpcEntries.AddRange(PlayerNpcEntries); map.portals.AddRange(Portals); map.portalEntries.AddRange(PortalEntries); map.questNpcEntries.AddRange(QuestNpcEntries); map.randomCombat = new RandomCombat(); map.randomCombat.CombatProbability = RandomCombat.CombatProbability; map.randomCombat.Entries.AddRange(RandomCombat.Entries); map.randomCombat.FleeProbability = RandomCombat.FleeProbability; map.randomCombat.MonsterCountRange = RandomCombat.MonsterCountRange; map.spawnMapPosition = SpawnMapPosition; map.storeEntries.AddRange(StoreEntries); map.texture = Texture; map.textureName = TextureName; map.tileSize = TileSize; map.tilesPerRow = tilesPerRow; return map; } #endregion #region Content Type Reader /// /// Read a Map object from the content pipeline. /// public class MapReader : ContentTypeReader { protected override Map Read(ContentReader input, Map existingInstance) { Map map = existingInstance; if (map == null) { map = new Map(); } map.AssetName = input.AssetName; map.Name = input.ReadString(); map.MapDimensions = input.ReadObject(); map.TileSize = input.ReadObject(); map.SpawnMapPosition = input.ReadObject(); map.TextureName = input.ReadString(); map.texture = input.ContentManager.Load( System.IO.Path.Combine(@"Textures\Maps\NonCombat", map.TextureName)); map.tilesPerRow = map.texture.Width / map.TileSize.X; map.CombatTextureName = input.ReadString(); map.combatTexture = input.ContentManager.Load( System.IO.Path.Combine(@"Textures\Maps\Combat", map.CombatTextureName)); map.MusicCueName = input.ReadString(); map.CombatMusicCueName = input.ReadString(); map.BaseLayer = input.ReadObject(); map.FringeLayer = input.ReadObject(); map.ObjectLayer = input.ReadObject(); map.CollisionLayer = input.ReadObject(); map.Portals.AddRange(input.ReadObject>()); map.PortalEntries.AddRange( input.ReadObject>>()); foreach (MapEntry portalEntry in map.PortalEntries) { portalEntry.Content = map.Portals.Find(delegate(Portal portal) { return (portal.Name == portalEntry.ContentName); }); } map.ChestEntries.AddRange( input.ReadObject>>()); foreach (MapEntry chestEntry in map.chestEntries) { chestEntry.Content = input.ContentManager.Load( System.IO.Path.Combine(@"Maps\Chests", chestEntry.ContentName)).Clone() as Chest; } // load the fixed combat entries Random random = new Random(); map.FixedCombatEntries.AddRange( input.ReadObject>>()); foreach (MapEntry fixedCombatEntry in map.fixedCombatEntries) { fixedCombatEntry.Content = input.ContentManager.Load( System.IO.Path.Combine(@"Maps\FixedCombats", fixedCombatEntry.ContentName)); // clone the map sprite in the entry, as there may be many entries // per FixedCombat fixedCombatEntry.MapSprite = fixedCombatEntry.Content.Entries[0].Content.MapSprite.Clone() as AnimatingSprite; // play the idle animation fixedCombatEntry.MapSprite.PlayAnimation("Idle", fixedCombatEntry.Direction); // advance in a random amount so the animations aren't synchronized fixedCombatEntry.MapSprite.UpdateAnimation( 4f * (float)random.NextDouble()); } map.RandomCombat = input.ReadObject(); map.QuestNpcEntries.AddRange( input.ReadObject>>()); foreach (MapEntry questNpcEntry in map.questNpcEntries) { questNpcEntry.Content = input.ContentManager.Load( System.IO.Path.Combine(@"Characters\QuestNpcs", questNpcEntry.ContentName)); questNpcEntry.Content.MapPosition = questNpcEntry.MapPosition; questNpcEntry.Content.Direction = questNpcEntry.Direction; } map.PlayerNpcEntries.AddRange( input.ReadObject>>()); foreach (MapEntry playerNpcEntry in map.playerNpcEntries) { playerNpcEntry.Content = input.ContentManager.Load( System.IO.Path.Combine(@"Characters\Players", playerNpcEntry.ContentName)).Clone() as Player; playerNpcEntry.Content.MapPosition = playerNpcEntry.MapPosition; playerNpcEntry.Content.Direction = playerNpcEntry.Direction; } map.InnEntries.AddRange( input.ReadObject>>()); foreach (MapEntry innEntry in map.innEntries) { innEntry.Content = input.ContentManager.Load( System.IO.Path.Combine(@"Maps\Inns", innEntry.ContentName)); } map.StoreEntries.AddRange( input.ReadObject>>()); foreach (MapEntry storeEntry in map.storeEntries) { storeEntry.Content = input.ContentManager.Load( System.IO.Path.Combine(@"Maps\Stores", storeEntry.ContentName)); } return map; } } #endregion } }