using System.Collections;
using System.Collections.Generic;
using MoreMountains.Feedbacks;
using TMPro;
using UnityEngine;
using UnityEngine.AI;
public abstract class Enemy : MonoBehaviour, IDamageable
{
[Header("Enemy Script")]
public PlayerController playerController;
public Animator animator;
[SerializeField] protected List<GameObject> playerFlanks;
[SerializeField] CapsuleCollider hitCapsuleCollider;
[SerializeField] protected Rigidbody rb;
[SerializeField] protected NavMeshAgent agent;
[SerializeField] Vector3 alertPos;
public Transform runAwayTarget;
public Enemies enemiesSO;
[Header("Prefabs")]
[SerializeField] GameObject silverCoinPrefab;
[SerializeField] GameObject bossCoinPrefab;
[SerializeField] GameObject healPrefab;
[SerializeField] GameObject dizzyPrefab;
[SerializeField] GameObject[] xpStarsPrefabs;
[SerializeField] GameObject alertPrefab;
[Header("Feels")]
[SerializeField] MMF_Player getHitFeel;
public enum EnemyStates { Idle, Moving, Aiming, Shooting, Dodging, Running };
[Header("States")]
public EnemyStates enemyState = EnemyStates.Moving;
public bool isDizzy;
public bool isAlerted;
public bool isDodging;
public bool isRunningAway;
[Header("Enemy Stats")]
public int enemyHP;
public int dizzyAmount;
public float rotationSpeed;
public int playerHarvest;
public float forceStrength;
public bool isAttackable;
public static event System.Action<GameObject> OnEnemyDeath;
// Start is called once before the first execution of Update after the MonoBehaviour is created
protected virtual void Start()
{
playerController = GameObject.FindGameObjectWithTag("Player").GetComponent<PlayerController>();
rb = GetComponent<Rigidbody>();
agent = GetComponent<NavMeshAgent>();
animator = GetComponentInChildren<Animator>();
enemyHP = enemiesSO.enemyHP;
playerHarvest = playerController.playerData.harvesting;
foreach (var playerFlank in playerController.playerFlanks)
{
playerFlanks.Add(playerFlank);
}
isAttackable = true;
SwitchState(EnemyStates.Idle);
agent.enabled = true;
}
protected virtual void Update()
{
if (dizzyAmount >= enemiesSO.dizzyMax && !isDizzy)
{
StartCoroutine(DizzyTimer());
}
}
public virtual void TakeDamage(int weaponDamage)
{
if (enemyHP <= 0) return; // Prevent multiple death calls
enemyHP -= weaponDamage;
if (!isAlerted)
{
AlertEnemy();
}
StartCoroutine(CalculateKnockback(playerController.playerData.knockback, gameObject));
if (getHitFeel != null) getHitFeel.PlayFeedbacks();
if (enemyHP <= 0)
{
OnEnemyDeath?.Invoke(gameObject);
hitCapsuleCollider.enabled = false;
agent.enabled = true;
agent.isStopped = true;
Destroy(gameObject, 1f);
}
}
public virtual void Dodge()
{
if (!isDodging)
{
bool canDodge = Random.value < 0.9;
if (canDodge)
{
StopAllCoroutines();
StartCoroutine(DodgeTimer());
}
}
}
IEnumerator DodgeTimer()
{
rb.isKinematic = false;
agent.enabled = false;
isDodging = true;
animator.SetBool("isDodging", true);
Vector3 dodgeDirection = (Random.value > 0.5f) ? -transform.right : transform.right;
dodgeDirection.y = 0;
rb.AddForce(dodgeDirection * 20, ForceMode.Impulse);
yield return new WaitForSeconds(0.5f);
animator.SetBool("isDodging", false);
isDodging = false;
rb.isKinematic = true;
agent.enabled = true;
SwitchState(EnemyStates.Moving);
}
protected virtual void RunAway()
{
if (!isRunningAway)
{
StopAllCoroutines();
agent.isStopped = false;
StartCoroutine(RunAwayTimer());
isRunningAway = true;
}
}
IEnumerator RunAwayTimer()
{
agent.enabled = true;
rb.isKinematic = true;
animator.SetBool("isMoving", true);
while (Vector3.Distance(transform.position, playerController.transform.position) < 15f)
{
Vector3 direction = (transform.position - playerController.transform.position).normalized;
Vector3 runTo = transform.position + direction * 10f;
runAwayTarget.transform.position = runTo;
agent.SetDestination(runAwayTarget.transform.position);
// Debug.DrawLine(transform.position, runTo, Color.red, 2f);
yield return new WaitForSeconds(0.5f); // re-check interval
}
isRunningAway = false;
animator.SetBool("isMoving", false);
SwitchState(EnemyStates.Shooting);
}
public virtual void AlertEnemy()
{
if (!isAlerted)
{
Instantiate(alertPrefab, transform.position + alertPos, Quaternion.Euler(-25, 0, 0));
isAlerted = true;
}
}
IEnumerator CalculateKnockback(int knockback, GameObject gameObject)
{
// agent.enabled = false;
rb.linearVelocity = Vector3.zero;
rb.angularVelocity = Vector3.zero;
Vector3 direction = gameObject.transform.position - playerController.gameObject.transform.position;
rb.AddForce(direction * knockback, ForceMode.Impulse);
yield return new WaitForSeconds(0.1f);
rb.linearVelocity = Vector3.zero;
rb.angularVelocity = Vector3.zero;
agent.enabled = true;
}
IEnumerator DizzyTimer()
{
isDizzy = true;
agent.isStopped = true;
agent.speed = 0;
GameObject dizzyInstance = Instantiate(dizzyPrefab, gameObject.transform.position + new Vector3(0, 1, 0), Quaternion.identity);
dizzyInstance.transform.SetParent(gameObject.transform);
yield return new WaitForSeconds(3f);
Destroy(dizzyInstance);
agent.isStopped = false;
agent.speed = enemiesSO.enemySpeed;
dizzyAmount = 0;
isDizzy = false;
}
private void OnDestroy()
{
if (enemyHP > 0) return;
OnEnemyDeath?.Invoke(gameObject);
DropXP();
int healDropChance = Random.Range(1, 10);
if (healDropChance <= 2) // 20%
{
Instantiate(healPrefab, gameObject.transform.position, Quaternion.Euler(0, 0, -30f));
}
//enemy will always drop a coin, then with player harvest there's a 50% he'll more
GameObject coin = Instantiate(silverCoinPrefab, transform.position + new Vector3(0, 1f, 0f), Quaternion.Euler(90, 0, 0));
coin.transform.SetParent(null);
if (playerHarvest >= 1)
{
for (int i = 0; i < playerHarvest; i++)
{
int randomNumber = Random.Range(0, 9);
if (randomNumber <= 4)
{
GameObject additionalCoin = Instantiate(silverCoinPrefab, transform.position + new Vector3(0, 1f, 0f), Quaternion.Euler(90, 0, 0));
additionalCoin.transform.SetParent(null);
}
if (randomNumber <= 0)
{
GameObject additionalCoin = Instantiate(bossCoinPrefab, transform.position + new Vector3(0, 1f, 0f), Quaternion.Euler(90, 0, 0));
additionalCoin.transform.SetParent(null);
}
}
}
}
void DropXP()
{
for (int i = 0; i < enemiesSO.xpAmount; i++)
{
Vector3 randomOffset = new Vector3(Random.Range(-1, 1), 0, Random.Range(-1, 1));
float randomStarWeight = Random.value;
GameObject starToDrop;
if (randomStarWeight < 0.1f)
{
starToDrop = xpStarsPrefabs[2];
}
else if (randomStarWeight < 0.4f)
{
starToDrop = xpStarsPrefabs[1];
}
else
{
starToDrop = xpStarsPrefabs[0];
}
Rigidbody xpRb = Instantiate(starToDrop, transform.position, Quaternion.identity).GetComponent<Rigidbody>();
xpRb.AddForce((randomOffset + Vector3.up * 1.1f) * Random.Range(forceStrength - 0.2f, forceStrength), ForceMode.Impulse);
}
}
public void SwitchState(EnemyStates newState)
{
enemyState = newState;
if (!agent.enabled) agent.enabled = true;
}
protected void Rotate()
{
var direction = (playerController.transform.position - transform.position).normalized;
direction.y = 0;
Quaternion targetRotation = Quaternion.LookRotation(direction);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);
}
}