//Written by Billy. Please distribute freely!
//*******************************************
//Total Clean-Up and RunUO 1.0 -> 2.X Conversion performed by Vorspire: 06 March 2012
//All features from previous version remain intact and should work exactly as they used to, but now with an improved codebase.
//*******************************************
//Additional edits & bug fixes made by Orbit Storm.
//*******************************************
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using Server;
using Server.Regions;
using Server.Network;
using Server.Mobiles;
using Server.Items;
using Server.Gumps;
using Server.Spells;

namespace Server.Items
{
	public class FFAStone : Item
	{
		public enum GameState
		{
			Disabled,
			AwaitingSignups,
			AwaitingStart,
			InProgress,
			Ended
		}

		private static Point3D[] FeluccaCities = new Point3D[]	   //Edit Felucca cities here
		{
			new Point3D( 4454, 1165, 0 ), // Moonglow
			new Point3D( 1423, 1698, 0 ), // Britain
			new Point3D( 1336, 3769, 0 ), // Jhelom
			new Point3D(  674,  808, 0 ), // Yew
			new Point3D( 2496,  563, 0 ), // Minoc
			new Point3D( 1825, 2819, 0 ), // Trinsic
			new Point3D(  600, 2133, 0 ), // Skara Brae
			new Point3D( 3714, 2157, 20), // Magincia
			new Point3D( 2723, 2180, 0 )  // Buccaneer's Den
		};

		private static Point3D _SpectatorLocation;
		private static FFAStone _Stone;

		private GameState _GameState;
		private FFAFightingRegion _FightingRegion;
		private Rectangle3D _FightingArea;
		private FFASpectatorRegion _SpectatorRegion;
		private Rectangle3D _SpectatorArea;
		private List<Mobile> _PlayerList;
		private int _MinimumPlayers;
		private SignupTimer _SignupTimer;
		private StartTimer _StartTimer;
		private List<FFAMoongate> _Moongates;
		private FFAAnnouncer _Announcer;
		private bool _CanHeal;
		private bool _CanUsePotions;
		private bool _MagicWeapons;
		private BitArray _RestrictedSpells;
		private BitArray _RestrictedSkills;

		public List<Mobile> PlayerList { get { return _PlayerList; } }
		public BitArray RestrictedSpells { get { return _RestrictedSpells; } set { _RestrictedSpells = value; } }
		public BitArray RestrictedSkills { get { return _RestrictedSkills; } set { _RestrictedSkills = value; } }
		public FFAFightingRegion FightingRegion { get { return _FightingRegion; } }

		[CommandProperty(AccessLevel.GameMaster)]
		public Rectangle3D FightingArea
		{
			get { return _FightingArea; }
			set
			{
				_FightingArea = value;
				OnAreaChanged();
			}
		}

		// Location to move players after they're killed / tournament ends
		[CommandProperty(AccessLevel.GameMaster)]
		public Point3D SpectatorLocation { get { return _SpectatorLocation; } set { _SpectatorLocation = value; } }

		[CommandProperty(AccessLevel.GameMaster)]
		public Rectangle3D SpectatorArea { get { return _SpectatorArea; } set { _SpectatorArea = value; OnAreaChanged(); } }

		[CommandProperty(AccessLevel.GameMaster)]
		public int PlayerCount { get { return _PlayerList.Count; } }

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

		[CommandProperty(AccessLevel.GameMaster)]
		public GameState CurrentGameState { get { return _GameState; } set { OnGameStateChanged(value); } }

		[CommandProperty(AccessLevel.GameMaster)]
		public FFAAnnouncer Announcer { get { return _Announcer; } set { _Announcer = value; } }

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

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

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

		[Constructable]
		public FFAStone()
			: base(0xEDD)
		{
			_Stone = this;

			_MinimumPlayers = 1;
			_PlayerList = new List<Mobile>();

			_RestrictedSpells = new BitArray(SpellRegistry.Types.Length);
			_RestrictedSkills = new BitArray(SkillInfo.Table.Length);

			Movable = false;
		}

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

		public static void Initialize()
		{
			Console.WriteLine("FFA System");
			EventSink.Login += EventSink_Login;
			EventSink.PlayerDeath += EventSink_PlayerDeath;
			EventSink.Logout += EventSink_Logout;
		}

		private static void EventSink_Logout(LogoutEventArgs e)
		{
		if( ( _Stone != null && !_Stone.Deleted ) && ( e.Mobile != null && !e.Mobile.Deleted ) )
            _Stone.RemovePlayer(e.Mobile);
		}

