initial commit

This commit is contained in:
2026-10-12 21:28:47 +01:00
commit 6dd11d33ae
11551 changed files with 2353518 additions and 0 deletions

View File

@@ -0,0 +1,222 @@
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<NavMeshAgent>();
if (animator == null)
{
animator = GetComponentInChildren<Animator>();
}
if (audioSource == null)
{
audioSource = GetComponent<AudioSource>();
}
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<Projectile>(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<Collider>();
if (col != null)
{
col.enabled = false;
}
AreaClearanceManager.Instance?.UnregisterEnemy(this);
Destroy(gameObject, 5f);
}
}