예제 #1
0
        /// <summary>
        /// Updates the target situation to retreat from an enemy.
        /// </summary>
        public static void Retreat(AIController controller, ref AISituation situation, NavMeshAgent agent)
        {
            situation.TargetCover    = null;
            situation.TargetPosition = situation.CurrentPosition;

            var vec = (controller.transform.position - situation.ThreatGroundPosition).normalized;

            float extra = 0f;

            while (extra < 20f)
            {
                var p = situation.ThreatGroundPosition + vec * (controller.Distances.MinRetreat + extra);

                var path = new NavMeshPath();
                agent.CalculatePath(p, path);

                if (path.status != NavMeshPathStatus.PathInvalid)
                {
                    situation.TargetPosition = p;
                    break;
                }
                else
                {
                    extra += 1f;
                }
            }
        }
예제 #2
0
        /// <summary>
        /// Updates target situation to visit a patrol point.
        /// </summary>
        public static void Patrol(AIController controller, ref AISituation situation)
        {
            situation.TargetCover    = null;
            situation.TargetPosition = situation.CurrentPosition;

            if (controller.Waypoints != null && controller.Waypoints.Length > 0)
            {
                if (controller.Waypoints.Length == 1)
                {
                    situation.PatrolPoint = 0;
                }
                else
                {
                    situation.PatrolPoint = situation.PatrolPoint % controller.Waypoints.Length;

                    if (Vector3.Distance(controller.transform.position, controller.Waypoints[situation.PatrolPoint].Position) < 1)
                    {
                        situation.PatrolPoint++;
                        situation.PatrolPoint = situation.PatrolPoint % controller.Waypoints.Length;
                    }
                }

                situation.TargetPosition = controller.Waypoints[situation.PatrolPoint].Position;
            }
        }
예제 #3
0
        /// <summary>
        /// Updates the target situation to retreat from an enemy.
        /// </summary>
        public static void AvoidGrenade(AIController controller, ref AISituation situation, NavMeshAgent agent)
        {
            situation.TargetCover    = null;
            situation.TargetPosition = situation.CurrentPosition;

            var vec = controller.transform.position - situation.NearestGrenadePosition;

            vec.y = 0;
            vec.Normalize();

            situation.TargetPosition = situation.NearestGrenadePosition + vec * controller.Grenades.AvoidDistance;
        }
예제 #4
0
        /// <summary>
        /// Looks for a position to investigate around the character.
        /// </summary>
        public static void FindNewThreatPosition(AIController controller, ref AISituation situation, NavMeshAgent agent, int attempts = 0)
        {
            var vec = Quaternion.AngleAxis(UnityEngine.Random.Range(0, 360), Vector3.up) * Vector3.forward;

            if (!FindNewThreatPositionInDirection(vec, controller, ref situation, agent))
            {
                if (attempts < 3)
                {
                    FindNewThreatPosition(controller, ref situation, agent, attempts + 1);
                }
            }
        }
예제 #5
0
        /// <summary>
        /// Updates the target situation to approach an enemy that's in cover.
        /// </summary>
        public static void ApproachACovered(AIController controller, ref AISituation situation)
        {
            var path    = new NavMeshPath();
            var corners = new Vector3[16];

            var resultPosition = situation.ThreatGroundPosition;
            var resultDistance = 9999f;

            var coverAngle = Util.AngleOfVector(situation.ThreatCoverForward);

            for (int a = 0; a < 18; a++)
            {
                var angle    = coverAngle - 180f + a * 10;
                var position = situation.ThreatGroundPosition + new Vector3(Mathf.Cos(angle * Mathf.Deg2Rad), 0, Mathf.Sin(angle * Mathf.Deg2Rad)) * controller.Distances.MinEnemy;

                NavMeshHit hit;
                if (NavMesh.SamplePosition(position, out hit, 1.0f, NavMesh.AllAreas) &&
                    AIUtil.IsInSight(controller, hit.position, situation.ThreatStandingTopPosition))
                {
                    NavMesh.CalculatePath(controller.transform.position, hit.position, NavMesh.AllAreas, path);

                    if (path.status == NavMeshPathStatus.PathComplete &&
                        !AIUtil.IsPositionTooCloseToFriends(controller, hit.position))
                    {
                        var dist = 0f;

                        for (int i = 1; i < path.GetCornersNonAlloc(corners); i++)
                        {
                            dist += Vector3.Distance(corners[i - 1], corners[i]);
                        }

                        if (dist < resultDistance)
                        {
                            resultDistance = dist;
                            resultPosition = position;
                        }
                    }
                }
            }

            situation.TargetPosition = resultPosition;
            situation.TargetCover    = null;
        }
        /// <summary>
        /// Sets the current state machine state. Resets the state and burst timers and counters.
        /// </summary>
        private void setNewState(AIState value, AISituation situation)
        {
            _situation = situation;

            if (value != AIState.reloadInCover && _state != AIState.reloadInCover)
            {
                _stateTime = 0;
            }

            if (_state != value && value == AIState.fireInCover)
            {
                _situation.BurstCount++;
            }
            else if (value == AIState.takeCover || value == AIState.retreat || value == AIState.approach)
            {
                _situation.BurstCount = 0;
            }

            _state            = value;
            _walkingBurstWait = 0;
        }