		private static void EventSink_PlayerDeath(PlayerDeathEventArgs e)
		{
			if ((_Stone != null && !_Stone.Deleted) && (e.Mobile != null && !e.Mobile.Deleted) && (e.Mobile.Region is FFAFightingRegion))
                _Stone.OnDeath(e.Mobile);
		}

		private static void EventSink_Login(LoginEventArgs e)
		{
			// if game isnt running then move player to spectatorlocation
			if ((_Stone != null) && (e.Mobile.Region == _Stone.FightingRegion) && ((_Stone.CurrentGameState != GameState.InProgress) || (!_Stone.IsParticipant(e.Mobile))))
			{ _Stone.MovePlayerToSpectatorArea(e.Mobile); }
		}

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

			int version = 0;

			writer.Write(version);

			switch (version)
			{
				case 0:
					{
						writer.Write(_SpectatorLocation);
						writer.Write(_FightingArea);
						writer.Write(_SpectatorArea);
						writer.Write(_MinimumPlayers);
						writer.Write(_Announcer);
						writer.Write(_CanHeal);
						writer.Write(_CanUsePotions);
						writer.Write(_MagicWeapons);

						WriteBitArray(writer, _RestrictedSkills);
						WriteBitArray(writer, _RestrictedSpells);
					} break;
			}
		}

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

			_Stone = this;

			int version = reader.ReadInt();

