Encounter / Assets / Scripts / BattleLogic / SpellManagement.cs
SpellManagement.cs
Raw
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
using System.Xml;
using Unity.VisualScripting;
using static UnityEngine.EventSystems.EventTrigger;
using static UnityEngine.Rendering.DebugUI;
using UnityEngine.UIElements;
using UnityEditor;

public class SpellManagement : MonoBehaviour
{
    public enum PreparationCalls
    {
        NextAttackingEntity,
        NextTurn,
        FinishRound,
        NextEntity
    }


    private BattleHandler bhReference;      // Reference to the battle handler that is feeding the information to the manager
    private Player playerRetainer;         // Reference to the player object
    private Enemy[] enemyRetainers;        // Reference to all of the enemy objects in the battle
    private Ally[] allyRetainers;          // Reference to all of the ally objects in the battle
    private Spell currentSpell;            // The current spell that is being casted, information is held 

    private Target[] enemyTargets;         // Target components for the enemies for the arrow instantiation when trying to activate a spell
    private Target[] allyTargets;          // Target components for the allies for the arrow instantiation when trying to activate a spell
    private Target playerTarget;           // Target component for the player when trying to activate a spell when self-activating TODO---> Implement array for more players
    private Task chosenTarget;             // Task system to await for the chosen target for the player selecting the enemy to attack

    private Entity spellUser;              // The current spell caster entity
    private Entity spellTarget;            // The current target of the spell being casted
    private SpellSO.Element spellElement;

    private int valueRetainer;             // Container for a value if the spell is advanced
    private bool stopSpellTask;
    private bool[] signals;
    private int[] damageValues;

    private const int MaxChance = 100;

