using UnityEngine; using UnityEngine.AI; [RequireComponent(typeof(NavMeshAgent))] public class EnemyController : MonoBehaviour { public enum State { Idle, Chase, Attack, Dead } private State currentState = State.Idle; [Header("Stats")] [SerializeField] private int maxHealth = 60; [SerializeField] private int attackDamage = 15; [SerializeField] private float attackRange = 1.8f; [SerializeField] private float attackCooldown = 1.2f; [SerializeField] private float detectionRadius = 18f; [Header("Ranged Attack Settings (Optional)")] [SerializeField] private bool isRangedEnemy = false; [SerializeField] private GameObject rangedProjectilePrefab; [SerializeField] private Transform projectileSpawnPoint; [SerializeField] private float projectileSpeed = 25f; [Header("References")] [SerializeField] private Animator animator; [SerializeField] private AudioSource audioSource; [SerializeField] private AudioClip attackSound; [SerializeField] private AudioClip hitSound; [SerializeField] private AudioClip deathSound; private NavMeshAgent agent; private Transform playerTransform; private int currentHealth; private float nextAttackTime; public State CurrentState => currentState; public int CurrentHealth => currentHealth; private void Awake() { agent = GetComponent(); if (animator == null) { animator = GetComponentInChildren(); } if (audioSource == null) { audioSource = GetComponent(); } currentHealth = maxHealth; } private void Start() { GameObject playerObj = GameObject.FindGameObjectWithTag("Player"); if (playerObj != null) { playerTransform = playerObj.transform; } AreaClearanceManager.Instance?.RegisterEnemy(this); } private void Update() { if (currentState == State.Dead) return; // Re-acquire player reference if it was spawned dynamically if (playerTransform == null) { GameObject playerObj = GameObject.FindGameObjectWithTag("Player"); if (playerObj != null) { playerTransform = playerObj.transform; } return; } float distanceToPlayer = Vector3.Distance(transform.position, playerTransform.position); switch (currentState) { case State.Idle: if (distanceToPlayer <= detectionRadius) { currentState = State.Chase; } break; case State.Chase: if (agent.isOnNavMesh) { agent.SetDestination(playerTransform.position); } if (animator != null) { animator.SetFloat("Speed", agent.velocity.magnitude); } if (distanceToPlayer <= attackRange) { currentState = State.Attack; if (agent.isOnNavMesh) { agent.ResetPath(); } } break; case State.Attack: Vector3 lookTarget = new Vector3(playerTransform.position.x, transform.position.y, playerTransform.position.z); transform.LookAt(lookTarget); if (Time.time >= nextAttackTime) { PerformAttack(); nextAttackTime = Time.time + attackCooldown; } if (distanceToPlayer > attackRange) { currentState = State.Chase; } break; } } private void PerformAttack() { if (animator != null) { animator.SetTrigger("Attack"); } if (attackSound != null && audioSource != null) { audioSource.PlayOneShot(attackSound); } if (isRangedEnemy && rangedProjectilePrefab != null) { // Fire projectile towards player Vector3 spawnPos = projectileSpawnPoint != null ? projectileSpawnPoint.position : transform.position + Vector3.up * 1.2f + transform.forward * 0.5f; Vector3 aimDir = (playerTransform.position + Vector3.up * 1.0f - spawnPos).normalized; GameObject projObj = Instantiate(rangedProjectilePrefab, spawnPos, Quaternion.LookRotation(aimDir)); if (projObj.TryGetComponent(out var proj)) { proj.Initialize(attackDamage, projectileSpeed, "Enemy"); } } else { // Direct melee strike if (PlayerHealthController.Instance != null) { PlayerHealthController.Instance.TakeDamage(attackDamage); } } } public void TakeDamage(int damage) { if (currentState == State.Dead) return; currentHealth -= damage; if (hitSound != null && audioSource != null) { audioSource.PlayOneShot(hitSound); } if (animator != null) { animator.SetTrigger("Hit"); } // Auto-aggro when damaged if idle if (currentState == State.Idle) { currentState = State.Chase; } if (currentHealth <= 0) { Die(); } } private void Die() { currentState = State.Dead; if (agent != null && agent.isOnNavMesh) { agent.isStopped = true; } if (agent != null) { agent.enabled = false; } if (animator != null) { animator.SetBool("IsDead", true); } if (deathSound != null && audioSource != null) { audioSource.PlayOneShot(deathSound); } Collider col = GetComponent(); if (col != null) { col.enabled = false; } AreaClearanceManager.Instance?.UnregisterEnemy(this); Destroy(gameObject, 5f); } }