			switch (version)
			{
				case 0:
					{
						_SpectatorLocation = reader.ReadPoint3D();
						_FightingArea = reader.ReadRect3D();
						_SpectatorArea = reader.ReadRect3D();
						_MinimumPlayers = reader.ReadInt();
						_Announcer = (FFAAnnouncer)reader.ReadMobile();
						_CanHeal = reader.ReadBool();
						_CanUsePotions = reader.ReadBool();
						_MagicWeapons = reader.ReadBool();
						_RestrictedSkills = ReadBitArray(reader);
						_RestrictedSpells = ReadBitArray(reader);

						_PlayerList = new List<Mobile>();

						UpdateRegions();
					} break;
			}
		}

		#region Serialization Helpers
		public static void WriteBitArray(GenericWriter writer, BitArray ba)
		{
			writer.Write(ba.Length);

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

		public static BitArray ReadBitArray(GenericReader reader)
		{
			int length = reader.ReadInt();
			BitArray newBA = new BitArray(length);

			for (int i = 0; i < length; i++)
			{ newBA[i] = reader.ReadBool(); }

			return newBA;
		}
		#endregion

		public static int GetRegistryNumber(ISpell s)
		{ return SpellRegistry.GetRegistryNumber(s); }

		public bool IsRestrictedSpell(ISpell s)
		{
			int regNum = GetRegistryNumber(s);

			if (regNum < 0)	//Happens with unregistered Spells
			{ return false; }

			return _RestrictedSpells[regNum];
		}

		public bool IsRestrictedSkill(int skill)
		{
			if (skill < 0)
			{ return false; }

			return _RestrictedSkills[skill];
		}

		public void OnDeath(Mobile mobile)
		{
			if (_GameState == GameState.InProgress && IsParticipant(mobile))
			{
				RemovePlayer(mobile);
				MovePlayerToSpectatorArea(mobile);
				mobile.Resurrect();
				HandleCorpse(mobile);
				HealPlayer(mobile);

				if (_PlayerList.Count > 1)
				{ BroadcastMessage("{0} hath been bested, {1} players remain.", mobile.Name, PlayerCount); }
				else
				{ DeclareWinner(); }
			}
		}

		public void DeclareWinner()
		{
			if (_PlayerList[0] != null)
			{
				Mobile m = _PlayerList[0];
				BroadcastMessage("The tournament is over, {0} is the winner!", m.Name);

				//TourneyTicket does not exist, rewards for winning can be given by this example.
				//m.AddToBackpack(new TourneyTicket());
				//m.SendMessage(1161, "Congratulations on winning the tournament. The winner's ticket has been placed into your backpack.");
			}

			MoveAllPlayers();
			_PlayerList.Clear();

			OnGameStateChanged(GameState.Ended);
		}

		public void HandleCorpse(Mobile m)
		{
			if (m.Corpse == null)
			{ return; }

			Container c = m.Corpse;
			Container b = m.Backpack;

			foreach (Item i in c.Items)
			{
				if (i.Map != Map.Internal)
				{ b.AddItem(i); }
			}

			c.Delete();
			m.SendMessage(1161, "The contents of your corpse have been safely placed into your backpack.");
		}

		public bool CanRegister()
		{ return (_GameState <= GameState.AwaitingSignups); }

		private void HealPlayer(Mobile m)
		{
			m.Hits = m.HitsMax;
			m.Mana = m.ManaMax;
			m.Stam = m.StamMax;
		}

		public List<Mobile> GetInterestedParties()
		{
			List<Mobile> people = new List<Mobile>();

			foreach (Mobile m in _FightingRegion.GetMobiles())
			{
				if (!people.Contains(m))
				{ people.Add(m); }
			}

			foreach (Mobile m in _SpectatorRegion.GetMobiles())
			{
				if (!people.Contains(m))
				{ people.Add(m); }
			}

			return people;
		}

		public void VerifyPlayer(Mobile m)
		{ AddPlayer(m); }

		public void AddPlayer(Mobile mobile)
		{
			if (mobile == null)
			{ return; }

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

			if (IsParticipant(mobile))
			{
				mobile.SendMessage(1161, "You are already signed up for this tournament.");
				return;
			}

			if (!CanRegister())
			{ mobile.SendMessage(1161, "Sorry, signups are currently closed."); }

			if (_GameState <= GameState.AwaitingSignups && !IsParticipant(mobile))
			{ _PlayerList.Add(mobile); }

			if (_GameState == GameState.AwaitingSignups && _PlayerList.Count >= _MinimumPlayers)
			{ OnGameStateChanged(GameState.AwaitingStart); }
		}

		public void RemovePlayer(Mobile mobile)
		{
			if (mobile == null)
			{ return; }

			if (_PlayerList != null && IsParticipant(mobile))
			{ _PlayerList.Remove(mobile); }
		}

		public void MovePlayerToSpectatorArea(Mobile mobile)
		{
			if (mobile == null || mobile.Deleted || mobile.Region == null)
			{ return; }

			foreach (Mobile m in mobile.Region.GetMobiles())
			{
				BaseCreature bc = m as BaseCreature;

				if (bc == null || bc.Deleted)
				{ continue; }

				if (bc.Controlled && bc.ControlMaster == mobile)
				{
					bc.Combatant = null;
					bc.Criminal = false;
					bc.MoveToWorld(_SpectatorLocation, Map.Felucca);
				}
			}

			mobile.Combatant = null;
			mobile.Criminal = false;
			mobile.MoveToWorld(_SpectatorLocation, Map.Felucca);
		}

		public void MoveAllPlayers()
		{
			foreach (Mobile m in _PlayerList)
			{ MovePlayerToSpectatorArea(m); }

			foreach (Mobile m in _FightingRegion.GetMobiles())
			{
				if (m != null && m.Region == _FightingRegion)
				{
					m.Combatant = null;
					m.MoveToWorld(_SpectatorLocation, Map.Felucca);
				}
			}
		}

		public void BroadcastMessage(string format, params object[] args)
		{
			foreach (Mobile m in GetInterestedParties())
			{ m.SendMessage(format, args); }

			if (_Announcer != null)
			{ _Announcer.Say(format, args); }
		}

		public void BroadcastPlayers(string format, params object[] args)
		{
			foreach (Mobile m in _PlayerList)
			{ m.LocalOverheadMessage(MessageType.Emote, 0, true, String.Format(format, args)); }
		}

		public void BroadcastWorld(string message)
		{
			foreach (NetState state in NetState.Instances.ToArray())
			{
				if (state.Mobile == null)
				{ continue; }

				state.Mobile.SendMessage(1161, message);
			}
		}

		public Point3D GetRandomLocationInRegion()
		{
			int count = 0;
			return GetRandomLocationInRegion(ref count);
		}

		private Point3D GetRandomLocationInRegion(ref int count)
		{
			Point3D loc = new Point3D(_FightingArea.Start.X + Utility.Random(_FightingArea.Width), _FightingArea.Start.Y + Utility.Random(_FightingArea.Height), 0);

			loc.Z = GetZ(loc.X, loc.Y);

			if (CanFit(Map.Felucca, loc.X, loc.Y, loc.Z))
			{ return loc; }

			if (count++ > _FightingArea.Width * _FightingArea.Height)
			{ return _SpectatorLocation; }

			return GetRandomLocationInRegion(ref count);
		}

		public bool IsParticipant(Mobile m)
		{ return _PlayerList.Contains(m); }

		public int GetZ(int x, int y)
		{
			int z = 0;
			LandTile lt = Map.Felucca.Tiles.GetLandTile(x, y);
			TileFlag flags = TileData.LandTable[lt.ID & 0x3FFF].Flags;

			if ((flags & TileFlag.Impassable) == 0)
			{ z = lt.Z; }

			foreach (StaticTile st in Map.Felucca.Tiles.GetStaticTiles(x, y))
			{
				ItemData id = TileData.ItemTable[st.ID & 0x3FFF];

				if (id.Surface)
				{
					if ((st.Z + id.CalcHeight) > z)
					{ z = st.Z + id.CalcHeight; }
				}
			}

			foreach (Item item in Map.Felucca.GetSector(x, y).Items)
			{
				if (item.AtWorldPoint(x, y))
				{
					ItemData id = item.ItemData;

					if (id.Surface)
					{
						if ((item.Z + id.CalcHeight) > z)
						{ z = item.Z + id.CalcHeight; }
					}
				}
			}

			return z;
		}

		#region RunUO Floor Stuff
		private static bool CanFit(Map map, int x, int y, int z)
		{
			bool hasSurface = false;
			int
				lowZ = 0,
				avgZ = 0,
				topZ = 0;

			map.GetAverageZ(x, y, ref lowZ, ref avgZ, ref topZ);

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

			if ((landFlags & TileFlag.Impassable) != 0 && topZ > z && (z + 16) > lowZ)
			{ return false; }
			else if ((landFlags & TileFlag.Impassable) == 0 && z == avgZ && !lt.Ignored)
			{ hasSurface = true; }

			bool
				surface,
				impassable;

			foreach (StaticTile st in map.Tiles.GetStaticTiles(x, y))
			{
				ItemData id = TileData.ItemTable[st.ID & 0x3FFF];

				surface = id.Surface;
				impassable = id.Impassable;

				if ((surface || impassable) && (st.Z + id.CalcHeight) > z && (z + 16) > st.Z)
				{ return false; }
				else if (surface && !impassable && z == (st.Z + id.CalcHeight))
				{ hasSurface = true; }
			}

			foreach (Item item in map.GetSector(x, y).Items)
			{
				if (item.AtWorldPoint(x, y))
				{
					ItemData id = item.ItemData;

					surface = id.Surface;
					impassable = id.Impassable;

					if ((surface || impassable) && (item.Z + id.CalcHeight) > z && (z + 16) > item.Z)
					{ return false; }
					else if (surface && !impassable && z == (item.Z + id.CalcHeight))
					{ hasSurface = true; }
				}
			}

			return hasSurface;
		}

		private static bool GetFloorZ(Map map, int x, int y, out int z)
		{
			LandTile lt = map.Tiles.GetLandTile(x, y);

			if (IsFloor(lt.ID) && map.CanFit(x, y, lt.Z, 16, false, false))
			{
				z = lt.Z;
				return true;
			}

			foreach (StaticTile st in map.Tiles.GetStaticTiles(x, y))
			{
				ItemData id = TileData.ItemTable[st.ID & 0x3FFF];

				if (IsStaticFloor(st.ID) && map.CanFit(x, y, st.Z + (id.Surface ? id.CalcHeight : 0), 16, false, false))
				{
					z = st.Z + (id.Surface ? id.CalcHeight : 0);
					return true;
				}
			}

			z = 0;
			return false;
		}

		private static bool IsFloor(Map map, int x, int y, bool canFit)
		{
			LandTile lt = map.Tiles.GetLandTile(x, y);

			if (IsFloor(lt.ID) && (canFit || CanFit(map, x, y, lt.Z)))
			{ return true; }

			foreach (StaticTile st in map.Tiles.GetStaticTiles(x, y))
			{
				ItemData id = TileData.ItemTable[st.ID & 0x3FFF];

				if (IsStaticFloor(st.ID) && (canFit || CanFit(map, x, y, st.Z + (id.Surface ? id.CalcHeight : 0))))
				{ return true; }
			}

			return false;
		}

		private static bool IsFloor(int itemID)
		{
			itemID &= 0x3FFF;
			return (itemID >= 0x406 && itemID <= 0x51A);
		}

		private static bool IsStaticFloor(int itemID)
		{
			itemID &= 0x3FFF;
			return (itemID >= 0x495 && itemID <= 0x514) || (itemID >= 0x519 && itemID <= 0x53A);
		}
		#endregion

		public void UpdateRegions()
		{
			if (_FightingRegion != null)
			{ _FightingRegion.Unregister(); }

			if (_SpectatorRegion != null)
			{ _SpectatorRegion.Unregister(); }

			_FightingRegion = new FFAFightingRegion(this);
			_SpectatorRegion = new FFASpectatorRegion(this);
		}

		public void CreateGates()
		{
			if (_Moongates == null)
			{ _Moongates = new List<FFAMoongate>(); }

			foreach (Point3D felCity in FeluccaCities)
			{
				FFAMoongate g = new FFAMoongate(this, _SpectatorLocation);
				g.MoveToWorld(felCity, Map.Felucca);
				_Moongates.Add(g);
			}
		}

		public void RemoveGates()
		{
			if (_Moongates == null)
			{ return; }

			foreach (FFAMoongate mg in _Moongates)
			{ mg.Delete(); }
		}

		public override void OnDoubleClick(Mobile m)
		{
			if (m.AccessLevel < AccessLevel.GameMaster)
			{ return; }

			m.SendGump(new FFASkillRestrictGump(_RestrictedSkills));
			m.SendGump(new FFASpellRestrictGump(_RestrictedSpells));
		}

		public void OnGameStateChanged(GameState newstate)
		{
			GameState oldstate = _GameState;

			if (newstate == GameState.AwaitingSignups)
			{
				if (_Announcer != null)
				{ _Announcer.Say("Signups are now open!"); }
			}

			if (oldstate == GameState.Disabled && newstate == GameState.AwaitingSignups)
			{
				CreateGates();
				BroadcastWorld("A new tournament is about to start, please see your nearest bank for a moongate.");
			}

			if (oldstate == GameState.AwaitingSignups && newstate == GameState.AwaitingStart)
			{
				if (_SignupTimer != null)
				{ _SignupTimer.Stop(); }

				_SignupTimer = new SignupTimer(this);
				_SignupTimer.Start();

				BroadcastMessage("Signups are closed, the game will start shortly.");
			}

			if (newstate == GameState.InProgress)
			{ UnfreezePlayers(); }

			if (newstate == GameState.Ended)
			{
				UnfreezePlayers();
				MoveAllPlayers();
				_PlayerList.Clear();
			}

			if (newstate == GameState.Disabled)
			{
				_PlayerList.Clear();
				RemoveGates();
			}

			_GameState = newstate;
		}

		private void StartGame()
		{
			foreach (Mobile m in _PlayerList.ToArray())
			{
				if (m == null)
				{ continue; }

				if (m.Alive)
				{
					m.MoveToWorld(GetRandomLocationInRegion(), Map.Felucca);
					m.Criminal = true;
				}
				else
				{ RemovePlayer(m); }
			}

			if (PlayerCount < 2)
			{
				OnGameStateChanged(GameState.Ended);
				return;
			}

			FreezePlayers();

			if (_StartTimer != null)
			{ _StartTimer.Stop(); }

			_StartTimer = new StartTimer(this, 5);
			_StartTimer.Start();
		}

		public void OnAreaChanged()
		{ UpdateRegions(); }

		public void FreezePlayers()
		{
			foreach (Mobile m in _PlayerList)
			{
				m.Frozen = true;
				m.Hidden = true;
			}
		}

		public void UnfreezePlayers()
		{
			foreach (Mobile m in _PlayerList)
			{
				m.Frozen = false;
				m.Hidden = false;
			}
		}

		private class SignupTimer : Timer
		{
			private FFAStone _Stone;

			public SignupTimer(FFAStone stone)
				: base(TimeSpan.FromSeconds(30.0))
			{
				Priority = TimerPriority.TwoFiftyMS;
				_Stone = stone;
			}

			protected override void OnTick()
			{
				_Stone.StartGame();
				Stop();
			}
		}

		private class StartTimer : Timer
		{
			private FFAStone _Stone;
			private int _CountDown;

			public StartTimer(FFAStone stone, int countDown)
				: base(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0))
			{
				_CountDown = countDown;
				_Stone = stone;
			}

			protected override void OnTick()
			{
				_Stone.BroadcastPlayers("{0}", _CountDown);

				if (_CountDown == 0)
				{
					_Stone.OnGameStateChanged(GameState.InProgress);
					Stop();
				}
				else
				{ _CountDown--; }
			}
		}
	}

	public class FFASignupBook : Item
	{
		private FFAStone _Stone;

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

		[Constructable]
		public FFASignupBook()
			: base(0xFF4)
		{
			Movable = false;
			Name = "tournament signup book";
		}

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

		public override void OnDoubleClick(Mobile m)
		{
			if (_Stone == null)
			{ return; }

			if (!m.InRange(GetWorldLocation(), 4))
			{
				m.SendLocalizedMessage(500446); // That is too far away.
				return;
			}

			if (_Stone.IsParticipant(m))
			{
				m.SendMessage(1161, "You are already signed up for this tournament.");
				return;
			}

			if (!_Stone.CanRegister())
			{
				m.SendAsciiMessage("Sorry, signups are currently closed.");
				return;
			}

			m.CloseGump(typeof(FFAJoinGump));
			m.SendGump(new FFAJoinGump(_Stone));
		}

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

			int version = 0;

			writer.Write(version);

			switch (version)
			{
				case 0: { writer.Write(_Stone); } break;
			}
		}

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

			int version = reader.ReadInt();

			switch (version)
			{
				case 0: { _Stone = (FFAStone)reader.ReadItem(); } break;
			}
		}
	}

	public class FFAMoongate : Moongate
	{
		private FFAStone _Stone;

		public FFAMoongate(FFAStone stone, Point3D target)
			: base(target, Map.Felucca)
		{
			_Stone = stone;

			Dispellable = false;
			Name = "FFA Tournament Portal";
		}

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

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

			int version = 0;

			writer.Write(version);

			switch (version)
			{
				case 0: { } break;
			}
		}

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

			int version = reader.ReadInt();

			switch (version)
			{
				case 0: { Delete(); } break;
			}
		}
	}
}

