using System;
using System.Linq;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using InfServer.Logic;
using InfServer.Game;
using InfServer.Scripting;
using InfServer.Bots;
using InfServer.Protocol;

using Assets;

namespace InfServer.Script.GameType_Multi
{   // Script Class
    /// Provides the interface between the script and arena
    ///////////////////////////////////////////////////////
    partial class Script_Multi : Scripts.IScript
    {   ///////////////////////////////////////////////////
        // Member Variables
        ///////////////////////////////////////////////////
        private Arena _arena;                   //Pointer to our arena class
        private CfgInfo _config;				//The zone config
        private Settings.GameTypes _gameType;


        //Poll variables
        private int _lastGameCheck;         //Tick at which we checked for game availability
        private int _tickGameStarting;      //Tick at which the game began starting (0 == not initiated)
        private int _tickGameStarted;       //Tick at which the game actually started (0 == stopped)
        private int _lastTickerUpdate;      //Tick at which the scoreboard was last updated
        private int _tickVotingStarted;     //Tick at which players started voting
        private int _lastKillStreakUpdate;  //Tick at which a players kill streak started
        private bool _isScrim = false;
        private bool _scrimStarted = false;

        //Misc variables
        private int _tickStartDelay;
        private int _minPlayers;            //Do we have the # of min players to start a game?
        private Dictionary<string, List<int>> _mapVotes;
        private int _votingTime = 30;            //Our voting countdown timer
        private bool _votingEnded = false;
        private string _currentMap;
        private Player _lastUAVPlayer;
        private int _uavWaves;
        Dictionary<Player, int> _uavSpots = new Dictionary<Player, int>();
        List<Duel> duels = new List<Duel>();

        public class Duel
        {
            public bool duelStarted;
            public Player challenger;
            public Player challenged;
            public Player winner;
            public int tickChallengeSent;

            public Team challengerTeam;
            public Team challengedTeam;

        }


        /// <summary>
        /// Our gametype handlers
        /// </summary>
        private TDM _tdm;
        private Demolition _demolition;
        private Match _match;
        private Snipers _snipers;
        private KOTH _koth;

        ///////////////////////////////////////////////////
        // Member Functions
        ///////////////////////////////////////////////////
        /// <summary>
        /// Performs script initialization
        /// </summary>
        public bool init(IEventObject invoker)
        {   //Populate our variables
            _arena = invoker as Arena;
            _config = _arena._server._zoneConfig;

            //Load up our gametype handlers
            _tdm = new TDM(_arena);
            _demolition = new Demolition(_arena);
            _match = new Match(_arena);
            _snipers = new Snipers(_arena);
            _koth = new KOTH(_arena);

            //Populate our map/gametype choices
            _mapVotes = new Dictionary<string, List<int>>(StringComparer.InvariantCultureIgnoreCase);


            //Default to TDM
            _gameType = Settings.GameTypes.TDM;
            _minPlayers = 1;

            Team team1 = _arena.getTeamByName("Titan Militia");
            Team team2 = _arena.getTeamByName("Collective Military");
            _tdm.setTeams(team1, team2, false);
            _demolition.setTeams(team1, team2, false);
            _lastSpawn = new Dictionary<string, Helpers.ObjectState>();


            return true;
        }

        /// <summary>
        /// Allows the script to maintain itself
        /// </summary>
        public bool poll()
        {	//Should we check game state yet?
            int now = Environment.TickCount;

            if (now - _lastGameCheck <= Arena.gameCheckInterval)
                return true;
            _lastGameCheck = now;

            //Do we have enough players?
            int playing = _arena.PlayerCount;
            if (_arena._bGameRunning && playing < _minPlayers && _arena._bIsPublic)
            {
                //Stop the game and reset voting
                _arena.gameEnd();
            }

            if (_uavSpots.Count > 0)
            {
                foreach (KeyValuePair<Player, int> spots in _uavSpots)
                {
                    Player player = spots.Key;
                    int tickSpotStop = spots.Value;

                    int countdown = tickSpotStop > 0 ? ((tickSpotStop - now) / 1000) : 0;
                    if (countdown == 0)
                    {
                        _uavSpots.Remove(player);
                        continue;
                    }


                    IEnumerable<Player> routeTo = _arena.PlayersIngame.Where(p => p._team != player._team);

                    if (routeTo.Count() > 0)
                    {
                        Helpers.Player_RouteExplosion(routeTo, 1093, player._state.positionX, player._state.positionY, 0, 0, 0);
                    }
                }
            }

            if (playing < _minPlayers && _arena._bIsPublic)
            {
                _tickGameStarting = 0;
                _votingEnded = false;
                _arena.setTicker(1, 3, 0, "Not Enough Players");
            }

            if (playing < _minPlayers && !_arena._bIsPublic && !_arena._bGameRunning)
            {
                _tickGameStarting = 0;
                _arena.setTicker(1, 3, 0, "Private arena, Waiting for arena owner to start the game!");
            }

            //Do we have enough to start a game?
            if (!_arena._bGameRunning && _tickGameStarting == 0 && playing >= _minPlayers && _arena._bIsPublic)
            {
                _tickGameStarting = now;
                _arena.setTicker(1, 3, _config.deathMatch.startDelay * 100, "Next game: ",
                    delegate ()
                    {   //Trigger the game start
                        _arena.gameStart();
                    });
            }

            //Poll our current gametype!
            switch (_gameType)
            {
                case Settings.GameTypes.TDM:
                    _tdm.Poll(now);
                    break;

                case Settings.GameTypes.DEMOLITION:
                    _demolition.Poll(now);
                    break;

                case Settings.GameTypes.LEAGUEMATCH:
                    _match.Poll(now);
                    break;
                case Settings.GameTypes.SNIPERS:
                    _snipers.Poll(now);
                    break;
                case Settings.GameTypes.KOTH:
                    _koth.Poll(now);
                    break;


                default:
                    //Do nothing
                    break;
            }

            return true;
        }

        #region Events

        [Scripts.Event("Player.Portal")]
        public bool playerPortal(Player player, LioInfo.Portal portal)
        {
            if (portal.GeneralData.Name.Contains("DS Portal"))
            {
                Helpers.ObjectState playerPoint = findPlayerWarp(player);
                Helpers.ObjectState warpPoint;

                if (playerPoint == null)
                {
                    Log.write(TLog.Normal, String.Format("Could not find suitable player warp for {0}", player._alias));

                    if (!_lastSpawn.ContainsKey(player._alias))
                    {
                        player.sendMessage(-1, "Could not find suitable warp, Please try again");
                        return false;
                    }
                    else
                        warpPoint = _lastSpawn[player._alias];
                }
                else
                {
                    warpPoint = findOpenWarp(player, _arena, playerPoint.positionX, 1744, _playerWarpRadius);
                }

                if (warpPoint == null)
                {
                    Log.write(TLog.Normal, String.Format("Could not find open warp for {0} (Warp Blocked)", player._alias));
                    player.sendMessage(-1, "Warp was blocked, please try again");
                    return false;
                }

                warp(player, warpPoint);

                if (_lastSpawn.ContainsKey(player._alias))
                    _lastSpawn[player._alias] = warpPoint;
                else
                    _lastSpawn.Add(player._alias, warpPoint);
                return false;
            }
            return false;
        }

