- Targetting tags can now hold more than one tag, keeping IsValidTarget, AlliesContains and HasSameTag checks available. - Added generic target tags for enemies and players - Optional specific targetting tags if needed for future enhanced targetting
252 lines
7.3 KiB
C#
252 lines
7.3 KiB
C#
using Photon.Pun;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.AI;
|
|
|
|
public class NPCController : MonoBehaviour
|
|
{
|
|
[Header("Events:")]
|
|
[SerializeField] protected GameEvent_Float experienceOnDeath;
|
|
[Header("Settings:")]
|
|
[SerializeField] protected float sightRange;
|
|
[SerializeField] protected float sightRangeMultiplierForProjectiles;
|
|
[SerializeField] protected float distanceToChangePatrolDestination;
|
|
[SerializeField] protected float patrolAgentSpeed;
|
|
[SerializeField] protected float chasingAgentSpeed;
|
|
[SerializeField] protected float timeBetweenAttacks = 1f;
|
|
[HideInInspector]
|
|
public PhotonView photonView;
|
|
[HideInInspector]
|
|
public Taggable myTag;
|
|
|
|
protected NavMeshAgent agent;
|
|
|
|
public List<Taggable> possibleTargets = new List<Taggable>();
|
|
public Taggable currentTarget;
|
|
|
|
protected SphereCollider sight;
|
|
|
|
protected PhotonView otherView;
|
|
protected Taggable possibleTarget;
|
|
protected NPCAbilityBinder abilityBinder;
|
|
protected DropTable dropTable;
|
|
|
|
protected Vector3 patrolDestination = new Vector3();
|
|
|
|
protected float counter = 0f;
|
|
|
|
protected Health health;
|
|
|
|
protected bool isDead = false;
|
|
|
|
#region Death Animation
|
|
protected float moveSpeed = 0.5f; // Speed of the downwards movement
|
|
protected float moveDistance = 3.0f; // Distance to move downwards
|
|
protected float moveDelay = 0.25f; // Delay before starting the movement
|
|
protected Vector3 startPosition;
|
|
protected Vector3 endPosition;
|
|
#endregion
|
|
|
|
protected virtual void Awake()
|
|
{
|
|
myTag = GetComponentInParent<Taggable>();
|
|
agent = GetComponentInParent<NavMeshAgent>();
|
|
photonView = GetComponentInParent<PhotonView>();
|
|
abilityBinder = GetComponent<NPCAbilityBinder>();
|
|
health = GetComponent<Health>();
|
|
dropTable = GetComponentInChildren<DropTable>();
|
|
|
|
if (!photonView.IsMine) return;
|
|
}
|
|
|
|
// Start is called before the first frame update
|
|
protected virtual void Start()
|
|
{
|
|
if (!photonView.IsMine) return;
|
|
|
|
sight = this.gameObject.AddComponent<SphereCollider>();
|
|
sight.radius = sightRange;
|
|
sight.isTrigger = true;
|
|
isDead = false;
|
|
|
|
counter = timeBetweenAttacks / 2f;
|
|
|
|
health.onDeath.AddListener(OnDeath);
|
|
}
|
|
|
|
protected virtual void OnDeath()
|
|
{
|
|
photonView.RPC(nameof(RPC_OnDeath), RpcTarget.All, dropTable.CalculateLootDrop());
|
|
}
|
|
|
|
[PunRPC]
|
|
protected virtual void RPC_OnDeath(bool lootDropped)
|
|
{
|
|
if (isDead) return;
|
|
|
|
Debug.Log($"{this.gameObject.name} died!");
|
|
|
|
isDead = true;
|
|
|
|
agent.enabled = false;
|
|
|
|
experienceOnDeath.Raise(health.GetMaxValue() * GameConstants.GameBalancing.HealthIntoExperienceMultiplier);
|
|
|
|
dropTable.DropLoot(lootDropped);
|
|
|
|
if (!photonView.IsMine) return;
|
|
|
|
StartCoroutine(AnimateOnDeath());
|
|
}
|
|
|
|
protected virtual IEnumerator AnimateOnDeath()
|
|
{
|
|
startPosition = transform.position;
|
|
endPosition = transform.position - Vector3.up * moveDistance;
|
|
|
|
yield return new WaitForSeconds(moveDelay);
|
|
|
|
float elapsedTime = 0.0f;
|
|
while (elapsedTime < 1.0f)
|
|
{
|
|
// Increment elapsed time based on Time.deltaTime
|
|
elapsedTime += Time.deltaTime * moveSpeed;
|
|
|
|
// Move the transform towards the end position
|
|
transform.position = Vector3.Lerp(transform.position, endPosition, elapsedTime);
|
|
|
|
yield return null; // Yielding null will make the coroutine wait until the next frame
|
|
}
|
|
|
|
DestroyAfterEffect();
|
|
}
|
|
|
|
protected virtual void DestroyAfterEffect()
|
|
{
|
|
if (!photonView.IsMine) return;
|
|
|
|
PhotonNetwork.Destroy(this.gameObject);
|
|
}
|
|
|
|
protected virtual void Update()
|
|
{
|
|
if (!photonView.IsMine) return;
|
|
if (isDead) return;
|
|
|
|
if (possibleTargets.Count <= 0)
|
|
{
|
|
if (currentTarget != null)
|
|
{
|
|
currentTarget = null;
|
|
PatrolNewPosition();
|
|
}
|
|
if (agent.destination == null)
|
|
{
|
|
PatrolNewPosition();
|
|
}
|
|
else if (agent.remainingDistance < distanceToChangePatrolDestination)
|
|
{
|
|
PatrolNewPosition();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (possibleTargets[0] == null)
|
|
{
|
|
possibleTargets.RemoveAt(0);
|
|
return;
|
|
}
|
|
|
|
currentTarget = possibleTargets[0];
|
|
agent.speed = chasingAgentSpeed;
|
|
patrolDestination = currentTarget.transform.position;
|
|
patrolDestination.y = 0f;
|
|
agent.SetDestination(patrolDestination);
|
|
|
|
counter += Time.deltaTime;
|
|
if (agent.remainingDistance <= sightRange * sightRangeMultiplierForProjectiles)
|
|
{
|
|
agent.isStopped = true;
|
|
if (counter >= timeBetweenAttacks)
|
|
{
|
|
counter = 0f;
|
|
if (!abilityBinder.ValidCoreAbility())
|
|
abilityBinder.UsePrimaryAbility();
|
|
|
|
else
|
|
{
|
|
if (Random.Range(0, 10) > 3)
|
|
abilityBinder.UsePrimaryAbility();
|
|
else
|
|
abilityBinder.UseCoreAbility(currentTarget.transform.position);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
agent.isStopped = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected virtual void PatrolNewPosition()
|
|
{
|
|
agent.speed = patrolAgentSpeed;
|
|
patrolDestination.x = Random.Range(-5, 5);
|
|
patrolDestination.y = 0f;
|
|
patrolDestination.z = Random.Range(-5, 5);
|
|
agent.SetDestination(this.transform.position + patrolDestination);
|
|
agent.isStopped = false;
|
|
}
|
|
|
|
protected virtual void OnTriggerEnter(Collider other)
|
|
{
|
|
if (!photonView.IsMine) return;
|
|
|
|
otherView = other.GetComponentInParent<PhotonView>();
|
|
if (otherView != null)
|
|
{
|
|
if (otherView == photonView) return;
|
|
}
|
|
|
|
possibleTarget = other.GetComponentInParent<Taggable>();
|
|
|
|
if (possibleTarget == null) return;
|
|
|
|
//if (possibleTarget.targetTag == myTag.targetTag || myTag.targetTag.AlliedTags.Contains(possibleTarget.targetTag)) return;
|
|
if (possibleTarget.HasSameTag(myTag.targetTag) || myTag.AlliedTagsContains(possibleTarget.targetTag)) return;
|
|
|
|
if (possibleTargets.Contains(possibleTarget)) return;
|
|
|
|
possibleTargets.Add(possibleTarget);
|
|
}
|
|
|
|
protected virtual void OnTriggerExit(Collider other)
|
|
{
|
|
if (!photonView.IsMine) return;
|
|
|
|
otherView = other.GetComponentInParent<PhotonView>();
|
|
if (otherView != null)
|
|
{
|
|
if (otherView == photonView) return;
|
|
}
|
|
|
|
possibleTarget = other.GetComponentInParent<Taggable>();
|
|
|
|
if (possibleTarget == null) return;
|
|
|
|
if (possibleTarget.targetTag == myTag.targetTag) return;
|
|
|
|
if (!possibleTargets.Contains(possibleTarget)) return;
|
|
|
|
possibleTargets.Remove(possibleTarget);
|
|
}
|
|
|
|
|
|
protected virtual void OnDrawGizmosSelected()
|
|
{
|
|
Gizmos.DrawWireSphere(this.transform.position, sightRange);
|
|
}
|
|
}
|