]> Dogcows Code - chaz/carfire/commitdiff
Map drawing implemented.
authorCharles <Charles@92bb83a3-7c8f-8a45-bc97-515c4e399668>
Tue, 13 Apr 2010 05:12:26 +0000 (05:12 +0000)
committerCharles <Charles@92bb83a3-7c8f-8a45-bc97-515c4e399668>
Tue, 13 Apr 2010 05:12:26 +0000 (05:12 +0000)
Test map API's implemented.

git-svn-id: https://bd85.net/svn/cs3505_group@67 92bb83a3-7c8f-8a45-bc97-515c4e399668

CarFire/CarFire/CarFire.suo
CarFire/CarFire/CarFire/Content/Content.contentproj
CarFire/CarFire/CarFire/Map.cs
CarFire/CarFire/CarFire/MapReader.cs
CarFire/CarFire/CarFire/Parse.cs
CarFire/CarFire/CarFire/XnaGame.cs

index 153a3e4100f5796d979125a3bce9213fc1c94218..a29eeb84ea67b47fa78dc58d715ab8b38e6d701d 100644 (file)
Binary files a/CarFire/CarFire/CarFire.suo and b/CarFire/CarFire/CarFire.suo differ
index 077c9a1ba6db124ccb5585ee333e72d912f695b2..59b77ca1aa4e67bf4b20e51dbe421d823cbd9b38 100644 (file)
       <Name>sandbox</Name>\r
     </Compile>\r
   </ItemGroup>\r
       <Name>sandbox</Name>\r
     </Compile>\r
   </ItemGroup>\r
+  <ItemGroup>\r
+    <Compile Include="default.png">\r
+      <Name>default</Name>\r
+      <Importer>TextureImporter</Importer>\r
+      <Processor>TextureProcessor</Processor>\r
+    </Compile>\r
+  </ItemGroup>\r
   <Import Project="$(MSBuildExtensionsPath)\Microsoft\XNA Game Studio\v3.0\Microsoft.Xna.GameStudio.ContentPipeline.targets" />\r
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. \r
        Other similar extension points exist, see Microsoft.Common.targets.\r
   <Import Project="$(MSBuildExtensionsPath)\Microsoft\XNA Game Studio\v3.0\Microsoft.Xna.GameStudio.ContentPipeline.targets" />\r
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. \r
        Other similar extension points exist, see Microsoft.Common.targets.\r
index ed8c39fd1957986f0014a404d44c702ed982bfe3..b61f2bbcb1eef2f8e0e556efdcff645b3daf5d63 100644 (file)
@@ -2,11 +2,11 @@
 using System.Collections.Generic;\r
 using System.Linq;\r
 using System.Text;\r
 using System.Collections.Generic;\r
 using System.Linq;\r
 using System.Text;\r
-using Microsoft.Xna.Framework;\r
 using System.IO;\r
 using System.IO;\r
-using System.Text.RegularExpressions;\r
 using System.Runtime.Serialization;\r
 using System.Diagnostics;\r
 using System.Runtime.Serialization;\r
 using System.Diagnostics;\r
