Combat Calculation Functions

From Ymirheim Wiki
using System;
using System.Runtime.CompilerServices;

namespace Ymirheim.Server.Game.Entities.Combat;

//// To port to javascript, replace:
//// "[MethodImpl(MethodImplOptions.AggressiveInlining)]" -> ""
//// "public static double" -> "function"
//// "double " -> ""
//// "Math.Pow" -> "Math.pow"
//// "Math.Min" -> "Math.min"
//// "Math.Max" -> "Math.max"
//// "Math.Abs" -> "Math.abs"
public static class CombatCalculations
{
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double CalculateHitChance(double sourceAccuracy, double targetEvasion)
    {
        var hitChance = 85.0 + (sourceAccuracy - targetEvasion);
        hitChance = Math.Max(Math.Min(hitChance, 95.0), 2.0); // Limit from 2 to 95%

        return hitChance;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double EvasionModifierPerEnemyCount(double enemyCount)
    {
        return 1.0 - (0.1 * Math.Max(enemyCount - 2, 0));
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double BaseMeleeBalance()
    {
        return 565.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double BaseRangedBalance()
    {
        return 565.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double BaseMagicBalance()
    {
        return 565.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double MeleeBalancePerConcentration(double concentration)
    {
        return Math.Pow(concentration, 0.5) * 35.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double RangedBalancePerConcentration(double concentration)
    {
        return Math.Pow(concentration, 0.5) * 35.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double MagicBalancePerConcentration(double concentration)
    {
        return Math.Pow(concentration, 0.5) * 35.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double MeleeAttackPerLevel(double level)
    {
        return 10 + level;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double MeleeAttackPerStrength(double strength)
    {
        return strength * 2.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double MeleeAttackPerBaseConcentration(double concentration)
    {
        return concentration / 2.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double RangedAttackPerLevel(double level)
    {
        return 12 + level;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double RangedAttackPerConcentration(double concentration)
    {
        return concentration * 2.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double RangedAttackPerBaseStrength(double strength)
    {
        return strength / 2.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double MagicalPowerPerLevel(double level)
    {
        return 19 + level;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double MagicalPowerPerIntelligence(double intelligence)
    {
        return intelligence * 3.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double MagicalPowerPerBaseConcentration(double concentration)
    {
        return concentration / 2.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double DamageReductionPerDefense(double defense)
    {
        return defense / 2.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double DamageReductionFactorPerDefense(double defense)
    {
        return 150 / (150 + defense);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double DamageReductionFactorPerElementalDefense(double defense)
    {
        var value = 150 / (150 + Math.Abs(defense));

        if (defense < 0)
        {
            value = 1 / value;
        }

        return value;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double DamageReductionFactorPerSizeDefense(double defense)
    {
        var value = 150 / (150 + Math.Abs(defense));

        if (defense < 0)
        {
            value = 1 / value;
        }

        return value;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double DamageReductionFactorPerTypeDefense(double defense)
    {
        var value = 150 / (150 + Math.Abs(defense));

        if (defense < 0)
        {
            value = 1 / value;
        }

        return value;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double MinAttackModifierPerBalance(double balance)
    {
        return Math.Min(1000.0, balance) / 1000.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double HealthMultiplierPerVitality(double vitality)
    {
        return 1.0 + (Math.Pow(vitality, 0.7) * 0.1);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double HealthPerLevel(double level)
    {
        return 90.0 + (level * 4) + Math.Pow(level, 1.75);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double ManaPerLevel(double level)
    {
        return 85 + (level * 5);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double ManaMultiplierPerIntelligence(double intelligence)
    {
        return 1 + (intelligence * 0.03);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double BaseHealthRecoveryRate()
    {
        return 1.4;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double HealthRecoveryRatePerVitality(double vitality)
    {
        return vitality / 15.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double HealthRecoveryRatePerHealth(double health)
    {
        return health / 3000.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double HealthRecoveryRatePerLevel(double level)
    {
        return level / 75.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double BaseManaRecoveryRate()
    {
        return 0.9;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double ManaRecoveryRatePerIntelligence(double intelligence)
    {
        return intelligence / 48.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double ManaRecoveryRatePerMana(double mana)
    {
        return mana / 800.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double DefensePerVitality(double vitality)
    {
        return vitality;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double MagicResistancePerIntelligence(double intelligence)
    {
        return intelligence;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double MagicResistancePerBaseDexterity(double dexterity)
    {
        return dexterity / 4.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double MagicResistancePerLevel(double level)
    {
        return level / 4.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double MeleeAccuracyPerLevel(double level)
    {
        return 100.0 + (level / 2.0);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double MeleeAccuracyPerConcentration(double concentration)
    {
        return Math.Pow(concentration, 0.8) * 2.5;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double RangedAccuracyPerLevel(double level)
    {
        return 100.0 + (level / 2.0);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double RangedAccuracyPerConcentration(double concentration)
    {
        return Math.Pow(concentration, 0.8) * 2.5;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double MagicAccuracyPerLevel(double level)
    {
        return 100.0 + (level / 2.0);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double MagicAccuracyPerConcentration(double concentration)
    {
        return Math.Pow(concentration, 0.8) * 2.5;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double MeleeEvasionPerLevel(double level)
    {
        return 100.0 + (level / 2.0);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double MeleeEvasionPerDexterity(double dexterity)
    {
        return Math.Pow(dexterity, 0.8) * 2;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double RangedEvasionPerLevel(double level)
    {
        return 100.0 + (level / 2.0);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double RangedEvasionPerDexterity(double dexterity)
    {
        return Math.Pow(dexterity, 0.8) * 2;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double MagicEvasionPerLevel(double level)
    {
        return 100.0 + (level / 2.0);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double MagicEvasionPerDexterity(double dexterity)
    {
        return Math.Pow(dexterity, 0.8) * 2;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double BaseLuckyDodgeChance()
    {
        return 0.9;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double LuckyDodgeChancePerLuck(double luck)
    {
        return luck / 10.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double BaseMeleeCriticalHitChance()
    {
        return 1.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double BaseRangedCriticalHitChance()
    {
        return 1.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double BaseMagicCriticalHitChance()
    {
        return 1.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double BaseMeleeCriticalHitEvasion()
    {
        return 0.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double BaseRangedCriticalHitEvasion()
    {
        return 0.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double BaseMagicCriticalHitEvasion()
    {
        return 0.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double MeleeCriticalHitChancePerLuck(double luck)
    {
        return luck / 3.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double RangedCriticalHitChancePerLuck(double luck)
    {
        return luck / 3.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double MagicCriticalHitChancePerLuck(double luck)
    {
        return luck / 3.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double MeleeCriticalHitEvasionPerLuck(double luck)
    {
        return luck / 6.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double RangedCriticalHitEvasionPerLuck(double luck)
    {
        return luck / 6.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double MagicCriticalHitEvasionPerLuck(double luck)
    {
        return luck / 6.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double BaseMeleeCriticalAttack()
    {
        return 40.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double BaseRangedCriticalAttack()
    {
        return 40.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double BaseMagicCriticalPower()
    {
        return 40.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double AttackSpeedPerDexterity(double dexterity)
    {
        return dexterity * 1.75;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double CooldownSpeedPerDexterity(double dexterity)
    {
        return dexterity * 0.25;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double CastingSpeedPerDexterity(double dexterity)
    {
        return dexterity * 3.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double DelayReductionPerSpeed(double attackSpeed)
    {
        return 1.0 / (1.0 + (attackSpeed / 100.0));
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double MinCooldown()
    {
        return 200.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double PoisonResistancePerLuck(double luck)
    {
        return Math.Pow(luck, 1.5) / 100.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double RootResistancePerLuck(double luck)
    {
        return Math.Pow(luck, 1.5) / 100.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double SlowResistancePerLuck(double luck)
    {
        return Math.Pow(luck, 1.5) / 100.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double SleepResistancePerLuck(double luck)
    {
        return Math.Pow(luck, 1.5) / 100.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double StunResistancePerLuck(double luck)
    {
        return Math.Pow(luck, 1.5) / 100.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double SilenceResistancePerLuck(double luck)
    {
        return Math.Pow(luck, 1.5) / 100.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double BlindResistancePerLuck(double luck)
    {
        return Math.Pow(luck, 1.5) / 100.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double BleedingResistancePerLuck(double luck)
    {
        return Math.Pow(luck, 1.5) / 100.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double BurningResistancePerLuck(double luck)
    {
        return Math.Pow(luck, 1.5) / 100.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double FreezeResistancePerLuck(double luck)
    {
        return Math.Pow(luck, 1.5) / 100.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double PoisonResistancePerVitality(double vitality)
    {
        return Math.Pow(vitality, 1.5) / 35.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double RootResistancePerDexterity(double dexterity)
    {
        return Math.Pow(dexterity, 1.5) / 35.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double SlowResistancePerDexterity(double dexterity)
    {
        return Math.Pow(dexterity, 1.5) / 35.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double SleepResistancePerConcentration(double concentration)
    {
        return Math.Pow(concentration, 1.5) / 35.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double StunResistancePerStrength(double strength)
    {
        return Math.Pow(strength, 1.5) / 35.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double SilenceResistancePerIntelligence(double intelligence)
    {
        return Math.Pow(intelligence, 1.5) / 35.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double BlindResistancePerConcentration(double concentration)
    {
        return Math.Pow(concentration, 1.5) / 35.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double BleedingResistancePerVitality(double vitality)
    {
        return Math.Pow(vitality, 1.5) / 35.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double BurningResistancePerStrength(double strength)
    {
        return Math.Pow(strength, 1.5) / 35.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double FreezeResistancePerIntelligence(double intelligence)
    {
        return Math.Pow(intelligence, 1.5) / 35.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double RangedAccuracyMultiplierPerDistance(double distance, double maxAttackDistance)
    {
        return Math.Max(0, 1.0 - (distance / maxAttackDistance * 0.5)); // 50% the hit rate at maxAttackDistance
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double BaseWeightCapacity()
    {
        return 25000.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double WeightCapacityPerBaseStrength(double strength)
    {
        return (strength - 1) * 300.0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double OverEncumberedPenaltyFactor(double totalEquipmentWeight, double weightCapacity)
    {
        // 5000 of weight is for free (no penalty)
        totalEquipmentWeight = Math.Max(totalEquipmentWeight - 5000.0, 0);
        weightCapacity = Math.Max(weightCapacity - 5000.0, 0);

        if (weightCapacity == 0)
        {
            return 1.0;
        }

        return Math.Min(totalEquipmentWeight / weightCapacity, 1.0);
    }
}