        /// <summary>
        /// Triggered when a player attempts to use a warp item
        /// </summary>
        [Scripts.Event("Player.MakeVehicle")]
        public bool playerMakeVehicle(Player player, ItemInfo.VehicleMaker item, short posX, short posY)
        {

            return true;
        }

        [Scripts.Event("Player.WarpItem")]
        public bool playerWarpItem(Player player, ItemInfo.WarpItem item, ushort targetVehicle, short posX, short posY)
        {
            if (item.name == "UAV")
            {
                Helpers.Player_RouteExplosion(_arena.Players, 3064, 12416, 6176, 0, 180, player._id);
                _lastUAVPlayer = player;
                player.inventoryModify(21, -1);
                player.syncInventory();
            }
            return false;
        }

        /// <summary>
        /// Triggered when a player notifies the server of an explosion
        /// </summary>
        [Scripts.Event("Player.DamageEvent")]
        public bool playerDamageEvent(Player player, ItemInfo.Projectile weapon, short posX, short posY, short posZ)
        {
            //UAV Spotting?
            if (weapon.id == 1066)
            {
                int tickUAVStop = (Environment.TickCount + (3000 * 10));
                if (!_uavSpots.ContainsKey(player))
                    _uavSpots.Add(player, tickUAVStop);
            }

            if (weapon.id == 1105)
            {
                if (player._occupiedVehicle != null)
                {
                    ItemInfo repair = _arena._server._assets.getItemByID(22);
                    if (player._occupiedVehicle._state.health < player._occupiedVehicle._type.Hitpoints)
                    {
                        player._occupiedVehicle._state.health = (short)(player._occupiedVehicle._state.health + 4);
                        player._occupiedVehicle.update(false);
                    }
                }
            }
            return true;
        }

        /// <summary>
        /// Triggered when a player notifies the server of an explosion
        /// </summary>
        [Scripts.Event("Player.Explosion")]
        public bool playerExplosion(Player player, ItemInfo.Projectile weapon, short posX, short posY, short posZ)
        {


            //Defer to our current gametype handler!
            switch (_gameType)
            {
                case Settings.GameTypes.TDM:
                    _tdm.playerExplosion(player, weapon, posX, posY, posZ);
                    break;
                case Settings.GameTypes.DEMOLITION:
                    _demolition.playerExplosion(player, weapon, posX, posY, posZ);
                    break;
                case Settings.GameTypes.LEAGUEMATCH:
                    _match.playerExplosion(player, weapon, posX, posY, posZ);
                    break;
                case Settings.GameTypes.SNIPERS:
                    _snipers.playerExplosion(player, weapon, posX, posY, posZ);
                    break;
                case Settings.GameTypes.KOTH:
                    _koth.playerExplosion(player, weapon, posX, posY, posZ);
                    break;

                default:
                    //Do nothing
                    break;
            }

            if (weapon.id == 1110)
            {
                VehInfo pack = _arena._server._assets.getVehicleByID(405);
                Helpers.ObjectState state = new Helpers.ObjectState();
                state.positionX = posX;
                state.positionY = posY;
                Vehicle packveh = _arena.newVehicle(pack, player._team, player, state);

                packveh.update(false);

                player._team.sendArenaMessage(String.Format("Ammo pack dropped at [{0}]", Helpers.posToLetterCoord(posX, posY)));
            }
            return true;
        }

        /// <summary>
        /// Triggered when a player wants to unspec and join the game
        /// </summary>
        [Scripts.Event("Player.JoinGame")]
        public bool playerJoinGame(Player player)
        {
            bool handler = true;
            //Defer to our current gametype handler!
            switch (_gameType)
            {
                case Settings.GameTypes.TDM:
                    handler = _tdm.playerUnspec(player);
                    break;
                case Settings.GameTypes.DEMOLITION:
                    handler = _demolition.playerUnspec(player);
                    break;
                case Settings.GameTypes.LEAGUEMATCH:
                    handler = _match.playerUnspec(player);
                    break;
                case Settings.GameTypes.SNIPERS:
                    handler = _snipers.playerUnspec(player);
                    break;
                case Settings.GameTypes.KOTH:
                    handler = _koth.playerUnspec(player);
                    break;

                default:
                    //Do nothing
                    break;
            }
            return handler;
        }

        /// <summary>
        /// Called when a player enters the game
        /// </summary>
        [Scripts.Event("Player.Enter")]
        public void playerEnter(Player player)
        {

        }

        /// <summary>
        /// Called when a player leaves the game
        /// </summary>
        [Scripts.Event("Player.Leave")]
        public bool playerLeave(Player player)
        {

            //Defer to our current gametype handler!
            switch (_gameType)
            {
                case Settings.GameTypes.TDM:
                    _tdm.playerSpec(player);
                    break;
                case Settings.GameTypes.DEMOLITION:
                    _demolition.playerSpec(player);
                    break;
                case Settings.GameTypes.LEAGUEMATCH:
                    _match.playerSpec(player);
                    break;
                case Settings.GameTypes.SNIPERS:
                    _snipers.playerSpec(player);
                    break;
                case Settings.GameTypes.KOTH:
                    _koth.playerSpec(player);
                    break;


                default:
                    //Do nothing
                    break;
            }
            return false;
        }

        /// <summary>
        /// Called when a player enters the arena
        /// </summary>
        [Scripts.Event("Player.EnterArena")]
        public void playerEnterArena(Player player)
        {
            //Defer to our current gametype handler!
            switch (_gameType)
            {
                case Settings.GameTypes.TDM:
                    _tdm.playerEnterArena(player);
                    break;
                case Settings.GameTypes.DEMOLITION:
                    _demolition.playerEnterArena(player);
                    break;
                case Settings.GameTypes.LEAGUEMATCH:
                    _match.playerEnterArena(player);
                    break;
                case Settings.GameTypes.SNIPERS:
                    _snipers.playerEnterArena(player);
                    break;
                case Settings.GameTypes.KOTH:
                    _koth.playerEnterArena(player);
                    break;

                default:
                    //Do nothing
                    break;
            }
        }

