Mixed luck stopping a coroutine.

Throwing this one out to the community. I’m in the process of building a state machine for a turn-based rpg battle system, but have had trouble getting one of my coroutines to stop running. I am attempting to instruct the PlayerTurn coroutine to run the PlayerAttack function, which generates the damage score, just once before ceasing. To do this I’m employing a flag called coRunning. When I don’t use the NextState function to advance to the next state this seems to work perfectly fine. The damage number outputs once to the console and is not calculated a second time.

When I use the NextState function, however, it outputs 32 times before moving to the next state. That in particular is what I find confusing, it isn’t that it isn’t working, it’s just doing so on a delay. Messing with the WaitForSeconds parameter seems to change the number of times it outputs, but I don’t understand why since the NextState function should be sending me into another state in which PlayerTurn cannot run. Just FYI in case of relevance, BattleProgression is being called in the Update function.

public enum BattleStates {
        START,
        PLAYERTURN,
        ENEMYTURN,
        LOSE,
        WIN
    }

    private BattleStates currentState;
    
    void BattleProgression() {
        switch (currentState) {
            case (BattleStates.START):
                StartCoroutine(BattleStart());
                break;
            case (BattleStates.PLAYERTURN):
                StartCoroutine(PlayerTurn());
                break;
            case (BattleStates.ENEMYTURN):
                StartCoroutine(EnemyTurn());
                break;
            case (BattleStates.LOSE):
                break;
            case (BattleStates.WIN):
                break;
        }
    }

    //establishes a coroutine flag.
    bool coRunning = true;
    
    //Used to advance to the next state once all code is executed and text printed.
    void NextState(BattleStates state) {
        if (newText.textPrinted == newText.textToPrint.Length) {                                  
            currentState = state;
            coRunning = true;
        }
    }

    IEnumerator BattleStart() {
        yield return new WaitForSeconds(0.5f);
        newText.textToPrint = "The " + enemyAttackStat.brownFang.name + " approaches you.";       //a + sign is used to join strings, not a &&.  
        if (coRunning == true) {
            StartCoroutine(newText.PrintTextByLetter());
            coRunning = false;
        }
        NextState(BattleStates.PLAYERTURN);
    }

    IEnumerator PlayerTurn() {
        yield return new WaitForSeconds(0.5f);
        if (coRunning == true) {
            PlayerAttack();
            Debug.Log(playerTotalDamage);
            coRunning = false;
        }
        NextState(BattleStates.ENEMYTURN);
    }

    IEnumerator EnemyTurn() {
        yield return new WaitForSeconds(0.5f);
        if (coRunning == true) {
            EnemyAttack();
            Debug.Log(enemyTotalDamage);
            coRunning = false;
        }
    }

During the time you wait 0.5sec in BattleStart(), Update() will run approximately 30 times (at 60 fps) and each time it will start a new Coroutine. That’s probably not what you intended.