Coroutine being called multiple times

Hello everyone,

I’m facing an issue in my Unity project related to shotgun reloading, and I can not seem to figure it out, been trying last three days with no progress which is driving my head in. I’ve implemented a ReloadShotgun coroutine that handles the reloading process, but it seems to be consuming more shotgun shells than necessary due to the reloadshotgun courtine is being called several times.

Here’s a breakdown:
The ReloadShotgun coroutine, responsible for reloading the shotgun, is being called directly after it is done. The coroutine starts twice, eventhough the shotgun has already been loaded once.

From the console, both logged at the same time:

ReloadShotgun coroutine completed.
UnityEngine.Debug:Log (object)
Shotgun/d__14:MoveNext () (at Assets/Scripts/Collectables/Shotgun.cs:119)
UnityEngine.MonoBehaviour:StartCoroutine (System.Collections.IEnumerator)
Shotgun:Update () (at Assets/Scripts/Collectables/Shotgun.cs:44)

Reload key pressed. Starting ReloadShotgun coroutine.
UnityEngine.Debug:Log (object)
Shotgun:Update () (at Assets/Scripts/Collectables/Shotgun.cs:42)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;

public class Shotgun : MonoBehaviour
{
    public CollectableType requiredAmmoType = CollectableType.SHOTGUNSHELLS;
    public int ammoConsumedPerShot = 1;
    public GameObject bulletPrefab;
    private int loadedShells = 0;
    private Transform firePoint;
    private TextMeshProUGUI ammoHintText;
    private bool isReloading = false;
    private bool isReloadCoroutineRunning = false;
    private bool reloadKeyPressed = false;
    private Coroutine updateAmmoHintCoroutine;

    void Start()
    {
        FindAmmoText();
        HideAmmoHintText();
        FindFirePoint();
    }

    void Update()
    {
        if (IsShotgunEquipped())
        {
            if (Input.GetButtonDown("Fire1"))
            {
                if (CanFire())
                {
                    SpawnBullets();
                    loadedShells--;
                    UpdateAmmoHintText();
                }
            }

            if (Input.GetKeyDown(KeyCode.R) && !reloadKeyPressed && !isReloadCoroutineRunning)
            {
                Debug.Log("Reload key pressed. Starting ReloadShotgun coroutine.");
                reloadKeyPressed = true;
                StartCoroutine(ReloadShotgun());
            }
            else if (Input.GetKeyUp(KeyCode.R))
            {
                reloadKeyPressed = false;
                Debug.Log("R released");
            }
        }
        else
        {
            HideAmmoHintText();
        }
    }

    Player FindPlayer()
    {
        GameObject playerObject = GameObject.FindGameObjectWithTag("Player");
        return playerObject != null ? playerObject.GetComponent<Player>() : null;
    }

    bool IsShotgunEquipped()
    {
        Player player = FindPlayer();
        return player != null && player.GetEquippedSlot() >= 0 && player.inventory.slots[player.GetEquippedSlot()].type == CollectableType.SHOTGUN;
    }

    IEnumerator ReloadShotgun()
    {
        Debug.Log("ReloadShotgun coroutine started.");
        if (isReloading || isReloadCoroutineRunning)
        {
            yield return null;
        }

        isReloadCoroutineRunning = true;
        isReloading = true;

        Player player = FindPlayer();
        if (player != null)
        {
            if (loadedShells >= 4)
            {
                Debug.Log("Shotgun is already fully loaded.");
            }
            else
            {
                Debug.Log($"Starting shotgun reload. Current loadedShells: {loadedShells}");

                foreach (Inventory.Slot slot in player.inventory.slots)
                {
                    if (slot.type == requiredAmmoType && slot.count > 0 && loadedShells < 4)
                    {
                        Debug.Log($"Condition met: Ammo type is {requiredAmmoType}, count is {slot.count}, and loaded shells are {loadedShells}. Loading shells...");
                        int shellsToLoad = Mathf.Min(slot.count, 4 - loadedShells);
                        Debug.Log($"Calculating shells to load. Available ammo: {slot.count}, Loaded shells: {loadedShells}. Shells to load: {shellsToLoad}");

                        if (loadedShells + shellsToLoad <= 4)
                        {
                            Debug.Log($"Condition met: Loaded shells ({loadedShells}) + Shells to load ({shellsToLoad}) <= 4");
                            loadedShells += shellsToLoad;
                            yield return StartCoroutine(ConsumeAmmoCoroutine(slot, shellsToLoad));
                            Debug.Log($"Loaded Shells: {loadedShells}");
                            UpdateAmmoHintText();
                        }
                        else
                        {
                            Debug.Log("Cannot load more shells. Shotgun is already full.");
                            break;  // Break the loop if the shotgun is already full
                        }
                    }
                }

                Debug.Log($"Shotgun reload completed. Final loadedShells: {loadedShells}");
            }
        }
        Debug.Log("ReloadShotgun coroutine completed.");

        isReloading = false;
        isReloadCoroutineRunning = false;
    }

