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);
    }
Example #2
0
    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;
        }
    }
Example #5
0
    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;
        }
    }
Example #8
0
    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);
    }