+using Microsoft.Xna.Framework;\r
+using Microsoft.Xna.Framework.Graphics;\r
 \r
 namespace CarFire\r
 {\r
 \r
 namespace CarFire\r
 {\r
@@ -18,20 +18,21 @@ namespace CarFire
     /// </summary>\r
     public class Map\r
     {\r
     /// </summary>\r
     public class Map\r
     {\r
-        #region Types\r
+        // DEBUG: Tilesets not implemented at all.\r
+        public static Texture2D DefaultTile;\r
 \r
 \r
-        public class Data\r
-        {\r
-            public string Author;\r
-            public string Name;\r
-            public int MinNumPlayers;\r
-            public int MaxNumPlayers;\r
-            public Point Dimensions;\r
-            public string Tileset;        // TODO: this should be a tilemap object\r
-            public Type Type;\r
-            public char[,] Grid;\r
-        }\r
-        \r
+        #region Public Constants\r
+\r
+        public const float PixelsToUnitSquares = 64.0f;\r
+\r
+        #endregion\r
+\r
+\r
+        #region Public Types\r
+\r
+        /// <summary>\r
+        /// The type of a map helps determine how the map is intended to be used.\r
+        /// </summary>\r
         public enum Type\r
         {\r
             None,\r
         public enum Type\r
         {\r
             None,\r
@@ -39,12 +40,301 @@ namespace CarFire
             Battle\r
         }\r
 \r
             Battle\r
         }\r
 \r
+        /// <summary>\r
+        /// The container class for map metadata.\r
+        /// </summary>\r
+        public class Metadata\r
+        {\r
+            public string Name;\r
+            public Type Type;\r
+            public string Author;\r
+            public HashSet<int> NumPlayers = new HashSet<int>();\r
+            public string Tileset;\r
+            public int GridWidth;\r
+            public int GridHeight;\r
+        }\r
+\r
+        #endregion\r
+\r
+\r
+        #region Public Attributes\r
+\r
+        /// <summary>\r
+        /// Get the name of the map.\r
+        /// </summary>\r
+        public string Name { get { return mData.Metadata.Name; } }\r
+\r
+        /// <summary>\r
+        /// Get the type of the map.\r
+        /// </summary>\r
+        //public Type Type { get { return mData.mMetadata.Type; } }\r
+\r
+        /// <summary>\r
+        /// Get the author of the map.\r
+        /// </summary>\r
+        public string Author { get { return mData.Metadata.Author; } }\r
+\r
+        /// <summary>\r
+        /// Get a set of integers containing each allowable number of players.\r
+        /// </summary>\r
+        public HashSet<int> NumPlayers { get { return mData.Metadata.NumPlayers; } }\r
+\r
+        /// <summary>\r
+        /// Get the width of the map, in grid units.\r
+        /// </summary>\r
+        public int Width { get { return mData.Metadata.GridWidth; } }\r
+\r
+        /// <summary>\r
+        /// Get the height of the map, in grid units.\r
+        /// </summary>\r
+        public int Height { get { return mData.Metadata.GridHeight; } }\r
+\r
+        // TODO: This should return whatever object we end up using for tilesets.\r
+        public string Tileset { get { return mData.Metadata.Tileset; } }\r
+\r
+\r
+        public Vector2 CenterCell\r
+        {\r
+            get { return mView.CenterCell; }\r
+            set { mView.CenterCell = value; }\r
+        }\r
+\r
+        #endregion\r
+\r
+\r
+        #region Public Methods\r
+\r
+        /// <summary>\r
+        /// Construct a map with the provided map data.\r
+        /// </summary>\r
+        /// <param name="data">Map data.</param>\r
+        public Map(Metadata metadata, char[,] grid, Dictionary<char, Dictionary<string, string>> entities)\r
+        {\r
+            mData = new Modal(metadata, grid, entities);\r
+            mView = new View(mData);\r
+        }\r
+\r
+\r
+        /// <summary>\r
+        /// Draw a representation of the map to the screen.\r
+        /// </summary>\r
+        /// <param name="spriteBatch">The jeewiz.</param>\r
+        public void Draw(SpriteBatch spriteBatch)\r
+        {\r
+            mView.Draw(spriteBatch);\r
+        }\r
+\r
+\r
+        /// <summary>\r
+        /// Get a point in screen-space from a coordinate in gridspace.\r
+        /// </summary>\r
+        /// <param name="x">X-coordinate.</param>\r
+        /// <param name="y">Y-coordinate.</param>\r
+        /// <returns>Transformed point.</returns>\r
+        public Point GetPointFromCoordinates(float x, float y)\r
+        {\r
+            return mView.GetPointFromCoordinates(x, y);\r
+        }\r
+\r
+        /// <summary>\r
+        /// Get a point in screen-space from a coordinate in gridspace.\r
+        /// </summary>\r
+        /// <param name="point">X,Y-coordinates.</param>\r
+        /// <returns>Transformed point.</returns>\r
+        public Point GetPointFromCoordinates(Vector2 point)\r
+        {\r
+            return mView.GetPointFromCoordinates(point.X, point.Y);\r
+        }\r
+\r
+        /// <summary>\r
+        /// Get a rectangle in screen-space centered around a coordinate in gridspace.\r
+        /// </summary>\r
+        /// <param name="x">X-coordinate.</param>\r
+        /// <param name="y">Y-coordinate.</param>\r
+        /// <returns>Transformed rectangle.</returns>\r
+        public Rectangle GetRectangleFromCoordinates(float x, float y)\r
+        {\r
+            return mView.GetRectangleFromCoordinates(x, y);\r
+        }\r
+\r
+        /// <summary>\r
+        /// Get a rectangle in screen-space centered around a coordinate in gridspace.\r
+        /// </summary>\r
+        /// <param name="point">X,Y-coordinates.</param>\r
+        /// <returns>Transformed rectangle.</returns>\r
+        public Rectangle GetRectangleFromCoordinates(Vector2 point)\r
+        {\r
+            return mView.GetRectangleFromCoordinates(point.X, point.Y);\r
+        }\r
+\r
+\r
+        /// <summary>\r
+        /// Determine whether or not a cell can be occupied by a game entity.\r
+        /// </summary>\r
+        /// <param name="x">X-coordinate.</param>\r
+        /// <param name="y">Y-coordinate.</param>\r
+        /// <returns>True if cell can be occupied, false otherwise.</returns>\r
+        public bool IsCellOpen(int x, int y)\r
+        {\r
+            return mData.IsCellOpen(x, y);\r
+        }\r
+\r
+        /// <summary>\r
+        /// Determine whether or not a cell can be occupied by a game entity.\r
+        /// </summary>\r
+        /// <param name="x">X,Y-coordinates.</param>\r
+        /// <returns>True if cell can be occupied, false otherwise.</returns>\r
+        public bool IsCellOpen(Point point)\r
+        {\r
+            return mData.IsCellOpen(point.X, point.Y);\r
+        }\r
+\r
+\r
+        /// <summary>\r
+        /// Get the entities loaded from the map file.\r
+        /// </summary>\r
+        /// <returns>Dictionary of entities.  The keys are the entity\r
+        /// identifiers and the value is a dictionary of key-value pairs\r
+        /// associated with that entity.</returns>\r
+        public Dictionary<char, Dictionary<string, string>> GetEntities()\r
+        {\r
+            return mData.Entities;\r
+        }\r
+\r
         #endregion\r
 \r
 \r
         #endregion\r
 \r
 \r
-        public Map(Data data)\r
+        #region Private Types\r
+\r
+        class Modal\r
+        {\r
+            Metadata mMetadata;\r
+            char[,] mGrid;\r
+            Dictionary<char, Dictionary<string, string>> mEntities;\r
+\r
+            public Modal(Metadata metadata, char[,] grid, Dictionary<char, Dictionary<string, string>> entities)\r
+            {\r
+                Debug.Assert(metadata != null);\r
+                Debug.Assert(grid != null);\r
+                Debug.Assert(entities != null);\r
+                Debug.Assert(metadata.GridWidth * metadata.GridHeight == grid.Length);\r
+\r
+                mMetadata = metadata;\r
+                mGrid = grid;\r
+                mEntities = entities;\r
+\r
+#if DEBUG\r
+                Console.WriteLine("Loaded map {0} of type {1} written by {2}.",\r
+                    metadata.Name,\r
+                    metadata.Type,\r
+                    metadata.Author);\r
+#endif\r
+            }\r
+\r
+\r
+            public Metadata Metadata { get { return mMetadata; } }\r
+            public Dictionary<char, Dictionary<string, string>> Entities { get { return mEntities; } }\r
+\r
+\r
+            public bool IsCellOpen(int x, int y)\r
+            {\r
+                // TODO: Still need to define characters for types of scenery.\r
+                return mGrid[x, y] == ' ';\r
+            }\r
+        }\r
+\r
+        class View\r
         {\r
         {\r
-            Console.WriteLine("Read map " + data.Name + " of type " + data.Type + " written by " + data.Author);\r
+            Modal mData;\r
+\r
+            public Vector2 CenterCell;\r
+            Viewport mViewport;\r
+\r
+\r
+            public View(Modal data)\r
+            {\r
+                Debug.Assert(data != null);\r
+                mData = data;\r
+            }\r
+\r
+            public void Draw(SpriteBatch spriteBatch)\r
+            {\r
+                mViewport = spriteBatch.GraphicsDevice.Viewport;\r
+\r
+                // TODO: There is no culling yet, but it runs so fast that it probably won't ever need it.\r
+                for (int y = 0; y < mData.Metadata.GridHeight; y++)\r
+                {\r
+                    for (int x = 0; x < mData.Metadata.GridWidth; x++)\r
+                    {\r
+                        if (mData.IsCellOpen(x, y))\r
+                        {\r
+                            spriteBatch.Draw(Map.DefaultTile, GetRectangleFromCoordinates(x, y), Color.White);\r
+                        }\r
+                        else\r
+                        {\r
+                            spriteBatch.Draw(Map.DefaultTile, GetRectangleFromCoordinates(x, y), Color.DarkBlue);\r
+                        }\r
+                    }\r
+                }\r
+            }\r
+\r
+            /// <summary>\r
+            /// Get a matrix to transform a point from grid-space to screen coordinates.  This\r
+            /// method uses the viewport to bound the edges of the map such that the camera\r
+            /// will not show anything outside of the grid.\r
+            /// </summary>\r
+            /// <param name="center">The point to put in the center.</param>\r
+            /// <returns>The transformation matrix.</returns>\r
+            Matrix GetTransformation(Vector2 center)\r
+            {\r
+                float halfRatio = PixelsToUnitSquares * 0.5f;\r
+                Matrix transform = Matrix.CreateTranslation(-center.X, -center.Y, 0.0f);\r
+                transform *= Matrix.CreateScale(PixelsToUnitSquares);\r
+                transform *= Matrix.CreateTranslation(mViewport.Width * 0.5f - halfRatio,\r
+                    mViewport.Height * 0.5f - halfRatio, 0.0f);\r
+\r
+                Vector2 topLeft = Vector2.Transform(new Vector2(0.0f, 0.0f), transform);\r
+                topLeft.X = Math.Max(mViewport.X, topLeft.X);\r
+                topLeft.Y = Math.Max(mViewport.Y, topLeft.Y);\r
+                transform *= Matrix.CreateTranslation(-topLeft.X, -topLeft.Y, 0.0f);\r
+\r
+                Vector2 bottomRight = Vector2.Transform(new Vector2((float)mData.Metadata.GridWidth,\r
+                    (float)mData.Metadata.GridHeight), transform);\r
+                float right = mViewport.X + mViewport.Width;\r
+                float bottom = mViewport.Y + mViewport.Height;\r
+                bottomRight.X = Math.Min(right, bottomRight.X) - right;\r
+                bottomRight.Y = Math.Min(bottom, bottomRight.Y) - bottom;\r
+                transform *= Matrix.CreateTranslation(-bottomRight.X, -bottomRight.Y, 0.0f);\r
+\r
+                return transform;\r
+            }\r
+\r
+\r
+            public Point GetPointFromCoordinates(float x, float y)\r
+            {\r
+                Matrix transform = GetTransformation(CenterCell);\r
+                Vector2 point = Vector2.Transform(new Vector2(x, y), transform);\r
+\r
+                return new Point((int)point.X, (int)point.Y);\r
+            }\r
+\r
+            public Rectangle GetRectangleFromCoordinates(float x, float y)\r
+            {\r
+                Matrix transform = GetTransformation(CenterCell);\r
+                Vector2 point = Vector2.Transform(new Vector2(x, y), transform);\r
+                \r
+                return new Rectangle((int)point.X, (int)point.Y, (int)PixelsToUnitSquares, (int)PixelsToUnitSquares);\r
+            }\r
         }\r
         }\r
+\r
+        #endregion\r
+\r
+\r
+        #region Private Variables\r
+\r
+        Modal mData;\r
+        View mView;\r
+\r
+        #endregion\r
     }\r
 }\r
     }\r
 }\r
