#region References
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

using CustomsFramework;

using Server.ContextMenus;
using Server.Items;
using Server.Network;
using Server.Targeting;
#endregion

namespace Server
{
    /// <summary>
    ///     Enumeration of item layer values.
    /// </summary>
    public enum Layer : byte
    {
        /// <summary>
        ///     Invalid layer.
        /// </summary>
        Invalid = 0x00,

        /// <summary>
        ///     First valid layer. Equivalent to <c>Layer.OneHanded</c>.
        /// </summary>
        FirstValid = 0x01,

        /// <summary>
        ///     One handed weapon.
        /// </summary>
        OneHanded = 0x01,

        /// <summary>
        ///     Two handed weapon or shield.
        /// </summary>
        TwoHanded = 0x02,

        /// <summary>
        ///     Shoes.
        /// </summary>
        Shoes = 0x03,

        /// <summary>
        ///     Pants.
        /// </summary>
        Pants = 0x04,

        /// <summary>
        ///     Shirts.
        /// </summary>
        Shirt = 0x05,

        /// <summary>
        ///     Helmets, hats, and masks.
        /// </summary>
        Helm = 0x06,

        /// <summary>
        ///     Gloves.
        /// </summary>
        Gloves = 0x07,

        /// <summary>
        ///     Rings.
        /// </summary>
        Ring = 0x08,

        /// <summary>
        ///     Talismans.
        /// </summary>
        Talisman = 0x09,

        /// <summary>
        ///     Gorgets and necklaces.
        /// </summary>
        Neck = 0x0A,

        /// <summary>
        ///     Hair.
        /// </summary>
        Hair = 0x0B,

        /// <summary>
        ///     Half aprons.
        /// </summary>
        Waist = 0x0C,

        /// <summary>
        ///     Torso, inner layer.
        /// </summary>
        InnerTorso = 0x0D,

        /// <summary>
        ///     Bracelets.
        /// </summary>
        Bracelet = 0x0E,

        /// <summary>
        ///     Face.
        /// </summary>
        Face = 0x0F,

        /// <summary>
        ///     Beards and mustaches.
        /// </summary>
        FacialHair = 0x10,

        /// <summary>
        ///     Torso, outer layer.
        /// </summary>
        MiddleTorso = 0x11,

        /// <summary>
        ///     Earings.
        /// </summary>
        Earrings = 0x12,

        /// <summary>
        ///     Arms and sleeves.
        /// </summary>
        Arms = 0x13,

        /// <summary>
        ///     Cloaks.
        /// </summary>
        Cloak = 0x14,

        /// <summary>
        ///     Backpacks.
        /// </summary>
        Backpack = 0x15,

        /// <summary>
        ///     Torso, outer layer.
        /// </summary>
        OuterTorso = 0x16,

        /// <summary>
        ///     Leggings, outer layer.
        /// </summary>
        OuterLegs = 0x17,

        /// <summary>
        ///     Leggings, inner layer.
        /// </summary>
        InnerLegs = 0x18,

        /// <summary>
        ///     Last valid non-internal layer. Equivalent to <c>Layer.InnerLegs</c>.
        /// </summary>
        LastUserValid = 0x18,

        /// <summary>
        ///     Mount item layer.
        /// </summary>
        Mount = 0x19,

        /// <summary>
        ///     Vendor 'buy pack' layer.
        /// </summary>
        ShopBuy = 0x1A,

        /// <summary>
        ///     Vendor 'resale pack' layer.
        /// </summary>
        ShopResale = 0x1B,

        /// <summary>
        ///     Vendor 'sell pack' layer.
        /// </summary>
        ShopSell = 0x1C,

        /// <summary>
        ///     Bank box layer.
        /// </summary>
        Bank = 0x1D,

        /// <summary>
        /// Unused, using this layer makes you invisible to other players. Strange.
        /// </summary>
        /// 
        Reserved_1 = 0x1E,

        /// <summary>
        ///     Secure Trade Layer
        /// </summary>
        SecureTrade = 0x1F,
    }

    /// <summary>
    ///     Internal flags used to signal how the item should be updated and resent to nearby clients.
    /// </summary>
    [Flags]
    public enum ItemDelta
    {
        /// <summary>
        ///     Nothing.
        /// </summary>
        None = 0x00000000,

        /// <summary>
        ///     Resend the item.
        /// </summary>
        Update = 0x00000001,

        /// <summary>
        ///     Resend the item only if it is equiped.
        /// </summary>
        EquipOnly = 0x00000002,

        /// <summary>
        ///     Resend the item's properties.
        /// </summary>
        Properties = 0x00000004
    }

    /// <summary>
    ///     Enumeration containing possible ways to handle item ownership on death.
    /// </summary>
    public enum DeathMoveResult
    {
        /// <summary>
        ///     The item should be placed onto the corpse.
        /// </summary>
        MoveToCorpse,

        /// <summary>
        ///     The item should remain equiped.
        /// </summary>
        RemainEquiped,

        /// <summary>
        ///     The item should be placed into the owners backpack.
        /// </summary>
        MoveToBackpack
    }

    /// <summary>
    ///     Enumeration containing all possible light types. These are only applicable to light source items, like lanterns, candles, braziers, etc.
    /// </summary>
    public enum LightType
    {
        /// <summary>
        ///     Window shape, arched, ray shining east.
        /// </summary>
        ArchedWindowEast = 0,

        /// <summary>
        ///     Medium circular shape.
        /// </summary>
        Circle225 = 1,

        /// <summary>
        ///     Small circular shape.
        /// </summary>
        Circle150 = 2,

        /// <summary>
        ///     Door shape, shining south.
        /// </summary>
        DoorSouth = 3,

        /// <summary>
        ///     Door shape, shining east.
        /// </summary>
        DoorEast = 4,

        /// <summary>
        ///     Large semicircular shape (180 degrees), north wall.
        /// </summary>
        NorthBig = 5,

        /// <summary>
        ///     Large pie shape (90 degrees), north-east corner.
        /// </summary>
        NorthEastBig = 6,

        /// <summary>
        ///     Large semicircular shape (180 degrees), east wall.
        /// </summary>
        EastBig = 7,

        /// <summary>
        ///     Large semicircular shape (180 degrees), west wall.
        /// </summary>
        WestBig = 8,

        /// <summary>
        ///     Large pie shape (90 degrees), south-west corner.
        /// </summary>
        SouthWestBig = 9,

        /// <summary>
        ///     Large semicircular shape (180 degrees), south wall.
        /// </summary>
        SouthBig = 10,

        /// <summary>
        ///     Medium semicircular shape (180 degrees), north wall.
        /// </summary>
        NorthSmall = 11,

        /// <summary>
        ///     Medium pie shape (90 degrees), north-east corner.
        /// </summary>
        NorthEastSmall = 12,

        /// <summary>
        ///     Medium semicircular shape (180 degrees), east wall.
        /// </summary>
        EastSmall = 13,

        /// <summary>
        ///     Medium semicircular shape (180 degrees), west wall.
        /// </summary>
        WestSmall = 14,

        /// <summary>
        ///     Medium semicircular shape (180 degrees), south wall.
        /// </summary>
        SouthSmall = 15,

        /// <summary>
        ///     Shaped like a wall decoration, north wall.
        /// </summary>
        DecorationNorth = 16,

        /// <summary>
        ///     Shaped like a wall decoration, north-east corner.
        /// </summary>
        DecorationNorthEast = 17,

        /// <summary>
        ///     Small semicircular shape (180 degrees), east wall.
        /// </summary>
        EastTiny = 18,

        /// <summary>
        ///     Shaped like a wall decoration, west wall.
        /// </summary>
        DecorationWest = 19,

        /// <summary>
        ///     Shaped like a wall decoration, south-west corner.
        /// </summary>
        DecorationSouthWest = 20,

        /// <summary>
        ///     Small semicircular shape (180 degrees), south wall.
        /// </summary>
        SouthTiny = 21,

        /// <summary>
        ///     Window shape, rectangular, no ray, shining south.
        /// </summary>
        RectWindowSouthNoRay = 22,

        /// <summary>
        ///     Window shape, rectangular, no ray, shining east.
        /// </summary>
        RectWindowEastNoRay = 23,

        /// <summary>
        ///     Window shape, rectangular, ray shining south.
        /// </summary>
        RectWindowSouth = 24,

        /// <summary>
        ///     Window shape, rectangular, ray shining east.
        /// </summary>
        RectWindowEast = 25,

        /// <summary>
        ///     Window shape, arched, no ray, shining south.
        /// </summary>
        ArchedWindowSouthNoRay = 26,

        /// <summary>
        ///     Window shape, arched, no ray, shining east.
        /// </summary>
        ArchedWindowEastNoRay = 27,

        /// <summary>
        ///     Window shape, arched, ray shining south.
        /// </summary>
        ArchedWindowSouth = 28,

        /// <summary>
        ///     Large circular shape.
        /// </summary>
        Circle300 = 29,

        /// <summary>
        ///     Large pie shape (90 degrees), north-west corner.
        /// </summary>
        NorthWestBig = 30,

        /// <summary>
        ///     Negative light. Medium pie shape (90 degrees), south-east corner.
        /// </summary>
        DarkSouthEast = 31,

        /// <summary>
        ///     Negative light. Medium semicircular shape (180 degrees), south wall.
        /// </summary>
        DarkSouth = 32,

        /// <summary>
        ///     Negative light. Medium pie shape (90 degrees), north-west corner.
        /// </summary>
        DarkNorthWest = 33,

        /// <summary>
        ///     Negative light. Medium pie shape (90 degrees), south-east corner. Equivalent to <c>LightType.SouthEast</c>.
        /// </summary>
        DarkSouthEast2 = 34,

        /// <summary>
        ///     Negative light. Medium circular shape (180 degrees), east wall.
        /// </summary>
        DarkEast = 35,

        /// <summary>
        ///     Negative light. Large circular shape.
        /// </summary>
        DarkCircle300 = 36,

        /// <summary>
        ///     Opened door shape, shining south.
        /// </summary>
        DoorOpenSouth = 37,

        /// <summary>
        ///     Opened door shape, shining east.
        /// </summary>
        DoorOpenEast = 38,

        /// <summary>
        ///     Window shape, square, ray shining east.
        /// </summary>
        SquareWindowEast = 39,

        /// <summary>
        ///     Window shape, square, no ray, shining east.
        /// </summary>
        SquareWindowEastNoRay = 40,

        /// <summary>
        ///     Window shape, square, ray shining south.
        /// </summary>
        SquareWindowSouth = 41,

        /// <summary>
        ///     Window shape, square, no ray, shining south.
        /// </summary>
        SquareWindowSouthNoRay = 42,

        /// <summary>
        ///     Empty.
        /// </summary>
        Empty = 43,

        /// <summary>
        ///     Window shape, skinny, no ray, shining south.
        /// </summary>
        SkinnyWindowSouthNoRay = 44,

        /// <summary>
        ///     Window shape, skinny, ray shining east.
        /// </summary>
        SkinnyWindowEast = 45,

        /// <summary>
        ///     Window shape, skinny, no ray, shining east.
        /// </summary>
        SkinnyWindowEastNoRay = 46,

        /// <summary>
        ///     Shaped like a hole, shining south.
        /// </summary>
        HoleSouth = 47,

        /// <summary>
        ///     Shaped like a hole, shining south.
        /// </summary>
        HoleEast = 48,

        /// <summary>
        ///     Large circular shape with a moongate graphic embeded.
        /// </summary>
        Moongate = 49,

        /// <summary>
        ///     Unknown usage. Many rows of slightly angled lines.
        /// </summary>
        Strips = 50,

        /// <summary>
        ///     Shaped like a small hole, shining south.
        /// </summary>
        SmallHoleSouth = 51,

        /// <summary>
        ///     Shaped like a small hole, shining east.
        /// </summary>
        SmallHoleEast = 52,

        /// <summary>
        ///     Large semicircular shape (180 degrees), north wall. Identical graphic as <c>LightType.NorthBig</c>, but slightly different positioning.
        /// </summary>
        NorthBig2 = 53,

        /// <summary>
        ///     Large semicircular shape (180 degrees), west wall. Identical graphic as <c>LightType.WestBig</c>, but slightly different positioning.
        /// </summary>
        WestBig2 = 54,

        /// <summary>
        ///     Large pie shape (90 degrees), north-west corner. Equivalent to <c>LightType.NorthWestBig</c>.
        /// </summary>
        NorthWestBig2 = 55
    }

    /// <summary>
    ///     Enumeration of an item's loot and steal state.
    /// </summary>
    public enum LootType : byte
    {
        /// <summary>
        ///     Stealable. Lootable.
        /// </summary>
        Regular = 0,

        /// <summary>
        ///     Unstealable. Unlootable, unless owned by a murderer.
        /// </summary>
        Newbied = 1,

        /// <summary>
        ///     Unstealable. Unlootable, always.
        /// </summary>
        Blessed = 2,

        /// <summary>
        ///     Stealable. Lootable, always.
        /// </summary>
        Cursed = 3
    }

    public class BounceInfo
    {
        public Map m_Map;
        public Point3D m_Location, m_WorldLoc;
        public object m_Parent;
        public object m_ParentStack;
        public byte m_GridLocation;
        public Mobile m_Mobile;

        public BounceInfo(Mobile from, Item item)
        {
            m_Map = item.Map;
            m_Location = item.Location;
            m_WorldLoc = item.GetWorldLocation();
            m_Parent = item.Parent;
            m_ParentStack = null;
            m_GridLocation = item.GridLocation;
            m_Mobile = from;
        }

        private BounceInfo(Map map, Point3D loc, Point3D worldLoc, IEntity parent)
        {
            m_Map = map;
            m_Location = loc;
            m_WorldLoc = worldLoc;
            m_Parent = parent;
            m_ParentStack = null;
        }

        public static BounceInfo Deserialize(GenericReader reader)
        {
            if (reader.ReadBool())
            {
                Map map = reader.ReadMap();
                Point3D loc = reader.ReadPoint3D();
                Point3D worldLoc = reader.ReadPoint3D();

                IEntity parent;

                Serial serial = reader.ReadInt();

                if (serial.IsItem)
                {
                    parent = World.FindItem(serial);
                }
                else if (serial.IsMobile)
                {
                    parent = World.FindMobile(serial);
                }
                else
                {
                    parent = null;
                }

                return new BounceInfo(map, loc, worldLoc, parent);
            }
            else
            {
                return null;
            }
        }

        public static void Serialize(BounceInfo info, GenericWriter writer)
        {
            if (info == null)
            {
                writer.Write(false);
            }
            else
            {
                writer.Write(true);

                writer.Write(info.m_Map);
                writer.Write(info.m_Location);
                writer.Write(info.m_WorldLoc);

                if (info.m_Parent is Mobile)
                {
                    writer.Write((Mobile)info.m_Parent);
                }
                else if (info.m_Parent is Item)
                {
                    writer.Write((Item)info.m_Parent);
                }
                else
                {
                    writer.Write((Serial)0);
                }
            }
        }
    }

    public enum TotalType
    {
        Gold,
        Items,
        Weight,
    }

    [Flags]
    public enum ExpandFlag
    {
        None = 0x000,

        Name = 0x001,
        Items = 0x002,
        Bounce = 0x004,
        Holder = 0x008,
        Blessed = 0x010,
        TempFlag = 0x020,
        SaveFlag = 0x040,
        Weight = 0x080,
        Spawner = 0x100
    }

    public class Item : IEntity, IHued, IComparable<Item>, ISerializable, ISpawnable
    {
        #region Customs Framework
        private List<BaseModule> m_Modules = new List<BaseModule>();

        [CommandProperty(AccessLevel.Developer)]
        public List<BaseModule> Modules { get { return m_Modules; } set { m_Modules = value; } }

        public BaseModule GetModule(string name)
        {
            return Modules.FirstOrDefault(mod => mod.Name == name);
        }

        public BaseModule GetModule(Type type)
        {
            return Modules.FirstOrDefault(mod => mod.GetType() == type);
        }

        public List<BaseModule> GetModules(string name)
        {
            return Modules.Where(mod => mod.Name == name).ToList();
        }

        public List<BaseModule> SearchModules(string search)
        {
            var keywords = search.ToLower().Split(' ');
            var modules = new List<BaseModule>();

            foreach (BaseModule mod in Modules)
            {
                bool match = true;
                string name = mod.Name.ToLower();

                foreach (string keyword in keywords)
                {
                    if (name.IndexOf(keyword, StringComparison.Ordinal) == -1)
                    {
                        match = false;
                    }
                }

                if (match)
                {
                    modules.Add(mod);
                }
            }

            return modules;
        }
        #endregion

        public static readonly List<Item> EmptyItems = new List<Item>();

        public int CompareTo(IEntity other)
        {
            if (other == null)
            {
                return -1;
            }

            return m_Serial.CompareTo(other.Serial);
        }

        public int CompareTo(Item other)
        {
            return CompareTo((IEntity)other);
        }

        public int CompareTo(object other)
        {
            if (other == null || other is IEntity)
            {
                return CompareTo((IEntity)other);
            }

            throw new ArgumentException();
        }

        #region Standard fields
        private Serial m_Serial;
        private Point3D m_Location;
        private int m_ItemID;
        private int m_Hue;
        private int m_Amount;
        private Layer m_Layer;
        private object m_Parent; // Mobile, Item, or null=World
        private Map m_Map;
        private LootType m_LootType;
        private DateTime m_LastMovedTime;
        private Direction m_Direction;
        private LightType m_Light;
        #endregion

        private ItemDelta m_DeltaFlags;
        private ImplFlag m_Flags;

        #region Packet caches
        private Packet m_WorldPacket;
        private Packet m_WorldPacketSA;
        private Packet m_WorldPacketHS;
        private Packet m_RemovePacket;

        private Packet m_OPLPacket;
        private ObjectPropertyList m_PropertyList;
        #endregion

        public int TempFlags
        {
            get
            {
                CompactInfo info = LookupCompactInfo();

                if (info != null)
                {
                    return info.m_TempFlags;
                }

                return 0;
            }
            set
            {
                CompactInfo info = AcquireCompactInfo();

                info.m_TempFlags = value;

                if (info.m_TempFlags == 0)
                {
                    VerifyCompactInfo();
                }
            }
        }

        public int SavedFlags
        {
            get
            {
                CompactInfo info = LookupCompactInfo();

                if (info != null)
                {
                    return info.m_SavedFlags;
                }

                return 0;
            }
            set
            {
                CompactInfo info = AcquireCompactInfo();

                info.m_SavedFlags = value;

                if (info.m_SavedFlags == 0)
                {
                    VerifyCompactInfo();
                }
            }
        }

