using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using UnityEngine; /// <summary> /// Handles mostly pure function components that populate parts of the code a lot /// </summary> public static class UtilityHandler { /// <summary> /// General Dice Rolling Functionality for collections of dice values /// </summary> /// <param name="diceBundle">The collection of dice to roll</param> /// <returns>The total int value created from the rolling of all the dice</returns> public static int RollValue(params Dice.DiceR[] diceBundle) { int output = 0; foreach (var d in diceBundle) { Dice dice = DiceRoller.DispenseDice(d); output += DiceRoller.RollDice(dice); } return output; } /// <summary> /// General Dice Rolling Functionality for collections in inclusion of a base value /// </summary> /// <param name="value">The base value that will always be the start of the output</param> /// <param name="diceBundle">The collection of dices to roll</param> /// <returns>The total int value created from the base value and rolling all dice in diceBundle</returns> public static int RollValueWithBase(int value, params Dice.DiceR[] diceBundle) { int output = value; foreach (var d in diceBundle) { Dice dice = DiceRoller.DispenseDice(d); output += DiceRoller.RollDice(dice); } return output; } public static int Average(int[] damageValues) { int output = 0; foreach (int value in damageValues) output += value; return output / damageValues.Length; } public static int SpellTypeCheck(Spell currentSpell, int spellValue, Entity spellTarget, Entity spellUser) { SpellSO.Element targetType = spellTarget.GetEntityType(); SpellSO.Element spellType = currentSpell.SpellType; spellType = ElementalAugmentationFunction(spellUser, spellType); spellValue = DeathAndDecayCheck(currentSpell, spellValue, spellUser, spellType); spellValue = ElementWeaknessAndResistChecks(spellValue, spellType, spellTarget, spellUser, currentSpell); spellValue = UniqueSpellTypeChecks(currentSpell, spellValue, targetType); return spellValue; } private static int DeathAndDecayCheck(Spell currentSpell, int spellValue, Entity spellUser, SpellSO.Element spellType) { if (spellUser.GetInventory().Abilities.Contains(AbilityBrain.GetAbilityList()[114]) && spellType is SpellSO.Element.Necro) { SpellSO.SpellEffect spellEffect = currentSpell.SpellEffects[0]; bool isDamaging = spellEffect is SpellSO.SpellEffect.Damage; if (isDamaging) spellValue += RollValue(currentSpell.Dice[0], currentSpell.Dice[0]); } return spellValue; } private static SpellSO.Element ElementalAugmentationFunction(Entity spellUser, SpellSO.Element spellType) { Inventory spellUserInven = spellUser.GetInventory(); if (spellUserInven.Abilities.Contains(AbilityBrain.GetAbilityList()[95])) { if (spellType is SpellSO.Element.Normal || spellType is SpellSO.Element.Energy) { ClimateManager.Weather currentWeather = ClimateManager.Instance.GetWeather(); spellType = currentWeather switch { ClimateManager.Weather.Sunny => SpellSO.Element.Fire, ClimateManager.Weather.Cloudy => SpellSO.Element.Wind, ClimateManager.Weather.Rainy => SpellSO.Element.Water, ClimateManager.Weather.Monsoon => SpellSO.Element.Water, ClimateManager.Weather.Eclipsed => SpellSO.Element.Darkness, ClimateManager.Weather.Snowy => SpellSO.Element.Ice, ClimateManager.Weather.Blizzard => SpellSO.Element.Ice, _ => spellType, }; } } return spellType; } private static int UniqueSpellTypeChecks(Spell currentSpell, int spellValue, SpellSO.Element targetType) { switch (currentSpell.SpellID) { case 6: // Life Extraction attacking Necro Entity check if (targetType == SpellSO.Element.Necro) return 0; break; case 16: // Exorcise attacking Light Entity check if (targetType is SpellSO.Element.Light) return 0; break; } return spellValue; } public static int ElementTypeChecks(int damage, Entity attacker, Entity target, Dictionary<int, (List<SpellSO.Element>, List<WeaponSO.Type>)> typesUsed, SortedDictionary<int, (int, Dice)> damageRolls, bool isOffhand = false) { SpellSO.Element attackerElement; if (!isOffhand) { attackerElement = attacker is Enemy enemy ? enemy.GetEntityType() : (attacker.GetInventory().GetPrimaryActiveWeapon() != null ? attacker.GetInventory().GetPrimaryActiveWeapon().WeaponElement : ((Ally)attacker).GetEntityType()); } else { attackerElement = attacker is Enemy enemy ? enemy.GetEntityType() : (attacker.GetInventory().GetSecondaryActiveWeapon() != null ? attacker.GetInventory().GetSecondaryActiveWeapon().WeaponElement : ((Ally)attacker).GetEntityType()); } Dictionary<SpellSO.Element, int> targetWeaknesses = target.GetElementWeaknesses(); attackerElement = ElementalAugmentationFunction(attacker, attackerElement); damage = AbilityBrain.DamageChecks(damage, attackerElement, attacker, target, damageRolls); if (target.GetElementResists().ContainsKey(attackerElement)) { int times = target.GetElementResists()[attackerElement] / 25; if (typesUsed is not null) { if (typesUsed.ContainsKey(1)) { if (!typesUsed[1].Item1.Contains(attackerElement)) { List<SpellSO.Element> elementResists = new List<SpellSO.Element>(typesUsed[1].Item1); List<WeaponSO.Type> weaponResists = new List<WeaponSO.Type>(typesUsed[1].Item2); typesUsed.Remove(1); elementResists.Add(attackerElement); typesUsed.Add(1, (elementResists, weaponResists)); } } else typesUsed.Add(1, (new List<SpellSO.Element> { attackerElement }, new List<WeaponSO.Type>())); } if (times is 4) damage = 0; else { for (int i = 0; i < times; ++i) damage /= 2; } } else if (targetWeaknesses.ContainsKey(attackerElement)) { damage *= 2; if (typesUsed is not null) { if (typesUsed.ContainsKey(0)) { if (!typesUsed[0].Item1.Contains(attackerElement)) { List<SpellSO.Element> elementResists = new List<SpellSO.Element>(typesUsed[0].Item1); List<WeaponSO.Type> weaponResists = new List<WeaponSO.Type>(typesUsed[0].Item2); typesUsed.Remove(0); elementResists.Add(attackerElement); typesUsed.Add(0, (elementResists, weaponResists)); } } else typesUsed.Add(0, (new List<SpellSO.Element> { attackerElement }, new List<WeaponSO.Type>())); } } return damage; } public static int ElementWeaknessAndResistChecks(int damage, SpellSO.Element damageType, Entity target, Entity spellUser = null, Spell spellUsed = null) { if (spellUser != null && spellUsed != null) damage = AbilityBrain.DamageChecks(damage, damageType, spellUser, target, null, spellUsed); Dictionary<SpellSO.Element, int> targetWeaknesses = target.GetElementWeaknesses(); if (target.GetElementResists().ContainsKey(damageType)) { int times = target.GetElementResists()[damageType] / 25; if (times is 4) damage = 0; else { for (int i = 0; i < times; ++i) damage /= 2; } } else if (targetWeaknesses.ContainsKey(damageType)) damage *= 2; return damage; } public static int WeaponTypeChecks(int damage, Entity attacker, Entity target, Dictionary<int, (List<SpellSO.Element>, List<WeaponSO.Type>)> typesUsed = null, bool isOffhand = false) { WeaponSO.Type attackerType; if (!isOffhand) { attackerType = attacker.GetInventory().GetPrimaryActiveWeapon() != null ? attacker.GetInventory().GetPrimaryActiveWeapon().WeaponType : (attacker is Ally ally ? ally.attackType : ((Enemy)attacker).GetDamageType()); } else { attackerType = attacker.GetInventory().GetSecondaryActiveWeapon() != null ? attacker.GetInventory().GetSecondaryActiveWeapon().WeaponType : (attacker is Ally ally ? ally.GetInventory().GetSecondaryActiveWeapon().WeaponType : ((Enemy)attacker).GetDamageType()); } Dictionary<WeaponSO.Type, int> weaponResists = target.GetWeaponResists(); Dictionary<WeaponSO.Type, int> weaponWeaknesses = target.GetWeaponWeaknesses(); if (weaponWeaknesses.ContainsKey(attackerType)) { damage *= 2; if (typesUsed is not null) { if (typesUsed.ContainsKey(0)) { if (!typesUsed[0].Item2.Contains(attackerType)) { List<SpellSO.Element> elementResists = new List<SpellSO.Element>(typesUsed[0].Item1); List<WeaponSO.Type> resists = new List<WeaponSO.Type>(typesUsed[0].Item2); typesUsed.Remove(0); resists.Add(attackerType); typesUsed.Add(0, (elementResists, resists)); } } else typesUsed.Add(0, (new List<SpellSO.Element>(), new List<WeaponSO.Type> { attackerType })); } } else if (weaponResists.ContainsKey(attackerType)) { int times = weaponResists[attackerType] / 25; if (typesUsed is not null) { if (typesUsed.ContainsKey(1)) { if (!typesUsed[1].Item2.Contains(attackerType)) { List<SpellSO.Element> elementResists = new List<SpellSO.Element>(typesUsed[1].Item1); List<WeaponSO.Type> resists = new List<WeaponSO.Type>(typesUsed[1].Item2); typesUsed.Remove(1); resists.Add(attackerType); typesUsed.Add(1, (elementResists, resists)); } } else typesUsed.Add(1, (new List<SpellSO.Element>(), new List<WeaponSO.Type> { attackerType })); } if (times is 4) damage = 0; else { for (int i = 0; i < times; ++i) damage /= 2; } } return damage; } public static int DetermineModifierAndCalculate(WeaponSO.Type type, Entity attackingEntity) { int outputValue = type switch { // Use STR Mod WeaponSO.Type.Sharp => attackingEntity.GatherStats().GetStat(EntityStats.Stat.Strength), // Use STR Mod WeaponSO.Type.Blunt => attackingEntity.GatherStats().GetStat(EntityStats.Stat.Strength), // Use INT Mod WeaponSO.Type.Magic => attackingEntity.GatherStats().GetStat(EntityStats.Stat.Intelligence), // Use DEX Mod WeaponSO.Type.Ranged => attackingEntity.GatherStats().GetStat(EntityStats.Stat.Dexterity), // Use DEX Mod WeaponSO.Type.Extended => attackingEntity.GatherStats().GetStat(EntityStats.Stat.Dexterity), // Use WIS Mod WeaponSO.Type.Firearm => attackingEntity.GatherStats().GetStat(EntityStats.Stat.Wisdom), _ => throw new InvalidEnumArgumentException(), }; return outputValue; } public static int ToHitAddDecipher(Dictionary<string, (List<string>, List<object>)> triggers, int toHitRoll) { foreach (var value in triggers["ToHitAdd"].Item2) { if (value is float fVal) toHitRoll += (int)Math.Floor(toHitRoll * fVal); else if (value is int iVal) toHitRoll += iVal; else if (value is Dice dice) toHitRoll += DiceRoller.RollDice(dice); else if (value is Dice[] dices) toHitRoll += DiceRoller.RollDice(dices); else if (value is Dice.DiceR d) toHitRoll += RollValue(d); else if (value is Dice.DiceR[] ds) toHitRoll += RollValue(ds); else if (value is List<object> incepValues) toHitRoll += (int)incepValues[0]; else throw new TypeLoadException(); } return toHitRoll; } public static int AddArmorDecipher(Dictionary<string, (List<string>, List<object>)> triggers, int armorValue) { foreach (var value in triggers["AddArmor"].Item2) { if (value is float fVal) // Multiplier Ratios armorValue += (int)Math.Floor(armorValue * fVal); else if (value is int iVal) // Straight Values armorValue += iVal; else if (value is Dice dice) // Dice Rolls (This and below needs to be optimized better) armorValue += DiceRoller.RollDice(dice); else if (value is Dice[] dices) // Dice Rolls armorValue += DiceRoller.RollDice(dices); else if (value is Dice.DiceR d) // Dice Rolls armorValue += RollValue(d); else if (value is Dice.DiceR[] ds) // Dice Rolls armorValue += RollValue(ds); else throw new TypeLoadException(); } return armorValue; } public static int ToHitMinusDecipher(Dictionary<string, (List<string>, List<object>)> triggers, int toHitRoll) { foreach (var value in triggers["ToHitMinus"].Item2) { if (value is float fVal) toHitRoll += (int)Math.Floor(toHitRoll * fVal); else if (value is int iVal) toHitRoll += iVal; else if (value is Dice dice) toHitRoll += DiceRoller.RollDice(dice); else if (value is Dice[] dices) toHitRoll += DiceRoller.RollDice(dices); else if (value is Dice.DiceR d) toHitRoll += RollValue(d); else if (value is Dice.DiceR[] ds) toHitRoll += RollValue(ds); else throw new TypeLoadException(); } return toHitRoll; } public static int RollSavingThrow(EntityStats.Stat statToBase, Entity entity, Entity spellUser = null, bool isAdvan = false, bool isDisadvan = false) { if (spellUser != null) { Dictionary<int, Ability> abList = AbilityBrain.GetAbilityList(); bool check = spellUser.GetInventory().Abilities.Contains(abList[109]) || spellUser.GetInventory().limitedAbilities.Contains(abList[109]); if (check) isDisadvan = true; } EntityStats statRef = entity.GatherStats(); int savingThrowValue = DiceRoller.RollDice(new DTwenty()) + statRef.GetStat(statToBase); if (!isAdvan && isDisadvan) { isAdvan = statRef.GetModifiedSaveThrows()[statToBase] is 1; isDisadvan = statRef.GetModifiedSaveThrows()[statToBase] is -1; } if (isAdvan && !isDisadvan) { int newSavingThrowValue = DiceRoller.RollDice(new DTwenty()) + statRef.GetStat(statToBase); if (newSavingThrowValue > savingThrowValue) savingThrowValue = newSavingThrowValue; } else if (!isAdvan && isDisadvan) { int newSavingThrowValue = DiceRoller.RollDice(new DTwenty()) + statRef.GetStat(statToBase); if (newSavingThrowValue < savingThrowValue) savingThrowValue = newSavingThrowValue; } if (entity.GatherModList().SmartModCheck("Spiritual Guidance")) savingThrowValue += DiceRoller.RollDice(new DFour()); return savingThrowValue; } public static Enemy GenerateBounty() { ClimateManager.Locations randomLoc = ClimateManager.Instance.GetRandomLocation(); string areaLoad = randomLoc switch { ClimateManager.Locations.Swamplands => "Swamplands", ClimateManager.Locations.DarkVillage => "DarkVillage", ClimateManager.Locations.AbandonedManor => "AbandonedManor", ClimateManager.Locations.FrozenWasteland => "FrozenWasteland", _ => throw new InvalidEnumArgumentException(), }; List<GameObject> enemyPrefabs = Resources.LoadAll<GameObject>($"Entities/Enemies/{areaLoad}").ToList(); enemyPrefabs.AddRange(Resources.LoadAll<GameObject>($"Entities/Enemies/General")); Enemy enemyGathered = enemyPrefabs[UnityEngine.Random.Range(0, enemyPrefabs.Count)].GetComponent<Enemy>(); return enemyGathered; } public static int RetrieveMaxValue(Dice.DiceR indexDice) { return indexDice switch { Dice.DiceR.Four => 4, Dice.DiceR.Six => 6, Dice.DiceR.Eight => 8, Dice.DiceR.Ten => 10, Dice.DiceR.Twelve => 12, Dice.DiceR.Twenty => 20, Dice.DiceR.Hundred => 100, _ => throw new InvalidEnumArgumentException(), }; } public static int MinusArmorDecipher(Dictionary<string, (List<string>, List<object>)> triggers, int armorValue) { foreach (var value in triggers["MinusArmor"].Item2) { if (value is float fVal) // Multiplier Ratios armorValue -= (int)Math.Floor(armorValue * fVal); else if (value is int iVal) // Straight Values armorValue -= iVal; else if (value is Dice dice) // Dice Rolls (This and below needs to be optimized better) armorValue -= DiceRoller.RollDice(dice); else if (value is Dice[] dices) // Dice Rolls armorValue -= DiceRoller.RollDice(dices); else if (value is Dice.DiceR d) // Dice Rolls armorValue -= RollValue(d); else if (value is Dice.DiceR[] ds) // Dice Rolls armorValue -= RollValue(ds); else throw new TypeLoadException(); } return armorValue; } }