private void CalculateShowForecast(TacticsMove attacker, TacticsMove defender)
    {
        bool         isDamage = (currentMode.value == ActionMode.ATTACK);
        BattleAction act1     = new BattleAction(true, isDamage, attacker, defender);

        if (isDamage)
        {
            BattleAction act2     = new BattleAction(false, true, defender, attacker);
            int          distance = MapCreator.DistanceTo(defender, walkTile.value);
            int          atk      = (attacker.GetWeapon().InRange(distance)) ? act1.GetDamage() : -1;
            int          def      = (defender.GetWeapon() != null && defender.GetWeapon().InRange(distance)) ? act2.GetDamage() : -1;
            int          spd      = attacker.stats.spd - defender.stats.spd;
            bool         atkWeak  = attacker.stats.IsWeakAgainst(defender.stats.GetWeapon());
            bool         defWeak  = defender.stats.IsWeakAgainst(attacker.stats.GetWeapon());
            ShowForecast(attacker, defender, atk, def, spd, act1.GetAdvantage(), atkWeak, defWeak);
        }
        else
        {
            ShowHealForecast(attacker, defender, act1.GetHeals());
        }
    }
    private IEnumerator ActionLoop()
    {
        state = State.ACTION;

        for (int i = 0; i < actions.Count; i++)
        {
            BattleAction act = actions[i];
            if (act.type == BattleAction.Type.DAMAGE && act.attacker.inventory.GetFirstUsableItemTuple(ItemCategory.WEAPON).currentCharges <= 0)
            {
                continue;                 //Broken weapon
            }
            if (act.type != BattleAction.Type.DAMAGE && act.attacker.inventory.GetFirstUsableItemTuple(ItemCategory.SUPPORT).currentCharges <= 0)
            {
                continue;                 //Broken staff
            }

            yield return(new WaitForSeconds(1f * currentGameSpeed.value));

            BattleAnimator.AnimationInfo animationInfo = new BattleAnimator.AnimationInfo();

            // Deal damage
            bool isCrit = false;
            if (act.type == BattleAction.Type.DAMAGE)
            {
                int damage = act.AttemptAttack(useTrueHit.value);
                if (damage != -1 && act.AttemptCrit())
                {
                    damage = (int)(damage * BattleCalc.CRIT_MODIFIER);
                    isCrit = true;
                }
                act.defender.TakeDamage(damage, isCrit);
                BattleAnimator.HitType hitType = (damage < 0) ? BattleAnimator.HitType.MISS : (isCrit) ? BattleAnimator.HitType.CRIT : BattleAnimator.HitType.NORMAL;
                animationInfo.side       = act.side;
                animationInfo.weaponType = act.weaponAtk.weaponType;
                animationInfo.hitType    = hitType;
                animationInfo.leathal    = !act.defender.IsAlive();
                animationInfo.damage     = damage;

                if (damage > 0)
                {
                    if (act.side == AttackSide.LEFT)
                    {
                        _attackerDealtDamage = true;
                    }
                    else
                    {
                        _defenderDealtDamage = true;
                    }
                }

                act.attacker.inventory.ReduceItemCharge(ItemCategory.WEAPON);
            }
            else
            {
                // Heal or buff
                if (act.staffAtk.weaponType == WeaponType.MEDKIT)
                {
                    int health = act.GetHeals();
                    act.defender.TakeHeals(health);
                    //StartCoroutine(DamageDisplay(act.leftSide, health, false, false));
                }
                else if (act.staffAtk.weaponType == WeaponType.BARRIER)
                {
                    act.defender.ReceiveBuff(act.attacker.GetEquippedWeapon(ItemCategory.SUPPORT).boost, true, true);
                }
                act.attacker.inventory.ReduceItemCharge(ItemCategory.SUPPORT);
                _attackerDealtDamage = true;
            }

            //Animate!!
            waitForAnimation = true;
            battleAnimator.PlayAttack(animationInfo);
            while (waitForAnimation)
            {
                yield return(null);
            }

            //Check Death
            // Debug.Log("Check death");
            if (!act.defender.IsAlive())
            {
                yield return(new WaitForSeconds(1f * currentGameSpeed.value));

                if (act.defender.stats.charData.deathQuote != null)
                {
                    Debug.Log("Death quote");
                    state = State.DEATH;
                    dialogueMode.value    = (int)DialogueMode.QUOTE;
                    currentDialogue.value = dialogue = act.defender.stats.charData.deathQuote;
                    showDialogueEvent.Invoke();
                    lockControls.value = false;

                    while (state == State.DEATH)
                    {
                        yield return(null);
                    }
                }
                break;
            }
        }

        //Handle exp
        yield return(StartCoroutine(ShowExpGain()));

        //Broken weapons
        yield return(StartCoroutine(HandleBrokenWeapons()));

        //Drop Items
        if (!actions[0].attacker.IsAlive() && actions[0].attacker.faction == Faction.ENEMY)
        {
            yield return(StartCoroutine(DropItems(actions[0].attacker, actions[0].defender)));
        }
        else if (!actions[0].defender.IsAlive() && actions[0].defender.faction == Faction.ENEMY)
        {
            yield return(StartCoroutine(DropItems(actions[0].defender, actions[0].attacker)));
        }

        //Give debuffs
        if (actions[0].type == BattleAction.Type.DAMAGE)
        {
            actions[0].attacker.ActivateSkills(SkillActivation.POSTCOMBAT, actions[0].defender);
            actions[0].defender.ActivateSkills(SkillActivation.POSTCOMBAT, actions[0].attacker);
            actions[0].defender.stats.fatigueAmount = Mathf.Min(actions[0].defender.fatigueCap, actions[0].defender.stats.fatigueAmount + 1);
        }

        //Clean up
        state = State.INIT;
        currentAction.value = ActionMode.NONE;
        battleAnimator.CleanupScene();
        battleAnimationObject.SetActive(false);
        uiCanvas.SetActive(true);
        actions[0].attacker.EndSkills(SkillActivation.INITCOMBAT, actions[0].defender);
        actions[0].attacker.EndSkills(SkillActivation.PRECOMBAT, actions[0].defender);
        actions[0].defender.EndSkills(SkillActivation.PRECOMBAT, actions[0].attacker);
        actions.Clear();
        if (currentTurn.value == Faction.PLAYER)
        {
            lockControls.value = false;
        }
        _currentCharacter.End();
        _currentCharacter = null;

        yield return(new WaitForSeconds(0.5f * currentGameSpeed.value));

        //Music
        stopTransitionMusicEvent.Invoke();

        //Send game finished
        battleFinishedEvent.Invoke();
    }
    private IEnumerator ActionLoop()
    {
        leftDamageObject.SetActive(false);
        rightDamageObject.SetActive(false);
        leftTransform.GetComponent <SpriteRenderer>().sprite  = actions[0].attacker.stats.battleSprite;
        rightTransform.GetComponent <SpriteRenderer>().sprite = actions[0].defender.stats.battleSprite;
        leftTransform.GetComponent <SpriteRenderer>().color   = Color.white;
        rightTransform.GetComponent <SpriteRenderer>().color  = Color.white;

        for (int i = 0; i < actions.Count; i++)
        {
            BattleAction act              = actions[i];
            Transform    attackTransform  = (!useBattleAnimations.value) ? act.attacker.transform : (act.leftSide) ? leftTransform : rightTransform;
            Transform    defenseTransform = (!useBattleAnimations.value) ? act.defender.transform : (act.leftSide) ? rightTransform : leftTransform;
            Vector3      startPos         = attackTransform.localPosition;
            Vector3      enemyPos         = defenseTransform.localPosition;
            enemyPos = startPos + (enemyPos - startPos).normalized;

            battleAnimationObject.SetActive(useBattleAnimations.value);
            if (useBattleAnimations.value)
            {
                leftHealth.fillAmount  = actions[0].attacker.GetHealthPercent();
                rightHealth.fillAmount = actions[0].defender.GetHealthPercent();
                yield return(new WaitForSeconds(1f));
            }

            //Move forward
            float f = 0;
            Debug.Log("Start moving");
            while (f < 0.5f)
            {
                f += Time.deltaTime * speed;
                attackTransform.localPosition = Vector3.Lerp(startPos, enemyPos, f);
                yield return(null);
            }
            // Deal damage
            if (act.isDamage)
            {
                int damage = act.GetDamage();
                if (act.attacker.SkillReady(SkillType.DAMAGE))
                {
                    damage = act.attacker.GetSkill().GenerateDamage(damage);
                    act.attacker.skillCharge = -1;
                    Debug.Log("Bonus damage!");
                }
                act.defender.TakeDamage(damage);
                StartCoroutine(DamageDisplay(act.leftSide, damage, true));

                Debug.Log(i + " Dealt damage :  " + damage);
                if (act.attacker.SkillReady(SkillType.HEAL))
                {
                    int health = act.attacker.GetSkill().GenerateHeal(damage);
                    act.attacker.skillCharge = -1;
                    act.attacker.TakeHeals(health);
                    rightDamageText.text = damage.ToString();
                    StartCoroutine(DamageDisplay(!act.leftSide, damage, false));
                    Debug.Log(i + " Healt damage :  " + health);
                }

                if (!act.defender.IsAlive())
                {
                    if (act.leftSide)
                    {
                        rightTransform.GetComponent <SpriteRenderer>().color = new Color(0.4f, 0.4f, 0.4f);
                    }
                    else
                    {
                        leftTransform.GetComponent <SpriteRenderer>().color = new Color(0.4f, 0.4f, 0.4f);
                    }
                }

                //Add skill charges
                act.attacker.IncreaseSkill();
                act.defender.IncreaseSkill();
            }
            else
            {
                if (act.attacker.GetSupport().supportType == SupportType.HEAL)
                {
                    int health = act.GetHeals();
                    act.defender.TakeHeals(health);
                    StartCoroutine(DamageDisplay(act.leftSide, health, false));
                    Debug.Log(i + " Healt damage :  " + health);
                    if (act.attacker.faction == Faction.PLAYER)
                    {
                        act.attacker.GainSP(1, true);
                    }
                }
                else if (act.attacker.GetSupport().supportType == SupportType.BUFF)
                {
                    act.defender.ReceiveBuff(act.attacker.GetSupport().boost, true, true);
                    Debug.Log("Boost them up!");
                }
            }
            //Update health
            leftHealth.fillAmount  = (act.leftSide) ? act.attacker.GetHealthPercent() : act.defender.GetHealthPercent();
            rightHealth.fillAmount = (act.leftSide) ? act.defender.GetHealthPercent() : act.attacker.GetHealthPercent();

            // Move back
            Debug.Log("Moving back");
            while (f > 0f)
            {
                f -= Time.deltaTime * speed;
                attackTransform.localPosition = Vector3.Lerp(startPos, enemyPos, f);
                yield return(null);
            }

            //Check Death
            Debug.Log("Check death");
            if (!act.defender.IsAlive())
            {
                if (act.attacker.faction == Faction.PLAYER)
                {
                    act.attacker.GainSP(3, true);
                }
                yield return(new WaitForSeconds(1f));

                break;
            }
        }

        //Handle exp
        yield return(StartCoroutine(ShowExpGain()));

        //Give debuffs
        if (actions[0].isDamage)
        {
            actions[0].attacker.ActivateSkills(Activation.POSTCOMBAT, actions[0].defender);
            actions[0].defender.ActivateSkills(Activation.POSTCOMBAT, actions[0].attacker);
        }

        //Check game finished
        battleFinishedEvent.Invoke();

        //Clean up
        battleAnimationObject.SetActive(false);
        leftDamageObject.SetActive(false);
        rightDamageObject.SetActive(false);
        actions[0].attacker.EndSkills(Activation.INITCOMBAT, actions[0].defender);
        actions[0].attacker.EndSkills(Activation.PRECOMBAT, actions[0].defender);
        actions[0].defender.EndSkills(Activation.PRECOMBAT, actions[0].attacker);
        actions.Clear();
        TurnController.busy = false;
        _currentCharacter.End();
        _currentCharacter = null;
    }