public GiveResetData(ResetData data) : base()
 {
     this.Arg1    = data.Arg1;
     this.Arg2    = data.Arg2;
     this.Arg3    = data.Arg3;
     this.Arg4    = data.Arg4;
     this.Command = data.Command;
     this.Inner   = data.Inner;
 }
 public MobileResetData(ResetData data)
 {
     this.Arg1    = data.Arg1;
     this.Arg2    = data.Arg2;
     this.Arg3    = data.Arg3;
     this.Arg4    = data.Arg4;
     this.Command = data.Command;
     this.Inner   = data.Inner;
 }
        internal static ResetData ParseResetData(string areaFile, ref int lineNum, string firstLine, ResetData lastMob)
        {
            //if (log)
            //Logging.Log.Debug(String.Format("ParseResetData() called for area {0} starting on line {1}", areaFile, lineNum));

            // Instantiate variables for the method
            ResetData outReset = new ResetData();
            string    lineData = firstLine;

            // Regex to parse reset lines
            Regex lineRegex = new Regex(@"^(\w){1}\s+(\d+)\s+(\d+)\s+(-*\d+)\s*(\d+)*\s*(\d+)*(\s*\*(.*))*\s*$");

            // Attempt to parse the line
            Match match = lineRegex.Match(lineData);

            // Ensure we have at least one match so we can check the command
            if (match.Success)
            {
                string value = match.Groups[1].ToString();
                outReset.Arg1 = Convert.ToInt32(match.Groups[3].ToString());
                outReset.Arg2 = Convert.ToInt32(match.Groups[4].ToString());
                outReset.Arg3 = (value.Equals("G") || value.Equals("R")) ? 0 : Convert.ToInt32(match.Groups[5].ToString());
                outReset.Arg4 = (value.Equals("P") || value.Equals("M")) ? 0 : (match.Groups[6].Success) ? Convert.ToInt32(match.Groups[6].ToString()) : 0;

                switch (value)
                {
                case "M":
                    // Mob reset
                    outReset.Command = ResetCommand.SpawnMobile;

                    // Validate the mob and room VNUMs
                    if (!Program.World.Mobs.Exists(m => m.VNUM == outReset.Arg1))
                    {
                        Logging.Log.Error(String.Format("Encountered unknown mobile VNUM {0} in reset data on line {1} of area {2}", outReset.Arg1, lineNum, areaFile));
                        return(null);
                    }
                    else if (!Program.World.Rooms.Exists(r => r.VNUM == outReset.Arg3))
                    {
                        Logging.Log.Error(String.Format("Encountered unknown room VNUM {0} in reset data on line {1} of area {2}", outReset.Arg3, lineNum, areaFile));
                        return(null);
                    }
                    else
                    {
                        Logging.Log.Debug(String.Format("Loaded mobile reset for mobile {0} in room {1} with maximum occupancy {2} for area {3}", outReset.Arg1, outReset.Arg3, outReset.Arg2, areaFile));

                        // Return the reset
                        return(outReset);
                    }

                case "E":
                    // Equip reset
                    outReset.Command = ResetCommand.EquipObjectOnMob;

                    // Ensure we have a mob
                    if (lastMob == null || lastMob.Command != ResetCommand.SpawnMobile)
                    {
                        Logging.Log.Error(String.Format("Encountered equip reset command in an invalid location, no previous mob to apply to - line {0} of area {1}", lineNum, areaFile));
                        return(null);
                    }

                    // Validate the object VNUM
                    if (!Program.World.Objects.Exists(o => o.VNUM == outReset.Arg1))
                    {
                        Logging.Log.Error(String.Format("Encountered unknown object VNUM {0} in equip reset data on line {1} of area {2}", outReset.Arg1, lineNum, areaFile));
                        return(null);
                    }
                    // Validate the wear location
                    else if (!Enum.TryParse <Enums.WearFlag>(outReset.Arg3.ToString(), out _))
                    {
                        Logging.Log.Error(String.Format("Encountered invalid wear location {0} in equip reset data on line {1} of area {2}", outReset.Arg3, lineNum, areaFile));
                        return(null);
                    }

                    // Reset Arg2 based on the old limit rules
                    if (outReset.Arg2 > 50)
                    {
                        outReset.Arg2 = 6;
                    }
                    else if (outReset.Arg2 == -1)
                    {
                        outReset.Arg2 = 999;
                    }

                    Logging.Log.Debug(String.Format("Loaded equip reset for mobile {0} with item {1} in wear location {2} with limit of {3} on line {4} of area {5}", outReset.Arg4, outReset.Arg1, outReset.Arg3, outReset.Arg2, lineNum, areaFile));

                    return(outReset);

                case "G":
                    // Give reset
                    outReset.Command = ResetCommand.GiveObjectToMob;

                    // Ensure we have a mob
                    if (lastMob == null || lastMob.Command != ResetCommand.SpawnMobile)
                    {
                        Logging.Log.Error(String.Format("Encountered give reset command in an invalid location, no previous mob to apply to - line {0} of area {1}", lineNum, areaFile));
                        return(null);
                    }

                    // Validate the object VNUM
                    if (!Program.World.Objects.Exists(o => o.VNUM == outReset.Arg1))
                    {
                        Logging.Log.Error(String.Format("Encountered unknown object VNUM {0} in equip reset data on line {1} of area {2}", outReset.Arg1, lineNum, areaFile));
                        return(null);
                    }

                    // Reset Arg2 based on the old limit rules
                    if (outReset.Arg2 > 50)
                    {
                        outReset.Arg2 = 6;
                    }
                    else if (outReset.Arg2 == -1)
                    {
                        outReset.Arg2 = 999;
                    }

                    Logging.Log.Debug(String.Format("Loaded give reset for mobile {0} with item {1} with limit of {2} on line {3} of area {4}", outReset.Arg4, outReset.Arg1, outReset.Arg2, lineNum, areaFile));

                    return(outReset);

                case "O":
                    // Object reset
                    return(null);

                case "P":
                    // Put reset
                    return(null);

                case "R":
                    // Randomize reset
                    return(null);

                case "D":
                    // Door reset
                    return(null);

                default:
                    Logging.Log.Warn(String.Format("Encountered unknown reset type {0} on line {1} in file {2}", value, lineNum, areaFile));
                    return(null);
                }
            }
            else
            {
                Logging.Log.Warn(String.Format("Encountered invalid reset data ({0}) on line {1} of file {2}", lineData, lineNum, areaFile));
                return(null);
            }
        }
        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);
            //}
        }