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 possibleTargets = new List(); 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(); agent = GetComponentInParent(); photonView = GetComponentInParent(); abilityBinder = GetComponent(); health = GetComponent(); dropTable = GetComponentInChildren(); if (!photonView.IsMine) return; } // Start is called before the first frame update protected virtual void Start() { if (!photonView.IsMine) return; sight = this.gameObject.AddComponent(); 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(); if (otherView != null) { if (otherView == photonView) return; } possibleTarget = other.GetComponentInParent(); 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(); if (otherView != null) { if (otherView == photonView) return; } possibleTarget = other.GetComponentInParent(); 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); } }