context: using System;
  using System.Collections.Generic;
  using Server.ContextMenus;
  using Server.Engines.PartySystem;
  using Server.Engines.Quests;
  using Server.Engines.Quests.Doom;
  using Server.Engines.Quests.Haven;
  using Server.Guilds;
  using Server.Misc;
  using Server.Mobiles;
  using Server.Network;
  
  namespace Server.Items
  {
      public interface IDevourer
  	{
  		bool Devour( Corpse corpse );
  	}
  
  	[Flags]
  	public enum CorpseFlag
  	{
  		None					= 0x00000000,
  
  		/// <summary>
  		/// Has this corpse been carved?
  		/// </summary>
  		Carved					= 0x00000001,
  
  		/// <summary>
  		/// If true, this corpse will not turn into bones
  		/// </summary>
  		NoBones					= 0x00000002,
  
  		/// <summary>
  		/// If true, the corpse has turned into bones
  		/// </summary>
  		IsBones					= 0x00000004,
  
  		/// <summary>
  		/// Has this corpse yet been visited by a taxidermist?
  		/// </summary>
  		VisitedByTaxidermist	= 0x00000008,
  
  		/// <summary>
  		/// Has this corpse yet been used to channel spiritual energy? (AOS Spirit Speak)
  		/// </summary>
  		Channeled				= 0x00000010,
  
  		/// <summary>
  		/// Was the owner criminal when he died?
  		/// </summary>
  		Criminal				= 0x00000020,
  
  		/// <summary>
  		/// Has this corpse been animated?
  		/// </summary>
  		Animated				= 0x00000040,
  
  		/// <summary>
  		/// Has this corpse been self looted?
  		/// </summary>
  		SelfLooted				= 0x00000080,
  	}
  
  	public class Corpse : Container, ICarvable
  	{
  		private Mobile				m_Owner;				// Whos corpse is this?
  		private Mobile				m_Killer;				// Who killed the owner?
  		private CorpseFlag			m_Flags;				// @see CorpseFlag
  
  		private List<Mobile>		m_Looters;				// Who's looted this corpse?
  		private List<Item>			m_EquipItems;			// List of dropped equipment when the owner died. Ingame, these items display /on/ the corpse, not just inside
  		private List<Item>			m_RestoreEquip;			// List of items equipped when the owner died. Includes insured and blessed items.
  		private List<Mobile>		m_Aggressors;			// Anyone from this list will be able to loot this corpse; we attacked them, or they attacked us when we were freely attackable
  
  		private string				m_CorpseName;			// Value of the CorpseNameAttribute attached to the owner when he died -or- null if the owner had no CorpseNameAttribute; use "the remains of ~name~"
  		private IDevourer			m_Devourer;				// The creature that devoured this corpse
  
  		// For notoriety:
  		private AccessLevel			m_AccessLevel;			// Which AccessLevel the owner had when he died
  		private Guild				m_Guild;				// Which Guild the owner was in when he died
  		private int					m_Kills;				// How many kills the owner had when he died
  
  		private DateTime			m_TimeOfDeath;			// What time was this corpse created?
  
  		private HairInfo			m_Hair;					// This contains the hair of the owner
  		private FacialHairInfo		m_FacialHair;			// This contains the facial hair of the owner
  
  		// For Forensics Evaluation
  		public string				m_Forensicist;			// Name of the first PlayerMobile who used Forensic Evaluation on the corpse
  
  		public static readonly TimeSpan MonsterLootRightSacrifice = TimeSpan.Zero;
  
  		public static readonly TimeSpan InstancedCorpseTime = TimeSpan.FromMinutes( 3.0 );
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public virtual bool InstancedCorpse
  		{
  			get
  			{
  				if ( !Core.SE )
  					return false;
  
  				return ( DateTime.UtcNow < (m_TimeOfDeath + InstancedCorpseTime) );
  			}
  		}
  
  		private Dictionary<Item, InstancedItemInfo> m_InstancedItems;
  
  		private class InstancedItemInfo
  		{
  			private Mobile m_Mobile;
  			private Item m_Item;
  
  			private bool m_Perpetual;   //Needed for Rummaged stuff.  CONTRARY to the Patchlog, cause a later FoF contradicts it.  Verify on OSI.
  			public bool Perpetual { get { return m_Perpetual; } set { m_Perpetual = value; } }
  
  			public InstancedItemInfo( Item i, Mobile m )
  			{
  				m_Item = i;
  				m_Mobile = m;
  			}
  
  			public bool IsOwner( Mobile m )
  			{
  				if ( m_Item.LootType == LootType.Cursed )   //Cursed Items are part of everyone's instanced corpse... (?)
  					return true;
  
  				if ( m == null )
  					return false;   //sanity
  
  				if ( m_Mobile == m )
  					return true;
  
  				Party myParty = Party.Get( m_Mobile );
  
  				return (myParty != null && myParty == Party.Get( m ));
  			}
  		}
  
  		public override bool IsChildVisibleTo( Mobile m, Item child )
  		{
  			if ( !m.Player || m.AccessLevel > AccessLevel.Player )   //Staff and creatures not subject to instancing.
  				return true;
  
  			if ( m_InstancedItems != null )
  			{
  				InstancedItemInfo info;
  
  				if ( m_InstancedItems.TryGetValue( child, out info ) && (InstancedCorpse || info.Perpetual) )
  				{
  					return info.IsOwner( m );   //IsOwner checks Party stuff.
  				}
  			}
  
  			return true;
  		}
  
  		private void AssignInstancedLoot()
  		{
  			if ( m_Aggressors.Count == 0 || this.Items.Count == 0 )
  				return;
  
  			if ( m_InstancedItems == null )
  				m_InstancedItems = new Dictionary<Item, InstancedItemInfo>();
  
  			List<Item> m_Stackables = new List<Item>();
  			List<Item> m_Unstackables = new List<Item>();
  
  			for ( int i = 0; i < this.Items.Count; i++ )
  			{
  				Item item = this.Items[i];
  
  				if ( item.LootType != LootType.Cursed ) //Don't have curesd items take up someone's item spot.. (?)
  				{
  					if ( item.Stackable )
  						m_Stackables.Add( item );
  					else
  						m_Unstackables.Add( item );
  				}
  			}
  
  			List<Mobile> attackers = new List<Mobile>( m_Aggressors );
  
  			for ( int i = 1; i < attackers.Count -1; i++ )  //randomize
  			{
  				int rand = Utility.Random( i + 1 );
  
  				Mobile temp = attackers[rand];
  				attackers[rand] = attackers[i];
  				attackers[i] = temp;
  			}
  
  			//stackables first, for the remaining stackables, have those be randomly added after
  
  			for ( int i = 0; i < m_Stackables.Count; i++ )
  			{
  				Item item = m_Stackables[i];
  
  				if ( item.Amount >= attackers.Count )
  				{
  					int amountPerAttacker = (item.Amount / attackers.Count);
  					int remainder = (item.Amount % attackers.Count);
  
  					for ( int j = 0; j < ((remainder == 0) ? attackers.Count -1 : attackers.Count); j++ )
  					{
  						Item splitItem = Mobile.LiftItemDupe( item, item.Amount - amountPerAttacker );  //LiftItemDupe automagically adds it as a child item to the corpse
  
  						m_InstancedItems.Add( splitItem, new InstancedItemInfo( splitItem, attackers[j] ) );
  
  						//What happens to the remaining portion?  TEMP FOR NOW UNTIL OSI VERIFICATION:  Treat as Single Item.
  					}
  
  					if ( remainder == 0 )
  					{
  						m_InstancedItems.Add( item, new InstancedItemInfo( item, attackers[attackers.Count - 1] ) );
  						//Add in the original item (which has an equal amount as the others) to the instance for the last attacker, cause it wasn't added above.
  					}
  					else
  					{
  						m_Unstackables.Add( item );
  					}
  				}
  				else
  				{
  					//What happens in this case?  TEMP FOR NOW UNTIL OSI VERIFICATION:  Treat as Single Item.
  					m_Unstackables.Add( item );
  				}
  			}
  
  			for ( int i = 0; i < m_Unstackables.Count; i++ )
  			{
  				Mobile m = attackers[i % attackers.Count];
  				Item item = m_Unstackables[i];
  
  				m_InstancedItems.Add( item, new InstancedItemInfo( item, m ) );
  			}
  		}
  
  		public void AddCarvedItem( Item carved, Mobile carver )
  		{
  			this.DropItem( carved );
  
  			if ( this.InstancedCorpse )
  			{
  				if ( m_InstancedItems == null )
  					m_InstancedItems = new Dictionary<Item, InstancedItemInfo>();
  
  				m_InstancedItems.Add( carved, new InstancedItemInfo( carved, carver ) );
  			}
  		}
  
  		public override bool IsDecoContainer
  		{
  			get{ return false; }
  		}
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public DateTime TimeOfDeath
  		{
  			get{ return m_TimeOfDeath; }
  			set{ m_TimeOfDeath = value; }
  		}
  
  		public override bool DisplayWeight { get { return false; } }
  
  		public HairInfo Hair { get { return m_Hair; } }
  		public FacialHairInfo FacialHair { get { return m_FacialHair; } }
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public bool IsBones
  		{
  			get { return GetFlag( CorpseFlag.IsBones ); }
  		}
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public bool Devoured
  		{
  			get { return (m_Devourer != null); }
  		}
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public bool Carved
  		{
  			get{ return GetFlag( CorpseFlag.Carved ); }
  			set { SetFlag( CorpseFlag.Carved, value ); }
  		}
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public bool VisitedByTaxidermist
  		{
  			get { return GetFlag( CorpseFlag.VisitedByTaxidermist ); }
  			set { SetFlag( CorpseFlag.VisitedByTaxidermist, value ); }
  		}
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public bool Channeled
  		{
  			get { return GetFlag( CorpseFlag.Channeled ); }
  			set { SetFlag( CorpseFlag.Channeled, value ); }
  		}
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public bool Animated
  		{
  			get { return GetFlag( CorpseFlag.Animated ); }
  			set { SetFlag( CorpseFlag.Animated, value ); }
  		}
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public bool SelfLooted
  		{
  			get { return GetFlag( CorpseFlag.SelfLooted ); }
  			set { SetFlag( CorpseFlag.SelfLooted, value ); }
  		}
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public AccessLevel AccessLevel
  		{
  			get{ return m_AccessLevel; }
  		}
  
  		public List<Mobile> Aggressors
  		{
  			get{ return m_Aggressors; }
  		}
  
  		public List<Mobile> Looters
  		{
  			get{ return m_Looters; }
  		}
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public Mobile Killer
  		{
  			get{ return m_Killer; }
  		}
  
  		public List<Item> EquipItems
  		{
  			get{ return m_EquipItems; }
  		}
  
  		public List<Item> RestoreEquip
  		{
  			get { return m_RestoreEquip; }
  			set { m_RestoreEquip = value; }
  		}
  
  		public Guild Guild
  		{
  			get{ return m_Guild; }
  		}
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public int Kills
  		{
  			get{ return m_Kills; }
  			set{ m_Kills = value; }
  		}
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public bool Criminal
  		{
  			get { return GetFlag( CorpseFlag.Criminal ); }
  			set { SetFlag( CorpseFlag.Criminal, value ); }
  		}
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public Mobile Owner
  		{
  			get{ return m_Owner; }
  		}
  
  		public void TurnToBones()
  		{
  			if ( Deleted )
  				return;
  
  			ProcessDelta();
  			SendRemovePacket();
  			ItemID = Utility.Random( 0xECA, 9 ); // bone graphic
  			Hue = 0;
  			ProcessDelta();
  
  			SetFlag( CorpseFlag.NoBones, true );
  			SetFlag( CorpseFlag.IsBones, true );
  
  			BeginDecay( m_BoneDecayTime );
  		}
  
  		private static TimeSpan m_DefaultDecayTime = TimeSpan.FromMinutes( 7.0 );
  		private static TimeSpan m_BoneDecayTime = TimeSpan.FromDays( 1.0 );
  
  		private Timer m_DecayTimer;
  		private DateTime m_DecayTime;
  
  		public void BeginDecay( TimeSpan delay )
  		{
  			if ( m_DecayTimer != null )
  				m_DecayTimer.Stop();
  
  			m_DecayTime = DateTime.UtcNow + delay;
  
  			m_DecayTimer = new InternalTimer( this, delay );
  			m_DecayTimer.Start();
  		}
  
  		public override void OnAfterDelete()
  		{
  			if ( m_DecayTimer != null )
  				m_DecayTimer.Stop();
  
  			m_DecayTimer = null;
  		}
  
  		private class InternalTimer : Timer
  		{
  			private Corpse m_Corpse;
  
  			public InternalTimer( Corpse c, TimeSpan delay ) : base( delay )
  			{
  				m_Corpse = c;
  				Priority = TimerPriority.FiveSeconds;
  			}
  
  			protected override void OnTick()
  			{
  				if ( !m_Corpse.GetFlag( CorpseFlag.NoBones ) )
  					m_Corpse.TurnToBones();
  				else
  					m_Corpse.Delete();
  			}
  		}
  
  		public static string GetCorpseName( Mobile m )
  		{
  			if ( m is BaseCreature )
  			{
  				BaseCreature bc = (BaseCreature)m;
  
  				if ( bc.CorpseNameOverride != null )
  					return bc.CorpseNameOverride;
  			}
  
  			Type t = m.GetType();
  
  			object[] attrs = t.GetCustomAttributes( typeof( CorpseNameAttribute ), true );
  
  			if ( attrs != null && attrs.Length > 0 )
  			{
  				CorpseNameAttribute attr = attrs[0] as CorpseNameAttribute;
  
  				if ( attr != null )
  					return attr.Name;
  			}
  
  			return null;
  		}
  
  		public static void Initialize()
  		{
  			Mobile.CreateCorpseHandler += new CreateCorpseHandler( Mobile_CreateCorpseHandler );
  		}
  
  		public static Container Mobile_CreateCorpseHandler( Mobile owner, HairInfo hair, FacialHairInfo facialhair, List<Item> initialContent, List<Item> equipItems )
  		{
  			bool shouldFillCorpse = true;
  
  			//if ( owner is BaseCreature )
  			//	shouldFillCorpse = !((BaseCreature)owner).IsBonded;
  
  			Corpse c;
  			if( owner is MilitiaFighter )
  				c = new MilitiaFighterCorpse( owner, hair, facialhair, shouldFillCorpse ? equipItems : new List<Item>() );
  			else
  				c = new Corpse( owner, hair, facialhair, shouldFillCorpse ? equipItems : new List<Item>() );
  
  			owner.Corpse = c;
  
  			if ( shouldFillCorpse )
  			{
  				for ( int i = 0; i < initialContent.Count; ++i )
  				{
  					Item item = initialContent[i];
  
  					if ( Core.AOS && owner.Player && item.Parent == owner.Backpack )
  						c.AddItem( item );
  					else
  						c.DropItem( item );
  
  					if ( owner.Player && Core.AOS )
  						c.SetRestoreInfo( item, item.Location );
  				}
  
  				if ( Core.SE && !owner.Player )
  				{
  					c.AssignInstancedLoot();
  				}
  				else if ( Core.AOS )
  				{
  					PlayerMobile pm = owner as PlayerMobile;
  
  					if ( pm != null )
  						c.RestoreEquip = pm.EquipSnapshot;
  				}
  			}
  			else
  			{
  				c.Carved = true; // TODO: Is it needed?
  			}
  
  			Point3D loc = owner.Location;
  			Map map = owner.Map;
  
  			if ( map == null || map == Map.Internal )
  			{
  				loc = owner.LogoutLocation;
  				map = owner.LogoutMap;
  			}
  
  			c.MoveToWorld( loc, map );
  
  			return c;
  		}
  
  		// Why was this public?
  		// public override bool IsPublicContainer{ get{ return true; } }
  
  		public Corpse( Mobile owner, List<Item> equipItems ) : this( owner, null, null, equipItems )
  		{
  		}
  
  		public Corpse( Mobile owner, HairInfo hair, FacialHairInfo facialhair, List<Item> equipItems )
  			: base( 0x2006 )
  		{
  			// To supress console warnings, stackable must be true
  			Stackable = true;
  			Amount = owner.Body; // protocol defines that for itemid 0x2006, amount=body
  			Stackable = false;
  
  			Movable = false;
  			Hue = owner.Hue;
  			Direction = owner.Direction;
  			Name = owner.Name;
  
  			m_Owner = owner;
  
  
gt: 			m_CorpseName = GetCorpseName( owner );