Exemple #1
0
    public static DamageHandler CalculateDamage(SkillCast skill, IFieldActor source, IFieldActor target, double luckCoefficient)
    {
        // TODO: get accuracyWeakness from enemy stats from enemy buff. new stat recommended
        const double AccuracyWeakness = 0;
        double       hitRate          = (source.Stats[StatAttribute.Accuracy].Total + AccuracyWeakness) / Math.Max(target.Stats[StatAttribute.Evasion].Total, 0.1);

        if (Random.Shared.NextDouble() > hitRate)
        {
            return(new(source, target, 0, HitType.Miss)); // we missed
        }

        bool isCrit = skill.IsGuaranteedCrit() || RollCrit(source, target, luckCoefficient);

        double finalCritDamage = 1;

        if (isCrit)
        {
            // TODO: get critResist from enemy stats from enemy buff. new stat recommended
            const double CritResist = 1;
            double       critDamage = 1000 + source.Stats[StatAttribute.CritDamage].Total;
            finalCritDamage = CritResist * ((critDamage / 1000) - 1) + 1;
        }

        double damageBonus = 1 + FetchMultiplier(source.Stats, StatAttribute.TotalDamage);

        damageBonus *= finalCritDamage;

        switch (skill.GetElement())
        {
        case Element.Fire:
            damageBonus += FetchMultiplier(source.Stats, StatAttribute.FireDamage);
            break;

        case Element.Ice:
            damageBonus += FetchMultiplier(source.Stats, StatAttribute.IceDamage);
            break;

        case Element.Electric:
            damageBonus += FetchMultiplier(source.Stats, StatAttribute.ElectricDamage);
            break;

        case Element.Holy:
            damageBonus += FetchMultiplier(source.Stats, StatAttribute.HolyDamage);
            break;

        case Element.Dark:
            damageBonus += FetchMultiplier(source.Stats, StatAttribute.DarkDamage);
            break;

        case Element.Poison:
            damageBonus += FetchMultiplier(source.Stats, StatAttribute.PoisonDamage);
            break;
        }

        SkillRangeType rangeType = skill.GetRangeType();

        if (rangeType != SkillRangeType.Special)
        {
            damageBonus += FetchMultiplier(source.Stats, rangeType == SkillRangeType.Melee ? StatAttribute.MeleeDamage : StatAttribute.RangedDamage);
        }

        bool isBoss = false;

        if (target is INpc npc)
        {
            isBoss = npc.Value.IsBoss();
        }

        damageBonus += isBoss ? FetchMultiplier(source.Stats, StatAttribute.BossDamage) : 0;

        // TODO: properly fetch enemy attack speed weakness from enemy buff. new stat recommended
        const double AttackSpeedWeakness = 0;

        damageBonus += AttackSpeedWeakness * FetchMultiplier(source.Stats, StatAttribute.AttackSpeed);

        double damageMultiplier = damageBonus * skill.GetDamageRate();

        // TODO: properly fetch enemy pierce resistance from enemy buff. new stat recommended
        const double EnemyPierceResistance = 1;

        double defensePierce = 1 - Math.Min(0.3, EnemyPierceResistance * FetchMultiplier(source.Stats, StatAttribute.Pierce));

        damageMultiplier *= 1 / (Math.Max(target.Stats[StatAttribute.Defense].Total, 1) * defensePierce);

        bool          isPhysical     = skill.GetSkillDamageType() == DamageType.Physical;
        StatAttribute resistanceStat = isPhysical ? StatAttribute.PhysicalRes : StatAttribute.MagicRes;
        StatAttribute attackStat     = isPhysical ? StatAttribute.PhysicalAtk : StatAttribute.MagicAtk;
        StatAttribute piercingStat   = isPhysical ? StatAttribute.PhysicalPiercing : StatAttribute.MagicPiercing;

        double targetRes  = target.Stats[resistanceStat].Total;
        double attackType = source.Stats[attackStat].Total;
        double resPierce  = FetchMultiplier(source.Stats, piercingStat);
        double resistance = (1500.0 - Math.Max(0, targetRes - 1500 * resPierce)) / 1500;

        // does this need to be divided by anything at all to account for raw physical attack?
        damageMultiplier *= attackType * resistance;

        // TODO: apply special standalone multipliers like Spicy Maple Noodles buff? it seems to have had it's own multiplier. new stat recommended
        const double FinalDamageMultiplier = 1;

        damageMultiplier *= FinalDamageMultiplier;

        double attackDamage = 300;

        if (source is IFieldActor <Player> player)
        {
            double bonusAttack = player.Stats[StatAttribute.BonusAtk].Total + 0.4 * player.Stats[StatAttribute.PetBonusAtk].Total;

            // TODO: properly fetch enemy bonus attack weakness from enemy buff. new stat recommended
            const double BonusAttackWeakness = 1;

            double minDamage = player.Stats[StatAttribute.MinWeaponAtk].Total + BonusAttackWeakness * bonusAttack;
            double maxDamage = player.Stats[StatAttribute.MaxWeaponAtk].Total + BonusAttackWeakness * bonusAttack;

            attackDamage = minDamage + (maxDamage - minDamage) * Random.Shared.NextDouble();
        }

        attackDamage *= damageMultiplier;

        return(new(source, target, Math.Max(1, attackDamage), isCrit ? HitType.Critical : HitType.Normal));
    }