index 35089ba6531d7345f15e0a164790f01f81c3d322..fbae4df99f8b7988a1e2f9f7069c7062c470af24 100644 (file)
@@ -2,7 +2,6 @@ using System;
 using System.Collections.Generic;\r
 using System.Linq;\r
 using System.Runtime.Serialization;\r
 using System.Collections.Generic;\r
 using System.Linq;\r
 using System.Runtime.Serialization;\r
-using System.Text.RegularExpressions;\r
 using Microsoft.Xna.Framework;\r
 using Microsoft.Xna.Framework.Content;\r
 using Microsoft.Xna.Framework.Graphics;\r
 using Microsoft.Xna.Framework;\r
 using Microsoft.Xna.Framework.Content;\r
 using Microsoft.Xna.Framework.Graphics;\r
@@ -15,61 +14,51 @@ namespace CarFire
     /// </summary>\r
     public class MapReader : ContentTypeReader<Map>\r
     {\r
     /// </summary>\r
     public class MapReader : ContentTypeReader<Map>\r
     {\r
+        #region Public Exceptions\r
+\r
+        /// <summary>\r
+        /// This exception is thrown during the loading of a map if any\r
+        /// part of the map file is inconsistent with the expected format\r
+        /// and order.\r
+        /// </summary>\r
         public class ParserException : System.ApplicationException\r
         {\r
         public class ParserException : System.ApplicationException\r
         {\r
-            public ParserException()\r
-            {\r
-            }\r
+            public ParserException() {}\r
 \r
             public ParserException(string message) :\r
 \r
             public ParserException(string message) :\r
-                base(message)\r
-            {\r
-            }\r
+                base(message) {}\r
 \r
             public ParserException(string message, System.Exception inner) :\r
 \r
             public ParserException(string message, System.Exception inner) :\r
-                base(message, inner)\r
-            {\r
-            }\r
+                base(message, inner) {}\r
 \r
             protected ParserException(SerializationInfo info, StreamingContext context) :\r
 \r
             protected ParserException(SerializationInfo info, StreamingContext context) :\r
-                base(info, context)\r
-            {\r
-            }\r
+                base(info, context) {}\r
         }\r
 \r
         }\r
 \r
-        ContentReader mInput;\r
-        int mLineNumber = 0;\r
-        int mExpectedNumberOfLines;\r
+        #endregion\r
 \r
 \r
-        Map.Data mData;\r
 \r
 \r
+        #region Protected Methods\r
 \r
         protected override Map Read(ContentReader input, Map existingInstance)\r
         {\r
 \r
         protected override Map Read(ContentReader input, Map existingInstance)\r
         {\r
-            mInput = input;\r
-            mExpectedNumberOfLines =  mInput.ReadInt32();\r
-\r
-            ReadData();\r
-\r
-            return new Map(mData);\r
+            mInput = new LineReader(input);\r
+            ReadSectionHeaders();\r
+            return new Map(mMetadata, mGrid, mEntities);\r
         }\r
 \r
         }\r
 \r
+        #endregion\r
 \r
 \r
-        #region Private Reading Methods\r
-        \r
-        string ReadLine()\r
-        {\r
-            return mInput.ReadString();\r
-        }\r
 \r
 \r
-        void ReadData()\r
+        #region Private Methods\r
+\r
+        void ReadSectionHeaders()\r
         {\r
         {\r
-            mData = new Map.Data();\r
+            mMetadata = new Map.Metadata();\r
 \r
 \r
-            while (mLineNumber < mExpectedNumberOfLines)\r
+            while (!mInput.End)\r
             {\r
             {\r
-                string line = ReadLine();\r
-                mLineNumber++;\r
+                string line = mInput.ReadLine();\r
 \r
                 while (line != null)\r
                 {\r
 \r
                 while (line != null)\r
                 {\r
@@ -92,12 +81,12 @@ namespace CarFire
                         }\r
                         else\r
                         {\r
                         }\r
                         else\r
                         {\r
-                            ThrowException("Unknown section", section);\r
+                            throw new ParserException("Unexpected section on line " + mInput.LineNumber + ": " + section);\r
                         }\r
                     }\r
                     else\r
                     {\r
                         }\r
                     }\r
                     else\r
                     {\r
-                        ThrowException("Unexpected text", line);\r
+                        throw new ParserException("Unexpected text on line " + mInput.LineNumber + ": " + line);\r
                     }\r
                 }\r
             }\r
                     }\r
                 }\r
             }\r