namespace Server.Mobiles
{
	public class FFAAnnouncer : Mobile
	{
		[Constructable]
		public FFAAnnouncer()
		{
			Str = 100;
			Int = 10;
			Dex = 10;
			Blessed = true;
			Hue = Utility.RandomSkinHue();
			Female = Utility.RandomBool();

			if (Female)
			{
				Body = 0x191;
				Name = NameList.RandomName("female");
			}
			else
			{
				Body = 0x190;
				Name = NameList.RandomName("male");
			}
		}

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

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

			int version = 0;

			writer.Write(version);

			switch (version)
			{
				case 0: { } break;
			}
		}

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

			int version = reader.ReadInt();

			switch (version)
			{
				case 0: { } break;
			}
		}
	}

	public class FFANox : DummyNox
	{
		private static int _TeamID = 0;

		private FFAStone _Stone;
		private Horse _Horse;

		[Constructable]
		public FFANox()
		{
			Team = _TeamID++;
			Kills = 100;
			Female = Utility.RandomBool();

			if (Female)
			{
				Body = 0x191;
				Name = NameList.RandomName("female");
			}
			else
			{
				Body = 0x190;
				Name = NameList.RandomName("male");
			}

			if (Utility.RandomBool())
			{
				_Horse = new Horse();
				_Horse.Controlled = true;
				_Horse.ControlMaster = this;
				_Horse.MoveToWorld(this.Location, this.Map);
				_Horse.Rider = this;
			}

			Register();
		}

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

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

			int version = 0;

			writer.Write(version);

			switch (version)
			{
				case 0: { } break;
			}
		}

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

			int version = reader.ReadInt();

			switch (version)
			{
				case 0: { Delete(); } break;
			}
		}

		public override bool OnBeforeDeath()
		{
			if (_Stone == null)
			{ return base.OnBeforeDeath(); }

			_Stone.OnDeath(this);

			return base.OnBeforeDeath();
		}

		public override void OnDeath(Container c)
		{
			base.OnDeath(c);

			c.Delete();

			if (_Horse != null)
			{ _Horse.Delete(); }
		}

		public void Register()
		{
			foreach (Item item in World.Items.Values)
			{
				if (item == null || item.Deleted || !(item is FFAStone))
				{ continue; }

				FFAStone stone = (FFAStone)item;

				stone.AddPlayer(this);
				_Stone = stone;
			}
		}
	}
}