예제 #7
0
        /// <summary>
        /// Checks if this situation as better info than the other.
        /// </summary>
        public bool HasBetterThreatInfo(AIController me, ref AISituation other)
        {
            if (Threat == null || IsThreatPositionInvestigative)
            {
                return(false);
            }

            if (other.Threat == null || other.IsThreatPositionInvestigative)
            {
                if (LastSeenThreatTime - 1 > other.LastSeenThreatTime)
                {
                    return(true);
                }
            }

            if (LastSeenThreatTime - 0.005f > other.LastSeenThreatTime)
            {
                return(true);
            }

            return(false);
        }
        /// <summary>
        /// Returns true if the current selected state is still valid.
        /// </summary>
        private bool isStateStillValid(AIState state, float time, AISituation previous, AISituation next)
        {
            var hasEnemyMoved          = Vector3.Distance(previous.ThreatGroundPosition, next.ThreatGroundPosition) > 4 || (previous.IsThreatInCover != next.IsThreatInCover);
            var canUseCover            = (next.IsRetreating && Behaviour.IsRetreatingUsingCovers) || (!next.IsRetreating && Behaviour.IsApproachingUsingCovers);
            var isTargetPositionUseful = next.CanSeeFromTargetPosition || next.IsRetreating;
            var burst = Behaviour.IsFightingUsingCovers ? CoveredFightingBursts : CoveredApproachBursts;

            if (next.IsNearGrenade)
            {
                return(state == AIState.avoidGrenade);
            }
            else
            {
                switch (state)
                {
                case AIState.avoidGrenade: return(time < 2 || next.IsNearGrenade);

                case AIState.investigate: return(next.IsAlerted && !next.HasInvestigatedTheLatestAlert && next.Threat == null && Vector3.Distance(next.ThreatGroundPosition, previous.TargetPosition) < Distances.ThreatInvestigation);

                case AIState.approach:
                    if (next.DidntFindCover && time < 4)
                    {
                        return(true);
                    }

                    return(!next.IsRetreating && (!Behaviour.IsFightingUsingCovers || !Behaviour.IsApproachingUsingCovers) && isTargetPositionUseful);

                case AIState.fireInCover: return(next.IsAllowedToBeAggressive && canUseCover && !hasEnemyMoved && time <= burst.TotalPeekDuration && next.IsTargetCoverGood && isTargetPositionUseful);

                case AIState.hideInCover: return(canUseCover && !hasEnemyMoved && time <= burst.Wait && next.IsTargetCoverGood && isTargetPositionUseful);

                case AIState.retreat: return(next.IsRetreating && Behaviour.IsRetreatingUsingCovers && isTargetPositionUseful);

                case AIState.reloadInCover: return(!next.IsGunReady);

                case AIState.none: return(false);

                case AIState.patrol:
                {
                    if (!next.IsAlerted && Waypoints != null && Waypoints.Length > 0 && _situation.PatrolPoint < Waypoints.Length)
                    {
                        if (Waypoints[_situation.PatrolPoint].Pause < float.Epsilon && Vector3.Distance(next.TargetPosition, next.CurrentPosition) < 0.5f)
                        {
                            return(false);
                        }
                        else if (Vector3.Distance(transform.position, _situation.TargetPosition) > 1)
                        {
                            return(true);
                        }
                        else
                        {
                            return(false);
                        }
                    }
                    else
                    {
                        return(false);
                    }
                }

                case AIState.patrolPause: return(!next.IsAlerted && Waypoints != null && Waypoints.Length > 0 && _situation.PatrolPoint < Waypoints.Length && time <= Waypoints[_situation.PatrolPoint].Pause);

                case AIState.takeCover:
                    if (next.Threat == null || !next.Threat.IsAttacking)
                    {
                        return(false);
                    }

                    return(canUseCover && !hasEnemyMoved && next.IsTargetCoverGood && isTargetPositionUseful && next.CurrentCover != next.TargetCover);
                }
            }

            return(false);
        }
        /// <summary>
        /// Sets a new state and takes necessary actions.
        /// </summary>
        private void updateSituation(AIState state)
        {
            if (_registeredGroup != Group)
            {
                if (Group != null)
                {
                    register(Group);
                }
                else
                {
                    unregister();
                }
            }

            setNewState(state, _situation);

            switch (_state)
            {
            case AIState.approach:
                if (_situation.IsThreatInCover)
                {
                    Positioning.ApproachACovered(this, ref _situation);
                }
                else
                {
                    Positioning.ApproachAFree(this, ref _situation, _agent, Distances.MaxWalkingFight, true);
                }
                break;

            case AIState.retreat:
                Positioning.Retreat(this, ref _situation, _agent);
                break;

            case AIState.avoidGrenade:
                Positioning.AvoidGrenade(this, ref _situation, _agent);
                break;

            case AIState.investigate:
                _situation.HasInvestigatedTheLatestAlert = false;

                if (_situation.HasAnInvestigatedAlert)
                {
                    if (_situation.WasTheLastInvestigatedAlertTheFirst)
                    {
                        if (!Positioning.FindNewThreatPositionInDirection(_situation.DirectionOfInvestigation, this, ref _situation, _agent))
                        {
                            Positioning.FindNewThreatPosition(this, ref _situation, _agent);
                        }
                    }
                    else
                    {
                        Positioning.FindNewThreatPosition(this, ref _situation, _agent);
                    }
                }

                _situation.TargetCover    = null;
                _situation.TargetPosition = _situation.ThreatGroundPosition;

                _situation.DirectionOfInvestigation   = _situation.ThreatGroundPosition - transform.position;
                _situation.DirectionOfInvestigation.y = 0;
                _situation.DirectionOfInvestigation.Normalize();
                break;

            case AIState.takeCover:
                _situation.DidntFindCover = !Positioning.TakeCover(this, ref _situation);

                if (_situation.DidntFindCover)
                {
                    updateSituation(AIState.approach);
                }

                break;

            case AIState.reloadInCover:
                _motor.InputReload();
                break;

            case AIState.patrol:
                Positioning.Patrol(this, ref _situation);
                break;
            }

            updateAgent(_situation.TargetPosition);
            _previousSituation = _situation;
        }
        /// <summary>
        /// Calculates and returns best state to move to depending on the given situation.
        /// </summary>
        private AIState calcState(AISituation situation, AIState current, float time)
        {
            if (situation.IsAlerted)
            {
                if (situation.IsNearGrenade)
                {
                    return(AIState.avoidGrenade);
                }

                var burst = Behaviour.IsFightingUsingCovers ? CoveredFightingBursts : CoveredApproachBursts;

                var isTargetCoverTooCloseForApproach = !Behaviour.IsFightingUsingCovers &&
                                                       ((situation.CurrentCover != null && !situation.IsNewCover) ||
                                                        Vector3.Distance(situation.TargetPosition, situation.ThreatGroundPosition) < Distances.MaxApproach);
                var isCloseToFight = !Behaviour.IsFightingUsingCovers && Vector3.Distance(situation.CurrentPosition, situation.ThreatGroundPosition) < Distances.MaxApproach;

                if (situation.Threat == null)
                {
                    return(AIState.investigate);
                }
                else if (!situation.Threat.IsAttacking)
                {
                    return(AIState.approach);
                }
                else if ((situation.IsRetreating && Behaviour.IsRetreatingUsingCovers) ||
                         (!situation.IsRetreating && Behaviour.IsApproachingUsingCovers && !isCloseToFight && !isTargetCoverTooCloseForApproach))
                {
                    if (situation.CurrentCover == null || !situation.IsAllowedToBeAggressive || situation.CurrentCover != situation.TargetCover || (!situation.IsRetreating && (!situation.IsTargetCoverGood || !situation.CanSeeFromTargetPosition)))
                    {
                        return(AIState.takeCover);
                    }
                    else
                    {
                        if (!situation.IsGunReady)
                        {
                            return(AIState.reloadInCover);
                        }
                        else if (current == AIState.hideInCover)
                        {
                            if (time <= burst.Wait)
                            {
                                return(AIState.hideInCover);
                            }
                            else if (situation.BurstCount < burst.Count)
                            {
                                return(AIState.fireInCover);
                            }
                            else
                            {
                                return(AIState.takeCover);
                            }
                        }
                        else if (current == AIState.fireInCover)
                        {
                            if (time <= burst.TotalPeekDuration)
                            {
                                return(AIState.fireInCover);
                            }
                            else if (situation.BurstCount < burst.Count)
                            {
                                return(AIState.hideInCover);
                            }
                            else
                            {
                                return(AIState.takeCover);
                            }
                        }
                        else
                        {
                            return(AIState.hideInCover);
                        }
                    }
                }
                else
                {
                    if (situation.IsRetreating || !situation.IsAllowedToBeAggressive)
                    {
                        return(AIState.retreat);
                    }
                    else
                    {
                        return(AIState.approach);
                    }
                }
            }
            else
            {
                if (Waypoints != null)
                {
                    var isAlreadyCloseToTheOnlyOne = false;
                    var hasToWait = false;

                    if (Waypoints.Length == 0)
                    {
                        isAlreadyCloseToTheOnlyOne = true;
                        hasToWait = true;
                    }
                    else
                    {
                        if (_situation.PatrolPoint < Waypoints.Length)
                        {
                            hasToWait = Waypoints[_situation.PatrolPoint].Pause > float.Epsilon;
                        }

                        if (Waypoints.Length == 1)
                        {
                            isAlreadyCloseToTheOnlyOne = Vector3.Distance(transform.position, Waypoints[0].Position) < 0.7f;

                            if (!isAlreadyCloseToTheOnlyOne)
                            {
                                hasToWait = false;
                            }
                        }
                    }

                    if (hasToWait && (current == AIState.patrol || isAlreadyCloseToTheOnlyOne))
                    {
                        return(AIState.patrolPause);
                    }
                    else
                    {
                        return(AIState.patrol);
                    }
                }
                else
                {
                    return(AIState.patrolPause);
                }
            }
        }
        /// <summary>
        /// Returns an updated situation struct.
        /// </summary>
        public void Update(AIController controller, AISituation previous, bool updateInDetail)
        {
            if (!IsAlerted)
            {
                HasInvestigatedTheLatestAlert = false;
            }
            else if (Threat == null & Vector3.Distance(CurrentPosition, ThreatGroundPosition) < controller.Distances.ThreatInvestigation)
            {
                MarkInvestigated();
            }

            if (HasAnInvestigatedAlert)
            {
                InvestigatedAlertAge += Time.deltaTime;
            }

            // Check grenades
            {
                IsNearGrenade = false;
                float minDist = 1000;

                foreach (var grenade in GrenadeList.All)
                {
                    var vec  = grenade.transform.position - controller.transform.position;
                    var dist = vec.magnitude;

                    if (dist < grenade.ExplosionRadius)
                    {
                        if (!IsNearGrenade || dist < minDist)
                        {
                            minDist                = dist;
                            IsNearGrenade          = true;
                            NearestGrenadePosition = grenade.transform.position;

                            if (Threat == null)
                            {
                                HasInvestigatedTheLatestAlert = false;
                                HasAnInvestigatedAlert        = false;
                                ThreatGroundPosition          = grenade.transform.position;
                            }
                        }
                    }
                }
            }

            // Check friends and enemies.
            if (Threat == null || (!IsAlerted && !IsGettingAlerted))
            {
                var minEnemyInfoTimer = controller.View.EnemySustainTime;

                foreach (var actor in Actors.All)
                {
                    if (actor != controller.Actor)
                    {
                        if (actor.Side != controller.Actor.Side)
                        {
                            if (AIUtil.IsInSight(controller, actor.TopPosition))
                            {
                                IsAlerted = true;
                                ReadEnemyState(actor);
                                break;
                            }
                        }
                        else if (actor.AI != null && actor.AI.IsAlerted)
                        {
                            var vector = actor.transform.position - controller.transform.position;

                            if (vector.magnitude < controller.View.CommunicationDistance)
                            {
                                IsAlerted = true;

                                if (actor.AI.Situation.NoThreatTimer < actor.AI.View.EnemySustainTime &&
                                    minEnemyInfoTimer > actor.AI.Situation.NoThreatTimer)
                                {
                                    TakeEnemyState(actor.AI);
                                }
                            }
                        }
                    }
                }
            }

            // Check friends if they had investigated the same position
            if (IsAlerted && Threat == null && !HasInvestigatedTheLatestAlert)
            {
                foreach (var friend in Actors.All)
                {
                    if (friend != controller.Actor &&
                        friend.Side == controller.Actor.Side &&
                        friend.AI.IsAlerted &&
                        friend.AI.Situation.HasAnInvestigatedAlert &&
                        friend.AI.Situation.InvestigatedAlertAge < 10 &&
                        Vector3.Distance(friend.transform.position, controller.transform.position) < controller.View.CommunicationDistance &&
                        Vector3.Distance(friend.AI.Situation.InvestigatedThreatPosition, ThreatGroundPosition) < controller.Distances.ThreatInvestigation)
                    {
                        MarkInvestigated();
                        break;
                    }
                }
            }


            // Check threats
            if (Threat == null)
            {
                var minDist = 100000f;

                foreach (var alert in Alerts.All)
                {
                    var dist = Vector3.Distance(controller.transform.position, alert.Position);

                    if (dist < alert.Range)
                    {
                        if (dist < minDist)
                        {
                            minDist                       = dist;
                            IsAlerted                     = true;
                            ThreatGroundPosition          = alert.Position;
                            HasAnInvestigatedAlert        = false;
                            HasInvestigatedTheLatestAlert = false;
                        }
                    }
                }
            }

            // React to grenades
            if (IsNoticingGrenade)
            {
                if (GrenadeReaction < float.Epsilon)
                {
                    IsNoticingGrenade = false;
                }
                else
                {
                    GrenadeReaction -= Time.deltaTime;
                    IsNearGrenade    = false;
                }
            }
            else if (IsNearGrenade && !previous.IsNearGrenade)
            {
                GrenadeReaction   = controller.Fighting.GrenadeReactionTime;
                IsNoticingGrenade = true;
                IsNearGrenade     = false;
            }

            if (IsNearGrenade)
            {
                IsAlerted = true;
            }

            // React to being alerted.
            if (IsGettingAlerted)
            {
                if (AlertReaction < float.Epsilon)
                {
                    IsGettingAlerted = false;
                    IsAlerted        = true;
                }
                else
                {
                    AlertReaction -= Time.deltaTime;
                    IsAlerted      = false;
                }
            }
            else if (IsAlerted && !previous.IsAlerted)
            {
                AlertReaction    = controller.Fighting.ReactionTime;
                IsGettingAlerted = true;
                IsAlerted        = false;
            }

            if (previous.TargetCover != null &&
                (controller.Motor.LeftCover == previous.TargetCover || controller.Motor.RightCover == previous.TargetCover || controller.Motor.Cover == previous.TargetCover))
            {
                CurrentCover = previous.TargetCover;
            }
            else
            {
                CurrentCover = controller.Motor.Cover;
            }

            CurrentPosition = controller.transform.position;
            IsGunReady      = controller.Motor.IsGunReady && controller.Motor.Gun.Clip >= controller.Motor.Gun.ClipSize * controller.Fighting.ReloadFraction;

            if (controller.Health != null && Threat != null && Threat.IsAttacking)
            {
                IsRetreating = IsAlerted && controller.Health.Health <= controller.Fighting.MinHealth;
            }
            else
            {
                IsRetreating = false;
            }

            if (IsAlerted)
            {
                var couldSeeTheEnemy = CanSeeTheThreat;
                CanSeeTheThreat = false;

                if (Threat != null)
                {
                    if (couldSeeTheEnemy || updateInDetail)
                    {
                        CanSeeTheThreat = AIUtil.IsInSight(controller, Threat.TopPosition);
                    }

                    if (CanSeeTheThreat)
                    {
                        ReadEnemyState(Threat);
                    }
                    else
                    {
                        NoThreatTimer += Time.deltaTime;

                        if (updateInDetail)
                        {
                            // Check friends and enemies.
                            foreach (var friend in Actors.All)
                            {
                                if (friend != controller.Actor &&
                                    friend.Side == controller.Actor.Side &&
                                    friend.AI != null &&
                                    friend.AI.IsAlerted &&
                                    friend.AI.Situation.CanSeeTheThreat &&
                                    friend.AI.Situation.Threat == Threat)
                                {
                                    var vector = friend.transform.position - controller.transform.position;

                                    if (vector.magnitude < controller.View.CommunicationDistance)
                                    {
                                        TakeEnemyState(friend.AI);
                                    }
                                }
                            }
                        }
                    }
                }

                if (TargetCover != null && updateInDetail)
                {
                    var distanceToThreat = Vector3.Distance(TargetPosition, ThreatGroundPosition);

                    IsTargetCoverGood = distanceToThreat >= controller.Distances.MinEnemy &&
                                        distanceToThreat >= controller.Cover.MinCoverToEnemyDistance &&
                                        AIUtil.IsGoodAngle(controller,
                                                           TargetCover,
                                                           TargetPosition,
                                                           ThreatGroundPosition,
                                                           TargetCover.IsTall(controller.Motor.CoverSettings.TallThreshold)) &&
                                        !AIUtil.IsCoverPositionTooCloseToFriends(TargetCover, controller, TargetPosition);
                }

                if (updateInDetail)
                {
                    if (TargetCover != null && TargetCover.IsTall(controller.Motor.CoverSettings.TallThreshold))
                    {
                        Vector3 aimPoint;

                        if (TargetDirection < 0)
                        {
                            aimPoint = TargetCover.LeftCorner(0, controller.Motor.CoverSettings.CornerOffset.x);
                        }
                        else
                        {
                            aimPoint = TargetCover.RightCorner(0, controller.Motor.CoverSettings.CornerOffset.x);
                        }

                        CanSeeFromTargetPosition = AIUtil.IsInSight(controller, aimPoint, ThreatStandingTopPosition);
                    }
                    else
                    {
                        CanSeeFromTargetPosition = AIUtil.IsInSight(controller, TargetPosition, ThreatStandingTopPosition);
                    }
                }
            }
            else
            {
                if (TargetCover != null && updateInDetail)
                {
                    IsTargetCoverGood = !AIUtil.IsCoverPositionTooCloseToFriends(TargetCover, controller, TargetPosition);
                }

                CanSeeFromTargetPosition = true;
            }
        }
