TowerDefenseGame/Assets/Scripts/Runtime/AI/EnemyManager/Gobler.cs
Nico a492afdf1b Switch over to isometric map and set navmesh
Refactor attack animator
Refactor SpawnerManager - utilize SO (SpawnableEnemyInfo)
2025-07-18 23:51:27 -07:00

278 lines
7.1 KiB
C#

using AI.Base;
using NUnit.Framework.Internal.Execution;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using UnityEngine;
using UnityEngine.AI;
using static EnemySpawnerData;
public class Gobler : Alive {
[Header("Mechanics Attributes")]
[SerializeField] public float ChaseDistance = 10f;
[SerializeField] public float ChaseDistanceBuffer = 1f;
[SerializeField] public float AttackDistance = 1f;
[SerializeField] public float AttackMaskDiameter = 1f;
[Header("Enemy Attributes")]
[SerializeField] public float Attack = 10f;
[SerializeField] public float Speed = 10f;
[SerializeField] public float Health = 10f;
[SerializeField] public float Energy = 10f;
protected FloatingTextSpawner TextPopUp;
private Player Player { get { return Player.Instance; } }
private Transform PlayerTransform { get { return Player.transform; } }
private Vector2 PlayerPos { get { return PlayerTransform.position; } }
private Vector2 CrystalPos { get { return GameManager.Crystal.transform.position; } }
private GameObject Owner;
private Rigidbody2D Rigidbody;
private Transform MyTransform { get { return Owner.transform; } }
private Vector2 MyPos { get { return MyTransform.position; } set { MyTransform.position = value; } }
private NavMeshAgent PathAgent;
private List<Material> Materials;
public Gobler(GameObject owner) {
Owner = owner;
PathAgent = Owner.GetComponent<NavMeshAgent>();
PathAgent.updateRotation = false;
PathAgent.updateUpAxis = false;
PathAgent.speed = Speed;
IsUpdating = false;
MaxHealth = 100;
CurrentHealth = MaxHealth;
OnTakeDamage += (damage, direction) => SetState(State.TakeDamage);
OnDeath += () => SetState(State.Die);
Rigidbody = Owner.GetComponent<Rigidbody2D>();
Materials = Owner.GetComponentsInChildren<SpriteRenderer>().Select(x => x.material).ToList();
TextPopUp = new FloatingTextSpawner(Owner.transform, 1);
SetState(State.GoToCrystal);
EnemyManagers[Enemies.Gobler].UpdateTick += Update;
EnemyManagers[Enemies.Gobler].GizmoTick += OnDrawGizmos;
}
public float DistFromPlayer { get; private set; }
public float DistFromCrystal { get; private set; }
public State CurrentState;
public enum State {
None,
GoToCrystal,
AttackCrystal,
ChasePlayer,
AttackPlayer,
PrepareAttack,
FinalizeAttack,
TakeDamage,
Die
}
protected void SetPriorityState() {
if (CurrentState == State.AttackPlayer) return;
if (CurrentState == State.ChasePlayer) return;
if (CurrentState == State.TakeDamage) return;
if (CurrentState == State.Die) return;
if (DistFromPlayer > ChaseDistance) return;
SetState(State.ChasePlayer);
}
private bool IsUpdating;
protected void Update() {
if (Owner == null) return;
if (GameManager.Crystal == null) return;
if (IsUpdating) return;
IsUpdating = true;
DistFromPlayer = Vector2.Distance(MyPos, PlayerPos);
DistFromCrystal = Vector2.Distance(MyPos, CrystalPos);
SetPriorityState();
switch (CurrentState) {
case State.None:
SetState(State.GoToCrystal);
break;
case State.GoToCrystal:
PathAgent.SetDestination(CrystalPos);
break;
case State.AttackCrystal:
break;
case State.ChasePlayer:
if (DistFromPlayer > ChaseDistance + ChaseDistanceBuffer)
SetState(State.GoToCrystal);
if (DistFromPlayer > ChaseDistance + ChaseDistanceBuffer)
SetState(State.GoToCrystal);
else
PathAgent.SetDestination(PlayerPos);
break;
case State.AttackPlayer:
break;
case State.TakeDamage:
Rigidbody.linearVelocity = DirectionOfDamage * Knockback * 10 * InvincibilityLeft;
Materials.ForEach(x => x.SetFloat("_FlashAmount", InvincibilityLeft));
if (!IsInvincible) SetState(State.None);
break;
case State.PrepareAttack:
break;
case State.FinalizeAttack:
break;
}
IsUpdating = false;
}
protected void SetState(State newState) {
CurrentState = newState;
switch (CurrentState) {
case State.None:
PathAgent.isStopped = false;
break;
case State.GoToCrystal:
break;
case State.AttackCrystal:
break;
case State.ChasePlayer:
break;
case State.AttackPlayer:
break;
case State.TakeDamage:
PathAgent.isStopped = true;
TextPopUp.SpawnFloatingText(DamageTaken.ToString(), Color.red, 3);
Materials.ForEach(x => x.SetColor("_FlashColor", Color.white));
break;
case State.Die:
SpawnableEnemyInfo.DestroyEnemy(Owner, Enemies.Gobler);
SetState(State.None);
break;
}
}
protected void OnDrawGizmos() {
if (Owner == null) return;
DrawChaseDistance();
DrawAttackDistance();
DrawAttackReach();
}
private void DrawWireSphere(Color color, Vector2 center, float radius) => EnemyManagers[Enemies.Gobler].DrawWireSphere(color, center, radius);
private void DrawChaseDistance() {
DrawWireSphere(Color.green, MyPos, ChaseDistance);
}
private void DrawAttackDistance() {
//Gizmos.color = CurrentState == (IState)AttackState ? Color.red : Color.green;
//Vector2 center = this.transform.position;
//Gizmos.DrawWireSphere(center, AttackDistance);
}
private void DrawAttackReach() {
//if (CurrentState != (IState)AttackState) return;
//Gizmos.color = Color.red;
Vector2 direction2D = (PlayerPos - MyPos).normalized;
Vector2 point2D = MyPos + direction2D * AttackDistance;
//Gizmos.DrawWireSphere(point2D, AttackMaskDiameter);
DrawWireSphere(Color.red, point2D, AttackMaskDiameter);
}
}
public class Alive {
public int MaxHealth { get; protected set; }
public int CurrentHealth { get; protected set; }
public bool IsDead => CurrentHealth <= 0;
public bool IsInvincible => Time.time < InvincibleUntil;
protected float InvincibleUntil = 0f;
protected float InvincibilityLeft => Math.Max(0, InvincibleUntil - Time.time);
protected float InvincibilityDuration = 0.1f;
public event Action<int, Vector2> OnTakeDamage;
public event Action OnDeath;
public event Action<int> OnHeal;
protected int DamageTaken;
protected Vector2 DirectionOfDamage;
protected int Knockback;
public void Damage(int amount, Vector2 hitDirection, int knockback = 1) {
if (IsDead || IsInvincible) return;
CurrentHealth -= amount;
CurrentHealth = Mathf.Max(CurrentHealth, 0);
InvincibleUntil = Time.time + InvincibilityDuration;
DamageTaken = amount;
Knockback = knockback;
hitDirection = hitDirection.normalized;
if (hitDirection == Vector2.zero) hitDirection = Vector2.up;
DirectionOfDamage = hitDirection;
OnTakeDamage?.Invoke(amount, hitDirection);
if (CurrentHealth == 0) OnDeath?.Invoke();
}
public void Heal(int amount) {
if (IsDead) return;
CurrentHealth += amount;
CurrentHealth = Mathf.Min(CurrentHealth, MaxHealth);
OnHeal?.Invoke(amount);
}
public void Reset() {
CurrentHealth = MaxHealth;
InvincibleUntil = 0f;
}
}