/// <summary>
        /// React to attacks on other AI.
        /// </summary>
        public void OnFriendHit(Actor friend)
        {
            if (friend == null)
            {
                return;
            }

            var vector = friend.transform.position - transform.position;

            if (vector.magnitude < View.CommunicationDistance)
            {
                _situation.IsAlerted = true;
            }
            else if (vector.magnitude < View.SightDistance && !IsAlerted)
            {
                if (AIUtil.IsInSight(this, friend.TopPosition))
                {
                    _situation.IsAlerted = true;
                }
            }

            if (_situation.IsAlerted && _situation.Threat == null)
            {
                var ai = friend.GetComponent <AIController>();

                if (ai != null)
                {
                    _situation.TakeEnemyState(ai);
                }
            }
        }
Example #2
0
        private bool shouldRunTo(Vector3 position)
        {
            var distance = Vector3.Distance(transform.position, position);

            if (distance > WalkDistance || (distance > VerifyDistance && !AIUtil.IsInSight(_actor, position, VerifyDistance, 360)))
            {
                return(true);
            }
            else
            {
                return(false);
            }
        }
Example #3
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;
        }
Example #4
0
        private bool canBeInvestigated(SearchPoint point)
        {
            var position        = point.Position + Vector3.up * VerifyHeight;
            var distanceToPoint = Vector3.Distance(transform.position, position);

            var checkDistance = VerifyDistance;

            if (point.Visibility < checkDistance)
            {
                checkDistance = point.Visibility;
            }

            if (distanceToPoint < checkDistance &&
                (distanceToPoint < 1 ||
                 AIUtil.IsInSight(_actor, position, checkDistance, FieldOfView)))
            {
                return(!point.RequiresReaching || distanceToPoint < 1.1f);
            }

            return(false);
        }