        /// <summary>
        /// Called when a player leaves the arena
        /// </summary>
        [Scripts.Event("Player.LeaveArena")]
        public void playerLeaveArena(Player player)
        {
            //Defer to our current gametype handler!
            switch (_gameType)
            {
                case Settings.GameTypes.TDM:
                    _tdm.playerLeaveArena(player);
                    break;
                case Settings.GameTypes.DEMOLITION:
                    _demolition.playerLeaveArena(player);
                    break;
                case Settings.GameTypes.LEAGUEMATCH:
                    _match.playerLeaveArena(player);
                    break;
                case Settings.GameTypes.SNIPERS:
                    _snipers.playerLeaveArena(player);
                    break;
                case Settings.GameTypes.KOTH:
                    _koth.playerLeaveArena(player);
                    break;


                default:
                    //Do nothing
                    break;
            }
        }

        /// <summary>
        /// Triggered when a player has spawned
        /// </summary>
        [Scripts.Event("Player.Spawn")]
        public bool playerSpawn(Player player, bool death)
        {
            //Defer to our current gametype handler!
            switch (_gameType)
            {
                case Settings.GameTypes.TDM:
                    _tdm.playerSpawn(player, death);
                    break;
                case Settings.GameTypes.DEMOLITION:
                    _demolition.playerSpawn(player, death);
                    break;
                case Settings.GameTypes.LEAGUEMATCH:
                    _match.playerSpawn(player, death);
                    break;
                case Settings.GameTypes.SNIPERS:
                    _snipers.playerSpawn(player, death);
                    break;
                case Settings.GameTypes.KOTH:
                    _koth.playerSpawn(player, death);
                    break;

                default:
                    //Do nothing
                    break;
            }
            return true;
        }

        /// <summary>
        /// Called when the game begins
        /// </summary>
        [Scripts.Event("Game.Start")]
        public bool gameStart()
        {
            _tickGameStarting = 0;
            _tickGameStarted = Environment.TickCount;

            if (_arena.ActiveTeams.Count() == 0)
                return false;

            _lastSpawn = new Dictionary<string, Helpers.ObjectState>();

            foreach (Player player in _tdm._team2.ActivePlayers)
            {
                Helpers.ObjectState lastKnown = new Helpers.ObjectState();
                lastKnown.positionX = 20224;
                lastKnown.positionY = 992;
                _lastSpawn.Add(player._alias, lastKnown);
            }

            foreach (Player player in _tdm._team1.ActivePlayers)
            {
                Helpers.ObjectState lastKnown = new Helpers.ObjectState();
                lastKnown.positionX = 14512;
                lastKnown.positionY = 1824;
                _lastSpawn.Add(player._alias, lastKnown);
            }

            _arena.initialHideSpawns();

            //Defer to our current gametype handler!
            switch (_gameType)
            {
                case Settings.GameTypes.TDM:
                    _tdm.gameStart(_currentMap);
                    break;
                case Settings.GameTypes.DEMOLITION:
                    _demolition.gameStart(_currentMap);
                    break;
                case Settings.GameTypes.LEAGUEMATCH:
                    _match.gameStart();
                    break;
                case Settings.GameTypes.SNIPERS:
                    _snipers.gameStart(_currentMap);
                    break;
                case Settings.GameTypes.KOTH:
                    _koth.gameStart(_currentMap);
                    break;

                default:
                    //Do nothing
                    break;
            }
            return true;
        }

        /// <summary>
        /// Called when the game ends
        /// </summary>
        [Scripts.Event("Game.End")]
        public bool gameEnd()
        {   //Game finished, perhaps start a new one



            _tickGameStarted = 0;
            _tickGameStarting = 0;
            _votingEnded = false;

            //Defer to our current gametype handler!
            switch (_gameType)
            {
                case Settings.GameTypes.TDM:
                    _tdm.gameEnd();
                    break;
                case Settings.GameTypes.DEMOLITION:
                    _demolition.gameEnd();
                    break;
                case Settings.GameTypes.LEAGUEMATCH:
                    _match.gameEnd();
                    break;
                case Settings.GameTypes.SNIPERS:
                    _snipers.gameEnd();
                    break;
                case Settings.GameTypes.KOTH:
                    _koth.gameEnd();
                    break;


                default:
                    //Do nothing
                    break;
            }
            return true;
        }

        /// <summary>
        /// Called when the statistical breakdown is displayed
        /// </summary>
        [Scripts.Event("Game.Breakdown")]
        public bool gameBreakdown()
        {	//Allows additional "custom" breakdown information

            //Always return true;
            return true;
        }

        /// <summary>
        /// Called to reset the game state
        /// </summary>
        [Scripts.Event("Game.Reset")]
        public bool gameReset()
        {
            //Defer to our current gametype handler!
            switch (_gameType)
            {
                case Settings.GameTypes.TDM:
                    _tdm.gameReset();
                    break;
                case Settings.GameTypes.DEMOLITION:
                    _demolition.gameReset();
                    break;
                case Settings.GameTypes.LEAGUEMATCH:
                    _match.gameReset();
                    break;
                case Settings.GameTypes.SNIPERS:
                    _snipers.gameReset();
                    break;
                case Settings.GameTypes.KOTH:
                    _koth.gameReset();
                    break;

                default:
                    //Do nothing
                    break;
            }
            return true;
        }

        /// <summary>
        /// Handles a player's flag request
        /// </summary>
        [Scripts.Event("Player.FlagAction")]
        public bool playerFlagAction(Player player, bool bPickup, bool bInPlace, LioInfo.Flag flag)
        {
            //Defer to our current gametype handler!
            switch (_gameType)
            {
                case Settings.GameTypes.TDM:
                    return _tdm.playerFlagAction(player, bPickup, bInPlace, flag);
                case Settings.GameTypes.DEMOLITION:
                    return _demolition.playerFlagAction(player, bPickup, bInPlace, flag);
                case Settings.GameTypes.LEAGUEMATCH:
                    return _match.playerFlagAction(player, bPickup, bInPlace, flag);
                case Settings.GameTypes.SNIPERS:
                    return _snipers.playerFlagAction(player, bPickup, bInPlace, flag);
                case Settings.GameTypes.KOTH:
                    return _koth.playerFlagAction(player, bPickup, bInPlace, flag);
                default:
                    //Do nothing
                    break;
            }
            return true;
        }

