Files
2024-11-20 15:21:28 +01:00

382 lines
15 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Invector.vCharacterController.AI
{
[vClassHeader("AI Cover")]
public class vAICover : vMonoBehaviour, vIAIComponent
{
public Type ComponentType
{
get
{
return typeof(vAICover);
}
}
[vHelpBox("This component requires CoverPoint in your scene, please check the documentation for more information on how to set up CoverPoints")]
[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("<i>\n Check AI Controller receivedDamage in Debug Toolbar to debug massive damage Values</i>",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 vAICoverPoint coverPoint;
internal vIControlAICombat controller;
internal Vector3 threatdir;
internal Vector3 threatPos;
private float _timeInCover;
private float _timeToChangeByDamage;
private bool inGetCover;
public List<vAICoverPoint> _coverPoints = new List<vAICoverPoint>();
protected vAIMovementSpeed getCoverSpeed = vAIMovementSpeed.Running;
protected virtual void Start()
{
controller = GetComponent<vIControlAICombat>();
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);
vAICoverPoint 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++)
{
vAICoverPoint c = coverColliders[i].GetComponent<vAICoverPoint>();
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;
vAICoverPoint 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;
vAICoverPoint 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();
}
}
}