@@ -105,11 +94,9 @@ namespace CarFire
 \r
         string ReadMetadataSection()\r
         {\r
 \r
         string ReadMetadataSection()\r
         {\r
-            while (mLineNumber < mExpectedNumberOfLines)\r
+            while (!mInput.End)\r
             {\r
             {\r
-                string line = ReadLine();\r
-                mLineNumber++;\r
-\r
+                string line = mInput.ReadLine();\r
                 if (!IsLineSignificant(line)) continue;\r
 \r
                 string[] pair = Parse.KeyValuePair(line);\r
                 if (!IsLineSignificant(line)) continue;\r
 \r
                 string[] pair = Parse.KeyValuePair(line);\r
@@ -117,14 +104,14 @@ namespace CarFire
                 {\r
                     if (pair[0] == "type")\r
                     {\r
                 {\r
                     if (pair[0] == "type")\r
                     {\r
-                        object type = Parse.Constant<Map.Type>(pair[1]);\r
-                        if (type != null)\r
+                        Map.Type type = Parse.Constant<Map.Type>(pair[1]);\r
+                        if (type != default(Map.Type))\r
                         {\r
                         {\r
-                            mData.Type = (Map.Type)type;\r
+                            mMetadata.Type = type;\r
                         }\r
                         else\r
                         {\r
                         }\r
                         else\r
                         {\r
-                            ThrowException("Invalid type", pair[1]);\r
+                            throw new ParserException("Unexpected type on line " + mInput.LineNumber + ": " + pair[1]);\r
                         }\r
                     }\r
                     else if (pair[0] == "dimensions")\r
                         }\r
                     }\r
                     else if (pair[0] == "dimensions")\r
@@ -132,15 +119,16 @@ namespace CarFire
                         Point? dimensions = Parse.Coordinates(pair[1]);\r
                         if (dimensions != null)\r
                         {\r
                         Point? dimensions = Parse.Coordinates(pair[1]);\r
                         if (dimensions != null)\r
                         {\r
-                            mData.Dimensions = dimensions.Value;\r
-                            if (mData.Dimensions.X <= 0 || mData.Dimensions.Y <= 0)\r
+                            mMetadata.GridWidth = dimensions.Value.X;\r
+                            mMetadata.GridHeight = dimensions.Value.Y;\r
+                            if (mMetadata.GridWidth <= 0 || mMetadata.GridHeight <= 0)\r
                             {\r
                             {\r
-                                ThrowException("Invalid dimensions", pair[1]);\r
+                                throw new ParserException("Invalid dimensions on line " + mInput.LineNumber + ": " + pair[1]);\r
                             }\r
                         }\r
                         else\r
                         {\r
                             }\r
                         }\r
                         else\r
                         {\r
-                            ThrowException("Invalid value", pair[1]);\r
+                            throw new ParserException("Unexpected value on line " + mInput.LineNumber + ": " + pair[1]);\r
                         }\r
                     }\r
                     else if (pair[0] == "tileset")\r
                         }\r
                     }\r
                     else if (pair[0] == "tileset")\r
@@ -148,29 +136,46 @@ namespace CarFire
                         string tileset = Parse.String(pair[1]);\r
                         if (tileset != null)\r
                         {\r
                         string tileset = Parse.String(pair[1]);\r
                         if (tileset != null)\r
                         {\r
-                            mData.Tileset = tileset;\r
+                            mMetadata.Tileset = tileset;\r
                         }\r
                         else\r
                         {\r
                         }\r
                         else\r
                         {\r
-                            ThrowException("Invalid tileset", pair[1]);\r
+                            throw new ParserException("Unexpected tileset on line " + mInput.LineNumber + ": " + pair[1]);\r
                         }\r
                     }\r
                     else if (pair[0] == "numplayers")\r
                     {\r
                         }\r
                     }\r
                     else if (pair[0] == "numplayers")\r
                     {\r
-                        int[] numPlayers = Parse.Range(pair[1]);\r
-                        if (numPlayers != null)\r
+                        string[] list = Parse.List(pair[1]);\r
+                        if (list != null)\r
                         {\r
                         {\r
-                            mData.MinNumPlayers = numPlayers[0];\r
-                            mData.MaxNumPlayers = numPlayers[1];\r
-                            if (mData.MinNumPlayers <= 0 || mData.MaxNumPlayers <= 0 ||\r
-                                mData.MinNumPlayers > mData.MaxNumPlayers)\r
+                            foreach (string atom in list)\r
+                            {\r
+                                int[] range = Parse.Range(atom);\r
+                                if (range != null)\r
+                                {\r
+                                    for (int i = range[0]; i <= range[1]; i++)\r
+                                    {\r
+                                        mMetadata.NumPlayers.Add(i);\r
+                                    }\r
+                                    continue;\r
+                                }\r
+                                int? integer = Parse.Integer(atom);\r
+                                if (integer != null)\r
+                                {\r
+                                    mMetadata.NumPlayers.Add(integer.Value);\r
+                                    continue;\r
+                                }\r
+\r
+                                throw new ParserException("Unexpected atom on line " + mInput.LineNumber + ": " + atom);\r
+                            }\r
+                            if (mMetadata.NumPlayers.Count == 0)\r
                             {\r
                             {\r
-                                ThrowException("Invalid range", pair[1]);\r
+                                throw new ParserException("No numbers given on line " + mInput.LineNumber + ": " + pair[1]);\r
                             }\r
                         }\r
                         else\r
                         {\r
                             }\r
                         }\r
                         else\r
                         {\r
-                            ThrowException("Invalid value", pair[1]);\r
+                            throw new ParserException("Unexpected value on line " + mInput.LineNumber + ": " + pair[1]);\r
                         }\r
                     }\r
                     else if (pair[0] == "author")\r
                         }\r
                     }\r
                     else if (pair[0] == "author")\r
@@ -178,11 +183,11 @@ namespace CarFire
                         string author = Parse.String(pair[1]);\r
                         if (author != null)\r
                         {\r
                         string author = Parse.String(pair[1]);\r
                         if (author != null)\r
                         {\r
-                            mData.Author = author;\r
+                            mMetadata.Author = author;\r
                         }\r
                         else\r
                         {\r
                         }\r
                         else\r
                         {\r
-                            ThrowException("Invalid value", pair[1]);\r
+                            throw new ParserException("Unexpected value on line " + mInput.LineNumber + ": " + pair[1]);\r
                         }\r
                     }\r
                     else if (pair[0] == "levelname")\r
                         }\r
                     }\r
                     else if (pair[0] == "levelname")\r