        /// <summary>
        /// Called when the statistical breakdown is displayed
        /// </summary>
        [Scripts.Event("Player.Breakdown")]
        public bool individualBreakdown(Player from, bool bCurrent)
        {
            //Defer to our current gametype handler!
            switch (_gameType)
            {
                case Settings.GameTypes.TDM:
                    _tdm.individualBreakdown(from, bCurrent);
                    break;
                case Settings.GameTypes.DEMOLITION:
                    _demolition.individualBreakdown(from, bCurrent);
                    break;
                case Settings.GameTypes.LEAGUEMATCH:
                    _match.individualBreakdown(from, bCurrent);
                    break;
                case Settings.GameTypes.SNIPERS:
                    _snipers.individualBreakdown(from, bCurrent);
                    break;
                case Settings.GameTypes.KOTH:
                    _koth.individualBreakdown(from, bCurrent);
                    break;


                default:
                    //Do nothing
                    break;
            }
            return true;
        }

        /// <summary>
        /// Handles a player's produce request
        /// </summary>
        [Scripts.Event("Player.Produce")]
        public bool playerProduce(Player player, Computer computer, VehInfo.Computer.ComputerProduct product)
        {
            //Defer to our current gametype handler!
            switch (_gameType)
            {
                case Settings.GameTypes.DEMOLITION:
                    _demolition.playerProduce(player, computer, product);
                    break;

                default:
                    //Do nothing
                    break;
            }

            if (product.Title == "Refill Ammunition")
            {
                if (player._team != computer._team)
                    return false;

                Logic_Assets.RunEvent(player, "vehicleEvent3");

                player.sendMessage(0, "Ammo Refilled....");

                computer._state.health -= 25;

                if (computer._state.health == 0)
                {
                    computer.destroy(false);
                }
                computer.update(false);

                return false;


            }
            return true;
        }

        /// <summary>
        /// Triggered when a player has died, by any means
        /// </summary>
        /// <remarks>killer may be null if it wasn't a player kill</remarks>
        [Scripts.Event("Player.Death")]
        public bool playerDeath(Player victim, Player killer, Helpers.KillType killType, CS_VehicleDeath update)
        {

            //Defer to our current gametype handler!
            switch (_gameType)
            {
                case Settings.GameTypes.TDM:
                    _tdm.playerDeath(victim, killer, killType, update);
                    break;
                case Settings.GameTypes.DEMOLITION:
                    _demolition.playerDeath(victim, killer, killType, update);
                    break;
                case Settings.GameTypes.LEAGUEMATCH:
                    _match.playerDeath(victim, killer, killType, update);
                    break;
                case Settings.GameTypes.SNIPERS:
                    _snipers.playerDeath(victim, killer, killType, update);
                    break;
                case Settings.GameTypes.KOTH:
                    _koth.playerDeath(victim, killer, killType, update);
                    break;
                default:
                    //Do nothing
                    break;
            }
            return true;
        }

        /// <summary>
        /// Triggered when one player has killed another
        /// </summary>
        [Scripts.Event("Player.PlayerKill")]
        public bool playerPlayerKill(Player victim, Player killer)
        {

            Duel duel = duels.FirstOrDefault
                (d => (d.challenger == victim && d.challenged == killer) || (d.challenger == killer && d.challenged == victim));

            if (duel != null && duel.duelStarted)
            {
                _arena.sendArenaMessage(String.Format("!{0} has beat {1} in a duel with {2} HP remaining.",
                killer._alias, victim._alias, killer._state.health));

                killer.inventoryModify(true, 104, 1);

                killer.sendMessage(0, "Please use ?duel targetalias or ?duelaccept challengeralias to go again!");
                victim.sendMessage(0, "Please use ?duel targetalias or ?duelaccept challengeralias to go again!");

                duels.Remove(duel);

                if (killer == duel.challenger)
                {
                    duel.challengerTeam.addPlayer(killer);
                    duel.challengedTeam.addPlayer(victim);
                }
                else
                {
                    duel.challengerTeam.addPlayer(victim);
                    duel.challengedTeam.addPlayer(killer);
                }

                Logic_Assets.RunEvent(killer, "warp=1");
                Logic_Assets.RunEvent(victim, "warp=1");
            }
            //Defer to our current gametype handler!
            switch (_gameType)
            {
                case Settings.GameTypes.TDM:
                    _tdm.playerPlayerKill(victim, killer);
                    break;
                case Settings.GameTypes.DEMOLITION:
                    _demolition.playerPlayerKill(victim, killer);
                    break;
                case Settings.GameTypes.LEAGUEMATCH:
                    _match.playerPlayerKill(victim, killer);
                    break;
                case Settings.GameTypes.SNIPERS:
                    _snipers.playerPlayerKill(victim, killer);
                    break;
                case Settings.GameTypes.KOTH:
                    _koth.playerPlayerKill(victim, killer);
                    break;

                default:
                    //Do nothing
                    break;
            }
            return true;
        }

        /// <summary>
        /// Triggered when a player requests to buy a skill
        /// </summary>
        [Scripts.Event("Shop.SkillRequest")]
        public bool PlayerShopSkillRequest(Player from, SkillInfo skill)
        {

            if (!_arena._bIsPublic)
            {
                if (skill.Name == "Engineer")
                {
                    from.sendMessage(-1, "This class is not allowed in private arenas");
                    return false;
                }
                if (skill.Name == "Scout")
                {
                    from.sendMessage(-1, "This class is not allowed in private arenas");
                    return false;
                }
                if (skill.Name == "Rocketeer")
                {
                    from.sendMessage(-1, "This class is not allowed in private arenas");
                    return false;
                }
            }
            //Defer to our current gametype handler!
            switch (_gameType)
            {
                case Settings.GameTypes.TDM:
                   // _tdm.PlayerRepair(from, item);
                    break;
                case Settings.GameTypes.DEMOLITION:
                    // _demolition.PlayerRepair(from, item);
                    break;
                case Settings.GameTypes.LEAGUEMATCH:
                   // _match.PlayerRepair(from, item);
                    break;
                case Settings.GameTypes.SNIPERS:
                     return _snipers.skillRequest(from, skill);

                default:
                    //Do nothing
                    break;
            }

            return true;
        }

        /// <summary>
        /// Triggers when a repair item is used
        /// </summary>
        [Scripts.Event("Player.Repair")]
        public bool playerPlayerRepair(Player player, ItemInfo.RepairItem item, UInt16 target, short posX, short posY)
        { 
            //Defer to our current gametype handler!
            switch (_gameType)
            {
                case Settings.GameTypes.TDM:
                    _tdm.PlayerRepair(player, item);
                    break;
                case Settings.GameTypes.DEMOLITION:
                   // _demolition.PlayerRepair(from, item);
                    break;
                case Settings.GameTypes.LEAGUEMATCH:
                    _match.PlayerRepair(player, item);
                    break;
                case Settings.GameTypes.SNIPERS:
                    _snipers.PlayerRepair(player, item);
                    break;
                case Settings.GameTypes.KOTH:
                    _koth.PlayerRepair(player, item);
                    break;

                default:
                    //Do nothing
                    break;
            }
            return true;
        }