namespace Server.Regions
{
	public class FFAFightingRegion : BaseRegion
	{
		private FFAStone _Stone;

		public FFAFightingRegion(FFAStone stone)
			: base("FFA Region", Map.Felucca, Region.DefaultPriority, stone.FightingArea)
		{ _Stone = stone; }

		public override bool AllowBeneficial(Mobile m, Mobile target)
		{ return (_Stone.CurrentGameState == FFAStone.GameState.InProgress); }

		public override bool AllowHarmful(Mobile m, Mobile target)
		{ return (_Stone.CurrentGameState == FFAStone.GameState.InProgress); }

		public override bool CanUseStuckMenu(Mobile m)
		{ return false; }

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

		public override bool AllowSpawn()
		{ return false; }

		public override bool OnDamage(Mobile m, ref int Damage)
		{ return true; }

		public override bool OnBeginSpellCast(Mobile m, ISpell s)
		{
			if (_Stone == null || m.AccessLevel > AccessLevel.Player)
			{ return true; }

			bool restricted = _Stone.IsRestrictedSpell(s);

			if (restricted)
			{
				m.SendMessage("You may not cast this spell in this area.");
				return false;
			}

			Spell spell = s as Spell;

			if (spell != null && spell.Name == "Ethereal Mount")
			{
				m.SendMessage("You may not mount an ethereal in this area.");
				return false;
			}

			return true;
		}

