PkmnLib_rs/src/dynamic_data/models/battle_random.rs

123 lines
3.7 KiB
Rust
Executable File

use anyhow::Result;
use anyhow_ext::anyhow;
use std::fmt::{Debug, Formatter};
use std::sync::{Arc, Mutex};
use crate::dynamic_data::models::executing_move::ExecutingMove;
use crate::dynamic_data::models::pokemon::Pokemon;
use crate::dynamic_data::script_handling::ScriptSource;
use crate::utils::Random;
use crate::{script_hook, ValueIdentifiable, ValueIdentifier};
/// The RNG for a battle.
#[derive(Default)]
pub struct BattleRandom {
/// A unique identifier so we know what value this is.
identifier: ValueIdentifier,
/// The actual underlying RNG. This is in a mutex, so it is thread safe, and can be ran
/// predictably, with guaranteed the same outputs.
random: Mutex<Random>,
}
impl BattleRandom {
/// Initializes a new RNG with a given seed.
pub fn new_with_seed(seed: u128) -> Self {
BattleRandom {
identifier: Default::default(),
random: Mutex::new(Random::new(seed)),
}
}
/// Returns the underlying random number generator.
pub fn get_rng(&self) -> &Mutex<Random> {
&self.random
}
/// Get a random 32 bit integer. Can be any value between min int and max int.
pub fn get(&self) -> Result<i32> {
match self.get_rng().lock() {
Ok(mut l) => Ok(l.get()),
Err(_) => Err(anyhow!("Failed to get a RNG lock")),
}
}
/// Get a random 32 bit integer between 0 and max.
pub fn get_max(&self, max: i32) -> Result<i32> {
match self.get_rng().lock() {
Ok(mut l) => Ok(l.get_max(max)),
Err(_) => Err(anyhow!("Failed to get a RNG lock")),
}
}
/// Get a random 32 bit integer between min and max.
pub fn get_between(&self, min: i32, max: i32) -> Result<i32> {
match self.get_rng().lock() {
Ok(mut l) => Ok(l.get_between(min, max)),
Err(_) => Err(anyhow!("Failed to get a RNG lock")),
}
}
/// Gets whether or not a move triggers its secondary effect. This takes its chance, and
/// rolls whether it triggers. As a side effect this run scripts to allow modifying this random
/// chance.
pub fn effect_chance(
&self,
mut chance: f32,
executing_move: &ExecutingMove,
target: &Arc<Pokemon>,
hit_number: u8,
) -> Result<bool> {
script_hook!(
change_effect_chance,
executing_move,
executing_move,
target,
hit_number,
&mut chance
);
script_hook!(
change_incoming_effect_chance,
target,
executing_move,
target,
hit_number,
&mut chance
);
if chance < 100.0 {
if chance > 0.0 {
match self.get_rng().lock() {
Ok(mut l) => Ok(l.get_float() < (chance / 100.0)),
Err(_) => Err(anyhow!("Failed to get a RNG lock")),
}
} else {
Ok(false)
}
} else {
Ok(true)
}
}
}
impl Debug for BattleRandom {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.debug_struct("BattleRandom").finish()
}
}
impl Clone for BattleRandom {
fn clone(&self) -> Self {
Self {
identifier: Default::default(),
// As cloning when we can't get a lock on the randomness is completely impossible, we
// should unwrap here.
#[allow(clippy::unwrap_used)]
random: Mutex::new(self.random.lock().unwrap().clone()),
}
}
}
impl ValueIdentifiable for BattleRandom {
fn value_identifier(&self) -> ValueIdentifier {
self.identifier
}
}