        /// <summary>
        /// Triggered only when a special communication command is created here that isn't a server command.
        /// </summary>
        [Scripts.Event("Player.CommCommand")]
        public bool playerCommCommand(Player player, Player recipient, string command, string payload)
        {
            return true;
        }

        /// <summary>
        /// Triggered only when a special chat command is created here that isn't a server command.
        /// </summary>
        [Scripts.Event("Player.ChatCommand")]
        public bool playerChatCommand(Player player, Player recipient, string command, string payload)
        {
            if (command.ToLower() == "duel")
            {
                Player target = _arena.getPlayerByName(payload);
                if (payload == "")
                {
                    player.sendMessage(-1, "Payload cannot be empty. Ex: ?duel <targetalias>");
                    return false;
                }
                if (target == null)
                {
                    player.sendMessage(-1, "Could not find specified alias. Ex: ?duel <targetalias>");
                    return false;
                }

                Duel duel = new Duel();
                duel.challenged = target;
                duel.challenger = player;
                duels.Add(duel);
                player.sendMessage(0, "Challenge sent!");
                target.sendMessage(3,
                    String.Format("{0} has challenged you to a duel. use ?duelaccept {0} to accept this challenge", player._alias));
            }
            if (command.ToLower() == "duelaccept")
            {
                Player target = _arena.getPlayerByName(payload);
                if (payload == "")
                {
                    player.sendMessage(-1, "Payload cannot be empty. Ex: ?duelaccept <targetalias>");
                    return false;
                }
                if (target == null)
                {
                    player.sendMessage(-1, "Could not find specified alias. Ex: ?duelaccept <targetalias>");
                    return false;
                }

                Duel duel = duels.FirstOrDefault(d => d.challenger == target && d.challenged == player);

                if (duel == null)
                {
                    player.sendMessage(-1, "Could not find specified challenge");
                    return false;
                }

                startDuel(duel, target, player);
            }
                //Map Voting?
                if (command.ToLower() == "map")
            {

                //Don't allow voting with a game running
                if (_arena._bGameRunning)
                {
                    player.sendMessage(0, "You must wait until the end of this game to vote");
                    return false;
                }
                //Empty payload? Get outta here
                if (payload == null)
                {
                    player.sendMessage(0, "Invalid syntax. Example: ?map KaiserPassTDM");
                    return true;
                }
                //Does it exist?
                if (!_mapVotes.ContainsKey(payload))
                {
                    player.sendMessage(0, "Invalid syntax Example: ?map KaiserPassTDM");
                    return true;
                }

                //Has he already voted?
                if (_mapVotes.Values.Any(p => p.Contains(player._id)))
                {
                    player.sendMessage(0, "You have already voted!");
                    return true;
                }

                //Tally his vote lad!
                _mapVotes[payload].Add(player._id);
                player.sendMessage(0, "Thanks for voting!");
                _arena.triggerMessage(0, 400, String.Format("{0} has voted for {1}", player._alias, payload));

            }
            return true;
        }

