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 Materials; public Gobler(GameObject owner) { Owner = owner; PathAgent = Owner.GetComponent(); 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(); Materials = Owner.GetComponentsInChildren().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 OnTakeDamage; public event Action OnDeath; public event Action 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; } }