/// <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); } } }
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); } }
/// <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; }
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); }
/// <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; } }
/// <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++; } } }
/// <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); } }
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)); }
/// <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); } } }
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); } } }