PkmnLib.NET/Plugins/PkmnLib.Plugin.Gen7/Libraries/Gen7BattleStatCalculator.cs

123 lines
5.1 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);
var targetEvasion = target.StatBoost.Evasion;
var userAccuracy = executingMove.User.StatBoost.Accuracy;
var difference = targetEvasion - userAccuracy;
var statModifier = difference switch
{
> 0 => 3.0f / (3.0f + difference),
< 0 => 3.0f + Math.Abs(difference) / 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"),
};
}
}