170 lines
6.3 KiB
Rust
Executable File
170 lines
6.3 KiB
Rust
Executable File
use anyhow::{bail, Result};
|
|
use std::fmt::Debug;
|
|
|
|
use crate::dynamic_data::Pokemon;
|
|
use crate::static_data::Statistic;
|
|
use crate::static_data::StatisticSet;
|
|
use crate::{ValueIdentifiable, ValueIdentifier};
|
|
|
|
/// A battle stat calculator is used to calculate stats for a Pokemon.
|
|
pub trait BattleStatCalculator: Debug + ValueIdentifiable {
|
|
/// Calculate all the flat stats of a Pokemon, disregarding stat boosts.
|
|
fn calculate_flat_stats(&self, pokemon: &Pokemon, stats: &StatisticSet<u32>) -> Result<()>;
|
|
/// Calculate a single flat stat of a Pokemon, disregarding stat boost
|
|
fn calculate_flat_stat(&self, pokemon: &Pokemon, stat: Statistic) -> Result<u32>;
|
|
/// Calculate all the boosted stats of a Pokemon, including stat boosts.
|
|
fn calculate_boosted_stats(&self, pokemon: &Pokemon, stats: &StatisticSet<u32>) -> Result<()>;
|
|
/// Calculate a single boosted stat of a Pokemon, including stat boosts.
|
|
fn calculate_boosted_stat(&self, pokemon: &Pokemon, stat: Statistic) -> Result<u32>;
|
|
}
|
|
|
|
/// A basic implementation of the Gen 7 stat calculator.
|
|
#[derive(Debug)]
|
|
pub struct Gen7BattleStatCalculator {
|
|
/// A unique identifier so we know what value this is.
|
|
identifier: ValueIdentifier,
|
|
}
|
|
|
|
impl Default for Gen7BattleStatCalculator {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
impl Gen7BattleStatCalculator {
|
|
/// Creates a new Gen 7 battle stat calculator
|
|
pub fn new() -> Self {
|
|
Self {
|
|
identifier: Default::default(),
|
|
}
|
|
}
|
|
|
|
/// The calculation used for health points.
|
|
fn calculate_health_stat(&self, pokemon: &Pokemon) -> Result<u32> {
|
|
let base = pokemon.form().get_base_stat(Statistic::HP) as u32;
|
|
let iv = pokemon.individual_values().hp() as u32;
|
|
let ev = pokemon.effort_values().hp() as u32;
|
|
let level = pokemon.level() as u32;
|
|
Ok((((2 * base + iv + (ev / 4)) * level) / 100) + level + 10)
|
|
}
|
|
|
|
/// The calculation used for all other stats
|
|
fn calculate_other_stat(&self, pokemon: &Pokemon, stat: Statistic) -> Result<u32> {
|
|
let base = pokemon.form().get_base_stat(stat) as u32;
|
|
let iv = pokemon.individual_values().get_stat(stat) as u32;
|
|
let ev = pokemon.effort_values().get_stat(stat) as u32;
|
|
let level = pokemon.level() as u32;
|
|
let unmodified = (((2 * base + iv + (ev / 4)) * level) / 100) + 5;
|
|
Ok((unmodified as f32 * pokemon.nature().get_stat_modifier(stat)) as u32)
|
|
}
|
|
|
|
/// This functions returns the modifier we need to do to a stat for a given stat boost.
|
|
fn get_stat_boost_modifier(&self, pokemon: &Pokemon, stat: Statistic) -> Result<f32> {
|
|
let boost = pokemon.stat_boost(stat);
|
|
Ok(match boost {
|
|
-6 => 2.0 / 8.0,
|
|
-5 => 2.0 / 7.0,
|
|
-4 => 2.0 / 6.0,
|
|
-3 => 2.0 / 5.0,
|
|
-2 => 2.0 / 4.0,
|
|
-1 => 2.0 / 3.0,
|
|
0 => 1.0,
|
|
1 => 3.0 / 2.0,
|
|
2 => 4.0 / 2.0,
|
|
3 => 5.0 / 2.0,
|
|
4 => 6.0 / 2.0,
|
|
5 => 7.0 / 2.0,
|
|
6 => 8.0 / 2.0,
|
|
_ => bail!("Stat boost was out of expected range of -6 to 6"),
|
|
})
|
|
}
|
|
}
|
|
|
|
impl BattleStatCalculator for Gen7BattleStatCalculator {
|
|
fn calculate_flat_stats(&self, pokemon: &Pokemon, stats: &StatisticSet<u32>) -> Result<()> {
|
|
stats.set_stat(Statistic::HP, self.calculate_health_stat(pokemon)?);
|
|
stats.set_stat(
|
|
Statistic::Attack,
|
|
self.calculate_other_stat(pokemon, Statistic::Attack)?,
|
|
);
|
|
stats.set_stat(
|
|
Statistic::Defense,
|
|
self.calculate_other_stat(pokemon, Statistic::Defense)?,
|
|
);
|
|
stats.set_stat(
|
|
Statistic::SpecialAttack,
|
|
self.calculate_other_stat(pokemon, Statistic::SpecialAttack)?,
|
|
);
|
|
stats.set_stat(
|
|
Statistic::SpecialDefense,
|
|
self.calculate_other_stat(pokemon, Statistic::SpecialDefense)?,
|
|
);
|
|
stats.set_stat(Statistic::Speed, self.calculate_other_stat(pokemon, Statistic::Speed)?);
|
|
Ok(())
|
|
}
|
|
|
|
fn calculate_flat_stat(&self, pokemon: &Pokemon, stat: Statistic) -> Result<u32> {
|
|
if stat == Statistic::HP {
|
|
self.calculate_health_stat(pokemon)
|
|
} else {
|
|
self.calculate_other_stat(pokemon, stat)
|
|
}
|
|
}
|
|
|
|
fn calculate_boosted_stats(&self, pokemon: &Pokemon, stats: &StatisticSet<u32>) -> Result<()> {
|
|
stats.set_stat(Statistic::HP, self.calculate_boosted_stat(pokemon, Statistic::HP)?);
|
|
stats.set_stat(
|
|
Statistic::Attack,
|
|
self.calculate_boosted_stat(pokemon, Statistic::Attack)?,
|
|
);
|
|
stats.set_stat(
|
|
Statistic::Defense,
|
|
self.calculate_boosted_stat(pokemon, Statistic::Defense)?,
|
|
);
|
|
stats.set_stat(
|
|
Statistic::SpecialAttack,
|
|
self.calculate_boosted_stat(pokemon, Statistic::SpecialAttack)?,
|
|
);
|
|
stats.set_stat(
|
|
Statistic::SpecialDefense,
|
|
self.calculate_boosted_stat(pokemon, Statistic::SpecialDefense)?,
|
|
);
|
|
stats.set_stat(
|
|
Statistic::Speed,
|
|
self.calculate_boosted_stat(pokemon, Statistic::Speed)?,
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
fn calculate_boosted_stat(&self, pokemon: &Pokemon, stat: Statistic) -> Result<u32> {
|
|
Ok((self.calculate_flat_stat(pokemon, stat)? as f32 * self.get_stat_boost_modifier(pokemon, stat)?) as u32)
|
|
}
|
|
}
|
|
|
|
impl ValueIdentifiable for Gen7BattleStatCalculator {
|
|
fn value_identifier(&self) -> ValueIdentifier {
|
|
self.identifier
|
|
}
|
|
}
|
|
#[cfg(test)]
|
|
#[allow(clippy::indexing_slicing)]
|
|
pub mod tests {
|
|
use super::*;
|
|
|
|
mockall::mock! {
|
|
#[derive(Debug)]
|
|
pub BattleStatCalculator{}
|
|
impl BattleStatCalculator for BattleStatCalculator {
|
|
fn calculate_flat_stats(&self, pokemon: &Pokemon, stats: &StatisticSet<u32>) -> Result<()>;
|
|
fn calculate_flat_stat(&self, pokemon: &Pokemon, stat: Statistic) -> Result<u32>;
|
|
fn calculate_boosted_stats(&self, pokemon: &Pokemon, stats: &StatisticSet<u32>) -> Result<()>;
|
|
fn calculate_boosted_stat(&self, pokemon: &Pokemon, stat: Statistic) -> Result<u32>;
|
|
}
|
|
impl ValueIdentifiable for BattleStatCalculator {
|
|
fn value_identifier(&self) -> ValueIdentifier{
|
|
ValueIdentifier::new(0)
|
|
}
|
|
}
|
|
}
|
|
}
|