Encounter / Assets / Scripts / EntityStateManagement / CharacterCreation.cs
CharacterCreation.cs
Raw
using System;
using System.Collections.Generic;
using System.Linq;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

/// <summary>
/// Handles the character creation aspect before starting a game
/// </summary>
public class CharacterCreation : MonoBehaviour
{
    #region Variables
    [Header("Character Creation UI Elements")]
    [SerializeField] private Canvas titleCanvas;                        // Contains the canvas for the title screen
    [SerializeField] private Canvas creationCanvas;                     // Contains the canvas for the character creation screen
    [SerializeField] private Canvas continueScreen;                     // Contains the canvas for the game settings and continue screen
    [SerializeField] private GameObject warningPanel;                   // Warning panel if the player has not set a name for their character 
    [SerializeField] private TMP_InputField seedBox;                    // Holds the Seed reference and can be typed
    [SerializeField] private TMP_InputField namingField;                // Input field for the player's name
    [SerializeField] private List<TextMeshProUGUI> statFields;          // Collection of all of the listed value text fields between the arrow buttons on the creation screen
    [SerializeField] private TextMeshProUGUI pointsRemainField;         // Text field for the points remaining value
    [SerializeField] private TextMeshProUGUI healthField;               // Text field for the health value
    [SerializeField] private TextMeshProUGUI manaField;                 // Text field for the mana value
    [SerializeField] private TextMeshProUGUI armorField;                // Text field for the armor value
    [SerializeField] private TextMeshProUGUI aspectField;               // Text field for the aspect currently being shown to the player
    [SerializeField] private List<Image> imgs;                          // Aspect_Reference to each of the images that represent which of the aspects are active in the aspect panel
    [SerializeField] private Transform aspectHolder;                    // Full Panel reference that holds the layout for the image containers for each of the aspect screens
    private Dictionary<string, TextMeshProUGUI> fieldRefs;              // Aspect_Reference fields for the collection of stat fields
    private Dictionary<string, int> stats;                              // Holds each of the stat values after randomization by abbreviation
    private int healthStat;                                             // Holds the stat value for the health amount
    private int healthCon;                                              // Contains the true random value for the health amount to prevent reapplication of modifiers
    private int manaStat;                                               // Holds the stat value for the mana amount
    private int manaCon;                                                // Contains the true random value for the mana amount to prevent reapplication of modifiers
    private int armorStat;                                              // Holds the stat value for the armor amount
    private int armorCon;                                               // Contains the true random value for the armor amount to prevent reapplication of modifiers
    private string characterName;                                       // Retainer/Holder for the player's current name, if empty the player cannot go to the continue screen
    private int currentAspect;                                          // Container of the current aspect identifier for the aspects that the player can view, starts at Weapons
    private List<WeaponSO> weapons;                                     // Holds the deserialized objects from the Weapons Json
    private List<Weapon> shownWeapons;                                  // List that holds the Weapons that had their requirements met to be shown
    private List<ApparelSO> apparel;                                    // Holds the deserialized objects from the Apparel Json
    private List<Apparel> shownApparel;                                 // List that holds the Apparel that had their requirements met to be shown
    private List<SpellSO> spells;                                       // Holds the deserialized objects from the Spells Json
    private List<Spell> shownSpells;                                    // List that holds the Spells that had their requirements met to be shown
    private List<AbilitySO> abilities;                                  // Holds the deserialized objects from the Abilities Json
    private List<Ability> shownAbilities;                               // List that holds the Abilities that had their requirements met to be shown
    private Player.PlayerType playerTypeChosen;                         // Indicator of what player class the player has selected
    private GameObject currentSkillTree;

    [Header("Pre-Customization Fields")]
    [SerializeField] private Image classImage;
    [SerializeField] private Sprite[] classSprites;
    [SerializeField] private TextMeshProUGUI bonusList;