    private void DumpGarbage(bool endSpell)
    {
        // Garbage is dumped
        if (endSpell)
            GarbageCleanUp();
    }
    public void StopSpellTask() 
        => stopSpellTask = true;
    /// <summary>
    /// Passes the target of the spell as an entity and indicates what child type the passed parameter it is
    /// </summary>
    /// <param name="target">Parent Entity class for the Enemies and Player classes</param>
    /// <exception cref="NullReferenceException">When target is null or an unexpected Entity type</exception>
    public void ChooseTarget(Entity target)
    {
        if (target is Player)
            spellTarget = target as Player;
        else if (target is Enemy)
            spellTarget = target as Enemy;
        else if (target is Ally)
            spellTarget = target as Ally;
        else
            throw new NullReferenceException();

        chosenTarget = Task.CompletedTask;
    }
    /// <summary>
    /// Initializes all the information for the spell to process and perform as its intended
    /// </summary>
    /// <param name="user">The user of the spell, can be any entity type class</param>
    /// <param name="spell">The spell being casted</param>
    /// <param name="player">The Player component for reference</param>
    /// <param name="enemyComps">All of the enemy components on the field</param>
    /// <param name="allyComps">All of the ally components on the field</param>
    /// <param name="bh">The BattleHandler that called the spell management logic</param>
    private void InitializeAllNecessaryComponents(Entity user, Spell spell, Player player, Enemy[] enemyComps, Ally[] allyComps, BattleHandler bh)
    {
        stopSpellTask = false;
        valueRetainer = -100;

        // Stores all necessary data
        bhReference = bh;
        currentSpell = spell;
        spellElement = spell.SpellType;
        spellUser = user;

        if (player != null)
        {
            playerRetainer = player;
            playerTarget = player.gameObject.GetComponent<Target>();
            playerTarget.InjectManager(this);
        }
        else
        {
            playerRetainer = null;
            playerTarget = null;
        }

        enemyRetainers = new Enemy[enemyComps.Length];
        enemyRetainers = enemyComps;
        if (allyComps != null)
        {
            allyRetainers = new Ally[allyComps.Length];
            allyRetainers = allyComps;
            allyTargets = new Target[allyComps.Length];

            for (int i = 0; i < allyRetainers.Length; i++)
            {
                allyTargets[i] = allyRetainers[i].gameObject.GetComponent<Target>();
                allyTargets[i].InjectManager(this);
            }
        }
        else
        {
            allyRetainers = null;
            allyTargets = null;
        }

        enemyTargets = new Target[enemyComps.Length];
        for (int i = 0; i < enemyRetainers.Length; i++)
        {
            enemyTargets[i] = enemyRetainers[i].gameObject.GetComponent<Target>();
            enemyTargets[i].InjectManager(this);
        }
    }
    /// <summary>
    /// Begins the spell process by storing necessary information and determining how to activate the spell based off its type.
    /// If more than one type is detected, the method simply passes to the advanced process
    /// </summary>
    /// <param name="user">User of the spell by Entity</param>
    /// <param name="spell">The spell being used</param>
    /// <param name="player">The Player Object</param>
    /// <param name="enemyComps">The Enemy Objects</param>
    /// <param name="bh">The Battle Handler reference</param>
    /// <param name="ally_Targetting">True - If the player is the one being targetted | False - if otherwise</param>
    /// <exception cref="ArgumentException">An exception occurs when a spells type is unable to be determined</exception>
    public async Task BeginSpellProcess(Entity user, Spell spell, Player player, Enemy[] enemyComps, Ally[] allyComps, BattleHandler bh)
    {
        InitializeAllNecessaryComponents(user, spell, player, enemyComps, allyComps, bh);

        if (currentSpell.CanChooseAllOfType)
        {
            if (currentSpell.SpellEffects.Count > 1)
                await StartAdvancedSpellProcess(null, true);
            else
                await StartNormalSpellTypeProcess();
        }
        else if (currentSpell.CanChooseEffects)
        {
            SwitchIndicatorsOfSide(true, true, true);
            bhReference.AwaitSpellChoice(spellUser);
            bool trigger = await Awaiter();
            SwitchIndicatorsOfSide(true, true, false);
            if (!trigger)
            {
                GarbageCleanUp();
                return;
            }

            List<int> path = DecipherEffect();
            // Indicates the path of indexes in the spell effect list to take
            if (path.Count > 1)
                await StartAdvancedSpellProcess(path);
            // Indicates the index value from the path of the spell effect that is wanted
            else
                await StartNormalSpellProcess(path[0]);
        }
        // If the effect count is greater than one then the Advanced Spell Process will begin
        else if (currentSpell.SpellEffects.Count > 1)
            await StartAdvancedSpellProcess(null, currentSpell.CanChooseAllOfType);
        // Otherwise the simple methods are processed based on the singular type indicated
        else
            await StartNormalSpellProcess();
    }
    /// <summary>
    /// Determines what route the spell effects need to take the logic
    /// </summary>
    /// <param name="spellIndexRef">Index of the spell effects to use and consider</param>
    /// <param name="indicator">Indicator if the spell is finished</param>
    private async Task StartNormalSpellProcess(int spellIndexRef = -1, bool indicator = true)
    {
        SpellSO.SpellEffect sType = currentSpell.SpellEffects[spellIndexRef is -1 ? 0 : spellIndexRef];

        switch (sType)
        {
            case SpellSO.SpellEffect.Heal:
                signals = new bool[6] { true, false, false, false, false, false };
                await ActivateHealSpell(indicator);
                break;
            case SpellSO.SpellEffect.Refill:
                signals = new bool[6] { false, true, false, false, false, false };
                await ActivateRefillSpell(indicator);
                break;
            case SpellSO.SpellEffect.Buff:
                signals = new bool[6] { false, false, true, false, false, false };
                await ActivateBuffSpell(indicator);
                break;
            case SpellSO.SpellEffect.Debuff:
                signals = new bool[6] { false, false, false, true, false, false };
                await ActivateDebuffSpell(indicator);
                break;
            case SpellSO.SpellEffect.Damage:
                signals = new bool[6] { false, false, false, false, true, false };
                await ActivateDamageSpell(indicator);
                break;
            case SpellSO.SpellEffect.Other:
                signals = new bool[6] { false, false, false, false, false, true };
                await ActivateMiscSpellEffects(indicator);
                break;
        }
    }
    /// <summary>
    /// Calculates the normal spell process for a type or side of entities
    /// </summary>
    /// <param name="spellIndexRef">The spell effect index reference point</param>
    private async Task StartNormalSpellTypeProcess(int spellIndexRef = -1)
    {
        SpellSO.SpellEffect sType = currentSpell.SpellEffects[spellIndexRef is -1 ? 0 : spellIndexRef];

        switch (sType)
        {
            case SpellSO.SpellEffect.Damage:
                signals = new bool[5] { true, false, false, false, false };
                await ActivateDamageSpellForType(currentSpell.Left, currentSpell.Right);
                break;
            case SpellSO.SpellEffect.Heal:
                signals = new bool[5] { false, true, false, false, false };
                await ActivateHealSpellForType(currentSpell.Left, currentSpell.Right);
                break;
            case SpellSO.SpellEffect.Buff:
                signals = new bool[5] { false, false, true, false, false };
                await ActivateBuffSpellForType(currentSpell.Left, currentSpell.Right);
                break;
            case SpellSO.SpellEffect.Debuff:
                signals = new bool[5] { false, false, false, true, false };
                await ActivateDebuffSpellForType(currentSpell.Left, currentSpell.Right);
                break;
            case SpellSO.SpellEffect.Other:
                signals = new bool[5] { false, false, false, false, true };
                await ActivateMiscSpellEffects();
                break;
        }
    }
    /// <summary>
    /// Goes through each of the effects in the spell and activates each of their portions on the target
    /// </summary>
    private async Task StartAdvancedSpellProcess(List<int> path = null, bool isAllType = false)
    {
        bool trigger = false;
        valueRetainer = -100;
        
        if (isAllType)
        {
            int totalValue = currentSpell.SpellEffects.Count;
            for (int i = 0; i < totalValue; ++i)
            {
                if (i == totalValue - 1)
                    trigger = true;

                switch (currentSpell.SpellEffects[i])
                {
                    case SpellSO.SpellEffect.Damage:
                        signals = new bool[5] { true, false, false, false, false };
                        await ActivateDamageSpellForType(currentSpell.Left, currentSpell.Right, trigger);
                        break;
                    case SpellSO.SpellEffect.Heal:
                        signals = new bool[5] { false, true, false, false, false };
                        await ActivateHealSpellForType(currentSpell.Left, currentSpell.Right, trigger);
                        break;
                    case SpellSO.SpellEffect.Buff:
                        signals = new bool[5] { false, false, true, false, false };
                        await ActivateBuffSpellForType(currentSpell.Left, currentSpell.Right, trigger);
                        break;
                    case SpellSO.SpellEffect.Debuff:
                        signals = new bool[5] { false, false, false, true, false };
                        await ActivateDebuffSpellForType(currentSpell.Left, currentSpell.Right, trigger);
                        break;
                    case SpellSO.SpellEffect.Other:
                        signals = new bool[5] { false, false, false, false, true };
                        await ActivateMiscSpellEffects();
                        break;
                }
            }
        }
        else
        {
            if (path is null)
            {
                int totalValue = currentSpell.SpellEffects.Count;
                // Cycles through each effect of the spell into their respective functionalities
                for (int i = 0; i < totalValue; i++)
                {
                    if (i == totalValue - 1)
                        trigger = true;

                    await StartNormalSpellProcess(i, trigger);
                }
            }
            else
            {
                int totalValue = path.Count;
                for (int i = 0; i < totalValue; i++)
                {
                    if (i == totalValue - 1)
                        trigger = true;

                    await StartNormalSpellProcess(path[i], trigger);
                }
            }
        }
    }
    private int DecipherRefillValue()
    {
        int refillAmount;
        switch (currentSpell.SpellID)
        {
            case 51:                // Mana Transfusion
                int manaTransferAmount = spellUser.GetManaPercentage() < 25 ? spellUser.RetrieveMana() : (int)Math.Ceiling(spellUser.RetrieveMaxMana() * .25f);

                Dictionary<int, Ability> abList = AbilityBrain.GetAbilityList();
                bool subtracting = true;
                if (spellUser.GetInventory().Abilities.Contains(abList[116]) || spellUser.GetInventory().limitedAbilities.Contains(abList[116]))
                {
                    int rollValue = UnityEngine.Random.Range(1, 6);

                    if (rollValue is 1)
                        subtracting = false;
                }
                
                if (subtracting)
                {
                    spellUser.DecreaseMana(manaTransferAmount);
                    spellUser.StatusChanged();
                }

                refillAmount = manaTransferAmount;
                break;
            default:
                throw new InvalidOperationException();
        }

        return refillAmount;
    }
    private Entity FindMostExhausted()
    {
        Enemy output = null;
        Enemy[] exhaustedEnemies = enemyRetainers
                                    .Where(enemy => enemy.RetrieveMana() < enemy.RetrieveMaxMana())
                                    .ToArray();

        if (exhaustedEnemies.Length is 0)
            output = enemyRetainers[UnityEngine.Random.Range(0, enemyRetainers.Length)];

        // Cycles through all of the damaged enemies for the most damaged
        // If two enemies have the same health than a coin flip for the place
        // holder of being held is essentially held
        foreach (Enemy e in exhaustedEnemies)
        {
            if (output == null)
            {
                output = e;
                continue;
            }
            if (output.GetManaPercentage() > e.GetManaPercentage())
                output = e;
            else if (output.GetManaPercentage() == e.GetManaPercentage())
            {
                int z = UnityEngine.Random.Range(0 , MaxChance + 1);
                if (z > MaxChance / 2)
                    output = e;
            }
        }

        return output;
    }
    /// <summary>
    /// Activates the spell which primarily just does damage
    /// </summary>
    private async Task ActivateDamageSpell(bool endSpell = true)
    {
        bool extraDice = FieldFocusCheck();
        bool isSelf = false;
        bool systemChecked;

        int damageValue = UtilityHandler.RollValue(currentSpell.Dice.ToArray());
        if (extraDice)
            damageValue += UtilityHandler.RollValue(currentSpell.Dice[0]);

        // If the player or ally is the target
        if (spellUser is Player || spellUser is Ally)
        {
            if (!currentSpell.UniqueTargetting)
                SwitchIndicatorsOfSide(false, true, true);
            else
            {
                (systemChecked, isSelf) = EnableUniqueTargettingSystem();

                if (!systemChecked)
                {
                    GarbageCleanUp();
                    return;
                }
            }

            if (!isSelf)
            {
                bhReference.AwaitSpellChoice(spellUser);
                bool condition = await Awaiter();

                SwitchIndicatorsOfSide(true, true, false);

                if (!condition)
                {
                    GarbageCleanUp();
                    return;
                }
            }

            bhReference.GetUIHandler().ResetUI();

            damageValue = UtilityHandler.SpellTypeCheck(currentSpell, damageValue, spellTarget, spellUser);

            if (!endSpell)
                valueRetainer = damageValue;

            (bool specialEffectUsed, bool uniqueEndCall) = UniqueSpellEffectCheck(valueRetainer != -100 ? valueRetainer : damageValue, 0);

            if (uniqueEndCall)
            {
                GarbageCleanUp();
                return;
            }
            else if (!specialEffectUsed && endSpell)
                SpellCasterCasts();
        }
        else
        {
            // Modifies the spell user's stats
            spellTarget = ChooseRandomTargetForEnemy();
            damageValue = UtilityHandler.SpellTypeCheck(currentSpell, damageValue, spellTarget, spellUser);

            if (!endSpell)
                valueRetainer = damageValue;

            if (spellTarget == null)
            {
                GarbageCleanUp();
                return;
            }

            (bool specialEffectUsed, bool uniqueEndCall) = UniqueSpellEffectCheck(valueRetainer != -100 ? valueRetainer : damageValue, 0);

            if (uniqueEndCall)
            {
                GarbageCleanUp();
                return;
            }
            else if (!specialEffectUsed && endSpell)
                SpellCasterCasts();
        }

        if (endSpell)
        {
            if (TargetRollsSavingThrow())          // Succeeded Saving Throw
            {
                switch (currentSpell.SaveFailEffect)
                {
                    case SpellSO.SavingThrowFailEffect.SpellFails:
                        await bhReference.FailedSpellCall(currentSpell);
                        DumpGarbage(true);
                        return;
                    case SpellSO.SavingThrowFailEffect.HalfSpellValue:
                        damageValue /= 2;
                        if (spellUser is Player player)
                            damageValue += player.ParseClassDamageModifiers(spellTarget);

                        await InitializeSpellEffect();

                        await bhReference.AttackWithSpell(damageValue, currentSpell, spellUser, spellTarget, endSpell);
                        break;
                    case SpellSO.SavingThrowFailEffect.NoDebuff:
                        if (spellUser is Player player2)
                            damageValue += player2.ParseClassDamageModifiers(spellTarget);

                        await InitializeSpellEffect();

                        await bhReference.AttackWithSpell(damageValue, currentSpell, spellUser, spellTarget, endSpell);
                        break;
                }

                if (spellTarget.GetInventory().Abilities.Contains(AbilityBrain.GetAbilityList()[86]))
                {
                    spellTarget.IncreaseMana((int)Math.Floor(currentSpell.SpellCost * .5f));
                    spellTarget.StatusChanged();
                }
            }
            else
            {
                if (spellUser is Player player)
                    damageValue += player.ParseClassDamageModifiers(spellTarget);

                if (spellTarget.GetInventory().Abilities.Contains(AbilityBrain.GetAbilityList()[86]))
                {
                    spellTarget.IncreaseMana((int)Math.Floor(currentSpell.SpellCost * .5f));
                    spellTarget.StatusChanged();
                }

                await InitializeSpellEffect();

                await bhReference.AttackWithSpell(damageValue, currentSpell, spellUser, spellTarget, endSpell);
            }
        }

        DumpGarbage(endSpell);
    }
    /// <summary>
    /// Damages either all enemies or all allies on one side
    /// </summary>
    /// <param name="left">Ally side indicator</param>
    /// <param name="right">Enemy side indicator</param>
    /// <param name="endSpell">Indicator if this step is the end of the spell</param>
    private async Task ActivateDamageSpellForType(bool left, bool right, bool endSpell = true)
    {
        if (!left && !right)
        {
            if (spellUser is Enemy)
                left = true;
            else
                right = true;
        }

        bool extraDice = FieldFocusCheck();

        // Calculates damage value
        int damageValue = UtilityHandler.RollValue(currentSpell.Dice.ToArray());
        if (extraDice)
            damageValue += UtilityHandler.RollValue(currentSpell.Dice[0]);

        (bool specialEffectUsed, bool uniqueEndCall) = UniqueSpellEffectCheck(valueRetainer != -100 ? valueRetainer : damageValue, -4);

        if (uniqueEndCall)
        {
            GarbageCleanUp();
            return;
        }
        else if (!specialEffectUsed)
        {
            damageValues = new int[4];

            if (left)
            {
                if (playerRetainer != null)
                {
                    int value = UtilityHandler.SpellTypeCheck(currentSpell, damageValue, playerRetainer, spellUser);
                    if (endSpell)
                    {
                        bool succeeded = TargetRollsSavingThrow(playerRetainer);
                        switch (currentSpell.SaveFailEffect)
                        {
                            case SpellSO.SavingThrowFailEffect.SpellFails:
                                break;
                            case SpellSO.SavingThrowFailEffect.HalfSpellValue:
                                if (succeeded)
                                    value /= 2;
                                break;
                            case SpellSO.SavingThrowFailEffect.NoDebuff:
                                break;
                        }
                        damageValues[0] = value;

                        if (playerRetainer.IsHidden)
                            playerRetainer.ShowEntity();
                    }
                }

                if (!endSpell)
                    valueRetainer = damageValue;

                if (allyRetainers != null)
                {
                    for (int i = 0; i < allyRetainers.Length; i++)
                    {
                        int value = UtilityHandler.SpellTypeCheck(currentSpell, damageValue, allyRetainers[i], spellUser);
                        if (endSpell)
                        {
                            bool succeeded = TargetRollsSavingThrow(allyRetainers[i]);
                            switch (currentSpell.SaveFailEffect)
                            {
                                case SpellSO.SavingThrowFailEffect.SpellFails:
                                    continue;
                                case SpellSO.SavingThrowFailEffect.HalfSpellValue:
                                    if (succeeded)
                                        value /= 2;
                                    break;
                                case SpellSO.SavingThrowFailEffect.NoDebuff:
                                    break;
                            }

                            if (allyRetainers[i].IsHidden)
                                allyRetainers[i].ShowEntity();
                        }

                        if (spellUser is Player player)
                            value += player.ParseClassDamageModifiers(allyRetainers[i]);

                        damageValues[i + 1] = value;
                    }
                }
            }
            else
            {
                for (int i = 0; i < enemyRetainers.Length; ++i)
                {
                    int value = UtilityHandler.SpellTypeCheck(currentSpell, damageValue, enemyRetainers[i], spellUser);
                    if (endSpell)
                    {
                        bool succeeded = TargetRollsSavingThrow(enemyRetainers[i]);
                        switch (currentSpell.SaveFailEffect)
                        {
                            case SpellSO.SavingThrowFailEffect.SpellFails:
                                continue;
                            case SpellSO.SavingThrowFailEffect.HalfSpellValue:
                                if (succeeded)
                                    value /= 2;
                                break;
                            case SpellSO.SavingThrowFailEffect.NoDebuff:
                                break;
                        }

                        if (enemyRetainers[i].IsHidden)
                            enemyRetainers[i].ShowEntity();
                    }

                    if (spellUser is Player player)
                        value += player.ParseClassDamageModifiers(enemyRetainers[i]);

                    damageValues[i] = value;
                }
            }
        }

        if (damageValues.All(x => x is 0))
        {
            await bhReference.FailedSpellCall(currentSpell);
            DumpGarbage(true);
            return;
        }

        if (endSpell)
        {
            if (spellTarget != null)
            {
                if (spellTarget.GetInventory().Abilities.Contains(AbilityBrain.GetAbilityList()[86]))
                {
                    spellTarget.IncreaseMana((int)Math.Floor(currentSpell.SpellCost * .5f));
                    spellTarget.StatusChanged();
                }
            }

            SpellCasterCasts();

            await InitializeSpellEffect();

            await bhReference.AttackWithSpell(damageValues, currentSpell, spellUser, left, right);
        }    

        DumpGarbage(endSpell);
    }
    /// <summary>
    /// Heals the allies of the user of the spell based on their targetting
    /// </summary>
    /// <param name="endSpell">Indicator for if the end of the spell is occuring and the garbage should be cleared and the turn should finally end</param>
    /// <param name="firstOfAdvanced">Indicator for when the enemy should determine who the best ally is to heal on their side</param>
    private async Task ActivateHealSpell(bool endSpell = true)
    {
        bool extraDice = FieldFocusCheck();
        bool systemChecked;
        bool isSelf = false;

        int healValue = UtilityHandler.RollValue(currentSpell.Dice.ToArray());
        if (extraDice)
            healValue += UtilityHandler.RollValue(currentSpell.Dice[0]);

        // When the player uses a healing spell, heals themselves
        if (spellUser is Player || spellUser is Ally)
        {
            if (!currentSpell.UniqueTargetting)
                SwitchIndicatorsOfSide(true, false, true);
            else
            {
                (systemChecked, isSelf) = EnableUniqueTargettingSystem();

                if (!systemChecked)
                {
                    GarbageCleanUp();
                    return;
                }
            }

            if (!isSelf)
            {
                bhReference.AwaitSpellChoice(spellUser);
                bool condition = await Awaiter();

                SwitchIndicatorsOfSide(true, false, false);

                if (!condition)
                {
                    GarbageCleanUp();
                    return;
                }
            }

            bhReference.GetUIHandler().ResetUI();

            (bool specialEffectUsed, bool uniqueEndCall) = UniqueSpellEffectCheck(valueRetainer != -100 ? valueRetainer : healValue, 1);

            if (uniqueEndCall)
            {
                GarbageCleanUp();
                return;
            }
            else if (!specialEffectUsed && endSpell)
            {
                healValue = AbilityBrain.SpellChecks(currentSpell, spellUser, spellTarget, healValue, signals);

                await InitializeSpellEffect();

                spellTarget.IncreaseHealth(healValue);
                spellTarget.StatusChanged();

                if (spellTarget.GetInventory().Abilities.Contains(AbilityBrain.GetAbilityList()[86]))
                {
                    spellTarget.IncreaseMana((int)Math.Floor(currentSpell.SpellCost * .5f));
                    spellTarget.StatusChanged();
                }

                // Modifies the spell user's stats
                SpellCasterCasts();

                await bhReference.AttackWithSpell(-healValue, currentSpell, spellUser, spellTarget, endSpell);
            }

        }
        // When the enemy uses a healing spell, they choose the most damaged enemy to heal from the
        // group, if there is a tie they randomly choose
        else
        {
            if (!currentSpell.UniqueTargetting)
                spellTarget = FindMostDamaged();
            else
            {
                (systemChecked, isSelf) = EnableUniqueTargettingSystem();

                if (!systemChecked)
                {
                    GarbageCleanUp();
                    return;
                }
            }

            (bool specialEffectUsed, bool uniqueEndCall) = UniqueSpellEffectCheck(valueRetainer != -100 ? valueRetainer : healValue, 1);

            if (uniqueEndCall)
            {
                GarbageCleanUp();
                return;
            }
            else if (!specialEffectUsed && endSpell)
            {
                healValue = AbilityBrain.SpellChecks(currentSpell, spellUser, spellTarget, healValue, signals);

                await InitializeSpellEffect();

                spellTarget.IncreaseHealth(healValue);
                spellTarget.StatusChanged();

                if (spellTarget.GetInventory().Abilities.Contains(AbilityBrain.GetAbilityList()[86]))
                {
                    spellTarget.IncreaseMana((int)Math.Floor(currentSpell.SpellCost * .5f));
                    spellTarget.StatusChanged();
                }

                // Modifies the spell user's stats
                SpellCasterCasts();

                bhReference.CallSpellActionText(spellUser.GetName(), spellTarget.GetName(), currentSpell, healValue);
            }
        }

        DumpGarbage(endSpell);
    }
    /// <summary>
    /// Advanced call for healing a side of a type
    /// </summary>
    /// <param name="left">The indicator if the companion side is being targetted</param>
    /// <param name="right">The indicator if the enemy side is being targetted</param>
    /// <param name="endSpell">Indicator if the spell is advanced and is at the end of its life</param>
    private async Task ActivateHealSpellForType(bool left, bool right, bool endSpell = true)
    {
        if (!left && !right)
        {
            if (spellUser is Enemy)
                right = true;
            else
                left = true;
        }

        bool extraDice = FieldFocusCheck();

        // Determine the value for the healing
        int healValue = UtilityHandler.RollValue(currentSpell.Dice.ToArray());
        if (extraDice)
            healValue += UtilityHandler.RollValue(currentSpell.Dice[0]);

        if (left)           // Heal the companion side
        {
            (bool specialEffectUsed, bool uniqueEndCall) = UniqueSpellEffectCheck(valueRetainer != -100 ? valueRetainer : healValue, -3);

            if (uniqueEndCall)
            {
                GarbageCleanUp();
                return;
            }
            else if (!specialEffectUsed && endSpell)
            {
                await InitializeSpellEffect();

                if (playerRetainer != null)
                {
                    playerRetainer.IncreaseHealth(AbilityBrain.SpellChecks(currentSpell, spellUser, playerRetainer, healValue, signals));
                    playerRetainer.StatusChanged();
                }

                if (allyRetainers != null)
                {
                    foreach (Ally ally in allyRetainers)
                    {
                        ally.IncreaseHealth(AbilityBrain.SpellChecks(currentSpell, spellUser, ally, healValue, signals));
                        ally.StatusChanged();
                    }
                }
                
                // Modifies the spell user's stats
                SpellCasterCasts();

                if (playerRetainer.GetInventory().Abilities.Contains(AbilityBrain.GetAbilityList()[86]))
                {
                    playerRetainer.IncreaseMana((int)Math.Floor(currentSpell.SpellCost * .5f));
                    playerRetainer.StatusChanged();
                }

                bhReference.GetUIHandler().ResetUI();
                bhReference.CallSpellActionText(spellUser, left, right, currentSpell.SpellEffects[0], currentSpell, healValue);
                bhReference.DisablePlayerTurn();

                GarbageCleanUp();
            }
        }
        if (right)     // Heal the enemy side
        {
            (bool specialEffectUsed, bool uniqueEndCall) = UniqueSpellEffectCheck(valueRetainer != -100 ? valueRetainer : healValue, -3);

            if (uniqueEndCall)
            {
                GarbageCleanUp();
                return;
            }
            else if (!specialEffectUsed && endSpell)
            {
                await InitializeSpellEffect();

                foreach (Enemy enemy in enemyRetainers)
                {
                    enemy.IncreaseHealth(AbilityBrain.SpellChecks(currentSpell, spellUser, enemy, healValue, signals));
                    enemy.StatusChanged();
                }

                // Modifies the spell user's stats
                SpellCasterCasts();

                bhReference.GetUIHandler().ResetUI();
                bhReference.CallSpellActionText(spellUser, left, right, currentSpell.SpellEffects[0], currentSpell, healValue);

                if (spellUser is Player)
                    bhReference.DisablePlayerTurn();

                GarbageCleanUp();
            }
        }
    }
    private async Task ActivateRefillSpell(bool endSpell = true)
    {
        bool systemChecked;
        bool isSelf = false;

        // When the player uses a healing spell, heals themselves
        if (spellUser is Player || spellUser is Ally)
        {
            if (!currentSpell.UniqueTargetting)
                SwitchIndicatorsOfSide(true, false, true);
            else
            {
                (systemChecked, isSelf) = EnableUniqueTargettingSystem();

                if (!systemChecked)
                {
                    GarbageCleanUp();
                    return;
                }
            }

            if (!isSelf)
            {
                bhReference.AwaitSpellChoice(spellUser);
                bool condition = await Awaiter();

                if (!condition)
                {
                    GarbageCleanUp();
                    return;
                }

                SwitchIndicatorsOfSide(true, false, false);
            }

            int refillValue = DecipherRefillValue();

            (bool specialEffectUsed, bool uniqueEndCall) = UniqueSpellEffectCheck(valueRetainer != -100 ? valueRetainer : refillValue, 1);

            if (uniqueEndCall)
            {
                GarbageCleanUp();
                return;
            }
            else if (!specialEffectUsed && endSpell)
            {
                refillValue = AbilityBrain.SpellChecks(currentSpell, spellUser, spellTarget, refillValue, signals);

                await InitializeSpellEffect();

                spellTarget.IncreaseMana(refillValue);
                spellTarget.StatusChanged();

                await bhReference.AttackWithSpell(-refillValue, currentSpell, spellUser, spellTarget, endSpell);
            }

        }
        // When the enemy uses a refill spell, they choose the enemy with the least mana in terms of percentage
        else
        {
            if (!currentSpell.UniqueTargetting)
                spellTarget = FindMostExhausted();
            else
            {
                (systemChecked, isSelf) = EnableUniqueTargettingSystem();

                if (!systemChecked)
                {
                    GarbageCleanUp();
                    return;
                }
            }

            int refillValue = DecipherRefillValue();

            (bool specialEffectUsed, bool uniqueEndCall) = UniqueSpellEffectCheck(valueRetainer != -100 ? valueRetainer : refillValue, 1);

            if (uniqueEndCall)
            {
                GarbageCleanUp();
                return;
            }
            else if (!specialEffectUsed && endSpell)
            {
                refillValue = AbilityBrain.SpellChecks(currentSpell, spellUser, spellTarget, refillValue, signals);

                await InitializeSpellEffect();

                spellTarget.IncreaseMana(refillValue);
                spellTarget.StatusChanged();

                await bhReference.AttackWithSpell(-refillValue, currentSpell, spellUser, spellTarget, endSpell);   
            }
        }

        DumpGarbage(endSpell);
    }
    /// <summary>
    /// Buffs the designated entity/spell_target with the spell and gives it a buff modifier
    /// </summary>
    /// <param name="endSpell">Indicator for if the end of the spell is occuring and the garbage should be cleared and the turn should finally end</param>
    private async Task ActivateBuffSpell(bool endSpell = true)
    {
        bool systemChecked;
        bool isSelf = false;

        // If the player is not the target
        if (spellUser is Enemy)
        {
            if (!currentSpell.UniqueTargetting)
            {
                int enemy = UnityEngine.Random.Range(0, enemyRetainers.Length);
                spellTarget = enemyRetainers[enemy];
            }
            else
            {
                (systemChecked, isSelf) = EnableUniqueTargettingSystem();

                if (!systemChecked)
                {
                    GarbageCleanUp();
                    return;
                }
            }

            (bool specialEffectUsed, bool uniqueEndCall) = UniqueSpellEffectCheck(valueRetainer, 2);

            if (uniqueEndCall)
            {
                GarbageCleanUp();
                return;
            }
            else if (!specialEffectUsed)
                await NonDamageSpellCall(true, endSpell);
        }
        else
        {
            if (!currentSpell.UniqueTargetting)
                SwitchIndicatorsOfSide(true, false, true);
            else
            {
                (systemChecked, isSelf) = EnableUniqueTargettingSystem();

                if (!systemChecked)
                {
                    GarbageCleanUp();
                    return;
                }
            }

            if (!isSelf)
            {
                bhReference.AwaitSpellChoice(spellUser);
                bool condition = await Awaiter();

                if (!condition)
                {
                    GarbageCleanUp();
                    return;
                }

                SwitchIndicatorsOfSide(true, false, false);
            }

            (bool specialEffectUsed, bool uniqueEndCall) = UniqueSpellEffectCheck(valueRetainer, 2);

            if (uniqueEndCall)
            {
                GarbageCleanUp();
                return;
            }
            else if (!specialEffectUsed)
                await NonDamageSpellCall(true, endSpell);
        }

        DumpGarbage(endSpell);
    }
    /// <summary>
    /// Simply calls a different version of the NonDamageSpellCall before cleaning garbage if needed
    /// </summary>
    /// <param name="left">Indicator if the left side is all targetted</param>
    /// <param name="right">Indicator if the right side is all targetted</param>
    /// <param name="endSpell">Indicator if the spell is advanced and at the end of its life</param>
    private async Task ActivateBuffSpellForType(bool left, bool right, bool endSpell = true)
    {
        if (!left && !right)
        {
            if (spellUser is Enemy)
                right = true;
            else
                left = true;
        }

        (bool specialEffectUsed, bool uniqueEndCall) = UniqueSpellEffectCheck(valueRetainer, -2);

        if (left && right)
        {
            if (currentSpell.IsDebuffLeftAndBuffRight())
                await NonDamageSpellCall(false, true, endSpell, true);
            else if (currentSpell.IsDebuffRightAndBuffLeft())
                await NonDamageSpellCall(true, false, endSpell, true);

            return;
        }

        if (uniqueEndCall)
        {
            GarbageCleanUp();
            return;
        }
        else if (!specialEffectUsed)
            await NonDamageSpellCall(left, right, endSpell);

        DumpGarbage(endSpell);
    }
    /// <summary>
    /// Debuffs the designated entity/spell_target with the spell and gives the entity a debuff modifier
    /// </summary>
    /// <param name="endSpell">Indicator for if the end of the spell is occuring and the garbage should be cleared and the turn should finally end</param>
    private async Task ActivateDebuffSpell(bool endSpell = true)
    {
        bool succeeded;

        if (spellUser is Enemy)
        {
            // If the spell target for an enemy was already gathered via other spell effect calls, skip
            if (spellTarget == null)
                spellTarget = ChooseRandomTargetForEnemy();

            if (spellTarget == null)
            {
                GarbageCleanUp();
                return;
            }

            succeeded = TargetRollsSavingThrow();
            if (succeeded && endSpell)
            {
                switch (currentSpell.SaveFailEffect)
                {
                    case SpellSO.SavingThrowFailEffect.SpellFails:
                        await bhReference.FailedSpellCall(currentSpell);
                        DumpGarbage(true);
                        return;
                    case SpellSO.SavingThrowFailEffect.HalfSpellValue:
                        valueRetainer /= 2;
                        break;
                    case SpellSO.SavingThrowFailEffect.NoDebuff:
                        break;
                }
            }
            (bool specialEffectUsed, bool uniqueEndCall) = UniqueSpellEffectCheck(valueRetainer, 3);

            if (uniqueEndCall)
            {
                GarbageCleanUp();
                return;
            }
            else if (!specialEffectUsed)
                await NonDamageSpellCall(succeeded, endSpell);
        }
        else
        {
            SwitchIndicatorsOfSide(false, true, true);
            bhReference.AwaitSpellChoice(spellUser);
            bool condition = await Awaiter();

            if (!condition)
            {
                GarbageCleanUp();
                return;
            }

            SwitchIndicatorsOfSide(false, true, false);

            succeeded = TargetRollsSavingThrow();
            if (succeeded && endSpell)
            {
                switch (currentSpell.SaveFailEffect)
                {
                    case SpellSO.SavingThrowFailEffect.SpellFails:
                        await bhReference.FailedSpellCall(currentSpell);
                        DumpGarbage(endSpell);
                        return;
                    case SpellSO.SavingThrowFailEffect.HalfSpellValue:
                        valueRetainer /= 2;
                        break;
                    case SpellSO.SavingThrowFailEffect.NoDebuff:
                        break;
                }
            }

            (bool specialEffectUsed, bool uniqueEndCall) = UniqueSpellEffectCheck(valueRetainer, 3);

            if (uniqueEndCall)
            {
                GarbageCleanUp();
                return;
            }
            else if (!specialEffectUsed)
                await NonDamageSpellCall(succeeded, endSpell);
        }

        DumpGarbage(endSpell);
    }
    /// <summary>
    /// Simply calls a different version of the NonDamageSpellCall before cleaning garbage if needed
    /// </summary>
    /// <param name="left">Indicator if the left side is all targetted</param>
    /// <param name="right">Indicator if the right side is all targetted</param>
    /// <param name="endSpell">Indicator if the spell is advanced and at the end of its life</param>
    private async Task ActivateDebuffSpellForType(bool left, bool right, bool endSpell = true)
    {
        if (!left && !right)
        {
            if (spellUser is Enemy)
                left = true;
            else
                right = true;
        }

        (bool specialEffectUsed, bool uniqueEndCall) = UniqueSpellEffectCheck(valueRetainer, -1);

        if (left && right)
        {
            if (currentSpell.IsDebuffLeftAndBuffRight())
                await NonDamageSpellCall(true, false, endSpell, false);
            else if (currentSpell.IsDebuffRightAndBuffLeft())
                await NonDamageSpellCall(false, true, endSpell, false);

            return;
        }

        if (uniqueEndCall)
        {
            GarbageCleanUp();
            return;
        }
        else if (!specialEffectUsed)
            await NonDamageSpellCall(left, right, endSpell);

        DumpGarbage(endSpell);
    }
    private async Task ActivateMiscSpellEffects(bool indicator = true)
    {
        foreach (SpellSO.OtherEffects effect in currentSpell.miscEffects)
        {
            switch (effect)
            {
                // Simply switches the weather type
                case SpellSO.OtherEffects.ManipulateWeather:
                    bhReference.GetUIHandler().ResetUI();
                    ClimateManager.Weather weatherType = currentSpell.weatherToChangeTo;

                    SpellCasterCasts();
                    await InitializeSpellEffect();

                    ClimateManager.Instance.ManipulateWeather(weatherType);
                    if (weatherType is ClimateManager.Weather.Blizzard)
                        bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(1);
                    else if (weatherType is ClimateManager.Weather.Rainy)
                        bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(20);
                    else if (weatherType is ClimateManager.Weather.Sunny)
                        bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(37);

                    if (spellUser is Player || spellUser is Ally)
                        bhReference.DisablePlayerTurn();

                    break;
                case SpellSO.OtherEffects.SummonMinions:

                    bool successful;
                    bool isLeft = spellUser is Ally || spellUser is Player;

                    switch (currentSpell.SpellID)
                    {
                        case 25: // Summon Golem
                            ClimateManager.Weather currentWeather = ClimateManager.Instance.GetWeather();

                            // Determine Golem Type from current weather

                            // TODO ---> Make a Strange Golem that occurs during Eclipse

                            GameObject golem;
                            if (currentWeather is ClimateManager.Weather.Sunny)
                                golem = currentSpell.minionSummons[1];          // Fire
                            else if (currentWeather is ClimateManager.Weather.Cloudy || currentWeather is ClimateManager.Weather.Eclipsed || currentWeather is ClimateManager.Weather.Rainy)
                                golem = currentSpell.minionSummons[0];          // Rock
                            else if (currentWeather is ClimateManager.Weather.Monsoon || currentWeather is ClimateManager.Weather.Foggy)
                                golem = currentSpell.minionSummons[2];          // Wind
                            else
                                golem = currentSpell.minionSummons[3];          // Ice

                            SpellCasterCasts();
                            await InitializeSpellEffect(true);

                            successful = bhReference.CreateMinion(golem, isLeft);

                            bhReference.GetUIHandler().ResetUI();

                            if (successful)
                                bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(2);  // Succeeded in summoning a golem
                            else
                                bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(3);  // Failed to summon a golem

                            if (isLeft)
                                bhReference.DisablePlayerTurn();
                            break;
                        case 53:    // Summon Demonic Soldier
                            bhReference.GetUIHandler().ResetUI();
                            GameObject demonicSoldier = isLeft ? currentSpell.minionSummons[1] : currentSpell.minionSummons[0];

                            SpellCasterCasts();
                            await InitializeSpellEffect(true);

                            successful = bhReference.CreateMinion(demonicSoldier, isLeft);

                            if (successful)
                                bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(18);
                            else
                                bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(19);

                            if (isLeft)
                                bhReference.DisablePlayerTurn();

                            break;
                        case 76:    // Summon Damned Soul
                            bhReference.GetUIHandler().ResetUI();
                            GameObject damnedSoul = isLeft ? currentSpell.minionSummons[1] : currentSpell.minionSummons[0];
                            SpellCasterCasts();
                            await InitializeSpellEffect(true);

                            successful = bhReference.CreateMinion(damnedSoul, isLeft);

                            if (successful)
                                bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(38);
                            else
                                bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(39);

                            if (isLeft)
                                bhReference.DisablePlayerTurn();
                            
                            break;
                    }
                    break;
                case SpellSO.OtherEffects.CureAilments:
                    bhReference.GetUIHandler().ResetUI();

                    SwitchIndicatorsOfSide(true, false, true);

                    bhReference.AwaitSpellChoice(spellUser);
                    bool condition = Awaiter().GetAwaiter().GetResult();

                    if (!condition)
                    {
                        GarbageCleanUp();
                        return;
                    }

                    SpellCasterCasts();
                    await InitializeSpellEffect();

                    bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(7);
                    spellTarget.CleanseAilments();

                    if (spellUser is Player || spellUser is Ally)
                        bhReference.DisablePlayerTurn();
                    break;
                case SpellSO.OtherEffects.Revive:

                    bhReference.GetUIHandler().ResetUI();

                    if (spellUser is Ally || spellUser is Player)
                    {
                        bool picked;

                        (picked, spellTarget) = bhReference.IndicateRevivalChoices().GetAwaiter().GetResult();

                        if (!picked)
                        {
                            GarbageCleanUp();
                            return;
                        }
                    }
                    else
                    {
                        spellTarget = bhReference.ChooseRandomDeadEnemy();

                        if (spellTarget == null)
                        {
                            GarbageCleanUp();
                            return;
                        }
                    }
                    SpellCasterCasts();
                    await InitializeSpellEffect();

                    spellTarget.Revive();
                    bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextSpell(spellUser.GetName(), spellTarget.GetName(), ActionTextBox.ActionType.ReviveOne, currentSpell);

                    if (spellUser is Player || spellUser is Ally)
                        bhReference.DisablePlayerTurn();

                    break;
                case SpellSO.OtherEffects.Unique:
                    await DecipherUniqueEffect();
                    break;
            }
        }

        DumpGarbage(indicator);
    }
    private async Task DecipherUniqueEffect()
    {
        bool condition = false;
        bool succeeded = false;
        int totalDamage = 0;
        switch (currentSpell.SpellID)
        {
            case 33:                    // Life Contract

                bhReference.GetUIHandler().ResetUI();

                // Heal entity
                int healValue = UtilityHandler.RollValue(Dice.DiceR.Four, Dice.DiceR.Four, Dice.DiceR.Four, Dice.DiceR.Four);
                spellUser.IncreaseHealth(healValue);
                spellUser.StatusChanged();

                // Set Life Contract Mod
                Modifier modToAdd = AbilityBrain.GetModifierList()["Life Contract"];
                modToAdd.RetentionObject = new List<int> { healValue, 0 };
                spellUser.AddModToList(modToAdd);
                spellUser.AfflictedStatus();

                SpellCasterCasts();
                await InitializeSpellEffect();
                bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(4);
                if (spellUser is Player || spellUser is Ally)
                    bhReference.DisablePlayerTurn();

                break;
            case 43:                    // Erratic Bursts
                bhReference.GetUIHandler().ResetUI();
                SpellCasterCasts();

                for (int i = 0; i < 3; ++i)
                {
                    if (enemyRetainers.All(enemy => enemy.HasDied))
                    {
                        bhReference.DisablePlayerTurn();
                        break;
                    }

                    int targetAttempts = 0;
                    do
                    {
                        spellTarget = enemyRetainers[UnityEngine.Random.Range(0, enemyRetainers.Length)];
                        targetAttempts++;

                    } while (spellTarget.HasDied && targetAttempts < 10);

                    if (targetAttempts >= 10)
                        break;

                    bool erraticSucceed = TargetRollsSavingThrow();
                    int rolledDamage = UtilityHandler.RollValue(Dice.DiceR.Eight, Dice.DiceR.Eight);

                    if (erraticSucceed)
                    {
                        totalDamage += rolledDamage / 2;
                        await InitializeSpellEffect();
                        await bhReference.AttackWithSpell(rolledDamage / 2, currentSpell, spellUser, spellTarget, false);
                    }
                    else
                    {
                        totalDamage += rolledDamage;
                        await InitializeSpellEffect();
                        await bhReference.AttackWithSpell(rolledDamage, currentSpell, spellUser, spellTarget, false);
                    }

                    spellTarget.StatusChanged();
                }

                bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(32, totalDamage, null, spellUser);
                if (spellUser is Player || spellUser is Ally)
                    bhReference.DisablePlayerTurn();

                break;
            case 58:                    // Chain Lightning
                Entity secondSpellTarget;

                if (spellUser is Player || spellUser is Ally)
                {
                    SwitchIndicatorsOfSide(false, true, true);
                    bhReference.AwaitSpellChoice(spellUser);
                    condition = await Awaiter();

                    SwitchIndicatorsOfSide(false, true, false);

                    if (!condition)
                    {
                        GarbageCleanUp();
                        return;
                    }

                    bhReference.GetUIHandler().ResetUI();

                    SpellCasterCasts();
                    await InitializeSpellEffect();

                    succeeded = TargetRollsSavingThrow();
                    int primaryDamage = UtilityHandler.RollValue(Dice.DiceR.Eight, Dice.DiceR.Eight, Dice.DiceR.Eight, Dice.DiceR.Eight);

                    if (succeeded)
                        primaryDamage /= 2;

                    ActionTextBox atb = bhReference.GetUIHandler().RetrieveActionText();

                    atb.UpdateActionTextUnique(40, primaryDamage, spellTarget, spellUser);
                    bhReference.DamageEnemy(spellTarget as Enemy, primaryDamage, false, false);

                    await atb.GetTextShownStatus();

                    if (enemyRetainers.Length > 1)
                    {
                        ClimateManager.Weather currentWeather = ClimateManager.Instance.GetWeather();
                        int chainTimes = currentWeather is ClimateManager.Weather.Rainy || currentWeather is ClimateManager.Weather.Monsoon ? 2 : 1;

                        for (int times = 0; times < chainTimes; ++times)
                        {
                            int targetAttempts = 0;

                            do
                            {
                                secondSpellTarget = enemyRetainers[UnityEngine.Random.Range(0, enemyRetainers.Length)];
                                targetAttempts++;
                            }
                            while ((secondSpellTarget.HasDied is true || secondSpellTarget.GetUniqueID() == spellTarget.GetUniqueID()) && targetAttempts < 10);

                            if (targetAttempts >= 10)
                            {
                                bhReference.DisablePlayerTurn();
                                break;
                            }

                            int secondaryDamage = UtilityHandler.RollValue(Dice.DiceR.Eight, Dice.DiceR.Eight);
                            succeeded = TargetRollsSavingThrow(secondSpellTarget);

                            if (succeeded)
                                secondaryDamage /= 2;

                            bhReference.DamageEnemy(secondSpellTarget as Enemy, secondaryDamage, false, false);
                            atb.UpdateActionTextUnique(41, secondaryDamage, secondSpellTarget, spellUser);

                            await atb.GetTextShownStatus();
                        }

                        bhReference.DisablePlayerTurn();
                    }
                }
                else
                {
                    spellTarget = ChooseRandomTargetForEnemy();

                    if (spellTarget == null)
                    {
                        GarbageCleanUp();
                        return;
                    }

                    SpellCasterCasts();
                    await InitializeSpellEffect();

                    int primaryDamage = UtilityHandler.RollValue(Dice.DiceR.Eight, Dice.DiceR.Eight, Dice.DiceR.Eight, Dice.DiceR.Eight);
                    succeeded = TargetRollsSavingThrow();

                    if (succeeded)
                        primaryDamage /= 2;

                    if (spellTarget is Player)
                        bhReference.DamagePlayer(primaryDamage, false);
                    else
                        bhReference.DamageAlly(spellTarget as Ally, primaryDamage, false);

                    ActionTextBox atb = bhReference.GetUIHandler().RetrieveActionText();
                    atb.UpdateActionTextUnique(40, primaryDamage, spellTarget, spellUser);

                    await atb.GetTextShownStatus();

                    int newLength = playerRetainer != null && allyRetainers != null ? allyRetainers.Length + 1 : 
                        (playerRetainer != null && allyRetainers == null ? 1 : 
                        (playerRetainer == null && allyRetainers != null ? allyRetainers.Length : 0)); 

                    Entity[] leftEntities = new Entity[newLength];
                    int indexCorrelator = 0;

                    if (playerRetainer != null)
                        leftEntities[indexCorrelator++] = playerRetainer;

                    if (allyRetainers != null)
                    {
                        for (int i = 0; i < allyRetainers.Length; ++i)
                            leftEntities[indexCorrelator++] = allyRetainers[i];
                    }

                    if (newLength > 1)
                    {
                        ClimateManager.Weather currentWeather = ClimateManager.Instance.GetWeather();
                        int chainTimes = currentWeather is ClimateManager.Weather.Rainy || currentWeather is ClimateManager.Weather.Monsoon ? 2 : 1;
                        for (int times = 0; times < chainTimes; ++chainTimes)
                        {
                            int targetAttempts = 0;
                            do
                            {
                                secondSpellTarget = leftEntities[UnityEngine.Random.Range(0, leftEntities.Length)];
                                targetAttempts++;

                            } while ((secondSpellTarget.HasDied is true || secondSpellTarget.GetUniqueID() == spellTarget.GetUniqueID()) && targetAttempts < 10);

                            if (targetAttempts >= 10)
                                break;

                            int secondaryDamage = UtilityHandler.RollValue(Dice.DiceR.Eight, Dice.DiceR.Eight);
                            succeeded = TargetRollsSavingThrow(secondSpellTarget);

                            if (succeeded)
                                secondaryDamage /= 2;

                            if (secondSpellTarget is Player)
                                bhReference.DamagePlayer(secondaryDamage, false);
                            else
                                bhReference.DamageAlly(secondSpellTarget as Ally, secondaryDamage, false);

                            atb.UpdateActionTextUnique(41, secondaryDamage, secondSpellTarget, spellUser);
                            await atb.GetTextShownStatus();
                        }
                    }
                }

                break;
            case 61:                    // Sacrificial Rite
                bhReference.GetUIHandler().ResetUI();

                bool canCast = spellUser.RetrieveHealth() > (spellUser.RetrieveMaxHealth() / 2);

                if (canCast)
                {
                    spellUser.DecreaseHealth(spellUser.RetrieveMaxHealth() / 2);
                    SpellCasterCasts();

                    await InitializeSpellEffect();

                    if (spellUser is Player || spellUser is Ally)
                    {
                        foreach (Enemy enemyComp in enemyRetainers)
                            enemyComp.AddModToList(Globals.TurnModifierFromObject(currentSpell.DebuffModifier));

                        if (allyRetainers != null)
                        {
                            foreach (Ally allyComp in allyRetainers)
                                allyComp.AddModToList(Globals.TurnModifierFromObject(currentSpell.BuffModifier));
                        }

                        if (playerRetainer != null)
                            playerRetainer.AddModToList(Globals.TurnModifierFromObject(currentSpell.BuffModifier));
                    }
                    else
                    {
                        foreach (Enemy enemyComp in enemyRetainers)
                            enemyComp.AddModToList(Globals.TurnModifierFromObject(currentSpell.BuffModifier));

                        if (allyRetainers != null)
                        {
                            foreach (Ally allyComp in allyRetainers)
                                allyComp.AddModToList(Globals.TurnModifierFromObject(currentSpell.DebuffModifier));
                        }

                        if (playerRetainer != null)
                            playerRetainer.AddModToList(Globals.TurnModifierFromObject(currentSpell.DebuffModifier));
                    }


                    bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(23, -1, null, spellUser);
                }
                else
                    bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(22);

                if (spellUser is Player || spellUser is Ally)
                    bhReference.DisablePlayerTurn();

                break;
            case 63:                    // Rampage
                SpellCasterCasts();
                bool isLeft = spellUser is Player || spellUser is Ally;

                for (int i = 0; i < 5; ++i)
                {
                    int randomTargetValue;

                    if (isLeft)
                    {
                        if (enemyRetainers.All(enemy => enemy.HasDied))
                        {
                            bhReference.DisablePlayerTurn();
                            break;
                        }

                        do
                        {
                            spellTarget = enemyRetainers[UnityEngine.Random.Range(0, enemyRetainers.Length)];

                        } while (spellTarget.HasDied);

                        int damageRoll = UtilityHandler.RollValue(currentSpell.Dice.ToArray());
                        totalDamage += damageRoll;

                        await InitializeSpellEffect();
                        await bhReference.AttackWithSpell(damageRoll, currentSpell, spellUser, spellTarget, false);
                    }
                    else
                    {
                        if (playerRetainer != null)
                        {
                            if (allyRetainers != null)
                            {
                                if (playerRetainer.HasDied && allyRetainers.All(ally => ally.HasDied))
                                    break;
                            }
                            else
                            {
                                if (playerRetainer.HasDied)
                                    break;
                            }
                        }
                        else
                        {
                            if (allyRetainers.All(ally => ally.HasDied))
                                break;
                        }

                        do
                        {
                            randomTargetValue = UnityEngine.Random.Range(playerRetainer == null ? 0 : -1, allyRetainers.Length);
                            spellTarget = randomTargetValue is -1 ? playerRetainer : allyRetainers[randomTargetValue];

                        } while (spellTarget.HasDied);

                        int damageRoll = UtilityHandler.RollValue(currentSpell.Dice.ToArray());
                        totalDamage += damageRoll;

                        await InitializeSpellEffect();
                        await bhReference.AttackWithSpell(damageRoll, currentSpell, spellUser, spellTarget, false);
                    }
                }

                bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(24, totalDamage, null, spellUser);

                if (spellUser is Player || spellUser is Ally)
                    bhReference.DisablePlayerTurn();

                break;
            case 72:        // Pillage Item stealing
                SwitchIndicatorsOfSide(false, true, true);

                bhReference.AwaitSpellChoice(spellUser);
                condition = await Awaiter();

                SwitchIndicatorsOfSide(false, true, false);
                
                if (!condition)
                {
                    GarbageCleanUp();
                    return;
                }

                SpellCasterCasts();
                await InitializeSpellEffect();
                bhReference.GetUIHandler().ResetUI();

                succeeded = TargetRollsSavingThrow();

                Item itemPillaged = null;

                if (spellTarget is Enemy enemy && !succeeded)
                {
                    spellTarget.Pillaged = true;
                    Dictionary<ItemSO, float> loot = enemy.GetPotentialLoot();

                    ItemSO gatheredLoot = loot.ElementAt(UnityEngine.Random.Range(0, loot.Count)).Key;

                    Item turnedItem;

                    if (gatheredLoot is WeaponSO weapon)
                        turnedItem = Globals.TurnWeaponFromObject(weapon);
                    else if (gatheredLoot is ApparelSO apparel)
                        turnedItem = Globals.TurnApparelFromObject(apparel);
                    else if (gatheredLoot is ConsumableSO consumable)
                        turnedItem = Globals.TurnConsumableFromObject(consumable);
                    else if (gatheredLoot is RelicSO relic)
                        turnedItem = Globals.TurnRelicFromObject(relic);
                    else
                        turnedItem = Globals.TurnItemFromObject(gatheredLoot);

                    itemPillaged = turnedItem;

                    if (turnedItem.ItemName is "Gold")
                        playerRetainer.GetInventory().AddGold(UnityEngine.Random.Range(enemy.GetGoldRange().x, enemy.GetGoldRange().y));
                    else
                        playerRetainer.GetInventory().Add(turnedItem, 1);

                    totalDamage = UtilityHandler.RollValue(Dice.DiceR.Six, Dice.DiceR.Six);

                    await InitializeSpellEffect();

                    await bhReference.AttackWithSpell(totalDamage, currentSpell, spellUser, spellTarget, false);
                    bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(34, totalDamage, spellTarget, null, itemPillaged);
                }
                else if (spellTarget is Enemy && succeeded)
                    bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(35, -1, spellTarget);

                if (spellUser is Player || spellUser is Ally)
                    bhReference.DisablePlayerTurn();

                break;
            case 83:            // Engorge

                bhReference.GetUIHandler().ResetUI();
                succeeded = false;

                if (spellUser is Player || spellUser is Ally)
                {
                    SwitchIndicatorsOfSide(false, true, true);

                    bhReference.AwaitSpellChoice(spellUser);
                    condition = await Awaiter();

                    if (!condition)
                    {
                        GarbageCleanUp();
                        return;
                    }

                    SwitchIndicatorsOfSide(true, true, false);
                }
                else
                    spellTarget = ChooseRandomTargetForEnemy();

                if (!spellTarget.TryGetComponent(out Boss _) && spellTarget.GetComponent<Enemy>().GetEnemyDifficulty() is not Enemy.DifficultyRating.Legendary)
                    succeeded = TargetRollsSavingThrow();
                else
                    succeeded = true;

                int spellValue = spellTarget.RetrieveHealth();

                if (!succeeded)
                {
                    await InitializeSpellEffect();

                    spellTarget.TrueDeath = true;

                    if (spellTarget is Enemy enemy2)
                        bhReference.DamageEnemy(enemy2, spellValue, false);
                    else if (spellTarget is Ally ally)
                        bhReference.DamageAlly(ally, spellValue, false);
                    else
                        bhReference.DamagePlayer(spellValue, false);
                }

                bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(succeeded ? 25 : 26, spellValue, null, spellUser);

                if (spellUser is Player || spellUser is Ally)
                    bhReference.DisablePlayerTurn();

                break;
            case 84:            // Psionic Overload

                SpellCasterCasts();
                bhReference.GetUIHandler().ResetUI();

                int psiHMax = spellUser.RetrieveMaxHealth() < 10 ? 10 : spellUser.RetrieveMaxHealth();
                int psiMMax = spellUser.RetrieveMaxMana() < 10 ? 10 : spellUser.RetrieveMaxMana();

                int psiHeal = UnityEngine.Random.Range(10, psiHMax);
                int psiRefill = UnityEngine.Random.Range(10, psiMMax);

                if (spellUser is Player || spellUser is Ally)
                {
                    if (playerRetainer != null)
                    {
                        playerRetainer.IncreaseHealth(psiHeal);
                        playerRetainer.IncreaseMana(psiRefill);
                        playerRetainer.StatusChanged();
                    }

                    if (allyRetainers != null)
                    {
                        foreach (Ally ally in allyRetainers)
                        {
                            ally.IncreaseHealth(psiHeal);
                            ally.IncreaseMana(psiRefill);
                            ally.StatusChanged();
                        }
                    }
                }
                else
                {
                    foreach (Enemy enemy3 in enemyRetainers)
                    {
                        enemy3.IncreaseHealth(psiHeal);
                        enemy3.IncreaseMana(psiRefill);
                        enemy3.StatusChanged();
                    }
                }

                await InitializeSpellEffect();

                bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(27, -1, null, spellUser);
                if (spellUser is Player || spellUser is Ally)
                    bhReference.DisablePlayerTurn();

                break;
            case 86:            // Standoff
                SpellCasterCasts();
                bhReference.GetUIHandler().ResetUI();

                List<Dice.DiceR> dices = new List<Dice.DiceR>
                {
                    Dice.DiceR.Eight,
                    Dice.DiceR.Eight,
                    Dice.DiceR.Eight,
                    Dice.DiceR.Eight,
                    Dice.DiceR.Eight
                };

                spellUser.IsPreparing = (true, (PreparationCalls.NextAttackingEntity, dices));

                await InitializeSpellEffect();

                bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(28, -1, null, spellUser);
                if (spellUser is Player || spellUser is Ally)
                    bhReference.DisablePlayerTurn();

                break;
            case 87:            // Precise Shot

                bhReference.GetUIHandler().ResetUI();

                if (spellUser is Player || spellUser is Ally)
                {
                    SwitchIndicatorsOfSide(false, true, true);

                    bhReference.AwaitSpellChoice(spellUser);
                    condition = await Awaiter();

                    if (!condition)
                    {
                        GarbageCleanUp();
                        return;
                    }

                    SwitchIndicatorsOfSide(true, true, false);
                }
                else
                    spellTarget = ChooseRandomTargetForEnemy();

                await InitializeSpellEffect();

                SpellCasterCasts();
                

                List<Dice.DiceR> preciseDice = new List<Dice.DiceR>
                {
                    Dice.DiceR.Ten,
                    Dice.DiceR.Ten,
                    Dice.DiceR.Ten,
                    Dice.DiceR.Ten,
                    Dice.DiceR.Ten,
                    Dice.DiceR.Ten
                };

                spellUser.IsPreparing = (true, (PreparationCalls.NextTurn, new List<object>() { preciseDice, spellTarget, "Precise Shot" }));

                bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(30, -1, null, spellUser);
                if (spellUser is Player || spellUser is Ally)
                    bhReference.DisablePlayerTurn(true);

                break;
        }

        GarbageCleanUp();
    }
    private (bool, bool) UniqueSpellEffectCheck(int spellValue, int effectID)
    {
        bool uniqueEffectActivated = false;
        bool endOfSpell = false;

        switch (effectID)
        {
            case -4:    // Type Damage
                switch (currentSpell.SpellID)
                {
                    case 23:    // Order: Rupture
                        List<Entity> entitiesAffected = new List<Entity>();
                        entitiesAffected.AddRange(enemyRetainers.Where(enemy => enemy.GatherModList().SmartModCheck("Sick")));
                        entitiesAffected.AddRange(allyRetainers.Where(ally => ally.GatherModList().SmartModCheck("Sick")));

                        SpellCasterCasts();
                        bhReference.GetUIHandler().ResetUI();

                        if (entitiesAffected.Count is 0)
                            bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(5);
                        else
                        {
                            Modifier sickMod = AbilityBrain.GetModifierList()["Sick"];

                            // Damage each entity and remove sick from them
                            foreach (Entity entity in entitiesAffected)
                            {
                                int baseValue = spellValue;

                                if (entity is Ally ally)
                                {
                                    if (spellUser is Player player)
                                        baseValue += player.ParseClassDamageModifiers(entity);

                                    bhReference.DamageAlly(ally, spellValue, false);
                                    ally.GatherModList().RemoveModifier(sickMod);
                                    ally.AfflictedStatus();
                                }
                                else if (entity is Enemy enemy)
                                {
                                    if (spellUser is Player player)
                                        baseValue += player.ParseClassDamageModifiers(entity);

                                    bhReference.DamageEnemy(enemy, spellValue, false, false);
                                    enemy.GatherModList().RemoveModifier(sickMod);
                                    enemy.AfflictedStatus();
                                }
                                else if (entity is Player player)
                                {
                                    bhReference.DamagePlayer(spellValue, false);
                                    player.GatherModList().RemoveModifier(sickMod);
                                    player.AfflictedStatus();
                                }
                            }

                            bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(6, spellValue);
                        }

                        uniqueEffectActivated = true;
                        endOfSpell = true;

                        if (spellUser is Player || spellUser is Ally)
                            bhReference.DisablePlayerTurn();

                        break;
                }
                break;
            case -3:    // Type Healing
                break;
            case -2:    // Type Buffing
                break;
            case -1:    // Type Debuffing
                break;
            case 0:     // Damage

                ClimateManager.Weather currentWeather = ClimateManager.Instance.GetWeather();

                switch (currentSpell.SpellID)
                {
                    case 2:                 // Magic Blast check presence of Concentrated and Rapid
                        List<Ability> userAbilities = new List<Ability>(spellUser.GetInventory().Abilities);
                        userAbilities.AddRange(spellUser.GetInventory().limitedAbilities);

                        Dictionary<int, Ability> abilityList = AbilityBrain.GetAbilityList();

                        if (userAbilities.Contains(abilityList[91]))        // Rapid - Additional D6
                            spellValue += UtilityHandler.RollValue(Dice.DiceR.Six);
                        if (userAbilities.Contains(abilityList[92]))        // Concentrated - D6's turn into D10's
                            spellValue = UtilityHandler.RollValue(Dice.DiceR.Ten, Dice.DiceR.Ten);

                        break;
                    case 26:                // Assimilate Golem Instant Kill Health and Mana Absorb
                        SpellCasterCasts();
                        int healthAbsorbed = spellTarget.RetrieveHealth();
                        spellUser.IncreaseHealth(healthAbsorbed / 2);
                        spellUser.IncreaseMana(healthAbsorbed / 4);
                        spellUser.StatusChanged();
                        uniqueEffectActivated = true;
                        endOfSpell = true;
                        bhReference.AttackWithSpell(healthAbsorbed, currentSpell, spellUser, spellTarget, endOfSpell).GetAwaiter();

                        bhReference.GetUIHandler().ResetUI();
                        if (spellUser is Player || spellUser is Ally)
                            bhReference.DisablePlayerTurn();

                        break;
                    case 39:                // Piercing Shot doing double to targets that are resistant to Ranged
                        if (spellTarget.GetWeaponResists().ContainsKey(WeaponSO.Type.Ranged))
                        {
                            if (spellUser is Player player)
                                spellValue += player.ParseClassDamageModifiers(spellTarget);

                            SpellCasterCasts();
                            uniqueEffectActivated = true;
                            endOfSpell = true;
                            bhReference.AttackWithSpell(spellValue * 2, currentSpell, spellUser, spellTarget, endOfSpell).GetAwaiter();

                            bhReference.GetUIHandler().ResetUI();
                            if (spellUser is Player || spellUser is Ally)
                                bhReference.DisablePlayerTurn();
                        }
                        break;
                    case 56:                // Aqua Ball
                        SpellCasterCasts();
                        InitializeSpellEffect().GetAwaiter();

                        if (currentWeather is ClimateManager.Weather.Rainy || currentWeather is ClimateManager.Weather.Monsoon)
                            bhReference.AttackWithSpell(spellValue + DiceRoller.RollDice(new DSix()), currentSpell, spellUser, spellTarget, true).GetAwaiter();
                        else if (currentWeather is not ClimateManager.Weather.Blizzard && currentWeather is not ClimateManager.Weather.Snowy)
                            bhReference.AttackWithSpell(spellValue, currentSpell, spellUser, spellTarget, true).GetAwaiter();

                        uniqueEffectActivated = true;
                        endOfSpell = true;

                        if (currentWeather is ClimateManager.Weather.Snowy || currentWeather is ClimateManager.Weather.Blizzard)
                            bhReference.AttackWithSpell(spellValue + DiceRoller.RollDice(new DSix()), currentSpell, spellUser, spellTarget, endOfSpell).GetAwaiter();

                        bhReference.GetUIHandler().ResetUI();

                        break;
                }
                break;
            case 1:     // Health
                switch (currentSpell.SpellID)
                {
                    case 6:                     // Life Extraction Spell Healing Effect
                        spellValue /= 2;
                        spellUser.IncreaseHealth(spellValue);
                        SpellCasterCasts();
                        uniqueEffectActivated = true;
                        endOfSpell = true;
                        bhReference.CallSpellActionText(spellUser.GetName(), spellTarget.GetName(), currentSpell, spellValue);
                        bhReference.DisablePlayerTurn();
                        break;
                }
                break;
            case 2:     // Buff
                break;
            case 3:     // Debuff
                break;
        }

        return (uniqueEffectActivated, endOfSpell);
    }
    /// <summary>
    /// Deciphers which effect was triggered by taking the target that was just gathered and 
    /// matching it with the correct Entity Path for the desired effects
    /// </summary>
    private List<int> DecipherEffect()
    {
        foreach (Dictionary<Entity.Entities, List<int>> item in currentSpell.Paths)
        {
            Dictionary<Entity.Entities, List<int>> effectSelection = item;
            foreach (KeyValuePair<Entity.Entities, List<int>> pair in effectSelection)
            {
                switch (pair.Key)
                {
                    case Entity.Entities.Companion:  // General Left side target
                        if (spellTarget is Player || spellTarget is Ally)
                            return pair.Value;
                        break;
                    case Entity.Entities.Companions: // All left side targets
                        if (spellTarget is Player || spellTarget is Ally)
                            return pair.Value;
                        break;
                    case Entity.Entities.Player:     // Only a player effect
                        if (spellTarget is Player)
                            return pair.Value;
                        break;
                    case Entity.Entities.Ally:       // Only an ally effect
                        if (spellTarget is Ally)
                            return pair.Value;
                        break;
                    case Entity.Entities.Allies:     // Affects all allies
                        if (spellTarget is Ally)
                            return pair.Value;
                        break;
                    case Entity.Entities.Enemy:      // General right side target
                        if (spellTarget is Enemy)
                            return pair.Value;
                        break;
                    case Entity.Entities.Enemies:    // All right side targets
                        if (spellTarget is Enemy)
                            return pair.Value;
                        break;
                    case Entity.Entities.Boss:       // Unique effect when a boss is selected
                        if (spellTarget is Boss)
                            return pair.Value;
                        break;
                }
            }
        }

        return null;
    }
    private (bool, bool) EnableUniqueTargettingSystem()
    {
        bool output = false;
        bool isSelf = false;

        switch (currentSpell.SpellID)
        {
            case 26:                            // Assimilate Golem Targetting
                foreach (Ally ally in allyRetainers)
                {
                    if (ally.GetName().Contains("Golem"))
                    {
                        ally.GetComponent<Target>().SwitchSpellIndicationMode(true);
                        output = true;
                    }
                }
                foreach (Enemy enemy in enemyRetainers)
                {
                    if (enemy.GetName().Contains("Golem"))
                    {
                        enemy.GetComponent<Target>().SwitchSpellIndicationMode(true);
                        output = true;
                    }
                }
                break;
            case 33:                            // Life Contract Self Targetting
                spellTarget = spellUser;
                output = true;
                isSelf = true;
                break;
            case 35:                            // Ignite Self Targetting
                spellTarget = spellUser;
                output = true;
                isSelf = true;
                break;
            case 43:                            // Erratic Bursts Random Targetting
                if (spellUser is Ally || spellUser is Player)
                {
                    do
                    {
                        spellTarget = enemyRetainers[UnityEngine.Random.Range(0, enemyRetainers.Length)];

                    } while (spellTarget.HasDied);
                }
                else
                {
                    do
                    {
                        int targetValue = UnityEngine.Random.Range(-1, allyRetainers.Length);
                        spellTarget = targetValue is -1 ? playerRetainer : allyRetainers[targetValue];

                    } while (spellTarget.HasDied);
                }

                output = true;
                break;
            case 62:                            // Rage Self Targetting
                spellTarget = spellUser;
                output = true;
                isSelf = true;
                break;
            case 92:                            // Deflection Self Targetting
                spellTarget = spellUser;
                output = true;
                isSelf = true;
                break;
            default:
                break;
        }

        return (output, isSelf);
    }
    /// <summary>
    /// General method calls for when the enemies or player use a buff or debuff spell on another target
    /// </summary>
    /// <param name="alwaysPut">Indicator if the buff/debuff is always put onto the target</param>
    private async Task NonDamageSpellCall(bool savingThrowSucceeded, bool endSpell = true)
    {
        // Checks if the debuff or buff requires a certain characteristic of the target, such as a mod needing to be present
        bool uniqueCheck = UniqueNonDamageChecks(currentSpell.SpellID);

        if (endSpell)
        {
            // Modifies the spell user's stats
            SpellCasterCasts();
            await InitializeSpellEffect();
        }

        if (uniqueCheck)
        {
            Modifier modifier = Globals.TurnModifierFromObject(currentSpell.Modifier);

            if (modifier.Type is ModifierSO.Type.Buff)
            {
                int baseTurnValueToAdd = 0;

                // Turn amount increased for buffs
                if (spellUser.GetInventory().Abilities.Contains(AbilityBrain.GetAbilityList()[114]) && spellElement is SpellSO.Element.Necro && modifier.Type is ModifierSO.Type.Buff)
                    baseTurnValueToAdd += 2;

                spellTarget.AddModToList(modifier, baseTurnValueToAdd);
            }
            else if (modifier.Type is ModifierSO.Type.Debuff && savingThrowSucceeded is false)
                spellTarget.AddModToList(modifier);
        }
        
        // Magic Devourer
        if (spellTarget.GetInventory().Abilities.Contains(AbilityBrain.GetAbilityList()[86]))
        {
            spellTarget.IncreaseMana((int)Math.Floor(currentSpell.SpellCost * .5f));
            spellTarget.StatusChanged();
        }

        if (endSpell)
            await bhReference.AttackWithSpell(valueRetainer, currentSpell, spellUser, spellTarget, true);
            
    }
    /// <summary>
    /// General method call for when the enemies or player use a buff that affects an entire side
    /// </summary>
    /// <param name="alwaysPut">Indicator if the modifier is always gonna effect the side indicated</param>
    /// <param name="left">Indicator if companion side is affected</param>
    /// <param name="right">Indicator if the enemy side is affected</param>
    private async Task NonDamageSpellCall(bool left, bool right, bool endSpell, bool? isBuff = null)
    {
        bhReference.CallSpellActionText(spellUser, left, right, currentSpell.SpellEffects[0], currentSpell, valueRetainer);
        ModifierSO modObject = isBuff is null ? currentSpell.Modifier : (isBuff is false ? currentSpell.DebuffModifier : currentSpell.BuffModifier);
        Modifier mod = Globals.TurnModifierFromObject(modObject);

        bool succeeded;
        if (left)
        {
            if (mod.Type is ModifierSO.Type.Debuff)
            {
                if (playerRetainer != null)
                {
                    succeeded = TargetRollsSavingThrow(playerRetainer);
                    bool noDebuff = false;
                    if (succeeded && endSpell)
                    {
                        switch (currentSpell.SaveFailEffect)
                        {
                            case SpellSO.SavingThrowFailEffect.SpellFails:
                                break;
                            case SpellSO.SavingThrowFailEffect.HalfSpellValue:
                                damageValues[0] /= 2;
                                break;
                            case SpellSO.SavingThrowFailEffect.NoDebuff:
                                noDebuff = true;
                                break;
                        }
                    }

                    if (!noDebuff)
                        playerRetainer.AddModToList(mod);
                }
                if (allyRetainers != null)
                {
                    for (int i = 0; i < allyRetainers.Length; ++i)
                    {
                        succeeded = TargetRollsSavingThrow(allyRetainers[i]);
                        bool noDebuff = false;
                        if (succeeded && endSpell)
                        {
                            switch (currentSpell.SaveFailEffect)
                            {
                                case SpellSO.SavingThrowFailEffect.SpellFails:
                                    break;
                                case SpellSO.SavingThrowFailEffect.HalfSpellValue:
                                    damageValues[i + 1] /= 2;
                                    break;
                                case SpellSO.SavingThrowFailEffect.NoDebuff:
                                    noDebuff = true;
                                    break;
                            }
                        }

                        if (!noDebuff)
                            allyRetainers[i].AddModToList(mod);
                    }
                }
            }
            else
            {
                playerRetainer?.AddModToList(mod);

                if (allyRetainers != null)
                {
                    foreach (Ally ally in allyRetainers)
                        ally.AddModToList(mod);
                }
            }
        }
        else if (right)
        {
            if (mod.Type is ModifierSO.Type.Debuff)
            {
                for (int i = 0; i < enemyRetainers.Length; ++i)
                {
                    succeeded = TargetRollsSavingThrow(enemyRetainers[i]);
                    bool noDebuff = false;
                    if (succeeded && endSpell)
                    {
                        switch (currentSpell.SaveFailEffect)
                        {
                            case SpellSO.SavingThrowFailEffect.SpellFails:
                                break;
                            case SpellSO.SavingThrowFailEffect.HalfSpellValue:
                                damageValues[i] /= 2;
                                break;
                            case SpellSO.SavingThrowFailEffect.NoDebuff:
                                noDebuff = true;
                                break;
                        }
                    }

                    if (!noDebuff)
                        enemyRetainers[i].AddModToList(mod);
                }
            }
            else
            {
                foreach (Enemy enemy in enemyRetainers)
                    enemy.AddModToList(mod);
            }
        }

        if (endSpell)
        {
            await InitializeSpellEffect();

            if (playerRetainer != null)
            {
                if (playerRetainer.GetInventory().Abilities.Contains(AbilityBrain.GetAbilityList()[86]))
                {
                    playerRetainer.IncreaseMana((int)Math.Floor(currentSpell.SpellCost * .5f));
                    playerRetainer.StatusChanged();
                }
            }

            // Modifies the spell user's stats
            SpellCasterCasts();

            bool check = UniqueSpellTextCheck();

            if (!check)
                await bhReference.AttackWithSpell(damageValues, currentSpell, spellUser, left, right);
        }
    }
    private bool UniqueSpellTextCheck()
    {
        bool check = false;
        switch (currentSpell.SpellID)
        {
            case 14:
                bhReference.GetUIHandler().ResetUI();
                bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(33, -1, null, spellUser);

                if (spellUser is Player || spellUser is Ally)
                    bhReference.DisablePlayerTurn();

                check = true;
                break;
            default:
                break;
        }

        return check;
    }
    private bool UniqueNonDamageChecks(int spellId)
    {
        return spellId switch
        {
            // Create Nightmare requires the target to be sleeping
            21 => spellTarget.GatherModList().SmartModCheck("Sleeping"),

            // All other values simply return true
            _ => true,
        };
    }
    /// <summary>
    /// Cleans the management slate of all information for the next spell use
    /// </summary>
    private void GarbageCleanUp()
    {
        spellUser          = null;
        spellTarget        = null;
        playerRetainer     = null;
        enemyRetainers     = null;
        allyRetainers      = null;
        currentSpell       = null;
        chosenTarget       = null;

        if (enemyTargets is not null)
        {
            foreach (Target t in enemyTargets)
            {
                if (t != null)
                    t.SwitchSpellIndicationMode(false);
            }
        }
        
        if (allyTargets is not null)
        {
            foreach (Target t in allyTargets)
            {
                if (t != null)
                    t.SwitchSpellIndicationMode(false);
            }
        }

        playerTarget?.SwitchSpellIndicationMode(false);

        enemyTargets       = null;
        allyTargets        = null;
        playerTarget       = null;
    }
    /// <summary>
    /// Public call to reset all of the information fields for the spell manager and stops the tasks within the spell manager
    /// </summary>
    public void Cancel()
    {
        StopSpellTask();
        GarbageCleanUp();
    }
    /// <summary>
    /// Chooses a random entity for the enemies target on the left side that is currently active
    /// </summary>
    /// <returns>An entity target</returns>
    private Entity ChooseRandomTargetForEnemy()
    {
        Entity output;
        int index;

        do
        {
            if (playerRetainer != null)
                index = UnityEngine.Random.Range(-1, allyRetainers.Length);
            else
                index = UnityEngine.Random.Range(0, allyRetainers.Length);

            if (index is -1)
                output = playerRetainer;
            else
                output = allyRetainers[index];
        }
        while (output == null || output.IsHidden);

        return output;
    }
    /// <summary>
    /// Filters through each of the enemies in the retainer to determine which is the most damaged
    /// </summary>
    /// <param name="most_Damaged">The Enemy parameter to modify</param>
    /// <returns>The enemy that would benefit the most from the Healing</returns>
    private Enemy FindMostDamaged()
    {
        Enemy output = null;
        Enemy[] damagedEnemies = enemyRetainers
                                    .Where(enemy => enemy.RetrieveHealth() < enemy.RetrieveMaxHealth())
                                    .ToArray();

        if (damagedEnemies.Length is 0)
            output = enemyRetainers[UnityEngine.Random.Range(0, enemyRetainers.Length)];

        // Cycles through all of the damaged enemies for the most damaged
        // If two enemies have the same health than a coin flip for the place
        // holder of being held is essentially held
        foreach (Enemy enemy in damagedEnemies)
        {
            if (output == null)
            {
                output = enemy;
                continue;
            }
            if (output.RetrieveHealth() > enemy.RetrieveHealth())
                output = enemy;
            else if (output.RetrieveHealth() == enemy.RetrieveHealth())
            {
                // TODO ---> Better calculations more than likely
                int randomizedValue = UnityEngine.Random.Range(0 , MaxChance + 1);
                if (randomizedValue > MaxChance / 2)
                    output = enemy;
            }
        }

        return output;
    }
    /// <summary>
    /// Spell Awaiter for a spell selection for the players and allies that can be called to cancelled
    /// </summary>
    /// <returns>The completed task once an entity has been selected</returns>
    private async Task<bool> Awaiter()
    {
        stopSpellTask = false;

        // Waits until a target is selected by click
        while (chosenTarget == null)
        {
            // Cancel component that will simply stop the process and prevent multiple usages of spells
            if (stopSpellTask)
                return false;

            await Task.Yield();
        }

        return true;
    }
    /// <summary>
    /// Simple method that calls the two primary functions when a spell user casts a spell
    /// </summary>
    private void SpellCasterCasts(int manaCost = 0)
    {
        Dictionary<int, Ability> abList = AbilityBrain.GetAbilityList();
        int spellCost = manaCost is 0 ? currentSpell.SpellCost : manaCost;
        
        // 116 ---> Mana Singularity
        if (spellUser.GetInventory().Abilities.Contains(abList[116]) || spellUser.GetInventory().limitedAbilities.Contains(abList[116]))
        {
            int rollValue = UnityEngine.Random.Range(1, 6);

            // If roll was 1 the spell is free in cost
            if (rollValue is 1)
                return;
        }

        // 125 ---> Interference
        spellCost = InterferenceFunction(abList, spellCost);

        spellUser.DecreaseMana(spellCost);
        spellUser.StatusChanged();

        // If any entity on the opposite side of the spell user has Interference the spell cost is increased by a stacking 50%
        int InterferenceFunction(Dictionary<int, Ability> abList, int spellCost)
        {
            if (spellUser is Enemy)
            {
                List<Entity> leftList = new List<Entity>(allyRetainers)
                {
                    playerRetainer
                };

                foreach (Entity companion in leftList)
                {
                    Inventory companionInven = companion.GetInventory();

                    if (companionInven.Abilities.Contains(abList[125]) || companionInven.limitedAbilities.Contains(abList[125]))
                        spellCost += (int)Math.Ceiling(spellCost * .5);
                }
            }
            else
            {
                foreach (Enemy enemy in enemyRetainers)
                {
                    Inventory enemyInven = enemy.GetInventory();

                    if (enemyInven.Abilities.Contains(abList[125]) || enemyInven.limitedAbilities.Contains(abList[125]))
                        spellCost += (int)Math.Ceiling(spellCost * .5);
                }
            }

            return spellCost;
        }
    }
    /// <summary>
    /// Switches a designated sides indicators when necessary
    /// </summary>
    /// <param name="left">The companion side indicators</param>
    /// <param name="right">The enemy side indicators</param>
    /// <param name="status">The condition of the targets indicators</param>
    private void SwitchIndicatorsOfSide(bool left, bool right, bool status)
    {
        if (left)                                   // Companion side
        {
            if (SpellIsNotSelfTargetting(playerTarget))
                playerTarget?.SwitchSpellIndicationMode(status);
            if (allyTargets != null)
            {
                foreach (Target allyTarget in allyTargets)
                {
                    if (SpellIsNotSelfTargetting(allyTarget))
                        allyTarget.SwitchSpellIndicationMode(status);
                }
            }
        }

        if (right && enemyTargets != null)     // Enemy side
        {
            foreach (Target enemyTarget in enemyTargets)
            {
                if (SpellIsNotSelfTargetting(enemyTarget))
                    enemyTarget.SwitchSpellIndicationMode(status);
            }
        }
    }
    private bool SpellIsNotSelfTargetting(Target potentialUser)
    {
        if (potentialUser.gameObject.GetComponent<Entity>().GetName() != spellUser.GetName())
            return true;
        else
        {
            return currentSpell.SpellName switch
            {
                "Mana Transfusion" => false,

                _ => true,
            };
        }
    }
    /// <summary>
    /// Rolls the targets saving throw based on the current spells configuration
    /// </summary>
    /// <param name="target">Entity parameter set to null, for when multiple targets are considered, otherwise use spellTarget</param>
    /// <returns>True if succeeded, false if otherwise</returns>
    private bool TargetRollsSavingThrow(Entity target = null)
    {
        bool outcome;
        int addToThrowRequirement = 0;

        // Death and Decay increased throw requirement
        if (spellUser.GetInventory().Abilities.Contains(AbilityBrain.GetAbilityList()[114]) && spellElement is SpellSO.Element.Necro)
            addToThrowRequirement = 1;

        if (target == null)
        {
            outcome = UtilityHandler.RollSavingThrow(DecipherSaveThrow(), spellTarget, spellUser) >= (currentSpell.SavingThrow.y + spellUser.GetStat(DecipherSaveThrow()) + addToThrowRequirement);
        }
        else
        {
            outcome = UtilityHandler.RollSavingThrow(DecipherSaveThrow(), target, spellUser) >= (currentSpell.SavingThrow.y + spellUser.GetStat(DecipherSaveThrow()) + addToThrowRequirement);
        }

        return outcome;

        EntityStats.Stat DecipherSaveThrow()
        {
            return currentSpell.SavingThrow.x switch
            {
                0 => EntityStats.Stat.Strength,
                1 => EntityStats.Stat.Intelligence,
                2 => EntityStats.Stat.Constitution,
                3 => EntityStats.Stat.Dexterity,
                4 => EntityStats.Stat.Charisma,
                5 => EntityStats.Stat.Wisdom,
                6 => EntityStats.Stat.Luck,

                _ => throw new InvalidOperationException(),
            };
        }
    }
    private bool FieldFocusCheck()
    {
        bool extraDice = false;
        if (spellUser.GetInventory().Abilities.Contains(AbilityBrain.GetAbilityList()[93]))
        {
            Ability abRef = spellUser.GetInventory().Abilities.Where(ability => ability.AbilityID is 93).FirstOrDefault();
            SpellSO.Element elementChosen = (SpellSO.Element)((List<object>)abRef.ReferenceObject)[0];

            if (spellElement == elementChosen)
            {
                try
                {
                    extraDice = currentSpell.Dice.Count > 0;
                }
                catch { /* If the spell is not a damaging spell */ }
            }
        }

        return extraDice;
    }
    private async Task InitializeSpellEffect(bool isSummoning = false)
    {
        try
        {
            if (currentSpell.SpellEffectSequence == null)
                throw new InvalidOperationException();

            // Just for Boss Encounters right now
            if (Globals.IsBossEncounter)
            {
                GameObject effectData = Instantiate(currentSpell.SpellEffectSequence, new Vector3(0f, 0f), Quaternion.identity);

                int castLocation = PositionHandler.Instance.GetEntityPositionIndex(spellUser);
                int targetLocation = -1;
                if (spellTarget != null)
                    targetLocation = PositionHandler.Instance.GetEntityPositionIndex(spellTarget);
                else if (spellTarget == null)
                {
                    if (isSummoning)
                    {
                        targetLocation = PositionHandler.Instance.GetEmptyPosition(spellUser);

                        if (targetLocation is -1)
                            return;
                    }
                }

                bool isTargettingSameSide = isSummoning;

                SpellVisualEffects visualEffectData = effectData.GetComponent<SpellVisualEffects>();
                visualEffectData.GatherLocationSequence(castLocation, targetLocation, spellUser is Enemy, isTargettingSameSide);

                await visualEffectData.BeginEffectSequence(spellUser is Enemy);
            }
        }
        catch { }
    }
}