context: using System;
  using System.Collections.Generic;
  using Server.Mobiles;
  
  namespace Server.Engines.ConPVP
  {
      public class ArenaController : Item
  	{
  		private Arena m_Arena;
  		private bool m_IsPrivate;
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public Arena Arena{ get{ return m_Arena; } set{} }
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public bool IsPrivate{ get{ return m_IsPrivate; } set{ m_IsPrivate = value; } }
  
  		public override string DefaultName
  		{
  			get { return "arena controller"; }
  		}
  
  		[Constructable]
  		public ArenaController() : base( 0x1B7A )
  		{
  			Visible = false;
  			Movable = false;
  
  			m_Arena = new Arena();
  
  			m_Instances.Add( this );
  		}
  
  		public override void OnDelete()
  		{
  			base.OnDelete();
  
  			m_Instances.Remove( this );
  			m_Arena.Delete();
  		}
  
  		public override void OnDoubleClick( Mobile from )
  		{
  			if ( from.AccessLevel >= AccessLevel.GameMaster )
  				from.SendGump( new Gumps.PropertiesGump( from, m_Arena ) );
  		}
  
  		public ArenaController( Serial serial ) : base( serial )
  		{
  		}
  
  		public override void Serialize( GenericWriter writer )
  		{
  			base.Serialize( writer );
  
  			writer.Write( (int) 1 );
  
  			writer.Write( (bool) m_IsPrivate );
  
  			m_Arena.Serialize( writer );
  		}
  
  		public override void Deserialize( GenericReader reader )
  		{
  			base.Deserialize( reader );
  
  			int version = reader.ReadInt();
  
  			switch ( version )
  			{
  				case 1:
  				{
  					m_IsPrivate = reader.ReadBool();
  
  					goto case 0;
  				}
  				case 0:
  				{
  					m_Arena = new Arena( reader );
  					break;
  				}
  			}
  
  			m_Instances.Add( this );
  		}
  
  		private static List<ArenaController> m_Instances = new List<ArenaController>();
  
  		public static List<ArenaController> Instances{ get{ return m_Instances; } set{ m_Instances = value; } }
  	}
  
  	[PropertyObject]
  	public class ArenaStartPoints
  	{
  		private Point3D[] m_Points;
  
  		public Point3D[] Points{ get{ return m_Points; } }
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public Point3D EdgeWest{ get{ return m_Points[0]; } set{ m_Points[0] = value; } }
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public Point3D EdgeEast{ get{ return m_Points[1]; } set{ m_Points[1] = value; } }
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public Point3D EdgeNorth{ get{ return m_Points[2]; } set{ m_Points[2] = value; } }
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public Point3D EdgeSouth{ get{ return m_Points[3]; } set{ m_Points[3] = value; } }
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public Point3D CornerNW{ get{ return m_Points[4]; } set{ m_Points[4] = value; } }
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public Point3D CornerSE{ get{ return m_Points[5]; } set{ m_Points[5] = value; } }
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public Point3D CornerSW{ get{ return m_Points[6]; } set{ m_Points[6] = value; } }
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public Point3D CornerNE{ get{ return m_Points[7]; } set{ m_Points[7] = value; } }
  
  		public override string ToString()
  		{
  			return "...";
  		}
  
  		public ArenaStartPoints() : this( new Point3D[8] )
  		{
  		}
  
  		public ArenaStartPoints( Point3D[] points )
  		{
  			m_Points = points;
  		}
  
  		public ArenaStartPoints( GenericReader reader )
  		{
  			m_Points = new Point3D[reader.ReadEncodedInt()];
  
  			for ( int i = 0; i < m_Points.Length; ++i )
  				m_Points[i] = reader.ReadPoint3D();
  		}
  
  		public void Serialize( GenericWriter writer )
  		{
  			writer.WriteEncodedInt( (int) m_Points.Length );
  
  			for ( int i = 0; i < m_Points.Length; ++i )
  				writer.Write( (Point3D) m_Points[i] );
  		}
  	}
  
  	[PropertyObject]
  	public class Arena : IComparable
  	{
  		private Map m_Facet;
  		private Rectangle2D m_Bounds;
  		private Rectangle2D m_Zone;
  		private Point3D m_Outside;
  		private Point3D m_Wall;
  		private Point3D m_GateIn;
  		private Point3D m_GateOut;
  		private ArenaStartPoints m_Points;
  		private bool m_Active;
  		private string m_Name;
  
  		private bool m_IsGuarded;
  
  		private Item m_Teleporter;
  
  		private List<Mobile> m_Players;
  
  		private TournamentController m_Tournament;
  		private Mobile m_Announcer;
  
  		private LadderController m_Ladder;
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public LadderController Ladder
  		{
  			get{ return m_Ladder; }
  			set{ m_Ladder = value; }
  		}
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public bool IsGuarded
  		{
  			get{ return m_IsGuarded; }
  			set
  			{
  				m_IsGuarded = value;
  
  				if ( m_Region != null )
  					m_Region.Disabled = !m_IsGuarded;
  			}
  		}
  
  		public Ladder AcquireLadder()
  		{
  			if ( m_Ladder != null )
  				return m_Ladder.Ladder;
  
  			return Server.Engines.ConPVP.Ladder.Instance;
  		}
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public TournamentController Tournament
  		{
  			get{ return m_Tournament; }
  			set
  			{
  				if ( m_Tournament != null )
  					m_Tournament.Tournament.Arenas.Remove( this );
  
  				m_Tournament = value;
  
  				if ( m_Tournament != null )
  					m_Tournament.Tournament.Arenas.Add( this );
  			}
  		}
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public Mobile Announcer
  		{
  			get{ return m_Announcer; }
  			set{ m_Announcer = value; }
  		}
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public string Name
  		{
  			get{ return m_Name; }
  			set{ m_Name = value; if ( m_Active ) m_Arenas.Sort(); }
  		}
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public Map Facet
  		{
  			get{ return m_Facet; }
  			set
  			{
  				m_Facet = value;
  
  				if ( m_Teleporter != null )
  					m_Teleporter.Map = value;
  
  				if ( m_Region != null )
  					m_Region.Unregister();
  
  				if ( m_Zone.Start != Point2D.Zero && m_Zone.End != Point2D.Zero && m_Facet != null )
  					m_Region = new SafeZone( m_Zone, m_Outside, m_Facet, m_IsGuarded );
  				else
  					m_Region = null;
  			}
  		}
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public Rectangle2D Bounds{ get{ return m_Bounds; } set{ m_Bounds = value; } }
  
  		private SafeZone m_Region;
  
  		public int Spectators
  		{
  			get
  			{
  				if ( m_Region == null )
  					return 0;
  
  				int specs = m_Region.GetPlayerCount() - m_Players.Count;
  
  				if ( specs < 0 )
  					specs = 0;
  
  				return specs;
  			}
  		}
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public Rectangle2D Zone
  		{
  			get{ return m_Zone; }
  			set
  			{
  				m_Zone = value;
  
  				if ( m_Zone.Start != Point2D.Zero && m_Zone.End != Point2D.Zero && m_Facet != null )
  				{
  					if ( m_Region != null )
  						m_Region.Unregister();
  
  					m_Region = new SafeZone( m_Zone, m_Outside, m_Facet, m_IsGuarded );
  				}
  				else
  				{
  					if ( m_Region != null )
  						m_Region.Unregister();
  
  					m_Region = null;
  				}
  			}
  		}
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public Point3D Outside{ get{ return m_Outside; } set{ m_Outside = value; } }
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public Point3D GateIn{ get{ return m_GateIn; } set{ m_GateIn = value; } }
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public Point3D GateOut{ get{ return m_GateOut; } set{ m_GateOut = value; if ( m_Teleporter != null ) m_Teleporter.Location = m_GateOut; } }
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public Point3D Wall{ get{ return m_Wall; } set{ m_Wall = value; } }
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public bool IsOccupied{ get{ return ( m_Players.Count > 0 ); } }
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public ArenaStartPoints Points{ get{ return m_Points; } set{} }
  
  		public Item Teleporter{ get{ return m_Teleporter; } set{ m_Teleporter = value; } }
  
  		public List<Mobile> Players{ get{ return m_Players; } }
  
  		[CommandProperty( AccessLevel.GameMaster )]
  		public bool Active
  		{
  			get{ return m_Active; }
  			set
  			{
  				if ( m_Active == value )
  					return;
  
  				m_Active = value;
  
  				if ( m_Active )
  				{
  					m_Arenas.Add( this );
  					m_Arenas.Sort();
  				}
  				else
  				{
  					m_Arenas.Remove( this );
  				}
  			}
  		}
  
  		public void Delete()
  		{
  			 Active = false;
  
  			 if ( m_Region != null )
  				 m_Region.Unregister();
  
  			 m_Region = null;
  		}
  
  		public override string ToString()
  		{
  			return "...";
  		}
  
  		public Point3D GetBaseStartPoint( int index )
  		{
  			if ( index < 0 )
  				index = 0;
  
  			return m_Points.Points[index % m_Points.Points.Length];
  		}
  
  		#region Offsets & Rotation
  		private static Point2D[] m_EdgeOffsets = new Point2D[]
  			{
  				/*
  				 *        /\
  				 *       /\/\
  				 *      /\/\/\
  				 *      \/\/\/
  				 *       \/\/\
  				 *        \/\/
  				 */
  				new Point2D( 0, 0 ),
  				new Point2D( 0, -1 ),
  				new Point2D( 0, +1 ),
  				new Point2D( 1, 0 ),
  				new Point2D( 1, -1 ),
  				new Point2D( 1, +1 ),
  				new Point2D( 2, 0 ),
  				new Point2D( 2, -1 ),
  				new Point2D( 2, +1 ),
  				new Point2D( 3, 0 )
  			};
  
  		// nw corner
  		private static Point2D[] m_CornerOffsets = new Point2D[]
  			{
  				/*
  				 *         /\
  				 *        /\/\
  				 *       /\/\/\
  				 *      /\/\/\/\
  				 *      \/\/\/\/
  				 */
  				new Point2D( 0, 0 ),
  				new Point2D( 0, 1 ),
  				new Point2D( 1, 0 ),
  				new Point2D( 1, 1 ),
  				new Point2D( 0, 2 ),
  				new Point2D( 2, 0 ),
  				new Point2D( 2, 1 ),
  				new Point2D( 1, 2 ),
  				new Point2D( 0, 3 ),
  				new Point2D( 3, 0 )
  			};
  
  		private static int[][,] m_Rotate = new int[][,]
  			{
  				new int[,]{ { +1, 0 }, { 0, +1 } }, // west
  				new int[,]{ { -1, 0 }, { 0, -1 } }, // east
  				new int[,]{ { 0, +1 }, { +1, 0 } }, // north
  				new int[,]{ { 0, -1 }, { -1, 0 } }, // south
  				new int[,]{ { +1, 0 }, { 0, +1 } }, // nw
  				new int[,]{ { -1, 0 }, { 0, -1 } }, // se
  				new int[,]{ { 0, +1 }, { +1, 0 } }, // sw
  				new int[,]{ { 0, -1 }, { -1, 0 } }, // ne
  			};
  		#endregion
  
  		public void MoveInside( DuelPlayer[] players, int index )
  		{
  			if ( index < 0 )
  				index = 0;
  			else
  				index %= m_Points.Points.Length;
  
  			Point3D start = GetBaseStartPoint( index );
  
  			int offset = 0;
  
  			Point2D[] offsets = ( index < 4 ) ? m_EdgeOffsets : m_CornerOffsets;
  			int[,] matrix = m_Rotate[index];
  
  			for ( int i = 0; i < players.Length; ++i )
  			{
  				DuelPlayer pl = players[i];
  
  				if ( pl == null )
  					continue;
  
  				Mobile mob = pl.Mobile;
  
  				Point2D p;
  
  				if ( offset < offsets.Length )
  					p = offsets[offset++];
  				else
  					p = offsets[offsets.Length - 1];
  
  				p.X = (p.X * matrix[0, 0]) + (p.Y * matrix[0, 1]);
  				p.Y = (p.X * matrix[1, 0]) + (p.Y * matrix[1, 1]);
  
  				mob.MoveToWorld( new Point3D( start.X + p.X, start.Y + p.Y, start.Z ), m_Facet );
  				mob.Direction = mob.GetDirectionTo( m_Wall );
  
  				m_Players.Add( mob );
  			}
  		}
  
  		public Arena()
  		{
  			m_Points = new ArenaStartPoints();
  			m_Players = new List<Mobile>();
  		}
  
  		public Arena( GenericReader reader )
  		{
  			int version = reader.ReadEncodedInt();
  
  			switch ( version )
  			{
  				case 7:
  				{
  					m_IsGuarded = reader.ReadBool();
  
  					goto case 6;
  				}
  				case 6:
  				{
  					m_Ladder = reader.ReadItem() as LadderController;
  
  					goto case 5;
  				}
  				case 5:
  				{
  					m_Tournament = reader.ReadItem() as TournamentController;
  					m_Announcer = reader.ReadMobile();
  
  					goto case 4;
  				}
  				case 4:
  				{
  					m_Name = reader.ReadString();
  
  					goto case 3;
  				}
  				case 3:
  				{
  					m_Zone = reader.ReadRect2D();
  
  					goto case 2;
  				}
  				case 2:
  				{
  					m_GateIn = reader.ReadPoint3D();
  					m_GateOut = reader.ReadPoint3D();
  					m_Teleporter = reader.ReadItem();
  
  					goto case 1;
  				}
  				case 1:
  				{
  					m_Players = reader.ReadStrongMobileList();
  
  					goto case 0;
  				}
  				case 0:
  				{
  					m_Facet = reader.ReadMap();
  					m_Bounds = reader.ReadRect2D();
  					m_Outside = reader.ReadPoint3D();
  					m_Wall = reader.ReadPoint3D();
  
  					if ( version == 0 )
  					{
  						reader.ReadBool();
  						m_Players = new List<Mobile>();
  					}
  
  					m_Active = reader.ReadBool();
  					m_Points = new ArenaStartPoints( reader );
  
  					if ( m_Active )
  					{
  						m_Arenas.Add( this );
  						m_Arenas.Sort();
  					}
  
  					break;
  				}
  			}
  
  			if ( m_Zone.Start != Point2D.Zero && m_Zone.End != Point2D.Zero && m_Facet != null )
  				m_Region = new SafeZone( m_Zone, m_Outside, m_Facet, m_IsGuarded );
  
  			if ( IsOccupied )
  				Timer.DelayCall( TimeSpan.FromSeconds( 2.0 ), new TimerCallback( Evict ) );
  
  			if ( m_Tournament != null )
  				Timer.DelayCall( TimeSpan.Zero, new TimerCallback( AttachToTournament_Sandbox ) );
  		}
  
  		private void AttachToTournament_Sandbox()
  		{
  			if ( m_Tournament != null )
  				m_Tournament.Tournament.Arenas.Add( this );
  		}
  
  		[CommandProperty( AccessLevel.Administrator, AccessLevel.Administrator )]
  		public bool ForceEvict{ get{ return false; } set{ if ( value ) Evict(); } }
  
  		public void Evict()
  		{
  			Point3D loc;
  			Map facet;
  
  			if ( m_Facet == null )
  			{
  				loc = new Point3D( 2715, 2165, 0 );
  				facet = Map.Felucca;
  			}
  			else
  			{
  				loc = m_Outside;
  				facet = m_Facet;
  			}
  
  			bool hasBounds = ( m_Bounds.Start != Point2D.Zero && m_Bounds.End != Point2D.Zero );
  
  			for ( int i = 0; i < m_Players.Count; ++i )
  			{
  				Mobile mob = m_Players[i];
  
  				if ( mob == null )
  					continue;
  
  				if ( mob.Map == Map.Internal )
  				{
  					if ( (m_Facet == null || mob.LogoutMap == m_Facet) && (!hasBounds || m_Bounds.Contains( mob.LogoutLocation )) )
  						mob.LogoutLocation = loc;
  				}
  				else if ( (m_Facet == null || mob.Map == m_Facet) && (!hasBounds || m_Bounds.Contains( mob.Location )) )
  				{
  					mob.MoveToWorld( loc, facet );
  				}
  
  				mob.Combatant = null;
  				mob.Frozen = false;
  				DuelContext.Debuff( mob );
  				DuelContext.CancelSpell( mob );
  			}
  
  			if ( hasBounds ) {
  				List<Mobile> pets = new List<Mobile>();
  
  				foreach ( Mobile mob in facet.GetMobilesInBounds( m_Bounds ) ) {
  					BaseCreature pet = mob as BaseCreature;
  
  					if ( pet != null && pet.Controlled && pet.ControlMaster != null ) {
  						if ( m_Players.Contains( pet.ControlMaster ) ) {
  							pets.Add( pet );
  						}
  					}
  				}
  
  				foreach ( Mobile pet in pets ) {
  					pet.Combatant = null;
  					pet.Frozen = false;
  
  					pet.MoveToWorld( loc, facet );
  				}
  			}
  
  			m_Players.Clear();
  		}
  
  		public void Serialize( GenericWriter writer )
  		{
  			writer.WriteEncodedInt( (int) 7 );
  
  			writer.Write( (bool) m_IsGuarded );
  
  			writer.Write( (Item) m_Ladder );
  
  			writer.Write( (Item) m_Tournament );
  			writer.Write( (Mobile) m_Announcer );
  
  			writer.Write( (string) m_Name );
  
  			writer.Write( (Rectangle2D) m_Zone );
  
  			writer.Write( (Point3D) m_GateIn );
  			writer.Write( (Point3D) m_GateOut );
  			writer.Write( (Item) m_Teleporter );
  
  			writer.Write( m_Players );
  
  			writer.Write( (Map) m_Facet );
  			writer.Write( (Rectangle2D) m_Bounds );
  			writer.Write( (Point3D) m_Outside );
  			writer.Write( (Point3D) m_Wall );
  			writer.Write( (bool) m_Active );
  
  			m_Points.Serialize( writer );
  		}
  
  		private static List<Arena> m_Arenas = new List<Arena>();
  
  		public static List<Arena> Arenas{ get{ return m_Arenas; } }
  
  		public static Arena FindArena( List<Mobile> players )
  		{
  			Preferences prefs = Preferences.Instance;
  
  			if ( prefs == null )
  				return FindArena();
  
  			if ( m_Arenas.Count == 0 )
  				return null;
  
  			if ( players.Count > 0 )
  			{
  				Mobile first = players[0];
  
  				List<ArenaController> allControllers = ArenaController.Instances;
  
  				for ( int i = 0; i < allControllers.Count; ++i )
  				{
  					ArenaController controller = allControllers[i];
  
  					if ( controller != null && !controller.Deleted && controller.Arena != null && controller.IsPrivate && controller.Map == first.Map && first.InRange( controller, 24 ) )
  					{
  						Multis.BaseHouse house = Multis.BaseHouse.FindHouseAt( controller );
  						bool allNear = true;
  
  						for ( int j = 0; j < players.Count; ++j )
  						{
  							Mobile check = players[j];
  							bool isNear;
  
  							if ( house == null )
  								isNear = ( controller.Map == check.Map && check.InRange( controller, 24 ) );
  							else
  								isNear = ( Multis.BaseHouse.FindHouseAt( check ) == house );
  
  							if ( !isNear )
  							{
  								allNear = false;
  								break;
  							}
  						}
  
  						if ( allNear )
  							return controller.Arena;
  					}
  				}
  			}
  
  			List<ArenaEntry> arenas = new List<ArenaEntry>();
  
  			for ( int i = 0; i < m_Arenas.Count; ++i )
  			{
  				Arena arena = m_Arenas[i];
  
  				if ( !arena.IsOccupied )
  					arenas.Add( new ArenaEntry( arena ) );
  			}
  
  			if ( arenas.Count == 0 )
  				return m_Arenas[0];
  
  			int tc = 0;
  
  			for ( int i = 0; i < arenas.Count; ++i )
  			{
  
gt: 				ArenaEntry ae = arenas[i];