        /// <summary>
        ///     The <see cref="Mobile" /> who is currently <see cref="Mobile.Holding">holding</see> this item.
        /// </summary>
        public Mobile HeldBy
        {
            get
            {
                CompactInfo info = LookupCompactInfo();

                if (info != null)
                {
                    return info.m_HeldBy;
                }

                return null;
            }
            set
            {
                CompactInfo info = AcquireCompactInfo();

                info.m_HeldBy = value;

                if (info.m_HeldBy == null)
                {
                    VerifyCompactInfo();
                }
            }
        }
        
        private byte m_GridLocation = 0;

        [CommandProperty(AccessLevel.GameMaster)]
        public byte GridLocation
        {
            get { return m_GridLocation; }
            set
            {
                if (Parent is Container)
                {
                    if (value < 0 || value > 0x7C || !((Container)Parent).IsFreePosition(value))
                    {
                        m_GridLocation = ((Container)Parent).GetNewPosition(0);
                    }
                    else
                    {
                        m_GridLocation = value;
                    }
                }
                else
                {
                    m_GridLocation = value;
                }
            }
        }

        [Flags]
        private enum ImplFlag : byte
        {
            None = 0x00,
            Visible = 0x01,
            Movable = 0x02,
            Deleted = 0x04,
            Stackable = 0x08,
            InQueue = 0x10,
            Insured = 0x20,
            PayedInsurance = 0x40,
            QuestItem = 0x80
        }

        private class CompactInfo
        {
            public string m_Name;

            public List<Item> m_Items;
            public BounceInfo m_Bounce;

            public Mobile m_HeldBy;
            public Mobile m_BlessedFor;

            public ISpawner m_Spawner;

            public int m_TempFlags;
            public int m_SavedFlags;

            public double m_Weight = -1;
        }

        private CompactInfo m_CompactInfo;

        public ExpandFlag GetExpandFlags()
        {
            CompactInfo info = LookupCompactInfo();

            ExpandFlag flags = 0;

            if (info != null)
            {
                if (info.m_BlessedFor != null)
                {
                    flags |= ExpandFlag.Blessed;
                }

                if (info.m_Bounce != null)
                {
                    flags |= ExpandFlag.Bounce;
                }

                if (info.m_HeldBy != null)
                {
                    flags |= ExpandFlag.Holder;
                }

                if (info.m_Items != null)
                {
                    flags |= ExpandFlag.Items;
                }

                if (info.m_Name != null)
                {
                    flags |= ExpandFlag.Name;
                }

                if (info.m_Spawner != null)
                {
                    flags |= ExpandFlag.Spawner;
                }

                if (info.m_SavedFlags != 0)
                {
                    flags |= ExpandFlag.SaveFlag;
                }

                if (info.m_TempFlags != 0)
                {
                    flags |= ExpandFlag.TempFlag;
                }

                if (info.m_Weight != -1)
                {
                    flags |= ExpandFlag.Weight;
                }
            }

            return flags;
        }

        private CompactInfo LookupCompactInfo()
        {
            return m_CompactInfo;
        }

        private CompactInfo AcquireCompactInfo()
        {
            if (m_CompactInfo == null)
            {
                m_CompactInfo = new CompactInfo();
            }

            return m_CompactInfo;
        }

        private void ReleaseCompactInfo()
        {
            m_CompactInfo = null;
        }

        private void VerifyCompactInfo()
        {
            CompactInfo info = m_CompactInfo;

            if (info == null)
            {
                return;
            }

            bool isValid = (info.m_Name != null) || (info.m_Items != null) || (info.m_Bounce != null) || (info.m_HeldBy != null) ||
                           (info.m_BlessedFor != null) || (info.m_Spawner != null) || (info.m_TempFlags != 0) || (info.m_SavedFlags != 0) ||
                           (info.m_Weight != -1);

            if (!isValid)
            {
                ReleaseCompactInfo();
            }
        }

        public List<Item> LookupItems()
        {
            if (this is Container)
            {
                return ((Container)this).m_Items;
            }

            CompactInfo info = LookupCompactInfo();

            if (info != null)
            {
                return info.m_Items;
            }

            return null;
        }

        public List<Item> AcquireItems()
        {
            if (this is Container)
            {
                Container cont = (Container)this;

                if (cont.m_Items == null)
                {
                    cont.m_Items = new List<Item>();
                }

                return cont.m_Items;
            }

            CompactInfo info = AcquireCompactInfo();

            if (info.m_Items == null)
            {
                info.m_Items = new List<Item>();
            }

            return info.m_Items;
        }

        #region Mondain's Legacy
        public static Bitmap GetBitmap(int itemID)
        {
            try
            {
                return Ultima.Art.GetStatic(itemID);
            }
            catch
            {
                if (Core.Debug)
                {
                    Utility.PushColor(ConsoleColor.Red);
                    Console.WriteLine("Ultima Art: Unable to read client files.");
                    Utility.PopColor();
                }
            }

            return null;
        }

        public static void Measure(Bitmap bmp, out int xMin, out int yMin, out int xMax, out int yMax)
        {
            Ultima.Art.Measure(bmp, out xMin, out yMin, out xMax, out yMax);
        }

        public static Rectangle MeasureBound(Bitmap bmp)
        {
            int xMin, yMin, xMax, yMax;
            Measure(bmp, out xMin, out yMin, out xMax, out yMax);
            return new Rectangle(xMin, yMin, xMax - xMin, yMax - yMin);
        }

        public static Size MeasureSize(Bitmap bmp)
        {
            int xMin, yMin, xMax, yMax;
            Measure(bmp, out xMin, out yMin, out xMax, out yMax);
            return new Size(xMax - xMin, yMax - yMin);
        }
        #endregion

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

        private bool GetFlag(ImplFlag flag)
        {
            return ((m_Flags & flag) != 0);
        }

        public BounceInfo GetBounce()
        {
            CompactInfo info = LookupCompactInfo();

            if (info != null)
            {
                return info.m_Bounce;
            }

            return null;
        }

        public void RecordBounce(Mobile from, Item parentstack = null)
        {
            CompactInfo info = AcquireCompactInfo();

            info.m_Bounce = new BounceInfo(from, this);
            info.m_Bounce.m_ParentStack = parentstack;
        }

        public void ClearBounce()
        {
            CompactInfo info = LookupCompactInfo();

            if (info != null)
            {
                BounceInfo bounce = info.m_Bounce;

                if (bounce != null)
                {
                    info.m_Bounce = null;

                    if (bounce.m_Parent is Item)
                    {
                        Item parent = (Item)bounce.m_Parent;

                        if (!parent.Deleted)
                        {
                            parent.OnItemBounceCleared(this);
                        }
                    }
                    else if (bounce.m_Parent is Mobile)
                    {
                        Mobile parent = (Mobile)bounce.m_Parent;

                        if (!parent.Deleted)
                        {
                            parent.OnItemBounceCleared(this);
                        }
                    }

                    VerifyCompactInfo();
                }
            }
        }

        /// <summary>
        ///     Overridable. Virtual event invoked when a client, <paramref name="from" />, invokes a 'help request' for the Item. Seemingly no longer functional in newer clients.
        /// </summary>
        public virtual void OnHelpRequest(Mobile from)
        { }

        /// <summary>
        ///     Overridable. Method checked to see if the item can be traded.
        /// </summary>
        /// <returns>True if the trade is allowed, false if not.</returns>
        public virtual bool AllowSecureTrade(Mobile from, Mobile to, Mobile newOwner, bool accepted)
        {
            return true;
        }

        /// <summary>
        ///     Overridable. Virtual event invoked when a trade has completed, either successfully or not.
        /// </summary>
        public virtual void OnSecureTrade(Mobile from, Mobile to, Mobile newOwner, bool accepted)
        { }

        /// <summary>
        ///     Overridable. Method checked to see if the elemental resistances of this Item conflict with another Item on the
        ///     <see
        ///         cref="Mobile" />
        ///     .
        /// </summary>
        /// <returns>
        ///     <list type="table">
        ///         <item>
        ///             <term>True</term>
        ///             <description>
        ///                 There is a confliction. The elemental resistance bonuses of this Item should not be applied to the
        ///                 <see
        ///                     cref="Mobile" />
        ///             </description>
        ///         </item>
        ///         <item>
        ///             <term>False</term>
        ///             <description>There is no confliction. The bonuses should be applied.</description>
        ///         </item>
        ///     </list>
        /// </returns>
        public virtual bool CheckPropertyConfliction(Mobile m)
        {
            return false;
        }

        /// <summary>
        ///     Overridable. Sends the <see cref="PropertyList">object property list</see> to <paramref name="from" />.
        /// </summary>
        public virtual void SendPropertiesTo(Mobile from)
        {
            from.Send(PropertyList);
        }

        /// <summary>
        ///     Overridable. Adds the name of this item to the given <see cref="ObjectPropertyList" />. This method should be
        ///     overriden if the item requires a complex naming format.
        /// </summary>
        public virtual void AddNameProperty(ObjectPropertyList list)
        {
            string name = Name ?? String.Empty;

            if (String.IsNullOrWhiteSpace(name))
            {
                if (m_Amount <= 1)
                {
                    list.Add(LabelNumber);
                }
                else
                {
                    list.Add(1050039, "{0}\t#{1}", m_Amount, LabelNumber); // ~1_NUMBER~ ~2_ITEMNAME~
                }
            }
            else
            {
                if (m_Amount <= 1)
                {
                    list.Add(name);
                }
                else
                {
                    list.Add(1050039, "{0}\t{1}", m_Amount, Name); // ~1_NUMBER~ ~2_ITEMNAME~
                }
            }
        }

        /// <summary>
        ///     Overridable. Adds the loot type of this item to the given <see cref="ObjectPropertyList" />. By default, this will be either 'blessed', 'cursed', or 'insured'.
        /// </summary>
        public virtual void AddLootTypeProperty(ObjectPropertyList list)
        {
            if (DisplayLootType)
            {               
                if (m_LootType == LootType.Blessed)
                {
                    list.Add(1038021); // blessed
                }
                else if (m_LootType == LootType.Cursed)
                {
                    list.Add(1049643); // cursed
                }
                else if (Insured)
                {
                    list.Add(1061682); // <b>insured</b>
                }
            }
        }

        /// <summary>
        ///     Overridable. Adds any elemental resistances of this item to the given <see cref="ObjectPropertyList" />.
        /// </summary>
        public virtual void AddResistanceProperties(ObjectPropertyList list)
        {
            int v = PhysicalResistance;

            if (v != 0)
            {
                list.Add(1060448, v.ToString()); // physical resist ~1_val~%
            }

            v = FireResistance;

            if (v != 0)
            {
                list.Add(1060447, v.ToString()); // fire resist ~1_val~%
            }

            v = ColdResistance;

            if (v != 0)
            {
                list.Add(1060445, v.ToString()); // cold resist ~1_val~%
            }

            v = PoisonResistance;

            if (v != 0)
            {
                list.Add(1060449, v.ToString()); // poison resist ~1_val~%
            }

            v = EnergyResistance;

            if (v != 0)
            {
                list.Add(1060446, v.ToString()); // energy resist ~1_val~%
            }
        }

        /// <summary>
        ///     Overridable. Determines whether the item will show <see cref="AddWeightProperty" />.
        /// </summary>
        public virtual bool DisplayWeight
        {
            get
            {
                if (!Core.ML)
                {
                    return false;
                }

                if (!Movable && !(IsLockedDown || IsSecure) && ItemData.Weight == 255)
                {
                    return false;
                }

                return true;
            }
        }

        /// <summary>
        ///     Overridable. Adds header properties. By default, this invokes <see cref="AddNameProperty" />,
        ///     <see
        ///         cref="AddBlessedForProperty" />
        ///     (if applicable), and <see cref="AddLootTypeProperty" /> (if
        ///     <see
        ///         cref="DisplayLootType" />
        ///     ).
        /// </summary>
        public virtual void AddNameProperties(ObjectPropertyList list)
        {
            AddNameProperty(list);

            if (IsSecure)
            {
                AddSecureProperty(list);
            }
            else if (IsLockedDown)
            {
                AddLockedDownProperty(list);
            }

            AddCraftedProperties(list);
            AddLootTypeProperty(list);
            AddUsesRemainingProperties(list);
            AddWeightProperty(list);

            AppendChildNameProperties(list);

            if (QuestItem)
            {
                AddQuestItemProperty(list);
            }
        }

        /// <summary>
        /// Overrideable, used to add crafted by, excpetional, etc properties to items
        /// </summary>
        /// <param name="list"></param>
        public virtual void AddCraftedProperties(ObjectPropertyList list)
        {
        }

        /// <summary>
        /// Overrideable, used for IUsesRemaining UsesRemaining property
        /// </summary>
        /// <param name="list"></param>
        public virtual void AddUsesRemainingProperties(ObjectPropertyList list)
        {
        }

        /// <summary>
        ///     Overridable. Displays cliloc 1072788-1072789.
        /// </summary>
        public virtual void AddWeightProperty(ObjectPropertyList list)
        {
            if (DisplayWeight && Weight > 0)
            {
                int weight = PileWeight + TotalWeight;

                if (weight == 1)
                {
                    list.Add(1072788, weight.ToString()); //Weight: ~1_WEIGHT~ stone
                }
                else
                {
                    list.Add(1072789, weight.ToString()); //Weight: ~1_WEIGHT~ stones
                }
            }
        }

        /// <summary>
        ///     Overridable. Adds the "Quest Item" property to the given <see cref="ObjectPropertyList" />.
        /// </summary>
        public virtual void AddQuestItemProperty(ObjectPropertyList list)
        {
            list.Add(1072351); // Quest Item
        }

        /// <summary>
        ///     Overridable. Adds the "Locked Down & Secure" property to the given <see cref="ObjectPropertyList" />.
        /// </summary>
        public virtual void AddSecureProperty(ObjectPropertyList list)
        {
            list.Add(501644); // locked down & secure
        }

        /// <summary>
        ///     Overridable. Adds the "Locked Down" property to the given <see cref="ObjectPropertyList" />.
        /// </summary>
        public virtual void AddLockedDownProperty(ObjectPropertyList list)
        {
            list.Add(501643); // locked down
        }

        /// <summary>
        ///     Overridable. Adds the "Blessed for ~1_NAME~" property to the given <see cref="ObjectPropertyList" />.
        /// </summary>
        public virtual void AddBlessedForProperty(ObjectPropertyList list, Mobile m)
        {
            list.Add(1062203, "{0}", m.Name); // Blessed for ~1_NAME~
        }

        public virtual void AddItemSocketProperties(ObjectPropertyList list)
        {
            if (Sockets != null)
            {
                foreach (var socket in Sockets)
                {
                    socket.GetProperties(list);
                }
            }
        }

        public virtual void AddItemPowerProperties(ObjectPropertyList list)
        {
        }

        /// <summary>
        ///     Overridable. Fills an <see cref="ObjectPropertyList" /> with everything applicable. By default, this invokes
        ///     <see
        ///         cref="AddNameProperties" />
        ///     , then <see cref="Item.GetChildProperties">Item.GetChildProperties</see> or
        ///     <see
        ///         cref="Mobile.GetChildProperties">
        ///         Mobile.GetChildProperties
        ///     </see>
        ///     . This method should be overriden to add any custom properties.
        /// </summary>
        public virtual void GetProperties(ObjectPropertyList list)
        {
            AddNameProperties(list);

            AddItemSocketProperties(list);

            if (Spawner != null)
            {
                Spawner.GetSpawnProperties(this, list);
            }

            AddItemPowerProperties(list);
        }

        /// <summary>
        ///     Overridable. Event invoked when a child (<paramref name="item" />) is building it's <see cref="ObjectPropertyList" />. Recursively calls
        ///     <see
        ///         cref="Item.GetChildProperties">
        ///         Item.GetChildProperties
        ///     </see>
        ///     or <see cref="Mobile.GetChildProperties">Mobile.GetChildProperties</see>.
        /// </summary>
        public virtual void GetChildProperties(ObjectPropertyList list, Item item)
        {
            if (m_Parent is Item)
            {
                ((Item)m_Parent).GetChildProperties(list, item);
            }
            else if (m_Parent is Mobile)
            {
                ((Mobile)m_Parent).GetChildProperties(list, item);
            }
        }

        /// <summary>
        ///     Overridable. Event invoked when a child (<paramref name="item" />) is building it's Name
        ///     <see
        ///         cref="ObjectPropertyList" />
        ///     . Recursively calls <see cref="Item.GetChildProperties">Item.GetChildNameProperties</see> or
        ///     <see
        ///         cref="Mobile.GetChildProperties">
        ///         Mobile.GetChildNameProperties
        ///     </see>
        ///     .
        /// </summary>
        public virtual void GetChildNameProperties(ObjectPropertyList list, Item item)
        {
            if (m_Parent is Item)
            {
                ((Item)m_Parent).GetChildNameProperties(list, item);
            }
            else if (m_Parent is Mobile)
            {
                ((Mobile)m_Parent).GetChildNameProperties(list, item);
            }
        }

        public virtual bool IsChildVisibleTo(Mobile m, Item child)
        {
            return true;
        }

        public void Bounce(Mobile from)
        {
            if (m_Parent is Item)
            {
                ((Item)m_Parent).RemoveItem(this);
            }
            else if (m_Parent is Mobile)
            {
                ((Mobile)m_Parent).RemoveItem(this);
            }

            m_Parent = null;

            BounceInfo bounce = GetBounce();

            if (bounce != null)
            {
                var stack = bounce.m_ParentStack;

                if (stack is Item)
                {
                    var s = (Item)stack;

                    if (!s.Deleted)
                    {
                        if (s.IsAccessibleTo(from))
                        {
                            s.StackWith(from, this);
                        }
                    }
                }

                var parent = bounce.m_Parent;

                if (parent is Item && !((Item)parent).Deleted)
                {
                    Item p = (Item)parent;
                    var root = p.RootParent;

                    if (p.IsAccessibleTo(from) && (!(root is Mobile) || ((Mobile)root).CheckNonlocalDrop(from, this, p)))
                    {
                        Location = bounce.m_Location;

                        p.AddItem(this);
                    }
                    else
                    {
                        MoveToWorld(from.Location, from.Map);
                    }
                }
                else if (parent is Mobile && !((Mobile)parent).Deleted)
                {
                    if (!((Mobile)parent).EquipItem(this))
                    {
                        MoveToWorld(bounce.m_WorldLoc, bounce.m_Map);
                    }
                }
                else
                {
                    MoveToWorld(bounce.m_WorldLoc, bounce.m_Map);
                }

                ClearBounce();
            }
            else
            {
                MoveToWorld(from.Location, from.Map);
            }
        }

