using System;
using System.Collections;
using Server;
using Server.Gumps;
using Server.Items;
using Server.Mobiles;
using Server.Regions;
using System.Collections.Generic;

namespace Server.Engines.CannedEvil
{
	public class ChampionSpawn : Item
	{
		private bool m_Active;
		private bool m_RandomizeType;
		private ChampionSpawnType m_Type;
		private List<Mobile> m_Creatures;
		private List<Item> m_RedSkulls;
		private List<Item> m_WhiteSkulls;
		private ChampionPlatform m_Platform;
		private ChampionAltar m_Altar;
		private int m_Kills;
		private Mobile m_Champion;

		//private int m_SpawnRange;
		private Rectangle2D m_SpawnArea;
		private ChampionSpawnRegion m_Region;

		private TimeSpan m_ExpireDelay;
		private DateTime m_ExpireTime;

		private TimeSpan m_RestartDelay;
		private DateTime m_RestartTime;

		private Timer m_Timer, m_RestartTimer;

		private IdolOfTheChampion m_Idol;

		private bool m_HasBeenAdvanced;
		private bool m_ConfinedRoaming;

		private Dictionary<Mobile, int> m_DamageEntries;

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

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

		[Constructable]
		public ChampionSpawn() : base( 0xBD2 )
		{
			Movable = false;
			Visible = false;

			m_Creatures = new List<Mobile>();
			m_RedSkulls = new List<Item>();
			m_WhiteSkulls = new List<Item>();

			m_Platform = new ChampionPlatform( this );
			m_Altar = new ChampionAltar( this );
			m_Idol = new IdolOfTheChampion( this );

			m_ExpireDelay = TimeSpan.FromMinutes( 10.0 );
			m_RestartDelay = TimeSpan.FromMinutes( 10.0 );

			m_DamageEntries = new Dictionary<Mobile, int>();

			Timer.DelayCall( TimeSpan.Zero, new TimerCallback( SetInitialSpawnArea ) );
		}

		public void SetInitialSpawnArea()
		{
			//Previous default used to be 24;
			SpawnArea = new Rectangle2D( new Point2D( X - 24, Y - 24 ), new Point2D( X + 24, Y + 24 ) );
		}

		public void UpdateRegion()
		{
			if( m_Region != null )
				m_Region.Unregister();

			if( !Deleted && this.Map != Map.Internal )
			{
				m_Region = new ChampionSpawnRegion( this );
				m_Region.Register();
			}

			/*
			if( m_Region == null )
			{
				m_Region = new ChampionSpawnRegion( this );
			}
			else
			{
				m_Region.Unregister();
				//Why doesn't Region allow me to set it's map/Area meself? ><
				m_Region = new ChampionSpawnRegion( this );
			}
			*/
		}

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

		[CommandProperty( AccessLevel.GameMaster )]
		public int Kills
		{
			get
			{
				return m_Kills;
			}
			set
			{
				m_Kills = value;
				InvalidateProperties();
			}
		}

		[CommandProperty( AccessLevel.GameMaster )]
		public Rectangle2D SpawnArea
		{
			get
			{
				return m_SpawnArea;
			}
			set
			{
				m_SpawnArea = value;
				InvalidateProperties();
				UpdateRegion();
			}
		}

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

		[CommandProperty( AccessLevel.GameMaster )]
		public DateTime RestartTime
		{
			get
			{
				return m_RestartTime;
			}
		}

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

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

		[CommandProperty( AccessLevel.GameMaster )]
		public ChampionSpawnType Type
		{
			get
			{
				return m_Type;
			}
			set
			{
				m_Type = value;
				InvalidateProperties();
			}
		}

		[CommandProperty( AccessLevel.GameMaster )]
		public bool Active
		{
			get
			{
				return m_Active;
			}
			set
			{
				if( value )
					Start();
				else
					Stop();

				InvalidateProperties();
			}
		}

		[CommandProperty( AccessLevel.GameMaster )]
		public Mobile Champion
		{
			get
			{
				return m_Champion;
			}
			set
			{
				m_Champion = value;
			}
		}