예제 #12
0
        /// <summary>
        /// Updates the target situation to approach an enemy that's not in cover.
        /// </summary>
        public static void ApproachAFree(AIController controller, ref AISituation situation, NavMeshAgent agent, float maxDistance, bool approachStanding)
        {
            situation.TargetCover    = null;
            situation.TargetPosition = situation.ThreatGroundPosition;

            var path = new NavMeshPath();

            agent.CalculatePath(situation.ThreatGroundPosition, path);

            var corners = new Vector3[16];
            var count   = path.GetCornersNonAlloc(corners);

            if (count < 2)
            {
                return;
            }

            var i  = 0;
            var p0 = corners[i];
            var p1 = corners[i + 1];
            var f  = 1f;

            var targetPosition = approachStanding ? situation.ThreatStandingTopPosition : situation.ThreatGroundPosition;

            {
                var distLeft = Vector3.Distance(controller.transform.position, situation.ThreatGroundPosition);

                while (distLeft > maxDistance)
                {
                    var pd   = Vector3.Distance(p0, p1);
                    var left = distLeft - maxDistance;
                    distLeft -= pd;

                    if (pd >= left)
                    {
                        f = left / pd;
                        break;
                    }
                    else
                    {
                        i++;

                        if (i + 1 >= count)
                        {
                            i = 0;
                            break;
                        }
                        else
                        {
                            p0 = corners[i];
                            p1 = corners[i + 1];
                        }
                    }
                }
            }

            while (i + 1 < count)
            {
                var p = p0 + (p1 - p0) * f;

                if (AIUtil.IsInSight(controller, p, targetPosition))
                {
                    situation.TargetPosition = p;
                    break;
                }

                f += 0.2f;

                if (f >= 1f)
                {
                    if (AIUtil.IsInSight(controller, p1, targetPosition))
                    {
                        situation.TargetPosition = p1;
                        break;
                    }

                    f = 0;
                    i++;
                }
            }
        }
