730 lines
25 KiB
C#
730 lines
25 KiB
C#
using Invector.vEventSystems;
|
|
using System.Collections;
|
|
using UnityEngine;
|
|
|
|
namespace Invector.vCharacterController.AI
|
|
{
|
|
[vClassHeader("Simple Melee AI", "This is a Simple Melee AI that comes with the MeleeCombat package as a bonus, if you want a more advanced AI check our AI Template")]
|
|
public class vSimpleMeleeAI_Controller : vSimpleMeleeAI_Animator, vIMeleeFighter
|
|
{
|
|
[vEditorToolbar("Iterations")]
|
|
public float stateRoutineIteration = 0.15f;
|
|
public float destinationRoutineIteration = 0.25f;
|
|
public float findTargetIteration = 0.25f;
|
|
public float smoothSpeed = 5f;
|
|
|
|
[vEditorToolbar("Events")]
|
|
[Header("--- On Change State Events ---")]
|
|
public UnityEngine.Events.UnityEvent onIdle;
|
|
public UnityEngine.Events.UnityEvent onChase;
|
|
public UnityEngine.Events.UnityEvent onPatrol;
|
|
protected AIStates oldState;
|
|
protected float ignorePatrolTimer;
|
|
protected float moveToSpeed;
|
|
protected Vector3 moveToDestination;
|
|
|
|
protected override void Start()
|
|
{
|
|
base.Start();
|
|
ignorePatrolTimer = -1f;
|
|
moveToDestination = transform.position;
|
|
Init();
|
|
StartCoroutine(StateRoutine());
|
|
StartCoroutine(FindTarget());
|
|
StartCoroutine(DestinationBehaviour());
|
|
}
|
|
|
|
protected void FixedUpdate()
|
|
{
|
|
ControlLocomotion();
|
|
}
|
|
|
|
#region AI Target
|
|
|
|
public virtual void SetCurrentTarget(Transform target)
|
|
{
|
|
if (target != currentTarget.transform)
|
|
{
|
|
currentTarget.transform = target;
|
|
currentTarget.colliderTarget = target.GetComponent<Collider>();
|
|
currentTarget.character = target.GetComponent<vIHealthController>();
|
|
}
|
|
AddTagsToDetect(target.gameObject.tag);
|
|
sphereSensor.AddTarget(target);
|
|
}
|
|
|
|
public virtual void RemoveCurrentTarget()
|
|
{
|
|
if (currentTarget.transform)
|
|
{
|
|
currentTarget.transform = null;
|
|
currentTarget.colliderTarget = null;
|
|
currentTarget.character = null;
|
|
}
|
|
}
|
|
|
|
public virtual void AddTagsToDetect(string tag)
|
|
{
|
|
if (!tagsToDetect.Contains(tag))
|
|
{
|
|
tagsToDetect.Add(tag);
|
|
}
|
|
}
|
|
|
|
public virtual void RemoveTagToDetect(string tag)
|
|
{
|
|
if (tagsToDetect.Contains(tag))
|
|
{
|
|
tagsToDetect.Remove(tag);
|
|
}
|
|
}
|
|
|
|
protected void SetTarget()
|
|
{
|
|
if (currentHealth > 0 && sphereSensor != null)
|
|
{
|
|
if (currentTarget.transform == null || (sortTargetFromDistance))
|
|
{
|
|
sphereSensor.CheckTargetsAround(fieldOfView, minDetectDistance, maxDetectDistance, tagsToDetect, layersToDetect, sortTargetFromDistance);
|
|
var vChar = sphereSensor.GetTargetvCharacter();
|
|
if (vChar != null && vChar.currentHealth > 0)
|
|
{
|
|
currentTarget.transform = vChar.transform;
|
|
currentTarget.character = vChar;
|
|
}
|
|
}
|
|
|
|
if (!CheckTargetIsAlive() || TargetDistance > lostTargetDistance)
|
|
{
|
|
currentTarget.transform = null;
|
|
}
|
|
}
|
|
else if (currentHealth <= 0f)
|
|
{
|
|
destination = transform.position;
|
|
currentTarget.transform = null;
|
|
}
|
|
}
|
|
|
|
bool CheckTargetIsAlive()
|
|
{
|
|
if (currentTarget.transform == null || currentTarget.character == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (currentTarget.character.currentHealth > 0)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
protected IEnumerator FindTarget()
|
|
{
|
|
while (true)
|
|
{
|
|
yield return new WaitForSeconds(findTargetIteration);
|
|
if (currentHealth > 0)
|
|
{
|
|
SetTarget();
|
|
CheckTarget();
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region AI Locomotion
|
|
|
|
void ControlLocomotion()
|
|
{
|
|
if (AgentDone() && agent.updatePosition || lockMovement)
|
|
{
|
|
agent.speed = 0f;
|
|
combatMovement = Vector3.zero;
|
|
}
|
|
if (agent.isOnOffMeshLink)
|
|
{
|
|
float speed = agent.desiredVelocity.magnitude;
|
|
UpdateAnimator(AgentDone() ? 0f : speed, direction);
|
|
}
|
|
else
|
|
{
|
|
var desiredVelocity = agent.enabled ? agent.updatePosition ? agent.desiredVelocity : (agent.nextPosition - transform.position) : (destination - transform.position);
|
|
if (OnStrafeArea)
|
|
{
|
|
var destin = transform.InverseTransformDirection(desiredVelocity).normalized;
|
|
combatMovement = Vector3.Lerp(combatMovement, destin, 2f * Time.deltaTime);
|
|
UpdateAnimator(AgentDone() ? 0f : combatMovement.z, combatMovement.x);
|
|
}
|
|
else
|
|
{
|
|
float speed = desiredVelocity.magnitude;
|
|
combatMovement = Vector3.zero;
|
|
UpdateAnimator(AgentDone() ? 0f : speed, 0f);
|
|
}
|
|
}
|
|
}
|
|
|
|
Vector3 AgentDirection()
|
|
{
|
|
var forward = AgentDone() ? (currentTarget.transform != null && OnStrafeArea && canSeeTarget ?
|
|
(new Vector3(destination.x, transform.position.y, destination.z) - transform.position) :
|
|
transform.forward) : agent.desiredVelocity;
|
|
|
|
fwd = Vector3.Lerp(fwd, forward, 20 * Time.deltaTime);
|
|
return fwd;
|
|
}
|
|
|
|
protected virtual IEnumerator DestinationBehaviour()
|
|
{
|
|
while (true)
|
|
{
|
|
yield return new WaitForSeconds(destinationRoutineIteration);
|
|
CheckGroundDistance();
|
|
if (agent.updatePosition)
|
|
{
|
|
UpdateDestination(destination);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected virtual void UpdateDestination(Vector3 position)
|
|
{
|
|
if (agent.isOnNavMesh)
|
|
{
|
|
agent.SetDestination(position);
|
|
}
|
|
|
|
#region debug Path
|
|
if (agent.enabled && agent.hasPath)
|
|
{
|
|
if (drawAgentPath)
|
|
{
|
|
Debug.DrawLine(transform.position, position, Color.red, 0.5f);
|
|
var oldPos = transform.position;
|
|
for (int i = 0; i < agent.path.corners.Length; i++)
|
|
{
|
|
var pos = agent.path.corners[i];
|
|
Debug.DrawLine(oldPos, pos, Color.green, 0.5f);
|
|
oldPos = pos;
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
}
|
|
|
|
protected void CheckIsOnNavMesh()
|
|
{
|
|
// check if the AI is on a valid Navmesh, if not he dies
|
|
if (!agent.isOnNavMesh && agent.enabled && !ragdolled)
|
|
{
|
|
Debug.LogWarning("Missing NavMesh Bake, character will die - Please Bake your navmesh again!");
|
|
currentHealth = 0;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region AI States
|
|
|
|
protected IEnumerator StateRoutine()
|
|
{
|
|
while (this.enabled)
|
|
{
|
|
CheckIsOnNavMesh();
|
|
CheckAutoCrouch();
|
|
yield return new WaitForSeconds(stateRoutineIteration);
|
|
if (!lockMovement)
|
|
{
|
|
switch (currentState)
|
|
{
|
|
case AIStates.Idle:
|
|
if (currentState != oldState) { onIdle.Invoke(); oldState = currentState; }
|
|
yield return StartCoroutine(Idle());
|
|
break;
|
|
case AIStates.Chase:
|
|
if (currentState != oldState) { onChase.Invoke(); oldState = currentState; }
|
|
yield return StartCoroutine(Chase());
|
|
break;
|
|
case AIStates.PatrolSubPoints:
|
|
if (currentState != oldState) { onPatrol.Invoke(); oldState = currentState; }
|
|
yield return StartCoroutine(PatrolSubPoints());
|
|
break;
|
|
case AIStates.PatrolWaypoints:
|
|
if (currentState != oldState) { onPatrol.Invoke(); oldState = currentState; }
|
|
yield return StartCoroutine(PatrolWaypoints());
|
|
break;
|
|
case AIStates.Wander:
|
|
if (currentState != oldState) { onPatrol.Invoke(); oldState = currentState; }
|
|
yield return StartCoroutine(Wander());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
protected IEnumerator Idle()
|
|
{
|
|
while (currentHealth <= 0)
|
|
{
|
|
yield return null;
|
|
}
|
|
|
|
if (canSeeTarget)
|
|
{
|
|
currentState = AIStates.Chase;
|
|
}
|
|
else
|
|
{
|
|
agent.speed = Mathf.Lerp(agent.speed, 0f, smoothSpeed * Time.deltaTime);
|
|
}
|
|
}
|
|
|
|
protected IEnumerator Chase()
|
|
{
|
|
while (currentHealth <= 0)
|
|
{
|
|
yield return null;
|
|
}
|
|
|
|
agent.speed = Mathf.Lerp(agent.speed, chaseSpeed, smoothSpeed * Time.deltaTime);
|
|
agent.stoppingDistance = chaseStopDistance;
|
|
|
|
if (!isBlocking && !tryingBlock)
|
|
{
|
|
StartCoroutine(CheckChanceToBlock(chanceToBlockInStrafe, lowerShield));
|
|
}
|
|
|
|
if (currentTarget.transform == null || !agressiveAtFirstSight)
|
|
{
|
|
currentState = AIStates.PatrolWaypoints;
|
|
}
|
|
|
|
|
|
// begin the Attack Routine when close to the Target
|
|
if (TargetDistance <= distanceToAttack && meleeManager != null && canAttack && !actions)
|
|
{
|
|
canAttack = false;
|
|
|
|
yield return StartCoroutine(MeleeAttackRotine());
|
|
}
|
|
if (attackCount <= 0 && !inResetAttack && !isAttacking)
|
|
{
|
|
StartCoroutine(ResetAttackCount());
|
|
yield return null;
|
|
}
|
|
// strafing while close to the Target
|
|
if (OnStrafeArea && strafeSideways)
|
|
{
|
|
//Debug.DrawRay(transform.position, dir * 2, Color.red, 0.2f);
|
|
if (strafeSwapeFrequency <= 0)
|
|
{
|
|
sideMovement = GetRandonSide();
|
|
strafeSwapeFrequency = UnityEngine.Random.Range(minStrafeSwape, maxStrafeSwape);
|
|
}
|
|
else
|
|
{
|
|
strafeSwapeFrequency -= Time.deltaTime;
|
|
}
|
|
fwdMovement = (TargetDistance < distanceToAttack) ? (strafeBackward ? -1 : 0) : TargetDistance > distanceToAttack ? 1 : 0;
|
|
var dir = ((transform.right * sideMovement) + (transform.forward * fwdMovement));
|
|
Ray ray = new Ray(new Vector3(transform.position.x, currentTarget.transform != null ? currentTarget.transform.position.y : transform.position.y, transform.position.z), dir);
|
|
if (TargetDistance < strafeDistance - 0.5f)
|
|
{
|
|
destination = OnStrafeArea ? ray.GetPoint(agent.stoppingDistance + 0.5f) : currentTarget.transform.position;
|
|
}
|
|
else if (currentTarget.transform != null)
|
|
{
|
|
destination = currentTarget.transform.position;
|
|
}
|
|
}
|
|
// chase Target
|
|
else
|
|
{
|
|
if (!OnStrafeArea && currentTarget.transform != null)
|
|
{
|
|
destination = currentTarget.transform.position;
|
|
}
|
|
else
|
|
{
|
|
fwdMovement = (TargetDistance < distanceToAttack) ? (strafeBackward ? -1 : 0) : TargetDistance > distanceToAttack ? 1 : 0;
|
|
Ray ray = new Ray(transform.position, transform.forward * fwdMovement);
|
|
if (TargetDistance < strafeDistance - 0.5f)
|
|
{
|
|
destination = (fwdMovement != 0) ? ray.GetPoint(agent.stoppingDistance + ((fwdMovement > 0) ? TargetDistance : 1f)) : transform.position;
|
|
}
|
|
else if (currentTarget.transform != null)
|
|
{
|
|
destination = currentTarget.transform.position;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
protected IEnumerator PatrolSubPoints()
|
|
{
|
|
while (!agent.enabled)
|
|
{
|
|
yield return null;
|
|
}
|
|
|
|
if (targetWaypoint)
|
|
{
|
|
if (targetPatrolPoint == null || !targetPatrolPoint.isValid)
|
|
{
|
|
targetPatrolPoint = GetPatrolPoint(targetWaypoint);
|
|
}
|
|
else
|
|
{
|
|
agent.speed = Mathf.Lerp(agent.speed, (agent.hasPath && targetPatrolPoint.isValid) ? patrolSpeed : 0, smoothSpeed * Time.deltaTime);
|
|
agent.stoppingDistance = patrollingStopDistance;
|
|
destination = targetPatrolPoint.isValid ? targetPatrolPoint.position : transform.position;
|
|
if (Vector3.Distance(transform.position, destination) < targetPatrolPoint.areaRadius && targetPatrolPoint.CanEnter(transform) && !targetPatrolPoint.IsOnWay(transform))
|
|
{
|
|
targetPatrolPoint.Enter(transform);
|
|
wait = Time.time + targetPatrolPoint.timeToStay;
|
|
visitedPatrolPoint.Add(targetPatrolPoint);
|
|
}
|
|
else if (Vector3.Distance(transform.position, destination) < targetPatrolPoint.areaRadius && (!targetPatrolPoint.CanEnter(transform) || !targetPatrolPoint.isValid))
|
|
{
|
|
targetPatrolPoint = GetPatrolPoint(targetWaypoint);
|
|
}
|
|
|
|
if (targetPatrolPoint != null && (targetPatrolPoint.IsOnWay(transform) && Vector3.Distance(transform.position, destination) < distanceToChangeWaypoint))
|
|
{
|
|
if (wait < Time.time || !targetPatrolPoint.isValid)
|
|
{
|
|
wait = 0;
|
|
if (visitedPatrolPoint.Count == pathArea.GetValidSubPoints(targetWaypoint).Count)
|
|
{
|
|
currentState = AIStates.PatrolWaypoints;
|
|
targetWaypoint.Exit(transform);
|
|
targetPatrolPoint.Exit(transform);
|
|
targetWaypoint = null;
|
|
targetPatrolPoint = null;
|
|
visitedPatrolPoint.Clear();
|
|
}
|
|
else
|
|
{
|
|
targetPatrolPoint.Exit(transform);
|
|
targetPatrolPoint = GetPatrolPoint(targetWaypoint);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (canSeeTarget)
|
|
{
|
|
currentState = AIStates.Chase;
|
|
}
|
|
}
|
|
|
|
protected IEnumerator PatrolWaypoints()
|
|
{
|
|
while (!agent.enabled)
|
|
{
|
|
yield return null;
|
|
}
|
|
|
|
if (pathArea != null && pathArea.waypoints.Count > 0)
|
|
{
|
|
if (targetWaypoint == null || !targetWaypoint.isValid)
|
|
{
|
|
targetWaypoint = GetWaypoint();
|
|
}
|
|
else
|
|
{
|
|
agent.speed = Mathf.Lerp(agent.speed, (agent.hasPath && targetWaypoint.isValid) ? patrolSpeed : 0, smoothSpeed * Time.deltaTime);
|
|
|
|
agent.stoppingDistance = patrollingStopDistance;
|
|
|
|
destination = targetWaypoint.position;
|
|
if (Vector3.Distance(transform.position, destination) < targetWaypoint.areaRadius && targetWaypoint.CanEnter(transform) && !targetWaypoint.IsOnWay(transform))
|
|
{
|
|
targetWaypoint.Enter(transform);
|
|
wait = Time.time + targetWaypoint.timeToStay;
|
|
}
|
|
else if (Vector3.Distance(transform.position, destination) < targetWaypoint.areaRadius && (!targetWaypoint.CanEnter(transform) || !targetWaypoint.isValid))
|
|
{
|
|
targetWaypoint = GetWaypoint();
|
|
}
|
|
|
|
if (targetWaypoint != null && targetWaypoint.IsOnWay(transform) && Vector3.Distance(transform.position, destination) < distanceToChangeWaypoint)
|
|
{
|
|
if (wait < Time.time || !targetWaypoint.isValid)
|
|
{
|
|
wait = 0;
|
|
if (targetWaypoint.subPoints.Count > 0)
|
|
{
|
|
currentState = AIStates.PatrolSubPoints;
|
|
}
|
|
else
|
|
{
|
|
targetWaypoint.Exit(transform);
|
|
visitedPatrolPoint.Clear();
|
|
targetWaypoint = GetWaypoint();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (ignorePatrolTimer < Time.time)
|
|
{
|
|
switch (patrolWithoutAreaStyle)
|
|
{
|
|
case AIPatrolWithOutAreaStyle.GoToStartPoint:
|
|
yield return StartCoroutine(GoToStartingPoint());
|
|
break;
|
|
case AIPatrolWithOutAreaStyle.Idle:
|
|
currentState = AIStates.Idle;
|
|
break;
|
|
case AIPatrolWithOutAreaStyle.Wander:
|
|
currentState = AIStates.Wander;
|
|
break;
|
|
}
|
|
|
|
}
|
|
else if (ignorePatrolTimer > Time.time)
|
|
{
|
|
yield return StartCoroutine(GoToDestionation());
|
|
}
|
|
|
|
if (canSeeTarget)
|
|
{
|
|
currentState = AIStates.Chase;
|
|
}
|
|
}
|
|
|
|
protected virtual IEnumerator GoToDestionation()
|
|
{
|
|
yield return null;
|
|
agent.speed = Mathf.Lerp(agent.speed, moveToSpeed, smoothSpeed * Time.deltaTime);
|
|
agent.stoppingDistance = patrollingStopDistance;
|
|
destination = moveToDestination;
|
|
}
|
|
|
|
protected virtual IEnumerator GoToStartingPoint()
|
|
{
|
|
yield return null;
|
|
agent.speed = Mathf.Lerp(agent.speed, patrolSpeed, smoothSpeed * Time.deltaTime);
|
|
agent.stoppingDistance = patrollingStopDistance;
|
|
destination = startPosition;
|
|
|
|
}
|
|
|
|
protected virtual IEnumerator Wander()
|
|
{
|
|
|
|
agent.speed = Mathf.Lerp(agent.speed, wanderSpeed, smoothSpeed * Time.deltaTime);
|
|
do
|
|
{
|
|
yield return null;
|
|
destination = transform.position + (Quaternion.AngleAxis(UnityEngine.Random.Range(-120, 120), transform.up) * transform.forward) * (patrollingStopDistance + 4);
|
|
} while (agent.enabled && agent.isOnNavMesh && agent.remainingDistance <= patrollingStopDistance);
|
|
if (canSeeTarget)
|
|
{
|
|
currentState = AIStates.Chase;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region AI Waypoint & PatrolPoint
|
|
|
|
vWaypoint GetWaypoint()
|
|
{
|
|
var waypoints = pathArea.GetValidPoints();
|
|
|
|
if (randomWaypoints)
|
|
{
|
|
currentWaypoint = randomWaypoint.Next(waypoints.Count);
|
|
}
|
|
else
|
|
{
|
|
currentWaypoint++;
|
|
}
|
|
|
|
if (currentWaypoint >= waypoints.Count)
|
|
{
|
|
currentWaypoint = 0;
|
|
}
|
|
|
|
if (waypoints.Count == 0)
|
|
{
|
|
agent.isStopped = true;
|
|
return null;
|
|
}
|
|
if (visitedWaypoint.Count == waypoints.Count)
|
|
{
|
|
visitedWaypoint.Clear();
|
|
}
|
|
|
|
if (visitedWaypoint.Contains(waypoints[currentWaypoint]))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
agent.isStopped = false;
|
|
return waypoints[currentWaypoint];
|
|
}
|
|
|
|
vPoint GetPatrolPoint(vWaypoint waypoint)
|
|
{
|
|
var subPoints = pathArea.GetValidSubPoints(waypoint);
|
|
if (waypoint.randomPatrolPoint)
|
|
{
|
|
currentPatrolPoint = randomPatrolPoint.Next(subPoints.Count);
|
|
}
|
|
else
|
|
{
|
|
currentPatrolPoint++;
|
|
}
|
|
|
|
if (currentPatrolPoint >= subPoints.Count)
|
|
{
|
|
currentPatrolPoint = 0;
|
|
}
|
|
|
|
if (subPoints.Count == 0)
|
|
{
|
|
agent.isStopped = true;
|
|
return null;
|
|
}
|
|
if (visitedPatrolPoint.Contains(subPoints[currentPatrolPoint]))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
agent.isStopped = false;
|
|
return subPoints[currentPatrolPoint];
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region AI Melee Combat
|
|
|
|
protected IEnumerator MeleeAttackRotine()
|
|
{
|
|
if (!isAttacking && !actions && attackCount > 0 && !lockMovement && !isRolling)
|
|
{
|
|
sideMovement = GetRandonSide();
|
|
agent.stoppingDistance = distanceToAttack;
|
|
attackCount--;
|
|
MeleeAttack();
|
|
yield return null;
|
|
}
|
|
//else if (!actions && attackCount > 0) canAttack = true;
|
|
}
|
|
|
|
public void FinishAttack()
|
|
{
|
|
// if(attackCount > 0)
|
|
canAttack = true;
|
|
}
|
|
|
|
IEnumerator ResetAttackCount()
|
|
{
|
|
inResetAttack = true;
|
|
canAttack = false;
|
|
var value = 0f;
|
|
if (firstAttack)
|
|
{
|
|
firstAttack = false;
|
|
value = firstAttackDelay;
|
|
}
|
|
else
|
|
{
|
|
value = UnityEngine.Random.Range(minTimeToAttack, maxTimeToAttack);
|
|
}
|
|
|
|
yield return new WaitForSeconds(value);
|
|
attackCount = randomAttackCount ? UnityEngine.Random.Range(1, maxAttackCount + 1) : maxAttackCount;
|
|
canAttack = true;
|
|
inResetAttack = false;
|
|
}
|
|
|
|
public void OnEnableAttack()
|
|
{
|
|
isAttacking = true;
|
|
}
|
|
|
|
public void OnDisableAttack()
|
|
{
|
|
isAttacking = false;
|
|
canAttack = true;
|
|
}
|
|
|
|
public void ResetAttackTriggers()
|
|
{
|
|
animator.ResetTrigger("WeakAttack");
|
|
}
|
|
|
|
public void BreakAttack(int breakAtkID)
|
|
{
|
|
ResetAttackCount();
|
|
ResetAttackTriggers();
|
|
OnRecoil(breakAtkID);
|
|
}
|
|
|
|
public void OnRecoil(int recoilID)
|
|
{
|
|
TriggerRecoil(recoilID);
|
|
}
|
|
|
|
public void OnReceiveAttack(vDamage damage, vIMeleeFighter attacker)
|
|
{
|
|
StartCoroutine(CheckChanceToBlock(chanceToBlockAttack, 0));
|
|
|
|
var attackPos = (attacker != null && attacker.character != null) ? attacker.character.transform.position : damage.hitPosition;
|
|
if (!damage.ignoreDefense && isBlocking && meleeManager != null && meleeManager.CanBlockAttack(attackPos))
|
|
{
|
|
var damageReduction = meleeManager != null ? meleeManager.GetDefenseRate() : 0;
|
|
if (damageReduction > 0)
|
|
{
|
|
damage.ReduceDamage(damageReduction);
|
|
}
|
|
|
|
if (attacker != null && meleeManager != null && meleeManager.CanBreakAttack())
|
|
{
|
|
attacker.OnRecoil(meleeManager.GetDefenseRecoilID());
|
|
}
|
|
|
|
meleeManager.OnDefense();
|
|
}
|
|
// apply tag from the character that hit you and start chase
|
|
if (!passiveToDamage && damage.sender != null)
|
|
{
|
|
SetCurrentTarget(damage.sender);
|
|
currentState = AIStates.Chase;
|
|
|
|
}
|
|
damage.hitReaction = !isBlocking;
|
|
if (!passiveToDamage)
|
|
{
|
|
SetAgressive(true);
|
|
}
|
|
|
|
TakeDamage(damage);
|
|
}
|
|
|
|
public virtual void MoveTo(Vector3 position, float moveToSpeed = 1f, float ignorePatrolTimer = 2f)
|
|
{
|
|
moveToDestination = position;
|
|
currentState = AIStates.PatrolWaypoints;
|
|
this.moveToSpeed = moveToSpeed;
|
|
this.ignorePatrolTimer = Time.time + ignorePatrolTimer;
|
|
|
|
}
|
|
|
|
public vICharacter character
|
|
{
|
|
get { return this; }
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|