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; }