context: #region Header
  // **********
  // ServUO - BaseCreature.cs
  // **********
  #endregion
  
  #region References
  using System;
  using System.Collections.Generic;
  using System.Threading.Tasks;
  
  using Server.ContextMenus;
  using Server.Engines.PartySystem;
  using Server.Engines.Quests;
  using Server.Engines.Quests.Doom;
  using Server.Engines.Quests.Haven;
  using Server.Engines.XmlSpawner2;
  using Server.Ethics;
  using Server.Factions;
  using Server.Items;
  using Server.Misc;
  using Server.Multis;
  using Server.Network;
  using Server.Regions;
  using Server.SkillHandlers;
  using Server.Spells;
  using Server.Spells.Bushido;
  using Server.Spells.Necromancy;
  using Server.Spells.Sixth;
  using Server.Spells.Spellweaving;
  using Server.Targeting;
  using System.Linq;
  using Server.Spells.SkillMasteries;
  using Server.Prompts;
  #endregion
  
  namespace Server.Mobiles
  {
  
      #region Enums
      /// <summary>
      ///     Summary description for MobileAI.
      /// </summary>
      public enum FightMode
      {
          None, // Never focus on others
          Aggressor, // Only attack aggressors
          Strongest, // Attack the strongest
          Weakest, // Attack the weakest
          Closest, // Attack the closest
          Evil, // Only attack aggressor -or- negative karma
          Good // Only attack aggressor -or- positive karma
      }
  
      public enum OrderType
      {
          None, //When no order, let's roam
          Come, //"(All/Name) come"  Summons all or one pet to your location.
          Drop, //"(Name) drop"  Drops its loot to the ground (if it carries any).
          Follow, //"(Name) follow"  Follows targeted being.
                  //"(All/Name) follow me"  Makes all or one pet follow you.
          Friend, //"(Name) friend"  Allows targeted player to confirm resurrection.
          Unfriend, // Remove a friend
          Guard, //"(Name) guard"  Makes the specified pet guard you. Pets can only guard their owner.
                 //"(All/Name) guard me"  Makes all or one pet guard you.
          Attack, //"(All/Name) kill",
                  //"(All/Name) attack"  All or the specified pet(s) currently under your control attack the target.
          Patrol, //"(Name) patrol"  Roves between two or more guarded targets.
          Release, //"(Name) release"  Releases pet back into the wild (removes "tame" status).
          Stay, //"(All/Name) stay" All or the specified pet(s) will stop and stay in current spot.
          Stop, //"(All/Name) stop Cancels any current orders to attack, guard or follow.
          Transfer //"(Name) transfer" Transfers complete ownership to targeted player.
      }
  
      [Flags]
      public enum FoodType
      {
          None = 0x0000,
          Meat = 0x0001,
          FruitsAndVegies = 0x0002,
          GrainsAndHay = 0x0004,
          Fish = 0x0008,
          Eggs = 0x0010,
          Gold = 0x0020,
          Metal = 0x0040
      }
  
      [Flags]
      public enum PackInstinct
      {
          None = 0x0000,
          Canine = 0x0001,
          Ostard = 0x0002,
          Feline = 0x0004,
          Arachnid = 0x0008,
          Daemon = 0x0010,
          Bear = 0x0020,
          Equine = 0x0040,
          Bull = 0x0080
      }
  
      public enum ScaleType
      {
          Red,
          Yellow,
          Black,
          Green,
          White,
          Blue,
          MedusaLight,
          MedusaDark,
          All
      }
  
      public enum MeatType
      {
          Ribs,
          Bird,
          LambLeg
      }
  
      public enum HideType
      {
          Regular,
          Spined,
          Horned,
          Barbed,
          Fur
      }
  
      public enum TribeType
      {
          None,
          Terathan,
          Ophidian,
          Savage,
          Orc,
          Fey,
          Undead,
          GrayGoblin,
          GreenGoblin
      }
      #endregion
  
      public class DamageStore : IComparable
      {
          public Mobile m_Mobile;
          public int m_Damage;
          public bool m_HasRight;
  
          public DamageStore(Mobile m, int damage)
          {
              m_Mobile = m;
              m_Damage = damage;
          }
  
          public int CompareTo(object obj)
          {
              DamageStore ds = (DamageStore)obj;
  
              return ds.m_Damage - m_Damage;
          }
      }
  
      [AttributeUsage(AttributeTargets.Class)]
      public class FriendlyNameAttribute : Attribute
      {
          //future use: Talisman 'Protection/Bonus vs. Specific Creature
          private readonly TextDefinition m_FriendlyName;
  
          public TextDefinition FriendlyName { get { return m_FriendlyName; } }
  
          public FriendlyNameAttribute(TextDefinition friendlyName)
          {
              m_FriendlyName = friendlyName;
          }
  
          public static TextDefinition GetFriendlyNameFor(Type t)
          {
              if (t.IsDefined(typeof(FriendlyNameAttribute), false))
              {
                  var objs = t.GetCustomAttributes(typeof(FriendlyNameAttribute), false);
  
                  if (objs != null && objs.Length > 0)
                  {
                      FriendlyNameAttribute friendly = objs[0] as FriendlyNameAttribute;
  
                      return friendly.FriendlyName;
                  }
              }
  
              return t.Name;
          }
      }
  
      public class BaseCreature : Mobile, IHonorTarget
      {
          public const int MaxLoyalty = 100;
  
          #region Var declarations
          private BaseAI m_AI; // THE AI
  
          private AIType m_CurrentAI; // The current AI
          private AIType m_DefaultAI; // The default AI
  
          private FightMode m_FightMode; // The style the mob uses
  
          private int m_iRangePerception; // The view area
          private int m_iRangeFight; // The fight distance
  
          private bool m_bDebugAI; // Show debug AI messages
  
          private int m_iTeam; // Monster Team
  
          private double m_dActiveSpeed; // Timer speed when active
          private double m_dPassiveSpeed; // Timer speed when not active
          private double m_dCurrentSpeed; // The current speed, lets say it could be changed by something;
  
          private Point3D m_pHome; // The home position of the creature, used by some AI
          private int m_iRangeHome = 10; // The home range of the creature
  
          private readonly List<Type> m_arSpellAttack; // List of attack spell/power
          private readonly List<Type> m_arSpellDefense; // List of defensive spell/power
  
          private bool m_bControlled; // Is controlled
          private Mobile m_ControlMaster; // My master
          private IDamageable m_ControlTarget; // My target mobile
          private Point3D m_ControlDest; // My target destination (patrol)
          private OrderType m_ControlOrder; // My order
  
          private int m_Loyalty;
  
          private double m_dMinTameSkill;
          private bool m_bTamable;
  
          private bool m_bSummoned;
          private DateTime m_SummonEnd;
          private int m_iControlSlots = 1;
  
          private bool m_bBardProvoked;
          private bool m_bBardPacified;
          private Mobile m_bBardMaster;
          private Mobile m_bBardTarget;
          private WayPoint m_CurrentWayPoint;
          private IPoint2D m_TargetLocation;
  
          private int _CurrentNavPoint;
          private Dictionary<Map, List<Point2D>> _NavPoints;
  
          private Mobile m_SummonMaster;
  
          private int m_HitsMax = -1;
          private int m_StamMax = -1;
          private int m_ManaMax = -1;
          private int m_DamageMin = -1;
          private int m_DamageMax = -1;
  
          private int m_PhysicalResistance, m_PhysicalDamage = 100;
          private int m_FireResistance, m_FireDamage;
          private int m_ColdResistance, m_ColdDamage;
          private int m_PoisonResistance, m_PoisonDamage;
          private int m_EnergyResistance, m_EnergyDamage;
  
          private List<Mobile> m_Owners;
          private List<Mobile> m_Friends;
  
          private bool m_IsStabled;
  
          private bool m_HasGeneratedLoot; // have we generated our loot yet?
  
          private bool m_Paragon;
  
          private string m_CorpseNameOverride;
  
          private int m_FailedReturnHome; /* return to home failure counter */
  
          private bool m_IsChampionSpawn;
  
          private Mobile m_InitialFocus;
          #endregion
  
          #region Monster Stealables
          private bool m_HasBeenStolen;
          [CommandProperty(AccessLevel.Administrator)]
          public bool HasBeenStolen
          {
              get { return m_HasBeenStolen; }
              set { m_HasBeenStolen = value; }
          }
          #endregion
  
          public virtual InhumanSpeech SpeechType { get { return null; } }
  
          public int FollowRange { get; set; }
  
          public virtual bool CanBeParagon { get { return true; } }
  
          /* Do not serialize this till the code is finalized */
  
          private bool m_SeeksHome;
  
          [CommandProperty(AccessLevel.GameMaster)]
          public bool SeeksHome { get { return m_SeeksHome; } set { m_SeeksHome = value; } }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public string CorpseNameOverride { get { return m_CorpseNameOverride; } set { m_CorpseNameOverride = value; } }
  
          [CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]
          public bool IsStabled
          {
              get { return m_IsStabled; }
              set
              {
                  m_IsStabled = value;
                  if (m_IsStabled)
                  {
                      StopDeleteTimer();
                  }
              }
          }
  
          [CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]
          public Mobile StabledBy { get; set; }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public bool IsPrisoner { get; set; }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int QLPoints { get; set; }
  
          protected DateTime SummonEnd { get { return m_SummonEnd; } set { m_SummonEnd = value; } }
  
          public virtual Faction FactionAllegiance { get { return null; } }
          public virtual int FactionSilverWorth { get { return 30; } }
  
          public virtual int BaseLootBudget { get { return RandomItemGenerator.GetBaseBudget(this); } }
  
          public int HumilityBuff = 0;
  
          #region Bonding
          public const bool BondingEnabled = true;
  
          public virtual bool IsBondable { get { return (BondingEnabled && !Summoned && !m_Allured); } }
          public virtual TimeSpan BondingDelay { get { return TimeSpan.FromDays(7.0); } }
          public virtual TimeSpan BondingAbandonDelay { get { return TimeSpan.FromDays(1.0); } }
  
          public override bool CanRegenHits { get { return !m_IsDeadPet && base.CanRegenHits; } }
          public override bool CanRegenStam { get { return !IsParagon && !m_IsDeadPet && base.CanRegenStam; } }
          public override bool CanRegenMana { get { return !m_IsDeadPet && base.CanRegenMana; } }
  
          public override bool IsDeadBondedPet { get { return m_IsDeadPet; } }
  
          private bool m_IsBonded;
          private bool m_IsDeadPet;
          private DateTime m_BondingBegin;
          private DateTime m_OwnerAbandonTime;
  
          [CommandProperty(AccessLevel.GameMaster)]
          public Spawner MySpawner
          {
              get
              {
                  if (Spawner is Spawner)
                  {
                      return (Spawner as Spawner);
                  }
  
                  return null;
              }
              set { }
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public Mobile LastOwner
          {
              get
              {
                  if (m_Owners == null || m_Owners.Count == 0)
                  {
                      return null;
                  }
  
                  return m_Owners[m_Owners.Count - 1];
              }
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public bool IsBonded
          {
              get { return m_IsBonded; }
              set
              {
                  m_IsBonded = value;
                  InvalidateProperties();
              }
          }
  
          public bool IsDeadPet { get { return m_IsDeadPet; } set { m_IsDeadPet = value; } }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public DateTime BondingBegin { get { return m_BondingBegin; } set { m_BondingBegin = value; } }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public DateTime OwnerAbandonTime { get { return m_OwnerAbandonTime; } set { m_OwnerAbandonTime = value; } }
          #endregion
  
          #region Delete Previously Tamed Timer
          private DeleteTimer m_DeleteTimer;
  
          [CommandProperty(AccessLevel.GameMaster)]
          public TimeSpan DeleteTimeLeft
          {
              get
              {
                  if (m_DeleteTimer != null && m_DeleteTimer.Running)
                  {
                      return m_DeleteTimer.Next - DateTime.UtcNow;
                  }
  
                  return TimeSpan.Zero;
              }
          }
  
          private class DeleteTimer : Timer
          {
              private readonly Mobile m;
  
              public DeleteTimer(Mobile creature, TimeSpan delay)
                  : base(delay)
              {
                  m = creature;
                  Priority = TimerPriority.OneMinute;
              }
  
              protected override void OnTick()
              {
                  m.Delete();
              }
          }
  
          public void BeginDeleteTimer()
          {
              if (!(this is BaseEscortable) && !Summoned && !Deleted && !IsStabled)
              {
                  StopDeleteTimer();
                  m_DeleteTimer = new DeleteTimer(this, TimeSpan.FromDays(3.0));
                  m_DeleteTimer.Start();
              }
          }
  
          public void StopDeleteTimer()
          {
              if (m_DeleteTimer != null)
              {
                  m_DeleteTimer.Stop();
                  m_DeleteTimer = null;
              }
          }
          #endregion
  
          public virtual bool AutoRearms { get { return false; } }
  
          public virtual double WeaponAbilityChance { get { return 0.4; } }
  
          public virtual WeaponAbility GetWeaponAbility()
          {
              return null;
          }
  
          #region Elemental Resistance/Damage
          public override int BasePhysicalResistance { get { return m_PhysicalResistance; } }
          public override int BaseFireResistance { get { return m_FireResistance; } }
          public override int BaseColdResistance { get { return m_ColdResistance; } }
          public override int BasePoisonResistance { get { return m_PoisonResistance; } }
          public override int BaseEnergyResistance { get { return m_EnergyResistance; } }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int PhysicalResistanceSeed
          {
              get { return m_PhysicalResistance; }
              set
              {
                  m_PhysicalResistance = value;
                  UpdateResistances();
              }
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int FireResistSeed
          {
              get { return m_FireResistance; }
              set
              {
                  m_FireResistance = value;
                  UpdateResistances();
              }
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int ColdResistSeed
          {
              get { return m_ColdResistance; }
              set
              {
                  m_ColdResistance = value;
                  UpdateResistances();
              }
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int PoisonResistSeed
          {
              get { return m_PoisonResistance; }
              set
              {
                  m_PoisonResistance = value;
                  UpdateResistances();
              }
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int EnergyResistSeed
          {
              get { return m_EnergyResistance; }
              set
              {
                  m_EnergyResistance = value;
                  UpdateResistances();
              }
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int PhysicalDamage { get { return m_PhysicalDamage; } set { m_PhysicalDamage = value; } }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int FireDamage { get { return m_FireDamage; } set { m_FireDamage = value; } }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int ColdDamage { get { return m_ColdDamage; } set { m_ColdDamage = value; } }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int PoisonDamage { get { return m_PoisonDamage; } set { m_PoisonDamage = value; } }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int EnergyDamage { get { return m_EnergyDamage; } set { m_EnergyDamage = value; } }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int ChaosDamage { get; set; }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public int DirectDamage { get; set; }
          #endregion
  
          [CommandProperty(AccessLevel.GameMaster)]
          public bool IsParagon
          {
              get { return m_Paragon; }
              set
              {
                  if (m_Paragon == value)
                  {
                      return;
                  }
                  else if (value)
                  {
                      XmlParagon.Convert(this);
                  }
                  else
                  {
                      XmlParagon.UnConvert(this);
                  }
  
                  m_Paragon = value;
  
                  InvalidateProperties();
              }
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public bool IsChampionSpawn
          {
              get { return m_IsChampionSpawn; }
              set
              {
                  if (!m_IsChampionSpawn && value)
                      SetToChampionSpawn();
  
                  m_IsChampionSpawn = value;
              }
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public Mobile InitialFocus
          {
              get { return m_InitialFocus; }
              set { m_InitialFocus = value; }
          }
  
          [CommandProperty(AccessLevel.GameMaster)]
          public override IDamageable Combatant
          {
              get
              {
                  return base.Combatant;
              }
              set
              {
                  if (base.Combatant == null)
                  {
                      if (value is Mobile && AttacksFocus)
                          m_InitialFocus = (Mobile)value;
                  }
                  else if (AttacksFocus && m_InitialFocus != null && value != m_InitialFocus && !m_InitialFocus.Hidden && InRange(m_InitialFocus.Location, this.RangePerception))
                  {
                      //Keeps focus
                      base.Combatant = m_InitialFocus;
                      return;
                  }
  
                  base.Combatant = value;
              }
          }
  
          public bool IsAmbusher { get; set; }
  
          public virtual bool HasManaOveride { get { return false; } }
  
          public virtual FoodType FavoriteFood { get { return FoodType.Meat; } }
          public virtual PackInstinct PackInstinct { get { return PackInstinct.None; } }
  
          public List<Mobile> Owners { get { return m_Owners; } }
  
          public virtual bool AllowMaleTamer { get { return true; } }
          public virtual bool AllowFemaleTamer { get { return true; } }
          public virtual bool SubdueBeforeTame { get { return false; } }
          public virtual bool StatLossAfterTame { get { return SubdueBeforeTame; } }
          public virtual bool ReduceSpeedWithDamage { get { return true; } }
          public virtual bool IsSubdued { get { return SubdueBeforeTame && (Hits < ((double)HitsMax / 10)); } }
  
          public virtual bool Commandable { get { return true; } }
  
          public virtual Poison HitPoison { get { return null; } }
          public virtual double HitPoisonChance { get { return 0.5; } }
          public virtual Poison PoisonImmune { get { return null; } }
  
          public virtual bool BardImmune { get { return false; } }
          public virtual bool Unprovokable { get { return BardImmune || m_IsDeadPet; } }
          public virtual bool Uncalmable { get { return BardImmune || m_IsDeadPet; } }
          public virtual bool AreaPeaceImmune { get { return BardImmune || m_IsDeadPet; } }
  
          public virtual bool BleedImmune { get { return false; } }
          public virtual double BonusPetDamageScalar { get { return 1.0; } }
          public virtual bool AllureImmune { get { return false; } }
  
          public virtual bool DeathAdderCharmable { get { return false; } }
  
          public virtual bool GivesFameAndKarmaAward { get { return true; } }
  
          //TODO: Find the pub 31 tweaks to the DispelDifficulty and apply them of course.
          public virtual double DispelDifficulty { get { return 0.0; } } // at this skill level we dispel 50% chance
          public virtual double DispelFocus { get { return 20.0; } }
          // at difficulty - focus we have 0%, at difficulty + focus we have 100%
          public virtual bool DisplayWeight { get { return Backpack is StrongBackpack; } }
  
          public virtual double TeleportChance { get { return 0.05; } }
          public virtual bool AttacksFocus { get { return false; } }
          public virtual bool ShowSpellMantra { get { return false; } }
          public virtual bool FreezeOnCast { get { return ShowSpellMantra; } }
  
          #region High Seas
          public virtual bool TaintedLifeAura { get { return false; } }
          #endregion
  
          #region Breath ability, like dragon fire breath
          private long m_NextBreathTime;
  
          // Must be overriden in subclass to enable
          public virtual bool HasBreath { get { return false; } }
  
          // Base damage given is: CurrentHitPoints * BreathDamageScalar
          public virtual double BreathDamageScalar { get { return (Core.AOS ? 0.16 : 0.05); } }
  
          // Min/max seconds until next breath
          public virtual double BreathMinDelay { get { return 30.0; } }
          public virtual double BreathMaxDelay { get { return 45.0; } }
  
          // Creature stops moving for 1.0 seconds while breathing
          public virtual double BreathStallTime { get { return 1.0; } }
  
          // Effect is sent 1.3 seconds after BreathAngerSound and BreathAngerAnimation is played
          public virtual double BreathEffectDelay { get { return 1.3; } }
  
          // Damage is given 1.0 seconds after effect is sent
          public virtual double BreathDamageDelay { get { return 1.0; } }
  
          public virtual int BreathRange { get { return RangePerception; } }
  
          // Damage types
          public virtual int BreathChaosDamage { get { return 0; } }
          public virtual int BreathPhysicalDamage { get { return 0; } }
          public virtual int BreathFireDamage { get { return 100; } }
          public virtual int BreathColdDamage { get { return 0; } }
          public virtual int BreathPoisonDamage { get { return 0; } }
          public virtual int BreathEnergyDamage { get { return 0; } }
  
          // Is immune to breath damages
          public virtual bool BreathImmune { get { return false; } }
  
          // Effect details and sound
          public virtual int BreathEffectItemID { get { return 0x36D4; } }
          public virtual int BreathEffectSpeed { get { return 5; } }
          public virtual int BreathEffectDuration { get { return 0; } }
          public virtual bool BreathEffectExplodes { get { return false; } }
          public virtual bool BreathEffectFixedDir { get { return false; } }
          public virtual int BreathEffectHue { get { return 0; } }
          public virtual int BreathEffectRenderMode { get { return 0; } }
  
          public virtual int BreathEffectSound { get { return 0x227; } }
  
          // Anger sound/animations
          public virtual int BreathAngerSound { get { return GetAngerSound(); } }
          public virtual int BreathAngerAnimation { get { return 12; } }
  
          public virtual void BreathStart(IDamageable target)
          {
              BreathStallMovement();
              BreathPlayAngerSound();
              BreathPlayAngerAnimation();
  
              Direction = GetDirectionTo(target);
  
              Timer.DelayCall(TimeSpan.FromSeconds(BreathEffectDelay), new TimerStateCallback(BreathEffect_Callback), target);
          }
  
          public virtual void BreathStallMovement()
          {
              if (m_AI != null)
              {
                  m_AI.NextMove = Core.TickCount + (int)(BreathStallTime * 1000);
              }
          }
  
          public virtual void BreathPlayAngerSound()
          {
              PlaySound(BreathAngerSound);
          }
  
          public virtual void BreathPlayAngerAnimation()
          {
              Animate(BreathAngerAnimation, 5, 1, true, false, 0);
          }
  
          public virtual void BreathEffect_Callback(object state)
          {
              IDamageable target = (IDamageable)state;
  
              if (!target.Alive || !CanBeHarmful(target))
              {
                  return;
              }
  
              BreathPlayEffectSound();
              BreathPlayEffect(target);
  
              Timer.DelayCall(TimeSpan.FromSeconds(BreathDamageDelay), new TimerStateCallback(BreathDamage_Callback), target);
          }
  
          public virtual void BreathPlayEffectSound()
          {
              PlaySound(BreathEffectSound);
          }
  
          public virtual void BreathPlayEffect(IDamageable target)
          {
              Effects.SendMovingEffect(
                  this,
                  target,
                  BreathEffectItemID,
                  BreathEffectSpeed,
                  BreathEffectDuration,
                  BreathEffectFixedDir,
                  BreathEffectExplodes,
                  BreathEffectHue,
                  BreathEffectRenderMode);
          }
  
          public virtual void BreathDamage_Callback(object state)
          {
              IDamageable target = (IDamageable)state;
  
              if (target is BaseCreature && ((BaseCreature)target).BreathImmune)
              {
                  return;
              }
  
              if (CanBeHarmful(target))
              {
                  DoHarmful(target);
                  BreathDealDamage(target);
              }
          }
  
          public virtual void BreathDealDamage(IDamageable target)
          {
              if (!(target is Mobile) || !Evasion.CheckSpellEvasion((Mobile)target))
              {
                  int physDamage = BreathPhysicalDamage;
                  int fireDamage = BreathFireDamage;
                  int coldDamage = BreathColdDamage;
                  int poisDamage = BreathPoisonDamage;
                  int nrgyDamage = BreathEnergyDamage;
  
                  if (BreathChaosDamage > 0)
                  {
                      switch (Utility.Random(5))
                      {
                          case 0:
                              physDamage += BreathChaosDamage;
                              break;
                          case 1:
                              fireDamage += BreathChaosDamage;
                              break;
                          case 2:
                              coldDamage += BreathChaosDamage;
                              break;
                          case 3:
                              poisDamage += BreathChaosDamage;
                              break;
                          case 4:
                              nrgyDamage += BreathChaosDamage;
                              break;
                      }
                  }
  
                  if (physDamage == 0 && fireDamage == 0 && coldDamage == 0 && poisDamage == 0 && nrgyDamage == 0)
                  {
                      target.Damage(BreathComputeDamage(), this); // Unresistable damage even in AOS
                  }
                  else
                  {
                      AOS.Damage(target, this, BreathComputeDamage(), physDamage, fireDamage, coldDamage, poisDamage, nrgyDamage);
                  }
              }
          }
  
          public virtual int BreathComputeDamage()
          {
              int damage = (int)(Hits * BreathDamageScalar);
  
              if (IsParagon)
              {
                  damage = (int)(damage / XmlParagon.GetHitsBuff(this));
              }
  
              if (damage > 200)
              {
                  damage = 200;
              }
  
              return damage;
          }
          #endregion
  
          public virtual bool CanFly { get { return false; } }
  
          #region Spill Acid
          public void SpillAcid(int Amount)
          {
              SpillAcid(null, Amount);
          }
  
          public void SpillAcid(Mobile target, int Amount)
          {
              if ((target != null && target.Map == null) || Map == null)
              {
                  return;
              }
  
              for (int i = 0; i < Amount; ++i)
              {
                  Point3D loc = Location;
                  Map map = Map;
                  Item acid = NewHarmfulItem();
  
                  if (target != null && target.Map != null && Amount == 1)
                  {
                      loc = target.Location;
                      map = target.Map;
                  }
                  else
                  {
                      bool validLocation = false;
                      for (int j = 0; !validLocation && j < 10; ++j)
                      {
                          loc = new Point3D(loc.X + (Utility.Random(0, 3) - 2), loc.Y + (Utility.Random(0, 3) - 2), loc.Z);
                          loc.Z = map.GetAverageZ(loc.X, loc.Y);
                          validLocation = map.CanFit(loc, 16, false, false);
                      }
                  }
                  acid.MoveToWorld(loc, map);
              }
          }
  
          /*
          Solen Style, override me for other mobiles/items:
          kappa+acidslime, grizzles+whatever, etc.
          */
  
          public virtual Item NewHarmfulItem()
          {
              return new PoolOfAcid(TimeSpan.FromSeconds(10), 30, 30);
          }
          #endregion
  
          #region Life Drain
          public virtual bool DrainsLife { get { return false; } }
          public virtual double DrainsLifeChance { get { return 0.1; } }
          public virtual int DrainAmount { get { return Utility.RandomMinMax(10, 40); } }
  
          public virtual int GetDrainAmount(Mobile target)
          {
              return DrainAmount;
          }
  
          public virtual void DrainLife()
          {
              List<Mobile> list = new List<Mobile>();
  
              foreach (Mobile m in this.GetMobilesInRange(2))
              {
                  if (m == this || !CanBeHarmful(m))
                      continue;
  
                  if (m is BaseCreature && (((BaseCreature)m).Controlled || ((BaseCreature)m).Summoned || ((BaseCreature)m).Team != this.Team))
                      list.Add(m);
                  else if (m.Player)
                      list.Add(m);
              }
  
              foreach (Mobile m in list)
              {
                  DoHarmful(m);
  
                  m.FixedParticles(0x374A, 10, 15, 5013, 0x496, 0, EffectLayer.Waist);
                  m.PlaySound(0x231);
  
                  m.SendMessage("You feel the life drain out of you!");
  
                  int toDrain = GetDrainAmount(m);
  
                  //Monster Stealables
                  if (m is PlayerMobile)
                  {
                      PlayerMobile pm = m as PlayerMobile;
                      toDrain = (int)drNO.ThieveItems.LifeShieldLotion.HandleLifeDrain(pm, toDrain);
                  }
                  //end
  
  
                  Hits += toDrain;
                  m.Damage(toDrain, this);
              }
          }
          #endregion
  
          #region Flee!!!
          public virtual bool CanFlee { get { return !m_Paragon; } }
  
          private DateTime m_EndFlee;
  
          public DateTime EndFleeTime { get { return m_EndFlee; } set { m_EndFlee = value; } }
  
          public virtual void StopFlee()
          {
              m_EndFlee = DateTime.MinValue;
          }
  
          public virtual bool CheckFlee()
          {
              if (m_EndFlee == DateTime.MinValue)
              {
                  return false;
              }
  
              if (DateTime.UtcNow >= m_EndFlee)
              {
                  StopFlee();
                  return false;
              }
  
              return true;
          }
  
          public virtual void BeginFlee(TimeSpan maxDuration)
          {
              m_EndFlee = DateTime.UtcNow + maxDuration;
          }
          #endregion
  
          public virtual bool IsInvulnerable { get { return false; } }
  
          public BaseAI AIObject { get { return m_AI; } }
  
          public const int MaxOwners = 5;
  
          public virtual OppositionGroup OppositionGroup { get { return null; } }
          public virtual bool IsMilitiaFighter { get { return false; } }
  
          // Tribe Opposition stuff
          public virtual TribeType Tribe{ get{ return TribeType.None ; } } // What opposition list am I in?
  
          private static readonly Tuple<TribeType, TribeType>[] TribeConflicts =
              new Tuple<TribeType, TribeType>[]
          {
              Tuple.Create(TribeType.Terathan, TribeType.Ophidian),
              Tuple.Create(TribeType.Savage, TribeType.Orc),
              Tuple.Create(TribeType.Fey, TribeType.Undead),
              Tuple.Create(TribeType.GrayGoblin, TribeType.GreenGoblin)
          };
  
          private Dictionary<TribeType, ISet<TribeType>> m_TribeTable;
  
          public Dictionary<TribeType, ISet<TribeType>> TribeTable
          {
              get
              {
                  if (m_TribeTable == null)
                  {
                      m_TribeTable = new Dictionary<TribeType, ISet<TribeType>>();
  
                      foreach (var tribeConflict in TribeConflicts)
                      {
                          ISet<TribeType> conflicts;
                          if (m_TribeTable.ContainsKey(tribeConflict.Item1))
                          {
                              conflicts = m_TribeTable[tribeConflict.Item1];
                          }
                          else
                          {
                              conflicts = m_TribeTable[tribeConflict.Item1] = new HashSet<TribeType>();
                          }
                          conflicts.Add(tribeConflict.Item2);
  
                          ISet<TribeType> conflicts2;
                          if (m_TribeTable.ContainsKey(tribeConflict.Item2))
                          {
                              conflicts2 = m_TribeTable[tribeConflict.Item2];
                          }
                          else
                          {
                              conflicts2 = m_TribeTable[tribeConflict.Item2] = new HashSet<TribeType>();
                          }
                          conflicts2.Add(tribeConflict.Item1);
                      }
                  }
                  return m_TribeTable;
              }
          }
  
          private bool IsTribeEnemy(Mobile m)
          {
              // Target must be BaseCreature
              if (!(m is BaseCreature))
              {
                  return false;
              }
  /*
              // Sanity check. Only necessary if you have a non-None creature with no enemy TribeType.
              if (!TribeTable.ContainsKey(Tribe))
              {
                  return false;
              }
  */
              return TribeTable[Tribe].Contains(((BaseCreature)m).Tribe);
          }
  
          #region Friends
          public List<Mobile> Friends { get { return m_Friends; } }
  
          public virtual bool AllowNewPetFriend { get { return (m_Friends == null || m_Friends.Count < 5); } }
  
          public virtual bool IsPetFriend(Mobile m)
          {
              return (m_Friends != null && m_Friends.Contains(m));
          }
  
          public virtual void AddPetFriend(Mobile m)
          {
              if (m_Friends == null)
              {
                  m_Friends = new List<Mobile>();
              }
  
              m_Friends.Add(m);
          }
  
          public virtual void RemovePetFriend(Mobile m)
          {
              if (m_Friends != null)
              {
                  m_Friends.Remove(m);
              }
          }
  
  		public virtual bool IsFriend(Mobile m)
  		{
  			if (Tribe != TribeType.None && IsTribeEnemy(m))
  			{
  				return false;
  			}
  
  			if (!(m is BaseCreature))
  			{
  				return false;
  			}
  
  			BaseCreature c = (BaseCreature)m;
  
  			if (m_iTeam != c.m_iTeam)
  			{
  				return false;
  			}
  /*
  			if (c.Combatant == this)
  			{
  				return false;
  			}
  */
  			return ((m_bSummoned || m_bControlled) == (c.m_bSummoned || c.m_bControlled));
  		}
          #endregion
  
          #region Allegiance
          public virtual Ethic EthicAllegiance { get { return null; } }
  
          public enum Allegiance
          {
              None,
              Ally,
              Enemy
          }
  
          public virtual Allegiance GetFactionAllegiance(Mobile mob)
          {
              if (mob == null || mob.Map != Faction.Facet || FactionAllegiance == null)
              {
                  return Allegiance.None;
              }
  
              Faction fac = Faction.Find(mob, true);
  
              if (fac == null)
              {
                  return Allegiance.None;
              }
  
              return (fac == FactionAllegiance ? Allegiance.Ally : Allegiance.Enemy);
          }
  
          public virtual Allegiance GetEthicAllegiance(Mobile mob)
          {
              if (mob == null || mob.Map != Faction.Facet || EthicAllegiance == null)
              {
                  return Allegiance.None;
              }
  
              Ethic ethic = Ethic.Find(mob, true);
  
              if (ethic == null)
              {
                  return Allegiance.None;
              }
  
              return (ethic == EthicAllegiance ? Allegiance.Ally : Allegiance.Enemy);
          }
          #endregion
  
  		public virtual bool IsEnemy(Mobile m)
  		{
  			XmlIsEnemy a = (XmlIsEnemy)XmlAttach.FindAttachment(this, typeof(XmlIsEnemy));
  
  			if (a != null)
  			{
  				return a.IsEnemy(m);
  			}
  
  			if (Tribe != TribeType.None && IsTribeEnemy(m))
  			{
  				return true;
  			}
  
  			if (m is BaseGuard)
  			{
  				return false;
  			}
  
  			// Faction Allied Players/Pets are not my enemies
  			if (GetFactionAllegiance(m) == Allegiance.Ally)
  			{
  				return false;
  			}
  
  			Ethic ourEthic = EthicAllegiance;
  			Player pl = Ethics.Player.Find(m, true);
  
  			// Ethic Allied Players/Pets are not my enemies
  			if (pl != null && pl.IsShielded && (ourEthic == null || ourEthic == pl.Ethic))
  			{
  				return false;
  			}
  
  			if (m is PlayerMobile && ((PlayerMobile)m).HonorActive)
  			{
  				return false;
  			}
  
  			if (TransformationSpellHelper.UnderTransformation(m, typeof(EtherealVoyageSpell)))
  			{
  				return false;
  			}
  
  			if (!(m is BaseCreature))
  			{
  				return true;
  			}
  
  			BaseCreature c = (BaseCreature)m;
  
  			if (c.IsMilitiaFighter)
  			{
  				return true;
  			}
  
  			BaseCreature t = this;
  
  			// Summons should have same rules as their master
  			if (c.Summoned && c.SummonMaster != null && c.SummonMaster is BaseCreature)
  			{
  				c = c.SummonMaster as BaseCreature;
  			}
  
  			if (t.Summoned && t.SummonMaster != null && t.SummonMaster is BaseCreature)
  			{
  				t = t.SummonMaster as BaseCreature;
  			}
  
  			// Creatures on other teams are my enemies
  			if (t.m_iTeam != c.m_iTeam)
  			{
  				return true;
  			}
  /*
  			// Creatures attacking me are my enemies
  			if (c.Combatant == this)
  			{
  				return true;
  			}
  */
  			// If I'm summoned/controlled and they aren't summoned/controlled, they are my enemy
  			// If I'm not summoned/controlled and they are summoned/controlled, they are my enemy
  			return ((t.m_bSummoned || t.m_bControlled) != (c.m_bSummoned || c.m_bControlled));
  		}
  
          public override string ApplyNameSuffix(string suffix)
          {
              XmlData customtitle = (XmlData)XmlAttach.FindAttachment(this, typeof(XmlData), "ParagonTitle");
  
              if (customtitle != null)
              {
                  suffix = customtitle.Data;
              }
              else if (IsParagon && !GivesMLMinorArtifact)
              {
                  if (suffix.Length == 0)
                  {
                      suffix = XmlParagon.GetParagonLabel(this);
                  }
                  else
                  {
                      suffix = String.Concat(suffix, " " + XmlParagon.GetParagonLabel(this));
                  }
  
                  XmlAttach.AttachTo(this, new XmlData("ParagonTitle", suffix));
              }
  
              return base.ApplyNameSuffix(suffix);
          }
  
          public virtual bool CheckControlChance(Mobile m)
          {
              if (GetControlChance(m) > Utility.RandomDouble())
              {
                  Loyalty += 1;
                  return true;
              }
  
              PlaySound(GetAngerSound());
  
              if (Body.IsAnimal)
              {
                  Animate(10, 5, 1, true, false, 0);
              }
              else if (Body.IsMonster)
              {
                  Animate(18, 5, 1, true, false, 0);
              }
  
              Loyalty -= 3;
              return false;
          }
  
          public virtual bool CanBeControlledBy(Mobile m)
          {
              return (GetControlChance(m) > 0.0);
          }
  
          public double GetControlChance(Mobile m)
          {
              return GetControlChance(m, false);
          }
  
          public virtual double GetControlChance(Mobile m, bool useBaseSkill)
          {
              if (m_dMinTameSkill <= 29.1 || m_bSummoned || m.AccessLevel >= AccessLevel.GameMaster)
              {
                  return 1.0;
              }
  
              double dMinTameSkill = m_dMinTameSkill;
  
              if (dMinTameSkill > -24.9 && AnimalTaming.CheckMastery(m, this))
              {
                  dMinTameSkill = -24.9;
              }
  
              int taming =
                  (int)((useBaseSkill ? m.Skills[SkillName.AnimalTaming].Base : m.Skills[SkillName.AnimalTaming].Value) * 10);
              int lore = (int)((useBaseSkill ? m.Skills[SkillName.AnimalLore].Base : m.Skills[SkillName.AnimalLore].Value) * 10);
              int bonus = 0, chance = 700;
  
              if (Core.ML)
              {
                  int SkillBonus = taming - (int)(dMinTameSkill * 10);
                  int LoreBonus = lore - (int)(dMinTameSkill * 10);
  
                  int SkillMod = 6, LoreMod = 6;
  
                  if (SkillBonus < 0)
                  {
                      SkillMod = 28;
                  }
                  if (LoreBonus < 0)
                  {
                      LoreMod = 14;
                  }
  
                  SkillBonus *= SkillMod;
                  LoreBonus *= LoreMod;
  
                  bonus = (SkillBonus + LoreBonus) / 2;
              }
              else
              {
                  int difficulty = (int)(dMinTameSkill * 10);
                  int weighted = ((taming * 4) + lore) / 5;
                  bonus = weighted - difficulty;
  
                  if (bonus <= 0)
                  {
                      bonus *= 14;
                  }
                  else
                  {
                      bonus *= 6;
                  }
              }
  
              chance += bonus;
  
              if (chance >= 0 && chance < 200)
              {
                  chance = 200;
              }
              else if (chance > 990)
              {
                  chance = 990;
              }
  
              chance -= (MaxLoyalty - m_Loyalty) * 10;
  
              chance += (int)XmlMobFactions.GetScaledFaction(m, this, -250, 250, 0.001);
  
              return ((double)chance / 1000);
          }
  
          public virtual bool CanTransfer(Mobile m)
          {
              return true;
          }
  
          public virtual bool CanFriend(Mobile m)
          {
              return true;
          }
  
          private static readonly Type[] m_AnimateDeadTypes = new[]
          {
              typeof(MoundOfMaggots), typeof(HellSteed), typeof(SkeletalMount), typeof(WailingBanshee), typeof(Wraith),
              typeof(SkeletalDragon), typeof(LichLord), typeof(FleshGolem), typeof(Lich), typeof(SkeletalKnight),
              typeof(BoneKnight), typeof(Mummy), typeof(SkeletalMage), typeof(BoneMagi), typeof(PatchworkSkeleton)
          };
  
          public virtual bool IsAnimatedDead
          {
              get
              {
                  if (!Summoned)
                  {
                      return false;
                  }
  
                  Type type = GetType();
  
                  bool contains = false;
  
                  for (int i = 0; !contains && i < m_AnimateDeadTypes.Length; ++i)
                  {
                      contains = (type == m_AnimateDeadTypes[i]);
                  }
  
                  return contains;
              }
          }
  
          public virtual bool IsNecroFamiliar
          {
              get
              {
                  if (!Summoned)
                  {
                      return false;
                  }
  
                  if (m_ControlMaster != null && SummonFamiliarSpell.Table.Contains(m_ControlMaster))
                  {
  
gt:                     return SummonFamiliarSpell.Table[m_ControlMaster] == this;