예제 #1
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);
        }
        /// <summary>
        /// Sets the Values array of <paramref name="outObj"/> based on input values from <paramref name="splitLine"/> appropriate where object type is Wand or Staff
        /// </summary>
        /// <returns><c>true</c> if values were set without errors, <c>false</c> otherwise. Errors are logged within this method.</returns>
        /// <param name="splitLine">Array of values to parse</param>
        /// <param name="outObj">Object whose Values property should be set</param>
        /// <param name="areaFile">Filename of the area file being parsed, used for error messages</param>
        /// <param name="lineNum">Line number the values came from, used for error messages</param>
        private static bool SetFurnitureValues(string[] splitLine, ObjectPrototypeData outObj, string areaFile, int lineNum)
        {
            // All segments should be parsed as ints
            SetValue_Int(splitLine[0], ref outObj.Values[0], "furniture", areaFile, lineNum, outObj.VNUM);
            SetValue_Int(splitLine[1], ref outObj.Values[1], "furniture", areaFile, lineNum, outObj.VNUM);

            // Segment 3, wear flag
            try
            {
                FurnitureFlag furnFlags = (FurnitureFlag)AlphaConversions.ConvertROMAlphaToInt32(splitLine[2]);
                outObj.Values[2] = furnFlags;
            }
            catch (ArgumentException e)
            {
                // Invalid extra flags
                throw new ObjectParsingException(String.Format("Error parsing furniture flags for object {0} in area {1}: invalid furniture flag value \"{2}\" found on line {3}", outObj.VNUM, areaFile, splitLine[2], lineNum), e);
            }

            SetValue_Int(splitLine[3], ref outObj.Values[3], "furniture", areaFile, lineNum, outObj.VNUM);
            SetValue_Int(splitLine[4], ref outObj.Values[4], "furniture", areaFile, lineNum, outObj.VNUM);

            return(true);
        }
        /// <summary>
        /// Sets the Values array of <paramref name="outObj"/> based on input values from <paramref name="splitLine"/> appropriate where object type is Weapon
        /// </summary>
        /// <returns><c>true</c> if values were set without errors, <c>false</c> otherwise. Errors are logged within this method.</returns>
        /// <param name="splitLine">Array of values to parse</param>
        /// <param name="outObj">Object whose Values property should be set</param>
        /// <param name="areaFile">Filename of the area file being parsed, used for error messages</param>
        /// <param name="lineNum">Line number the values came from, used for error messages</param>
        private static bool SetWeaponValues(string[] splitLine, ObjectPrototypeData outObj, string areaFile, int lineNum)
        {
            // Segment 1 - Weapon Type, should be a string matching the Name of a WeaponClass in the WeaponTable
            WeaponClass weapClass = Consts.WeaponClass.WeaponTable.SingleOrDefault(w => w.Name.ToLower().Equals(splitLine[0].ToLower()));

            if (weapClass == null)
            {
                // Invalid weapon class
                throw new ObjectParsingException(String.Format("Error parsing weapon class for object {0} in area {1}: unknown weapon class \"{2}\" found on line {3}", outObj.VNUM, areaFile, splitLine[0], lineNum));
            }
            else
            {
                // Store the weapon class
                outObj.Values[0] = weapClass;
            }

            // Segment 2 - Damage dice number, should be an integer
            int damDiceNum;

            if (!Int32.TryParse(splitLine[1], out damDiceNum))
            {
                // Invalid damage dice number
                throw new ObjectParsingException(String.Format("Error parsing weapon damage dice number for object {0} in area {1}: expected an integer but found \"{2}\" on line {3}", outObj.VNUM, areaFile, splitLine[1], lineNum));
            }
            else
            {
                // Store the damage dice number
                outObj.Values[1] = damDiceNum;
            }

            // Segment 3 - Damage dice type, should be an integer
            int damDiceType;

            if (!Int32.TryParse(splitLine[2], out damDiceType))
            {
                // Invalid damage dice number
                throw new ObjectParsingException(String.Format("Error parsing weapon damage dice type for object {0} in area {1}: expected an integer but found \"{2}\" on line {3}", outObj.VNUM, areaFile, splitLine[2], lineNum));
            }
            else
            {
                // Store the damage dice number
                outObj.Values[2] = damDiceType;
            }

            // Segment 4 - Attack Type, should be a string matching the Abreviation of a DamageType in the AttackTable
            DamageType damType = Consts.DamageTypes.AttackTable.SingleOrDefault(w => w.Abbreviation.ToLower().Equals(splitLine[3].ToLower()));

            if (weapClass == null)
            {
                // Invalid weapon class
                throw new ObjectParsingException(String.Format("Error parsing attack type for object {0} in area {1}: unknown attachk type \"{2}\" found on line {3}", outObj.VNUM, areaFile, splitLine[3], lineNum));
            }
            else
            {
                // Store the weapon class
                outObj.Values[3] = damType;
            }

            // Segment 5 - Weapon Flags
            try
            {
                WeaponFlag weapFlags = AlphaConversions.ConvertROMAlphaToWeaponFlag(splitLine[4]);
                outObj.Values[4] = weapFlags;
            }
            catch (ArgumentException e)
            {
                // Invalid extra flags
                throw new ObjectParsingException(String.Format("Error parsing weapon flags for object {0} in area {1}: invalid weapon flag value \"{2}\" found on line {3}", outObj.VNUM, areaFile, splitLine[4], lineNum), e);
            }

            return(true);
        }
        /// <summary>
        /// Sets the Values array of <paramref name="outObj"/> based on input values from <paramref name="splitLine"/> appropriate where object type is Container
        /// </summary>
        /// <returns><c>true</c> if values were set without errors, <c>false</c> otherwise. Errors are logged within this method.</returns>
        /// <param name="splitLine">Array of values to parse</param>
        /// <param name="outObj">Object whose Values property should be set</param>
        /// <param name="areaFile">Filename of the area file being parsed, used for error messages</param>
        /// <param name="lineNum">Line number the values came from, used for error messages</param>
        private static bool SetContainerValues(string[] splitLine, ObjectPrototypeData outObj, string areaFile, int lineNum)
        {
            // Segment 1 - Capacity, should be an integer
            int capacity = 0;

            if (!Int32.TryParse(splitLine[0], out capacity))
            {
                // Invalid damage dice number
                throw new ObjectParsingException(String.Format("Error parsing capacity for container object {0} in area {1}: expected an integer but found \"{2}\" on line {3}", outObj.VNUM, areaFile, splitLine[0], lineNum));
            }
            else
            {
                // Store the damage dice number
                outObj.Values[0] = capacity;
            }

            // Segment 2 - Flags, should be a string matching Container flags
            try
            {
                ContainerFlag conFlags = AlphaConversions.ConvertROMAlphaToContainerFlag(splitLine[1]);
                outObj.Values[1] = conFlags;
            }
            catch (ArgumentException e)
            {
                // Invalid extra flags
                throw new ObjectParsingException(String.Format("Error parsing container flags for object {0} in area {1}: invalid weapon flag value \"{2}\" found on line {3}", outObj.VNUM, areaFile, splitLine[1], lineNum), e);
            }

            // Segment 3 - Appears to be unused, but should be an integer
            int unknown = 0;

            if (!Int32.TryParse(splitLine[2], out unknown))
            {
                // Invalid damage dice number
                throw new ObjectParsingException(String.Format("Error parsing unknown value (value #3) for container object {0} in area {1}: expected an integer but found \"{2}\" on line {3}", outObj.VNUM, areaFile, splitLine[2], lineNum));
            }
            else
            {
                // Store the damage dice number
                outObj.Values[2] = unknown;
            }

            // Segment 4 - Maximum weight, should be an integer
            int maxWeight = 0;

            if (!Int32.TryParse(splitLine[3], out maxWeight))
            {
                // Invalid damage dice number
                throw new ObjectParsingException(String.Format("Error parsing maximum weight for container object {0} in area {1}: expected an integer but found \"{2}\" on line {3}", outObj.VNUM, areaFile, splitLine[3], lineNum));
            }
            else
            {
                // Store the damage dice number
                outObj.Values[3] = maxWeight;
            }

            // Segment 5 - Weight multiplier, should be an integer
            int weightMult = 0;

            if (!Int32.TryParse(splitLine[4], out weightMult))
            {
                // Invalid damage dice number
                throw new ObjectParsingException(String.Format("Error parsing weight multiplier for container object {0} in area {1}: expected an integer but found \"{2}\" on line {3}", outObj.VNUM, areaFile, splitLine[4], lineNum));
            }
            else
            {
                // Store the damage dice number
                outObj.Values[4] = weightMult;
            }

            return(true);
        }
        internal static ObjectPrototypeData ParseObjectData(ref StringReader sr, string areaFile, ref int lineNum, string firstLine, bool log = true)
        {
            if (log)
            {
                Logging.Log.Debug(String.Format("ParseObjectData() called for area {0} starting on line {1}", areaFile, lineNum));
            }

            // Instantiate variables for the method
            ObjectPrototypeData outObj = new ObjectPrototypeData();
            string lineData            = firstLine;

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

            if (!vnum.Equals(0))
            {
                outObj.VNUM = vnum;
            }
            else
            {
                return(null);
            }

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

            // Set NewFormat to true - old format objects will be parsed from another method
            outObj.NewFormat = true;

            // Set reset number to zero
            outObj.ResetNum = 0;

            // Read the name
            outObj.Name = Data.ReadLongText(sr, ref lineNum, areaFile, outObj.VNUM);

            // Read the short description
            outObj.ShortDescription = Data.ReadLongText(sr, ref lineNum, areaFile, outObj.VNUM);

            // Read the long description
            outObj.Description = Data.ReadLongText(sr, ref lineNum, areaFile, outObj.VNUM);

            // Read the material - it may be oldstyle, but the original ROM code doesn't do anything with that either
            outObj.Material = Data.ReadLongText(sr, ref lineNum, areaFile, outObj.VNUM);

            // Read the next line and split, expect 3 segments
            lineData = sr.ReadLine();
            lineNum++;
            string[] splitLine = lineData.Split(' ');

            if (splitLine.Length != 3)
            {
                throw new ObjectParsingException(String.Format("Error parsing object {0} in area {1}: invalid type/extra flag/wear flag line, expected 3 segments but got {2} - value {3} on line {4}", outObj.VNUM, areaFile, splitLine.Length, lineData, lineNum));
            }

            // Segment 1, item type - attempt to pull a match from the ItemTypeTable
            ItemType objType = Consts.ItemTypes.ItemTypeTable.SingleOrDefault(it => it.Name.ToLower().Equals(splitLine[0].ToLower()));

            if (objType == null)
            {
                // Invalid item type
                throw new ObjectParsingException(String.Format("Error parsing item type for object {0} in area {1}: unknown item type \"{2}\" found on line {3}", outObj.VNUM, areaFile, splitLine[0], lineNum));
            }
            else
            {
                outObj.ObjectType = objType.Type;
            }

            // Segment 2, extra flag
            try
            {
                ItemExtraFlag extraFlags = AlphaConversions.ConvertROMAlphaToItemExtraFlag(splitLine[1]);
                outObj.ExtraFlags = extraFlags;
            }
            catch (ArgumentException e)
            {
                // Invalid extra flags
                throw new ObjectParsingException(String.Format("Error parsing extra flags for object {0} in area {1}: invalid extra flag value \"{2}\" found on line {3}", outObj.VNUM, areaFile, splitLine[1], lineNum), e);
            }

            // Segment 3, wear flag
            try
            {
                WearFlag wearFlags = AlphaConversions.ConvertROMAlphaToWearFlag(splitLine[2]);
                outObj.WearFlags = wearFlags;
            }
            catch (ArgumentException e)
            {
                // Invalid extra flags
                throw new ObjectParsingException(String.Format("Error parsing wear flags for object {0} in area {1}: invalid extra flag value \"{2}\" found on line {3}", outObj.VNUM, areaFile, splitLine[2], lineNum), e);
            }

            // Read the next line and split, expect 5 segments
            lineData = sr.ReadLine();
            lineNum++;

            string[] splitValues = ParseValuesLine(lineData, outObj, areaFile, lineNum);

            if (splitValues.Length != 5)
            {
                throw new ObjectParsingException(String.Format("Error parsing object {0} in area {1}: invalid values line, expected 5 segments but got {2} - value {3} on line {4}", outObj.VNUM, areaFile, splitLine.Length, lineData, lineNum));
            }

            // The meaning and data type of each of the 5 values changes depending on the item type
            // TODO: Finish implementing with Regex
            switch (outObj.ObjectType)
            {
            case ItemClass.Weapon:
                // Parse and set values
                if (!SetWeaponValues(splitValues, outObj, areaFile, lineNum))
                {
                    // An error was encountered, return a null object
                    return(null);
                }

                break;

            case ItemClass.Container:
                // Parse and set values
                if (!SetContainerValues(splitValues, outObj, areaFile, lineNum))
                {
                    // An error was encountered, return a null object
                    return(null);
                }

                break;

            case ItemClass.DrinkContainer:
            case ItemClass.Fountain:
                // Parse and set values
                if (!SetFountainAndDrinkContainerValues(splitValues, outObj, areaFile, lineNum))
                {
                    // An error was encountered, return a null object
                    return(null);
                }

                break;

            case ItemClass.Wand:
            case ItemClass.Staff:
                // Parse and set values
                if (!SetWandAndStaffValues(splitValues, outObj, areaFile, lineNum))
                {
                    // An error was encountered, return a null object
                    return(null);
                }

                break;

            case ItemClass.Potion:
            case ItemClass.Pill:
            case ItemClass.Scroll:
                // Parse and set values
                if (!SetPotionPillScrollValues(splitValues, outObj, areaFile, lineNum))
                {
                    // An error was encountered, return a null object
                    return(null);
                }

                break;

            case ItemClass.Furniture:
                // Parse and set values
                if (!SetFurnitureValues(splitValues, outObj, areaFile, lineNum))
                {
                    // An error was encountered, return a null object
                    return(null);
                }

                break;

            default:
                // Parse and set values
                if (!SetOtherItemTypeValues(splitValues, outObj, areaFile, lineNum))
                {
                    // An error was encountered, return a null object
                    return(null);
                }

                break;
            }

            // Read the next line and split, expect 4 segments
            lineData = sr.ReadLine();
            lineNum++;
            splitLine = lineData.Split(' ');

            if (splitLine.Length != 4)
            {
                throw new ObjectParsingException(String.Format("Error parsing object {0} in area {1}: invalid level/weight/cost/condition line, expected 4 segments but got {2} - value {3} on line {4}", outObj.VNUM, areaFile, splitLine.Length, lineData, lineNum));
            }

            // Segment 1 - Level
            int lvl = 0;

            if (!Int32.TryParse(splitLine[0], out lvl))
            {
                throw new ObjectParsingException(String.Format("Error parsing level for object {0} in area {1}: expected an integer but found \"{2}\" on line {3}", outObj.VNUM, areaFile, splitLine[0], lineNum));
            }
            else
            {
                outObj.Level = lvl;
            }

            // Segment 2 - Weight
            int weight = 0;

            if (!Int32.TryParse(splitLine[1], out weight))
            {
                throw new ObjectParsingException(String.Format("Error parsing weight for object {0} in area {1}: expected an integer but found \"{2}\" on line {3}", outObj.VNUM, areaFile, splitLine[1], lineNum));
            }
            else
            {
                outObj.Weight = weight;
            }

            // Segment 3 - Cost
            int cost = 0;

            if (!Int32.TryParse(splitLine[2], out cost))
            {
                throw new ObjectParsingException(String.Format("Error parsing cost for object {0} in area {1}: expected an integer but found \"{2}\" on line {3}", outObj.VNUM, areaFile, splitLine[2], lineNum));
            }
            else
            {
                outObj.Cost = cost;
            }

            // Segment 4 - Condition
            switch (splitLine[3].ToLower())
            {
            case "p":
                outObj.Condition = 100;
                break;

            case "g":
                outObj.Condition = 90;
                break;

            case "a":
                outObj.Condition = 75;
                break;

            case "w":
                outObj.Condition = 50;
                break;

            case "d":
                outObj.Condition = 25;
                break;

            case "b":
                outObj.Condition = 10;
                break;

            case "r":
                outObj.Condition = 0;
                break;

            default:
                outObj.Condition = 100;
                break;
            }

            bool readingAffects = true;

            while (readingAffects)
            {
                // Peek at the start of the next line
                char nextLineStart = (char)sr.Peek();

                // If the next line does not start with a #, we have more to do
                if (!nextLineStart.Equals('#'))
                {
                    AffectData aff = new AffectData();

                    // Read the full line (more just to advance the cursor than anything; we've already read all the data)
                    lineData = sr.ReadLine();
                    lineNum++;

                    // Different behavior for different characters
                    switch (lineData.Trim().ToLower())
                    {
                    // Permanent affect
                    case "a":
                        // Read and split the next line for location and modifier
                        lineData = sr.ReadLine();
                        lineNum++;
                        splitLine = lineData.Split(' ');

                        // Should be two elements
                        if (splitLine.Length != 2)
                        {
                            throw new ObjectParsingException(String.Format("Error parsing object {0} in area {1}: invalid affect line, expected 2 segments but got {2} - value {3} on line {4}", outObj.VNUM, areaFile, splitLine.Length, lineData, lineNum));
                        }

                        // Set up properties of the affect
                        aff.Where     = ToWhere.Object;
                        aff.Type      = null;
                        aff.Level     = outObj.Level;
                        aff.Duration  = -1;
                        aff.BitVector = AffectedByFlag.None;

                        // Segment 1 - Location
                        int location = 0;
                        if (!Int32.TryParse(splitLine[0], out location))
                        {
                            throw new ObjectParsingException(String.Format("Error parsing location for object {0} affect in area {1}: expected an integer but found \"{2}\" on line {3}", outObj.VNUM, areaFile, splitLine[0], lineNum));
                        }
                        else
                        {
                            aff.Location = (ApplyType)location;
                        }

                        // Segment 2 - Modifier
                        int modifier = 0;
                        if (!Int32.TryParse(splitLine[1], out modifier))
                        {
                            throw new ObjectParsingException(String.Format("Error parsing modifier for object {0} affect in area {1}: expected an integer but found \"{2}\" on line {3}", outObj.VNUM, areaFile, splitLine[1], lineNum));
                        }
                        else
                        {
                            aff.Modifier = modifier;
                        }

                        if (log)
                        {
                            Logging.Log.Debug(String.Format("Object affect to {0} with modifier {1} added to object {2}", aff.Location.ToString(), aff.Modifier, outObj.VNUM));
                        }

                        // Add the affect to the object
                        outObj.Affected.Add(aff);

                        break;

                    case "f":
                        // Read and split the next line for location and modifier
                        lineData = sr.ReadLine();
                        lineNum++;
                        splitLine = lineData.Split(' ');

                        // Should be two elements
                        if (splitLine.Length != 4)
                        {
                            throw new ObjectParsingException(String.Format("Error parsing object {0} in area {1}: invalid affect line, expected 4 segments but got {2} - value {3} on line {4}", outObj.VNUM, areaFile, splitLine.Length, lineData, lineNum));
                        }

                        // Set up properties of the affect
                        aff.Type     = null;
                        aff.Level    = outObj.Level;
                        aff.Duration = -1;

                        // Segment 1 - Flag type (A, I, R, or V)
                        switch (splitLine[0].ToLower())
                        {
                        case "a":
                            aff.Where = ToWhere.Affects;
                            break;

                        case "i":
                            aff.Where = ToWhere.Immune;
                            break;

                        case "r":
                            aff.Where = ToWhere.Resist;
                            break;

                        case "v":
                            aff.Where = ToWhere.Vuln;
                            break;

                        default:
                            throw new ObjectParsingException(String.Format("Error parsing affect flags for object {0} in area {1}: invalid flag location type \"{2}\" encountered, expected A, I, R, or V on line {3}", outObj.VNUM, areaFile, splitLine[1], lineNum));
                        }

                        // Segment 2 - Location
                        int flagLocation = 0;
                        if (!Int32.TryParse(splitLine[1], out flagLocation))
                        {
                            throw new ObjectParsingException(String.Format("Error parsing affect flags location for object {0} in area {1}: expected an integer but found \"{2}\" on line {3}", outObj.VNUM, areaFile, splitLine[1], lineNum));
                        }
                        else
                        {
                            aff.Location = (ApplyType)flagLocation;
                        }

                        // Segment 3 - Modifier
                        int flagMod = 0;
                        if (!Int32.TryParse(splitLine[2], out flagMod))
                        {
                            throw new ObjectParsingException(String.Format("Error parsing affect flags modifier for object {0} affect in area {1}: expected an integer but found \"{2}\" on line {3}", outObj.VNUM, areaFile, splitLine[2], lineNum));
                        }
                        else
                        {
                            aff.Modifier = flagMod;
                        }

                        // Segment 4 - Bitvector (value of the flag to apply)
                        try
                        {
                            int bitvector = AlphaConversions.ConvertROMAlphaToInt32(splitLine[3]);
                            aff.BitVector = (AffectedByFlag)bitvector;
                        }
                        catch (ArgumentException e)
                        {
                            // Invalid extra flags
                            throw new ObjectParsingException(String.Format("Error parsing affect flags bitvector for object {0} in area {1}: invalid flag value \"{2}\" found on line {3}", outObj.VNUM, areaFile, splitLine[3], lineNum), e);
                        }

                        if (log)
                        {
                            Logging.Log.Debug(String.Format("Object affect flag loaded to {0} modifying {1} with modifier {2} and bitvector {3} on object {4} in area {5}", aff.Where.ToString(), aff.Location.ToString(), aff.Modifier, aff.BitVector, outObj.VNUM, areaFile));
                        }

                        // Add the affect
                        outObj.Affected.Add(aff);
                        break;

                    case "e":
                        // Object extra descriptions
                        // TODO: This is an almost straight copy of extra description loading in rooms, should be made its own method
                        ExtraDescription desc = new ExtraDescription();

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

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

                        // Pull the extra description's data
                        desc.Description = Data.ReadLongText(sr, ref lineNum, areaFile, outObj.VNUM);

                        if (log)
                        {
                            Logging.Log.Debug(String.Format("Extra description loaded for object {0} in area {1} with keywords {2}", outObj.VNUM, areaFile, desc.Keywords));
                        }

                        // Append the ExtraDescription to the object
                        outObj.ExtraDescriptions.Add(desc);
                        break;

                    default:
                        if (log)
                        {
                            Logging.Log.Warn(String.Format("Invalid object modifier \"{0}\" found in object {1} in area {2} on line {3}", lineData.Trim(), outObj.VNUM, areaFile, lineNum));
                        }
                        break;
                    }
                }
                else
                {
                    // We're done with this object
                    readingAffects = false;
                }
            }


            return(outObj);
        }