        /// <summary>
        /// Triggered only when a special mod command created here that isn't a server command.
        /// </summary>
        [Scripts.Event("Player.ModCommand")]
        public bool playerModcommand(Player player, Player recipient, string command, string payload)
        {
            if (command.Equals("coinflip"))
            {
                _arena.sendArenaMessage("The winner is...   " + (_arena._rand.Next(0, 2) == 0 ? "HEADS!" : "TAILS!"));
            }


            if (command.Equals("mvp") && player.PermissionLevelLocal >= Data.PlayerPermission.Mod)
            {
                if (String.IsNullOrWhiteSpace(payload))
                {
                    player.sendMessage(-1, "Syntax: *mvp alias OR ::*mvp");
                    return false;
                }

                if (!_match.AwardMVP)
                {
                    player.sendMessage(-1, "Cannot award yet till the end of a match.");
                    return false;
                }

                Player target = recipient != null ? recipient : _arena.getPlayerByName(payload);
                _arena.sendArenaMessage("MVP award goes to......... ");
                _arena.sendArenaMessage(target != null ? target._alias : payload);

                if (target != null)
                {
                    target.ZoneStat3 += 1;
                    _arena._server._db.updatePlayer(target);
                }

                if (!String.IsNullOrEmpty(_match.FileName))
                {
                    StreamWriter fs = Logic.Logic_File.OpenStatFile(_match.FileName, String.Format("Season {0}", _match.LeagueSeason.ToString()));
                    fs.WriteLine();
                    fs.WriteLine("MVP: {0}", target != null ? target._alias : payload);
                    fs.Close();
                }

                _match.AwardMVP = false;
                return true;
            }

            if (command.Equals("setscore"))
            {
                if (String.IsNullOrEmpty(payload))
                {
                    player.sendMessage(-1, "Syntax: *setscore 1,2  (In order by teamname per scoreboard)");
                    return false;
                }

                if (!payload.Contains(','))
                {
                    player.sendMessage(-1, "Error in syntax, missing comma seperation.");
                    return false;
                }

                string[] args = payload.Split(',');
                if (!Helpers.IsNumeric(args[0]) || !Helpers.IsNumeric(args[1]))
                {
                    player.sendMessage(-1, "Value is not numeric.");
                    return false;
                }

                IEnumerable<Team> activeTeams = _arena.ActiveTeams;
                if (activeTeams.ElementAt(0) != null)
                    Int32.TryParse(args[0].Trim(), out activeTeams.ElementAt(0)._currentGameKills);
                if (activeTeams.ElementAt(1) != null)
                    Int32.TryParse(args[1].Trim(), out activeTeams.ElementAt(1)._currentGameKills);

                //Immediately notify the change
                _match.UpdateTickers();

                return true;
            }

            if (command.Equals("poweradd"))
            {
                if (player.PermissionLevelLocal < Data.PlayerPermission.SMod)
                {
                    player.sendMessage(-1, "Nice try.");
                    return false;
                }

                int level = (int)Data.PlayerPermission.ArenaMod;
                //Pm'd?
                if (recipient != null)
                {
                    //Check for a possible level
                    if (!String.IsNullOrWhiteSpace(payload))
                    {
                        try
                        {
                            level = Convert.ToInt16(payload);
                        }
                        catch
                        {
                            player.sendMessage(-1, "Invalid level. Level must be either 1 or 2.");
                            return false;
                        }

                        if (level < 1 || level > (int)player.PermissionLevelLocal
                            || level == (int)Data.PlayerPermission.SMod)
                        {
                            player.sendMessage(-1, ":alias:*poweradd level(optional), :alias:*poweradd level (Defaults to 1)");
                            player.sendMessage(0, "Note: there can only be 1 admin level.");
                            return false;
                        }

                        switch (level)
                        {
                            case 1:
                                recipient._permissionStatic = Data.PlayerPermission.ArenaMod;
                                break;
                            case 2:
                                recipient._permissionStatic = Data.PlayerPermission.Mod;
                                break;
                        }
                        recipient._developer = true;
                        recipient.sendMessage(0, String.Format("You have been powered to level {0}. Use *help to familiarize with the commands and please read all rules.", level));
                        player.sendMessage(0, String.Format("You have promoted {0} to level {1}.", recipient._alias, level));
                    }
                    else
                    {
                        recipient._developer = true;
                        recipient._permissionStatic = Data.PlayerPermission.ArenaMod;
                        recipient.sendMessage(0, String.Format("You have been powered to level {0}. Use *help to familiarize with the commands and please read all rules.", level));
                        player.sendMessage(0, String.Format("You have promoted {0} to level {1}.", recipient._alias, level));
                    }

                    //Lets send it to the database
                    //Send it to the db
                    CS_ModQuery<Data.Database> query = new CS_ModQuery<Data.Database>();
                    query.queryType = CS_ModQuery<Data.Database>.QueryType.mod;
                    query.sender = player._alias;
                    query.query = recipient._alias;
                    query.level = level;
                    //Send it!
                    player._server._db.send(query);
                    return true;
                }
                else
                {
                    //We arent
                    //Get name and possible level
                    Int16 number;
                    if (String.IsNullOrEmpty(payload))
                    {
                        player.sendMessage(-1, "Syntax: *poweradd alias:level(optional) Note: if using a level, put : before it otherwise defaults to arena mod");
                        player.sendMessage(0, "Note: there can only be 1 admin.");
                        return false;
                    }
                    if (payload.Contains(':'))
                    {
                        string[] param = payload.Split(':');
                        try
                        {
                            number = Convert.ToInt16(param[1]);
                            if (number >= 0)
                                level = number;
                        }
                        catch
                        {
                            player.sendMessage(-1, "That is not a valid level. Possible powering levels are 1 or 2.");
                            return false;
                        }
                        if (level < 1 || level > (int)player.PermissionLevelLocal
                            || level == (int)Data.PlayerPermission.SMod)
                        {
                            player.sendMessage(-1, String.Format("Syntax: *poweradd alias:level(optional) OR :alias:*poweradd level(optional) possible levels are 1-{0}", ((int)player.PermissionLevelLocal).ToString()));
                            player.sendMessage(0, "Note: there can be only 1 admin level.");
                            return false;
                        }
                        payload = param[0];
                    }
                    player.sendMessage(0, String.Format("You have promoted {0} to level {1}.", payload, level));
                    if ((recipient = player._server.getPlayer(payload)) != null)
                    { //They are playing, lets update them
                        switch (level)
                        {
                            case 1:
                                recipient._permissionStatic = Data.PlayerPermission.ArenaMod;
                                break;
                            case 2:
                                recipient._permissionStatic = Data.PlayerPermission.Mod;
                                break;
                        }
                        recipient._developer = true;
                        recipient.sendMessage(0, String.Format("You have been powered to level {0}. Use *help to familiarize with the commands and please read all rules.", level));
                    }

                    //Lets send it off
                    CS_ModQuery<Data.Database> query = new CS_ModQuery<Data.Database>();
                    query.queryType = CS_ModQuery<Data.Database>.QueryType.dev;
                    query.sender = player._alias;
                    query.query = payload;
                    query.level = level;
                    //Send it!
                    player._server._db.send(query);
                    return true;
                }
            }

            if (command.Equals("powerremove"))
            {
                if (player.PermissionLevelLocal < Data.PlayerPermission.SMod)
                {
                    player.sendMessage(-1, "Nice try.");
                    return false;
                }

                int level = (int)Data.PlayerPermission.Normal;
                //Pm'd?
                if (recipient != null)
                {
                    //Check for a possible level
                    if (!String.IsNullOrWhiteSpace(payload))
                    {
                        try
                        {
                            level = Convert.ToInt16(payload);
                        }
                        catch
                        {
                            player.sendMessage(-1, "Invalid level. Levels must be between 0 and 2.");
                            return false;
                        }

                        if (level < 0 || level > (int)player.PermissionLevelLocal
                            || level == (int)Data.PlayerPermission.SMod)
                        {
                            player.sendMessage(-1, ":alias:*powerremove level(optional), :alias:*powerremove level (Defaults to 0)");
                            return false;
                        }

                        switch (level)
                        {
                            case 0:
                                recipient._permissionStatic = Data.PlayerPermission.Normal;
                                recipient._developer = false;
                                break;
                            case 1:
                                recipient._permissionStatic = Data.PlayerPermission.ArenaMod;
                                break;
                            case 2:
                                recipient._permissionStatic = Data.PlayerPermission.Mod;
                                break;
                        }
                        recipient.sendMessage(0, String.Format("You have been demoted to level {0}.", level));
                        player.sendMessage(0, String.Format("You have demoted {0} to level {1}.", recipient._alias, level));
                    }
                    else
                    {
                        recipient._developer = false;
                        recipient._permissionStatic = Data.PlayerPermission.Normal;
                        recipient.sendMessage(0, String.Format("You have been demoted to level {0}.", level));
                        player.sendMessage(0, String.Format("You have demoted {0} to level {1}.", recipient._alias, level));
                    }

                    //Lets send it to the database
                    //Send it to the db
                    CS_ModQuery<Data.Database> query = new CS_ModQuery<Data.Database>();
                    query.queryType = CS_ModQuery<Data.Database>.QueryType.dev;
                    query.sender = player._alias;
                    query.query = recipient._alias;
                    query.level = level;
                    //Send it!
                    player._server._db.send(query);
                    return true;
                }
                else
                {
                    //We arent
                    //Get name and possible level
                    Int16 number;
                    if (String.IsNullOrEmpty(payload))
                    {
                        player.sendMessage(-1, "Syntax: *powerremove alias:level(optional) Note: if using a level, put : before it otherwise defaults to arena mod");
                        return false;
                    }
                    if (payload.Contains(':'))
                    {
                        string[] param = payload.Split(':');
                        try
                        {
                            number = Convert.ToInt16(param[1]);
                            if (number >= 0)
                                level = number;
                        }
                        catch
                        {
                            player.sendMessage(-1, "That is not a valid level. Possible depowering levels are between 0 and 2.");
                            return false;
                        }
                        if (level < 0 || level > (int)player.PermissionLevelLocal
                            || level == (int)Data.PlayerPermission.SMod)
                        {
                            player.sendMessage(-1, String.Format("Syntax: *powerremove alias:level(optional) OR :alias:*powerremove level(optional) possible levels are 0-{0}", ((int)player.PermissionLevelLocal).ToString()));
                            return false;
                        }
                        payload = param[0];
                    }
                    player.sendMessage(0, String.Format("You have demoted {0} to level {1}.", payload, level));
                    if ((recipient = player._server.getPlayer(payload)) != null)
                    { //They are playing, lets update them
                        switch (level)
                        {
                            case 0:
                                recipient._permissionStatic = Data.PlayerPermission.Normal;
                                recipient._developer = false;
                                break;
                            case 1:
                                recipient._permissionStatic = Data.PlayerPermission.ArenaMod;
                                break;
                            case 2:
                                recipient._permissionStatic = Data.PlayerPermission.Mod;
                                break;
                        }
                        recipient.sendMessage(0, String.Format("You have been depowered to level {0}.", level));
                    }

                    //Lets send it off
                    CS_ModQuery<Data.Database> query = new CS_ModQuery<Data.Database>();
                    query.queryType = CS_ModQuery<Data.Database>.QueryType.dev;
                    query.sender = player._alias;
                    query.query = payload;
                    query.level = level;
                    //Send it!
                    player._server._db.send(query);
                    return true;
                }
            }

            return false;
        }

