Newer
Older
TheVengeance-Project-IADE-Unity2D / Assets / Scripts / NPC / NPCController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using MyCollections.AI.FinitStateMachine;
using UnityEngine.UI;
using MyCollections.DesignPatterns.Visitor;
using System;

[RequireComponent(typeof(Animator))]
public abstract class NPCController : MonoBehaviour, IHealthSystem, INotifyObservers
{
    //Attributes
    [Header("NPC Attributes: ")]
    [SerializeField] protected string npcName;
    [SerializeField] protected int health;
    [SerializeField] protected int maxHealth;

    //Idle settings
    [Header("NPC Idle Settings: ")]
    public float timer = 0f;
    [SerializeField] protected float minIdleTime = 2f;
    [SerializeField] protected float maxIdleTime = 10f;
    [SerializeField] protected Vector2 lastDirection = Vector2.right; // Default facing right

    //Components
    [Header("NPC Components: ")]
    [SerializeField] protected FSM fsm;
    [SerializeField] protected Agent agent;
    [SerializeField] protected Animator animator;
    [SerializeField] protected FOV agentFOV;
    [SerializeField] protected Image healthBarImage;
    [SerializeField] private GameObject healthDamageObject;
    [SerializeField] protected AudioSource audioSource;

    protected NPCManager npcManager;
    private bool isDead = false;


    //Observers
    protected List<IObserver> observers = new List<IObserver>();

    //TESTING
    [SerializeField] protected List<string> observersNames = new List<string>();

    //Events
    public event Action<NPCController> OnNPCDied = delegate { };

    //Properties
    public Agent Agent => agent;
    public Animator Animator => animator;
    public FOV AgentFOV => agentFOV;
    public int MaxHealth => maxHealth;
    public bool IsCoroutineRunning { get; set; }
    public NPCManager NPCManager => npcManager;
    public string NPCname => npcName;
    public Image HealthBarImage => healthBarImage;
    public Vector2 LastDirection
    {
        get => lastDirection;
        set => lastDirection = value;
    }

    public AudioSource AudioSource => audioSource;

    protected virtual void Awake()
    {
        if (!TryGetComponent(out agent))
        {
            Debug.LogError("Agent component not found on this GameObject!");
        }

        if (!TryGetComponent(out agentFOV))
            Debug.LogError($"{nameof(FOV)} component not found on this GameObject!");

        animator = GetComponent<Animator>();
    }

    // Start is called before the first frame update
    protected virtual void Start()
    {
        npcManager = gameObject.GetComponentInParent<NPCManager>();

        foreach (IObserver observer in observers)
        {
            if (observer != null)
            {
                observersNames.Add(observer.ToString());
            }
        }
    }

    // Update is called once per frame
    protected virtual void Update()
    {
        //Debug.Log("Manager" + NPCManager.Instance);
        AnimationController();
    }

    protected virtual void OnEnable()
    {
        health = maxHealth;
        isDead = false;
    }

    protected virtual void OnDisable()
    {
        
    }

    protected virtual void AnimationController()
    {
        if (!agent.agentStop)
        {
            lastDirection = agent.agentVelocity.normalized;
        }

        animator.SetBool("IsStopped", agent.agentStop);
        animator.SetFloat("VelocityX", agent.agentVelocity.x);
        animator.SetFloat("VelocityY", agent.agentVelocity.y);
        animator.SetFloat("LookDirectionX", lastDirection.x);
        animator.SetFloat("LookDirectionY", lastDirection.y);
    }

    //Notify the observers
    public void NotifyObservers(NotificationType type,params (string key, object value)[] parameters)
    {
        ObserverNotification notification = new ObserverNotification(type);
        
        foreach ((string key, object value) param in parameters)
        {
            notification.AddData(param.key, param.value);
        }

        foreach (IObserver observer in observers)
        {
            if (observer != null)
            {
                Debug.Log("Observer: " + observer.ToString());
                observer.OnNotify(notification);
            }

            else
            {
                Debug.Log("Observer is NULL: " + nameof(observer));
            }
        }
    }

    public virtual int CurrentHealth() => health;

    public virtual void Heal(int amount) => health += amount;

    public virtual bool IsDead()
    {
        if (health <= 0 && !isDead)
        {
            isDead = true;
            OnNPCDied.Invoke(this);
            return true;
        }
        return false;
    }

    public virtual void TakeDamage(int amount)
    {
        if (isDead) return;

        health -= amount;
        animator.SetFloat("DamageDirectionX", lastDirection.x);
        animator.SetFloat("DamageDirectionY", lastDirection.y);

        //Notify Observers
        NotifyObservers(NotificationType.WorldUI, ("NPCController", this), ("DamageAmount", amount));
        IsDead();
    }

    //Set Timer to Idle
    public void SetIdleTime() => timer = UnityEngine.Random.Range(2f, maxIdleTime);

    // Observer pattern methods
    public void AddObserver(IObserver observer)
    {
        if (!observers.Contains(observer))
        {
            observers.Add(observer);
        }
    }

    public void RemoveObserver(IObserver observer)
    {
        if (observers.Contains(observer))
        {
            observers.Remove(observer);
        }
    }
}