128 lines
5.5 KiB
C#
128 lines
5.5 KiB
C#
using System;
|
|
using PkmnLib.Dynamic.Libraries;
|
|
using PkmnLib.Static;
|
|
|
|
namespace PkmnLib.Plugin.Gen7.Libraries;
|
|
|
|
public class Gen7BattleStatCalculator : IBattleStatCalculator
|
|
{
|
|
/// <inheritdoc />
|
|
public void CalculateFlatStats(IPokemon pokemon, StatisticSet<uint> stats)
|
|
{
|
|
stats.SetStatistic(Statistic.Hp, CalculateHealthStat(pokemon));
|
|
stats.SetStatistic(Statistic.Attack, CalculateNormalStat(pokemon, Statistic.Attack));
|
|
stats.SetStatistic(Statistic.Defense, CalculateNormalStat(pokemon, Statistic.Defense));
|
|
stats.SetStatistic(Statistic.SpecialAttack, CalculateNormalStat(pokemon, Statistic.SpecialAttack));
|
|
stats.SetStatistic(Statistic.SpecialDefense, CalculateNormalStat(pokemon, Statistic.SpecialDefense));
|
|
stats.SetStatistic(Statistic.Speed, CalculateNormalStat(pokemon, Statistic.Speed));
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public uint CalculateFlatStat(IPokemon pokemon, Statistic stat)
|
|
{
|
|
return stat switch
|
|
{
|
|
Statistic.Hp => CalculateHealthStat(pokemon),
|
|
_ => CalculateNormalStat(pokemon, stat),
|
|
};
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void CalculateBoostedStats(IPokemon pokemon, StatisticSet<uint> stats)
|
|
{
|
|
stats.SetStatistic(Statistic.Hp, CalculateBoostedStat(pokemon, Statistic.Hp));
|
|
stats.SetStatistic(Statistic.Attack, CalculateBoostedStat(pokemon, Statistic.Attack));
|
|
stats.SetStatistic(Statistic.Defense, CalculateBoostedStat(pokemon, Statistic.Defense));
|
|
stats.SetStatistic(Statistic.SpecialAttack, CalculateBoostedStat(pokemon, Statistic.SpecialAttack));
|
|
stats.SetStatistic(Statistic.SpecialDefense, CalculateBoostedStat(pokemon, Statistic.SpecialDefense));
|
|
stats.SetStatistic(Statistic.Speed, CalculateBoostedStat(pokemon, Statistic.Speed));
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public uint CalculateBoostedStat(IPokemon pokemon, Statistic stat)
|
|
{
|
|
var flatStat = CalculateFlatStat(pokemon, stat);
|
|
var boostModifier = GetStatBoostModifier(pokemon, stat);
|
|
var boostedStat = flatStat * boostModifier;
|
|
if (boostedStat > uint.MaxValue) boostedStat = uint.MaxValue;
|
|
return (uint)boostedStat;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public byte CalculateModifiedAccuracy(IExecutingMove executingMove, IPokemon target, byte hitIndex, byte moveAccuracy)
|
|
{
|
|
var accuracyModifier = 1.0f;
|
|
executingMove.RunScriptHook(x => x.ChangeAccuracyModifier(executingMove, target, hitIndex, ref accuracyModifier));
|
|
var modifiedAccuracy = (int)(moveAccuracy * accuracyModifier);
|
|
executingMove.RunScriptHook(x => x.ChangeAccuracy(executingMove, target, hitIndex, ref modifiedAccuracy));
|
|
var targetEvasion = target.StatBoost.Evasion;
|
|
var ignoreEvasion = false;
|
|
executingMove.RunScriptHook(x => x.BypassEvasionStatBoosts(executingMove, target, hitIndex, ref ignoreEvasion));
|
|
if (ignoreEvasion)
|
|
targetEvasion = 0;
|
|
var userAccuracy = executingMove.User.StatBoost.Accuracy;
|
|
var difference = targetEvasion - userAccuracy;
|
|
var statModifier = difference switch
|
|
{
|
|
> 0 => 3.0f / (3.0f + Math.Min(difference, 6)),
|
|
< 0 => 3.0f + -Math.Max(difference, -6) / 3.0f,
|
|
_ => 1.0f
|
|
};
|
|
modifiedAccuracy = (int)(modifiedAccuracy * statModifier);
|
|
modifiedAccuracy = modifiedAccuracy switch
|
|
{
|
|
> 255 => 255,
|
|
< 0 => 0,
|
|
_ => modifiedAccuracy
|
|
};
|
|
// NOTE: the main games also consider friendship here, but we don't yet have the concept of a "player Pokémon"
|
|
// in the battle system, so for now we're just ignoring that.
|
|
return (byte)modifiedAccuracy;
|
|
}
|
|
|
|
private static uint CalculateHealthStat(IPokemon pokemon)
|
|
{
|
|
var baseValue = (ulong)pokemon.Form.BaseStats.Hp;
|
|
var iv = (ulong)pokemon.IndividualValues.Hp;
|
|
var ev = (ulong)pokemon.EffortValues.Hp;
|
|
var level = (ulong)pokemon.Level;
|
|
var health = (((2 * baseValue + iv + (ev / 4)) * level) / 100) + level + 10;
|
|
if (health > uint.MaxValue) health = uint.MaxValue;
|
|
return (uint)health;
|
|
}
|
|
|
|
private static uint CalculateNormalStat(IPokemon pokemon, Statistic statistic)
|
|
{
|
|
var baseValue = (ulong)pokemon.Form.BaseStats.GetStatistic(statistic);
|
|
var iv = (ulong)pokemon.IndividualValues.GetStatistic(statistic);
|
|
var ev = (ulong)pokemon.EffortValues.GetStatistic(statistic);
|
|
var level = (ulong)pokemon.Level;
|
|
var unmodified = (((2 * baseValue + iv + (ev / 4)) * level) / 100) + 5;
|
|
var natureModifier = pokemon.Nature.GetStatModifier(statistic);
|
|
var modified = (unmodified * natureModifier);
|
|
if (modified > uint.MaxValue) modified = uint.MaxValue;
|
|
return (uint)modified;
|
|
}
|
|
|
|
private static float GetStatBoostModifier(IPokemon pokemon, Statistic statistic)
|
|
{
|
|
var boost = pokemon.StatBoost.GetStatistic(statistic);
|
|
return boost switch
|
|
{
|
|
-6 => 2.0f / 8.0f,
|
|
-5 => 2.0f / 7.0f,
|
|
-4 => 2.0f / 6.0f,
|
|
-3 => 2.0f / 5.0f,
|
|
-2 => 2.0f / 4.0f,
|
|
-1 => 2.0f / 3.0f,
|
|
0 => 1.0f,
|
|
1 => 3.0f / 2.0f,
|
|
2 => 4.0f / 2.0f,
|
|
3 => 5.0f / 2.0f,
|
|
4 => 6.0f / 2.0f,
|
|
5 => 7.0f / 2.0f,
|
|
6 => 8.0f / 2.0f,
|
|
_ => throw new System.ArgumentException("Stat boost was out of expected range of -6 to 6"),
|
|
};
|
|
}
|
|
} |