using System; using System.Linq; using System.Collections; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using UnityEngine; using System.Xml; using Unity.VisualScripting; using static UnityEngine.EventSystems.EventTrigger; using static UnityEngine.Rendering.DebugUI; using UnityEngine.UIElements; using UnityEditor; public class SpellManagement : MonoBehaviour { public enum PreparationCalls { NextAttackingEntity, NextTurn, FinishRound, NextEntity } private BattleHandler bhReference; // Reference to the battle handler that is feeding the information to the manager private Player playerRetainer; // Reference to the player object private Enemy[] enemyRetainers; // Reference to all of the enemy objects in the battle private Ally[] allyRetainers; // Reference to all of the ally objects in the battle private Spell currentSpell; // The current spell that is being casted, information is held private Target[] enemyTargets; // Target components for the enemies for the arrow instantiation when trying to activate a spell private Target[] allyTargets; // Target components for the allies for the arrow instantiation when trying to activate a spell private Target playerTarget; // Target component for the player when trying to activate a spell when self-activating TODO---> Implement array for more players private Task chosenTarget; // Task system to await for the chosen target for the player selecting the enemy to attack private Entity spellUser; // The current spell caster entity private Entity spellTarget; // The current target of the spell being casted private SpellSO.Element spellElement; private int valueRetainer; // Container for a value if the spell is advanced private bool stopSpellTask; private bool[] signals; private int[] damageValues; private const int MaxChance = 100; private void DumpGarbage(bool endSpell) { // Garbage is dumped if (endSpell) GarbageCleanUp(); } public void StopSpellTask() => stopSpellTask = true; /// <summary> /// Passes the target of the spell as an entity and indicates what child type the passed parameter it is /// </summary> /// <param name="target">Parent Entity class for the Enemies and Player classes</param> /// <exception cref="NullReferenceException">When target is null or an unexpected Entity type</exception> public void ChooseTarget(Entity target) { if (target is Player) spellTarget = target as Player; else if (target is Enemy) spellTarget = target as Enemy; else if (target is Ally) spellTarget = target as Ally; else throw new NullReferenceException(); chosenTarget = Task.CompletedTask; } /// <summary> /// Initializes all the information for the spell to process and perform as its intended /// </summary> /// <param name="user">The user of the spell, can be any entity type class</param> /// <param name="spell">The spell being casted</param> /// <param name="player">The Player component for reference</param> /// <param name="enemyComps">All of the enemy components on the field</param> /// <param name="allyComps">All of the ally components on the field</param> /// <param name="bh">The BattleHandler that called the spell management logic</param> private void InitializeAllNecessaryComponents(Entity user, Spell spell, Player player, Enemy[] enemyComps, Ally[] allyComps, BattleHandler bh) { stopSpellTask = false; valueRetainer = -100; // Stores all necessary data bhReference = bh; currentSpell = spell; spellElement = spell.SpellType; spellUser = user; if (player != null) { playerRetainer = player; playerTarget = player.gameObject.GetComponent<Target>(); playerTarget.InjectManager(this); } else { playerRetainer = null; playerTarget = null; } enemyRetainers = new Enemy[enemyComps.Length]; enemyRetainers = enemyComps; if (allyComps != null) { allyRetainers = new Ally[allyComps.Length]; allyRetainers = allyComps; allyTargets = new Target[allyComps.Length]; for (int i = 0; i < allyRetainers.Length; i++) { allyTargets[i] = allyRetainers[i].gameObject.GetComponent<Target>(); allyTargets[i].InjectManager(this); } } else { allyRetainers = null; allyTargets = null; } enemyTargets = new Target[enemyComps.Length]; for (int i = 0; i < enemyRetainers.Length; i++) { enemyTargets[i] = enemyRetainers[i].gameObject.GetComponent<Target>(); enemyTargets[i].InjectManager(this); } } /// <summary> /// Begins the spell process by storing necessary information and determining how to activate the spell based off its type. /// If more than one type is detected, the method simply passes to the advanced process /// </summary> /// <param name="user">User of the spell by Entity</param> /// <param name="spell">The spell being used</param> /// <param name="player">The Player Object</param> /// <param name="enemyComps">The Enemy Objects</param> /// <param name="bh">The Battle Handler reference</param> /// <param name="ally_Targetting">True - If the player is the one being targetted | False - if otherwise</param> /// <exception cref="ArgumentException">An exception occurs when a spells type is unable to be determined</exception> public async Task BeginSpellProcess(Entity user, Spell spell, Player player, Enemy[] enemyComps, Ally[] allyComps, BattleHandler bh) { InitializeAllNecessaryComponents(user, spell, player, enemyComps, allyComps, bh); if (currentSpell.CanChooseAllOfType) { if (currentSpell.SpellEffects.Count > 1) await StartAdvancedSpellProcess(null, true); else await StartNormalSpellTypeProcess(); } else if (currentSpell.CanChooseEffects) { SwitchIndicatorsOfSide(true, true, true); bhReference.AwaitSpellChoice(spellUser); bool trigger = await Awaiter(); SwitchIndicatorsOfSide(true, true, false); if (!trigger) { GarbageCleanUp(); return; } List<int> path = DecipherEffect(); // Indicates the path of indexes in the spell effect list to take if (path.Count > 1) await StartAdvancedSpellProcess(path); // Indicates the index value from the path of the spell effect that is wanted else await StartNormalSpellProcess(path[0]); } // If the effect count is greater than one then the Advanced Spell Process will begin else if (currentSpell.SpellEffects.Count > 1) await StartAdvancedSpellProcess(null, currentSpell.CanChooseAllOfType); // Otherwise the simple methods are processed based on the singular type indicated else await StartNormalSpellProcess(); } /// <summary> /// Determines what route the spell effects need to take the logic /// </summary> /// <param name="spellIndexRef">Index of the spell effects to use and consider</param> /// <param name="indicator">Indicator if the spell is finished</param> private async Task StartNormalSpellProcess(int spellIndexRef = -1, bool indicator = true) { SpellSO.SpellEffect sType = currentSpell.SpellEffects[spellIndexRef is -1 ? 0 : spellIndexRef]; switch (sType) { case SpellSO.SpellEffect.Heal: signals = new bool[6] { true, false, false, false, false, false }; await ActivateHealSpell(indicator); break; case SpellSO.SpellEffect.Refill: signals = new bool[6] { false, true, false, false, false, false }; await ActivateRefillSpell(indicator); break; case SpellSO.SpellEffect.Buff: signals = new bool[6] { false, false, true, false, false, false }; await ActivateBuffSpell(indicator); break; case SpellSO.SpellEffect.Debuff: signals = new bool[6] { false, false, false, true, false, false }; await ActivateDebuffSpell(indicator); break; case SpellSO.SpellEffect.Damage: signals = new bool[6] { false, false, false, false, true, false }; await ActivateDamageSpell(indicator); break; case SpellSO.SpellEffect.Other: signals = new bool[6] { false, false, false, false, false, true }; await ActivateMiscSpellEffects(indicator); break; } } /// <summary> /// Calculates the normal spell process for a type or side of entities /// </summary> /// <param name="spellIndexRef">The spell effect index reference point</param> private async Task StartNormalSpellTypeProcess(int spellIndexRef = -1) { SpellSO.SpellEffect sType = currentSpell.SpellEffects[spellIndexRef is -1 ? 0 : spellIndexRef]; switch (sType) { case SpellSO.SpellEffect.Damage: signals = new bool[5] { true, false, false, false, false }; await ActivateDamageSpellForType(currentSpell.Left, currentSpell.Right); break; case SpellSO.SpellEffect.Heal: signals = new bool[5] { false, true, false, false, false }; await ActivateHealSpellForType(currentSpell.Left, currentSpell.Right); break; case SpellSO.SpellEffect.Buff: signals = new bool[5] { false, false, true, false, false }; await ActivateBuffSpellForType(currentSpell.Left, currentSpell.Right); break; case SpellSO.SpellEffect.Debuff: signals = new bool[5] { false, false, false, true, false }; await ActivateDebuffSpellForType(currentSpell.Left, currentSpell.Right); break; case SpellSO.SpellEffect.Other: signals = new bool[5] { false, false, false, false, true }; await ActivateMiscSpellEffects(); break; } } /// <summary> /// Goes through each of the effects in the spell and activates each of their portions on the target /// </summary> private async Task StartAdvancedSpellProcess(List<int> path = null, bool isAllType = false) { bool trigger = false; valueRetainer = -100; if (isAllType) { int totalValue = currentSpell.SpellEffects.Count; for (int i = 0; i < totalValue; ++i) { if (i == totalValue - 1) trigger = true; switch (currentSpell.SpellEffects[i]) { case SpellSO.SpellEffect.Damage: signals = new bool[5] { true, false, false, false, false }; await ActivateDamageSpellForType(currentSpell.Left, currentSpell.Right, trigger); break; case SpellSO.SpellEffect.Heal: signals = new bool[5] { false, true, false, false, false }; await ActivateHealSpellForType(currentSpell.Left, currentSpell.Right, trigger); break; case SpellSO.SpellEffect.Buff: signals = new bool[5] { false, false, true, false, false }; await ActivateBuffSpellForType(currentSpell.Left, currentSpell.Right, trigger); break; case SpellSO.SpellEffect.Debuff: signals = new bool[5] { false, false, false, true, false }; await ActivateDebuffSpellForType(currentSpell.Left, currentSpell.Right, trigger); break; case SpellSO.SpellEffect.Other: signals = new bool[5] { false, false, false, false, true }; await ActivateMiscSpellEffects(); break; } } } else { if (path is null) { int totalValue = currentSpell.SpellEffects.Count; // Cycles through each effect of the spell into their respective functionalities for (int i = 0; i < totalValue; i++) { if (i == totalValue - 1) trigger = true; await StartNormalSpellProcess(i, trigger); } } else { int totalValue = path.Count; for (int i = 0; i < totalValue; i++) { if (i == totalValue - 1) trigger = true; await StartNormalSpellProcess(path[i], trigger); } } } } private int DecipherRefillValue() { int refillAmount; switch (currentSpell.SpellID) { case 51: // Mana Transfusion int manaTransferAmount = spellUser.GetManaPercentage() < 25 ? spellUser.RetrieveMana() : (int)Math.Ceiling(spellUser.RetrieveMaxMana() * .25f); Dictionary<int, Ability> abList = AbilityBrain.GetAbilityList(); bool subtracting = true; if (spellUser.GetInventory().Abilities.Contains(abList[116]) || spellUser.GetInventory().limitedAbilities.Contains(abList[116])) { int rollValue = UnityEngine.Random.Range(1, 6); if (rollValue is 1) subtracting = false; } if (subtracting) { spellUser.DecreaseMana(manaTransferAmount); spellUser.StatusChanged(); } refillAmount = manaTransferAmount; break; default: throw new InvalidOperationException(); } return refillAmount; } private Entity FindMostExhausted() { Enemy output = null; Enemy[] exhaustedEnemies = enemyRetainers .Where(enemy => enemy.RetrieveMana() < enemy.RetrieveMaxMana()) .ToArray(); if (exhaustedEnemies.Length is 0) output = enemyRetainers[UnityEngine.Random.Range(0, enemyRetainers.Length)]; // Cycles through all of the damaged enemies for the most damaged // If two enemies have the same health than a coin flip for the place // holder of being held is essentially held foreach (Enemy e in exhaustedEnemies) { if (output == null) { output = e; continue; } if (output.GetManaPercentage() > e.GetManaPercentage()) output = e; else if (output.GetManaPercentage() == e.GetManaPercentage()) { int z = UnityEngine.Random.Range(0 , MaxChance + 1); if (z > MaxChance / 2) output = e; } } return output; } /// <summary> /// Activates the spell which primarily just does damage /// </summary> private async Task ActivateDamageSpell(bool endSpell = true) { bool extraDice = FieldFocusCheck(); bool isSelf = false; bool systemChecked; int damageValue = UtilityHandler.RollValue(currentSpell.Dice.ToArray()); if (extraDice) damageValue += UtilityHandler.RollValue(currentSpell.Dice[0]); // If the player or ally is the target if (spellUser is Player || spellUser is Ally) { if (!currentSpell.UniqueTargetting) SwitchIndicatorsOfSide(false, true, true); else { (systemChecked, isSelf) = EnableUniqueTargettingSystem(); if (!systemChecked) { GarbageCleanUp(); return; } } if (!isSelf) { bhReference.AwaitSpellChoice(spellUser); bool condition = await Awaiter(); SwitchIndicatorsOfSide(true, true, false); if (!condition) { GarbageCleanUp(); return; } } bhReference.GetUIHandler().ResetUI(); damageValue = UtilityHandler.SpellTypeCheck(currentSpell, damageValue, spellTarget, spellUser); if (!endSpell) valueRetainer = damageValue; (bool specialEffectUsed, bool uniqueEndCall) = UniqueSpellEffectCheck(valueRetainer != -100 ? valueRetainer : damageValue, 0); if (uniqueEndCall) { GarbageCleanUp(); return; } else if (!specialEffectUsed && endSpell) SpellCasterCasts(); } else { // Modifies the spell user's stats spellTarget = ChooseRandomTargetForEnemy(); damageValue = UtilityHandler.SpellTypeCheck(currentSpell, damageValue, spellTarget, spellUser); if (!endSpell) valueRetainer = damageValue; if (spellTarget == null) { GarbageCleanUp(); return; } (bool specialEffectUsed, bool uniqueEndCall) = UniqueSpellEffectCheck(valueRetainer != -100 ? valueRetainer : damageValue, 0); if (uniqueEndCall) { GarbageCleanUp(); return; } else if (!specialEffectUsed && endSpell) SpellCasterCasts(); } if (endSpell) { if (TargetRollsSavingThrow()) // Succeeded Saving Throw { switch (currentSpell.SaveFailEffect) { case SpellSO.SavingThrowFailEffect.SpellFails: await bhReference.FailedSpellCall(currentSpell); DumpGarbage(true); return; case SpellSO.SavingThrowFailEffect.HalfSpellValue: damageValue /= 2; if (spellUser is Player player) damageValue += player.ParseClassDamageModifiers(spellTarget); await InitializeSpellEffect(); await bhReference.AttackWithSpell(damageValue, currentSpell, spellUser, spellTarget, endSpell); break; case SpellSO.SavingThrowFailEffect.NoDebuff: if (spellUser is Player player2) damageValue += player2.ParseClassDamageModifiers(spellTarget); await InitializeSpellEffect(); await bhReference.AttackWithSpell(damageValue, currentSpell, spellUser, spellTarget, endSpell); break; } if (spellTarget.GetInventory().Abilities.Contains(AbilityBrain.GetAbilityList()[86])) { spellTarget.IncreaseMana((int)Math.Floor(currentSpell.SpellCost * .5f)); spellTarget.StatusChanged(); } } else { if (spellUser is Player player) damageValue += player.ParseClassDamageModifiers(spellTarget); if (spellTarget.GetInventory().Abilities.Contains(AbilityBrain.GetAbilityList()[86])) { spellTarget.IncreaseMana((int)Math.Floor(currentSpell.SpellCost * .5f)); spellTarget.StatusChanged(); } await InitializeSpellEffect(); await bhReference.AttackWithSpell(damageValue, currentSpell, spellUser, spellTarget, endSpell); } } DumpGarbage(endSpell); } /// <summary> /// Damages either all enemies or all allies on one side /// </summary> /// <param name="left">Ally side indicator</param> /// <param name="right">Enemy side indicator</param> /// <param name="endSpell">Indicator if this step is the end of the spell</param> private async Task ActivateDamageSpellForType(bool left, bool right, bool endSpell = true) { if (!left && !right) { if (spellUser is Enemy) left = true; else right = true; } bool extraDice = FieldFocusCheck(); // Calculates damage value int damageValue = UtilityHandler.RollValue(currentSpell.Dice.ToArray()); if (extraDice) damageValue += UtilityHandler.RollValue(currentSpell.Dice[0]); (bool specialEffectUsed, bool uniqueEndCall) = UniqueSpellEffectCheck(valueRetainer != -100 ? valueRetainer : damageValue, -4); if (uniqueEndCall) { GarbageCleanUp(); return; } else if (!specialEffectUsed) { damageValues = new int[4]; if (left) { if (playerRetainer != null) { int value = UtilityHandler.SpellTypeCheck(currentSpell, damageValue, playerRetainer, spellUser); if (endSpell) { bool succeeded = TargetRollsSavingThrow(playerRetainer); switch (currentSpell.SaveFailEffect) { case SpellSO.SavingThrowFailEffect.SpellFails: break; case SpellSO.SavingThrowFailEffect.HalfSpellValue: if (succeeded) value /= 2; break; case SpellSO.SavingThrowFailEffect.NoDebuff: break; } damageValues[0] = value; if (playerRetainer.IsHidden) playerRetainer.ShowEntity(); } } if (!endSpell) valueRetainer = damageValue; if (allyRetainers != null) { for (int i = 0; i < allyRetainers.Length; i++) { int value = UtilityHandler.SpellTypeCheck(currentSpell, damageValue, allyRetainers[i], spellUser); if (endSpell) { bool succeeded = TargetRollsSavingThrow(allyRetainers[i]); switch (currentSpell.SaveFailEffect) { case SpellSO.SavingThrowFailEffect.SpellFails: continue; case SpellSO.SavingThrowFailEffect.HalfSpellValue: if (succeeded) value /= 2; break; case SpellSO.SavingThrowFailEffect.NoDebuff: break; } if (allyRetainers[i].IsHidden) allyRetainers[i].ShowEntity(); } if (spellUser is Player player) value += player.ParseClassDamageModifiers(allyRetainers[i]); damageValues[i + 1] = value; } } } else { for (int i = 0; i < enemyRetainers.Length; ++i) { int value = UtilityHandler.SpellTypeCheck(currentSpell, damageValue, enemyRetainers[i], spellUser); if (endSpell) { bool succeeded = TargetRollsSavingThrow(enemyRetainers[i]); switch (currentSpell.SaveFailEffect) { case SpellSO.SavingThrowFailEffect.SpellFails: continue; case SpellSO.SavingThrowFailEffect.HalfSpellValue: if (succeeded) value /= 2; break; case SpellSO.SavingThrowFailEffect.NoDebuff: break; } if (enemyRetainers[i].IsHidden) enemyRetainers[i].ShowEntity(); } if (spellUser is Player player) value += player.ParseClassDamageModifiers(enemyRetainers[i]); damageValues[i] = value; } } } if (damageValues.All(x => x is 0)) { await bhReference.FailedSpellCall(currentSpell); DumpGarbage(true); return; } if (endSpell) { if (spellTarget != null) { if (spellTarget.GetInventory().Abilities.Contains(AbilityBrain.GetAbilityList()[86])) { spellTarget.IncreaseMana((int)Math.Floor(currentSpell.SpellCost * .5f)); spellTarget.StatusChanged(); } } SpellCasterCasts(); await InitializeSpellEffect(); await bhReference.AttackWithSpell(damageValues, currentSpell, spellUser, left, right); } DumpGarbage(endSpell); } /// <summary> /// Heals the allies of the user of the spell based on their targetting /// </summary> /// <param name="endSpell">Indicator for if the end of the spell is occuring and the garbage should be cleared and the turn should finally end</param> /// <param name="firstOfAdvanced">Indicator for when the enemy should determine who the best ally is to heal on their side</param> private async Task ActivateHealSpell(bool endSpell = true) { bool extraDice = FieldFocusCheck(); bool systemChecked; bool isSelf = false; int healValue = UtilityHandler.RollValue(currentSpell.Dice.ToArray()); if (extraDice) healValue += UtilityHandler.RollValue(currentSpell.Dice[0]); // When the player uses a healing spell, heals themselves if (spellUser is Player || spellUser is Ally) { if (!currentSpell.UniqueTargetting) SwitchIndicatorsOfSide(true, false, true); else { (systemChecked, isSelf) = EnableUniqueTargettingSystem(); if (!systemChecked) { GarbageCleanUp(); return; } } if (!isSelf) { bhReference.AwaitSpellChoice(spellUser); bool condition = await Awaiter(); SwitchIndicatorsOfSide(true, false, false); if (!condition) { GarbageCleanUp(); return; } } bhReference.GetUIHandler().ResetUI(); (bool specialEffectUsed, bool uniqueEndCall) = UniqueSpellEffectCheck(valueRetainer != -100 ? valueRetainer : healValue, 1); if (uniqueEndCall) { GarbageCleanUp(); return; } else if (!specialEffectUsed && endSpell) { healValue = AbilityBrain.SpellChecks(currentSpell, spellUser, spellTarget, healValue, signals); await InitializeSpellEffect(); spellTarget.IncreaseHealth(healValue); spellTarget.StatusChanged(); if (spellTarget.GetInventory().Abilities.Contains(AbilityBrain.GetAbilityList()[86])) { spellTarget.IncreaseMana((int)Math.Floor(currentSpell.SpellCost * .5f)); spellTarget.StatusChanged(); } // Modifies the spell user's stats SpellCasterCasts(); await bhReference.AttackWithSpell(-healValue, currentSpell, spellUser, spellTarget, endSpell); } } // When the enemy uses a healing spell, they choose the most damaged enemy to heal from the // group, if there is a tie they randomly choose else { if (!currentSpell.UniqueTargetting) spellTarget = FindMostDamaged(); else { (systemChecked, isSelf) = EnableUniqueTargettingSystem(); if (!systemChecked) { GarbageCleanUp(); return; } } (bool specialEffectUsed, bool uniqueEndCall) = UniqueSpellEffectCheck(valueRetainer != -100 ? valueRetainer : healValue, 1); if (uniqueEndCall) { GarbageCleanUp(); return; } else if (!specialEffectUsed && endSpell) { healValue = AbilityBrain.SpellChecks(currentSpell, spellUser, spellTarget, healValue, signals); await InitializeSpellEffect(); spellTarget.IncreaseHealth(healValue); spellTarget.StatusChanged(); if (spellTarget.GetInventory().Abilities.Contains(AbilityBrain.GetAbilityList()[86])) { spellTarget.IncreaseMana((int)Math.Floor(currentSpell.SpellCost * .5f)); spellTarget.StatusChanged(); } // Modifies the spell user's stats SpellCasterCasts(); bhReference.CallSpellActionText(spellUser.GetName(), spellTarget.GetName(), currentSpell, healValue); } } DumpGarbage(endSpell); } /// <summary> /// Advanced call for healing a side of a type /// </summary> /// <param name="left">The indicator if the companion side is being targetted</param> /// <param name="right">The indicator if the enemy side is being targetted</param> /// <param name="endSpell">Indicator if the spell is advanced and is at the end of its life</param> private async Task ActivateHealSpellForType(bool left, bool right, bool endSpell = true) { if (!left && !right) { if (spellUser is Enemy) right = true; else left = true; } bool extraDice = FieldFocusCheck(); // Determine the value for the healing int healValue = UtilityHandler.RollValue(currentSpell.Dice.ToArray()); if (extraDice) healValue += UtilityHandler.RollValue(currentSpell.Dice[0]); if (left) // Heal the companion side { (bool specialEffectUsed, bool uniqueEndCall) = UniqueSpellEffectCheck(valueRetainer != -100 ? valueRetainer : healValue, -3); if (uniqueEndCall) { GarbageCleanUp(); return; } else if (!specialEffectUsed && endSpell) { await InitializeSpellEffect(); if (playerRetainer != null) { playerRetainer.IncreaseHealth(AbilityBrain.SpellChecks(currentSpell, spellUser, playerRetainer, healValue, signals)); playerRetainer.StatusChanged(); } if (allyRetainers != null) { foreach (Ally ally in allyRetainers) { ally.IncreaseHealth(AbilityBrain.SpellChecks(currentSpell, spellUser, ally, healValue, signals)); ally.StatusChanged(); } } // Modifies the spell user's stats SpellCasterCasts(); if (playerRetainer.GetInventory().Abilities.Contains(AbilityBrain.GetAbilityList()[86])) { playerRetainer.IncreaseMana((int)Math.Floor(currentSpell.SpellCost * .5f)); playerRetainer.StatusChanged(); } bhReference.GetUIHandler().ResetUI(); bhReference.CallSpellActionText(spellUser, left, right, currentSpell.SpellEffects[0], currentSpell, healValue); bhReference.DisablePlayerTurn(); GarbageCleanUp(); } } if (right) // Heal the enemy side { (bool specialEffectUsed, bool uniqueEndCall) = UniqueSpellEffectCheck(valueRetainer != -100 ? valueRetainer : healValue, -3); if (uniqueEndCall) { GarbageCleanUp(); return; } else if (!specialEffectUsed && endSpell) { await InitializeSpellEffect(); foreach (Enemy enemy in enemyRetainers) { enemy.IncreaseHealth(AbilityBrain.SpellChecks(currentSpell, spellUser, enemy, healValue, signals)); enemy.StatusChanged(); } // Modifies the spell user's stats SpellCasterCasts(); bhReference.GetUIHandler().ResetUI(); bhReference.CallSpellActionText(spellUser, left, right, currentSpell.SpellEffects[0], currentSpell, healValue); if (spellUser is Player) bhReference.DisablePlayerTurn(); GarbageCleanUp(); } } } private async Task ActivateRefillSpell(bool endSpell = true) { bool systemChecked; bool isSelf = false; // When the player uses a healing spell, heals themselves if (spellUser is Player || spellUser is Ally) { if (!currentSpell.UniqueTargetting) SwitchIndicatorsOfSide(true, false, true); else { (systemChecked, isSelf) = EnableUniqueTargettingSystem(); if (!systemChecked) { GarbageCleanUp(); return; } } if (!isSelf) { bhReference.AwaitSpellChoice(spellUser); bool condition = await Awaiter(); if (!condition) { GarbageCleanUp(); return; } SwitchIndicatorsOfSide(true, false, false); } int refillValue = DecipherRefillValue(); (bool specialEffectUsed, bool uniqueEndCall) = UniqueSpellEffectCheck(valueRetainer != -100 ? valueRetainer : refillValue, 1); if (uniqueEndCall) { GarbageCleanUp(); return; } else if (!specialEffectUsed && endSpell) { refillValue = AbilityBrain.SpellChecks(currentSpell, spellUser, spellTarget, refillValue, signals); await InitializeSpellEffect(); spellTarget.IncreaseMana(refillValue); spellTarget.StatusChanged(); await bhReference.AttackWithSpell(-refillValue, currentSpell, spellUser, spellTarget, endSpell); } } // When the enemy uses a refill spell, they choose the enemy with the least mana in terms of percentage else { if (!currentSpell.UniqueTargetting) spellTarget = FindMostExhausted(); else { (systemChecked, isSelf) = EnableUniqueTargettingSystem(); if (!systemChecked) { GarbageCleanUp(); return; } } int refillValue = DecipherRefillValue(); (bool specialEffectUsed, bool uniqueEndCall) = UniqueSpellEffectCheck(valueRetainer != -100 ? valueRetainer : refillValue, 1); if (uniqueEndCall) { GarbageCleanUp(); return; } else if (!specialEffectUsed && endSpell) { refillValue = AbilityBrain.SpellChecks(currentSpell, spellUser, spellTarget, refillValue, signals); await InitializeSpellEffect(); spellTarget.IncreaseMana(refillValue); spellTarget.StatusChanged(); await bhReference.AttackWithSpell(-refillValue, currentSpell, spellUser, spellTarget, endSpell); } } DumpGarbage(endSpell); } /// <summary> /// Buffs the designated entity/spell_target with the spell and gives it a buff modifier /// </summary> /// <param name="endSpell">Indicator for if the end of the spell is occuring and the garbage should be cleared and the turn should finally end</param> private async Task ActivateBuffSpell(bool endSpell = true) { bool systemChecked; bool isSelf = false; // If the player is not the target if (spellUser is Enemy) { if (!currentSpell.UniqueTargetting) { int enemy = UnityEngine.Random.Range(0, enemyRetainers.Length); spellTarget = enemyRetainers[enemy]; } else { (systemChecked, isSelf) = EnableUniqueTargettingSystem(); if (!systemChecked) { GarbageCleanUp(); return; } } (bool specialEffectUsed, bool uniqueEndCall) = UniqueSpellEffectCheck(valueRetainer, 2); if (uniqueEndCall) { GarbageCleanUp(); return; } else if (!specialEffectUsed) await NonDamageSpellCall(true, endSpell); } else { if (!currentSpell.UniqueTargetting) SwitchIndicatorsOfSide(true, false, true); else { (systemChecked, isSelf) = EnableUniqueTargettingSystem(); if (!systemChecked) { GarbageCleanUp(); return; } } if (!isSelf) { bhReference.AwaitSpellChoice(spellUser); bool condition = await Awaiter(); if (!condition) { GarbageCleanUp(); return; } SwitchIndicatorsOfSide(true, false, false); } (bool specialEffectUsed, bool uniqueEndCall) = UniqueSpellEffectCheck(valueRetainer, 2); if (uniqueEndCall) { GarbageCleanUp(); return; } else if (!specialEffectUsed) await NonDamageSpellCall(true, endSpell); } DumpGarbage(endSpell); } /// <summary> /// Simply calls a different version of the NonDamageSpellCall before cleaning garbage if needed /// </summary> /// <param name="left">Indicator if the left side is all targetted</param> /// <param name="right">Indicator if the right side is all targetted</param> /// <param name="endSpell">Indicator if the spell is advanced and at the end of its life</param> private async Task ActivateBuffSpellForType(bool left, bool right, bool endSpell = true) { if (!left && !right) { if (spellUser is Enemy) right = true; else left = true; } (bool specialEffectUsed, bool uniqueEndCall) = UniqueSpellEffectCheck(valueRetainer, -2); if (left && right) { if (currentSpell.IsDebuffLeftAndBuffRight()) await NonDamageSpellCall(false, true, endSpell, true); else if (currentSpell.IsDebuffRightAndBuffLeft()) await NonDamageSpellCall(true, false, endSpell, true); return; } if (uniqueEndCall) { GarbageCleanUp(); return; } else if (!specialEffectUsed) await NonDamageSpellCall(left, right, endSpell); DumpGarbage(endSpell); } /// <summary> /// Debuffs the designated entity/spell_target with the spell and gives the entity a debuff modifier /// </summary> /// <param name="endSpell">Indicator for if the end of the spell is occuring and the garbage should be cleared and the turn should finally end</param> private async Task ActivateDebuffSpell(bool endSpell = true) { bool succeeded; if (spellUser is Enemy) { // If the spell target for an enemy was already gathered via other spell effect calls, skip if (spellTarget == null) spellTarget = ChooseRandomTargetForEnemy(); if (spellTarget == null) { GarbageCleanUp(); return; } succeeded = TargetRollsSavingThrow(); if (succeeded && endSpell) { switch (currentSpell.SaveFailEffect) { case SpellSO.SavingThrowFailEffect.SpellFails: await bhReference.FailedSpellCall(currentSpell); DumpGarbage(true); return; case SpellSO.SavingThrowFailEffect.HalfSpellValue: valueRetainer /= 2; break; case SpellSO.SavingThrowFailEffect.NoDebuff: break; } } (bool specialEffectUsed, bool uniqueEndCall) = UniqueSpellEffectCheck(valueRetainer, 3); if (uniqueEndCall) { GarbageCleanUp(); return; } else if (!specialEffectUsed) await NonDamageSpellCall(succeeded, endSpell); } else { SwitchIndicatorsOfSide(false, true, true); bhReference.AwaitSpellChoice(spellUser); bool condition = await Awaiter(); if (!condition) { GarbageCleanUp(); return; } SwitchIndicatorsOfSide(false, true, false); succeeded = TargetRollsSavingThrow(); if (succeeded && endSpell) { switch (currentSpell.SaveFailEffect) { case SpellSO.SavingThrowFailEffect.SpellFails: await bhReference.FailedSpellCall(currentSpell); DumpGarbage(endSpell); return; case SpellSO.SavingThrowFailEffect.HalfSpellValue: valueRetainer /= 2; break; case SpellSO.SavingThrowFailEffect.NoDebuff: break; } } (bool specialEffectUsed, bool uniqueEndCall) = UniqueSpellEffectCheck(valueRetainer, 3); if (uniqueEndCall) { GarbageCleanUp(); return; } else if (!specialEffectUsed) await NonDamageSpellCall(succeeded, endSpell); } DumpGarbage(endSpell); } /// <summary> /// Simply calls a different version of the NonDamageSpellCall before cleaning garbage if needed /// </summary> /// <param name="left">Indicator if the left side is all targetted</param> /// <param name="right">Indicator if the right side is all targetted</param> /// <param name="endSpell">Indicator if the spell is advanced and at the end of its life</param> private async Task ActivateDebuffSpellForType(bool left, bool right, bool endSpell = true) { if (!left && !right) { if (spellUser is Enemy) left = true; else right = true; } (bool specialEffectUsed, bool uniqueEndCall) = UniqueSpellEffectCheck(valueRetainer, -1); if (left && right) { if (currentSpell.IsDebuffLeftAndBuffRight()) await NonDamageSpellCall(true, false, endSpell, false); else if (currentSpell.IsDebuffRightAndBuffLeft()) await NonDamageSpellCall(false, true, endSpell, false); return; } if (uniqueEndCall) { GarbageCleanUp(); return; } else if (!specialEffectUsed) await NonDamageSpellCall(left, right, endSpell); DumpGarbage(endSpell); } private async Task ActivateMiscSpellEffects(bool indicator = true) { foreach (SpellSO.OtherEffects effect in currentSpell.miscEffects) { switch (effect) { // Simply switches the weather type case SpellSO.OtherEffects.ManipulateWeather: bhReference.GetUIHandler().ResetUI(); ClimateManager.Weather weatherType = currentSpell.weatherToChangeTo; SpellCasterCasts(); await InitializeSpellEffect(); ClimateManager.Instance.ManipulateWeather(weatherType); if (weatherType is ClimateManager.Weather.Blizzard) bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(1); else if (weatherType is ClimateManager.Weather.Rainy) bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(20); else if (weatherType is ClimateManager.Weather.Sunny) bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(37); if (spellUser is Player || spellUser is Ally) bhReference.DisablePlayerTurn(); break; case SpellSO.OtherEffects.SummonMinions: bool successful; bool isLeft = spellUser is Ally || spellUser is Player; switch (currentSpell.SpellID) { case 25: // Summon Golem ClimateManager.Weather currentWeather = ClimateManager.Instance.GetWeather(); // Determine Golem Type from current weather // TODO ---> Make a Strange Golem that occurs during Eclipse GameObject golem; if (currentWeather is ClimateManager.Weather.Sunny) golem = currentSpell.minionSummons[1]; // Fire else if (currentWeather is ClimateManager.Weather.Cloudy || currentWeather is ClimateManager.Weather.Eclipsed || currentWeather is ClimateManager.Weather.Rainy) golem = currentSpell.minionSummons[0]; // Rock else if (currentWeather is ClimateManager.Weather.Monsoon || currentWeather is ClimateManager.Weather.Foggy) golem = currentSpell.minionSummons[2]; // Wind else golem = currentSpell.minionSummons[3]; // Ice SpellCasterCasts(); await InitializeSpellEffect(true); successful = bhReference.CreateMinion(golem, isLeft); bhReference.GetUIHandler().ResetUI(); if (successful) bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(2); // Succeeded in summoning a golem else bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(3); // Failed to summon a golem if (isLeft) bhReference.DisablePlayerTurn(); break; case 53: // Summon Demonic Soldier bhReference.GetUIHandler().ResetUI(); GameObject demonicSoldier = isLeft ? currentSpell.minionSummons[1] : currentSpell.minionSummons[0]; SpellCasterCasts(); await InitializeSpellEffect(true); successful = bhReference.CreateMinion(demonicSoldier, isLeft); if (successful) bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(18); else bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(19); if (isLeft) bhReference.DisablePlayerTurn(); break; case 76: // Summon Damned Soul bhReference.GetUIHandler().ResetUI(); GameObject damnedSoul = isLeft ? currentSpell.minionSummons[1] : currentSpell.minionSummons[0]; SpellCasterCasts(); await InitializeSpellEffect(true); successful = bhReference.CreateMinion(damnedSoul, isLeft); if (successful) bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(38); else bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(39); if (isLeft) bhReference.DisablePlayerTurn(); break; } break; case SpellSO.OtherEffects.CureAilments: bhReference.GetUIHandler().ResetUI(); SwitchIndicatorsOfSide(true, false, true); bhReference.AwaitSpellChoice(spellUser); bool condition = Awaiter().GetAwaiter().GetResult(); if (!condition) { GarbageCleanUp(); return; } SpellCasterCasts(); await InitializeSpellEffect(); bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(7); spellTarget.CleanseAilments(); if (spellUser is Player || spellUser is Ally) bhReference.DisablePlayerTurn(); break; case SpellSO.OtherEffects.Revive: bhReference.GetUIHandler().ResetUI(); if (spellUser is Ally || spellUser is Player) { bool picked; (picked, spellTarget) = bhReference.IndicateRevivalChoices().GetAwaiter().GetResult(); if (!picked) { GarbageCleanUp(); return; } } else { spellTarget = bhReference.ChooseRandomDeadEnemy(); if (spellTarget == null) { GarbageCleanUp(); return; } } SpellCasterCasts(); await InitializeSpellEffect(); spellTarget.Revive(); bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextSpell(spellUser.GetName(), spellTarget.GetName(), ActionTextBox.ActionType.ReviveOne, currentSpell); if (spellUser is Player || spellUser is Ally) bhReference.DisablePlayerTurn(); break; case SpellSO.OtherEffects.Unique: await DecipherUniqueEffect(); break; } } DumpGarbage(indicator); } private async Task DecipherUniqueEffect() { bool condition = false; bool succeeded = false; int totalDamage = 0; switch (currentSpell.SpellID) { case 33: // Life Contract bhReference.GetUIHandler().ResetUI(); // Heal entity int healValue = UtilityHandler.RollValue(Dice.DiceR.Four, Dice.DiceR.Four, Dice.DiceR.Four, Dice.DiceR.Four); spellUser.IncreaseHealth(healValue); spellUser.StatusChanged(); // Set Life Contract Mod Modifier modToAdd = AbilityBrain.GetModifierList()["Life Contract"]; modToAdd.RetentionObject = new List<int> { healValue, 0 }; spellUser.AddModToList(modToAdd); spellUser.AfflictedStatus(); SpellCasterCasts(); await InitializeSpellEffect(); bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(4); if (spellUser is Player || spellUser is Ally) bhReference.DisablePlayerTurn(); break; case 43: // Erratic Bursts bhReference.GetUIHandler().ResetUI(); SpellCasterCasts(); for (int i = 0; i < 3; ++i) { if (enemyRetainers.All(enemy => enemy.HasDied)) { bhReference.DisablePlayerTurn(); break; } int targetAttempts = 0; do { spellTarget = enemyRetainers[UnityEngine.Random.Range(0, enemyRetainers.Length)]; targetAttempts++; } while (spellTarget.HasDied && targetAttempts < 10); if (targetAttempts >= 10) break; bool erraticSucceed = TargetRollsSavingThrow(); int rolledDamage = UtilityHandler.RollValue(Dice.DiceR.Eight, Dice.DiceR.Eight); if (erraticSucceed) { totalDamage += rolledDamage / 2; await InitializeSpellEffect(); await bhReference.AttackWithSpell(rolledDamage / 2, currentSpell, spellUser, spellTarget, false); } else { totalDamage += rolledDamage; await InitializeSpellEffect(); await bhReference.AttackWithSpell(rolledDamage, currentSpell, spellUser, spellTarget, false); } spellTarget.StatusChanged(); } bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(32, totalDamage, null, spellUser); if (spellUser is Player || spellUser is Ally) bhReference.DisablePlayerTurn(); break; case 58: // Chain Lightning Entity secondSpellTarget; if (spellUser is Player || spellUser is Ally) { SwitchIndicatorsOfSide(false, true, true); bhReference.AwaitSpellChoice(spellUser); condition = await Awaiter(); SwitchIndicatorsOfSide(false, true, false); if (!condition) { GarbageCleanUp(); return; } bhReference.GetUIHandler().ResetUI(); SpellCasterCasts(); await InitializeSpellEffect(); succeeded = TargetRollsSavingThrow(); int primaryDamage = UtilityHandler.RollValue(Dice.DiceR.Eight, Dice.DiceR.Eight, Dice.DiceR.Eight, Dice.DiceR.Eight); if (succeeded) primaryDamage /= 2; ActionTextBox atb = bhReference.GetUIHandler().RetrieveActionText(); atb.UpdateActionTextUnique(40, primaryDamage, spellTarget, spellUser); bhReference.DamageEnemy(spellTarget as Enemy, primaryDamage, false, false); await atb.GetTextShownStatus(); if (enemyRetainers.Length > 1) { ClimateManager.Weather currentWeather = ClimateManager.Instance.GetWeather(); int chainTimes = currentWeather is ClimateManager.Weather.Rainy || currentWeather is ClimateManager.Weather.Monsoon ? 2 : 1; for (int times = 0; times < chainTimes; ++times) { int targetAttempts = 0; do { secondSpellTarget = enemyRetainers[UnityEngine.Random.Range(0, enemyRetainers.Length)]; targetAttempts++; } while ((secondSpellTarget.HasDied is true || secondSpellTarget.GetUniqueID() == spellTarget.GetUniqueID()) && targetAttempts < 10); if (targetAttempts >= 10) { bhReference.DisablePlayerTurn(); break; } int secondaryDamage = UtilityHandler.RollValue(Dice.DiceR.Eight, Dice.DiceR.Eight); succeeded = TargetRollsSavingThrow(secondSpellTarget); if (succeeded) secondaryDamage /= 2; bhReference.DamageEnemy(secondSpellTarget as Enemy, secondaryDamage, false, false); atb.UpdateActionTextUnique(41, secondaryDamage, secondSpellTarget, spellUser); await atb.GetTextShownStatus(); } bhReference.DisablePlayerTurn(); } } else { spellTarget = ChooseRandomTargetForEnemy(); if (spellTarget == null) { GarbageCleanUp(); return; } SpellCasterCasts(); await InitializeSpellEffect(); int primaryDamage = UtilityHandler.RollValue(Dice.DiceR.Eight, Dice.DiceR.Eight, Dice.DiceR.Eight, Dice.DiceR.Eight); succeeded = TargetRollsSavingThrow(); if (succeeded) primaryDamage /= 2; if (spellTarget is Player) bhReference.DamagePlayer(primaryDamage, false); else bhReference.DamageAlly(spellTarget as Ally, primaryDamage, false); ActionTextBox atb = bhReference.GetUIHandler().RetrieveActionText(); atb.UpdateActionTextUnique(40, primaryDamage, spellTarget, spellUser); await atb.GetTextShownStatus(); int newLength = playerRetainer != null && allyRetainers != null ? allyRetainers.Length + 1 : (playerRetainer != null && allyRetainers == null ? 1 : (playerRetainer == null && allyRetainers != null ? allyRetainers.Length : 0)); Entity[] leftEntities = new Entity[newLength]; int indexCorrelator = 0; if (playerRetainer != null) leftEntities[indexCorrelator++] = playerRetainer; if (allyRetainers != null) { for (int i = 0; i < allyRetainers.Length; ++i) leftEntities[indexCorrelator++] = allyRetainers[i]; } if (newLength > 1) { ClimateManager.Weather currentWeather = ClimateManager.Instance.GetWeather(); int chainTimes = currentWeather is ClimateManager.Weather.Rainy || currentWeather is ClimateManager.Weather.Monsoon ? 2 : 1; for (int times = 0; times < chainTimes; ++chainTimes) { int targetAttempts = 0; do { secondSpellTarget = leftEntities[UnityEngine.Random.Range(0, leftEntities.Length)]; targetAttempts++; } while ((secondSpellTarget.HasDied is true || secondSpellTarget.GetUniqueID() == spellTarget.GetUniqueID()) && targetAttempts < 10); if (targetAttempts >= 10) break; int secondaryDamage = UtilityHandler.RollValue(Dice.DiceR.Eight, Dice.DiceR.Eight); succeeded = TargetRollsSavingThrow(secondSpellTarget); if (succeeded) secondaryDamage /= 2; if (secondSpellTarget is Player) bhReference.DamagePlayer(secondaryDamage, false); else bhReference.DamageAlly(secondSpellTarget as Ally, secondaryDamage, false); atb.UpdateActionTextUnique(41, secondaryDamage, secondSpellTarget, spellUser); await atb.GetTextShownStatus(); } } } break; case 61: // Sacrificial Rite bhReference.GetUIHandler().ResetUI(); bool canCast = spellUser.RetrieveHealth() > (spellUser.RetrieveMaxHealth() / 2); if (canCast) { spellUser.DecreaseHealth(spellUser.RetrieveMaxHealth() / 2); SpellCasterCasts(); await InitializeSpellEffect(); if (spellUser is Player || spellUser is Ally) { foreach (Enemy enemyComp in enemyRetainers) enemyComp.AddModToList(Globals.TurnModifierFromObject(currentSpell.DebuffModifier)); if (allyRetainers != null) { foreach (Ally allyComp in allyRetainers) allyComp.AddModToList(Globals.TurnModifierFromObject(currentSpell.BuffModifier)); } if (playerRetainer != null) playerRetainer.AddModToList(Globals.TurnModifierFromObject(currentSpell.BuffModifier)); } else { foreach (Enemy enemyComp in enemyRetainers) enemyComp.AddModToList(Globals.TurnModifierFromObject(currentSpell.BuffModifier)); if (allyRetainers != null) { foreach (Ally allyComp in allyRetainers) allyComp.AddModToList(Globals.TurnModifierFromObject(currentSpell.DebuffModifier)); } if (playerRetainer != null) playerRetainer.AddModToList(Globals.TurnModifierFromObject(currentSpell.DebuffModifier)); } bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(23, -1, null, spellUser); } else bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(22); if (spellUser is Player || spellUser is Ally) bhReference.DisablePlayerTurn(); break; case 63: // Rampage SpellCasterCasts(); bool isLeft = spellUser is Player || spellUser is Ally; for (int i = 0; i < 5; ++i) { int randomTargetValue; if (isLeft) { if (enemyRetainers.All(enemy => enemy.HasDied)) { bhReference.DisablePlayerTurn(); break; } do { spellTarget = enemyRetainers[UnityEngine.Random.Range(0, enemyRetainers.Length)]; } while (spellTarget.HasDied); int damageRoll = UtilityHandler.RollValue(currentSpell.Dice.ToArray()); totalDamage += damageRoll; await InitializeSpellEffect(); await bhReference.AttackWithSpell(damageRoll, currentSpell, spellUser, spellTarget, false); } else { if (playerRetainer != null) { if (allyRetainers != null) { if (playerRetainer.HasDied && allyRetainers.All(ally => ally.HasDied)) break; } else { if (playerRetainer.HasDied) break; } } else { if (allyRetainers.All(ally => ally.HasDied)) break; } do { randomTargetValue = UnityEngine.Random.Range(playerRetainer == null ? 0 : -1, allyRetainers.Length); spellTarget = randomTargetValue is -1 ? playerRetainer : allyRetainers[randomTargetValue]; } while (spellTarget.HasDied); int damageRoll = UtilityHandler.RollValue(currentSpell.Dice.ToArray()); totalDamage += damageRoll; await InitializeSpellEffect(); await bhReference.AttackWithSpell(damageRoll, currentSpell, spellUser, spellTarget, false); } } bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(24, totalDamage, null, spellUser); if (spellUser is Player || spellUser is Ally) bhReference.DisablePlayerTurn(); break; case 72: // Pillage Item stealing SwitchIndicatorsOfSide(false, true, true); bhReference.AwaitSpellChoice(spellUser); condition = await Awaiter(); SwitchIndicatorsOfSide(false, true, false); if (!condition) { GarbageCleanUp(); return; } SpellCasterCasts(); await InitializeSpellEffect(); bhReference.GetUIHandler().ResetUI(); succeeded = TargetRollsSavingThrow(); Item itemPillaged = null; if (spellTarget is Enemy enemy && !succeeded) { spellTarget.Pillaged = true; Dictionary<ItemSO, float> loot = enemy.GetPotentialLoot(); ItemSO gatheredLoot = loot.ElementAt(UnityEngine.Random.Range(0, loot.Count)).Key; Item turnedItem; if (gatheredLoot is WeaponSO weapon) turnedItem = Globals.TurnWeaponFromObject(weapon); else if (gatheredLoot is ApparelSO apparel) turnedItem = Globals.TurnApparelFromObject(apparel); else if (gatheredLoot is ConsumableSO consumable) turnedItem = Globals.TurnConsumableFromObject(consumable); else if (gatheredLoot is RelicSO relic) turnedItem = Globals.TurnRelicFromObject(relic); else turnedItem = Globals.TurnItemFromObject(gatheredLoot); itemPillaged = turnedItem; if (turnedItem.ItemName is "Gold") playerRetainer.GetInventory().AddGold(UnityEngine.Random.Range(enemy.GetGoldRange().x, enemy.GetGoldRange().y)); else playerRetainer.GetInventory().Add(turnedItem, 1); totalDamage = UtilityHandler.RollValue(Dice.DiceR.Six, Dice.DiceR.Six); await InitializeSpellEffect(); await bhReference.AttackWithSpell(totalDamage, currentSpell, spellUser, spellTarget, false); bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(34, totalDamage, spellTarget, null, itemPillaged); } else if (spellTarget is Enemy && succeeded) bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(35, -1, spellTarget); if (spellUser is Player || spellUser is Ally) bhReference.DisablePlayerTurn(); break; case 83: // Engorge bhReference.GetUIHandler().ResetUI(); succeeded = false; if (spellUser is Player || spellUser is Ally) { SwitchIndicatorsOfSide(false, true, true); bhReference.AwaitSpellChoice(spellUser); condition = await Awaiter(); if (!condition) { GarbageCleanUp(); return; } SwitchIndicatorsOfSide(true, true, false); } else spellTarget = ChooseRandomTargetForEnemy(); if (!spellTarget.TryGetComponent(out Boss _) && spellTarget.GetComponent<Enemy>().GetEnemyDifficulty() is not Enemy.DifficultyRating.Legendary) succeeded = TargetRollsSavingThrow(); else succeeded = true; int spellValue = spellTarget.RetrieveHealth(); if (!succeeded) { await InitializeSpellEffect(); spellTarget.TrueDeath = true; if (spellTarget is Enemy enemy2) bhReference.DamageEnemy(enemy2, spellValue, false); else if (spellTarget is Ally ally) bhReference.DamageAlly(ally, spellValue, false); else bhReference.DamagePlayer(spellValue, false); } bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(succeeded ? 25 : 26, spellValue, null, spellUser); if (spellUser is Player || spellUser is Ally) bhReference.DisablePlayerTurn(); break; case 84: // Psionic Overload SpellCasterCasts(); bhReference.GetUIHandler().ResetUI(); int psiHMax = spellUser.RetrieveMaxHealth() < 10 ? 10 : spellUser.RetrieveMaxHealth(); int psiMMax = spellUser.RetrieveMaxMana() < 10 ? 10 : spellUser.RetrieveMaxMana(); int psiHeal = UnityEngine.Random.Range(10, psiHMax); int psiRefill = UnityEngine.Random.Range(10, psiMMax); if (spellUser is Player || spellUser is Ally) { if (playerRetainer != null) { playerRetainer.IncreaseHealth(psiHeal); playerRetainer.IncreaseMana(psiRefill); playerRetainer.StatusChanged(); } if (allyRetainers != null) { foreach (Ally ally in allyRetainers) { ally.IncreaseHealth(psiHeal); ally.IncreaseMana(psiRefill); ally.StatusChanged(); } } } else { foreach (Enemy enemy3 in enemyRetainers) { enemy3.IncreaseHealth(psiHeal); enemy3.IncreaseMana(psiRefill); enemy3.StatusChanged(); } } await InitializeSpellEffect(); bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(27, -1, null, spellUser); if (spellUser is Player || spellUser is Ally) bhReference.DisablePlayerTurn(); break; case 86: // Standoff SpellCasterCasts(); bhReference.GetUIHandler().ResetUI(); List<Dice.DiceR> dices = new List<Dice.DiceR> { Dice.DiceR.Eight, Dice.DiceR.Eight, Dice.DiceR.Eight, Dice.DiceR.Eight, Dice.DiceR.Eight }; spellUser.IsPreparing = (true, (PreparationCalls.NextAttackingEntity, dices)); await InitializeSpellEffect(); bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(28, -1, null, spellUser); if (spellUser is Player || spellUser is Ally) bhReference.DisablePlayerTurn(); break; case 87: // Precise Shot bhReference.GetUIHandler().ResetUI(); if (spellUser is Player || spellUser is Ally) { SwitchIndicatorsOfSide(false, true, true); bhReference.AwaitSpellChoice(spellUser); condition = await Awaiter(); if (!condition) { GarbageCleanUp(); return; } SwitchIndicatorsOfSide(true, true, false); } else spellTarget = ChooseRandomTargetForEnemy(); await InitializeSpellEffect(); SpellCasterCasts(); List<Dice.DiceR> preciseDice = new List<Dice.DiceR> { Dice.DiceR.Ten, Dice.DiceR.Ten, Dice.DiceR.Ten, Dice.DiceR.Ten, Dice.DiceR.Ten, Dice.DiceR.Ten }; spellUser.IsPreparing = (true, (PreparationCalls.NextTurn, new List<object>() { preciseDice, spellTarget, "Precise Shot" })); bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(30, -1, null, spellUser); if (spellUser is Player || spellUser is Ally) bhReference.DisablePlayerTurn(true); break; } GarbageCleanUp(); } private (bool, bool) UniqueSpellEffectCheck(int spellValue, int effectID) { bool uniqueEffectActivated = false; bool endOfSpell = false; switch (effectID) { case -4: // Type Damage switch (currentSpell.SpellID) { case 23: // Order: Rupture List<Entity> entitiesAffected = new List<Entity>(); entitiesAffected.AddRange(enemyRetainers.Where(enemy => enemy.GatherModList().SmartModCheck("Sick"))); entitiesAffected.AddRange(allyRetainers.Where(ally => ally.GatherModList().SmartModCheck("Sick"))); SpellCasterCasts(); bhReference.GetUIHandler().ResetUI(); if (entitiesAffected.Count is 0) bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(5); else { Modifier sickMod = AbilityBrain.GetModifierList()["Sick"]; // Damage each entity and remove sick from them foreach (Entity entity in entitiesAffected) { int baseValue = spellValue; if (entity is Ally ally) { if (spellUser is Player player) baseValue += player.ParseClassDamageModifiers(entity); bhReference.DamageAlly(ally, spellValue, false); ally.GatherModList().RemoveModifier(sickMod); ally.AfflictedStatus(); } else if (entity is Enemy enemy) { if (spellUser is Player player) baseValue += player.ParseClassDamageModifiers(entity); bhReference.DamageEnemy(enemy, spellValue, false, false); enemy.GatherModList().RemoveModifier(sickMod); enemy.AfflictedStatus(); } else if (entity is Player player) { bhReference.DamagePlayer(spellValue, false); player.GatherModList().RemoveModifier(sickMod); player.AfflictedStatus(); } } bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(6, spellValue); } uniqueEffectActivated = true; endOfSpell = true; if (spellUser is Player || spellUser is Ally) bhReference.DisablePlayerTurn(); break; } break; case -3: // Type Healing break; case -2: // Type Buffing break; case -1: // Type Debuffing break; case 0: // Damage ClimateManager.Weather currentWeather = ClimateManager.Instance.GetWeather(); switch (currentSpell.SpellID) { case 2: // Magic Blast check presence of Concentrated and Rapid List<Ability> userAbilities = new List<Ability>(spellUser.GetInventory().Abilities); userAbilities.AddRange(spellUser.GetInventory().limitedAbilities); Dictionary<int, Ability> abilityList = AbilityBrain.GetAbilityList(); if (userAbilities.Contains(abilityList[91])) // Rapid - Additional D6 spellValue += UtilityHandler.RollValue(Dice.DiceR.Six); if (userAbilities.Contains(abilityList[92])) // Concentrated - D6's turn into D10's spellValue = UtilityHandler.RollValue(Dice.DiceR.Ten, Dice.DiceR.Ten); break; case 26: // Assimilate Golem Instant Kill Health and Mana Absorb SpellCasterCasts(); int healthAbsorbed = spellTarget.RetrieveHealth(); spellUser.IncreaseHealth(healthAbsorbed / 2); spellUser.IncreaseMana(healthAbsorbed / 4); spellUser.StatusChanged(); uniqueEffectActivated = true; endOfSpell = true; bhReference.AttackWithSpell(healthAbsorbed, currentSpell, spellUser, spellTarget, endOfSpell).GetAwaiter(); bhReference.GetUIHandler().ResetUI(); if (spellUser is Player || spellUser is Ally) bhReference.DisablePlayerTurn(); break; case 39: // Piercing Shot doing double to targets that are resistant to Ranged if (spellTarget.GetWeaponResists().ContainsKey(WeaponSO.Type.Ranged)) { if (spellUser is Player player) spellValue += player.ParseClassDamageModifiers(spellTarget); SpellCasterCasts(); uniqueEffectActivated = true; endOfSpell = true; bhReference.AttackWithSpell(spellValue * 2, currentSpell, spellUser, spellTarget, endOfSpell).GetAwaiter(); bhReference.GetUIHandler().ResetUI(); if (spellUser is Player || spellUser is Ally) bhReference.DisablePlayerTurn(); } break; case 56: // Aqua Ball SpellCasterCasts(); InitializeSpellEffect().GetAwaiter(); if (currentWeather is ClimateManager.Weather.Rainy || currentWeather is ClimateManager.Weather.Monsoon) bhReference.AttackWithSpell(spellValue + DiceRoller.RollDice(new DSix()), currentSpell, spellUser, spellTarget, true).GetAwaiter(); else if (currentWeather is not ClimateManager.Weather.Blizzard && currentWeather is not ClimateManager.Weather.Snowy) bhReference.AttackWithSpell(spellValue, currentSpell, spellUser, spellTarget, true).GetAwaiter(); uniqueEffectActivated = true; endOfSpell = true; if (currentWeather is ClimateManager.Weather.Snowy || currentWeather is ClimateManager.Weather.Blizzard) bhReference.AttackWithSpell(spellValue + DiceRoller.RollDice(new DSix()), currentSpell, spellUser, spellTarget, endOfSpell).GetAwaiter(); bhReference.GetUIHandler().ResetUI(); break; } break; case 1: // Health switch (currentSpell.SpellID) { case 6: // Life Extraction Spell Healing Effect spellValue /= 2; spellUser.IncreaseHealth(spellValue); SpellCasterCasts(); uniqueEffectActivated = true; endOfSpell = true; bhReference.CallSpellActionText(spellUser.GetName(), spellTarget.GetName(), currentSpell, spellValue); bhReference.DisablePlayerTurn(); break; } break; case 2: // Buff break; case 3: // Debuff break; } return (uniqueEffectActivated, endOfSpell); } /// <summary> /// Deciphers which effect was triggered by taking the target that was just gathered and /// matching it with the correct Entity Path for the desired effects /// </summary> private List<int> DecipherEffect() { foreach (Dictionary<Entity.Entities, List<int>> item in currentSpell.Paths) { Dictionary<Entity.Entities, List<int>> effectSelection = item; foreach (KeyValuePair<Entity.Entities, List<int>> pair in effectSelection) { switch (pair.Key) { case Entity.Entities.Companion: // General Left side target if (spellTarget is Player || spellTarget is Ally) return pair.Value; break; case Entity.Entities.Companions: // All left side targets if (spellTarget is Player || spellTarget is Ally) return pair.Value; break; case Entity.Entities.Player: // Only a player effect if (spellTarget is Player) return pair.Value; break; case Entity.Entities.Ally: // Only an ally effect if (spellTarget is Ally) return pair.Value; break; case Entity.Entities.Allies: // Affects all allies if (spellTarget is Ally) return pair.Value; break; case Entity.Entities.Enemy: // General right side target if (spellTarget is Enemy) return pair.Value; break; case Entity.Entities.Enemies: // All right side targets if (spellTarget is Enemy) return pair.Value; break; case Entity.Entities.Boss: // Unique effect when a boss is selected if (spellTarget is Boss) return pair.Value; break; } } } return null; } private (bool, bool) EnableUniqueTargettingSystem() { bool output = false; bool isSelf = false; switch (currentSpell.SpellID) { case 26: // Assimilate Golem Targetting foreach (Ally ally in allyRetainers) { if (ally.GetName().Contains("Golem")) { ally.GetComponent<Target>().SwitchSpellIndicationMode(true); output = true; } } foreach (Enemy enemy in enemyRetainers) { if (enemy.GetName().Contains("Golem")) { enemy.GetComponent<Target>().SwitchSpellIndicationMode(true); output = true; } } break; case 33: // Life Contract Self Targetting spellTarget = spellUser; output = true; isSelf = true; break; case 35: // Ignite Self Targetting spellTarget = spellUser; output = true; isSelf = true; break; case 43: // Erratic Bursts Random Targetting if (spellUser is Ally || spellUser is Player) { do { spellTarget = enemyRetainers[UnityEngine.Random.Range(0, enemyRetainers.Length)]; } while (spellTarget.HasDied); } else { do { int targetValue = UnityEngine.Random.Range(-1, allyRetainers.Length); spellTarget = targetValue is -1 ? playerRetainer : allyRetainers[targetValue]; } while (spellTarget.HasDied); } output = true; break; case 62: // Rage Self Targetting spellTarget = spellUser; output = true; isSelf = true; break; case 92: // Deflection Self Targetting spellTarget = spellUser; output = true; isSelf = true; break; default: break; } return (output, isSelf); } /// <summary> /// General method calls for when the enemies or player use a buff or debuff spell on another target /// </summary> /// <param name="alwaysPut">Indicator if the buff/debuff is always put onto the target</param> private async Task NonDamageSpellCall(bool savingThrowSucceeded, bool endSpell = true) { // Checks if the debuff or buff requires a certain characteristic of the target, such as a mod needing to be present bool uniqueCheck = UniqueNonDamageChecks(currentSpell.SpellID); if (endSpell) { // Modifies the spell user's stats SpellCasterCasts(); await InitializeSpellEffect(); } if (uniqueCheck) { Modifier modifier = Globals.TurnModifierFromObject(currentSpell.Modifier); if (modifier.Type is ModifierSO.Type.Buff) { int baseTurnValueToAdd = 0; // Turn amount increased for buffs if (spellUser.GetInventory().Abilities.Contains(AbilityBrain.GetAbilityList()[114]) && spellElement is SpellSO.Element.Necro && modifier.Type is ModifierSO.Type.Buff) baseTurnValueToAdd += 2; spellTarget.AddModToList(modifier, baseTurnValueToAdd); } else if (modifier.Type is ModifierSO.Type.Debuff && savingThrowSucceeded is false) spellTarget.AddModToList(modifier); } // Magic Devourer if (spellTarget.GetInventory().Abilities.Contains(AbilityBrain.GetAbilityList()[86])) { spellTarget.IncreaseMana((int)Math.Floor(currentSpell.SpellCost * .5f)); spellTarget.StatusChanged(); } if (endSpell) await bhReference.AttackWithSpell(valueRetainer, currentSpell, spellUser, spellTarget, true); } /// <summary> /// General method call for when the enemies or player use a buff that affects an entire side /// </summary> /// <param name="alwaysPut">Indicator if the modifier is always gonna effect the side indicated</param> /// <param name="left">Indicator if companion side is affected</param> /// <param name="right">Indicator if the enemy side is affected</param> private async Task NonDamageSpellCall(bool left, bool right, bool endSpell, bool? isBuff = null) { bhReference.CallSpellActionText(spellUser, left, right, currentSpell.SpellEffects[0], currentSpell, valueRetainer); ModifierSO modObject = isBuff is null ? currentSpell.Modifier : (isBuff is false ? currentSpell.DebuffModifier : currentSpell.BuffModifier); Modifier mod = Globals.TurnModifierFromObject(modObject); bool succeeded; if (left) { if (mod.Type is ModifierSO.Type.Debuff) { if (playerRetainer != null) { succeeded = TargetRollsSavingThrow(playerRetainer); bool noDebuff = false; if (succeeded && endSpell) { switch (currentSpell.SaveFailEffect) { case SpellSO.SavingThrowFailEffect.SpellFails: break; case SpellSO.SavingThrowFailEffect.HalfSpellValue: damageValues[0] /= 2; break; case SpellSO.SavingThrowFailEffect.NoDebuff: noDebuff = true; break; } } if (!noDebuff) playerRetainer.AddModToList(mod); } if (allyRetainers != null) { for (int i = 0; i < allyRetainers.Length; ++i) { succeeded = TargetRollsSavingThrow(allyRetainers[i]); bool noDebuff = false; if (succeeded && endSpell) { switch (currentSpell.SaveFailEffect) { case SpellSO.SavingThrowFailEffect.SpellFails: break; case SpellSO.SavingThrowFailEffect.HalfSpellValue: damageValues[i + 1] /= 2; break; case SpellSO.SavingThrowFailEffect.NoDebuff: noDebuff = true; break; } } if (!noDebuff) allyRetainers[i].AddModToList(mod); } } } else { playerRetainer?.AddModToList(mod); if (allyRetainers != null) { foreach (Ally ally in allyRetainers) ally.AddModToList(mod); } } } else if (right) { if (mod.Type is ModifierSO.Type.Debuff) { for (int i = 0; i < enemyRetainers.Length; ++i) { succeeded = TargetRollsSavingThrow(enemyRetainers[i]); bool noDebuff = false; if (succeeded && endSpell) { switch (currentSpell.SaveFailEffect) { case SpellSO.SavingThrowFailEffect.SpellFails: break; case SpellSO.SavingThrowFailEffect.HalfSpellValue: damageValues[i] /= 2; break; case SpellSO.SavingThrowFailEffect.NoDebuff: noDebuff = true; break; } } if (!noDebuff) enemyRetainers[i].AddModToList(mod); } } else { foreach (Enemy enemy in enemyRetainers) enemy.AddModToList(mod); } } if (endSpell) { await InitializeSpellEffect(); if (playerRetainer != null) { if (playerRetainer.GetInventory().Abilities.Contains(AbilityBrain.GetAbilityList()[86])) { playerRetainer.IncreaseMana((int)Math.Floor(currentSpell.SpellCost * .5f)); playerRetainer.StatusChanged(); } } // Modifies the spell user's stats SpellCasterCasts(); bool check = UniqueSpellTextCheck(); if (!check) await bhReference.AttackWithSpell(damageValues, currentSpell, spellUser, left, right); } } private bool UniqueSpellTextCheck() { bool check = false; switch (currentSpell.SpellID) { case 14: bhReference.GetUIHandler().ResetUI(); bhReference.GetUIHandler().RetrieveActionText().UpdateActionTextUnique(33, -1, null, spellUser); if (spellUser is Player || spellUser is Ally) bhReference.DisablePlayerTurn(); check = true; break; default: break; } return check; } private bool UniqueNonDamageChecks(int spellId) { return spellId switch { // Create Nightmare requires the target to be sleeping 21 => spellTarget.GatherModList().SmartModCheck("Sleeping"), // All other values simply return true _ => true, }; } /// <summary> /// Cleans the management slate of all information for the next spell use /// </summary> private void GarbageCleanUp() { spellUser = null; spellTarget = null; playerRetainer = null; enemyRetainers = null; allyRetainers = null; currentSpell = null; chosenTarget = null; if (enemyTargets is not null) { foreach (Target t in enemyTargets) { if (t != null) t.SwitchSpellIndicationMode(false); } } if (allyTargets is not null) { foreach (Target t in allyTargets) { if (t != null) t.SwitchSpellIndicationMode(false); } } playerTarget?.SwitchSpellIndicationMode(false); enemyTargets = null; allyTargets = null; playerTarget = null; } /// <summary> /// Public call to reset all of the information fields for the spell manager and stops the tasks within the spell manager /// </summary> public void Cancel() { StopSpellTask(); GarbageCleanUp(); } /// <summary> /// Chooses a random entity for the enemies target on the left side that is currently active /// </summary> /// <returns>An entity target</returns> private Entity ChooseRandomTargetForEnemy() { Entity output; int index; do { if (playerRetainer != null) index = UnityEngine.Random.Range(-1, allyRetainers.Length); else index = UnityEngine.Random.Range(0, allyRetainers.Length); if (index is -1) output = playerRetainer; else output = allyRetainers[index]; } while (output == null || output.IsHidden); return output; } /// <summary> /// Filters through each of the enemies in the retainer to determine which is the most damaged /// </summary> /// <param name="most_Damaged">The Enemy parameter to modify</param> /// <returns>The enemy that would benefit the most from the Healing</returns> private Enemy FindMostDamaged() { Enemy output = null; Enemy[] damagedEnemies = enemyRetainers .Where(enemy => enemy.RetrieveHealth() < enemy.RetrieveMaxHealth()) .ToArray(); if (damagedEnemies.Length is 0) output = enemyRetainers[UnityEngine.Random.Range(0, enemyRetainers.Length)]; // Cycles through all of the damaged enemies for the most damaged // If two enemies have the same health than a coin flip for the place // holder of being held is essentially held foreach (Enemy enemy in damagedEnemies) { if (output == null) { output = enemy; continue; } if (output.RetrieveHealth() > enemy.RetrieveHealth()) output = enemy; else if (output.RetrieveHealth() == enemy.RetrieveHealth()) { // TODO ---> Better calculations more than likely int randomizedValue = UnityEngine.Random.Range(0 , MaxChance + 1); if (randomizedValue > MaxChance / 2) output = enemy; } } return output; } /// <summary> /// Spell Awaiter for a spell selection for the players and allies that can be called to cancelled /// </summary> /// <returns>The completed task once an entity has been selected</returns> private async Task<bool> Awaiter() { stopSpellTask = false; // Waits until a target is selected by click while (chosenTarget == null) { // Cancel component that will simply stop the process and prevent multiple usages of spells if (stopSpellTask) return false; await Task.Yield(); } return true; } /// <summary> /// Simple method that calls the two primary functions when a spell user casts a spell /// </summary> private void SpellCasterCasts(int manaCost = 0) { Dictionary<int, Ability> abList = AbilityBrain.GetAbilityList(); int spellCost = manaCost is 0 ? currentSpell.SpellCost : manaCost; // 116 ---> Mana Singularity if (spellUser.GetInventory().Abilities.Contains(abList[116]) || spellUser.GetInventory().limitedAbilities.Contains(abList[116])) { int rollValue = UnityEngine.Random.Range(1, 6); // If roll was 1 the spell is free in cost if (rollValue is 1) return; } // 125 ---> Interference spellCost = InterferenceFunction(abList, spellCost); spellUser.DecreaseMana(spellCost); spellUser.StatusChanged(); // If any entity on the opposite side of the spell user has Interference the spell cost is increased by a stacking 50% int InterferenceFunction(Dictionary<int, Ability> abList, int spellCost) { if (spellUser is Enemy) { List<Entity> leftList = new List<Entity>(allyRetainers) { playerRetainer }; foreach (Entity companion in leftList) { Inventory companionInven = companion.GetInventory(); if (companionInven.Abilities.Contains(abList[125]) || companionInven.limitedAbilities.Contains(abList[125])) spellCost += (int)Math.Ceiling(spellCost * .5); } } else { foreach (Enemy enemy in enemyRetainers) { Inventory enemyInven = enemy.GetInventory(); if (enemyInven.Abilities.Contains(abList[125]) || enemyInven.limitedAbilities.Contains(abList[125])) spellCost += (int)Math.Ceiling(spellCost * .5); } } return spellCost; } } /// <summary> /// Switches a designated sides indicators when necessary /// </summary> /// <param name="left">The companion side indicators</param> /// <param name="right">The enemy side indicators</param> /// <param name="status">The condition of the targets indicators</param> private void SwitchIndicatorsOfSide(bool left, bool right, bool status) { if (left) // Companion side { if (SpellIsNotSelfTargetting(playerTarget)) playerTarget?.SwitchSpellIndicationMode(status); if (allyTargets != null) { foreach (Target allyTarget in allyTargets) { if (SpellIsNotSelfTargetting(allyTarget)) allyTarget.SwitchSpellIndicationMode(status); } } } if (right && enemyTargets != null) // Enemy side { foreach (Target enemyTarget in enemyTargets) { if (SpellIsNotSelfTargetting(enemyTarget)) enemyTarget.SwitchSpellIndicationMode(status); } } } private bool SpellIsNotSelfTargetting(Target potentialUser) { if (potentialUser.gameObject.GetComponent<Entity>().GetName() != spellUser.GetName()) return true; else { return currentSpell.SpellName switch { "Mana Transfusion" => false, _ => true, }; } } /// <summary> /// Rolls the targets saving throw based on the current spells configuration /// </summary> /// <param name="target">Entity parameter set to null, for when multiple targets are considered, otherwise use spellTarget</param> /// <returns>True if succeeded, false if otherwise</returns> private bool TargetRollsSavingThrow(Entity target = null) { bool outcome; int addToThrowRequirement = 0; // Death and Decay increased throw requirement if (spellUser.GetInventory().Abilities.Contains(AbilityBrain.GetAbilityList()[114]) && spellElement is SpellSO.Element.Necro) addToThrowRequirement = 1; if (target == null) { outcome = UtilityHandler.RollSavingThrow(DecipherSaveThrow(), spellTarget, spellUser) >= (currentSpell.SavingThrow.y + spellUser.GetStat(DecipherSaveThrow()) + addToThrowRequirement); } else { outcome = UtilityHandler.RollSavingThrow(DecipherSaveThrow(), target, spellUser) >= (currentSpell.SavingThrow.y + spellUser.GetStat(DecipherSaveThrow()) + addToThrowRequirement); } return outcome; EntityStats.Stat DecipherSaveThrow() { return currentSpell.SavingThrow.x switch { 0 => EntityStats.Stat.Strength, 1 => EntityStats.Stat.Intelligence, 2 => EntityStats.Stat.Constitution, 3 => EntityStats.Stat.Dexterity, 4 => EntityStats.Stat.Charisma, 5 => EntityStats.Stat.Wisdom, 6 => EntityStats.Stat.Luck, _ => throw new InvalidOperationException(), }; } } private bool FieldFocusCheck() { bool extraDice = false; if (spellUser.GetInventory().Abilities.Contains(AbilityBrain.GetAbilityList()[93])) { Ability abRef = spellUser.GetInventory().Abilities.Where(ability => ability.AbilityID is 93).FirstOrDefault(); SpellSO.Element elementChosen = (SpellSO.Element)((List<object>)abRef.ReferenceObject)[0]; if (spellElement == elementChosen) { try { extraDice = currentSpell.Dice.Count > 0; } catch { /* If the spell is not a damaging spell */ } } } return extraDice; } private async Task InitializeSpellEffect(bool isSummoning = false) { try { if (currentSpell.SpellEffectSequence == null) throw new InvalidOperationException(); // Just for Boss Encounters right now if (Globals.IsBossEncounter) { GameObject effectData = Instantiate(currentSpell.SpellEffectSequence, new Vector3(0f, 0f), Quaternion.identity); int castLocation = PositionHandler.Instance.GetEntityPositionIndex(spellUser); int targetLocation = -1; if (spellTarget != null) targetLocation = PositionHandler.Instance.GetEntityPositionIndex(spellTarget); else if (spellTarget == null) { if (isSummoning) { targetLocation = PositionHandler.Instance.GetEmptyPosition(spellUser); if (targetLocation is -1) return; } } bool isTargettingSameSide = isSummoning; SpellVisualEffects visualEffectData = effectData.GetComponent<SpellVisualEffects>(); visualEffectData.GatherLocationSequence(castLocation, targetLocation, spellUser is Enemy, isTargettingSameSide); await visualEffectData.BeginEffectSequence(spellUser is Enemy); } } catch { } } }