コード例 #1
0
            public void StatRoom(CharacterData ch, int vnum)
            {
                // Attempt to find the room
                Models.RoomIndexData targetRoom = Program.World.Rooms.SingleOrDefault(r => r.VNUM.Equals(vnum));

                // Did we find it?
                if (targetRoom == null)
                {
                    // Inform the user
                    Network.Send("No such location\n\r\n\r", ch.Descriptor);
                }
                else
                {
                    string output = String.Empty;
                    output += String.Format("Name: '{0}'\n\rArea: '{1}'\n\r", targetRoom.Name, Program.World.Areas.Single(a => a.MinVNum <= targetRoom.VNUM && a.MaxVNum >= targetRoom.VNUM).Name);
                    output += String.Format("VNUM: {0}  Sector: {1}  Light: {2}  Healing: {3}  Mana: {4}\n\r",
                                            targetRoom.VNUM,
                                            targetRoom.SectorType.ToString(),
                                            targetRoom.LightLevel,
                                            targetRoom.HealRate.ToString(),
                                            targetRoom.ManaRate.ToString());
                    output += String.Format("Room Flags: {0}\n\r", ((Enums.AlphaMacros)targetRoom.Attributes).ToString().Replace(",", "").Replace(" ", ""));
                    output += String.Format("Description:\n\r{0}\n\r", targetRoom.Description);
                    output += "Extra description keywords: " + String.Join(",", targetRoom.ExtraDescriptions.Select(ed => ed.Keywords)) + "\n\r";

                    // TODO: Should check that the character can be seen by the person invoking this command
                    output += "Characters: " + String.Join(",", targetRoom.Characters.Select(person => person.Name)) + "\n\r";
                    output += "Objects: " + String.Join(",", targetRoom.Objects.Select(obj => obj.Name)) + "\n\r";

                    // Loop over possible exits
                    for (int i = 0; i <= 5; i++)
                    {
                        // Check that the exit is defined
                        if (targetRoom.Exits[i] != null)
                        {
                            // Add to output
                            output += String.Format("Door: {0}.  To: {1}.  Key: {2}.  Exit flags: {3}.\n\rKeyword: '{4}'.  Description: {5}\n\r",
                                                    i,
                                                    targetRoom.Exits[i].ToVNUM,
                                                    targetRoom.Exits[i].KeyVNUM,
                                                    ((Enums.AlphaMacros)targetRoom.Exits[i].Attributes).ToString(),
                                                    targetRoom.Exits[i].Keywords,
                                                    targetRoom.Exits[i].Description);
                        }
                    }

                    output += "\n\r";

                    // Send output to the user
                    Network.Send(output, ch.Descriptor);
                }
            }
コード例 #2
0
        /// <summary>
        /// Parses room flags and sector type definition for a room
        /// </summary>
        /// <returns><c>true</c>, if room flags and sector type were parsed, <c>false</c> otherwise.</returns>
        /// <param name="outRoom">Room to set the flags in</param>
        /// <param name="lineData">Line data to parse</param>
        /// <param name="lineNum">Current line number, used for error messages</param>
        /// <param name="areaFile">Filename of the area being parsed, used for error messages.</param>
        private static bool ParseRoomFlagsAndSectorType(RoomIndexData outRoom, string lineData, int lineNum, string areaFile)
        {
            // Split the line on space
            string[] splitLine = lineData.Split(' ');

            // Should be three parts; throw an error if not
            if (splitLine.Length != 3)
            {
                // Log an error and return null
                Logging.Log.Error(String.Format("Error parsing room flags & sector type of room {0} in area {1} on line {2}: expected three parts, encountered {3} in data {4}", outRoom.VNUM, areaFile, lineNum, splitLine.Length, lineData));
                return(false);
            }

            // First part is obsolete - start with part 2, room flags
            // Convert the ROM flags to RoomAttributes
            outRoom.Attributes = AlphaConversions.ConvertROMAlphaToRoomAttributes(splitLine[1]);

            int sectType = 0;

            if (!Int32.TryParse(splitLine[2], out sectType))
            {
                // Log an error and return null
                Logging.Log.Error(String.Format("Error parsing sector type of room {0} in area {1} on line {2}: could not convert value \"{3}\" to integer", outRoom.VNUM, areaFile, lineNum, splitLine[2]));
                return(false);
            }

            // Validate the value
            if (!Enum.IsDefined(typeof(SectorType), sectType))
            {
                // Log an error and return null
                Logging.Log.Error(String.Format("Error parsing sector type of room {0} in area {1} on line {2}: invalid sector type {3}", outRoom.VNUM, areaFile, lineNum, sectType));
                return(false);
            }

            // Store the sector type
            outRoom.SectorType = (SectorType)sectType;

            // All done
            return(true);
        }
