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); } } }