Exemple #1
0
        public override bool Equals(object obj)
        {
            MobPrototypeData other = (MobPrototypeData)obj;

            return(other.VNUM.Equals(other.VNUM) &&
                   other.Killed.Equals(Killed) &&
                   other.Name.Equals(Name) &&
                   other.ShortDescription.Equals(ShortDescription) &&
                   other.LongDescription.Equals(LongDescription) &&
                   other.Description.Equals(Description) &&
                   other.Race.Equals(Race) &&
                   other.Resistance.Equals(Resistance) &&
                   other.Vulnerability.Equals(Vulnerability) &&
                   other.Form.Equals(Form) &&
                   other.Parts.Equals(Parts) &&
                   other.Actions.Equals(Actions) &&
                   other.AffectedBy.Equals(AffectedBy) &&
                   other.Alignment.Equals(Alignment) &&
                   other.Group.Equals(Group) &&
                   other.Level.Equals(Level) &&
                   other.HitRoll.Equals(HitRoll) &&
                   other.Health.Equals(Health) &&
                   other.Mana.Equals(Mana) &&
                   other.Damage.Equals(Damage) &&
                   other.DamageType.Equals(DamageType) &&
                   other.ArmorRating.Equals(ArmorRating) &&
                   other.Offense.Equals(Offense) &&
                   other.Immunity.Equals(Immunity) &&
                   other.Resistance.Equals(Resistance) &&
                   other.StartingPosition.Equals(StartingPosition) &&
                   other.DefaultPosition.Equals(DefaultPosition) &&
                   other.Gender.Equals(Gender) &&
                   other.Wealth.Equals(Wealth) &&
                   other.Size.Equals(Size));
        }
        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);
            }
        }
Exemple #3
0
        internal static MobPrototypeData ParseMobData(ref StringReader sr, string areaFile, ref int lineNum, string firstLine)
        {
            Logging.Log.Debug(String.Format("ParseMobData() called for area {0} starting on line {1}", areaFile, lineNum));

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

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

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

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

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

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

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

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

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

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

            lineNum++;

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

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

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

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

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

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

            int parsedValue = 0;

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

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

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

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

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

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

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

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

            // Third segment, hit dice
            DiceRoll dice;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            // Fourth segment, wealth
            int wealth = 0;

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

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

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

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

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

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

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

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

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

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

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

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

                    break;

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

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

                    break;

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

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

                    break;

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

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

                    break;

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

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

                    break;

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

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

                    break;

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

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

                    break;

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

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

                    break;

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

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

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