@@ -190,16 +195,16 @@ namespace CarFire
                         string level = Parse.String(pair[1]);\r
                         if (level != null)\r
                         {\r
                         string level = Parse.String(pair[1]);\r
                         if (level != null)\r
                         {\r
-                            mData.Name = level;\r
+                            mMetadata.Name = level;\r
                         }\r
                         else\r
                         {\r
                         }\r
                         else\r
                         {\r
-                            ThrowException("Invalid value", pair[1]);\r
+                            throw new ParserException("Unexpected value on line " + mInput.LineNumber + ": " + pair[1]);\r
                         }\r
                     }\r
                     else\r
                     {\r
                         }\r
                     }\r
                     else\r
                     {\r
-                        Console.WriteLine("Unimplemented key: " + pair[0]);\r
+                        throw new ParserException("Unexpected key on line " + mInput.LineNumber + ": " + pair[0]);\r
                     }\r
                 }\r
                 else\r
                     }\r
                 }\r
                 else\r
@@ -213,33 +218,35 @@ namespace CarFire
 \r
         string ReadMapTableSection()\r
         {\r
 \r
         string ReadMapTableSection()\r
         {\r
-            if (mData.Dimensions.X == 0 || mData.Dimensions.Y == 0)\r
+            if (mMetadata == null || mMetadata.GridWidth == 0 || mMetadata.GridHeight == 0)\r
             {\r
             {\r
-                ThrowException("Unexpected section", "You must define the map dimensions before this section.");\r
+                throw new ParserException("Unexpected section on line " + mInput.LineNumber +\r
+                    ": You must define the map dimensions before this section.");\r
             }\r
 \r
             }\r
 \r
-            mData.Grid = new char[mData.Dimensions.X, mData.Dimensions.Y];\r
+            mGrid = new char[mMetadata.GridWidth, mMetadata.GridHeight];\r
 \r
             int y;\r
 \r
             int y;\r
-            for (y = 0; y < mData.Dimensions.Y && mLineNumber < mExpectedNumberOfLines; y++)\r
+            for (y = 0; y < mMetadata.GridHeight && !mInput.End; y++)\r
             {\r
             {\r
-                string line = ReadLine();\r
-                mLineNumber++;\r
+                string line = mInput.ReadLine();\r
 \r
 \r
-                if (line.Length < mData.Dimensions.X)\r
+                if (line.Length < mMetadata.GridWidth)\r
                 {\r
                 {\r
-                    ThrowException("Not enough characters", "Expecting " + mData.Dimensions.X + " characters.");\r
+                    throw new ParserException("Unexpected EOL on line " + mInput.LineNumber +\r
+                        ": The number of characters should match the width dimension (" + mMetadata.GridWidth + ").");\r
                 }\r
 \r
                 }\r
 \r
-                for (int x = 0; x < mData.Dimensions.X; x++)\r
+                for (int x = 0; x < mMetadata.GridWidth; x++)\r
                 {\r
                 {\r
-                    mData.Grid[x, y] = line[x];\r
+                    mGrid[x, y] = line[x];\r
                 }\r
             }\r
 \r
                 }\r
             }\r
 \r
-            if (y < mData.Dimensions.Y)\r
+            if (y < mMetadata.GridHeight)\r
             {\r
             {\r
-                ThrowException("Unexpected ", "");\r
+                throw new ParserException("Unexpected EOF on line " + mInput.LineNumber +\r
+                    ": The number of lines in this section should match the height dimension (" + mMetadata.GridHeight + ").");\r
             }\r
 \r
             return null;\r
             }\r
 \r
             return null;\r
@@ -247,15 +254,17 @@ namespace CarFire
 \r
         string ReadEntitySection(char entity)\r
         {\r
 \r
         string ReadEntitySection(char entity)\r
         {\r
-            while (mLineNumber < mExpectedNumberOfLines)\r
+            Dictionary<string, string> pairs = new Dictionary<string, string>();\r
+            mEntities[entity] = pairs;\r
+\r
+            while (!mInput.End)\r
             {\r
             {\r
-                string line = ReadLine();\r
-                mLineNumber++;\r
+                string line = mInput.ReadLine();\r
 \r
                 string[] pair = Parse.KeyValuePair(line);\r
                 if (pair != null)\r
                 {\r
 \r
                 string[] pair = Parse.KeyValuePair(line);\r
                 if (pair != null)\r
                 {\r
-                    // TODO\r
+                    pairs[pair[0]] = pair[1];\r
                 }\r
                 else\r
                 {\r
                 }\r
                 else\r
                 {\r
@@ -266,22 +275,56 @@ namespace CarFire
             return null;\r
         }\r
 \r
             return null;\r
         }\r
 \r
-        #endregion\r
-\r
-\r
-        #region Private Methods\r
 \r
         bool IsLineSignificant(string line)\r
         {\r
 \r
         bool IsLineSignificant(string line)\r
         {\r
-            if (line.Length == 0 || Regex.IsMatch(line, @"^;|^\s*$")) return false;\r
+            if (line.Trim().Length == 0 || Parse.IniComment(line) != null) return false;\r
             return true;\r
         }\r
 \r
             return true;\r
         }\r
 \r
-        void ThrowException(string problem, string text)\r
+        #endregion\r
+\r
+\r
+        #region Private Types\r
+\r
+        /// <summary>\r
+        /// This private class wraps around ContentReader to make it more\r
+        /// convenient to use it as an input stream reader.\r
+        /// </summary>\r
+        class LineReader\r
         {\r
         {\r
-            throw new ParserException(problem + " on line " + mLineNumber + ": " + text);\r
+            ContentReader mInput;\r
+            int mLineNumber = 0;\r
+            int mExpectedNumberOfLines;\r
+\r
+            public LineReader(ContentReader input)\r
+            {\r
+                mInput = input;\r
+                mExpectedNumberOfLines = mInput.ReadInt32();\r
+            }\r
+\r
+            public string ReadLine()\r
+            {\r
+                mLineNumber++;\r
+                return mInput.ReadString();\r
+            }\r
+\r
+            public int LineNumber { get { return mLineNumber; } }\r
+\r
+            public bool End { get { return mLineNumber >= mExpectedNumberOfLines; } }\r
         }\r
 \r
         #endregion\r
         }\r
 \r
         #endregion\r
+\r
+\r
+        #region Private Variables\r
+\r
+        Map.Metadata mMetadata;\r
+        char[,] mGrid;\r
+        Dictionary<char, Dictionary<string, string>> mEntities = new Dictionary<char, Dictionary<string, string>>();\r
+\r
+        LineReader mInput;\r
+\r
+        #endregion\r
     }\r
 }\r
     }\r
 }\r
index 169cf30a7b8340b59af9aa61aae521c9e7c04ad8..b9320778babcbcdfb62d685aec9306ce2639d6c7 100644 (file)
@@ -25,6 +25,18 @@ namespace CarFire
             return null;\r
         }\r
 \r
             return null;\r
         }\r
 \r