		public override bool OnHeal(Mobile m, ref int Heal)
		{
			if (_Stone == null || m.AccessLevel > AccessLevel.Player)
			{ return true; }

			if (!_Stone.CanHeal)
			{
				m.SendMessage("You may not heal yourself.");
				return false;
			}

			return true;
		}

		public override bool OnSkillUse(Mobile m, int skill)
		{
			if (_Stone == null || m.AccessLevel > AccessLevel.Player)
			{ return true; }

			if (_Stone.IsRestrictedSkill(skill))
			{
				m.SendMessage("You may not use that skill in this duel.");
				return false;
			}

			return base.OnSkillUse(m, skill);
		}

		public override bool OnMoveInto(Mobile m, Direction d, Point3D newLocation, Point3D oldLocation)
		{
			if (_Stone == null || m.AccessLevel > AccessLevel.Player)
			{ return true; }

			if (!Contains(oldLocation))
			{
				m.SendMessage("You may not enter.");
				return false;
			}

			return true;
		}

		public override bool OnDoubleClick(Mobile m, object o)
		{
			if (_Stone == null || m.AccessLevel > AccessLevel.Player)
			{ return true; }

			if (o is BasePotion && !_Stone.CanUsePotions)
			{
				m.SendMessage("You cannot drink potions in this area.");
				return false;
			}

			return base.OnDoubleClick(m, o);
		}
	}

