X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fcarfire;a=blobdiff_plain;f=CarFire%2FCarFire%2FCarFire%2FMapReader.cs;h=cfb1f945219cb1bf73e315957bee1cc0fd7285f4;hp=fbae4df99f8b7988a1e2f9f7069c7062c470af24;hb=722dcd763d115992b2a56d4001db80d11e583064;hpb=1393586d1e5639ac8f1e9fc8183644050dd54165 diff --git a/CarFire/CarFire/CarFire/MapReader.cs b/CarFire/CarFire/CarFire/MapReader.cs index fbae4df..cfb1f94 100644 --- a/CarFire/CarFire/CarFire/MapReader.cs +++ b/CarFire/CarFire/CarFire/MapReader.cs @@ -5,6 +5,7 @@ using System.Runtime.Serialization; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; +using System.Reflection; namespace CarFire { @@ -42,276 +43,393 @@ namespace CarFire protected override Map Read(ContentReader input, Map existingInstance) { - mInput = new LineReader(input); - ReadSectionHeaders(); - return new Map(mMetadata, mGrid, mEntities); + mImpl = new Impl(input); + return mImpl.GetMap(); } #endregion - #region Private Methods + #region Private Types - void ReadSectionHeaders() + /// + /// This private class wraps around ContentReader to make it more + /// convenient to use it as an input stream reader. + /// + class LineReader { - mMetadata = new Map.Metadata(); + ContentReader mInput; + int mLineNumber = 0; + int mExpectedNumberOfLines; + + public LineReader(ContentReader input) + { + mInput = input; + mExpectedNumberOfLines = mInput.ReadInt32(); + } - while (!mInput.End) + public string ReadLine() { - string line = mInput.ReadLine(); + mLineNumber++; + return mInput.ReadString(); + } - while (line != null) + public int LineNumber { get { return mLineNumber; } } + + public bool End { get { return mLineNumber >= mExpectedNumberOfLines; } } + } + + + /// + /// This class is the actual implementation. The implementation is wrapped + /// in a subclass because the invoker seems to only be able to invoke public + /// methods, and this needs to invoke methods that shouldn't be public. + /// + class Impl + { + public Impl(ContentReader input) + { + mInput = new LineReader(input); + ReadSectionHeaders(); + PostProcess(); + } + + public Map GetMap() + { + return new Map(mMetadata, mGrid, mEntities); + } + + + public void ReadSectionHeaders() + { + mMetadata = new Map.Metadata(); + + while (!mInput.End) { - if (!IsLineSignificant(line)) break; + string line = mInput.ReadLine(); - string section = Parse.IniSectionHeader(line); - if (section != null) + while (line != null) { - if (section == "metadata") + if (!IsLineSignificant(line)) break; + + string section = Parse.IniSectionHeader(line); + if (section != null) { - line = ReadMetadataSection(); + if (section == "metadata") + { + line = ReadMetadataSection(); + } + else if (section == "maptable") + { + line = ReadMapTableSection(); + } + else if (section.Length == 1 && IsValidEntityIdentifier(section[0])) + { + line = ReadEntitySection(section[0]); + } + else + { + throw new ParserException("Unexpected section on line " + mInput.LineNumber + ": " + section); + } } - else if (section == "maptable") + else { - line = ReadMapTableSection(); + throw new ParserException("Unexpected text on line " + mInput.LineNumber + ": " + line); } - else if (section.Length == 1) + } + } + } + + string ReadMetadataSection() + { + while (!mInput.End) + { + string line = mInput.ReadLine(); + if (!IsLineSignificant(line)) continue; + + string[] pair = Parse.KeyValuePair(line); + if (pair != null) + { + try { - line = ReadEntitySection(section[0]); + string methodName = "set_" + pair[0].ToLowerInvariant(); + object[] args = new object[1]; + args[0] = pair[1]; + GetType().InvokeMember(methodName, BindingFlags.InvokeMethod, null, this, args); } - else +#pragma warning disable 0168 + catch (System.MissingMethodException ex) +#pragma warning restore 0168 { - throw new ParserException("Unexpected section on line " + mInput.LineNumber + ": " + section); + throw new ParserException("Unexpected key on line " + mInput.LineNumber + ": " + pair[0]); } } else { - throw new ParserException("Unexpected text on line " + mInput.LineNumber + ": " + line); + return line; } } + + return null; } - } - string ReadMetadataSection() - { - while (!mInput.End) + string ReadMapTableSection() { - string line = mInput.ReadLine(); - if (!IsLineSignificant(line)) continue; + if (mMetadata == null || mMetadata.GridWidth == 0 || mMetadata.GridHeight == 0) + { + throw new ParserException("Unexpected section on line " + mInput.LineNumber + + ": You must define the map dimensions before this section."); + } - string[] pair = Parse.KeyValuePair(line); - if (pair != null) + mGrid = new char[mMetadata.GridWidth, mMetadata.GridHeight]; + + int y; + for (y = 0; y < mMetadata.GridHeight && !mInput.End; y++) { - if (pair[0] == "type") + string line = mInput.ReadLine(); + + if (line.Length < mMetadata.GridWidth) { - Map.Type type = Parse.Constant(pair[1]); - if (type != default(Map.Type)) - { - mMetadata.Type = type; - } - else - { - throw new ParserException("Unexpected type on line " + mInput.LineNumber + ": " + pair[1]); - } + throw new ParserException("Unexpected EOL on line " + mInput.LineNumber + + ": The number of characters should match the width dimension (" + mMetadata.GridWidth + ")."); } - else if (pair[0] == "dimensions") + + for (int x = 0; x < mMetadata.GridWidth; x++) { - Point? dimensions = Parse.Coordinates(pair[1]); - if (dimensions != null) - { - mMetadata.GridWidth = dimensions.Value.X; - mMetadata.GridHeight = dimensions.Value.Y; - if (mMetadata.GridWidth <= 0 || mMetadata.GridHeight <= 0) - { - throw new ParserException("Invalid dimensions on line " + mInput.LineNumber + ": " + pair[1]); - } - } - else - { - throw new ParserException("Unexpected value on line " + mInput.LineNumber + ": " + pair[1]); - } + mGrid[x, y] = line[x]; } - else if (pair[0] == "tileset") + } + + if (y < mMetadata.GridHeight) + { + throw new ParserException("Unexpected EOF on line " + mInput.LineNumber + + ": The number of lines in this section should match the height dimension (" + mMetadata.GridHeight + ")."); + } + + return null; + } + + string ReadEntitySection(char identifier) + { + Dictionary pairs = new Dictionary(); + mEntitySections[identifier] = pairs; + + while (!mInput.End) + { + string line = mInput.ReadLine(); + + string[] pair = Parse.KeyValuePair(line); + if (pair != null) { - string tileset = Parse.String(pair[1]); - if (tileset != null) - { - mMetadata.Tileset = tileset; - } - else - { - throw new ParserException("Unexpected tileset on line " + mInput.LineNumber + ": " + pair[1]); - } + pairs[pair[0]] = pair[1]; } - else if (pair[0] == "numplayers") + else { - string[] list = Parse.List(pair[1]); - if (list != null) + return line; + } + } + + return null; + } + + + void PostProcess() + { + if (mMetadata == null || mGrid == null) + { + throw new ParserException("Missing a required section. Make sure the metadata and grid are there."); + } + + mEntities = new List(); + mPlayerPositions = new Point[mMetadata.NumPlayers.Max() + 1]; + + // create entities defined completely + foreach (char identifier in mEntitySections.Keys) + { + Dictionary pairs = mEntitySections[identifier]; + if (pairs.ContainsKey("create")) + { + string[] list = Parse.List(pairs["create"]); + foreach (string positionString in list) { - foreach (string atom in list) + Point? position = Parse.Coordinates(positionString); + if (position != null) { - int[] range = Parse.Range(atom); - if (range != null) - { - for (int i = range[0]; i <= range[1]; i++) - { - mMetadata.NumPlayers.Add(i); - } - continue; - } - int? integer = Parse.Integer(atom); - if (integer != null) - { - mMetadata.NumPlayers.Add(integer.Value); - continue; - } - - throw new ParserException("Unexpected atom on line " + mInput.LineNumber + ": " + atom); + Map.RawEntity createEntity = new Map.RawEntity(); + createEntity.Id = identifier; + createEntity.Position = position.Value; + createEntity.Attributes = pairs; + mEntities.Add(createEntity); } - if (mMetadata.NumPlayers.Count == 0) + else { - throw new ParserException("No numbers given on line " + mInput.LineNumber + ": " + pair[1]); + throw new ParserException("Unexpected value of key `create' defined for entity " + identifier + "."); } } - else - { - throw new ParserException("Unexpected value on line " + mInput.LineNumber + ": " + pair[1]); - } + pairs.Remove("create"); } - else if (pair[0] == "author") - { - string author = Parse.String(pair[1]); - if (author != null) - { - mMetadata.Author = author; - } - else - { - throw new ParserException("Unexpected value on line " + mInput.LineNumber + ": " + pair[1]); - } - } - else if (pair[0] == "levelname") + } + + // create entities with positions defined on the grid + // and get player starting positions + for (int x = 0; x < mMetadata.GridWidth; x++) + { + for (int y = 0; y < mMetadata.GridHeight; y++) { - string level = Parse.String(pair[1]); - if (level != null) + char identifier = mGrid[x, y]; + if (IsValidEntityIdentifier(identifier)) { - mMetadata.Name = level; + if (mEntitySections.ContainsKey(identifier)) + { + Map.RawEntity createEntity = new Map.RawEntity(); + createEntity.Id = identifier; + createEntity.Position = new Point(x, y); + createEntity.Attributes = mEntitySections[identifier]; + mEntities.Add(createEntity); + } + else + { + throw new ParserException("Unexpected entity (" + identifier + + ") placed on the grid at [" + x + "," + y + "] but not defined."); + } + mGrid[x, y] = mDefaultTile; } - else + else if ('1' <= identifier && identifier <= '9') { - throw new ParserException("Unexpected value on line " + mInput.LineNumber + ": " + pair[1]); + int playerNum = identifier - 48; + if (playerNum < mPlayerPositions.Count()) + { + mPlayerPositions[playerNum] = new Point(x, y); + } + mGrid[x, y] = mDefaultTile; } } - else - { - throw new ParserException("Unexpected key on line " + mInput.LineNumber + ": " + pair[0]); - } } - else + + // check if all needed player positions are defined + for (int i = 1; i < mPlayerPositions.Count(); i++) { - return line; + if (mPlayerPositions[i] == default(Point)) + { + throw new ParserException("Not enough player positions were defined on the grid; " + + "are missing a spot for player " + i + "."); + } } } - return null; - } - string ReadMapTableSection() - { - if (mMetadata == null || mMetadata.GridWidth == 0 || mMetadata.GridHeight == 0) + bool IsLineSignificant(string line) { - throw new ParserException("Unexpected section on line " + mInput.LineNumber + - ": You must define the map dimensions before this section."); + if (line.Trim().Length == 0 || Parse.IniComment(line) != null) return false; + return true; } - mGrid = new char[mMetadata.GridWidth, mMetadata.GridHeight]; - - int y; - for (y = 0; y < mMetadata.GridHeight && !mInput.End; y++) + bool IsValidEntityIdentifier(char id) { - string line = mInput.ReadLine(); + if (('a' <= id && id <= 'z') || ('A' <= id && id <= 'Z')) return true; + return false; + } - if (line.Length < mMetadata.GridWidth) - { - throw new ParserException("Unexpected EOL on line " + mInput.LineNumber + - ": The number of characters should match the width dimension (" + mMetadata.GridWidth + ")."); - } - for (int x = 0; x < mMetadata.GridWidth; x++) - { - mGrid[x, y] = line[x]; - } + public void set_author(string atom) + { + string value = Parse.String(atom); + if (value != null) mMetadata.Author = value; + else throw new ParserException("Unexpected value on line " + mInput.LineNumber + ": " + atom); } - if (y < mMetadata.GridHeight) + public void set_levelname(string atom) { - throw new ParserException("Unexpected EOF on line " + mInput.LineNumber + - ": The number of lines in this section should match the height dimension (" + mMetadata.GridHeight + ")."); + string value = Parse.String(atom); + if (value != null) mMetadata.Name = value; + else throw new ParserException("Unexpected value on line " + mInput.LineNumber + ": " + atom); } - return null; - } - - string ReadEntitySection(char entity) - { - Dictionary pairs = new Dictionary(); - mEntities[entity] = pairs; - - while (!mInput.End) + public void set_type(string atom) { - string line = mInput.ReadLine(); + Map.Type value = Parse.Constant(atom); + if (value != default(Map.Type)) mMetadata.Type = value; + else throw new ParserException("Unexpected type on line " + mInput.LineNumber + ": " + atom); + } - string[] pair = Parse.KeyValuePair(line); - if (pair != null) + public void set_dimensions(string atom) + { + Point? dimensions = Parse.Coordinates(atom); + if (dimensions != null) { - pairs[pair[0]] = pair[1]; + mMetadata.GridWidth = dimensions.Value.X; + mMetadata.GridHeight = dimensions.Value.Y; + if (mMetadata.GridWidth <= 0 || mMetadata.GridHeight <= 0) + { + throw new ParserException("Invalid dimensions on line " + mInput.LineNumber + ": " + atom); + } } else { - return line; + throw new ParserException("Unexpected value on line " + mInput.LineNumber + ": " + atom); } } - return null; - } - - - bool IsLineSignificant(string line) - { - if (line.Trim().Length == 0 || Parse.IniComment(line) != null) return false; - return true; - } - - #endregion - - - #region Private Types - - /// - /// This private class wraps around ContentReader to make it more - /// convenient to use it as an input stream reader. - /// - class LineReader - { - ContentReader mInput; - int mLineNumber = 0; - int mExpectedNumberOfLines; + public void set_tileset(string atom) + { + string value = Parse.String(atom); + if (value != null) mMetadata.Tileset = value; + else throw new ParserException("Unexpected tileset on line " + mInput.LineNumber + ": " + atom); + } - public LineReader(ContentReader input) + public void set_defaulttile(string atom) { - mInput = input; - mExpectedNumberOfLines = mInput.ReadInt32(); + char? value = Parse.Char(atom); + if (value != null) mDefaultTile = value.Value; + else throw new ParserException("Unexpected tile value on line " + mInput.LineNumber + ": " + atom); } - public string ReadLine() + public void set_numplayers(string atom) { - mLineNumber++; - return mInput.ReadString(); + string[] list = Parse.List(atom); + if (list != null) + { + foreach (string item in list) + { + int[] range = Parse.Range(item); + if (range != null) + { + for (int i = range[0]; i <= range[1]; i++) + { + mMetadata.NumPlayers.Add(i); + } + continue; + } + int? integer = Parse.Integer(item); + if (integer != null) + { + mMetadata.NumPlayers.Add(integer.Value); + continue; + } + + throw new ParserException("Unexpected atom on line " + mInput.LineNumber + ": " + item); + } + if (mMetadata.NumPlayers.Count == 0) + { + throw new ParserException("No numbers given on line " + mInput.LineNumber + ": " + atom); + } + } + else + { + throw new ParserException("Unexpected value on line " + mInput.LineNumber + ": " + atom); + } } - public int LineNumber { get { return mLineNumber; } } - public bool End { get { return mLineNumber >= mExpectedNumberOfLines; } } + Map.Metadata mMetadata; + char[,] mGrid; + List mEntities; + Point[] mPlayerPositions; + + Dictionary> mEntitySections = new Dictionary>(); + char mDefaultTile = ' '; + + LineReader mInput; } #endregion @@ -319,11 +437,7 @@ namespace CarFire #region Private Variables - Map.Metadata mMetadata; - char[,] mGrid; - Dictionary> mEntities = new Dictionary>(); - - LineReader mInput; + Impl mImpl; #endregion }