Example #5
0
        /// <summary>
        /// Checks if the given actor is field of view. Result is immediate, does not execute any events or messages.
        /// </summary>
        public bool CheckVisibility(Actor actor)
        {
            var viewDistance = actor.GetViewDistance(Distance, _isAlerted);

            return(AIUtil.IsInSight(_actor, actor.TopPosition, viewDistance, FieldOfView, ObstacleIgnoreDistance));
        }
        private void Update()
        {
            if (_situation.IsAlerted)
            {
                if (!_hasAddedToAlertedControllers)
                {
                    _hasAddedToAlertedControllers = true;
                    _alertedControllers.Add(this);
                }
            }
            else if (_hasAddedToAlertedControllers)
            {
                _alertedControllers.Remove(this);
            }

            _stateTime += Time.deltaTime;

            _agent.updatePosition = false;
            _agent.updateRotation = false;
            _agent.updateUpAxis   = false;
            _agent.autoRepath     = true;
            _agent.autoBraking    = false;

            if (!_motor.IsAlive)
            {
                _situation.TargetCover = null;
            }

            if (_lastTargetCover != _situation.TargetCover)
            {
                if (_lastTargetCover != null)
                {
                    _lastTargetCover.UnregisterUser(_actor);
                }

                _lastTargetCover = _situation.TargetCover;

                if (_lastTargetCover != null)
                {
                    _lastTargetCover.RegisterUser(_actor);
                }
            }

            if (_isWaitingForPath && !_agent.pathPending)
            {
                _pathLength       = _agent.path.GetCornersNonAlloc(_path);
                _currentPathIndex = 0;
                _isWaitingForPath = false;
            }

            if (!_motor.IsAlive)
            {
                _state         = AIState.none;
                _agent.enabled = false;
                return;
            }
            else
            {
                _agent.enabled = true;
            }

            if (_motor.PotentialCover != null)
            {
                _motor.InputTakeCover();
            }

            if (_situation.IsAlerted)
            {
                if (!_motor.IsInCover)
                {
                    _motor.InputAim();
                }

                // Make the AI pick up a weapon.
                if (Fighting.WeaponToUse <= 0)
                {
                    _motor.InputWeapon(0);
                }
                else if (Fighting.WeaponToUse - 1 < _motor.Weapons.Length)
                {
                    _motor.InputWeapon(Fighting.WeaponToUse);
                }
                else
                {
                    _motor.InputWeapon(_motor.Weapons.Length);
                }

                _situation.IsAllowedToBeAggressive = true;

                ///
                /// POTENTIALLY FORGET ABOUT THE ENEMY. CHECK IF CAN BE AGGRESSIVE.
                ///
                if (_situation.Threat != null)
                {
                    if (_situation.Threat.IsAlive)
                    {
                        if (!_situation.CanSeeTheThreat && _situation.NoThreatTimer > View.EnemySustainTime)
                        {
                            _situation.RemoveEnemyState();
                        }
                        else
                        {
                            if (_registeredGroup != null)
                            {
                                _registeredGroup.MarkAsPotentialAggressive(this);
                                _situation.IsAllowedToBeAggressive = _registeredGroup.IsAggressive(this);
                            }
                        }
                    }
                    else
                    {
                        _situation.RemoveEnemyState();
                    }
                }

                ///
                /// AIM AND FIRE
                ///
                if (_situation.Threat != null && (_situation.CanSeeTheThreat || _situation.NoThreatTimer < View.SightSustainTime))
                {
                    // Process grenade throwing
                    if (_situation.ThrownGrenadeCount < Grenades.GrenadeCount)
                    {
                        var doThrow = false;

                        if (_situation.IsAllowedToBeAggressive)
                        {
                            if (_hasThrowFirstGrenade)
                            {
                                if (_grenadeTimer < Grenades.Interval)
                                {
                                    _grenadeTimer += Time.deltaTime;
                                }
                                else
                                {
                                    doThrow = true;
                                }
                            }
                            else
                            {
                                if (_grenadeTimer < Grenades.FirstGrenadeDelay)
                                {
                                    _grenadeTimer += Time.deltaTime;
                                }
                                else
                                {
                                    doThrow = true;
                                }
                            }
                        }

                        if (doThrow && _motor.PotentialGrenade != null)
                        {
                            if (_grenadeCheckTimer <= float.Epsilon)
                            {
                                GrenadeDescription desc;
                                desc.Gravity    = _motor.Grenade.Gravity;
                                desc.Duration   = _motor.PotentialGrenade.Timer;
                                desc.Bounciness = _motor.PotentialGrenade.Bounciness;

                                var length = GrenadePath.Calculate(GrenadePath.Origin(_motor, Util.AngleOfVector(_situation.ThreatGroundPosition - transform.position)),
                                                                   _situation.ThreatGroundPosition,
                                                                   _motor.Grenade.MaxVelocity,
                                                                   desc,
                                                                   _grenadePath,
                                                                   _motor.Grenade.Step);

                                if (Vector3.Distance(_grenadePath[length - 1], _situation.ThreatGroundPosition) > Grenades.MaxRadius ||
                                    Vector3.Distance(_grenadePath[length - 1], _situation.CurrentPosition) < Grenades.AvoidDistance)
                                {
                                    _grenadeCheckTimer = Grenades.CheckInterval;
                                }
                                else
                                {
                                    _motor.InputThrowGrenade(_grenadePath, length, _motor.Grenade.Step);
                                    _situation.ThrownGrenadeCount++;

                                    _grenadeTimer         = 0;
                                    _hasThrowFirstGrenade = true;
                                }
                            }
                            else
                            {
                                _grenadeCheckTimer -= Time.deltaTime;
                            }
                        }
                        else
                        {
                            _grenadeCheckTimer = 0;
                        }
                    }

                    // Position in world space that is best fit to aim at.
                    var perfectTarget = _situation.ThreatGroundPosition + (_situation.ThreatTopPosition - _situation.ThreatGroundPosition) * 0.7f;

                    if (_situation.IsThreatInCover)
                    {
                        var dot = Vector3.Dot(_situation.ThreatCoverForward, (_situation.ThreatGroundPosition - transform.position).normalized);

                        if (dot < -0.1f)
                        {
                            perfectTarget = _situation.ThreatStandingTopPosition;
                        }
                    }

                    // Make the AI look at the target.
                    {
                        var vector = perfectTarget - _motor.transform.position;
                        vector.y = 0;

                        if (vector.magnitude < 2)
                        {
                            _motor.SetLookTarget(perfectTarget + vector * 2);
                        }
                        else
                        {
                            _motor.SetLookTarget(perfectTarget);
                        }

                        _motor.SetBodyLookTarget(perfectTarget);
                    }

                    // Aim the gun at the inprecise target.
                    var targetRadius = Fighting.TargetRadius.Get(Vector3.Distance(_situation.ThreatGroundPosition, transform.position));
                    _motor.SetFireTarget(perfectTarget + new Vector3(UnityEngine.Random.Range(-1, 1) * targetRadius,
                                                                     UnityEngine.Random.Range(-1, 1) * targetRadius,
                                                                     UnityEngine.Random.Range(-1, 1) * targetRadius));

                    // We want AI too look at the corner when standing near it.
                    if (_motor.IsInTallCover)
                    {
                        if (_situation.TargetDirection < 0)
                        {
                            _motor.InputStandLeft();
                        }
                        else if (_situation.TargetDirection > 0)
                        {
                            _motor.InputStandRight();
                        }
                    }

                    // Fire
                    {
                        var isAllowedToFire = _situation.IsAllowedToBeAggressive;

                        if (Behaviour.OnlyFireWhenSeen || !_situation.IsAllowedToBeAggressive)
                        {
                            var up   = Camera.main.WorldToViewportPoint(transform.position);
                            var down = Camera.main.WorldToViewportPoint(transform.position + Vector3.up * 2);

                            if ((up.x < 0 ||
                                 up.y < 0 ||
                                 up.x > 1 ||
                                 up.y > 1 ||
                                 up.z < 0) &&
                                (down.x < 0 ||
                                 down.y < 0 ||
                                 down.x > 1 ||
                                 down.y > 1 ||
                                 down.z < 0))
                            {
                                isAllowedToFire = false;
                            }
                            else
                            {
                                isAllowedToFire = true;
                            }
                        }

                        if (isAllowedToFire)
                        {
                            var burst  = Behaviour.IsFightingUsingCovers ? CoveredFightingBursts : CoveredApproachBursts;
                            var isDumb = _state == AIState.fireInCover &&
                                         (_stateTime <= burst.IntroDuration || _stateTime >= burst.IntroDuration + burst.Duration);

                            var isWalking = _state != AIState.takeCover && _state != AIState.approach && _state != AIState.retreat;

                            // Check if can fire right now.
                            if (Behaviour.CanFire &&
                                (!isWalking || _walkingBurstWait < WalkingBursts.Duration) &&
                                (!_situation.IsTargetCoverGood ||
                                 (_state != AIState.hideInCover &&
                                  (_state != AIState.takeCover || _motor.Cover == null))))
                            {
                                var aimPoint = transform.position;

                                if (_motor.IsInTallCover && _situation.IsTargetCoverGood)
                                {
                                    if (_situation.TargetDirection < 0)
                                    {
                                        aimPoint = _motor.Cover.LeftCorner(transform.position.y, _motor.CoverSettings.CornerOffset.x);
                                    }
                                    else
                                    {
                                        aimPoint = _motor.Cover.RightCorner(transform.position.y, _motor.CoverSettings.CornerOffset.x);
                                    }
                                }

                                // See if the motor can hit the standing enemy after potentially peeking.
                                if (AIUtil.IsInSight(this, aimPoint, _situation.ThreatStandingTopPosition))
                                {
                                    if (_state == AIState.fireInCover)
                                    {
                                        _motor.InputAim();
                                    }

                                    if (!isDumb)
                                    {
                                        _motor.InputFire();
                                    }
                                }
                            }
                        }

                        _lookPointTimer = 0;
                    }
                }
                else
                {
                    _motor.SetFireTarget(_situation.ThreatGroundPosition);
                    _motor.SetLookTarget(_situation.ThreatGroundPosition);
                    _motor.SetBodyLookTarget(_situation.ThreatGroundPosition);
                    lookAround(_situation.ThreatGroundPosition - _motor.transform.position);
                }
            }
            else
            {
                _motor.InputWeapon(0);

                if (_state == AIState.patrol || _state == AIState.patrolPause)
                {
                    Vector3 forward;

                    if (Waypoints.Length > 1)
                    {
                        var next = Waypoints[_situation.PatrolPoint];

                        forward = next.Position - transform.position;

                        if (forward.magnitude < 0.5f)
                        {
                            var previous = _situation.PatrolPoint == 0 ? Waypoints[Waypoints.Length - 1] : Waypoints[_situation.PatrolPoint - 1];
                            var line     = next.Position - previous.Position;

                            if (Vector2.Dot(line, forward) > 0)
                            {
                                forward = line;
                            }
                        }
                    }
                    else
                    {
                        forward = transform.forward;
                    }

                    lookAround(forward);
                }
                else
                {
                    _lookPointTimer = 0;
                }
            }

            var positionToMoveTo = transform.position;

            ///
            /// STATE MANAGEMENT
            ///
            {
                var updateSituationInDetail = false;

                {
                    // Update the update delay timer.
                    _updateTimer -= Time.deltaTime;

                    if (_updateTimer < 0)
                    {
                        _updateTimer            = Fighting.ReactionTime;
                        updateSituationInDetail = true;
                    }
                }

                // Update standing burst timer.
                {
                    _walkingBurstWait += Time.deltaTime;

                    if (_walkingBurstWait > WalkingBursts.Duration + WalkingBursts.Wait)
                    {
                        _walkingBurstWait = 0;
                    }
                }

                {
                    _situation.Update(this, _previousSituation, updateSituationInDetail);

                    if (!isStateStillValid(_state, _stateTime, _situation, _situation))
                    {
                        updateSituation(calcState(_situation, _state, _stateTime));
                    }
                }
            }

            ///
            /// WALK
            ///
            if (!_motor.IsInCornerAimState)
            {
                if (_state == AIState.takeCover ||
                    _state == AIState.approach ||
                    _state == AIState.investigate ||
                    _state == AIState.retreat ||
                    _state == AIState.patrol ||
                    _state == AIState.avoidGrenade)
                {
                    var toTarget = _situation.TargetPosition - transform.position;

                    // If patrolling, set the body to rotate towards the target.
                    if (_state == AIState.patrol)
                    {
                        if (toTarget.magnitude > 0.1f)
                        {
                            var normalized = toTarget.normalized;
                            var position   = transform.position + normalized * 4;
                            var dot        = Vector3.Dot(toTarget.normalized, _motor.transform.forward);

                            if (Waypoints[_situation.PatrolPoint].Run)
                            {
                                _motor.SetBodyLookTarget(position, 8);
                            }
                            else
                            {
                                if (dot > 0)
                                {
                                    _motor.SetBodyLookTarget(position, 1.6f);
                                }
                                else if (dot > -0.5f)
                                {
                                    _motor.SetBodyLookTarget(position, 2.5f);
                                }
                                else
                                {
                                    _motor.SetBodyLookTarget(position, 4f);
                                }
                            }
                        }
                    }

                    // Move the last meter without using the agent.
                    if (toTarget.magnitude > 1.0f)
                    {
                        var vectorToAgent   = _agent.nextPosition - transform.position;
                        var distanceToAgent = vectorToAgent.magnitude;

                        if (distanceToAgent > float.Epsilon)
                        {
                            vectorToAgent /= distanceToAgent;
                        }

                        // If agent moved too far, reset it.
                        if (distanceToAgent > 1f)
                        {
                            updateAgent(_situation.TargetPosition);
                        }

                        float walkSpeed;

                        if (_state == AIState.patrol && !Patrol.IsAlwaysRunning && (Waypoints.Length == 0 || !Waypoints[_situation.PatrolPoint].Run))
                        {
                            walkSpeed = 0.5f;
                        }
                        else
                        {
                            walkSpeed = 1.0f;
                        }

                        var point = _currentPathIndex + 1 < _pathLength ? _path[_currentPathIndex + 1] : _situation.TargetPosition;

                        var vectorToPoint   = point - transform.position;
                        var distanceToPoint = vectorToPoint.magnitude;

                        if (distanceToPoint > float.Epsilon)
                        {
                            vectorToPoint /= distanceToPoint;
                        }

                        if (distanceToPoint < 0.1f && _currentPathIndex + 1 < _pathLength)
                        {
                            _currentPathIndex++;
                        }

                        _agent.speed = 0.1f;
                        _motor.InputMovement(new CharacterMovement(vectorToPoint, walkSpeed));
                    }
                    else
                    {
                        if (_state == AIState.takeCover)
                        {
                            _motor.InputImmediateCoverSearch();
                            _motor.InputTakeCover();
                        }

                        if (toTarget.magnitude > 0.05f)
                        {
                            _motor.InputMovement(new CharacterMovement(toTarget.normalized, 0.5f));
                        }
                        else
                        {
                            _motor.transform.position = _situation.TargetPosition;
                        }
                    }
                }
            }
        }
        /// <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;
            }
        }
