server-master/srcs/_plugins/Plugin.ResourceLoader/BattleEntityAlgorithmService.cs
2026-02-10 18:21:30 +01:00

724 lines
No EOL
24 KiB
C#

using System;
using WingsEmu.Game._enum;
using WingsEmu.Game.Algorithm;
using WingsEmu.Packets.Enums;
using WingsEmu.Packets.Enums.Battle;
using WingsEmu.Packets.Enums.Character;
namespace Plugin.ResourceLoader
{
/*
* Coefficient of statistics for the class:
* Adventurer: 0, 0, 0
* Swordsman: 8, 2, 0
* Archer: 3, 6, 1
* Mage: 0, 2, 8
* Martial Artist: 5, 3, 2
*
* Additional statistics:
* Adventurer: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
* Swordsman: 0, 15, 0, 0, 0, 0, 0, 0, 0, 0
* Archer: 0, 15, 50, 15, 0, 0, 10, 0, 10, 0
* Magician: 0, 0, 0, 0, 0, 0, 10, 20, 0, 0
* Martial Artist: 0, 0, 20, 0, 0, 0, 20, 10, 10, 0
*/
public class BattleEntityAlgorithmService : IBattleEntityAlgorithmService
{
private static readonly double[] DefenseRace0 = { 16, 13.5, 11, 50, 50, 50 };
private static readonly double[] DefenseRace1 = { 20, 17, 19, 100, 100, 100 };
private static readonly double[] DefenseRace2 = { 15, 15, 15, 75, 50, 40 };
private static readonly double[] DefenseRace3 = { 15, 15, 15, 50, 50, 50 };
private static readonly double[] DefenseRace4 = { 17.4, 17.4, 17.4, 60, 60, 100 };
private static readonly double[] DefenseRace5 = { 13.4, 13.4, 13.4, 40, 40, 40 };
private static readonly double[] DefenseRace6 = { 11.5, 15, 25, 50, 50, 75 };
private static readonly double[] DefenseRace8 = { 10, 10, 10, 100, 100, 100 };
private static readonly double[] DefaultDefense = { 0, 0, 0, 0, 0, 0 };
private static readonly int[,] Statistics =
{
{ 0, 0, 0 },
{ 8, 2, 0 },
{ 3, 6, 1 },
{ 0, 2, 8 },
{ 5, 3, 2 }
};
private static readonly int[,] AdditionalStatistics =
{
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 15, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 15, 50, 15, 0, 0, 10, 0, 10, 0 },
{ 0, 0, 0, 0, 0, 0, 10, 20, 0, 0 },
{ 0, 0, 20, 0, 0, 0, 20, 10, 10, 0 }
};
public int GetBasicHp(int race, int level, int modifier, int additionalHp = 0, bool isMonster = true)
{
double hp = 0;
int a = 0;
int b = 0;
int c = 0;
if (isMonster)
{
modifier = 0;
}
switch (race)
{
case 0:
a = 0;
b = 2;
c = 138;
break;
case 1:
a = 10;
b = 10;
c = 610;
break;
case 2:
a = 5;
b = 0;
c = 105;
break;
case 3:
a = 0;
b = 0;
c = 205;
break;
case 4:
a = 2;
b = 5;
c = 695;
break;
case 5:
a = -2;
b = -3;
c = 263;
break;
case 6:
a = 0;
b = -7;
c = 21;
break;
default:
a = 0;
b = 0;
c = 0;
break;
}
if (race == 8)
{
hp = 7;
}
else
{
int x = level;
if ((modifier + a) != 0)
{
x += (int)Math.Floor((level - 1) / (decimal)(10.0 / (modifier + a)));
}
hp = 0.5 * (x * x) + (15.5 + b) * x + c;
}
if (!isMonster)
{
return (int)Math.Floor(hp + additionalHp);
}
switch (level)
{
case >= 37 and <= 51:
hp *= 1.2;
break;
case >= 52 and <= 61:
hp *= 1.5;
break;
case >= 62 and <= 71:
hp *= 1.8;
break;
case >= 72 and <= 81:
hp *= 2.5;
break;
case >= 81:
hp *= 3.5;
break;
}
return (int)Math.Floor(hp + additionalHp);
}
public int GetBaseStatistic(int level, ClassType classType, StatisticType statisticType)
{
switch (statisticType)
{
case StatisticType.ATTACK_MELEE:
return (int)(level + 9 + Math.Floor((decimal)((level - 1) * Statistics[(int)classType, 0] / 10.0))) + AdditionalStatistics[(int)classType, 0];
case StatisticType.ATTACK_RANGED:
return (int)(level + 9 + Math.Floor((decimal)((level - 1) * Statistics[(int)classType, 1] / 10.0))) + AdditionalStatistics[(int)classType, 2];
case StatisticType.ATTACK_MAGIC:
return (int)(level + 9 + Math.Floor((decimal)((level - 1) * Statistics[(int)classType, 2] / 10.0))) + AdditionalStatistics[(int)classType, 4];
case StatisticType.HITRATE_MELEE:
return (int)(level + 9 + Math.Floor((decimal)((level - 1) * Statistics[(int)classType, 1] / 10.0))) + AdditionalStatistics[(int)classType, 1];
case StatisticType.HITRATE_RANGED:
return (int)((level + 9 + Math.Floor((decimal)((level - 1) * Statistics[(int)classType, 1] / 10.0))) * 2) + AdditionalStatistics[(int)classType, 3];
case StatisticType.DEFENSE_MELEE:
return (int)((level + 9 + Math.Floor((decimal)((level - 1) * Statistics[(int)classType, 0] / 10.0))) * (decimal)0.5) + AdditionalStatistics[(int)classType, 5];
case StatisticType.DEFENSE_RANGED:
return (int)((level + 9 + Math.Floor((decimal)((level - 1) * Statistics[(int)classType, 1] / 10.0))) * (decimal)0.5) + AdditionalStatistics[(int)classType, 7];
case StatisticType.DEFENSE_MAGIC:
return (int)((level + 9 + Math.Floor((decimal)((level - 1) * Statistics[(int)classType, 2] / 10.0))) * (decimal)0.5) + AdditionalStatistics[(int)classType, 9];
case StatisticType.DODGE_MELEE:
return (int)(level + 9 + Math.Floor((decimal)((level - 1) * Statistics[(int)classType, 1] / 10.0))) + AdditionalStatistics[(int)classType, 6];
case StatisticType.DODGE_RANGED:
return (int)(level + 9 + Math.Floor((decimal)((level - 1) * Statistics[(int)classType, 1] / 10.0))) + AdditionalStatistics[(int)classType, 8];
}
return 0;
}
public int GetBasicMp(int race, int level, int modifier, int additionalMp = 0, bool isMonster = true)
{
double mp = 0;
int z = 0;
double d = 0;
int a = 0;
int g = 0;
int x = 0;
if (isMonster)
{
modifier = 0;
}
switch (race)
{
case 0:
d = 4.75;
a = 0;
g = 0;
break;
case 1:
d = 178.75;
a = 10;
g = 8;
break;
case 2:
d = 50.75;
a = -2;
g = 4;
break;
case 3:
d = 50.75;
a = 0;
g = 4;
break;
case 4:
d = 385.75;
a = 10;
g = 6;
z = 1;
break;
case 5:
d = 23.75;
a = -2;
g = 2;
z = 1;
break;
case 6:
d = 705.75;
a = 5;
g = 14;
break;
case 8:
return (int)Math.Floor(4 + (double)additionalMp);
default:
d = 0;
a = 0;
g = 0;
break;
}
x = level;
if ((modifier + a) != 0)
{
x += (int)Math.Floor((level - 1) / (decimal)(10.0 / (modifier + a)) + z);
}
mp = Math.Floor((5.25 + g) * x + d) + (Math.Floor((double)(x - 6) / 4) + 1) * 2 * (Mod(x - 2, 4) + 1 + Math.Floor(((double)x - 6) / 4) * 2);
return (int)Math.Floor(mp + additionalMp);
}
public int GetBasicHpByClass(ClassType classType, int level)
{
int hp = classType switch
{
ClassType.Adventurer => GetBasicHp(3, level, 0, 0, false),
ClassType.Swordman => GetBasicHp(3, level, 8, 0, false),
ClassType.Archer => GetBasicHp(3, level, 3, 0, false),
ClassType.Magician => GetBasicHp(3, level, 0, 0, false),
ClassType.Wrestler => GetBasicHp(3, level, 5, 0, false)
};
return hp;
}
public int GetBasicMpByClass(ClassType classType, int level)
{
int mp = classType switch
{
ClassType.Adventurer => GetBasicMp(3, level, 0, 0, false),
ClassType.Swordman => GetBasicMp(3, level, 0, 0, false),
ClassType.Archer => GetBasicMp(3, level, 1, 0, false),
ClassType.Magician => GetBasicMp(3, level, 8, 0, false),
ClassType.Wrestler => GetBasicMp(3, level, 2, 0, false)
};
return mp;
}
public int GetAttack(bool isMin, int race, AttackType attackType, short weaponLevel, byte wInfo, short level, int modifier, int additional, bool isWild = true, short petLevel = 0,
MateType mateType = MateType.Pet)
{
int calcLevel;
int weaponMod;
int a;
int b;
int c;
if (isWild)
{
modifier = 0;
calcLevel = level;
weaponMod = weaponLevel;
}
else
{
calcLevel = petLevel;
weaponMod = petLevel + level - weaponLevel;
}
switch (attackType)
{
case AttackType.Melee:
switch (race)
{
case 0:
a = 35;
b = 0;
break;
case 1:
a = 43;
b = 10;
break;
case 2:
a = 33;
b = 5;
break;
case 3:
a = 33;
b = 0;
break;
case 4:
a = 38;
b = 2;
break;
case 5:
a = 30;
b = -2;
break;
case 6:
a = 26;
b = 0;
break;
case 8:
a = 23;
b = 0;
break;
default:
a = 0;
b = 0;
break;
}
break;
case AttackType.Ranged:
switch (race)
{
case 0:
a = 30;
b = 0;
break;
case 1:
a = 38;
b = 10;
break;
case 2:
a = 33;
b = 0;
break;
case 3:
a = 33;
b = 0;
break;
case 4:
a = 38;
b = 2;
break;
case 5:
a = 30;
b = -2;
break;
case 6:
a = 33;
b = 0;
break;
case 8:
a = 23;
b = 0;
break;
default:
a = 0;
b = 0;
break;
}
break;
case AttackType.Magical:
switch (race)
{
case 0:
a = 25;
b = 0;
break;
case 1:
a = 41;
b = 10;
break;
case 2:
a = 33;
b = -2;
break;
case 3:
a = 33;
b = 0;
break;
case 4:
a = 38;
b = 10;
break;
case 5:
a = 30;
b = -2;
break;
case 6:
a = 53;
b = 5;
break;
case 8:
a = 23;
b = 0;
break;
default:
a = 0;
b = 0;
break;
}
break;
default:
a = 0;
b = 0;
break;
}
if (wInfo > 1)
{
c = (int)Math.Floor((decimal)((calcLevel + 2.0) / (wInfo - 1))) + 1;
}
else
{
c = 0;
}
if (!isWild && mateType == MateType.Partner)
{
return calcLevel + 9 + (int)Math.Floor((calcLevel - 1) * (modifier / 10.0));
}
if (isMin)
{
return (int)Math.Floor(calcLevel + (a - 7.2) + 3.2 * weaponMod + Math.Floor((calcLevel - 1) * ((modifier + b) / 10.0)) + additional + c);
}
return (int)Math.Floor(calcLevel + a + 4.8 * weaponMod + Math.Floor((calcLevel - 1) * ((modifier + b) / 10.0)) + additional - c);
}
public int GetHitrate(int race, AttackType attackType, short weaponLevel, short level, int modifier, int additional, bool isWild = true, short petLevel = 0, MateType mateType = MateType.Pet)
{
int calcLevel;
int weaponMod;
int a;
int b;
if (isWild)
{
modifier = 0;
calcLevel = level;
weaponMod = weaponLevel;
}
else
{
calcLevel = petLevel;
weaponMod = petLevel + level - weaponLevel;
}
if (!isWild && mateType == MateType.Partner)
{
switch (attackType)
{
case AttackType.Melee:
return calcLevel + 9 + (int)Math.Floor((calcLevel - 1) * (modifier / 10.0));
case AttackType.Ranged:
return 2 * (calcLevel + 9 + (int)Math.Floor((calcLevel - 1) * (modifier / 10.0)));
default:
return 0;
}
}
switch (attackType)
{
case AttackType.Melee:
switch (race)
{
case 0:
a = 22;
b = 0;
break;
case 1:
a = 30;
b = 10;
break;
case 2:
a = 25;
b = 0;
break;
case 3:
a = 25;
b = 0;
break;
case 4:
a = 30;
b = 2;
break;
case 5:
a = 22;
b = -2;
break;
case 6:
a = 25;
b = 0;
break;
case 8:
a = 15;
b = 0;
break;
default:
a = 0;
b = 0;
break;
}
return (int)Math.Floor(calcLevel + 4 * weaponMod + a + Math.Floor((calcLevel - 1) * ((modifier + b) / 10.0)) + additional);
case AttackType.Ranged:
switch (race)
{
case 0:
a = 28;
b = 0;
break;
case 1:
a = 44;
b = 10;
break;
case 2:
a = 34;
b = 0;
break;
case 3:
a = 34;
b = 0;
break;
case 4:
a = 44;
b = 2;
break;
case 5:
a = 28;
b = -2;
break;
case 6:
a = 34;
b = 0;
break;
case 8:
a = 15;
b = 0;
break;
default:
a = 0;
b = 0;
break;
}
return (int)Math.Floor(2 * calcLevel + 4 * weaponMod + a + Math.Floor((calcLevel - 1) * ((modifier + b) / 10.0)) * 2 + additional);
case AttackType.Magical:
return 70 + additional;
default:
return 0;
}
}
public int GetDodge(int race, short armorLevel, short level, int modifier, int additional, bool isWild = true, short petLevel = 0, MateType mateType = MateType.Partner)
{
int calcLevel;
int armorMod;
int a;
int b;
if (isWild)
{
modifier = 0;
calcLevel = level;
armorMod = armorLevel;
}
else
{
calcLevel = petLevel;
armorMod = petLevel + level - armorLevel;
}
if (!isWild && mateType == MateType.Partner)
{
return calcLevel + 9 + (int)Math.Floor((calcLevel - 1) * (modifier / 10.0));
}
switch (race)
{
case 0:
a = 26;
b = 0;
break;
case 1:
a = 34;
b = 10;
break;
case 2:
a = 29;
b = 0;
break;
case 3:
a = 29;
b = 0;
break;
case 4:
a = 34;
b = 2;
break;
case 5:
a = 26;
b = -2;
break;
case 6:
a = 29;
b = 0;
break;
case 8:
a = 19;
b = 0;
break;
default:
a = 0;
b = 0;
break;
}
return (int)Math.Floor(calcLevel + 4 * armorMod + a + Math.Floor((calcLevel - 1) * ((modifier + b) / 10.0)) + additional);
}
public int GetDefense(int race, AttackType attackType, short armorLevel, short level, int modifier, int additional, bool isWild = true, short petLevel = 0, MateType mateType = MateType.Pet)
{
int calcLevel;
int armorMod;
if (isWild)
{
modifier = 0;
calcLevel = level;
armorMod = armorLevel;
}
else
{
calcLevel = petLevel;
armorMod = petLevel + level - armorLevel;
}
if (!isWild && mateType == MateType.Partner)
{
return (int)(0.5 * (calcLevel + 9 + (int)Math.Floor((calcLevel - 1) * (modifier / 10.0))));
}
double[] raceInfo = race switch
{
0 => DefenseRace0,
1 => DefenseRace1,
2 => DefenseRace2,
3 => DefenseRace3,
4 => DefenseRace4,
5 => DefenseRace5,
6 => DefenseRace6,
8 => DefenseRace8,
_ => DefaultDefense
};
return attackType switch
{
AttackType.Melee => (int)Math.Floor(2 * armorMod + raceInfo[0] + Math.Floor((armorMod + 5) * 0.08) + (calcLevel - 1) * ((modifier * 10 + (raceInfo[3] - 5 * modifier)) / 100.0) +
additional),
AttackType.Ranged => (int)Math.Floor(2 * armorMod + raceInfo[1] + Math.Floor((armorMod + 5) * 0.36) + (calcLevel - 1) * ((modifier * 10 + (raceInfo[4] - 5 * modifier)) / 100.0) +
additional),
AttackType.Magical => (int)Math.Floor(2 * armorMod + raceInfo[2] + Math.Floor((armorMod + 5) * 0.04) + (calcLevel - 1) * ((modifier * 10 + (raceInfo[5] - 5 * modifier)) / 100.0) +
additional)
};
}
public byte GetSpeed(ClassType classType)
{
byte speed = classType switch
{
ClassType.Adventurer => 11,
ClassType.Swordman => 11,
ClassType.Archer => 12,
ClassType.Magician => 10,
ClassType.Wrestler => 11,
_ => 0
};
return speed;
}
private static double Mod(int a, int modulo)
{
if (a >= 0)
{
return a % modulo;
}
return Math.Abs((a - 2) % modulo);
}
}
}