public IEnumerator PerformChargeCoroutine(LivingEntity caster, LivingEntity target, TileScript destination, Action action)
    {
        Ability charge = caster.mySpellBook.GetAbilityByName("Charge");

        // Charge movement
        Action chargeMovement = MovementLogic.Instance.MoveEntity(caster, destination, 6);

        // yield wait until movement complete
        yield return(new WaitUntil(() => chargeMovement.ActionResolved() == true));

        // Charge attack
        caster.StartCoroutine(caster.AttackMovement(target));

        // Calculate hit or miss
        bool attackSuccesful = CombatLogic.Instance.CalculateIfAttackHitOrMiss(caster, target);

        if (attackSuccesful)
        {
            CombatLogic.Instance.HandleDamage(CombatLogic.Instance.CalculateDamage(WeaponLogic.Instance.CalculateRandomWeaponDamageValue(caster.myMeleeWeapon), target, caster, charge.abilityDamageType), caster, target);
        }
        else
        {
            // TO DO IN FUTURE: method here should be something like CombatLogic.Instance.HandleMiss()
            StartCoroutine(VisualEffectManager.Instance.CreateStatusEffect(target.transform.position, "Miss!", false));
        }

        OnAbilityUsed(charge, caster);
        action.actionResolved = true;
    }
    public IEnumerator PerformTwinStrikeCoroutine(LivingEntity attacker, LivingEntity victim)
    {
        Ability twinStrike = attacker.mySpellBook.GetAbilityByName("Twin Strike");

        StartCoroutine(attacker.AttackMovement(victim));
        CombatLogic.Instance.HandleDamage(CombatLogic.Instance.CalculateDamage(twinStrike.abilityPrimaryValue, victim, attacker, twinStrike.abilityDamageType), attacker, victim);
        yield return(new WaitForSeconds(0.3f));

        // check to make sure the enemy wasnt killed by the first attack
        if (victim.currentHealth > 0 && victim != null)
        {
            StartCoroutine(attacker.AttackMovement(victim));
            CombatLogic.Instance.HandleDamage(CombatLogic.Instance.CalculateDamage(twinStrike.abilityPrimaryValue, victim, attacker, twinStrike.abilityDamageType), attacker, victim);
        }

        OnAbilityUsed(twinStrike, attacker);
    }
    public IEnumerator PerformChemicalReactionCoroutine(LivingEntity caster, LivingEntity victim)
    {
        Ability chemicalReaction = caster.mySpellBook.GetAbilityByName("Chemical Reaction");

        StartCoroutine(caster.AttackMovement(victim));
        victim.ModifyPoison(victim.poisonStacks);
        OnAbilityUsed(chemicalReaction, caster);
        yield return(null);
    }
    public IEnumerator PerformSnipeCoroutine(LivingEntity attacker, LivingEntity victim)
    {
        Ability snipe = attacker.mySpellBook.GetAbilityByName("Snipe");

        StartCoroutine(attacker.AttackMovement(victim));
        CombatLogic.Instance.HandleDamage(CombatLogic.Instance.CalculateDamage(snipe.abilityPrimaryValue, victim, attacker, snipe.abilityDamageType), attacker, victim);
        OnAbilityUsed(snipe, attacker);
        yield return(null);
    }
    public IEnumerator PerformFireBallCoroutine(LivingEntity attacker, LivingEntity victim)
    {
        Ability fireball = attacker.mySpellBook.GetAbilityByName("Fire Ball");

        attacker.StartCoroutine(attacker.AttackMovement(victim));
        CombatLogic.Instance.HandleDamage(CombatLogic.Instance.CalculateDamage(fireball.abilityPrimaryValue, victim, attacker, fireball.abilityDamageType), attacker, victim);
        OnAbilityUsed(fireball, attacker);
        yield return(null);
    }
    public IEnumerator PerformPoisonDartCoroutine(LivingEntity caster, LivingEntity victim)
    {
        Ability poisonDart = caster.mySpellBook.GetAbilityByName("Poison Dart");

        StartCoroutine(caster.AttackMovement(victim));
        victim.ModifyPoison(poisonDart.abilitySecondaryValue);
        OnAbilityUsed(poisonDart, caster);
        yield return(null);
    }
    public IEnumerator PerformChaosBoltCoroutine(LivingEntity attacker, LivingEntity victim)
    {
        Ability chaosBolt = attacker.mySpellBook.GetAbilityByName("Chaos Bolt");

        attacker.StartCoroutine(attacker.AttackMovement(victim));
        CombatLogic.Instance.HandleDamage(CombatLogic.Instance.CalculateDamage(chaosBolt.abilityPrimaryValue, victim, attacker, chaosBolt.abilityDamageType), attacker, victim);
        //victim.ApplyKnockDown();
        victim.myPassiveManager.ModifyExposed(chaosBolt.abilitySecondaryValue);
        OnAbilityUsed(chaosBolt, attacker);
        yield return(null);
    }
    public IEnumerator PerformFrostBoltCoroutine(LivingEntity caster, LivingEntity victim)
    {
        Ability frostbolt = caster.mySpellBook.GetAbilityByName("Frost Bolt");

        caster.StartCoroutine(caster.AttackMovement(victim));
        CombatLogic.Instance.HandleDamage(CombatLogic.Instance.CalculateDamage(frostbolt.abilityPrimaryValue, victim, caster, frostbolt.abilityDamageType), caster, victim);

        victim.ApplyPinned();
        OnAbilityUsed(frostbolt, caster);
        yield return(null);
    }
    public IEnumerator PerformRockTossCoroutine(LivingEntity caster, LivingEntity victim)
    {
        Ability rockToss = caster.mySpellBook.GetAbilityByName("Rock Toss");

        // Attack
        StartCoroutine(caster.AttackMovement(victim));
        CombatLogic.Instance.HandleDamage(CombatLogic.Instance.CalculateDamage(rockToss.abilityPrimaryValue, victim, caster, rockToss.abilityDamageType), caster, victim);

        // Knockback
        MovementLogic.Instance.KnockBackEntity(caster, victim, rockToss.abilitySecondaryValue);
        OnAbilityUsed(rockToss, caster);
        yield return(null);
    }
    public IEnumerator PerformFreeStrikeCoroutine(LivingEntity attacker, LivingEntity victim, Action action)
    {
        StartCoroutine(VisualEffectManager.Instance.CreateStatusEffect(attacker.transform.position, "Free Strike", false));
        yield return(new WaitForSeconds(0.5f));

        Ability strike = attacker.mySpellBook.GetAbilityByName("Strike");

        attacker.StartCoroutine(attacker.AttackMovement(victim));
        CombatLogic.Instance.HandleDamage(CombatLogic.Instance.CalculateDamage(strike.abilityPrimaryValue, victim, attacker, strike.abilityDamageType), attacker, victim);
        yield return(new WaitForSeconds(1f));

        action.actionResolved = true;
    }
    public IEnumerator PerformAcidSpitCoroutine(LivingEntity caster, TileScript location)
    {
        Ability acidSpit = caster.mySpellBook.GetAbilityByName("Acid Spit");

        if (caster.myCurrentTarget != null)
        {
            StartCoroutine(caster.AttackMovement(caster.myCurrentTarget));
        }
        CombatLogic.Instance.CreateAoEAttackEvent(caster, acidSpit, location, acidSpit.abilitySecondaryValue, false, false);
        //CombatLogic.Instance.HandleDamage(CombatLogic.Instance.CalculateDamage(acidSpit.abilityPrimaryValue, victim, caster, acidSpit.abilityDamageType), caster, victim);
        OnAbilityUsed(acidSpit, caster);
        yield return(null);
    }
    public IEnumerator PerformCrushingBlowCoroutine(LivingEntity caster, LivingEntity target)
    {
        Ability crushingBlow = caster.mySpellBook.GetAbilityByName("Crushing Blow");

        caster.StartCoroutine(caster.AttackMovement(target));

        CombatLogic.Instance.HandleDamage(CombatLogic.Instance.CalculateDamage(crushingBlow.abilityPrimaryValue, target, caster, crushingBlow.abilityDamageType), caster, target);

        target.ApplyStunned();

        OnAbilityUsed(crushingBlow, caster);

        yield return(null);
    }
    public IEnumerator PerformSmashCoroutine(LivingEntity attacker, LivingEntity victim)
    {
        Debug.Log("Performing Smash...");
        Ability smash = attacker.mySpellBook.GetAbilityByName("Smash");

        // Attack
        StartCoroutine(attacker.AttackMovement(victim));
        CombatLogic.Instance.HandleDamage(CombatLogic.Instance.CalculateDamage(smash.abilityPrimaryValue, victim, attacker, smash.abilityDamageType), attacker, victim);

        // Knock back.
        MovementLogic.Instance.KnockBackEntity(attacker, victim, smash.abilitySecondaryValue);
        OnAbilityUsed(smash, attacker);
        yield return(null);
    }
    public IEnumerator PerformPrimalBlastCoroutine(LivingEntity attacker, LivingEntity target)
    {
        Debug.Log("Performing Primal Blast...");
        Ability primalBlast = attacker.mySpellBook.GetAbilityByName("Primal Blast");

        StartCoroutine(attacker.AttackMovement(target));

        CombatLogic.Instance.HandleDamage(CombatLogic.Instance.CalculateDamage(primalBlast.abilityPrimaryValue, target, attacker, AbilityDataSO.DamageType.Physical), attacker, target);
        yield return(new WaitForSeconds(0.2f));

        CombatLogic.Instance.HandleDamage(CombatLogic.Instance.CalculateDamage(primalBlast.abilityPrimaryValue, target, attacker, AbilityDataSO.DamageType.Magic), attacker, target);
        yield return(new WaitForSeconds(0.2f));

        OnAbilityUsed(primalBlast, attacker);
    }
    public IEnumerator PerformSliceAndDiceCoroutine(LivingEntity caster, LivingEntity victim, int attacks)
    {
        int     timesAttacked = 0;
        Ability sliceAndDice  = caster.mySpellBook.GetAbilityByName("Slice And Dice");

        caster.ModifyCurrentAP(-attacks);

ShotStart:
        StartCoroutine(caster.AttackMovement(victim));
        CombatLogic.Instance.HandleDamage(CombatLogic.Instance.CalculateDamage(sliceAndDice.abilityPrimaryValue, victim, caster, sliceAndDice.abilityDamageType), caster, victim);
        yield return(new WaitForSeconds(0.3f));

        timesAttacked++;
        if (victim.currentHealth > 0 && victim != null && timesAttacked < attacks)
        {
            goto ShotStart;
        }

        OnAbilityUsed(sliceAndDice, caster);
    }
    public IEnumerator PerformStrikeCoroutine(LivingEntity attacker, LivingEntity victim)
    {
        Ability strike = attacker.mySpellBook.GetAbilityByName("Strike");

        attacker.StartCoroutine(attacker.AttackMovement(victim));
        bool attackSuccesful = CombatLogic.Instance.CalculateIfAttackHitOrMiss(attacker, victim);

        if (attackSuccesful)
        {
            CombatLogic.Instance.HandleDamage(CombatLogic.Instance.CalculateDamage(WeaponLogic.Instance.CalculateRandomWeaponDamageValue(attacker.myMeleeWeapon), victim, attacker, strike.abilityDamageType), attacker, victim);
        }
        else
        {
            // TO DO IN FUTURE: method here should be something like CombatLogic.Instance.HandleMiss()
            StartCoroutine(VisualEffectManager.Instance.CreateStatusEffect(victim.transform.position, "Miss!", false));
        }

        OnAbilityUsed(strike, attacker);
        yield return(null);
    }
    public IEnumerator PerformOverwatchShotCoroutine(LivingEntity attacker, LivingEntity victim, Action action)
    {
        yield return(new WaitForSeconds(1f));

        Action cameraMovement = CameraManager.Instance.mainCamera.GetComponent <CinemachineCameraController>().MoveToOverwatchViewLocation(victim, attacker);

        yield return(new WaitUntil(() => cameraMovement.ActionResolved() == true));

        StartCoroutine(VisualEffectManager.Instance.CreateStatusEffect(attacker.transform.position, "Overwatch!", false));
        attacker.myPassiveManager.ModifyOverwatch(-1);
        yield return(new WaitForSeconds(1f));

        Ability shoot = attacker.mySpellBook.GetAbilityByName("Shoot");

        attacker.StartCoroutine(attacker.AttackMovement(victim));
        // Apply overwatch aim penalty
        attacker.ModifyCurrentAim(-20);

        bool attackSuccesful = CombatLogic.Instance.CalculateIfAttackHitOrMiss(attacker, victim);

        if (attackSuccesful)
        {
            CombatLogic.Instance.HandleDamage(CombatLogic.Instance.CalculateDamage(WeaponLogic.Instance.CalculateRandomWeaponDamageValue(attacker.myRangedWeapon), victim, attacker, shoot.abilityDamageType), attacker, victim);
        }
        else
        {
            // TO DO IN FUTURE: method here should be something like CombatLogic.Instance.HandleMiss()
            StartCoroutine(VisualEffectManager.Instance.CreateStatusEffect(victim.transform.position, "Miss!", false));
        }

        // remove overwatch aim penalty
        attacker.ModifyCurrentAim(20);
        yield return(new WaitForSeconds(1f));

        action.actionResolved = true;
    }
    public IEnumerator PerformChainLightningCoroutine(LivingEntity attacker, LivingEntity target)
    {
        Debug.Log("Performing Chain Lightning...");
        Ability chainLightning = attacker.mySpellBook.GetAbilityByName("Chain Lightning");

        StartCoroutine(attacker.AttackMovement(target));

        LivingEntity currentTarget  = target;
        LivingEntity previousTarget = target;

        CombatLogic.Instance.HandleDamage(CombatLogic.Instance.CalculateDamage(chainLightning.abilityPrimaryValue, target, attacker, chainLightning.abilityDamageType), attacker, target);
        yield return(new WaitForSeconds(0.2f));

        for (int lightningJumps = 0; lightningJumps < chainLightning.abilitySecondaryValue; lightningJumps++)
        {
            List <TileScript> adjacentTiles = LevelManager.Instance.GetTilesWithinRange(1, currentTarget.TileCurrentlyOn);

            foreach (Enemy enemy in EnemyManager.Instance.allEnemies)
            {
                if (adjacentTiles.Contains(enemy.TileCurrentlyOn))
                {
                    previousTarget = currentTarget;
                    currentTarget  = enemy;
                }
            }

            if (previousTarget != currentTarget)
            {
                CombatLogic.Instance.HandleDamage(CombatLogic.Instance.CalculateDamage(chainLightning.abilityPrimaryValue, currentTarget, attacker, chainLightning.abilityDamageType), attacker, currentTarget);
                yield return(new WaitForSeconds(0.2f));
            }
        }

        OnAbilityUsed(chainLightning, attacker);
        yield return(null);
    }