    bool CanFire()
    {
        return loadedShells >= ammoConsumedPerShot;
    }

    IEnumerator ConsumeAmmoCoroutine(Inventory.Slot ammoSlot, int amount)
    {
        if (ammoSlot == null)
        {
            Debug.Log("Ammo slot reference is null.");
            yield break; // exit the coroutine
        }

        int ammoToConsume = Mathf.Min(ammoSlot.count, amount / ammoConsumedPerShot);
        ammoSlot.count -= ammoToConsume;

        amount -= ammoToConsume * ammoConsumedPerShot;

        Debug.Log($"Consumed {ammoToConsume} {requiredAmmoType}. Remaining amount: {amount}. Remaining slot count: {ammoSlot.count}");

        if (amount == 0)
        {
            Debug.Log("Ammo consumption is complete. Exiting coroutine.");
            yield break; // exit the coroutine if ammo consumption is complete
        }

        // introduce a short delay (you can adjust the time)
        yield return new WaitForSeconds(1f);
    }

    void SpawnBullets()
    {
        if (bulletPrefab != null && firePoint != null)
        {
            Vector3 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);

            Vector2 direction = (mousePosition - firePoint.position).normalized;

            float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;

            Instantiate(bulletPrefab, firePoint.position, Quaternion.Euler(0, 0, angle));
        }
    }

    void UpdateAmmoHintText()
    {
        if (ammoHintText != null)
        {
            string currentAmmoText = $"{loadedShells}/{4}";

            if (ammoHintText.text != currentAmmoText)
            {
                if (updateAmmoHintCoroutine != null)
                {
                    StopCoroutine(updateAmmoHintCoroutine);
                }

                updateAmmoHintCoroutine = StartCoroutine(DelayedUpdateAmmoHintText(currentAmmoText));
            }
        }
    }

    IEnumerator DelayedUpdateAmmoHintText(string newText)
    {
        yield return new WaitForSeconds(0.1f);

        ammoHintText.text = newText;

        ammoHintText.gameObject.SetActive(true);
        updateAmmoHintCoroutine = null; // Reset the coroutine reference
    }

    void FindAmmoText()
    {
        if (ammoHintText == null)
        {
            Canvas canvas = GameObject.FindObjectOfType<Canvas>();
            if (canvas != null)
            {
                GameObject ammoHintObject = canvas.transform.Find("AmmoHint").gameObject;

                if (ammoHintObject != null)
                {
                    ammoHintText = ammoHintObject.GetComponentInChildren<TextMeshProUGUI>();
                }
                else
                {
                    Debug.LogWarning("AmmoHintObject not found within the canvas!");
                }
            }
            else
            {
                Debug.LogWarning("Canvas not found!");
            }
        }
    }

    void FindFirePoint()
    {
        Player player = FindPlayer();

        if (player != null)
        {
            firePoint = player.transform.Find("FirePoint");

            if (firePoint == null)
            {
                Debug.LogError("FirePoint not found as a child of the player!");
            }
        }
    }

    void HideAmmoHintText()
    {
        if (ammoHintText != null)
        {
            ammoHintText.gameObject.SetActive(false);
        }
    }
}