Encounter / Assets / Scripts / EntityHandler / Entity.cs
Entity.cs
Raw
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using UnityEngine;

public class Entity : MonoBehaviour
{
    public enum Entities
    {
        Companion,
        Companions,
        Player,
        Ally,
        Allies,
        Minion,
        Minions,
        Enemy,
        Enemies,
        Boss
    }

    public enum EntityType
    {
        Golem,
        Undead,
        Humanoid,
        Demonic,
        Beast
    }

    [Header("Entity Common Values")]
    [SerializeField] protected string Entity_Name;
    [SerializeField] protected Sprite entity_Sprite;
    [SerializeField] protected EntityType entityType;
    [SerializeField] protected List<Dice.DiceR> HealthDice;
    [SerializeField] protected int baseHealth;
    [SerializeField] protected List<Dice.DiceR> ManaDice;
    [SerializeField] protected int baseMana;
    [SerializeField] protected Vector2Int ArmorRange;
    [SerializeField] protected List<GameObject> availableSkillTrees;
    [SerializeField] protected bool IsFlying;
    
    [SerializeField]
    [Range(0,2)]
    [Tooltip("0 is the smallest entity sprite while 2 is the largest entity sprite")]
    protected int sizeIndic;

    [SerializeField] protected Collider2D secondaryCollider;
    [SerializeField] protected SpellSO.Element entityElement;
    [SerializeField] protected List<SpellSO.Element> elementWeaknesses;
    [SerializeField] protected List<WeaponSO.Type> weaponWeaknesses;
    [SerializeField] protected List<SpellSO.Element> elementResistances;
    [SerializeField] protected List<WeaponSO.Type> weaponResistances;
    protected int Health;
    protected int Mana;
    protected int Armor;
    protected int maxHealth;
    protected int maxMana;
    protected int maxArmor;

    private int numberOfActions;
    private int numberOfAttacks;

    protected Dictionary<string, object> ClassModifications;

    public int ActionCount
    {
        get => numberOfActions;
        set => numberOfActions = value;
    }

    public int AttackCount
    {
        get => numberOfAttacks;
        set => numberOfAttacks = value;
    }

    protected readonly Dictionary<int, int> levelMapExpRequirements = new Dictionary<int, int>()
    {
        { 2, 150 },       // 150 total exp needed
        { 3, 300 },       // 450 total exp needed
        { 4, 550 },       // 1000 total exp needed
        { 5, 800 },       // 1800 total exp needed
        { 6, 1250 },      // 3050 total exp needed
        { 7, 1700 },      // 4750 total exp needed
        { 8, 2250 },      // 7000 total exp needed
        { 9, 2500 },      // 9500 total exp needed
        { 10, 3000 },     // 12500 total exp needed
        { 11, 3500 },     // 16000 total exp needed
        { 12, 4000 },     // 20000 total exp needed
        { 13, 5000 },     // 25000 total exp needed
        { 14, 6500 },     // 31500 total exp needed
        { 15, 8000 },     // 39500 total exp needed
        { 16, 10000 },    // 49500 total exp needed
        { 17, 12500 },    // 62000 total exp needed
        { 18, 15000 },    // 77000 total exp needed
        { 19, 17500 },    // 94500 total exp needed
        { 20, 20000 }     // 114500 total exp needed
    };

    protected List<string> skillTreeNames;

    protected int CurrentLevelExperience;
    protected int TotalExperience;
    protected int Level;
    protected const int MaxLevel = 20;

    protected int LastLevelIntegrated;
    protected bool HasLeveledUp;

    protected int SkillPoints;

    public bool HasDied 
    {
        get => this.Health is 0; 
    }
    public bool IsHidden {  get; protected set; }
    public bool Pillaged { get; set; }
    public bool ForcedMiss { get; set; }
    public bool TrueDeath { get; set; }
    public (bool, (SpellManagement.PreparationCalls, object)) IsPreparing { get; set; }

    private float manaRegenRate = .1f;

    protected EntityStats stats;
    protected EntityModList modList;
    protected EntityAnimation animator;

    protected bool genStatsChanged = false;
    protected bool statusAfflicted = false;

    protected Dictionary<Modifier, int> dotModifierValues;
    protected bool dotApplied = false;
    protected int turnsLeft = 0;
    protected int dotDamagePerTurn = 0;

    private Dictionary<Modifier, (int, EntityStats.Stat)> modifiersWithChance;

    private List<Modifier> implementedMods;

    protected int UniqueID;                                                         // The Instance ID of the entity
    public List<string> EntityFlags;                                 // This flag system will be used to determine the state of the Entity, such as access to Dream Master unique effects
    public Transform spawnedPosition;