コード例 #3
0
        private static Exit ParseExit(StringReader sr, RoomIndexData outRoom, string lineData, ref int lineNum, string areaFile)
        {
            // Instantiate a new ExitAttributes object to hold the exit data
            Exit exit = new Exit();

            // The second character should be an integer
            int exitDirection = -1;

            if (!Int32.TryParse(lineData[1].ToString(), out exitDirection))
            {
                Logging.Log.Error(String.Format("Error parsing exits of room {0} in area {1}: expected a numeric exit number on line {2} character 2 but instead found {3}", outRoom.VNUM, areaFile, lineNum, lineData[1]));
                return(null);
            }
            else
            {
                // Test that the exitNum is valid
                if (exitDirection > Enum.GetValues(typeof(Enums.Direction)).Length - 1)
                {
                    Logging.Log.Error(String.Format("Error parsing exits of room {0} in area {1}: invalid exit direction {2} found on line {3}", outRoom.VNUM, areaFile, exitDirection, lineNum));
                    return(null);
                }
                else
                {
                    // Valid exit direction, store it
                    exit.Direction = (Enums.Direction)exitDirection;
                }
            }

            // Begin reading the exit description
            bool readingExitDescription = true;

            while (readingExitDescription)
            {
                // Read the next line
                lineData = sr.ReadLine();
                lineNum++;

                // Is the line a sole tilde? If so, we're done.
                if (lineData.Equals("~"))
                {
                    readingExitDescription = false;
                }
                else
                {
                    // Append to the exit's description - trim any trailing tildes (stock Quifael does this)
                    exit.Description += lineData.TrimEnd('~');

                    // If the line ends with ~, we're done reading
                    if (lineData.EndsWith("~", StringComparison.CurrentCulture))
                    {
                        readingExitDescription = false;
                    }
                }
            }

            // Begin reading the exit keywords
            bool readingKeywords = true;

            while (readingKeywords)
            {
                // Pull the next line and store as the keywords
                lineData = sr.ReadLine();
                lineNum++;

                if (lineData.Equals("~"))
                {
                    readingKeywords = false;
                }
                else
                {
                    // Append to the exit's keywords - trim any trailing tildes
                    exit.Keywords += lineData.TrimEnd('~');

                    // If the line end with ~, we're done reading
                    if (lineData.EndsWith("~", StringComparison.CurrentCulture))
                    {
                        readingKeywords = false;
                    }
                }
            }


            // Pull the final line of the exit definition, [lock #] [key #] [toRoom #]
            lineData = sr.ReadLine();
            lineNum++;

            // Split the line on spaces
            string[] splitLine = lineData.Split(' ');

            // Expecting three segments
            if (splitLine.Length != 3)
            {
                Logging.Log.Error(String.Format("Error parsing exits of room {0} in area {1}: invalid lock-key-room line, expected 3 segments but got {2} - value {3} on line {4}", outRoom.VNUM, areaFile, splitLine.Length, lineData, lineNum));
                return(null);
            }

            // First segment - value should be 0-2 according to the
            // ROM2.4b6 area.txt documentation, but the code accepts
            // values from 0-4, so we'll use those. Interestingly,
            // these map to the EX_* macros in merc.h, but at least for
            // rooms, there are some values (e.g. EX_HARD and
            // EX_INFURIATING) that go unused; perhaps object suse them?
            int door = 0;

            if (!Int32.TryParse(splitLine[0], out door))
            {
                Logging.Log.Error(String.Format("Error parsing exits of room {0} in area {1}: door type is invalid, expected an integer but got {2} on line {3}", outRoom.VNUM, areaFile, splitLine[0], lineNum));
                return(null);
            }

            if (door < 0 || door > 4)
            {
                Logging.Log.Error(String.Format("Error parsing exits of room {0} in area {1}: door type is invalid, expected value from 0-4, got {2} on line {3}", outRoom.VNUM, areaFile, splitLine[0], lineNum));
                return(null);
            }

            switch (door)
            {
            case 0:
                // Not a door, do nothing
                break;

            case 1:
                // Is door.
                exit.Attributes = ExitAttributes.Door;
                break;

            case 2:
                // Pickproof door
                exit.Attributes = ExitAttributes.Door | ExitAttributes.PickProof;
                break;

            case 3:
                // Gandalf door
                exit.Attributes = ExitAttributes.Door | ExitAttributes.NoPass;
                break;

            case 4:
                // A door that's sort of not a door
                exit.Attributes = ExitAttributes.Door | ExitAttributes.NoPass | ExitAttributes.PickProof;
                break;
            }

            // Second segment - value should be positive integer, the VNUM of the key object
            int key = 0;

            if (!Int32.TryParse(splitLine[1], out key))
            {
                Logging.Log.Error(String.Format("Error parsing exits of room {0} in area {1}: key VNUM is invalid, expected integer value but got {2} on line {3}", outRoom.VNUM, areaFile, splitLine[1], lineNum));
                return(null);
            }

            // Key VNUM is -1 and up
            if (key < -1)
            {
                Logging.Log.Info(String.Format("Error parsing exits of room {0} in area {1}: key VNUM is invalid, must be greater than zero - got value {2} on line {3}", outRoom.VNUM, areaFile, splitLine[1], lineNum));
                return(null);
            }

            // Set the exit's key's VNUM
            exit.KeyVNUM = key;

            // Third argument - value should be positive integer, the VNUM of the room the door leads to
            int exitRoomVNUM = 0;

            if (!Int32.TryParse(splitLine[2], out exitRoomVNUM))
            {
                Logging.Log.Error(String.Format("Error parsing exits of room {0} in area {1}: exit room VNUM is invalid, expected integer value but got {2} on line {3}", outRoom.VNUM, areaFile, splitLine[2], lineNum));
                return(null);
            }

            // Exit room VNUMs are zero or greater
            if (exitRoomVNUM < -1)
            {
                Logging.Log.Error(String.Format("Error parsing exits of room {0} in area {1}: exit room VNUM is invalid, must be greater than zero - got value {2} on line {3}", outRoom.VNUM, areaFile, exitRoomVNUM, lineNum));
                return(null);
            }

            // Set the exit roomTo
            exit.ToVNUM = exitRoomVNUM;

            // Return the exit data
            return(exit);
        }