예제 #13
0
        /// <summary>
        /// Updates the target situation to take cover. Returns true if a cover was found.
        /// </summary>
        public static bool TakeCover(AIController controller, ref AISituation situation)
        {
            var currentVectorToTarget   = situation.ThreatGroundPosition - situation.CurrentPosition;
            var currentDistanceToTarget = currentVectorToTarget.magnitude;

            Cover   result             = null;
            float   resultPathDistance = 0;
            int     resultDirection    = 0;
            Vector3 resultPosition     = situation.CurrentPosition;

            var path    = new NavMeshPath();
            var corners = new Vector3[32];

            var isAlreadyTooClose = currentDistanceToTarget <= controller.Distances.MinEnemy || currentDistanceToTarget <= controller.Cover.MinCoverToEnemyDistance;

            var covers = new List <CoverItem>();

            foreach (var collider in Physics.OverlapSphere(controller.transform.position, controller.Cover.MaxDistance, 0x1 << 8, QueryTriggerInteraction.Collide))
            {
                if (!collider.isTrigger)
                {
                    continue;
                }

                var cover = CoverSearch.GetCover(collider.gameObject);

                if (cover == null || cover == situation.CurrentCover)
                {
                    continue;
                }

                var item = new CoverItem();
                item.Cover  = cover;
                item.IsTall = cover.IsTall(controller.Motor.CoverSettings.TallThreshold);

                if (item.IsTall)
                {
                    item.Direction = cover.ClosestCornerTo(situation.CurrentPosition, controller.Motor.CoverSettings.CornerAimTriggerDistance, out item.Point);
                }
                else
                {
                    item.Point = cover.ClosestPointTo(situation.CurrentPosition, controller.Motor.CoverSettings.LowSideEnterRadius, 0.3f);
                }

                if (float.IsNaN(item.Point.x) || float.IsNaN(item.Point.z))
                {
                    continue;
                }

                item.Point.y  = cover.Bottom;
                item.Distance = Vector3.Distance(controller.transform.position, item.Point);

                covers.Add(item);
            }

            foreach (var item in covers.OrderBy(o => o.Distance))
            {
                var isTall         = item.IsTall;
                var cover          = item.Cover;
                var point          = item.Point;
                var coverDirection = item.Direction;

                if (!AIUtil.IsGoodAngle(controller, cover, point, situation.ThreatGroundPosition, isTall))
                {
                    continue;
                }

                var distanceToTarget = Vector3.Distance(situation.ThreatGroundPosition, point);

                if (distanceToTarget < controller.Distances.MinEnemy ||
                    distanceToTarget < controller.Cover.MinCoverToEnemyDistance)
                {
                    continue;
                }

                if (situation.CurrentCover != null && Vector3.Distance(situation.CurrentPosition, point) < controller.Cover.MinSwitchDistance)
                {
                    continue;
                }

                if ((controller.Health == null || controller.Health.Health > controller.Fighting.MinHealth))
                {
                    if (isTall)
                    {
                        Vector3 aimPoint;
                        coverDirection = cover.ClosestCornerTo(point, -controller.Motor.CoverSettings.CornerOffset.x, out aimPoint);

                        if (!AIUtil.IsInSight(controller, aimPoint, situation.ThreatStandingTopPosition))
                        {
                            continue;
                        }
                    }
                    else if (!AIUtil.IsInSight(controller, point, situation.ThreatStandingTopPosition))
                    {
                        continue;
                    }
                }

                var distanceToOrigin = Vector3.Distance(situation.CurrentPosition, point);

                if (situation.CurrentCover == null)
                {
                    if (distanceToOrigin > controller.Cover.MaxDistance)
                    {
                        continue;
                    }
                }
                else
                if (distanceToOrigin > controller.Cover.MaxSwitchDistance)
                {
                    continue;
                }

                var areThereFriends = false;

                {
                    var hasChangedPosition = false;

                    Vector3 side;

                    if (Vector3.Dot((point - situation.CurrentPosition).normalized, cover.Right) > 0)
                    {
                        side = cover.Right;
                    }
                    else
                    {
                        side = cover.Left;
                    }

                    do
                    {
                        hasChangedPosition = false;

                        if (AIUtil.IsCoverPositionTooCloseToFriends(cover, controller, point))
                        {
                            var next = point + side * 0.5f;

                            if (cover.IsInFront(next, false))
                            {
                                point = next;
                                hasChangedPosition = true;
                            }
                            else
                            {
                                areThereFriends = true;
                            }
                        }
                    }while (hasChangedPosition);
                }

                if (areThereFriends)
                {
                    continue;
                }

                var isOk = false;

                NavMesh.CalculatePath(situation.CurrentPosition, point, NavMesh.AllAreas, path);

                float pathDistance = 0f;

                var count = path.GetCornersNonAlloc(corners);

                if (count < 2)
                {
                    continue;
                }

                var isTooCloseToEnemy = false;

                for (int i = 1; i < count; i++)
                {
                    pathDistance += Vector3.Distance(corners[i - 1], corners[i]);

                    if (!isAlreadyTooClose)
                    {
                        if (Util.DistanceToSegment(situation.ThreatGroundPosition, corners[i - 1], corners[i]) <= controller.Distances.MinPassing)
                        {
                            isTooCloseToEnemy = true;
                            break;
                        }
                    }
                }

                if (isTooCloseToEnemy)
                {
                    continue;
                }

                if (situation.CurrentCover == null)
                {
                    isOk = result == null || pathDistance < resultPathDistance;
                }
                else if (controller.Health == null || controller.Health.Health > controller.Fighting.MinHealth)
                {
                    isOk = (isAlreadyTooClose || distanceToTarget < currentDistanceToTarget) &&
                           (result == null || pathDistance < resultPathDistance);
                }
                else
                {
                    isOk = distanceToTarget > currentDistanceToTarget && (result == null || pathDistance < resultPathDistance);
                }

                if (isOk)
                {
                    result             = cover;
                    resultPosition     = point;
                    resultPathDistance = pathDistance;
                    resultDirection    = coverDirection;
                    break;
                }
            }

            situation.TargetDirection = resultDirection;

            if (result == null)
            {
                if (situation.IsThreatInCover)
                {
                    ApproachACovered(controller, ref situation);
                }
                else
                {
                    ApproachAFree(controller, ref situation, controller.Agent, controller.Distances.MaxWalkingFight, true);
                }

                return(false);
            }
            else
            {
                situation.IsNewCover     = true;
                situation.TargetCover    = result;
                situation.TargetPosition = resultPosition;

                return(true);
            }
        }
