/// <summary> /// Tell the threat to mark itself as being behind the given cover. /// </summary> public void OnEnterCover(Cover cover) { if (_cover != null) { _cover.UnregisterUser(this); } _cover = cover; _cover.RegisterUser(this, transform.position); }
private void updateRegistration() { if (_registeredCover != _targetCover) { if (_registeredCover != null) { _registeredCover.UnregisterUser(_actor); } _registeredCover = _targetCover; } if (_registeredCover != null) { if (_actor.Cover == _registeredCover) { _registeredCover.RegisterUser(_actor, _actor.transform.position); } else { _registeredCover.RegisterUser(_actor, _targetPosition); } } }
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; } } } } }