コード例 #4
0
        internal static RoomIndexData ParseRoomData(ref StringReader sr, string areaFile, ref int lineNum, string firstLine)
        {
            Logging.Log.Debug(String.Format("ParseRoomData() called for area {0} starting on line {1}", areaFile, lineNum));

            // Instantiate variables for the method
            RoomIndexData outRoom  = new RoomIndexData();
            string        lineData = firstLine;

            bool readingRooms = true;

            //// Read line
            //lineData = sr.ReadLine();
            //lineNum++;

            // First, pull the vnum; then set it, if it's valid
            int vnum = Data.ParseVNUM(lineData);

            if (!vnum.Equals(0))
            {
                outRoom.VNUM = vnum;

                // Rooms with VNUMs 3000-3399 are hard-coded to be law...ed? WTF is the right term for this?
                if (3000 <= outRoom.VNUM && outRoom.VNUM < 3400)
                {
                    outRoom.Attributes |= RoomAttributes.Law;
                }
            }
            else
            {
                return(null);
            }

            Logging.Log.Debug(String.Format("Found room definition for vnum {0} beginning on line {1}", outRoom.VNUM, lineNum));

            // Read the room name - one line, terminated with a tilde at the end
            outRoom.Name = sr.ReadLine().TrimEnd('~');
            lineNum++;

            // Next, read the description
            outRoom.Description = Data.ReadLongText(sr, ref lineNum, areaFile, outRoom.VNUM);

            // Then, read the next line and parse as room flags and sector type
            lineData = sr.ReadLine();
            lineNum++;

            ParseRoomFlagsAndSectorType(outRoom, lineData, lineNum, areaFile);

            // Finally, read remaining possible line definitions
            while (readingRooms)
            {
                // Read the next line
                lineData = sr.ReadLine();
                lineNum++;

                //Logging.Log.Debug(String.Format("Reading line {0}, begins with {1}", lineNum, lineData[0]));

                // Flow control based on the first letter of the line
                if (lineData.Length > 0)    // Temporary - once we handle everything, this shouldn't happen any more.
                {
                    switch (lineData[0])
                    {
                    case 'C':
                        // TODO: Implement setting rooms as belonging to a clan
                        break;

                    case 'D':
                        // Room exit
                        Exit roomExit = ParseExit(sr, outRoom, lineData, ref lineNum, areaFile);

                        // If the exit is not null, set it
                        if (roomExit != null)
                        {
                            outRoom.Exits[(int)roomExit.Direction] = roomExit;
                        }
                        break;

                    case 'E':
                        // Room extra descriptions
                        ExtraDescription desc = new ExtraDescription();

                        // Read the next line
                        lineData = sr.ReadLine();
                        lineNum++;

                        // Store the value as the description's keyowrds
                        desc.Keywords = lineData.TrimEnd('~');

                        bool          readingDescription = true;
                        StringBuilder sb = new StringBuilder();

                        while (readingDescription)
                        {
                            // Read the line
                            lineData = sr.ReadLine().Trim();
                            lineNum++;

                            // Check that the line either is only a tilde, or ends with a tilde
                            if (lineData.Trim().Equals("~") || (lineData.Length > 0 && lineData[lineData.Length - 1] == '~'))
                            {
                                readingDescription = false;
                            }
                            else
                            {
                                sb.AppendLine(lineData);
                            }
                        }

                        // Store the description
                        desc.Description = sb.ToString();

                        // Append the ExtraDescription to the room
                        outRoom.ExtraDescriptions.Add(desc);
                        break;

                    case 'H':
                    case 'M':
                        int heal, mana;

                        // Parse the line
                        ParseHealManaRateDef(lineData, outRoom.VNUM, ref lineNum, areaFile, out heal, out mana);

                        // Set the values
                        outRoom.HealRate = heal;
                        outRoom.ManaRate = mana;

                        break;

                    case 'O':
                        // TODO: Implement room ownership
                        break;

                    case 'S':
                        // We're finished reading the area
                        readingRooms = false;
                        Logging.Log.Debug(String.Format("Finished reading room {0} of area {1} on line {2}", outRoom.VNUM, areaFile, lineNum));
                        break;

                    default:
                        Logging.Log.Warn(String.Format("Encounted unexpected identifier {0} in room {1} of area {2} on line {3}", lineData[0], outRoom.VNUM, areaFile, lineNum));
                        break;
                    }
                }
            }

            return(outRoom);
        }