    public int GetSkillPoints()
        => this.SkillPoints;
    public bool HasSkillPoints()
        => this.SkillPoints != 0; 
    public bool TryUseSkillPoint(int skillCost)
    {
        if (skillCost > this.SkillPoints)
            return false;
        else
        {
            this.SkillPoints -= skillCost;
            return true;
        }
    }
    public void HideEntity()
        => this.IsHidden = true;
    public void ShowEntity()
        => this.IsHidden = false;
    /// <summary>
    /// Retrieval for the Instance ID of the entity, since there can be multiple entities with the same name
    /// </summary>
    /// <returns>Unique Entity ID</returns>
    public int GetUniqueID()
        => this.UniqueID;
    /// <summary>
    /// Triggers the update method to update the HP 
    /// </summary>
    public void StatusChanged() 
        => genStatsChanged = true;
    public void AfflictedStatus() 
        => statusAfflicted = true;
    public EntityModList GatherModList() 
        => modList;
    public EntityStats GatherStats() 
        => stats;
    public SpellSO.Element GetEntityType()
        => entityElement;
    public int GetSizeIndic()
        => this.sizeIndic;
    private void Awake()
    {
        this.stats               = stats != null ? stats : GetComponent<EntityStats>();
        this.modList             = new EntityModList();
        this.dotModifierValues   = new Dictionary<Modifier, int>();
        this.implementedMods     = new List<Modifier>();
        this.EntityFlags         = new List<string>();
        this.ClassModifications = new Dictionary<string, object>();

        this.SkillPoints = 0;

        // If skill point cheat is turned on
        if (Globals.Add100SkillPointsCheat)
            this.SkillPoints += 100;

        this.Level = 1;

        this.ActionCount = 1;
        this.AttackCount = 1;

        this.LastLevelIntegrated = 1;
        this.HasLeveledUp = false;

        this.skillTreeNames = new List<string>();
        foreach (GameObject skillTree in this.availableSkillTrees)
            skillTreeNames.Add(skillTree.name);

        UniqueID = GetInstanceID();
    }
    protected virtual void OnEnable()
    {
        // Generates random values based on the dice indicators for the enemy
        if (this is Enemy || this is Ally)
        {
            if (this is Ally && Globals.LargeHealthCheat)
                this.Health         = UtilityHandler.RollValueWithBase(baseHealth + 300, HealthDice.ToArray());
            else
                this.Health         = UtilityHandler.RollValueWithBase(baseHealth, HealthDice.ToArray());

            if (this is Ally && Globals.LargeManaCheat)
                this.Mana           = UtilityHandler.RollValueWithBase(baseMana + 300, ManaDice.ToArray());
            else
                this.Mana           = UtilityHandler.RollValueWithBase(baseMana, ManaDice.ToArray());

            this.Armor              = UnityEngine.Random.Range(ArmorRange.x, ArmorRange.y + 1);
        }

        // Sets the max values for all of the entity components
        this.maxHealth              = this.Health;
        this.maxMana                = this.Mana;
        this.maxArmor               = this.Armor;

        if (this.spawnedPosition == null)
            this.spawnedPosition    = transform.parent;

        this.stats                  = this.stats != null ? this.stats : GetComponent<EntityStats>();
        this.modList                = new EntityModList();
    }
    protected void Update()
    {
        if (genStatsChanged)
            UpdateHealthVis();
        else if (statusAfflicted)
            UpdateStatusFields();
    }
    /// <summary>
    /// Retrieves the name of the enemy object
    /// </summary>
    /// <returns>Enemy name</returns>
    public string GetName() 
        => Entity_Name;
    /// <summary>
    /// Decreases health of the enemy and signals if the enemy has lost all life points
    /// </summary>
    /// <param name="damage">Amount of damage that the enemy is taking</param>
    /// <returns>True if the enemy has lost all health, false if otherwise</returns>
    public bool DecreaseHealth(int damage, bool mercied = false)
    {
        bool stateCheck = this.Health - damage <= 0;
        if (this is Enemy && damage is not 0 && !stateCheck)
            animator.PlayTakeHitAnimation();

        if (stateCheck)
        {
            if (mercied)
                this.Health = 1;
            else
            {
                this.Health = 0;

                if (this is Enemy || this is Player)
                {
                    animator.PlayDeathAnimation();

                    if (this.IsFlying)
                        ActivateSecondaryColliderFall();
                }

                return true;
            }
        }

        this.Health -= damage;

        PhyllSurpriseDamagedCheck();

        return false;

        void PhyllSurpriseDamagedCheck()
        {
            if (GetName().Equals("Phyll") && GetInventory().Abilities.Contains(AbilityBrain.GetAbilityList()[57]))
            {
                Ability abRef = GetInventory().Abilities.Where(ability => ability.AbilityID is 57).FirstOrDefault();
                abRef.active = false;
                this.IsHidden = false;
            }
        }
    }
    /// <summary>
    /// Helps better align the flying enemy deaths with a secondary collider to detect the final sprites collision box
    /// </summary>
    private void ActivateSecondaryColliderFall()
    {
        GetComponent<BoxCollider2D>().enabled = false;
        GetComponent<Rigidbody2D>().bodyType = RigidbodyType2D.Dynamic;
        secondaryCollider.enabled = true;
    }
    /// <summary>
    /// Heals the health of the enemy within the range set by the max health
    /// </summary>
    /// <param name="restoredAmount">Heal amount</param>
    public void IncreaseHealth(int restoredAmount, bool isFirst = true)
    {
        if (isFirst)
        {
            Dictionary<int, Ability> abilityList = AbilityBrain.GetAbilityList();
            if (this.GetInventory().Abilities.Contains(abilityList[108]) || this.GetInventory().limitedAbilities.Contains(abilityList[108]))
                this.IncreaseMana((int)Math.Floor(restoredAmount * .33f), false);
        }

        if (this.Health + restoredAmount > this.maxHealth)
        {
            this.Health = this.maxHealth;
            return;
        } 

        this.Health += restoredAmount;
    }
    /// <summary>
    /// Increases the max health of the entity by the indicated amount
    /// </summary>
    /// <param name="increaseAmount">The amount to increase the max health by</param>
    public void IncreaseMaxHealth(int increaseAmount)
    {
        if (this.maxHealth + increaseAmount <= 3)
        {
            this.maxHealth = 3;
            return;
        }

        this.maxHealth += increaseAmount;
    }
    /// <summary>
    /// The mana used is taken from the mana pool of the enemy
    /// </summary>
    /// <param name="mana_Used">The amount to be taken from the enemies mana</param>
    public void DecreaseMana(int mana_Used)
    {
        if (this.Mana - mana_Used <= 0)
        {
            this.Mana = 0;
            return;
        }

        this.Mana -= mana_Used;
    }
    /// <summary>
    /// The mana is refilled based on the parameter passed with a limit to the max mana
    /// </summary>
    /// <param name="refill">The amount of mana to refill</param>
    public void IncreaseMana(int refill, bool isFirst = true)
    {
        if (isFirst)
        {
            Dictionary<int, Ability> abilityList = AbilityBrain.GetAbilityList();
            if (this.GetInventory().Abilities.Contains(abilityList[108]) || this.GetInventory().limitedAbilities.Contains(abilityList[108]))
                this.IncreaseHealth((int)Math.Floor(refill * .33f), false);
        }

        if (this.Mana + refill > this.maxMana)
        {
            this.Mana = this.maxMana;
            return;
        }

        this.Mana += refill;
    }
    /// <summary>
    /// Increases the max mana of the entity by the indicated amount
    /// </summary>
    /// <param name="increaseAmount">The amount to increase the max mana by</param>
    public void IncreaseMaxMana(int increaseAmount)
    {
        if (this.maxMana + increaseAmount <= 0)
        {
            this.maxMana = 1;
            return;
        }

        this.maxMana += increaseAmount;
    }
    /// <summary>
    /// Handles stats such as health and mana in terms of their visual updates
    /// </summary>
    protected virtual void UpdateHealthVis()
    {
        // DOT damage is applied the same throughout all entities by applying all listed DOT modifiers,
        // when all decrement their turn counters to 0 and are removed, then the DOT application is switched off
        if (dotApplied && dotModifierValues.Count > 0)
            ApplyDOTDamage();
        else
            dotApplied = false;
    }
    private void ApplyDOTDamage()
    {
        for (int i = 0; i < dotModifierValues.Count; i++)
        {
            // Specific values are set up here as reference points for unique modifiers
            KeyValuePair<Modifier, int> col = dotModifierValues.ElementAt(i);
            int damageAmount = 0;

            // If the DOT modifier is 'Experiencing Nightmare', then check to make sure the sleep mod is also active
            // otherwise remove this specific modifier from both the DOT Dictionary and the Mod List Dictionary
            if (col.Key.Name.Equals("Experiencing Nightmare"))
            {
                if (!this.modList.SmartModCheck("Sleeping"))
                {
                    dotModifierValues.Remove(col.Key);
                    this.modList.RemoveModifier(col.Key);
                    continue;
                }
                
                // Experiencing Nightmare increments its damage each turn
                damageAmount = col.Value;
                damageAmount += UtilityHandler.RollValue(col.Key.GetDiceValues().ToArray());
            }

            // When the damage value hasn't been affected simply retrieve the damage value indicated in the Tuple
            if (damageAmount is 0)
                damageAmount = col.Value;

            // Then decrease this entities health and update the DOT modifier values in the Dictionary
            DecreaseHealth(damageAmount);
            dotModifierValues.Remove(col.Key);
            dotModifierValues.Add(col.Key, damageAmount);
        }
    }
    /// <summary>
    /// Clears out all of the modifiers gained from the battle 
    /// </summary>
    public void ClearModifiers()
    {
        dotModifierValues.Clear();
        dotDamagePerTurn = 0;
        dotApplied = false;

        modList.ClearModifiers();
        this.AfflictedStatus();
    }
    protected virtual void UpdateStatusFields()
    {
        this.UpdateModsOfEntity();
        statusAfflicted = false;
    }
    protected void UpdateModsOfEntity()
    {
        List<Modifier> modsToRemove = new List<Modifier>();
        foreach (KeyValuePair<Modifier, int> mod in modList)
        {
            if (mod.Value is 0)
            {
                modsToRemove.Add(mod.Key);
                RemoveModBonus(mod);
            }
            else
            {
                if (implementedMods.Contains(mod.Key))
                    continue;
                else
                    ImplementModBonus(mod);
            }
        }

        foreach (Modifier modRemoving in modsToRemove)
            modList.RemoveModifier(modRemoving);

        if (this is Enemy enemy)
        {
            Transform modifierTransform = enemy.GetModifierField();
            foreach (Transform mod in modifierTransform)
            {
                ModifierHelper modHelper = mod.GetComponent<ModifierHelper>();
                modHelper.CallTurnDecrement();
            }
        }
    }
    /// <summary>
    /// When the entity is cured, all of the debuff and DOT modifiers are removed from their mod list
    /// </summary>
    public void CleanseAilments()
    {
        List<Modifier> modsToRemove = new List<Modifier>();
        foreach (KeyValuePair<Modifier, int> mod in modList)
        {
            if (mod.Key.Type is ModifierSO.Type.Debuff || mod.Key.Effect is ModifierSO.Effect.DOT)
            {
                modsToRemove.Add(mod.Key);
                RemoveModBonus(mod);
            }
        }

        foreach (Modifier modRemoving in modsToRemove)
            modList.RemoveModifier(modRemoving);

        this.AfflictedStatus();
    }
    private void ImplementModBonus(KeyValuePair<Modifier, int> mod)
    {
        switch (mod.Key.Effect)
        {
            case ModifierSO.Effect.Stat:
                foreach (EntityStats.Stat statValue in mod.Key.GetStatsModified())
                {
                    if (mod.Key.IsValues[0])                // Base Values
                        stats.SetModifierToStat(statValue, mod.Key.GetNormalValue());
                    else if (mod.Key.IsValues[1])           // Percentage Values
                        stats.SetModifierToStat(statValue, mod.Key.GetPercentageValue());
                    else                                    // Dice Values
                        stats.SetModifierToStat(statValue, mod.Key.GetDiceValues());
                }

                break;
            case ModifierSO.Effect.HPRegen:
                if (mod.Key.IsValues[0])                // Base Values
                    stats.SetModifierToStat(EntityStats.Stat.HPRegen, mod.Key.GetNormalValue());
                else if (mod.Key.IsValues[1])           // Percentage Values
                    stats.SetModifierToStat(EntityStats.Stat.HPRegen, mod.Key.GetPercentageValue());
                else                                    // Dice Values
                    stats.SetModifierToStat(EntityStats.Stat.HPRegen, mod.Key.GetDiceValues());
                break;
            case ModifierSO.Effect.ManaRegen:
                if (mod.Key.IsValues[0])                // Base Values
                    stats.SetModifierToStat(EntityStats.Stat.MPRegen, mod.Key.GetNormalValue());
                else if (mod.Key.IsValues[1])           // Percentage Values
                    stats.SetModifierToStat(EntityStats.Stat.MPRegen, mod.Key.GetPercentageValue());
                else                                    // Dice Values
                    stats.SetModifierToStat(EntityStats.Stat.MPRegen, mod.Key.GetDiceValues());
                break;
            case ModifierSO.Effect.ArmorModifier:
                if (mod.Key.IsValues[0])                // Base Values
                    stats.SetModifierToStat(EntityStats.Stat.Armor, mod.Key.GetNormalValue());
                else if (mod.Key.IsValues[1])           // Percentage Values
                    stats.SetModifierToStat(EntityStats.Stat.Armor, mod.Key.GetPercentageValue());
                else                                    // Dice Values
                    stats.SetModifierToStat(EntityStats.Stat.Armor, mod.Key.GetDiceValues());
                break;
            case ModifierSO.Effect.Accuracy:
                if (mod.Key.IsValues[0])                // Base Values
                    stats.SetModifierToStat(EntityStats.Stat.ToHit, mod.Key.GetNormalValue());
                else if (mod.Key.IsValues[1])           // Percentage Values
                    stats.SetModifierToStat(EntityStats.Stat.ToHit, mod.Key.GetPercentageValue());
                else                                    // Dice Values
                    stats.SetModifierToStat(EntityStats.Stat.ToHit, mod.Key.GetDiceValues());
                break;
            case ModifierSO.Effect.Crit:
                if (mod.Key.IsValues[0])                // Base Values
                    stats.SetModifierToStat(EntityStats.Stat.CritDamage, mod.Key.GetNormalValue());
                else if (mod.Key.IsValues[1])           // Percentage Values
                    stats.SetModifierToStat(EntityStats.Stat.CritDamage, mod.Key.GetPercentageValue());
                else                                    // Dice Values
                    stats.SetModifierToStat(EntityStats.Stat.CritDamage, mod.Key.GetDiceValues());
                break;
            case ModifierSO.Effect.DamageModifier:
                if (mod.Key.IsValues[0])                // Base Values
                    stats.SetModifierToStat(EntityStats.Stat.Damage, mod.Key.GetNormalValue());
                else if (mod.Key.IsValues[1])           // Percentage Values
                    stats.SetModifierToStat(EntityStats.Stat.Damage, mod.Key.GetPercentageValue());
                else                                    // Dice Values
                    stats.SetModifierToStat(EntityStats.Stat.Damage, mod.Key.GetDiceValues());
                
                break;
            case ModifierSO.Effect.DOT:
                if (mod.Key.IsValues[0])        // Is Value
                    dotModifierValues.Add(mod.Key, mod.Key.GetNormalValue());
                else if (mod.Key.IsValues[1])   // Is Percent
                    dotModifierValues.Add(mod.Key, (int)Math.Floor((double)this.maxHealth * mod.Key.GetPercentageValue()));
                else                            // Is Dice
                    dotModifierValues.Add(mod.Key, UtilityHandler.RollValue(mod.Key.GetDiceValues().ToArray()));

                dotApplied = true;
                break;
            case ModifierSO.Effect.Other:
                switch (mod.Key.Name)
                {
                    case "Unquenched":
                        // Limit INT by 2
                        this.GatherStats().SetModifierToStat(EntityStats.Stat.Intelligence, -2);
                        this.GetInventory().limitedAbilities.Add(AbilityBrain.GetAbilityList()[86]);
                        this.GetInventory().limitedSpells.Add(AbilityBrain.GetSpellList()[83]);

                        break;
                }
                break;
        }

        implementedMods.Add(mod.Key);
    }
    private async void RemoveModBonus(KeyValuePair<Modifier, int> mod)
    {
        implementedMods.Remove(mod.Key);
        switch (mod.Key.Effect)
        {
            case ModifierSO.Effect.Stat:
                foreach (EntityStats.Stat statValue in mod.Key.GetStatsModified())
                {
                    if (mod.Key.IsValues[0])                // Base Values
                        stats.RemoveModifierToStat(statValue, mod.Key.GetNormalValue());
                    else if (mod.Key.IsValues[1])           // Percentage Values
                        stats.RemoveModifierToStat(statValue, mod.Key.GetPercentageValue());
                    else                                    // Dice Values
                        stats.RemoveModifierToStat(statValue, mod.Key.GetDiceValues());
                }

                break;
            case ModifierSO.Effect.HPRegen:
                if (mod.Key.IsValues[0])                // Base Values
                    stats.RemoveModifierToStat(EntityStats.Stat.HPRegen, mod.Key.GetNormalValue());
                else if (mod.Key.IsValues[1])           // Percentage Values
                    stats.RemoveModifierToStat(EntityStats.Stat.HPRegen, mod.Key.GetPercentageValue());
                else                                    // Dice Values
                    stats.RemoveModifierToStat(EntityStats.Stat.HPRegen, mod.Key.GetDiceValues());
                break;
            case ModifierSO.Effect.ManaRegen:
                if (mod.Key.IsValues[0])                // Base Values
                    stats.RemoveModifierToStat(EntityStats.Stat.MPRegen, mod.Key.GetNormalValue());
                else if (mod.Key.IsValues[1])           // Percentage Values
                    stats.RemoveModifierToStat(EntityStats.Stat.MPRegen, mod.Key.GetPercentageValue());
                else                                    // Dice Values
                    stats.RemoveModifierToStat(EntityStats.Stat.MPRegen, mod.Key.GetDiceValues());
                break;
            case ModifierSO.Effect.ArmorModifier:
                if (mod.Key.IsValues[0])                // Base Values
                    stats.RemoveModifierToStat(EntityStats.Stat.Armor, mod.Key.GetNormalValue());
                else if (mod.Key.IsValues[1])           // Percentage Values
                    stats.RemoveModifierToStat(EntityStats.Stat.Armor, mod.Key.GetPercentageValue());
                else                                    // Dice Values
                    stats.RemoveModifierToStat(EntityStats.Stat.Armor, mod.Key.GetDiceValues());
                break;
            case ModifierSO.Effect.Accuracy:
                if (mod.Key.IsValues[0])                // Base Values
                    stats.RemoveModifierToStat(EntityStats.Stat.ToHit, mod.Key.GetNormalValue());
                else if (mod.Key.IsValues[1])           // Percentage Values
                    stats.RemoveModifierToStat(EntityStats.Stat.ToHit, mod.Key.GetPercentageValue());
                else                                    // Dice Values
                    stats.RemoveModifierToStat(EntityStats.Stat.ToHit, mod.Key.GetDiceValues());
                break;
            case ModifierSO.Effect.Crit:
                if (mod.Key.IsValues[0])                // Base Values
                    stats.RemoveModifierToStat(EntityStats.Stat.CritDamage, mod.Key.GetNormalValue());
                else if (mod.Key.IsValues[1])           // Percentage Values
                    stats.RemoveModifierToStat(EntityStats.Stat.CritDamage, mod.Key.GetPercentageValue());
                else                                    // Dice Values
                    stats.RemoveModifierToStat(EntityStats.Stat.CritDamage, mod.Key.GetDiceValues());
                break;
            case ModifierSO.Effect.DamageModifier:
                if (mod.Key.IsValues[0])                // Base Values
                    stats.RemoveModifierToStat(EntityStats.Stat.Damage, mod.Key.GetNormalValue());
                else if (mod.Key.IsValues[1])           // Percentage Values
                    stats.RemoveModifierToStat(EntityStats.Stat.Damage, mod.Key.GetPercentageValue());
                else                                    // Dice Values
                    stats.RemoveModifierToStat(EntityStats.Stat.Damage, mod.Key.GetDiceValues());
                break;
            case ModifierSO.Effect.DOT:
                try
                {
                    dotModifierValues.Remove(mod.Key);
                }
                catch { }

                break;
            case ModifierSO.Effect.Other:
                await DecipherUniqueEffectRemoved(mod.Key);
                break;
        }
    }
    private async Task DecipherUniqueEffectRemoved(Modifier mod)
    {
        switch (mod.Name)
        {
            case "Life Contract":
                List<int> retainedDamage = mod.RetentionObject as List<int>;

                // Deal damage to random target if damage accumulated exceeds the heal value
                if (retainedDamage[1] >= retainedDamage[0])
                {
                    if (this is Enemy)
                        await BattleHandler.Instance.DealDamageToLeftRandom(retainedDamage[0]);
                    else
                        await BattleHandler.Instance.DealDamageToRightRandom(retainedDamage[0]);
                }
                // Otherwise take the health regenerated back
                else
                {
                    int damageValue = retainedDamage[0];

                    if (this is Enemy enemy)
                    {
                        BattleHandler.Instance.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(15, damageValue, enemy);
                        BattleHandler.Instance.DamageEnemy(enemy, damageValue, false);
                    }
                    else if (this is Player player)
                    {
                        BattleHandler.Instance.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(15, damageValue, player);
                        BattleHandler.Instance.DamagePlayer(damageValue, false);
                    }
                    else
                    {
                        BattleHandler.Instance.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(15, damageValue, this as Ally);
                        BattleHandler.Instance.DamageAlly(this as Ally, damageValue, false);
                    }
                }
                
                break;
            case "Unquenched":
                this.GatherStats().RemoveModifierToStat(EntityStats.Stat.Intelligence, -2);
                this.GetInventory().Remove(AbilityBrain.GetAbilityList()[86]);
                this.GetInventory().Remove(AbilityBrain.GetSpellList()[83]);
                break;
        }
    }
    /// <summary>
    /// Call for the Entities order initiative value for the order of combat class
    /// </summary>
    /// <returns></returns>
    public int RollOrderValue()
        => DiceRoller.RollDice(new DTwenty()) + stats.GetStat(EntityStats.Stat.Dexterity);
    /// <summary>
    /// Getter for the current health of this entity
    /// </summary>
    /// <returns>This entities current health</returns>
    public int RetrieveHealth() 
        => this.Health;
    /// <summary>
    /// Getter for Max Health
    /// </summary>
    /// <returns>This entities Max Health</returns>
    public int RetrieveMaxHealth() 
        => this.maxHealth;
    /// <summary>
    /// Retrieves the health percentage of the entity for comparisons of others
    /// </summary>
    /// <returns>This entities health percentage</returns>
    public int GetHealthPercentage()
    {
        float currentHealth = (float)this.Health;
        float maxHP = (float)this.maxHealth;

        return (int)(currentHealth / maxHP * 100);
    }
    /// <summary>
    /// Retrieves this entities current mana value
    /// </summary>
    /// <returns>This entities current mana</returns>
    public int RetrieveMana() 
        => this.Mana;
    /// <summary>
    /// Getter for the max mana of this entity
    /// </summary>
    /// <returns>This entities Max Mana</returns>
    public int RetrieveMaxMana() 
        => this.maxMana;
    /// <summary>
    /// Retrieval for the percentage of missing mana this entity has
    /// which is then used for comparisons
    /// </summary>
    /// <returns>Percentage of missing mana</returns>
    public int GetManaPercentage()
    {
        float currentMana = (float)this.Mana;
        float maxMA = (float)this.maxMana;

        return (int)(currentMana / maxMA * 100);
    }
    public int RetrieveArmor() 
        => this.Armor;
    public int RetrieveMaxArmor() 
        => this.maxArmor;
    public void SetArmor(int newArmor)
    {
        this.maxArmor = newArmor;
        this.Armor = newArmor;
    }
    public int GetStat(EntityStats.Stat stat) 
        => stats.GetStat(stat);
    public int GetBaseStat(EntityStats.Stat stat)
        => stats.GetBaseStat(stat);
    /// <summary>
    /// Retrieval for the entities level
    /// </summary>
    /// <returns>The current level of the entity</returns>
    public int GetLevel()
        => this.Level;
    public void Revive(bool hasFainted = false, int healAmount = 1)
    {
        if (TrueDeath)
            return;
        
        if (!hasFainted)
        {
            this.Health = this.maxHealth / 2;
            GameManager.Instance.GetComponent<BattleHandler>().Revive(this);
        }
        else
            this.Health = healAmount;

        if (this is Ally ally)
        {
            ally.GetGUI().UpdateStats(ally);
            ally.StatusChanged();

            ally.ReactivateVisually();
        }
        else if (this is Player player)
        {
            player.StatusChanged();
            GeneralUI.Instance.SetInformation(false);

            player.ReactivateVisually();
        }
        else
        {
            Enemy enemy = this as Enemy;
            enemy.UpdateHealthVis();
        }
        Debug.Log($"{this.GetName()} has been revived!");
        
    }
    public void AddModToList(Modifier mod, int baseTurnValue = 0, int substituteTurnValue = 0)
    {
        // Resilient Nature Chcek
        if (mod.Name.Equals("Envenomated") && this is Enemy enemy && enemy.GetInventory().Abilities.Contains(AbilityBrain.GetAbilityList()[18]))
            return;

        // Check to make sure that the mod isn't already present, if it is just increment the turn counter
        if (modList.Contains(mod))
        {
            modList.IncreaseModifierTime(mod, this);
            return;
        }

        int turns;
        if (substituteTurnValue is 0)
        {
            if (mod.LengthOfEffect.Count is not 0)
            {
                if (mod.LengthOfEffect.Contains(Dice.DiceR.Hundred))
                    turns = 999;
                else
                    turns = UtilityHandler.RollValue(mod.LengthOfEffect.ToArray()) + baseTurnValue;
            }
            else
            {
                turns = mod.LengthOfEffectNoDice + baseTurnValue;

                // If Smoldering Rage is available in this entities abilities
                if (mod.Name is "Rage" && GetInventory().Abilities.Contains(AbilityBrain.GetAbilityList()[83]))
                    turns += 2;
            }
        }
        else
            turns = substituteTurnValue;
        

        // Sticky Aura Ability Check
        if (mod.Type is ModifierSO.Type.Buff && this.GetInventory().Abilities.Contains(AbilityBrain.GetAbilityList()[106]))
            turns += 2;

        modList.AddModifier(mod, turns);
        AfflictedStatus();
    }
    public void DecrementTurnsOnMods()
    {
        modList.DecrementTurnsForMods();
        AfflictedStatus();
    }
    /// <summary>
    /// Retrieval for the sprite icon of the entity
    /// </summary>
    /// <returns>Sprite Icon, meant for Party GUI</returns>
    public virtual Sprite GetSprite()
        => entity_Sprite;
    /// <summary>
    /// Sets the modifiers that can potentially happen from the AbilityBrain when checking status' 
    /// </summary>
    /// <param name="m">The Modifier to add</param>
    /// <param name="chance">The chance of the modifier occurred to a target FLOAT VALUE SHOULD BE 0 < | x | < 1 for percentage values</param>
    public void SetModifierAndChance(Modifier m, Vector2Int savingThrow, bool reset = false)
    {
        if (reset)
            modifiersWithChance = new();

        if (modifiersWithChance.ContainsKey(m))
        {
            (int currentValue, EntityStats.Stat statConsidered) = modifiersWithChance[m];

            int addedValue = this.GatherStats().GetStat(statConsidered);
            
            // Guard to prevent lowering of the saving throw
            if (addedValue < 0)
                return;

            int newCurrentValue = currentValue + addedValue;
            modifiersWithChance[m] = (newCurrentValue, statConsidered);
        }
        else
        {
            switch (savingThrow.x)
            {
                case 0:
                    modifiersWithChance.Add(m, (savingThrow.y, EntityStats.Stat.Strength));
                    break;
                case 1:
                    modifiersWithChance.Add(m, (savingThrow.y, EntityStats.Stat.Intelligence));
                    break;
                case 2:
                    modifiersWithChance.Add(m, (savingThrow.y, EntityStats.Stat.Constitution));
                    break;
                case 3:
                    modifiersWithChance.Add(m, (savingThrow.y, EntityStats.Stat.Dexterity));
                    break;
                case 4:
                    modifiersWithChance.Add(m, (savingThrow.y, EntityStats.Stat.Charisma));
                    break;
                case 5:
                    modifiersWithChance.Add(m, (savingThrow.y, EntityStats.Stat.Wisdom));
                    break;
                case 6:
                    modifiersWithChance.Add(m, (savingThrow.y, EntityStats.Stat.Luck));
                    break;
            }
        }
    }
    /// <summary>
    /// Gathers all of the mods from the mod chance dictionary and calculates if they happen for each, then 
    /// creates a new dictionary with true for activated and false for not activated
    /// </summary>
    /// <returns>Dictionary of activate Modifiers</returns>
    public Dictionary<Modifier, bool> CallMods(Entity target)
    {
        Dictionary<Modifier, bool> modTriggers = new();

        if (modifiersWithChance == null || modifiersWithChance.Count is 0)
            return modTriggers;

        foreach (KeyValuePair<Modifier, (int, EntityStats.Stat)> pair in modifiersWithChance)
        {
            int savingThrow = UtilityHandler.RollSavingThrow(pair.Value.Item2, target);
            
            if (savingThrow >= pair.Value.Item1)
                modTriggers.Add(pair.Key, true);
        }

        return modTriggers;
    }
    /// <summary>
    /// Before a turn is taken the entity will receive DOT damage if applicable
    /// </summary>
    public (bool, bool)CallTurnModifierEffects()
    {
        bool turnSkipped = false;
        bool confused = false;
        this.UpdateHealthVis();

        // This check just checks to make sure that modifiers that skip turns will be called after the DOT systems are checked
        if (this.modList.SmartModCheck("Sleeping"))
            turnSkipped = true;

        if (this.modList.SmartModCheck("Paralyzed"))
            turnSkipped = true;

        if (this.modList.SmartModCheck("Mind Controlled") || this.modList.SmartModCheck("Confusion"))
        {
            int savingThrow = UtilityHandler.RollSavingThrow(EntityStats.Stat.Intelligence, this);

            if (savingThrow < 10)
                confused = true;
        }
        
        return (turnSkipped, confused);
    }
    public EntityAnimation GetAnimationHandler()
        => animator;
    public void AlterManaRegenerationRate(float regenerationRate)
        => manaRegenRate += (manaRegenRate * regenerationRate);
    public float GetManaRegen()
        => manaRegenRate;
    /// <summary>
    /// Retrieval for the Inventory attached to this entity
    /// </summary>
    /// <returns>This entity's inventory component</returns>
    public virtual Inventory GetInventory()
    {
        return null;
    }
    /// <summary>
    /// Increases the experience level of this entity, handles leveling up internally
    /// </summary>
    /// <param name="expVal">The experience value the entity has gained</param>
    /// <returns>True if the entity has leveled up, false otherwise</returns>
    public bool IncreaseExperience(int expVal)
    {
        bool hasLeveledUp = false;

        // If level is already 20. simply return
        if (this.Level is MaxLevel)
        {
            this.TotalExperience += expVal;
            return false;
        }

        // Get next level requirements
        int levelThreshhold = levelMapExpRequirements[this.Level + 1];
        this.CurrentLevelExperience += expVal;
        this.TotalExperience += expVal;

        // If the current experience for the level the entity is at reaches the threshold, the
        // entity levels up until the current experience level is not above the threshold
        while (this.CurrentLevelExperience >= levelThreshhold)
        {
            Debug.Log($"{this.Entity_Name} has leveled up!");
            this.CurrentLevelExperience -= levelThreshhold;
            ++this.Level;
            IncreaseSecondaryStatMaxes();

            // If Desire for Power is an ability this entity has, double the skill point increase
            if (this.GetInventory().Abilities.Contains(AbilityBrain.GetAbilityList()[96]))
                ++this.SkillPoints;

            ++this.SkillPoints;
            levelThreshhold = levelMapExpRequirements[this.Level + 1];

            this.HasLeveledUp = true;
            hasLeveledUp = true;
        }

        return hasLeveledUp;
    }
    /// <summary>
    /// Increases the health and mana of the entity when leveling up
    /// </summary>
    private void IncreaseSecondaryStatMaxes()
    {
        if (this is Player player)
        {
            Player.PlayerType playerType = player.GetPlayerType();

            switch (playerType)
            {
                case Player.PlayerType.Fighter:         // Increase Health by D6, Mana by D6
                    IncreaseEntityStats(new DSix(), new DSix());
                    break;
                case Player.PlayerType.Berserker:       // Increase Health by D8, Mana by D4
                    IncreaseEntityStats(new DEight(), new DFour());
                    break;
                case Player.PlayerType.Wizard:          // Increase Health by D4, Mana by D10
                    IncreaseEntityStats(new DFour(), new DTen());
                    break;
                case Player.PlayerType.Sorcerer:        // Increase Health by D6, Mana by D8
                    IncreaseEntityStats(new DSix(), new DEight());
                    break;
                case Player.PlayerType.Ranger:          // Increase Health by D6, Mana by D6
                    IncreaseEntityStats(new DSix(), new DSix());
                    break;
                case Player.PlayerType.Gunslinger:      // Increase Health by D6, Mana by D4
                    IncreaseEntityStats(new DSix(), new DFour());
                    break;
            }
        }
        else
        {
            // Can't use local function since the dice params are an enum type and not the class itself
            int rolledHealth = UtilityHandler.RollValue(HealthDice[0]);
            int rolledMana = UtilityHandler.RollValue(ManaDice[0]);
            
            IncreaseMaxHealth(rolledHealth);
            IncreaseHealth(rolledHealth);
            IncreaseMaxMana(rolledMana);
            IncreaseMana(rolledMana);

            this.StatusChanged();
        }

        void IncreaseEntityStats(Dice hpDice, Dice mpDice)
        {
            Dictionary<int, Ability> abilityList = AbilityBrain.GetAbilityList();

            int rolledHP = DiceRoller.RollDice(hpDice) + this.GatherStats().GetStat(EntityStats.Stat.Constitution);

            // Inspiring Courage
            if (this.GetInventory().Abilities.Contains(abilityList[102]) || this.GetInventory().limitedAbilities.Contains(abilityList[102]))
                rolledHP += DiceRoller.RollDice(hpDice) + this.GatherStats().GetStat(EntityStats.Stat.Constitution);
            
            int rolledMP = DiceRoller.RollDice(mpDice) + this.GatherStats().GetStat(EntityStats.Stat.Intelligence);

            // Mana Growth
            if (this.GetInventory().Abilities.Contains(abilityList[142]) || this.GetInventory().limitedAbilities.Contains(abilityList[142]))
                rolledMP += DiceRoller.RollDice(mpDice) + this.GatherStats().GetStat(EntityStats.Stat.Intelligence);

            this.IncreaseMaxHealth(rolledHP);
            this.IncreaseHealth(rolledHP);
            this.IncreaseMaxMana(rolledMP);
            this.IncreaseMana(rolledMP);

            this.StatusChanged();
        }
    }
    /// <summary>
    /// Signal method to indicate that the entity has leveled up their components
    /// </summary>
    public void HasIntegratedLevel()
    {
        this.LastLevelIntegrated = this.Level;
        this.HasLeveledUp = false;
    }
    /// <summary>
    /// Retrieval for the signal if the entity has leveled up and can access their skill tree 
    /// and improve their stats in the inventory manipulator component
    /// </summary>
    /// <returns>Signal for if the skill tree and stats can be modified</returns>
    public bool HasEntityLeveledUp()
        => this.HasLeveledUp;
    public void SignalPlayerModification(string damageModificationName, object damageModifier)
        => ClassModifications.Add(damageModificationName, damageModifier);
    public int ParseClassDamageModifiers(Entity target, Dictionary<string, (List<string>, List<object>)> triggers = null, SortedDictionary<int, (int, Dice)> damageRolls = null)
    {
        int additionalDamage = 0;

        foreach (KeyValuePair<string, object> modification in ClassModifications)
        {
            switch (modification.Key)
            {
                case "Favored Enemy":

                    string enemyType = (string)((List<object>)modification.Value)[0];
                    if (enemyType.Equals(target.RetrieveEnemyType()))
                    {
                        Dice dice = (Dice)((List<object>)modification.Value)[1];
                        int damageValue = DiceRoller.RollDice(dice);
                        if (damageRolls is not null && triggers is not null)
                        {
                            int currentDice = damageRolls.Last().Key;
                            damageRolls.Add(++currentDice, (damageValue, dice));

                            if (triggers.ContainsKey("AddDamageValue"))
                            {
                                triggers["AddDamageValue"].Item1.Add("Favored Enemy");
                                triggers["AddDamageValue"].Item2.Add(new List<object> { damageValue, dice });
                            }
                            else
                                triggers.Add("AddDamageValue", (new List<string> { "Favored Enemy" }, new List<object> { new List<object> { damageValue, dice } }));
                        }

                        additionalDamage += damageValue;
                    }

                    break;
                default:
                    break;
            }
        }

        return additionalDamage;
    }
    public int ParseToHitClassModifiers(Entity target, Dictionary<string, (List<string>, List<object>)> triggers = null, SortedDictionary<int, (int, Dice)> toHitRolls = null)
    {
        int additionalToHit = 0;

        foreach (KeyValuePair<string, object> modification in ClassModifications)
        {
            switch (modification.Key)
            {
                case "Favored Enemy":

                    string enemyType = (string)((List<object>)modification.Value)[0];
                    if (enemyType.Equals(target.RetrieveEnemyType()))
                    {
                        Dice dice = (Dice)((List<object>)modification.Value)[2];
                        int toHitValue = DiceRoller.RollDice(dice);
                        if (toHitRolls is not null && triggers is not null)
                        {
                            int currentDice = toHitRolls.Last().Key;
                            toHitRolls.Add(++currentDice, (toHitValue, dice));
                        }

                        if (triggers.ContainsKey("ToHitAdd"))
                        {
                            triggers["ToHitAdd"].Item1.Add("Favored Enemy");
                            triggers["ToHitAdd"].Item2.Add(new List<object> { toHitValue, dice });
                        }
                        else
                            triggers.Add("ToHitAdd", (new List<string> { "Favored Enemy" }, new List<object> { new List<object> { toHitValue, dice } }));

                        additionalToHit += toHitValue;
                    }

                    break;
                default:
                    break;
            }
        }

        return additionalToHit;
    }
    public (List<SpellSO.Element>, List<SpellSO.Element>) RetrieveStartingElementalTypes()
        => (elementWeaknesses, elementResistances);
    public (List<WeaponSO.Type>, List<WeaponSO.Type>) RetrieveStartingWeaponTypes()
        => (weaponWeaknesses, weaponResistances);
    /// <summary>
    /// Easier retrieval for this entity's weapon weaknesses
    /// </summary>
    /// <returns></returns>
    public Dictionary<WeaponSO.Type, int> GetWeaponWeaknesses()
    {
        this.stats.WeaponWeaknesses ??= new Dictionary<WeaponSO.Type, int>();
        return this.stats.WeaponWeaknesses;
    }
    /// <summary>
    /// Easier retrieval for this entity's weapon resistances
    /// </summary>
    /// <returns></returns>
    public Dictionary<WeaponSO.Type, int> GetWeaponResists()
    {
        this.stats.WeaponResistances ??= new Dictionary<WeaponSO.Type, int>();
        return this.stats.WeaponResistances;
    }
    /// <summary>
    /// Easier retrieval for this entity's elemental weaknesses
    /// </summary>
    /// <returns></returns>
    public Dictionary<SpellSO.Element, int> GetElementWeaknesses()
    {
        this.stats.ElementWeaknesses ??= new Dictionary<SpellSO.Element, int>();
        return this.stats.ElementWeaknesses;
    }
    /// <summary>
    /// Easier retrieval for this entity's elemental resistances
    /// </summary>
    /// <returns></returns>
    public Dictionary<SpellSO.Element, int> GetElementResists()
    {
        this.stats.ElementResistances ??= new Dictionary<SpellSO.Element, int>();
        return this.stats.ElementResistances;
    }
    /// <summary>
    /// Deactivates the entities sprite component to give off the illusion of destruction
    /// </summary>
    public void DeactivateVisually()
        => transform.GetChild(0).gameObject.SetActive(false);
    /// <summary>
    /// Reactivates the entities sprite component to give off the illusion of instantiation
    /// </summary>
    public void ReactivateVisually()
        => transform.GetChild(0).gameObject.SetActive(true);
    /// <summary>
    /// Retrieval for the skill tree objects
    /// </summary>
    /// <returns></returns>
    public List<GameObject> GetSkillTrees()
        => this.availableSkillTrees;
    /// <summary>
    /// Adds a skill tree object to the entities list of available trees to them
    /// </summary>
    /// <param name="skillTreeObject">The whole game object for the entities new skill tree</param>
    public void AddSkillTree(GameObject skillTreeObject)
    {
        // Make sure the skill tree isn't already present in the current trees available to the player
        if (!skillTreeNames.Contains(skillTreeObject.name))
            availableSkillTrees.Add(skillTreeObject);
    }
    /// <summary>
    /// Removes a skill tree from the entities list of available ones
    /// </summary>
    /// <param name="skillTreeName">The name of which the object that needs to be removed</param>
    public void RemoveSkillTree(string skillTreeName)
    {
        // Guard for if the names doesn't contain the name indicated
        if (!skillTreeNames.Contains(skillTreeName))
            return;

        // Cycles through the skill trees to remove the matching skill tree object to the name given
        // Then simply removes the item at the designated index
        for (int i = 0; i < availableSkillTrees.Count; ++i)
        {
            if (availableSkillTrees[i].name == skillTreeName)
                availableSkillTrees.RemoveAt(i);
        }
    }
    /// <summary>
    /// A catch function for if trying to activate a skill indicates requirements not fulfilled, refunds the points since points were paid already
    /// </summary>
    /// <param name="skillPointCost">Cost of the skill</param>
    public void RefundSkillPoints(int skillPointCost)
        => this.SkillPoints += skillPointCost;
    public string RetrieveEnemyType()
    {
        return entityType switch
        {
            EntityType.Golem => "Golem",
            EntityType.Undead => "Undead",
            EntityType.Humanoid => "Humanoid",
            EntityType.Demonic => "Demonic",
            EntityType.Beast => "Beast",

            _ => throw new InvalidEnumArgumentException(),
        };
    }
    public static bool operator ==(Entity left, Entity right)
        => left?.GetUniqueID() == right?.GetUniqueID();
    public static bool operator !=(Entity left, Entity right)
        => !(left == right);
    public override bool Equals(object other)
    {
        if (other is not Entity)
            return false;
        else 
            return other as Entity == this;
    }
    public override int GetHashCode()
        => base.GetHashCode();
    public IEnumerator GetEnumerator()
    {
        yield return this;
    }
    public override string ToString()
        => Entity_Name;
}