/// <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); }
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); }