public static MinMaxRange CalculateDamageConversion(ActorData data, int[] convertedDamageMin, int[] convertedDamageMax, IEnumerable <GroupType> tags, MinMaxRange damageContainer, float[] conversions, ElementType element, HashSet <BonusType> multi, StatBonus minBonus, StatBonus maxBonus, StatBonus multiplierBonus) { float baseMin = damageContainer.min + minBonus.CalculateStat(0); float baseMax = damageContainer.max + maxBonus.CalculateStat(0); float finalBaseMin = baseMin; float finalBaseMax = baseMax; for (int i = 0; i < 7; i++) { if (conversions[i] == 0) { continue; } float convertedMin = baseMin * conversions[i]; float convertedMax = baseMax * conversions[i]; finalBaseMin -= convertedMin; finalBaseMax -= convertedMax; HashSet <BonusType> bonusesForConverted = new HashSet <BonusType>(multi); bonusesForConverted.UnionWith(Helpers.GetMultiplierTypes(AbilityType.NON_ABILITY, element, (ElementType)i)); StatBonus totalMultiplierBonus = data.GetMultiStatBonus(null, tags, bonusesForConverted.ToArray()); totalMultiplierBonus.AddBonuses(multiplierBonus); convertedMin = totalMultiplierBonus.CalculateStat(convertedMin); convertedMax = totalMultiplierBonus.CalculateStat(convertedMax); convertedDamageMin[i] += (int)Math.Max(convertedMin, 0); convertedDamageMax[i] += (int)Math.Max(convertedMax, 0); } MinMaxRange returnValue = new MinMaxRange(); if (finalBaseMin < 1) { finalBaseMin = 0; } if (finalBaseMax < 1) { finalBaseMax = 0; } if (finalBaseMax == 0 && finalBaseMin == 0) { returnValue.min = 0; returnValue.max = 0; } else { StatBonus finalMultiplier = data.GetMultiStatBonus(null, tags, multi.ToArray()); finalMultiplier.AddBonuses(multiplierBonus); returnValue.min = (int)Math.Max(finalMultiplier.CalculateStat(finalBaseMin), 0); returnValue.max = (int)Math.Max(finalMultiplier.CalculateStat(finalBaseMax), 0); } return(returnValue); }
protected static float CalculateStat(float stat, Dictionary <BonusType, StatBonus> dic, params BonusType[] bonusTypes) { StatBonus totalBonus = new StatBonus(); foreach (BonusType bonusType in bonusTypes) { if (dic.TryGetValue(bonusType, out StatBonus bonus)) { totalBonus.AddBonuses(bonus); } } return(totalBonus.CalculateStat(stat)); }
protected void UpdateOnHitDataBonuses(ActorData data, IEnumerable <GroupType> tags) { string triggeredEffectSourceName = abilityBase.idName + abilitySlot; ClearTriggeredEffects(data, triggeredEffectSourceName); foreach (TriggeredEffectBonusProperty triggerProp in abilityBase.triggeredEffects) { TriggeredEffect triggeredEffect = new TriggeredEffect(triggerProp, triggerProp.effectMinValue + triggerProp.effectMaxValue * abilityLevel, triggeredEffectSourceName); triggeredEffects[triggerProp.triggerType].Add(triggeredEffect); if (!abilityBase.isSoulAbility && (abilityBase.abilityType == AbilityType.AURA || abilityBase.abilityType == AbilityType.SELF_BUFF)) { data.TriggeredEffects[triggerProp.triggerType].Add(triggeredEffect); } } if (abilityBase.abilityType == AbilityType.AURA || abilityBase.abilityType == AbilityType.SELF_BUFF) { return; } abilityOnHitData.UpdateStatusEffectData(data, tags, abilityBonuses); StatBonus physicalNegate = data.GetMultiStatBonus(abilityBonuses, tags, BonusType.PHYSICAL_RESISTANCE_NEGATION); abilityOnHitData.SetNegation(ElementType.PHYSICAL, physicalNegate.CalculateStat(0)); StatBonus fireNegate = data.GetMultiStatBonus(abilityBonuses, tags, BonusType.FIRE_RESISTANCE_NEGATION, BonusType.ELEMENTAL_RESISTANCE_NEGATION); abilityOnHitData.SetNegation(ElementType.FIRE, fireNegate.CalculateStat(0)); StatBonus coldNegate = data.GetMultiStatBonus(abilityBonuses, tags, BonusType.COLD_RESISTANCE_NEGATION, BonusType.ELEMENTAL_RESISTANCE_NEGATION); abilityOnHitData.SetNegation(ElementType.COLD, coldNegate.CalculateStat(0)); StatBonus lightningNegate = data.GetMultiStatBonus(abilityBonuses, tags, BonusType.LIGHTNING_RESISTANCE_NEGATION, BonusType.ELEMENTAL_RESISTANCE_NEGATION); abilityOnHitData.SetNegation(ElementType.LIGHTNING, lightningNegate.CalculateStat(0)); StatBonus earthNegate = data.GetMultiStatBonus(abilityBonuses, tags, BonusType.EARTH_RESISTANCE_NEGATION, BonusType.ELEMENTAL_RESISTANCE_NEGATION); abilityOnHitData.SetNegation(ElementType.EARTH, earthNegate.CalculateStat(0)); StatBonus divineNegate = data.GetMultiStatBonus(abilityBonuses, tags, BonusType.DIVINE_RESISTANCE_NEGATION, BonusType.PRIMORDIAL_RESISTANCE_NEGATION); abilityOnHitData.SetNegation(ElementType.DIVINE, divineNegate.CalculateStat(0)); StatBonus voidNegate = data.GetMultiStatBonus(abilityBonuses, tags, BonusType.VOID_RESISTANCE_NEGATION, BonusType.PRIMORDIAL_RESISTANCE_NEGATION); abilityOnHitData.SetNegation(ElementType.VOID, voidNegate.CalculateStat(0)); }
protected void UpdateTypeParameters(EnemyData data, IEnumerable <GroupType> tags) { StatBonus critBonus = data.GetMultiStatBonus(abilityBonuses, tags, BonusType.GLOBAL_CRITICAL_CHANCE); StatBonus critDamageBonus = data.GetMultiStatBonus(abilityBonuses, tags, BonusType.GLOBAL_CRITICAL_DAMAGE); if (abilityBase.abilityType == AbilityType.SPELL) { StatBonus speedBonus = data.GetMultiStatBonus(abilityBonuses, tags, BonusType.CAST_SPEED, BonusType.GLOBAL_ABILITY_SPEED); StatBonus rangeBonus = data.GetMultiStatBonus(abilityBonuses, tags, BonusType.SPELL_RANGE); MainCriticalChance = critBonus.CalculateStat(abilityBase.baseCritical); Cooldown = 1f / speedBonus.CalculateStat(abilityBase.attacksPerSec); TargetRange = rangeBonus.CalculateStat(abilityBase.targetRange); } else if (abilityBase.abilityType == AbilityType.ATTACK) { StatBonus speedBonus = data.GetMultiStatBonus(abilityBonuses, tags, BonusType.GLOBAL_ATTACK_SPEED, BonusType.GLOBAL_ABILITY_SPEED); StatBonus rangeBonus = new StatBonus(); if (abilityBase.GetGroupTypes().Contains(GroupType.MELEE_ATTACK)) { data.GetMultiStatBonus(rangeBonus, abilityBonuses, tags, BonusType.MELEE_ATTACK_RANGE); } if (abilityBase.GetGroupTypes().Contains(GroupType.RANGED_ATTACK)) { data.GetMultiStatBonus(rangeBonus, abilityBonuses, tags, BonusType.RANGED_ATTACK_RANGE); } MainCriticalChance = critBonus.CalculateStat(data.BaseEnemyData.attackCriticalChance); Cooldown = 1 / speedBonus.CalculateStat(data.BaseEnemyData.attackSpeed); TargetRange = rangeBonus.CalculateStat(data.BaseEnemyData.attackTargetRange); } MainCriticalDamage = 1f + (critDamageBonus.CalculateStat(50) / 100f); if (float.IsInfinity(Cooldown)) { Cooldown = 0.001f; } }
public void UpdateStatusEffectData(ActorData data, IEnumerable <GroupType> tags, Dictionary <BonusType, StatBonus> abilityBonus) { StatBonus bleedChance = data.GetMultiStatBonus(abilityBonus, tags, BonusType.BLEED_CHANCE, BonusType.STATUS_EFFECT_CHANCE); StatBonus bleedEffectiveness = data.GetMultiStatBonus(abilityBonus, tags, BonusType.BLEED_EFFECTIVENESS, BonusType.STATUS_EFFECT_DAMAGE, BonusType.DAMAGE_OVER_TIME); StatBonus bleedDuration = data.GetMultiStatBonus(abilityBonus, tags, BonusType.BLEED_DURATION, BonusType.STATUS_EFFECT_DURATION); float bleedSpeed = data.GetMultiStatBonus(abilityBonus, tags, BonusType.BLEED_SPEED, BonusType.DAMAGE_OVER_TIME_SPEED).CalculateStat(100f) / 100f; effectData[EffectType.BLEED].SetChance(bleedChance.CalculateStat(0f)); effectData[EffectType.BLEED].SetEffectiveness(bleedEffectiveness.CalculateStat(100f) / 100f * bleedSpeed); effectData[EffectType.BLEED].SetDuration(bleedDuration.CalculateStat(BleedEffect.BASE_DURATION) / bleedSpeed); effectData[EffectType.BLEED].SetMaxStacks(data.GetMultiStatBonus(abilityBonus, tags, BonusType.MAX_BLEED_STACKS).CalculateStat(1)); effectData[EffectType.BLEED].SetSecondaryEffectiveness(data.GetMultiStatBonus(abilityBonus, tags, BonusType.BLEED_BONUS_DAMAGE_OVER_DISTANCE).CalculateStat(1f)); effectData[EffectType.BLEED].SetResistance(1 - data.GetMultiStatBonus(abilityBonus, tags, BonusType.BLEED_RESISTANCE).CalculateStat(0f) / 100f); StatBonus burnChance = data.GetMultiStatBonus(abilityBonus, tags, BonusType.BURN_CHANCE, BonusType.STATUS_EFFECT_CHANCE, BonusType.ELEMENTAL_STATUS_EFFECT_CHANCE); StatBonus burnEffectiveness = data.GetMultiStatBonus(abilityBonus, tags, BonusType.BURN_EFFECTIVENESS, BonusType.STATUS_EFFECT_DAMAGE, BonusType.DAMAGE_OVER_TIME, BonusType.ELEMENTAL_STATUS_EFFECT_EFFECTIVENESS); StatBonus burnDuration = data.GetMultiStatBonus(abilityBonus, tags, BonusType.BURN_DURATION, BonusType.STATUS_EFFECT_DURATION, BonusType.ELEMENTAL_STATUS_EFFECT_DURATION); float burnSpeed = data.GetMultiStatBonus(abilityBonus, tags, BonusType.BURN_SPEED, BonusType.DAMAGE_OVER_TIME_SPEED).CalculateStat(100f) / 100f; effectData[EffectType.BURN].SetChance(burnChance.CalculateStat(0f)); effectData[EffectType.BURN].SetEffectiveness(burnEffectiveness.CalculateStat(100f) / 100f * burnSpeed); effectData[EffectType.BURN].SetDuration(burnDuration.CalculateStat(BurnEffect.BASE_DURATION) / burnSpeed); effectData[EffectType.BURN].SetMaxStacks(data.GetMultiStatBonus(abilityBonus, tags, BonusType.MAX_BURN_STACKS).CalculateStat(1)); effectData[EffectType.BURN].SetResistance(1 - data.GetMultiStatBonus(abilityBonus, tags, BonusType.BURN_RESISTANCE).CalculateStat(0f) / 100f); StatBonus chillChance = data.GetMultiStatBonus(abilityBonus, tags, BonusType.CHILL_CHANCE, BonusType.STATUS_EFFECT_CHANCE, BonusType.ELEMENTAL_STATUS_EFFECT_CHANCE); StatBonus chillEffectiveness = data.GetMultiStatBonus(abilityBonus, tags, BonusType.CHILL_EFFECTIVENESS, BonusType.NONDAMAGE_STATUS_EFFECTIVENESS, BonusType.ELEMENTAL_STATUS_EFFECT_EFFECTIVENESS); StatBonus chillDuration = data.GetMultiStatBonus(abilityBonus, tags, BonusType.CHILL_DURATION, BonusType.STATUS_EFFECT_DURATION, BonusType.ELEMENTAL_STATUS_EFFECT_DURATION); effectData[EffectType.CHILL].SetChance(chillChance.CalculateStat(0f)); effectData[EffectType.CHILL].SetEffectiveness(chillEffectiveness.CalculateStat(100f) / 100f); effectData[EffectType.CHILL].SetDuration(chillDuration.CalculateStat(ChillEffect.BASE_DURATION)); StatBonus electrocuteChance = data.GetMultiStatBonus(abilityBonus, tags, BonusType.ELECTROCUTE_CHANCE, BonusType.STATUS_EFFECT_CHANCE, BonusType.ELEMENTAL_STATUS_EFFECT_CHANCE); StatBonus electrocuteEffectiveness = data.GetMultiStatBonus(abilityBonus, tags, BonusType.ELECTROCUTE_EFFECTIVENESS, BonusType.STATUS_EFFECT_DAMAGE, BonusType.DAMAGE_OVER_TIME, BonusType.ELEMENTAL_STATUS_EFFECT_EFFECTIVENESS); StatBonus electrocuteDuration = data.GetMultiStatBonus(abilityBonus, tags, BonusType.ELECTROCUTE_DURATION, BonusType.STATUS_EFFECT_DURATION, BonusType.ELEMENTAL_STATUS_EFFECT_DURATION); float electrocuteSpeed = data.GetMultiStatBonus(abilityBonus, tags, BonusType.ELECTROCUTE_SPEED, BonusType.DAMAGE_OVER_TIME_SPEED).CalculateStat(100f) / 100f; effectData[EffectType.ELECTROCUTE].SetChance(electrocuteChance.CalculateStat(0f)); effectData[EffectType.ELECTROCUTE].SetEffectiveness(electrocuteEffectiveness.CalculateStat(100f) / 100f * electrocuteSpeed); effectData[EffectType.ELECTROCUTE].SetDuration(electrocuteDuration.CalculateStat(ElectrocuteEffect.BASE_DURATION) / electrocuteSpeed); effectData[EffectType.ELECTROCUTE].SetMaxStacks(data.GetMultiStatBonus(abilityBonus, tags, BonusType.MAX_ELECTROCUTE_STACKS).CalculateStat(1)); effectData[EffectType.ELECTROCUTE].SetResistance(1 - data.GetMultiStatBonus(abilityBonus, tags, BonusType.ELECTROCUTE_RESISTANCE).CalculateStat(0f) / 100f); StatBonus fractureChance = data.GetMultiStatBonus(abilityBonus, tags, BonusType.FRACTURE_CHANCE, BonusType.STATUS_EFFECT_CHANCE, BonusType.ELEMENTAL_STATUS_EFFECT_CHANCE); StatBonus fractureEffectiveness = data.GetMultiStatBonus(abilityBonus, tags, BonusType.FRACTURE_EFFECTIVENESS, BonusType.NONDAMAGE_STATUS_EFFECTIVENESS, BonusType.ELEMENTAL_STATUS_EFFECT_EFFECTIVENESS); StatBonus fractureDuration = data.GetMultiStatBonus(abilityBonus, tags, BonusType.FRACTURE_DURATION, BonusType.STATUS_EFFECT_DURATION, BonusType.ELEMENTAL_STATUS_EFFECT_DURATION); effectData[EffectType.FRACTURE].SetChance(fractureChance.CalculateStat(0f)); effectData[EffectType.FRACTURE].SetEffectiveness(fractureEffectiveness.CalculateStat(100f) / 100f); effectData[EffectType.FRACTURE].SetDuration(fractureDuration.CalculateStat(FractureEffect.BASE_DURATION)); StatBonus pacifyChance = data.GetMultiStatBonus(abilityBonus, tags, BonusType.PACIFY_CHANCE, BonusType.STATUS_EFFECT_CHANCE, BonusType.PRIMORDIAL_STATUS_EFFECT_CHANCE); StatBonus pacifyEffectiveness = data.GetMultiStatBonus(abilityBonus, tags, BonusType.PACIFY_EFFECTIVENESS, BonusType.NONDAMAGE_STATUS_EFFECTIVENESS, BonusType.PRIMORDIAL_STATUS_EFFECT_EFFECTIVENESS); StatBonus pacifyDuration = data.GetMultiStatBonus(abilityBonus, tags, BonusType.PACIFY_DURATION, BonusType.STATUS_EFFECT_DURATION, BonusType.PRIMORDIAL_STATUS_EFFECT_DURATION); effectData[EffectType.PACIFY].SetChance(pacifyChance.CalculateStat(0f)); effectData[EffectType.PACIFY].SetEffectiveness(pacifyEffectiveness.CalculateStat(100f) / 100f); effectData[EffectType.PACIFY].SetDuration(pacifyDuration.CalculateStat(PacifyEffect.BASE_DURATION)); StatBonus radiationChance = data.GetMultiStatBonus(abilityBonus, tags, BonusType.RADIATION_CHANCE, BonusType.STATUS_EFFECT_CHANCE, BonusType.PRIMORDIAL_STATUS_EFFECT_CHANCE); StatBonus radiationEffectiveness = data.GetMultiStatBonus(abilityBonus, tags, BonusType.RADIATION_EFFECTIVENESS, BonusType.STATUS_EFFECT_DAMAGE, BonusType.DAMAGE_OVER_TIME, BonusType.PRIMORDIAL_STATUS_EFFECT_EFFECTIVENESS); StatBonus radiationDuration = data.GetMultiStatBonus(abilityBonus, tags, BonusType.RADIATION_DURATION, BonusType.STATUS_EFFECT_DURATION, BonusType.PRIMORDIAL_STATUS_EFFECT_DURATION); float radiationSpeed = data.GetMultiStatBonus(abilityBonus, tags, BonusType.RADIATION_SPEED, BonusType.DAMAGE_OVER_TIME_SPEED).CalculateStat(100f) / 100f; effectData[EffectType.RADIATION].SetChance(radiationChance.CalculateStat(0f)); effectData[EffectType.RADIATION].SetEffectiveness(radiationEffectiveness.CalculateStat(100f) / 100f * radiationSpeed); effectData[EffectType.RADIATION].SetDuration(radiationDuration.CalculateStat(RadiationEffect.BASE_DURATION) / radiationSpeed); effectData[EffectType.RADIATION].SetMaxStacks(data.GetMultiStatBonus(abilityBonus, tags, BonusType.MAX_RADIATION_STACKS).CalculateStat(1)); effectData[EffectType.RADIATION].SetResistance(1 - data.GetMultiStatBonus(abilityBonus, tags, BonusType.RADIATION_RESISTANCE).CalculateStat(0f) / 100f); StatBonus poisonChance = data.GetMultiStatBonus(abilityBonus, tags, BonusType.POISON_CHANCE); StatBonus poisonEffectiveness = data.GetMultiStatBonus(abilityBonus, tags, BonusType.POISON_EFFECTIVENESS, BonusType.STATUS_EFFECT_DAMAGE, BonusType.DAMAGE_OVER_TIME); StatBonus poisonDuration = data.GetMultiStatBonus(abilityBonus, tags, BonusType.POISON_DURATION, BonusType.STATUS_EFFECT_DURATION); float poisonSpeed = data.GetMultiStatBonus(abilityBonus, tags, BonusType.POISON_SPEED, BonusType.DAMAGE_OVER_TIME_SPEED).CalculateStat(100f) / 100f; effectData[EffectType.POISON].SetChance(poisonChance.CalculateStat(0f)); effectData[EffectType.POISON].SetEffectiveness(poisonEffectiveness.CalculateStat(100f) / 100f * poisonSpeed); effectData[EffectType.POISON].SetDuration(poisonDuration.CalculateStat(PoisonEffect.BASE_DURATION) / poisonSpeed); effectData[EffectType.POISON].SetMaxStacks(data.GetMultiStatBonus(abilityBonus, tags, BonusType.MAX_POISON_STACKS).CalculateStat(PoisonEffect.BASE_MAX_STACKS)); effectData[EffectType.POISON].SetResistance(1 - data.GetMultiStatBonus(abilityBonus, tags, BonusType.POISON_RESISTANCE).CalculateStat(0f) / 100f); this.vsBossDamage = data.GetMultiStatBonus(abilityBonus, tags, BonusType.DAMAGE_VS_BOSS).CalculateStat(1f); this.directHitDamage = data.GetMultiStatBonus(abilityBonus, tags, BonusType.DIRECT_HIT_DAMAGE).CalculateStat(1f); }
/// <summary> /// Calculates Elemental Conversions. Each element is run once so conversion chains cannot happen. /// After conversion, the converted damage is calculated with all multiplier bonuses and /// is added to an array. Afterwards it should be added to the main damage dictionary/array. /// </summary> /// <param name="data"></param> /// <param name="flatDamageMod"></param> /// <param name="convertedDamageMin"></param> /// <param name="convertedDamageMax"></param> /// <param name="tags"></param> /// <param name="damageContainer"></param> /// <param name="element"></param> /// <param name="multi"></param> /// <param name="minBonus"></param> /// <param name="maxBonus"></param> /// <param name="multiplierBonus"></param> /// <returns></returns> private MinMaxRange CalculateDamageConversion(ActorData data, float flatDamageMod, int[] convertedDamageMin, int[] convertedDamageMax, IEnumerable <GroupType> tags, AbilityDamageContainer damageContainer, ElementType element, HashSet <BonusType> multi, StatBonus minBonus = null, StatBonus maxBonus = null, StatBonus multiplierBonus = null) { float baseMin, baseMax; if (minBonus != null) { baseMin = damageContainer.baseMin + minBonus.CalculateStat(0) * flatDamageMod; } else { baseMin = damageContainer.baseMin + damageContainer.minBonus.CalculateStat(0) * flatDamageMod; } if (maxBonus != null) { baseMax = damageContainer.baseMax + maxBonus.CalculateStat(0) * flatDamageMod; } else { baseMax = damageContainer.baseMax + damageContainer.maxBonus.CalculateStat(0) * flatDamageMod; } float finalBaseMin = baseMin; float finalBaseMax = baseMax; for (int i = 0; i < 7; i++) { if (damageContainer.conversions[i] == 0) { continue; } float convertedMin = baseMin * damageContainer.conversions[i]; float convertedMax = baseMax * damageContainer.conversions[i]; finalBaseMin -= convertedMin; finalBaseMax -= convertedMax; HashSet <BonusType> bonusesForConverted = new HashSet <BonusType>(multi); bonusesForConverted.UnionWith(Helpers.GetMultiplierTypes(abilityBase.abilityType, element, (ElementType)i)); StatBonus totalMultiplierBonus = data.GetMultiStatBonus(abilityBonuses, tags, bonusesForConverted.ToArray()); totalMultiplierBonus.AddBonuses(multiplierBonus); convertedMin = totalMultiplierBonus.CalculateStat(convertedMin); convertedMax = totalMultiplierBonus.CalculateStat(convertedMax); convertedDamageMin[i] += (int)Math.Max(convertedMin * finalDamageModifier, 0); convertedDamageMax[i] += (int)Math.Max(convertedMax * finalDamageModifier, 0); } MinMaxRange returnValue = new MinMaxRange(); if (finalBaseMin < 1) { finalBaseMin = 0; } if (finalBaseMax < 1) { finalBaseMax = 0; } if (finalBaseMax == 0 && finalBaseMin == 0) { returnValue.min = 0; returnValue.max = 0; } else { StatBonus finalMultiplier = data.GetMultiStatBonus(abilityBonuses, tags, multi.ToArray()); finalMultiplier.AddBonuses(multiplierBonus); returnValue.min = (int)Math.Max(finalMultiplier.CalculateStat(finalBaseMin) * finalDamageModifier, 0); returnValue.max = (int)Math.Max(finalMultiplier.CalculateStat(finalBaseMax) * finalDamageModifier, 0); } return(returnValue); }
protected void UpdateTypeParameters(HeroData data, IEnumerable <GroupType> tags) { if (abilityBase.abilityType == AbilityType.AURA || abilityBase.abilityType == AbilityType.SELF_BUFF) { return; } if (abilityBase.abilityType == AbilityType.SPELL) { StatBonus critBonus = data.GetMultiStatBonus(abilityBonuses, tags, BonusType.SPELL_CRITICAL_CHANCE, BonusType.GLOBAL_CRITICAL_CHANCE); StatBonus critDamageBonus = data.GetMultiStatBonus(abilityBonuses, tags, BonusType.SPELL_CRITICAL_DAMAGE, BonusType.GLOBAL_CRITICAL_DAMAGE); StatBonus speedBonus = data.GetMultiStatBonus(abilityBonuses, tags, BonusType.CAST_SPEED, BonusType.GLOBAL_ABILITY_SPEED); StatBonus rangeBonus = data.GetMultiStatBonus(abilityBonuses, tags, BonusType.SPELL_RANGE); if (abilityBase.abilityShotType == AbilityShotType.NOVA_AOE) { data.GetMultiStatBonus(rangeBonus, abilityBonuses, tags, BonusType.AREA_RADIUS); } MainCriticalChance = critBonus.CalculateStat(abilityBase.baseCritical); MainCriticalDamage = 1f + (critDamageBonus.CalculateStat(50) / 100f); Cooldown = 1f / speedBonus.CalculateStat(abilityBase.attacksPerSec); TargetRange = rangeBonus.CalculateStat(abilityBase.targetRange); } else if (abilityBase.abilityType == AbilityType.ATTACK) { StatBonus rangeBonus = new StatBonus(); StatBonus speedBonus = data.GetMultiStatBonus(abilityBonuses, tags, BonusType.GLOBAL_ATTACK_SPEED, BonusType.GLOBAL_ABILITY_SPEED); if (abilityBase.GetGroupTypes().Contains(GroupType.MELEE_ATTACK)) { data.GetMultiStatBonus(rangeBonus, abilityBonuses, tags, BonusType.MELEE_ATTACK_RANGE); } if (abilityBase.GetGroupTypes().Contains(GroupType.RANGED_ATTACK)) { data.GetMultiStatBonus(rangeBonus, abilityBonuses, tags, BonusType.RANGED_ATTACK_RANGE); } if (abilityBase.useWeaponRangeForAOE && abilityBase.useWeaponRangeForTargeting) { data.GetMultiStatBonus(rangeBonus, abilityBonuses, tags, BonusType.AREA_RADIUS); } if (data.GetEquipmentInSlot(EquipSlotType.WEAPON) is Weapon weapon) { HashSet <GroupType> nonWeaponTags = data.GetGroupTypes(false); HashSet <GroupType> mainTags = new HashSet <GroupType>(nonWeaponTags); mainTags.UnionWith(weapon.GetGroupTypes()); float weaponSpeed = weapon.AttackSpeed; float mainCritical = weapon.CriticalChance; float range = weapon.WeaponRange; if (data.GetEquipmentInSlot(EquipSlotType.OFF_HAND) is Weapon offhand) { if (AlternatesAttacks) { HashSet <GroupType> offTags = new HashSet <GroupType>(nonWeaponTags); offTags.UnionWith(offhand.GetGroupTypes()); StatBonus offCritBonus = data.GetMultiStatBonus(abilityBonuses, offTags, BonusType.GLOBAL_CRITICAL_CHANCE, BonusType.ATTACK_CRITICAL_CHANCE); OffhandCriticalChance = offCritBonus.CalculateStat(offhand.CriticalChance); StatBonus offCritDamageBonus = data.GetMultiStatBonus(abilityBonuses, offTags, BonusType.GLOBAL_CRITICAL_DAMAGE, BonusType.ATTACK_CRITICAL_DAMAGE); OffhandCriticalDamage = 1f + (offCritDamageBonus.CalculateStat(50) / 100f); } else { mainTags.UnionWith(offhand.GetGroupTypes()); mainCritical = (mainCritical + offhand.CriticalChance) / 2f; } range = (range + offhand.WeaponRange) / 2f; weaponSpeed = (weaponSpeed + offhand.AttackSpeed) / 2f; } Cooldown = 1f / speedBonus.CalculateStat(weaponSpeed); StatBonus mainCritBonus = data.GetMultiStatBonus(abilityBonuses, mainTags, BonusType.GLOBAL_CRITICAL_CHANCE, BonusType.ATTACK_CRITICAL_CHANCE); MainCriticalChance = mainCritBonus.CalculateStat(mainCritical); StatBonus mainCritDamageBonus = data.GetMultiStatBonus(abilityBonuses, mainTags, BonusType.GLOBAL_CRITICAL_DAMAGE, BonusType.ATTACK_CRITICAL_DAMAGE); MainCriticalDamage = 1f + (mainCritDamageBonus.CalculateStat(50) / 100f); if (abilityBase.useWeaponRangeForTargeting) { TargetRange = rangeBonus.CalculateStat(range); } else { TargetRange = rangeBonus.CalculateStat(abilityBase.targetRange); } } else { StatBonus critBonus = data.GetMultiStatBonus(abilityBonuses, tags, BonusType.GLOBAL_CRITICAL_CHANCE, BonusType.ATTACK_CRITICAL_CHANCE); //Unarmed default values if (abilityBase.useWeaponRangeForTargeting) { TargetRange = rangeBonus.CalculateStat(1f); } else { TargetRange = rangeBonus.CalculateStat(abilityBase.targetRange); } Cooldown = 1f / speedBonus.CalculateStat(1f); MainCriticalChance = critBonus.CalculateStat(3.5f); } } if (float.IsInfinity(Cooldown) || float.IsNaN(Cooldown)) { Cooldown = 0.001f; } }
public Dictionary <ElementType, float> ScaleSecondaryDamageValue(Actor target, Dictionary <ElementType, MinMaxRange> baseDamage, IEnumerable <GroupType> effectTags) { HashSet <GroupType> targetTypes = target.GetActorTagsAsTarget(); Dictionary <BonusType, StatBonus> whenHitBonusDict = new Dictionary <BonusType, StatBonus>(); int[] minDamage = new int[7]; int[] maxDamage = new int[7]; int[] convertedMinDamage = new int[7]; int[] convertedMaxDamage = new int[7]; float[] conversions = new float[7]; HashSet <GroupType> tagsToUse = new HashSet <GroupType>(effectTags); tagsToUse.UnionWith(GetActorTagsAndDataTags()); foreach (TriggeredEffect triggeredEffect in Data.TriggeredEffects[TriggerType.WHEN_HITTING]) { if (targetTypes.Contains(triggeredEffect.BaseEffect.restriction) && triggeredEffect.RollTriggerChance()) { if (whenHitBonusDict.ContainsKey(triggeredEffect.BaseEffect.statBonusType)) { whenHitBonusDict[triggeredEffect.BaseEffect.statBonusType].AddBonus(triggeredEffect.BaseEffect.statModifyType, triggeredEffect.Value); } else { StatBonus bonus = new StatBonus(); bonus.AddBonus(triggeredEffect.BaseEffect.statModifyType, triggeredEffect.Value); whenHitBonusDict.Add(triggeredEffect.BaseEffect.statBonusType, bonus); } } } foreach (ElementType elementType in Enum.GetValues(typeof(ElementType))) { if (!baseDamage.ContainsKey(elementType)) { baseDamage[elementType] = new MinMaxRange(); } minDamage[(int)elementType] = baseDamage[elementType].min; maxDamage[(int)elementType] = baseDamage[elementType].max; HashSet <BonusType> minTypes = new HashSet <BonusType>(); HashSet <BonusType> maxTypes = new HashSet <BonusType>(); HashSet <BonusType> multiTypes = new HashSet <BonusType>(); Helpers.GetGlobalAndFlatDamageTypes(elementType, tagsToUse, minTypes, maxTypes, multiTypes); multiTypes.UnionWith(Helpers.GetMultiplierTypes(AbilityType.NON_ABILITY, elementType)); StatBonus minBonus = Data.GetMultiStatBonus(tagsToUse, minTypes.ToArray()); StatBonus maxBonus = Data.GetMultiStatBonus(tagsToUse, maxTypes.ToArray()); StatBonus multiBonus = new StatBonus(); foreach (KeyValuePair <BonusType, StatBonus> keyValue in whenHitBonusDict) { if (minTypes.Contains(keyValue.Key)) { minBonus.AddBonuses(keyValue.Value); } else if (maxTypes.Contains(keyValue.Key)) { maxBonus.AddBonuses(keyValue.Value); } else if (multiTypes.Contains(keyValue.Key)) { multiBonus.AddBonuses(keyValue.Value); } } HashSet <BonusType> availableConversions = Data.BonusesIntersection(null, Helpers.GetConversionTypes(elementType)); if (availableConversions.Count > 0) { Array.Clear(conversions, 0, 7); ActorAbility.GetElementConversionValues(Data, tagsToUse, availableConversions, conversions, null); MinMaxRange baseRange = ActorAbility.CalculateDamageConversion(Data, convertedMinDamage, convertedMaxDamage, tagsToUse, baseDamage[elementType], conversions, elementType, multiTypes, minBonus, maxBonus, multiBonus); minDamage[(int)elementType] = baseRange.min; maxDamage[(int)elementType] = baseRange.max; } else { Data.GetMultiStatBonus(multiBonus, null, tagsToUse, multiTypes.ToArray()); minDamage[(int)elementType] = (int)Math.Max(multiBonus.CalculateStat(baseDamage[elementType].min + minBonus.CalculateStat(0f)), 0); maxDamage[(int)elementType] = (int)Math.Max(multiBonus.CalculateStat(baseDamage[elementType].max + maxBonus.CalculateStat(0f)), 0); } } float critChance = Data.GetMultiStatBonus(tagsToUse, BonusType.GLOBAL_CRITICAL_CHANCE).CalculateStat(0f); bool isCrit = UnityEngine.Random.Range(0f, 100f) < critChance; Dictionary <ElementType, float> returnDict = new Dictionary <ElementType, float>(); foreach (ElementType elementType in Enum.GetValues(typeof(ElementType))) { float damage = UnityEngine.Random.Range(minDamage[(int)elementType] + convertedMinDamage[(int)elementType], maxDamage[(int)elementType] + convertedMaxDamage[(int)elementType] + 1); if (isCrit) { damage *= 1 + (Data.GetMultiStatBonus(tagsToUse, BonusType.GLOBAL_CRITICAL_DAMAGE).CalculateStat(50) / 100f); } returnDict.Add(elementType, damage); } return(returnDict); }