public void ApplyAffect(AffectData aff) { // Add the affect to the object this.Affected.Add(aff); // Apply affect vectors if appropriate if (aff.BitVector != Enums.AffectedByFlag.None) { switch (aff.Where) { case Enums.ToWhere.Object: // Not 100% sure this is correct, should I be loading BitVector as an ItemExtraFlag initially? this.ExtraFlags |= (Enums.ItemExtraFlag)aff.BitVector; break; case Enums.ToWhere.Weapon: if (this.ObjectType == Enums.ItemClass.Weapon) { this.Values[4] = aff.BitVector; } break; } } }
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 void ApplyAffect(AffectData affect, bool adding = true) { // Add the affect to the list Affects.Add(affect); // Add/remove the affect flag to the appropriate property switch (affect.Where) { case ToWhere.Affects: if (adding) { this.AffectedBy |= (AffectedByFlag)affect.BitVector; } else { this.AffectedBy &= ~(AffectedByFlag)affect.BitVector; } break; case ToWhere.Vuln: if (adding) { this.Vulnerability |= (VulnerabilityFlag)affect.BitVector; } else { this.Vulnerability &= ~(VulnerabilityFlag)affect.BitVector; } break; case ToWhere.Resist: if (adding) { this.Resist |= (ResistanceFlag)affect.BitVector; } else { this.Resist &= ~(ResistanceFlag)affect.BitVector; } break; case ToWhere.Immune: if (adding) { this.Immunity |= (ImmunityFlag)affect.BitVector; } else { this.Immunity &= ~(ImmunityFlag)affect.BitVector; } break; } // Set the modifier int modifier = affect.Modifier; // If we're removing, reverse the modifier if (!adding) { modifier *= -1; } // Apply the modifier to the appropriate location switch (affect.Location) { case ApplyType.None: break; case ApplyType.Strength: this.ModifiedStats.Strength += modifier; break; case ApplyType.Dexterity: this.ModifiedStats.Dexterity += modifier; break; case ApplyType.Intelligence: this.ModifiedStats.Intelligence += modifier; break; case ApplyType.Wisdom: this.ModifiedStats.Wisdom += modifier; break; case ApplyType.Constitution: this.ModifiedStats.Constitution += modifier; break; case ApplyType.Gender: // Calculate the new gender value int newGenderVal = (int)this.Gender.GenderCode + modifier; // Attempt to locate a matching new gender value Gender newGender = Consts.Gender.GenderTable.SingleOrDefault(g => (int)g.GenderCode == newGenderVal); if (newGender != null) { this.Gender = newGender; } else { throw new ArgumentException("Unable to find new gender matching modified value " + newGenderVal); } break; case ApplyType.Class: break; case ApplyType.Level: break; case ApplyType.Age: break; case ApplyType.Height: break; case ApplyType.Weight: break; case ApplyType.Health: this.MaxHealth += modifier; break; case ApplyType.Mana: this.MaxMana += modifier; break; case ApplyType.Movement: this.MaxMove += modifier; break; case ApplyType.Gold: break; case ApplyType.Experience: break; case ApplyType.AC: this.Armor.Modify(modifier); break; case ApplyType.HitRoll: this.HitRoll += modifier; break; case ApplyType.DamageRoll: this.DamRoll += modifier; break; // There is actually only one saving throw stat, all feed into it case ApplyType.Saves: case ApplyType.Saving_Rod: case ApplyType.Saving_Spell: case ApplyType.Saving_Breath: case ApplyType.Saving_Paralyze: case ApplyType.Saving_Petrification: this.SavingThrow += modifier; break; case ApplyType.SpellAffect: break; } // Check if the PC is wielding a weapon heavier than their maximum wield weight; if so, drop the item (can happens as strength-boosting effects drop, for example) if (!IsNPC && Equipment.Wield != null && Equipment.Wield.Weight > Consts.AttributeBonuses.StrengthBonusTable[GetEffectiveStat(Enums.Attribute.Strength)].Wield * 10) { // Drop the item // TODO: Implement } }
public CharacterData(MobPrototypeData prototype) : this() { // Simple assignments Prototype = prototype; Name = prototype.Name; ShortDescription = prototype.ShortDescription; LongDescription = prototype.LongDescription; Description = prototype.Description; SpecialFunction = prototype.SpecialFunction; Prompt = null; Group = prototype.Group; Action = prototype.Actions; Communication = CommunicationFlag.NoChannels | CommunicationFlag.NoShout | CommunicationFlag.NoTell; AffectedBy = prototype.AffectedBy; Alignment = prototype.Alignment; Level = prototype.Level; HitRoll = prototype.HitRoll; DamRoll = prototype.Damage.Bonus; Damage = prototype.Damage; Armor = prototype.ArmorRating; Offense = prototype.Offense; Immunity = prototype.Immunity; Resist = prototype.Resistance; Vulnerability = prototype.Vulnerability; StartPosition = prototype.StartingPosition; DefaultPosition = prototype.DefaultPosition; Race = prototype.Race; Form = prototype.Form; Parts = prototype.Parts; Size = prototype.Size; Material = prototype.Material; Affects = new List <AffectData>(); PermanentStats = new Stats(); ModifiedStats = new Stats(); // Calculated assignments if (prototype.Wealth == 0) { Gold = 0; Silver = 0; } else { Random rand = new Random(); // Calculate the randomized wealth of the mob (0.5x - 1.5x the mob's wealth value) long actualWealth = rand.Next(prototype.Wealth / 2, 3 * prototype.Wealth / 2); // Set gold based on actual wealth Gold = rand.Next((int)actualWealth / 200, (int)actualWealth / 100); Silver = actualWealth - (Gold * 100); } // Roll the hit dice to determine max health; set current to the max. MaxHealth = prototype.Health.RollDice(); Health = MaxHealth; // Roll the mana dice to determine max mana; set current to the max MaxMana = prototype.Mana.RollDice(); Mana = MaxMana; // If no damage class is set, choose from slash, pound, and pierce if (prototype.DamageType.DamageClass == DamageClass.None) { Random rand = new Random(); DamageType = Consts.DamageTypes.AttackTable[rand.Next(3)]; } else { DamageType = prototype.DamageType; } // If the prototype gender is random, choose one now; otherwise, set if (prototype.Gender.GenderCode == Sex.Random) { Random rng = new Random(); Gender = Consts.Gender.GenderTable[rng.Next(2)]; } else { Gender = prototype.Gender; } // Set stats - first, set a base across all stats PermanentStats = new Stats(Helpers.Miscellaneous.LowestOf(new int[] { 25, 11 + (Level / 4) })); // Next, adjust stats based on action flags (classes, mostly) if (Action.HasFlag(ActionFlag.Warrior)) { PermanentStats.Strength += 3; PermanentStats.Intelligence -= 1; PermanentStats.Constitution += 2; } if (Action.HasFlag(ActionFlag.Thief)) { PermanentStats.Dexterity += 3; PermanentStats.Intelligence += 1; PermanentStats.Wisdom -= 1; } if (Action.HasFlag(ActionFlag.Cleric)) { PermanentStats.Wisdom += 3; PermanentStats.Dexterity -= 1; PermanentStats.Strength += 1; } if (Action.HasFlag(ActionFlag.Mage)) { PermanentStats.Intelligence += 3; PermanentStats.Strength -= 1; PermanentStats.Dexterity += 1; } // 2 point dexterity bonus for Fast if (Offense.HasFlag(OffensiveFlag.Offense_Fast)) { PermanentStats.Dexterity += 2; } // Size modifiers to strength and constitution - increase when size > medium PermanentStats.Strength += (int)Size.SizeCode - (int)Enums.Size.Medium; PermanentStats.Constitution += ((int)Size.SizeCode - (int)Enums.Size.Medium) / 2; // Permanent spells // Sanctuary if (AffectedBy.HasFlag(AffectedByFlag.Sanctuary)) { SkillType skill = Consts.Skills.SkillTable.SingleOrDefault(s => s.Name.ToLower().Equals("sanctuary")); AffectData affect = new AffectData() { Where = ToWhere.Affects, Type = skill, Level = Level, Duration = -1, Location = ApplyType.None, Modifier = 0, BitVector = AffectedByFlag.Sanctuary }; ApplyAffect(affect); } // Haste - also applies a dexterity modifier based on level if (AffectedBy.HasFlag(AffectedByFlag.Haste)) { SkillType skill = Consts.Skills.SkillTable.Single(s => s.Name.ToLower().Equals("haste")); AffectData affect = new AffectData() { Where = ToWhere.Affects, Type = skill, Level = Level, Duration = -1, Location = ApplyType.Dexterity, Modifier = (Level >= 32) ? 4 : (Level >= 25) ? 3 : (Level >= 18) ? 2 : 1, BitVector = AffectedByFlag.Haste }; ApplyAffect(affect); } // Protection from Evil if (AffectedBy.HasFlag(AffectedByFlag.ProtectEvil)) { SkillType skill = Consts.Skills.SkillTable.Single(s => s.Name.ToLower().Equals("protection evil")); AffectData affect = new AffectData() { Where = ToWhere.Affects, Type = skill, Level = Level, Duration = -1, Location = ApplyType.Saves, Modifier = -1, BitVector = AffectedByFlag.ProtectEvil }; ApplyAffect(affect); } // Protection from Good if (AffectedBy.HasFlag(AffectedByFlag.ProtectGood)) { SkillType skill = Consts.Skills.SkillTable.Single(s => s.Name.ToLower().Equals("protection good")); AffectData affect = new AffectData() { Where = ToWhere.Affects, Type = skill, Level = Level, Duration = -1, Location = ApplyType.Saves, Modifier = -1, BitVector = AffectedByFlag.ProtectGood }; ApplyAffect(affect); } }