		[CommandProperty( AccessLevel.GameMaster )]
		public int Level
		{
			get
			{
				return m_RedSkulls.Count;
			}
			set
			{
				for( int i = m_RedSkulls.Count - 1; i >= value; --i )
				{
					m_RedSkulls[i].Delete();
					m_RedSkulls.RemoveAt( i );
				}

				for( int i = m_RedSkulls.Count; i < value; ++i )
				{
					Item skull = new Item( 0x1854 );

					skull.Hue = 0x26;
					skull.Movable = false;
					skull.Light = LightType.Circle150;

					skull.MoveToWorld( GetRedSkullLocation( i ), Map );

					m_RedSkulls.Add( skull );
				}

				InvalidateProperties();
			}
		}

		public int MaxKills
		{
			get
			{
				return 250 - (Level * 12);
			}
		}

		public bool IsChampionSpawn( Mobile m )
		{
			return m_Creatures.Contains( m );
		}

		public void SetWhiteSkullCount( int val )
		{
			for( int i = m_WhiteSkulls.Count - 1; i >= val; --i )
			{
				m_WhiteSkulls[i].Delete();
				m_WhiteSkulls.RemoveAt( i );
			}

			for( int i = m_WhiteSkulls.Count; i < val; ++i )
			{
				Item skull = new Item( 0x1854 );

				skull.Movable = false;
				skull.Light = LightType.Circle150;

				skull.MoveToWorld( GetWhiteSkullLocation( i ), Map );

				m_WhiteSkulls.Add( skull );

				Effects.PlaySound( skull.Location, skull.Map, 0x29 );
				Effects.SendLocationEffect( new Point3D( skull.X + 1, skull.Y + 1, skull.Z ), skull.Map, 0x3728, 10 );
			}
		}

		public void Start()
		{
			if( m_Active || Deleted )
				return;

			m_Active = true;
			m_HasBeenAdvanced = false;

			if( m_Timer != null )
				m_Timer.Stop();

			m_Timer = new SliceTimer( this );
			m_Timer.Start();

			if( m_RestartTimer != null )
				m_RestartTimer.Stop();

			m_RestartTimer = null;

			if( m_Altar != null )
			{
				if ( m_Champion != null )
					m_Altar.Hue = 0x26;
				else
					m_Altar.Hue = 0;
			}

			if ( m_Platform != null )
				m_Platform.Hue = 0x452;
		}

		public void Stop()
		{
			if( !m_Active || Deleted )
				return;

			m_Active = false;
			m_HasBeenAdvanced = false;

			if( m_Timer != null )
				m_Timer.Stop();

			m_Timer = null;

			if( m_RestartTimer != null )
				m_RestartTimer.Stop();

			m_RestartTimer = null;

			if( m_Altar != null )
				m_Altar.Hue = 0;

			if ( m_Platform != null )
				m_Platform.Hue = 0x497;
		}

		public void BeginRestart( TimeSpan ts )
		{
			if( m_RestartTimer != null )
				m_RestartTimer.Stop();

			m_RestartTime = DateTime.Now + ts;

			m_RestartTimer = new RestartTimer( this, ts );
			m_RestartTimer.Start();
		}

		public void EndRestart()
		{
			if( RandomizeType )
			{
				switch( Utility.Random( 5 ) )
				{
					case 0: Type = ChampionSpawnType.VerminHorde; break;
					case 1: Type = ChampionSpawnType.UnholyTerror; break;
					case 2: Type = ChampionSpawnType.ColdBlood; break;
					case 3: Type = ChampionSpawnType.Abyss; break;
					case 4: Type = ChampionSpawnType.Arachnid; break;
				}
			}
			
			m_HasBeenAdvanced = false;

			Start();
		}

		#region Scroll of Transcendence
		private ScrollofTranscendence CreateRandomSoT( bool felucca )
		{
			int level = Utility.RandomMinMax( 1, 5 );
			
			if ( felucca )
				level += 5;

			return ScrollofTranscendence.CreateRandom(level, level);
		}
		#endregion