	public class FFASpectatorRegion : BaseRegion
	{
		private FFAStone _Stone;

		public FFASpectatorRegion(FFAStone stone)
			: base("FFA Spectator Region", Map.Felucca, Region.DefaultPriority, stone.SpectatorArea)
		{ _Stone = stone; }

		public override bool AllowBeneficial(Mobile m, Mobile target)
		{
			if (_Stone == null || m.AccessLevel > AccessLevel.Player)
			{ return true; }

			m.SendAsciiMessage("You may not do that in this area.");
			return false;
		}

		public override bool AllowHarmful(Mobile m, Mobile target)
		{
			if (_Stone == null || m.AccessLevel > AccessLevel.Player)
			{ return true; }

			m.SendAsciiMessage("You may not do that in this area.");
			return false;
		}

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

		public override bool OnBeginSpellCast(Mobile m, ISpell s)
		{
			if (_Stone == null || m.AccessLevel > AccessLevel.Player)
			{ return true; }

			m.SendLocalizedMessage(502629); // You cannot cast spells here.
			return false;
		}

		public override bool OnSkillUse(Mobile m, int Skill)
		{
			if (_Stone == null || m.AccessLevel > AccessLevel.Player)
			{ return true; }

			m.SendAsciiMessage("You may not use skills in this area.");
			return false;
		}

		public override bool OnCombatantChange(Mobile m, Mobile Old, Mobile New)
		{
			if (_Stone == null || m.AccessLevel > AccessLevel.Player)
			{ return true; }

			return false;
		}
	}
}

namespace Server.Gumps
{
	public class FFAJoinGump : Gump
	{
		private FFAStone _Stone;