+        /// <summary>\r
+        /// Parses a comment of an INI file.\r
+        /// </summary>\r
+        /// <param name="line">Text.</param>\r
+        /// <returns>The comment.</returns>\r
+        public static string IniComment(string line)\r
+        {\r
+            Match match = Regex.Match(line, @"^;\s*(.*)\s*$");\r
+            if (match.Success) return match.Groups[1].Value;\r
+            return null;\r
+        }\r
+\r
         /// <summary>\r
         /// Parses a key-value pair.\r
         /// </summary>\r
         /// <summary>\r
         /// Parses a key-value pair.\r
         /// </summary>\r
@@ -182,7 +194,6 @@ namespace CarFire
             // FIXME: This may barf all over itself if there are nested parentheses, doublequotes, brackets, etc.\r
             foreach (Match match in matches)\r
             {\r
             // FIXME: This may barf all over itself if there are nested parentheses, doublequotes, brackets, etc.\r
             foreach (Match match in matches)\r
             {\r
-                Console.WriteLine("matched: " + match.Value);\r
                 list.Add(match.Value);\r
             }\r
 \r
                 list.Add(match.Value);\r
             }\r
 \r
index 0623ae9f413b96f62555e2f648d2f373732ce99a..6f8a421358d73dae6d66e61f1d102d2a4f59124e 100644 (file)
@@ -1,3 +1,7 @@
+\r
+// DEBUG: for map testing\r
+//#define MAP_TESTING\r
+\r
 using System;\r
 using System.Collections.Generic;\r
 using System.Linq;\r
 using System;\r
 using System.Collections.Generic;\r
 using System.Linq;\r
