Newer
Older
TheVengeance-Project-IADE-Unity2D / Assets / Scripts / Player / PlayerHUD.cs
using MyCollections.Stats;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

public class PlayerHUD : MonoBehaviour
{
    //HUD Bars
    [Header("Health Bar References")]
    [SerializeField] private Image healthBar;
    [SerializeField] private TextMeshProUGUI healthAmountTMP;

    [Header("Mana Bar References")]
    [SerializeField] private Image manaBar;
    [SerializeField] private TextMeshProUGUI manaAmountTMP;

    [Header("XP Bar References")]
    [SerializeField] private Image xpBarImage;
    [SerializeField] private TextMeshProUGUI currentXpAmountTMP;
    [SerializeField] private TextMeshProUGUI currentLevelMaxXpAmountTMP;
    [SerializeField] private TextMeshProUGUI lvlAmountTMP;

    [Header("Currency")]
    [SerializeField] private TextMeshProUGUI goldAmount;

    [Header("Stats Modifiers: ")]
    [SerializeField] private GameObject statsModifierParent;
    [SerializeField] private GameObject modifierPrefab;
    [SerializeField] private float spacingX = 2f;
    [SerializeField] private float timerAnim = 1.2f;

    [SerializeField] private float barSpeed = 0.5f;

    private List<Tuple<GameObject, StatModifier>> modifiers = new List<Tuple<GameObject, StatModifier>>();

    //Player Controller
    PlayerController controller;

    //GameManager
    GameManager gameManager;

    private void Awake()
    {

    }

    // Start is called before the first frame update
    void Start()
    {
        gameManager = GameManager.Instance;
        controller = gameManager.PlayerController;

        goldAmount.text = gameManager.PlayerGold.ToString();
        lvlAmountTMP.text = gameManager.PlayerLevel.ToString();
        currentXpAmountTMP.text = gameManager.PlayerCurrentXP.ToString() + " XP ";
        currentLevelMaxXpAmountTMP.text = gameManager.TargetLevelXP.ToString() + " XP ";

        controller.OnHealthChanged += UpdateHealthBar;
        controller.OnManaChanged += UpdateManaBar;
        controller.Stats.Mediator.OnModifierAdded += AddStatModifierUI;
        controller.Stats.Mediator.OnModifierRemoved += RemoveStateModifierUI;

        gameManager.OnXpGained += UpdateXPBar;
        gameManager.OnLevelUp += UpdateLvlNumber;
        gameManager.OnGoldAdded += UpdateGoldAmount;
        
    }

    private void OnDestroy()
    {
        controller.OnHealthChanged -= UpdateHealthBar;
        controller.OnManaChanged -= UpdateManaBar;
    }

    // Update is called once per frame
    void Update()
    {
    }

    private void UpdateHealthBar(int newHealth)
    {
        // Start coroutine for smooth health bar animation
        StartCoroutine(SmoothHealthBarUpdate(newHealth));
    }

    public void UpdateManaBar(float newMana)
    {
        // Clamp the newMana to avoid exceeding MaxMana
        newMana = Mathf.Clamp(newMana, 0, controller.MaxMana);

        // Update the mana bar's fill amount directly
        manaBar.fillAmount = newMana / controller.MaxMana;

        // Update the text to reflect the current mana
        manaAmountTMP.text = $"{(int)newMana} / {controller.MaxMana}";
        manaAmountTMP.color = Color.Lerp(Color.red, Color.white, manaBar.fillAmount);
    }

    public void UpdateXPBar(int newXp)
    {
        StartCoroutine(SmoothUpdateXPBar(newXp));
    }

    private IEnumerator SmoothUpdateXPBar(int newXp)
    {
        float targetFillAmount = (float)newXp / gameManager.TargetLevelXP;
        while (Mathf.Abs(xpBarImage.fillAmount - targetFillAmount) > 0.01f)
        {
            xpBarImage.fillAmount = Mathf.Lerp(xpBarImage.fillAmount, targetFillAmount, Time.deltaTime * barSpeed);
            currentXpAmountTMP.text = $"{(int)(xpBarImage.fillAmount * gameManager.TargetLevelXP)} XP";
            yield return null;
        }

        // Ensure the final value is accurate after the loop
        xpBarImage.fillAmount = targetFillAmount;
        currentXpAmountTMP.text = $"{newXp} XP";
    }

    public void UpdateLvlNumber(int lvl)
    {
        lvlAmountTMP.text = lvl.ToString(); //Updates the level
        currentLevelMaxXpAmountTMP.text = gameManager.TargetLevelXP.ToString() + " XP "; //Updates the current level xp amount
    }

    private void UpdateGoldAmount(int amount)
    {
        int targetAmount = amount;
        int currentAmount = gameManager.PlayerGold;

        StartCoroutine(SmoothGoldTransition(targetAmount, currentAmount));
    }

