Example #1
0
        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);
            }
        }