@@ -25,6 +29,10 @@ namespace CarFire
         IScreenManager screenManager;\r
         IDeterministicGame deterministicGame;\r
 \r
         IScreenManager screenManager;\r
         IDeterministicGame deterministicGame;\r
 \r
+#if MAP_TESTING\r
+        Map map;\r
+#endif\r
+\r
         public XnaGame()\r
         {\r
             graphics = new GraphicsDeviceManager(this);\r
         public XnaGame()\r
         {\r
             graphics = new GraphicsDeviceManager(this);\r
@@ -67,6 +75,12 @@ namespace CarFire
 \r
             screenManager.LoadContent(Content, graphics);\r
             deterministicGame.LoadContent(Content);\r
 \r
             screenManager.LoadContent(Content, graphics);\r
             deterministicGame.LoadContent(Content);\r
+\r
+#if MAP_TESTING\r
+            map = Content.Load<Map>("Maps/sandbox");\r
+            Map.DefaultTile = Content.Load<Texture2D>("default");\r
+            map.CenterCell = new Vector2(7, 7);\r
+#endif\r
         }\r
 \r
         /// <summary>\r
         }\r
 \r
         /// <summary>\r
@@ -101,6 +115,9 @@ namespace CarFire
 \r
             spriteBatch.Begin();\r
             networkGame.Draw(gameTime, spriteBatch);\r
 \r
             spriteBatch.Begin();\r
             networkGame.Draw(gameTime, spriteBatch);\r
+#if MAP_TESTING\r
+            map.Draw(spriteBatch);\r
+#endif\r
             spriteBatch.End();\r
 \r
             base.Draw(gameTime);\r
             spriteBatch.End();\r
 \r
             base.Draw(gameTime);\r
This page took 0.048431 seconds and 4 git commands to generate.