        #endregion

        #region duels

        public void startDuel(Duel duel, Player challenger, Player challenged)
        {
            //Save their original teams
            duel.challengedTeam = challenged._team;
            duel.challengerTeam = challenger._team;


            Team team1 = _arena.getTeamByName(challenger._alias);
            Team team2 = _arena.getTeamByName(challenged._alias);

            if (_arena.getTeamByName(challenger._alias) == null)
            {
                team1 = new Team(_arena, _arena._server);
                //Assign some information to the team
                team1._name = challenger._alias;
                team1._isPrivate = true;
                team1._id = (short)_arena.Teams.Count();
                _arena.createTeam(team1);
            }

            if (_arena.getTeamByName(challenged._alias) == null)
            {
                team2 = new Team(_arena, _arena._server);
                //Assign some information to the team
                team2._name = challenged._alias;
                team2._isPrivate = true;
                team2._id = (short)_arena.Teams.Count();
                _arena.createTeam(team2);
            }

            team1.addPlayer(challenger, true);
            team2.addPlayer(challenged, true);

            duel.duelStarted = true;
            challenger.warp(19168, 1408);
            challenger.inventoryModify(true, 104, 1);
            challenger.sendMessage(0, "You have been healed and given ammo, Please use a grenade to start your duel.");
            challenged.warp(19168, 1408);
            challenged.inventoryModify(true, 104, 1);
            challenged.sendMessage(0, "You have been healed and given ammo, Please use a grenade to start your duel.");
        }
        #endregion


        #region Map Voting

        /// <summary>
        /// Switches the teams before a game start.
        /// </summary>
        private void SwitchTeams()
        {
            foreach (var player in _arena.PlayersIngame)
            {
                if (_currentMap.Contains("RedBlue"))
                {
                    Team red = _arena.getTeamByName("Red");
                    Team blue = _arena.getTeamByName("Blue");

                    //Sanity checks
                    if (red == null || blue == null)
                        return;

                    if (red.ActivePlayerCount <= blue.ActivePlayerCount)
                    {
                        if (player._team != red)
                            red.addPlayer(player);
                    }
                    else
                    {
                        if (player._team != blue)
                            blue.addPlayer(player);
                    }
                }

                if (_currentMap.Contains("Rebels"))
                {
                    Team red = _arena.getTeamByName("Kaiser Rebels");
                    Team blue = _arena.getTeamByName("Kaiser Republic");

                    //Sanity checks
                    if (red == null || blue == null)
                        return;

                    if (red.ActivePlayerCount <= blue.ActivePlayerCount)
                    {
                        if (player._team != red)
                            red.addPlayer(player);
                    }
                    else
                    {
                        if (player._team != blue)
                            blue.addPlayer(player);
                    }
                }

                if (_currentMap.Contains("KaiserPass"))
                {
                    Team titan = _arena.getTeamByName("Titan Militia");
                    Team colly = _arena.getTeamByName("Collective Military");

                    //Sanity checks
                    if (titan == null || colly == null)
                        return;

                    if (titan.ActivePlayerCount <= colly.ActivePlayerCount)
                    {
                        if (player._team != titan)
                            titan.addPlayer(player);
                    }
                    else
                    {
                        if (player._team != colly)
                            colly.addPlayer(player);
                    }

                }

                if (_currentMap.Contains("GreenYellow"))
                {
                    Team green = _arena.getTeamByName("Green");
                    Team yellow = _arena.getTeamByName("Yellow");

                    //Sanity checks
                    if (green == null || yellow == null)
                        return;

                    if (green.ActivePlayerCount <= yellow.ActivePlayerCount)
                    {
                        if (player._team != green)
                            green.addPlayer(player);
                    }
                    else
                    {
                        if (player._team != yellow)
                            yellow.addPlayer(player);
                    }
                }
                if (_currentMap.Contains("PinkPurple"))
                {
                    Team pink = _arena.getTeamByName("Pink");
                    Team purple = _arena.getTeamByName("Purple");

                    //Sanity checks
                    if (pink == null || purple == null)
                        return;

                    if (pink.ActivePlayerCount <= purple.ActivePlayerCount)
                    {
                        if (player._team != pink)
                            pink.addPlayer(player);
                    }
                    else
                    {
                        if (player._team != purple)
                            purple.addPlayer(player);
                    }
                }
                if (_currentMap.Contains("GoldSilver"))
                {
                    Team gold = _arena.getTeamByName("Gold");
                    Team silver = _arena.getTeamByName("Silver");

                    //Sanity checks
                    if (gold == null || silver == null)
                        return;

                    if (gold.ActivePlayerCount <= silver.ActivePlayerCount)
                    {
                        if (player._team != gold)
                            gold.addPlayer(player);
                    }
                    else
                    {
                        if (player._team != silver)
                            silver.addPlayer(player);
                    }
                }
            }
        }

       


