]> Dogcows Code - chaz/carfire/commitdiff
Entity loading implemented.
authorCharles <Charles@92bb83a3-7c8f-8a45-bc97-515c4e399668>
Thu, 15 Apr 2010 00:30:08 +0000 (00:30 +0000)
committerCharles <Charles@92bb83a3-7c8f-8a45-bc97-515c4e399668>
Thu, 15 Apr 2010 00:30:08 +0000 (00:30 +0000)
New parsing method for single characters.
Refactored the map reader a bit.
Updated documentation related to entity-loading changes.

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

CarFire/CarFire/CarFire/Content/Maps/sandbox.cfmap
CarFire/CarFire/CarFire/Map.cs
CarFire/CarFire/CarFire/MapReader.cs
CarFire/CarFire/CarFire/Parse.cs
CarFire/CarFire/CarFire/XnaGame.cs
CarFire/leveleditor/doc/leveleditor.txt

index 48338b32e783155fd5a8f4b170bfd03a1670304f..9ae2acdea3bd359c690165489acb0f364f465e72 100644 (file)
        numplayers = <1,4>\r
 \r
 [A]\r
        numplayers = <1,4>\r
 \r
 [A]\r
-       entity = SaberMonster\r
+       type = SaberMonster\r
        path = [1,1] [5,6] wait(5) [45,2]\r
        loop = true\r
        path = [1,1] [5,6] wait(5) [45,2]\r
        loop = true\r
+       create = [5,7] [2,3]\r
 [B]\r
        condition = has(key)\r
        event = wait(2) remove([2,6]) remove([2,7])\r
 [B]\r
        condition = has(key)\r
        event = wait(2) remove([2,6]) remove([2,7])\r
 +------------------------------------------------------------------------------+\r
 |                                                                              |\r
 | 1                                                                            |\r
 +------------------------------------------------------------------------------+\r
 |                                                                              |\r
 | 1                                                                            |\r
-|                                                                              |\r
+|             B                                                                |\r
 | 2                +----                                                       |\r
 |                  |                                                           |\r
 | 2                +----                                                       |\r
 |                  |                                                           |\r
-| 3                |                                                           |\r
+| 3                |        A                                                  |\r
 |                  |                                                           |\r
 | 4                |                                                           |\r
 |                  |                                                           |\r
 |                  |                                                           |\r
 | 4                |                                                           |\r
 |                  |                                                           |\r
index 95a8c2c9b75253b17cc4debd169d312ca3cf43f5..c277a7aa4590633359fd50a7d7ef9349f0ec0eff 100644 (file)
@@ -7,6 +7,7 @@ using System.Runtime.Serialization;
 using System.Diagnostics;\r
 using Microsoft.Xna.Framework;\r
 using Microsoft.Xna.Framework.Graphics;\r
 using System.Diagnostics;\r
 using Microsoft.Xna.Framework;\r
 using Microsoft.Xna.Framework.Graphics;\r
+using System.Reflection;\r
 \r
 namespace CarFire\r
 {\r
 \r
 namespace CarFire\r
 {\r
@@ -23,7 +24,7 @@ namespace CarFire
 \r
         #region Public Constants\r
 \r
 \r
         #region Public Constants\r
 \r
-        public const float PixelsToUnitSquares = 64.0f;\r
+        public const float PixelsToUnitSquares = 8.0f;\r
 \r
         #endregion\r
 \r
 \r
         #endregion\r
 \r
@@ -54,6 +55,16 @@ namespace CarFire
             public int GridHeight;\r
         }\r
 \r
             public int GridHeight;\r
         }\r
 \r
+        /// <summary>\r
+        /// The container class for information about an entity defined in the map.\r
+        /// </summary>\r
+        public class RawEntity\r
+        {\r
+            public char Id;\r
+            public Point Position;\r
+            public Dictionary<string, string> Attributes = new Dictionary<string, string>();\r
+        }\r
+\r
         #endregion\r
 \r
 \r
         #endregion\r
 \r
 \r
@@ -92,6 +103,11 @@ namespace CarFire
         // TODO: This should return whatever object we end up using for tilesets.\r
         public string Tileset { get { return mData.Metadata.Tileset; } }\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
+        /// <summary>\r
+        /// Get a list of the raw entity containers loaded with the map.\r
+        /// </summary>\r
+        public List<RawEntity> RawEntities { get { return mData.Entities; } }\r
+\r
 \r
         /// <summary>\r
         /// Get and set the coordinate of the grid cell that should be in\r
 \r
         /// <summary>\r
         /// Get and set the coordinate of the grid cell that should be in\r
