ServUO Version
Publish 58
Ultima Expansion
Endless Journey
I just ported over FS-ATS to the newest ServUO version. Got it to compile, world loaded fine.
But, upon restarting after that, an error hits, starting with serial 0x00000001 and going down the line. If I restore to backup, loads fine, works fine. I save the world, restart it... same error. When it first starts, I can play for as long as I can, plenty of saves, everything working charmingly, but no matter what, even if I load the server and then immediately restart it, the damn "error encountered" pops up wanting me to delete it all.

ANY insight would be GREATLY appreciated... My players are growing restless, and I'm at wits end trying to figure out what could be causing this.
Thanks ahead of time.
 
Last edited:
Okay... I've narrowed it down to my port of FS:ATS. If I make any more progress I will post here
here's the only distro files I had to change... gotta be something here that is causing it. Ignore the /* */, that's just me x'ing them out so that the server wouldn't implode without FS:ATS

PlayerMobile.cs:
#region References
using Server.Accounting;
using Server.ContextMenus;
using Server.Engines.BulkOrders;
using Server.Engines.CannedEvil;
using Server.Engines.CityLoyalty;
using Server.Engines.Craft;
using Server.Engines.Help;
using Server.Engines.PartySystem;
using Server.Engines.Points;
using Server.Engines.Quests;
using Server.Engines.Shadowguard;
using Server.Engines.SphynxFortune;
using Server.Engines.VendorSearching;
using Server.Engines.VoidPool;
using Server.Engines.VvV;
using Server.Guilds;
using Server.Gumps;
using Server.Items;
using Server.Misc;
using Server.Multis;
using Server.Network;
using Server.Regions;
using Server.Services.Virtues;
using Server.SkillHandlers;
using Server.Spells;
using Server.Spells.Bushido;
using Server.Spells.Fifth;
using Server.Spells.First;
using Server.Spells.Fourth;
using Server.Spells.Necromancy;
using Server.Spells.Ninjitsu;
using Server.Spells.Seventh;
using Server.Spells.Sixth;
using Server.Spells.SkillMasteries;
using Server.Targeting;

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Server.Engines.ArenaSystem;
using RankDefinition = Server.Guilds.RankDefinition;
#endregion

namespace Server.Mobiles
{

    #region Enums
    [Flags]
    public enum PlayerFlag
    {
        None = 0x00000000,
        Glassblowing = 0x00000001,
        Masonry = 0x00000002,
        SandMining = 0x00000004,
        StoneMining = 0x00000008,
        ToggleMiningStone = 0x00000010,
        KarmaLocked = 0x00000020,
        AutoRenewInsurance = 0x00000040,
        UseOwnFilter = 0x00000080,
        PublicMyRunUO = 0x00000100,
        PagingSquelched = 0x00000200,
        Young = 0x00000400,
        AcceptGuildInvites = 0x00000800,
        DisplayChampionTitle = 0x00001000,
        HasStatReward = 0x00002000,
        Bedlam = 0x00010000,
        LibraryFriend = 0x00020000,
        Spellweaving = 0x00040000,
        GemMining = 0x00080000,
        ToggleMiningGem = 0x00100000,
        BasketWeaving = 0x00200000,
        AbyssEntry = 0x00400000,
        ToggleClippings = 0x00800000,
        ToggleCutClippings = 0x01000000,
        ToggleCutReeds = 0x02000000,
        MechanicalLife = 0x04000000,
        Unused = 0x08000000,
        ToggleCutTopiaries = 0x10000000,
        HasValiantStatReward = 0x20000000,
        RefuseTrades = 0x40000000,
    }

    [Flags]
    public enum ExtendedPlayerFlag
    {
        Unused = 0x00000001,
        ToggleStoneOnly = 0x00000002,
        CanBuyCarpets = 0x00000004,
        VoidPool = 0x00000008,
        DisabledPvpWarning = 0x00000010,
    }

    public enum NpcGuild
    {
        None,
        MagesGuild,
        WarriorsGuild,
        ThievesGuild,
        RangersGuild,
        HealersGuild,
        MinersGuild,
        MerchantsGuild,
        TinkersGuild,
        TailorsGuild,
        FishermensGuild,
        BardsGuild,
        BlacksmithsGuild
    }

    public enum SolenFriendship
    {
        None,
        Red,
        Black
    }
    #endregion

    public partial class PlayerMobile : Mobile, IHonorTarget
    {
        public static List<PlayerMobile> Instances { get; private set; }

        static PlayerMobile()
        {
            Instances = new List<PlayerMobile>(0x1000);
        }

/*        #region FS:ATS Edtis
        private DateTime m_NextTamingBulkOrder;
        private bool m_Bioenginer;

        [CommandProperty(AccessLevel.GameMaster)]
        public TimeSpan NextTamingBulkOrder
        {
            get
            {
                TimeSpan ts = m_NextTamingBulkOrder - DateTime.UtcNow;

                if (ts < TimeSpan.Zero)
                    ts = TimeSpan.Zero;

                return ts;
            }
            set
            {
                try { m_NextTamingBulkOrder = DateTime.UtcNow + value; }
                catch { }
            }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool Bioenginer
        {
            get { return m_Bioenginer; }
            set { m_Bioenginer = value; }
        }
        #endregion*/

        #region Mount Blocking
        public void SetMountBlock(BlockMountType type, TimeSpan duration, bool dismount)
        {
            if (dismount)
            {
                BaseMount.Dismount(this, this, type, duration, false);
            }
            else
            {
                BaseMount.SetMountPrevention(this, type, duration);
            }
        }
        #endregion

        #region Stygian Abyss
        public override void ToggleFlying()
        {
            if (Race != Race.Gargoyle)
                return;

            if (Frozen)
            {
                SendLocalizedMessage(1060170); // You cannot use this ability while frozen.
                return;
            }

            if (!Flying)
            {
                if (BeginAction(typeof(FlySpell)))
                {
                    if (Spell is Spell)
                        ((Spell)Spell).Disturb(DisturbType.Unspecified, false, false);

                    Spell spell = new FlySpell(this);
                    spell.Cast();

                    Timer.DelayCall(TimeSpan.FromSeconds(3), () => EndAction(typeof(FlySpell)));
                }
                else
                {
                    LocalOverheadMessage(MessageType.Regular, 0x3B2, 1075124); // You must wait before casting that spell again.
                }
            }
            else if (IsValidLandLocation(Location, Map))
            {
                if (BeginAction(typeof(FlySpell)))
                {
                    if (Spell is Spell)
                        ((Spell)Spell).Disturb(DisturbType.Unspecified, false, false);

                    Animate(AnimationType.Land, 0);
                    Flying = false;
                    BuffInfo.RemoveBuff(this, BuffIcon.Fly);

                    Timer.DelayCall(TimeSpan.FromSeconds(3), () => EndAction(typeof(FlySpell)));
                }
                else
                {
                    LocalOverheadMessage(MessageType.Regular, 0x3B2, 1075124); // You must wait before casting that spell again.
                }
            }
            else
                LocalOverheadMessage(MessageType.Regular, 0x3B2, 1113081); // You may not land here.
        }

        public static bool IsValidLandLocation(Point3D p, Map map)
        {
            return map.CanFit(p.X, p.Y, p.Z, 16, false, false);
        }
        #endregion

        private class CountAndTimeStamp
        {
            private int m_Count;
            private DateTime m_Stamp;

            public DateTime TimeStamp => m_Stamp;

            public int Count
            {
                get { return m_Count; }
                set
                {
                    m_Count = value;
                    m_Stamp = DateTime.UtcNow;
                }
            }
        }

        private DesignContext m_DesignContext;
    public Xanthos.SafeResurrection.SafeResContext SafeResContext; // <- Xanthos Safe Res change - added

        private NpcGuild m_NpcGuild;
        private DateTime m_NpcGuildJoinTime;
        private TimeSpan m_NpcGuildGameTime;
        private PlayerFlag m_Flags;
        private ExtendedPlayerFlag m_ExtendedFlags;
        private int m_Profession;

        private int m_NonAutoreinsuredItems;
        // number of items that could not be automaitically reinsured because gold in bank was not enough

        /*
        * a value of zero means, that the mobile is not executing the spell. Otherwise,
        * the value should match the BaseMana required
        */
        private int m_ExecutesLightningStrike; // move to Server.Mobiles??

        private DateTime m_LastOnline;
        private RankDefinition m_GuildRank;
        private bool m_NextEnhanceSuccess;

        [CommandProperty(AccessLevel.GameMaster)]
        public bool NextEnhanceSuccess { get { return m_NextEnhanceSuccess; } set { m_NextEnhanceSuccess = value; } }

        private int m_GuildMessageHue, m_AllianceMessageHue;

        private List<Mobile> m_AutoStabled;
        private List<Mobile> m_AllFollowers;
        private List<Mobile> m_RecentlyReported;

        public bool UseSummoningRite { get; set; }

        #region Points System
        private PointsSystemProps _PointsSystemProps;
        private BODProps _BODProps;
        private AccountGoldProps _AccountGold;

        [CommandProperty(AccessLevel.GameMaster)]
        public PointsSystemProps PointSystems
        {
            get
            {
                if (_PointsSystemProps == null)
                    _PointsSystemProps = new PointsSystemProps(this);

                return _PointsSystemProps;
            }
            set
            {
            }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public BODProps BODData
        {
            get
            {
                if (_BODProps == null)
                {
                    _BODProps = new BODProps(this);
                }

                return _BODProps;
            }
            set
            {
            }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public AccountGoldProps AccountGold
        {
            get
            {
                if (_AccountGold == null)
                {
                    _AccountGold = new AccountGoldProps(this);
                }

                return _AccountGold;
            }
            set
            {
            }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public int AccountSovereigns
        {
            get
            {
                Account acct = Account as Account;

                if (acct != null)
                {
                    return acct.Sovereigns;
                }

                return 0;
            }
            set
            {
                Account acct = Account as Account;

                if (acct != null)
                {
                    acct.SetSovereigns(value);
                }
            }
        }

        public bool DepositSovereigns(int amount)
        {
            Account acct = Account as Account;

            if (acct != null)
            {
                return acct.DepositSovereigns(amount);
            }

            return false;
        }

        public bool WithdrawSovereigns(int amount)
        {
            Account acct = Account as Account;

            if (acct != null)
            {
                return acct.WithdrawSovereigns(amount);
            }

            return false;
        }
        #endregion

        #region Getters & Setters
        public List<Mobile> RecentlyReported { get { return m_RecentlyReported; } set { m_RecentlyReported = value; } }

        public List<Mobile> AutoStabled => m_AutoStabled;

        public bool NinjaWepCooldown { get; set; }

        public List<Mobile> AllFollowers
        {
            get
            {
                if (m_AllFollowers == null)
                {
                    m_AllFollowers = new List<Mobile>();
                }

                return m_AllFollowers;
            }
        }

        [CommandProperty(AccessLevel.GameMaster, true)]
        public RankDefinition GuildRank
        {
            get
            {
                if (AccessLevel >= AccessLevel.GameMaster)
                {
                    return RankDefinition.Leader;
                }
                else
                {
                    return m_GuildRank;
                }
            }
            set { m_GuildRank = value; }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public int GuildMessageHue { get { return m_GuildMessageHue; } set { m_GuildMessageHue = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public int AllianceMessageHue { get { return m_AllianceMessageHue; } set { m_AllianceMessageHue = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public int Profession { get { return m_Profession; } set { m_Profession = value; } }

        public int StepsTaken { get; set; }

        [CommandProperty(AccessLevel.GameMaster)]
        public NpcGuild NpcGuild { get { return m_NpcGuild; } set { m_NpcGuild = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public DateTime NpcGuildJoinTime { get { return m_NpcGuildJoinTime; } set { m_NpcGuildJoinTime = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public DateTime NextBODTurnInTime { get; set; }

        [CommandProperty(AccessLevel.GameMaster)]
        public DateTime LastOnline { get { return m_LastOnline; } set { m_LastOnline = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public TimeSpan NpcGuildGameTime { get { return m_NpcGuildGameTime; } set { m_NpcGuildGameTime = value; } }

        public int ExecutesLightningStrike { get { return m_ExecutesLightningStrike; } set { m_ExecutesLightningStrike = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public int ToothAche { get { return BaseSweet.GetToothAche(this); } set { BaseSweet.SetToothAche(this, value, true); } }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool MechanicalLife { get { return GetFlag(PlayerFlag.MechanicalLife); } set { SetFlag(PlayerFlag.MechanicalLife, value); } }
        #endregion

        #region PlayerFlags
        public PlayerFlag Flags { get { return m_Flags; } set { m_Flags = value; } }
        public ExtendedPlayerFlag ExtendedFlags { get { return m_ExtendedFlags; } set { m_ExtendedFlags = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool PagingSquelched { get { return GetFlag(PlayerFlag.PagingSquelched); } set { SetFlag(PlayerFlag.PagingSquelched, value); } }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool Glassblowing { get { return GetFlag(PlayerFlag.Glassblowing); } set { SetFlag(PlayerFlag.Glassblowing, value); } }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool Masonry { get { return GetFlag(PlayerFlag.Masonry); } set { SetFlag(PlayerFlag.Masonry, value); } }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool SandMining { get { return GetFlag(PlayerFlag.SandMining); } set { SetFlag(PlayerFlag.SandMining, value); } }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool StoneMining { get { return GetFlag(PlayerFlag.StoneMining); } set { SetFlag(PlayerFlag.StoneMining, value); } }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool GemMining { get { return GetFlag(PlayerFlag.GemMining); } set { SetFlag(PlayerFlag.GemMining, value); } }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool BasketWeaving { get { return GetFlag(PlayerFlag.BasketWeaving); } set { SetFlag(PlayerFlag.BasketWeaving, value); } }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool ToggleMiningStone { get { return GetFlag(PlayerFlag.ToggleMiningStone); } set { SetFlag(PlayerFlag.ToggleMiningStone, value); } }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool AbyssEntry { get { return GetFlag(PlayerFlag.AbyssEntry); } set { SetFlag(PlayerFlag.AbyssEntry, value); } }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool ToggleMiningGem { get { return GetFlag(PlayerFlag.ToggleMiningGem); } set { SetFlag(PlayerFlag.ToggleMiningGem, value); } }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool KarmaLocked { get { return GetFlag(PlayerFlag.KarmaLocked); } set { SetFlag(PlayerFlag.KarmaLocked, value); } }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool AutoRenewInsurance { get { return GetFlag(PlayerFlag.AutoRenewInsurance); } set { SetFlag(PlayerFlag.AutoRenewInsurance, value); } }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool UseOwnFilter { get { return GetFlag(PlayerFlag.UseOwnFilter); } set { SetFlag(PlayerFlag.UseOwnFilter, value); } }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool AcceptGuildInvites { get { return GetFlag(PlayerFlag.AcceptGuildInvites); } set { SetFlag(PlayerFlag.AcceptGuildInvites, value); } }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool HasStatReward { get { return GetFlag(PlayerFlag.HasStatReward); } set { SetFlag(PlayerFlag.HasStatReward, value); } }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool HasValiantStatReward { get { return GetFlag(PlayerFlag.HasValiantStatReward); } set { SetFlag(PlayerFlag.HasValiantStatReward, value); } }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool RefuseTrades
        {
            get { return GetFlag(PlayerFlag.RefuseTrades); }
            set { SetFlag(PlayerFlag.RefuseTrades, value); }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool DisabledPvpWarning
        {
            get { return GetFlag(ExtendedPlayerFlag.DisabledPvpWarning); }
            set { SetFlag(ExtendedPlayerFlag.DisabledPvpWarning, value); }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool CanBuyCarpets
        {
            get { return GetFlag(ExtendedPlayerFlag.CanBuyCarpets); }
            set { SetFlag(ExtendedPlayerFlag.CanBuyCarpets, value); }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool VoidPool
        {
            get { return GetFlag(ExtendedPlayerFlag.VoidPool); }
            set { SetFlag(ExtendedPlayerFlag.VoidPool, value); }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool ToggleStoneOnly
        {
            get { return GetFlag(ExtendedPlayerFlag.ToggleStoneOnly); }
            set { SetFlag(ExtendedPlayerFlag.ToggleStoneOnly, value); }
        }

        #region Plant system
        [CommandProperty(AccessLevel.GameMaster)]
        public bool ToggleClippings { get { return GetFlag(PlayerFlag.ToggleClippings); } set { SetFlag(PlayerFlag.ToggleClippings, value); } }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool ToggleCutReeds { get { return GetFlag(PlayerFlag.ToggleCutReeds); } set { SetFlag(PlayerFlag.ToggleCutReeds, value); } }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool ToggleCutClippings { get { return GetFlag(PlayerFlag.ToggleCutClippings); } set { SetFlag(PlayerFlag.ToggleCutClippings, value); } }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool ToggleCutTopiaries { get { return GetFlag(PlayerFlag.ToggleCutTopiaries); } set { SetFlag(PlayerFlag.ToggleCutTopiaries, value); } }

        private DateTime m_SSNextSeed;

        [CommandProperty(AccessLevel.GameMaster)]
        public DateTime SSNextSeed { get { return m_SSNextSeed; } set { m_SSNextSeed = value; } }

        private DateTime m_SSSeedExpire;

        [CommandProperty(AccessLevel.GameMaster)]
        public DateTime SSSeedExpire { get { return m_SSSeedExpire; } set { m_SSSeedExpire = value; } }

        private Point3D m_SSSeedLocation;

        public Point3D SSSeedLocation { get { return m_SSSeedLocation; } set { m_SSSeedLocation = value; } }

        private Map m_SSSeedMap;

        public Map SSSeedMap { get { return m_SSSeedMap; } set { m_SSSeedMap = value; } }
        #endregion

        #endregion

        #region Auto Arrow Recovery
        private readonly Dictionary<Type, int> m_RecoverableAmmo = new Dictionary<Type, int>();

        public Dictionary<Type, int> RecoverableAmmo => m_RecoverableAmmo;

        public void RecoverAmmo()
        {
            if (Alive)
            {
                foreach (KeyValuePair<Type, int> kvp in m_RecoverableAmmo)
                {
                    if (kvp.Value > 0)
                    {
                        Item ammo = null;

                        try
                        {
                            ammo = Activator.CreateInstance(kvp.Key) as Item;
                        }
                        catch (Exception e)
                        {
                            Diagnostics.ExceptionLogging.LogException(e);
                        }

                        if (ammo != null)
                        {
                            string name = ammo.Name;
                            ammo.Amount = kvp.Value;

                            if (name == null)
                            {
                                if (ammo is Arrow)
                                {
                                    name = "arrow";
                                }
                                else if (ammo is Bolt)
                                {
                                    name = "bolt";
                                }
                            }

                            if (name != null && ammo.Amount > 1)
                            {
                                name = string.Format("{0}s", name);
                            }

                            if (name == null)
                            {
                                name = string.Format("#{0}", ammo.LabelNumber);
                            }

                            PlaceInBackpack(ammo);
                            SendLocalizedMessage(1073504, string.Format("{0}\t{1}", ammo.Amount, name)); // You recover ~1_NUM~ ~2_AMMO~.
                        }
                    }
                }

                m_RecoverableAmmo.Clear();
            }
        }
        #endregion

        #region Reward Stable Slots
        [CommandProperty(AccessLevel.GameMaster)]
        public int RewardStableSlots { get; set; }
        #endregion

        private DateTime m_AnkhNextUse;

        [CommandProperty(AccessLevel.GameMaster)]
        public DateTime AnkhNextUse { get { return m_AnkhNextUse; } set { m_AnkhNextUse = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public DateTime NextGemOfSalvationUse { get; set; }

        #region Mondain's Legacy
        [CommandProperty(AccessLevel.GameMaster)]
        public bool Bedlam { get { return GetFlag(PlayerFlag.Bedlam); } set { SetFlag(PlayerFlag.Bedlam, value); } }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool LibraryFriend { get { return GetFlag(PlayerFlag.LibraryFriend); } set { SetFlag(PlayerFlag.LibraryFriend, value); } }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool Spellweaving { get { return GetFlag(PlayerFlag.Spellweaving); } set { SetFlag(PlayerFlag.Spellweaving, value); } }
        #endregion

        [CommandProperty(AccessLevel.GameMaster)]
        public TimeSpan DisguiseTimeLeft => DisguiseTimers.TimeRemaining(this);

        [CommandProperty(AccessLevel.GameMaster)]
        public DateTime PeacedUntil { get; set; }

        [CommandProperty(AccessLevel.Decorator)]
        public override string TitleName
        {
            get
            {
                string name;

                if (Fame >= 10000)
                    name = string.Format("{0} {1}", Female ? "Lady" : "Lord", RawName);
                else
                    name = RawName;

                return name;
            }
        }

        #region Scroll of Alacrity
        [CommandProperty(AccessLevel.GameMaster)]
        public DateTime AcceleratedStart { get; set; }

        [CommandProperty(AccessLevel.GameMaster)]
        public SkillName AcceleratedSkill { get; set; }
        #endregion

        public static Direction GetDirection4(Point3D from, Point3D to)
        {
            int dx = from.X - to.X;
            int dy = from.Y - to.Y;

            int rx = dx - dy;
            int ry = dx + dy;

            Direction ret;

            if (rx >= 0 && ry >= 0)
            {
                ret = Direction.West;
            }
            else if (rx >= 0 && ry < 0)
            {
                ret = Direction.South;
            }
            else if (rx < 0 && ry < 0)
            {
                ret = Direction.East;
            }
            else
            {
                ret = Direction.North;
            }

            return ret;
        }

        public override bool OnDroppedItemToWorld(Item item, Point3D location)
        {
            if (!base.OnDroppedItemToWorld(item, location))
            {
                return false;
            }

            IPooledEnumerable mobiles = Map.GetMobilesInRange(location, 0);

            foreach (Mobile m in mobiles)
            {
                if (m.Z >= location.Z && m.Z < location.Z + 16)
                {
                    mobiles.Free();
                    return false;
                }
            }

            mobiles.Free();

            BounceInfo bi = item.GetBounce();

            if (bi != null && AccessLevel > AccessLevel.Counselor)
            {
                Type type = item.GetType();

                if (type.IsDefined(typeof(FurnitureAttribute), true) || type.IsDefined(typeof(DynamicFlipingAttribute), true))
                {
                    object[] objs = type.GetCustomAttributes(typeof(FlipableAttribute), true);

                    if (objs.Length > 0)
                    {
                        FlipableAttribute fp = objs[0] as FlipableAttribute;

                        if (fp != null)
                        {
                            int[] itemIDs = fp.ItemIDs;

                            Point3D oldWorldLoc = bi.m_WorldLoc;
                            Point3D newWorldLoc = location;

                            if (oldWorldLoc.X != newWorldLoc.X || oldWorldLoc.Y != newWorldLoc.Y)
                            {
                                Direction dir = GetDirection4(oldWorldLoc, newWorldLoc);

                                if (itemIDs.Length == 2)
                                {
                                    switch (dir)
                                    {
                                        case Direction.North:
                                        case Direction.South:
                                            item.ItemID = itemIDs[0];
                                            break;
                                        case Direction.East:
                                        case Direction.West:
                                            item.ItemID = itemIDs[1];
                                            break;
                                    }
                                }
                                else if (itemIDs.Length == 4)
                                {
                                    switch (dir)
                                    {
                                        case Direction.South:
                                            item.ItemID = itemIDs[0];
                                            break;
                                        case Direction.East:
                                            item.ItemID = itemIDs[1];
                                            break;
                                        case Direction.North:
                                            item.ItemID = itemIDs[2];
                                            break;
                                        case Direction.West:
                                            item.ItemID = itemIDs[3];
                                            break;
                                    }
                                }
                            }
                        }
                    }
                }
            }

            return true;
        }

        public override int GetPacketFlags()
        {
            int flags = base.GetPacketFlags();

            return flags;
        }

        public override int GetOldPacketFlags()
        {
            int flags = base.GetOldPacketFlags();

            return flags;
        }

        public bool GetFlag(PlayerFlag flag)
        {
            return ((m_Flags & flag) != 0);
        }

        public void SetFlag(PlayerFlag flag, bool value)
        {
            if (value)
            {
                m_Flags |= flag;
            }
            else
            {
                m_Flags &= ~flag;
            }
        }

        public bool GetFlag(ExtendedPlayerFlag flag)
        {
            return ((m_ExtendedFlags & flag) != 0);
        }

        public void SetFlag(ExtendedPlayerFlag flag, bool value)
        {
            if (value)
            {
                m_ExtendedFlags |= flag;
            }
            else
            {
                m_ExtendedFlags &= ~flag;
            }
        }

        public DesignContext DesignContext { get { return m_DesignContext; } set { m_DesignContext = value; } }

        public static void Initialize()
        {
            if (FastwalkPrevention)
            {
                PacketHandlers.RegisterThrottler(0x02, MovementThrottle_Callback);
            }

            EventSink.Login += OnLogin;
            EventSink.Logout += OnLogout;
            EventSink.Connected += EventSink_Connected;
            EventSink.Disconnected += EventSink_Disconnected;

            #region Enchanced Client
            EventSink.TargetedSkill += Targeted_Skill;
            EventSink.EquipMacro += EquipMacro;
            EventSink.UnequipMacro += UnequipMacro;
            #endregion

            Timer.DelayCall(TimeSpan.Zero, CheckPets);
        }

        #region Enhanced Client
        private static void Targeted_Skill(TargetedSkillEventArgs e)
        {
            Mobile from = e.Mobile;
            IEntity target = e.Target;

            if (from == null || target == null)
                return;

            from.TargetLocked = true;

            if (e.SkillID == 35)
            {
                AnimalTaming.DisableMessage = true;
                AnimalTaming.DeferredTarget = false;
            }

            if (from.UseSkill(e.SkillID) && from.Target != null)
            {
                from.Target.Invoke(from, target);
            }

            if (e.SkillID == 35)
            {
                AnimalTaming.DeferredTarget = true;
                AnimalTaming.DisableMessage = false;
            }

            from.TargetLocked = false;
        }

        public static void EquipMacro(EquipMacroEventArgs e)
        {
            PlayerMobile pm = e.Mobile as PlayerMobile;

            if (pm != null && pm.Backpack != null && pm.Alive && e.List != null && e.List.Count > 0)
            {
                if (pm.IsStaff() || Core.TickCount - pm.NextActionTime >= 0)
                {
                    Container pack = pm.Backpack;

                    e.List.ForEach(serial =>
                    {
                        Item item = pack.Items.FirstOrDefault(i => i.Serial == serial);

                        if (item != null)
                        {
                            Item toMove = pm.FindItemOnLayer(item.Layer);

                            if (toMove != null)
                            {
                                toMove.Internalize();

                                if (!pm.EquipItem(item))
                                {
                                    pm.EquipItem(toMove);
                                }
                                else
                                {
                                    pack.DropItem(toMove);
                                }
                            }
                            else
                            {
                                pm.EquipItem(item);
                            }
                        }
                    });

                    pm.NextActionTime = Core.TickCount + (ActionDelay * e.List.Count);
                }
                else
                {
                    pm.SendActionMessage();
                }
            }
        }

        public static void UnequipMacro(UnequipMacroEventArgs e)
        {
            PlayerMobile pm = e.Mobile as PlayerMobile;

            if (pm != null && pm.Backpack != null && pm.Alive && e.List != null && e.List.Count > 0)
            {
                if (pm.IsStaff() || Core.TickCount - pm.NextActionTime >= 0)
                {
                    Container pack = pm.Backpack;

                    List<Item> worn = new List<Item>(pm.Items);

                    foreach (Item item in worn)
                    {
                        if (e.List.Contains((int)item.Layer))
                        {
                            pack.TryDropItem(pm, item, false);
                        }
                    }

                    pm.NextActionTime = Core.TickCount + ActionDelay;
                    ColUtility.Free(worn);
                }
                else
                {
                    pm.SendActionMessage();
                }
            }
        }
        #endregion

        private static void CheckPets()
        {
            foreach (PlayerMobile pm in World.Mobiles.Values.OfType<PlayerMobile>())
            {
                if (((!pm.Mounted || (pm.Mount != null && pm.Mount is EtherealMount)) &&
                     (pm.AllFollowers.Count > pm.AutoStabled.Count)) ||
                    (pm.Mounted && (pm.AllFollowers.Count > (pm.AutoStabled.Count + 1))))
                {
                    pm.AutoStablePets(); /* autostable checks summons, et al: no need here */
                }
            }
        }

        public override void OnSkillInvalidated(Skill skill)
        {
            if (skill.SkillName == SkillName.MagicResist)
            {
                UpdateResistances();
            }
        }

        public override int GetMaxResistance(ResistanceType type)
        {
            if (IsStaff())
            {
                return 100;
            }

            int max = base.GetMaxResistance(type);
            int refineBonus = BaseArmor.GetRefinedResist(this, type);

            if (refineBonus != 0)
            {
                max += refineBonus;
            }
            else
            {
                max += Spells.Mysticism.StoneFormSpell.GetMaxResistBonus(this);
            }

            if (Race == Race.Elf && type == ResistanceType.Energy)
            {
                max += 5; //Intended to go after the 60 max from curse
            }

            if (type != ResistanceType.Physical && 60 < max && CurseSpell.UnderEffect(this))
            {
                max -= 10;
            }

            if ((type == ResistanceType.Fire || type == ResistanceType.Poison) && CorpseSkinSpell.IsUnderEffects(this))
            {
                max = CorpseSkinSpell.GetResistMalus(this);
            }

            if (type == ResistanceType.Physical && MagicReflectSpell.HasReflect(this))
            {
                max -= 5;
            }

            return max;
        }

        public override void ComputeResistances()
        {
            base.ComputeResistances();

            for (int i = 0; i < Resistances.Length; ++i)
            {
                Resistances[i] = 0;
            }

            Resistances[0] += BasePhysicalResistance;
            Resistances[1] += BaseFireResistance;
            Resistances[2] += BaseColdResistance;
            Resistances[3] += BasePoisonResistance;
            Resistances[4] += BaseEnergyResistance;

            for (int i = 0; ResistanceMods != null && i < ResistanceMods.Count; ++i)
            {
                ResistanceMod mod = ResistanceMods[i];
                int v = (int)mod.Type;

                if (v >= 0 && v < Resistances.Length)
                {
                    Resistances[v] += mod.Offset;
                }
            }

            for (int i = 0; i < Items.Count; ++i)
            {
                Item item = Items[i];

                if (item.CheckPropertyConfliction(this))
                {
                    continue;
                }

                ISetItem setItem = item as ISetItem;

                Resistances[0] += setItem != null && setItem.SetEquipped ? setItem.SetResistBonus(ResistanceType.Physical) : item.PhysicalResistance;
                Resistances[1] += setItem != null && setItem.SetEquipped ? setItem.SetResistBonus(ResistanceType.Fire) : item.FireResistance;
                Resistances[2] += setItem != null && setItem.SetEquipped ? setItem.SetResistBonus(ResistanceType.Cold) : item.ColdResistance;
                Resistances[3] += setItem != null && setItem.SetEquipped ? setItem.SetResistBonus(ResistanceType.Poison) : item.PoisonResistance;
                Resistances[4] += setItem != null && setItem.SetEquipped ? setItem.SetResistBonus(ResistanceType.Energy) : item.EnergyResistance;
            }

            for (int i = 0; i < Resistances.Length; ++i)
            {
                int min = GetMinResistance((ResistanceType)i);
                int max = GetMaxResistance((ResistanceType)i);

                if (max < min)
                {
                    max = min;
                }

                if (Resistances[i] > max)
                {
                    Resistances[i] = max;
                }
                else if (Resistances[i] < min)
                {
                    Resistances[i] = min;
                }
            }
        }

        protected override void OnRaceChange(Race oldRace)
        {
            if (oldRace == Race.Gargoyle && Flying)
            {
                Flying = false;
                SendSpeedControl(SpeedControlType.Disable);
                BuffInfo.RemoveBuff(this, BuffIcon.Fly);
            }
            else if (oldRace != Race.Gargoyle && Race == Race.Gargoyle && Mounted)
            {
                Mount.Rider = null;
            }

            ValidateEquipment();
            UpdateResistances();
        }

        public override int MaxWeight
        {
            get
            {
                int baseCarryWeight = Config.Get("CarryWeight.BasePlayerCarryWeight", 40);

                if (Race == Race.Human)
                {
                    baseCarryWeight += Config.Get("CarryWeight.HumanBonusToCarryWeight", 60);

                }

                return baseCarryWeight + (int)(Config.Get("CarryWeight.CarryWeightPerStr", 3.5) * Str);
            }
        }

        private int m_LastGlobalLight = -1, m_LastPersonalLight = -1;

        public override void OnNetStateChanged()
        {
            m_LastGlobalLight = -1;
            m_LastPersonalLight = -1;
        }

        public override void ComputeBaseLightLevels(out int global, out int personal)
        {
            global = LightCycle.ComputeLevelFor(this);

            bool racialNightSight = Race == Race.Elf;

            if (LightLevel < 21 && (AosAttributes.GetValue(this, AosAttribute.NightSight) > 0 || racialNightSight))
            {
                personal = 21;
            }
            else
            {
                personal = LightLevel;
            }
        }

        public override void CheckLightLevels(bool forceResend)
        {
            NetState ns = NetState;

            if (ns == null)
            {
                return;
            }

            int global, personal;

            ComputeLightLevels(out global, out personal);

            if (!forceResend)
            {
                forceResend = (global != m_LastGlobalLight || personal != m_LastPersonalLight);
            }

            if (!forceResend)
            {
                return;
            }

            m_LastGlobalLight = global;
            m_LastPersonalLight = personal;

            ns.Send(GlobalLightLevel.Instantiate(global));
            ns.Send(new PersonalLightLevel(this, personal));
        }

        public override bool SendSpeedControl(SpeedControlType type)
        {
            AnimalFormContext context = AnimalForm.GetContext(this);

            if (context != null && context.SpeedBoost)
            {
                switch (type)
                {
                    case SpeedControlType.WalkSpeed: return base.SendSpeedControl(SpeedControlType.WalkSpeedFast);
                    case SpeedControlType.Disable: return base.SendSpeedControl(SpeedControlType.MountSpeed);
                }
            }

            return base.SendSpeedControl(type);
        }

        public override int GetMinResistance(ResistanceType type)
        {
            int magicResist = (int)(Skills[SkillName.MagicResist].Value * 10);
            int min = int.MinValue;

            if (magicResist >= 1000)
            {
                min = 40 + ((magicResist - 1000) / 50);
            }
            else if (magicResist >= 400)
            {
                min = (magicResist - 400) / 15;
            }

            return Math.Max(MinPlayerResistance, Math.Min(MaxPlayerResistance, min));
        }

        #region City Loyalty
        public override int GetResistance(ResistanceType type)
        {
            int resistance = base.GetResistance(type) + SphynxFortune.GetResistanceBonus(this, type);

            if (CityLoyaltySystem.HasTradeDeal(this, TradeDeal.SocietyOfClothiers))
            {
                resistance++;
                return Math.Min(resistance, GetMaxResistance(type));
            }

            return resistance;
        }
        #endregion

        public override void OnManaChange(int oldValue)
        {
            base.OnManaChange(oldValue);
            if (m_ExecutesLightningStrike > 0)
            {
                if (Mana < m_ExecutesLightningStrike)
                {
                    SpecialMove.ClearCurrentMove(this);
                }
            }
        }

        private static void OnLogin(LoginEventArgs e)
        {
            Mobile from = e.Mobile;

            CheckAtrophies(from);

            if (AccountHandler.LockdownLevel > AccessLevel.VIP)
            {
                string notice;

                Account acct = from.Account as Account;

                if (acct == null || !acct.HasAccess(from.NetState))
                {
                    if (from.IsPlayer())
                    {
                        notice = "The server is currently under lockdown. No players are allowed to log in at this time.";
                    }
                    else
                    {
                        notice = "The server is currently under lockdown. You do not have sufficient access level to connect.";
                    }

                    Timer.DelayCall(TimeSpan.FromSeconds(1.0), new TimerStateCallback(Disconnect), from);
                }
                else if (from.AccessLevel >= AccessLevel.Administrator)
                {
                    notice =
                        "The server is currently under lockdown. As you are an administrator, you may change this from the [Admin gump.";
                }
                else
                {
                    notice = "The server is currently under lockdown. You have sufficient access level to connect.";
                }

                from.SendGump(new NoticeGump(1060637, 30720, notice, 0xFFC000, 300, 140, null, null));
                return;
            }

            var pm = from as PlayerMobile;

            if (pm != null)
            {
                pm.ClaimAutoStabledPets();
                pm.ValidateEquipment();

                ReportMurdererGump.CheckMurderer(pm);
            }

            if (Siege.SiegeShard && from.Map == Map.Trammel && from.AccessLevel == AccessLevel.Player)
            {
                from.Map = Map.Felucca;
            }

            if (from.NetState != null && from.NetState.IsEnhancedClient && from.Mount is EtherealMount fromMount)
            {
                Timer.DelayCall(TimeSpan.FromSeconds(1), mount =>
                {
                    if (mount.IsChildOf(from.Backpack))
                    {
                        mount.Rider = from;
                    }
                },
                fromMount);
            }

            from.CheckStatTimers();
        }

        private bool m_NoDeltaRecursion;

        public void ValidateEquipment()
        {
            if (m_NoDeltaRecursion || Map == null || Map == Map.Internal)
            {
                return;
            }

            if (Items == null)
            {
                return;
            }

            m_NoDeltaRecursion = true;
            Timer.DelayCall(TimeSpan.Zero, ValidateEquipment_Sandbox);
        }

        private void ValidateEquipment_Sandbox()
        {
            try
            {
                if (Map == null || Map == Map.Internal)
                {
                    return;
                }

                List<Item> items = Items;

                if (items == null)
                {
                    return;
                }

                bool moved = false;

                int str = Str;
                int dex = Dex;
                int intel = Int;

                Mobile from = this;

                for (int i = items.Count - 1; i >= 0; --i)
                {
                    if (i >= items.Count)
                    {
                        continue;
                    }

                    Item item = items[i];
                    bool drop = false;

                    if (!RaceDefinitions.ValidateEquipment(from, item, false))
                    {
                        drop = true;
                    }

                    if (item is BaseWeapon weapon)
                    {
                        if (!drop)
                        {
                            if (dex < weapon.DexRequirement)
                            {
                                drop = true;
                            }
                            else if (str < AOS.Scale(weapon.StrRequirement, 100 - weapon.GetLowerStatReq()))
                            {
                                drop = true;
                            }
                            else if (intel < weapon.IntRequirement)
                            {
                                drop = true;
                            }
                        }

                        if (drop)
                        {
                            string name = weapon.Name;

                            if (name == null)
                            {
                                name = string.Format("#{0}", weapon.LabelNumber);
                            }

                            from.SendLocalizedMessage(1062001, name); // You can no longer wield your ~1_WEAPON~
                            from.AddToBackpack(weapon);
                            moved = true;
                        }
                    }
                    else if (item is BaseArmor armor)
                    {
                        if (!drop)
                        {
                            if (!armor.AllowMaleWearer && !from.Female && from.AccessLevel < AccessLevel.GameMaster)
                            {
                                drop = true;
                            }
                            else if (!armor.AllowFemaleWearer && from.Female && from.AccessLevel < AccessLevel.GameMaster)
                            {
                                drop = true;
                            }
                            else
                            {
                                int strBonus = armor.ComputeStatBonus(StatType.Str), strReq = armor.ComputeStatReq(StatType.Str);
                                int dexBonus = armor.ComputeStatBonus(StatType.Dex), dexReq = armor.ComputeStatReq(StatType.Dex);
                                int intBonus = armor.ComputeStatBonus(StatType.Int), intReq = armor.ComputeStatReq(StatType.Int);

                                if (dex < dexReq || (dex + dexBonus) < 1)
                                {
                                    drop = true;
                                }
                                else if (str < strReq || (str + strBonus) < 1)
                                {
                                    drop = true;
                                }
                                else if (intel < intReq || (intel + intBonus) < 1)
                                {
                                    drop = true;
                                }
                            }
                        }

                        if (drop)
                        {
                            string name = armor.Name;

                            if (name == null)
                            {
                                name = string.Format("#{0}", armor.LabelNumber);
                            }

                            if (armor is BaseShield)
                            {
                                from.SendLocalizedMessage(1062003, name); // You can no longer equip your ~1_SHIELD~
                            }
                            else
                            {
                                from.SendLocalizedMessage(1062002, name); // You can no longer wear your ~1_ARMOR~
                            }

                            from.AddToBackpack(armor);
                            moved = true;
                        }
                    }
                    else if (item is BaseClothing clothing)
                    {
                        if (!drop)
                        {
                            if (!clothing.AllowMaleWearer && !from.Female && from.AccessLevel < AccessLevel.GameMaster)
                            {
                                drop = true;
                            }
                            else if (!clothing.AllowFemaleWearer && from.Female && from.AccessLevel < AccessLevel.GameMaster)
                            {
                                drop = true;
                            }
                            else
                            {
                                int strBonus = clothing.ComputeStatBonus(StatType.Str);
                                int strReq = clothing.ComputeStatReq(StatType.Str);

                                if (str < strReq || (str + strBonus) < 1)
                                {
                                    drop = true;
                                }
                            }
                        }

                        if (drop)
                        {
                            string name = clothing.Name;

                            if (name == null)
                            {
                                name = string.Format("#{0}", clothing.LabelNumber);
                            }

                            from.SendLocalizedMessage(1062002, name); // You can no longer wear your ~1_ARMOR~

                            from.AddToBackpack(clothing);
                            moved = true;
                        }
                    }
                    else if (item is BaseQuiver && drop)
                    {
                        from.AddToBackpack(item);

                        from.SendLocalizedMessage(1062002, "quiver"); // You can no longer wear your ~1_ARMOR~
                        moved = true;
                    }

                    #region Vice Vs Virtue
                    IVvVItem vvvItem = item as IVvVItem;

                    if (vvvItem != null && vvvItem.IsVvVItem && !ViceVsVirtueSystem.IsVvV(from))
                    {
                        from.AddToBackpack(item);
                        moved = true;
                    }
                    #endregion
                }

                if (from.Mount is VvVMount && !ViceVsVirtueSystem.IsVvV(from))
                {
                    from.Mount.Rider = null;
                }

                if (moved)
                {
                    from.SendLocalizedMessage(500647); // Some equipment has been moved to your backpack.
                }
            }
            catch (Exception e)
            {
                Diagnostics.ExceptionLogging.LogException(e);
            }
            finally
            {
                m_NoDeltaRecursion = false;
            }
        }

        public override void Delta(MobileDelta flag)
        {
            base.Delta(flag);

            if ((flag & MobileDelta.Stat) != 0)
            {
                ValidateEquipment();
            }

            InvalidateProperties();
        }

        private static void Disconnect(object state)
        {
            NetState ns = ((Mobile)state).NetState;

            if (ns != null)
            {
                ns.Dispose();
            }
        }

        private static void OnLogout(LogoutEventArgs e)
        {
            PlayerMobile pm = e.Mobile as PlayerMobile;

            if (pm == null)
                return;

            #region Scroll of Alacrity
            if (pm.AcceleratedStart > DateTime.UtcNow)
            {
                pm.AcceleratedStart = DateTime.UtcNow;
                ScrollOfAlacrity.AlacrityEnd(pm);
            }
            #endregion

            BaseFamiliar.OnLogout(pm);

            BasketOfHerbs.CheckBonus(pm);

            BaseEscort.DeleteEscort(pm);
        }

        private static void EventSink_Connected(ConnectedEventArgs e)
        {
            PlayerMobile pm = e.Mobile as PlayerMobile;

            if (pm != null)
            {
                pm.m_SessionStart = DateTime.UtcNow;

                if (pm.m_Quest != null)
                {
                    pm.m_Quest.StartTimer();
                }

                #region Mondain's Legacy
                QuestHelper.StartTimer(pm);
                #endregion

                pm.BedrollLogout = false;
                pm.BlanketOfDarknessLogout = false;
                pm.LastOnline = DateTime.UtcNow;
            }

            DisguiseTimers.StartTimer(e.Mobile);

            Timer.DelayCall(TimeSpan.Zero, new TimerStateCallback(ClearSpecialMovesCallback), e.Mobile);
        }

        private static void ClearSpecialMovesCallback(object state)
        {
            Mobile from = (Mobile)state;

            SpecialMove.ClearAllMoves(from);
        }

        private static void EventSink_Disconnected(DisconnectedEventArgs e)
        {
            Mobile from = e.Mobile;
            DesignContext context = DesignContext.Find(from);

            if (context != null)
            {
                /* Client disconnected
                *  - Remove design context
                *  - Eject all from house
                *  - Restore relocated entities
                */
                // Remove design context
                DesignContext.Remove(from);

                // Eject all from house
                from.RevealingAction();

                foreach (Item item in context.Foundation.GetItems())
                {
                    item.Location = context.Foundation.BanLocation;
                }

                foreach (Mobile mobile in context.Foundation.GetMobiles())
                {
                    mobile.Location = context.Foundation.BanLocation;
                }

                // Restore relocated entities
                context.Foundation.RestoreRelocatedEntities();
            }

            PlayerMobile pm = e.Mobile as PlayerMobile;

            if (pm != null)
            {
                pm.m_GameTime += (DateTime.UtcNow - pm.m_SessionStart);

                if (pm.m_Quest != null)
                {
                    pm.m_Quest.StopTimer();
                }

                QuestHelper.StopTimer(pm);

                pm.m_SpeechLog = null;
                pm.LastOnline = DateTime.UtcNow;

                pm.AutoStablePets();
            }

            DisguiseTimers.StopTimer(from);
        }

        public override void RevealingAction()
        {
            if (m_DesignContext != null)
            {
                return;
            }

            InvisibilitySpell.RemoveTimer(this);

            base.RevealingAction();
        }

        public override void OnHiddenChanged()
        {
            base.OnHiddenChanged();

            RemoveBuff(BuffIcon.Invisibility);
            //Always remove, default to the hiding icon EXCEPT in the invis spell where it's explicitly set

            if (!Hidden)
            {
                RemoveBuff(BuffIcon.HidingAndOrStealth);
            }
            else // if( !InvisibilitySpell.HasTimer( this ) )
            {
                BuffInfo.AddBuff(this, new BuffInfo(BuffIcon.HidingAndOrStealth, 1075655)); //Hidden/Stealthing & You Are Hidden
            }
        }

        public override void OnSubItemAdded(Item item)
        {
            if (AccessLevel < AccessLevel.GameMaster && item.IsChildOf(Backpack))
            {
                int curWeight = BodyWeight + TotalWeight;

                if (curWeight > MaxWeight)
                {
                    SendLocalizedMessage(1019035, true, string.Format(" : {0} / {1}", curWeight, MaxWeight));
                }
            }
        }

        public override void OnSubItemRemoved(Item item)
        {
            if (Engines.UOStore.UltimaStore.HasPendingItem(this))
                Timer.DelayCall(TimeSpan.FromSeconds(1.5), Engines.UOStore.UltimaStore.CheckPendingItem, this);
        }

        public override void AggressiveAction(Mobile aggressor, bool criminal)
        {
            // This will update aggressor for the aggressors master
            if (aggressor is BaseCreature creature && creature.ControlMaster != null && creature.ControlMaster != this)
            {
                Mobile aggressiveMaster = creature.ControlMaster;

                // First lets find out if the creatures master is in our aggressor list
                AggressorInfo info = Aggressors.FirstOrDefault(i => i.Attacker == aggressiveMaster);

                if (info != null)
                {
                    // already in the list, so we're refreshing it
                    info.Refresh();
                    info.CriminalAggression = criminal;
                }
                else
                {
                    // not in the list, so we're adding it
                    Aggressors.Add(AggressorInfo.Create(aggressiveMaster, this, criminal));

                    if (CanSee(aggressiveMaster) && NetState != null)
                    {
                        NetState.Send(MobileIncoming.Create(NetState, this, aggressiveMaster));
                    }

                    UpdateAggrExpire();
                }

                // Now, if I am in the creatures master aggressor list, it needs to be refreshed
                info = aggressiveMaster.Aggressors.FirstOrDefault(i => i.Attacker == this);

                if (info != null)
                {
                    info.Refresh();
                }

                info = Aggressed.FirstOrDefault(i => i.Defender == aggressiveMaster);

                if (info != null)
                {
                    info.Refresh();
                }

                // next lets find out if we're on the creatures master aggressed list
                info = aggressiveMaster.Aggressed.FirstOrDefault(i => i.Defender == this);

                if (info != null)
                {
                    // already in the list, so we're refreshing it
                    info.Refresh();
                    info.CriminalAggression = criminal;
                }
                else
                {
                    // not in the list, so we're adding it
                    creature.Aggressed.Add(AggressorInfo.Create(aggressiveMaster, this, criminal));

                    if (CanSee(aggressiveMaster) && NetState != null)
                    {
                        NetState.Send(MobileIncoming.Create(NetState, this, aggressiveMaster));
                    }

                    UpdateAggrExpire();
                }

                if (aggressiveMaster is PlayerMobile || (aggressiveMaster is BaseCreature bc && !bc.IsMonster))
                {
                    BuffInfo.AddBuff(this, new BuffInfo(BuffIcon.HeatOfBattleStatus, 1153801, 1153827, Aggression.CombatHeatDelay, this, true));
                    BuffInfo.AddBuff(aggressiveMaster, new BuffInfo(BuffIcon.HeatOfBattleStatus, 1153801, 1153827, Aggression.CombatHeatDelay, aggressiveMaster, true));
                }
            }

            base.AggressiveAction(aggressor, criminal);
        }

        public override void DoHarmful(IDamageable damageable, bool indirect)
        {
            base.DoHarmful(damageable, indirect);

            if (ViceVsVirtueSystem.Enabled && (ViceVsVirtueSystem.EnhancedRules || Map == ViceVsVirtueSystem.Facet) && damageable is Mobile mobile)
            {
                ViceVsVirtueSystem.CheckHarmful(this, mobile);
            }
        }

        public override void DoBeneficial(Mobile target)
        {
            base.DoBeneficial(target);

            if (ViceVsVirtueSystem.Enabled && (ViceVsVirtueSystem.EnhancedRules || Map == ViceVsVirtueSystem.Facet) && target != null)
            {
                ViceVsVirtueSystem.CheckBeneficial(this, target);
            }
        }

        public override bool CanBeHarmful(IDamageable damageable, bool message, bool ignoreOurBlessedness, bool ignorePeaceCheck)
        {
            Mobile target = damageable as Mobile;

            if (m_DesignContext != null || (target is PlayerMobile mobile && mobile.m_DesignContext != null))
            {
                return false;
            }

            if ((target is BaseVendor vendor && vendor.IsInvulnerable) || target is PlayerVendor || target is TownCrier)
            {
                if (message)
                {
                    if (target.Title == null)
                    {
                        SendMessage("{0} the vendor cannot be harmed.", target.Name);
                    }
                    else
                    {
                        SendMessage("{0} {1} cannot be harmed.", target.Name, target.Title);
                    }
                }

                return false;
            }

            if (damageable is IDamageableItem item && !item.CanDamage)
            {
                if (message)
                    SendMessage("That cannot be harmed.");

                return false;
            }

            return base.CanBeHarmful(damageable, message, ignoreOurBlessedness, ignorePeaceCheck);
        }

        public override bool CanBeBeneficial(Mobile target, bool message, bool allowDead)
        {
            if (m_DesignContext != null || target is PlayerMobile pm && pm.m_DesignContext != null)
            {
                return false;
            }

            return base.CanBeBeneficial(target, message, allowDead);
        }

        public override bool CheckContextMenuDisplay(IEntity target)
        {
            return (m_DesignContext == null);
        }

        public override void OnItemAdded(Item item)
        {
            base.OnItemAdded(item);

            if (item is BaseArmor || item is BaseWeapon)
            {
                Hits = Hits;
                Stam = Stam;
                Mana = Mana;
            }

            if (NetState != null)
            {
                CheckLightLevels(false);
            }
        }

        private BaseWeapon m_LastWeapon;

        [CommandProperty(AccessLevel.GameMaster)]
        public BaseWeapon LastWeapon { get { return m_LastWeapon; } set { m_LastWeapon = value; } }

        public override void OnItemRemoved(Item item)
        {
            base.OnItemRemoved(item);

            if (item is BaseArmor || item is BaseWeapon)
            {
                Hits = Hits;
                Stam = Stam;
                Mana = Mana;
            }

            if (item is BaseWeapon weapon)
            {
                m_LastWeapon = weapon;
            }

            if (NetState != null)
            {
                CheckLightLevels(false);
            }
        }

        #region [Stats]Max
        [CommandProperty(AccessLevel.GameMaster)]
        public override int HitsMax
        {
            get
            {
                int strBase;
                int strOffs = GetStatOffset(StatType.Str);

                strBase = Str; //Str already includes GetStatOffset/str
                strOffs = AosAttributes.GetValue(this, AosAttribute.BonusHits);

                if (strOffs > 2500 && IsPlayer())
                {
                    strOffs = 2500;
                }

                if (AnimalForm.UnderTransformation(this, typeof(BakeKitsune)) ||
                    AnimalForm.UnderTransformation(this, typeof(GreyWolf)))
                {
                    strOffs += 2000;
                }

                // Skill Masteries
                strOffs += ToughnessSpell.GetHPBonus(this);
                strOffs += InvigorateSpell.GetHPBonus(this);

                return (strBase / 2) + 50 + strOffs;
            }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public override int StamMax => base.StamMax + AosAttributes.GetValue(this, AosAttribute.BonusStam);

        [CommandProperty(AccessLevel.GameMaster)]
        public override int ManaMax => base.ManaMax + AosAttributes.GetValue(this, AosAttribute.BonusMana) + (Race == Race.Elf ? 20 : 0) + MasteryInfo.IntuitionBonus(this) + UraliTranceTonic.GetManaBuff(this);
        #endregion

        #region Stat Getters/Setters
        [CommandProperty(AccessLevel.GameMaster)]
        public override int Str
        {
            get
            {
                if (IsPlayer())
                {
                    return Math.Min(base.Str, StrMaxCap);
                }

                return base.Str;
            }
            set { base.Str = value; }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public override int Int
        {
            get
            {
                if (IsPlayer())
                {
                    return Math.Min(base.Int, IntMaxCap);
                }

                return base.Int;
            }
            set { base.Int = value; }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public override int Dex
        {
            get
            {
                if (IsPlayer())
                {
                    int dex = base.Dex;

                    return Math.Min(dex, DexMaxCap);
                }

                return base.Dex;
            }
            set { base.Dex = value; }
        }
        #endregion

        public long NextPassiveDetectHidden { get; set; }

        public override bool Move(Direction d)
        {
            NetState ns = NetState;

            if (ns != null)
            {
                if (HasGump(typeof(ResurrectGump)))
                {
                    if (Alive)
                    {
                        CloseGump(typeof(ResurrectGump));
                    }
                    else
                    {
                        SendLocalizedMessage(500111); // You are frozen and cannot move.
                        return false;
                    }
                }
            }

            int speed = ComputeMovementSpeed(d);

            bool result = base.Move(d);

            if (result && !Siege.SiegeShard && Core.TickCount - NextPassiveDetectHidden >= 0)
            {
                DetectHidden.DoPassiveDetect(this);
                NextPassiveDetectHidden = Core.TickCount + (int)TimeSpan.FromSeconds(2).TotalMilliseconds;
            }

            m_NextMovementTime += speed;

            return result;
        }

        public override bool CheckMovement(Direction d, out int newZ)
        {
            DesignContext context = m_DesignContext;

            if (context == null)
            {
                bool check = base.CheckMovement(d, out newZ);

                if (check && VvVSigil.ExistsOn(this, true) && !VvVSigil.CheckMovement(this, d))
                {
                    SendLocalizedMessage(1155414); // You may not remove the sigil from the battle region!
                    return false;
                }

                return check;
            }

            HouseFoundation foundation = context.Foundation;

            newZ = foundation.Z + HouseFoundation.GetLevelZ(context.Level, context.Foundation);

            int newX = X, newY = Y;
            Movement.Movement.Offset(d, ref newX, ref newY);

            int startX = foundation.X + foundation.Components.Min.X + 1;
            int startY = foundation.Y + foundation.Components.Min.Y + 1;
            int endX = startX + foundation.Components.Width - 1;
            int endY = startY + foundation.Components.Height - 2;

            return (newX >= startX && newY >= startY && newX < endX && newY < endY && Map == foundation.Map);
        }

        public override void OnHitsChange(int oldValue)
        {
            if (Race == Race.Gargoyle)
            {
                if (Hits <= HitsMax / 2)
                {
                    BuffInfo.AddBuff(this, new BuffInfo(BuffIcon.Berserk, 1080449, 1115021, string.Format("{0}\t{1}", GetRacialBerserkBuff(false), GetRacialBerserkBuff(true)), false));
                    Delta(MobileDelta.WeaponDamage);
                }
                else if (oldValue < Hits && Hits > HitsMax / 2)
                {
                    BuffInfo.RemoveBuff(this, BuffIcon.Berserk);
                    Delta(MobileDelta.WeaponDamage);
                }
            }

            base.OnHitsChange(oldValue);
        }

        /// <summary>
        /// Returns Racial Berserk value, for spell or melee
        /// </summary>
        /// <param name="spell">true for spell damage, false for damage increase (melee)</param>
        /// <returns></returns>
        public virtual int GetRacialBerserkBuff(bool spell)
        {
            if (Race != Race.Gargoyle || Hits > HitsMax / 2)
                return 0;

            double perc = (Hits / (double)HitsMax) * 100;
            int value = 0;

            perc = (100 - perc) / 20;

            if (perc > 4)
                value += spell ? 12 : 60;
            else if (perc >= 3)
                value += spell ? 9 : 45;
            else if (perc >= 2)
                value += spell ? 6 : 30;
            else if (perc >= 1)
                value += spell ? 3 : 15;

            return value;
        }

        public override void OnHeal(ref int amount, Mobile from)
        {
            base.OnHeal(ref amount, from);

            if (from == null)
                return;

            BestialSetHelper.OnHeal(this, from, ref amount);

            if (amount > 0 && from != this)
            {
                for (int i = Aggressed.Count - 1; i >= 0; i--)
                {
                    AggressorInfo info = Aggressed[i];

                    if (info.Defender.InRange(Location, Core.GlobalMaxUpdateRange) && info.Defender.DamageEntries.Any(de => de.Damager == this))
                    {
                        info.Defender.RegisterDamage(amount, from);
                    }

                    if (info.Defender.Player && from.CanBeHarmful(info.Defender, false))
                    {
                        from.DoHarmful(info.Defender, true);
                    }
                }

                for (int i = Aggressors.Count - 1; i >= 0; i--)
                {
                    AggressorInfo info = Aggressors[i];

                    if (info.Attacker.InRange(Location, Core.GlobalMaxUpdateRange) && info.Attacker.DamageEntries.Any(de => de.Damager == this))
                    {
                        info.Attacker.RegisterDamage(amount, from);
                    }

                    if (info.Attacker.Player && from.CanBeHarmful(info.Attacker, false))
                    {
                        from.DoHarmful(info.Attacker, true);
                    }
                }
            }
        }

        public override bool AllowItemUse(Item item)
        {
            return DesignContext.Check(this);
        }

        public SkillName[] AnimalFormRestrictedSkills => m_AnimalFormRestrictedSkills;

        private readonly SkillName[] m_AnimalFormRestrictedSkills = new[]
        {
            SkillName.ArmsLore, SkillName.Begging, SkillName.Discordance, SkillName.Forensics, SkillName.Inscribe,
            SkillName.ItemID, SkillName.Meditation, SkillName.Peacemaking, SkillName.Provocation, SkillName.RemoveTrap,
            SkillName.SpiritSpeak, SkillName.Stealing, SkillName.TasteID
        };

        public override bool AllowSkillUse(SkillName skill)
        {
            if (AnimalForm.UnderTransformation(this))
            {
                for (int i = 0; i < m_AnimalFormRestrictedSkills.Length; i++)
                {
                    if (m_AnimalFormRestrictedSkills[i] == skill)
                    {
                        #region Mondain's Legacy
                        AnimalFormContext context = AnimalForm.GetContext(this);

                        if (skill == SkillName.Stealing && context.StealingMod != null && context.StealingMod.Value > 0)
                        {
                            continue;
                        }
                        #endregion

                        SendLocalizedMessage(1070771); // You cannot use that skill in this form.
                        return false;
                    }
                }
            }

            return DesignContext.Check(this);
        }

        private bool m_LastProtectedMessage;
        private int m_NextProtectionCheck = 10;

        public virtual void RecheckTownProtection()
        {
            m_NextProtectionCheck = 10;

            GuardedRegion reg = (GuardedRegion)Region.GetRegion(typeof(GuardedRegion));
            bool isProtected = (reg != null && !reg.IsDisabled());

            if (isProtected != m_LastProtectedMessage)
            {
                if (isProtected)
                {
                    SendLocalizedMessage(500112); // You are now under the protection of the town guards.
                }
                else
                {
                    SendLocalizedMessage(500113); // You have left the protection of the town guards.
                }

                m_LastProtectedMessage = isProtected;
            }
        }

        public override void MoveToWorld(Point3D loc, Map map)
        {
            base.MoveToWorld(loc, map);

            RecheckTownProtection();
        }

        public override void SetLocation(Point3D loc, bool isTeleport)
        {
            if (!isTeleport && IsPlayer() && !Flying)
            {
                // moving, not teleporting
                int zDrop = (Location.Z - loc.Z);

                if (zDrop > 20) // we fell more than one story
                {
                    Hits -= ((zDrop / 20) * 10) - 5; // deal some damage; does not kill, disrupt, etc
                    SendMessage("Ouch!");
                }
            }

            base.SetLocation(loc, isTeleport);

            if (isTeleport || --m_NextProtectionCheck == 0)
            {
                RecheckTownProtection();
            }
        }

        public override void GetContextMenuEntries(Mobile from, List<ContextMenuEntry> list)
        {
            list.Add(new PaperdollEntry(this));

            if (from == this)
            {
                #region TOL Shadowguard
                if (ShadowguardController.GetInstance(Location, Map) != null)
                {
                    list.Add(new ExitEntry(this));
                }
                #endregion

                if (Alive)
                {
                    list.Add(new SearchVendors(this));
                }

                BaseHouse house = BaseHouse.FindHouseAt(this);

                if (house != null)
                {
                    if (house.IsCoOwner(this))
                    {
                        list.Add(new CallbackEntry(6205, ReleaseCoOwnership));
                    }
                }

                list.Add(new TitlesMenuEntry(this));

                if (Alive)
                {
                    list.Add(new Engines.Points.LoyaltyRating(this));
                }

                list.Add(new OpenBackpackEntry(this));

                if (Alive && InsuranceEnabled)
                {
                    list.Add(new CallbackEntry(1114299, OpenItemInsuranceMenu));
                    list.Add(new CallbackEntry(6201, ToggleItemInsurance));
                }
                else if (Siege.SiegeShard)
                {
                    list.Add(new CallbackEntry(3006168, SiegeBlessItem));
                }

                if (Alive)
                {
                    QuestHelper.GetContextMenuEntries(list);
                }

                if (m_Quest != null)
                {
                    m_Quest.GetContextMenuEntries(list);
                }

                if (house != null)
                {
                    if (Alive && house.InternalizedVendors.Count > 0 && house.IsOwner(this))
                    {
                        list.Add(new CallbackEntry(6204, GetVendor));
                    }

                    list.Add(new CallbackEntry(6207, LeaveHouse));
                }

                list.Add(new CallbackEntry(RefuseTrades ? 1154112 : 1154113, ToggleTrades)); // Allow Trades / Refuse Trades               

                if (m_JusticeProtectors.Count > 0)
                {
                    list.Add(new CallbackEntry(6157, CancelProtection));
                }

                #region Void Pool
                if (VoidPool || Region.IsPartOf<VoidPoolRegion>())
                {
                    VoidPoolController controller = Map == Map.Felucca ? VoidPoolController.InstanceFel : VoidPoolController.InstanceTram;

                    if (controller != null)
                    {
                        if (!VoidPool)
                        {
                            VoidPool = true;
                        }

                        list.Add(new VoidPoolInfo(this, controller));
                    }
                }
                #endregion

                if (DisabledPvpWarning)
                {
                    list.Add(new CallbackEntry(1113797, EnablePvpWarning));
                }
            }
            else
            {
                BaseGalleon galleon = BaseGalleon.FindGalleonAt(from.Location, from.Map);

                if (galleon != null && galleon.IsOwner(from))
                    list.Add(new ShipAccessEntry(this, from, galleon));

                if (Alive)
                {
                    Party theirParty = from.Party as Party;
                    Party ourParty = Party as Party;

                    if (theirParty == null && ourParty == null)
                    {
                        list.Add(new AddToPartyEntry(from, this));
                    }
                    else if (theirParty != null && theirParty.Leader == from)
                    {
                        if (ourParty == null)
                        {
                            list.Add(new AddToPartyEntry(from, this));
                        }
                        else if (ourParty == theirParty)
                        {
                            list.Add(new RemoveFromPartyEntry(from, this));
                        }
                    }
                }

                if (from.InRange(this, 10))
                {
                    list.Add(new CallbackEntry(1077728, () => OpenTrade(from))); // Trade
                }

                if (Alive && EjectPlayerEntry.CheckAccessible(from, this))
                {
                    list.Add(new EjectPlayerEntry(from, this));
                }
            }
        }

        private void CancelProtection()
        {
            for (int i = 0; i < m_JusticeProtectors.Count; ++i)
            {
                Mobile prot = m_JusticeProtectors[i];

                string args = string.Format("{0}\t{1}", Name, prot.Name);

                prot.SendLocalizedMessage(1049371, args);
                // The protective relationship between ~1_PLAYER1~ and ~2_PLAYER2~ has been ended.
                SendLocalizedMessage(1049371, args);
                // The protective relationship between ~1_PLAYER1~ and ~2_PLAYER2~ has been ended.
            }

            m_JusticeProtectors.Clear();
        }

        #region Insurance
        private void ToggleItemInsurance()
        {
            if (!CheckAlive())
            {
                return;
            }

            BeginTarget(-1, false, TargetFlags.None, ToggleItemInsurance_Callback);
            SendLocalizedMessage(1060868); // Target the item you wish to toggle insurance status on <ESC> to cancel
        }

        private bool CanInsure(Item item)
        {
            if (item is BaseQuiver && item.LootType == LootType.Regular)
            {
                return true;
            }

            if (((item is Container) && !(item is BaseQuiver)) || item is BagOfSending || item is KeyRing || item is MountItem)
            {
                return false;
            }

            if ((item is Spellbook && item.LootType == LootType.Blessed) || item is Runebook || item is PotionKeg ||
                item is VvVSigil)
            {
                return false;
            }

            if (item is BaseBalmOrLotion || item is GemOfSalvation || item is SeedOfLife || item is ManaDraught)
            {
                return false;
            }

            if (item.Stackable)
            {
                return false;
            }

            if (item.LootType == LootType.Cursed)
            {
                return false;
            }

            if (item.ItemID == 0x204E) // death shroud
            {
                return false;
            }

            if (item.LootType == LootType.Blessed)
                return false;

            return true;
        }

        private void ToggleItemInsurance_Callback(Mobile from, object obj)
        {
            if (!CheckAlive())
                return;

            ToggleItemInsurance_Callback(from, obj as Item, true);
        }

        private void ToggleItemInsurance_Callback(Mobile from, Item item, bool target)
        {
            if (item == null || !item.IsChildOf(this))
            {
                if (target)
                    BeginTarget(-1, false, TargetFlags.None, ToggleItemInsurance_Callback);

                SendLocalizedMessage(1060871, "", 0x23); // You can only insure items that you have equipped or that are in your backpack
            }
            else if (item.Insured)
            {
                item.Insured = false;

                SendLocalizedMessage(1060874, "", 0x35); // You cancel the insurance on the item

                if (target)
                {
                    BeginTarget(-1, false, TargetFlags.None, ToggleItemInsurance_Callback);
                    SendLocalizedMessage(1060868, "", 0x23); // Target the item you wish to toggle insurance status on <ESC> to cancel
                }
            }
            else if (!CanInsure(item))
            {
                if (target)
                    BeginTarget(-1, false, TargetFlags.None, ToggleItemInsurance_Callback);

                SendLocalizedMessage(1060869, "", 0x23); // You cannot insure that
            }
            else
            {
                if (!item.PayedInsurance)
                {
                    int cost = GetInsuranceCost(item);

                    if (Banker.Withdraw(from, cost))
                    {
                        SendLocalizedMessage(1060398, cost.ToString()); // ~1_AMOUNT~ gold has been withdrawn from your bank box.
                        item.PayedInsurance = true;
                    }
                    else
                    {
                        SendLocalizedMessage(1061079, "", 0x23); // You lack the funds to purchase the insurance
                        return;
                    }
                }

                item.Insured = true;

                SendLocalizedMessage(1060873, "", 0x23); // You have insured the item

                if (target)
                {
                    BeginTarget(-1, false, TargetFlags.None, ToggleItemInsurance_Callback);
                    SendLocalizedMessage(1060868, "", 0x23); // Target the item you wish to toggle insurance status on <ESC> to cancel
                }
            }
        }

        public int GetInsuranceCost(Item item)
        {
            int imbueWeight = Imbuing.GetTotalWeight(item, -1, false, false);
            int cost = 600; // this handles old items, set items, etc

            if (item is IVvVItem vItem && vItem.IsVvVItem)
                cost = 800;
            else if (imbueWeight > 0)
                cost = Math.Min(800, Math.Max(10, imbueWeight));
            else if (GenericBuyInfo.BuyPrices.ContainsKey(item.GetType()))
                cost = Math.Min(800, Math.Max(10, GenericBuyInfo.BuyPrices[item.GetType()]));
            else if (item.LootType == LootType.Newbied)
                cost = 10;

            NegativeAttributes negAttrs = RunicReforging.GetNegativeAttributes(item);

            if (negAttrs != null && negAttrs.Prized > 0)
                cost *= 2;

            if (Region != null)
                cost = (int)(cost * Region.InsuranceMultiplier);

            return cost;
        }

        private void AutoRenewInventoryInsurance()
        {
            if (!CheckAlive())
            {
                return;
            }

            SendLocalizedMessage(1060881, "", 0x23); // You have selected to automatically reinsure all insured items upon death
            AutoRenewInsurance = true;
        }

        #region Siege Bless Item
        private Item _BlessedItem;

        [CommandProperty(AccessLevel.GameMaster)]
        public Item BlessedItem { get { return _BlessedItem; } set { _BlessedItem = value; } }

        private void SiegeBlessItem()
        {
            if (_BlessedItem != null && _BlessedItem.Deleted)
                _BlessedItem = null;

            BeginTarget(2, false, TargetFlags.None, (from, targeted) =>
            {
                Siege.TryBlessItem(this, targeted);
            });
        }

        public override bool Drop(Point3D loc)
        {
            if (!Siege.SiegeShard || _BlessedItem == null)
                return base.Drop(loc);

            Item item = Holding;
            bool drop = base.Drop(loc);

            if (item != null && drop && item.Parent == null && _BlessedItem != null && _BlessedItem == item)
            {
                _BlessedItem = null;
                item.LootType = LootType.Regular;

                SendLocalizedMessage(1075292, item.Name != null ? item.Name : "#" + item.LabelNumber.ToString()); // ~1_NAME~ has been unblessed.
            }

            return drop;
        }
        #endregion

        private class CancelRenewInventoryInsuranceGump : Gump
        {
            private readonly PlayerMobile m_Player;
            private readonly ItemInsuranceMenuGump m_InsuranceGump;

            public CancelRenewInventoryInsuranceGump(PlayerMobile player, ItemInsuranceMenuGump insuranceGump)
                : base(250, 200)
            {
                m_Player = player;
                m_InsuranceGump = insuranceGump;

                AddBackground(0, 0, 240, 142, 0x13BE);
                AddImageTiled(6, 6, 228, 100, 0xA40);
                AddImageTiled(6, 116, 228, 20, 0xA40);
                AddAlphaRegion(6, 6, 228, 142);

                AddHtmlLocalized(8, 8, 228, 100, 1071021, 0x7FFF, false, false);
                // You are about to disable inventory insurance auto-renewal.

                AddButton(6, 116, 0xFB1, 0xFB2, 0, GumpButtonType.Reply, 0);
                AddHtmlLocalized(40, 118, 450, 20, 1060051, 0x7FFF, false, false); // CANCEL

                AddButton(114, 116, 0xFA5, 0xFA7, 1, GumpButtonType.Reply, 0);
                AddHtmlLocalized(148, 118, 450, 20, 1071022, 0x7FFF, false, false); // DISABLE IT!
            }

            public override void OnResponse(NetState sender, RelayInfo info)
            {
                if (!m_Player.CheckAlive())
                {
                    return;
                }

                if (info.ButtonID == 1)
                {
                    m_Player.SendLocalizedMessage(1061075, "", 0x23);
                    // You have cancelled automatically reinsuring all insured items upon death
                    m_Player.AutoRenewInsurance = false;
                }
                else
                {
                    m_Player.SendLocalizedMessage(1042021); // Cancelled.
                }

                if (m_InsuranceGump != null)
                    m_Player.SendGump(m_InsuranceGump.NewInstance());
            }
        }

        private void OpenItemInsuranceMenu()
        {
            if (!CheckAlive())
                return;

            List<Item> items = new List<Item>();

            foreach (Item item in Items)
            {
                if (DisplayInItemInsuranceGump(item))
                    items.Add(item);
            }

            Container pack = Backpack;

            if (pack != null)
                items.AddRange(pack.FindItemsByType<Item>(true, DisplayInItemInsuranceGump));

            // TODO: Investigate item sorting

            CloseGump(typeof(ItemInsuranceMenuGump));

            if (items.Count == 0)
                SendLocalizedMessage(1114915, "", 0x35); // None of your current items meet the requirements for insurance.
            else
                SendGump(new ItemInsuranceMenuGump(this, items.ToArray()));
        }

        private bool DisplayInItemInsuranceGump(Item item)
        {
            if (item.Parent is LockableContainer container && container.Locked)
                return false;

            return (item.Visible || AccessLevel >= AccessLevel.GameMaster) && (item.Insured || CanInsure(item));
        }

        private class ItemInsuranceMenuGump : Gump
        {
            private readonly PlayerMobile m_From;
            private readonly Item[] m_Items;
            private readonly bool[] m_Insure;
            private readonly int m_Page;

            public ItemInsuranceMenuGump(PlayerMobile from, Item[] items)
                : this(from, items, null, 0)
            {
            }

            public ItemInsuranceMenuGump(PlayerMobile from, Item[] items, bool[] insure, int page)
                : base(25, 50)
            {
                m_From = from;
                m_Items = items;

                if (insure == null)
                {
                    insure = new bool[items.Length];

                    for (int i = 0; i < items.Length; ++i)
                        insure[i] = items[i].Insured;
                }

                m_Insure = insure;
                m_Page = page;

                AddPage(0);

                AddBackground(0, 0, 520, 510, 0x13BE);
                AddImageTiled(10, 10, 500, 30, 0xA40);
                AddImageTiled(10, 50, 500, 355, 0xA40);
                AddImageTiled(10, 415, 500, 80, 0xA40);
                AddAlphaRegion(10, 10, 500, 485);

                AddButton(15, 470, 0xFB1, 0xFB2, 0, GumpButtonType.Reply, 0);
                AddHtmlLocalized(50, 472, 80, 20, 1011012, 0x7FFF, false, false); // CANCEL

                if (from.AutoRenewInsurance)
                    AddButton(360, 10, 9723, 9724, 1, GumpButtonType.Reply, 0);
                else
                    AddButton(360, 10, 9720, 9722, 1, GumpButtonType.Reply, 0);

                AddHtmlLocalized(395, 14, 105, 20, 1114122, 0x7FFF, false, false); // AUTO REINSURE

                AddButton(395, 470, 0xFA5, 0xFA6, 2, GumpButtonType.Reply, 0);
                AddHtmlLocalized(430, 472, 50, 20, 1006044, 0x7FFF, false, false); // OK

                AddHtmlLocalized(10, 14, 150, 20, 1114121, 0x7FFF, false, false); // <CENTER>ITEM INSURANCE MENU</CENTER>

                AddHtmlLocalized(45, 54, 70, 20, 1062214, 0x7FFF, false, false); // Item
                AddHtmlLocalized(250, 54, 70, 20, 1061038, 0x7FFF, false, false); // Cost
                AddHtmlLocalized(400, 54, 70, 20, 1114311, 0x7FFF, false, false); // Insured

                int balance = Banker.GetBalance(from);
                int cost = 0;

                for (int i = 0; i < items.Length; ++i)
                {
                    if (insure[i])
                        cost += m_From.GetInsuranceCost(items[i]);
                }

                AddHtmlLocalized(15, 420, 300, 20, 1114310, 0x7FFF, false, false); // GOLD AVAILABLE:
                AddLabel(215, 420, 0x481, balance.ToString());
                AddHtmlLocalized(15, 435, 300, 20, 1114123, 0x7FFF, false, false); // TOTAL COST OF INSURANCE:
                AddLabel(215, 435, 0x481, cost.ToString());

                if (cost != 0)
                {
                    AddHtmlLocalized(15, 450, 300, 20, 1114125, 0x7FFF, false, false); // NUMBER OF DEATHS PAYABLE:
                    AddLabel(215, 450, 0x481, (balance / cost).ToString());
                }

                for (int i = page * 4, y = 72; i < (page + 1) * 4 && i < items.Length; ++i, y += 75)
                {
                    Item item = items[i];
                    Rectangle2D b = ItemBounds.Table[item.ItemID];

                    AddImageTiledButton(40, y, 0x918, 0x918, 0, GumpButtonType.Page, 0, item.ItemID, item.Hue, 40 - b.Width / 2 - b.X, 30 - b.Height / 2 - b.Y);
                    AddItemProperty(item.Serial);

                    if (insure[i])
                    {
                        AddButton(400, y, 9723, 9724, 100 + i, GumpButtonType.Reply, 0);
                        AddLabel(250, y, 0x481, m_From.GetInsuranceCost(item).ToString());
                    }
                    else
                    {
                        AddButton(400, y, 9720, 9722, 100 + i, GumpButtonType.Reply, 0);
                        AddLabel(250, y, 0x66C, m_From.GetInsuranceCost(item).ToString());
                    }
                }

                if (page >= 1)
                {
                    AddButton(15, 380, 0xFAE, 0xFAF, 3, GumpButtonType.Reply, 0);
                    AddHtmlLocalized(50, 380, 450, 20, 1044044, 0x7FFF, false, false); // PREV PAGE
                }

                if ((page + 1) * 4 < items.Length)
                {
                    AddButton(400, 380, 0xFA5, 0xFA7, 4, GumpButtonType.Reply, 0);
                    AddHtmlLocalized(435, 380, 70, 20, 1044045, 0x7FFF, false, false); // NEXT PAGE
                }
            }

            public ItemInsuranceMenuGump NewInstance()
            {
                return new ItemInsuranceMenuGump(m_From, m_Items, m_Insure, m_Page);
            }

            public override void OnResponse(NetState sender, RelayInfo info)
            {
                if (info.ButtonID == 0 || !m_From.CheckAlive())
                    return;

                switch (info.ButtonID)
                {
                    case 1: // Auto Reinsure
                        {
                            if (m_From.AutoRenewInsurance)
                            {
                                if (!m_From.HasGump(typeof(CancelRenewInventoryInsuranceGump)))
                                    m_From.SendGump(new CancelRenewInventoryInsuranceGump(m_From, this));
                            }
                            else
                            {
                                m_From.AutoRenewInventoryInsurance();
                                m_From.SendGump(new ItemInsuranceMenuGump(m_From, m_Items, m_Insure, m_Page));
                            }

                            break;
                        }
                    case 2: // OK
                        {
                            m_From.SendGump(new ItemInsuranceMenuConfirmGump(m_From, m_Items, m_Insure, m_Page));

                            break;
                        }
                    case 3: // Prev
                        {
                            if (m_Page >= 1)
                                m_From.SendGump(new ItemInsuranceMenuGump(m_From, m_Items, m_Insure, m_Page - 1));

                            break;
                        }
                    case 4: // Next
                        {
                            if ((m_Page + 1) * 4 < m_Items.Length)
                                m_From.SendGump(new ItemInsuranceMenuGump(m_From, m_Items, m_Insure, m_Page + 1));

                            break;
                        }
                    default:
                        {
                            int idx = info.ButtonID - 100;

                            if (idx >= 0 && idx < m_Items.Length)
                                m_Insure[idx] = !m_Insure[idx];

                            m_From.SendGump(new ItemInsuranceMenuGump(m_From, m_Items, m_Insure, m_Page));

                            break;
                        }
                }
            }
        }

        private class ItemInsuranceMenuConfirmGump : Gump
        {
            private readonly PlayerMobile m_From;
            private readonly Item[] m_Items;
            private readonly bool[] m_Insure;
            private readonly int m_Page;

            public ItemInsuranceMenuConfirmGump(PlayerMobile from, Item[] items, bool[] insure, int page)
                : base(250, 200)
            {
                m_From = from;
                m_Items = items;
                m_Insure = insure;
                m_Page = page;

                AddBackground(0, 0, 240, 142, 0x13BE);
                AddImageTiled(6, 6, 228, 100, 0xA40);
                AddImageTiled(6, 116, 228, 20, 0xA40);
                AddAlphaRegion(6, 6, 228, 142);

                AddHtmlLocalized(8, 8, 228, 100, 1114300, 0x7FFF, false, false); // Do you wish to insure all newly selected items?

                AddButton(6, 116, 0xFB1, 0xFB2, 0, GumpButtonType.Reply, 0);
                AddHtmlLocalized(40, 118, 450, 20, 1060051, 0x7FFF, false, false); // CANCEL

                AddButton(114, 116, 0xFA5, 0xFA7, 1, GumpButtonType.Reply, 0);
                AddHtmlLocalized(148, 118, 450, 20, 1073996, 0x7FFF, false, false); // ACCEPT
            }

            public override void OnResponse(NetState sender, RelayInfo info)
            {
                if (!m_From.CheckAlive())
                    return;

                if (info.ButtonID == 1)
                {
                    for (int i = 0; i < m_Items.Length; ++i)
                    {
                        Item item = m_Items[i];

                        if (item.Insured != m_Insure[i])
                            m_From.ToggleItemInsurance_Callback(m_From, item, false);
                    }
                }
                else
                {
                    m_From.SendLocalizedMessage(1042021); // Cancelled.
                    m_From.SendGump(new ItemInsuranceMenuGump(m_From, m_Items, m_Insure, m_Page));
                }
            }
        }

        #endregion

        private void ToggleTrades()
        {
            RefuseTrades = !RefuseTrades;
        }

        private void GetVendor()
        {
            BaseHouse house = BaseHouse.FindHouseAt(this);

            if (CheckAlive() && house != null && house.IsOwner(this) && house.InternalizedVendors.Count > 0)
            {
                CloseGump(typeof(ReclaimVendorGump));
                SendGump(new ReclaimVendorGump(house));
            }
        }

        private void LeaveHouse()
        {
            BaseHouse house = BaseHouse.FindHouseAt(this);

            if (house != null)
            {
                Location = house.BanLocation;
            }
        }

        private void ReleaseCoOwnership()
        {
            BaseHouse house = BaseHouse.FindHouseAt(this);

            if (house != null && house.IsCoOwner(this))
            {
                SendGump(new WarningGump(1060635, 30720, 1062006, 32512, 420, 280, ClearCoOwners_Callback, house));
            }
        }

        public void ClearCoOwners_Callback(Mobile from, bool okay, object state)
        {
            BaseHouse house = (BaseHouse)state;

            if (house.Deleted)
                return;

            if (okay && house.IsCoOwner(from))
            {
                if (house.CoOwners != null)
                    house.CoOwners.Remove(from);

                from.SendLocalizedMessage(501300); // You have been removed as a house co-owner.
            }
        }

        private void EnablePvpWarning()
        {
            DisabledPvpWarning = false;
            SendLocalizedMessage(1113798); // Your PvP warning query has been re-enabled.
        }

        private delegate void ContextCallback();

        private class CallbackEntry : ContextMenuEntry
        {
            private readonly ContextCallback m_Callback;

            public CallbackEntry(int number, ContextCallback callback)
                : this(number, -1, callback)
            { }

            public CallbackEntry(int number, int range, ContextCallback callback)
                : base(number, range)
            {
                m_Callback = callback;
            }

            public override void OnClick()
            {
                if (m_Callback != null)
                {
                    m_Callback();
                }
            }
        }

        public override void DisruptiveAction()
        {
            if (Meditating)
            {
                RemoveBuff(BuffIcon.ActiveMeditation);
            }

            base.DisruptiveAction();
        }

        public override bool Meditating
        {
            set
            {
                base.Meditating = value;
                if (value == false)
                {
                    RemoveBuff(BuffIcon.ActiveMeditation);
                }
            }
        }

        public override void OnDoubleClick(Mobile from)
        {
            if (this == from && !Warmode)
            {
                IMount mount = Mount;

                if (mount != null && !DesignContext.Check(this))
                {
                    return;
                }
            }

            base.OnDoubleClick(from);
        }

        public override void DisplayPaperdollTo(Mobile to)
        {
            if (DesignContext.Check(this))
            {
                base.DisplayPaperdollTo(to);
            }
        }

        private static bool m_NoRecursion;

        public override bool CheckEquip(Item item)
        {
            if (!base.CheckEquip(item))
            {
                return false;
            }

            Region r = Region.Find(Location, Map);

            if (r is ArenaRegion region && !region.AllowItemEquip(this, item))
            {
                return false;
            }

            #region Vice Vs Virtue
            IVvVItem vvvItem = item as IVvVItem;

            if (vvvItem != null && vvvItem.IsVvVItem && !ViceVsVirtueSystem.IsVvV(this))
            {
                return false;
            }
            #endregion

            if (AccessLevel < AccessLevel.GameMaster && item.Layer != Layer.Mount && HasTrade)
            {
                BounceInfo bounce = item.GetBounce();

                if (bounce != null)
                {
                    if (bounce.m_Parent is Item parent)
                    {
                        if (parent == Backpack || parent.IsChildOf(Backpack))
                        {
                            return true;
                        }
                    }
                    else if (bounce.m_Parent == this)
                    {
                        return true;
                    }
                }

                SendLocalizedMessage(1004042); // You can only equip what you are already carrying while you have a trade pending.
                return false;
            }

            return true;
        }

        public override bool OnDragLift(Item item)
        {
            if (item is IPromotionalToken token && token.GumpType != null)
            {
                Type t = token.GumpType;

                if (HasGump(t))
                    CloseGump(t);
            }

            return base.OnDragLift(item);
        }

        public override bool CheckTrade(
            Mobile to, Item item, SecureTradeContainer cont, bool message, bool checkItems, int plusItems, int plusWeight)
        {
            int msgNum = 0;

            if (_BlessedItem != null && _BlessedItem == item)
            {
                msgNum = 1075282; // You cannot trade a blessed item.
            }

            if (msgNum == 0 && cont == null)
            {
                if (to.Holding != null)
                {
                    msgNum = 1062727; // You cannot trade with someone who is dragging something.
                }
                else if (HasTrade)
                {
                    msgNum = 1062781; // You are already trading with someone else!
                }
                else if (to.HasTrade)
                {
                    msgNum = 1062779; // That person is already involved in a trade
                }
                else if (to is PlayerMobile pm && pm.RefuseTrades)
                {
                    msgNum = 1154111; // ~1_NAME~ is refusing all trades.
                }
            }

            if (msgNum == 0 && item != null)
            {
                if (cont != null)
                {
                    plusItems += cont.TotalItems;
                    plusWeight += cont.TotalWeight;
                }

                if (Backpack == null || !Backpack.CheckHold(this, item, false, checkItems, plusItems, plusWeight))
                {
                    msgNum = 1004040; // You would not be able to hold this if the trade failed.
                }
                else if (to.Backpack == null || !to.Backpack.CheckHold(to, item, false, checkItems, plusItems, plusWeight))
                {
                    msgNum = 1004039; // The recipient of this trade would not be able to carry
                }
                else
                {
                    msgNum = CheckContentForTrade(item);
                }
            }

            if (msgNum == 0)
            {
                return true;
            }

            if (!message)
            {
                return false;
            }

            if (msgNum == 1154111)
            {
                if (to != null)
                {
                    SendLocalizedMessage(msgNum, to.Name);
                }
            }
            else
            {
                SendLocalizedMessage(msgNum);
            }

            return false;
        }

        private static int CheckContentForTrade(Item item)
        {
            if (item is TrapableContainer container && container.TrapType != TrapType.None)
            {
                return 1004044; // You may not trade trapped items.
            }

            if (StolenItem.IsStolen(item))
            {
                return 1004043; // You may not trade recently stolen items.
            }

            if (item is Container)
            {
                foreach (Item subItem in item.Items)
                {
                    int msg = CheckContentForTrade(subItem);

                    if (msg != 0)
                    {
                        return msg;
                    }
                }
            }

            return 0;
        }

        public override bool CheckHasTradeDrop(Mobile from, Item item, Item target)
        {
            if (!base.CheckHasTradeDrop(from, item, target))
            {
                return false;
            }

            if (from.AccessLevel >= AccessLevel.GameMaster)
            {
                return true;
            }

            Container pack = Backpack;
            if (from == this && HasTrade && (target == pack || target.IsChildOf(pack)))
            {
                BounceInfo bounce = item.GetBounce();

                if (bounce != null && bounce.m_Parent is Item pItem && pItem == pack)
                {
                    if (pItem.IsChildOf(pack))
                    {
                        return true;
                    }
                }

                SendLocalizedMessage(1004041); // You can't do that while you have a trade pending.
                return false;
            }

            return true;
        }

        protected override void OnLocationChange(Point3D oldLocation)
        {
            CheckLightLevels(false);

            DesignContext context = m_DesignContext;

            if (context == null || m_NoRecursion)
            {
                return;
            }

            m_NoRecursion = true;

            HouseFoundation foundation = context.Foundation;

            int newX = X, newY = Y;
            int newZ = foundation.Z + HouseFoundation.GetLevelZ(context.Level, context.Foundation);

            int startX = foundation.X + foundation.Components.Min.X + 1;
            int startY = foundation.Y + foundation.Components.Min.Y + 1;
            int endX = startX + foundation.Components.Width - 1;
            int endY = startY + foundation.Components.Height - 2;

            if (newX >= startX && newY >= startY && newX < endX && newY < endY && Map == foundation.Map)
            {
                if (Z != newZ)
                {
                    Location = new Point3D(X, Y, newZ);
                }

                m_NoRecursion = false;
                return;
            }

            Location = new Point3D(foundation.X, foundation.Y, newZ);
            Map = foundation.Map;

            m_NoRecursion = false;
        }

        public override bool OnMoveOver(Mobile m)
        {
            if (m is BaseCreature bc && !bc.Controlled)
            {
                return (!Alive || !bc.Alive || IsDeadBondedPet || bc.IsDeadBondedPet) || (Hidden && IsStaff());
            }

            return base.OnMoveOver(m);
        }

        public override bool CheckShove(Mobile shoved)
        {
            if (TransformationSpellHelper.UnderTransformation(shoved, typeof(WraithFormSpell)))
            {
                return true;
            }

            return base.CheckShove(shoved);
        }

        protected override void OnMapChange(Map oldMap)
        {
            ViceVsVirtueSystem.OnMapChange(this);

            if (NetState != null && NetState.IsEnhancedClient)
            {
                Waypoints.OnMapChange(this, oldMap);
            }

            if ((Map != ViceVsVirtueSystem.Facet && oldMap == ViceVsVirtueSystem.Facet) || (Map == ViceVsVirtueSystem.Facet && oldMap != ViceVsVirtueSystem.Facet))
            {
                InvalidateProperties();
            }

            BaseGump.CheckCloseGumps(this);

            DesignContext context = m_DesignContext;

            if (context == null || m_NoRecursion)
            {
                return;
            }

            m_NoRecursion = true;

            HouseFoundation foundation = context.Foundation;

            if (Map != foundation.Map)
            {
                Map = foundation.Map;
            }

            m_NoRecursion = false;
        }

        public override void OnBeneficialAction(Mobile target, bool isCriminal)
        {
            if (m_SentHonorContext != null)
            {
                m_SentHonorContext.OnSourceBeneficialAction(target);
            }

            if (Siege.SiegeShard && isCriminal)
            {
                Criminal = true;
                return;
            }

            base.OnBeneficialAction(target, isCriminal);
        }

        public override bool IsBeneficialCriminal(Mobile target)
        {
            if (!target.Criminal && target is BaseCreature bc && bc.GetMaster() == this)
                return false;

            return base.IsBeneficialCriminal(target);
        }

        public override void OnDamage(int amount, Mobile from, bool willKill)
        {
            int disruptThreshold;

            if (from != null && from.Player)
            {
                disruptThreshold = 19;
            }
            else
            {
                disruptThreshold = 26;
            }

            disruptThreshold += Dex / 12;

            if (amount > disruptThreshold)
            {
                BandageContext c = BandageContext.GetContext(this);

                if (c != null)
                {
                    c.Slip();
                }
            }

            if (Confidence.IsRegenerating(this))
            {
                Confidence.StopRegenerating(this);
            }

            if (m_ReceivedHonorContext != null)
            {
                m_ReceivedHonorContext.OnTargetDamaged(from, amount);
            }
            if (m_SentHonorContext != null)
            {
                m_SentHonorContext.OnSourceDamaged(from, amount);
            }

            if (willKill && from is PlayerMobile pm)
            {
                Timer.DelayCall(TimeSpan.FromSeconds(10), pm.RecoverAmmo);
            }

            #region Mondain's Legacy
            if (InvisibilityPotion.HasTimer(this))
            {
                InvisibilityPotion.Iterrupt(this);
            }
            #endregion

            UndertakersStaff.TryRemoveTimer(this);

            base.OnDamage(amount, from, willKill);
        }

        public override void Resurrect()
        {
            bool wasAlive = Alive;

            base.Resurrect();

            if (Alive && !wasAlive)
            {
                Item deathRobe = new DeathRobe();

                if (!EquipItem(deathRobe))
                {
                    deathRobe.Delete();
                }

                if (NetState != null /*&& NetState.IsEnhancedClient*/)
                {
                    Waypoints.RemoveHealers(this, Map);
                }

                #region Scroll of Alacrity
                if (AcceleratedStart > DateTime.UtcNow)
                {
                    BuffInfo.AddBuff(this, new BuffInfo(BuffIcon.ArcaneEmpowerment, 1078511, 1078512, AcceleratedSkill.ToString()));
                }
                #endregion
            }
        }

        public override double RacialSkillBonus
        {
            get
            {
                if (Race == Race.Human)
                {
                    return 20.0;
                }

                return 0;
            }
        }

        public override double GetRacialSkillBonus(SkillName skill)
        {
            if (Race == Race.Human)
                return 20.0;

            if (Race == Race.Gargoyle)
            {
                if (skill == SkillName.Imbuing)
                    return 30.0;

                if (skill == SkillName.Throwing)
                    return 20.0;
            }

            return RacialSkillBonus;
        }

        public override void OnWarmodeChanged()
        {
            if (!Warmode)
            {
                Timer.DelayCall(TimeSpan.FromSeconds(10), RecoverAmmo);
            }
        }

        private Mobile m_InsuranceAward;
        private int m_InsuranceCost;
        private int m_InsuranceBonus;

        private List<Item> m_EquipSnapshot;

        public List<Item> EquipSnapshot => m_EquipSnapshot;

        private bool FindItems_Callback(Item item)
        {
            if (!item.Deleted && (item.LootType == LootType.Blessed || item.Insured))
            {
                if (Backpack != item.Parent)
                {
                    return true;
                }
            }
            return false;
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public override bool Criminal
        {
            get
            {
                return base.Criminal;
            }
            set
            {
                bool crim = base.Criminal;
                base.Criminal = value;

                if (value != crim)
                {
                    if (value)
                        BuffInfo.AddBuff(this, new BuffInfo(BuffIcon.CriminalStatus, 1153802, 1153828));
                    else
                        BuffInfo.RemoveBuff(this, BuffIcon.CriminalStatus);
                }
            }
        }

        public override bool OnBeforeDeath()
        {
            NetState state = NetState;

            if (state != null)
            {
                state.CancelAllTrades();
            }

            if (Criminal)
                BuffInfo.RemoveBuff(this, BuffIcon.CriminalStatus);

            DropHolding();

            if (Backpack != null && !Backpack.Deleted)
            {
                List<Item> ilist = Backpack.FindItemsByType<Item>(FindItems_Callback);

                for (int i = 0; i < ilist.Count; i++)
                {
                    Backpack.AddItem(ilist[i]);
                }
            }

            m_EquipSnapshot = new List<Item>(Items);

            m_NonAutoreinsuredItems = 0;
            m_InsuranceCost = 0;
            m_InsuranceAward = FindMostRecentDamager(false);

            if (m_InsuranceAward is BaseCreature bc)
            {
                Mobile master = bc.GetMaster();

                if (master != null)
                {
                    m_InsuranceAward = master;
                }
            }

            if (m_InsuranceAward != null && (!m_InsuranceAward.Player || m_InsuranceAward == this))
            {
                m_InsuranceAward = null;
            }

            if (m_InsuranceAward is PlayerMobile pm)
            {
                pm.m_InsuranceBonus = 0;
            }

            if (m_ReceivedHonorContext != null)
            {
                m_ReceivedHonorContext.OnTargetKilled();
            }

            if (m_SentHonorContext != null)
            {
                m_SentHonorContext.OnSourceKilled();
            }

            RecoverAmmo();

            if (NetState != null && NetState.IsEnhancedClient)
            {
                Waypoints.AddCorpse(this);
            }

            return base.OnBeforeDeath();
        }

        private bool CheckInsuranceOnDeath(Item item)
        {
            if (Young)
                return false;

            if (InsuranceEnabled && item.Insured)
            {
                int insuredAmount = GetInsuranceCost(item);

                if (AutoRenewInsurance)
                {
                    int cost = (m_InsuranceAward == null ? insuredAmount : insuredAmount / 2);

                    if (Banker.Withdraw(this, cost))
                    {
                        m_InsuranceCost += cost;
                        item.PayedInsurance = true;
                        SendLocalizedMessage(1060398, cost.ToString()); // ~1_AMOUNT~ gold has been withdrawn from your bank box.
                    }
                    else
                    {
                        SendLocalizedMessage(1061079, "", 0x23); // You lack the funds to purchase the insurance
                        item.PayedInsurance = false;
                        item.Insured = false;
                        m_NonAutoreinsuredItems++;
                    }
                }
                else
                {
                    item.PayedInsurance = false;
                    item.Insured = false;
                }

                if (m_InsuranceAward != null)
                {
                    if (Banker.Deposit(m_InsuranceAward, insuredAmount / 2) && m_InsuranceAward is PlayerMobile pm)
                    {
                        pm.m_InsuranceBonus += insuredAmount / 2;
                    }
                }

                return true;
            }

            return false;
        }

        public override DeathMoveResult GetParentMoveResultFor(Item item)
        {
            if (CheckInsuranceOnDeath(item) && !Young)
            {
                return DeathMoveResult.MoveToBackpack;
            }

            DeathMoveResult res = base.GetParentMoveResultFor(item);

            if (res == DeathMoveResult.MoveToCorpse && item.Movable && Young)
            {
                res = DeathMoveResult.MoveToBackpack;
            }

            return res;
        }

        public override DeathMoveResult GetInventoryMoveResultFor(Item item)
        {
            if (CheckInsuranceOnDeath(item) && !Young)
            {
                return DeathMoveResult.MoveToBackpack;
            }

            DeathMoveResult res = base.GetInventoryMoveResultFor(item);

            if (res == DeathMoveResult.MoveToCorpse && item.Movable && Young)
            {
                res = DeathMoveResult.MoveToBackpack;
            }

            return res;
        }

        public override void OnDeath(Container c)
        {
            if (NetState != null)
            {
                Waypoints.OnDeath(this);
            }

            Mobile m = FindMostRecentDamager(false);
            PlayerMobile killer = m as PlayerMobile;

            if (killer == null && m is BaseCreature bc)
            {
                killer = bc.GetMaster() as PlayerMobile;
            }

            if (m_NonAutoreinsuredItems > 0)
            {
                SendLocalizedMessage(1061115);
            }

            base.OnDeath(c);

            m_EquipSnapshot = null;

            HueMod = -1;
            NameMod = null;
            SavagePaintExpiration = TimeSpan.Zero;

            SetHairMods(-1, -1);

            PolymorphSpell.StopTimer(this);
            IncognitoSpell.StopTimer(this);
            DisguiseTimers.RemoveTimer(this);

            WeakenSpell.RemoveEffects(this);
            ClumsySpell.RemoveEffects(this);
            FeeblemindSpell.RemoveEffects(this);
            CurseSpell.RemoveEffect(this);
            Spells.Second.ProtectionSpell.EndProtection(this);


            EndAction(typeof(PolymorphSpell));
            EndAction(typeof(IncognitoSpell));

            MeerMage.StopEffect(this, false);

            BaseEscort.DeleteEscort(this);

            #region Stygian Abyss
            if (Flying)
            {
                Flying = false;
                BuffInfo.RemoveBuff(this, BuffIcon.Fly);
            }
            #endregion

            StolenItem.ReturnOnDeath(this, c);

            if (m_PermaFlags.Count > 0)
            {
                m_PermaFlags.Clear();

                if (c is Corpse corpse)
                {
                    corpse.Criminal = true;
                }

                if (Stealing.ClassicMode)
                {
                    Criminal = true;
                }
            }

            if (killer != null && Murderer && DateTime.UtcNow >= killer.m_NextJustAward)
            {
                // This scales 700.0 skill points to 1000 valor points
                int pointsToGain = SkillsTotal / 7;

                // This scales 700.0 skill points to 7 minutes wait
                int minutesToWait = Math.Max(1, SkillsTotal / 1000);

                bool gainedPath = false;

                if (VirtueHelper.Award(m, VirtueName.Justice, pointsToGain, ref gainedPath))
                {
                    if (gainedPath)
                    {
                        m.SendLocalizedMessage(1049367); // You have gained a path in Justice!
                    }
                    else
                    {
                        m.SendLocalizedMessage(1049363); // You have gained in Justice.
                    }

                    m.FixedParticles(0x375A, 9, 20, 5027, EffectLayer.Waist);
                    m.PlaySound(0x1F7);

                    killer.m_NextJustAward = DateTime.UtcNow + TimeSpan.FromMinutes(minutesToWait);
                }
            }

            if (m_InsuranceAward is PlayerMobile pm && pm.m_InsuranceBonus > 0)
            {
                pm.SendLocalizedMessage(1060397, pm.m_InsuranceBonus.ToString()); // ~1_AMOUNT~ gold has been deposited into your bank box.
            }

            if (Young)
            {
                if (YoungDeathTeleport())
                {
                    Timer.DelayCall(TimeSpan.FromSeconds(2.5), SendYoungDeathNotice);
                }
            }

            Guilds.Guild.HandleDeath(this, killer);

            if (m_BuffTable != null)
            {
                List<BuffInfo> list = new List<BuffInfo>();

                foreach (BuffInfo buff in m_BuffTable.Values)
                {
                    if (!buff.RetainThroughDeath)
                    {
                        list.Add(buff);
                    }
                }

                for (int i = 0; i < list.Count; i++)
                {
                    RemoveBuff(list[i]);
                }
            }

            #region Stygian Abyss
            if (Region.IsPartOf("Abyss") && SSSeedExpire > DateTime.UtcNow)
            {
                SendGump(new ResurrectGump(this, ResurrectMessage.SilverSapling));
            }

            if (LastKiller is BaseVoidCreature)
                ((BaseVoidCreature)LastKiller).Mutate(VoidEvolution.Killing);
            #endregion
        }

        private List<Mobile> m_PermaFlags;
        private readonly List<Mobile> m_VisList;
        private readonly Hashtable m_AntiMacroTable;
        private TimeSpan m_GameTime;
        private TimeSpan m_ShortTermElapse;
        private TimeSpan m_LongTermElapse;
        private DateTime m_SessionStart;
        private DateTime m_SavagePaintExpiration;
        private SkillName m_Learning = (SkillName)(-1);

        public SkillName Learning { get { return m_Learning; } set { m_Learning = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public TimeSpan SavagePaintExpiration
        {
            get
            {
                TimeSpan ts = m_SavagePaintExpiration - DateTime.UtcNow;

                if (ts < TimeSpan.Zero)
                {
                    ts = TimeSpan.Zero;
                }

                return ts;
            }
            set { m_SavagePaintExpiration = DateTime.UtcNow + value; }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public TimeSpan NextSmithBulkOrder
        {
            get
            {
                return BulkOrderSystem.GetNextBulkOrder(BODType.Smith, this);
            }
            set
            {
                BulkOrderSystem.SetNextBulkOrder(BODType.Smith, this, value);
            }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public TimeSpan NextTailorBulkOrder
        {
            get
            {
                return BulkOrderSystem.GetNextBulkOrder(BODType.Tailor, this);
            }
            set
            {
                BulkOrderSystem.SetNextBulkOrder(BODType.Tailor, this, value);
            }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public TimeSpan NextAlchemyBulkOrder
        {
            get
            {
                return BulkOrderSystem.GetNextBulkOrder(BODType.Alchemy, this);
            }
            set
            {
                BulkOrderSystem.SetNextBulkOrder(BODType.Alchemy, this, value);
            }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public TimeSpan NextInscriptionBulkOrder
        {
            get
            {
                return BulkOrderSystem.GetNextBulkOrder(BODType.Inscription, this);
            }
            set
            {
                BulkOrderSystem.SetNextBulkOrder(BODType.Inscription, this, value);
            }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public TimeSpan NextTinkeringBulkOrder
        {
            get
            {
                return BulkOrderSystem.GetNextBulkOrder(BODType.Tinkering, this);
            }
            set
            {
                BulkOrderSystem.SetNextBulkOrder(BODType.Tinkering, this, value);
            }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public TimeSpan NextFletchingBulkOrder
        {
            get
            {
                return BulkOrderSystem.GetNextBulkOrder(BODType.Fletching, this);
            }
            set
            {
                BulkOrderSystem.SetNextBulkOrder(BODType.Fletching, this, value);
            }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public TimeSpan NextCarpentryBulkOrder
        {
            get
            {
                return BulkOrderSystem.GetNextBulkOrder(BODType.Carpentry, this);
            }
            set
            {
                BulkOrderSystem.SetNextBulkOrder(BODType.Carpentry, this, value);
            }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public TimeSpan NextCookingBulkOrder
        {
            get
            {
                return BulkOrderSystem.GetNextBulkOrder(BODType.Cooking, this);
            }
            set
            {
                BulkOrderSystem.SetNextBulkOrder(BODType.Cooking, this, value);
            }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public DateTime LastEscortTime { get; set; }

        [CommandProperty(AccessLevel.GameMaster)]
        public DateTime LastPetBallTime { get; set; }

        public PlayerMobile()
        {
            Instances.Add(this);

            m_AutoStabled = new List<Mobile>();

            m_DoneQuests = new List<QuestRestartInfo>();
            m_Collections = new Dictionary<Collection, int>();
            m_RewardTitles = new List<object>();

            m_VisList = new List<Mobile>();
            m_PermaFlags = new List<Mobile>();
            m_AntiMacroTable = new Hashtable();
            m_RecentlyReported = new List<Mobile>();

            /*#region FS:ATS Edits
            m_TamingBOBFilter = new Engines.BulkOrders.TamingBOBFilter();
            #endregion*/

            m_GameTime = TimeSpan.Zero;
            m_ShortTermElapse = TimeSpan.FromHours(8.0);
            m_LongTermElapse = TimeSpan.FromHours(40.0);

            m_JusticeProtectors = new List<Mobile>();
            m_GuildRank = RankDefinition.Lowest;

            m_ChampionTitles = new ChampionTitleInfo();
        }

        public override bool MutateSpeech(List<Mobile> hears, ref string text, ref object context)
        {
            if (Alive)
            {
                return false;
            }

            if (Skills[SkillName.SpiritSpeak].Value >= 100.0)
            {
                return false;
            }

            for (int i = 0; i < hears.Count; ++i)
            {
                Mobile m = hears[i];

                if (m != this && m.Skills[SkillName.SpiritSpeak].Value >= 100.0)
                {
                    return false;
                }
            }

            return base.MutateSpeech(hears, ref text, ref context);
        }

        public override void DoSpeech(string text, int[] keywords, MessageType type, int hue)
        {
            if (type == MessageType.Guild || type == MessageType.Alliance)
            {
                Guild g = Guild as Guild;
                if (g == null)
                {
                    SendLocalizedMessage(1063142); // You are not in a guild!
                }
                else if (type == MessageType.Alliance)
                {
                    if (g.Alliance != null && g.Alliance.IsMember(g))
                    {
                        //g.Alliance.AllianceTextMessage( hue, "[Alliance][{0}]: {1}", Name, text );
                        g.Alliance.AllianceChat(this, text);
                        SendToStaffMessage(this, "[Alliance]: {0}", text);

                        m_AllianceMessageHue = hue;
                    }
                    else
                    {
                        SendLocalizedMessage(1071020); // You are not in an alliance!
                    }
                }
                else //Type == MessageType.Guild
                {
                    m_GuildMessageHue = hue;

                    g.GuildChat(this, text);
                    SendToStaffMessage(this, "[Guild]: {0}", text);
                }
            }
            else
            {
                base.DoSpeech(text, keywords, type, hue);
            }
        }

        private static void SendToStaffMessage(Mobile from, string text)
        {
            Packet p = null;

            foreach (NetState ns in from.GetClientsInRange(8))
            {
                Mobile mob = ns.Mobile;

                if (mob != null && mob.AccessLevel >= AccessLevel.GameMaster && mob.AccessLevel > from.AccessLevel)
                {
                    if (p == null)
                    {
                        p =
                            Packet.Acquire(
                                new UnicodeMessage(
                                    from.Serial, from.Body, MessageType.Regular, from.SpeechHue, 3, from.Language, from.Name, text));
                    }

                    ns.Send(p);
                }
            }

            Packet.Release(p);
        }

        private static void SendToStaffMessage(Mobile from, string format, params object[] args)
        {
            SendToStaffMessage(from, string.Format(format, args));
        }

        #region Poison
        public override void OnCured(Mobile from, Poison oldPoison)
        {
            BuffInfo.RemoveBuff(this, BuffIcon.Poison);
        }

        public override ApplyPoisonResult ApplyPoison(Mobile from, Poison poison)
        {
            if (!Alive || poison == null)
            {
                return ApplyPoisonResult.Immune;
            }

            //Skill Masteries
            if (ResilienceSpell.UnderEffects(this) && 0.25 > Utility.RandomDouble())
            {
                return ApplyPoisonResult.Immune;
            }

            if (EvilOmenSpell.TryEndEffect(this))
            {
                poison = PoisonImpl.IncreaseLevel(poison);
            }

            //Skill Masteries
            if ((Poison == null || Poison.Level < poison.Level) && ToleranceSpell.OnPoisonApplied(this))
            {
                poison = PoisonImpl.DecreaseLevel(poison);

                if (poison == null || poison.Level <= 0)
                {
                    PrivateOverheadMessage(MessageType.Regular, 0x3F, 1053092, NetState); // * You feel yourself resisting the effects of the poison *
                    return ApplyPoisonResult.Immune;
                }
            }

            ApplyPoisonResult result = base.ApplyPoison(from, poison);

            if (from != null && result == ApplyPoisonResult.Poisoned && PoisonTimer is PoisonImpl.PoisonTimer)
            {
                (PoisonTimer as PoisonImpl.PoisonTimer).From = from;
            }

            return result;
        }

        public override bool CheckPoisonImmunity(Mobile from, Poison poison)
        {
            if (Young)
            {
                return true;
            }

            return base.CheckPoisonImmunity(from, poison);
        }

        public override void OnPoisonImmunity(Mobile from, Poison poison)
        {
            if (Young)
            {
                SendLocalizedMessage(502808);
                // You would have been poisoned, were you not new to the land of Britannia. Be careful in the future.
            }
            else
            {
                base.OnPoisonImmunity(from, poison);
            }
        }
        #endregion

        public PlayerMobile(Serial s)
            : base(s)
        {
            Instances.Add(this);

            m_VisList = new List<Mobile>();
            m_AntiMacroTable = new Hashtable();
        }

        public List<Mobile> VisibilityList => m_VisList;

        public List<Mobile> PermaFlags => m_PermaFlags;

        public override int Luck => AosAttributes.GetValue(this, AosAttribute.Luck) + TenthAnniversarySculpture.GetLuckBonus(this);

        public int RealLuck
        {
            get
            {
                int facetBonus = !Siege.SiegeShard && Map == Map.Felucca ? RandomItemGenerator.FeluccaLuckBonus : 0;

                return Luck + FountainOfFortune.GetLuckBonus(this) + facetBonus;
            }
        }

        public override bool IsHarmfulCriminal(IDamageable damageable)
        {
            Mobile target = damageable as Mobile;

            if (Stealing.ClassicMode && target is PlayerMobile pm && pm.m_PermaFlags.Count > 0)
            {
                int noto = Notoriety.Compute(this, target);

                if (noto == Notoriety.Innocent)
                {
                    pm.Delta(MobileDelta.Noto);
                }

                return false;
            }

            if (target is BaseCreature bc && bc.InitialInnocent && !bc.Controlled)
            {
                return false;
            }

            if (target is BaseCreature creature && creature.Controlled && creature.ControlMaster == this)
            {
                return false;
            }

            if (target is BaseCreature baseCreature && baseCreature.Summoned && baseCreature.SummonMaster == this)
            {
                return false;
            }

            return base.IsHarmfulCriminal(damageable);
        }

        public bool AntiMacroCheck(Skill skill, object obj)
        {
            if (obj == null || m_AntiMacroTable == null || IsStaff())
            {
                return true;
            }

            Hashtable tbl = (Hashtable)m_AntiMacroTable[skill];
            if (tbl == null)
            {
                m_AntiMacroTable[skill] = tbl = new Hashtable();
            }

            CountAndTimeStamp count = (CountAndTimeStamp)tbl[obj];
            if (count != null)
            {
                if (count.TimeStamp + SkillCheck.AntiMacroExpire <= DateTime.UtcNow)
                {
                    count.Count = 1;
                    return true;
                }
                else
                {
                    ++count.Count;
                    if (count.Count <= SkillCheck.Allowance)
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
            }
            else
            {
                tbl[obj] = count = new CountAndTimeStamp();
                count.Count = 1;

                return true;
            }
        }

        /*#region FS:ATS Edits
        private Engines.BulkOrders.TamingBOBFilter m_TamingBOBFilter;
        #endregion*/

        public BOBFilter BOBFilter => BulkOrderSystem.GetBOBFilter(this);

        /*#region FS:ATS Edits
        public Engines.BulkOrders.TamingBOBFilter TamingBOBFilter
        {
            get{ return m_TamingBOBFilter; }
        }
        #endregion*/

        public override void Deserialize(GenericReader reader)
        {
            base.Deserialize(reader);

            int version = reader.ReadInt();

            switch (version)
            {
                case 42: // upgraded quest serialization
                case 41: // removed PeacedUntil - no need to serialize this
                case 40: // Version 40, moved gauntlet points, virtua artys and TOT convert to PointsSystem
                case 39: // Version 39, removed ML quest save/load
                case 38:
                    NextGemOfSalvationUse = reader.ReadDateTime();
                    goto case 37;
                case 37:
                    m_ExtendedFlags = (ExtendedPlayerFlag)reader.ReadInt();
                    goto case 36;
                case 36:
                    RewardStableSlots = reader.ReadInt();
                    goto case 35;
                case 35: // Siege Blessed Item
                    _BlessedItem = reader.ReadItem();
                    goto case 34;
                // Version 34 - new BOD System
                case 34:
                case 33:
                    {
                        ExploringTheDeepQuest = (ExploringTheDeepQuestChain)reader.ReadInt();
                        goto case 31;
                    }
                case 32:
                case 31:
                    {
                        DisplayGuildTitle = version > 31 && reader.ReadBool();
                        m_FameKarmaTitle = reader.ReadString();
                        m_PaperdollSkillTitle = reader.ReadString();
                        m_OverheadTitle = reader.ReadString();
                        m_SubtitleSkillTitle = reader.ReadString();

                        m_CurrentChampTitle = reader.ReadString();
                        m_CurrentVeteranTitle = reader.ReadInt();
                        goto case 30;
                    }
                case 30: goto case 29;
                case 29:
                    {
                        if (version < 40)
                        {
                            PointsSystem.DoomGauntlet.SetPoints(this, reader.ReadDouble());
                        }

                        m_SSNextSeed = reader.ReadDateTime();
                        m_SSSeedExpire = reader.ReadDateTime();
                        m_SSSeedLocation = reader.ReadPoint3D();
                        m_SSSeedMap = reader.ReadMap();

                        if (version < 30)
                        {
                            reader.ReadLong(); // Old m_LevelExp
                            int points = (int)reader.ReadLong();
                            if (points > 0)
                            {
                                PointsSystem.QueensLoyalty.ConvertFromOldSystem(this, points);
                            }

                            reader.ReadInt(); // Old m_Level
                            reader.ReadString(); // Old m_ExpTitle
                        }

                        if (version < 40)
                        {
                            PointsSystem.VirtueArtifacts.SetPoints(this, reader.ReadInt());
                        }

                        if (version < 39)
                        {
                            List<BaseQuest> quests = QuestReader.Quests(reader, this);
                            Dictionary<QuestChain, BaseChain> dic = QuestReader.Chains(reader);

                            if (quests != null && quests.Count > 0)
                                MondainQuestData.QuestData[this] = quests;

                            if (dic != null && dic.Count > 0)
                                MondainQuestData.ChainData[this] = dic;
                        }

                        m_Collections = new Dictionary<Collection, int>();
                        m_RewardTitles = new List<object>();

                        for (int i = reader.ReadInt(); i > 0; i--)
                        {
                            m_Collections.Add((Collection)reader.ReadInt(), reader.ReadInt());
                        }

                        for (int i = reader.ReadInt(); i > 0; i--)
                        {
                            m_RewardTitles.Add(QuestReader.Object(reader));
                        }

                        m_SelectedTitle = reader.ReadInt();

                        goto case 28;
                    }
                case 28:
                    {
                        if (version < 41)
                        {
                            reader.ReadDateTime();
                        }

                        goto case 27;
                    }
                case 27:
                    {
                        m_AnkhNextUse = reader.ReadDateTime();

                        goto case 26;
                    }
                case 26:
                    {
                        m_AutoStabled = reader.ReadStrongMobileList();

                        goto case 25;
                    }
                case 25:
                    {
                        int recipeCount = reader.ReadInt();

                        if (recipeCount > 0)
                        {
                            m_AcquiredRecipes = new Dictionary<int, bool>();

                            for (int i = 0; i < recipeCount; i++)
                            {
                                int r = reader.ReadInt();
                                if (reader.ReadBool()) //Don't add in recipies which we haven't gotten or have been removed
                                {
                                    m_AcquiredRecipes.Add(r, true);
                                }
                            }
                        }
                        goto case 24;
                    }
                case 24:
                    {
                        m_LastHonorLoss = reader.ReadDeltaTime();
                        goto case 23;
                    }
                case 23:
                    {
                        m_ChampionTitles = new ChampionTitleInfo(reader);
                        goto case 22;
                    }
                case 22:
                    {
                        m_LastValorLoss = reader.ReadDateTime();
                        goto case 21;
                    }
                case 21:
                    {
                        if (version < 40)
                        {
                            PointsSystem.TreasuresOfTokuno.Convert(this, reader.ReadEncodedInt(), reader.ReadInt());
                        }
                        goto case 20;
                    }
                case 20:
                    {
                        m_AllianceMessageHue = reader.ReadEncodedInt();
                        m_GuildMessageHue = reader.ReadEncodedInt();

                        goto case 19;
                    }
                case 19:
                    {
                        int rank = reader.ReadEncodedInt();
                        int maxRank = RankDefinition.Ranks.Length - 1;
                        if (rank > maxRank)
                        {
                            rank = maxRank;
                        }

                        m_GuildRank = RankDefinition.Ranks[rank];
                        m_LastOnline = reader.ReadDateTime();
                        goto case 18;
                    }
                case 18:
                    {
                        m_SolenFriendship = (SolenFriendship)reader.ReadEncodedInt();

                        goto case 17;
                    }
                case 17: // changed how DoneQuests is serialized
                case 16:
                    {
                        m_Quest = QuestSerializer.DeserializeQuest(reader);

                        if (m_Quest != null)
                        {
                            m_Quest.From = this;
                        }

                        int count = reader.ReadEncodedInt();

                        if (count > 0)
                        {
                            m_DoneQuests = new List<QuestRestartInfo>();

                            for (int i = 0; i < count; ++i)
                            {
                                Type questType;

                                if (version >= 42)
                                    questType = reader.ReadObjectType();
                                else
                                    questType = QuestSerializer.ReadQuestType(reader);

                                DateTime restartTime;

                                if (version < 17)
                                {
                                    restartTime = DateTime.MaxValue;
                                }
                                else
                                {
                                    restartTime = reader.ReadDateTime();
                                }

                                m_DoneQuests.Add(new QuestRestartInfo(questType, restartTime));
                            }
                        }

                        m_Profession = reader.ReadEncodedInt();
                        goto case 15;
                    }
                case 15:
                    {
                        m_LastCompassionLoss = reader.ReadDeltaTime();
                        goto case 14;
                    }
                case 14:
                    {
                        m_CompassionGains = reader.ReadEncodedInt();

                        if (m_CompassionGains > 0)
                        {
                            m_NextCompassionDay = reader.ReadDeltaTime();
                        }

                        goto case 13;
                    }
                case 13: // just removed m_PayedInsurance list
                case 12:
                    {
                        if (version < 34)
                            BulkOrderSystem.SetBOBFilter(this, new BOBFilter(reader));
                        goto case 11;
                    }
                case 11:
                    {
                        if (version < 13)
                        {
                            List<Item> payed = reader.ReadStrongItemList();

                            for (int i = 0; i < payed.Count; ++i)
                            {
                                payed[i].PayedInsurance = true;
                            }
                        }

                        goto case 10;
                    }
                case 10:
                    {
                        if (reader.ReadBool())
                        {
                            m_HairModID = reader.ReadInt();
                            m_HairModHue = reader.ReadInt();
                            m_BeardModID = reader.ReadInt();
                            m_BeardModHue = reader.ReadInt();
                        }

                        goto case 9;
                    }
                case 9:
                    {
                        SavagePaintExpiration = reader.ReadTimeSpan();

                        if (SavagePaintExpiration > TimeSpan.Zero)
                        {
                            BodyMod = (Female ? 184 : 183);
                            HueMod = 0;
                        }

                        goto case 8;
                    }
                case 8:
                    {
                        m_NpcGuild = (NpcGuild)reader.ReadInt();
                        m_NpcGuildJoinTime = reader.ReadDateTime();
                        m_NpcGuildGameTime = reader.ReadTimeSpan();
                        goto case 7;
                    }
                case 7:
                    {
                        m_PermaFlags = reader.ReadStrongMobileList();
                        goto case 6;
                    }
                case 6:
                    {
                        if (version < 34)
                            reader.ReadTimeSpan();
                        goto case 5;
                    }
                case 5:
                    {
                        if (version < 34)
                            reader.ReadTimeSpan();
                        goto case 4;
                    }
                case 4:
                    {
                        m_LastJusticeLoss = reader.ReadDeltaTime();
                        m_JusticeProtectors = reader.ReadStrongMobileList();
                        goto case 3;
                    }
                case 3:
                    {
                        m_LastSacrificeGain = reader.ReadDeltaTime();
                        m_LastSacrificeLoss = reader.ReadDeltaTime();
                        m_AvailableResurrects = reader.ReadInt();
                        goto case 2;
                    }
                case 2:
                    {
                        m_Flags = (PlayerFlag)reader.ReadInt();
                        goto case 1;
                    }
                case 1:
                    {
                        m_LongTermElapse = reader.ReadTimeSpan();
                        m_ShortTermElapse = reader.ReadTimeSpan();
                        m_GameTime = reader.ReadTimeSpan();
                        goto case 0;
                    }
                case 0:
                    {
                        if (version < 26)
                        {
                            m_AutoStabled = new List<Mobile>();
                        }
                        break;
                    }
            }

            if (version < 29)
            {
                m_SSNextSeed = m_SSSeedExpire = DateTime.UtcNow;
                m_SSSeedLocation = Point3D.Zero;
            }

            if (m_RecentlyReported == null)
            {
                m_RecentlyReported = new List<Mobile>();
            }

            #region Mondain's Legacy

            if (m_DoneQuests == null)
            {
                m_DoneQuests = new List<QuestRestartInfo>();
            }

            if (m_Collections == null)
            {
                m_Collections = new Dictionary<Collection, int>();
            }

            if (m_RewardTitles == null)
            {
                m_RewardTitles = new List<object>();
            }
            #endregion

            // Professions weren't verified on 1.0 RC0
            if (!CharacterCreation.VerifyProfession(m_Profession))
            {
                m_Profession = 0;
            }

            if (m_PermaFlags == null)
            {
                m_PermaFlags = new List<Mobile>();
            }

            if (m_JusticeProtectors == null)
            {
                m_JusticeProtectors = new List<Mobile>();
            }

            /*#region FS:ATS Edits
            if ( m_TamingBOBFilter == null )
            {
                m_TamingBOBFilter = new Engines.BulkOrders.TamingBOBFilter();
            }
            #endregion*/

            if (m_GuildRank == null)
            {
                m_GuildRank = RankDefinition.Member;
                //Default to member if going from older version to new version (only time it should be null)
            }

            if (m_LastOnline == DateTime.MinValue && Account != null)
            {
                m_LastOnline = ((Account)Account).LastLogin;
            }

            if (m_ChampionTitles == null)
            {
                m_ChampionTitles = new ChampionTitleInfo();
            }

            List<Mobile> list = Stabled;

            for (int i = 0; i < list.Count; ++i)
            {
                BaseCreature bc = list[i] as BaseCreature;

                if (bc != null)
                {
                    bc.IsStabled = true;
                    bc.StabledBy = this;
                }
            }

            CheckAtrophies(this);

            if (Hidden) //Hiding is the only buff where it has an effect that's serialized.
            {
                AddBuff(new BuffInfo(BuffIcon.HidingAndOrStealth, 1075655));
            }

            if (_BlessedItem != null)
            {
                Timer.DelayCall(
                b =>
                {
                    if (_BlessedItem == b && b.RootParent != this)
                    {
                        _BlessedItem = null;
                    }
                },
                _BlessedItem);
            }
        }

        public override void Serialize(GenericWriter writer)
        {
            //cleanup our anti-macro table
            foreach (Hashtable t in m_AntiMacroTable.Values)
            {
                ArrayList remove = new ArrayList();
                foreach (CountAndTimeStamp time in t.Values)
                {
                    if (time.TimeStamp + SkillCheck.AntiMacroExpire <= DateTime.UtcNow)
                    {
                        remove.Add(time);
                    }
                }

                for (int i = 0; i < remove.Count; ++i)
                {
                    t.Remove(remove[i]);
                }
            }

            CheckKillDecay();
            CheckAtrophies(this);

            base.Serialize(writer);

            writer.Write(42); // version

            writer.Write(NextGemOfSalvationUse);

            writer.Write((int)m_ExtendedFlags);

            writer.Write(RewardStableSlots);

            if (_BlessedItem != null && _BlessedItem.RootParent != this)
            {
                _BlessedItem = null;
            }

            writer.Write(_BlessedItem);

            /*// Version 35 FS:ATS
            m_TamingBOBFilter.Serialize( writer );
            
            // Version 34 FS:ATS
            writer.Write( m_Bioenginer );
            writer.Write( NextTamingBulkOrder );*/

            writer.Write((int)ExploringTheDeepQuest);

            // Version 31/32 Titles
            writer.Write(DisplayGuildTitle);
            writer.Write(m_FameKarmaTitle);
            writer.Write(m_PaperdollSkillTitle);
            writer.Write(m_OverheadTitle);
            writer.Write(m_SubtitleSkillTitle);
            writer.Write(m_CurrentChampTitle);
            writer.Write(m_CurrentVeteranTitle);

            // Version 30 open to take out old Queens Loyalty Info

            #region Plant System
            writer.Write(m_SSNextSeed);
            writer.Write(m_SSSeedExpire);
            writer.Write(m_SSSeedLocation);
            writer.Write(m_SSSeedMap);
            #endregion

            #region Mondain's Legacy

            if (m_Collections == null)
            {
                writer.Write(0);
            }
            else
            {
                writer.Write(m_Collections.Count);

                foreach (KeyValuePair<Collection, int> pair in m_Collections)
                {
                    writer.Write((int)pair.Key);
                    writer.Write(pair.Value);
                }
            }

            if (m_RewardTitles == null)
            {
                writer.Write(0);
            }
            else
            {
                writer.Write(m_RewardTitles.Count);

                for (int i = 0; i < m_RewardTitles.Count; i++)
                {
                    QuestWriter.Object(writer, m_RewardTitles[i]);
                }
            }

            writer.Write(m_SelectedTitle);
            #endregion

            // Version 28
            writer.Write(m_AnkhNextUse);
            writer.Write(m_AutoStabled, true);

            if (m_AcquiredRecipes == null)
            {
                writer.Write(0);
            }
            else
            {
                writer.Write(m_AcquiredRecipes.Count);

                foreach (KeyValuePair<int, bool> kvp in m_AcquiredRecipes)
                {
                    writer.Write(kvp.Key);
                    writer.Write(kvp.Value);
                }
            }

            writer.WriteDeltaTime(m_LastHonorLoss);

            ChampionTitleInfo.Serialize(writer, m_ChampionTitles);

            writer.Write(m_LastValorLoss);

            writer.WriteEncodedInt(m_AllianceMessageHue);
            writer.WriteEncodedInt(m_GuildMessageHue);

            writer.WriteEncodedInt(m_GuildRank.Rank);
            writer.Write(m_LastOnline);

            writer.WriteEncodedInt((int)m_SolenFriendship);

            QuestSerializer.Serialize(m_Quest, writer);

            if (m_DoneQuests == null)
            {
                writer.WriteEncodedInt(0);
            }
            else
            {
                writer.WriteEncodedInt(m_DoneQuests.Count);

                for (int i = 0; i < m_DoneQuests.Count; ++i)
                {
                    QuestRestartInfo restartInfo = m_DoneQuests[i];

                    writer.WriteObjectType(restartInfo.QuestType);
                    writer.Write(restartInfo.RestartTime);
                }
            }

            writer.WriteEncodedInt(m_Profession);

            writer.WriteDeltaTime(m_LastCompassionLoss);

            writer.WriteEncodedInt(m_CompassionGains);

            if (m_CompassionGains > 0)
            {
                writer.WriteDeltaTime(m_NextCompassionDay);
            }

            bool useMods = (m_HairModID != -1 || m_BeardModID != -1);

            writer.Write(useMods);

            if (useMods)
            {
                writer.Write(m_HairModID);
                writer.Write(m_HairModHue);
                writer.Write(m_BeardModID);
                writer.Write(m_BeardModHue);
            }

            writer.Write(SavagePaintExpiration);

            writer.Write((int)m_NpcGuild);
            writer.Write(m_NpcGuildJoinTime);
            writer.Write(m_NpcGuildGameTime);

            writer.Write(m_PermaFlags, true);

            writer.WriteDeltaTime(m_LastJusticeLoss);
            writer.Write(m_JusticeProtectors, true);

            writer.WriteDeltaTime(m_LastSacrificeGain);
            writer.WriteDeltaTime(m_LastSacrificeLoss);
            writer.Write(m_AvailableResurrects);

            writer.Write((int)m_Flags);

            writer.Write(m_LongTermElapse);
            writer.Write(m_ShortTermElapse);
            writer.Write(GameTime);
        }

        public static void CheckAtrophies(Mobile m)
        {
            SacrificeVirtue.CheckAtrophy(m);
            JusticeVirtue.CheckAtrophy(m);
            CompassionVirtue.CheckAtrophy(m);
            ValorVirtue.CheckAtrophy(m);

            if (m is PlayerMobile pm)
            {
                ChampionTitleInfo.CheckAtrophy(pm);
            }
        }

        public void CheckKillDecay()
        {
            if (m_ShortTermElapse < GameTime)
            {
                m_ShortTermElapse += TimeSpan.FromHours(8);
                if (ShortTermMurders > 0)
                {
                    --ShortTermMurders;
                }
            }

            if (m_LongTermElapse < GameTime)
            {
                m_LongTermElapse += TimeSpan.FromHours(40);
                if (Kills > 0)
                {
                    --Kills;
                }
            }
        }

        public void ResetKillTime()
        {
            m_ShortTermElapse = GameTime + TimeSpan.FromHours(8);
            m_LongTermElapse = GameTime + TimeSpan.FromHours(40);
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public DateTime SessionStart => m_SessionStart;

        [CommandProperty(AccessLevel.GameMaster)]
        public TimeSpan GameTime
        {
            get
            {
                if (NetState != null)
                {
                    return m_GameTime + (DateTime.UtcNow - m_SessionStart);
                }
                else
                {
                    return m_GameTime;
                }
            }
        }

        public override bool CanSee(Mobile m)
        {
            if (m is IConditionalVisibility && !((IConditionalVisibility)m).CanBeSeenBy(this))
                return false;

            if (m is CharacterStatue statue)
            {
                statue.OnRequestedAnimation(this);
            }

            if (m is PlayerMobile pm && pm.m_VisList.Contains(this))
            {
                return true;
            }

            return base.CanSee(m);
        }

        public override bool CanSee(Item item)
        {
            if (item is IConditionalVisibility vis && !vis.CanBeSeenBy(this))
                return false;

            if (m_DesignContext != null && m_DesignContext.Foundation.IsHiddenToCustomizer(this, item))
            {
                return false;
            }
            else if (AccessLevel == AccessLevel.Player)
            {
                Region r = item.GetRegion();

                if (r is BaseRegion region && !region.CanSee(this, item))
                {
                    return false;
                }
            }

            return base.CanSee(item);
        }

        public override void OnAfterDelete()
        {
            base.OnAfterDelete();

            Instances.Remove(this);

            BaseHouse.HandleDeletion(this);

            DisguiseTimers.RemoveTimer(this);
        }

        public delegate void PlayerPropertiesEventHandler(PlayerPropertiesEventArgs e);

        public static event PlayerPropertiesEventHandler PlayerProperties;

        public class PlayerPropertiesEventArgs : EventArgs
        {
            public PlayerMobile Player = null;
            public ObjectPropertyList PropertyList = null;

            public PlayerPropertiesEventArgs(PlayerMobile player, ObjectPropertyList list)
            {
                Player = player;
                PropertyList = list;
            }
        }

        public override void GetProperties(ObjectPropertyList list)
        {
            base.GetProperties(list);

            Engines.JollyRoger.JollyRogerData.DisplayTitle(this, list);

            if (m_SubtitleSkillTitle != null)
                list.Add(1042971, m_SubtitleSkillTitle);

            if (m_CurrentVeteranTitle > 0)
                list.Add(m_CurrentVeteranTitle);

            if (m_RewardTitles != null && m_SelectedTitle > -1)
            {
                if (m_SelectedTitle < m_RewardTitles.Count)
                {
                    if (m_RewardTitles[m_SelectedTitle] is int)
                    {
                        string cust = null;

                        if ((int)m_RewardTitles[m_SelectedTitle] == 1154017 && CityLoyaltySystem.HasCustomTitle(this, out cust))
                        {
                            list.Add(1154017, cust); // ~1_TITLE~ of ~2_CITY~
                        }
                        else
                            list.Add((int)m_RewardTitles[m_SelectedTitle]);
                    }
                    else if (m_RewardTitles[m_SelectedTitle] is string)
                    {
                        list.Add(1070722, (string)m_RewardTitles[m_SelectedTitle]);
                    }
                }
            }

            for (int i = AllFollowers.Count - 1; i >= 0; i--)
            {
                BaseCreature c = AllFollowers[i] as BaseCreature;

                if (c != null && c.ControlOrder == OrderType.Guard)
                {
                    list.Add(501129); // guarded
                    break;
                }
            }

            if (TestCenter.Enabled)
            {
                VvVPlayerEntry entry = PointsSystem.ViceVsVirtue.GetPlayerEntry<VvVPlayerEntry>(this);

                list.Add(string.Format("Kills: {0} / Deaths: {1} / Assists: {2}", // no cliloc for this!
                    entry == null ? "0" : entry.Kills.ToString(), entry == null ? "0" : entry.Deaths.ToString(), entry == null ? "0" : entry.Assists.ToString()));

                list.Add(1060415, AosAttributes.GetValue(this, AosAttribute.AttackChance).ToString()); // hit chance increase ~1_val~%
                list.Add(1060408, AosAttributes.GetValue(this, AosAttribute.DefendChance).ToString()); // defense chance increase ~1_val~%
                list.Add(1060486, AosAttributes.GetValue(this, AosAttribute.WeaponSpeed).ToString()); // swing speed increase ~1_val~%
                list.Add(1060401, AosAttributes.GetValue(this, AosAttribute.WeaponDamage).ToString()); // damage increase ~1_val~%
                list.Add(1060483, AosAttributes.GetValue(this, AosAttribute.SpellDamage).ToString()); // spell damage increase ~1_val~%
                list.Add(1060433, AosAttributes.GetValue(this, AosAttribute.LowerManaCost).ToString()); // lower mana cost
            }

            if (PlayerProperties != null)
            {
                PlayerProperties(new PlayerPropertiesEventArgs(this, list));
            }
        }

        protected override bool OnMove(Direction d)
        {
            if (Party != null && NetState != null)
            {
                Waypoints.UpdateToParty(this);
            }

            if (IsStaff())
            {
                return true;
            }

            if (Hidden && DesignContext.Find(this) == null) //Hidden & NOT customizing a house
            {
                if (!Mounted && Skills.Stealth.Value >= 25.0)
                {
                    bool running = (d & Direction.Running) != 0;

                    if (running)
                    {
                        if ((AllowedStealthSteps -= 2) <= 0)
                        {
                            RevealingAction();
                        }
                    }
                    else if (AllowedStealthSteps-- <= 0)
                    {
                        Stealth.OnUse(this);
                    }
                }
                else
                {
                    RevealingAction();
                }
            }

            if (InvisibilityPotion.HasTimer(this))
            {
                InvisibilityPotion.Iterrupt(this);
            }

            return true;
        }

        public bool BedrollLogout { get; set; }
        public bool BlanketOfDarknessLogout { get; set; }

        [CommandProperty(AccessLevel.GameMaster)]
        public override bool Paralyzed
        {
            get { return base.Paralyzed; }
            set
            {
                base.Paralyzed = value;

                if (value)
                {
                    AddBuff(new BuffInfo(BuffIcon.Paralyze, 1075827)); //Paralyze/You are frozen and can not move
                }
                else
                {
                    RemoveBuff(BuffIcon.Paralyze);
                }
            }
        }

        #region Quests
        private QuestSystem m_Quest;
        private List<QuestRestartInfo> m_DoneQuests;
        private SolenFriendship m_SolenFriendship;

        public QuestSystem Quest { get { return m_Quest; } set { m_Quest = value; } }

        public List<QuestRestartInfo> DoneQuests { get { return m_DoneQuests; } set { m_DoneQuests = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public SolenFriendship SolenFriendship { get { return m_SolenFriendship; } set { m_SolenFriendship = value; } }
        #endregion

        #region Mondain's Legacy
        public List<BaseQuest> Quests => MondainQuestData.GetQuests(this);
        public Dictionary<QuestChain, BaseChain> Chains => MondainQuestData.GetChains(this);

        [CommandProperty(AccessLevel.GameMaster)]
        public bool Peaced => PeacedUntil > DateTime.UtcNow;

        private Dictionary<Collection, int> m_Collections;
        private List<object> m_RewardTitles;
        private int m_SelectedTitle;

        public Dictionary<Collection, int> Collections => m_Collections;

        public List<object> RewardTitles => m_RewardTitles;

        public int SelectedTitle => m_SelectedTitle;

        public bool RemoveRewardTitle(object o, bool silent)
        {
            if (m_RewardTitles.Contains(o))
            {
                int i = m_RewardTitles.IndexOf(o);

                if (i == m_SelectedTitle)
                    SelectRewardTitle(-1, silent);
                else if (i > m_SelectedTitle)
                    SelectRewardTitle(m_SelectedTitle - 1, silent);

                m_RewardTitles.Remove(o);

                return true;
            }

            return false;
        }

        public int GetCollectionPoints(Collection collection)
        {
            if (m_Collections == null)
            {
                m_Collections = new Dictionary<Collection, int>();
            }

            int points = 0;

            if (m_Collections.ContainsKey(collection))
            {
                m_Collections.TryGetValue(collection, out points);
            }

            return points;
        }

        public void AddCollectionPoints(Collection collection, int points)
        {
            if (m_Collections == null)
            {
                m_Collections = new Dictionary<Collection, int>();
            }

            if (m_Collections.ContainsKey(collection))
            {
                m_Collections[collection] += points;
            }
            else
            {
                m_Collections.Add(collection, points);
            }
        }

        public void SelectRewardTitle(int num, bool silent = false)
        {
            if (num == -1)
            {
                m_SelectedTitle = num;

                if (!silent)
                    SendLocalizedMessage(1074010); // You elect to hide your Reward Title.
            }
            else if (num < m_RewardTitles.Count && num >= -1)
            {
                if (m_SelectedTitle != num)
                {
                    m_SelectedTitle = num;

                    if (m_RewardTitles[num] is int && !silent)
                    {
                        SendLocalizedMessage(1074008, "#" + (int)m_RewardTitles[num]);
                        // You change your Reward Title to "~1_TITLE~".
                    }
                    else if (m_RewardTitles[num] is string && !silent)
                    {
                        SendLocalizedMessage(1074008, (string)m_RewardTitles[num]); // You change your Reward Title to "~1_TITLE~".
                    }
                }
                else if (!silent)
                {
                    SendLocalizedMessage(1074009); // You decide to leave your title as it is.
                }
            }

            InvalidateProperties();
        }

        public bool AddRewardTitle(object title)
        {
            if (m_RewardTitles == null)
            {
                m_RewardTitles = new List<object>();
            }

            if (title != null && !m_RewardTitles.Contains(title))
            {
                m_RewardTitles.Add(title);

                InvalidateProperties();
                return true;
            }

            return false;
        }

        public void ShowChangeTitle()
        {
            SendGump(new SelectTitleGump(this, m_SelectedTitle));
        }
        #endregion

        #region Titles
        private string m_FameKarmaTitle;
        private string m_PaperdollSkillTitle;
        private string m_SubtitleSkillTitle;
        private string m_CurrentChampTitle;
        private string m_OverheadTitle;
        private int m_CurrentVeteranTitle;

        public string FameKarmaTitle
        {
            get { return m_FameKarmaTitle; }
            set { m_FameKarmaTitle = value; InvalidateProperties(); }
        }

        public string PaperdollSkillTitle
        {
            get { return m_PaperdollSkillTitle; }
            set { m_PaperdollSkillTitle = value; InvalidateProperties(); }
        }

        public string SubtitleSkillTitle
        {
            get { return m_SubtitleSkillTitle; }
            set { m_SubtitleSkillTitle = value; InvalidateProperties(); }
        }

        public string CurrentChampTitle
        {
            get { return m_CurrentChampTitle; }
            set { m_CurrentChampTitle = value; InvalidateProperties(); }
        }

        public string OverheadTitle
        {
            get { return m_OverheadTitle; }
            set { m_OverheadTitle = value; InvalidateProperties(); }
        }

        public int CurrentVeteranTitle
        {
            get { return m_CurrentVeteranTitle; }
            set { m_CurrentVeteranTitle = value; InvalidateProperties(); }
        }

        public override bool ShowAccessTitle
        {
            get
            {
                switch (AccessLevel)
                {
                    case AccessLevel.VIP:
                    case AccessLevel.Counselor:
                    case AccessLevel.GameMaster:
                    case AccessLevel.Seer:
                        return true;
                }

                return false;
            }
        }

        public override void AddNameProperties(ObjectPropertyList list)
        {
            string prefix = "";

            if (ShowFameTitle && Fame >= 10000)
            {
                prefix = Female ? "Lady" : "Lord";
            }

            string suffix = "";

            if (PropertyTitle && !string.IsNullOrEmpty(Title))
            {
                suffix = Title;
            }

            BaseGuild guild = Guild;
            bool vvv = ViceVsVirtueSystem.IsVvV(this) && (ViceVsVirtueSystem.EnhancedRules || Map == ViceVsVirtueSystem.Facet);

            if (m_OverheadTitle != null)
            {
                if (vvv)
                {
                    suffix = "[VvV]";
                }
                else
                {
                    int loc = Utility.ToInt32(m_OverheadTitle);

                    if (loc > 0)
                    {
                        if (CityLoyaltySystem.ApplyCityTitle(this, list, prefix, loc))
                            return;
                    }
                    else if (suffix.Length > 0)
                    {
                        suffix = string.Format("{0} {1}", suffix, m_OverheadTitle);
                    }
                    else
                    {
                        suffix = string.Format("{0}", m_OverheadTitle);
                    }
                }
            }
            else if (guild != null && DisplayGuildAbbr)
            {
                if (vvv)
                {
                    suffix = string.Format("[{0}] [VvV]", Utility.FixHtml(guild.Abbreviation));
                }
                else if (suffix.Length > 0)
                {
                    suffix = string.Format("{0} [{1}]", suffix, Utility.FixHtml(guild.Abbreviation));
                }
                else
                {
                    suffix = string.Format("[{0}]", Utility.FixHtml(guild.Abbreviation));
                }
            }
            else if (vvv)
            {
                suffix = "[VvV]";
            }

            suffix = ApplyNameSuffix(suffix);
            string name = Name;

            list.Add(1050045, "{0} \t{1}\t {2}", prefix, name, suffix); // ~1_PREFIX~~2_NAME~~3_SUFFIX~

            if (guild != null && DisplayGuildTitle)
            {
                string title = GuildTitle;

                if (title == null)
                {
                    title = "";
                }
                else
                {
                    title = title.Trim();
                }

                if (title.Length > 0)
                {
                    list.Add("{0}, {1}", Utility.FixHtml(title), Utility.FixHtml(guild.Name));
                }
            }
        }

        public override void OnAfterNameChange(string oldName, string newName)
        {
            if (m_FameKarmaTitle != null)
            {
                FameKarmaTitle = FameKarmaTitle.Replace(oldName, newName);
            }
        }
        #endregion

        public override void OnKillsChange(int oldValue)
        {
            if (Young && Kills > oldValue)
            {
                Account acc = Account as Account;

                if (acc != null)
                {
                    acc.RemoveYoungStatus(0);
                }
            }
        }

        public override void OnKarmaChange(int oldValue)
        {
            EpiphanyHelper.OnKarmaChange(this);
        }

        public override void OnSkillChange(SkillName skill, double oldBase)
        {
            if (skill != SkillName.Alchemy && Skills.CurrentMastery == skill && Skills[skill].Value < MasteryInfo.MinSkillRequirement)
            {
                //SendLocalizedMessage(1156236, String.Format("{0}\t{1}", MasteryInfo.MinSkillRequirement.ToString(), Skills[skill].Info.Name)); // You need at least ~1_SKILL_REQUIREMENT~ ~2_SKILL_NAME~ skill to use that mastery.

                SkillName mastery = Skills.CurrentMastery;
                Skills.CurrentMastery = SkillName.Alchemy;

                MasteryInfo.OnMasteryChanged(this, mastery);
            }

            TransformContext context = TransformationSpellHelper.GetContext(this);

            if (context != null)
            {
                TransformationSpellHelper.CheckCastSkill(this, context);
            }
        }

        public override void OnAccessLevelChanged(AccessLevel oldLevel)
        {
            if (IsPlayer())
            {
                IgnoreMobiles = TransformationSpellHelper.UnderTransformation(this, typeof(WraithFormSpell));
            }
            else
            {
                IgnoreMobiles = true;
            }
        }

        public override void OnDelete()
        {
            Instances.Remove(this);

            if (m_ReceivedHonorContext != null)
            {
                m_ReceivedHonorContext.Cancel();
            }
            if (m_SentHonorContext != null)
            {
                m_SentHonorContext.Cancel();
            }
        }

        #region Fastwalk Prevention
        private static readonly bool FastwalkPrevention = true; // Is fastwalk prevention enabled?

        private static readonly int FastwalkThreshold = 400; // Fastwalk prevention will become active after 0.4 seconds

        private long m_NextMovementTime;
        private bool m_HasMoved;

        public long NextMovementTime => m_NextMovementTime;

        public virtual bool UsesFastwalkPrevention => IsPlayer();

        public override int ComputeMovementSpeed(Direction dir, bool checkTurning)
        {
            if (checkTurning && (dir & Direction.Mask) != (Direction & Direction.Mask))
            {
                return RunMount; // We are NOT actually moving (just a direction change)
            }

            bool running = ((dir & Direction.Running) != 0);

            bool onHorse = Mount != null || Flying;

            AnimalFormContext animalContext = AnimalForm.GetContext(this);

            if (onHorse || (animalContext != null && animalContext.SpeedBoost))
            {
                return (running ? RunMount : WalkMount);
            }

            return (running ? RunFoot : WalkFoot);
        }

        public static bool MovementThrottle_Callback(NetState ns, out bool drop)
        {
            drop = false;

            PlayerMobile pm = ns.Mobile as PlayerMobile;

            if (pm == null || !pm.UsesFastwalkPrevention)
            {
                return true;
            }

            if (!pm.m_HasMoved)
            {
                // has not yet moved
                pm.m_NextMovementTime = Core.TickCount;
                pm.m_HasMoved = true;
                return true;
            }

            long ts = pm.m_NextMovementTime - Core.TickCount;

            if (ts < 0)
            {
                // been a while since we've last moved
                pm.m_NextMovementTime = Core.TickCount;
                return true;
            }

            return (ts < FastwalkThreshold);
        }
        #endregion

        #region Hair and beard mods
        private int m_HairModID = -1, m_HairModHue;
        private int m_BeardModID = -1, m_BeardModHue;

        public void SetHairMods(int hairID, int beardID)
        {
            if (hairID == -1)
            {
                InternalRestoreHair(true, ref m_HairModID, ref m_HairModHue);
            }
            else if (hairID != -2)
            {
                InternalChangeHair(true, hairID, ref m_HairModID, ref m_HairModHue);
            }

            if (beardID == -1)
            {
                InternalRestoreHair(false, ref m_BeardModID, ref m_BeardModHue);
            }
            else if (beardID != -2)
            {
                InternalChangeHair(false, beardID, ref m_BeardModID, ref m_BeardModHue);
            }
        }

        private void CreateHair(bool hair, int id, int hue)
        {
            if (hair)
            {
                //TODO Verification?
                HairItemID = id;
                HairHue = hue;
            }
            else
            {
                FacialHairItemID = id;
                FacialHairHue = hue;
            }
        }

        private void InternalRestoreHair(bool hair, ref int id, ref int hue)
        {
            if (id == -1)
            {
                return;
            }

            if (hair)
            {
                HairItemID = 0;
            }
            else
            {
                FacialHairItemID = 0;
            }

            //if( id != 0 )
            CreateHair(hair, id, hue);

            id = -1;
            hue = 0;
        }

        private void InternalChangeHair(bool hair, int id, ref int storeID, ref int storeHue)
        {
            if (storeID == -1)
            {
                storeID = hair ? HairItemID : FacialHairItemID;
                storeHue = hair ? HairHue : FacialHairHue;
            }
            CreateHair(hair, id, 0);
        }
        #endregion

        #region Virtues
        private DateTime m_LastSacrificeGain;
        private DateTime m_LastSacrificeLoss;
        private int m_AvailableResurrects;

        public DateTime LastSacrificeGain { get { return m_LastSacrificeGain; } set { m_LastSacrificeGain = value; } }
        public DateTime LastSacrificeLoss { get { return m_LastSacrificeLoss; } set { m_LastSacrificeLoss = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public int AvailableResurrects { get { return m_AvailableResurrects; } set { m_AvailableResurrects = value; } }

        private DateTime m_NextJustAward;
        private DateTime m_LastJusticeLoss;
        private List<Mobile> m_JusticeProtectors;

        public DateTime LastJusticeLoss { get { return m_LastJusticeLoss; } set { m_LastJusticeLoss = value; } }
        public List<Mobile> JusticeProtectors { get { return m_JusticeProtectors; } set { m_JusticeProtectors = value; } }

        private DateTime m_LastCompassionLoss;
        private DateTime m_NextCompassionDay;
        private int m_CompassionGains;

        public DateTime LastCompassionLoss { get { return m_LastCompassionLoss; } set { m_LastCompassionLoss = value; } }
        public DateTime NextCompassionDay { get { return m_NextCompassionDay; } set { m_NextCompassionDay = value; } }
        public int CompassionGains { get { return m_CompassionGains; } set { m_CompassionGains = value; } }

        private DateTime m_LastValorLoss;

        public DateTime LastValorLoss { get { return m_LastValorLoss; } set { m_LastValorLoss = value; } }

        private DateTime m_LastHonorLoss;
        private HonorContext m_ReceivedHonorContext;
        private HonorContext m_SentHonorContext;
        public DateTime m_hontime;

        public DateTime LastHonorLoss { get { return m_LastHonorLoss; } set { m_LastHonorLoss = value; } }

        public DateTime LastHonorUse { get; set; }

        public bool HonorActive { get; set; }

        public HonorContext ReceivedHonorContext { get { return m_ReceivedHonorContext; } set { m_ReceivedHonorContext = value; } }
        public HonorContext SentHonorContext { get { return m_SentHonorContext; } set { m_SentHonorContext = value; } }
        #endregion

        #region Young system
        [CommandProperty(AccessLevel.GameMaster)]
        public bool Young
        {
            get { return GetFlag(PlayerFlag.Young); }
            set
            {
                SetFlag(PlayerFlag.Young, value);
                InvalidateProperties();
            }
        }

        public override string ApplyNameSuffix(string suffix)
        {
            if (Young)
            {
                if (suffix.Length == 0)
                {
                    suffix = "(Young)";
                }
                else
                {
                    suffix = string.Concat(suffix, " (Young)");
                }
            }

            return base.ApplyNameSuffix(suffix);
        }

        public override TimeSpan GetLogoutDelay()
        {
            if (Young || BedrollLogout || BlanketOfDarknessLogout || TestCenter.Enabled)
            {
                return TimeSpan.Zero;
            }

            return base.GetLogoutDelay();
        }

        private DateTime m_LastYoungMessage = DateTime.MinValue;

        public bool CheckYoungProtection(Mobile from)
        {
            if (!Young)
            {
                return false;
            }

            if (Region is BaseRegion && !((BaseRegion)Region).YoungProtected)
            {
                return false;
            }

            if (from is BaseCreature creature && creature.IgnoreYoungProtection)
            {
                return false;
            }

            if (Quest != null && Quest.IgnoreYoungProtection(from))
            {
                return false;
            }

            if (DateTime.UtcNow - m_LastYoungMessage > TimeSpan.FromMinutes(1.0))
            {
                m_LastYoungMessage = DateTime.UtcNow;
                SendLocalizedMessage(1019067);
                // A monster looks at you menacingly but does not attack.  You would be under attack now if not for your status as a new citizen of Britannia.
            }

            return true;
        }

        private DateTime m_LastYoungHeal = DateTime.MinValue;

        public bool CheckYoungHealTime()
        {
            if (DateTime.UtcNow - m_LastYoungHeal > TimeSpan.FromMinutes(5.0))
            {
                m_LastYoungHeal = DateTime.UtcNow;
                return true;
            }

            return false;
        }

        private static readonly Point3D[] m_TrammelDeathDestinations = new[]
        {
            new Point3D(1481, 1612, 20), new Point3D(2708, 2153, 0), new Point3D(2249, 1230, 0), new Point3D(5197, 3994, 37),
            new Point3D(1412, 3793, 0), new Point3D(3688, 2232, 20), new Point3D(2578, 604, 0), new Point3D(4397, 1089, 0),
            new Point3D(5741, 3218, -2), new Point3D(2996, 3441, 15), new Point3D(624, 2225, 0), new Point3D(1916, 2814, 0),
            new Point3D(2929, 854, 0), new Point3D(545, 967, 0), new Point3D(3469, 2559, 36)
        };

        private static readonly Point3D[] m_IlshenarDeathDestinations = new[]
        {
            new Point3D(1216, 468, -13), new Point3D(723, 1367, -60), new Point3D(745, 725, -28), new Point3D(281, 1017, 0),
            new Point3D(986, 1011, -32), new Point3D(1175, 1287, -30), new Point3D(1533, 1341, -3), new Point3D(529, 217, -44),
            new Point3D(1722, 219, 96)
        };

        private static readonly Point3D[] m_MalasDeathDestinations = new[]
        {new Point3D(2079, 1376, -70), new Point3D(944, 519, -71)};

        private static readonly Point3D[] m_TokunoDeathDestinations = new[]
        {new Point3D(1166, 801, 27), new Point3D(782, 1228, 25), new Point3D(268, 624, 15)};

        public bool YoungDeathTeleport()
        {
            if (Region.IsPartOf<Jail>() || Region.IsPartOf("Samurai start location") ||
                Region.IsPartOf("Ninja start location") || Region.IsPartOf("Ninja cave"))
            {
                return false;
            }

            Point3D loc;
            Map map;

            DungeonRegion dungeon = (DungeonRegion)Region.GetRegion(typeof(DungeonRegion));
            if (dungeon != null && dungeon.EntranceLocation != Point3D.Zero)
            {
                loc = dungeon.EntranceLocation;
                map = dungeon.EntranceMap;
            }
            else
            {
                loc = Location;
                map = Map;
            }

            Point3D[] list;

            if (map == Map.Trammel)
            {
                list = m_TrammelDeathDestinations;
            }
            else if (map == Map.Ilshenar)
            {
                list = m_IlshenarDeathDestinations;
            }
            else if (map == Map.Malas)
            {
                list = m_MalasDeathDestinations;
            }
            else if (map == Map.Tokuno)
            {
                list = m_TokunoDeathDestinations;
            }
            else
            {
                return false;
            }

            Point3D dest = Point3D.Zero;
            int sqDistance = int.MaxValue;

            for (int i = 0; i < list.Length; i++)
            {
                Point3D curDest = list[i];

                int width = loc.X - curDest.X;
                int height = loc.Y - curDest.Y;
                int curSqDistance = width * width + height * height;

                if (curSqDistance < sqDistance)
                {
                    dest = curDest;
                    sqDistance = curSqDistance;
                }
            }

            MoveToWorld(dest, map);
            return true;
        }

        private void SendYoungDeathNotice()
        {
            SendGump(new YoungDeathNotice());
        }
        #endregion

        #region Speech
        private SpeechLog m_SpeechLog;
        private bool m_TempSquelched;

        public SpeechLog SpeechLog => m_SpeechLog;

        [CommandProperty(AccessLevel.Administrator)]
        public bool TempSquelched { get { return m_TempSquelched; } set { m_TempSquelched = value; } }

        public override void OnSpeech(SpeechEventArgs e)
        {
            if (SpeechLog.Enabled && NetState != null)
            {
                if (m_SpeechLog == null)
                {
                    m_SpeechLog = new SpeechLog();
                }

                m_SpeechLog.Add(e.Mobile, e.Speech);
            }
        }

        public override void OnSaid(SpeechEventArgs e)
        {
            if (m_TempSquelched)
            {
                SendLocalizedMessage(500168); // You can not say anything, you have been muted.
                e.Blocked = true;
            }
            else
            {
                base.OnSaid(e);
            }
        }
        #endregion

        #region Champion Titles
        [CommandProperty(AccessLevel.GameMaster)]
        public bool DisplayChampionTitle { get { return GetFlag(PlayerFlag.DisplayChampionTitle); } set { SetFlag(PlayerFlag.DisplayChampionTitle, value); } }

        private ChampionTitleInfo m_ChampionTitles;

        [CommandProperty(AccessLevel.GameMaster)]
        public ChampionTitleInfo ChampionTitles { get { return m_ChampionTitles; } set { } }

        [PropertyObject]
        public class ChampionTitleInfo
        {
            public static TimeSpan LossDelay = TimeSpan.FromDays(1.0);
            public const int LossAmount = 90;

            private class TitleInfo
            {
                private int m_Value;
                private DateTime m_LastDecay;

                public int Value { get { return m_Value; } set { m_Value = value; } }
                public DateTime LastDecay { get { return m_LastDecay; } set { m_LastDecay = value; } }

                public TitleInfo()
                { }

                public TitleInfo(GenericReader reader)
                {
                    int version = reader.ReadEncodedInt();

                    switch (version)
                    {
                        case 0:
                            {
                                m_Value = reader.ReadEncodedInt();
                                m_LastDecay = reader.ReadDateTime();
                                break;
                            }
                    }
                }

                public static void Serialize(GenericWriter writer, TitleInfo info)
                {
                    writer.WriteEncodedInt(0); // version

                    writer.WriteEncodedInt(info.m_Value);
                    writer.Write(info.m_LastDecay);
                }
            }

            private TitleInfo[] m_Values;

            private int m_Harrower; //Harrower titles do NOT decay

            public int GetValue(ChampionSpawnType type)
            {
                return GetValue((int)type);
            }

            public void SetValue(ChampionSpawnType type, int value)
            {
                SetValue((int)type, value);
            }

            public void Award(ChampionSpawnType type, int value)
            {
                Award((int)type, value);
            }

            public int GetValue(int index)
            {
                if (m_Values == null || index < 0 || index >= m_Values.Length)
                {
                    return 0;
                }

                if (m_Values[index] == null)
                {
                    m_Values[index] = new TitleInfo();
                }

                return m_Values[index].Value;
            }

            public DateTime GetLastDecay(int index)
            {
                if (m_Values == null || index < 0 || index >= m_Values.Length)
                {
                    return DateTime.MinValue;
                }

                if (m_Values[index] == null)
                {
                    m_Values[index] = new TitleInfo();
                }

                return m_Values[index].LastDecay;
            }

            public void SetValue(int index, int value)
            {
                if (m_Values == null)
                {
                    m_Values = new TitleInfo[ChampionSpawnInfo.Table.Length];
                }

                if (value < 0)
                {
                    value = 0;
                }

                if (index < 0 || index >= m_Values.Length)
                {
                    return;
                }

                if (m_Values[index] == null)
                {
                    m_Values[index] = new TitleInfo();
                }

                m_Values[index].Value = value;
            }

            public void Award(int index, int value)
            {
                if (m_Values == null)
                {
                    m_Values = new TitleInfo[ChampionSpawnInfo.Table.Length];
                }

                if (index < 0 || index >= m_Values.Length || value <= 0)
                {
                    return;
                }

                if (m_Values[index] == null)
                {
                    m_Values[index] = new TitleInfo();
                }

                m_Values[index].Value += value;
            }

            public void Atrophy(int index, int value)
            {
                if (m_Values == null)
                {
                    m_Values = new TitleInfo[ChampionSpawnInfo.Table.Length];
                }

                if (index < 0 || index >= m_Values.Length || value <= 0)
                {
                    return;
                }

                if (m_Values[index] == null)
                {
                    m_Values[index] = new TitleInfo();
                }

                int before = m_Values[index].Value;

                if ((m_Values[index].Value - value) < 0)
                {
                    m_Values[index].Value = 0;
                }
                else
                {
                    m_Values[index].Value -= value;
                }

                if (before != m_Values[index].Value)
                {
                    m_Values[index].LastDecay = DateTime.UtcNow;
                }
            }

            public override string ToString()
            {
                return "...";
            }

            [CommandProperty(AccessLevel.GameMaster)]
            public int Abyss { get { return GetValue(ChampionSpawnType.Abyss); } set { SetValue(ChampionSpawnType.Abyss, value); } }

            [CommandProperty(AccessLevel.GameMaster)]
            public int Arachnid { get { return GetValue(ChampionSpawnType.Arachnid); } set { SetValue(ChampionSpawnType.Arachnid, value); } }

            [CommandProperty(AccessLevel.GameMaster)]
            public int ColdBlood { get { return GetValue(ChampionSpawnType.ColdBlood); } set { SetValue(ChampionSpawnType.ColdBlood, value); } }

            [CommandProperty(AccessLevel.GameMaster)]
            public int ForestLord { get { return GetValue(ChampionSpawnType.ForestLord); } set { SetValue(ChampionSpawnType.ForestLord, value); } }

            [CommandProperty(AccessLevel.GameMaster)]
            public int SleepingDragon { get { return GetValue(ChampionSpawnType.SleepingDragon); } set { SetValue(ChampionSpawnType.SleepingDragon, value); } }

            [CommandProperty(AccessLevel.GameMaster)]
            public int UnholyTerror { get { return GetValue(ChampionSpawnType.UnholyTerror); } set { SetValue(ChampionSpawnType.UnholyTerror, value); } }

            [CommandProperty(AccessLevel.GameMaster)]
            public int VerminHorde { get { return GetValue(ChampionSpawnType.VerminHorde); } set { SetValue(ChampionSpawnType.VerminHorde, value); } }

            [CommandProperty(AccessLevel.GameMaster)]
            public int Harrower { get { return m_Harrower; } set { m_Harrower = value; } }

            #region Mondain's Legacy Peerless Champion
            [CommandProperty(AccessLevel.GameMaster)]
            public int Glade { get { return GetValue(ChampionSpawnType.Glade); } set { SetValue(ChampionSpawnType.Glade, value); } }

            [CommandProperty(AccessLevel.GameMaster)]
            public int Corrupt { get { return GetValue(ChampionSpawnType.Corrupt); } set { SetValue(ChampionSpawnType.Corrupt, value); } }
            #endregion

            public ChampionTitleInfo()
            { }

            public ChampionTitleInfo(GenericReader reader)
            {
                int version = reader.ReadEncodedInt();

                switch (version)
                {
                    case 0:
                        {
                            m_Harrower = reader.ReadEncodedInt();

                            int length = reader.ReadEncodedInt();
                            m_Values = new TitleInfo[length];

                            for (int i = 0; i < length; i++)
                            {
                                m_Values[i] = new TitleInfo(reader);
                            }

                            if (m_Values.Length != ChampionSpawnInfo.Table.Length)
                            {
                                TitleInfo[] oldValues = m_Values;
                                m_Values = new TitleInfo[ChampionSpawnInfo.Table.Length];

                                for (int i = 0; i < m_Values.Length && i < oldValues.Length; i++)
                                {
                                    m_Values[i] = oldValues[i];
                                }
                            }
                            break;
                        }
                }
            }

            public static void Serialize(GenericWriter writer, ChampionTitleInfo titles)
            {
                writer.WriteEncodedInt(0); // version

                writer.WriteEncodedInt(titles.m_Harrower);

                int length = titles.m_Values.Length;
                writer.WriteEncodedInt(length);

                for (int i = 0; i < length; i++)
                {
                    if (titles.m_Values[i] == null)
                    {
                        titles.m_Values[i] = new TitleInfo();
                    }

                    TitleInfo.Serialize(writer, titles.m_Values[i]);
                }
            }

            public static void CheckAtrophy(PlayerMobile pm)
            {
                ChampionTitleInfo t = pm.m_ChampionTitles;
                if (t == null)
                {
                    return;
                }

                if (t.m_Values == null)
                {
                    t.m_Values = new TitleInfo[ChampionSpawnInfo.Table.Length];
                }

                for (int i = 0; i < t.m_Values.Length; i++)
                {
                    if ((t.GetLastDecay(i) + LossDelay) < DateTime.UtcNow)
                    {
                        t.Atrophy(i, LossAmount);
                    }
                }
            }

            public static void AwardHarrowerTitle(PlayerMobile pm)
            //Called when killing a harrower.  Will give a minimum of 1 point.
            {
                ChampionTitleInfo t = pm.m_ChampionTitles;
                if (t == null)
                {
                    return;
                }

                if (t.m_Values == null)
                {
                    t.m_Values = new TitleInfo[ChampionSpawnInfo.Table.Length];
                }

                int count = 1;

                for (int i = 0; i < t.m_Values.Length; i++)
                {
                    if (t.m_Values[i].Value > 900)
                    {
                        count++;
                    }
                }

                t.m_Harrower = Math.Max(count, t.m_Harrower); //Harrower titles never decay.
            }

            public bool HasChampionTitle(PlayerMobile pm)
            {
                if (m_Harrower > 0)
                    return true;

                if (m_Values == null)
                    return false;

                foreach (TitleInfo info in m_Values)
                {
                    if (info.Value > 300)
                        return true;
                }

                return false;
            }
        }
        #endregion

        #region Recipes
        private Dictionary<int, bool> m_AcquiredRecipes;

        public virtual bool HasRecipe(Recipe r)
        {
            if (r == null)
            {
                return false;
            }

            return HasRecipe(r.ID);
        }

        public virtual bool HasRecipe(int recipeID)
        {
            if (m_AcquiredRecipes != null && m_AcquiredRecipes.ContainsKey(recipeID))
            {
                return m_AcquiredRecipes[recipeID];
            }

            return false;
        }

        public virtual void AcquireRecipe(Recipe r)
        {
            if (r != null)
            {
                AcquireRecipe(r.ID);
            }
        }

        public virtual void AcquireRecipe(int recipeID)
        {
            if (m_AcquiredRecipes == null)
            {
                m_AcquiredRecipes = new Dictionary<int, bool>();
            }

            m_AcquiredRecipes[recipeID] = true;
        }

        public virtual void ResetRecipes()
        {
            m_AcquiredRecipes = null;
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public int KnownRecipes
        {
            get
            {
                if (m_AcquiredRecipes == null)
                {
                    return 0;
                }

                return m_AcquiredRecipes.Count;
            }
        }
        #endregion

        #region Buff Icons
        public void ResendBuffs()
        {
            if (m_BuffTable == null)
            {
                return;
            }

            NetState state = NetState;

            if (state != null)
            {
                foreach (BuffInfo info in m_BuffTable.Values)
                {
                    state.Send(new AddBuffPacket(this, info));
                }
            }
        }

        private Dictionary<BuffIcon, BuffInfo> m_BuffTable;

        public void AddBuff(BuffInfo b)
        {
            if (b == null)
            {
                return;
            }

            RemoveBuff(b); //Check & subsequently remove the old one.

            if (m_BuffTable == null)
            {
                m_BuffTable = new Dictionary<BuffIcon, BuffInfo>();
            }

            m_BuffTable.Add(b.ID, b);

            NetState state = NetState;

            if (state != null)
            {
                state.Send(new AddBuffPacket(this, b));
            }
        }

        public void RemoveBuff(BuffInfo b)
        {
            if (b == null)
            {
                return;
            }

            RemoveBuff(b.ID);
        }

        public void RemoveBuff(BuffIcon b)
        {
            if (m_BuffTable == null || !m_BuffTable.ContainsKey(b))
            {
                return;
            }

            BuffInfo info = m_BuffTable[b];

            if (info.Timer != null && info.Timer.Running)
            {
                info.Timer.Stop();
            }

            m_BuffTable.Remove(b);

            NetState state = NetState;

            if (state != null)
            {
                state.Send(new RemoveBuffPacket(this, b));
            }

            if (m_BuffTable.Count <= 0)
            {
                m_BuffTable = null;
            }
        }
        #endregion

        [CommandProperty(AccessLevel.GameMaster)]
        public ExploringTheDeepQuestChain ExploringTheDeepQuest { get; set; }

        public void AutoStablePets()
        {
            if (AllFollowers.Count > 0)
            {
                for (int i = m_AllFollowers.Count - 1; i >= 0; --i)
                {
                    BaseCreature pet = AllFollowers[i] as BaseCreature;

                    if (pet == null)
                    {
                        continue;
                    }

                    if (pet.Summoned && pet.Map != Map)
                    {
                        pet.PlaySound(pet.GetAngerSound());

                        Timer.DelayCall(pet.Delete);

                        continue;
                    }

                    if (!pet.CanAutoStable || Stabled.Count >= AnimalTrainer.GetMaxStabled(this))
                    {
                        continue;
                    }

                    pet.ControlTarget = null;
                    pet.ControlOrder = OrderType.Stay;
                    pet.Internalize();

                    pet.SetControlMaster(null);
                    pet.SummonMaster = null;

                    pet.IsStabled = true;
                    pet.StabledBy = this;

                    Stabled.Add(pet);
                    m_AutoStabled.Add(pet);
                }
            }
        }

        public void ClaimAutoStabledPets()
        {
            if (!Region.AllowAutoClaim(this) || m_AutoStabled.Count <= 0)
            {
                return;
            }

            if (!Alive)
            {
                SendGump(new ReLoginClaimGump());
                return;
            }

            for (int i = m_AutoStabled.Count - 1; i >= 0; --i)
            {
                BaseCreature pet = m_AutoStabled[i] as BaseCreature;

                if (pet == null || pet.Deleted)
                {
                    if (pet != null)
                    {
                        pet.IsStabled = false;
                        pet.StabledBy = null;

                        if (Stabled.Contains(pet))
                        {
                            Stabled.Remove(pet);
                        }
                    }

                    continue;
                }

                if ((Followers + pet.ControlSlots) <= FollowersMax)
                {
                    pet.SetControlMaster(this);

                    if (pet.Summoned)
                    {
                        pet.SummonMaster = this;
                    }

                    pet.ControlTarget = this;
                    pet.ControlOrder = OrderType.Follow;

                    pet.MoveToWorld(Location, Map);

                    pet.IsStabled = false;
                    pet.StabledBy = null;

                    if (Stabled.Contains(pet))
                    {
                        Stabled.Remove(pet);
                    }
                }
                else
                {
                    SendLocalizedMessage(1049612, pet.Name); // ~1_NAME~ remained in the stables because you have too many followers.
                }
            }

            m_AutoStabled.Clear();
        }
    }
}

BaseCreature.cs:
#region References
using Server.ContextMenus;
using Server.Engines.PartySystem;
using Server.Engines.Quests.Doom;
using Server.Engines.VvV;
using Server.Items;
using Server.Misc;
using Server.Multis;
using Server.Network;
using Server.Prompts;
using Server.Regions;
using Server.Services.Virtues;
using Server.SkillHandlers;
using Server.Spells;
using Server.Spells.Bushido;
using Server.Spells.Necromancy;
using Server.Spells.Sixth;
using Server.Spells.SkillMasteries;
using Server.Spells.Spellweaving;
using Server.Targeting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

using daat99;
#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,
        BlackrockStew = 0x0080
    }

    [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,
        //daat99 OWLTR start - custom scales 1
        Copper,
        Silver,
        Gold,
        //daat99 OWLTR end - custom scales 1
        All
    }

    public enum MeatType
    {
        Ribs,
        Bird,
        LambLeg,
        Rotworm,
        DinoRibs,
        SeaSerpentSteak
    }

    public enum HideType
    {
        Regular,
        Spined,
        Horned,
//daat99 OWLTR start - custom hides
        Polar,
        Synthetic,
        BlazeL,
        Daemonic,
        Shadow,
        Frost,
        Ethereal,
        //daat99 OWLTR end - custom hides
        Barbed
    }

    public enum FurType
    {
        None,
        Green,
        LightBrown,
        Yellow,
        Brown
    }

    public enum TribeType
    {
        None,
        Terathan,
        Ophidian,
        Savage,
        Orc,
        Fey,
        Undead,
        GrayGoblin,
        GreenGoblin
    }

    public enum LootStage
    {
        Spawning,
        Stolen,
        Death
    }
    #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 => m_FriendlyName;

        public FriendlyNameAttribute(TextDefinition friendlyName)
        {
            m_FriendlyName = friendlyName;
        }

        public static TextDefinition GetFriendlyNameFor(Type t)
        {
            if (t.IsDefined(typeof(FriendlyNameAttribute), false))
            {
                object[] 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, IEngravable
    {
        /*#region FS:ATS Edits
        private int m_RoarAttack;
        private int m_PetPoisonAttack;
        private int m_FireBreathAttack;

        private bool m_IsMating;

        private int m_ABPoints;
        private int m_Exp;
        private int m_NextLevel;
        private int m_Level = 1;
        private int m_MaxLevel;

        private bool m_AllowMating;

        private bool m_Evolves;
        private int m_Gen = 1;

        private DateTime m_MatingDelay;

        private int m_Form1;
        private int m_Form2;
        private int m_Form3;
        private int m_Form4;
        private int m_Form5;
        private int m_Form6;
        private int m_Form7;
        private int m_Form8;
        private int m_Form9;

        private int m_Sound1;
        private int m_Sound2;
        private int m_Sound3;
        private int m_Sound4;
        private int m_Sound5;
        private int m_Sound6;
        private int m_Sound7;
        private int m_Sound8;
        private int m_Sound9;

        private bool m_UsesForm1;
        private bool m_UsesForm2;
        private bool m_UsesForm3;
        private bool m_UsesForm4;
        private bool m_UsesForm5;
        private bool m_UsesForm6;
        private bool m_UsesForm7;
        private bool m_UsesForm8;
        private bool m_UsesForm9;

        public bool m_F0;
        public bool m_F1;
        public bool m_F2;
        public bool m_F3;
        public bool m_F4;
        public bool m_F5;
        public bool m_F6;
        public bool m_F7;
        public bool m_F8;
        public bool m_F9;

        [CommandProperty( AccessLevel.GameMaster )]
        public int RoarAttack
        {
            get{ return m_RoarAttack; }
            set{ m_RoarAttack = value; }
        }

        [CommandProperty( AccessLevel.GameMaster )]
        public int PetPoisonAttack
        {
            get{ return m_PetPoisonAttack; }
            set{ m_PetPoisonAttack = value; }
        }

        [CommandProperty( AccessLevel.GameMaster )]
        public int FireBreathAttack
        {
            get{ return m_FireBreathAttack; }
            set{ m_FireBreathAttack = value; }
        }

        [CommandProperty( AccessLevel.Administrator )]
        public DateTime MatingDelay
        {
            get{ return m_MatingDelay; }
            set{ m_MatingDelay = value; }
        }

        [CommandProperty( AccessLevel.GameMaster )]
        public int Generation
        {
            get{ return m_Gen; }
            set{ m_Gen = value; }
        }

        [CommandProperty( AccessLevel.GameMaster )]
        public int AbilityPoints
        {
            get{ return m_ABPoints; }
            set{ m_ABPoints = value; }
        }

        [CommandProperty( AccessLevel.GameMaster )]
        public int Exp
        {
            get{ return m_Exp; }
            set{ m_Exp = value; }
        }

        [CommandProperty( AccessLevel.GameMaster )]
        public int NextLevel
        {
            get{ return m_NextLevel; }
            set{ m_NextLevel = value; }
        }

        [CommandProperty( AccessLevel.GameMaster )]
        public int Level
        {
            get{ return m_Level; }
            set{ m_Level = value; }
        }

        [CommandProperty( AccessLevel.GameMaster )]
        public int MaxLevel
        {
            get{ return m_MaxLevel; }
            set{ m_MaxLevel = value; }
        }

        [CommandProperty( AccessLevel.GameMaster )]
        public bool Evolves
        {
            get{ return m_Evolves; }
            set{ m_Evolves = value; }
        }

        [CommandProperty( AccessLevel.GameMaster )]
        public bool AllowMating
        {
            get{ return m_AllowMating; }
            set{ m_AllowMating = value; }
        }

        public int Form1
        {
            get{ return m_Form1; }
            set{ m_Form1 = value; }
        }

        public int Form2
        {
            get{ return m_Form2; }
            set{ m_Form2 = value; }
        }

        public int Form3
        {
            get{ return m_Form3; }
            set{ m_Form3 = value; }
        }

        public int Form4
        {
            get{ return m_Form4; }
            set{ m_Form4 = value; }
        }

        public int Form5
        {
            get{ return m_Form5; }
            set{ m_Form5 = value; }
        }

        public int Form6
        {
            get{ return m_Form6; }
            set{ m_Form6 = value; }
        }

        public int Form7
        {
            get{ return m_Form7; }
            set{ m_Form7 = value; }
        }

        public int Form8
        {
            get{ return m_Form8; }
            set{ m_Form8 = value; }
        }

        public int Form9
        {
            get{ return m_Form9; }
            set{ m_Form9 = value; }
        }

        public int Sound1
        {
            get{ return m_Sound1; }
            set{ m_Sound1 = value; }
        }

        public int Sound2
        {
            get{ return m_Sound2; }
            set{ m_Sound2 = value; }
        }

        public int Sound3
        {
            get{ return m_Sound3; }
            set{ m_Sound3 = value; }
        }

        public int Sound4
        {
            get{ return m_Sound4; }
            set{ m_Sound4 = value; }
        }

        public int Sound5
        {
            get{ return m_Sound5; }
            set{ m_Sound5 = value; }
        }

        public int Sound6
        {
            get{ return m_Sound6; }
            set{ m_Sound6 = value; }
        }

        public int Sound7
        {
            get{ return m_Sound7; }
            set{ m_Sound7 = value; }
        }

        public int Sound8
        {
            get{ return m_Sound8; }
            set{ m_Sound8 = value; }
        }

        public int Sound9
        {
            get{ return m_Sound9; }
            set{ m_Sound9 = value; }
        }

        public bool UsesForm1
        {
            get{ return m_UsesForm1; }
            set{ m_UsesForm1 = value; }
        }

        public bool UsesForm2
        {
            get{ return m_UsesForm2; }
            set{ m_UsesForm2 = value; }
        }

        public bool UsesForm3
        {
            get{ return m_UsesForm3; }
            set{ m_UsesForm3 = value; }
        }

        public bool UsesForm4
        {
            get{ return m_UsesForm4; }
            set{ m_UsesForm4 = value; }
        }

        public bool UsesForm5
        {
            get{ return m_UsesForm5; }
            set{ m_UsesForm5 = value; }
        }

        public bool UsesForm6
        {
            get{ return m_UsesForm6; }
            set{ m_UsesForm6 = value; }
        }

        public bool UsesForm7
        {
            get{ return m_UsesForm7; }
            set{ m_UsesForm7 = value; }
        }

        public bool UsesForm8
        {
            get{ return m_UsesForm8; }
            set{ m_UsesForm8 = value; }
        }

        public bool UsesForm9
        {
            get{ return m_UsesForm9; }
            set{ m_UsesForm9 = value; }
        }

        public bool F0
        {
            get{ return m_F0; }
            set{ m_F0 = value; }
        }

        public bool F1
        {
            get{ return m_F1; }
            set{ m_F1 = value; }
        }

        public bool F2
        {
            get{ return m_F2; }
            set{ m_F2 = value; }
        }

        public bool F3
        {
            get{ return m_F3; }
            set{ m_F3 = value; }
        }

        public bool F4
        {
            get{ return m_F4; }
            set{ m_F4 = value; }
        }

        public bool F5
        {
            get{ return m_F5; }
            set{ m_F5 = value; }
        }

        public bool F6
        {
            get{ return m_F6; }
            set{ m_F6 = value; }
        }

        public bool F7
        {
            get{ return m_F7; }
            set{ m_F7 = value; }
        }

        public bool F8
        {
            get{ return m_F8; }
            set{ m_F8 = value; }
        }

        public bool F9
        {
            get{ return m_F9; }
            set{ m_F9 = value; }
        }
        #endregion    */
        public const int MaxLoyalty = 100;

        private bool _LockDirection;

        [CommandProperty(AccessLevel.GameMaster)]
        public bool LockDirection
        {
            get
            {
                if (AIObject == null)
                {
                    return _LockDirection;
                }

                return AIObject.DirectionLocked = _LockDirection;
            }
            set
            {
                _LockDirection = value;

                if (AIObject != null)
                {
                    AIObject.DirectionLocked = value;
                }
            }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool CanMove { get; set; }

        public virtual bool CanCallGuards => !Deleted && Alive && !AlwaysMurderer && Kills < 5 && (Player || Body.IsHuman);

        #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_ForceActiveSpeed;
        private double m_ForcePassiveSpeed;

        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 double m_CurrentTameSkill;
        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

        public virtual InhumanSpeech SpeechType => null;

        public virtual bool ForceStayHome => false;

        public int FollowRange { get; set; }

        public virtual bool CanBeParagon => 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; }

        public DateTime SummonEnd { get { return m_SummonEnd; } set { m_SummonEnd = value; } }

        public virtual int DefaultHitsRegen
        {
            get
            {
                int regen = 0;

                if (IsAnimatedDead)
                    regen = 4;

                if (IsParagon)
                    regen += 40;

                regen += HumilityVirtue.GetRegenBonus(this);

                if (AbilityProfile != null)
                    regen += AbilityProfile.RegenHits;

                return regen;
            }
        }

        public virtual int DefaultStamRegen
        {
            get
            {
                int regen = 0;

                regen += MasteryInfo.EnchantedSummoningBonus(this);

                if (IsParagon)
                    regen += 40;

                if (AbilityProfile != null)
                    regen += AbilityProfile.RegenStam;

                return regen;
            }
        }

        public virtual int DefaultManaRegen
        {
            get
            {
                int regen = 0;

                if (IsParagon)
                    regen += 40;

                if (AbilityProfile != null)
                    regen += AbilityProfile.RegenMana;

                return regen;
            }
        }

        #region Bonding
        public const bool BondingEnabled = true;

        public virtual bool IsBondable => (BondingEnabled && !Summoned && !m_Allured && !(this is IRepairableMobile));
        public virtual TimeSpan BondingDelay => TimeSpan.FromDays(7.0);
        public virtual TimeSpan BondingAbandonDelay => TimeSpan.FromDays(1.0);

        public override bool CanRegenHits => !m_IsDeadPet && !Summoned && base.CanRegenHits;
        public override bool CanRegenStam => !IsParagon && !m_IsDeadPet && base.CanRegenStam;
        public override bool CanRegenMana => !m_IsDeadPet && base.CanRegenMana;

        public override TimeSpan CorpseDecayTime { get { return IsChampionSpawn ? TimeSpan.FromMinutes(1) : base.CorpseDecayTime; } }

        public override bool IsDeadBondedPet => m_IsDeadPet;

        private bool m_IsBonded;
        private bool m_IsDeadPet;
        private DateTime m_BondingBegin;
        private DateTime m_OwnerAbandonTime;
        private DateTime m_DeleteTime;

        [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; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public DateTime DeleteTime
        {
            get { return m_DeleteTime; }
            set
            {
                m_DeleteTime = value;

                if (m_DeleteTime != DateTime.MinValue)
                {
                    CreatureDeleteTimer.RegisterTimer(this);
                }
                else
                {
                    CreatureDeleteTimer.RemoveFromTimer(this);
                }
            }
        }
        #endregion

        #region IEngravable Members
        private string m_EngravedText;

        [CommandProperty(AccessLevel.GameMaster)]
        public string EngravedText
        {
            get { return m_EngravedText != null ? Utility.FixHtml(m_EngravedText) : null; }
            set
            {
                m_EngravedText = value;
                InvalidateProperties();
            }
        }
        #endregion

        #region Pet Training
        public static double MaxTameRequirement = 108.0;

        private AbilityProfile _Profile;
        private TrainingProfile _TrainingProfile;

        [CommandProperty(AccessLevel.GameMaster)]
        public AbilityProfile AbilityProfile { get { return _Profile; } set { _Profile = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public TrainingProfile TrainingProfile { get { return _TrainingProfile; } set { _TrainingProfile = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public double BardingDifficulty => BaseInstrument.GetBaseDifficulty(this);

        public virtual WeaponAbility TryGetWeaponAbility()
        {
            if (_Profile != null && _Profile.WeaponAbilities != null && _Profile.WeaponAbilities.Length > 0)
            {
                return _Profile.WeaponAbilities[Utility.Random(_Profile.WeaponAbilities.Length)];
            }
            else
            {
                return GetWeaponAbility();
            }
        }

        public virtual TrainingDefinition TrainingDefinition => null;

        public virtual void InitializeAbilities()
        {
            switch (AI)
            {
                case AIType.AI_Mage: SetMagicalAbility(MagicalAbility.Magery); break;
                case AIType.AI_NecroMage: SetMagicalAbility(!Controlled ? MagicalAbility.Necromancy : MagicalAbility.Necromage); break;
                case AIType.AI_Necro: SetMagicalAbility(MagicalAbility.Necromancy); break;
                case AIType.AI_Spellweaving: SetMagicalAbility(MagicalAbility.Spellweaving); break;
                case AIType.AI_Mystic: SetMagicalAbility(MagicalAbility.Mysticism); break;
                case AIType.AI_Samurai: SetMagicalAbility(MagicalAbility.Bushido); break;
                case AIType.AI_Ninja: SetMagicalAbility(MagicalAbility.Ninjitsu); break;
                case AIType.AI_Paladin: SetMagicalAbility(MagicalAbility.Chivalry); break;
            }

            if (HealChance > 0.0 && HealChance >= Utility.RandomDouble())
            {
                SetSpecialAbility(SpecialAbility.Heal);
            }

            if (Skills[SkillName.Focus].Value == 0)
                SetSkill(SkillName.Focus, 2, 20);

            if (Skills[SkillName.DetectHidden].Value == 0 && !(this is BaseVendor))
                SetSkill(SkillName.DetectHidden, Utility.RandomList(10, 60));
        }

        public void SetMagicalAbility(MagicalAbility ability)
        {
            PetTrainingHelper.GetAbilityProfile(this, true).AddAbility(ability, false);
        }

        public void SetSpecialAbility(SpecialAbility ability)
        {
            PetTrainingHelper.GetAbilityProfile(this, true).AddAbility(ability, false);
        }

        public void SetAreaEffect(AreaEffect ability)
        {
            PetTrainingHelper.GetAbilityProfile(this, true).AddAbility(ability, false);
        }

        public void SetWeaponAbility(WeaponAbility ability)
        {
            PetTrainingHelper.GetAbilityProfile(this, true).AddAbility(ability, false);
        }

        public void RemoveMagicalAbility(MagicalAbility ability)
        {
            PetTrainingHelper.GetAbilityProfile(this, true).RemoveAbility(ability);
        }

        public void RemoveSpecialAbility(SpecialAbility ability)
        {
            PetTrainingHelper.GetAbilityProfile(this, true).RemoveAbility(ability);
        }

        public void RemoveAreaEffect(AreaEffect ability)
        {
            PetTrainingHelper.GetAbilityProfile(this, true).RemoveAbility(ability);
        }

        public void RemoveWeaponAbility(WeaponAbility ability)
        {
            PetTrainingHelper.GetAbilityProfile(this, true).RemoveAbility(ability);
        }

        public bool HasAbility(object o)
        {
            return PetTrainingHelper.GetAbilityProfile(this, true).HasAbility(o);
        }

        public virtual double AverageThreshold => 0.33;

        public List<double> _InitAverage;

        private void SetAverage(double min, double max, double value)
        {
            if (CanLowerSlot() && max > min)
            {
                if (_InitAverage == null)
                    _InitAverage = new List<double>();

                _InitAverage.Add((value - min) / (max - min));
            }
        }

        public static Type[] SlotLowerables => _SlotLowerables;
        private static readonly Type[] _SlotLowerables =
        {
            typeof(Nightmare), typeof(Najasaurus), typeof(RuneBeetle), typeof(GreaterDragon), typeof(FrostDragon),
            typeof(WhiteWyrm), typeof(Reptalon), typeof(DragonTurtleHatchling), typeof(Phoenix), typeof(FrostMite),
            typeof(DireWolf), typeof(Skree), typeof(HighPlainsBoura), typeof(LesserHiryu), typeof(DragonWolf),
            typeof(BloodFox)
        };

        private bool CanLowerSlot()
        {
            return _SlotLowerables.Any(t => t == GetType());
        }

        public void CalculateSlots(int slots)
        {
            TrainingDefinition def = PetTrainingHelper.GetTrainingDefinition(this);

            if (def == null)
            {
                ControlSlotsMin = slots;
                ControlSlotsMax = slots;
                return;
            }
            else
            {
                ControlSlotsMin = def.ControlSlotsMin;
                ControlSlotsMax = def.ControlSlotsMax;
            }

            if (_InitAverage == null)
                return;

            double total = _InitAverage.Sum(d => d);

            if (total / _InitAverage.Count <= AverageThreshold)
            {
                ControlSlotsMin = Math.Max(1, ControlSlotsMin - 1);
            }

            ColUtility.Free(_InitAverage);
            _InitAverage = null;
        }

        public void AdjustTameRequirements()
        {
            if (ControlSlots <= ControlSlotsMin)
            {
                CurrentTameSkill = MinTameSkill;
            }
            else
            {
                CurrentTameSkill = ((ControlSlots - ControlSlotsMin) * 21) + 1;
            }

            if (CurrentTameSkill > MaxTameRequirement)
            {
                CurrentTameSkill = MaxTameRequirement;
            }
        }
        #endregion

        #region Skill Masteries
        private SkillName _Mastery;

        [CommandProperty(AccessLevel.GameMaster)]
        public SkillName Mastery
        {
            get { return _Mastery; }
            set
            {
                SkillName old = _Mastery;
                _Mastery = value;

                if (old != _Mastery)
                {
                    UpdateMasteryInfo();
                }
            }
        }

        public virtual MasteryInfo[] Masteries { get; set; }
        public DateTime NextMastery { get; set; }

        public void UpdateMasteryInfo()
        {
            if (_Mastery == SkillName.Alchemy)
            {
                Masteries = null;
            }
            else
            {
                MasteryInfo[] masteries = MasteryInfo.Infos.Where(i => i.MasterySkill == _Mastery && !i.Passive && (i.SpellType != typeof(BodyGuardSpell) || Controlled)).ToArray();

                if (masteries != null && masteries.Length > 0)
                {
                    Masteries = masteries;
                }
            }
        }

        public virtual void CheckCastMastery()
        {
            if (Spell == null && Masteries != null && Masteries.Length > 0 && NextMastery < DateTime.UtcNow)
            {
                MasteryInfo info = Masteries[Utility.Random(Masteries.Length)];

                if (info != null)
                {
                    if (info.SpellType.IsSubclassOf(typeof(SkillMasteryMove)))
                    {
                        SpecialMove move = SpellRegistry.GetSpecialMove(info.SpellID);

                        if (move != null)
                        {
                            SpecialMove.SetCurrentMove(this, move);
                            NextMastery = DateTime.UtcNow + TimeSpan.FromSeconds(Utility.RandomMinMax(10, 60));
                        }
                    }
                    else
                    {
                        Spell spell = SpellRegistry.NewSpell(info.SpellID, this, null);

                        if (spell != null)
                        {
                            spell.Cast();
                            NextMastery = DateTime.UtcNow + TimeSpan.FromSeconds(Utility.RandomMinMax(10, 60));
                        }
                    }
                }
            }
        }
        #endregion

        #region Soulbound
        private bool _IsSoulBound;

        public bool IsSoulBound
        {
            get
            {
                if (!IsSoulboundEnemies)
                {
                    return false;
                }

                return _IsSoulBound || _SoulboundCreatures.Any(c => c == GetType());
            }
            set
            {
                if (IsSoulboundEnemies)
                {
                    _IsSoulBound = value;
                }
            }
        }

        public static bool IsSoulboundEnemies => Engines.Fellowship.ForsakenFoesEvent.Instance.Running;

        public static Type[] _SoulboundCreatures =
        {
            typeof(MerchantCaptain), typeof(PirateCrew), typeof(PirateCaptain), typeof(MerchantCrew), typeof(Osiredon), typeof(Charydbis), typeof(CorgulTheSoulBinder),
        };
        #endregion

        public virtual double WeaponAbilityChance => 0.4;

        public virtual WeaponAbility GetWeaponAbility()
        {
            return null;
        }

        #region Elemental Resistance/Damage
        public override int BasePhysicalResistance => m_PhysicalResistance;
        public override int BaseFireResistance => m_FireResistance;
        public override int BaseColdResistance => m_ColdResistance;
        public override int BasePoisonResistance => m_PoisonResistance;
        public override int BaseEnergyResistance => 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)
                {
                    Paragon.Convert(this);
                }
                else
                {
                    Paragon.UnConvert(this);
                }

                m_Paragon = value;

                InvalidateProperties();
            }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool IsChampionSpawn
        {
            get { return m_IsChampionSpawn; }
            set
            {
                if (m_IsChampionSpawn != value)
                {
                    if (!m_IsChampionSpawn && value)
                        SetToChampionSpawn();

                    m_IsChampionSpawn = value;

                    OnChampionSpawnChange();
                }
            }
        }

        protected virtual void OnChampionSpawnChange()
        { }

        [CommandProperty(AccessLevel.GameMaster)]
        public Mobile InitialFocus
        {
            get
            {
                if (m_InitialFocus != null && (!m_InitialFocus.Alive || m_InitialFocus.Deleted))
                {
                    m_InitialFocus = null;
                }

                return m_InitialFocus;
            }
            set { m_InitialFocus = value; }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public override IDamageable Combatant
        {
            get
            {
                return base.Combatant;
            }
            set
            {
                if (Deleted)
                    return;

                var c = base.Combatant;

                if (c == value)
                    return;

                if (AttacksFocus)
                {
                    Mobile focus = InitialFocus;

                    if (c != null)
                    {
                        if (focus != null && focus != value && InRange(focus.Location, RangePerception) && CanSee(focus))
                            value = focus;
                    }
                    else
                    {
                        if (focus == null && value is Mobile m)
                            InitialFocus = m;
                    }
                }
                else
                    InitialFocus = null;

                base.Combatant = value;

                if (Controlled)
                {
                    AdjustSpeeds();
                }
            }
        }

        public bool IsAmbusher { get; set; }

        public virtual FoodType FavoriteFood => FoodType.Meat;
        public virtual PackInstinct PackInstinct => PackInstinct.None;

        public List<Mobile> Owners => m_Owners;

        public virtual bool AllowMaleTamer => true;
        public virtual bool AllowFemaleTamer => true;
        public virtual bool SubdueBeforeTame => false;
        public virtual bool StatLossAfterTame => SubdueBeforeTame;
        public virtual bool ReduceSpeedWithDamage => true;
        public virtual bool IsSubdued => SubdueBeforeTame && (Hits < ((double)HitsMax / 10));

        public virtual bool Commandable => true;

        public virtual Poison HitPoison => null;
        public virtual double HitPoisonChance => 0.5;
        public virtual Poison PoisonImmune => null;

        public virtual bool BardImmune => false;
        public virtual bool Unprovokable => BardImmune || m_IsDeadPet;
        public virtual bool Uncalmable => BardImmune || m_IsDeadPet;
        public virtual bool AreaPeaceImmune => BardImmune || m_IsDeadPet;

        public virtual bool BleedImmune => false;
        public virtual double BonusPetDamageScalar => 1.0;
        public virtual bool AllureImmune => false;

        public virtual bool DeathAdderCharmable => false;

        public virtual bool GivesFameAndKarmaAward => true;

        //TODO: Find the pub 31 tweaks to the DispelDifficulty and apply them of course.
        public virtual double DispelDifficulty => 0.0; // at this skill level we dispel 50% chance
        public virtual double DispelFocus => 20.0;
        // at difficulty - focus we have 0%, at difficulty + focus we have 100%
        public virtual bool DisplayWeight => Backpack is StrongBackpack;

        public virtual double TeleportChance => 0.05;
        public virtual bool AttacksFocus => false;
        public virtual bool ShowSpellMantra => false;
        public virtual bool FreezeOnCast => ShowSpellMantra;
        public virtual bool CanFly => false;

        public virtual bool CanAutoStable
        {
            get
            {
                if (!(ControlMaster is PlayerMobile))
                    return false;

                if (Allured || Summoned)
                    return false;

                if (this is IMount && ((IMount)this).Rider != null)
                    return false;

                return true;
            }
        }

        public bool IsGolem => this is IRepairableMobile && GetMaster() != null;

        public virtual bool TaintedLifeAura => false;
        public virtual bool BreathImmune => 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);

                        if (!map.CanFit(loc, 16, false, false))
                        {
                            SpellHelper.AdjustField(ref loc, map, 16, true);
                        }

                        validLocation = map.CanFit(loc, 16, false, false);
                    }
                }

                acid.MoveToWorld(loc, map);
            }
        }

        public virtual Item NewHarmfulItem()
        {
            return new PoolOfAcid(TimeSpan.FromSeconds(10), 30, 30);
        }
        #endregion

        public virtual void OnDrainLife(Mobile victim)
        {
        }

        #region Flee!!!
        public virtual bool CanFlee => !m_Paragon && !GivesMLMinorArtifact && !SlayerGroup.GetEntryByName(SlayerName.Silver).Slays(this);
        public virtual double FleeChance => 0.25;
        public virtual double BreakFleeChance => 0.85;

        public long NextFleeCheck { get; set; }
        public DateTime ForceFleeUntil { get; set; }

        public bool CheckCanFlee()
        {
            if (ForceFleeUntil != DateTime.MinValue)
            {
                if (ForceFleeUntil < DateTime.UtcNow)
                {
                    ForceFleeUntil = DateTime.MinValue;
                }
                else
                {
                    return true;
                }
            }

            if (!CanFlee || NextFleeCheck > Core.TickCount)
            {
                return false;
            }

            NextFleeCheck = Core.TickCount + 1000;

            return CheckFlee() && FleeChance > Utility.RandomDouble();
        }

        public virtual bool CheckBreakFlee()
        {
            if ((ForceFleeUntil != DateTime.MinValue && ForceFleeUntil > DateTime.UtcNow) || Hits < HitsMax / 2)
            {
                return false;
            }

            bool caster = AI == AIType.AI_Mage || AI == AIType.AI_NecroMage || AI == AIType.AI_Spellbinder || AI == AIType.AI_Spellweaving || AI == AIType.AI_Mystic;

            return !caster || Mana > 20 || Mana == ManaMax;
        }

        public virtual bool CheckFlee()
        {
            return Hits <= (HitsMax * 16) / 100;
        }

        public virtual bool BreakFlee()
        {
            NextFleeCheck = Core.TickCount + Utility.RandomMinMax(2500, 10000);

            return true;
        }
        #endregion

        public virtual bool IsInvulnerable => false;

        public BaseAI AIObject => m_AI;

        public const int MaxOwners = 5;

        // Tribe Opposition (Replaces Opposition Group
        public virtual TribeType Tribe => TribeType.None; // What opposition list am I in?

        public virtual bool IsTribeEnemy(Mobile m)
        {
            // Target must be BaseCreature
            if (!(m is BaseCreature))
            {
                return false;
            }

            BaseCreature c = (BaseCreature)m;

            switch (Tribe)
            {
                case TribeType.Terathan: return (c.Tribe == TribeType.Ophidian);
                case TribeType.Ophidian: return (c.Tribe == TribeType.Terathan);
                case TribeType.Savage: return (c.Tribe == TribeType.Orc);
                case TribeType.Orc: return (c.Tribe == TribeType.Savage);
                case TribeType.Fey: return (c.Tribe == TribeType.Undead);
                case TribeType.Undead: return (c.Tribe == TribeType.Fey);
                case TribeType.GrayGoblin: return (c.Tribe == TribeType.GreenGoblin);
                case TribeType.GreenGoblin: return (c.Tribe == TribeType.GrayGoblin);
                default: return false;
            }
        }

        #region Friends
        public List<Mobile> Friends => m_Friends;

        public virtual bool AllowNewPetFriend => (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;
            }

            return ((m_bSummoned || m_bControlled) == (c.m_bSummoned || c.m_bControlled));
        }
        #endregion

        public virtual bool IsEnemy(Mobile m)
        {
            if (m is BaseGuard)
            {
                return false;
            }

            if (Combatant != m)
            {
                if (m is PlayerMobile && ((PlayerMobile)m).HonorActive)
                {
                    return false;
                }

                if (TransformationSpellHelper.UnderTransformation(m, typeof(EtherealVoyageSpell)))
                {
                    return false;
                }
            }

            if (Tribe != TribeType.None && IsTribeEnemy(m))
            {
                return true;
            }

            BaseCreature c = m as BaseCreature;

            // Are we a non-aggressive FightMode or are they an uncontrolled Summon?
            if (FightMode == FightMode.Aggressor || FightMode == FightMode.Evil || FightMode == FightMode.Good ||
                (c != null && c.m_bSummoned && !c.m_bControlled && c.SummonMaster != null))
            {
                // Negative Karma are my enemies
                if (FightMode == FightMode.Evil)
                {
                    if (c != null && c.GetMaster() != null)
                    {
                        return (c.GetMaster().Karma < 0);
                    }

                    return (m.Karma < 0);
                }

                // Positive Karma are my enemies
                if (FightMode == FightMode.Good)
                {
                    if (c != null && c.GetMaster() != null)
                    {
                        return (c.GetMaster().Karma > 0);
                    }

                    return (m.Karma > 0);
                }

                // Others are not my enemies
                return false;
            }

            if (c == null)
            {
                return true;
            }
            else
            {
                Mobile master = c.GetMaster();

                if (master != null && !(master is BaseCreature))
                {
                    return true;
                }
            }

            BaseCreature t = this;

            // Summons should have same rules as their master
            if (c.m_bSummoned && c.SummonMaster != null && c.SummonMaster is BaseCreature)
            {
                c = c.SummonMaster as BaseCreature;
            }

            // Summons should have same rules as their master
            if (t.m_bSummoned && 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;
            }

            // 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
            // Summoned creatures must have masters to count as summoned here
            return (((t.m_bSummoned && t.SummonMaster != null) || t.m_bControlled) !=
                ((c.m_bSummoned && c.SummonMaster != null) || c.m_bControlled));
        }

        public override string ApplyNameSuffix(string suffix)
        {
            if (IsParagon && !GivesMLMinorArtifact)
            {
                if (suffix.Length == 0)
                {
                    suffix = "(Paragon)";
                }
                else
                {
                    suffix = string.Concat(suffix, " (Paragon)");
                }
            }

            return base.ApplyNameSuffix(suffix);
        }

        public virtual bool CheckControlChance(Mobile m)
        {
            if (GetControlChance(m) > Utility.RandomDouble())
            {
                Loyalty += 1;
                return true;
            }

            PlaySound(GetAngerSound());

            Animate(AnimationType.Alert, 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_CurrentTameSkill <= 29.1 || m_bSummoned || m.AccessLevel >= AccessLevel.GameMaster)
            {
                return 1.0;
            }

            double dMinTameSkill = m_CurrentTameSkill;

            if (dMinTameSkill > -24.9 && DarkWolfFamiliar.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;

            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;

            chance += bonus;

            if (chance >= 0 && chance < 200)
            {
                chance = 200;
            }
            else if (chance > 990)
            {
                chance = 990;
            }

            chance -= (MaxLoyalty - m_Loyalty) * 10;

            return ((double)chance / 1000);
        }

        public static readonly TimeSpan DeleteTimeSpan = TimeSpan.FromDays(3);

        public virtual void BeginDeleteTimer()
        {
            DeleteTime = DateTime.UtcNow + DeleteTimeSpan;
        }

        public virtual void StopDeleteTimer()
        {
            DeleteTime = DateTime.MinValue;
        }

        public virtual bool CanTransfer(Mobile m)
        {
            return !Allured;
        }

        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 => Summoned && m_AnimateDeadTypes.Any(t => t == GetType());

        public virtual bool IsNecroFamiliar
        {
            get
            {
                if (!Summoned)
                {
                    return false;
                }

                if (m_ControlMaster != null && SummonFamiliarSpell.Table.Contains(m_ControlMaster))
                {
                    return SummonFamiliarSpell.Table[m_ControlMaster] == this;
                }

                return false;
            }
        }

        public override int Damage(int amount, Mobile from)
        {
            return Damage(amount, from, false, false);
        }

        public override int Damage(int amount, Mobile from, bool informMount)
        {
            return Damage(amount, from, informMount, false);
        }

        public override int Damage(int amount, Mobile from, bool informMount, bool checkDisrupt)
        {
            int oldHits = Hits;

            if (Controlled && from is BaseCreature && !((BaseCreature)from).Controlled && !((BaseCreature)from).Summoned)
                amount = (int)(amount * ((BaseCreature)from).BonusPetDamageScalar);

            amount = base.Damage(amount, from, informMount, checkDisrupt);

            if (SubdueBeforeTame && !Controlled)
            {
                if ((oldHits > ((double)HitsMax / 10)) && (Hits <= ((double)HitsMax / 10)))
                {
                    PublicOverheadMessage(MessageType.Regular, 0x3B2, false, "* The creature has been beaten into subjugation! *");
                }
            }

            return amount;
        }

        public virtual bool DeleteCorpseOnDeath => false;

        public override void SetLocation(Point3D newLocation, bool isTeleport)
        {
            base.SetLocation(newLocation, isTeleport);

            if (isTeleport && m_AI != null)
            {
                m_AI.OnTeleported();
            }
        }

        public override void OnBeforeSpawn(Point3D location, Map m)
        {
            if (Paragon.CheckConvert(this, location, m))
            {
                IsParagon = true;
            }

            base.OnBeforeSpawn(location, m);
        }

        public override ApplyPoisonResult ApplyPoison(Mobile from, Poison poison)
        {
            if (!Alive || IsDeadPet)
            {
                return ApplyPoisonResult.Immune;
            }

            if (EvilOmenSpell.TryEndEffect(this))
            {
                poison = PoisonImpl.IncreaseLevel(poison);
            }

            ApplyPoisonResult result = base.ApplyPoison(from, poison);

            if (from != null && result == ApplyPoisonResult.Poisoned && PoisonTimer is PoisonImpl.PoisonTimer)
            {
                (PoisonTimer as PoisonImpl.PoisonTimer).From = from;
            }

            return result;
        }

        public override bool CheckPoisonImmunity(Mobile from, Poison poison)
        {
            if (base.CheckPoisonImmunity(from, poison))
            {
                return true;
            }

            Poison p = PoisonImmune;

            return (p != null && p.RealLevel >= poison.RealLevel);
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public int Loyalty { get { return m_Loyalty; } set { m_Loyalty = Math.Min(Math.Max(value, 0), MaxLoyalty); } }

        [CommandProperty(AccessLevel.GameMaster)]
        public WayPoint CurrentWayPoint { get { return m_CurrentWayPoint; } set { m_CurrentWayPoint = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public int CurrentNavPoint
        {
            get
            {
                return _CurrentNavPoint;
            }
            set
            {
                _CurrentNavPoint = value;
            }
        }

        public Dictionary<Map, List<Point2D>> NavPoints
        {
            get
            {
                if (_NavPoints == null)
                    _NavPoints = new Dictionary<Map, List<Point2D>>();

                return _NavPoints;
            }
            set
            {
                _NavPoints = value;
            }
        }

        public List<Point2D> CurrentNavPoints
        {
            get
            {
                if (Map != null && _NavPoints.ContainsKey(Map))
                    return _NavPoints[Map];

                return null;
            }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public IPoint2D TargetLocation { get { return m_TargetLocation; } set { m_TargetLocation = value; } }

        public virtual Mobile ConstantFocus => null;

        public virtual bool DisallowAllMoves => false;

        public virtual bool InitialInnocent => false;
        public virtual bool AlwaysInnocent => false;

        public virtual bool AlwaysMurderer => false;

        public virtual bool AlwaysAttackable => false;

        public virtual bool ForceNotoriety => false;

        public virtual bool HoldSmartSpawning => IsParagon;
        public virtual bool UseSmartAI => false;

        [CommandProperty(AccessLevel.GameMaster)]
        public virtual int DamageMin { get { return m_DamageMin; } set { m_DamageMin = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public virtual int DamageMax { get { return m_DamageMax; } set { m_DamageMax = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public override int HitsMax
        {
            get
            {
                int value = Str;

                if (m_HitsMax > 0)
                {
                    value = m_HitsMax + GetStatOffset(StatType.Str);

                    if (value < 1)
                    {
                        value = 1;
                    }
                    else if (value > 1000000)
                    {
                        value = 1000000;
                    }
                }

                // Skill Masteries
                value += ToughnessSpell.GetHPBonus(this);
                value += InvigorateSpell.GetHPBonus(this);

                return value;
            }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public int HitsMaxSeed { get { return m_HitsMax; } set { m_HitsMax = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public override int StamMax
        {
            get
            {
                if (m_StamMax > 0)
                {
                    int value = m_StamMax + GetStatOffset(StatType.Dex);

                    if (value < 1)
                    {
                        value = 1;
                    }
                    else if (value > 1000000)
                    {
                        value = 1000000;
                    }

                    return value;
                }

                return Dex;
            }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public int StamMaxSeed { get { return m_StamMax; } set { m_StamMax = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public override int ManaMax
        {
            get
            {
                if (m_ManaMax > 0)
                {
                    int value = m_ManaMax + GetStatOffset(StatType.Int);

                    if (value < 1)
                    {
                        value = 1;
                    }
                    else if (value > 1000000)
                    {
                        value = 1000000;
                    }

                    return value;
                }

                return Int;
            }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public int ManaMaxSeed { get { return m_ManaMax; } set { m_ManaMax = value; } }

        public virtual bool CanOpenDoors => !Body.IsAnimal && !Body.IsSea;

        public virtual bool CanMoveOverObstacles => Body.IsMonster;

        public virtual bool CanDestroyObstacles =>
                // to enable breaking of furniture, 'return CanMoveOverObstacles;'
                false;

        public void Unpacify()
        {
            BardEndTime = DateTime.UtcNow;
            BardPacified = false;
        }

        private HonorContext m_ReceivedHonorContext;

        public HonorContext ReceivedHonorContext { get { return m_ReceivedHonorContext; } set { m_ReceivedHonorContext = value; } }

        public virtual void OnBeforeDamage(Mobile from, ref int totalDamage, DamageType type)
        {
            if (type >= DamageType.Spell && RecentSetControl)
            {
                totalDamage = 0;
            }
        }

        public override void OnDamage(int amount, Mobile from, bool willKill)
        {
            if (BardPacified && (HitsMax - Hits) * 0.001 > Utility.RandomDouble())
            {
                Unpacify();
            }

            int disruptThreshold;

            //NPCs can use bandages too!
            if (from != null && from.Player)
            {
                disruptThreshold = 18;
            }
            else
            {
                disruptThreshold = 25;
            }

            if (amount > disruptThreshold)
            {
                BandageContext c = BandageContext.GetContext(this);

                if (c != null)
                {
                    c.Slip();
                }
            }

            if (Confidence.IsRegenerating(this))
            {
                Confidence.StopRegenerating(this);
            }

            InhumanSpeech speechType = SpeechType;

            if (speechType != null && !willKill)
            {
                speechType.OnDamage(this, amount);
            }

            if (m_ReceivedHonorContext != null)
            {
                m_ReceivedHonorContext.OnTargetDamaged(from, amount);
            }

            if (from is PlayerMobile)
            {
                Timer.DelayCall(TimeSpan.FromSeconds(10), ((PlayerMobile)@from).RecoverAmmo);
            }

            base.OnDamage(amount, from, willKill);
        }

        public virtual void OnDamagedBySpell(Mobile from)
        {
        }

        public virtual void OnHarmfulSpell(Mobile from)
        { }

        #region Alter[...]Damage From/To
        public virtual void AlterDamageScalarFrom(Mobile caster, ref double scalar)
        { }

        public virtual void AlterDamageScalarTo(Mobile target, ref double scalar)
        { }

        public virtual void AlterSpellDamageFrom(Mobile from, ref int damage)
        {
            if (m_TempDamageAbsorb > 0 && VialofArmorEssence.UnderInfluence(this))
                damage -= damage / m_TempDamageAbsorb;
        }

        public virtual void AlterSpellDamageTo(Mobile to, ref int damage)
        { }

        public virtual void AlterMeleeDamageFrom(Mobile from, ref int damage)
        {
            #region Mondain's Legacy
            if (from != null && from.Talisman is BaseTalisman)
            {
                BaseTalisman talisman = (BaseTalisman)from.Talisman;

                if (talisman.Killer != null && talisman.Killer.Type != null)
                {
                    Type type = talisman.Killer.Type;

                    if (type.IsAssignableFrom(GetType()))
                    {
                        damage = (int)(damage * (1 + (double)talisman.Killer.Amount / 100));
                    }
                }
            }
            #endregion

            if (m_TempDamageAbsorb > 0 && VialofArmorEssence.UnderInfluence(this))
                damage -= damage / m_TempDamageAbsorb;
        }

        public virtual void AlterMeleeDamageTo(Mobile to, ref int damage)
        {
            if (m_TempDamageBonus > 0 && TastyTreat.UnderInfluence(this))
                damage += damage / m_TempDamageBonus;
        }
        #endregion

        #region SA / High Seas Tasty Treats/Vial of Armor Essense
        private int m_TempDamageBonus = 0;
        private int m_TempDamageAbsorb = 0;

        public int TempDamageBonus { get { return m_TempDamageBonus; } set { m_TempDamageBonus = value; } }
        public int TempDamageAbsorb { get { return m_TempDamageAbsorb; } set { m_TempDamageAbsorb = value; } }
        #endregion

        public virtual void CheckReflect(Mobile caster, ref bool reflect)
        { }

        public virtual void OnCarve(Mobile from, Corpse corpse, Item with)
        {
            int feathers = Feathers;
            int wool = Wool;
            int meat = Meat;
            int hides = Hides;
            int scales = Scales;
            int dragonblood = DragonBlood;
            int fur = Fur;

            bool special = with is HarvestersBlade;

            if ((feathers == 0 && wool == 0 && meat == 0 && hides == 0 && scales == 0 && fur == 0) || Summoned || IsBonded || corpse.Animated)
            {
                if (corpse.Animated)
                {
                    corpse.SendLocalizedMessageTo(from, 500464); // Use this on corpses to carve away meat and hide
                }
                else
                {
                    from.SendLocalizedMessage(500485); // You see nothing useful to carve from the corpse.
                }
            }
            else
            {
                if (from.Race == Race.Human)
                {
                    hides = (int)Math.Ceiling(hides * 1.1); // 10% bonus only applies to hides, ore & logs
                }

                if (corpse.Map == Map.Felucca && !Siege.SiegeShard)
                {
                    feathers *= 2;
                    wool *= 2;
                    hides *= 2;
                    fur *= 2;
                    meat *= 2;
                    scales *= 2;
                }

                if (special)
                {
                    feathers = (int)Math.Ceiling(feathers * 1.1);
                    wool = (int)Math.Ceiling(wool * 1.1);
                    hides = (int)Math.Ceiling(hides * 1.1);
                    meat = (int)Math.Ceiling(meat * 1.1);
                    scales = (int)Math.Ceiling(scales * 1.1);
                }

                new Blood(0x122D).MoveToWorld(corpse.Location, corpse.Map);

                if (feathers != 0)
                {
                    Item feather = new Feather(feathers);

                    if (!special || !from.AddToBackpack(feather))
                    {
                        corpse.AddCarvedItem(feather, from);
                        from.SendLocalizedMessage(500479); // You pluck the bird. The feathers are now on the corpse.
                    }
                    else
                    {
                        from.SendLocalizedMessage(1114097); // You pluck the bird and place the feathers in your backpack.
                    }
                }

                if (wool != 0)
                {
                    Item w = new TaintedWool(wool);

                    if (!special || !from.AddToBackpack(w))
                    {
                        corpse.AddCarvedItem(w, from);
                        from.SendLocalizedMessage(500483); // You shear it, and the wool is now on the corpse.
                    }
                    else
                    {
                        from.SendLocalizedMessage(1114099); // You shear the creature and put the resources in your backpack.
                    }
                }

                if (meat != 0)
                {
                    Item m = null;

                    switch (MeatType)
                    {
                        default:
                        case MeatType.Ribs: m = new RawRibs(meat); break;
                        case MeatType.Bird: m = new RawBird(meat); break;
                        case MeatType.LambLeg: m = new RawLambLeg(meat); break;
                        case MeatType.Rotworm: m = new RawRotwormMeat(meat); break;
                        case MeatType.DinoRibs: m = new RawDinoRibs(meat); break;
                        case MeatType.SeaSerpentSteak: m = new RawSeaSerpentSteak(meat); break;
                    }

                    if (!special || !from.AddToBackpack(m))
                    {
                        corpse.AddCarvedItem(m, from);
                        from.SendLocalizedMessage(500467); // You carve some meat, which remains on the corpse.
                    }
                    else
                    {
                        from.SendLocalizedMessage(1114101); // You carve some meat and put it in your backpack.
                    }
                }

                if (hides != 0)
                {
                    Item leather = null;
                    bool cutHides = (with is SkinningKnife && from.FindItemOnLayer(Layer.OneHanded) == with) || special || with is ButchersWarCleaver;

                    switch (HideType)
                    {
                        default:
                        case HideType.Regular:
                            if (cutHides) leather = new Leather(hides);
                            else leather = new Hides(hides);
                            break;
                        case HideType.Spined:
                            if (cutHides) leather = new SpinedLeather(hides);
                            else leather = new SpinedHides(hides);
                            break;
                        case HideType.Horned:
                            if (cutHides) leather = new HornedLeather(hides);
                            else leather = new HornedHides(hides);
                            break;
                        case HideType.Barbed:
                            if (cutHides) leather = new BarbedLeather(hides);
                            else leather = new BarbedHides(hides);
                            break;
                        //daat99 OWLTR start - custom leather
                        case HideType.Polar:
                            if (cutHides) leather = new PolarLeather(hides);
                            else leather = new PolarLeather(hides);
                             break;
                        case HideType.Synthetic:
                            if (cutHides) leather = new SyntheticLeather(hides);
                            else leather = new SyntheticLeather(hides);
                             break;
                        case HideType.BlazeL:
                            if (cutHides) leather = new BlazeLeather(hides);
                            else leather = new BlazeLeather(hides);
                             break;
                        case HideType.Daemonic:
                            if (cutHides) leather = new DaemonicLeather(hides);
                            else leather = new DaemonicLeather(hides);
                             break;
                        case HideType.Shadow:
                            if (cutHides) leather = new ShadowLeather(hides);
                            else leather = new ShadowLeather(hides);
                            break;
                        case HideType.Frost:
                            if (cutHides) leather = new FrostLeather(hides);
                            else leather = new FrostLeather(hides);
                             break;
                        case HideType.Ethereal:
                            if (cutHides) leather = new EtherealLeather(hides);
                            else leather = new EtherealLeather(hides);
                             break;
                        //daat99 OWLTR end - custom leather
                    }

                    if (!cutHides || !from.AddToBackpack(leather))
                    {
                        corpse.AddCarvedItem(leather, from);
                        from.SendLocalizedMessage(500471); // You skin it, and the hides are now in the corpse.
                    }
                    else
                    {
                        from.SendLocalizedMessage(1073555); // You skin it and place the cut-up hides in your backpack.
                    }
                }

                if (scales != 0)
                {
                    ScaleType sc = ScaleType;
                    List<Item> list = new List<Item>();

                    switch (sc)
                    {
                        default:
                        case ScaleType.Red: list.Add(new RedScales(scales)); break;
                        case ScaleType.Yellow: list.Add(new YellowScales(scales)); break;
                        case ScaleType.Black: list.Add(new BlackScales(scales)); break;
                        case ScaleType.Green: list.Add(new GreenScales(scales)); break;
                        case ScaleType.White: list.Add(new WhiteScales(scales)); break;
                        case ScaleType.Blue: list.Add(new BlueScales(scales)); break;
                        case ScaleType.All:
                            {
                                list.Add(new RedScales(scales));
                                list.Add(new YellowScales(scales));
                                list.Add(new BlackScales(scales));
                                list.Add(new GreenScales(scales));
                                list.Add(new WhiteScales(scales));
                                list.Add(new BlueScales(scales));
//daat99 OWLTR start - custom scales
                                list.Add(new CopperScales(scales));
                                list.Add(new SilverScales(scales));
                                list.Add(new GoldScales(scales));
                                //daat99 OWLTR end - custom scales
                                break;
                            }
                    }

                    if (special)
                    {
                        bool allPack = true;
                        bool anyPack = false;

                        foreach (Item s in list)
                        {
                            //corpse.AddCarvedItem(s, from);
                            if (!from.PlaceInBackpack(s))
                            {
                                corpse.AddCarvedItem(s, from);
                                allPack = false;
                            }
                            else if (!anyPack)
                            {
                                anyPack = true;
                            }
                        }

                        if (anyPack)
                            from.SendLocalizedMessage(1114098); // You cut away some scales and put them in your backpack.

                        if (!allPack)
                            from.SendLocalizedMessage(1079284); // You cut away some scales, but they remain on the corpse.
                    }
                    else
                    {
                        foreach (Item s in list)
                        {
                            corpse.AddCarvedItem(s, from);
                        }

                        from.SendLocalizedMessage(1079284); // You cut away some scales, but they remain on the corpse.
                    }

                    ColUtility.Free(list);
                }

                if (dragonblood != 0)
                {
                    Item dblood = new DragonBlood(dragonblood);

                    if (!special || !from.AddToBackpack(dblood))
                    {
                        corpse.AddCarvedItem(dblood, from);
                        from.SendLocalizedMessage(1094946); // Some blood is left on the corpse.
                    }
                    else
                    {
                        from.SendLocalizedMessage(1114100); // You take some blood off the corpse and put it in your backpack.
                    }
                }

                if (fur != 0)
                {
                    Item _fur = new Fur(FurType, fur);

                    corpse.AddCarvedItem(_fur, from);
                    from.SendLocalizedMessage(1112765); // You shear it, and the fur is now on the corpse.
                }

                corpse.Carved = true;

                if (corpse.IsCriminalAction(from))
                {
                    from.CriminalAction(true);
                }
            }
        }

        public const int DefaultRangePerception = 16;
        public const int OldRangePerception = 10;

        public BaseCreature(AIType ai, FightMode mode, int iRangePerception, int iRangeFight)
            : this(ai, mode, iRangePerception, iRangeFight, .2, .4)
        {
        }

        public BaseCreature(
            AIType ai, FightMode mode, int iRangePerception, int iRangeFight, double dActiveSpeed, double dPassiveSpeed)
        {
            /*#region FS:ATS Edits
            bool alwaysMale = false;
            Type typ = this.GetType();
            string nam = typ.Name;

            bool alwaysFemale = false;
            Type typ2 = this.GetType();
            string nam2 = typ2.Name;

            foreach ( string check in FSATS.AlwaysMale )
            {
                  if ( check == nam )
                        alwaysMale = true;
            }

            foreach ( string check2 in FSATS.AlwaysFemale )
            {
                  if ( check2 == nam2 )
                        alwaysFemale = true;
            }

            if ( alwaysMale == true )
                this.Female = false;
            else if ( alwaysFemale == true )
                this.Female = true;
            else
            {
                switch ( Utility.Random( 2 ) )
                {
                         case 0: this.Female = true; break;
                    
                    case 1: this.Female = false; break;
                }
            }

            m_MaxLevel = Utility.RandomMinMax( 10, 30 );
            #endregion*/
            PhysicalDamage = 100;

            CanMove = true;

            ApproachWait = false;
            ApproachRange = 10;

            if (iRangePerception == OldRangePerception)
            {
                iRangePerception = DefaultRangePerception;
            }

            m_Loyalty = MaxLoyalty; // Wonderfully Happy

            m_CurrentAI = ai;
            m_DefaultAI = ai;

            m_iRangePerception = iRangePerception;
            m_iRangeFight = iRangeFight;

            m_FightMode = mode;

            m_iTeam = 0;

            SpeedInfo.GetSpeeds(this, ref dActiveSpeed, ref dPassiveSpeed);

            m_dActiveSpeed = dActiveSpeed;
            m_dPassiveSpeed = dPassiveSpeed;
            m_dCurrentSpeed = dPassiveSpeed;

            m_bDebugAI = false;

            m_arSpellAttack = new List<Type>();
            m_arSpellDefense = new List<Type>();

            m_bControlled = false;
            m_ControlMaster = null;
            m_ControlTarget = null;
            m_ControlOrder = OrderType.None;

            m_bTamable = false;

            m_Owners = new List<Mobile>();

            m_NextReacquireTime = Core.TickCount + (int)ReacquireDelay.TotalMilliseconds;

            ChangeAIType(AI);

            InhumanSpeech speechType = SpeechType;

            if (speechType != null)
            {
                speechType.OnConstruct(this);
            }

            InitializeAbilities();
        }

        public BaseCreature(Serial serial)
            : base(serial)
        {
            m_arSpellAttack = new List<Type>();
            m_arSpellDefense = new List<Type>();

            m_bDebugAI = false;
        }

        protected override void OnCreate()
        {
            GenerateLoot(LootStage.Spawning);
        }

        public override void Serialize(GenericWriter writer)
        {
            base.Serialize(writer);

            writer.Write(32); // version

            writer.Write(StealPackGenerated);
            writer.Write(HasBeenStolen);

            writer.Write(m_ForceActiveSpeed);
            writer.Write(m_ForcePassiveSpeed);

            writer.Write(CanMove);
            writer.Write(_LockDirection);
            writer.Write(ApproachWait);
            writer.Write(ApproachRange);

            writer.Write((int)m_CurrentAI);
            writer.Write((int)m_DefaultAI);

            writer.Write(m_iRangePerception);
            writer.Write(m_iRangeFight);

            writer.Write(m_iTeam);

            writer.Write(m_dActiveSpeed);
            writer.Write(m_dPassiveSpeed);
            writer.Write(m_dCurrentSpeed);

            writer.Write(m_pHome.X);
            writer.Write(m_pHome.Y);
            writer.Write(m_pHome.Z);

            // Version 1
            writer.Write(m_iRangeHome);

            int i = 0;

            writer.Write(m_arSpellAttack.Count);

            for (i = 0; i < m_arSpellAttack.Count; i++)
            {
                writer.Write(m_arSpellAttack[i].ToString());
            }

            writer.Write(m_arSpellDefense.Count);

            for (i = 0; i < m_arSpellDefense.Count; i++)
            {
                writer.Write(m_arSpellDefense[i].ToString());
            }

            // Version 2
            writer.Write((int)m_FightMode);

            writer.Write(m_bControlled);
            writer.Write(m_ControlMaster);
            writer.Write(m_ControlTarget is Mobile ? (Mobile)m_ControlTarget : null);
            writer.Write(m_ControlDest);
            writer.Write((int)m_ControlOrder);
            writer.Write(m_dMinTameSkill);

            writer.Write(m_bTamable);
            writer.Write(m_bSummoned);

            if (m_bSummoned)
            {
                writer.WriteDeltaTime(m_SummonEnd);
            }

            writer.Write(m_iControlSlots);

            // Version 3
            writer.Write(m_Loyalty);

            // Version 4
            writer.Write(m_CurrentWayPoint);

            // Verison 5
            writer.Write(m_SummonMaster);

            // Version 6
            writer.Write(m_HitsMax);
            writer.Write(m_StamMax);
            writer.Write(m_ManaMax);
            writer.Write(m_DamageMin);
            writer.Write(m_DamageMax);

            // Version 7
            writer.Write(m_PhysicalResistance);
            writer.Write(m_PhysicalDamage);

            writer.Write(m_FireResistance);
            writer.Write(m_FireDamage);

            writer.Write(m_ColdResistance);
            writer.Write(m_ColdDamage);

            writer.Write(m_PoisonResistance);
            writer.Write(m_PoisonDamage);

            writer.Write(m_EnergyResistance);
            writer.Write(m_EnergyDamage);

            // Version 8
            writer.Write(m_Owners, true);

            // Version 10
            writer.Write(m_IsDeadPet);
            writer.Write(m_IsBonded);
            writer.Write(m_BondingBegin);
            writer.Write(m_OwnerAbandonTime);

            // Version 11
            writer.Write(m_HasGeneratedLoot);

            // Version 12
            writer.Write(m_Paragon);

            // Version 13
            writer.Write((m_Friends != null && m_Friends.Count > 0));

            if (m_Friends != null && m_Friends.Count > 0)
            {
                writer.Write(m_Friends, true);
            }

            // Version 14
            writer.Write(m_RemoveIfUntamed);
            writer.Write(m_RemoveStep);

            // Version 17
            if (IsStabled || (Controlled && ControlMaster != null))
            {
                writer.Write(DateTime.MinValue);
            }
            else
            {
                writer.Write(m_DeleteTime);
            }

            // Version 18
            writer.Write(m_CorpseNameOverride);

            // Mondain's Legacy version 19
            writer.Write(m_Allured);

            /*// Version 21 FS:ATS EDITS
            writer.Write( (bool) m_IsMating );
            writer.Write( (int) m_ABPoints );
            writer.Write( (int) m_Exp );
            writer.Write( (int) m_NextLevel );
            writer.Write( (int) m_Level );
            writer.Write( (int) m_MaxLevel );
            writer.Write( (bool) m_AllowMating );
            writer.Write( (bool) m_Evolves );
            writer.Write( (int) m_Gen );
            writer.Write( (DateTime) m_MatingDelay );
            writer.Write( (int) m_Form1 );
            writer.Write( (int) m_Form2 );
            writer.Write( (int) m_Form3 );
            writer.Write( (int) m_Form4 );
            writer.Write( (int) m_Form5 );
            writer.Write( (int) m_Form6 );
            writer.Write( (int) m_Form7 );
            writer.Write( (int) m_Form8 );
            writer.Write( (int) m_Form9 );
            writer.Write( (int) m_Sound1 );
            writer.Write( (int) m_Sound2 );
            writer.Write( (int) m_Sound3 );
            writer.Write( (int) m_Sound4 );
            writer.Write( (int) m_Sound5 );
            writer.Write( (int) m_Sound6 );
            writer.Write( (int) m_Sound7 );
            writer.Write( (int) m_Sound8 );
            writer.Write( (int) m_Sound9 );
            writer.Write( (bool) m_UsesForm1 );
            writer.Write( (bool) m_UsesForm2 );
            writer.Write( (bool) m_UsesForm3 );
            writer.Write( (bool) m_UsesForm4 );
            writer.Write( (bool) m_UsesForm5 );
            writer.Write( (bool) m_UsesForm6 );
            writer.Write( (bool) m_UsesForm7 );
            writer.Write( (bool) m_UsesForm8 );
            writer.Write( (bool) m_UsesForm9 );
            writer.Write( (bool) m_F0 );
            writer.Write( (bool) m_F1 );
            writer.Write( (bool) m_F2 );
            writer.Write( (bool) m_F3 );
            writer.Write( (bool) m_F4 );
            writer.Write( (bool) m_F5 );
            writer.Write( (bool) m_F6 );
            writer.Write( (bool) m_F7 );
            writer.Write( (bool) m_F8 );
            writer.Write( (bool) m_F9 );
            writer.Write( (int) m_RoarAttack );
            writer.Write( (int) m_PetPoisonAttack );
            writer.Write( (int) m_FireBreathAttack );*/

            // Pet Branding version 22
            writer.Write(m_EngravedText);

            // Version 24 Pet Training
            writer.Write(ControlSlotsMin);
            writer.Write(ControlSlotsMax);

            writer.Write((int)Mastery);

            if (_Profile != null)
            {
                writer.Write(1);
                _Profile.Serialize(writer);
            }
            else
            {
                writer.Write(0);
            }

            if (_TrainingProfile != null)
            {
                writer.Write(1);
                _TrainingProfile.Serialize(writer);
            }
            else
            {
                writer.Write(0);
            }

            // Version 25 Current Tame Skill
            writer.Write(m_CurrentTameSkill);
        }

        private static readonly double[] m_StandardActiveSpeeds = new[] { 0.175, 0.1, 0.15, 0.2, 0.25, 0.3, 0.4, 0.5, 0.6, 0.8 };

        private static readonly double[] m_StandardPassiveSpeeds = new[] { 0.350, 0.2, 0.4, 0.5, 0.6, 0.8, 1.0, 1.2, 1.6, 2.0 };

        public override void Deserialize(GenericReader reader)
        {
            base.Deserialize(reader);

            int version = reader.ReadInt();

            switch (version)
            {
                case 32:
                case 31:
                    StealPackGenerated = reader.ReadBool();
                    HasBeenStolen = reader.ReadBool();
                    goto case 28;
                case 30:
                    goto case 28;
                case 29:
                    reader.ReadBool();
                    goto case 28;
                case 28:
                    m_ForceActiveSpeed = reader.ReadDouble();
                    m_ForcePassiveSpeed = reader.ReadDouble();
                    goto case 27;
                case 27: // Pet Slot Fix
                case 26:
                    {
                        CanMove = reader.ReadBool();
                        _LockDirection = reader.ReadBool();

                        ApproachWait = reader.ReadBool();
                        ApproachRange = reader.ReadInt();
                    }
                    break;
            }

            m_CurrentAI = (AIType)reader.ReadInt();
            m_DefaultAI = (AIType)reader.ReadInt();

            m_iRangePerception = reader.ReadInt();
            m_iRangeFight = reader.ReadInt();

            m_iTeam = reader.ReadInt();

            m_dActiveSpeed = reader.ReadDouble();
            m_dPassiveSpeed = reader.ReadDouble();
            m_dCurrentSpeed = reader.ReadDouble();

            if (m_iRangePerception == OldRangePerception)
            {
                m_iRangePerception = DefaultRangePerception;
            }

            m_pHome.X = reader.ReadInt();
            m_pHome.Y = reader.ReadInt();
            m_pHome.Z = reader.ReadInt();

            if (version >= 1)
            {
                m_iRangeHome = reader.ReadInt();

                int i, iCount;

                iCount = reader.ReadInt();
                for (i = 0; i < iCount; i++)
                {
                    string str = reader.ReadString();
                    Type type = Type.GetType(str);

                    if (type != null)
                    {
                        m_arSpellAttack.Add(type);
                    }
                }

                iCount = reader.ReadInt();
                for (i = 0; i < iCount; i++)
                {
                    string str = reader.ReadString();
                    Type type = Type.GetType(str);

                    if (type != null)
                    {
                        m_arSpellDefense.Add(type);
                    }
                }
            }
            else
            {
                m_iRangeHome = 0;
            }

            if (version >= 2)
            {
                m_FightMode = (FightMode)reader.ReadInt();

                m_bControlled = reader.ReadBool();
                m_ControlMaster = reader.ReadMobile();
                m_ControlTarget = reader.ReadMobile();
                m_ControlDest = reader.ReadPoint3D();
                m_ControlOrder = (OrderType)reader.ReadInt();

                m_dMinTameSkill = reader.ReadDouble();

                if (version < 9)
                {
                    reader.ReadDouble();
                }

                m_bTamable = reader.ReadBool();
                m_bSummoned = reader.ReadBool();

                if (m_bSummoned)
                {
                    m_SummonEnd = reader.ReadDeltaTime();
                    TimerRegistry.Register<BaseCreature>("UnsummonTimer", this, m_SummonEnd - DateTime.UtcNow, c => c.Delete());
                }

                m_iControlSlots = reader.ReadInt();
            }
            else
            {
                m_FightMode = FightMode.Closest;

                m_bControlled = false;
                m_ControlMaster = null;
                m_ControlTarget = null;
                m_ControlOrder = OrderType.None;
            }

            if (version >= 3)
            {
                m_Loyalty = reader.ReadInt();
            }
            else
            {
                m_Loyalty = MaxLoyalty; // Wonderfully Happy
            }

            if (version >= 4)
            {
                m_CurrentWayPoint = reader.ReadItem() as WayPoint;
            }

            if (version >= 5)
            {
                m_SummonMaster = reader.ReadMobile();
            }

            if (version >= 6)
            {
                m_HitsMax = reader.ReadInt();
                m_StamMax = reader.ReadInt();
                m_ManaMax = reader.ReadInt();
                m_DamageMin = reader.ReadInt();
                m_DamageMax = reader.ReadInt();
            }

            if (version >= 7)
            {
                m_PhysicalResistance = reader.ReadInt();
                m_PhysicalDamage = reader.ReadInt();

                m_FireResistance = reader.ReadInt();
                m_FireDamage = reader.ReadInt();

                m_ColdResistance = reader.ReadInt();
                m_ColdDamage = reader.ReadInt();

                m_PoisonResistance = reader.ReadInt();
                m_PoisonDamage = reader.ReadInt();

                m_EnergyResistance = reader.ReadInt();
                m_EnergyDamage = reader.ReadInt();
            }

            if (version >= 8)
            {
                m_Owners = reader.ReadStrongMobileList();
            }
            else
            {
                m_Owners = new List<Mobile>();
            }

            if (version >= 10)
            {
                m_IsDeadPet = reader.ReadBool();
                m_IsBonded = reader.ReadBool();
                m_BondingBegin = reader.ReadDateTime();
                m_OwnerAbandonTime = reader.ReadDateTime();
            }

            if (version >= 11)
            {
                m_HasGeneratedLoot = reader.ReadBool();
            }
            else
            {
                m_HasGeneratedLoot = true;
            }

            if (version >= 12)
            {
                m_Paragon = reader.ReadBool();
            }
            else
            {
                m_Paragon = false;
            }

            if (version >= 13 && reader.ReadBool())
            {
                m_Friends = reader.ReadStrongMobileList();
            }
            else if (version < 13 && m_ControlOrder >= OrderType.Unfriend)
            {
                ++m_ControlOrder;
            }

            if (version < 16 && Loyalty != MaxLoyalty)
            {
                Loyalty *= 10;
            }

            double activeSpeed = m_dActiveSpeed;
            double passiveSpeed = m_dPassiveSpeed;

            SpeedInfo.GetSpeeds(this, ref activeSpeed, ref passiveSpeed);

            m_dActiveSpeed = activeSpeed;
            m_dPassiveSpeed = passiveSpeed;

            if (version >= 14)
            {
                m_RemoveIfUntamed = reader.ReadBool();
                m_RemoveStep = reader.ReadInt();
            }

            if (version >= 17)
            {
                if (version < 32)
                {
                    var span = reader.ReadTimeSpan();

                    if (span > TimeSpan.Zero)
                    {
                        DeleteTime = DateTime.UtcNow + span;
                    }
                }
                else
                {
                    DeleteTime = reader.ReadDateTime();
                }
            }

            if (version >= 18)
            {
                m_CorpseNameOverride = reader.ReadString();
            }

            if (version >= 19)
            {
                m_Allured = reader.ReadBool();
            }

            if (version == 20)
            {
                reader.ReadInt();
            }

            if (version < 26)
            {
                CanMove = true;

                ApproachWait = false;
                ApproachRange = 10;
            }

            if (version >= 22)
            {
                m_EngravedText = reader.ReadString();
            }

            if (version == 23)
            {
                reader.ReadBool();
            }

            if (version >= 24)
            {
                ControlSlotsMin = reader.ReadInt();
                ControlSlotsMax = reader.ReadInt();

                Mastery = (SkillName)reader.ReadInt();

                if (reader.ReadInt() == 1)
                {
                    _Profile = new AbilityProfile(this, reader);
                }

                if (reader.ReadInt() == 1)
                {
                    _TrainingProfile = new TrainingProfile(this, reader);
                }
            }
            else
            {
                if (Tamable)
                {
                    CalculateSlots(m_iControlSlots);

                    if (m_iControlSlots < ControlSlotsMin)
                    {
                        ControlSlotsMin = m_iControlSlots;
                    }

                    ControlSlots = ControlSlotsMin;
                }

                InitializeAbilities();
            }

            if (version >= 25)
            {
                CurrentTameSkill = reader.ReadDouble();

                if (Controlled && version == 26)
                {
                    AdjustTameRequirements();
                }
                else if (Controlled && CurrentTameSkill > MaxTameRequirement)
                {
                    CurrentTameSkill = MaxTameRequirement;
                }
            }
            else
            {
                AdjustTameRequirements();
            }

            if (version <= 14 && m_Paragon && Hue == 0x31)
            {
                Hue = Paragon.Hue; //Paragon hue fixed, should now be 0x501.
            }

            CheckStatTimers();

            ChangeAIType(m_CurrentAI);

            AddFollowers();

            if (IsAnimatedDead)
            {
                AnimateDeadSpell.Register(m_SummonMaster, this);
            }

            if (Tamable && CurrentTameSkill == 0)
            {
                AdjustTameRequirements();
            }

        }

        public virtual bool IsHumanInTown()
        {
            return (Body.IsHuman && Region.IsPartOf<GuardedRegion>());
        }

        public virtual bool CheckGold(Mobile from, Item dropped)
        {
            if (dropped is Gold)
            {
                return OnGoldGiven(from, (Gold)dropped);
            }

            return false;
        }

        public virtual bool OnGoldGiven(Mobile from, Gold dropped)
        {
            if (CheckTeachingMatch(from))
            {
                if (Teach(m_Teaching, from, dropped.Amount, true))
                {
                    dropped.Delete();
                    return true;
                }
            }
            else if (IsHumanInTown())
            {
                Direction = GetDirectionTo(from);

                int oldSpeechHue = SpeechHue;

                SpeechHue = 0x23F;
                SayTo(from, "Thou art giving me gold?");

                if (dropped.Amount >= 400)
                {
                    SayTo(from, "'Tis a noble gift.");
                }
                else
                {
                    SayTo(from, "Money is always welcome.");
                }

                SpeechHue = 0x3B2;
                SayTo(from, 501548); // I thank thee.

                SpeechHue = oldSpeechHue;

                dropped.Delete();
                return true;
            }

            return false;
        }

        public override bool ShouldCheckStatTimers => false;

        #region Food
        private static readonly Type[] m_Eggs = new[] { typeof(FriedEggs), typeof(Eggs) };

        private static readonly Type[] m_Fish = new[] { typeof(FishSteak), typeof(RawFishSteak) };

        private static readonly Type[] m_GrainsAndHay = new[] { typeof(BreadLoaf), typeof(FrenchBread), typeof(SheafOfHay) };

        private static readonly Type[] m_Meat = new[]
        {
            /* Cooked */
            typeof(Bacon), typeof(CookedBird), typeof(Sausage), typeof(Ham), typeof(Ribs), typeof(LambLeg), typeof(ChickenLeg),
            /* Uncooked */
            typeof(RawBird), typeof(RawRibs), typeof(RawLambLeg), typeof(RawChickenLeg), /* Body Parts */
            typeof(Head), typeof(LeftArm), typeof(LeftLeg), typeof(Torso), typeof(RightArm), typeof(RightLeg)
        };

        private static readonly Type[] m_FruitsAndVegies = new[]
        {
            typeof(HoneydewMelon), typeof(YellowGourd), typeof(GreenGourd), typeof(Banana), typeof(Bananas), typeof(Lemon),
            typeof(Lime), typeof(Dates), typeof(Grapes), typeof(Peach), typeof(Pear), typeof(Apple), typeof(Watermelon),
            typeof(Squash), typeof(Cantaloupe), typeof(Carrot), typeof(Cabbage), typeof(Onion), typeof(Lettuce), typeof(Pumpkin)
        };

        private static readonly Type[] m_Gold = new[]
        {
            // white wyrms eat gold..
            typeof(Gold)
        };

        private static readonly Type[] m_Metal = new[]
        {
            // Some Stygian Abyss Monsters eat Metal..
            typeof(IronIngot), typeof(DullCopperIngot), typeof(ShadowIronIngot), typeof(CopperIngot), typeof(BronzeIngot),
            typeof(GoldIngot), typeof(AgapiteIngot), typeof(VeriteIngot), typeof(ValoriteIngot)
        };

        private static readonly Type[] m_BlackrockStew =
        {
            typeof(BowlOfBlackrockStew)
        };

        public virtual bool CheckFoodPreference(Item f)
        {
            if (FavoriteFood == FoodType.None)
            {
                return false;
            }

            if (CheckFoodPreference(f, FoodType.Eggs, m_Eggs))
            {
                return true;
            }

            if (CheckFoodPreference(f, FoodType.Fish, m_Fish))
            {
                return true;
            }

            if (CheckFoodPreference(f, FoodType.GrainsAndHay, m_GrainsAndHay))
            {
                return true;
            }

            if (CheckFoodPreference(f, FoodType.Meat, m_Meat))
            {
                return true;
            }

            if (CheckFoodPreference(f, FoodType.FruitsAndVegies, m_FruitsAndVegies))
            {
                return true;
            }

            if (CheckFoodPreference(f, FoodType.Metal, m_Metal))
            {
                return true;
            }

            if (CheckFoodPreference(f, FoodType.BlackrockStew, m_BlackrockStew))
            {
                return true;
            }

            return false;
        }

        public virtual bool CheckFoodPreference(Item fed, FoodType type, Type[] types)
        {
            if ((FavoriteFood & type) == 0)
            {
                return false;
            }

            return types.Any(t => t == fed.GetType() || fed.GetType().IsSubclassOf(t));
        }

        public virtual bool CheckFeed(Mobile from, Item dropped)
        {
            if (!IsDeadPet && Controlled && (ControlMaster == from || IsPetFriend(from)))
            {
                Item f = dropped;

                if (CheckFoodPreference(f))
                {
                    int amount = f.Amount;

                    if (amount > 0)
                    {
                        bool happier = false;

                        int stamGain;

                        if (f is Gold)
                        {
                            stamGain = amount - 50;
                        }
                        else
                        {
                            stamGain = (amount * 15) - 50;
                        }

                        if (stamGain > 0)
                        {
                            Stam += stamGain;
                        }

                        if (m_Loyalty < MaxLoyalty)
                        {
                            m_Loyalty = MaxLoyalty;
                            happier = true;
                        }

                        if (happier)
                        {
                            SayTo(from, 502060); // Your pet looks happier.
                        }

                        Animate(AnimationType.Eat, 0);

                        if (IsBondable && !IsBonded)
                        {
                            Mobile master = m_ControlMaster;

                            if (master != null && master == from) //So friends can't start the bonding process
                            {
                                if (m_CurrentTameSkill <= 29.1 || master.Skills[SkillName.AnimalTaming].Base >= m_CurrentTameSkill ||
                                    OverrideBondingReqs() || (master.Skills[SkillName.AnimalTaming].Value >= m_CurrentTameSkill))
                                {
                                    if (BondingBegin == DateTime.MinValue)
                                    {
                                        BondingBegin = DateTime.UtcNow;
                                    }
                                    else if ((BondingBegin + BondingDelay) <= DateTime.UtcNow)
                                    {
                                        IsBonded = true;
                                        BondingBegin = DateTime.MinValue;
                                        from.SendLocalizedMessage(1049666); // Your pet has bonded with you!
                                    }
                                }
                                else
                                {
                                    from.SendLocalizedMessage(1075268);
                                    // Your pet cannot form a bond with you until your animal taming ability has risen.
                                }
                            }
                        }

                        dropped.Delete();
                        return true;
                    }
                }
            }

            return false;
        }
        #endregion

        public virtual bool OverrideBondingReqs()
        {
            return false;
        }

        public virtual bool CanAngerOnTame => false;

        #region OnAction[...]
        public virtual void OnActionWander()
        { }

        public virtual void OnActionCombat()
        { }

        public virtual void OnActionGuard()
        { }

        public virtual void OnActionFlee()
        { }

        public virtual void OnActionInteract()
        { }

        public virtual void OnActionBackoff()
        { }
        #endregion

        public override bool OnDragDrop(Mobile from, Item dropped)
        {
            bool canDrop = false;

            if (CheckFeed(from, dropped))
            {
                canDrop = true;
            }
            
            if (!canDrop && CheckGold(from, dropped))
            {
                canDrop = true;
            }
            
            if (!canDrop && !from.InRange(Location, 2) && base.OnDragDrop(from, dropped))
            {
                return true;
            }

            if (!canDrop)
            {
                PrivateOverheadMessage(MessageType.Regular, 0x3B2, 1043257, from.NetState); // The animal shies away.
            }

            return canDrop;
        }

        protected virtual BaseAI ForcedAI => null;

        public void ChangeAIType(AIType NewAI)
        {
            if (m_AI != null)
            {
                m_AI.m_Timer.Stop();
            }

            if (ForcedAI != null)
            {
                m_AI = ForcedAI;
                return;
            }

            m_AI = null;

            switch (NewAI)
            {
                case AIType.AI_Melee:
                    m_AI = new MeleeAI(this);
                    break;
                case AIType.AI_Archer:
                    m_AI = new ArcherAI(this);
                    break;
                case AIType.AI_Healer:
                    m_AI = new HealerAI(this);
                    break;
                case AIType.AI_Vendor:
                    m_AI = new VendorAI(this);
                    break;
                case AIType.AI_Mage:
                    m_AI = new MageAI(this);
                    break;
                case AIType.AI_NecroMage:
                    m_AI = new NecroMageAI(this);
                    break;
                case AIType.AI_OrcScout:
                    m_AI = new OrcScoutAI(this);
                    break;
                case AIType.AI_Samurai:
                    m_AI = new SamuraiAI(this);
                    break;
                case AIType.AI_Ninja:
                    m_AI = new NinjaAI(this);
                    break;
                case AIType.AI_Spellweaving:
                    m_AI = new SpellweavingAI(this);
                    break;
                case AIType.AI_Mystic:
                    m_AI = new MysticAI(this);
                    break;
                case AIType.AI_Paladin:
                    m_AI = new PaladinAI(this);
                    break;
                case AIType.AI_Spellbinder:
                    m_AI = new SpellbinderAI(this);
                    break;
                case AIType.AI_Necro:
                    m_AI = new NecroAI(this);
                    break;
        /*case AIType.AI_WildAnimal:
        if (!(this is BaseAnimal))
            {
                m_AI = new AnimalAI(this);
                m_CurrentAI = AIType.AI_Animal;
            }
            else m_AI = new WildAnimalAI(this);
            break;*/
            }
        }

        public void ChangeAIToDefault()
        {
            ChangeAIType(m_DefaultAI);
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public AIType AI
        {
            get { return m_CurrentAI; }
            set
            {
                m_CurrentAI = value;

                if (m_CurrentAI == AIType.AI_Use_Default)
                {
                    m_CurrentAI = m_DefaultAI;
                }

                ChangeAIType(m_CurrentAI);
            }
        }

        [CommandProperty(AccessLevel.Administrator)]
        public bool Debug { get { return m_bDebugAI; } set { m_bDebugAI = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public int Team
        {
            get { return m_iTeam; }
            set
            {
                m_iTeam = value;

                OnTeamChange();
            }
        }

        public virtual void OnTeamChange()
        { }

        [CommandProperty(AccessLevel.GameMaster)]
        public IDamageable FocusMob { get; set; }

        [CommandProperty(AccessLevel.GameMaster)]
        public FightMode FightMode { get { return m_FightMode; } set { m_FightMode = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public int RangePerception { get { return m_iRangePerception; } set { m_iRangePerception = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public int RangeFight { get { return m_iRangeFight; } set { m_iRangeFight = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public int RangeHome { get { return m_iRangeHome; } set { m_iRangeHome = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public double ForceActiveSpeed { get { return m_ForceActiveSpeed; } set { m_ForceActiveSpeed = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public double ForcePassiveSpeed { get { return m_ForcePassiveSpeed; } set { m_ForcePassiveSpeed = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public double ActiveSpeed { get { return m_ForceActiveSpeed != 0.0 ? m_ForceActiveSpeed : m_dActiveSpeed; } set { m_dActiveSpeed = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public double PassiveSpeed { get { return m_ForcePassiveSpeed != 0.0 ? m_ForcePassiveSpeed : m_dPassiveSpeed; } set { m_dPassiveSpeed = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public double CurrentSpeed
        {
            get
            {
                if (m_TargetLocation != null)
                {
                    return 0.3;
                }

                return m_dCurrentSpeed;
            }
            set
            {
                if (m_dCurrentSpeed != value)
                {
                    m_dCurrentSpeed = value;

                    if (m_AI != null)
                    {
                        m_AI.OnCurrentSpeedChanged();
                    }
                }
            }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public Point3D Home { get { return m_pHome; } set { m_pHome = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool Controlled
        {
            get { return m_bControlled; }
            set
            {
                if (m_bControlled == value)
                {
                    return;
                }

                m_bControlled = value;
                Delta(MobileDelta.Noto);

                InvalidateProperties();
            }
        }

        #region Snake Charming
        private Mobile m_CharmMaster;
        private Point2D m_CharmTarget;
        private Timer m_CharmTimer;

        public void BeginCharm(Mobile master, Point2D target)
        {
            m_CharmMaster = master;
            m_CharmTarget = target;

            m_CharmTimer = new CharmTimer(this);
            m_CharmTimer.Start();
        }

        public void EndCharm()
        {
            if (!Deleted && m_CharmMaster != null)
            {
                // The charm seems to wear off.
                PrivateOverheadMessage(MessageType.Regular, 0x3B2, 1112181, m_CharmMaster.NetState);

                Frozen = false;

                m_CharmMaster = null;
                m_CharmTarget = Point2D.Zero;

                if (m_CharmTimer != null)
                {
                    m_CharmTimer.Stop();
                    m_CharmTimer = null;
                }
            }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public Mobile CharmMaster => m_CharmMaster;

        [CommandProperty(AccessLevel.GameMaster)]
        public Point2D CharmTarget => m_CharmTarget;

        private class CharmTimer : Timer
        {
            private readonly BaseCreature m_Owner;
            private int m_Count;

            public CharmTimer(BaseCreature owner)
                : base(TimeSpan.Zero, TimeSpan.FromSeconds(2.0))
            {
                m_Owner = owner;
                m_Count = 10;
            }

            protected override void OnTick()
            {
                if (m_Count == 0 || m_Owner.CharmMaster == null || !m_Owner.CharmMaster.InRange(m_Owner.Location, 10))
                {
                    Stop();
                    m_Owner.EndCharm();
                }
                else
                {
                    m_Owner.FixedParticles(0x376A, 9, 32, 5030, EffectLayer.Waist);
                    m_Count--;
                }
            }
        }
        #endregion

        public override void RevealingAction()
        {
            InvisibilitySpell.RemoveTimer(this);

            base.RevealingAction();
        }

        public void RemoveFollowers()
        {
            if (m_ControlMaster != null)
            {
                m_ControlMaster.Followers -= ControlSlots;

                if (m_ControlMaster is PlayerMobile)
                {
                    PlayerMobile pm = (PlayerMobile)m_ControlMaster;

                    pm.AllFollowers.Remove(this);

                    if (pm.AutoStabled.Contains(this))
                    {
                        pm.AutoStabled.Remove(this);
                    }

                    NetState ns = m_ControlMaster.NetState;

                    if (ns != null && ns.IsEnhancedClient && Commandable)
                    {
                        ns.Send(new PetWindow(pm, this));
                    }

                    if (KhaldunTastyTreat.UnderInfluence(this))
                    {
                        Caddellite.UpdateBuff(m_ControlMaster);
                    }
                }
            }
            else if (m_SummonMaster != null)
            {
                m_SummonMaster.Followers -= ControlSlots;

                if (m_SummonMaster is PlayerMobile)
                {
                    ((PlayerMobile)m_SummonMaster).AllFollowers.Remove(this);
                }
            }

            if (m_ControlMaster != null && m_ControlMaster.Followers < 0)
            {
                m_ControlMaster.Followers = 0;
            }

            if (m_SummonMaster != null && m_SummonMaster.Followers < 0)
            {
                m_SummonMaster.Followers = 0;
            }
        }

        public void AddFollowers()
        {
            if (m_ControlMaster != null)
            {
                m_ControlMaster.Followers += ControlSlots;

                if (m_ControlMaster is PlayerMobile && !(this is PersonalAttendant))
                {
                    ((PlayerMobile)m_ControlMaster).AllFollowers.Add(this);

                    NetState ns = m_ControlMaster.NetState;

                    if (ns != null && ns.IsEnhancedClient && Commandable)
                    {
                        ns.Send(new PetWindow((PlayerMobile)m_ControlMaster, this));
                    }

                    if (KhaldunTastyTreat.UnderInfluence(this))
                    {
                        Caddellite.UpdateBuff(m_ControlMaster);
                    }
                }
            }
            else if (m_SummonMaster != null)
            {
                m_SummonMaster.Followers += ControlSlots;
                if (m_SummonMaster is PlayerMobile)
                {
                    ((PlayerMobile)m_SummonMaster).AllFollowers.Add(this);
                }
            }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public Mobile ControlMaster
        {
            get { return m_ControlMaster; }
            set
            {
                if (m_ControlMaster == value || this == value)
                {
                    return;
                }

                RemoveFollowers();
                m_ControlMaster = value;
                AddFollowers();

                if (m_ControlMaster != null)
                {
                    StopDeleteTimer();
                }

                Delta(MobileDelta.Noto);
            }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public Mobile SummonMaster
        {
            get { return m_SummonMaster; }
            set
            {
                if (m_SummonMaster == value || this == value)
                {
                    return;
                }

                RemoveFollowers();
                m_SummonMaster = value;
                AddFollowers();

                Delta(MobileDelta.Noto);
            }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public IDamageable ControlTarget { get { return m_ControlTarget; } set { m_ControlTarget = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public Point3D ControlDest { get { return m_ControlDest; } set { m_ControlDest = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public virtual OrderType ControlOrder
        {
            get { return m_ControlOrder; }
            set
            {
                m_ControlOrder = value;

                if (m_Allured && m_ControlOrder != OrderType.None)
                {
                    Say(1079120); // Very well.
                }

                if (m_AI != null)
                {
                    m_AI.OnCurrentOrderChanged();
                }

                InvalidateProperties();

                if (m_ControlMaster != null)
                {
                    m_ControlMaster.InvalidateProperties();
                }
            }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool BardProvoked { get { return m_bBardProvoked; } set { m_bBardProvoked = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool BardPacified { get { return m_bBardPacified; } set { m_bBardPacified = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public Mobile BardMaster { get { return m_bBardMaster; } set { m_bBardMaster = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public Mobile BardTarget { get { return m_bBardTarget; } set { m_bBardTarget = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public DateTime BardEndTime { get; set; }

        [CommandProperty(AccessLevel.GameMaster)]
        public double MinTameSkill
        {
            get { return m_dMinTameSkill; }
            set
            {
                double skill = m_dMinTameSkill;

                if (skill != value)
                {
                    m_dMinTameSkill = value;
                    double adjusted = CurrentTameSkill - skill;

                    if (adjusted > 0)
                    {
                        m_CurrentTameSkill = value + adjusted;
                    }
                    else
                    {
                        m_CurrentTameSkill = value;
                    }
                }
            }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public double CurrentTameSkill { get { return m_CurrentTameSkill; } set { m_CurrentTameSkill = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool Tamable { get { return m_bTamable && !m_Paragon; } set { m_bTamable = value; } }

        [CommandProperty(AccessLevel.Administrator)]
        public bool Summoned
        {
            get { return m_bSummoned; }
            set
            {
                if (m_bSummoned == value)
                {
                    return;
                }

                m_NextReacquireTime = Core.TickCount;

                m_bSummoned = value;
                Delta(MobileDelta.Noto);

                InvalidateProperties();
            }
        }

        [CommandProperty(AccessLevel.Administrator)]
        public int ControlSlots
        {
            get { return m_iControlSlots; }
            set
            {
                if (ControlSlotsMin == 0 && ControlSlotsMax == 0)
                {
                    m_iControlSlots = value;

                    CalculateSlots(value);

                    if (m_iControlSlots != ControlSlotsMin)
                    {
                        m_iControlSlots = ControlSlotsMin;
                    }
                    else if (m_iControlSlots > ControlSlotsMax)
                    {
                        m_iControlSlots = ControlSlotsMax;
                    }
                }
                else
                {
                    m_iControlSlots = value;
                }
            }
        }

        [CommandProperty(AccessLevel.Administrator)]
        public int ControlSlotsMax { get; set; }

        [CommandProperty(AccessLevel.Administrator)]
        public int ControlSlotsMin { get; set; }

        public virtual bool NoHouseRestrictions => false;
        public virtual bool IsHouseSummonable => false;

        #region Corpse Resources
        public virtual int Feathers => 0;
        public virtual int Wool => 0;

        public virtual int Fur => 0;
        public virtual FurType FurType => FurType.Green;

        public virtual MeatType MeatType => MeatType.Ribs;
        public virtual int Meat => 0;

        public virtual int Hides => 0;
        public virtual HideType HideType => HideType.Regular;

        public virtual int Scales => 0;
        public virtual ScaleType ScaleType => ScaleType.Red;

        public virtual int DragonBlood => 0;
        #endregion

        public virtual bool AutoDispel => false;
        public virtual double AutoDispelChance => 0.1;

        public virtual bool IsScaryToPets => false;
        public virtual bool IsScaredOfScaryThings => true;

        public virtual bool CanRummageCorpses => false;

        public virtual void OnGotMeleeAttack(Mobile attacker)
        {
            if (AutoDispel && attacker is BaseCreature && ((BaseCreature)attacker).IsDispellable &&
                AutoDispelChance > Utility.RandomDouble())
            {
                Dispel(attacker);
            }
        }

        public virtual void Dispel(Mobile m)
        {
            Effects.SendLocationParticles(EffectItem.Create(m.Location, m.Map, EffectItem.DefaultDuration), 0x3728, 8, 20, 5042);
            Effects.PlaySound(m, m.Map, 0x201);

            m.Delete();
        }

        public virtual bool DeleteOnRelease => m_bSummoned || m_Allured;

        public virtual void OnGaveMeleeAttack(Mobile defender)
        {
            Poison p = GetHitPoison();

            if (m_Paragon)
            {
                p = PoisonImpl.IncreaseLevel(p);
            }

            if (p != null)
            {
                if (TryHitPoison())
                {
                    defender.FixedEffect(0x3779, 1, 10, 1271, 0);
                    defender.ApplyPoison(this, p);
                }

                if (Controlled)
                {
                    if (AbilityProfile != null && AbilityProfile.HasAbility(MagicalAbility.Poisoning))
                    {
                        CheckSkill(SkillName.Poisoning, 0, Skills[SkillName.Poisoning].Cap);
                    }
                }
            }

            if (AutoDispel && defender is BaseCreature && ((BaseCreature)defender).IsDispellable &&
                AutoDispelChance > Utility.RandomDouble())
            {
                Dispel(defender);
            }

            if (ColossalRage.HasRage(this) && 0.33 >= Utility.RandomDouble())
            {
                DoRageHit(defender);
            }
        }

        public virtual Poison GetHitPoison()
        {
            if (!Controlled)
                return HitPoison;

            int current = 0;

            if (HitPoison != null)
                current = HitPoison.Level;

            AbilityProfile profile = AbilityProfile;

            if (profile == null || !profile.HasAbility(MagicalAbility.Poisoning) || current >= 4)
                return HitPoison;

            int level = 1;
            double total = Skills[SkillName.Poisoning].Value;

            // natural poisoner retains their poison level. Added spell school is capped at level 2.
            if (total >= 100)
                level = 4;
            else if (total > 85)
                level = 3;
            else if (total > 65)
                level = 2;
            else if (total > 35)
                level = 1;

            return Poison.GetPoison(Math.Max(current, level));
        }

        private bool TryHitPoison()
        {
            if (!Controlled)
                return HitPoisonChance >= Utility.RandomDouble();

            AbilityProfile profile = AbilityProfile;

            if (profile == null || !profile.HasAbility(MagicalAbility.Poisoning))
                return false;

            return Skills[SkillName.Poisoning].Value >= Utility.Random(300);
        }

        public override void OnAfterDelete()
        {
            if (m_AI != null)
            {
                if (m_AI.m_Timer != null)
                {
                    m_AI.m_Timer.Stop();
                }

                m_AI = null;
            }

            StopDeleteTimer();

            FocusMob = null;

            base.OnAfterDelete();
        }

        public void DebugSay(string text)
        {
            if (m_bDebugAI)
            {
                PublicOverheadMessage(MessageType.Regular, 41, false, text);
            }
        }

        public void DebugSay(string format, params object[] args)
        {
            if (m_bDebugAI)
            {
                PublicOverheadMessage(MessageType.Regular, 41, false, string.Format(format, args));
            }
        }

        /*
        * This function can be overriden.. so a "Strongest" mobile, can have a different definition depending
        * on who check for value
        * -Could add a FightMode.Prefered
        *
        */

        public virtual double GetFightModeRanking(Mobile m, FightMode acqType, bool bPlayerOnly)
        {
            if ((bPlayerOnly && m.Player) || !bPlayerOnly)
            {
                switch (acqType)
                {
                    case FightMode.Strongest:
                        return (m.Skills[SkillName.Tactics].Value + m.Str); //returns strongest mobile

                    case FightMode.Weakest:
                        return -m.Hits; // returns weakest mobile

                    default:
                        return -GetDistanceToSqrt(m); // returns closest mobile
                }
            }
            else
            {
                return double.MinValue;
            }
        }

        // Turn, - for left, + for right
        // Basic for now, needs work
        public virtual void Turn(int iTurnSteps)
        {
            int v = (int)Direction;

            Direction = (Direction)((((v & 0x7) + iTurnSteps) & 0x7) | (v & 0x80));
        }

        public virtual void TurnInternal(int iTurnSteps)
        {
            int v = (int)Direction;

            SetDirection((Direction)((((v & 0x7) + iTurnSteps) & 0x7) | (v & 0x80)));
        }

        public bool IsHurt()
        {
            return (Hits != HitsMax);
        }

        public double GetHomeDistance()
        {
            return GetDistanceToSqrt(m_pHome);
        }

        public virtual int GetTeamSize(int iRange)
        {
            int iCount = 0;

            IPooledEnumerable eable = GetMobilesInRange(iRange);

            foreach (Mobile m in eable)
            {
                if (m is BaseCreature)
                {
                    if (((BaseCreature)m).Team == Team)
                    {
                        if (!m.Deleted)
                        {
                            if (m != this)
                            {
                                if (CanSee(m))
                                {
                                    iCount++;
                                }
                            }
                        }
                    }
                }
            }

            eable.Free();

            return iCount;
        }

        private class TameEntry : ContextMenuEntry
        {
            private readonly BaseCreature m_Mobile;

            public TameEntry(Mobile from, BaseCreature creature)
                : base(6130, 6)
            {
                m_Mobile = creature;

                Enabled = Enabled && (from.Female ? creature.AllowFemaleTamer : creature.AllowMaleTamer);
            }

            public override void OnClick()
            {
                if (!Owner.From.CheckAlive())
                {
                    return;
                }

                Owner.From.TargetLocked = true;
                AnimalTaming.DisableMessage = true;
                AnimalTaming.DeferredTarget = false;

                if (Owner.From.UseSkill(SkillName.AnimalTaming) && Owner.From.Target != null)
                {
                    Owner.From.Target.Invoke(Owner.From, m_Mobile);
                }

                AnimalTaming.DeferredTarget = true;
                AnimalTaming.DisableMessage = false;
                Owner.From.TargetLocked = false;
            }
        }

        private class RenameEntry : ContextMenuEntry
        {
            private readonly Mobile m_From;
            private readonly BaseCreature m_Creature;

            public RenameEntry(Mobile from, BaseCreature creature)
                : base(1111680, 6)
            {
                m_From = from;
                m_Creature = creature;
            }

            public override void OnClick()
            {
                if (!m_Creature.Deleted && m_Creature.Controlled && m_Creature.ControlMaster == m_From)
                    m_From.Prompt = new PetRenamePrompt(m_Creature);
            }
        }

        public class PetRenamePrompt : Prompt
        {
            public override int MessageCliloc => 1115558; // Enter a new name for your pet.

            private readonly BaseCreature m_Creature;

            public PetRenamePrompt(BaseCreature creature)
                : base(creature)
            {
                m_Creature = creature;
            }

            public override void OnCancel(Mobile from)
            {
                from.SendLocalizedMessage(501806); // Request cancelled.
            }

            public override void OnResponse(Mobile from, string text)
            {
                if (!m_Creature.Deleted && m_Creature.Controlled && m_Creature.ControlMaster == from)
                {
                    if (Utility.IsAlpha(text))
                    {
                        m_Creature.Name = text;
                        from.SendLocalizedMessage(1115559); // Pet name changed.
                    }
                    else
                    {
                        from.SendLocalizedMessage(1075246); // That name is not valid.
                    }
                }
            }
        }

        #region Teaching
        public virtual bool CanTeach => false;

        public virtual bool CheckTeach(SkillName skill, Mobile from)
        {
            if (!CanTeach || Siege.SiegeShard)
            {
                return false;
            }

            if (skill == SkillName.Stealth && from.Skills[SkillName.Hiding].Base < Stealth.HidingRequirement)
            {
                return false;
            }

            return true;
        }

        public enum TeachResult
        {
            Success,
            Failure,
            KnowsMoreThanMe,
            KnowsWhatIKnow,
            SkillNotRaisable,
            NotEnoughFreePoints
        }

        public virtual TeachResult CheckTeachSkills(
            SkillName skill, Mobile m, int maxPointsToLearn, ref int pointsToLearn, bool doTeach)
        {
            if (!CheckTeach(skill, m) || !m.CheckAlive())
            {
                return TeachResult.Failure;
            }

            Skill ourSkill = Skills[skill];
            Skill theirSkill = m.Skills[skill];

            if (ourSkill == null || theirSkill == null)
            {
                return TeachResult.Failure;
            }

            int baseToSet = ourSkill.BaseFixedPoint / 3;

            if (baseToSet > 420)
            {
                baseToSet = 420;
            }
            else if (baseToSet < 200)
            {
                return TeachResult.Failure;
            }

            if (baseToSet > theirSkill.CapFixedPoint)
            {
                baseToSet = theirSkill.CapFixedPoint;
            }

            pointsToLearn = baseToSet - theirSkill.BaseFixedPoint;

            if (maxPointsToLearn > 0 && pointsToLearn > maxPointsToLearn)
            {
                pointsToLearn = maxPointsToLearn;
                baseToSet = theirSkill.BaseFixedPoint + pointsToLearn;
            }

            if (pointsToLearn < 0)
            {
                return TeachResult.KnowsMoreThanMe;
            }

            if (pointsToLearn == 0)
            {
                return TeachResult.KnowsWhatIKnow;
            }

            if (theirSkill.Lock != SkillLock.Up)
            {
                return TeachResult.SkillNotRaisable;
            }

            int freePoints = m.Skills.Cap - m.Skills.Total;
            int freeablePoints = 0;

            if (freePoints < 0)
            {
                freePoints = 0;
            }

            for (int i = 0; (freePoints + freeablePoints) < pointsToLearn && i < m.Skills.Length; ++i)
            {
                Skill sk = m.Skills[i];

                if (sk == theirSkill || sk.Lock != SkillLock.Down)
                {
                    continue;
                }

                freeablePoints += sk.BaseFixedPoint;
            }

            if ((freePoints + freeablePoints) == 0)
            {
                return TeachResult.NotEnoughFreePoints;
            }

            if ((freePoints + freeablePoints) < pointsToLearn)
            {
                pointsToLearn = freePoints + freeablePoints;
                baseToSet = theirSkill.BaseFixedPoint + pointsToLearn;
            }

            if (doTeach)
            {
                int need = pointsToLearn - freePoints;

                for (int i = 0; need > 0 && i < m.Skills.Length; ++i)
                {
                    Skill sk = m.Skills[i];

                    if (sk == theirSkill || sk.Lock != SkillLock.Down)
                    {
                        continue;
                    }

                    if (sk.BaseFixedPoint < need)
                    {
                        need -= sk.BaseFixedPoint;
                        sk.BaseFixedPoint = 0;
                    }
                    else
                    {
                        sk.BaseFixedPoint -= need;
                        need = 0;
                    }
                }

                /* Sanity check */
                if (baseToSet > theirSkill.CapFixedPoint || (m.Skills.Total - theirSkill.BaseFixedPoint + baseToSet) > m.Skills.Cap)
                {
                    // Full refund
                    m.Backpack.TryDropItem(m, new Gold(maxPointsToLearn), false);
                    return TeachResult.NotEnoughFreePoints;
                }

                // Partial refund if needed
                if (maxPointsToLearn > pointsToLearn)
                {
                    m.Backpack.TryDropItem(m, new Gold(maxPointsToLearn - pointsToLearn), false);
                }
                theirSkill.BaseFixedPoint = baseToSet;
            }

            return TeachResult.Success;
        }

        public virtual bool CheckTeachingMatch(Mobile m)
        {
            if (m_Teaching == (SkillName)(-1))
            {
                return false;
            }

            if (m is PlayerMobile)
            {
                return (((PlayerMobile)m).Learning == m_Teaching);
            }

            return true;
        }

        private SkillName m_Teaching = (SkillName)(-1);

        public virtual bool Teach(SkillName skill, Mobile m, int maxPointsToLearn, bool doTeach)
        {
            int pointsToLearn = 0;
            TeachResult res = CheckTeachSkills(skill, m, maxPointsToLearn, ref pointsToLearn, doTeach);

            switch (res)
            {
                case TeachResult.KnowsMoreThanMe:
                    {
                        Say(501508); // I cannot teach thee, for thou knowest more than I!
                        break;
                    }
                case TeachResult.KnowsWhatIKnow:
                    {
                        Say(501509); // I cannot teach thee, for thou knowest all I can teach!
                        break;
                    }
                case TeachResult.NotEnoughFreePoints:
                case TeachResult.SkillNotRaisable:
                    {
                        // Make sure this skill is marked to raise. If you are near the skill cap (700 points) you may need to lose some points in another skill first.
                        m.SendLocalizedMessage(501510, "", 0x22);
                        break;
                    }
                case TeachResult.Success:
                    {
                        if (doTeach)
                        {
                            Say(501539); // Let me show thee something of how this is done.
                            m.SendLocalizedMessage(501540); // Your skill level increases.

                            m_Teaching = (SkillName)(-1);

                            if (m is PlayerMobile)
                            {
                                ((PlayerMobile)m).Learning = (SkillName)(-1);
                            }
                        }
                        else
                        {
                            // I will teach thee all I know, if paid the amount in full.  The price is:
                            Say(1019077, AffixType.Append, string.Format(" {0}", pointsToLearn), "");
                            Say(1043108); // For less I shall teach thee less.

                            m_Teaching = skill;

                            if (m is PlayerMobile)
                            {
                                ((PlayerMobile)m).Learning = skill;
                            }
                        }

                        return true;
                    }
            }

            return false;
        }
        #endregion

        public override void AggressiveAction(Mobile aggressor, bool criminal)
        {
            if (ControlMaster != null && ControlMaster != aggressor)
            {
                var master = ControlMaster;
                AggressorInfo info = master.Aggressors.FirstOrDefault(i => i.Attacker == aggressor);

                if (info != null)
                {
                    // already in the list, so we're refreshing it
                    info.Refresh();
                    info.CriminalAggression = criminal;
                }
                else
                {
                    // not in the list, so we're adding it
                    master.Aggressors.Add(AggressorInfo.Create(aggressor, master, criminal));

                    if (CanSee(aggressor) && NetState != null)
                    {
                        master.NetState.Send(MobileIncoming.Create(NetState, master, aggressor));
                    }

                    master.UpdateAggrExpire();
                }

                // Now, if the master is in the aggressor list, it needs to be refreshed
                info = aggressor.Aggressors.FirstOrDefault(i => i.Attacker == master);

                if (info != null)
                {
                    info.Refresh();
                }

                info = master.Aggressed.FirstOrDefault(i => i.Defender == aggressor);

                if (info != null)
                {
                    info.Refresh();
                }

                // next lets find out if our master is on the aggressors aggressed list
                info = aggressor.Aggressed.FirstOrDefault(i => i.Defender == master);

                if (info != null)
                {
                    // already in the list, so we're refreshing it
                    info.Refresh();
                    info.CriminalAggression = criminal;
                }
                else
                {
                    // not in the list, so we're adding it
                    aggressor.Aggressed.Add(AggressorInfo.Create(aggressor, master, criminal));

                    if (CanSee(aggressor) && NetState != null)
                    {
                        master.NetState.Send(MobileIncoming.Create(NetState, master, aggressor));
                    }

                    master.UpdateAggrExpire();
                }

                if (aggressor is PlayerMobile || (aggressor is BaseCreature && !((BaseCreature)aggressor).IsMonster))
                {
                    BuffInfo.AddBuff(master, new BuffInfo(BuffIcon.HeatOfBattleStatus, 1153801, 1153827, Aggression.CombatHeatDelay, master, true));
                    BuffInfo.AddBuff(aggressor, new BuffInfo(BuffIcon.HeatOfBattleStatus, 1153801, 1153827, Aggression.CombatHeatDelay, aggressor, true));
                }
            }
            else if (aggressor is BaseCreature)
            {
                var pm = ((BaseCreature)aggressor).GetMaster() as PlayerMobile;

                if (pm != null)
                {
                    AggressiveAction(pm, criminal);
                }
            }

            base.AggressiveAction(aggressor, criminal);

            OrderType ct = m_ControlOrder;

            if (m_AI != null)
            {
                if (ct != OrderType.Follow && ct != OrderType.Stop && ct != OrderType.Stay)
                {
                    m_AI.OnAggressiveAction(aggressor);
                }
                else
                {
                    DebugSay("I'm being attacked but my master told me not to fight.");
                    Warmode = false;
                    return;
                }
            }

            //StopFlee();
            ForceReacquire();

            if (aggressor.ChangingCombatant && (m_bControlled || m_bSummoned) &&
                (ct == OrderType.Come || ct == OrderType.Stay || ct == OrderType.Stop || ct == OrderType.None ||
                 ct == OrderType.Follow))
            {
                ControlTarget = aggressor;
                ControlOrder = OrderType.Attack;
            }
            else if (Combatant == null && !m_bBardPacified)
            {
                Warmode = true;
                Combatant = aggressor;
            }
        }

        public override bool OnMoveOver(Mobile m)
        {
            if (m is BaseCreature && !((BaseCreature)m).Controlled)
            {
                return (!Alive || !m.Alive || IsDeadBondedPet || m.IsDeadBondedPet) || (Hidden && IsStaff());
            }

            return base.OnMoveOver(m);
        }

        public virtual void AddCustomContextEntries(Mobile from, List<ContextMenuEntry> list)
        {
            /*#region FS:ATS Edits
            if (this is BaseBioCreature || this is BioCreature || this is BioMount)
            {
            }
            else if (from.Alive && this.Alive && this.Controlled == true && this.Summoned == false && FSATS.EnablePetLeveling == true)
            {
                bool nolevel = false;
                Type typ = this.GetType();
                string nam = typ.Name;

                foreach ( string check in FSATS.NoLevelCreatures )
                {
                      if ( check == nam )
                            nolevel = true;
                }

                if ( nolevel != true )
                    list.Add( new ContextMenus.PetMenu( from, this ) );
            }
            #endregion*/
}

        public override void GetContextMenuEntries(Mobile from, List<ContextMenuEntry> list)
        {
            base.GetContextMenuEntries(from, list);

            if (CanBeRenamedBy(from) && m_bControlled && m_ControlMaster == from && !m_bSummoned)
            {
                list.Add(new RenameEntry(from, this));
            }

            if (m_AI != null && Commandable)
            {
                m_AI.GetContextMenuEntries(from, list);
            }

            if (m_bTamable && !m_bControlled && from.Alive)
            {
                list.Add(new TameEntry(from, this));
            }

            AddCustomContextEntries(from, list);

            if (CanTeach && from.Alive)
            {
                Skills ourSkills = Skills;
                Skills theirSkills = from.Skills;

                for (int i = 0; i < ourSkills.Length && i < theirSkills.Length; ++i)
                {
                    Skill skill = ourSkills[i];
                    Skill theirSkill = theirSkills[i];

                    if (skill != null && theirSkill != null && skill.Base >= 60.0 && CheckTeach(skill.SkillName, from))
                    {
                        int toTeach = skill.BaseFixedPoint / 3;

                        if (toTeach > 420)
                        {
                            toTeach = 420;
                        }

                        list.Add(new TeachEntry((SkillName)i, this, from, (toTeach > theirSkill.BaseFixedPoint)));
                    }
                }
            }
        }

        public override bool HandlesOnSpeech(Mobile from)
        {
            InhumanSpeech speechType = SpeechType;

            if (speechType != null && (speechType.Flags & IHSFlags.OnSpeech) != 0 && from.InRange(this, 3))
            {
                return true;
            }

            return (m_AI != null && m_AI.HandlesOnSpeech(from) && from.InRange(this, m_iRangePerception));
        }

        public override void OnSpeech(SpeechEventArgs e)
        {
            InhumanSpeech speechType = SpeechType;

            if (speechType != null && speechType.OnSpeech(this, e.Mobile, e.Speech))
            {
                e.Handled = true;
            }
            else if (!e.Handled && m_AI != null && e.Mobile.InRange(this, m_iRangePerception))
            {
                m_AI.OnSpeech(e);
            }
        }

        public override bool IsHarmfulCriminal(IDamageable damageable)
        {
            Mobile target = damageable as Mobile;

            if ((Controlled && target == m_ControlMaster) || (Summoned && target == m_SummonMaster))
            {
                return false;
            }

            if (target is BaseCreature && ((BaseCreature)target).InitialInnocent && !((BaseCreature)target).Controlled)
            {
                return false;
            }

            if (target is PlayerMobile && ((PlayerMobile)target).PermaFlags.Count > 0)
            {
                return false;
            }

            return base.IsHarmfulCriminal(damageable);
        }

        public override void CriminalAction(bool message)
        {
            base.CriminalAction(message);

            if (Controlled || Summoned)
            {
                if (m_ControlMaster != null && m_ControlMaster.Player)
                {
                    m_ControlMaster.CriminalAction(false);
                }
                else if (m_SummonMaster != null && m_SummonMaster.Player)
                {
                    m_SummonMaster.CriminalAction(false);
                }
            }
        }

        public override void DoHarmful(IDamageable damageable, bool indirect)
        {
            if (RecentSetControl && GetMaster() == damageable as Mobile)
            {
                return;
            }

            base.DoHarmful(damageable, indirect);

            Mobile target = damageable as Mobile;

            if (target == null)
                return;

            if (target == this || target == m_ControlMaster || target == m_SummonMaster || (!Controlled && !Summoned))
            {
                return;
            }

            if (ViceVsVirtueSystem.Enabled && Map == ViceVsVirtueSystem.Facet)
            {
                ViceVsVirtueSystem.CheckHarmful(this, target);
            }
        }

        public override void DoBeneficial(Mobile target)
        {
            base.DoBeneficial(target);

            if (ViceVsVirtueSystem.Enabled && Map == ViceVsVirtueSystem.Facet && target != null)
            {
                ViceVsVirtueSystem.CheckBeneficial(this, target);
            }
        }

        private static Mobile m_NoDupeGuards;

        public void ReleaseGuardDupeLock()
        {
            m_NoDupeGuards = null;
        }

        public void ReleaseGuardLock()
        {
            EndAction(typeof(GuardedRegion));
        }

        private DateTime m_IdleReleaseTime;

        public virtual bool CheckIdle()
        {
            if (Combatant != null)
            {
                return false; // in combat.. not idling
            }

            if (m_IdleReleaseTime > DateTime.MinValue)
            {
                // idling...
                if (DateTime.UtcNow >= m_IdleReleaseTime)
                {
                    m_IdleReleaseTime = DateTime.MinValue;
                    return false; // idle is over
                }

                return true; // still idling
            }

            if (95 > Utility.Random(100))
            {
                return false; // not idling, but don't want to enter idle state
            }

            m_IdleReleaseTime = DateTime.UtcNow + TimeSpan.FromSeconds(Utility.RandomMinMax(15, 25));

            Animate(AnimationType.Fidget, 0);

            PlaySound(GetIdleSound());

            return true; // entered idle state
        }

        public override void Animate(int action, int frameCount, int repeatCount, bool forward, bool repeat, int delay)
        {
            base.Animate(action, frameCount, repeatCount, forward, repeat, delay);
        }

        private void CheckAIActive()
        {
            Map map = Map;

            if (PlayerRangeSensitive && m_AI != null && map != null && map.GetSector(Location).Active)
            {
                m_AI.Activate();
            }
        }

        public override void OnCombatantChange()
        {
            base.OnCombatantChange();

            Warmode = (Combatant != null && !Combatant.Deleted && Combatant.Alive);

            if (Warmode)
            {
                Animate(AnimationType.Alert, 0);

                if (CanFly)
                {
                    Flying = false;
                }
            }
        }

        protected override void OnMapChange(Map oldMap)
        {
            CheckAIActive();

            base.OnMapChange(oldMap);
        }

        protected override void OnLocationChange(Point3D oldLocation)
        {
            CheckAIActive();

            base.OnLocationChange(oldLocation);
        }

        public virtual void ForceReacquire()
        {
            m_NextReacquireTime = Core.TickCount;
        }

        public virtual bool CanStealth => false;
        public virtual bool SupportsRunAnimation => true;

        protected override bool OnMove(Direction d)
        {

            if (Hidden) //Hidden, let's try stealth
            {
                if (!Mounted && Skills.Stealth.Value >= 25.0 && CanStealth)
                {
                    bool running = (d & Direction.Running) != 0;

                    if (running)
                    {
                        if ((AllowedStealthSteps -= 2) <= 0)
                            RevealingAction();
                    }
                    else if (AllowedStealthSteps-- <= 0)
                    {
                        Stealth.OnUse(this);
                    }
                }
                else
                {
                    RevealingAction();
                }
            }

            return true;

        }

        public override void OnMovement(Mobile m, Point3D oldLocation)
        {
            if (AcquireOnApproach && (!Controlled && !Summoned) && FightMode == FightMode.Closest && IsEnemy(m))
            {
                if (InRange(m.Location, AcquireOnApproachRange) && !InRange(oldLocation, AcquireOnApproachRange))
                {
                    if (CanBeHarmful(m) && IsEnemy(m))
                    {
                        Combatant = FocusMob = m;

                        if (AIObject != null)
                        {
                            AIObject.MoveTo(m, true, 1);
                        }

                        DoHarmful(m);
                    }
                }
            }
            else if (ReacquireOnMovement)
            {
                ForceReacquire();
            }

            SpecialAbility.CheckApproachTrigger(this, m, oldLocation);

            InhumanSpeech speechType = SpeechType;

            if (speechType != null)
            {
                speechType.OnMovement(this, m, oldLocation);
            }

            /* Begin notice sound */
            if ((!m.Hidden || m.IsPlayer()) && m.Player && m_FightMode != FightMode.Aggressor && m_FightMode != FightMode.None &&
                Combatant == null && !Controlled && !Summoned)
            {
                // If this creature defends itself but doesn't actively attack (animal) or
                // doesn't fight at all (vendor) then no notice sounds are played..
                // So, players are only notified of aggressive monsters
                // Monsters that are currently fighting are ignored
                // Controlled or summoned creatures are ignored
                if (InRange(m.Location, 18) && !InRange(oldLocation, 18))
                {
                    if (Body.IsMonster)
                    {
                        Animate(AnimationType.Pillage, 0);
                    }

                    PlaySound(GetAngerSound());
                }
            }
            /* End notice sound */

            if (m_NoDupeGuards == m)
            {
                return;
            }

            if (!Body.IsHuman || Murderer || AlwaysMurderer || AlwaysAttackable || m.Kills < 5 || !m.InRange(Location, 12) ||
                !m.Alive)
            {
                return;
            }

            GuardedRegion guardedRegion = (GuardedRegion)Region.GetRegion(typeof(GuardedRegion));

            if (guardedRegion != null)
            {
                if (!guardedRegion.IsDisabled() && guardedRegion.IsGuardCandidate(m) && BeginAction(typeof(GuardedRegion)))
                {
                    Say(1013037 + Utility.Random(16));
                    guardedRegion.CallGuards(Location);

                    Timer.DelayCall(TimeSpan.FromSeconds(5.0), ReleaseGuardLock);

                    m_NoDupeGuards = m;
                    Timer.DelayCall(TimeSpan.Zero, ReleaseGuardDupeLock);
                }
            }
        }

        public void AddSpellAttack(Type type)
        {
            m_arSpellAttack.Add(type);
        }

        public void AddSpellDefense(Type type)
        {
            m_arSpellDefense.Add(type);
        }

        public Spell GetAttackSpellRandom()
        {
            if (m_arSpellAttack.Count > 0)
            {
                Type type = m_arSpellAttack[Utility.Random(m_arSpellAttack.Count)];

                object[] args = { this, null };
                return Activator.CreateInstance(type, args) as Spell;
            }
            else
            {
                return null;
            }
        }

        public Spell GetDefenseSpellRandom()
        {
            if (m_arSpellDefense.Count > 0)
            {
                Type type = m_arSpellDefense[Utility.Random(m_arSpellDefense.Count)];

                object[] args = { this, null };
                return Activator.CreateInstance(type, args) as Spell;
            }
            else
            {
                return null;
            }
        }

        public Spell GetSpellSpecific(Type type)
        {
            int i;

            for (i = 0; i < m_arSpellAttack.Count; i++)
            {
                if (m_arSpellAttack[i] == type)
                {
                    object[] args = { this, null };
                    return Activator.CreateInstance(type, args) as Spell;
                }
            }

            for (i = 0; i < m_arSpellDefense.Count; i++)
            {
                if (m_arSpellDefense[i] == type)
                {
                    object[] args = { this, null };
                    return Activator.CreateInstance(type, args) as Spell;
                }
            }

            return null;
        }

        #region Set[...]
        public void SetDamage(int val)
        {
            m_DamageMin = val;
            m_DamageMax = val;
        }

        public void SetDamage(int min, int max)
        {
            m_DamageMin = min;
            m_DamageMax = max;
        }

        public void SetHits(int val)
        {
            m_HitsMax = val;
            Hits = HitsMax;
        }

        public void SetHits(int min, int max)
        {
            m_HitsMax = Utility.RandomMinMax(min, max);
            Hits = HitsMax;
            SetAverage(min, max, m_HitsMax);
        }

        public void SetStam(int val)
        {
            m_StamMax = val;
            Stam = StamMax;
        }

        public void SetStam(int min, int max)
        {
            m_StamMax = Utility.RandomMinMax(min, max);
            Stam = StamMax;
            SetAverage(min, max, m_StamMax);
        }

        public void SetMana(int val)
        {
            m_ManaMax = val;
            Mana = ManaMax;
        }

        public void SetMana(int min, int max)
        {
            m_ManaMax = Utility.RandomMinMax(min, max);
            Mana = ManaMax;
            SetAverage(min, max, m_ManaMax);
        }

        public void SetStr(int val)
        {
            RawStr = val;
            Hits = HitsMax;
        }

        public void SetStr(int min, int max)
        {
            RawStr = Utility.RandomMinMax(min, max);
            Hits = HitsMax;
            SetAverage(min, max, RawStr);
        }

        public void SetDex(int val)
        {
            RawDex = val;
            Stam = StamMax;
        }

        public void SetDex(int min, int max)
        {
            RawDex = Utility.RandomMinMax(min, max);
            Stam = StamMax;
            SetAverage(min, max, RawDex);
        }

        public void SetInt(int val)
        {
            RawInt = val;
            Mana = ManaMax;
        }

        public void SetInt(int min, int max)
        {
            RawInt = Utility.RandomMinMax(min, max);
            Mana = ManaMax;
            SetAverage(min, max, RawInt);
        }

        public void SetDamageType(ResistanceType type, int min, int max)
        {
            SetDamageType(type, Utility.RandomMinMax(min, max));
        }

        public void SetDamageType(ResistanceType type, int val)
        {
            switch (type)
            {
                case ResistanceType.Physical:
                    m_PhysicalDamage = val;
                    break;
                case ResistanceType.Fire:
                    m_FireDamage = val;
                    break;
                case ResistanceType.Cold:
                    m_ColdDamage = val;
                    break;
                case ResistanceType.Poison:
                    m_PoisonDamage = val;
                    break;
                case ResistanceType.Energy:
                    m_EnergyDamage = val;
                    break;
            }
        }

        public void SetResistance(ResistanceType type, int value)
        {
            SetResistance(type, value, value);
        }

        public void SetResistance(ResistanceType type, int min, int max)
        {
            int val = min == max ? min : Utility.RandomMinMax(min, max);

            SetAverage(min, max, val);

            switch (type)
            {
                case ResistanceType.Physical: m_PhysicalResistance = val; break;
                case ResistanceType.Fire: m_FireResistance = val; break;
                case ResistanceType.Cold: m_ColdResistance = val; break;
                case ResistanceType.Poison: m_PoisonResistance = val; break;
                case ResistanceType.Energy: m_EnergyResistance = val; break;
            }

            UpdateResistances();
        }

        public void SetSkill(SkillName name, double val)
        {
            Skills[name].BaseFixedPoint = (int)(val * 10);

            if (Skills[name].Base > Skills[name].Cap)
            {
                SkillsCap += (Skills[name].BaseFixedPoint - Skills[name].CapFixedPoint);

                Skills[name].Cap = Skills[name].Base;
            }

            if (name == SkillName.Poisoning && Skills[name].Base > 0 &&
                !Controlled &&
                (AbilityProfile == null || !AbilityProfile.HasAbility(MagicalAbility.Poisoning)))
            {
                SetMagicalAbility(MagicalAbility.Poisoning);
            }

            if (!Controlled && name == SkillName.Magery &&
                (AbilityProfile == null || !AbilityProfile.HasAbility(MagicalAbility.Magery)) &&
                Skills[SkillName.Magery].Base > 0 &&
                (AI == AIType.AI_Mage || AI == AIType.AI_Necro || AI == AIType.AI_NecroMage || AI == AIType.AI_Mystic || AI == AIType.AI_Spellweaving))

            {
                SetMagicalAbility(MagicalAbility.Magery);
            }
        }

        public void SetSkill(SkillName name, double min, double max)
        {
            int minFixed = (int)(min * 10);
            int maxFixed = (int)(max * 10);

            Skills[name].BaseFixedPoint = Utility.RandomMinMax(minFixed, maxFixed);

            SetAverage(min, max, Skills[name].BaseFixedPoint / 10);

            if (Skills[name].Base > Skills[name].Cap)
            {
                SkillsCap += (Skills[name].BaseFixedPoint - Skills[name].CapFixedPoint);

                Skills[name].Cap = Skills[name].Base;
            }

            if (name == SkillName.Poisoning && Skills[name].Base > 0 &&
                !Controlled &&
                (AbilityProfile == null || !AbilityProfile.HasAbility(MagicalAbility.Poisoning)))
            {
                SetMagicalAbility(MagicalAbility.Poisoning);
            }

            if (!Controlled && name == SkillName.Magery &&
                (AbilityProfile == null || !AbilityProfile.HasAbility(MagicalAbility.Magery)) &&
                Skills[SkillName.Magery].Base > 0 &&
                (AI == AIType.AI_Mage || AI == AIType.AI_Necro || AI == AIType.AI_NecroMage || AI == AIType.AI_Mystic || AI == AIType.AI_Spellweaving))

            {
                SetMagicalAbility(MagicalAbility.Magery);
            }
        }

        public void SetFameLevel(int level)
        {
            switch (level)
            {
                case 1:
                    Fame = Utility.RandomMinMax(0, 1249);
                    break;
                case 2:
                    Fame = Utility.RandomMinMax(1250, 2499);
                    break;
                case 3:
                    Fame = Utility.RandomMinMax(2500, 4999);
                    break;
                case 4:
                    Fame = Utility.RandomMinMax(5000, 9999);
                    break;
                case 5:
                    Fame = Utility.RandomMinMax(10000, 10000);
                    break;
            }
        }

        public void SetKarmaLevel(int level)
        {
            switch (level)
            {
                case 0:
                    Karma = -Utility.RandomMinMax(0, 624);
                    break;
                case 1:
                    Karma = -Utility.RandomMinMax(625, 1249);
                    break;
                case 2:
                    Karma = -Utility.RandomMinMax(1250, 2499);
                    break;
                case 3:
                    Karma = -Utility.RandomMinMax(2500, 4999);
                    break;
                case 4:
                    Karma = -Utility.RandomMinMax(5000, 9999);
                    break;
                case 5:
                    Karma = -Utility.RandomMinMax(10000, 10000);
                    break;
            }
        }

        public override void OnRawDexChange(int oldDex)
        {
            if (oldDex != RawDex)
            {
                AdjustSpeeds();
            }
        }

        public void AdjustSpeeds()
        {
            double activeSpeed = 0.0;
            double passiveSpeed = 0.0;

            SpeedInfo.GetSpeeds(this, ref activeSpeed, ref passiveSpeed);

            m_dActiveSpeed = activeSpeed;
            m_dPassiveSpeed = passiveSpeed;
        }
        #endregion

        public static void Cap(ref int val, int min, int max)
        {
            if (val < min)
            {
                val = min;
            }
            else if (val > max)
            {
                val = max;
            }
        }

        public virtual void DropBackpack()
        {
            if (Backpack != null)
            {
                if (Backpack.Items.Count > 0)
                {
                    Backpack b = new CreatureBackpack(Name);

                    List<Item> list = new List<Item>(Backpack.Items);
                    foreach (Item item in list)
                    {
                        if (item.Movable)
                            b.DropItem(item);
                        else
                            item.Delete();
                    }

                    BaseHouse house = BaseHouse.FindHouseAt(this);
                    if (house != null)
                    {
                        if (Backpack.Items.Count == 0)
                            b.Delete();
                        else
                            b.MoveToWorld(house.BanLocation, house.Map);
                    }
                    else
                    {
                        if (Backpack.Items.Count == 0)
                            b.Delete();
                        else
                            b.MoveToWorld(Location, Map);
                    }
                }
            }
        }

        public LootStage LootStage { get; protected set; }
        public int KillersLuck { get; protected set; }
        public bool StealPackGenerated { get; protected set; }
        public bool HasBeenStolen { get; set; }

        public virtual void GenerateLoot(bool spawning)
        {
            GenerateLoot(spawning ? LootStage.Spawning : LootStage.Death);
        }

        public virtual void GenerateLoot(LootStage stage)
        {
            if (m_NoLootOnDeath || m_Allured)
                return;

            LootStage = stage;

            switch (stage)
            {
                case LootStage.Stolen:
                    StealPackGenerated = true;
                    break;
                case LootStage.Death:
                    KillersLuck = LootPack.GetLuckChanceForKiller(this);
                    break;
            }

            GenerateLoot();

            if (m_Paragon)
            {
                if (Fame < 1250)
                {
                    AddLoot(LootPack.Meager);
                }
                else if (Fame < 2500)
                {
                    AddLoot(LootPack.Average);
                }
                else if (Fame < 5000)
                {
                    AddLoot(LootPack.Rich);
                }
                else if (Fame < 10000)
                {
                    AddLoot(LootPack.FilthyRich);
                }
                else
                {
                    AddLoot(LootPack.UltraRich);
                }
            }

            KillersLuck = 0;
        }

        public virtual void GenerateLoot()
        { }

        public virtual void AddLoot(LootPack pack, int min, int max)
        {
            AddLoot(pack, Utility.RandomMinMax(min, max), 100.0);
        }

        public virtual void AddLoot(LootPack pack, int min, int max, double chance)
        {
            if (min > max)
                min = max;

            AddLoot(pack, Utility.RandomMinMax(min, max), chance);
        }

        public virtual void AddLoot(LootPack pack, int amount)
        {
            AddLoot(pack, amount, 100.0);
        }

        public virtual void AddLoot(LootPack pack, int amount, double chance)
        {
            for (int i = 0; i < amount; ++i)
            {
                AddLoot(pack, chance);
            }
        }

        public virtual void AddLoot(LootPack pack)
        {
            AddLoot(pack, 100.0);
        }

        public virtual void AddLoot(LootPack pack, double chance)
        {
            if (Summoned || pack == null || (chance < 100.0 && Utility.RandomDouble() > chance / 100))
            {
                return;
            }

            Container backpack = Backpack;

            if (backpack == null)
            {
                backpack = new Backpack
                {
                    Movable = false
                };

                AddItem(backpack);
            }

            pack.Generate(this);
        }

        public static void GetRandomAOSStats(int minLevel, int maxLevel, out int attributeCount, out int min, out int max)
        {
            int v = RandomMinMaxScaled(minLevel, maxLevel);

            if (v >= 5)
            {
                attributeCount = Utility.RandomMinMax(2, 6);
                min = 20;
                max = 70;
            }
            else if (v == 4)
            {
                attributeCount = Utility.RandomMinMax(2, 4);
                min = 20;
                max = 50;
            }
            else if (v == 3)
            {
                attributeCount = Utility.RandomMinMax(2, 3);
                min = 20;
                max = 40;
            }
            else if (v == 2)
            {
                attributeCount = Utility.RandomMinMax(1, 2);
                min = 10;
                max = 30;
            }
            else
            {
                attributeCount = 1;
                min = 10;
                max = 20;
            }
        }

        public static int RandomMinMaxScaled(int min, int max)
        {
            if (min == max)
            {
                return min;
            }

            if (min > max)
            {
                int hold = min;
                min = max;
                max = hold;
            }

            /* Example:
            *    min: 1
            *    max: 5
            *  count: 5
            *
            * total = (5*5) + (4*4) + (3*3) + (2*2) + (1*1) = 25 + 16 + 9 + 4 + 1 = 55
            *
            * chance for min+0 : 25/55 : 45.45%
            * chance for min+1 : 16/55 : 29.09%
            * chance for min+2 :  9/55 : 16.36%
            * chance for min+3 :  4/55 :  7.27%
            * chance for min+4 :  1/55 :  1.81%
            */

            int count = max - min + 1;
            int total = 0, toAdd = count;

            for (int i = 0; i < count; ++i, --toAdd)
            {
                total += toAdd * toAdd;
            }

            int rand = Utility.Random(total);
            toAdd = count;

            int val = min;

            for (int i = 0; i < count; ++i, --toAdd, ++val)
            {
                rand -= toAdd * toAdd;

                if (rand < 0)
                {
                    break;
                }
            }

            return val;
        }

        public void PackGold(int amount)
        {
            if (amount > 0)
            {
                PackItem(new Gold(amount));
            }
        }

        public void PackGold(int min, int max)
        {
            PackGold(Utility.RandomMinMax(min, max));
        }

        public void PackStatue(int min, int max)
        {
            PackStatue(Utility.RandomMinMax(min, max));
        }

        public void PackStatue(int amount)
        {
            for (int i = 0; i < amount; ++i)
            {
                PackStatue();
            }
        }

        public void PackStatue()
        {
            PackItem(Loot.RandomStatue());
        }

        public void PackGem()
        {
            PackGem(1);
        }

        public void PackGem(int min, int max)
        {
            PackGem(Utility.RandomMinMax(min, max));
        }

        public void PackGem(int amount)
        {
            if (amount <= 0)
            {
                return;
            }

            Item gem = Loot.RandomGem();

            gem.Amount = amount;

            PackItem(gem);
        }

        public void PackItem(Item item)
        {
            if ((Summoned && item.Movable) || item == null)
            {
                if (item != null)
                {
                    item.Delete();
                }

                return;
            }

            Container pack = Backpack;

            if (pack == null)
            {
                pack = new Backpack
                {
                    Movable = false
                };

                AddItem(pack);
            }

            if (!item.Stackable || !pack.TryDropItem(this, item, false)) // try stack
            {
                pack.DropItem(item); // failed, drop it anyway
            }
        }

        public virtual void SetToChampionSpawn()
        {
        }

        public virtual void SetWearable(Item item, int hue = -1, double dropChance = 0.0)
        {
            if (hue > -1)
                item.Hue = hue;

            item.Movable = dropChance > Utility.RandomDouble();

            if (!CheckEquip(item) || !OnEquip(item) || !item.OnEquip(this))
            {
                PackItem(item);
            }
            else
            {
                AddItem(item);
            }
        }

        public override void OnDoubleClick(Mobile from)
        {
            if (from.AccessLevel >= AccessLevel.GameMaster && !Body.IsHuman)
            {
                Container pack = Backpack;

                if (pack != null)
                {
                    pack.DisplayTo(from);
                }
            }

            if (DeathAdderCharmable && from.CanBeHarmful(this, false))
            {
                DeathAdder da = SummonFamiliarSpell.Table[from] as DeathAdder;

                if (da != null && !da.Deleted)
                {
                    from.SendAsciiMessage("You charm the snake.  Select a target to attack.");
                    from.Target = new DeathAdderCharmTarget(this);
                }
            }

            base.OnDoubleClick(from);
        }

        private class DeathAdderCharmTarget : Target
        {
            private readonly BaseCreature m_Charmed;

            public DeathAdderCharmTarget(BaseCreature charmed)
                : base(-1, false, TargetFlags.Harmful)
            {
                m_Charmed = charmed;
            }

            protected override void OnTarget(Mobile from, object targeted)
            {
                if (!m_Charmed.DeathAdderCharmable || m_Charmed.Combatant != null || !from.CanBeHarmful(m_Charmed, false))
                {
                    return;
                }

                DeathAdder da = SummonFamiliarSpell.Table[from] as DeathAdder;
                if (da == null || da.Deleted)
                {
                    return;
                }

                Mobile targ = targeted as Mobile;
                if (targ == null || !from.CanBeHarmful(targ, false))
                {
                    return;
                }

                from.RevealingAction();
                from.DoHarmful(targ, true);

                m_Charmed.Combatant = targ;

                if (m_Charmed.AIObject != null)
                {
                    m_Charmed.AIObject.Action = ActionType.Combat;
                }
            }
        }

        public override void AddNameProperties(ObjectPropertyList list)
        {
            base.AddNameProperties(list);
            /*#region FS:ATS Edits
            if ( this.Tamable == true && FSATS.EnablePetBreeding == true )
            {
                bool nolevel = false;
                Type typ = this.GetType();
                string nam = typ.Name;

                foreach ( string check in FSATS.NoLevelCreatures )
                {
                      if ( check == nam )
                            nolevel = true;
                }

                if ( nolevel != true )
                {
                    if ( this.Female == true )
                        list.Add( 1060658, "Gender\tFemale" );
                    else
                        list.Add( 1060658, "Gender\tMale" );

                    if ( this.Controlled == false )
                        list.Add( 1060659, "Max Level\t{0}", this.MaxLevel );
                }
            }
            #endregion*/

            if (Controlled && !string.IsNullOrEmpty(EngravedText))
            {
                list.Add(1157315, EngravedText); // <BASEFONT COLOR=#668cff>Branded: ~1_VAL~<BASEFONT COLOR=#FFFFFF>
            }

            if (DisplayWeight)
            {
                list.Add(TotalWeight == 1 ? 1072788 : 1072789, TotalWeight.ToString()); // Weight: ~1_WEIGHT~ stones
            }

            if (m_ControlOrder == OrderType.Guard)
            {
                list.Add(1080078); // guarding
            }

            if (IsGolem)
                list.Add(1113697); // (Golem)

            if (Summoned && !IsAnimatedDead && !IsNecroFamiliar && !(this is Clone))
            {
                list.Add(1049646); // (summoned)
            }
            else if (Controlled && Commandable)
            {
                if (this is BaseHire)
                {
                    list.Add(1062030); // (hired)
                }
                else if (IsBonded) //Intentional difference (showing ONLY bonded when bonded instead of bonded & tame)
                {
                    list.Add(1049608); // (bonded)
                }
                else
                {
                    list.Add(502006); // (tame)
                }
            }

            if (IsSoulBound)
            {
                list.Add(1159188); // <BASEFONT COLOR=#FF8300>Soulbound<BASEFONT COLOR=#FFFFFF>
            }

            if (IsAmbusher)
                list.Add(1155480); // Ambusher
        }

        public virtual double TreasureMapChance => TreasureMap.LootChance;
        public virtual int TreasureMapLevel => -1;

        public virtual bool IgnoreYoungProtection => false;

        public override bool OnBeforeDeath()
        {
            int treasureLevel = TreasureMapInfo.ConvertLevel(TreasureMapLevel);

            /*#region FS:ATS Edits
            if ( FSATS.EnablePetLeveling )
            {
                List<BaseCreature> toCheck = new List<BaseCreature>();
                List<DamageEntry> rights = this.DamageEntries;
                foreach ( DamageEntry entry in rights )
                {
                    if ( entry.Damager is BaseCreature )
                    {
                        BaseCreature bc = (BaseCreature)entry.Damager;
                        if ( bc.Controlled && bc.ControlMaster != null )
                            toCheck.Add( bc );     
                    }
                }
                foreach ( BaseCreature mob in toCheck )
                {
                    PetLeveling.CheckLevel( this, mob, toCheck.Count );
                }
            }
            #endregion*/

            GetLootingRights();

            /*#region FS:ATS Edits
            if ( this is BaseBioCreature || this is BioCreature || this is BioMount )
            {
                PetLeveling.DoBioDeath( this );
            }
            else
            {
                if ( FSATS.EnablePetLeveling == true )
                    PetLeveling.DoDeathCheck( this );
            }
            #endregion*/

            if (!Summoned && !NoKillAwards && !IsBonded && !NoLootOnDeath)
            {
                if (treasureLevel >= 0)
                {
                    if (m_Paragon && Paragon.ChestChance > Utility.RandomDouble())
                    {
                        PackItem(new ParagonChest(Name, treasureLevel));
                    }
                    else if (TreasureMapChance >= Utility.RandomDouble())
                    {
                        Map map = Map;

                        if (map == Map.Trammel && Siege.SiegeShard)
                            map = Map.Felucca;

                        PackItem(new TreasureMap(treasureLevel, map, SpellHelper.IsEodon(map, Location)));
                    }
                }

                if (m_Paragon && Paragon.ChocolateIngredientChance > Utility.RandomDouble())
                {
                    switch (Utility.Random(4))
                    {
                        case 0:
                            PackItem(new CocoaButter());
                            break;
                        case 1:
                            PackItem(new CocoaLiquor());
                            break;
                        case 2:
                            PackItem(new SackOfSugar());
                            break;
                        case 3:
                            PackItem(new Vanilla());
                            break;
                    }
                }
            }

            if (!Summoned && !NoKillAwards && !m_HasGeneratedLoot && !m_NoLootOnDeath)
            {
                m_HasGeneratedLoot = true;
                GenerateLoot(LootStage.Death);
            }

            if (!NoKillAwards && Region.IsPartOf("Doom"))
            {
                int bones = TheSummoningQuest.GetDaemonBonesFor(this);

                if (bones > 0)
                {
                    PackItem(new DaemonBone(bones));
                }
            }

            if (IsAnimatedDead)
            {
                Effects.SendLocationEffect(Location, Map, 0x3728, 13, 1, 0x461, 4);
            }

            InhumanSpeech speechType = SpeechType;

            if (speechType != null)
            {
                speechType.OnDeath(this);
            }

            if (m_ReceivedHonorContext != null)
            {
                m_ReceivedHonorContext.OnTargetKilled();
            }

            return base.OnBeforeDeath();
        }

        private bool m_NoKillAwards;
        private bool m_NoLootOnDeath;

        [CommandProperty(AccessLevel.GameMaster)]
        public bool NoKillAwards { get { return m_NoKillAwards; } set { m_NoKillAwards = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool NoLootOnDeath { get { return m_NoLootOnDeath; } set { m_NoLootOnDeath = value; } }

        public int ComputeBonusDamage(List<DamageEntry> list, Mobile m)
        {
            int bonus = 0;

            for (int i = list.Count - 1; i >= 0; --i)
            {
                DamageEntry de = list[i];

                if (de.Damager == m || !(de.Damager is BaseCreature))
                {
                    continue;
                }

                BaseCreature bc = (BaseCreature)de.Damager;
                Mobile master = null;

                master = bc.GetMaster();

                if (master == m)
                {
                    bonus += de.DamageGiven;
                }
            }

            return bonus;
        }

        public Mobile GetMaster()
        {
            if (Controlled && ControlMaster != null)
            {
                return ControlMaster;
            }
            else if (Summoned && SummonMaster != null)
            {
                return SummonMaster;
            }

            return null;
        }

        public virtual bool IsMonster
        {
            get
            {
                if (!Controlled)
                    return true;

                Mobile master = GetMaster();

                return master == null || (master is BaseCreature && !((BaseCreature)master).Controlled);
            }
        }

        public virtual bool IsAggressiveMonster => IsMonster
                                                &&
                                                 ( m_FightMode == FightMode.Closest
                                                || m_FightMode == FightMode.Strongest
                                                || m_FightMode == FightMode.Weakest
                                                || m_FightMode == FightMode.Good
                                                 );

        private class FKEntry
        {
            public Mobile m_Mobile;
            public int m_Damage;

            public FKEntry(Mobile m, int damage)
            {
                m_Mobile = m;
                m_Damage = damage;
            }
        }

        public List<DamageStore> LootingRights { get; set; }

        public bool HasLootingRights(Mobile m)
        {
            if (LootingRights == null)
                return false;

            return LootingRights.FirstOrDefault(ds => ds.m_Mobile == m && ds.m_HasRight) != null;
        }

        public Mobile GetHighestDamager()
        {
            if (LootingRights == null || LootingRights.Count == 0)
                return null;

            return LootingRights[0].m_Mobile;
        }

        public bool IsHighestDamager(Mobile m)
        {
            return LootingRights != null && LootingRights.Count > 0 && LootingRights[0].m_Mobile == m;
        }

        public Mobile RandomPlayerWithLootingRights()
        {
            var rights = GetLootingRights();

            if (rights == null)
            {
                return null;
            }

            for (int i = rights.Count - 1; i >= 0; --i)
            {
                var ds = rights[i];

                if (!ds.m_HasRight)
                {
                    rights.RemoveAt(i);
                }
            }

            if (rights.Count > 0)
            {
                return rights[Utility.Random(rights.Count)].m_Mobile;
            }

            return null;
        }

        public List<DamageStore> GetLootingRights()
        {
            if (LootingRights != null)
                return LootingRights;

            List<DamageEntry> damageEntries = DamageEntries;
            int hitsMax = HitsMax;

            List<DamageStore> rights = new List<DamageStore>();

            for (int i = damageEntries.Count - 1; i >= 0; --i)
            {
                if (i >= damageEntries.Count)
                {
                    continue;
                }

                DamageEntry de = damageEntries[i];

                if (de.HasExpired)
                {
                    damageEntries.RemoveAt(i);
                    continue;
                }

                int damage = de.DamageGiven;

                List<DamageEntry> respList = de.Responsible;

                if (respList != null)
                {
                    for (int j = 0; j < respList.Count; ++j)
                    {
                        DamageEntry subEntry = respList[j];
                        Mobile master = subEntry.Damager;

                        if (master == null || master.Deleted || !master.Player)
                        {
                            continue;
                        }

                        bool needNewSubEntry = true;

                        for (int k = 0; needNewSubEntry && k < rights.Count; ++k)
                        {
                            DamageStore ds = rights[k];

                            if (ds.m_Mobile == master)
                            {
                                ds.m_Damage += subEntry.DamageGiven;
                                needNewSubEntry = false;
                            }
                        }

                        if (needNewSubEntry)
                        {
                            rights.Add(new DamageStore(master, subEntry.DamageGiven));
                        }

                        damage -= subEntry.DamageGiven;
                    }
                }

                Mobile m = de.Damager;

                if (m == null || m.Deleted || !m.Player)
                {
                    continue;
                }

                if (damage <= 0)
                {
                    continue;
                }

                bool needNewEntry = true;

                for (int j = 0; needNewEntry && j < rights.Count; ++j)
                {
                    DamageStore ds = rights[j];

                    if (ds.m_Mobile == m)
                    {
                        ds.m_Damage += damage;
                        needNewEntry = false;
                    }
                }

                if (needNewEntry)
                {
                    rights.Add(new DamageStore(m, damage));
                }
            }

            if (rights.Count > 0)
            {
                rights[0].m_Damage = (int)(rights[0].m_Damage * 1.25);
                //This would be the first valid person attacking it.  Gets a 25% bonus.  Per 1/19/07 Five on Friday

                if (rights.Count > 1)
                {
                    rights.Sort(); //Sort by damage
                }

                int topDamage = rights[0].m_Damage;
                int minDamage;

                minDamage = (int)(topDamage * 0.06);

                for (int i = 0; i < rights.Count; ++i)
                {
                    DamageStore ds = rights[i];

                    ds.m_HasRight = (ds.m_Damage >= minDamage);
                }
            }

            LootingRights = rights;
            return rights;
        }

        #region Mondain's Legacy
        private bool m_Allured;

        [CommandProperty(AccessLevel.GameMaster)]
        public bool Allured
        {
            get { return m_Allured; }
            set
            {
                m_Allured = value;

                if (value && Backpack != null)
                {
                    ColUtility.SafeDelete(Backpack.Items);
                }
            }
        }
        #endregion

        public virtual bool GivesMLMinorArtifact => false;

        public override void OnItemLifted(Mobile from, Item item)
        {
            base.OnItemLifted(from, item);

            InvalidateProperties();
        }

        public virtual void OnKilledBy(Mobile mob)
        {
            if (m_Paragon && Paragon.CheckArtifactChance(mob, this))
            {
                Paragon.GiveArtifactTo(mob);
            }

            EventSink.InvokeOnKilledBy(new OnKilledByEventArgs(this, mob));
        }

        public override void OnDeath(Container c)
        {
            MeerMage.StopEffect(this, false);

            if (IsBonded)
            {
                int sound = GetDeathSound();

                if (sound >= 0)
                {
                    Effects.PlaySound(this, Map, sound);
                }

                Warmode = false;

                Poison = null;
                Combatant = null;

                Hits = 0;
                Stam = 0;
                Mana = 0;

                IsDeadPet = true;
                ControlTarget = ControlMaster;
                ControlOrder = OrderType.Follow;

                ProcessDeltaQueue();
                SendIncomingPacket();
                SendIncomingPacket();

                List<AggressorInfo> aggressors = Aggressors;

                for (int i = 0; i < aggressors.Count; ++i)
                {
                    AggressorInfo info = aggressors[i];

                    if (info.Attacker.Combatant == this)
                    {
                        info.Attacker.Combatant = null;
                    }
                }

                List<AggressorInfo> aggressed = Aggressed;

                for (int i = 0; i < aggressed.Count; ++i)
                {
                    AggressorInfo info = aggressed[i];

                    if (info.Defender.Combatant == this)
                    {
                        info.Defender.Combatant = null;
                    }
                }

                Mobile owner = ControlMaster;

                if (owner == null || owner.Deleted || owner.Map != Map || !owner.InRange(this, 12) || !CanSee(owner) ||
                    !InLOS(owner))
                {
                    if (OwnerAbandonTime == DateTime.MinValue)
                    {
                        OwnerAbandonTime = DateTime.UtcNow;
                    }
                }
                else
                {
                    OwnerAbandonTime = DateTime.MinValue;
                }

                GiftOfLifeSpell.HandleDeath(this);

                CheckStatTimers();
            }
            else
            {
                LootingRights = null;

                if (!Summoned && !m_NoKillAwards)
                {
                    int totalFame = Fame / 100;
                    int totalKarma = -Karma / 100;

                    if (Map == Map.Felucca)
                    {
                        totalFame += ((totalFame / 10) * 3);
                        totalKarma += ((totalKarma / 10) * 3);
                    }

                    List<DamageStore> list = GetLootingRights();
                    List<Mobile> titles = new List<Mobile>();
                    List<int> fame = new List<int>();
                    List<int> karma = new List<int>();

                    for (int i = 0; i < list.Count; ++i)
                    {
                        DamageStore ds = list[i];

                        if (!ds.m_HasRight)
                        {
                            continue;
                        }

                        if (GivesFameAndKarmaAward)
                        {
                            Party party = Engines.PartySystem.Party.Get(ds.m_Mobile);

                            if (party != null)
                            {
                                int divedFame = totalFame / party.Members.Count;
                                int divedKarma = totalKarma / party.Members.Count;

                                for (int j = 0; j < party.Members.Count; ++j)
                                {
                                    PartyMemberInfo info = party.Members[j];

                                    if (info != null && info.Mobile != null)
                                    {
                                        int index = titles.IndexOf(info.Mobile);

                                        if (index == -1)
                                        {
                                            titles.Add(info.Mobile);
                                            fame.Add(divedFame);
                                            karma.Add(divedKarma);
                                        }
                                        else
                                        {
                                            fame[index] += divedFame;
                                            karma[index] += divedKarma;
                                        }
                                    }
                                }
                            }
                            else
                            {
                                if (ds.m_Mobile is PlayerMobile)
                                {
                                    foreach (Mobile pet in ((PlayerMobile)ds.m_Mobile).AllFollowers.Where(p => DamageEntries.Any(de => de.Damager == p)))
                                    {
                                        titles.Add(pet);
                                        fame.Add(totalFame);
                                        karma.Add(totalKarma);
                                    }
                                }

                                titles.Add(ds.m_Mobile);
                                fame.Add(totalFame);
                                karma.Add(totalKarma);
                            }
                        }

                        OnKilledBy(ds.m_Mobile);

                        if (HumilityVirtue.IsInHunt(ds.m_Mobile) && Karma < 0)
                            HumilityVirtue.RegisterKill(ds.m_Mobile, this, list.Count);
                    }

                    for (int i = 0; i < titles.Count; ++i)
                    {
                        Titles.AwardFame(titles[i], fame[i], true);
                        Titles.AwardKarma(titles[i], karma[i], true);
                    }
                }

                CreatureDeathEventArgs e = new CreatureDeathEventArgs(this, LastKiller, c);

                EventSink.InvokeCreatureDeath(e);

                if (!c.Deleted)
                {
                    int i;

                    if (e.ClearCorpse)
                    {
                        i = c.Items.Count;

                        while (--i >= 0)
                        {
                            if (i >= c.Items.Count)
                            {
                                continue;
                            }

                            Item o = c.Items[i];

                            if (o != null && !o.Deleted)
                            {
                                o.Delete();
                            }
                        }
                    }

                    i = e.ForcedLoot.Count;

                    while (--i >= 0)
                    {
                        if (i >= e.ForcedLoot.Count)
                        {
                            continue;
                        }

                        Item o = e.ForcedLoot[i];

                        if (o != null && !o.Deleted)
                        {
                            c.DropItem(o);
                        }
                    }

                    e.ClearLoot(false);
                }
                else
                {
                    int i = e.ForcedLoot.Count;

                    while (--i >= 0)
                    {
                        if (i >= e.ForcedLoot.Count)
                        {
                            continue;
                        }

                        Item o = e.ForcedLoot[i];

                        if (o != null && !o.Deleted)
                        {
                            o.Delete();
                        }
                    }

                    e.ClearLoot(true);
                }

                base.OnDeath(c);

                if (e.PreventDefault)
                {
                    return;
                }

                if (DeleteCorpseOnDeath && !e.PreventDelete)
                {
                    c.Delete();
                }
            }
        }

        public bool GivenSpecialArtifact { get; set; }

        /* To save on cpu usage, RunUO creatures only reacquire creatures under the following circumstances:
        *  - 10 seconds have elapsed since the last time it tried
        *  - The creature was attacked
        *  - Some creatures, like dragons, will reacquire when they see someone move
        *
        * This functionality appears to be implemented on OSI as well
        */

        private long m_NextReacquireTime;

        public long NextReacquireTime { get { return m_NextReacquireTime; } set { m_NextReacquireTime = value; } }

        public virtual TimeSpan ReacquireDelay => TimeSpan.FromSeconds(10.0);
        public virtual bool ReacquireOnMovement => false;

        public virtual bool AcquireOnApproach => m_Paragon || ApproachWait;
        public virtual int AcquireOnApproachRange => ApproachRange;

        [CommandProperty(AccessLevel.GameMaster)]
        public bool ApproachWait { get; set; }

        [CommandProperty(AccessLevel.GameMaster)]
        public int ApproachRange { get; set; }

        public override void OnDelete()
        {
            Mobile m = m_ControlMaster;

            SetControlMaster(null);
            SummonMaster = null;

            if (m_ReceivedHonorContext != null)
            {
                m_ReceivedHonorContext.Cancel();
            }

            base.OnDelete();

            if (m != null)
            {
                m.InvalidateProperties();
            }

            if (_NavPoints != null)
            {
                _NavPoints.Clear();
                _NavPoints = null;
            }
        }

        public override bool CanBeHarmful(IDamageable damageable, bool message, bool ignoreOurBlessedness)
        {
            Mobile target = damageable as Mobile;

            if (RecentSetControl && GetMaster() == target)
            {
                return false;
            }

            if ((target is BaseVendor && ((BaseVendor)target).IsInvulnerable) || target is PlayerVendor || target is TownCrier)
            {
                return false;
            }

            if (damageable is IDamageableItem && !((IDamageableItem)damageable).CanDamage)
            {
                return false;
            }

            return base.CanBeHarmful(damageable, message, ignoreOurBlessedness);
        }

        public override bool CanBeRenamedBy(Mobile from)
        {
            bool ret = base.CanBeRenamedBy(from);

            if (Controlled && from == ControlMaster && !from.Region.IsPartOf<Jail>() && !Allured)
            {
                ret = true;
            }

            return ret;
        }

        public bool SetControlMaster(Mobile m)
        {
            if (m == null)
            {
                ControlMaster = null;
                Controlled = false;
                ControlTarget = null;
                ControlOrder = OrderType.None;
                Guild = null;

                UpdateMasteryInfo();

                Delta(MobileDelta.Noto);
            }
            else
            {
                ISpawner se = Spawner;

                if (se != null && se.UnlinkOnTaming)
                {
                    Spawner.Remove(this);
                    Spawner = null;
                }

                if (m.Followers + ControlSlots > m.FollowersMax)
                {
                    m.SendLocalizedMessage(1049607); // You have too many followers to control that creature.
                    return false;
                }

                CurrentWayPoint = null; //so tamed animals don't try to go back

                Home = Point3D.Zero;

                ControlMaster = m;
                Controlled = true;
                ControlTarget = null;
                ControlOrder = OrderType.Come;
                Guild = null;

                UpdateMasteryInfo();

                AdjustSpeeds();
                CurrentSpeed = m_dActiveSpeed;

                StopDeleteTimer();

                RemoveAggressed(m);
                RemoveAggressor(m);
                m.RemoveAggressed(this);
                m.RemoveAggressor(this);

                if (Combatant != null)
                    Combatant = null;

                if (m.Combatant == this)
                    m.Combatant = null;

                RecentSetControl = true;
                Timer.DelayCall(TimeSpan.FromSeconds(3), () => RecentSetControl = false);

                Delta(MobileDelta.Noto);
            }

            InvalidateProperties();

            return true;
        }

        public bool RecentSetControl { get; set; }

        public virtual void OnAfterTame(Mobile tamer)
        {
            if (StatLossAfterTame && Owners.Count == 0)
            {
                AnimalTaming.ScaleStats(this, 0.5);
            }
        }

        public override void OnRegionChange(Region Old, Region New)
        {
            base.OnRegionChange(Old, New);

            if (Controlled)
            {
                SpawnEntry se = Spawner as SpawnEntry;

                if (se != null && !se.UnlinkOnTaming && (New == null || !New.AcceptsSpawnsFrom(se.Region)))
                {
                    Spawner.Remove(this);
                    Spawner = null;
                }
            }
        }

        public virtual double GetDispelDifficulty()
        {
            double dif = DispelDifficulty;
            if (SummonMaster != null)
                dif += ArcaneEmpowermentSpell.GetDispellBonus(SummonMaster);
            return dif;
        }

        private static bool m_Summoning;

        public static bool Summoning { get { return m_Summoning; } set { m_Summoning = value; } }

        public static bool Summon(BaseCreature creature, Mobile caster, Point3D p, int sound, TimeSpan duration)
        {
            return Summon(creature, true, caster, p, sound, duration);
        }

        public static bool Summon(
            BaseCreature creature, bool controlled, Mobile caster, Point3D p, int sound, TimeSpan duration)
        {
            if (caster.Followers + creature.ControlSlots > caster.FollowersMax)
            {
                caster.SendLocalizedMessage(1049645); // You have too many followers to summon that creature.
                creature.Delete();
                return false;
            }

            m_Summoning = true;

            if (controlled)
            {
                creature.SetControlMaster(caster);
            }

            creature.RangeHome = 10;
            creature.Summoned = true;

            creature.SummonMaster = caster;

            Container pack = creature.Backpack;

            if (pack != null)
            {
                for (int i = pack.Items.Count - 1; i >= 0; --i)
                {
                    if (i >= pack.Items.Count)
                    {
                        continue;
                    }

                    pack.Items[i].Delete();
                }
            }

            creature.SetHits(
                (int)Math.Floor(creature.HitsMax * (1 + ArcaneEmpowermentSpell.GetSpellBonus(caster, false) / 100.0)));

            creature.m_SummonEnd = DateTime.UtcNow + duration;
            TimerRegistry.Register<BaseCreature>("UnsummonTimer", creature, duration, c => c.Delete());

            creature.MoveToWorld(p, caster.Map);

            Effects.PlaySound(p, creature.Map, sound);

            m_Summoning = false;

            // Skill Masteries
            creature.HitsMaxSeed += MasteryInfo.EnchantedSummoningBonus(creature);
            creature.Hits = creature.HitsMaxSeed;

            return true;
        }

        private static readonly bool EnableRummaging = true;

        private const double ChanceToRummage = 0.5; // 50%

        private const double MinutesToNextRummageMin = 1.0;
        private const double MinutesToNextRummageMax = 4.0;

        private const double MinutesToNextChanceMin = 0.25;
        private const double MinutesToNextChanceMax = 0.75;

        private long m_NextRummageTime;

        public virtual bool IsDispellable => Summoned && !IsAnimatedDead;

        #region Healing
        public virtual double HealChance => 0.0;

        private long m_NextHealTime = Core.TickCount;
        private long m_NextHealOwnerTime = Core.TickCount;
        private Timer m_HealTimer;

        public bool IsHealing => (m_HealTimer != null);

        public virtual bool CheckHeal()
        {
            long tc = Core.TickCount;

            if (Alive && !IsHealing && !BardPacified)
            {
                Mobile owner = ControlMaster;

                if (owner != null && tc >= m_NextHealOwnerTime && CanBeBeneficial(owner, true, true) &&
                    owner.Map == Map && InRange(owner, 2) && InLOS(owner) && (owner.Poisoned || owner.Hits < .78 * owner.HitsMax))
                {
                    HealStart(owner);
                    m_NextHealOwnerTime = tc + (int)TimeSpan.FromSeconds(30).TotalMilliseconds;

                    return true;
                }
                else if (tc >= m_NextHealTime && CanBeBeneficial(this) && (Hits < .78 * HitsMax || Poisoned))
                {
                    HealStart(this);
                    m_NextHealTime = tc + (int)TimeSpan.FromSeconds(1.0).TotalMilliseconds;

                    return true;
                }
            }

            return false;
        }

        public virtual void HealStart(Mobile patient)
        {
            bool onSelf = (patient == this);

            //DoBeneficial( patient );

            RevealingAction();

            if (!onSelf)
            {
                patient.RevealingAction();
                patient.SendLocalizedMessage(1008078, false, Name); //  : Attempting to heal you.
            }

            double seconds = 6.5 + (patient.Alive ? 0.0 : 5.0);

            m_HealTimer = Timer.DelayCall(TimeSpan.FromSeconds(seconds), new TimerStateCallback(Heal_Callback), patient);
        }

        private void Heal_Callback(object state)
        {
            if (state is Mobile)
            {
                Heal((Mobile)state);
            }
        }

        public virtual void Heal(Mobile patient)
        {
            if (!Alive || Map == Map.Internal || !CanBeBeneficial(patient, true, true) || patient.Map != Map ||
                !InRange(patient, RangePerception))
            {
                StopHeal();
                return;
            }

            bool onSelf = (patient == this);

            if (!patient.Alive)
            { }
            else if (patient.Poisoned)
            {
                int poisonLevel = patient.Poison.RealLevel;

                double healing = Skills.Healing.Value;
                double anatomy = Skills.Anatomy.Value;
                double chance = (healing - 30.0) / 50.0 - poisonLevel * 0.1;

                if ((healing >= 60.0 && anatomy >= 60.0) && chance > Utility.RandomDouble())
                {
                    if (patient.CurePoison(this))
                    {
                        patient.SendLocalizedMessage(1010059); // You have been cured of all poisons.

                        CheckSkill(SkillName.Healing, 0.0, 60.0 + poisonLevel * 10.0); // TODO: Verify formula
                        CheckSkill(SkillName.Anatomy, 0.0, Skills[SkillName.Anatomy].Cap);
                    }
                }
            }
            else if (BleedAttack.IsBleeding(patient))
            {
                patient.SendLocalizedMessage(1060167); // The bleeding wounds have healed, you are no longer bleeding!
                BleedAttack.EndBleed(patient, false);
            }
            else
            {
                double healing = Skills.Healing.Value;
                double anatomy = Skills.Anatomy.Value;
                double chance = (healing + 10.0) / 100.0;

                if (chance > Utility.RandomDouble())
                {
                    double min, max;

                    min = (anatomy / 10.0) + (healing / 6.0) + 4.0;
                    max = (anatomy / 8.0) + (healing / 3.0) + 4.0;

                    if (onSelf)
                    {
                        max += 10;
                    }

                    double toHeal = min + (Utility.RandomDouble() * (max - min));

                    patient.Heal((int)toHeal, this);

                    CheckSkill(SkillName.Healing, 0.0, Skills[SkillName.Healing].Cap);
                    CheckSkill(SkillName.Anatomy, 0.0, Skills[SkillName.Anatomy].Cap);
                }
                else if (Controlled)
                {
                    CheckSkill(SkillName.Healing, 0.0, 10);
                    CheckSkill(SkillName.Anatomy, 0.0, 10);
                }
            }

            HealEffect(patient);

            StopHeal();

            if ((onSelf && Hits >= .78 * HitsMax && Hits < HitsMax) ||
                (!onSelf && patient.Hits >= .78 * patient.HitsMax && patient.Hits < patient.HitsMax))
            {
                HealStart(patient);
            }
        }

        public virtual void StopHeal()
        {
            if (m_HealTimer != null)
            {
                m_HealTimer.Stop();
            }

            m_HealTimer = null;
        }

        public virtual void HealEffect(Mobile patient)
        {
            patient.PlaySound(0x57);
        }
        #endregion

        public override void OnHeal(ref int amount, Mobile from)
        {
            base.OnHeal(ref amount, from);

            // Removed until we can figure out the endless loop, or do a complete refactor
            /*
            if (from == null)
                return;

            if (amount > 0 && from != null && from != this)
            {
                for (int i = Aggressed.Count - 1; i >= 0; i--)
                {
                    AggressorInfo info = Aggressed[i];

                    if (info.Defender.InRange(Location, Core.GlobalMaxUpdateRange) && info.Defender.DamageEntries.Any(de => de.Damager == this))
                    {
                        info.Defender.RegisterDamage(amount, from);
                    }

                    if (info.Defender.Player && from.CanBeHarmful(info.Defender))
                    {
                        from.DoHarmful(info.Defender, true);
                    }
                }

                for (int i = Aggressors.Count - 1; i >= 0; i--)
                {
                    AggressorInfo info = Aggressors[i];

                    if (info.Attacker.InRange(Location, Core.GlobalMaxUpdateRange) && info.Attacker.DamageEntries.Any(de => de.Damager == this))
                    {
                        info.Attacker.RegisterDamage(amount, from);
                    }

                    if (info.Attacker.Player && from.CanBeHarmful(info.Attacker))
                    {
                        from.DoHarmful(info.Attacker, true);
                    }
                }
            }*/
        }

        #region Spawn Position
        public virtual Point3D GetSpawnPosition(int range)
        {
            return GetSpawnPosition(Location, Map, range);
        }

        public static Point3D GetSpawnPosition(Point3D from, Map map, int range)
        {
            if (map == null)
                return from;

            for (int i = 0; i < 10; i++)
            {
                int x = from.X + Utility.RandomMinMax(-range, range);
                int y = from.Y + Utility.RandomMinMax(-range, range);
                int z = map.GetAverageZ(x, y);

                Point3D p = new Point3D(x, y, from.Z);

                if (map.CanSpawnMobile(p) && map.LineOfSight(from, p))
                    return p;

                p = new Point3D(x, y, z);

                if (map.CanSpawnMobile(p) && map.LineOfSight(from, p))
                    return p;
            }

            return from;
        }
        #endregion

        #region Rage
        public virtual void DoRageHit(Mobile defender)
        {
            if (defender != null && defender.Alive)
            {
                int damage = 0;

                SpecialAbility.ColossalBlow.DoEffects(this, defender, ref damage);
            }
        }
        #endregion

        #region Barding Skills
        private long m_NextDiscord;
        private long m_NextPeace;
        private long m_NextProvoke;

        public virtual bool CanDiscord
        {
            get
            {
                if (Controlled && AbilityProfile != null)
                {
                    return AbilityProfile.HasAbility(MagicalAbility.Discordance);
                }

                return false;
            }
        }

        public virtual bool CanPeace => false;
        public virtual bool CanProvoke => false;

        public virtual bool PlayInstrumentSound => true;

        public virtual bool DoDiscord()
        {
            Mobile target = GetBardTarget(Controlled);

            if (target == null || !target.InLOS(this) || !InRange(target.Location, BaseInstrument.GetBardRange(this, SkillName.Discordance)) || CheckInstrument() == null)
            {
                return false;
            }

            if (AbilityProfile != null && AbilityProfile.HasAbility(MagicalAbility.Discordance) && Mana < 25)
            {
                return false;
            }
            else
            {
                Mana -= 25;
            }

            if (Spell != null)
                Spell = null;

            if (!UseSkill(SkillName.Discordance))
            {
                return false;
            }

            if (Target is Discordance.DiscordanceTarget)
            {
                Target.Invoke(this, target);
                return true;
            }

            return false;
        }

        public virtual bool DoPeace()
        {
            Mobile target = GetBardTarget();

            if (target == null || !target.InLOS(this) || !InRange(target.Location, BaseInstrument.GetBardRange(this, SkillName.Peacemaking)) || CheckInstrument() == null)
                return false;

            if (Spell != null)
                Spell = null;

            if (!UseSkill(SkillName.Peacemaking))
                return false;

            if (Target is Peacemaking.InternalTarget)
            {
                Target.Invoke(this, target);
                return true;
            }

            return false;
        }

        public virtual bool DoProvoke()
        {
            Mobile target = GetBardTarget();

            if (target == null || !target.InLOS(this) || !InRange(target.Location, BaseInstrument.GetBardRange(this, SkillName.Provocation)) || CheckInstrument() == null || !(target is BaseCreature))
                return false;

            if (Spell != null)
                Spell = null;

            if (!UseSkill(SkillName.Provocation))
                return false;

            if (Target is Provocation.InternalFirstTarget)
            {
                Target.Invoke(this, target);

                if (Target is Provocation.InternalSecondTarget)
                {
                    Mobile second = GetSecondTarget((BaseCreature)target);

                    if (second != null)
                        Target.Invoke(this, second);

                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// Auto Checks creature for an instrument. Creates if none in pack, and sets for barding skills.
        /// </summary>
        /// <returns></returns>
        public BaseInstrument CheckInstrument()
        {
            BaseInstrument inst = BaseInstrument.GetInstrument(this);

            if (inst == null)
            {
                inst = Backpack == null ? null : Backpack.FindItemByType(typeof(BaseInstrument)) as BaseInstrument;

                if (inst == null)
                {
                    inst = new Harp
                    {
                        SuccessSound = PlayInstrumentSound ? 0x58B : 0,
                        FailureSound = PlayInstrumentSound ? 0x58C : 0,
                        Movable = false,
                        Quality = ItemQuality.Exceptional
                    };

                    PackItem(inst);
                }
            }

            BaseInstrument.SetInstrument(this, inst);

            return inst;
        }

        /// <summary>
        /// Default Method to get bard target. Simplisticly gets combatant. Override for a more dynamic way to choosing target
        /// </summary>
        /// <returns></returns>
        public virtual Mobile GetBardTarget(bool creaturesOnly = false)
        {
            Mobile m = Combatant as Mobile;

            if (m == null && GetMaster() is PlayerMobile)
            {
                m = GetMaster().Combatant as Mobile;
            }

            if (creaturesOnly && m is PlayerMobile)
            {
                return null;
            }

            if (m == null || m == this || !CanBeHarmful(m, false) || (creaturesOnly && !(m is BaseCreature)))
            {
                List<AggressorInfo> list = new List<AggressorInfo>();
                list.AddRange(Aggressors.Where(info => !creaturesOnly || info.Attacker is PlayerMobile));

                if (list.Count > 0)
                {
                    m = list[Utility.Random(list.Count)].Attacker;
                }
                else
                {
                    m = null;
                }

                ColUtility.Free(list);
            }

            return m;
        }

        /// <summary>
        /// Used for second Provocation target.
        /// </summary>
        /// <param name="first"></param>
        /// <returns></returns>
        public virtual Mobile GetSecondTarget(BaseCreature first)
        {
            if (first == null)
                return null;

            int range = BaseInstrument.GetBardRange(this, SkillName.Provocation);

            IPooledEnumerable eable = Map.GetMobilesInRange(Location, range);
            List<Mobile> possibles = new List<Mobile>();

            foreach (Mobile m in eable)
            {
                if (m != first && m != this && first.InRange(m.Location, range))
                {
                    if (CanBeHarmful(m, false) && first.CanBeHarmful(m, false))
                        possibles.Add(m);
                }
            }
            eable.Free();

            Mobile t = null;

            if (possibles.Count > 0)
                t = possibles[Utility.Random(possibles.Count)];

            ColUtility.Free(possibles);

            return t;
        }
        #endregion

        #region TeleportTo
        private long m_NextTeleport;

        public virtual bool TeleportsTo => false;
        public virtual TimeSpan TeleportDuration => TimeSpan.FromSeconds(5);
        public virtual int TeleportRange => 16;
        public virtual double TeleportProb => 0.25;

        public virtual bool TeleportsPets => false;

        private static readonly int[] m_Offsets = new int[]
            {
                -1, -1,
                -1,  0,
                -1,  1,
                0, -1,
                0,  1,
                1, -1,
                1,  0,
                1,  1
            };

        public void TryTeleport()
        {
            if (Deleted)
                return;

            if (TeleportProb > Utility.RandomDouble())
            {
                Mobile toTeleport = GetTeleportTarget();

                if (toTeleport != null)
                {
                    int offset = Utility.Random(8) * 2;

                    Point3D to = Location;

                    for (int i = 0; i < m_Offsets.Length; i += 2)
                    {
                        int x = X + m_Offsets[(offset + i) % m_Offsets.Length];
                        int y = Y + m_Offsets[(offset + i + 1) % m_Offsets.Length];

                        if (Map.CanSpawnMobile(x, y, Z))
                        {
                            to = new Point3D(x, y, Z);
                            break;
                        }
                        else
                        {
                            int z = Map.GetAverageZ(x, y);

                            if (Map.CanSpawnMobile(x, y, z))
                            {
                                to = new Point3D(x, y, z);
                                break;
                            }
                        }
                    }

                    Point3D from = toTeleport.Location;
                    toTeleport.MoveToWorld(to, Map);

                    SpellHelper.Turn(this, toTeleport);
                    SpellHelper.Turn(toTeleport, this);

                    toTeleport.ProcessDelta();

                    Effects.SendLocationParticles(EffectItem.Create(from, Map, EffectItem.DefaultDuration), 0x3728, 10, 10, 2023);
                    Effects.SendLocationParticles(EffectItem.Create(to, Map, EffectItem.DefaultDuration), 0x3728, 10, 10, 5023);

                    toTeleport.PlaySound(0x1FE);

                    Combatant = toTeleport;

                    OnAfterTeleport(toTeleport);
                }
            }
        }

        public virtual Mobile GetTeleportTarget()
        {
            IPooledEnumerable eable = GetMobilesInRange(TeleportRange);
            List<Mobile> list = new List<Mobile>();

            foreach (Mobile m in eable)
            {
                bool isPet = m is BaseCreature && ((BaseCreature)m).GetMaster() is PlayerMobile;

                if (m != this && (m.Player || (TeleportsPets && isPet)) && CanBeHarmful(m) && CanSee(m))
                {
                    list.Add(m);
                }
            }

            eable.Free();

            Mobile mob = null;

            if (list.Count > 0)
                mob = list[Utility.Random(list.Count)];

            ColUtility.Free(list);
            return mob;
        }

        public virtual void OnAfterTeleport(Mobile m)
        {
        }
        #endregion

        #region Detect Hidden
        private long _NextDetect;

        public virtual bool CanDetectHidden => Controlled && Skills.DetectHidden.Value > 0;

        public virtual int FindPlayerDelayBase => (15000 / Int);
        public virtual int FindPlayerDelayMax => 60;
        public virtual int FindPlayerDelayMin => 5;
        public virtual int FindPlayerDelayHigh => 10;
        public virtual int FindPlayerDelayLow => 9;

        public virtual void TryFindPlayer()
        {
            if (Deleted || Map == null)
            {
                return;
            }

            double srcSkill = Skills[SkillName.DetectHidden].Value;

            if (srcSkill <= 0)
            {
                return;
            }

            DetectHidden.OnUse(this);

            if (Target is DetectHidden.InternalTarget)
            {
                Target.Invoke(this, this);
                DebugSay("Checking for hidden players");
            }
            else
            {
                DebugSay("Failed Checking for hidden players");
            }
        }
        #endregion

        public virtual void OnThink()
        {
            long tc = Core.TickCount;

            /*//FS:ATS BEGIN
            if ( this.Tamable == true )
            {
                if ( this.NextLevel == 0 )
                {
                    int totalstats = this.Str + this.Dex + this.Int + this.HitsMax + this.StamMax + this.ManaMax + this.PhysicalResistance + this.FireResistance + this.ColdResistance + this.EnergyResistance + this.PoisonResistance + this.DamageMin + this.DamageMax + this.VirtualArmor;
                    int nextlevel = totalstats * 10;

                    this.NextLevel = nextlevel;
                }

                if ( this.MaxLevel == 0 )
                {
                    this.MaxLevel = Utility.RandomMinMax( 10, 30 );
                }
            }
            //FS:ATS END (modified by Regnak)*/

            if (Paralyzed || Frozen)
            {
                return;
            }

            if (!Summoned && _Profile != null)
            {
                SpecialAbility.CheckThinkTrigger(this);
                AreaEffect.CheckThinkTrigger(this);
            }

            if (Combatant != null)
            {
                CheckCastMastery();
            }

            if (EnableRummaging && CanRummageCorpses && !Summoned && !Controlled && tc >= m_NextRummageTime)
            {
                double min, max;

                if (ChanceToRummage > Utility.RandomDouble() && Rummage())
                {
                    min = MinutesToNextRummageMin;
                    max = MinutesToNextRummageMax;
                }
                else
                {
                    min = MinutesToNextChanceMin;
                    max = MinutesToNextChanceMax;
                }

                double delay = min + (Utility.RandomDouble() * (max - min));
                m_NextRummageTime = tc + (int)TimeSpan.FromMinutes(delay).TotalMilliseconds;
            }

            if (ReturnsToHome && IsSpawnerBound() && !InRange(Home, RangeHome))
            {
                if ((Combatant == null) && (Warmode == false) && Utility.RandomDouble() < .10) /* some throttling */
                {
                    m_FailedReturnHome = !Move(GetDirectionTo(Home.X, Home.Y)) ? m_FailedReturnHome + 1 : 0;

                    if (m_FailedReturnHome > 5)
                    {
                        SetLocation(Home, true);

                        m_FailedReturnHome = 0;
                    }
                }
            }
            else
            {
                m_FailedReturnHome = 0;
            }

            Mobile combatant = Combatant as Mobile;

            if (combatant != null && CanDiscord && !Discordance.UnderEffects(combatant) && tc >= m_NextDiscord && 0.33 > Utility.RandomDouble())
            {
                DoDiscord();
                m_NextDiscord = tc + Utility.RandomMinMax(5000, 12500);
            }
            else if (combatant != null && CanPeace && !Peacemaking.UnderEffects(combatant) && tc >= m_NextPeace && 0.33 > Utility.RandomDouble())
            {
                DoPeace();
                m_NextPeace = tc + Utility.RandomMinMax(5000, 12500);
            }
            else if (combatant != null && CanProvoke && tc >= m_NextProvoke && 0.33 > Utility.RandomDouble())
            {
                DoProvoke();
                m_NextProvoke = tc + Utility.RandomMinMax(5000, 12500);
            }

            if (combatant != null && TeleportsTo && tc >= m_NextTeleport)
            {
                TryTeleport();
                m_NextTeleport = tc + (int)TeleportDuration.TotalMilliseconds;
            }

            if (CanDetectHidden && Core.TickCount >= _NextDetect)
            {
                TryFindPlayer();

                // Not exactly OSI style, approximation.
                int delay = FindPlayerDelayBase;

                if (delay > FindPlayerDelayMax)
                {
                    delay = FindPlayerDelayMax; // 60s max at 250 int
                }
                else if (delay < FindPlayerDelayMin)
                {
                    delay = FindPlayerDelayMin; // 5s min at 3000 int
                }

                int min = delay * (FindPlayerDelayLow / FindPlayerDelayHigh); // 13s at 1000 int, 33s at 400 int, 54s at <250 int
                int max = delay * (FindPlayerDelayHigh / FindPlayerDelayLow); // 16s at 1000 int, 41s at 400 int, 66s at <250 int

                _NextDetect = Core.TickCount +
                    (int)TimeSpan.FromSeconds(Utility.RandomMinMax(min, max)).TotalMilliseconds;
            }
        }

        public virtual bool Rummage()
        {
            if (Map == null)
                return false;

            Corpse toRummage = null;

            IPooledEnumerable eable = Map.GetItemsInRange(Location, 2);
            foreach (Item item in eable)
            {
                if (item is Corpse && ((Corpse)item).Items.Count > 0)
                {
                    toRummage = (Corpse)item;
                    break;
                }
            }
            eable.Free();

            if (toRummage == null)
            {
                return false;
            }

            Container pack = Backpack;

            if (pack == null)
            {
                return false;
            }

            List<Item> items = toRummage.Items;

            bool rejected;
            LRReason reason;

            for (int i = 0; i < items.Count; ++i)
            {
                Item item = items[Utility.Random(items.Count)];

                Lift(item, item.Amount, out rejected, out reason);

                if (!rejected && Drop(pack, new Point3D(-1, -1, 0)))
                {
                    // *rummages through a corpse and takes an item*
                    PublicOverheadMessage(MessageType.Emote, 0x3B2, 1008086);
                    //TODO: Instancing of Rummaged stuff.
                    return true;
                }
            }

            return false;
        }

        public void Pacify(Mobile master, DateTime endtime)
        {
            BardPacified = true;
            BardEndTime = endtime;
        }

        public override Mobile GetDamageMaster(Mobile damagee)
        {
            if (m_bBardProvoked && damagee == m_bBardTarget)
            {
                return m_bBardMaster;
            }
            else if (m_bControlled && m_ControlMaster != null)
            {
                return m_ControlMaster;
            }
            else if (m_bSummoned && m_SummonMaster != null)
            {
                return m_SummonMaster;
            }

            return base.GetDamageMaster(damagee);
        }

        public void Provoke(Mobile master, Mobile target, bool bSuccess)
        {
            BardProvoked = true;

            if (bSuccess)
            {
                PlaySound(GetIdleSound());

                BardMaster = master;
                BardTarget = target;
                Combatant = target;
                BardEndTime = DateTime.UtcNow + TimeSpan.FromSeconds(30.0);

                if (target is BaseCreature)
                {
                    BaseCreature t = (BaseCreature)target;

                    if (t.Unprovokable || (t.IsParagon && BaseInstrument.GetBaseDifficulty(t) >= 160.0))
                    {
                        return;
                    }

                    t.BardProvoked = true;

                    t.BardMaster = master;
                    t.BardTarget = this;
                    t.Combatant = this;
                    t.BardEndTime = DateTime.UtcNow + TimeSpan.FromSeconds(30.0);
                }
                else if (target is PlayerMobile)
                {
                    ((PlayerMobile)target).Combatant = this;
                    Combatant = target;
                }
            }
            else
            {
                PlaySound(GetAngerSound());

                BardMaster = master;
                BardTarget = target;
            }
        }

        public bool FindMyName(string str, bool bWithAll)
        {
            int i, j;

            string name = Name;

            if (name == null || str.Length < name.Length)
            {
                return false;
            }

            string[] wordsString = str.Split(' ');
            string[] wordsName = name.Split(' ');

            for (j = 0; j < wordsName.Length; j++)
            {
                string wordName = wordsName[j];

                bool bFound = false;
                for (i = 0; i < wordsString.Length; i++)
                {
                    string word = wordsString[i];

                    if (Insensitive.Equals(word, wordName))
                    {
                        bFound = true;
                    }

                    if (bWithAll && Insensitive.Equals(word, "all"))
                    {
                        return true;
                    }
                }

                if (!bFound)
                {
                    return false;
                }
            }

            return true;
        }

        public static void TeleportPets(Mobile master, Point3D loc, Map map)
        {
            TeleportPets(master, loc, map, false);
        }

        public static void TeleportPets(Mobile master, Point3D loc, Map map, bool onlyBonded)
        {
            List<Mobile> move = new List<Mobile>();

            IPooledEnumerable eable = master.GetMobilesInRange(3);

            foreach (Mobile m in eable)
            {
                if (m is BaseCreature)
                {
                    BaseCreature pet = (BaseCreature)m;

                    if (pet.Controlled && pet.ControlMaster == master)
                    {
                        if (!onlyBonded || pet.IsBonded)
                        {
                            if (pet.ControlOrder == OrderType.Guard || pet.ControlOrder == OrderType.Follow ||
                                pet.ControlOrder == OrderType.Come)
                            {
                                move.Add(pet);
                            }
                        }
                    }
                }
            }

            eable.Free();

            foreach (Mobile m in move)
            {
                m.MoveToWorld(loc, map);
            }

            ColUtility.Free(move);
        }

        public virtual void ResurrectPet()
        {
            if (!IsDeadPet)
            {
                return;
            }

            OnBeforeResurrect();

            Poison = null;

            Warmode = false;

            Hits = 10;
            Stam = StamMax;
            Mana = 0;

            ProcessDeltaQueue();

            IsDeadPet = false;

            Effects.SendPacket(Location, Map, new BondedStatus(0, Serial, 0));

            SendIncomingPacket();
            SendIncomingPacket();

            OnAfterResurrect();

            Mobile owner = ControlMaster;

            if (owner == null || owner.Deleted || owner.Map != Map || !owner.InRange(this, 12) || !CanSee(owner) || !InLOS(owner))
            {
                if (OwnerAbandonTime == DateTime.MinValue)
                {
                    OwnerAbandonTime = DateTime.UtcNow;
                }
            }
            else
            {
                OwnerAbandonTime = DateTime.MinValue;
            }

            CheckStatTimers();
        }

        public override bool CanBeDamaged()
        {
            if (IsDeadPet || IsInvulnerable)
            {
                return false;
            }

            return base.CanBeDamaged();
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public virtual bool PlayerRangeSensitive => CurrentWayPoint == null && (_NavPoints == null || _NavPoints.Count == 0);
        //If they are following a waypoint, they'll continue to follow it even if players aren't around

        /* until we are sure about who should be getting deleted, move them instead */
        /* On OSI, they despawn */

        private bool m_ReturnQueued;

        private bool IsSpawnerBound()
        {
            if ((Map != null) && (Map != Map.Internal))
            {
                if (FightMode != FightMode.None && (RangeHome >= 0))
                {
                    if (!Controlled && !Summoned)
                    {
                        if (Spawner != null && Spawner is Spawner && ((Spawner as Spawner).Map) == Map)
                        {
                            return true;
                        }
                    }
                }
            }

            return false;
        }

        public virtual bool ReturnsToHome => (m_SeeksHome && (Home != Point3D.Zero) && !m_ReturnQueued && !Controlled && !Summoned);

        public override void OnSectorDeactivate()
        {
            if (!Deleted && ReturnsToHome && IsSpawnerBound() && !InRange(Home, (RangeHome + 5)))
            {
                Timer.DelayCall(TimeSpan.FromSeconds((Utility.Random(45) + 15)), GoHome_Callback);

                m_ReturnQueued = true;
            }
            else if (PlayerRangeSensitive && m_AI != null)
            {
                m_AI.Deactivate();
            }

            base.OnSectorDeactivate();
        }

        public void GoHome_Callback()
        {
            if (m_ReturnQueued && IsSpawnerBound())
            {
                if (!((Map.GetSector(X, Y)).Active))
                {
                    SetLocation(Home, true);

                    if (!((Map.GetSector(X, Y)).Active) && m_AI != null)
                    {
                        m_AI.Deactivate();
                    }
                }
            }

            m_ReturnQueued = false;
        }

        public override void OnSectorActivate()
        {
            if (PlayerRangeSensitive && m_AI != null)
            {
                m_AI.Activate();
            }

            base.OnSectorActivate();
        }

        private bool m_RemoveIfUntamed;

        // used for deleting untamed creatures [in houses]
        private int m_RemoveStep;

        [CommandProperty(AccessLevel.GameMaster)]
        public bool RemoveIfUntamed { get { return m_RemoveIfUntamed; } set { m_RemoveIfUntamed = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public int RemoveStep { get { return m_RemoveStep; } set { m_RemoveStep = value; } }

        // used for deleting untamed creatures [on save]
        private bool m_RemoveOnSave;

        [CommandProperty(AccessLevel.GameMaster)]
        public bool RemoveOnSave { get { return m_RemoveOnSave; } set { m_RemoveOnSave = value; } }
    }

    public class LoyaltyTimer : Timer
    {
        private static readonly TimeSpan InternalDelay = TimeSpan.FromMinutes(5.0);

        public static void Initialize()
        {
            new LoyaltyTimer().Start();
        }

        public LoyaltyTimer()
            : base(InternalDelay, InternalDelay)
        {
            m_NextHourlyCheck = DateTime.UtcNow + TimeSpan.FromHours(1.0);
            Priority = TimerPriority.FiveSeconds;
        }

        private DateTime m_NextHourlyCheck;

        protected override void OnTick()
        {
            if (DateTime.UtcNow >= m_NextHourlyCheck)
            {
                m_NextHourlyCheck = DateTime.UtcNow + TimeSpan.FromHours(1.0);
            }
            else
            {
                return;
            }

            List<BaseCreature> toRelease = new List<BaseCreature>();

            // added array for wild creatures in house regions to be removed
            List<BaseCreature> toRemove = new List<BaseCreature>();

            Parallel.ForEach(
                World.Mobiles.Values,
                m =>
                {
                    if (m is BaseMount && ((BaseMount)m).Rider != null)
                    {
                        ((BaseCreature)m).OwnerAbandonTime = DateTime.MinValue;
                    }
                    else if (m is BaseCreature)
                    {
                        BaseCreature c = (BaseCreature)m;

                        if (c.IsDeadPet)
                        {
                            Mobile owner = c.ControlMaster;

                            if (!c.IsStabled && !(c is BaseVendor) &&
                                (owner == null || owner.Deleted || owner.Map != c.Map || !owner.InRange(c, 12) || !c.CanSee(owner) ||
                                 !c.InLOS(owner)))
                            {
                                if (c.OwnerAbandonTime == DateTime.MinValue)
                                {
                                    c.OwnerAbandonTime = DateTime.UtcNow;
                                }
                                else if ((c.OwnerAbandonTime + c.BondingAbandonDelay) <= DateTime.UtcNow)
                                {
                                    toRemove.Add(c);
                                }
                            }
                            else
                            {
                                c.OwnerAbandonTime = DateTime.MinValue;
                            }
                        }
                        else if (c.Controlled && c.Commandable)
                        {
                            c.OwnerAbandonTime = DateTime.MinValue;

                            if (c.Map != Map.Internal)
                            {
                                c.Loyalty -= (BaseCreature.MaxLoyalty / 10);

                                if (c.Loyalty < (BaseCreature.MaxLoyalty / 10))
                                {
                                    c.Say(1043270, c.Name); // * ~1_NAME~ looks around desperately *
                                    c.PlaySound(c.GetIdleSound());
                                }

                                if (c.Loyalty <= 0)
                                {
                                    toRelease.Add(c);
                                }
                            }
                        }

                        // added lines to check if a wild creature in a house region has to be removed or not
                        if (!c.Controlled && !c.IsStabled &&
                            ((c.Region.IsPartOf<HouseRegion>() && c.CanBeDamaged()) || (c.RemoveIfUntamed && c.Spawner == null)))
                        {
                            c.RemoveStep++;

                            if (c.RemoveStep >= 20)
                            {
                                lock (toRemove)
                                    toRemove.Add(c);
                            }
                        }
                        else
                        {
                            c.RemoveStep = 0;
                        }
                    }
                });

            foreach (BaseCreature c in toRelease.Where(c => c != null))
            {
                if (c.IsDeadBondedPet)
                {
                    c.Delete();
                    continue;
                }

                c.Say(1043255, c.Name); // ~1_NAME~ appears to have decided that is better off without a master!
                c.Loyalty = BaseCreature.MaxLoyalty; // Wonderfully Happy
                c.IsBonded = false;
                c.BondingBegin = DateTime.MinValue;
                c.OwnerAbandonTime = DateTime.MinValue;
                c.ControlTarget = null;

                if (c.AIObject != null)
                {
                    c.AIObject.DoOrderRelease();
                }

                // this will prevent no release of creatures left alone with AI disabled (and consequent bug of Followers)
                c.DropBackpack();
                c.RemoveOnSave = true;
            }

            // added code to handle removing of wild creatures in house regions
            foreach (BaseCreature c in toRemove)
            {
                c.Delete();
            }

            ColUtility.Free(toRelease);
            ColUtility.Free(toRemove);
        }
    }

    #region Delete Previously Tamed Timer
    public class CreatureDeleteTimer : Timer
    {
        public static CreatureDeleteTimer Instance { get; set; }

        public List<BaseCreature> ToDelete { get; set; } = new List<BaseCreature>();

        public CreatureDeleteTimer()
            : base(TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5))
        {
            Priority = TimerPriority.OneMinute;
        }

        protected override void OnTick()
        {
            var toDelete = ToDelete.Where(bc => bc.Deleted || bc.DeleteTime < DateTime.UtcNow).ToList();

            for (int i = 0; i < toDelete.Count; i++)
            {
                var bc = toDelete[i];

                if (!bc.Summoned && !bc.Deleted && !bc.IsStabled && bc.DeleteTime != DateTime.MinValue)
                {
                    bc.Delete();
                }

                RemoveFromTimer(bc);
            }

            ColUtility.Free(toDelete);
        }

        public static void RegisterTimer(BaseCreature bc)
        {
            if (Instance == null)
            {
                Instance = new CreatureDeleteTimer();
            }

            if (!Instance.Running)
            {
                Instance.Start();
            }

            if (!Instance.ToDelete.Contains(bc) && !bc.Summoned && !bc.Deleted && !bc.IsStabled)
            {
                Instance.ToDelete.Add(bc);
            }
        }

        public static void RemoveFromTimer(BaseCreature bc)
        {
            if (Instance == null)
            {
                return;
            }

            if (Instance.ToDelete.Contains(bc))
            {
                Instance.ToDelete.Remove(bc);

                if (Instance.ToDelete.Count == 0)
                {
                    Instance.Stop();
                }
            }
        }
    }
    #endregion

    public sealed class PetWindow : Packet
    {
        public PetWindow(PlayerMobile owner, Mobile pet)
            : base(0x31)
        {
            int count = owner.AllFollowers.Count;

            EnsureCapacity(6 + (6 * count));

            m_Stream.Write(owner.Serial);
            m_Stream.Write((byte)count);

            for (int i = 0; i < owner.AllFollowers.Count; i++)
            {
                m_Stream.Write(owner.AllFollowers[i].Serial);
                m_Stream.Write((byte)0x01);
            }
        }
    }
}
 
Last edited:
Well the thing you "xed" out is actually the issue.

You need to also add it in the same order in Deserialize

Reason:

You save the data, but never read it. Since it is reading bytes and you have to assign them for the deserialization, the order needs to match as well as the data.

Otherwords:

What has been written MUST be read as well in the same order else your save doesnt align with what the server would say would be the "end" of the save.
 
I should have clarified - this is the stable version of the script with the changes still there but turned off. When they aren't x'ed out and FS:ATS is loaded up is when I have the errors occurring.

Unless I'm misunderstanding you. Which is possible - I am not very experienced with C#
 
You only code to save the data for the fs ats system is in the serialize method, you did not add the deserialization inside the deserialize methods
 
Okay. I just copied and pasted the FS:ATS edits as I found them. So what do I do to add the deserialize that I'm missing?
 
Where did you get these edits from? They should have included both the serialization and deserialization edits as they're both needed. What PyrO is saying is to take your edits from the serialize method and reverse engineer them into the deserialize method, hopefully that helps lol.. if you need more help I can look into it a bit more but I haven't read through all your edits yet.
 
Back