		public static void GiveScrollTo( Mobile killer, SpecialScroll scroll )
		{
			if( scroll == null || killer == null )	//sanity
				return;

			if ( scroll is ScrollofTranscendence )
				killer.SendLocalizedMessage( 1094936 ); // You have received a Scroll of Transcendence!
			else
				killer.SendLocalizedMessage( 1049524 ); // You have received a scroll of power!
			
			if ( killer.Alive )
				killer.AddToBackpack( scroll );
			else
			{
				if( killer.Corpse != null && !killer.Corpse.Deleted )
					killer.Corpse.DropItem( scroll );
				else
					killer.AddToBackpack( scroll );
			}
			
			// Justice reward
			PlayerMobile pm = (PlayerMobile)killer;
			for (int j = 0; j < pm.JusticeProtectors.Count; ++j)
			{
				Mobile prot = (Mobile)pm.JusticeProtectors[j];
				
				if ( prot.Map != killer.Map || prot.Kills >= 5 || prot.Criminal || !JusticeVirtue.CheckMapRegion( killer, prot ) )
					continue;

				int chance = 0;

				switch ( VirtueHelper.GetLevel( prot, VirtueName.Justice ) )
				{
					case VirtueLevel.Seeker: chance = 60; break;
					case VirtueLevel.Follower: chance = 80; break;
					case VirtueLevel.Knight: chance = 100; break;
				}

				if ( chance > Utility.Random( 100 ) )
				{
					try
					{
						prot.SendLocalizedMessage( 1049368 ); // You have been rewarded for your dedication to Justice!

						SpecialScroll scrollDupe = Activator.CreateInstance( scroll.GetType() ) as SpecialScroll;
					
						if ( scrollDupe != null )
						{
							scrollDupe.Skill = scroll.Skill;
							scrollDupe.Value = scroll.Value;
							prot.AddToBackpack( scrollDupe );
						}
					}
					catch{}
				}
			}
		}

		public void OnSlice()
		{
			if( !m_Active || Deleted )
				return;

			if( m_Champion != null )
			{
				if( m_Champion.Deleted )
				{
					RegisterDamageTo( m_Champion );

					if( m_Champion is BaseChampion )
						AwardArtifact( ((BaseChampion)m_Champion).GetArtifact() );

					m_DamageEntries.Clear();

					if( m_Platform != null )
						m_Platform.Hue = 0x497;

					if( m_Altar != null )
					{
						m_Altar.Hue = 0;

						if( !Core.ML || Map == Map.Felucca )
						{
							new StarRoomGate( true, m_Altar.Location, m_Altar.Map );
						}
					}

					m_Champion = null;
					Stop();

					BeginRestart( m_RestartDelay );
				}
			}
			else
			{
				int kills = m_Kills;

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

					if ( m.Deleted )
					{
						if( m.Corpse != null && !m.Corpse.Deleted )
						{
							((Corpse)m.Corpse).BeginDecay( TimeSpan.FromMinutes( 1 ));
						}
						m_Creatures.RemoveAt( i );
						--i;
						++m_Kills;

						Mobile killer = m.FindMostRecentDamager( false );

						RegisterDamageTo( m );

						if( killer is BaseCreature )
							killer = ((BaseCreature)killer).GetMaster();

						if( killer is PlayerMobile )
						{
							#region Scroll of Transcendence
							if ( Core.ML )
							{
								if ( Map == Map.Felucca )
								{
									if ( Utility.RandomDouble() < 0.001 )
									{
										PlayerMobile pm = (PlayerMobile)killer;
										double random = Utility.Random ( 49 );
										
										if ( random <= 24 )
										{
											ScrollofTranscendence SoTF = CreateRandomSoT( true );
											GiveScrollTo( pm, (SpecialScroll)SoTF );
										}
										else
										{
											PowerScroll PS = PowerScroll.CreateRandomNoCraft(5, 5);
											GiveScrollTo( pm, (SpecialScroll)PS );
										}
									}
								}

								if ( Map == Map.Ilshenar || Map == Map.Tokuno || Map == Map.Malas )
								{
									if ( Utility.RandomDouble() < 0.0015 )
									{
										killer.SendLocalizedMessage( 1094936 ); // You have received a Scroll of Transcendence!
										ScrollofTranscendence SoTT = CreateRandomSoT( false );
										killer.AddToBackpack( SoTT );
									}
								}
							}
							#endregion

							int mobSubLevel = GetSubLevelFor( m ) + 1;

							if( mobSubLevel >= 0 )
							{
								bool gainedPath = false;

								int pointsToGain = mobSubLevel * 40;

								if( VirtueHelper.Award( killer, VirtueName.Valor, pointsToGain, ref gainedPath ) )
								{
									if( gainedPath )
										m.SendLocalizedMessage( 1054032 ); // You have gained a path in Valor!
									else
										m.SendLocalizedMessage( 1054030 ); // You have gained in Valor!

									//No delay on Valor gains
								}

								PlayerMobile.ChampionTitleInfo info = ((PlayerMobile)killer).ChampionTitles;

								info.Award( m_Type, mobSubLevel );
							}
						}
					}
				}

				// Only really needed once.
				if ( m_Kills > kills )
					InvalidateProperties();

				double n = m_Kills / (double)MaxKills;
				int p = (int)(n * 100);

				if( p >= 90 )
					AdvanceLevel();
				else if( p > 0 )
					SetWhiteSkullCount( p / 20 );

				if( DateTime.Now >= m_ExpireTime )
					Expire();

				Respawn();
			}
		}