    [Header("Values")]
    [SerializeField] private int pointsToDisperse = 11;                 // The number of points the player can disperse among their stats
    private int pointsHolder;                                           // Holds the points at the start of the program to prevent accidental problems involving dispersing points when reloading the creation screen
    private int maxPointAllocation;
    #endregion
    /// <summary>
    /// Holds onto the points at the start of the program to reset whenever the player exits the creation screen
    /// and goes back into it
    /// </summary>
    private void Start()
    {
        pointsHolder = pointsToDisperse;
        maxPointAllocation = 70 + pointsHolder;
        SetPlayerClass(0);
    }
    /// <summary>
    /// Initializes various aspects of the character creation screen before switching the canvas' of the title and creation screens
    /// </summary>
    public void InitializeCharacterCreationScreen()
    {
        // Initializes seed and sets it in Global and Input Field
        InitializeSeed();

        // Randomizes the stat values using the points available
        RandomizeStats();

        // Takes the stat values and contains them within the new Dic while setting the health, mana, and armor
        stats = new();
        SetStats();
        SetSecondaryStats();

        // Gathers the aspects from the Json Files and then sets the aspects on the Creation Screen
        GatherAspects();
        AnalyzeAspects();
        currentAspect = -1;
        ChangeAspect(0);

        // Switches canvas'
        titleCanvas.enabled = false;
        creationCanvas.enabled = true;
    }
    /// <summary>
    /// Initializes the Seed and sets it within the globals which is then set within the UnityEngine Random Number Generator
    /// </summary>
    public void InitializeSeed()
    {
        int seed = UnityEngine.Random.Range(0, 1000000000);
        Globals.SetSeed(seed);
        seedBox.text = seed.ToString();
    }
    /// <summary>
    /// When the randomization button next to the seed box is pressed
    /// randomizes all stats along with the seed
    /// </summary>
    public void RandomizeAll()
    {
        InitializeSeed();
        RandomizeStats();
        stats = new();
        SetStats();
        SetSecondaryStats();
        AnalyzeAspects();
        ChangeAspect(currentAspect is -1 ? 0 : currentAspect, true);
    }
    /// <summary>
    /// When the player sets a custom seed, function parses the string into a numerical value
    /// </summary>
    /// <param name="seed">The string of the seed inputted into the seed field</param>
    public void RandomizeAllManual(string seed)
    {
        // If the seed can't be fully parsed, then the characters in the seed are converted to their numerical counterparts
        if (!Int32.TryParse(seed, out _))
        {
            string newValue = string.Empty;
            foreach (char character in seed)
                newValue += character % 10;

            Globals.SetSeed(Int32.Parse(newValue));
            seedBox.text = newValue;
        }
        else
            Globals.SetSeed(Int32.Parse(seed));

        RandomizeStats();
        stats = new();
        SetStats();
        SetSecondaryStats();
        AnalyzeAspects();
        ChangeAspect(currentAspect is -1 ? 0: currentAspect, true);
    }
    /// <summary>
    /// Loops between randomized selections of the stats to disperse the points
    /// If the points to disperse reaches 0 the function ends. 
    /// </summary>
    private void RandomizeStats()
    {
        List<int> valuesRandomized = new List<int>();
        pointsToDisperse = pointsHolder;

        // Minor check to make sure that the point allocation amount does not increase or decrease for whatever reason
        int totalSum = 0;

        while (valuesRandomized.Count != statFields.Count && pointsToDisperse != 0)
        {
            // Max is always 20 and Min is always 3, should honestly never reach 20 
            int heldValue = UnityEngine.Random.Range(Globals.BaseMinStat, Globals.BaseMaxStat);
            if (heldValue > Globals.BaseStat)
            {
                // If the held value is greater than 10, subtract the base value from the held value to 
                // retrieve the value to subtract from the dispersing points
                int sub = heldValue - Globals.BaseStat;

                // If the value is greater than the points available increment the counter and continue
                // Otherwise, subtract the value from dispersing points
                if (sub > pointsToDisperse)
                    continue;
                
                pointsToDisperse -= sub;
            }
            else if (heldValue < Globals.BaseStat)
            {
                // If the held value is less than 10, add the difference to the points to disperse
                int add = Globals.BaseStat - heldValue;
                pointsToDisperse += add;
            }

            // Contains the count of the values randomized before looping through the 
            // stat fields for a value to place the new held value.
            // If the value randomized has already been set then the while loop simply continues.
            int valuesCheckRef = valuesRandomized.Count;
            do
            {
                int v = UnityEngine.Random.Range(0, statFields.Count);
                if (!valuesRandomized.Contains(v))
                {
                    TextMeshProUGUI statField = statFields[v];
                    statField.text = heldValue.ToString();
                    valuesRandomized.Add(v);
                }
            } while (valuesCheckRef == valuesRandomized.Count);

            totalSum += heldValue;
        }

        if (totalSum > maxPointAllocation)
        {
            RandomizeStats();
            return;
        }

        // Simply writes the points remaining within the points remain field
        pointsRemainField.text = pointsToDisperse.ToString();
    }
    /// <summary>
    /// Each of the stats within the stat fields after being given values is set inside a dictionary to their respective references
    /// </summary>
    /// <exception cref="InvalidOperationException">When the name of the stat in the stat_Fields doesn't match any of the cases an exception is thrown</exception>
    private void SetStats()
    {
        fieldRefs = new Dictionary<string, TextMeshProUGUI>();

        // Cycles through each value in the stat fields and matches the name of the stat to a stat reference before adding 
        // the value within the stat text to the dictionary with the connecting name of the stat reference, i.e. STR -> 13
        foreach (TextMeshProUGUI stat in statFields)
        {
            string statRef = string.Empty;
            switch (stat.name)
            {
                case "Stat_Value_STR":
                {
                    statRef = "STR";
                    break;
                }
                case "Stat_Value_INT":
                {
                    statRef = "INT";
                    break;
                }
                case "Stat_Value_CON":
                {
                    statRef = "CON";
                    break;
                }
                case "Stat_Value_DEX":
                {
                    statRef = "DEX";
                    break;
                }
                case "Stat_Value_CHA":
                {
                    statRef = "CHA";
                    break;
                }
                case "Stat_Value_WIS":
                {
                    statRef = "WIS";
                    break;
                }
                case "Stat_Value_LCK":
                {
                    statRef = "LCK";
                    break;
                }
                default: // Throws an error when the name doesn't match any of the other indicated names
                    throw new InvalidOperationException("stat_Ref showed empty string, which will cause an error if added to dictionary!");
            }

            fieldRefs.Add(statRef, stat);
            stats.Add(statRef, int.Parse(stat.text));
        }
    }
    /// <summary>
    /// Simply calls all of the setters for the secondary stats
    /// Health, Mana, and Armor
    /// </summary>
    private void SetSecondaryStats()
    {
        SetHealth(true);
        SetMana(true);
        SetArmor(true);
    }
    /// <summary>
    /// Sets the health parameter within character creation as well as the text field
    /// </summary>
    private void SetHealth(bool randomize = false)
    {
        // Takes the Constitution value from the stats Dic and randomizes a value for health
        int conValue = stats["CON"];
        if (randomize)
        {
            healthStat = UnityEngine.Random.Range(18, 33);

            // If more health cheat is activated
            if (Globals.LargeHealthCheat)
                healthStat += 300;

            healthCon = healthStat;
        }

        // The health stat is then modified based on the CON value
        if (conValue >= 20)
            healthStat = healthCon + 5;
        else if (conValue >= 18)
            healthStat = healthCon + 4;
        else if (conValue >= 16)
            healthStat = healthCon + 3;
        else if (conValue >= 14)
            healthStat = healthCon + 2;
        else if (conValue >= 11)
            healthStat = healthCon + 1;
        else if (conValue <= 3)
            healthStat = healthCon - 4;
        else if (conValue <= 5)
            healthStat = healthCon - 3;
        else if (conValue <= 7)
            healthStat = healthCon - 2;
        else if (conValue <= 9)
            healthStat = healthCon - 1;
        else if (conValue == 10)
            healthStat = healthCon;

        // Then set to the text field reference
        healthField.text = healthStat.ToString();
    }
    /// <summary>
    /// Sets the mana parameter within character creation as well as the text field
    /// </summary>
    private void SetMana(bool randomize = false)
    {
        // Takes the Intelligence value from the stats Dic and randomizes a value for mana
        int intValue = stats["INT"];
        if (randomize)
        {
            manaStat = UnityEngine.Random.Range(16, 23);

            // If more mana cheat is activated
            if (Globals.LargeManaCheat)
                manaStat += 300;

            manaCon = manaStat;
        }

        // The mana stat is then modified based on the INT value
        if (intValue >= 20)
            manaStat = manaCon + 5;
        else if (intValue >= 18)
            manaStat = manaCon + 4;
        else if (intValue >= 16)
            manaStat = manaCon + 3;
        else if (intValue >= 14)
            manaStat = manaCon + 2;
        else if (intValue >= 11)
            manaStat = manaCon + 1;
        else if (intValue <= 3)
            manaStat = manaCon - 4;
        else if (intValue <= 5)
            manaStat = manaCon - 3;
        else if (intValue <= 7)
            manaStat = manaCon - 2;
        else if (intValue <= 9)
            manaStat = manaCon - 1;
        else if (intValue == 10)
            manaStat = manaCon;

        // Then set to the text field reference
        manaField.text = manaStat.ToString();
    }
    /// <summary>
    /// Sets the armor parameter within character creation as well as the text field
    /// </summary>
    private void SetArmor(bool randomize = false)
    {
        // Takes the Dexterity value from the stats Dic and randomizes a value for armor
        int dexValue = stats["DEX"];
        if (randomize)
        {
            const int ArmorMinInit = 10;
            const int ArmorMaxInit = 12;

            armorStat = UnityEngine.Random.Range(ArmorMinInit, ArmorMaxInit + 1);
            armorCon = armorStat;
        }

        // The armor stat is then modified based on the DEX value, but does check
        // in case the armor goes negative, which is not possible
        if (dexValue >= 20)
            armorStat = armorCon + 5;
        else if (dexValue >= 18)
            armorStat = armorCon + 4;
        else if (dexValue >= 16)
            armorStat = armorCon + 3;
        else if (dexValue >= 14)
            armorStat = armorCon + 2;
        else if (dexValue >= 11)
            armorStat = armorCon + 1;
        else if (dexValue <= 3)
            armorStat = armorCon - 4;
        else if (dexValue <= 5)
            armorStat = armorCon - 3;
        else if (dexValue <= 7)
            armorStat = armorCon - 2;
        else if (dexValue <= 9)
            armorStat = armorCon - 1;
        else if (dexValue == 10)
            armorStat = armorCon;

        // Then set to the text field reference
        armorField.text = armorStat.ToString();
    }
    /// <summary>
    /// Initializes the lists for the aspects and gathers each of the objects containers 
    /// from the Jsons and Scriptable Object files
    /// </summary>
    private void GatherAspects()
    {
        // Initialize Files
        int[] weaponIds             = GatherWeaponIDs(); 
        int[] apparelIds            = GatherApparelIDs();
        int[] spellIds              = GatherSpellIDs();  
        int[] abilityIds            = GatherAbilityIDs();

        weapons             = new List<WeaponSO>(JsonReader.AspectCollector<WeaponSO>.GatherAspects("Weapons.json", weaponIds));
        apparel             = new List<ApparelSO>(JsonReader.AspectCollector<ApparelSO>.GatherAspects("Apparel.json", apparelIds));
        spells              = new List<SpellSO>(JsonReader.AspectCollector<SpellSO>.GatherAspects("Spells.json", spellIds));
        abilities           = new List<AbilitySO>(JsonReader.AspectCollector<AbilitySO>.GatherAspects("Abilities.json", abilityIds));

        int[] GatherWeaponIDs()
        {
            return playerTypeChosen switch
            {
                Player.PlayerType.Fighter => new int[] { 0, 12, 13 },              // Done
                Player.PlayerType.Berserker => new int[] { 0, 5, 8, 12, 22 },      // Done
                Player.PlayerType.Wizard => new int[] { 6 },                       // Done
                Player.PlayerType.Sorcerer => new int[] { 3, 10 },                 // Done
                Player.PlayerType.Ranger => new int[] { 1, 10, 14 },               // Done
                Player.PlayerType.Gunslinger => new int[] { 31, 32 },              // Done

                _ => throw new ArgumentException(),
            };
        }
        int[] GatherApparelIDs()
        {
            return playerTypeChosen switch
            {
                Player.PlayerType.Fighter => new int[] { 0, 1, 6, 7, 8, 9, 14 },    // Done
                Player.PlayerType.Berserker => new int[] { 33, 7 },                 // Done
                Player.PlayerType.Wizard => new int[] { 10, 32 },                   // Done
                Player.PlayerType.Sorcerer => new int[] { 7, 24, 25, 32 },          // Done
                Player.PlayerType.Ranger => new int[] { 7, 8, 9, 14, 24, 29 },      // Done
                Player.PlayerType.Gunslinger => new int[] { 1, 7, 9, 14, 24, 25, 29 },     // Done

                _ => throw new ArgumentException(),
            };
        }
        int[] GatherSpellIDs()
        {
            return playerTypeChosen switch
            {
                Player.PlayerType.Fighter => new int[] { 0, 1, 5 },                 // Done
                Player.PlayerType.Berserker => new int[] { 0, 5, 13 },              // Done
                Player.PlayerType.Wizard => new int[] { 1, 2, 8, 17, 32, 78 },         // Done
                Player.PlayerType.Sorcerer => new int[] { 1, 2, 3, 7, 15, 17, 32 }, // Done
                Player.PlayerType.Ranger => new int[] { 4, 12, 39, 42 },            // Done
                Player.PlayerType.Gunslinger => new int[] { 11, 41 },               // Done

                _ => throw new ArgumentException(),
            };
        }
        int[] GatherAbilityIDs()
        {
            return playerTypeChosen switch                                          // Keep in mind multiple are correlated to multiple classes, still being adjusted
            {
                Player.PlayerType.Fighter => new int[] { 1, 2, 8, 22, 23, 24, 26  },            // Done
                Player.PlayerType.Berserker => new int[] { 1, 23, 24, 47, 55, 64, 103 },        // Done
                Player.PlayerType.Wizard => new int[] { 1, 10, 23, 24, 25, 26 },                // Done
                Player.PlayerType.Sorcerer => new int[] { 1, 3, 23, 24, 25, 26, 29 },           // Done
                Player.PlayerType.Ranger => new int[] { 1, 8, 10, 22, 23, 24, 26, 64 },         // Done
                Player.PlayerType.Gunslinger => new int[] { 1, 2, 6, 22, 23, 24, 64 },          // Done

                _ => throw new ArgumentException(),
            };
        }
    }
    /// <summary>
    /// Takes the stats that were generated and creates a player container which then
    /// generates the possible aspects for the player if they were to take the current
    /// generation
    /// </summary>
    private void AnalyzeAspects()
    {
        // Player generation
        List<int> statList = ExtractStats();
        PlayerHandler playerRetainer = new PlayerHandler(statList.ToArray(), armorStat, healthStat, manaStat, "Retainer", playerTypeChosen);
        RequirementHandler.SetPlayerInformation(playerRetainer);

        shownWeapons = new List<Weapon>();
        shownApparel = new List<Apparel>();
        shownSpells = new List<Spell>();
        shownAbilities = new List<Ability>();

        // Resets the player container for items
        int[] aspectQuantities = DetermineClassAspectCount();

        // Weapons collected from the Json and Scriptable Objects are then checked to see if their requirements
        // are met before turning them into their respective objects to be contained within the inventory of the
        // player container. Also eliminates weapons from the lists until only 3 remain
        weapons.ForEach(weap => 
        {
            (object weaponContainer, bool trigger) = RequirementHandler.DetermineObjectEligibility(weap);
            if (trigger)
                shownWeapons.Add(Globals.TurnWeaponFromObject(weaponContainer as WeaponSO));
        });
        while (shownWeapons.Count > aspectQuantities[0])
        {
            int v = UnityEngine.Random.Range(0, shownWeapons.Count);
            shownWeapons.RemoveAt(v);
        }
        shownWeapons.ForEach(w => playerRetainer.PlayerInven.Add(w, 1));

        // Similar to above, except for Apparel aspects
        apparel.ForEach(app =>
        {
            (object appContainer, bool trigger) = RequirementHandler.DetermineObjectEligibility(app);
            if (trigger)
                shownApparel.Add(Globals.TurnApparelFromObject(appContainer as ApparelSO));
        });
        RandomizeSet(aspectQuantities[1]);

        // Spells and Abilities are not necessary to store in the creation screen since none of them
        // are necessary with one another, however the process is the same in checking their 
        // respective requirements and turning them into in their respective objects
        spells.ForEach(spell =>
        {
            (object spellContainer, bool trigger) = RequirementHandler.DetermineObjectEligibility(spell);
            if (trigger)
                shownSpells.Add(Globals.TurnSpellFromObject(spellContainer as SpellSO));
        });
        abilities.ForEach(ab =>
        {
            (object abContainer, bool trigger) = RequirementHandler.DetermineObjectEligibility(ab);
            if (trigger)
                shownAbilities.Add(Globals.TurnAbilityFromObject(abContainer as AbilitySO));
        });

        // Generates random amount of spells and abilities that will be available for the player
        int spellCount = aspectQuantities[2];
        int abilityCount = aspectQuantities[3];

        // Removes a random spell or ability from the lists until the counts are met
        while (shownSpells.Count > spellCount || shownAbilities.Count > abilityCount)
        {
            if (shownSpells.Count > spellCount)
            {
                int v = UnityEngine.Random.Range(0, shownSpells.Count);

                // Wizard will always have Magic Blast
                if (playerTypeChosen is Player.PlayerType.Wizard && shownSpells[v].SpellName is "Magic Blast")
                    continue;

                shownSpells.RemoveAt(v);
            }
            if (shownAbilities.Count > abilityCount)
            {
                int v = UnityEngine.Random.Range(0, shownAbilities.Count);
                shownAbilities.RemoveAt(v);
            }
        }

        // Searches for specific location values to make sure no overlapping apparel are given
        // to the player
        void RandomizeSet(int amount)
        {
            Dictionary<int, List<Apparel>> locationCount = new Dictionary<int, List<Apparel>>();
            shownApparel.ForEach(app =>
            {
                for (int i = 0; i < (int)ApparelSO.Location.Neck; i++)
                {
                    if ((int)app.ApparelLocation == i)
                    {
                        if (!locationCount.ContainsKey(i))
                        {
                            locationCount.Add(i, new List<Apparel>());
                            locationCount[i].Add(app);
                        }
                        else
                            locationCount[i].Add(app);

                        break;
                    }
                }
            });

            // Since the first three locations are the primary locations then
            // the location count will work as the threshold for the loop
            shownApparel = new List<Apparel>();
            int[] locationDeterminants = LocationDeterminations(amount);
            for (int i = 0; i < amount; i++)
            {
                while (locationCount[locationDeterminants[i]].Count > 1)
                {
                    int v = UnityEngine.Random.Range(0, locationCount[locationDeterminants[i]].Count);
                    locationCount[locationDeterminants[i]].RemoveAt(v);
                }
                shownApparel.Add(locationCount[locationDeterminants[i]][0]);
            }
            shownApparel.ForEach(a => playerRetainer.PlayerInven.Add(a, 1));
        }

        int[] LocationDeterminations(int amount)
        {
            int[] locationsToUtilize = new int[amount];

            // 0 Represents the Head
            // 1 Represents the Chest
            // 2 Represents the Boots

            switch (playerTypeChosen)
            {
                case Player.PlayerType.Fighter:
                    locationsToUtilize[0] = 0;
                    locationsToUtilize[1] = 1;
                    locationsToUtilize[2] = 2;
                    break;
                case Player.PlayerType.Berserker:
                    locationsToUtilize[0] = 1;
                    locationsToUtilize[1] = 2;
                    break;
                case Player.PlayerType.Wizard:
                    locationsToUtilize[0] = 0;
                    locationsToUtilize[1] = 1;
                    break;
                case Player.PlayerType.Sorcerer:
                    locationsToUtilize[0] = 1;
                    locationsToUtilize[1] = 2;
                    break;
                case Player.PlayerType.Ranger:
                    locationsToUtilize[0] = 0;
                    locationsToUtilize[1] = 1;
                    locationsToUtilize[2] = 2;
                    break;
                case Player.PlayerType.Gunslinger:
                    locationsToUtilize[0] = 0;
                    locationsToUtilize[1] = 1;
                    locationsToUtilize[2] = 2;
                    break;  
            }

            return locationsToUtilize;
        }
    }
    /// <summary>
    /// Dynamically determines the quantity of each aspect based on the current chosen class
    /// </summary>
    /// <returns>Array of quantites for the aspects being gathered</returns>
    private int[] DetermineClassAspectCount()
    {
        int[] aspectCounts = new int[4];

        // 0 Index is Weapon Count
        // 1 Index is Apparel Count
        // 2 Index is Spell Count
        // 3 Index is Ability Count

        switch (playerTypeChosen)
        {
            case Player.PlayerType.Fighter:
                aspectCounts[0] = 2;
                aspectCounts[1] = 3;
                aspectCounts[2] = 2;
                aspectCounts[3] = 3;
                break;
            case Player.PlayerType.Berserker:
                aspectCounts[0] = 2;
                aspectCounts[1] = 2;
                aspectCounts[2] = 1;
                aspectCounts[3] = 2;
                break;
            case Player.PlayerType.Wizard: 
                aspectCounts[0] = 1;
                aspectCounts[1] = 2;
                aspectCounts[2] = 4;
                aspectCounts[3] = 2;
                break;
            case Player.PlayerType.Sorcerer: 
                aspectCounts[0] = 2;
                aspectCounts[1] = 2;
                aspectCounts[2] = 3;
                aspectCounts[3] = 3;
                break;
            case Player.PlayerType.Ranger: 
                aspectCounts[0] = 1;
                aspectCounts[1] = 3;
                aspectCounts[2] = 2;
                aspectCounts[3] = 4;
                break;
            case Player.PlayerType.Gunslinger: 
                aspectCounts[0] = 2;
                aspectCounts[1] = 3;
                aspectCounts[2] = 2;
                aspectCounts[3] = 2;
                break;
        }

        return aspectCounts;
    }
    /// <summary>
    /// Shows the aspects based on the current_Aspect value while injecting the managers with
    /// the necessary aspects
    /// </summary>
    private void ShowAspect()
    {
        // Gathers the image holders into an array within the panel
        Image[] aspectImages = new Image[4];
        AspectHelperManager[] managers = new AspectHelperManager[4];
        for (int i = 0; i < aspectImages.Length; i++)
        {
            foreach (Transform child in aspectHolder)
            {
                if (child.GetChild(0).name == "Aspect_Image_" + i.ToString())
                {
                    managers[i] = child.GetComponent<AspectHelperManager>();
                    aspectImages[i] = child.GetChild(0).GetComponent<Image>();
                    break;
                }
            }
        }

        // The image array is then cycled through and inputs various images from the aspect chosen
        // while also hiding the non-used images if the collection has less objects then image
        // holders
        switch (currentAspect)
        {
            case 0:
            {
                for (int i = 0; i < aspectImages.Length; i++)
                {
                    try
                    {
                        aspectImages[i].color = new Color(1f, 1f, 1f, 1f);
                        aspectImages[i].sprite = shownWeapons[i].ItemSprite;

                        managers[i].enabled = true;
                        managers[i].InjectAspect(shownWeapons[i]);
                    }
                    catch
                    {
                        aspectImages[i].color = new Color(1f, 1f, 1f, 0f);
                        managers[i].enabled = false;
                    }
                }

                break;
            }
            case 1:
            {
                for (int i = 0; i < aspectImages.Length; i++)
                {
                    try
                    {
                        aspectImages[i].color = new Color(1f, 1f, 1f, 1f);
                        aspectImages[i].sprite = shownApparel[i].ItemSprite;

                        managers[i].enabled = true;
                        managers[i].InjectAspect(shownApparel[i]);
                    }
                    catch
                    {
                        aspectImages[i].color = new Color(1f, 1f, 1f, 0f);
                        managers[i].enabled = false;
                    }
                }

                break;
            }
            case 2:
            {
                for (int i = 0; i < aspectImages.Length; i++)
                {
                    try
                    {
                        aspectImages[i].color = new Color(1f, 1f, 1f, 1f);
                        aspectImages[i].sprite = shownSpells[i].SpellSprite;

                        managers[i].enabled = true;
                        managers[i].InjectAspect(shownSpells[i]);
                    }
                    catch
                    {
                        aspectImages[i].color = new Color(1f, 1f, 1f, 0f);
                        managers[i].enabled = false;
                    }
                }

                break;
            }
            case 3:
            {
                for (int i = 0; i < aspectImages.Length; i++)
                {
                    try
                    {
                        aspectImages[i].color = new Color(1f, 1f, 1f, 1f);
                        aspectImages[i].sprite = shownAbilities[i].AbilitySprite;

                        managers[i].enabled = true;
                        managers[i].InjectAspect(shownAbilities[i]);
                    }
                    catch
                    {
                        aspectImages[i].color = new Color(1f, 1f, 1f, 0f);
                        managers[i].enabled = false;
                    }
                }

                break;
            }
        }
    }
    /// <summary>
    /// Holds onto the character name if a single char is available
    /// If only white space then simply returns
    /// </summary>
    public void RetainCharacterName()
    {
        if (string.IsNullOrEmpty(namingField.text))
            return;
        else if (namingField.text.Length > 12)              // Maximizes the characters for the naming field to 12
            namingField.text = namingField.text[..11];
        
        characterName = namingField.text;
    }
    /// <summary>
    /// Moves to the game setting screen for other statistics (TODO/WIP)
    /// Primarily sets the Player reference to the Globals value 
    /// </summary>
    public void PlayGame()
    {
        if (string.IsNullOrEmpty(characterName))
        {
            warningPanel.SetActive(true);
            return;
        }

        List<int> statList = ExtractStats();

        PlayerHandler playerRetainer = new PlayerHandler(statList.ToArray(), armorStat, healthStat, manaStat, characterName, playerTypeChosen,
            shownWeapons, shownApparel, shownAbilities, shownSpells, currentSkillTree);

        RequirementHandler.SetPlayerInformation(playerRetainer, true);
        Globals.SetPlayer(playerRetainer);

        GetComponent<TitleScreen>().StartGame();
    }
    public void ToCreationScreen()
    {
        creationCanvas.enabled = true;
        continueScreen.enabled = false;
    }
    public void ToClassScreen()
    {
        creationCanvas.enabled = false;
        continueScreen.enabled = true;
    }
    /// <summary>
    /// Converts the stats from the TextMeshProUGUIs into a List format
    /// </summary>
    /// <returns>List of stats in a set order</returns>
    private List<int> ExtractStats()
    {
        // Dictionary and List reorders the stats properly to be initialized into an array to be put into a Player constructor
        Dictionary<int, int> statMap = new Dictionary<int, int>();
        foreach (KeyValuePair<string, int> pair in stats)
        {
            switch (pair.Key)
            {
                case "STR":
                {
                    statMap.Add(0, pair.Value);
                    break;
                }
                case "INT":
                {
                    statMap.Add(1, pair.Value);
                    break;
                }
                case "CON":
                {
                    statMap.Add(2, pair.Value);
                    break;
                }
                case "DEX":
                {
                    statMap.Add(3, pair.Value);
                    break;
                }
                case "CHA":
                {
                    statMap.Add(4, pair.Value);
                    break;
                }
                case "WIS":
                {
                    statMap.Add(5, pair.Value);
                    break;
                }
                case "LCK":
                {
                    statMap.Add(6, pair.Value);
                    break;
                }
            }
        }

        List<int> statList = new List<int>();
        for (int i = 0; i < statMap.Count; i++)
            statList.Add(statMap[i]);

        return statList;
    }
    /// <summary>
    /// Function given to all Left facing buttons that will decrease the value
    /// within the box next to it by one, also checks secondary stats based on the
    /// stat_Ref passed
    /// </summary>
    /// <param name="statRef">String reference abbreviation for the stat, i.e. STR or INT</param>
    public void DecreaseStat(string statRef)
    {
        // Retrieves the original value
        int v = stats[statRef];

        // If the value were to go beneath the min stat for the creation screen simply return
        // Otherwise increase the dispersal points and decrease the stat
        if (v - 1 < Globals.BaseMinStat)
            return;
        else
        {
            pointsToDisperse++;
            pointsRemainField.text = pointsToDisperse.ToString();
            stats.Remove(statRef);
            stats.Add(statRef, --v);
            fieldRefs[statRef].text = stats[statRef].ToString();
        }

        switch (statRef)
        {
            case "CON":
            {
                SetHealth();
                break;
            }
            case "DEX":
            {
                SetArmor();
                break;
            }
            case "INT":
            {
                SetMana();
                break;
            }
            default:
                break;
        }

        AnalyzeAspects();
        ChangeAspect(currentAspect is -1 ? 0 : currentAspect, true);
    }
    /// <summary>
    /// Set to each of the Right facing buttons on the creation screen
    /// Increases the value in the box next to the button by one point if
    /// points are available, also checks secondary stats based on the
    /// stat_Ref passed
    /// </summary>
    /// <param name="statRef">String reference abbreviation for the stat, i.e. STR or INT</param>
    public void IncreaseStat(string statRef)
    {
        // Retrieves the original value
        int v = stats[statRef];

        // If the value would become greater than 20 or there are no more points to disperse simply return
        // Otherwise, increase the stat value based by the string ref and decrease the dispersing points value
        if (v + 1 > Globals.BaseMaxStat)
            return;
        else if (pointsToDisperse == 0)
            return;
        else
        {
            pointsToDisperse--;
            pointsRemainField.text = pointsToDisperse.ToString();
            stats.Remove(statRef);
            stats.Add(statRef, ++v);
            fieldRefs[statRef].text = stats[statRef].ToString();
        }

        switch (statRef)
        {
            case "CON":
            {
                SetHealth();
                break;
            }
            case "DEX":
            {
                SetArmor();
                break;
            }
            case "INT":
            {
                SetMana(); 
                break;
            }
            default:
                break;
        }

        AnalyzeAspects();
        ChangeAspect(currentAspect is -1 ? 0 : currentAspect, true);
    }
    /// <summary>
    /// A public call for the buttons on the character creation screen
    /// </summary>
    /// <param name="aspectId">The value attached to the buttons</param>
    public void InjectChangeCall(int aspectId) 
        => ChangeAspect(aspectId);
    /// <summary>
    /// Changes the aspect title and switches the aspects shown within the panel
    /// </summary>
    /// <param name="aspectId">The ID for which aspects to be shown</param>
    private void ChangeAspect(int aspectId, bool wasRandomized = false)
    {
        // If the ID passed matches the current aspect simply return to prevent rechecks
        if (aspectId == currentAspect && !wasRandomized)
            return;

        // Disable each of the activated images in the buttons next to the panel
        for (int i = 0; i < imgs.Count; i++)
            imgs[i].enabled = false;

        // Change the title of the aspect panels to match the ID
        switch (aspectId)
        {
            case 0:
            {
                aspectField.text = "Weapons";
                break;
            }
            case 1:
            {
                aspectField.text = "Apparel";
                break;
            }
            case 2:
            {
                aspectField.text = "Spells";
                break;
            }
            case 3:
            {
                aspectField.text = "Abilities";
                break;
            }
        }

        // Enable the button activation image correlated to the ID passed and 
        // also contain the ID that was just passed as the new check for the
        // beginning of the function
        imgs[aspectId].enabled = true;  
        currentAspect = aspectId;

        // Show the correlated aspects
        ShowAspect();
    }
    /// <summary>
    /// When the back button is clicked, it also resets the text field back to blank
    /// </summary>
    public void BackToTitleScreen()
    {
        namingField.text = string.Empty;
        characterName = string.Empty;
    }
    /// <summary>
    /// Sets the player type from the class chosen on the pre stats screen
    /// </summary>
    /// <param name="playerType">The class type chosen</param>
    public void SetPlayerClass(int playerType)
    {
        classImage.sprite = classSprites[playerType];
        playerTypeChosen = (Player.PlayerType)playerType;
        string className = ClassName();
        ClassSO[] classes = Resources.LoadAll<ClassSO>("Classes");
        
        foreach (ClassSO classObj in classes)
        {
            if (classObj.ClassName.Equals(className))
            {
                bonusList.text = string.Empty;
                int classLength = classObj.ClassBonuses.Length;
                for (int i = 0; i < classLength; ++i)
                {
                    if (i == classLength - 1)
                        bonusList.text += classObj.ClassBonuses[i];
                    else
                        bonusList.text += classObj.ClassBonuses[i] + '\n';
                }
                currentSkillTree = classObj.SkillTree;

                break;
            }
        }

        string ClassName()
        {
            return playerTypeChosen switch
            {
                Player.PlayerType.Fighter => "Fighter",
                Player.PlayerType.Berserker => "Berserker",
                Player.PlayerType.Wizard => "Wizard",
                Player.PlayerType.Sorcerer => "Sorcerer",
                Player.PlayerType.Ranger => "Ranger",
                Player.PlayerType.Gunslinger => "Gunslinger",

                _ => throw new ArgumentException(),
            };
        }
    }
    public void ToggleAlexsShopCheat(bool state)
        => Globals.AlexsShopCheat = state;
    public void ToggleInfiniteMoneyCheat(bool state)
        => Globals.InfiniteMoneyCheat = state;
    public void ToggleMoreHealthCheat(bool state)
        => Globals.LargeHealthCheat = state;
    public void ToggleMoreManaCheat(bool state)
        => Globals.LargeManaCheat = state;
    public void ToggleMoreSkillPointsCheat(bool state)
        => Globals.Add100SkillPointsCheat = state;

}