using UnityEngine; using Akila.FPSFramework; public enum TargetType { None, Enemy, Player, PatrolPoint } public abstract class BaseAIController : MonoBehaviour { [Header("Movement & Combat")] public float moveSpeed = 5f; public float chaseRange = 15f; public float stopCloseRange = 4f; public float strafeAmount; public float pointWaitTimer = 3f; public float currentHealth = 25f; public float waitToDisappear = 4f; public float damageAmount = 10f; [Header("Components")] public Rigidbody theRB; public Animator anim; [Header("Patrol")] public Transform[] PatrolPoints; protected int currentPatrolPoint; public Transform pointsHolder; protected float waitCounter; protected bool isDead; protected float attackCooldown = 1f; protected float attackTimer = 0f; protected FirstPersonController player; protected Damageable playerDamageable; protected virtual void Start() { player = FindFirstObjectByType(); if (player != null) { playerDamageable = player.GetComponent(); } strafeAmount = Random.Range(-0.75f, 0.75f); if (pointsHolder != null) { pointsHolder.SetParent(null); } waitCounter = Random.Range(0.75f, 1.25f) * pointWaitTimer; attackTimer = 0f; } protected void HandleDeath() { waitToDisappear -= Time.deltaTime; if (waitToDisappear <= 0) { transform.localScale = Vector3.MoveTowards(transform.localScale, Vector3.zero, Time.deltaTime * 2f); if (transform.localScale.magnitude < 0.1f) { Destroy(gameObject); if (pointsHolder != null) { Destroy(pointsHolder.gameObject); } } } } protected void UpdateTimers() { attackTimer -= Time.deltaTime; } protected bool ValidatePlayerReferences() { return player != null && playerDamageable != null; } protected void StopMovement() { theRB.linearVelocity = Vector3.zero; anim.SetFloat("speed", 0f); } protected void LookAt(Transform target) { if (target != null) { transform.LookAt(new Vector3(target.position.x, transform.position.y, target.position.z)); } } protected void MoveTowardsTarget(Transform target, float speedMultiplier) { if (target != null) { LookAt(target); theRB.linearVelocity = (transform.forward + transform.right * strafeAmount) * moveSpeed; anim.SetFloat("speed", speedMultiplier); } } protected void AttackTarget() { theRB.linearVelocity = Vector3.zero; anim.SetFloat("speed", 0f); if (attackTimer <= 0f) { anim.SetTrigger("attack"); attackTimer = attackCooldown; } } protected void HandlePatrolling() { if (PatrolPoints.Length > 0) { float distanceToPatrolPoint = Vector3.Distance(transform.position, new Vector3(PatrolPoints[currentPatrolPoint].position.x, transform.position.y, PatrolPoints[currentPatrolPoint].position.z)); if (distanceToPatrolPoint < 0.25f) { WaitAtPatrolPoint(); } else { MoveToPatrolPoint(); } } else { StopMovement(); } } protected void WaitAtPatrolPoint() { waitCounter -= Time.deltaTime; StopMovement(); if (waitCounter <= 0) { MoveToNextPatrolPoint(); } } protected void MoveToNextPatrolPoint() { currentPatrolPoint++; if (currentPatrolPoint >= PatrolPoints.Length) { currentPatrolPoint = 0; } waitCounter = Random.Range(0.75f, 1.25f) * pointWaitTimer; } protected void MoveToPatrolPoint() { MoveTowardsTarget(PatrolPoints[currentPatrolPoint], 0.5f); // Walking speed } protected void PreserveVerticalVelocity(float yStore) { theRB.linearVelocity = new Vector3(theRB.linearVelocity.x, yStore, theRB.linearVelocity.z); } public virtual void TakeDamage(float damageToTake) { currentHealth -= damageToTake; if (currentHealth <= 0) { anim.SetTrigger("dead"); isDead = true; theRB.linearVelocity = Vector3.zero; theRB.isKinematic = true; GetComponent().enabled = false; } } }