		public void AdvanceLevel()
		{
			m_ExpireTime = DateTime.Now + m_ExpireDelay;

			if( Level < 16 )
			{
				m_Kills = 0;
				++Level;
				InvalidateProperties();
				SetWhiteSkullCount( 0 );

				if( m_Altar != null )
				{
					Effects.PlaySound( m_Altar.Location, m_Altar.Map, 0x29 );
					Effects.SendLocationEffect( new Point3D( m_Altar.X + 1, m_Altar.Y + 1, m_Altar.Z ), m_Altar.Map, 0x3728, 10 );
				}
			}
			else
			{
				SpawnChampion();
			}
		}

		public void SpawnChampion()
		{
			if( m_Altar != null )
				m_Altar.Hue = 0x26;

			if ( m_Platform != null )
				m_Platform.Hue = 0x452;

			m_Kills = 0;
			Level = 0;
			InvalidateProperties();
			SetWhiteSkullCount( 0 );

			try
			{
				m_Champion = Activator.CreateInstance( ChampionSpawnInfo.GetInfo( m_Type ).Champion ) as Mobile;
			}
			catch { }

			if( m_Champion != null )
				m_Champion.MoveToWorld( new Point3D( X, Y, Z - 15 ), Map );
		}

		public void Respawn()
		{
			if( !m_Active || Deleted || m_Champion != null )
				return;

			while( m_Creatures.Count < (200 - (GetSubLevel() * 40)) )
			{
				Mobile m = Spawn();

				if( m == null )
					return;

				Point3D loc = GetSpawnLocation();

				// Allow creatures to turn into Paragons at Ilshenar champions.
				m.OnBeforeSpawn( loc, Map );

				m_Creatures.Add( m );
				m.MoveToWorld( loc, Map );

				if( m is BaseCreature )
				{
					BaseCreature bc = m as BaseCreature;
					bc.Tamable = false;

					if( !m_ConfinedRoaming )
					{
						bc.Home = this.Location;
						bc.RangeHome = (int)(Math.Sqrt( m_SpawnArea.Width * m_SpawnArea.Width + m_SpawnArea.Height * m_SpawnArea.Height )/2);
					}
					else
					{
						bc.Home = bc.Location;

						Point2D xWall1 = new Point2D( m_SpawnArea.X, bc.Y );
						Point2D xWall2 = new Point2D( m_SpawnArea.X + m_SpawnArea.Width, bc.Y );
						Point2D yWall1 = new Point2D( bc.X, m_SpawnArea.Y );
						Point2D yWall2 = new Point2D( bc.X, m_SpawnArea.Y + m_SpawnArea.Height );

						double minXDist = Math.Min( bc.GetDistanceToSqrt( xWall1 ), bc.GetDistanceToSqrt( xWall2 ) );
						double minYDist = Math.Min( bc.GetDistanceToSqrt( yWall1 ), bc.GetDistanceToSqrt( yWall2 ) );

						bc.RangeHome = (int)Math.Min( minXDist, minYDist );
					}
				}
			}
		}