@@ -112,7 +128,7 @@ namespace CarFire
         /// Construct a map with the provided map data.\r
         /// </summary>\r
         /// <param name="data">Map data.</param>\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
+        public Map(Metadata metadata, char[,] grid, List<RawEntity> entities)\r
         {\r
             mData = new Modal(metadata, grid, entities);\r
             mView = new View(mData);\r
         {\r
             mData = new Modal(metadata, grid, entities);\r
             mView = new View(mData);\r
@@ -186,7 +202,7 @@ namespace CarFire
         /// <summary>\r
         /// Determine whether or not a cell can be occupied by a game entity.\r
         /// </summary>\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
+        /// <param name="point">X,Y-coordinates.</param>\r
         /// <returns>True if cell can be occupied, false otherwise.</returns>\r
         public bool IsCellOpen(Point point)\r
         {\r
         /// <returns>True if cell can be occupied, false otherwise.</returns>\r
         public bool IsCellOpen(Point point)\r
         {\r
@@ -195,14 +211,24 @@ namespace CarFire
 \r
 \r
         /// <summary>\r
 \r
 \r
         /// <summary>\r
-        /// Get the entities loaded from the map file.\r
+        /// Get all the entities loaded from the map file.  Exceptions could be\r
+        /// thrown if there are entities without associated classes.\r
         /// </summary>\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
+        /// <returns>List of entity objects loaded.</returns>\r
+        public List<object> GetAllEntities()\r
         {\r
         {\r
-            return mData.Entities;\r
+            return mData.GetAllEntities();\r
+        }\r
+\r
+        /// <summary>\r
+        /// Get the entities of a certain type loaded from the map file.  Exceptions\r
+        /// could be thrown if there are entities without associated classes.\r
+        /// </summary>\r
+        /// <typeparam name="T">Type of the entity you want a list of.</typeparam>\r
+        /// <returns>List of entity objects loaded.</returns>\r
+        public List<T> GetEntities<T>()\r
+        {\r
+            return mData.GetEntities<T>();\r
         }\r
 \r
         #endregion\r
         }\r
 \r
         #endregion\r
@@ -214,9 +240,9 @@ namespace CarFire
         {\r
             Metadata mMetadata;\r
             char[,] mGrid;\r
         {\r
             Metadata mMetadata;\r
             char[,] mGrid;\r
-            Dictionary<char, Dictionary<string, string>> mEntities;\r
+            List<RawEntity> mEntities;\r
 \r
 \r
-            public Modal(Metadata metadata, char[,] grid, Dictionary<char, Dictionary<string, string>> entities)\r
+            public Modal(Metadata metadata, char[,] grid, List<RawEntity> entities)\r
             {\r
                 Debug.Assert(metadata != null);\r
                 Debug.Assert(grid != null);\r
             {\r
                 Debug.Assert(metadata != null);\r
                 Debug.Assert(grid != null);\r
@@ -237,7 +263,7 @@ namespace CarFire
 \r
 \r
             public Metadata Metadata { get { return mMetadata; } }\r
 \r
 \r
             public Metadata Metadata { get { return mMetadata; } }\r
-            public Dictionary<char, Dictionary<string, string>> Entities { get { return mEntities; } }\r
+            public List<RawEntity> Entities { get { return mEntities; } }\r
 \r
 \r
             public bool IsCellOpen(int x, int y)\r
 \r
 \r
             public bool IsCellOpen(int x, int y)\r
@@ -245,6 +271,59 @@ namespace CarFire
                 // TODO: Still need to define characters for types of scenery.\r
                 return mGrid[x, y] == ' ';\r
             }\r
                 // TODO: Still need to define characters for types of scenery.\r
                 return mGrid[x, y] == ' ';\r
             }\r
+\r
+\r
+            public List<object> GetAllEntities()\r
+            {\r
+                List<object> list = new List<object>();\r
+\r
+                foreach (RawEntity raw in mEntities)\r
+                {\r
+                    if (raw.Attributes.ContainsKey("type"))\r
+                    {\r
+                        string typename = raw.Attributes["type"];\r
+\r
+                        object[] args = new object[3];\r
+                        args[0] = raw.Id;\r
+                        args[1] = raw.Position;\r
+                        args[2] = raw.Attributes;\r
+\r
+                        object entity = Activator.CreateInstance(System.Type.GetType("CarFire." + typename), args);\r
+                        if (entity != null) list.Add(entity);\r
+                        else Console.WriteLine("Entity of type " + typename + " not loaded because an entity class can't be found.");\r
+                    }\r
+                    else\r
+                    {\r
+                        Console.WriteLine("Ignoring entity with identifier " + raw.Id + " since it has no type key.");\r
+                    }\r
+                }\r
+\r
+                return list;\r
+            }\r
+\r
+            public List<T> GetEntities<T>()\r
+            {\r
+                System.Type type = typeof(T);\r
+                List<T> list = new List<T>();\r
+\r
+                string typename = typeof(T).Name;\r
+                foreach (RawEntity raw in mEntities)\r
+                {\r
+                    if (raw.Attributes.ContainsKey("type") && typename == raw.Attributes["type"])\r
+                    {\r
+                        object[] args = new object[3];\r
+                        args[0] = raw.Id;\r
+                        args[1] = raw.Position;\r
+                        args[2] = raw.Attributes;\r
+\r
+                        T entity = (T)Activator.CreateInstance(type, args);\r
+                        if (entity != null) list.Add(entity);\r
+                        else Console.WriteLine("Entity of type " + typename + " not loaded because an entity class can't be found.");\r
+                    }\r
+                }\r
+\r
+                return list;\r
+            }\r
         }\r
 \r
         class View\r
         }\r
 \r
         class View\r
index fbae4df99f8b7988a1e2f9f7069c7062c470af24..cfb1f945219cb1bf73e315957bee1cc0fd7285f4 100644 (file)
@@ -5,6 +5,7 @@ using System.Runtime.Serialization;
 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
+using System.Reflection;\r
 \r
 namespace CarFire\r
 {\r
 \r
 namespace CarFire\r
 {\r
@@ -42,276 +43,393 @@ namespace CarFire
 \r
         protected override Map Read(ContentReader input, Map existingInstance)\r
         {\r
 \r
         protected override Map Read(ContentReader input, Map existingInstance)\r
         {\r
-            mInput = new LineReader(input);\r
-            ReadSectionHeaders();\r
-            return new Map(mMetadata, mGrid, mEntities);\r
+            mImpl = new Impl(input);\r
+            return mImpl.GetMap();\r
         }\r
 \r
         #endregion\r
 \r
 \r
         }\r
 \r
         #endregion\r
 \r
 \r
-        #region Private Methods\r
+        #region Private Types\r
 \r
 \r
-        void ReadSectionHeaders()\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
-            mMetadata = new Map.Metadata();\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
 \r
-            while (!mInput.End)\r
+            public string ReadLine()\r
             {\r
             {\r
-                string line = mInput.ReadLine();\r
+                mLineNumber++;\r
+                return mInput.ReadString();\r
+            }\r
 \r
 \r
-                while (line != null)\r
+            public int LineNumber { get { return mLineNumber; } }\r
+\r
+            public bool End { get { return mLineNumber >= mExpectedNumberOfLines; } }\r
+        }\r
+\r
+\r
+        /// <summary>\r
+        /// This class is the actual implementation.  The implementation is wrapped\r
+        /// in a subclass because the invoker seems to only be able to invoke public\r
+        /// methods, and this needs to invoke methods that shouldn't be public.\r
+        /// </summary>\r
+        class Impl\r
+        {\r
+            public Impl(ContentReader input)\r
+            {\r
+                mInput = new LineReader(input);\r
+                ReadSectionHeaders();\r
+                PostProcess();\r
+            }\r
+\r
+            public Map GetMap()\r
+            {\r
+                return new Map(mMetadata, mGrid, mEntities);\r
+            }\r
+\r
+\r
+            public void ReadSectionHeaders()\r
+            {\r
+                mMetadata = new Map.Metadata();\r
+\r
+                while (!mInput.End)\r
                 {\r
                 {\r
-                    if (!IsLineSignificant(line)) break;\r
+                    string line = mInput.ReadLine();\r
 \r
 \r
-                    string section = Parse.IniSectionHeader(line);\r
-                    if (section != null)\r
+                    while (line != null)\r
                     {\r
                     {\r
-                        if (section == "metadata")\r
+                        if (!IsLineSignificant(line)) break;\r
+\r
+                        string section = Parse.IniSectionHeader(line);\r
+                        if (section != null)\r
                         {\r
                         {\r
-                            line = ReadMetadataSection();\r
+                            if (section == "metadata")\r
+                            {\r
+                                line = ReadMetadataSection();\r
+                            }\r
+                            else if (section == "maptable")\r
+                            {\r
+                                line = ReadMapTableSection();\r
+                            }\r
+                            else if (section.Length == 1 && IsValidEntityIdentifier(section[0]))\r
+                            {\r
+                                line = ReadEntitySection(section[0]);\r
+                            }\r
+                            else\r
+                            {\r
+                                throw new ParserException("Unexpected section on line " + mInput.LineNumber + ": " + section);\r
+                            }\r
                         }\r
                         }\r
-                        else if (section == "maptable")\r
+                        else\r
                         {\r
                         {\r
-                            line = ReadMapTableSection();\r
+                            throw new ParserException("Unexpected text on line " + mInput.LineNumber + ": " + line);\r
                         }\r
                         }\r
-                        else if (section.Length == 1)\r
+                    }\r
+                }\r
+            }\r
+\r
+            string ReadMetadataSection()\r
+            {\r
+                while (!mInput.End)\r
+                {\r
+                    string line = mInput.ReadLine();\r
+                    if (!IsLineSignificant(line)) continue;\r
+\r
+                    string[] pair = Parse.KeyValuePair(line);\r
+                    if (pair != null)\r
+                    {\r
+                        try\r
                         {\r
                         {\r
-                            line = ReadEntitySection(section[0]);\r
+                            string methodName = "set_" + pair[0].ToLowerInvariant();\r
+                            object[] args = new object[1];\r
+                            args[0] = pair[1];\r
+                            GetType().InvokeMember(methodName, BindingFlags.InvokeMethod, null, this, args);\r
                         }\r
                         }\r
-                        else\r
+#pragma warning disable 0168\r
+                        catch (System.MissingMethodException ex)\r
+#pragma warning restore 0168\r
                         {\r
                         {\r
-                            throw new ParserException("Unexpected section on line " + mInput.LineNumber + ": " + section);\r
+                            throw new ParserException("Unexpected key on line " + mInput.LineNumber + ": " + pair[0]);\r
                         }\r
                     }\r
                     else\r
                     {\r
                         }\r
                     }\r
                     else\r
                     {\r
-                        throw new ParserException("Unexpected text on line " + mInput.LineNumber + ": " + line);\r
+                        return line;\r
                     }\r
                 }\r
                     }\r
                 }\r
+\r
+                return null;\r
             }\r
             }\r
-        }\r
 \r
 \r
-        string ReadMetadataSection()\r
-        {\r
-            while (!mInput.End)\r
+            string ReadMapTableSection()\r
             {\r
             {\r
-                string line = mInput.ReadLine();\r
-                if (!IsLineSignificant(line)) continue;\r
+                if (mMetadata == null || mMetadata.GridWidth == 0 || mMetadata.GridHeight == 0)\r
+                {\r
+                    throw new ParserException("Unexpected section on line " + mInput.LineNumber +\r
+                        ": You must define the map dimensions before this section.");\r
+                }\r
 \r
 \r
-                string[] pair = Parse.KeyValuePair(line);\r
-                if (pair != null)\r
+                mGrid = new char[mMetadata.GridWidth, mMetadata.GridHeight];\r
+\r
+                int y;\r
+                for (y = 0; y < mMetadata.GridHeight && !mInput.End; y++)\r
                 {\r
                 {\r
-                    if (pair[0] == "type")\r
+                    string line = mInput.ReadLine();\r
+\r
+                    if (line.Length < mMetadata.GridWidth)\r
                     {\r
                     {\r
-                        Map.Type type = Parse.Constant<Map.Type>(pair[1]);\r
-                        if (type != default(Map.Type))\r
-                        {\r
-                            mMetadata.Type = type;\r
-                        }\r
-                        else\r
-                        {\r
-                            throw new ParserException("Unexpected type on line " + mInput.LineNumber + ": " + pair[1]);\r
-                        }\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
-                    else if (pair[0] == "dimensions")\r
+\r
+                    for (int x = 0; x < mMetadata.GridWidth; x++)\r
                     {\r
                     {\r
-                        Point? dimensions = Parse.Coordinates(pair[1]);\r
-                        if (dimensions != null)\r
-                        {\r
-                            mMetadata.GridWidth = dimensions.Value.X;\r
-                            mMetadata.GridHeight = dimensions.Value.Y;\r
-                            if (mMetadata.GridWidth <= 0 || mMetadata.GridHeight <= 0)\r
-                            {\r
-                                throw new ParserException("Invalid dimensions on line " + mInput.LineNumber + ": " + pair[1]);\r
-                            }\r
-                        }\r
-                        else\r
-                        {\r
-                            throw new ParserException("Unexpected value on line " + mInput.LineNumber + ": " + pair[1]);\r
-                        }\r
+                        mGrid[x, y] = line[x];\r
                     }\r
                     }\r
-                    else if (pair[0] == "tileset")\r
+                }\r
+\r
+                if (y < mMetadata.GridHeight)\r
+                {\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
+            string ReadEntitySection(char identifier)\r
+            {\r
+                Dictionary<string, string> pairs = new Dictionary<string, string>();\r
+                mEntitySections[identifier] = pairs;\r
+\r
+                while (!mInput.End)\r
+                {\r
+                    string line = mInput.ReadLine();\r
+\r
+                    string[] pair = Parse.KeyValuePair(line);\r
+                    if (pair != null)\r
                     {\r
                     {\r
-                        string tileset = Parse.String(pair[1]);\r
-                        if (tileset != null)\r
-                        {\r
-                            mMetadata.Tileset = tileset;\r
-                        }\r
-                        else\r
-                        {\r
-                            throw new ParserException("Unexpected tileset on line " + mInput.LineNumber + ": " + pair[1]);\r
-                        }\r
+                        pairs[pair[0]] = pair[1];\r
                     }\r
                     }\r
-                    else if (pair[0] == "numplayers")\r
+                    else\r
                     {\r
                     {\r
-                        string[] list = Parse.List(pair[1]);\r
-                        if (list != null)\r
+                        return line;\r
+                    }\r
+                }\r
+\r
+                return null;\r
+            }\r
+\r
+\r
+            void PostProcess()\r
+            {\r
+                if (mMetadata == null || mGrid == null)\r
+                {\r
+                    throw new ParserException("Missing a required section.  Make sure the metadata and grid are there.");\r
+                }\r
+\r
+                mEntities = new List<Map.RawEntity>();\r
+                mPlayerPositions = new Point[mMetadata.NumPlayers.Max() + 1];\r
+\r
+                // create entities defined completely\r
+                foreach (char identifier in mEntitySections.Keys)\r
+                {\r
+                    Dictionary<string, string> pairs = mEntitySections[identifier];\r
+                    if (pairs.ContainsKey("create"))\r
+                    {\r
+                        string[] list = Parse.List(pairs["create"]);\r
+                        foreach (string positionString in list)\r
                         {\r
                         {\r
-                            foreach (string atom in list)\r
+                            Point? position = Parse.Coordinates(positionString);\r
+                            if (position != null)\r
                             {\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
+                                Map.RawEntity createEntity = new Map.RawEntity();\r
+                                createEntity.Id = identifier;\r
+                                createEntity.Position = position.Value;\r
+                                createEntity.Attributes = pairs;\r
+                                mEntities.Add(createEntity);\r
                             }\r
                             }\r
-                            if (mMetadata.NumPlayers.Count == 0)\r
+                            else\r
                             {\r
                             {\r
-                                throw new ParserException("No numbers given on line " + mInput.LineNumber + ": " + pair[1]);\r
+                                throw new ParserException("Unexpected value of key `create' defined for entity " + identifier + ".");\r
                             }\r
                         }\r
                             }\r
                         }\r
-                        else\r
-                        {\r
-                            throw new ParserException("Unexpected value on line " + mInput.LineNumber + ": " + pair[1]);\r
-                        }\r
+                        pairs.Remove("create");\r
                     }\r
                     }\r
-                    else if (pair[0] == "author")\r
-                    {\r
-                        string author = Parse.String(pair[1]);\r
-                        if (author != null)\r
-                        {\r
-                            mMetadata.Author = author;\r
-                        }\r
-                        else\r
-                        {\r
-                            throw new ParserException("Unexpected value on line " + mInput.LineNumber + ": " + pair[1]);\r
-                        }\r
-                    }\r
-                    else if (pair[0] == "levelname")\r
+                }\r
+\r
+                // create entities with positions defined on the grid\r
+                // and get player starting positions\r
+                for (int x = 0; x < mMetadata.GridWidth; x++)\r
+                {\r
+                    for (int y = 0; y < mMetadata.GridHeight; y++)\r
                     {\r
                     {\r
-                        string level = Parse.String(pair[1]);\r
-                        if (level != null)\r
+                        char identifier = mGrid[x, y];\r
+                        if (IsValidEntityIdentifier(identifier))\r
                         {\r
                         {\r
-                            mMetadata.Name = level;\r
+                            if (mEntitySections.ContainsKey(identifier))\r
+                            {\r
+                                Map.RawEntity createEntity = new Map.RawEntity();\r
+                                createEntity.Id = identifier;\r
+                                createEntity.Position = new Point(x, y);\r
+                                createEntity.Attributes = mEntitySections[identifier];\r
+                                mEntities.Add(createEntity);\r
+                            }\r
+                            else\r
+                            {\r
+                                throw new ParserException("Unexpected entity (" + identifier +\r
+                                    ") placed on the grid at [" + x + "," + y + "] but not defined.");\r
+                            }\r
+                            mGrid[x, y] = mDefaultTile;\r
                         }\r
                         }\r
-                        else\r
+                        else if ('1' <= identifier && identifier <= '9')\r
                         {\r
                         {\r
-                            throw new ParserException("Unexpected value on line " + mInput.LineNumber + ": " + pair[1]);\r
+                            int playerNum = identifier - 48;\r
+                            if (playerNum < mPlayerPositions.Count())\r
+                            {\r
+                                mPlayerPositions[playerNum] = new Point(x, y);\r
+                            }\r
+                            mGrid[x, y] = mDefaultTile;\r
                         }\r
                     }\r
                         }\r
                     }\r
-                    else\r
-                    {\r
-                        throw new ParserException("Unexpected key on line " + mInput.LineNumber + ": " + pair[0]);\r
-                    }\r
                 }\r
                 }\r
-                else\r
+\r
+                // check if all needed player positions are defined\r
+                for (int i = 1; i < mPlayerPositions.Count(); i++)\r
                 {\r
                 {\r
-                    return line;\r
+                    if (mPlayerPositions[i] == default(Point))\r
+                    {\r
+                        throw new ParserException("Not enough player positions were defined on the grid; " +\r
+                            "are missing a spot for player " + i + ".");\r
+                    }\r
                 }\r
             }\r
 \r
                 }\r
             }\r
 \r
-            return null;\r
-        }\r
 \r
 \r
-        string ReadMapTableSection()\r
-        {\r
-            if (mMetadata == null || mMetadata.GridWidth == 0 || mMetadata.GridHeight == 0)\r
+            bool IsLineSignificant(string line)\r
             {\r
             {\r
-                throw new ParserException("Unexpected section on line " + mInput.LineNumber +\r
-                    ": You must define the map dimensions before this section.");\r
+                if (line.Trim().Length == 0 || Parse.IniComment(line) != null) return false;\r
+                return true;\r
             }\r
 \r
             }\r
 \r
-            mGrid = new char[mMetadata.GridWidth, mMetadata.GridHeight];\r
-\r
-            int y;\r
-            for (y = 0; y < mMetadata.GridHeight && !mInput.End; y++)\r
+            bool IsValidEntityIdentifier(char id)\r
             {\r
             {\r
-                string line = mInput.ReadLine();\r
+                if (('a' <= id && id <= 'z') || ('A' <= id && id <= 'Z')) return true;\r
+                return false;\r
+            }\r
 \r
 \r
-                if (line.Length < mMetadata.GridWidth)\r
-                {\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
-                for (int x = 0; x < mMetadata.GridWidth; x++)\r
-                {\r
-                    mGrid[x, y] = line[x];\r
-                }\r
+            public void set_author(string atom)\r
+            {\r
+                string value = Parse.String(atom);\r
+                if (value != null) mMetadata.Author = value;\r
+                else throw new ParserException("Unexpected value on line " + mInput.LineNumber + ": " + atom);\r
             }\r
 \r
             }\r
 \r
-            if (y < mMetadata.GridHeight)\r
+            public void set_levelname(string atom)\r
             {\r
             {\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
+                string value = Parse.String(atom);\r
+                if (value != null) mMetadata.Name = value;\r
+                else throw new ParserException("Unexpected value on line " + mInput.LineNumber + ": " + atom);\r
             }\r
 \r
             }\r
 \r
-            return null;\r
-        }\r
-\r
-        string ReadEntitySection(char entity)\r
-        {\r
-            Dictionary<string, string> pairs = new Dictionary<string, string>();\r
-            mEntities[entity] = pairs;\r
-\r
-            while (!mInput.End)\r
+            public void set_type(string atom)\r
             {\r
             {\r
-                string line = mInput.ReadLine();\r
+                Map.Type value = Parse.Constant<Map.Type>(atom);\r
+                if (value != default(Map.Type)) mMetadata.Type = value;\r
+                else throw new ParserException("Unexpected type on line " + mInput.LineNumber + ": " + atom);\r
+            }\r
 \r
 \r
-                string[] pair = Parse.KeyValuePair(line);\r
-                if (pair != null)\r
+            public void set_dimensions(string atom)\r
+            {\r
+                Point? dimensions = Parse.Coordinates(atom);\r
+                if (dimensions != null)\r
                 {\r
                 {\r
-                    pairs[pair[0]] = pair[1];\r
+                    mMetadata.GridWidth = dimensions.Value.X;\r
+                    mMetadata.GridHeight = dimensions.Value.Y;\r
+                    if (mMetadata.GridWidth <= 0 || mMetadata.GridHeight <= 0)\r
+                    {\r
+                        throw new ParserException("Invalid dimensions on line " + mInput.LineNumber + ": " + atom);\r
+                    }\r
                 }\r
                 else\r
                 {\r
                 }\r
                 else\r
                 {\r
-                    return line;\r
+                    throw new ParserException("Unexpected value on line " + mInput.LineNumber + ": " + atom);\r
                 }\r
             }\r
 \r
                 }\r
             }\r
 \r
-            return null;\r
-        }\r
-\r
-\r
-        bool IsLineSignificant(string line)\r
-        {\r
-            if (line.Trim().Length == 0 || Parse.IniComment(line) != null) return false;\r
-            return true;\r
-        }\r
-\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
-            ContentReader mInput;\r
-            int mLineNumber = 0;\r
-            int mExpectedNumberOfLines;\r
+            public void set_tileset(string atom)\r
+            {\r
+                string value = Parse.String(atom);\r
+                if (value != null) mMetadata.Tileset = value;\r
+                else throw new ParserException("Unexpected tileset on line " + mInput.LineNumber + ": " + atom);\r
+            }\r
 \r
 \r
-            public LineReader(ContentReader input)\r
+            public void set_defaulttile(string atom)\r
             {\r
             {\r
-                mInput = input;\r
-                mExpectedNumberOfLines = mInput.ReadInt32();\r
+                char? value = Parse.Char(atom);\r
+                if (value != null) mDefaultTile = value.Value;\r
+                else throw new ParserException("Unexpected tile value on line " + mInput.LineNumber + ": " + atom);\r
             }\r
 \r
             }\r
 \r
-            public string ReadLine()\r
+            public void set_numplayers(string atom)\r
             {\r
             {\r
-                mLineNumber++;\r
-                return mInput.ReadString();\r
+                string[] list = Parse.List(atom);\r
+                if (list != null)\r
+                {\r
+                    foreach (string item in list)\r
+                    {\r
+                        int[] range = Parse.Range(item);\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(item);\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 + ": " + item);\r
+                    }\r
+                    if (mMetadata.NumPlayers.Count == 0)\r
+                    {\r
+                        throw new ParserException("No numbers given on line " + mInput.LineNumber + ": " + atom);\r
+                    }\r
+                }\r
+                else\r
+                {\r
+                    throw new ParserException("Unexpected value on line " + mInput.LineNumber + ": " + atom);\r
+                }\r
             }\r
 \r
             }\r
 \r
-            public int LineNumber { get { return mLineNumber; } }\r
 \r
 \r
-            public bool End { get { return mLineNumber >= mExpectedNumberOfLines; } }\r
+            Map.Metadata mMetadata;\r
+            char[,] mGrid;\r
+            List<Map.RawEntity> mEntities;\r
+            Point[] mPlayerPositions;\r
+\r
+            Dictionary<char, Dictionary<string, string>> mEntitySections = new Dictionary<char, Dictionary<string, string>>();\r
+            char mDefaultTile = ' ';\r
+\r
+            LineReader mInput;\r
         }\r
 \r
         #endregion\r
         }\r
 \r
         #endregion\r
@@ -319,11 +437,7 @@ namespace CarFire
 \r
         #region Private Variables\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
+        Impl mImpl;\r
 \r
         #endregion\r
     }\r
 \r
         #endregion\r
     }\r
index b9320778babcbcdfb62d685aec9306ce2639d6c7..2df3ca73028aadadc36013bb529593b988e38dfd 100644 (file)
@@ -9,7 +9,8 @@ namespace CarFire
 {\r
     /// <summary>\r
     /// Class with handy static methods taking strings and returning objects\r
 {\r
     /// <summary>\r
     /// Class with handy static methods taking strings and returning objects\r
-    /// parsed from those strings.\r
+    /// parsed from those strings.  For all of these functions, white space is\r
+    /// generally ignored, but any superfluous characters will make the parse fail.\r
     /// </summary>\r
     public class Parse\r
     {\r
     /// </summary>\r
     public class Parse\r
     {\r
@@ -108,6 +109,18 @@ namespace CarFire
             return null;\r
         }\r
 \r
             return null;\r
         }\r
 \r
+        /// <summary>\r
+        /// Parses a single character.\r
+        /// </summary>\r
+        /// <param name="atom">Text.</param>\r
+        /// <returns>The character, or null if parsing failed.</returns>\r
+        public static char? Char(string atom)\r
+        {\r
+            string str = String(atom);\r
+            if (str != null && str.Length == 1) return str[0];\r
+            return null;\r
+        }\r
+\r
         /// <summary>\r
         /// Parses a constant from an enum.\r
         /// </summary>\r
         /// <summary>\r
         /// Parses a constant from an enum.\r
         /// </summary>\r
index 6f8a421358d73dae6d66e61f1d102d2a4f59124e..da92bf0d7c011d7f118eac4996fbabf4fc493aa7 100644 (file)
@@ -79,7 +79,8 @@ namespace CarFire
 #if MAP_TESTING\r
             map = Content.Load<Map>("Maps/sandbox");\r
             Map.DefaultTile = Content.Load<Texture2D>("default");\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
+            map.CenterCell = new Vector2(2, 4);\r
+            List<object> entities = map.GetAllEntities();\r
 #endif\r
         }\r
 \r
 #endif\r
         }\r
 \r
index 377cbf95125457319b27a501c7a3b5d17780649e..cb56df862da4dbd6fd3bc19d977917f175f2a329 100644 (file)
@@ -108,7 +108,9 @@ objects and whatnot.  For example, if I wanted to place a monster on the
 map using the `A' character, I would create a section of the file named `A'\r
 and then place one or more `A' characters in the |leveleditor-maptable|\r
 section at the coordinates where I want the monsters to be created when the\r
 map using the `A' character, I would create a section of the file named `A'\r
 and then place one or more `A' characters in the |leveleditor-maptable|\r
 section at the coordinates where I want the monsters to be created when the\r
-map file is loaded.\r
+map file is loaded.  The static scenery at the places where entities are\r
+placed is determined by the `defaulttile' key in the |leveleditor-metadata|\r
+section.\r
 \r
 You can also use the same definitions to place identical copies of the same\r
 entity at different locations on the map.  This is what allows you to\r
 \r
 You can also use the same definitions to place identical copies of the same\r
 entity at different locations on the map.  This is what allows you to\r
@@ -117,6 +119,17 @@ although you are limited to only 52 different types of entities per level.
 By the way, these sections can appear anywhere in the file, either before\r
 or after the |leveleditor-maptable| section.\r
 \r
 By the way, these sections can appear anywhere in the file, either before\r
 or after the |leveleditor-maptable| section.\r
 \r
+There is also a way to place entities on the map without putting its\r
+identifying character in the |leveleditor-maptable| section.  You might\r
+want to do this if you want to place an entity at some cell on the map that\r
+you don't want to be the default tile.  This is accomplished by listing\r
+the coordinates where entities should be place after the `create' key.\r
+Sometime during the map loading process, entities defined with this key and\r
+one or more valid sets of coordinates will be made available to the game in\r
+the usual manner, just as if its identifier was put on the map.  The\r
+difference is, you can put any (open) static scenery you want at those\r
+locations.\r
+\r
 ===========================================================================\r
 B. Basic Syntax                                        *leveleditor-syntax*\r
 \r
 ===========================================================================\r
 B. Basic Syntax                                        *leveleditor-syntax*\r
 \r
This page took 0.051506 seconds and 4 git commands to generate.