        /// <summary>
        ///     Overridable. Method checked to see if this item may be equiped while casting a spell. By default, this returns false. It is overriden on spellbook and spell channeling weapons or shields.
        /// </summary>
        /// <returns>True if it may, false if not.</returns>
        /// <example>
        ///     <code>
        /// 	public override bool AllowEquipedCast( Mobile from )
        /// 	{
        /// 		if ( from.Int &gt;= 100 )
        /// 			return true;
        /// 		
        /// 		return base.AllowEquipedCast( from );
        ///  }</code>
        ///     When placed in an Item script, the item may be cast when equiped if the <paramref name="from" /> has 100 or more intelligence. Otherwise, it will drop to their backpack.
        /// </example>
        public virtual bool AllowEquipedCast(Mobile from)
        {
            return false;
        }

        public virtual bool CheckConflictingLayer(Mobile m, Item item, Layer layer)
        {
            return (m_Layer == layer);
        }

        public virtual bool CanEquip(Mobile m)
        {
            return m_Layer != Layer.Invalid && m.FindItemOnLayer(m_Layer) == null && CheckEquip(m, true);
        }

        public virtual bool CheckEquip(Mobile m, bool message)
        {
            if (m == null || m.Deleted)
            {
                return false;
            }

            if (this == m.Mount || this == m.Backpack || this == m.FindBankNoCreate())
            {
                return true;
            }

            var e = new CheckEquipItemEventArgs(m, this, message);

            EventSink.InvokeCheckEquipItem(e);

            if (e.Item != this || e.Item.Deleted || e.Block)
            {
                return false;
            }

            if (m.AccessLevel < AccessLevel.GameMaster && BlessedFor != null && BlessedFor != m)
            {
                if (message)
                {
                    m.SendMessage("You do not own that item.");
                }

                return false;
            }

            return true;
        }

        public virtual void GetChildContextMenuEntries(Mobile from, List<ContextMenuEntry> list, Item item)
        {
            if (m_Parent is Item)
            {
                ((Item)m_Parent).GetChildContextMenuEntries(from, list, item);
            }
            else if (m_Parent is Mobile)
            {
                ((Mobile)m_Parent).GetChildContextMenuEntries(from, list, item);
            }
        }

        public virtual void GetContextMenuEntries(Mobile from, List<ContextMenuEntry> list)
        {
            if (m_Parent is Item)
            {
                ((Item)m_Parent).GetChildContextMenuEntries(from, list, this);
            }
            else if (m_Parent is Mobile)
            {
                ((Mobile)m_Parent).GetChildContextMenuEntries(from, list, this);
            }

            if (from.Region != null)
            {
                from.Region.GetContextMenuEntries(from, list, this);
            }

            if (Spawner != null)
            {
                Spawner.GetSpawnContextEntries(this, from, list);
            }
        }

        public virtual bool DisplayContextMenu(Mobile from)
        {
            return ContextMenu.Display(from, this);
        }

        public virtual bool VerifyMove(Mobile from)
        {
            return Movable;
        }

        public virtual void OnParentKill(Mobile target, Container corpse)
        { }

        public virtual DeathMoveResult OnParentDeath(Mobile parent)
        {
            if (!Movable)
            {
                return DeathMoveResult.RemainEquiped;
            }
            else if (parent.KeepsItemsOnDeath)
            {
                return DeathMoveResult.MoveToBackpack;
            }
            else if (CheckBlessed(parent))
            {
                return DeathMoveResult.MoveToBackpack;
            }
            else if (CheckNewbied() && parent.Kills < 5)
            {
                return DeathMoveResult.MoveToBackpack;
            }
            else if (parent.Player && Nontransferable)
            {
                return DeathMoveResult.MoveToBackpack;
            }
            else
            {
                return DeathMoveResult.MoveToCorpse;
            }
        }

        public virtual DeathMoveResult OnInventoryDeath(Mobile parent)
        {
            if (!Movable)
            {
                return DeathMoveResult.MoveToBackpack;
            }
            else if (parent.KeepsItemsOnDeath)
            {
                return DeathMoveResult.MoveToBackpack;
            }
            else if (CheckBlessed(parent))
            {
                return DeathMoveResult.MoveToBackpack;
            }
            else if (CheckNewbied() && parent.Kills < 5)
            {
                return DeathMoveResult.MoveToBackpack;
            }
            else if (parent.Player && Nontransferable)
            {
                return DeathMoveResult.MoveToBackpack;
            }
            else
            {
                return DeathMoveResult.MoveToCorpse;
            }
        }

        /// <summary>
        ///     Moves the Item to <paramref name="location" />. The Item does not change maps.
        /// </summary>
        public virtual void MoveToWorld(Point3D location)
        {
            MoveToWorld(location, m_Map);
        }

        public void LabelTo(Mobile to, int number)
        {
            to.Send(new MessageLocalized(m_Serial, m_ItemID, MessageType.Label, 0x3B2, 3, number, "", ""));
        }

        public void LabelTo(Mobile to, int number, string args)
        {
            to.Send(new MessageLocalized(m_Serial, m_ItemID, MessageType.Label, 0x3B2, 3, number, "", args));
        }

        public void LabelTo(Mobile to, string text)
        {
            to.Send(new UnicodeMessage(m_Serial, m_ItemID, MessageType.Label, 0x3B2, 3, "ENU", "", text));
        }

        public void LabelTo(Mobile to, string format, params object[] args)
        {
            LabelTo(to, String.Format(format, args));
        }

        public void LabelToAffix(Mobile to, int number, AffixType type, string affix)
        {
            to.Send(new MessageLocalizedAffix(m_Serial, m_ItemID, MessageType.Label, 0x3B2, 3, number, "", type, affix, ""));
        }

        public void LabelToAffix(Mobile to, int number, AffixType type, string affix, string args)
        {
            to.Send(new MessageLocalizedAffix(m_Serial, m_ItemID, MessageType.Label, 0x3B2, 3, number, "", type, affix, args));
        }

        public virtual void LabelLootTypeTo(Mobile to)
        {
            if (m_LootType == LootType.Blessed)
            {
                LabelTo(to, 1041362); // (blessed)
            }
            else if (m_LootType == LootType.Cursed)
            {
                LabelTo(to, "(cursed)");
            }
        }

        public bool AtWorldPoint(int x, int y)
        {
            return (m_Parent == null && m_Location.m_X == x && m_Location.m_Y == y);
        }

        public bool AtPoint(int x, int y)
        {
            return (m_Location.m_X == x && m_Location.m_Y == y);
        }