    //Smoothly interpolates the new amount of gold
    private IEnumerator SmoothGoldTransition(int targetAmount, int currentAmount)
    {
        currentAmount = int.Parse(goldAmount.text);

        // Smooth transition over time
        float duration = 1f; // Duration for the transition
        float elapsed = 0f;

        while (elapsed < duration)
        {
            elapsed += Time.deltaTime;
            int result = (int)Mathf.Lerp(currentAmount, targetAmount, elapsed / duration);
            goldAmount.text = result.ToString();
            yield return null;
        }

        // Ensure the final value is set
        goldAmount.text = targetAmount.ToString();
    }

    // Coroutine for smooth health bar animation
    private IEnumerator SmoothHealthBarUpdate(int newHealth)
    {

        float targetFillAmount = (float)newHealth / 100;  // Normalize new health value (0 to 1)
        while (Mathf.Abs(healthBar.fillAmount - targetFillAmount) > 0.01f)
        {
            healthBar.fillAmount = Mathf.Lerp(healthBar.fillAmount, targetFillAmount, Time.deltaTime * barSpeed);
            healthAmountTMP.text = $"{(int)(healthBar.fillAmount * 100)} / {controller.MaxHealth}";  // Update text to match normalized value
            yield return null;
        }

        // Ensure the fill amount is set exactly to the target at the end
        healthBar.fillAmount = targetFillAmount;
        healthAmountTMP.text = $"{newHealth} / {controller.MaxHealth}";  // Ensure final text update matches target health
    }

    private void AddStatModifierUI(StatModifier modifier)
    {
        RectTransform parentRt = statsModifierParent.GetComponent<RectTransform>();
        Vector2 parentSize = parentRt.rect.size;

        // Instantiate and add the stat modifier to the UI
        GameObject instance = Instantiate(modifierPrefab, parentRt);
        RectTransform instanceRt = instance.GetComponent<RectTransform>();
        instanceRt.anchoredPosition = new Vector2(parentSize.x / 2, 0); // Place new modifier at initial position
        modifiers.Insert(0, new Tuple<GameObject, StatModifier>(instance, modifier));

        // Recalculate positions for all modifiers
        RepositionModifiers();
    }

    // Repositions all stat modifiers sequentially
    private void RepositionModifiers()
    {
        float currentXOffset = 0f; // Start from the parent's right edge
        for (int i = 0; i < modifiers.Count; i++)
        {
            RectTransform rtModifier = modifiers[i].Item1.GetComponent<RectTransform>();
            Vector2 size = rtModifier.rect.size;

            // Calculate the new anchored position
            Vector2 newPosition = new Vector2(currentXOffset - (size.x / 2), 0);

            // Move the modifier with animation
            StartCoroutine(MoveTheStatModifierObjectAnim(rtModifier, newPosition));

            // Update the current offset for the next modifier
            currentXOffset -= size.x + spacingX;
        }
    }

    // Handles the Stat Modifiers UI elements' animations
    private IEnumerator MoveTheStatModifierObjectAnim(RectTransform modifierRt, Vector2 finalPosition)
    {
        float elapsedTime = 0f;
        Vector2 startPosition = modifierRt.anchoredPosition;

        while (elapsedTime < timerAnim)
        {
            elapsedTime += Time.deltaTime;
            float t = elapsedTime / timerAnim;
            modifierRt.anchoredPosition = Vector2.Lerp(startPosition, finalPosition, t);
            yield return null;
        }
        modifierRt.anchoredPosition = finalPosition;
    }

    private void RemoveStateModifierUI(StatModifier modifier)
    {
        // Find the Tuple to remove
        Tuple<GameObject, StatModifier> result = modifiers.FirstOrDefault(tuple => tuple.Item2.Equals(modifier));

        if (result == null)
        {
            Debug.LogError("Modifier not found in the list.");
            return;
        }

        GameObject go = result.Item1;

        if (go == null)
        {
            Debug.LogError("GameObject doesn't exist");
            return;
        }

        RectTransform modifierRt = go.GetComponent<RectTransform>();
        if (modifierRt == null)
        {
            Debug.LogError("RectTransform not found on the GameObject.");
            return;
        }

        // Visual effect: Move up before removing
        Vector2 finalPosition = modifierRt.anchoredPosition + new Vector2(0, 50);

        StartCoroutine(RemoveModifierAfterAnimation(result, modifierRt, finalPosition));
    }

    // Coroutine to handle animation and destruction
    private IEnumerator RemoveModifierAfterAnimation(Tuple<GameObject, StatModifier> result, RectTransform modifierRt, Vector2 finalPosition)
    {
        yield return MoveTheStatModifierObjectAnim(modifierRt, finalPosition);

        // Remove the modifier from the list after animation completes
        modifiers.Remove(result);

        // Destroy the GameObject after it's no longer needed
        Destroy(result.Item1);

        // Reposition remaining modifiers
        RepositionModifiers();
    }

}