예제 #6
0
        internal static MobPrototypeData ParseMobData(ref StringReader sr, string areaFile, ref int lineNum, string firstLine)
        {
            Logging.Log.Debug(String.Format("ParseMobData() called for area {0} starting on line {1}", areaFile, lineNum));

            // Instantiate variables for the method
            MobPrototypeData outMob   = new MobPrototypeData();
            string           lineData = firstLine;

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

            if (!vnum.Equals(0))
            {
                outMob.VNUM = vnum;
            }
            else
            {
                return(null);
            }

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

            // Read the mob name - technically we should just read 'til we get a tilde, but it looks like they're always on one line anyway...
            outMob.Name = sr.ReadLine().TrimEnd('~');
            lineNum++;

            // Next, read the short description; again, a single line
            outMob.ShortDescription = sr.ReadLine().TrimEnd('~');
            lineNum++;

            // Then, read the long description
            outMob.LongDescription = Data.ReadLongText(sr, ref lineNum, areaFile, outMob.VNUM);

            // Finally, read yet another description
            outMob.Description = Data.ReadLongText(sr, ref lineNum, areaFile, outMob.VNUM);

            // TODO: Capitalize the first letter of LongDescription and Description. Or don't, I'm not your dad.

            // Next, parse the race
            string mobRaceName = sr.ReadLine().TrimEnd('~');

            lineNum++;

            Race mobRace = Consts.Races.RaceTable.SingleOrDefault(r => r.Name.ToLower().Equals(mobRaceName.ToLower()));

            // If we can't find the race, log a warning and return null
            if (mobRace == null)
            {
                Logging.Log.Error(String.Format("Encountered unknown race {0} for mob {1} of area {2} on line {3}", mobRaceName, outMob.VNUM, areaFile, lineNum));
                return(null);
            }
            else
            {
                // Otherwise, store the race
                outMob.Race = mobRace;

                // Race defaults a number of flags; set these now
                outMob.Form          = outMob.Race.Form;
                outMob.Parts         = outMob.Race.Parts;
                outMob.Actions       = outMob.Race.Actions;
                outMob.AffectedBy    = outMob.Race.Affects;
                outMob.Immunity      = outMob.Race.Immunities;
                outMob.Resistance    = outMob.Race.Resistances;
                outMob.Vulnerability = outMob.Race.Vulnerabilities;
            }

            // Split the next line into an array that should have four elements
            lineData = sr.ReadLine();
            lineNum++;

            string[] splitLine = lineData.Split(' ');

            // Expect four segments
            if (splitLine.Length != 4)
            {
                Logging.Log.Error(String.Format("Error parsing mobile {0} in area {1}: invalid act/aff/align/group line, expected 4 segments but got {2} - value {3} on line {4}", outMob.VNUM, areaFile, splitLine.Length, lineData, lineNum));
                return(null);
            }

            int parsedValue = 0;

            // First segment, action flags - also add IsNPC to all mobs
            outMob.Actions |= AlphaConversions.ConvertROMAlphaToActionFlag(splitLine[0]) | ActionFlag.IsNPC;

            // Second segment, affected by flags
            outMob.AffectedBy = AlphaConversions.ConvertROMAlphaToAffectedByFlag(splitLine[1]);

            // Third segment, alignment
            if (!Int32.TryParse(splitLine[2], out parsedValue))
            {
                Logging.Log.Error(String.Format("Error parsing alignment for mobile {0} in area {1}: non-numeric value {2} encountered on line {3}", outMob.VNUM, areaFile, splitLine[2], lineNum));
                return(null);
            }
            else
            {
                outMob.Alignment = Convert.ToInt32(splitLine[2]);
            }

            // Fourth segment, group
            if (!Int32.TryParse(splitLine[3], out parsedValue))
            {
                Logging.Log.Error(String.Format("Error parsing group number for mobile {0} in area {1}: non-numeric value {2} encountered on line {3}", outMob.VNUM, areaFile, splitLine[3], lineNum));
                return(null);
            }
            else
            {
                outMob.Group = Convert.ToInt32(splitLine[3]);
            }

            // Read the next line and split - expect six segments
            lineData = sr.ReadLine();
            lineNum++;
            splitLine = lineData.Split(' ');

            if (splitLine.Length != 6)
            {
                Logging.Log.Error(String.Format("Error parsing mobile {0} in area {1}: invalid level/hit/health/mana/damage/dtype line, expected 6 segments but got {2} - value {3} on line {4}", outMob.VNUM, areaFile, splitLine.Length, lineData, lineNum));
                return(null);
            }

            // First segment, level
            if (!Int32.TryParse(splitLine[0], out parsedValue))
            {
                Logging.Log.Error(String.Format("Error parsing level for mobile {0} in area {1}: non-numeric value {2} encountered on line {3}", outMob.VNUM, areaFile, splitLine[0], lineNum));
                return(null);
            }
            else
            {
                outMob.Level = Convert.ToInt32(splitLine[0]);
            }

            // Second segment, hitroll
            if (!Int32.TryParse(splitLine[1], out parsedValue))
            {
                Logging.Log.Error(String.Format("Error parsing hitroll for mobile {0} in area {1}: non-numeric value {2} encountered on line {3}", outMob.VNUM, areaFile, splitLine[1], lineNum));
                return(null);
            }
            else
            {
                outMob.HitRoll = Convert.ToInt32(splitLine[1]);
            }

            // Third segment, hit dice
            DiceRoll dice;

            if (!DiceRoll.TryParse(splitLine[2], out dice))
            {
                Logging.Log.Error(String.Format("Error parsing hit dice for mobile {0} in area {1}: invalid dice roll string ({2}) encountered on line {3}", outMob.VNUM, areaFile, splitLine[2], lineNum));
                return(null);
            }
            else
            {
                outMob.Health = dice;
            }

            // Fourth segment, mana dice
            if (!DiceRoll.TryParse(splitLine[3], out dice))
            {
                Logging.Log.Error(String.Format("Error parsing mana dice for mobile {0} in area {1}: invalid dice roll string ({2}) encountered on line {3}", outMob.VNUM, areaFile, splitLine[3], lineNum));
                return(null);
            }
            else
            {
                outMob.Mana = dice;
            }

            // Fifth segment, damage dice
            if (!DiceRoll.TryParse(splitLine[4], out dice))
            {
                Logging.Log.Error(String.Format("Error parsing damage dice for mobile {0} in area {1}: invalid dice roll string ({2}) encountered on line {3}", outMob.VNUM, areaFile, splitLine[4], lineNum));
                return(null);
            }
            else
            {
                outMob.Damage = dice;
            }

            // Sixth segment, damage type - check that we have a matching damage type with the given abbreviation
            DamageType dtype = Consts.DamageTypes.AttackTable.SingleOrDefault(d => d.Abbreviation.Equals(splitLine[5].Trim()));

            if (dtype == null)
            {
                Logging.Log.Error(String.Format("Error parsing damage type for mobile {0} in area {1}: unknown damage type {2} specified on line {4}", outMob.VNUM, areaFile, splitLine[5], lineNum));
                return(null);
            }
            else
            {
                outMob.DamageType = dtype;
            }

            // Read the next line and split again, armor classes - expect four segments
            lineData = sr.ReadLine();
            lineNum++;
            splitLine = lineData.Split(' ');

            if (splitLine.Length != 4)
            {
                Logging.Log.Error(String.Format("Error parsing mobile {0} in area {1}: invalid level/hit/health/mana/damage/dtype line, expected 4 segments but got {2} - value {3} on line {4}", outMob.VNUM, areaFile, splitLine.Length, lineData, lineNum));
                return(null);
            }

            // First segment, piercing AC
            if (!Int32.TryParse(splitLine[0], out parsedValue))
            {
                Logging.Log.Error(String.Format("Error parsing piercing AC for mobile {0} in area {1}: non-numeric value {2} encountered on line {3}", outMob.VNUM, areaFile, splitLine[0], lineNum));
                return(null);
            }
            else
            {
                outMob.ArmorRating.Pierce = Convert.ToInt32(splitLine[0]);
            }

            // Second segment, bashing AC
            if (!Int32.TryParse(splitLine[1], out parsedValue))
            {
                Logging.Log.Error(String.Format("Error parsing bashing AC for mobile {0} in area {1}: non-numeric value {2} encountered on line {3}", outMob.VNUM, areaFile, splitLine[1], lineNum));
                return(null);
            }
            else
            {
                outMob.ArmorRating.Bash = Convert.ToInt32(splitLine[1]);
            }

            // Third segment, slashing AC
            if (!Int32.TryParse(splitLine[2], out parsedValue))
            {
                Logging.Log.Error(String.Format("Error parsing slashing AC for mobile {0} in area {1}: non-numeric value {2} encountered on line {3}", outMob.VNUM, areaFile, splitLine[2], lineNum));
                return(null);
            }
            else
            {
                outMob.ArmorRating.Slash = Convert.ToInt32(splitLine[2]);
            }

            // Fourth segment, exotic AC
            if (!Int32.TryParse(splitLine[3], out parsedValue))
            {
                Logging.Log.Error(String.Format("Error parsing exotic AC for mobile {0} in area {1}: non-numeric value {2} encountered on line {3}", outMob.VNUM, areaFile, splitLine[3], lineNum));
                return(null);
            }
            else
            {
                outMob.ArmorRating.Exotic = Convert.ToInt32(splitLine[3]);
            }

            // Read the next line and split again, offense/imm/res/vuln flags - expect four segments
            lineData = sr.ReadLine();
            lineNum++;
            splitLine = lineData.Split(' ');

            if (splitLine.Length != 4)
            {
                Logging.Log.Error(String.Format("Error parsing mobile {0} in area {1}: invalid offense/imm/res/vuln line, expected 4 segments but got {2} - value {3} on line {4}", outMob.VNUM, areaFile, splitLine.Length, lineData, lineNum));
                return(null);
            }

            // First segment, offense flags
            outMob.Offense |= AlphaConversions.ConvertROMAlphaToOffensiveFlag(splitLine[0]);

            // Second segment, immunity flags
            outMob.Immunity |= AlphaConversions.ConvertROMAlphaToImmunitylag(splitLine[1]);

            // Third segment, resistance flags
            outMob.Resistance |= AlphaConversions.ConvertROMAlphaToResistanceFlag(splitLine[2]);

            // Fourth segment, vulnerability flags
            outMob.Vulnerability |= AlphaConversions.ConvertROMAlphaToVulnerabilityFlag(splitLine[3]);

            // Read the next line and split again, start pos/default pos/sex/wealth - epect four segments
            lineData = sr.ReadLine();
            lineNum++;
            splitLine = lineData.Split(' ');

            if (splitLine.Length != 4)
            {
                Logging.Log.Error(String.Format("Error parsing mobile {0} in area {1}: invalid start pos/default pos/sex/wealth line, expected 4 segments but got {2} - value {3} on line {4}", outMob.VNUM, areaFile, splitLine.Length, lineData, lineNum));
                return(null);
            }

            // First segment, start position
            Position startPos = Consts.Positions.PositionTable.SingleOrDefault(p => p.ShortName.Equals(splitLine[0]));

            if (startPos == null)
            {
                Logging.Log.Error(String.Format("Error parsing mobile {0} in area {1}: invalid start position \"{2}\" found on line {3}", outMob.VNUM, areaFile, splitLine[0], lineNum));
                return(null);
            }
            else
            {
                outMob.StartingPosition = startPos;
            }

            // Second segment, default position
            Position defPos = Consts.Positions.PositionTable.SingleOrDefault(p => p.ShortName.Equals(splitLine[1]));

            if (startPos == null)
            {
                Logging.Log.Error(String.Format("Error parsing mobile {0} in area {1}: invalid default position \"{2}\" found on line {3}", outMob.VNUM, areaFile, splitLine[1], lineNum));
                return(null);
            }
            else
            {
                outMob.DefaultPosition = defPos;
            }

            // Third segment, gender
            Gender mobGender = Consts.Gender.GenderTable.SingleOrDefault(g => g.Name.Equals(splitLine[2]));

            if (mobGender == null)
            {
                Logging.Log.Error(String.Format("Error parsing mobile {0} in area {1}: invalid gender \"{2}\" found on line {3}", outMob.VNUM, areaFile, splitLine[2], lineNum));
                return(null);
            }
            else
            {
                outMob.Gender = mobGender;
            }

            // Fourth segment, wealth
            int wealth = 0;

            if (!Int32.TryParse(splitLine[3], out wealth))
            {
                Logging.Log.Error(String.Format("Error parsing mobile {0} in area {1}: invalid wealth \"{2}\" found on line {3}, expected an integer", outMob.VNUM, areaFile, splitLine[3], lineNum));
                return(null);
            }
            else
            {
                outMob.Wealth = wealth;
            }

            // Read the next line and split again, form/parts/size/material - epect four segments
            lineData = sr.ReadLine();
            lineNum++;
            splitLine = lineData.Split(' ');

            if (splitLine.Length != 4)
            {
                Logging.Log.Error(String.Format("Error parsing mobile {0} in area {1}: invalid start pos/default pos/sex/wealth line, expected 4 segments but got {2} - value {3} on line {4}", outMob.VNUM, areaFile, splitLine.Length, lineData, lineNum));
                return(null);
            }

            // First segment, form
            outMob.Form |= AlphaConversions.ConvertROMAlphaToFormFlag(splitLine[0]);

            // Second segment, parts
            outMob.Parts |= AlphaConversions.ConvertROMAlphaToPartFlag(splitLine[1]);

            // Third segment, size
            Models.Size mobSize = Consts.Size.SizeTable.SingleOrDefault(s => s.Name.Equals(splitLine[2]));
            if (mobSize == null)
            {
                Logging.Log.Error(String.Format("Error parsing mobile {0} in area {1}: invalid size \"{2}\" found on line {3}", outMob.VNUM, areaFile, splitLine[2], lineNum));
                return(null);
            }
            else
            {
                outMob.Size = mobSize;
            }

            // Fourth segment, material
            outMob.Material = splitLine[3];

            // Peek ahead to the next line, see if it starts with F, which tells us to un-set some flags
            while ((char)sr.Peek() == 'F')
            {
                // Read the line
                lineData = sr.ReadLine();
                lineNum++;

                // Split the line, expect 3 segments
                splitLine = lineData.Split(' ');

                if (splitLine.Length != 3)
                {
                    Logging.Log.Error(String.Format("Error parsing mobile {0} in area {1}: invalid flag modification line, expected 3 segments but got {2} - value {3} on line {4}", outMob.VNUM, areaFile, splitLine.Length, lineData, lineNum));
                    return(null);
                }

                // First segment is line identifier, ignore it. Second segment is which flag to modify, third segment is flag(s) to remove.
                switch (splitLine[1])
                {
                case "act":     // Actions
                    Logging.Log.Debug(String.Format("Removing action flags {0} from mob {1} in area {2} on line {3}", splitLine[2], outMob.VNUM, areaFile, lineNum));

                    // Remove flags
                    outMob.Actions &= ~AlphaConversions.ConvertROMAlphaToActionFlag(splitLine[2]);

                    break;

                case "aff":     // AffectedBy
                    Logging.Log.Debug(String.Format("Removing affected by flags {0} from mob {1} in area {2} on line {3}", splitLine[2], outMob.VNUM, areaFile, lineNum));

                    // Remove flags
                    outMob.AffectedBy &= ~AlphaConversions.ConvertROMAlphaToAffectedByFlag(splitLine[2]);

                    break;

                case "off":     // Offense
                    Logging.Log.Debug(String.Format("Removing offense flags {0} from mob {1} in area {2} on line {3}", splitLine[2], outMob.VNUM, areaFile, lineNum));

                    // Remove flags
                    outMob.Offense &= ~AlphaConversions.ConvertROMAlphaToOffensiveFlag(splitLine[2]);

                    break;

                case "imm":     // Immunities
                    Logging.Log.Debug(String.Format("Removing immunity flags {0} from mob {1} in area {2} on line {3}", splitLine[2], outMob.VNUM, areaFile, lineNum));

                    // Remove flags
                    outMob.Immunity &= ~AlphaConversions.ConvertROMAlphaToImmunitylag(splitLine[2]);

                    break;

                case "res":     // Resistances
                    Logging.Log.Debug(String.Format("Removing restistance flags {0} from mob {1} in area {2} on line {3}", splitLine[2], outMob.VNUM, areaFile, lineNum));

                    // Remove flags
                    outMob.Resistance &= ~AlphaConversions.ConvertROMAlphaToResistanceFlag(splitLine[2]);

                    break;

                case "vul":     // Vulnerabilities
                    Logging.Log.Debug(String.Format("Removing vulnerability flags {0} from mob {1} in area {2} on line {3}", splitLine[2], outMob.VNUM, areaFile, lineNum));

                    // Remove flags
                    outMob.Vulnerability &= ~AlphaConversions.ConvertROMAlphaToVulnerabilityFlag(splitLine[2]);

                    break;

                case "for":     // Form
                    Logging.Log.Debug(String.Format("Removing form flags {0} from mob {1} in area {2} on line {3}", splitLine[2], outMob.VNUM, areaFile, lineNum));

                    // Remove flags
                    outMob.Form &= ~AlphaConversions.ConvertROMAlphaToFormFlag(splitLine[2]);

                    break;

                case "par":     // Parts
                    Logging.Log.Debug(String.Format("Removing parts flags {0} from mob {1} in area {2} on line {3}", splitLine[2], outMob.VNUM, areaFile, lineNum));

                    // Remove flags
                    outMob.Parts &= ~AlphaConversions.ConvertROMAlphaToPartFlag(splitLine[2]);

                    break;

                default:
                    Logging.Log.Warn(String.Format("Encountered unexpected mob flag modifier type {0} for mob {1} in file {2} on line {3}", splitLine[1], outMob.VNUM, areaFile, lineNum));
                    break;
                }
            }

            Logging.Log.Debug(String.Format("Mobile {0} in area {1} loaded", outMob.VNUM, areaFile));

            return(outMob);
        }