Example #8
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++;
                }
            }
        }
Example #9
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);
            }
        }
Example #10
0
        private bool verify(Vector3 position)
        {
            var distance = Vector3.Distance(transform.position, position);

            return((_cover != null && AIUtil.IsInSight(_actor, position + Vector3.up * VerifyHeight, _verifyDistance, FieldOfView)) || distance < VerifyRadius || (distance < _verifyDistance && _verifyDistance < VerifyRadius));
        }
Example #11
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);
                }
            }
        }
Example #12
0
        private void Update()
        {
            if (!_actor.IsAlive)
            {
                return;
            }

            _wait -= Time.deltaTime;

            if (_wait > float.Epsilon)
            {
                return;
            }

            _wait = Random.Range(UpdateDelay * 0.8f, UpdateDelay * 1.2f);

            _oldVisible.Clear();

            foreach (var actor in _visible)
            {
                _oldVisible.Add(actor);
            }

            _visible.Clear();

            foreach (var actor in _oldVisible)
            {
                if (actor != null && actor.IsAlive && AIUtil.IsInSight(_actor, actor.TopPosition, actor.GetViewDistance(Distance, _isAlerted), FieldOfView))
                {
                    _visible.Add(actor);
                }
                else
                {
                    Message("OnUnseeActor", actor);

                    if (!actor.IsAlive)
                    {
                        _knownAlive.Remove(actor);
                        Message("OnSeeDeath", actor);
                    }
                }
            }

            var count = Physics.OverlapSphereNonAlloc(_actor.TopPosition, Distance, _colliders, 0x1, QueryTriggerInteraction.Ignore);

            for (int i = 0; i < count; i++)
            {
                var actor = Actors.Get(_colliders[i].gameObject);

                if (actor != null && actor != _actor && actor.IsAlive && !_oldVisible.Contains(actor))
                {
                    if (AIUtil.IsInSight(_actor, actor.TopPosition, actor.GetViewDistance(Distance, _isAlerted), FieldOfView))
                    {
                        _visible.Add(actor);
                        Message("OnSeeActor", actor);
                    }
                }
            }

            _oldKnownAlive.Clear();

            foreach (var known in _knownAlive)
            {
                _oldKnownAlive.Add(known);
            }

            _knownAlive.Clear();

            foreach (var actor in _oldKnownAlive)
            {
                if (_visible.Contains(actor))
                {
                    _knownAlive.Add(actor);
                }
                else if (AIUtil.IsInSight(_actor, actor.TopPosition, actor.GetViewDistance(Distance, _isAlerted), FieldOfView))
                {
                    Message("OnSeeDeath", actor);
                }
                else
                {
                    _knownAlive.Add(actor);
                }
            }
        }