        public string getMap()
        {

            //Do we have enough players voting? 15%
            int inGameCount = _arena.PlayersIngame.Count();
            int votes = 0;
            foreach (KeyValuePair<string, List<int>> voting in _mapVotes)
            {
                votes += voting.Value.Count();
            }

            if (votes < Math.Round(inGameCount * 0.15f))
            {
                _arena.sendArenaMessage("&Not enough players voted. Playing last map!");
                return _currentMap;
            }

            if (votes > 0)
            {
                _currentMap = _mapVotes.OrderByDescending(x => x.Value.Count).First().Key;
                _arena.sendArenaMessage(String.Format("&{0} has won with {1} votes!", _currentMap, _mapVotes[_currentMap].Count()));
            }

            _votingEnded = true;

            //Clear our votes
            foreach (KeyValuePair<string, List<int>> k in _mapVotes)
            {
                k.Value.Clear();
            }

            SwitchTeams();

            if (_currentMap.Contains("Snipers"))
            {
                _gameType = Settings.GameTypes.SNIPERS;
                Team team1 = null;
                Team team2 = null;
                if (_currentMap.Contains("RedBlue"))
                {
                    team1 = _arena.getTeamByName("Red");
                    team2 = _arena.getTeamByName("Blue");
                }
                if (_currentMap.Contains("KaiserPass"))
                {
                    team1 = _arena.getTeamByName("Titan Militia");
                    team2 = _arena.getTeamByName("Collective Military");
                }
                if (_currentMap.Contains("PinkPurple"))
                {
                    team1 = _arena.getTeamByName("Pink");
                    team2 = _arena.getTeamByName("Purple");
                }
                _snipers.setTeams(team1, team2, false);
            }
            if (_currentMap.Contains("KOTH"))
            {
                _gameType = Settings.GameTypes.KOTH;
                Team team1 = null;
                Team team2 = null;
                if (_currentMap.Contains("KaiserPass"))
                {
                    team1 = _arena.getTeamByName("Titan Militia");
                    team2 = _arena.getTeamByName("Collective Military");
                }
                _koth.setTeams(team1, team2, false);
            }
            if (_currentMap.Contains("TDM"))
            {
                _gameType = Settings.GameTypes.TDM;
                Team team1 = null;
                Team team2 = null;
                if (_currentMap.Contains("RedBlue"))
                {
                    team1 = _arena.getTeamByName("Red");
                    team2 = _arena.getTeamByName("Blue");
                }
                if (_currentMap.Contains("Rebels"))
                {
                    team1 = _arena.getTeamByName("Kaiser Rebels");
                    team2 = _arena.getTeamByName("Kaiser Republic");
                }
                if (_currentMap.Contains("KaiserPass"))
                {
                    team1 = _arena.getTeamByName("Titan Militia");
                    team2 = _arena.getTeamByName("Collective Military");
                }
                if (_currentMap.Contains("PinkPurple"))
                {
                    team1 = _arena.getTeamByName("Pink");
                    team2 = _arena.getTeamByName("Purple");
                }
                if (_currentMap.Contains("GoldSilver"))
                {
                    team1 = _arena.getTeamByName("Gold");
                    team2 = _arena.getTeamByName("Silver");
                }
                if (_currentMap.Contains("GreenYellow"))
                {
                    team1 = _arena.getTeamByName("Green");
                    team2 = _arena.getTeamByName("Yellow");
                }
                _tdm.setTeams(team1, team2, false);
            }

            if (_currentMap.Contains("SnD"))
            {
                _gameType = Settings.GameTypes.DEMOLITION;
                Team team1 = null;
                Team team2 = null;
                if (_currentMap.Contains("RedBlue"))
                {
                    team1 = _arena.getTeamByName("Red");
                    team2 = _arena.getTeamByName("Blue");
                }
                if (_currentMap.Contains("KaiserPass"))
                {
                    team1 = _arena.getTeamByName("Titan Militia");
                    team2 = _arena.getTeamByName("Collective Military");
                }
                _demolition.setTeams(team1, team2, false);
            }


            return _currentMap;
        }
        #endregion

        #region USLMatch
        /// <summary>
        /// Gets the arena ready for our match
        /// </summary>
        private void MatchSetup()
        {
            foreach (Player p in _arena.Players)
            {
                //Make sure no one has grant
                if (_arena.IsOwner(p))
                {
                    _arena._owner.Remove(p._alias);
                    //Reset any temp powers they might have
                    if (!p._developer && !p._admin && p._permissionTemp > Data.PlayerPermission.Normal)
                        p._permissionTemp = Data.PlayerPermission.Normal;
                }

                //Turn off banners for in game players (can cause the player lag by spamming them)
                if (!p.IsSpectator)
                    p._bAllowBanner = false;
            }
        }
        #endregion

        #region Custom Calls
        private Player findSquadWarp(IEnumerable<Player> squadmates, Player player)
        {
            Player warpTo = null;

            List<Player> potentialTargets = new List<Player>();

            //Start shuffling through looking for potential targets
            foreach (Player squadmate in squadmates)
            {
                int enemycount = 0;
                double distance = 0;

                //Any enemies?
                enemycount = _arena.getPlayersInRange
                    (squadmate._state.positionX, squadmate._state.positionY, 625).Where
                    (p => p._team != squadmate._team && !p.IsDead).Count();

                //Decent distance away? Don't want to warp ourselves super sort distances
                distance = Helpers.distanceTo(squadmate._state, player._state);

                //Is he a match!?
                if (distance >= 2000 && enemycount == 0)
                {
                    potentialTargets.Add(squadmate);
                }
            }

            //Randomize!
            if (potentialTargets.Count > 0)
            {
                warpTo = potentialTargets[new Random().Next(0, potentialTargets.Count)];
            }


            return warpTo;
        }

        private bool warpToSquad(Player player)
        {
            //Public arena?
            if (!_arena._bIsPublic)
            {
                player.sendMessage(-1, "Only allowed in public arenas!");
                return false;
            }

            //No squad no laundry
            if (player._squad == "")
            {
                player.sendMessage(-1, "You don't have a squad to warp to! Join or create a squad.");
                return false;
            }

            //Lets find some squaddies
            IEnumerable<Player> squadmates = player._team.ActivePlayers.Where(p => p._squad == player._squad && p.IsDead == false && p != player);

            //No squadmates online on his team?
            if (squadmates.Count() == 0)
            {
                player.sendMessage(-1, "You don't have any squadmates online on your team!");
                return false;
            }

            Player warpTo = findSquadWarp(squadmates, player);

            //Can we find an appropriate target?
            if (warpTo == null)
            {
                player.sendMessage(-1, "Your squadmates are in battle or dead! Try again soon");
                return false;
            }

            //Warp him!
            player.warp(warpTo);
            warpTo.sendMessage(0, String.Format("!{0} has joined you in battle.", player._alias));

            return true;
        }
        #endregion
    }
}