		public Point3D GetSpawnLocation()
		{
			Map map = Map;

			if( map == null )
				return Location;

			// Try 20 times to find a spawnable location.
			for( int i = 0; i < 20; i++ )
			{
				/*
				int x = Location.X + (Utility.Random( (m_SpawnRange * 2) + 1 ) - m_SpawnRange);
				int y = Location.Y + (Utility.Random( (m_SpawnRange * 2) + 1 ) - m_SpawnRange);
				*/

				int x = Utility.Random( m_SpawnArea.X, m_SpawnArea.Width );
				int y = Utility.Random( m_SpawnArea.Y, m_SpawnArea.Height );

				int z = Map.GetAverageZ( x, y );

				if( Map.CanSpawnMobile( new Point2D( x, y ), z ) )
					return new Point3D( x, y, z );
			}

			return Location;
		}

		private const int Level1 = 4;  // First spawn level from 0-4 red skulls
		private const int Level2 = 8;  // Second spawn level from 5-8 red skulls
		private const int Level3 = 12; // Third spawn level from 9-12 red skulls

		public int GetSubLevel()
		{
			int level = this.Level;

			if( level <= Level1 )
				return 0;
			else if( level <= Level2 )
				return 1;
			else if( level <= Level3 )
				return 2;

			return 3;
		}

		public int GetSubLevelFor( Mobile m )
		{
			Type[][] types = ChampionSpawnInfo.GetInfo( m_Type ).SpawnTypes;
			Type t = m.GetType();

			for( int i = 0; i < types.GetLength( 0 ); i++ )
			{
				Type[] individualTypes = types[i];

				for( int j = 0; j < individualTypes.Length; j++ )
				{
					if( t == individualTypes[j] )
						return i;
				}
			}

			return -1;
		}

		public Mobile Spawn()
		{
			Type[][] types = ChampionSpawnInfo.GetInfo( m_Type ).SpawnTypes;

			int v = GetSubLevel();

			if( v >= 0 && v < types.Length )
				return Spawn( types[v] );

			return null;
		}

		public Mobile Spawn( params Type[] types )
		{
			try
			{
				return Activator.CreateInstance( types[Utility.Random( types.Length )] ) as Mobile;
			}
			catch
			{
				return null;
			}
		}

		public void Expire()
		{
			m_Kills = 0;

			if( m_WhiteSkulls.Count == 0 )
			{
				// They didn't even get 20%, go back a level

				if( Level > 0 )
					--Level;

				InvalidateProperties();
			}
			else
			{
				SetWhiteSkullCount( 0 );
			}

			m_ExpireTime = DateTime.Now + m_ExpireDelay;
		}

		public Point3D GetRedSkullLocation( int index )
		{
			int x, y;

			if( index < 5 )
			{
				x = index - 2;
				y = -2;
			}
			else if( index < 9 )
			{
				x = 2;
				y = index - 6;
			}
			else if( index < 13 )
			{
				x = 10 - index;
				y = 2;
			}
			else
			{
				x = -2;
				y = 14 - index;
			}

			return new Point3D( X + x, Y + y, Z - 15 );
		}

		public Point3D GetWhiteSkullLocation( int index )
		{
			int x, y;

			switch( index )
			{
				default:
				case 0: x = -1; y = -1; break;
				case 1: x =  1; y = -1; break;
				case 2: x =  1; y =  1; break;
				case 3: x = -1; y =  1; break;
			}

			return new Point3D( X + x, Y + y, Z - 15 );
		}