예제 #14
0
        /// <summary>
        /// Looks for a position to investigate in given direction.
        /// </summary>
        public static bool FindNewThreatPositionInDirection(Vector3 direction, AIController controller, ref AISituation situation, NavMeshAgent agent)
        {
            var corners = new Vector3[32];

            float extra = 0f;

            while (extra < 20f)
            {
                var p = situation.CurrentPosition + direction * (controller.Distances.MinSearch + extra);

                var path = new NavMeshPath();
                agent.CalculatePath(p, path);

                if (path.status != NavMeshPathStatus.PathInvalid)
                {
                    int count = path.GetCornersNonAlloc(corners);

                    if (count > 1)
                    {
                        situation.ThreatGroundPosition = corners[count - 1];
                        return(true);
                    }
                }

                extra += 1f;
            }

            return(false);
        }
예제 #15
0
        /// <summary>
        /// Returns an updated situation struct.
        /// </summary>
        public void Update(AIController controller, AISituation previous, bool updateInDetail)
        {
            if (!IsAlerted)
            {
                HasInvestigatedTheLatestAlert = false;
            }
            else if (!HasInvestigatedTheLatestAlert && Vector3.Distance(CurrentPosition, InvestigationPosition) < controller.Distances.ThreatInvestigation)
            {
                MarkInvestigated();
            }

            if (HasAnInvestigatedAlert)
            {
                InvestigatedAlertAge += Time.deltaTime;
            }

            if (IsIrritated)
            {
                if (IrritationTime > controller.Fighting.Irritation)
                {
                    IrritationTime = 0;
                    IsIrritated    = false;
                }
                else
                {
                    IrritationTime += Time.deltaTime;
                }
            }
            else
            {
                IrritationTime = 0;
            }

            // Check grenades
            {
                IsNearGrenade = false;
                float minDist = 1000;

                foreach (var grenade in GrenadeList.All)
                {
                    var vec  = grenade.transform.position - controller.transform.position;
                    var dist = vec.magnitude;

                    if (dist < grenade.ExplosionRadius)
                    {
                        if (!IsNearGrenade || dist < minDist)
                        {
                            minDist                = dist;
                            IsNearGrenade          = true;
                            NearestGrenadePosition = grenade.transform.position;

                            if (Threat == null)
                            {
                                HasInvestigatedTheLatestAlert = false;
                                HasAnInvestigatedAlert        = false;
                                IsThreatPositionANewAlert     = true;
                                SetThreatPosition(grenade.transform.position, true);
                            }
                        }
                    }
                }
            }

            // Check friends and enemies.
            if (Threat == null || (!IsAlerted && !IsGettingAlerted))
            {
                foreach (var actor in Actors.All)
                {
                    if (actor != controller.Actor)
                    {
                        if (actor.Side != controller.Actor.Side)
                        {
                            if (AIUtil.IsInSight(controller, actor.TopPosition))
                            {
                                IsAlerted = true;
                                ReadEnemyState(actor);
                                break;
                            }
                        }
                        else if (actor.AI != null && actor.AI.IsAlerted)
                        {
                            var vector = actor.transform.position - controller.transform.position;

                            if (vector.magnitude < controller.View.CommunicationDistance)
                            {
                                IsAlerted = true;

                                if (actor.AI.State != AIState.investigate)
                                {
                                    if (actor.AI.Situation.Threat != null && actor.AI.Situation.HasBetterThreatInfo(actor.AI, ref this))
                                    {
                                        TakeEnemyState(actor.AI);
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }
            }

            // Check friends if they had investigated the same position
            if (IsAlerted && !HasInvestigatedTheLatestAlert)
            {
                foreach (var friend in Actors.All)
                {
                    if (friend != controller.Actor &&
                        friend.Side == controller.Actor.Side &&
                        friend.AI != null &&
                        friend.AI.IsAlerted &&
                        friend.AI.Situation.HasAnInvestigatedAlert &&
                        friend.AI.Situation.InvestigatedAlertAge < 4 &&
                        Vector3.Distance(friend.transform.position, controller.transform.position) < controller.View.CommunicationDistance &&
                        Vector3.Distance(friend.AI.Situation.InvestigatedThreatPosition, InvestigationPosition) < controller.Distances.ThreatInvestigation)
                    {
                        MarkInvestigated();
                        break;
                    }
                }
            }

            var isCheckingAThreatInCover = Threat != null && IsThreatInCover && !CanSeeTheThreat;

            // Check threats
            if (Threat == null || CanSeeThatNoThreatAtLastPosition || isCheckingAThreatInCover)
            {
                var minDist = 100000f;

                foreach (var alert in Alerts.All)
                {
                    bool  isOk;
                    Actor newThreat = null;

                    if (Threat != null)
                    {
                        if (alert.Actor == null)
                        {
                            isOk = NoThreatVisibilityTime > 6;
                        }
                        else if (alert.Actor.Side != controller.Actor.Side)
                        {
                            isOk      = true;
                            newThreat = alert.Actor;
                        }
                        else if (alert.Actor.AI != null)
                        {
                            isOk = NoThreatVisibilityTime > 2 && alert.Actor.AI.Situation.NoThreatVisibilityTime < 1;
                        }
                        else
                        {
                            isOk = NoThreatVisibilityTime > 6;
                        }
                    }
                    else
                    {
                        isOk = true;
                    }

                    if (isOk)
                    {
                        var dist = Vector3.Distance(controller.transform.position, alert.Position);

                        if (dist < alert.Range)
                        {
                            if (dist < minDist)
                            {
                                minDist   = dist;
                                IsAlerted = true;

                                HasAnInvestigatedAlert        = false;
                                HasInvestigatedTheLatestAlert = false;

                                if (newThreat != null)
                                {
                                    ReadEnemyState(newThreat);
                                }
                                else
                                {
                                    IsThreatPositionANewAlert = true;
                                    SetThreatPosition(alert.Position, true);
                                }
                            }
                        }
                    }
                }
            }

            // React to grenades
            if (IsNoticingGrenade)
            {
                if (GrenadeReaction < float.Epsilon)
                {
                    IsNoticingGrenade = false;
                }
                else
                {
                    GrenadeReaction -= Time.deltaTime;
                    IsNearGrenade    = false;
                }
            }
            else if (IsNearGrenade && !previous.IsNearGrenade)
            {
                GrenadeReaction   = controller.Fighting.GrenadeReactionTime;
                IsNoticingGrenade = true;
                IsNearGrenade     = false;
            }

            if (IsNearGrenade)
            {
                IsAlerted = true;
            }

            // React to being alerted.
            if (IsGettingAlerted)
            {
                if (AlertReaction < float.Epsilon)
                {
                    IsGettingAlerted = false;
                    IsAlerted        = true;
                }
                else
                {
                    AlertReaction -= Time.deltaTime;
                    IsAlerted      = false;
                }
            }
            else if (IsAlerted && !previous.IsAlerted)
            {
                AlertReaction    = controller.Fighting.ReactionTime;
                IsGettingAlerted = true;
                IsAlerted        = false;
            }

            if (previous.TargetCover != null &&
                (controller.Motor.LeftCover == previous.TargetCover || controller.Motor.RightCover == previous.TargetCover || controller.Motor.Cover == previous.TargetCover))
            {
                CurrentCover = previous.TargetCover;
            }
            else
            {
                CurrentCover = controller.Motor.Cover;
            }

            CurrentPosition = controller.transform.position;
            IsGunReady      = controller.Motor.IsGunReady && controller.Motor.Gun.Clip >= controller.Motor.Gun.ClipSize * controller.Fighting.ReloadFraction;

            if (Threat == null || Threat.IsAggressive)
            {
                WouldLikeToRetreat = controller.Health.Health <= controller.Fighting.MinHealth;
            }
            else
            {
                WouldLikeToRetreat = false;
            }

            if (IsAlerted)
            {
                var couldSeeTheEnemy = CanSeeTheThreat;
                CanSeeTheThreat = false;

                if (Threat != null)
                {
                    var noPatience = NoThreatVisibilityTime > controller.Fighting.Patience;

                    if (couldSeeTheEnemy || updateInDetail)
                    {
                        CanSeeTheThreat = AIUtil.IsInSight(controller, Threat.TopPosition);
                    }

                    if (CanSeeTheThreat)
                    {
                        ReadEnemyState(Threat);

                        if (!couldSeeTheEnemy && noPatience)
                        {
                            IsIrritated = true;
                        }
                    }
                    else
                    {
                        if (noPatience || (NoThreatVisibilityTime > 2 && (!IsThreatInCover || Vector3.Dot(ThreatCoverForward, ThreatGroundPosition - CurrentPosition) > 0)))
                        {
                            if (!IsThreatInCover ||
                                noPatience ||
                                Vector3.Distance(CurrentPosition, ThreatGroundPosition) < controller.Distances.ThreatInvestigation ||
                                AIUtil.IsInSight(controller, (ThreatGroundPosition + ThreatTopPosition) * 0.5f))
                            {
                                CanSeeThatNoThreatAtLastPosition = true;
                            }
                        }

                        NoThreatVisibilityTime += Time.deltaTime;

                        if (updateInDetail)
                        {
                            // Check friends.
                            foreach (var friend in Actors.All)
                            {
                                if (friend != controller.Actor &&
                                    friend.Side == controller.Actor.Side &&
                                    friend.AI != null &&
                                    friend.AI.IsAlerted &&
                                    friend.AI.State != AIState.investigate &&
                                    friend.AI.Situation.Threat == Threat &&
                                    friend.AI.Situation.HasBetterThreatInfo(friend.AI, ref this))
                                {
                                    var vector = friend.transform.position - controller.transform.position;

                                    if (vector.magnitude < controller.View.CommunicationDistance)
                                    {
                                        TakeEnemyState(friend.AI);
                                    }
                                }
                            }
                        }
                    }
                }

                if (TargetCover != null && updateInDetail)
                {
                    var distanceToThreat = Vector3.Distance(TargetPosition, ThreatGroundPosition);

                    IsTargetCoverGood = distanceToThreat >= controller.Distances.MinEnemy &&
                                        distanceToThreat >= controller.Cover.MinCoverToEnemyDistance &&
                                        AIUtil.IsGoodAngle(controller,
                                                           TargetCover,
                                                           TargetPosition,
                                                           ThreatGroundPosition,
                                                           TargetCover.IsTall) &&
                                        !AIUtil.IsCoverPositionTooCloseToFriends(TargetCover, controller, TargetPosition);
                }

                if (updateInDetail)
                {
                    if (IsThreatInCover)
                    {
                        if (CurrentCover != null && CurrentCover.IsTall)
                        {
                            var aimPoint = Vector3.zero;
                            var isGood   = true;

                            if (TargetDirection < 0)
                            {
                                isGood = CurrentCover.IsLeft(Util.AngleOfVector(ThreatStandingTopPosition - CurrentCover.LeftCorner(0)), controller.Motor.CoverSettings.Angles.LeftCorner, false);

                                if (isGood)
                                {
                                    aimPoint = CurrentCover.LeftCorner(0, controller.Motor.CoverSettings.CornerOffset.x);
                                }
                            }
                            else
                            {
                                isGood = CurrentCover.IsRight(Util.AngleOfVector(ThreatStandingTopPosition - CurrentCover.RightCorner(0)), controller.Motor.CoverSettings.Angles.LeftCorner, false);

                                if (isGood)
                                {
                                    aimPoint = CurrentCover.RightCorner(0, controller.Motor.CoverSettings.CornerOffset.x);
                                }
                            }

                            CanSeeFromCurrentPosition = AIUtil.IsInSight(controller, aimPoint, ThreatStandingTopPosition);
                        }
                        else
                        {
                            CanSeeFromCurrentPosition = AIUtil.IsInSight(controller, CurrentPosition, ThreatStandingTopPosition);
                        }
                    }
                    else
                    {
                        CanSeeFromCurrentPosition = CanSeeTheThreat;
                    }
                }
            }
            else
            {
                if (TargetCover != null && updateInDetail)
                {
                    IsTargetCoverGood = !AIUtil.IsCoverPositionTooCloseToFriends(TargetCover, controller, TargetPosition);
                }
            }
        }