724 lines
No EOL
24 KiB
C#
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);
|
|
}
|
|
}
|
|
} |