		public FFAJoinGump(FFAStone stone)
			: base(0, 0)
		{
			_Stone = stone;

			string
				allow = "<baseFONT color='green'>Allowed</baseFONT>",
				disallow = "<baseFONT color='red'>Disallowed</baseFONT>",
				info = "Welcome to the tourney signups! Do you think you've 'got game'? If so, signup and show it. You just may be lucky enough to win the whole thing, and get the prize at the end! Good luck. A game master has set the following rules for this tourney:\n",
				rules = "Potions: " + (_Stone.CanUsePotions ? allow : disallow) + "\nHealing: " + (_Stone.CanHeal ? allow : disallow) + "\nMagic Weps: " + (_Stone.MagicWeapons ? allow : disallow),
				final = info + rules;

			Closable = false;
			Disposable = false;
			Dragable = true;
			Resizable = false;
			AddPage(0);
			AddBackground(145, 134, 282, 373, 2600);
			AddLabel(224, 158, 1267, @"FFA Tournament Signup");
			AddImage(175, 214, 2117);
			AddLabel(196, 212, 1267, @"What is it?");
			AddLabel(192, 237, 1168, @"- A free for all style tournament.");
			AddImage(175, 267, 2117);
			AddLabel(196, 264, 1267, @"How do I win?");
			AddLabel(192, 293, 1168, @"- Be the last man standing.");
			AddImage(175, 320, 2117);
			AddLabel(196, 316, 1267, @"More Information/Rules");
			AddHtml(186, 345, 200, 100, @final, (bool)true, (bool)true);
			AddButton(210, 458, 2510, 2511, (int)Buttons.JoinButton, GumpButtonType.Reply, 0);
			AddButton(298, 458, 2510, 2511, (int)Buttons.CancelButton, GumpButtonType.Reply, 0);
			AddLabel(231, 458, 1267, @"Join");
			AddLabel(319, 458, 1168, @"Cancel");
			AddImage(322, 186, 9804);
		}

		public enum Buttons
		{
			CancelButton = 0,
			JoinButton = 1
		}

		public override void OnResponse(NetState state, RelayInfo info)
		{
			Mobile m = state.Mobile;

			if (m == null || m.Deleted || m.NetState == null)
			{ return; }

			switch (info.ButtonID)
			{
				case (int)Buttons.CancelButton:
					{
						m.CloseGump(typeof(FFAJoinGump));
						m.SendMessage(1161, "You decide not to join the tourney.");

					} break;
				case (int)Buttons.JoinButton:
					{
						m.CloseGump(typeof(FFAJoinGump));
						_Stone.VerifyPlayer(m);
					} break;
			}
		}
	}

	public enum FFARestrictType
	{
		Spells,
		Skills
	}

	public abstract class FFARestrictGump : Gump
	{
		BitArray _RestrictedList;
		FFARestrictType _RestrictedType;

		public FFARestrictGump(BitArray ba, FFARestrictType t)
			: base(50, 50)
		{
			_RestrictedList = ba;
			_RestrictedType = t;

			Closable = true;
			Dragable = true;
			Resizable = false;

			AddPage(0);

			AddBackground(10, 10, 225, 425, 9390);
			AddLabel(73, 15, 1152, (t == FFARestrictType.Spells) ? "Restrict Spells" : "Restrict Skills");
			AddButton(91, 411, 2122, 2123, 1, GumpButtonType.Reply, 0);

			int
				itemsThisPage = 0,
				nextPageNumber = 1;

			object[] collection;

			if (t == FFARestrictType.Skills)
			{ collection = SkillInfo.Table; }
			else
			{ collection = SpellRegistry.Types; }

			for (int i = 0; i < collection.Length; i++)
			{
				if (collection[i] == null)
				{ continue; }

				if (itemsThisPage >= 8 || itemsThisPage == 0)
				{
					itemsThisPage = 0;

					if (nextPageNumber != 1)
					{ AddButton(190, 412, 9903, 9904, 2, GumpButtonType.Page, nextPageNumber); }

					AddPage(nextPageNumber++);

					if (nextPageNumber != 2)
					{ AddButton(29, 412, 9909, 9910, 3, GumpButtonType.Page, nextPageNumber - 2); }
				}

				AddCheck(40, 55 + (45 * itemsThisPage), 210, 211, ba[i], i + ((t == FFARestrictType.Spells) ? 100 : 500));
				AddLabel(70, 55 + (45 * itemsThisPage), 0, ((t == FFARestrictType.Spells) ? ((Type)(collection[i])).Name : ((SkillInfo)(collection[i])).Name));

				itemsThisPage++;
			}
		}

		public override void OnResponse(NetState sender, RelayInfo info)
		{
			switch (info.ButtonID)
			{
				case 0: { } break;
				case 1:
					{
						for (int i = 0; i < _RestrictedList.Length; i++)
						{ _RestrictedList[i] = info.IsSwitched(i + ((_RestrictedType == FFARestrictType.Spells) ? 100 : 500)); }
					} break;
			}
		}
	}

	public class FFASpellRestrictGump : FFARestrictGump
	{
		public FFASpellRestrictGump(BitArray ba)
			: base(ba, FFARestrictType.Spells)
		{ }
	}

	public class FFASkillRestrictGump : FFARestrictGump
	{
		public FFASkillRestrictGump(BitArray ba)
			: base(ba, FFARestrictType.Skills)
		{ }
	}
}