コード例 #5
0
        public static AreaData LoadFromFile(string areaPath)
        {
            // Check that the file exists
            //try
            //{
            int              lineNum = 0;
            string           lineData;
            string           areaFile      = Path.GetFileName(areaPath);
            AreaReadingState state         = AreaReadingState.WaitingForSection;
            AreaData         areaOut       = new AreaData();
            int              errors        = 0;
            int              loaded        = 0;
            bool             backFromError = false;

            if (!File.Exists(areaPath))
            {
                // Log an error and return null
                Logging.Log.Error(String.Format("Unable to load requested area file {0}", areaPath));
                return(null);
            }

            // Instantiate a StreamReader and read the entire file
            StreamReader sr       = new StreamReader(areaPath);
            string       fileData = sr.ReadToEnd();

            sr.Dispose();

            // Instantiate a StreamReader for the file
            StringReader strRdr = new StringReader(fileData);

            // Parse the file, one line at a time.
            while ((lineData = strRdr.ReadLine()) != null)
            {
                // Increment lineNum
                lineNum++;

                switch (state)
                {
                case AreaReadingState.WaitingForSection:
                    // Skip blank lines while in this state
                    if (lineData.Trim().Equals(String.Empty))
                    {
                        Logging.Log.Debug(String.Format("Skipping empty line at {0}:{1}", areaFile, lineNum));
                        continue;
                    }

                    // Expect the first data line we come across to start with #
                    if (!lineData.StartsWith("#", StringComparison.InvariantCulture))
                    {
                        // Log an error and return null
                        Logging.Log.Error(String.Format("Error parsing {0} line {1}: Expected a section identifier, instead got {2}", areaFile, lineNum, lineData));
                        return(null);
                    }

                    // We've encountered a section heading; which one?
                    switch (lineData.Trim().ToUpper())
                    {
                        #region #$
                    // End-of-File Heading
                    case "#$":
                        // Log
                        Logging.Log.Debug(String.Format("Found #$ EOF marker in file {0} on line {1}, finishing up", areaFile, lineNum));

                        // Set state to finished
                        state = AreaReadingState.Finished;

                        break;
                        #endregion

                        #region #OBJECTS
                    case "#OBJECTS":
                        Logging.Log.Debug(String.Format("Found #OBJECTS heading in file {0} on line {1}", areaFile, lineNum));

                        // Continue reading until we hit a #0
                        bool readingObjects = true;
                        backFromError = false;
                        errors        = 0;
                        loaded        = 0;

                        while (readingObjects)
                        {
                            // Read a line
                            lineData = strRdr.ReadLine();
                            lineNum++;

                            // If we've recently come back from failing to load a mob, we need to ignore some lines
                            // until we get to the start of the next mob definition
                            if (backFromError)
                            {
                                // If the line is not the section terminator but it does begin with #, it is
                                // (should be) a new mob definition, so un-set the backFromError flag
                                if (!lineData.Trim().Equals("#0") && lineData.Trim().Length > 0 && lineData.Trim()[0].Equals('#'))
                                {
                                    // Un-set the backFromError flag; it's time to resume loading
                                    backFromError = false;

                                    Logging.Log.Debug(String.Format("Resuming loading of #OBJECTS section in area {0} on line {1} with mob {2}", areaFile, lineNum, lineData.Trim()));
                                }
                                // Otherwise, just move on to the next iteration of the loop
                                else
                                {
                                    continue;
                                }
                            }

                            if (lineData == null)
                            {
                                readingObjects = false;
                            }
                            else if (lineData.Trim().Equals("#0"))
                            {
                                readingObjects = false;
                            }
                            else if (!lineData.Trim().Equals("#0") && !lineData.Trim().Equals("#$") && !lineData.Trim().Equals(""))
                            {
                                try
                                {
                                    ObjectPrototypeData newObj = ObjectPrototypeData.ParseObjectData(ref strRdr, areaFile, ref lineNum, lineData);

                                    // If we have a loaded room, add it to the world
                                    if (newObj != null)
                                    {
                                        Program.World.Objects.Add(newObj);
                                        loaded++;
                                    }
                                    else
                                    {
                                        // Record a failed mob load, and set the indicator that we're back because of an error and should keep reading
                                        // but do nothing until we find a new mob
                                        errors++;
                                        backFromError = true;
                                    }
                                }
                                catch (ObjectParsingException ex)
                                {
                                    Logging.Log.Error(String.Format("Error parsing object: {0}", ex.Message));
                                }
                            }
                        }

                        Logging.Log.Debug(String.Format("Finished reading #OBJECTS section of file {0} on line {1} - loaded {2} objects, failed loading {3} mobs", areaFile, lineNum, loaded, errors));

                        break;
                        #endregion

                        #region #RESETS
                    case "#RESETS":
                        Logging.Log.Debug(String.Format("Found #RESETS heading in file {0} on line {1}", areaFile, lineNum));

                        // Continue reading until we hit a #0
                        bool readingResets = true;
                        backFromError = false;
                        errors        = 0;
                        loaded        = 0;
                        ResetData lastMob = null;

                        while (readingResets)
                        {
                            // Read a line
                            lineData = strRdr.ReadLine();
                            lineNum++;

                            if (lineData == null)
                            {
                                readingResets = false;
                            }
                            else if (lineData.Trim().Equals("S"))
                            {
                                readingResets = false;
                            }
                            else if (!lineData.Trim().Equals("S") && !lineData.Trim().Equals("#$") && !lineData.Trim().Equals(""))
                            {
                                try
                                {
                                    // Parse the reset
                                    ResetData newReset = ResetData.ParseResetData(areaFile, ref lineNum, lineData, lastMob);

                                    // Check that the reset parsed
                                    if (newReset != null)
                                    {
                                        // Special handling if the new reset is an E(quip) or G(ive)
                                        if (newReset.Command == ResetCommand.EquipObjectOnMob || newReset.Command == ResetCommand.GiveObjectToMob)
                                        {
                                            // Nest in the mob reset it relates to
                                            lastMob.Inner.Add(newReset);
                                        }
                                        else
                                        {
                                            // Otherwise, add the reset to the area
                                            areaOut.Resets.Add(newReset);
                                        }

                                        // If the reset was to spawn a M(ob), reset lastMob
                                        if (newReset.Command == ResetCommand.SpawnMobile)
                                        {
                                            lastMob = newReset;
                                        }

                                        // Finally, increment the loaded count
                                        loaded++;
                                    }
                                    else
                                    {
                                        // Record a failed mob load, and set the indicator that we're back because of an error and should keep reading
                                        // but do nothing until we find a new mob
                                        errors++;
                                        backFromError = true;
                                    }
                                }
                                catch (ObjectParsingException ex)
                                {
                                    Logging.Log.Error(String.Format("Error parsing object: {0}", ex.Message));
                                }
                            }
                        }

                        break;
                        #endregion

                        #region #SHOPS
                    case "#SHOPS":
                        Logging.Log.Debug(String.Format("Found #SHOPS heading in file {0} on line {1}", areaFile, lineNum));

                        // Continue reading until we hit a #0
                        bool readingShops = true;
                        backFromError = false;
                        errors        = 0;
                        loaded        = 0;

                        while (readingShops)
                        {
                            // Read a line
                            lineData = strRdr.ReadLine();
                            lineNum++;

                            // If we've recently come back from failing to load a mob, we need to ignore some lines
                            // until we get to the start of the next mob definition
                            if (backFromError)
                            {
                                // If the line is not the section terminator but it does begin with #, it is
                                // (should be) a new mob definition, so un-set the backFromError flag
                                if (!lineData.Trim().Equals("#0") && lineData.Trim().Length > 0 && lineData.Trim()[0].Equals('#'))
                                {
                                    // Un-set the backFromError flag; it's time to resume loading
                                    backFromError = false;

                                    Logging.Log.Debug(String.Format("Resuming loading of #SHOPS section in area {0} on line {1} with mob {2}", areaFile, lineNum, lineData.Trim()));
                                }
                                // Otherwise, just move on to the next iteration of the loop
                                else
                                {
                                    continue;
                                }
                            }

                            if (lineData == null)
                            {
                                readingShops = false;
                            }
                            else if (lineData.Trim().Equals("#0"))
                            {
                                readingShops = false;
                            }
                            else if (!lineData.Trim().Equals("#0") && !lineData.Trim().Equals("#$") && !lineData.Trim().Equals(""))
                            {
                                // TODO: Implement #SHOPS parsing
                            }
                        }

                        break;
                        #endregion

                        #region #SPECIALS
                    case "#SPECIALS":
                        Logging.Log.Debug(String.Format("Found #SPECIALS heading in file {0} on line {1}", areaFile, lineNum));

                        // Continue reading until we hit a #0
                        bool readingSpecials = true;
                        backFromError = false;
                        errors        = 0;
                        loaded        = 0;

                        while (readingSpecials)
                        {
                            // Read a line
                            lineData = strRdr.ReadLine();
                            lineNum++;

                            // If we've recently come back from failing to load a mob, we need to ignore some lines
                            // until we get to the start of the next mob definition
                            if (backFromError)
                            {
                                // If the line is not the section terminator but it does begin with #, it is
                                // (should be) a new mob definition, so un-set the backFromError flag
                                if (!lineData.Trim().Equals("#0") && lineData.Trim().Length > 0 && lineData.Trim()[0].Equals('#'))
                                {
                                    // Un-set the backFromError flag; it's time to resume loading
                                    backFromError = false;

                                    Logging.Log.Debug(String.Format("Resuming loading of #SPECIALS section in area {0} on line {1} with mob {2}", areaFile, lineNum, lineData.Trim()));
                                }
                                // Otherwise, just move on to the next iteration of the loop
                                else
                                {
                                    continue;
                                }
                            }

                            if (lineData == null)
                            {
                                readingSpecials = false;
                            }
                            else if (lineData.Trim().Equals("#0"))
                            {
                                readingSpecials = false;
                            }
                            else if (!lineData.Trim().Equals("#0") && !lineData.Trim().Equals("#$") && !lineData.Trim().Equals(""))
                            {
                                // TODO: Implement #SPECIALS parsing
                            }
                        }

                        break;
                        #endregion

                        #region #MOBILES
                    case "#MOBILES":
                        Logging.Log.Debug(String.Format("Found #MOBILES heading in file {0} on line {1}", areaFile, lineNum));

                        errors        = 0;
                        loaded        = 0;
                        backFromError = false;

                        // Continue reading until we hit a #0
                        bool readingMobs = true;
                        while (readingMobs)
                        {
                            // Read a line
                            lineData = strRdr.ReadLine();
                            lineNum++;

                            // If we've recently come back from failing to load a mob, we need to ignore some lines
                            // until we get to the start of the next mob definition
                            if (backFromError)
                            {
                                // If the line is not the section terminator but it does begin with #, it is
                                // (should be) a new mob definition, so un-set the backFromError flag
                                if (!lineData.Trim().Equals("#0") && lineData.Trim().Length > 0 && lineData.Trim()[0].Equals('#'))
                                {
                                    // Un-set the backFromError flag; it's time to resume loading
                                    backFromError = false;

                                    Logging.Log.Debug(String.Format("Resuming loading of #MOBILES section in area {0} on line {1} with mob {2}", areaFile, lineNum, lineData.Trim()));
                                }
                                // Otherwise, just move on to the next iteration of the loop
                                else
                                {
                                    continue;
                                }
                            }

                            if (lineData == null)
                            {
                                readingMobs = false;
                            }
                            else if (lineData.Trim().Equals("#0"))
                            {
                                readingMobs = false;
                            }
                            else if (!lineData.Trim().Equals("#0") && !lineData.Trim().Equals("#$") && !lineData.Trim().Equals(""))
                            {
                                MobPrototypeData newMob = MobPrototypeData.ParseMobData(ref strRdr, areaFile, ref lineNum, lineData);

                                // If we have a loaded room, add it to the world
                                if (newMob != null)
                                {
                                    Program.World.Mobs.Add(newMob);
                                    loaded++;
                                }
                                else
                                {
                                    // Record a failed mob load, and set the indicator that we're back because of an error and should keep reading
                                    // but do nothing until we find a new mob
                                    errors++;
                                    backFromError = true;
                                }
                            }
                        }

                        Logging.Log.Debug(String.Format("Finished reading #MOBILES section of file {0} on line {1} - loaded {2} mobs, failed loading {3} mobs", areaFile, lineNum, loaded, errors));

                        break;
                        #endregion

                        #region #ROOMS
                    case "#ROOMS":
                        Logging.Log.Debug(String.Format("Found #ROOMS heading in file {0} on line {1}", areaFile, lineNum));

                        // Continue reading until we hit a #0
                        bool readingRooms = true;
                        backFromError = false;
                        errors        = 0;
                        loaded        = 0;

                        while (readingRooms)
                        {
                            // Read a line
                            lineData = strRdr.ReadLine();
                            lineNum++;

                            // If we've recently come back from failing to load a mob, we need to ignore some lines
                            // until we get to the start of the next mob definition
                            if (backFromError)
                            {
                                // If the line is not the section terminator but it does begin with #, it is
                                // (should be) a new mob definition, so un-set the backFromError flag
                                if (!lineData.Trim().Equals("#0") && lineData.Trim().Length > 0 && lineData.Trim()[0].Equals('#'))
                                {
                                    // Un-set the backFromError flag; it's time to resume loading
                                    backFromError = false;

                                    Logging.Log.Debug(String.Format("Resuming loading of #ROOMS section in area {0} on line {1} with mob {2}", areaFile, lineNum, lineData.Trim()));
                                }
                                // Otherwise, just move on to the next iteration of the loop
                                else
                                {
                                    continue;
                                }
                            }

                            if (lineData == null)
                            {
                                readingRooms = false;
                            }
                            else if (lineData.Trim().Equals("#0"))
                            {
                                readingRooms = false;
                            }
                            else if (!lineData.Trim().Equals("#0") && !lineData.Trim().Equals("#$") && !lineData.Trim().Equals(""))
                            {
                                RoomIndexData newRoom = RoomIndexData.ParseRoomData(ref strRdr, areaFile, ref lineNum, lineData);

                                // If we have a loaded room, add it to the world
                                if (newRoom != null)
                                {
                                    loaded++;
                                    Program.World.Rooms.Add(newRoom);
                                }
                                else
                                {
                                    errors++;
                                    backFromError = true;
                                }
                            }
                        }

                        Logging.Log.Debug(String.Format("Finished reading #ROOMS section of file {0} on line {1} - loaded {2} rooms, failed loading {3} rooms", areaFile, lineNum, loaded, errors));

                        break;

                    default:
                        break;
                        #endregion

                        #region #AREA
                    // AREA Heading
                    case "#AREA":
                        Logging.Log.Debug(String.Format("Found #AREA heading in file {0} on line {1}", areaFile, lineNum));

                        #region Filename
                        // Read the next line - will be the area filename
                        lineData = strRdr.ReadLine();
                        lineNum++;

                        // Trim the trailing tilde
                        lineData = lineData.Substring(0, lineData.Trim().Length - 1);

                        // Set filename
                        areaOut.Filename = lineData;

                        Logging.Log.Debug(String.Format("Read area filename in file {0} on line {1}", areaFile, lineNum));
                        #endregion

                        #region Name
                        // Read the next line - will be the area name
                        lineData = strRdr.ReadLine();
                        lineNum++;

                        // Trim the trailing tilde
                        lineData = lineData.Substring(0, lineData.Trim().Length - 1);

                        // Set name
                        areaOut.Name = lineData;

                        Logging.Log.Debug(String.Format("Read area name in file {0} on line {1}", areaFile, lineNum));
                        #endregion

                        #region Credits
                        // Read the next line - will be the credits
                        lineData = strRdr.ReadLine();
                        lineNum++;

                        // Trim the trailing tilde
                        lineData = lineData.Substring(0, lineData.Trim().Length - 1);

                        // Set credits
                        areaOut.Credits = lineData;

                        Logging.Log.Debug(String.Format("Read credits information in file {0} on line {1}", areaFile, lineNum));
                        #endregion

                        #region MinVNUM and MaxVNUM
                        // Read the next line - will be the VNUM values
                        lineData = strRdr.ReadLine();
                        lineNum++;

                        // Split the line on space
                        string[] vnumData = lineData.Trim().Split(' ');

                        // Should be two values
                        if (!vnumData.Length.Equals(2))
                        {
                            // Log an error and return null
                            Logging.Log.Error(String.Format("Error parsing {0} line {1}: Expected two numbers separated by space for VNUM limits, instead found {2}", areaFile, lineNum, lineData));
                            return(null);
                        }

                        // Tracks which of the two numbers we're on
                        bool firstNum = true;

                        // Loop over the two values, each should convert to an integer
                        foreach (string vnum in vnumData)
                        {
                            int intVal;

                            // Try to parse the value as a 32-bit integer
                            if (!Int32.TryParse(vnum, out intVal))
                            {
                                // Log an error and return null
                                Logging.Log.Error(String.Format("Error parsing {0} line {1}: Error converting VNUM value {2} to an integer", areaFile, lineNum, vnum));
                                return(null);
                            }
                            else
                            {
                                if (firstNum)
                                {
                                    areaOut.MinVNum = intVal;
                                    firstNum        = false;
                                }
                                else
                                {
                                    areaOut.MaxVNum = intVal;
                                }
                            }
                        }

                        Logging.Log.Debug(String.Format("Finished processing #AREA section of file {0} at line {1}", areaFile, lineNum));
                        #endregion

                        break;
                        #endregion
                    }
                    break;

                default:
                    break;
                }
            }

            strRdr.Dispose();

            // Return the output area
            return(areaOut);
            //}
            //catch (Exception e)
            //{
            //    // Log the exception and rethrow
            //    Logging.Log.Fatal(String.Format("Unhandled exception caught: {0}: {1}\n{2}", e.GetType(), e.Message, e.StackTrace));
            //    throw (e);
            //}
        }