/// <summary>
        /// Intitialize a new session with the given level number of players and teams.
        /// </summary>
        /// <param name="levelIndex"></param>
        /// <param name="numPlayers"></param>
        public void Initialize(int levelIndex, int numPlayers, int numTeams)
        {
            ScriptMain.DebugPrint("Initializing a new session with {0} players and {1} total teams.", numPlayers, numTeams);

            LevelManager levelMgr = ScriptThread.GetOrAddExtension <LevelManager>();

            InitTeamConstantData(numTeams);

            current = new SessionInfo(levelIndex, numPlayers);

            levelMgr.DoLoadLevel(current.LevelIndex);

            for (int i = 0; i < numPlayers; i++)
            {
                TeamData team = FindFreeTeam();

                Player player = CreatePlayer(i, team.Index, i < 1);

                player.OnDead += OnPlayerDead;

                current.AddPlayer(i, team.Index, player);

                //ScriptMain.DebugPrint("Added a new player at slot '{0}' with name \"{1}\" teamIdx: {2}", i, player.Name, team.Index);
            }

            ScriptThread.SetVar("scr_activesession", true);
        }
        private void OnPlayerDead(Player sender, EventArgs e)
        {
            sender.RegisterDeaths(1);

            var scoreAmount = ScriptThread.GetVar <int>("scr_score_per_death");

            RegisterTeamScore(sender.Info.Sess.TeamNum, scoreAmount.Value);

            var attackerEnt = Function.Call <Entity>(CustomNative.GET_PED_SOURCE_OF_DEATH, sender.Ped.Ref);

            if (attackerEnt != null)
            {
                ScriptMain.DebugPrint("OnPlayerDead: Found attacker entity.");

                var inflictor = current.Players
                                .FirstOrDefault(x => x.PlayerRef.Ped == attackerEnt || x.PlayerRef.Vehicle == attackerEnt);

                if (inflictor.PlayerRef != null && sender.Info.Sess.TeamNum != inflictor.TeamIdx)
                {
                    ScriptMain.DebugPrint("OnPlayerDead: Found inflicting player.");

                    inflictor.PlayerRef.RegisterKills(1);

                    scoreAmount = ScriptThread.GetVar <int>("scr_score_per_kill");

                    RegisterTeamScore(inflictor.TeamIdx, scoreAmount.Value);

                    if (inflictor.PlayerRef is LocalPlayer)
                    {
                        displayMgr.ShowKillPopup(sender.Name, sender.Info.Sess.TeamNum);
                    }
                }
            }

            // award assist points to players that inflicted damage..

            for (int x = 0; x < current.NumPlayers; x++)
            {
                if (current.Players[x].TeamIdx == sender.Info.Sess.TeamNum)
                {
                    continue;
                }

                if (sender.Vehicle.Ref.HasBeenDamagedBy(current.Players[x].PlayerRef.Ped) ||
                    sender.Vehicle.Ref.HasBeenDamagedBy(current.Players[x].PlayerRef.Vehicle))
                {
                    current.Players[x].PlayerRef.RegisterKills(1);

                    scoreAmount = ScriptThread.GetVar <int>("scr_score_per_assist");

                    RegisterTeamScore(current.Players[x].TeamIdx, scoreAmount.Value);

                    if (current.Players[x].PlayerRef is LocalPlayer)
                    {
                        displayMgr.ShowKillPopup(sender.Name, sender.Info.Sess.TeamNum);
                    }
                }
            }
        }
        private void MakeDecision()
        {
            var otherPlayer = sessionMgr.Current.Players[0];

            float dist = Player.Position.DistanceTo(otherPlayer.PlayerRef.Position);

            AITarget target = new AITarget(otherPlayer.PlayerRef, dist);

            if ((target.Distance > 1000.0f || Player.Info.Sess.TeamNum == target.Player.Info.Sess.TeamNum)) // distance to the local player is too great.. find another ai player to fight.
            {
                target = null;

                for (int x = 0; x < sessionMgr.Current.NumPlayers; x++)
                {
                    otherPlayer = sessionMgr.Current.Players[x];

                    // We don't want ourselves, or any of our team members...
                    if (Player == otherPlayer.PlayerRef || Player.Info.Sess.TeamNum == otherPlayer.TeamIdx)
                    {
                        continue;
                    }

                    // Get the distance between us and the potential target..
                    dist = Player.Position.DistanceTo(otherPlayer.PlayerRef.Position);

                    // If they are closer than any of the previous candidates, add them as the best candidate.
                    if (dist < 1000.0f && (target == null || dist < target.Distance))
                    {
                        target = new AITarget(otherPlayer.PlayerRef, dist);
                    }
                }
            }

            // we found a target, change our status to fighting
            if (target != null)
            {
                Player.PersueTarget(target.Player);

                ScriptMain.DebugPrint("PilotAIController: {0} chose to fight the nearby player {1}", Player.Name, target.Player.Name);

                state.Status = AIStatus.FightOther;

                attackStartedTime = Game.GameTime;
            }

            // no target found ): We will just fly randomly..
            else
            {
                SetRandomDestination();

                state.Status = AIStatus.RandomFlight;
            }
        }
        public override void OnUpdate(int gameTime)
        {
            base.OnUpdate(gameTime);

            var position = Player.Position;

            if (!ScriptThread.GetVar <bool>("scr_hardcore").Value)
            {
                if (Function.Call <bool>(Hash.IS_OBJECT_NEAR_POINT, 0x9A3207B7, position.X, position.Y, position.Z, (float)random.Next(50, 200)) &&
                    position.DistanceTo(GameplayCamera.Position) < 700.0f &&
                    !flareMgr.CooldownActive &&
                    Probability.GetBoolean(0.0020f + intelligenceBias))
                {
                    flareMgr.Start();
                }

                /*(  if (Player.Vehicle.Ref.Health < Player.Vehicle.Ref.MaxHealth &&
                 *    !extinguisher.CooldownActive &&
                 *    Probability.GetBoolean(intelligenceBias) )
                 * {
                 *    ScriptMain.DebugPrint("use extinguisher (" + Name + ")");
                 *    extinguisher.Start();
                 * }*/
            }

            if (gameTime > state.NextDecisionTime)
            {
                // If we arent chasing a target, they have moved too far away, or have been chasing a target for a set amount of time, make a new decision.
                if (Player.ActiveTarget == null || Player.ActiveTarget.Ped.Ref.IsDead ||
                    position.DistanceTo(Player.ActiveTarget.Position) > 1000.0f ||
                    !Utility.IsPositionInArea(Player.ActiveTarget.Position, levelMgr.Level.Bounds.Min, levelMgr.Level.Bounds.Max) ||
                    Game.GameTime - attackStartedTime > 30000)
                {
                    MakeDecision();
                }

                state.SetNextDecisionTime(gameTime + random.Next(1000, 5000));
            }

            switch (state.Status)
            {
            case AIStatus.FightOther:
            {
                if (Player.ActiveTarget != null)
                {
                    if (Player.ActiveTarget.Position.DistanceTo(Player.Position) > 600.0f)
                    {
                        if (!tooFar)
                        {
                            var destination = Player.ActiveTarget.Position;

                            Function.Call(Hash.TASK_PLANE_MISSION,
                                          Player.Ped.Ref,
                                          Player.Vehicle.Ref,
                                          0,
                                          0,
                                          destination.X, destination.Y, destination.Z,
                                          6, 309.0, 26.0f, 200.0, 1000.0, 20.0);

                            tooFar = true;
                        }
                    }

                    else if (Function.Call <int>(Hash.GET_ACTIVE_VEHICLE_MISSION_TYPE, Player.Vehicle.Ref) != 6)
                    {
                        tooFar = false;
                        Player.PersueTarget(Player.ActiveTarget);
                        UI.ShowSubtitle("persuing " + Function.Call <int>(Hash.GET_ACTIVE_VEHICLE_MISSION_TYPE, Player.Vehicle.Ref).ToString());
                    }
                }


                break;
            }

            case AIStatus.RandomFlight:
            {
                if (position.DistanceTo(destination) < 15.0f)
                {
                    SetRandomDestination();

                    ScriptMain.DebugPrint("Set new random destination for " + Player.Name);
                }

                else if (Player.Position.DistanceTo(sessionMgr.Current.Players[0].PlayerRef.Position) < 1000.0f &&
                         Player.Info.Sess.TeamNum != sessionMgr.Current.Players[0].TeamIdx)
                {
                    Player.PersueTarget(sessionMgr.Current.Players[0].PlayerRef);

                    state.Status = AIStatus.FightOther;
                }

                break;
            }
            }

            for (int i = 0; i < sessionMgr.Current.NumPlayers; i++)
            {
                if (gameTime - lastShotAtTime > 900)
                {
                    var otherPlayer = sessionMgr.Current.Players[i];

                    if (otherPlayer.TeamIdx == Player.Info.Sess.TeamNum)
                    {
                        continue;
                    }

                    var p = otherPlayer.PlayerRef.Position;

                    var direction = Vector3.Normalize(p - position);

                    var otherHeading = otherPlayer.PlayerRef.Vehicle.Ref.Heading;

                    if (Probability.GetBoolean(0.1f + intelligenceBias) && position.DistanceTo(p) < 400.0f)
                    {
                        Function.Call(Hash.SET_VEHICLE_SHOOT_AT_TARGET, Player.Ped.Ref, otherPlayer.PlayerRef.Vehicle.Ref, p.X, p.Y, p.Z);

                        ScriptMain.DebugPrint("Shoot at target (" + Player.Name + " > " + otherPlayer.PlayerRef.Name + ") team idx:" + otherPlayer.PlayerRef.Info.Sess.TeamNum.ToString() + " sess team:" + otherPlayer.TeamIdx);

                        lastShotAtTime = gameTime;
                    }
                }
            }
        }