		public override void AddNameProperty( ObjectPropertyList list )
		{
			list.Add( "champion spawn" );
		}

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

			if( m_Active )
			{
				list.Add( 1060742 ); // active
				list.Add( 1060658, "Type\t{0}", m_Type ); // ~1_val~: ~2_val~
				list.Add( 1060659, "Level\t{0}", Level ); // ~1_val~: ~2_val~
				list.Add( 1060660, "Kills\t{0} of {1} ({2:F1}%)", m_Kills, MaxKills, 100.0 * ((double)m_Kills / MaxKills) ); // ~1_val~: ~2_val~
				//list.Add( 1060661, "Spawn Range\t{0}", m_SpawnRange ); // ~1_val~: ~2_val~
			}
			else
			{
				list.Add( 1060743 ); // inactive
			}
		}

		public override void OnSingleClick( Mobile from )
		{
			if( m_Active )
				LabelTo( from, "{0} (Active; Level: {1}; Kills: {2}/{3})", m_Type, Level, m_Kills, MaxKills );
			else
				LabelTo( from, "{0} (Inactive)", m_Type );
		}

		public override void OnDoubleClick( Mobile from )
		{
			from.SendGump( new PropertiesGump( from, this ) );
		}

		public override void OnLocationChange( Point3D oldLoc )
		{
			if( Deleted )
				return;

			if( m_Platform != null )
				m_Platform.Location = new Point3D( X, Y, Z - 20 );

			if( m_Altar != null )
				m_Altar.Location = new Point3D( X, Y, Z - 15 );

			if( m_Idol != null )
				m_Idol.Location = new Point3D( X, Y, Z - 15 );

			if( m_RedSkulls != null )
			{
				for( int i = 0; i < m_RedSkulls.Count; ++i )
					m_RedSkulls[i].Location = GetRedSkullLocation( i );
			}

			if( m_WhiteSkulls != null )
			{
				for( int i = 0; i < m_WhiteSkulls.Count; ++i )
					m_WhiteSkulls[i].Location = GetWhiteSkullLocation( i );
			}

			m_SpawnArea.X += Location.X - oldLoc.X;
			m_SpawnArea.Y += Location.Y - oldLoc.Y;

			UpdateRegion();
		}

		public override void OnMapChange()
		{
			if( Deleted )
				return;

			if( m_Platform != null )
				m_Platform.Map = Map;

			if( m_Altar != null )
				m_Altar.Map = Map;

			if( m_Idol != null )
				m_Idol.Map = Map;

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

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

			UpdateRegion();
		}

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

			if( m_Platform != null )
				m_Platform.Delete();

			if( m_Altar != null )
				m_Altar.Delete();

			if( m_Idol != null )
				m_Idol.Delete();

			if( m_RedSkulls != null )
			{
				for( int i = 0; i < m_RedSkulls.Count; ++i )
					m_RedSkulls[i].Delete();

				m_RedSkulls.Clear();
			}

			if( m_WhiteSkulls != null )
			{
				for( int i = 0; i < m_WhiteSkulls.Count; ++i )
					m_WhiteSkulls[i].Delete();

				m_WhiteSkulls.Clear();
			}

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

					if( !mob.Player )
						mob.Delete();
				}

				m_Creatures.Clear();
			}

			if( m_Champion != null && !m_Champion.Player )
				m_Champion.Delete();

			Stop();

			UpdateRegion();
		}

		public ChampionSpawn( Serial serial ) : base( serial )
		{
		}

		public virtual void RegisterDamageTo( Mobile m )
		{
			if( m == null )
				return;

			foreach( DamageEntry de in m.DamageEntries )
			{
				if( de.HasExpired )
					continue;

				Mobile damager = de.Damager;

				Mobile master = damager.GetDamageMaster( m );

				if( master != null )
					damager = master;

				RegisterDamage( damager, de.DamageGiven );
			}
		}

		public void RegisterDamage( Mobile from, int amount )
		{
			if( from == null || !from.Player )
				return;

			if( m_DamageEntries.ContainsKey( from ) )
				m_DamageEntries[from] += amount;
			else
				m_DamageEntries.Add( from, amount );
		}

		public void AwardArtifact( Item artifact )
		{
			if (artifact == null )
				return;

			int totalDamage = 0;

			Dictionary<Mobile, int> validEntries = new Dictionary<Mobile, int>();

			foreach (KeyValuePair<Mobile, int> kvp in m_DamageEntries)
			{
				if( IsEligible( kvp.Key, artifact ) )
				{
					validEntries.Add( kvp.Key, kvp.Value );
					totalDamage += kvp.Value;
				}
			}

			int randomDamage = Utility.RandomMinMax( 1, totalDamage );

			totalDamage = 0;

			foreach (KeyValuePair<Mobile, int> kvp in validEntries)
			{
				totalDamage += kvp.Value;

				if( totalDamage >= randomDamage )
				{
					GiveArtifact( kvp.Key, artifact );
					return;
				}
			}

			artifact.Delete();
		}

		public void GiveArtifact( Mobile to, Item artifact )
		{
			if ( to == null || artifact == null )
				return;

			Container pack = to.Backpack;

			if ( pack == null || !pack.TryDropItem( to, artifact, false ) )
				artifact.Delete();
			else
				to.SendLocalizedMessage( 1062317 ); // For your valor in combating the fallen beast, a special artifact has been bestowed on you.
		}

		public bool IsEligible( Mobile m, Item Artifact )
		{
			return m.Player && m.Alive && m.Region != null && m.Region == m_Region && m.Backpack != null && m.Backpack.CheckHold( m, Artifact, false );
		}

		public override void Serialize( GenericWriter writer )
		{
			base.Serialize( writer );

			writer.Write( (int)5 ); // version

			writer.Write( m_DamageEntries.Count );
			foreach (KeyValuePair<Mobile, int> kvp in m_DamageEntries)
			{
				writer.Write( kvp.Key );
				writer.Write( kvp.Value );
			}

			writer.Write( m_ConfinedRoaming );
			writer.WriteItem<IdolOfTheChampion>( m_Idol );
			writer.Write( m_HasBeenAdvanced );
			writer.Write( m_SpawnArea );

			writer.Write( m_RandomizeType );

			//			writer.Write( m_SpawnRange );
			writer.Write( m_Kills );

			writer.Write( (bool)m_Active );
			writer.Write( (int)m_Type );
			writer.Write( m_Creatures, true );
			writer.Write( m_RedSkulls, true );
			writer.Write( m_WhiteSkulls, true );
			writer.WriteItem<ChampionPlatform>( m_Platform );
			writer.WriteItem<ChampionAltar>( m_Altar );
			writer.Write( m_ExpireDelay );
			writer.WriteDeltaTime( m_ExpireTime );
			writer.Write( m_Champion );
			writer.Write( m_RestartDelay );

			writer.Write( m_RestartTimer != null );

			if( m_RestartTimer != null )
				writer.WriteDeltaTime( m_RestartTime );
		}

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

			m_DamageEntries = new Dictionary<Mobile, int>();

			int version = reader.ReadInt();

			switch( version )
			{
				case 5:
				{
					int entries = reader.ReadInt();
					Mobile m;
					int damage;
					for( int i = 0; i < entries; ++i )
					{
						m = reader.ReadMobile();
						damage = reader.ReadInt();

						if ( m == null )
							continue;

						m_DamageEntries.Add( m, damage );
					}

					goto case 4;
				}
				case 4:
				{
					m_ConfinedRoaming = reader.ReadBool();
					m_Idol = reader.ReadItem<IdolOfTheChampion>();
					m_HasBeenAdvanced = reader.ReadBool();

					goto case 3;
				}
				case 3:
				{
					m_SpawnArea = reader.ReadRect2D();

					goto case 2;
				}
				case 2:
				{
					m_RandomizeType = reader.ReadBool();

					goto case 1;
				}
				case 1:
				{
					if( version < 3 )
					{
						int oldRange = reader.ReadInt();

						m_SpawnArea = new Rectangle2D( new Point2D( X - oldRange, Y - oldRange ), new Point2D( X + oldRange, Y + oldRange ) );
					}

					m_Kills = reader.ReadInt();

					goto case 0;
				}
				case 0:
				{
					if( version < 1 )
						m_SpawnArea = new Rectangle2D( new Point2D( X - 24, Y - 24 ), new Point2D( X + 24, Y + 24 ) );	//Default was 24

					bool active = reader.ReadBool();
					m_Type = (ChampionSpawnType)reader.ReadInt();
					m_Creatures = reader.ReadStrongMobileList();
					m_RedSkulls = reader.ReadStrongItemList();
					m_WhiteSkulls = reader.ReadStrongItemList();
					m_Platform = reader.ReadItem<ChampionPlatform>();
					m_Altar = reader.ReadItem<ChampionAltar>();
					m_ExpireDelay = reader.ReadTimeSpan();
					m_ExpireTime = reader.ReadDeltaTime();
					m_Champion = reader.ReadMobile();
					m_RestartDelay = reader.ReadTimeSpan();

					if( reader.ReadBool() )
					{
						m_RestartTime = reader.ReadDeltaTime();
						BeginRestart( m_RestartTime - DateTime.Now );
					}

					if( version < 4 )
					{
						m_Idol = new IdolOfTheChampion( this );
						m_Idol.MoveToWorld( new Point3D( X, Y, Z - 15 ), Map );
					}

					if( m_Platform == null || m_Altar == null || m_Idol == null )
						Delete();
					else if( active )
						Start();

					break;
				}
			}

			Timer.DelayCall( TimeSpan.Zero, new TimerCallback( UpdateRegion ) );
		}
	}

	public class ChampionSpawnRegion : BaseRegion
	{
		public override bool YoungProtected { get { return false; } }

		private ChampionSpawn m_Spawn;

		public ChampionSpawn ChampionSpawn
		{
			get { return m_Spawn; }
		}

		public ChampionSpawnRegion( ChampionSpawn spawn ) : base( null, spawn.Map, Region.Find( spawn.Location, spawn.Map ), spawn.SpawnArea )
		{
			m_Spawn = spawn;
		}

		public override bool AllowHousing( Mobile from, Point3D p )
		{
			return false;
		}

		public override void AlterLightLevel( Mobile m, ref int global, ref int personal )
		{
			base.AlterLightLevel( m, ref global, ref personal );
			global = Math.Max( global, 1 + m_Spawn.Level );	//This is a guesstimate.  TODO: Verify & get exact values // OSI testing: at 2 red skulls, light = 0x3 ; 1 red = 0x3.; 3 = 8; 9 = 0xD 8 = 0xD 12 = 0x12 10 = 0xD
		}
	}

	public class IdolOfTheChampion : Item
	{
		private ChampionSpawn m_Spawn;

		public ChampionSpawn Spawn { get { return m_Spawn; } }

		public override string DefaultName
		{
			get { return "Idol of the Champion"; }
		}


		public IdolOfTheChampion( ChampionSpawn spawn ): base( 0x1F18 )
		{
			m_Spawn = spawn;
			Movable = false;
		}

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

			if ( m_Spawn != null )
				m_Spawn.Delete();
		}

		public IdolOfTheChampion( Serial serial ) : base( serial )
		{
		}

		public override void Serialize( GenericWriter writer )
		{
			base.Serialize( writer );

			writer.Write( (int) 0 ); // version

			writer.Write( m_Spawn );
		}

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

			int version = reader.ReadInt();

			switch ( version )
			{
				case 0:
				{
					m_Spawn = reader.ReadItem() as ChampionSpawn;

					if ( m_Spawn == null )
						Delete();

					break;
				}
			}
		}
	}
}