using System; using System.Collections; using System.Collections.Generic; using UnityEngine; namespace Invector.vCharacterController.AI { // COVER PREFAB TYPE: // If you own the ShooterCover Add-on you can use the same cover prefabs from the add-on by commenting line 12 and uncommenting like 16 // use the default FSM AI Cover using CoverPoint = vAICoverPoint; // use the Shooter Cover Add-on // make sure to replace the cover prefabs and assign the layer CoverPoint to the CoverLayer of this component //using CoverPoint = vCoverPoint; [vClassHeader("AI Cover")] public class vAICover : vMonoBehaviour, vIAIComponent { public Type ComponentType { get { return typeof(vAICover); } } [vHelpBox("If you own the Shooter Cover Add-on you can also use the cover prefabs from the add-on, open this script and follow the instructions at the top", vHelpBoxAttribute.MessageType.Warning)] [vEditorToolbar("Settings")] public float getCoverRange = 10; public float minDistanceOfThreat = 5f; public float maxDistanceOfThreat = 50f; public float minAngleOfThreat = 100f; [vMinMax(minLimit = 2f, maxLimit = 100f)] public Vector2 timeToChangeCover = new Vector2(2, 10); public string coverTag = "CoverPoint"; public LayerMask coverLayer; [vHelpBox("\n Check AI Controller receivedDamage in Debug Toolbar to debug massive damage Values", vHelpBoxAttribute.MessageType.Info)] public bool changeCoverByDamage = true; [vToggleOption("Compare Damage", "Less", "GreaterEqual")] [SerializeField] protected bool greaterValue = true; [vToggleOption("Compare Type", "Massive Count", "Massive Value")] [SerializeField] protected bool massiveValue = true; [SerializeField] protected int valueToCompare = 100; [vMinMax(minLimit = 0f, maxLimit = 100f)] [Tooltip("Use this time to control when the valid damage checks are true after cover is changed to prevent that change cover every time")] [SerializeField] protected Vector2 timeToChangeByDamge = new Vector2(0, 2); [vEditorToolbar("Debug")] public bool debugMode; [vReadOnly] public bool isGointToCoverPoint; [vButton("Get Cover Test", "GetCoverTest", typeof(vAICover))] [SerializeField] private Transform targetTest = null; public CoverPoint coverPoint; internal vIControlAICombat controller; internal Vector3 threatdir; internal Vector3 threatPos; private float _timeInCover; private float _timeToChangeByDamage; private bool inGetCover; public List _coverPoints = new List(); protected vAIMovementSpeed getCoverSpeed = vAIMovementSpeed.Running; protected virtual void Start() { controller = GetComponent(); controller.onDead.AddListener(RemoveCoverOnDead); } protected virtual void OnDrawGizmosSelected() { if (debugMode) { Gizmos.DrawWireSphere(transform.position, getCoverRange); if (_coverPoints.Count == 0) { return; } for (int i = 0; i < _coverPoints.Count; i++) { var a = Vector3.Angle(_coverPoints[i].transform.forward, threatdir); CoverPoint c = _coverPoints[i]; Gizmos.color = a > minAngleOfThreat && !c.isOccuped ? Color.blue : Color.red; Gizmos.DrawSphere(c.posePosition, 0.25f); Gizmos.DrawRay(c.posePosition, Vector3.up); } if (coverPoint) { Gizmos.color = Color.green; Gizmos.DrawSphere(coverPoint.posePosition, 0.25f); Gizmos.DrawRay(coverPoint.posePosition, Vector3.up); Gizmos.DrawSphere(threatPos, 0.2f); Gizmos.DrawRay(threatPos, -threatdir.normalized); Gizmos.DrawRay(threatPos, Vector3.up); Gizmos.color = Color.red * 0.8f; Gizmos.DrawLine(transform.position, threatPos); } } } internal void OnExitCover() { if (coverPoint) { coverPoint.isOccuped = false; } coverPoint = null; controller.isCrouching = false; } public void GetCoverTest() { if (targetTest) { threatPos = targetTest.position; threatdir = threatPos - transform.position; } else { threatPos = transform.position + UnityEngine.Random.insideUnitSphere * UnityEngine.Random.Range(minDistanceOfThreat, maxDistanceOfThreat); threatPos.y = transform.position.y; threatdir = threatPos - transform.position; } GetCover(true); } public virtual void GetCoverFromRandomThreat(vAIMovementSpeed speed = vAIMovementSpeed.Running) { if (controller == null) { return; } getCoverSpeed = speed; CheckController(); if (!coverPoint) { threatPos = transform.position + transform.forward * UnityEngine.Random.Range(minDistanceOfThreat, maxDistanceOfThreat) + UnityEngine.Random.insideUnitSphere * UnityEngine.Random.Range(0, minDistanceOfThreat); threatPos.y = transform.position.y; } threatdir = threatPos - transform.position; GetCover(); } public virtual void GetCoverFromTargetThreat(vAIMovementSpeed speed = vAIMovementSpeed.Running) { if (controller == null) { return; } getCoverSpeed = speed; if (controller.currentTarget.transform && controller.currentTarget.isLost == false) { CheckController(); threatPos = controller.currentTarget.transform.position; threatdir = threatPos - transform.position; GetCoverOfTarget(); } else { GetCoverFromRandomThreat(); } } protected virtual void CheckController() { if (!isGointToCoverPoint && ChangeCoverByDamage() && _timeToChangeByDamage < Time.time) { _timeInCover = 0; _timeToChangeByDamage = Time.time + UnityEngine.Random.Range(timeToChangeByDamge.x, timeToChangeByDamge.y); } else if (isGointToCoverPoint && ChangeCoverByDamage()) { _timeToChangeByDamage = Time.time + UnityEngine.Random.Range(timeToChangeByDamge.x, timeToChangeByDamge.y); } if (controller.ragdolled) { controller.isCrouching = false; if (coverPoint) { coverPoint.isOccuped = false; } coverPoint = null; } } public virtual void UpdateCoverPoints(Collider[] coverColliders) { _coverPoints.Clear(); for (int i = 0; i < coverColliders.Length; i++) { CoverPoint c = coverColliders[i].GetComponent(); if (c && c.gameObject.CompareTag(coverTag)) { _coverPoints.Add(c); } } } public virtual bool HasValidCoversFromPosition(Vector3 threatdir, Vector3 threatPosition) { var has = _coverPoints.Exists(c => Vector3.Angle(c.transform.forward, threatdir) > minAngleOfThreat && (c.transform.position - threatPosition).magnitude > minDistanceOfThreat && c != coverPoint); return has; } public virtual void RemoveCoverPoint() { if (coverPoint) { coverPoint.isOccuped = false; coverPoint = null; } } protected virtual void GetCover(bool forceGet = false) { if (inGetCover) { return; } if (coverPoint && !forceGet && _timeInCover > Time.time) { return; } inGetCover = true; if (_coverPoints.Count == 0 || !HasValidCoversFromPosition(threatdir, threatPos)) { var coverColliders = Physics.OverlapSphere(transform.position, getCoverRange, coverLayer); UpdateCoverPoints(coverColliders); } var dist = maxDistanceOfThreat; var angle = minAngleOfThreat; CoverPoint cover = null; for (int i = 0; i < _coverPoints.Count; i++) { var coverP = _coverPoints[i]; if (coverP.isOccuped || !coverP) { continue; } var d = (threatPos - coverP.posePosition).magnitude; var a = Vector3.Angle(coverP.transform.forward, threatdir); if (d < dist && a > angle && (coverP != coverPoint)) { dist = d; cover = coverP; } } if (cover != coverPoint && !isGointToCoverPoint && cover) { if (coverPoint) { coverPoint.isOccuped = false; } coverPoint = cover; coverPoint.isOccuped = true; _coverPoints.Remove(coverPoint); StartCoroutine(GoToCoverPoint()); } inGetCover = false; } protected virtual void GetCoverOfTarget() { if (inGetCover) { return; } if (_timeInCover > Time.time) { if (coverPoint) { var a = Vector3.Angle(coverPoint.transform.forward, threatdir); if (a > minAngleOfThreat && controller.targetDistance > minDistanceOfThreat && !controller.currentTarget.isLost) { return; } } if (controller.currentTarget.transform && controller.currentTarget.isLost) { if (coverPoint) { coverPoint.isOccuped = false; } coverPoint = null; return; } if (isGointToCoverPoint) { return; } } else if (controller.currentTarget.isLost) { _timeInCover = Time.time + UnityEngine.Random.Range(timeToChangeCover.x, timeToChangeCover.y); if (_coverPoints.Count > 0) { _coverPoints.Clear(); } return; } inGetCover = true; var angle = minAngleOfThreat; if (_coverPoints.Count == 0 || !HasValidCoversFromPosition(threatdir, threatPos)) { var coverColliders = Physics.OverlapSphere(transform.position, getCoverRange, coverLayer); UpdateCoverPoints(coverColliders); } var dist = maxDistanceOfThreat; CoverPoint newcoverP = null; for (int i = 0; i < _coverPoints.Count; i++) { var coverp = _coverPoints[i]; var d = (threatPos - coverp.posePosition).magnitude;// Vector3.Distance(controlAI.currentTarget.transform.position, _coverPoints[i].transform.position); var a = Vector3.Angle(coverp.transform.forward, threatdir); if (coverp.isOccuped) { continue; } if ((d < dist && d > minDistanceOfThreat && a > angle) && coverp != coverPoint) { dist = d; newcoverP = coverp; } } if (newcoverP != null && newcoverP != coverPoint && !isGointToCoverPoint) { if (coverPoint) { coverPoint.isOccuped = false; } coverPoint = newcoverP; coverPoint.isOccuped = true; _coverPoints.Remove(coverPoint); StartCoroutine(GoToCoverPointFromTarget()); } inGetCover = false; } protected virtual bool ChangeCoverByDamage() { if (!changeCoverByDamage || controller.receivedDamage == null) { return false; } var value = massiveValue ? controller.receivedDamage.massiveValue : controller.receivedDamage.massiveCount; return (greaterValue ? (value >= valueToCompare) : (value < valueToCompare)); } protected virtual IEnumerator GoToCoverPointFromTarget() { if (Vector3.Distance(transform.position, coverPoint.posePosition) > controller.stopingDistance) { controller.isCrouching = Vector3.Distance(transform.position, coverPoint.posePosition) < 2; isGointToCoverPoint = true; if (controller.isAiming || controller.isStrafing) { controller.StrafeMoveTo(coverPoint.posePosition, controller.currentTarget.transform.position - transform.position, getCoverSpeed); } else { controller.MoveTo(coverPoint.posePosition, getCoverSpeed); } yield return new WaitForSeconds(1f); while (!controller.isInDestination) { if (controller.remainingDistance < 1f + controller.stopingDistance && !controller.isCrouching) { controller.isCrouching = true; } if (coverPoint != null) { if (controller.isAiming || controller.isStrafing) { controller.StrafeMoveTo(coverPoint.posePosition, controller.currentTarget.transform.position - transform.position, getCoverSpeed); } else { controller.MoveTo(coverPoint.posePosition, getCoverSpeed); } } if (controller.targetDistance < minDistanceOfThreat || coverPoint == null) { _timeInCover = 0; break; } yield return null; } } if (coverPoint && controller.targetDistance > minDistanceOfThreat) { controller.isCrouching = true; } _timeInCover = Time.time + UnityEngine.Random.Range(timeToChangeCover.x, timeToChangeCover.y); isGointToCoverPoint = false; } protected virtual IEnumerator GoToCoverPoint() { if (Vector3.Distance(transform.position, coverPoint.posePosition) > controller.stopingDistance) { controller.isCrouching = Vector3.Distance(transform.position, coverPoint.posePosition) < 2; isGointToCoverPoint = true; controller.ForceUpdatePath(2); if (controller.isAiming || controller.isStrafing) { controller.StrafeMoveTo(coverPoint.posePosition, transform.forward, getCoverSpeed); } else { controller.MoveTo(coverPoint.posePosition, getCoverSpeed); } controller.MoveTo(coverPoint.posePosition, getCoverSpeed); yield return new WaitForSeconds(1f); while (!controller.isInDestination) { if (controller.remainingDistance < 1f + controller.stopingDistance && !controller.isCrouching) { controller.isCrouching = true; } if (!coverPoint) { break; } yield return null; } } if (coverPoint) { // controlAI.RotateTo(-coverPoint.transform.forward); controller.isCrouching = true; _timeInCover = Time.time + UnityEngine.Random.Range(timeToChangeCover.x, timeToChangeCover.y); } isGointToCoverPoint = false; } protected virtual void RemoveCoverOnDead(GameObject g) { RemoveCoverPoint(); } } }