public ObjectData(ObjectPrototypeData proto) : this() { Level = proto.Level; Name = proto.Name; ShortDescription = proto.ShortDescription; Description = proto.Description; Material = proto.Material; ObjectType = proto.ObjectType; ExtraFlags = proto.ExtraFlags; WearFlags = proto.WearFlags; Values = proto.Values; Weight = proto.Weight; Cost = proto.Cost; Prototype = proto; if (ObjectType == Enums.ItemClass.Light && (int)Values[2] == 999) { Values[2] = -1; } foreach (AffectData aff in Affected) { if (aff.Location == Enums.ApplyType.SpellAffect) { ApplyAffect(aff); } } }
/// <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 SetOtherItemTypeValues(string[] splitLine, ObjectPrototypeData outObj, string areaFile, int lineNum) { // All segments should be parsed as ints SetValue_Int(splitLine[0], ref outObj.Values[0], "other type", areaFile, lineNum, outObj.VNUM); SetValue_Int(splitLine[1], ref outObj.Values[1], "other type", areaFile, lineNum, outObj.VNUM); SetValue_Int(splitLine[2], ref outObj.Values[2], "other type", areaFile, lineNum, outObj.VNUM); SetValue_Int(splitLine[3], ref outObj.Values[3], "other type", areaFile, lineNum, outObj.VNUM); SetValue_Int(splitLine[4], ref outObj.Values[4], "other type", 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 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 SetWandAndStaffValues(string[] splitLine, ObjectPrototypeData outObj, string areaFile, int lineNum) { // Segment 1 - Level, should be an integer SetValue_Int(splitLine[0], ref outObj.Values[0], "staff or wand", areaFile, lineNum, outObj.VNUM); // Segment 2 - Current charges, should be an integer SetValue_Int(splitLine[1], ref outObj.Values[1], "staff or wand", areaFile, lineNum, outObj.VNUM); // Segment 3 - Max charges, should be an integer SetValue_Int(splitLine[2], ref outObj.Values[2], "staff or wand", areaFile, lineNum, outObj.VNUM); // Segment 4 - Skill for wielding SetValue_Skill(splitLine[3], ref outObj.Values[3], "staff or wand", areaFile, lineNum, outObj.VNUM); // Segment 5 - Appears to be unused, but should be an integer SetValue_Int(splitLine[4], ref outObj.Values[4], "staff or wand", 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 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 SetPotionPillScrollValues(string[] splitLine, ObjectPrototypeData outObj, string areaFile, int lineNum) { // Segment 1 - Level, should be an integer SetValue_Int(splitLine[0], ref outObj.Values[0], "potion, pill, or scroll", areaFile, lineNum, outObj.VNUM); // Segment 2 - Spell #1, should be a skill SetValue_Skill(splitLine[1], ref outObj.Values[1], "potion, pill, or scroll", areaFile, lineNum, outObj.VNUM); // Segment 3 - Spell #2, should be a skill SetValue_Skill(splitLine[2], ref outObj.Values[2], "potion, pill, or scroll", areaFile, lineNum, outObj.VNUM); // Segment 4 - Spell #3, should be a skill SetValue_Skill(splitLine[3], ref outObj.Values[3], "potion, pill, or scroll", areaFile, lineNum, outObj.VNUM); // Segment 5 - Spell #4, should be a skill SetValue_Skill(splitLine[4], ref outObj.Values[4], "potion, pill, or scroll", areaFile, lineNum, outObj.VNUM); return(true); }
private static string[] ParseValuesLine(string lineData, ObjectPrototypeData outObj, string areaFile, int lineNum) { string[] splitInput = lineData.Split(' '); string[] output = new string[5]; bool inQuoted = false; int count = 0; string buf = String.Empty; foreach (string val in splitInput) { // If we're not in a quoted string and either the value doesn't start with a quote or is fully quoted... if (!inQuoted && (!val.StartsWith("'") || (val.StartsWith("'") && val.EndsWith("'")))) { // Add the value to the output, removing all quotes output[count] = val.Trim('\''); count++; } // If we're in a quoted string and the value contains the ending quote... else if (inQuoted && val.EndsWith("'")) { // Add the buffer and this value to the output, stripping the end quote output[count] = buf + " " + val.Trim('\''); count++; inQuoted = false; } // If we're in a quoted string and the value does not contain the end quote... else if (inQuoted && !val.EndsWith("'")) { // Add it to the buffer buf += " " + val; } // If (implied) we're not in a quoted string and the value starts with a quote but doesn't end with a quote... else if (val.StartsWith("'") && !val.EndsWith("'")) { // Add the value to the buffer, stripping the quote, and note that we're in a quoted string buf = val.Trim('\''); inQuoted = true; } } return(output); }
/// <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); }
/// <summary> /// Sets the Values array of <paramref name="outObj"/> based on input values from <paramref name="splitLine"/> appropriate where object type is DrinkContainer or Fountain /// </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 SetFountainAndDrinkContainerValues(string[] splitLine, ObjectPrototypeData outObj, string areaFile, int lineNum) { // Segment 1 - Container 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 fountain/drink 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 - Container fill level, should be an integer int fillLevel = 0; if (!Int32.TryParse(splitLine[1], out fillLevel)) { // Invalid damage dice number throw new ObjectParsingException(String.Format("Error parsing fill level for fountain/drink container 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] = fillLevel; } // Segment 3 - Liquid Type, should be a string matching the Name of a LiquidType in the LiquidTable string liqType = String.Empty; if (splitLine[2].StartsWith("'")) { int i = 2; string nextElem; do { nextElem = splitLine[i++]; liqType += " " + nextElem; }while (!nextElem.EndsWith("'")); liqType = liqType.Trim().Trim('\''); } else { liqType = splitLine[2]; } LiquidType liqiudType = Consts.Liquids.LiquidTable.SingleOrDefault(l => l.Name.ToLower().Equals(liqType.ToLower())); if (liqiudType == null) { // Invalid weapon class throw new ObjectParsingException(String.Format("Error parsing liquid type for fountain/drink container object {0} in area {1}: unknown liquid type \"{2}\" found on line {3}", outObj.VNUM, areaFile, splitLine[2], lineNum)); } else { // Store the weapon class outObj.Values[2] = liqiudType; } 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); }
public ObjectData(ObjectPrototypeData proto, int level) : this(proto) { Level = (level > 0) ? level : 0; }
public static AreaData LoadFromFile(string areaPath) { // Check that the file exists //try //{ int lineNum = 0; string lineData; string areaFile = Path.GetFileName(areaPath); AreaReadingState state = AreaReadingState.WaitingForSection; AreaData areaOut = new AreaData(); int errors = 0; int loaded = 0; bool backFromError = false; if (!File.Exists(areaPath)) { // Log an error and return null Logging.Log.Error(String.Format("Unable to load requested area file {0}", areaPath)); return(null); } // Instantiate a StreamReader and read the entire file StreamReader sr = new StreamReader(areaPath); string fileData = sr.ReadToEnd(); sr.Dispose(); // Instantiate a StreamReader for the file StringReader strRdr = new StringReader(fileData); // Parse the file, one line at a time. while ((lineData = strRdr.ReadLine()) != null) { // Increment lineNum lineNum++; switch (state) { case AreaReadingState.WaitingForSection: // Skip blank lines while in this state if (lineData.Trim().Equals(String.Empty)) { Logging.Log.Debug(String.Format("Skipping empty line at {0}:{1}", areaFile, lineNum)); continue; } // Expect the first data line we come across to start with # if (!lineData.StartsWith("#", StringComparison.InvariantCulture)) { // Log an error and return null Logging.Log.Error(String.Format("Error parsing {0} line {1}: Expected a section identifier, instead got {2}", areaFile, lineNum, lineData)); return(null); } // We've encountered a section heading; which one? switch (lineData.Trim().ToUpper()) { #region #$ // End-of-File Heading case "#$": // Log Logging.Log.Debug(String.Format("Found #$ EOF marker in file {0} on line {1}, finishing up", areaFile, lineNum)); // Set state to finished state = AreaReadingState.Finished; break; #endregion #region #OBJECTS case "#OBJECTS": Logging.Log.Debug(String.Format("Found #OBJECTS heading in file {0} on line {1}", areaFile, lineNum)); // Continue reading until we hit a #0 bool readingObjects = true; backFromError = false; errors = 0; loaded = 0; while (readingObjects) { // Read a line lineData = strRdr.ReadLine(); lineNum++; // If we've recently come back from failing to load a mob, we need to ignore some lines // until we get to the start of the next mob definition if (backFromError) { // If the line is not the section terminator but it does begin with #, it is // (should be) a new mob definition, so un-set the backFromError flag if (!lineData.Trim().Equals("#0") && lineData.Trim().Length > 0 && lineData.Trim()[0].Equals('#')) { // Un-set the backFromError flag; it's time to resume loading backFromError = false; Logging.Log.Debug(String.Format("Resuming loading of #OBJECTS section in area {0} on line {1} with mob {2}", areaFile, lineNum, lineData.Trim())); } // Otherwise, just move on to the next iteration of the loop else { continue; } } if (lineData == null) { readingObjects = false; } else if (lineData.Trim().Equals("#0")) { readingObjects = false; } else if (!lineData.Trim().Equals("#0") && !lineData.Trim().Equals("#$") && !lineData.Trim().Equals("")) { try { ObjectPrototypeData newObj = ObjectPrototypeData.ParseObjectData(ref strRdr, areaFile, ref lineNum, lineData); // If we have a loaded room, add it to the world if (newObj != null) { Program.World.Objects.Add(newObj); loaded++; } else { // Record a failed mob load, and set the indicator that we're back because of an error and should keep reading // but do nothing until we find a new mob errors++; backFromError = true; } } catch (ObjectParsingException ex) { Logging.Log.Error(String.Format("Error parsing object: {0}", ex.Message)); } } } Logging.Log.Debug(String.Format("Finished reading #OBJECTS section of file {0} on line {1} - loaded {2} objects, failed loading {3} mobs", areaFile, lineNum, loaded, errors)); break; #endregion #region #RESETS case "#RESETS": Logging.Log.Debug(String.Format("Found #RESETS heading in file {0} on line {1}", areaFile, lineNum)); // Continue reading until we hit a #0 bool readingResets = true; backFromError = false; errors = 0; loaded = 0; ResetData lastMob = null; while (readingResets) { // Read a line lineData = strRdr.ReadLine(); lineNum++; if (lineData == null) { readingResets = false; } else if (lineData.Trim().Equals("S")) { readingResets = false; } else if (!lineData.Trim().Equals("S") && !lineData.Trim().Equals("#$") && !lineData.Trim().Equals("")) { try { // Parse the reset ResetData newReset = ResetData.ParseResetData(areaFile, ref lineNum, lineData, lastMob); // Check that the reset parsed if (newReset != null) { // Special handling if the new reset is an E(quip) or G(ive) if (newReset.Command == ResetCommand.EquipObjectOnMob || newReset.Command == ResetCommand.GiveObjectToMob) { // Nest in the mob reset it relates to lastMob.Inner.Add(newReset); } else { // Otherwise, add the reset to the area areaOut.Resets.Add(newReset); } // If the reset was to spawn a M(ob), reset lastMob if (newReset.Command == ResetCommand.SpawnMobile) { lastMob = newReset; } // Finally, increment the loaded count loaded++; } else { // Record a failed mob load, and set the indicator that we're back because of an error and should keep reading // but do nothing until we find a new mob errors++; backFromError = true; } } catch (ObjectParsingException ex) { Logging.Log.Error(String.Format("Error parsing object: {0}", ex.Message)); } } } break; #endregion #region #SHOPS case "#SHOPS": Logging.Log.Debug(String.Format("Found #SHOPS heading in file {0} on line {1}", areaFile, lineNum)); // Continue reading until we hit a #0 bool readingShops = true; backFromError = false; errors = 0; loaded = 0; while (readingShops) { // Read a line lineData = strRdr.ReadLine(); lineNum++; // If we've recently come back from failing to load a mob, we need to ignore some lines // until we get to the start of the next mob definition if (backFromError) { // If the line is not the section terminator but it does begin with #, it is // (should be) a new mob definition, so un-set the backFromError flag if (!lineData.Trim().Equals("#0") && lineData.Trim().Length > 0 && lineData.Trim()[0].Equals('#')) { // Un-set the backFromError flag; it's time to resume loading backFromError = false; Logging.Log.Debug(String.Format("Resuming loading of #SHOPS section in area {0} on line {1} with mob {2}", areaFile, lineNum, lineData.Trim())); } // Otherwise, just move on to the next iteration of the loop else { continue; } } if (lineData == null) { readingShops = false; } else if (lineData.Trim().Equals("#0")) { readingShops = false; } else if (!lineData.Trim().Equals("#0") && !lineData.Trim().Equals("#$") && !lineData.Trim().Equals("")) { // TODO: Implement #SHOPS parsing } } break; #endregion #region #SPECIALS case "#SPECIALS": Logging.Log.Debug(String.Format("Found #SPECIALS heading in file {0} on line {1}", areaFile, lineNum)); // Continue reading until we hit a #0 bool readingSpecials = true; backFromError = false; errors = 0; loaded = 0; while (readingSpecials) { // Read a line lineData = strRdr.ReadLine(); lineNum++; // If we've recently come back from failing to load a mob, we need to ignore some lines // until we get to the start of the next mob definition if (backFromError) { // If the line is not the section terminator but it does begin with #, it is // (should be) a new mob definition, so un-set the backFromError flag if (!lineData.Trim().Equals("#0") && lineData.Trim().Length > 0 && lineData.Trim()[0].Equals('#')) { // Un-set the backFromError flag; it's time to resume loading backFromError = false; Logging.Log.Debug(String.Format("Resuming loading of #SPECIALS section in area {0} on line {1} with mob {2}", areaFile, lineNum, lineData.Trim())); } // Otherwise, just move on to the next iteration of the loop else { continue; } } if (lineData == null) { readingSpecials = false; } else if (lineData.Trim().Equals("#0")) { readingSpecials = false; } else if (!lineData.Trim().Equals("#0") && !lineData.Trim().Equals("#$") && !lineData.Trim().Equals("")) { // TODO: Implement #SPECIALS parsing } } break; #endregion #region #MOBILES case "#MOBILES": Logging.Log.Debug(String.Format("Found #MOBILES heading in file {0} on line {1}", areaFile, lineNum)); errors = 0; loaded = 0; backFromError = false; // Continue reading until we hit a #0 bool readingMobs = true; while (readingMobs) { // Read a line lineData = strRdr.ReadLine(); lineNum++; // If we've recently come back from failing to load a mob, we need to ignore some lines // until we get to the start of the next mob definition if (backFromError) { // If the line is not the section terminator but it does begin with #, it is // (should be) a new mob definition, so un-set the backFromError flag if (!lineData.Trim().Equals("#0") && lineData.Trim().Length > 0 && lineData.Trim()[0].Equals('#')) { // Un-set the backFromError flag; it's time to resume loading backFromError = false; Logging.Log.Debug(String.Format("Resuming loading of #MOBILES section in area {0} on line {1} with mob {2}", areaFile, lineNum, lineData.Trim())); } // Otherwise, just move on to the next iteration of the loop else { continue; } } if (lineData == null) { readingMobs = false; } else if (lineData.Trim().Equals("#0")) { readingMobs = false; } else if (!lineData.Trim().Equals("#0") && !lineData.Trim().Equals("#$") && !lineData.Trim().Equals("")) { MobPrototypeData newMob = MobPrototypeData.ParseMobData(ref strRdr, areaFile, ref lineNum, lineData); // If we have a loaded room, add it to the world if (newMob != null) { Program.World.Mobs.Add(newMob); loaded++; } else { // Record a failed mob load, and set the indicator that we're back because of an error and should keep reading // but do nothing until we find a new mob errors++; backFromError = true; } } } Logging.Log.Debug(String.Format("Finished reading #MOBILES section of file {0} on line {1} - loaded {2} mobs, failed loading {3} mobs", areaFile, lineNum, loaded, errors)); break; #endregion #region #ROOMS case "#ROOMS": Logging.Log.Debug(String.Format("Found #ROOMS heading in file {0} on line {1}", areaFile, lineNum)); // Continue reading until we hit a #0 bool readingRooms = true; backFromError = false; errors = 0; loaded = 0; while (readingRooms) { // Read a line lineData = strRdr.ReadLine(); lineNum++; // If we've recently come back from failing to load a mob, we need to ignore some lines // until we get to the start of the next mob definition if (backFromError) { // If the line is not the section terminator but it does begin with #, it is // (should be) a new mob definition, so un-set the backFromError flag if (!lineData.Trim().Equals("#0") && lineData.Trim().Length > 0 && lineData.Trim()[0].Equals('#')) { // Un-set the backFromError flag; it's time to resume loading backFromError = false; Logging.Log.Debug(String.Format("Resuming loading of #ROOMS section in area {0} on line {1} with mob {2}", areaFile, lineNum, lineData.Trim())); } // Otherwise, just move on to the next iteration of the loop else { continue; } } if (lineData == null) { readingRooms = false; } else if (lineData.Trim().Equals("#0")) { readingRooms = false; } else if (!lineData.Trim().Equals("#0") && !lineData.Trim().Equals("#$") && !lineData.Trim().Equals("")) { RoomIndexData newRoom = RoomIndexData.ParseRoomData(ref strRdr, areaFile, ref lineNum, lineData); // If we have a loaded room, add it to the world if (newRoom != null) { loaded++; Program.World.Rooms.Add(newRoom); } else { errors++; backFromError = true; } } } Logging.Log.Debug(String.Format("Finished reading #ROOMS section of file {0} on line {1} - loaded {2} rooms, failed loading {3} rooms", areaFile, lineNum, loaded, errors)); break; default: break; #endregion #region #AREA // AREA Heading case "#AREA": Logging.Log.Debug(String.Format("Found #AREA heading in file {0} on line {1}", areaFile, lineNum)); #region Filename // Read the next line - will be the area filename lineData = strRdr.ReadLine(); lineNum++; // Trim the trailing tilde lineData = lineData.Substring(0, lineData.Trim().Length - 1); // Set filename areaOut.Filename = lineData; Logging.Log.Debug(String.Format("Read area filename in file {0} on line {1}", areaFile, lineNum)); #endregion #region Name // Read the next line - will be the area name lineData = strRdr.ReadLine(); lineNum++; // Trim the trailing tilde lineData = lineData.Substring(0, lineData.Trim().Length - 1); // Set name areaOut.Name = lineData; Logging.Log.Debug(String.Format("Read area name in file {0} on line {1}", areaFile, lineNum)); #endregion #region Credits // Read the next line - will be the credits lineData = strRdr.ReadLine(); lineNum++; // Trim the trailing tilde lineData = lineData.Substring(0, lineData.Trim().Length - 1); // Set credits areaOut.Credits = lineData; Logging.Log.Debug(String.Format("Read credits information in file {0} on line {1}", areaFile, lineNum)); #endregion #region MinVNUM and MaxVNUM // Read the next line - will be the VNUM values lineData = strRdr.ReadLine(); lineNum++; // Split the line on space string[] vnumData = lineData.Trim().Split(' '); // Should be two values if (!vnumData.Length.Equals(2)) { // Log an error and return null Logging.Log.Error(String.Format("Error parsing {0} line {1}: Expected two numbers separated by space for VNUM limits, instead found {2}", areaFile, lineNum, lineData)); return(null); } // Tracks which of the two numbers we're on bool firstNum = true; // Loop over the two values, each should convert to an integer foreach (string vnum in vnumData) { int intVal; // Try to parse the value as a 32-bit integer if (!Int32.TryParse(vnum, out intVal)) { // Log an error and return null Logging.Log.Error(String.Format("Error parsing {0} line {1}: Error converting VNUM value {2} to an integer", areaFile, lineNum, vnum)); return(null); } else { if (firstNum) { areaOut.MinVNum = intVal; firstNum = false; } else { areaOut.MaxVNum = intVal; } } } Logging.Log.Debug(String.Format("Finished processing #AREA section of file {0} at line {1}", areaFile, lineNum)); #endregion break; #endregion } break; default: break; } } strRdr.Dispose(); // Return the output area return(areaOut); //} //catch (Exception e) //{ // // Log the exception and rethrow // Logging.Log.Fatal(String.Format("Unhandled exception caught: {0}: {1}\n{2}", e.GetType(), e.Message, e.StackTrace)); // throw (e); //} }