        /// <summary>
        ///     Moves the Item to a given <paramref name="location" /> and <paramref name="map" />.
        /// </summary>
        public void MoveToWorld(Point3D location, Map map)
        {
            if (Deleted)
            {
                return;
            }

            Point3D oldLocation = GetWorldLocation();
            Point3D oldRealLocation = m_Location;

            SetLastMoved();

            if (Parent is Mobile)
            {
                ((Mobile)Parent).RemoveItem(this);
            }
            else if (Parent is Item)
            {
                ((Item)Parent).RemoveItem(this);
            }

            if (m_Map != map)
            {
                Map old = m_Map;

                if (m_Map != null)
                {
                    m_Map.OnLeave(this);

                    if (oldLocation.m_X != 0)
                    {
                        var eable = m_Map.GetClientsInRange(oldLocation, Core.GlobalRadarRange - 4);

                        foreach (NetState state in eable)
                        {
                            Mobile m = state.Mobile;

                            if (Utility.InRange(oldLocation, m.Location, GetUpdateRange(m)))
                            {
                                state.Send(RemovePacket);
                            }
                        }

                        eable.Free();
                    }
                }

                m_Location = location;
                OnLocationChange(oldRealLocation);

                ReleaseWorldPackets();

                var items = LookupItems();

                if (items != null)
                {
                    for (int i = 0; i < items.Count; ++i)
                    {
                        items[i].Map = map;
                    }
                }

                m_Map = map;

                if (m_Map != null)
                {
                    m_Map.OnEnter(this);
                }

                OnMapChange();

                if (m_Map != null)
                {
                    var eable = m_Map.GetClientsInRange(m_Location, Core.GlobalRadarRange);

                    foreach (NetState state in eable)
                    {
                        Mobile m = state.Mobile;

                        if (m.CanSee(this) && Utility.InRange(m_Location, m.Location, GetUpdateRange(m)))
                        {
                            SendInfoTo(state);
                        }
                    }

                    eable.Free();
                }

                RemDelta(ItemDelta.Update);

                if (old == null || old == Map.Internal)
                {
                    InvalidateProperties();
                }
            }
            else if (m_Map != null)
            {
                IPooledEnumerable<NetState> eable;

                if (oldLocation.m_X != 0)
                {
                    eable = m_Map.GetClientsInRange(oldLocation, Core.GlobalMaxUpdateRange);

                    foreach (NetState state in eable)
                    {
                        Mobile m = state.Mobile;

                        if (!m.InUpdateRange(location))
                        {
                            state.Send(RemovePacket);
                        }
                    }

                    eable.Free();
                }

                Point3D oldInternalLocation = m_Location;

                m_Location = location;
                OnLocationChange(oldRealLocation);

                ReleaseWorldPackets();

                eable = m_Map.GetClientsInRange(m_Location, Core.GlobalMaxUpdateRange);

                foreach (NetState state in eable)
                {
                    Mobile m = state.Mobile;

                    if (m.CanSee(this) && m.InUpdateRange(m_Location))
                    {
                        SendInfoTo(state);
                    }
                }

                eable.Free();

                m_Map.OnMove(oldInternalLocation, this);

                RemDelta(ItemDelta.Update);
            }
            else
            {
                Map = map;
                Location = location;
            }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool HonestyItem { get; set; }

        /// <summary>
        ///     Has the item been deleted?
        /// </summary>
        public bool Deleted { get { return GetFlag(ImplFlag.Deleted); } }

        [CommandProperty(AccessLevel.GameMaster)]
        public LootType LootType
        {
            get { return m_LootType; }
            set
            {
                if (m_LootType != value)
                {
                    m_LootType = value;

                    if (DisplayLootType)
                    {
                        InvalidateProperties();
                    }
                }
            }
        }

        /// <summary>
        ///		If true the item should be considered an artifact
        /// </summary>
        [CommandProperty(AccessLevel.GameMaster)]
        public virtual bool IsArtifact
        {
            get { return this is IArtifact && ((IArtifact)this).ArtifactRarity > 0; }
        }

        private static TimeSpan m_DDT = TimeSpan.FromMinutes(Config.Get("General.DefaultItemDecayTime", 60));

        public static TimeSpan DefaultDecayTime { get { return m_DDT; } set { m_DDT = value; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public virtual int DecayMultiplier { get { return 1; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public virtual bool DefaultDecaySetting { get { return true; } }

        [CommandProperty(AccessLevel.Decorator)]
        public virtual TimeSpan DecayTime { get { return TimeSpan.FromMinutes(m_DDT.TotalMinutes * DecayMultiplier); } }

        [CommandProperty(AccessLevel.Decorator)]
        public virtual bool Decays
        {
            get
            {
                // TODO: Make item decay an option on the spawner
                return DefaultDecaySetting && Movable && Visible && !HonestyItem/* && Spawner == null*/;
            }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public TimeSpan TimeToDecay
        {
            get
            {
                return TimeSpan.FromMinutes((DecayTime - (DateTime.UtcNow - LastMoved)).TotalMinutes);
            }
        }

        public virtual bool OnDecay()
        {
            return (Decays && Parent == null && Map != Map.Internal && Region.Find(Location, Map).OnDecay(this));
        }

        public void SetLastMoved()
        {
            m_LastMovedTime = DateTime.UtcNow;
        }

        public DateTime LastMoved { get { return m_LastMovedTime; } set { m_LastMovedTime = value; } }

        public virtual bool StackIgnoreItemID { get { return false; } }
        public virtual bool StackIgnoreHue { get { return false; } }
        public virtual bool StackIgnoreName { get { return false; } }

        public bool StackWith(Mobile from, Item dropped)
        {
            return StackWith(from, dropped, true);
        }

        public virtual bool StackWith(Mobile from, Item dropped, bool playSound)
        {
            if (WillStack(from, dropped))
            {
                if (m_LootType != dropped.m_LootType)
                {
                    m_LootType = LootType.Regular;
                }

                Amount += dropped.Amount;
                dropped.Delete();

                if (playSound && from != null)
                {
                    int soundID = GetDropSound();

                    if (soundID == -1)
                    {
                        soundID = 0x42;
                    }

                    from.SendSound(soundID, GetWorldLocation());
                }

                return true;
            }

            return false;
        }

        public virtual bool WillStack(Mobile from, Item item)
        {
            if (item == this || item.GetType() != GetType())
            {
                return false;
            }

            if (!item.Stackable || !Stackable)
            {
                return false;
            }

            if (item.Nontransferable || Nontransferable)
            {
                return false;
            }

            if ((!item.StackIgnoreItemID || !StackIgnoreItemID) && item.ItemID != ItemID)
            {
                return false;
            }

            if ((!item.StackIgnoreHue || !StackIgnoreHue) && item.Hue != Hue)
            {
                return false;
            }

            if ((!item.StackIgnoreName || !StackIgnoreName) && item.Name != Name)
            {
                return false;
            }

            if (item.Amount + Amount > 60000)
            {
                return false;
            }

            if ((Sockets == null && item.Sockets != null) || (Sockets != null && item.Sockets == null))
            {
                return false;
            }
            else  if (Sockets != null && item.Sockets != null)
            {
                if (Sockets.Any(s => !item.HasSocket(s.GetType())))
                {
                    return false;
                }

                if (item.Sockets.Any(s => !HasSocket(s.GetType())))
                {
                    return false;
                }
            }

            return true;
        }

        public virtual bool OnDragDrop(Mobile from, Item dropped)
        {
            if (Parent is Container)
            {
                return ((Container)Parent).OnStackAttempt(from, this, dropped);
            }

            return StackWith(from, dropped);
        }

        public Rectangle2D GetGraphicBounds()
        {
            int itemID = m_ItemID;
            bool doubled = m_Amount > 1;

            if (itemID >= 0xEEA && itemID <= 0xEF2) // Are we coins?
            {
                int coinBase = (itemID - 0xEEA) / 3;
                coinBase *= 3;
                coinBase += 0xEEA;

                doubled = false;

                if (m_Amount <= 1)
                {
                    // A single coin
                    itemID = coinBase;
                }
                else if (m_Amount <= 5)
                {
                    // A stack of coins
                    itemID = coinBase + 1;
                }
                else // m_Amount > 5
                {
                    // A pile of coins
                    itemID = coinBase + 2;
                }
            }

            Rectangle2D bounds = ItemBounds.Table[itemID & 0x3FFF];

            if (doubled)
            {
                bounds.Set(bounds.X, bounds.Y, bounds.Width + 5, bounds.Height + 5);
            }

            return bounds;
        }

        [CommandProperty(AccessLevel.Decorator)]
        public bool Stackable { get { return GetFlag(ImplFlag.Stackable); } set { SetFlag(ImplFlag.Stackable, value); } }

        private readonly object _rpl = new object();

        public Packet RemovePacket
        {
            get
            {
                if (m_RemovePacket == null)
                {
                    lock (_rpl)
                    {
                        if (m_RemovePacket == null)
                        {
                            m_RemovePacket = new RemoveItem(this);
                            m_RemovePacket.SetStatic();
                        }
                    }
                }

                return m_RemovePacket;
            }
        }

        private readonly object _opll = new object();

        public Packet OPLPacket
        {
            get
            {
                if (m_OPLPacket == null)
                {
                    lock (_opll)
                    {
                        if (m_OPLPacket == null)
                        {
                            m_OPLPacket = new OPLInfo(PropertyList);
                            m_OPLPacket.SetStatic();
                        }
                    }
                }

                return m_OPLPacket;
            }
        }

        public ObjectPropertyList PropertyList
        {
            get
            {
                if (m_PropertyList == null)
                {
                    m_PropertyList = new ObjectPropertyList(this);

                    GetProperties(m_PropertyList);
                    AppendChildProperties(m_PropertyList);

                    m_PropertyList.Terminate();
                    m_PropertyList.SetStatic();
                }

                return m_PropertyList;
            }
        }

        public virtual void AppendChildProperties(ObjectPropertyList list)
        {
            if (m_Parent is Item)
            {
                ((Item)m_Parent).GetChildProperties(list, this);
            }
            else if (m_Parent is Mobile)
            {
                ((Mobile)m_Parent).GetChildProperties(list, this);
            }
        }

        public virtual void AppendChildNameProperties(ObjectPropertyList list)
        {
            if (m_Parent is Item)
            {
                ((Item)m_Parent).GetChildNameProperties(list, this);
            }
            else if (m_Parent is Mobile)
            {
                ((Mobile)m_Parent).GetChildNameProperties(list, this);
            }
        }

        public void ClearProperties()
        {
            Packet.Release(ref m_PropertyList);
            Packet.Release(ref m_OPLPacket);
        }

        public void InvalidateProperties()
        {
            if (!ObjectPropertyList.Enabled)
            {
                return;
            }

            if (m_Map != null && m_Map != Map.Internal && !World.Loading)
            {
                ObjectPropertyList oldList = m_PropertyList;
                m_PropertyList = null;
                ObjectPropertyList newList = PropertyList;

                if (oldList == null || oldList.Hash != newList.Hash)
                {
                    Packet.Release(ref m_OPLPacket);
                    Delta(ItemDelta.Properties);
                }
            }
            else
            {
                ClearProperties();
            }
        }

        private readonly object _wpl = new object();
        private readonly object _wplsa = new object();
        private readonly object _wplhs = new object();

        public Packet WorldPacket
        {
            get
            {
                // This needs to be invalidated when any of the following changes:
                //  - ItemID
                //  - Amount
                //  - Location
                //  - Hue
                //  - Packet Flags
                //  - Direction

                if (m_WorldPacket == null)
                {
                    lock (_wpl)
                    {
                        if (m_WorldPacket == null)
                        {
                            m_WorldPacket = new WorldItem(this);
                            m_WorldPacket.SetStatic();
                        }
                    }
                }

                return m_WorldPacket;
            }
        }

        public Packet WorldPacketSA
        {
            get
            {
                // This needs to be invalidated when any of the following changes:
                //  - ItemID
                //  - Amount
                //  - Location
                //  - Hue
                //  - Packet Flags
                //  - Direction

                if (m_WorldPacketSA == null)
                {
                    lock (_wplsa)
                    {
                        if (m_WorldPacketSA == null)
                        {
                            m_WorldPacketSA = new WorldItemSA(this);
                            m_WorldPacketSA.SetStatic();
                        }
                    }
                }

                return m_WorldPacketSA;
            }
        }

        public virtual Packet WorldPacketHS
        {
            get
            {
                // This needs to be invalidated when any of the following changes:
                //  - ItemID
                //  - Amount
                //  - Location
                //  - Hue
                //  - Packet Flags
                //  - Direction

                if (m_WorldPacketHS == null)
                {
                    lock (_wplhs)
                    {
                        if (m_WorldPacketHS == null)
                        {
                            m_WorldPacketHS = new WorldItemHS(this);
                            m_WorldPacketHS.SetStatic();
                        }
                    }
                }

                return m_WorldPacketHS;
            }
        }

        public virtual void ReleaseWorldPackets()
        {
            Packet.Release(ref m_WorldPacket);
            Packet.Release(ref m_WorldPacketSA);
            Packet.Release(ref m_WorldPacketHS);
        }

        [CommandProperty(AccessLevel.Decorator)]
        public bool Visible
        {
            get { return GetFlag(ImplFlag.Visible); }
            set
            {
                if (GetFlag(ImplFlag.Visible) != value)
                {
                    SetFlag(ImplFlag.Visible, value);
                    ReleaseWorldPackets();

                    if (m_Map != null && m_Map != Map.Internal)
                    {
                        Point3D worldLoc = GetWorldLocation();

                        var eable = m_Map.GetClientsInRange(worldLoc, Core.GlobalMaxUpdateRange);

                        foreach (NetState state in eable)
                        {
                            Mobile m = state.Mobile;

                            if (!m.CanSee(this) && m.InRange(worldLoc, GetUpdateRange(m)))
                            {
                                state.Send(RemovePacket);
                            }
                        }

                        eable.Free();
                    }

                    Delta(ItemDelta.Update);
                }
            }
        }

        [CommandProperty(AccessLevel.Decorator)]
        public bool Movable
        {
            get { return GetFlag(ImplFlag.Movable); }
            set
            {
                if (GetFlag(ImplFlag.Movable) != value)
                {
                    SetFlag(ImplFlag.Movable, value);
                    ReleaseWorldPackets();
                    Delta(ItemDelta.Update);
                }
            }
        }

        public virtual bool ForceShowProperties { get { return IsLockedDown || IsSecure; } }

        public virtual int GetPacketFlags()
        {
            int flags = 0;

            if (!Visible)
            {
                flags |= 0x80;
            }

            if (Movable || ForceShowProperties)
            {
                flags |= 0x20;
            }

            return flags;
        }

        public virtual bool OnMoveOff(Mobile m)
        {
            return true;
        }

        public virtual bool OnMoveOver(Mobile m)
        {
            return true;
        }

        public virtual bool HandlesOnMovement { get { return false; } }

        public virtual void OnMovement(Mobile m, Point3D oldLocation)
        { }

        public void Internalize()
        {
            MoveToWorld(Point3D.Zero, Map.Internal);
        }

        public virtual void OnMapChange()
        { }

        public virtual void OnRemoved(object parent)
        { }

        public virtual void OnAdded(object parent)
        { }

        [CommandProperty(AccessLevel.Counselor, AccessLevel.Decorator)]
        public Map Map
        {
            get { return m_Map; }
            set
            {
                if (m_Map != value)
                {
                    Map old = m_Map;

                    if (m_Map != null && m_Parent == null)
                    {
                        m_Map.OnLeave(this);
                        SendRemovePacket();
                    }

                    var items = LookupItems();

                    if (items != null)
                    {
                        for (int i = 0; i < items.Count; ++i)
                        {
                            items[i].Map = value;
                        }
                    }

                    m_Map = value;

                    if (m_Map != null && m_Parent == null)
                    {
                        m_Map.OnEnter(this);
                    }

                    Delta(ItemDelta.Update);

                    OnMapChange();

                    if (old == null || old == Map.Internal)
                    {
                        InvalidateProperties();
                    }
                }
            }
        }

        [Flags]
        private enum SaveFlag
        {
            None = 0x00000000,
            Direction = 0x00000001,
            Bounce = 0x00000002,
            LootType = 0x00000004,
            LocationFull = 0x00000008,
            ItemID = 0x00000010,
            Hue = 0x00000020,
            Amount = 0x00000040,
            Layer = 0x00000080,
            Name = 0x00000100,
            Parent = 0x00000200,
            Items = 0x00000400,
            WeightNot1or0 = 0x00000800,
            Map = 0x00001000,
            Visible = 0x00002000,
            Movable = 0x00004000,
            Stackable = 0x00008000,
            WeightIs0 = 0x00010000,
            LocationSByteZ = 0x00020000,
            LocationShortXY = 0x00040000,
            LocationByteXY = 0x00080000,
            ImplFlags = 0x00100000,
            InsuredFor = 0x00200000,
            BlessedFor = 0x00400000,
            HeldBy = 0x00800000,
            IntWeight = 0x01000000,
            SavedFlags = 0x02000000,
            NullWeight = 0x04000000,
            Light = 0x08000000
        }

        private static void SetSaveFlag(ref SaveFlag flags, SaveFlag toSet, bool setIf)
        {
            if (setIf)
            {
                flags |= toSet;
            }
        }

        private static bool GetSaveFlag(SaveFlag flags, SaveFlag toGet)
        {
            return ((flags & toGet) != 0);
        }

        int ISerializable.TypeReference { get { return m_TypeRef; } }

        int ISerializable.SerialIdentity { get { return m_Serial; } }

        public virtual void Serialize(GenericWriter writer)
        {
            writer.Write(14); // version

            // 14
            writer.Write(Sockets != null ? Sockets.Count : 0);
			
			if(Sockets != null)
			{
				foreach(var socket in Sockets)
				{
					ItemSocket.Save(socket, writer);
				}
			}

			// 13: Merge sync
            // 12: Light no longer backed by Direction

            // 11
            writer.Write(m_GridLocation);

            // 10: Honesty moved to ItemSockets

            // 9
            SaveFlag flags = SaveFlag.None;

            int x = m_Location.m_X, y = m_Location.m_Y, z = m_Location.m_Z;

            if (x != 0 || y != 0 || z != 0)
            {
                if (x >= short.MinValue && x <= short.MaxValue && y >= short.MinValue && y <= short.MaxValue && z >= sbyte.MinValue &&
                    z <= sbyte.MaxValue)
                {
                    if (x != 0 || y != 0)
                    {
                        if (x >= byte.MinValue && x <= byte.MaxValue && y >= byte.MinValue && y <= byte.MaxValue)
                        {
                            flags |= SaveFlag.LocationByteXY;
                        }
                        else
                        {
                            flags |= SaveFlag.LocationShortXY;
                        }
                    }

                    if (z != 0)
                    {
                        flags |= SaveFlag.LocationSByteZ;
                    }
                }
                else
                {
                    flags |= SaveFlag.LocationFull;
                }
            }

            var info = LookupCompactInfo();
            var items = LookupItems();

            if (m_Direction != Direction.North)
            {
                flags |= SaveFlag.Direction;
            }

            if (m_Light != 0)
            {
                flags |= SaveFlag.Light;
            }

            if (info != null && info.m_Bounce != null)
            {
                flags |= SaveFlag.Bounce;
            }

            if (m_LootType != LootType.Regular)
            {
                flags |= SaveFlag.LootType;
            }

            if (m_ItemID != 0)
            {
                flags |= SaveFlag.ItemID;
            }

            if (m_Hue != 0)
            {
                flags |= SaveFlag.Hue;
            }

            if (m_Amount != 1)
            {
                flags |= SaveFlag.Amount;
            }

            if (m_Layer != Layer.Invalid)
            {
                flags |= SaveFlag.Layer;
            }

            if (info != null && info.m_Name != null)
            {
                flags |= SaveFlag.Name;
            }

            if (m_Parent != null)
            {
                flags |= SaveFlag.Parent;
            }

            if (items != null && items.Count > 0)
            {
                flags |= SaveFlag.Items;
            }

            if (m_Map != Map.Internal)
            {
                flags |= SaveFlag.Map;
            }

            if (info != null && info.m_BlessedFor != null && !info.m_BlessedFor.Deleted)
            {
                flags |= SaveFlag.BlessedFor;
            }

            if (info != null && info.m_HeldBy != null && !info.m_HeldBy.Deleted)
            {
                flags |= SaveFlag.HeldBy;
            }

            if (info != null && info.m_SavedFlags != 0)
            {
                flags |= SaveFlag.SavedFlags;
            }

            if (info == null || info.m_Weight == -1)
            {
                flags |= SaveFlag.NullWeight;
            }
            else if (info.m_Weight == 0.0)
            {
                flags |= SaveFlag.WeightIs0;
            }
            else if (info.m_Weight != 1.0)
            {
                if (info.m_Weight == (int)info.m_Weight)
                {
                    flags |= SaveFlag.IntWeight;
                }
                else
                {
                    flags |= SaveFlag.WeightNot1or0;
                }
            }

            var implFlags = (m_Flags & (ImplFlag.Visible | ImplFlag.Movable | ImplFlag.Stackable | ImplFlag.Insured |
                                        ImplFlag.PayedInsurance | ImplFlag.QuestItem));

            if (implFlags != (ImplFlag.Visible | ImplFlag.Movable))
            {
                flags |= SaveFlag.ImplFlags;
            }

            writer.Write((int)flags);

            /* begin last moved time optimization */
            long ticks = m_LastMovedTime.Ticks;
            long now = DateTime.UtcNow.Ticks;

            TimeSpan d;

            try
            {
                d = new TimeSpan(ticks - now);
            }
            catch
            {
                if (ticks < now)
                {
                    d = TimeSpan.MaxValue;
                }
                else
                {
                    d = TimeSpan.MaxValue;
                }
            }

            double minutes = -d.TotalMinutes;

            if (minutes < int.MinValue)
            {
                minutes = int.MinValue;
            }
            else if (minutes > int.MaxValue)
            {
                minutes = int.MaxValue;
            }

            writer.WriteEncodedInt((int)minutes);
            /* end */

            if (GetSaveFlag(flags, SaveFlag.Direction))
            {
                writer.Write((byte)m_Direction);
            }

            if (GetSaveFlag(flags, SaveFlag.Light))
            {
                writer.Write((byte)m_Light);
            }

            if (GetSaveFlag(flags, SaveFlag.Bounce))
            {
                // ReSharper disable once PossibleNullReferenceException
                BounceInfo.Serialize(info.m_Bounce, writer);
            }

            if (GetSaveFlag(flags, SaveFlag.LootType))
            {
                writer.Write((byte)m_LootType);
            }

            if (GetSaveFlag(flags, SaveFlag.LocationFull))
            {
                writer.WriteEncodedInt(x);
                writer.WriteEncodedInt(y);
                writer.WriteEncodedInt(z);
            }
            else
            {
                if (GetSaveFlag(flags, SaveFlag.LocationByteXY))
                {
                    writer.Write((byte)x);
                    writer.Write((byte)y);
                }
                else if (GetSaveFlag(flags, SaveFlag.LocationShortXY))
                {
                    writer.Write((short)x);
                    writer.Write((short)y);
                }

                if (GetSaveFlag(flags, SaveFlag.LocationSByteZ))
                {
                    writer.Write((sbyte)z);
                }
            }

            if (GetSaveFlag(flags, SaveFlag.ItemID))
            {
                writer.WriteEncodedInt(m_ItemID);
            }

            if (GetSaveFlag(flags, SaveFlag.Hue))
            {
                writer.WriteEncodedInt(m_Hue);
            }

            if (GetSaveFlag(flags, SaveFlag.Amount))
            {
                writer.WriteEncodedInt(m_Amount);
            }

            if (GetSaveFlag(flags, SaveFlag.Layer))
            {
                writer.Write((byte)m_Layer);
            }

            if (GetSaveFlag(flags, SaveFlag.Name))
            {
                writer.Write(info.m_Name);
            }

            if (GetSaveFlag(flags, SaveFlag.Parent))
            {
                if (m_Parent is Mobile && !((Mobile)m_Parent).Deleted)
                {
                    writer.Write(((Mobile)m_Parent).Serial);
                }
                else if (m_Parent is Item && !((Item)m_Parent).Deleted)
                {
                    writer.Write(((Item)m_Parent).Serial);
                }
                else
                {
                    writer.Write(Serial.MinusOne);
                }
            }

            if (GetSaveFlag(flags, SaveFlag.Items))
            {
                writer.Write(items, false);
            }

            if (GetSaveFlag(flags, SaveFlag.IntWeight))
            {
                writer.WriteEncodedInt((int)info.m_Weight);
            }
            else if (GetSaveFlag(flags, SaveFlag.WeightNot1or0))
            {
                writer.Write(info.m_Weight);
            }

            if (GetSaveFlag(flags, SaveFlag.Map))
            {
                writer.Write(m_Map);
            }

            if (GetSaveFlag(flags, SaveFlag.ImplFlags))
            {
                writer.WriteEncodedInt((int)implFlags);
            }

            if (GetSaveFlag(flags, SaveFlag.InsuredFor))
            {
                writer.Write((Mobile)null);
            }

            if (GetSaveFlag(flags, SaveFlag.BlessedFor))
            {
                writer.Write(info.m_BlessedFor);
            }

            if (GetSaveFlag(flags, SaveFlag.HeldBy))
            {
                writer.Write(info.m_HeldBy);
            }

            if (GetSaveFlag(flags, SaveFlag.SavedFlags))
            {
                writer.WriteEncodedInt(info.m_SavedFlags);
            }
        }

        public IPooledEnumerable<IEntity> GetObjectsInRange(int range)
        {
            Map map = m_Map;

            if (map == null)
            {
                return Map.NullEnumerable<IEntity>.Instance;
            }

            if (m_Parent == null)
            {
                return map.GetObjectsInRange(m_Location, range);
            }

            return map.GetObjectsInRange(GetWorldLocation(), range);
        }

        public IPooledEnumerable<Item> GetItemsInRange(int range)
        {
            Map map = m_Map;

            if (map == null)
            {
                return Map.NullEnumerable<Item>.Instance;
            }

            if (m_Parent == null)
            {
                return map.GetItemsInRange(m_Location, range);
            }

            return map.GetItemsInRange(GetWorldLocation(), range);
        }

        public IPooledEnumerable<Mobile> GetMobilesInRange(int range)
        {
            Map map = m_Map;

            if (map == null)
            {
                return Map.NullEnumerable<Mobile>.Instance;
            }

            if (m_Parent == null)
            {
                return map.GetMobilesInRange(m_Location, range);
            }

            return map.GetMobilesInRange(GetWorldLocation(), range);
        }

        public IPooledEnumerable<NetState> GetClientsInRange(int range)
        {
            Map map = m_Map;

            if (map == null)
            {
                return Map.NullEnumerable<NetState>.Instance;
            }

            if (m_Parent == null)
            {
                return map.GetClientsInRange(m_Location, range);
            }

            return map.GetClientsInRange(GetWorldLocation(), range);
        }

        private static int m_LockedDownFlag;
        private static int m_SecureFlag;

        public static int LockedDownFlag { get { return m_LockedDownFlag; } set { m_LockedDownFlag = value; } }

        public static int SecureFlag { get { return m_SecureFlag; } set { m_SecureFlag = value; } }

        public bool IsLockedDown
        {
            get { return GetTempFlag(m_LockedDownFlag); }
            set
            {
                SetTempFlag(m_LockedDownFlag, value);
                InvalidateProperties();

                OnLockDownChange();
            }
        }

        public virtual void OnLockDownChange()
        {
        }

        public bool IsSecure
        {
            get { return GetTempFlag(m_SecureFlag); }
            set
            {
                SetTempFlag(m_SecureFlag, value);
                InvalidateProperties();

                OnSecureChange();
            }
        }

        public virtual void OnSecureChange()
        {
        }

        public bool GetTempFlag(int flag)
        {
            CompactInfo info = LookupCompactInfo();

            if (info == null)
            {
                return false;
            }

            return ((info.m_TempFlags & flag) != 0);
        }

        public void SetTempFlag(int flag, bool value)
        {
            CompactInfo info = AcquireCompactInfo();

            if (value)
            {
                info.m_TempFlags |= flag;
            }
            else
            {
                info.m_TempFlags &= ~flag;
            }

            if (info.m_TempFlags == 0)
            {
                VerifyCompactInfo();
            }
        }

        public bool GetSavedFlag(int flag)
        {
            CompactInfo info = LookupCompactInfo();

            if (info == null)
            {
                return false;
            }

            return ((info.m_SavedFlags & flag) != 0);
        }

        public void SetSavedFlag(int flag, bool value)
        {
            CompactInfo info = AcquireCompactInfo();

            if (value)
            {
                info.m_SavedFlags |= flag;
            }
            else
            {
                info.m_SavedFlags &= ~flag;
            }

            if (info.m_SavedFlags == 0)
            {
                VerifyCompactInfo();
            }
        }

        public virtual void Deserialize(GenericReader reader)
        {
            int version = reader.ReadInt();

            SetLastMoved();

            switch (version)
            {
                case 14:
                    var socketCount = reader.ReadInt();

                    for(int i = 0; i < socketCount; i++)
                    {
                        ItemSocket.Load(this, reader);
                    }

                    goto case 13;
				case 13:
                case 12:
                case 11:
                    m_GridLocation = reader.ReadByte();
                    goto case 10;
                case 10:
                    {
                        // Honesty removed to ItemSockets
                        if (version < 14)
                        {
                            reader.ReadDateTime();
                            reader.ReadBool();
                            reader.ReadMobile();
                            reader.ReadString();

                            HonestyItem = reader.ReadBool();
                        }

                        goto case 9;
                    }
                case 9:
                case 8:
                case 7:
                case 6:
                    {
                        SaveFlag flags = (SaveFlag)reader.ReadInt();

                        if (version < 7)
                        {
                            LastMoved = reader.ReadDeltaTime();
                        }
                        else
                        {
                            int minutes = reader.ReadEncodedInt();

                            try
                            {
                                LastMoved = DateTime.UtcNow - TimeSpan.FromMinutes(minutes);
                            }
                            catch
                            {
                                LastMoved = DateTime.UtcNow;
                            }
                        }

                        if (GetSaveFlag(flags, SaveFlag.Direction))
                        {
                            m_Direction = (Direction)reader.ReadByte();
                        }

                        if (GetSaveFlag(flags, SaveFlag.Light))
                        {
                            m_Light = (LightType)reader.ReadByte();
                        }
                        else if(version < 12)
                        {
                            m_Light = (LightType)m_Direction;
                        }

                        if (GetSaveFlag(flags, SaveFlag.Bounce))
                        {
                            AcquireCompactInfo().m_Bounce = BounceInfo.Deserialize(reader);
                        }

                        if (GetSaveFlag(flags, SaveFlag.LootType))
                        {
                            m_LootType = (LootType)reader.ReadByte();
                        }

                        int x = 0, y = 0, z = 0;

                        if (GetSaveFlag(flags, SaveFlag.LocationFull))
                        {
                            x = reader.ReadEncodedInt();
                            y = reader.ReadEncodedInt();
                            z = reader.ReadEncodedInt();
                        }
                        else
                        {
                            if (GetSaveFlag(flags, SaveFlag.LocationByteXY))
                            {
                                x = reader.ReadByte();
                                y = reader.ReadByte();
                            }
                            else if (GetSaveFlag(flags, SaveFlag.LocationShortXY))
                            {
                                x = reader.ReadShort();
                                y = reader.ReadShort();
                            }

                            if (GetSaveFlag(flags, SaveFlag.LocationSByteZ))
                            {
                                z = reader.ReadSByte();
                            }
                        }

                        m_Location = new Point3D(x, y, z);

                        if (GetSaveFlag(flags, SaveFlag.ItemID))
                        {
                            m_ItemID = reader.ReadEncodedInt();
                        }

                        if (GetSaveFlag(flags, SaveFlag.Hue))
                        {
                            m_Hue = reader.ReadEncodedInt();
                        }

                        if (GetSaveFlag(flags, SaveFlag.Amount))
                        {
                            m_Amount = reader.ReadEncodedInt();
                        }
                        else
                        {
                            m_Amount = 1;
                        }

                        if (GetSaveFlag(flags, SaveFlag.Layer))
                        {
                            m_Layer = (Layer)reader.ReadByte();
                        }

                        if (GetSaveFlag(flags, SaveFlag.Name))
                        {
                            string name = reader.ReadString();

                            if (name != DefaultName)
                            {
                                AcquireCompactInfo().m_Name = name;
                            }
                        }

                        if (GetSaveFlag(flags, SaveFlag.Parent))
                        {
                            Serial parent = reader.ReadInt();

                            if (parent.IsMobile)
                            {
                                m_Parent = World.FindMobile(parent);
                            }
                            else if (parent.IsItem)
                            {
                                m_Parent = World.FindItem(parent);
                            }
                            else
                            {
                                m_Parent = null;
                            }

                            if (m_Parent == null && (parent.IsMobile || parent.IsItem))
                            {
                                Delete();
                            }
                        }

                        if (GetSaveFlag(flags, SaveFlag.Items))
                        {
                            var items = reader.ReadStrongItemList();

                            if (this is Container)
                            {
                                (this as Container).m_Items = items;
                            }
                            else
                            {
                                AcquireCompactInfo().m_Items = items;
                            }
                        }

                        if (version < 8 || !GetSaveFlag(flags, SaveFlag.NullWeight))
                        {
                            double weight;

                            if (GetSaveFlag(flags, SaveFlag.IntWeight))
                            {
                                weight = reader.ReadEncodedInt();
                            }
                            else if (GetSaveFlag(flags, SaveFlag.WeightNot1or0))
                            {
                                weight = reader.ReadDouble();
                            }
                            else if (GetSaveFlag(flags, SaveFlag.WeightIs0))
                            {
                                weight = 0.0;
                            }
                            else
                            {
                                weight = 1.0;
                            }

                            if (weight != DefaultWeight)
                            {
                                AcquireCompactInfo().m_Weight = weight;
                            }
                        }

                        if (GetSaveFlag(flags, SaveFlag.Map))
                        {
                            m_Map = reader.ReadMap();
                        }
                        else
                        {
                            m_Map = Map.Internal;
                        }

                        if (GetSaveFlag(flags, SaveFlag.Visible))
                        {
                            SetFlag(ImplFlag.Visible, reader.ReadBool());
                        }
                        else
                        {
                            SetFlag(ImplFlag.Visible, true);
                        }

                        if (GetSaveFlag(flags, SaveFlag.Movable))
                        {
                            SetFlag(ImplFlag.Movable, reader.ReadBool());
                        }
                        else
                        {
                            SetFlag(ImplFlag.Movable, true);
                        }

                        if (GetSaveFlag(flags, SaveFlag.Stackable))
                        {
                            SetFlag(ImplFlag.Stackable, reader.ReadBool());
                        }

                        if (GetSaveFlag(flags, SaveFlag.ImplFlags))
                        {
                            m_Flags = (ImplFlag)reader.ReadEncodedInt();
                        }

                        if (GetSaveFlag(flags, SaveFlag.InsuredFor))
                        {
                            /*m_InsuredFor = */
                            reader.ReadMobile();
                        }

                        if (GetSaveFlag(flags, SaveFlag.BlessedFor))
                        {
                            AcquireCompactInfo().m_BlessedFor = reader.ReadMobile();
                        }

                        if (GetSaveFlag(flags, SaveFlag.HeldBy))
                        {
                            AcquireCompactInfo().m_HeldBy = reader.ReadMobile();
                        }

                        if (GetSaveFlag(flags, SaveFlag.SavedFlags))
                        {
                            AcquireCompactInfo().m_SavedFlags = reader.ReadEncodedInt();
                        }

                        if (m_Map != null && m_Parent == null)
                        {
                            m_Map.OnEnter(this);
                        }

                        break;
                    }
                case 5:
                    {
                        SaveFlag flags = (SaveFlag)reader.ReadInt();

                        LastMoved = reader.ReadDeltaTime();

                        if (GetSaveFlag(flags, SaveFlag.Direction))
                        {
                            m_Direction = (Direction)reader.ReadByte();
                        }

                        if (GetSaveFlag(flags, SaveFlag.Bounce))
                        {
                            AcquireCompactInfo().m_Bounce = BounceInfo.Deserialize(reader);
                        }

                        if (GetSaveFlag(flags, SaveFlag.LootType))
                        {
                            m_LootType = (LootType)reader.ReadByte();
                        }

                        if (GetSaveFlag(flags, SaveFlag.LocationFull))
                        {
                            m_Location = reader.ReadPoint3D();
                        }

                        if (GetSaveFlag(flags, SaveFlag.ItemID))
                        {
                            m_ItemID = reader.ReadInt();
                        }

                        if (GetSaveFlag(flags, SaveFlag.Hue))
                        {
                            m_Hue = reader.ReadInt();
                        }

                        if (GetSaveFlag(flags, SaveFlag.Amount))
                        {
                            m_Amount = reader.ReadInt();
                        }
                        else
                        {
                            m_Amount = 1;
                        }

                        if (GetSaveFlag(flags, SaveFlag.Layer))
                        {
                            m_Layer = (Layer)reader.ReadByte();
                        }

                        if (GetSaveFlag(flags, SaveFlag.Name))
                        {
                            string name = reader.ReadString();

                            if (name != DefaultName)
                            {
                                AcquireCompactInfo().m_Name = name;
                            }
                        }

                        if (GetSaveFlag(flags, SaveFlag.Parent))
                        {
                            Serial parent = reader.ReadInt();

                            if (parent.IsMobile)
                            {
                                m_Parent = World.FindMobile(parent);
                            }
                            else if (parent.IsItem)
                            {
                                m_Parent = World.FindItem(parent);
                            }
                            else
                            {
                                m_Parent = null;
                            }

                            if (m_Parent == null && (parent.IsMobile || parent.IsItem))
                            {
                                Delete();
                            }
                        }

                        if (GetSaveFlag(flags, SaveFlag.Items))
                        {
                            var items = reader.ReadStrongItemList();

                            if (this is Container)
                            {
                                (this as Container).m_Items = items;
                            }
                            else
                            {
                                AcquireCompactInfo().m_Items = items;
                            }
                        }

                        double weight;

                        if (GetSaveFlag(flags, SaveFlag.IntWeight))
                        {
                            weight = reader.ReadEncodedInt();
                        }
                        else if (GetSaveFlag(flags, SaveFlag.WeightNot1or0))
                        {
                            weight = reader.ReadDouble();
                        }
                        else if (GetSaveFlag(flags, SaveFlag.WeightIs0))
                        {
                            weight = 0.0;
                        }
                        else
                        {
                            weight = 1.0;
                        }

                        if (weight != DefaultWeight)
                        {
                            AcquireCompactInfo().m_Weight = weight;
                        }

                        if (GetSaveFlag(flags, SaveFlag.Map))
                        {
                            m_Map = reader.ReadMap();
                        }
                        else
                        {
                            m_Map = Map.Internal;
                        }

                        if (GetSaveFlag(flags, SaveFlag.Visible))
                        {
                            SetFlag(ImplFlag.Visible, reader.ReadBool());
                        }
                        else
                        {
                            SetFlag(ImplFlag.Visible, true);
                        }

                        if (GetSaveFlag(flags, SaveFlag.Movable))
                        {
                            SetFlag(ImplFlag.Movable, reader.ReadBool());
                        }
                        else
                        {
                            SetFlag(ImplFlag.Movable, true);
                        }

                        if (GetSaveFlag(flags, SaveFlag.Stackable))
                        {
                            SetFlag(ImplFlag.Stackable, reader.ReadBool());
                        }

                        if (m_Map != null && m_Parent == null)
                        {
                            m_Map.OnEnter(this);
                        }

                        break;
                    }
                case 4: // Just removed variables
                case 3:
                    {
                        m_Direction = (Direction)reader.ReadInt();

                        goto case 2;
                    }
                case 2:
                    {
                        AcquireCompactInfo().m_Bounce = BounceInfo.Deserialize(reader);
                        LastMoved = reader.ReadDeltaTime();

                        goto case 1;
                    }
                case 1:
                    {
                        m_LootType = (LootType)reader.ReadByte(); //m_Newbied = reader.ReadBool();

                        goto case 0;
                    }
                case 0:
                    {
                        m_Location = reader.ReadPoint3D();
                        m_ItemID = reader.ReadInt();
                        m_Hue = reader.ReadInt();
                        m_Amount = reader.ReadInt();
                        m_Layer = (Layer)reader.ReadByte();

                        string name = reader.ReadString();

                        if (name != DefaultName)
                        {
                            AcquireCompactInfo().m_Name = name;
                        }

                        Serial parent = reader.ReadInt();

                        if (parent.IsMobile)
                        {
                            m_Parent = World.FindMobile(parent);
                        }
                        else if (parent.IsItem)
                        {
                            m_Parent = World.FindItem(parent);
                        }
                        else
                        {
                            m_Parent = null;
                        }

                        if (m_Parent == null && (parent.IsMobile || parent.IsItem))
                        {
                            Delete();
                        }

                        int count = reader.ReadInt();

                        if (count > 0)
                        {
                            var items = new List<Item>(count);

                            for (int i = 0; i < count; ++i)
                            {
                                Item item = reader.ReadItem();

                                if (item != null)
                                {
                                    items.Add(item);
                                }
                            }

                            if (this is Container)
                            {
                                (this as Container).m_Items = items;
                            }
                            else
                            {
                                AcquireCompactInfo().m_Items = items;
                            }
                        }

                        double weight = reader.ReadDouble();

                        if (weight != DefaultWeight)
                        {
                            AcquireCompactInfo().m_Weight = weight;
                        }

                        if (version <= 3)
                        {
                            reader.ReadInt();
                            reader.ReadInt();
                            reader.ReadInt();
                        }

                        m_Map = reader.ReadMap();
                        SetFlag(ImplFlag.Visible, reader.ReadBool());
                        SetFlag(ImplFlag.Movable, reader.ReadBool());

                        if (version <= 3)
                        {
                            /*m_Deleted =*/
                            reader.ReadBool();
                        }

                        Stackable = reader.ReadBool();

                        if (m_Map != null && m_Parent == null)
                        {
                            m_Map.OnEnter(this);
                        }

                        break;
                    }
            }

            if (HeldBy != null)
            {
                Timer.DelayCall(TimeSpan.Zero, FixHolding_Sandbox);
            }

            VerifyCompactInfo();

            UpdateLight();
        }

        private void FixHolding_Sandbox()
        {
            Mobile heldBy = HeldBy;

            if (heldBy != null)
            {
                if (GetBounce() != null)
                {
                    Bounce(heldBy);
                }
                else
                {
                    heldBy.Holding = null;
                    heldBy.AddToBackpack(this);
                    ClearBounce();
                }
            }
        }

        public virtual int GetMaxUpdateRange()
        {
            return Core.GlobalMaxUpdateRange;
        }

        public virtual int GetUpdateRange(Mobile m)
        {
            return m.NetState == null ? Core.GlobalUpdateRange : m.NetState.UpdateRange;
        }

        public void SendInfoTo(NetState state)
        {
            SendInfoTo(state, state.Mobile != null && state.Mobile.ViewOPL);
        }

        public virtual void SendInfoTo(NetState state, bool sendOplPacket)
        {
            state.Send(GetWorldPacketFor(state));

            if (sendOplPacket)
            {
                state.Send(OPLPacket);
            }
        }
        
        protected virtual Packet GetWorldPacketFor(NetState state)
        {
            if (state.HighSeas)
            {
                return WorldPacketHS;
            }
            else if (state.StygianAbyss)
            {
                return WorldPacketSA;
            }
            else
            {
                return WorldPacket;
            }
        }

        public virtual bool IsVirtualItem { get { return false; } }

        public virtual int GetTotal(TotalType type)
        {
            return 0;
        }

        public virtual void UpdateTotal(Item sender, TotalType type, int delta)
        {
            if (!IsVirtualItem)
            {
                if (m_Parent is Item)
                {
                    (m_Parent as Item).UpdateTotal(sender, type, delta);
                }
                else if (m_Parent is Mobile)
                {
                    (m_Parent as Mobile).UpdateTotal(sender, type, delta);
                }
                else if (HeldBy != null)
                {
                    (HeldBy).UpdateTotal(sender, type, delta);
                }
            }
        }

        public virtual void UpdateTotals()
        { }

        public virtual int LabelNumber
        {
            get
            {
                if (m_ItemID < 0x4000)
                {
                    return 1020000 + m_ItemID;
                }
                else
                {
                    return 1078872 + m_ItemID;
                }
            }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public int TotalGold { get { return GetTotal(TotalType.Gold); } }

        [CommandProperty(AccessLevel.GameMaster)]
        public int TotalItems { get { return GetTotal(TotalType.Items); } }

        [CommandProperty(AccessLevel.GameMaster)]
        public int TotalWeight { get { return GetTotal(TotalType.Weight); } }

        public virtual double DefaultWeight
        {
            get
            {
                if (m_ItemID < 0 || m_ItemID > TileData.MaxItemValue || this is BaseMulti)
                {
                    return 0;
                }

                int weight = TileData.ItemTable[m_ItemID].Weight;

                if (weight == 255 || weight == 0)
                {
                    weight = 1;
                }

                return weight;
            }
        }

        [CommandProperty(AccessLevel.Counselor, AccessLevel.GameMaster)]
        public double Weight
        {
            get
            {
                CompactInfo info = LookupCompactInfo();

                if (info != null && info.m_Weight != -1)
                {
                    return info.m_Weight;
                }

                return DefaultWeight;
            }
            set
            {
                if (Weight != value)
                {
                    CompactInfo info = AcquireCompactInfo();

                    int oldPileWeight = PileWeight;

                    info.m_Weight = value;

                    if (info.m_Weight == -1)
                    {
                        VerifyCompactInfo();
                    }

                    int newPileWeight = PileWeight;

                    UpdateTotal(this, TotalType.Weight, newPileWeight - oldPileWeight);

                    InvalidateProperties();
                }
            }
        }

        [CommandProperty(AccessLevel.Counselor, AccessLevel.GameMaster)]
        public int PileWeight { get { return (int)Math.Ceiling(Weight * Amount); } }

        public virtual int HuedItemID { get { return m_ItemID; } }

        [Hue, CommandProperty(AccessLevel.GameMaster)]
        public virtual int Hue
        {
            get { return m_Hue; }
            set
            {
                if (m_Hue != value)
                {
                    m_Hue = value;
                    ReleaseWorldPackets();

                    Delta(ItemDelta.Update);
                }
            }
        }

        public virtual bool HiddenQuestItemHue { get; set; }

        public int QuestItemHue { get { return (HiddenQuestItemHue ? Hue : 0x04EA); } }

        public virtual bool Nontransferable { get { return QuestItem; } }

        public virtual void HandleInvalidTransfer(Mobile from)
        {
            // OSI sends 1074769, bug!
            if (QuestItem)
            {
                from.SendLocalizedMessage(1049343);
                // You can only drop quest items into the top-most level of your backpack while you still need them for your quest.
            }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public virtual Layer Layer
        {
            get { return m_Layer; }
            set
            {
                if (m_Layer != value)
                {
                    m_Layer = value;

                    Delta(ItemDelta.EquipOnly);
                }
            }
        }

        public List<Item> Items
        {
            get
            {
                var items = LookupItems();

                if (items == null)
                {
                    items = EmptyItems;
                }

                return items;
            }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public object RootParent
        {
            get
            {
                var p = m_Parent;

                while (p is Item)
                {
                    Item item = (Item)p;

                    if (item.m_Parent == null)
                    {
                        break;
                    }
                    else
                    {
                        p = item.m_Parent;
                    }
                }

                return p;
            }
        }

        public bool ParentsContain<T>() where T : Item
        {
            var p = m_Parent;

            while (p is Item)
            {
                if (p is T)
                {
                    return true;
                }

                Item item = (Item)p;

                if (item.m_Parent == null)
                {
                    break;
                }
                else
                {
                    p = item.m_Parent;
                }
            }

            return false;
        }

        public virtual void AddItem(Item item)
        {
            if (item == null || item.Deleted || item.m_Parent == this)
            {
                return;
            }
            else if (item == this)
            {
                Console.WriteLine(
                    "Warning: Adding item to itself: [0x{0:X} {1}].AddItem( [0x{2:X} {3}] )",
                    Serial.Value,
                    GetType().Name,
                    item.Serial.Value,
                    item.GetType().Name);
                Console.WriteLine(new StackTrace());
                return;
            }
            else if (IsChildOf(item))
            {
                Console.WriteLine(
                    "Warning: Adding parent item to child: [0x{0:X} {1}].AddItem( [0x{2:X} {3}] )",
                    Serial.Value,
                    GetType().Name,
                    item.Serial.Value,
                    item.GetType().Name);
                Console.WriteLine(new StackTrace());
                return;
            }
            else if (item.m_Parent is Mobile)
            {
                ((Mobile)item.m_Parent).RemoveItem(item);
            }
            else if (item.m_Parent is Item)
            {
                ((Item)item.m_Parent).RemoveItem(item);
            }
            else
            {
                item.SendRemovePacket();
            }

            item.Parent = this;
            item.Map = m_Map;

            var items = AcquireItems();

            items.Add(item);

            if (!item.IsVirtualItem)
            {
                UpdateTotal(item, TotalType.Gold, item.TotalGold);
                UpdateTotal(item, TotalType.Items, item.TotalItems + 1);
                UpdateTotal(item, TotalType.Weight, item.TotalWeight + item.PileWeight);
            }

            item.Delta(ItemDelta.Update);

            item.OnAdded(this);
            OnItemAdded(item);
        }

        private static readonly List<Item> m_DeltaQueue = new List<Item>();

        public void Delta(ItemDelta flags)
        {
            if (m_Map == null || m_Map == Map.Internal)
            {
                return;
            }

            m_DeltaFlags |= flags;

            if (!GetFlag(ImplFlag.InQueue))
            {
                SetFlag(ImplFlag.InQueue, true);

                m_DeltaQueue.Add(this);
            }

            Core.Set();
        }

        public void RemDelta(ItemDelta flags)
        {
            m_DeltaFlags &= ~flags;

            if (GetFlag(ImplFlag.InQueue) && m_DeltaFlags == ItemDelta.None)
            {
                SetFlag(ImplFlag.InQueue, false);

                m_DeltaQueue.Remove(this);
            }
        }

        private bool m_NoMoveHS;

        public bool NoMoveHS { get { return m_NoMoveHS; } set { m_NoMoveHS = value; } }

        public void ProcessDelta()
        {
            ItemDelta flags = m_DeltaFlags;

            SetFlag(ImplFlag.InQueue, false);
            m_DeltaFlags = ItemDelta.None;

            Map map = m_Map;

            if (map != null && !Deleted)
            {
                bool sendOPLUpdate = ObjectPropertyList.Enabled && (flags & ItemDelta.Properties) != 0;

                Container contParent = m_Parent as Container;

                if (contParent != null && !contParent.IsPublicContainer)
                {
                    if ((flags & ItemDelta.Update) != 0)
                    {
                        Point3D worldLoc = GetWorldLocation();

                        Mobile rootParent = contParent.RootParent as Mobile;
                        Mobile tradeRecip = null;

                        if (rootParent != null)
                        {
                            NetState ns = rootParent.NetState;

                            if (ns != null)
                            {
                                if (rootParent.CanSee(this) && rootParent.InRange(worldLoc, GetUpdateRange(rootParent)))
                                {
                                    if (ns.ContainerGridLines)
                                    {
                                        ns.Send(new ContainerContentUpdate6017(this));
                                    }
                                    else
                                    {
                                        ns.Send(new ContainerContentUpdate(this));
                                    }

                                    if (rootParent.ViewOPL)
                                    {
                                        ns.Send(OPLPacket);
                                    }
                                }
                            }
                        }

                        SecureTradeContainer stc = GetSecureTradeCont();

                        if (stc != null)
                        {
                            SecureTrade st = stc.Trade;

                            if (st != null)
                            {
                                Mobile test = st.From.Mobile;

                                if (test != null && test != rootParent)
                                {
                                    tradeRecip = test;
                                }

                                test = st.To.Mobile;

                                if (test != null && test != rootParent)
                                {
                                    tradeRecip = test;
                                }

                                if (tradeRecip != null)
                                {
                                    NetState ns = tradeRecip.NetState;

                                    if (ns != null)
                                    {
                                        if (tradeRecip.CanSee(this) && tradeRecip.InRange(worldLoc, GetUpdateRange(tradeRecip)))
                                        {
                                            if (ns.ContainerGridLines)
                                            {
                                                ns.Send(new ContainerContentUpdate6017(this));
                                            }
                                            else
                                            {
                                                ns.Send(new ContainerContentUpdate(this));
                                            }

                                            if (tradeRecip.ViewOPL)
                                            {
                                                ns.Send(OPLPacket);
                                            }
                                        }
                                    }
                                }
                            }
                        }

                        var openers = contParent.Openers;

                        if (openers != null)
                        {
                            lock (openers)
                            {
                                for (int i = 0; i < openers.Count; ++i)
                                {
                                    Mobile mob = openers[i];

                                    int range = GetUpdateRange(mob);

                                    if (mob.Map != map || !mob.InRange(worldLoc, range))
                                    {
                                        openers.RemoveAt(i--);
                                    }
                                    else
                                    {
                                        if (mob == rootParent || mob == tradeRecip)
                                        {
                                            continue;
                                        }

                                        NetState ns = mob.NetState;

                                        if (ns != null && ns.Seeded)
                                        {
                                            if (mob.CanSee(this))
                                            {
                                                if (ns.ContainerGridLines)
                                                {
                                                    ns.Send(new ContainerContentUpdate6017(this));
                                                }
                                                else
                                                {
                                                    ns.Send(new ContainerContentUpdate(this));
                                                }

                                                if (mob.ViewOPL)
                                                {
                                                    ns.Send(OPLPacket);
                                                }
                                            }
                                        }
                                    }
                                }

                                if (openers.Count == 0)
                                {
                                    contParent.Openers = null;
                                }
                            }
                        }
                        return;
                    }
                }

                if ((flags & ItemDelta.Update) != 0)
                {
                    Packet p = null;
                    Point3D worldLoc = GetWorldLocation();

                    var eable = map.GetClientsInRange(worldLoc, Core.GlobalMaxUpdateRange);

                    foreach (NetState state in eable)
                    {
                        Mobile m = state.Mobile;

                        if (m.CanSee(this) && m.InUpdateRange(worldLoc))
                        {
                            if (m_Parent == null)
                            {
                                SendInfoTo(state);
                            }
                            else
                            {
                                if (p == null)
                                {
                                    if (m_Parent is Item)
                                    {
                                        if (state.ContainerGridLines)
                                        {
                                            state.Send(new ContainerContentUpdate6017(this));
                                        }
                                        else
                                        {
                                            state.Send(new ContainerContentUpdate(this));
                                        }
                                    }
                                    else if (m_Parent is Mobile)
                                    {
                                        p = new EquipUpdate(this);
                                        p.Acquire();

                                        state.Send(p);
                                    }
                                }
                                else
                                {
                                    state.Send(p);
                                }

                                if (m.ViewOPL)
                                {
                                    state.Send(OPLPacket);
                                }
                            }
                        }
                    }

                    if (p != null)
                    {
                        Packet.Release(p);
                    }

                    eable.Free();
                    sendOPLUpdate = false;
                }
                else if ((flags & ItemDelta.EquipOnly) != 0)
                {
                    if (m_Parent is Mobile)
                    {
                        Packet p = null;
                        Point3D worldLoc = GetWorldLocation();

                        var eable = map.GetClientsInRange(worldLoc, Core.GlobalMaxUpdateRange);

                        foreach (NetState state in eable)
                        {
                            Mobile m = state.Mobile;

                            if (m.CanSee(this) && m.InUpdateRange(worldLoc))
                            {
                                //if ( sendOPLUpdate )
                                //	state.Send( RemovePacket );

                                if (p == null)
                                {
                                    p = Packet.Acquire(new EquipUpdate(this));
                                }

                                state.Send(p);

                                if (m.ViewOPL)
                                {
                                    state.Send(OPLPacket);
                                }
                            }
                        }

                        Packet.Release(p);

                        eable.Free();
                        sendOPLUpdate = false;
                    }
                }

                if (sendOPLUpdate)
                {
                    Point3D worldLoc = GetWorldLocation();
                    var eable = map.GetClientsInRange(worldLoc, Core.GlobalMaxUpdateRange);

                    foreach (NetState state in eable)
                    {
                        Mobile m = state.Mobile;

                        if (m.CanSee(this) && m.InUpdateRange(worldLoc))
                        {
                            state.Send(OPLPacket);
                        }
                    }

                    eable.Free();
                }
            }
        }

        private static bool _Processing;

        public static void ProcessDeltaQueue()
        {
            if (_Processing)
            {
                return;
            }

            _Processing = true;

            var i = m_DeltaQueue.Count;

            while (--i >= 0)
            {
                if (i < m_DeltaQueue.Count)
                {
                    m_DeltaQueue[i].ProcessDelta();
                    m_DeltaQueue.RemoveAt(i);
                }
            }

            _Processing = false;
        }

        public virtual void OnDelete()
        {
            if (Spawner != null)
            {
                Spawner.Remove(this);
                Spawner = null;
            }

            var region = Region.Find(GetWorldLocation(), Map);

            if (region != null)
            {
                region.OnDelete(this);
            }
        }

        public virtual void OnParentDeleted(object parent)
        {
            Delete();
        }

        public virtual void FreeCache()
        {
            ReleaseWorldPackets();
            Packet.Release(ref m_RemovePacket);
            Packet.Release(ref m_OPLPacket);
            Packet.Release(ref m_PropertyList);
        }

        public virtual void Delete()
        {
            if (Deleted)
            {
                return;
            }
            else if (!World.OnDelete(this))
            {
                return;
            }

            OnDelete();

            var items = LookupItems();

            if (items != null)
            {
                for (int i = items.Count - 1; i >= 0; --i)
                {
                    if (i < items.Count)
                    {
                        items[i].OnParentDeleted(this);
                    }
                }
            }

            SendRemovePacket();

            SetFlag(ImplFlag.Deleted, true);

            if (Parent is Mobile)
            {
                ((Mobile)Parent).RemoveItem(this);
            }
            else if (Parent is Item)
            {
                ((Item)Parent).RemoveItem(this);
            }

            ClearBounce();

            if (m_Map != null)
            {
                if (m_Parent == null)
                {
                    m_Map.OnLeave(this);
                }
                m_Map = null;
            }

            World.RemoveItem(this);

            OnAfterDelete();

            FreeCache();
        }

        public void PublicOverheadMessage(MessageType type, int hue, bool ascii, string text)
        {
            if (m_Map != null)
            {
                Packet p = null;
                Point3D worldLoc = GetWorldLocation();

                var eable = m_Map.GetClientsInRange(worldLoc, Core.GlobalMaxUpdateRange);

                foreach (NetState state in eable)
                {
                    Mobile m = state.Mobile;

                    if (m.CanSee(this) && m.InUpdateRange(worldLoc))
                    {
                        if (p == null)
                        {
                            if (ascii)
                            {
                                p = new AsciiMessage(m_Serial, m_ItemID, type, hue, 3, Name, text);
                            }
                            else
                            {
                                p = new UnicodeMessage(m_Serial, m_ItemID, type, hue, 3, "ENU", Name, text);
                            }

                            p.Acquire();
                        }

                        state.Send(p);
                    }
                }

                Packet.Release(p);

                eable.Free();
            }
        }

        public void PublicOverheadMessage(MessageType type, int hue, int number)
        {
            PublicOverheadMessage(type, hue, number, "");
        }

        public void PublicOverheadMessage(MessageType type, int hue, int number, string args)
        {
            if (m_Map != null)
            {
                Packet p = null;
                Point3D worldLoc = GetWorldLocation();

                var eable = m_Map.GetClientsInRange(worldLoc, Core.GlobalMaxUpdateRange);

                foreach (NetState state in eable)
                {
                    Mobile m = state.Mobile;

                    if (m.CanSee(this) && m.InUpdateRange(worldLoc))
                    {
                        if (p == null)
                        {
                            p = Packet.Acquire(new MessageLocalized(m_Serial, m_ItemID, type, hue, 3, number, Name, args));
                        }

                        state.Send(p);
                    }
                }

                Packet.Release(p);

                eable.Free();
            }
        }

        public void PrivateOverheadMessage(MessageType type, int hue, int number, NetState state, string args = "")
        {
            if (Map != null && state != null)
            {
                Packet p = null;
                Point3D worldLoc = GetWorldLocation();

                Mobile m = state.Mobile;

                if (m != null && m.CanSee(this) && m.InRange(worldLoc, GetUpdateRange(m)))
                {
                    if (p == null)
                        p = Packet.Acquire(new MessageLocalized(m_Serial, m_ItemID, type, hue, 3, number, Name, args));

                    state.Send(p);
                }

                Packet.Release(p);
            }
        }

        public void PrivateOverheadMessage(MessageType type, int hue, bool ascii, string text, NetState state)
        {
            if (Map != null && state != null)
            {
                Point3D worldLoc = GetWorldLocation();
                Mobile m = state.Mobile;

                Packet asciip = null;
                Packet p = null;

                if (m != null && m.CanSee(this) && m.InRange(worldLoc, GetUpdateRange(m)))
                {
                    if (ascii)
                    {
                        if (asciip == null)
                            asciip = Packet.Acquire(new AsciiMessage(m_Serial, m_ItemID, type, hue, 3, Name, text));

                        state.Send(asciip);
                    }
                    else
                    {
                        if (p == null)
                            p = Packet.Acquire(new UnicodeMessage(m_Serial, m_ItemID, type, hue, 3, m.Language, Name, text));

                        state.Send(p);
                    }
                }

                Packet.Release(asciip);
                Packet.Release(p);
            }
        }

        public Region GetRegion()
        {
            return Region.Find(GetWorldLocation(), Map);
        }

        public double GetDistanceToSqrt(IPoint3D p)
        {
            Point3D loc = GetWorldLocation();

            int xDelta = loc.X - p.X;
            int yDelta = loc.Y - p.Y;

            return Math.Sqrt((xDelta * xDelta) + (yDelta * yDelta));
        }

        public bool InRange(IPoint3D p, int range)
        {
            Point3D loc = GetWorldLocation();

            return (p.X >= (loc.X - range))
                && (p.X <= (loc.X + range))
                && (p.Y >= (loc.Y - range))
                && (p.Y <= (loc.Y + range));
        }

        public bool InLOS(Point3D target)
        {
            if (Deleted || Map == null || Parent != null)
                return false;

            return Map.LineOfSight(this, target);
        }

        public virtual void OnAfterDelete()
        {
            foreach (BaseModule module in World.GetModules(this))
            {
                module.Delete();
            }

            if(Sockets != null)
            {
                Sockets.IterateReverse(socket =>
                {
                    socket.Remove();
                });
            }

            Timer.DelayCall(EventSink.InvokeItemDeleted, new ItemDeletedEventArgs(this));
        }

        public virtual void RemoveItem(Item item)
        {
            var items = LookupItems();

            if (items != null && items.Remove(item))
            {
                item.SendRemovePacket();

                if (!item.IsVirtualItem)
                {
                    UpdateTotal(item, TotalType.Gold, -item.TotalGold);
                    UpdateTotal(item, TotalType.Items, -(item.TotalItems + 1));
                    UpdateTotal(item, TotalType.Weight, -(item.TotalWeight + item.PileWeight));
                }

                item.Parent = null;

                item.OnRemoved(this);
                OnItemRemoved(item);
            }
        }

        public virtual void OnAfterDuped(Item newItem)
        {
            if (Sockets != null)
            {
                for (int i = 0; i < Sockets.Count; i++)
                {
                    Sockets[i].OnOwnerDuped(newItem);
                }
            }
        }

        public virtual bool OnDragLift(Mobile from)
        {
            return true;
        }

        public virtual bool OnEquip(Mobile from)
        {
            return true;
        }

        public ISpawner Spawner
        {
            get
            {
                CompactInfo info = LookupCompactInfo();

                if (info != null)
                {
                    return info.m_Spawner;
                }

                return null;
            }
            set
            {
                CompactInfo info = AcquireCompactInfo();

                info.m_Spawner = value;

                if (info.m_Spawner == null)
                {
                    VerifyCompactInfo();
                }
            }
        }

        public virtual void OnBeforeSpawn(Point3D location, Map m)
        { }

        public virtual void OnAfterSpawn()
        { }

        public virtual int PhysicalResistance { get { return 0; } }
        public virtual int FireResistance { get { return 0; } }
        public virtual int ColdResistance { get { return 0; } }
        public virtual int PoisonResistance { get { return 0; } }
        public virtual int EnergyResistance { get { return 0; } }

        [CommandProperty(AccessLevel.Counselor)]
        public Serial Serial { get { return m_Serial; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public IEntity ParentEntity { get { return Parent as IEntity; } }

        [CommandProperty(AccessLevel.GameMaster)]
        public IEntity RootParentEntity { get { return RootParent as IEntity; } }

        #region Location Location Location!
        public virtual void OnLocationChange(Point3D oldLocation)
        { 
            var items = Items;

            if (items == null)
            {
                return;
            }

            var i = items.Count;

            while (--i >= 0)
            {
                if (i >= items.Count)
                {
                    continue;
                }

                var o = items[i];

                if (o != null)
                {
                    o.OnParentLocationChange(oldLocation);
                }
            }
		}

        public virtual void OnParentLocationChange(Point3D oldLocation)
        { }

        [CommandProperty(AccessLevel.Counselor, AccessLevel.Decorator)]
        public virtual Point3D Location
        {
            get { return m_Location; }
            set
            {
                Point3D oldLocation = m_Location;

                if (oldLocation != value)
                {
                    if (m_Map != null)
                    {
                        if (m_Parent == null)
                        {
                            IPooledEnumerable<NetState> eable;

                            if (m_Location.m_X != 0)
                            {
                                eable = m_Map.GetClientsInRange(oldLocation, Core.GlobalMaxUpdateRange);

                                foreach (NetState state in eable)
                                {
                                    Mobile m = state.Mobile;

                                    if (!m.InUpdateRange(value))
                                    {
                                        state.Send(RemovePacket);
                                    }
                                }

                                eable.Free();
                            }

                            Point3D oldLoc = m_Location;
                            m_Location = value;
                            ReleaseWorldPackets();

                            SetLastMoved();

                            eable = m_Map.GetClientsInRange(m_Location, Core.GlobalMaxUpdateRange);

                            foreach (NetState state in eable)
                            {
                                Mobile m = state.Mobile;

                                if (m.CanSee(this) && m.InUpdateRange(m_Location) &&
                                    (!state.HighSeas || !m_NoMoveHS || (m_DeltaFlags & ItemDelta.Update) != 0 ||
                                     !m.InRange(oldLoc, GetUpdateRange(m))))
                                {
                                    SendInfoTo(state);
                                }
                            }

                            eable.Free();

                            RemDelta(ItemDelta.Update);
                        }
                        else if (m_Parent is Item)
                        {
                            m_Location = value;
                            ReleaseWorldPackets();

                            Delta(ItemDelta.Update);
                        }
                        else
                        {
                            m_Location = value;
                            ReleaseWorldPackets();
                        }

                        if (m_Parent == null)
                        {
                            m_Map.OnMove(oldLocation, this);
                        }
                    }
                    else
                    {
                        m_Location = value;
                        ReleaseWorldPackets();
                    }

                    OnLocationChange(oldLocation);
                }
            }
        }

        [CommandProperty(AccessLevel.Counselor, AccessLevel.Decorator)]
        public int X { get { return m_Location.m_X; } set { Location = new Point3D(value, m_Location.m_Y, m_Location.m_Z); } }

        [CommandProperty(AccessLevel.Counselor, AccessLevel.Decorator)]
        public int Y { get { return m_Location.m_Y; } set { Location = new Point3D(m_Location.m_X, value, m_Location.m_Z); } }

        [CommandProperty(AccessLevel.Counselor, AccessLevel.Decorator)]
        public int Z { get { return m_Location.m_Z; } set { Location = new Point3D(m_Location.m_X, m_Location.m_Y, value); } }
        #endregion

        [CommandProperty(AccessLevel.Decorator)]
        public virtual int ItemID
        {
            get { return m_ItemID; }
            set
            {
                if (m_ItemID != value)
                {
                    int oldPileWeight = PileWeight;

                    m_ItemID = value;
                    
                    ReleaseWorldPackets();

                    int newPileWeight = PileWeight;

                    UpdateTotal(this, TotalType.Weight, newPileWeight - oldPileWeight);

                    UpdateLight();

                    InvalidateProperties();
                    Delta(ItemDelta.Update);
                }
            }
        }

        public virtual string DefaultName { get { return null; } }

        [CommandProperty(AccessLevel.Decorator)]
        public string Name
        {
            get
            {
                CompactInfo info = LookupCompactInfo();

                if (info != null && info.m_Name != null)
                {
                    return info.m_Name;
                }

                return DefaultName;
            }
            set
            {
                if (value == null || value != DefaultName)
                {
                    CompactInfo info = AcquireCompactInfo();

                    info.m_Name = value;

                    if (info.m_Name == null)
                    {
                        VerifyCompactInfo();
                    }

                    InvalidateProperties();
                }
            }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public virtual object Parent
        {
            get { return m_Parent; }
            set
            {
                if (m_Parent == value)
                {
                    return;
                }

                var oldParent = m_Parent;

                m_Parent = value;

                OnParentChanged(oldParent);
            }
        }

        protected virtual void OnParentChanged(object oldParent)
        {
            if (m_Map != null)
            {
                if (oldParent != null && m_Parent == null)
                {
                    m_Map.OnEnter(this);
                }
                else if (oldParent == null && m_Parent != null)
                {
                    m_Map.OnLeave(this);
                }
            }

            if (!World.Loading)
            {
                if (oldParent is Item)
                {
                    oldParent = ((Item)oldParent).RootParent;
                }

                var root = RootParent as Mobile;

                if (root != null && oldParent != root)
                {
                    root.Obtained(this);
                }
            }
        }

        [CommandProperty(AccessLevel.Decorator)]
        public LightType Light
        {
            get { return m_Light; }
            set
            {
                if (m_Light != value)
                {
                    m_Light = value;
                    ReleaseWorldPackets();

                    Delta(ItemDelta.Update);
                }
            }
        }

        [CommandProperty(AccessLevel.Decorator)]
        public Direction Direction
        {
            get { return m_Direction; }
            set
            {
                if (m_Direction != value)
                {
                    m_Direction = value;
                    ReleaseWorldPackets();

                    Delta(ItemDelta.Update);
                }
            }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public int Amount
        {
            get { return m_Amount; }
            set
            {
                int oldValue = m_Amount;

                if (oldValue != value)
                {
                    int oldPileWeight = PileWeight;

                    m_Amount = value;
                    ReleaseWorldPackets();

                    int newPileWeight = PileWeight;

                    UpdateTotal(this, TotalType.Weight, newPileWeight - oldPileWeight);

                    OnAmountChange(oldValue);

                    Delta(ItemDelta.Update);

                    if (oldValue > 1 || value > 1)
                    {
                        InvalidateProperties();
                    }

                    if (!Stackable && m_Amount > 1)
                    {
                        Console.WriteLine(
                            "Warning: 0x{0:X}: Amount changed for non-stackable item '{2}'. ({1})", m_Serial.Value, m_Amount, GetType().Name);
                    }
                }
            }
        }

        protected virtual void OnAmountChange(int oldValue)
        { }

        public virtual bool HandlesOnSpeech { get { return false; } }

        public virtual void OnSpeech(SpeechEventArgs e)
        { }

        public virtual bool OnDroppedToMobile(Mobile from, Mobile target)
        {
            if (Nontransferable && from.Player && !from.IsStaff())
            {
                HandleInvalidTransfer(from);
                return false;
            }

            return true;
        }

        public virtual bool DropToMobile(Mobile from, Mobile target, Point3D p)
        {
            if (Deleted || from.Deleted || target.Deleted || from.Map != target.Map || from.Map == null || target.Map == null)
            {
                return false;
            }
            else if (from.AccessLevel < AccessLevel.GameMaster && !from.InRange(target.Location, 2))
            {
                return false;
            }
            else if (!from.CanSee(target) || !from.InLOS(target))
            {
                return false;
            }
            else if (!from.OnDroppedItemToMobile(this, target))
            {
                return false;
            }
            else if (!OnDroppedToMobile(from, target))
            {
                return false;
            }
            else if (!target.OnDragDrop(from, this))
            {
                return false;
            }
            else
            {
                return true;
            }
        }

        public virtual bool OnDroppedInto(Mobile from, Container target, Point3D p)
        {
            if (!from.OnDroppedItemInto(this, target, p))
            {
                return false;
            }
            else if (Nontransferable && from.Player && target != from.Backpack)
            {
                HandleInvalidTransfer(from);
                return false;
            }

            return target.OnDragDropInto(from, this, p);
        }

        public virtual bool OnDroppedOnto(Mobile from, Item target)
        {
            if (Deleted || from.Deleted || target.Deleted || from.Map != target.Map || from.Map == null || target.Map == null)
            {
                return false;
            }
            else if (from.AccessLevel < AccessLevel.GameMaster && !from.InRange(target.GetWorldLocation(), 2))
            {
                return false;
            }
            else if (!from.CanSee(target) || !from.InLOS(target))
            {
                return false;
            }
            else if (!target.IsAccessibleTo(from))
            {
                return false;
            }
            else if (!from.OnDroppedItemOnto(this, target))
            {
                return false;
            }
            else if (Nontransferable && from.Player && target != from.Backpack && !from.IsStaff())
            {
                HandleInvalidTransfer(from);
                return false;
            }
            else
            {
                return target.OnDragDrop(from, this);
            }
        }

        public virtual bool DropToItem(Mobile from, Item target, Point3D p)
        {
            if (Deleted || from.Deleted || target.Deleted || from.Map != target.Map || from.Map == null || target.Map == null)
            {
                return false;
            }

            var root = target.RootParent;

            if (from.AccessLevel < AccessLevel.GameMaster && !from.InRange(target.GetWorldLocation(), 2))
            {
                return false;
            }
            else if (!from.CanSee(target) || !from.InLOS(target))
            {
                return false;
            }
            else if (!target.IsAccessibleTo(from))
            {
                return false;
            }
            else if (root is Mobile && !((Mobile)root).CheckNonlocalDrop(from, this, target))
            {
                return false;
            }
            else if (!from.OnDroppedItemToItem(this, target, p))
            {
                return false;
            }
            else if (target is Container && p.m_X != -1 && p.m_Y != -1)
            {
                return OnDroppedInto(from, (Container)target, p);
            }
            else
            {
                return OnDroppedOnto(from, target);
            }
        }

        public virtual bool OnDroppedToWorld(Mobile from, Point3D p)
        {
            if (Nontransferable && from.Player && !from.IsStaff())
            {
                HandleInvalidTransfer(from);
                return false;
            }

            return true;
        }

        public virtual int GetLiftSound(Mobile from)
        {
            return 0x57;
        }

        private static int m_OpenSlots;

        public virtual bool DropToWorld(Mobile from, Point3D p)
        {
            if (Deleted || from.Deleted || from.Map == null)
            {
                return false;
            }
            else if (!from.InRange(p, 2))
            {
                return false;
            }

            if (QuestItem)
            {
                from.SendLocalizedMessage(1074769); // An item must be in your backpack (and not in a container within) to be toggled as a quest item.
                return false;
            }

            Map map = from.Map;

            if (map == null)
            {
                return false;
            }

            Point3D dest = FindDropPoint(p, map, from.Z + 17);
            if (dest == Point3D.Zero)
                return false;

            if (!from.InLOS(new Point3D(dest.X, dest.Y, dest.Z + 1)))
            {
                return false;
            }

            else if (!from.OnDroppedItemToWorld(this, dest))
            {
                return false;
            }
            else if (!OnDroppedToWorld(from, dest))
            {
                return false;
            }

            int soundID = GetDropSound();

            MoveToWorld(dest, from.Map);

            from.SendSound(soundID == -1 ? 0x42 : soundID, GetWorldLocation());

            return true;
        }

        public bool DropToWorld(Point3D p, Map map)
        {
            Point3D dest = FindDropPoint(p, map, int.MaxValue);
            if (dest == Point3D.Zero)
                return false;
            MoveToWorld(dest, map);
            return true;
        }

        private Point3D FindDropPoint(Point3D p, Map map, int maxZ)
        {
            if (map == null)
                return Point3D.Zero;

            int myTop = -255;
            int x = p.m_X, y = p.m_Y;
            int z = int.MinValue;

            LandTile landTile = map.Tiles.GetLandTile(x, y);
            TileFlag landFlags = TileData.LandTable[landTile.ID & TileData.MaxLandValue].Flags;

            int landZ = 0, landAvg = 0, landTop = 0;
            map.GetAverageZ(x, y, ref landZ, ref landAvg, ref landTop);

            if (!landTile.Ignored && (landFlags & TileFlag.Impassable) == 0)
            {
                if (landAvg <= maxZ)
                {
                    z = landAvg;
                }
            }

            var tiles = map.Tiles.GetStaticTiles(x, y, true);

            for (int i = 0; i < tiles.Length; ++i)
            {
                StaticTile tile = tiles[i];
                ItemData id = TileData.ItemTable[tile.ID & TileData.MaxItemValue];

                if (!id.Surface)
                {
                    continue;
                }

                int top = tile.Z + id.CalcHeight;
                if (top > p.Z) myTop = top;

                if (top > maxZ || top < z)
                {
                    continue;
                }

                z = top;
            }

            var items = new List<Item>();

            var eable = map.GetItemsInRange(p, 0);

            foreach (Item item in eable)
            {
                if (item is BaseMulti || item.ItemID > TileData.MaxItemValue)
                {
                    continue;
                }

                items.Add(item);

                ItemData id = item.ItemData;

                if (!id.Surface)
                {
                    continue;
                }

                int top = item.Z + id.CalcHeight;
                if (top > p.Z) myTop = top;

                if (top > maxZ || top < z)
                {
                    continue;
                }

                z = top;
            }

            eable.Free();

            if (z == int.MinValue)
            {
                return Point3D.Zero;
            }

            if (z > maxZ)
            {
                return Point3D.Zero;
            }

            m_OpenSlots = (1 << 20) - 1;

            int surfaceZ = z;

            for (int i = 0; i < tiles.Length; ++i)
            {
                StaticTile tile = tiles[i];
                ItemData id = TileData.ItemTable[tile.ID & TileData.MaxItemValue];

                int checkZ = tile.Z;
                int checkTop = checkZ + id.CalcHeight;

                if (checkTop == checkZ && !id.Surface)
                {
                    ++checkTop;
                }

                int zStart = checkZ - z;
                int zEnd = checkTop - z;

                if (zStart >= 20 || zEnd < 0)
                {
                    continue;
                }

                if (zStart < 0)
                {
                    zStart = 0;
                }

                if (zEnd > 19)
                {
                    zEnd = 19;
                }

                int bitCount = zEnd - zStart;

                m_OpenSlots &= ~(((1 << bitCount) - 1) << zStart);
            }

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

                int checkZ = item.Z;
                int checkTop = checkZ + id.CalcHeight;

                if (checkTop == checkZ && !id.Surface)
                {
                    ++checkTop;
                }

                int zStart = checkZ - z;
                int zEnd = checkTop - z;

                if (zStart >= 20 || zEnd < 0)
                {
                    continue;
                }

                if (zStart < 0)
                {
                    zStart = 0;
                }

                if (zEnd > 19)
                {
                    zEnd = 19;
                }

                int bitCount = zEnd - zStart;

                m_OpenSlots &= ~(((1 << bitCount) - 1) << zStart);
            }

            int height = ItemData.Height;

            if (height == 0)
            {
                ++height;
            }

            if (height > 30)
            {
                height = 30;
            }

            /*
            if (myTop != -255)
            {
                int match = (1 << height) - 1;
                bool okay = false;

                for (int i = 0; i < 20; ++i)
                {
                    if ((i + height) > 20)
                    {
                        match >>= 1;
                    }

                    okay = ((m_OpenSlots >> i) & match) == match;
                  
                    if (okay)
                    {
                        z += i;
                        break;
                    }                   
                }
			    if (!okay)
			    {
				    return Point3D.Zero;
			    }
            }
            */

            height = ItemData.Height;

            if (height == 0)
            {
                ++height;
            }

            if (landAvg > z && (z + height) > landZ)
            {
                return Point3D.Zero;
            }
            else if ((landFlags & TileFlag.Impassable) != 0 && landAvg > surfaceZ && (z + height) > landZ)
            {
                return Point3D.Zero;
            }

            for (int i = 0; i < tiles.Length; ++i)
            {
                StaticTile tile = tiles[i];
                ItemData id = TileData.ItemTable[tile.ID & TileData.MaxItemValue];

                int checkZ = tile.Z;
                int checkTop = checkZ + id.CalcHeight;

                if (checkTop > z && (z + height) > checkZ)
                {
                    return Point3D.Zero;
                }
                else if ((id.Surface || id.Impassable) && checkTop > surfaceZ && (z + height) > checkZ)
                {
                    return Point3D.Zero;
                }
            }

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

                if (item.Z > p.Z + 17 || item.Z < p.Z) continue;

                z += id.CalcHeight;

                if (((item.Z + id.CalcHeight) >= maxZ) || (myTop != -255 && (item.Z + id.CalcHeight) > myTop)) /*&& (z + height) > item.Z)*/
                {
                    return Point3D.Zero;
                }
            }

            return new Point3D(x, y, z);
        }

        public void SendRemovePacket()
        {
            if (!Deleted && m_Map != null)
            {
                Point3D worldLoc = GetWorldLocation();

                var eable = m_Map.GetClientsInRange(worldLoc, Core.GlobalRadarRange - 4);

                foreach (NetState state in eable)
                {
                    Mobile m = state.Mobile;

                    if (Utility.InRange(worldLoc, m.Location, GetUpdateRange(m)))
                    {
                        state.Send(RemovePacket);
                    }
                }

                eable.Free();
            }
        }

        public virtual int GetDropSound()
        {
            return -1;
        }

        public Point3D GetWorldLocation()
        {
            var root = RootParent;

            if (root == null)
            {
                return m_Location;
            }
            else
            {
                return ((IEntity)root).Location;
            }

            //return root == null ? m_Location : new Point3D( (IPoint3D) root );
        }

        public virtual bool BlocksFit { get { return false; } }

        public Point3D GetSurfaceTop()
        {
            var root = RootParentEntity;

            if (root == null)
            {
                return new Point3D(m_Location.m_X, m_Location.m_Y, m_Location.m_Z + (ItemData.Surface ? ItemData.CalcHeight : 0));
            }
            else
            {
                return root.Location;
            }
        }

        public Point3D GetWorldTop()
        {
            var root = RootParentEntity;

            if (root == null)
            {
                return new Point3D(m_Location.m_X, m_Location.m_Y, m_Location.m_Z + ItemData.CalcHeight);
            }
            else
            {
                return root.Location;
            }
        }

        public void SendLocalizedMessageTo(Mobile to, int number)
        {
            if (Deleted || !to.CanSee(this))
            {
                return;
            }

            to.Send(new MessageLocalized(Serial, ItemID, MessageType.Regular, 0x3B2, 3, number, "", ""));
        }

        public void SendLocalizedMessageTo(Mobile to, int number, string args)
        {
            if (Deleted || !to.CanSee(this))
            {
                return;
            }

            to.Send(new MessageLocalized(Serial, ItemID, MessageType.Regular, 0x3B2, 3, number, "", args));
        }

        public void SendLocalizedMessage(int number, string args)
        {
            if (Deleted || Map == null)
            {
                return;
            }

            IPooledEnumerable eable = Map.GetClientsInRange(Location, Core.GlobalMaxUpdateRange);
            Packet p = Packet.Acquire(new MessageLocalized(m_Serial, m_ItemID, MessageType.Regular, 0x3B2, 3, number, Name, args));

            foreach (NetState ns in eable)
            {
                ns.Send(p);
            }

            Packet.Release(p);
            eable.Free();
        }

        public void SendLocalizedMessage(MessageType type, int number, AffixType affixType, string affix, string args)
        {
            IPooledEnumerable eable = Map.GetClientsInRange(Location, Core.GlobalMaxUpdateRange);
            Packet p = Packet.Acquire(new MessageLocalizedAffix(m_Serial, m_ItemID, type, 0x3B2, 3, number, "", affixType, affix, args));

            foreach (NetState ns in eable)
            {
                ns.Send(p);
            }

            Packet.Release(p);
            eable.Free();
        }

        public void SendLocalizedMessageTo(Mobile to, int number, AffixType affixType, string affix, string args)
        {
            if (Deleted || !to.CanSee(this))
            {
                return;
            }

            to.Send(new MessageLocalizedAffix(Serial, ItemID, MessageType.Regular, 0x3B2, 3, number, "", affixType, affix, args));
        }

        #region OnDoubleClick[...]
        public virtual void OnDoubleClick(Mobile from)
        { }

        public virtual void OnDoubleClickOutOfRange(Mobile from)
        { }

        public virtual void OnDoubleClickCantSee(Mobile from)
        { }

        public virtual void OnDoubleClickDead(Mobile from)
        {
            from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019048); // I am dead and cannot do that.
        }

        public virtual void OnDoubleClickNotAccessible(Mobile from)
        {
            from.SendLocalizedMessage(500447); // That is not accessible.
        }

        public virtual void OnDoubleClickSecureTrade(Mobile from)
        {
            from.SendLocalizedMessage(500447); // That is not accessible.
        }
        #endregion

        public virtual void OnSnoop(Mobile from)
        { }

        public bool InSecureTrade { get { return (GetSecureTradeCont() != null); } }

        public SecureTradeContainer GetSecureTradeCont()
        {
            object p = this;

            while (p is Item)
            {
                if (p is SecureTradeContainer)
                {
                    return (SecureTradeContainer)p;
                }

                p = ((Item)p).m_Parent;
            }

            return null;
        }

        public virtual void OnItemAdded(Item item)
        {
            if (m_Parent is Item)
            {
                ((Item)m_Parent).OnSubItemAdded(item);
            }
            else if (m_Parent is Mobile)
            {
                ((Mobile)m_Parent).OnSubItemAdded(item);
            }
        }

        public virtual void OnItemRemoved(Item item)
        {
            if (m_Parent is Item)
            {
                ((Item)m_Parent).OnSubItemRemoved(item);
            }
            else if (m_Parent is Mobile)
            {
                ((Mobile)m_Parent).OnSubItemRemoved(item);
            }
        }

        public virtual void OnSubItemAdded(Item item)
        {
            if (m_Parent is Item)
            {
                ((Item)m_Parent).OnSubItemAdded(item);
            }
            else if (m_Parent is Mobile)
            {
                ((Mobile)m_Parent).OnSubItemAdded(item);
            }
        }

        public virtual void OnSubItemRemoved(Item item)
        {
            if (m_Parent is Item)
            {
                ((Item)m_Parent).OnSubItemRemoved(item);
            }
            else if (m_Parent is Mobile)
            {
                ((Mobile)m_Parent).OnSubItemRemoved(item);
            }
        }

        public virtual void OnItemBounceCleared(Item item)
        {
            if (m_Parent is Item)
            {
                ((Item)m_Parent).OnSubItemBounceCleared(item);
            }
            else if (m_Parent is Mobile)
            {
                ((Mobile)m_Parent).OnSubItemBounceCleared(item);
            }
        }

        public virtual void OnSubItemBounceCleared(Item item)
        {
            if (m_Parent is Item)
            {
                ((Item)m_Parent).OnSubItemBounceCleared(item);
            }
            else if (m_Parent is Mobile)
            {
                ((Mobile)m_Parent).OnSubItemBounceCleared(item);
            }
        }

        public virtual bool CheckTarget(Mobile from, Target targ, object targeted)
        {
            if (m_Parent is Item)
            {
                return ((Item)m_Parent).CheckTarget(from, targ, targeted);
            }
            else if (m_Parent is Mobile)
            {
                return ((Mobile)m_Parent).CheckTarget(from, targ, targeted);
            }

            return true;
        }

        public virtual void OnStatsQuery(Mobile m)
        {
            if (m == null || m.Deleted || m.Map != Map || m.NetState == null)
            {
                return;
            }

            if (Utility.InUpdateRange(m, this) && m.CanSee(this))
            {
                SendStatusTo(m.NetState);
            }
        }

        public virtual void SendStatusTo(NetState state)
        {
            var p = GetStatusPacketFor(state);

            if (p != null)
            {
                state.Send(p);
            }
        }

        public virtual Packet GetStatusPacketFor(NetState state)
        {
            if (this is IDamageable && state != null && state.Mobile != null && state.HighSeas)
            {
                return new MobileStatusCompact(CanBeRenamedBy(state.Mobile), (IDamageable)this);
            }

            return null;
        }

        public virtual bool CanBeRenamedBy(Mobile m)
        {
            return m != null && m.AccessLevel >= AccessLevel.GameMaster;
        }

        public virtual bool IsAccessibleTo(Mobile check)
        {
            if (m_Parent is Item)
            {
                return ((Item)m_Parent).IsAccessibleTo(check);
            }

            Region reg = Region.Find(GetWorldLocation(), m_Map);

            return reg.CheckAccessibility(this, check);
        }

        public bool IsChildOf(object o)
        {
            return IsChildOf(o, false);
        }

        public bool IsChildOf(object o, bool allowNull)
        {
            var p = m_Parent;

            if ((p == null || o == null) && !allowNull)
            {
                return false;
            }

            if (p == o)
            {
                return true;
            }

            while (p is Item)
            {
                Item item = (Item)p;

                if (item.m_Parent == null)
                {
                    break;
                }
                else
                {
                    p = item.m_Parent;

                    if (p == o)
                    {
                        return true;
                    }
                }
            }

            return false;
        }

        public ItemData ItemData { get { return TileData.ItemTable[m_ItemID & TileData.MaxItemValue]; } }

        public virtual void OnItemUsed(Mobile from, Item item)
        {
            if (m_Parent is Item)
            {
                ((Item)m_Parent).OnItemUsed(from, item);
            }
            else if (m_Parent is Mobile)
            {
                ((Mobile)m_Parent).OnItemUsed(from, item);
            }
        }

        public bool CheckItemUse(Mobile from)
        {
            return CheckItemUse(from, this);
        }

        public virtual bool CheckItemUse(Mobile from, Item item)
        {
            if (m_Parent is Item)
            {
                return ((Item)m_Parent).CheckItemUse(from, item);
            }
            else if (m_Parent is Mobile)
            {
                return ((Mobile)m_Parent).CheckItemUse(from, item);
            }
            else
            {
                return true;
            }
        }

        public virtual void OnItemLifted(Mobile from, Item item)
        {
            if (m_Parent is Item)
            {
                ((Item)m_Parent).OnItemLifted(from, item);
            }
            else if (m_Parent is Mobile)
            {
                ((Mobile)m_Parent).OnItemLifted(from, item);
            }
        }

        public bool CheckLift(Mobile from)
        {
            LRReason reject = LRReason.Inspecific;

            return CheckLift(from, this, ref reject);
        }

        public virtual bool CheckLift(Mobile from, Item item, ref LRReason reject)
        {
            if (m_Parent is Item)
            {
                return ((Item)m_Parent).CheckLift(from, item, ref reject);
            }
            else if (m_Parent is Mobile)
            {
                return ((Mobile)m_Parent).CheckLift(from, item, ref reject);
            }
            else
            {
                return true;
            }
        }

        public virtual bool CanTarget { get { return true; } }
        public virtual bool DisplayLootType { get { return true; } }

        public virtual void OnSingleClickContained(Mobile from, Item item)
        {
            if (m_Parent is Item)
            {
                ((Item)m_Parent).OnSingleClickContained(from, item);
            }
        }

        public virtual void OnAosSingleClick(Mobile from)
        {
            ObjectPropertyList opl = PropertyList;

            if (opl.Header > 0)
            {
                from.Send(new MessageLocalized(m_Serial, m_ItemID, MessageType.Label, 0x3B2, 3, opl.Header, Name, opl.HeaderArgs));
            }
        }

        public virtual void OnSingleClick(Mobile from)
        {
            if (Deleted || !from.CanSee(this))
            {
                return;
            }

            if (DisplayLootType)
            {
                LabelLootTypeTo(from);
            }

            NetState ns = from.NetState;

            if (ns != null)
            {
                if (Name == null)
                {
                    if (m_Amount <= 1)
                    {
                        ns.Send(new MessageLocalized(m_Serial, m_ItemID, MessageType.Label, 0x3B2, 3, LabelNumber, "", ""));
                    }
                    else
                    {
                        ns.Send(
                            new MessageLocalizedAffix(
                                m_Serial,
                                m_ItemID,
                                MessageType.Label,
                                0x3B2,
                                3,
                                LabelNumber,
                                "",
                                AffixType.Append,
                                String.Format(" : {0}", m_Amount),
                                ""));
                    }
                }
                else
                {
                    ns.Send(
                        new UnicodeMessage(
                            m_Serial, m_ItemID, MessageType.Label, 0x3B2, 3, "ENU", "", Name + (m_Amount > 1 ? " : " + m_Amount : "")));
                }
            }
        }

        private static bool m_ScissorCopyLootType;

        public static bool ScissorCopyLootType { get { return m_ScissorCopyLootType; } set { m_ScissorCopyLootType = value; } }

        public virtual void ScissorHelper(Mobile from, Item newItem, int amountPerOldItem)
        {
            ScissorHelper(from, newItem, amountPerOldItem, true);
        }

        public virtual void ScissorHelper(Mobile from, Item newItem, int amountPerOldItem, bool carryHue)
        {
            int amount = Amount;

            if (amount > (60000 / amountPerOldItem)) // let's not go over 60000
            {
                amount = (60000 / amountPerOldItem);
            }

            Amount -= amount;

            int ourHue = Hue;
            Map thisMap = Map;
            var thisParent = m_Parent;
            Point3D worldLoc = GetWorldLocation();
            LootType type = LootType;

            if (Amount == 0)
            {
                Delete();
            }

            newItem.Amount = amount * amountPerOldItem;

            if (carryHue)
            {
                newItem.Hue = ourHue;
            }

            if (m_ScissorCopyLootType)
            {
                newItem.LootType = type;
            }

            if (!(thisParent is Container) || !((Container)thisParent).TryDropItem(from, newItem, false))
            {
                newItem.MoveToWorld(worldLoc, thisMap);
            }
        }

        public virtual void Consume()
        {
            Consume(1);
        }

        public virtual void Consume(int amount)
        {
            Amount -= amount;

            if (Amount <= 0)
            {
                Delete();
            }
        }

        public virtual void ReplaceWith(Item newItem)
        {
            if (m_Parent is Container)
            {
                ((Container)m_Parent).AddItem(newItem);
                newItem.Location = m_Location;
            }
            else
            {
                newItem.MoveToWorld(GetWorldLocation(), m_Map);
            }

            Delete();
        }

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

                InvalidateProperties();

                ReleaseWorldPackets();

                Delta(ItemDelta.Update);
            }
        }

        public bool Insured
        {
            get { return GetFlag(ImplFlag.Insured); }
            set
            {
                SetFlag(ImplFlag.Insured, value);
                InvalidateProperties();
            }
        }

        public bool PayedInsurance { get { return GetFlag(ImplFlag.PayedInsurance); } set { SetFlag(ImplFlag.PayedInsurance, value); } }

        public Mobile BlessedFor
        {
            get
            {
                CompactInfo info = LookupCompactInfo();

                if (info != null)
                {
                    return info.m_BlessedFor;
                }

                return null;
            }
            set
            {
                CompactInfo info = AcquireCompactInfo();

                info.m_BlessedFor = value;

                if (info.m_BlessedFor == null)
                {
                    VerifyCompactInfo();
                }

                InvalidateProperties();
            }
        }

        public virtual bool CheckBlessed(object o)
        {
            return CheckBlessed(o as Mobile);
        }

        public virtual bool CheckBlessed(Mobile m)
        {
            if (m_LootType == LootType.Blessed || (Mobile.InsuranceEnabled && Insured))
            {
                return true;
            }

            return (m != null && m == BlessedFor);
        }

        public virtual bool CheckNewbied()
        {
            return (m_LootType == LootType.Newbied);
        }

        public virtual bool IsStandardLoot()
        {
            if (Mobile.InsuranceEnabled && Insured)
            {
                return false;
            }

            if (BlessedFor != null)
            {
                return false;
            }

            return (m_LootType == LootType.Regular);
        }

        public override string ToString()
        {
            return String.Format("0x{0:X} \"{1}\"", m_Serial.Value, GetType().Name);
        }

        internal int m_TypeRef;

        public Item()
        {
            m_Serial = Serial.NewItem;
            m_Map = Map.Internal;
            
            m_Light = LightType.Empty;

            m_Amount = 1;

            Visible = true;
            Movable = true;

            SetLastMoved();

            World.AddItem(this);

            Type ourType = GetType();
            m_TypeRef = World.m_ItemTypes.IndexOf(ourType);

            if (m_TypeRef == -1)
            {
                World.m_ItemTypes.Add(ourType);
                m_TypeRef = World.m_ItemTypes.Count - 1;
            }

            Timer.DelayCall(EventSink.InvokeItemCreated, new ItemCreatedEventArgs(this));
        }

        [Constructable]
        public Item(int itemID)
            : this()
        {
            m_ItemID = itemID;

            UpdateLight();
        }

        public Item(Serial serial)
        {
            m_Serial = serial;

            Type ourType = GetType();
            m_TypeRef = World.m_ItemTypes.IndexOf(ourType);

            if (m_TypeRef == -1)
            {
                World.m_ItemTypes.Add(ourType);
                m_TypeRef = World.m_ItemTypes.Count - 1;
            }
        }

        public virtual void UpdateLight()
        {
            var data = ItemData;

            if (!data.Flags.HasFlag(TileFlag.LightSource))
            {
                return;
            }

            if (m_Light == LightType.Empty)
            {
                var light = data.Quality;

                if (light >= 0 && light <= 255)
                {
                    m_Light = (LightType)light;
                }
            }
        }

        public virtual void OnSectorActivate()
        { }

        public virtual void OnSectorDeactivate()
        { }

        #region Item Sockets
        public List<ItemSocket> Sockets { get; private set; }

        public void AttachSocket(ItemSocket socket)
		{
			if(Sockets == null)
			{
				Sockets = new List<ItemSocket>();
			}

			Sockets.Add(socket);
			socket.Owner = this;
			
			InvalidateProperties();
		}

        public bool RemoveSocket<T>()
        {
            var socket = GetSocket(typeof(T));

            if (socket != null)
            {
                RemoveItemSocket(socket);
                return true;
            }

            return false;
        }

        public void RemoveItemSocket(ItemSocket socket)
        {
            if (Sockets == null)
            {
                return;
            }

            Sockets.Remove(socket);
            socket.OnRemoved();

            if (Sockets.Count == 0)
            {
                Sockets = null;
            }

            InvalidateProperties();
        }
		
		public T GetSocket<T>() where T : ItemSocket
		{
            if (Sockets == null)
            {
                return null;
            }

			return Sockets.FirstOrDefault(s => s.GetType() == typeof(T)) as T;
		}
		
		public T GetSocket<T>(Func<T, bool> predicate) where T : ItemSocket
		{
            if (Sockets == null)
            {
                return null;
            }

			return Sockets.FirstOrDefault(s => s.GetType() == typeof(T) && (predicate == null || predicate(s as T))) as T;
		}

        public ItemSocket GetSocket(Type type)
        {
            if (Sockets == null)
            {
                return null;
            }

            return Sockets.FirstOrDefault(s => s.GetType() == type);
        }

        public bool HasSocket<T>()
		{
            if (Sockets == null)
            {
                return false;
            }

            return Sockets.Any(s => s.GetType() == typeof(T));
		}

        public bool HasSocket(Type t)
        {
            if (Sockets == null)
            {
                return false;
            }

            return Sockets.Any(s => s.GetType() == t);
        }
        #endregion
    }

    public class ItemSocket
	{
		[CommandProperty(AccessLevel.GameMaster)]
		public Item Owner { get; set; }
		
		[CommandProperty(AccessLevel.GameMaster)]
		public DateTime Expires { get; set; }
		
		public virtual TimeSpan TickDuration { get { return TimeSpan.FromMinutes(1); } }
		
		public Timer Timer { get; set; }

        public ItemSocket()
            : this(TimeSpan.Zero)
        {
        }
		
		public ItemSocket(TimeSpan duration)
		{
			if(duration != TimeSpan.Zero)
			{
				Expires = DateTime.UtcNow + duration;
				
				BeginTimer();
			}
		}
		
		protected void BeginTimer()
		{
			EndTimer();
			
			Timer = Timer.DelayCall(TickDuration, TickDuration, OnTick);
            Timer.Start();
		}
		
		protected void EndTimer()
		{
			if(Timer != null)
			{
				Timer.Stop();
				Timer = null;
			}
		}
		
		protected virtual void OnTick()
		{
			if(Expires < DateTime.UtcNow || Owner.Deleted)
			{
				Remove();
			}
		}
		
		public virtual void Remove()
		{
			EndTimer();
			
			Owner.RemoveItemSocket(this);
		}
		
		public virtual void OnRemoved()
		{
		}
		
		public virtual void GetProperties(ObjectPropertyList list)
		{
		}

        public virtual void OnOwnerDuped(Item newItem)
        {
            ItemSocket newSocket = null;

            try
            {
                newSocket = Activator.CreateInstance(GetType()) as ItemSocket;
            }
            catch
            {
                Console.WriteLine(
                    "Warning: 0x{0:X}: Item socket must have a zero paramater constructor to be separated from a stack. '{1}'.",
                    Owner.Serial.Value,
                    GetType().Name);
            }

            if (newSocket != null)
            {
                newSocket.Expires = Expires;

                if (newSocket.Expires != DateTime.MinValue)
                {
                    newSocket.BeginTimer();
                }

                newSocket.OnAfterDuped(this);
                newItem.AttachSocket(newSocket);
            }
        }

        public virtual void OnAfterDuped(ItemSocket oldSocket)
        {
        }

		public virtual void Serialize(GenericWriter writer)
		{
			writer.Write(0);
			
			writer.Write(Expires);
		}
		
		public virtual void Deserialize(Item owner, GenericReader reader)
		{
			reader.ReadInt(); // version
			
			Expires = reader.ReadDateTime();
			
			if(Expires != DateTime.MinValue)
			{
				if(Expires < DateTime.UtcNow)
				{
					return;
				}
				else
				{
					BeginTimer();
				}
			}
			
			owner.AttachSocket(this);
		}
		
		public static void Save(ItemSocket socket, GenericWriter writer)
		{
			writer.Write(socket.GetType().Name);
			socket.Serialize(writer);
		}
		
		public static void Load(Item item, GenericReader reader)
		{
			var typeName = ScriptCompiler.FindTypeByName(reader.ReadString());
            var socket = Activator.CreateInstance(typeName) as ItemSocket;

            socket.Deserialize(item, reader);
        }
    }
}
