Replace most panics in the core library with results
continuous-integration/drone/push Build is failing
Details
continuous-integration/drone/push Build is failing
Details
This commit is contained in:
parent
00d596d656
commit
c0e4702e45
|
@ -160,6 +160,7 @@ impl ValueIdentifiable for ChoiceQueue {
|
|||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::defines::LevelInt;
|
||||
|
|
|
@ -213,7 +213,7 @@ impl Battle {
|
|||
.library()
|
||||
.static_data()
|
||||
.types()
|
||||
.get_effectiveness(hit_type, &target.types());
|
||||
.get_effectiveness(hit_type, &target.types())?;
|
||||
script_hook!(
|
||||
change_effectiveness,
|
||||
executing_move,
|
||||
|
|
|
@ -147,6 +147,7 @@ impl ValueIdentifiable for Gen7BattleStatCalculator {
|
|||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
|
||||
|
|
|
@ -127,6 +127,7 @@ impl ValueIdentifiable for DynamicLibraryImpl {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
pub mod test {
|
||||
use super::*;
|
||||
use crate::dynamic_data::libraries::battle_stat_calculator::Gen7BattleStatCalculator;
|
||||
|
|
|
@ -101,6 +101,7 @@ impl ValueIdentifiable for LearnedMove {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::static_data::tests::MockMoveData;
|
||||
|
|
|
@ -421,7 +421,7 @@ impl Pokemon {
|
|||
}
|
||||
|
||||
let form = self.form();
|
||||
let ability = form.get_ability(self.ability_index);
|
||||
let ability = form.get_ability(self.ability_index)?;
|
||||
Ok(self
|
||||
.library
|
||||
.static_data()
|
||||
|
|
|
@ -53,7 +53,7 @@ impl PokemonBuilder {
|
|||
.species()
|
||||
.get(&self.species)
|
||||
.ok_or(PkmnError::InvalidSpeciesName { species: self.species })?;
|
||||
let form = species.get_default_form();
|
||||
let form = species.get_default_form()?;
|
||||
let p = Pokemon::new(
|
||||
self.library,
|
||||
species,
|
||||
|
|
|
@ -388,6 +388,8 @@ impl Clone for ScriptContainer {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
mod tests {
|
||||
use std::sync::atomic::{AtomicBool, AtomicPtr};
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::defines::LevelInt;
|
||||
use crate::ffi::{BorrowedPtr, ExternPointer, OwnedPtr};
|
||||
use crate::ffi::{BorrowedPtr, ExternPointer, NativeResult, OwnedPtr};
|
||||
use crate::static_data::{LearnableMoves, LearnableMovesImpl};
|
||||
use std::ffi::{c_char, CStr};
|
||||
use std::ptr::drop_in_place;
|
||||
|
@ -22,6 +22,8 @@ unsafe extern "C" fn learnable_moves_add_level_move(
|
|||
mut ptr: ExternPointer<Box<dyn LearnableMoves>>,
|
||||
level: LevelInt,
|
||||
move_name: BorrowedPtr<c_char>,
|
||||
) {
|
||||
ptr.as_mut().add_level_move(level, &CStr::from_ptr(move_name).into())
|
||||
) -> NativeResult<()> {
|
||||
ptr.as_mut()
|
||||
.add_level_move(level, &CStr::from_ptr(move_name).into())
|
||||
.into()
|
||||
}
|
||||
|
|
|
@ -23,9 +23,10 @@ unsafe extern "C" fn growth_rate_library_calculate_level(
|
|||
ptr: ExternPointer<Box<dyn GrowthRateLibrary>>,
|
||||
growth_rate: BorrowedPtr<c_char>,
|
||||
experience: u32,
|
||||
) -> LevelInt {
|
||||
) -> NativeResult<LevelInt> {
|
||||
ptr.as_ref()
|
||||
.calculate_level(&CStr::from_ptr(growth_rate).into(), experience)
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Calculates the experience for a given growth key name and a certain level.
|
||||
|
|
|
@ -54,9 +54,12 @@ unsafe extern "C" fn nature_library_get_nature(
|
|||
unsafe extern "C" fn nature_library_get_random_nature(
|
||||
ptr: ExternPointer<Box<dyn NatureLibrary>>,
|
||||
seed: u64,
|
||||
) -> IdentifiablePointer<Arc<dyn Nature>> {
|
||||
) -> NativeResult<IdentifiablePointer<Arc<dyn Nature>>> {
|
||||
let mut rand = Random::new(seed as u128);
|
||||
ptr.as_ref().get_random_nature(&mut rand).into()
|
||||
match ptr.as_ref().get_random_nature(&mut rand) {
|
||||
Ok(nature) => NativeResult::ok(nature.into()),
|
||||
Err(e) => NativeResult::err(e),
|
||||
}
|
||||
}
|
||||
|
||||
/// Finds a nature name by nature.
|
||||
|
@ -68,9 +71,12 @@ unsafe extern "C" fn nature_library_get_nature_name(
|
|||
match nature.as_ref() {
|
||||
Some(nature) => {
|
||||
let name = ptr.as_ref().get_nature_name(nature);
|
||||
match CString::new(name.str()) {
|
||||
Ok(cstr) => NativeResult::ok(cstr.into_raw()),
|
||||
Err(_) => NativeResult::err(anyhow!("Failed to convert nature name to C string")),
|
||||
match name {
|
||||
Ok(name) => match CString::new(name.str()) {
|
||||
Ok(cstr) => NativeResult::ok(cstr.into_raw()),
|
||||
Err(_) => NativeResult::err(anyhow!("Failed to convert nature name to C string")),
|
||||
},
|
||||
Err(e) => NativeResult::err(e),
|
||||
}
|
||||
}
|
||||
None => NativeResult::err(PkmnError::NullReference.into()),
|
||||
|
|
|
@ -58,8 +58,8 @@ extern "C" fn type_library_get_single_effectiveness(
|
|||
ptr: ExternPointer<Box<dyn TypeLibrary>>,
|
||||
attacking: TypeIdentifier,
|
||||
defending: TypeIdentifier,
|
||||
) -> f32 {
|
||||
ptr.as_ref().get_single_effectiveness(attacking, defending)
|
||||
) -> NativeResult<f32> {
|
||||
ptr.as_ref().get_single_effectiveness(attacking, defending).into()
|
||||
}
|
||||
|
||||
/// Gets the effectiveness for a single attacking type against an amount of defending types.
|
||||
|
@ -71,9 +71,9 @@ unsafe extern "C" fn type_library_get_effectiveness(
|
|||
attacking: TypeIdentifier,
|
||||
defending: OwnedPtr<TypeIdentifier>,
|
||||
defending_length: usize,
|
||||
) -> f32 {
|
||||
) -> NativeResult<f32> {
|
||||
let v = std::slice::from_raw_parts(defending, defending_length);
|
||||
ptr.as_ref().get_effectiveness(attacking, v)
|
||||
ptr.as_ref().get_effectiveness(attacking, v).into()
|
||||
}
|
||||
|
||||
/// Registers a new type in the library.
|
||||
|
@ -92,6 +92,8 @@ unsafe extern "C" fn type_library_set_effectiveness(
|
|||
attacking: TypeIdentifier,
|
||||
defending: TypeIdentifier,
|
||||
effectiveness: f32,
|
||||
) {
|
||||
ptr.as_mut().set_effectiveness(attacking, defending, effectiveness);
|
||||
) -> NativeResult<()> {
|
||||
ptr.as_mut()
|
||||
.set_effectiveness(attacking, defending, effectiveness)
|
||||
.into()
|
||||
}
|
||||
|
|
19
src/lib.rs
19
src/lib.rs
|
@ -8,12 +8,12 @@
|
|||
#![deny(clippy::missing_docs_in_private_items)]
|
||||
// Linter rules to prevent panics
|
||||
// Currently still a WIP to fix all of these
|
||||
// #![deny(clippy::unwrap_used)]
|
||||
// #![deny(clippy::expect_used)]
|
||||
// #![deny(clippy::indexing_slicing)]
|
||||
// #![deny(clippy::string_slice)]
|
||||
// #![deny(clippy::exit)]
|
||||
// #![deny(clippy::panic)]
|
||||
#![deny(clippy::unwrap_used)]
|
||||
#![deny(clippy::expect_used)]
|
||||
#![deny(clippy::indexing_slicing)]
|
||||
#![deny(clippy::string_slice)]
|
||||
#![deny(clippy::exit)]
|
||||
#![deny(clippy::panic)]
|
||||
// Features
|
||||
#![feature(test)]
|
||||
#![feature(const_option)]
|
||||
|
@ -58,6 +58,7 @@ pub mod static_data;
|
|||
/// The utils module includes misc utils that are used within PkmnLib
|
||||
pub mod utils;
|
||||
|
||||
use crate::static_data::TypeIdentifier;
|
||||
use thiserror::Error;
|
||||
|
||||
/// The PkmnError enum is the error type for PkmnLib. It is used to return common errors from functions
|
||||
|
@ -103,6 +104,12 @@ pub enum PkmnError {
|
|||
/// The species that was requested
|
||||
species: StringKey,
|
||||
},
|
||||
/// Unable to get type
|
||||
#[error("Unable to get type {type_id}")]
|
||||
InvalidTypeIdentifier {
|
||||
/// The type that was requested
|
||||
type_id: TypeIdentifier,
|
||||
},
|
||||
/// The given pointer is not a valid CString
|
||||
#[error("The given pointer is not a valid CString")]
|
||||
InvalidCString,
|
||||
|
|
|
@ -29,7 +29,7 @@ register! {
|
|||
defending: u8
|
||||
) -> f32 {
|
||||
let lib = lib.value_func_arc(&env).unwrap();
|
||||
lib.get_single_effectiveness(attacking.into(), defending.into())
|
||||
lib.get_single_effectiveness(attacking.into(), defending.into()).unwrap()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -49,6 +49,7 @@ impl GrowthRate for LookupGrowthRate {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
|
||||
|
|
|
@ -135,6 +135,7 @@ impl ValueIdentifiable for ItemImpl {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
|
||||
|
|
|
@ -47,6 +47,8 @@ impl ValueIdentifiable for AbilityLibraryImpl {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
pub mod tests {
|
||||
use crate::static_data::libraries::ability_library::AbilityLibraryImpl;
|
||||
use crate::static_data::AbilityImpl;
|
||||
|
|
|
@ -11,7 +11,7 @@ use crate::{StringKey, ValueIdentifiable, ValueIdentifier};
|
|||
/// A library to store all growth rates.
|
||||
pub trait GrowthRateLibrary: Debug + ValueIdentifiable {
|
||||
/// Calculates the level for a given growth key name and a certain experience.
|
||||
fn calculate_level(&self, growth_rate: &StringKey, experience: u32) -> LevelInt;
|
||||
fn calculate_level(&self, growth_rate: &StringKey, experience: u32) -> Result<LevelInt>;
|
||||
/// Calculates the experience for a given growth key name and a certain level.
|
||||
fn calculate_experience(&self, growth_rate: &StringKey, level: LevelInt) -> Result<u32>;
|
||||
/// Adds a new growth rate with a name and value.
|
||||
|
@ -38,12 +38,19 @@ impl GrowthRateLibraryImpl {
|
|||
|
||||
impl GrowthRateLibrary for GrowthRateLibraryImpl {
|
||||
/// Calculates the level for a given growth key name and a certain experience.
|
||||
fn calculate_level(&self, growth_rate: &StringKey, experience: u32) -> LevelInt {
|
||||
self.growth_rates[growth_rate].calculate_level(experience)
|
||||
fn calculate_level(&self, growth_rate: &StringKey, experience: u32) -> Result<LevelInt> {
|
||||
Ok(self
|
||||
.growth_rates
|
||||
.get(growth_rate)
|
||||
.ok_or_else(|| anyhow::anyhow!("No growth rate found with key {}", growth_rate.to_string()))?
|
||||
.calculate_level(experience))
|
||||
}
|
||||
/// Calculates the experience for a given growth key name and a certain level.
|
||||
fn calculate_experience(&self, growth_rate: &StringKey, level: LevelInt) -> Result<u32> {
|
||||
self.growth_rates[growth_rate].calculate_experience(level)
|
||||
self.growth_rates
|
||||
.get(growth_rate)
|
||||
.ok_or_else(|| anyhow::anyhow!("No growth rate found with key {}", growth_rate.to_string()))?
|
||||
.calculate_experience(level)
|
||||
}
|
||||
|
||||
/// Adds a new growth rate with a name and value.
|
||||
|
@ -65,6 +72,8 @@ impl Debug for GrowthRateLibraryImpl {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
use crate::static_data::growth_rates::LookupGrowthRate;
|
||||
|
@ -89,7 +98,7 @@ pub mod tests {
|
|||
#[derive(Debug)]
|
||||
pub GrowthRateLibrary{}
|
||||
impl GrowthRateLibrary for GrowthRateLibrary {
|
||||
fn calculate_level(&self, growth_rate: &StringKey, experience: u32) -> LevelInt;
|
||||
fn calculate_level(&self, growth_rate: &StringKey, experience: u32) -> Result<LevelInt>;
|
||||
fn calculate_experience(&self, growth_rate: &StringKey, level: LevelInt) -> Result<u32>;
|
||||
fn add_growth_rate(&mut self, key: &StringKey, value: Box<dyn GrowthRate>);
|
||||
}
|
||||
|
@ -103,8 +112,8 @@ pub mod tests {
|
|||
#[test]
|
||||
fn add_growth_rate_to_library_and_calculate_level() {
|
||||
let lib = build();
|
||||
assert_eq!(lib.calculate_level(&"test_growthrate".into(), 3), 1);
|
||||
assert_eq!(lib.calculate_level(&"test_growthrate".into(), 50), 3);
|
||||
assert_eq!(lib.calculate_level(&"test_growthrate".into(), 3).unwrap(), 1);
|
||||
assert_eq!(lib.calculate_level(&"test_growthrate".into(), 50).unwrap(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
use crate::static_data::Nature;
|
||||
use crate::{Random, StringKey, ValueIdentifiable, ValueIdentifier};
|
||||
use anyhow::bail;
|
||||
use anyhow_ext::{ensure, Result};
|
||||
use indexmap::IndexMap;
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
|
@ -11,9 +13,9 @@ pub trait NatureLibrary: Debug + ValueIdentifiable {
|
|||
/// Gets a nature by name.
|
||||
fn get_nature(&self, key: &StringKey) -> Option<Arc<dyn Nature>>;
|
||||
/// Gets a random nature.
|
||||
fn get_random_nature(&self, rand: &mut Random) -> Arc<dyn Nature>;
|
||||
fn get_random_nature(&self, rand: &mut Random) -> Result<Arc<dyn Nature>>;
|
||||
/// Finds a nature name by nature.
|
||||
fn get_nature_name(&self, nature: &Arc<dyn Nature>) -> StringKey;
|
||||
fn get_nature_name(&self, nature: &Arc<dyn Nature>) -> Result<StringKey>;
|
||||
}
|
||||
|
||||
/// A library of all natures that can be used, stored by their names.
|
||||
|
@ -46,21 +48,28 @@ impl NatureLibrary for NatureLibraryImpl {
|
|||
self.map.get(key).cloned()
|
||||
}
|
||||
|
||||
fn get_random_nature(&self, rand: &mut Random) -> Arc<dyn Nature> {
|
||||
fn get_random_nature(&self, rand: &mut Random) -> Result<Arc<dyn Nature>> {
|
||||
ensure!(!self.map.is_empty(), "No natures were loaded into the library.");
|
||||
let i = rand.get_between(0, self.map.len() as i32);
|
||||
return self.map.get_index(i as usize).unwrap().1.clone();
|
||||
Ok(self
|
||||
.map
|
||||
.get_index(i as usize)
|
||||
// this should never happen, but we'll check anyway.
|
||||
.ok_or(anyhow_ext::anyhow!("Failed to get a random nature from the library."))?
|
||||
.1
|
||||
.clone())
|
||||
}
|
||||
|
||||
/// Finds a nature name by nature.
|
||||
fn get_nature_name(&self, nature: &Arc<dyn Nature>) -> StringKey {
|
||||
fn get_nature_name(&self, nature: &Arc<dyn Nature>) -> Result<StringKey> {
|
||||
for kv in &self.map {
|
||||
// As natures can't be copied, and should always be the same reference as the value
|
||||
// in the map, we just compare by reference.
|
||||
if kv.1.value_identifier() == nature.value_identifier() {
|
||||
return kv.0.clone();
|
||||
return Ok(kv.0.clone());
|
||||
}
|
||||
}
|
||||
panic!("No name was found for the given nature. This should never happen.");
|
||||
bail!("No name was found for the given nature. The nature may not be in the library.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,6 +80,9 @@ impl ValueIdentifiable for NatureLibraryImpl {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
#[allow(clippy::expect_used)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
use crate::static_data::statistics::Statistic;
|
||||
|
@ -93,8 +105,8 @@ pub mod tests {
|
|||
impl NatureLibrary for NatureLibrary {
|
||||
fn load_nature(&mut self, name: StringKey, nature: Arc<dyn Nature>);
|
||||
fn get_nature(&self, key: &StringKey) -> Option<Arc<dyn Nature>>;
|
||||
fn get_nature_name(&self, nature: &Arc<dyn Nature>) -> StringKey;
|
||||
fn get_random_nature(&self, rand: &mut Random) -> Arc<dyn Nature>;
|
||||
fn get_nature_name(&self, nature: &Arc<dyn Nature>) -> Result<StringKey>;
|
||||
fn get_random_nature(&self, rand: &mut Random) -> Result<Arc<dyn Nature>>;
|
||||
}
|
||||
impl ValueIdentifiable for NatureLibrary {
|
||||
fn value_identifier(&self) -> ValueIdentifier{
|
||||
|
@ -134,10 +146,10 @@ pub mod tests {
|
|||
);
|
||||
|
||||
let n1 = lib.get_nature(&"foo".into()).expect("Nature was not found");
|
||||
let name = lib.get_nature_name(&n1);
|
||||
let name = lib.get_nature_name(&n1).unwrap();
|
||||
assert_eq!(name, "foo".into());
|
||||
let n2 = lib.get_nature(&"bar".into()).expect("Nature was not found");
|
||||
let name2 = lib.get_nature_name(&n2);
|
||||
let name2 = lib.get_nature_name(&n2).unwrap();
|
||||
assert_eq!(name2, "bar".into());
|
||||
}
|
||||
|
||||
|
@ -148,7 +160,7 @@ pub mod tests {
|
|||
"foo".into(),
|
||||
NatureImpl::new(Statistic::HP, Statistic::Attack, 1.1, 0.9),
|
||||
);
|
||||
let n1 = lib.get_random_nature(&mut Random::new(0));
|
||||
let n1 = lib.get_random_nature(&mut Random::new(0)).unwrap();
|
||||
assert_eq!(n1.increased_stat(), Statistic::HP);
|
||||
assert_eq!(n1.decreased_stat(), Statistic::Attack);
|
||||
assert_eq!(n1.get_stat_modifier(n1.increased_stat()), 1.1);
|
||||
|
|
|
@ -47,6 +47,8 @@ impl ValueIdentifiable for SpeciesLibraryImpl {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
use crate::static_data::{FormImpl, LearnableMovesImpl, SpeciesImpl, StaticStatisticSet};
|
||||
|
|
|
@ -164,6 +164,8 @@ impl ValueIdentifiable for StaticDataImpl {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
pub mod test {
|
||||
use super::*;
|
||||
use crate::static_data::LibrarySettingsImpl;
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use anyhow_ext::Result;
|
||||
use atomig::Atom;
|
||||
use hashbrown::HashMap;
|
||||
use std::fmt::Debug;
|
||||
use std::fmt::{Debug, Display};
|
||||
|
||||
use crate::{StringKey, ValueIdentifiable, ValueIdentifier};
|
||||
use crate::{PkmnError, StringKey, ValueIdentifiable, ValueIdentifier};
|
||||
|
||||
/// A unique key that can be used to store a reference to a type. Opaque reference to a byte
|
||||
/// internally.
|
||||
|
@ -25,6 +26,12 @@ impl From<TypeIdentifier> for u8 {
|
|||
}
|
||||
}
|
||||
|
||||
impl Display for TypeIdentifier {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "TypeId({})", self.val)
|
||||
}
|
||||
}
|
||||
|
||||
/// All data related to types and effectiveness.
|
||||
pub trait TypeLibrary: Debug + ValueIdentifiable {
|
||||
/// Gets the type identifier for a type with a name.
|
||||
|
@ -34,18 +41,23 @@ pub trait TypeLibrary: Debug + ValueIdentifiable {
|
|||
fn get_type_name(&self, t: TypeIdentifier) -> Option<StringKey>;
|
||||
|
||||
/// Gets the effectiveness for a single attacking type against a single defending type.
|
||||
fn get_single_effectiveness(&self, attacking: TypeIdentifier, defending: TypeIdentifier) -> f32;
|
||||
fn get_single_effectiveness(&self, attacking: TypeIdentifier, defending: TypeIdentifier) -> Result<f32>;
|
||||
|
||||
/// Gets the effectiveness for a single attacking type against an amount of defending types.
|
||||
/// This is equivalent to running [`get_single_effectiveness`] on each defending type, and
|
||||
/// multiplying the results with each other.
|
||||
fn get_effectiveness(&self, attacking: TypeIdentifier, defending: &[TypeIdentifier]) -> f32;
|
||||
fn get_effectiveness(&self, attacking: TypeIdentifier, defending: &[TypeIdentifier]) -> Result<f32>;
|
||||
|
||||
/// Registers a new type in the library.
|
||||
fn register_type(&mut self, name: &StringKey) -> TypeIdentifier;
|
||||
|
||||
/// Sets the effectiveness for an attacking type against a defending type.
|
||||
fn set_effectiveness(&mut self, attacking: TypeIdentifier, defending: TypeIdentifier, effectiveness: f32);
|
||||
fn set_effectiveness(
|
||||
&mut self,
|
||||
attacking: TypeIdentifier,
|
||||
defending: TypeIdentifier,
|
||||
effectiveness: f32,
|
||||
) -> Result<()>;
|
||||
}
|
||||
|
||||
/// All data related to types and effectiveness.
|
||||
|
@ -87,19 +99,24 @@ impl TypeLibrary for TypeLibraryImpl {
|
|||
}
|
||||
|
||||
/// Gets the effectiveness for a single attacking type against a single defending type.
|
||||
fn get_single_effectiveness(&self, attacking: TypeIdentifier, defending: TypeIdentifier) -> f32 {
|
||||
self.effectiveness[(attacking.val - 1) as usize][(defending.val - 1) as usize]
|
||||
fn get_single_effectiveness(&self, attacking: TypeIdentifier, defending: TypeIdentifier) -> Result<f32> {
|
||||
Ok(*self
|
||||
.effectiveness
|
||||
.get((attacking.val - 1) as usize)
|
||||
.ok_or(PkmnError::InvalidTypeIdentifier { type_id: attacking })?
|
||||
.get((defending.val - 1) as usize)
|
||||
.ok_or(PkmnError::InvalidTypeIdentifier { type_id: defending })?)
|
||||
}
|
||||
|
||||
/// Gets the effectiveness for a single attacking type against an amount of defending types.
|
||||
/// This is equivalent to running [`get_single_effectiveness`] on each defending type, and
|
||||
/// multiplying the results with each other.
|
||||
fn get_effectiveness(&self, attacking: TypeIdentifier, defending: &[TypeIdentifier]) -> f32 {
|
||||
fn get_effectiveness(&self, attacking: TypeIdentifier, defending: &[TypeIdentifier]) -> Result<f32> {
|
||||
let mut e = 1.0;
|
||||
for def in defending {
|
||||
e *= self.get_single_effectiveness(attacking, *def);
|
||||
e *= self.get_single_effectiveness(attacking, *def)?;
|
||||
}
|
||||
e
|
||||
Ok(e)
|
||||
}
|
||||
|
||||
/// Registers a new type in the library.
|
||||
|
@ -116,8 +133,19 @@ impl TypeLibrary for TypeLibraryImpl {
|
|||
}
|
||||
|
||||
/// Sets the effectiveness for an attacking type against a defending type.
|
||||
fn set_effectiveness(&mut self, attacking: TypeIdentifier, defending: TypeIdentifier, effectiveness: f32) {
|
||||
self.effectiveness[(attacking.val - 1) as usize][(defending.val - 1) as usize] = effectiveness;
|
||||
fn set_effectiveness(
|
||||
&mut self,
|
||||
attacking: TypeIdentifier,
|
||||
defending: TypeIdentifier,
|
||||
effectiveness: f32,
|
||||
) -> Result<()> {
|
||||
*self
|
||||
.effectiveness
|
||||
.get_mut((attacking.val - 1) as usize)
|
||||
.ok_or(PkmnError::InvalidTypeIdentifier { type_id: attacking })?
|
||||
.get_mut((defending.val - 1) as usize)
|
||||
.ok_or(PkmnError::InvalidTypeIdentifier { type_id: defending })? = effectiveness;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -128,6 +156,8 @@ impl ValueIdentifiable for TypeLibraryImpl {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
pub mod tests {
|
||||
use assert_approx_eq::assert_approx_eq;
|
||||
|
||||
|
@ -143,8 +173,8 @@ pub mod tests {
|
|||
let t1 = w.register_type(&"bar".into());
|
||||
// Drops borrow as mut
|
||||
|
||||
w.set_effectiveness(t0, t1, 0.5);
|
||||
w.set_effectiveness(t1, t0, 2.0);
|
||||
w.set_effectiveness(t0, t1, 0.5).unwrap();
|
||||
w.set_effectiveness(t1, t0, 2.0).unwrap();
|
||||
|
||||
lib
|
||||
}
|
||||
|
@ -173,14 +203,14 @@ pub mod tests {
|
|||
let w = &mut lib;
|
||||
let t0 = w.register_type(&"foo".into());
|
||||
let t1 = w.register_type(&"bar".into());
|
||||
w.set_effectiveness(t0, t1, 0.5);
|
||||
w.set_effectiveness(t1, t0, 2.0);
|
||||
w.set_effectiveness(t0, t1, 0.5).unwrap();
|
||||
w.set_effectiveness(t1, t0, 2.0).unwrap();
|
||||
// Drops borrow as mut
|
||||
|
||||
// Borrow as read so we can read
|
||||
let r = &lib;
|
||||
assert_approx_eq!(r.get_single_effectiveness(t0, t1), 0.5);
|
||||
assert_approx_eq!(r.get_single_effectiveness(t1, t0), 2.0);
|
||||
assert_approx_eq!(r.get_single_effectiveness(t0, t1).unwrap(), 0.5);
|
||||
assert_approx_eq!(r.get_single_effectiveness(t1, t0).unwrap(), 2.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -191,14 +221,14 @@ pub mod tests {
|
|||
let w = &mut lib;
|
||||
let t0 = w.register_type(&"foo".into());
|
||||
let t1 = w.register_type(&"bar".into());
|
||||
w.set_effectiveness(t0, t1, 0.5);
|
||||
w.set_effectiveness(t1, t0, 2.0);
|
||||
w.set_effectiveness(t0, t1, 0.5).unwrap();
|
||||
w.set_effectiveness(t1, t0, 2.0).unwrap();
|
||||
// Drops borrow as mut
|
||||
|
||||
// Borrow as read so we can read
|
||||
let r = &lib;
|
||||
assert_approx_eq!(r.get_effectiveness(t0, &[t1, t1]), 0.25);
|
||||
assert_approx_eq!(r.get_effectiveness(t1, &[t0, t0]), 4.0);
|
||||
assert_approx_eq!(r.get_effectiveness(t0, &[t1, t1]).unwrap(), 0.25);
|
||||
assert_approx_eq!(r.get_effectiveness(t1, &[t0, t0]).unwrap(), 4.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -208,6 +208,8 @@ impl ValueIdentifiable for MoveDataImpl {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
|
||||
|
|
|
@ -59,6 +59,8 @@ impl ValueIdentifiable for SecondaryEffectImpl {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use assert_approx_eq::assert_approx_eq;
|
||||
|
|
|
@ -96,6 +96,8 @@ impl ValueIdentifiable for NatureImpl {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
|
||||
|
|
|
@ -70,6 +70,8 @@ pub struct AbilityIndex {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use anyhow_ext::{ensure, Result};
|
||||
use hashbrown::HashSet;
|
||||
use std::fmt::Debug;
|
||||
|
||||
|
@ -5,8 +6,8 @@ use crate::static_data::Statistic;
|
|||
use crate::static_data::TypeIdentifier;
|
||||
use crate::static_data::{Ability, StaticStatisticSet};
|
||||
use crate::static_data::{AbilityIndex, LearnableMoves};
|
||||
use crate::StringKey;
|
||||
use crate::{Random, ValueIdentifiable, ValueIdentifier};
|
||||
use crate::{StringKey, VecExt};
|
||||
|
||||
/// A form is a variant of a specific species. A species always has at least one form, but can have
|
||||
/// many more.
|
||||
|
@ -34,7 +35,7 @@ pub trait Form: ValueIdentifiable + Debug {
|
|||
fn flags(&self) -> &HashSet<StringKey>;
|
||||
|
||||
/// Get a type of the move at a certain index.
|
||||
fn get_type(&self, index: usize) -> TypeIdentifier;
|
||||
fn get_type(&self, index: usize) -> Result<TypeIdentifier>;
|
||||
|
||||
/// Gets a single base stat value.
|
||||
fn get_base_stat(&self, stat: Statistic) -> u16;
|
||||
|
@ -42,12 +43,12 @@ pub trait Form: ValueIdentifiable + Debug {
|
|||
fn find_ability_index(&self, ability: &dyn Ability) -> Option<AbilityIndex>;
|
||||
|
||||
/// Gets an ability from the form.
|
||||
fn get_ability(&self, index: AbilityIndex) -> &StringKey;
|
||||
fn get_ability(&self, index: AbilityIndex) -> Result<&StringKey>;
|
||||
|
||||
/// Gets a random ability from the form.
|
||||
fn get_random_ability(&self, rand: &mut Random) -> &StringKey;
|
||||
fn get_random_ability(&self, rand: &mut Random) -> Result<&StringKey>;
|
||||
/// Gets a random hidden ability from the form.
|
||||
fn get_random_hidden_ability(&self, rand: &mut Random) -> &StringKey;
|
||||
fn get_random_hidden_ability(&self, rand: &mut Random) -> Result<&StringKey>;
|
||||
|
||||
/// Check if the form has a specific flag set.
|
||||
fn has_flag(&self, key: &StringKey) -> bool;
|
||||
|
@ -158,8 +159,8 @@ impl Form for FormImpl {
|
|||
}
|
||||
|
||||
/// Get a type of the move at a certain index.
|
||||
fn get_type(&self, index: usize) -> TypeIdentifier {
|
||||
self.types[index]
|
||||
fn get_type(&self, index: usize) -> Result<TypeIdentifier> {
|
||||
Ok(*self.types.get_res(index)?)
|
||||
}
|
||||
|
||||
/// Gets a single base stat value.
|
||||
|
@ -189,21 +190,25 @@ impl Form for FormImpl {
|
|||
}
|
||||
|
||||
/// Gets an ability from the form.
|
||||
fn get_ability(&self, index: AbilityIndex) -> &StringKey {
|
||||
fn get_ability(&self, index: AbilityIndex) -> Result<&StringKey> {
|
||||
if index.hidden {
|
||||
&self.hidden_abilities[index.index as usize]
|
||||
Ok(self.hidden_abilities.get_res(index.index as usize)?)
|
||||
} else {
|
||||
&self.abilities[index.index as usize]
|
||||
Ok(self.abilities.get_res(index.index as usize)?)
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets a random ability from the form.
|
||||
fn get_random_ability(&self, rand: &mut Random) -> &StringKey {
|
||||
&self.abilities[rand.get_between_unsigned(0, self.abilities.len() as u32) as usize]
|
||||
fn get_random_ability(&self, rand: &mut Random) -> Result<&StringKey> {
|
||||
ensure!(!self.abilities.is_empty(), "No abilities on form");
|
||||
self.abilities
|
||||
.get_res(rand.get_between_unsigned(0, self.abilities.len() as u32) as usize)
|
||||
}
|
||||
/// Gets a random hidden ability from the form.
|
||||
fn get_random_hidden_ability(&self, rand: &mut Random) -> &StringKey {
|
||||
&self.hidden_abilities[rand.get_between_unsigned(0, self.hidden_abilities.len() as u32) as usize]
|
||||
fn get_random_hidden_ability(&self, rand: &mut Random) -> Result<&StringKey> {
|
||||
ensure!(!self.hidden_abilities.is_empty(), "No hidden abilities on form");
|
||||
self.hidden_abilities
|
||||
.get_res(rand.get_between_unsigned(0, self.hidden_abilities.len() as u32) as usize)
|
||||
}
|
||||
|
||||
/// Check if the form has a specific flag set.
|
||||
|
@ -223,6 +228,8 @@ impl ValueIdentifiable for FormImpl {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
|
||||
|
@ -240,12 +247,12 @@ pub(crate) mod tests {
|
|||
fn hidden_abilities(&self) -> &Vec<StringKey>;
|
||||
fn moves(&self) -> &Box<dyn LearnableMoves>;
|
||||
fn flags(&self) -> &HashSet<StringKey>;
|
||||
fn get_type(&self, index: usize) -> TypeIdentifier;
|
||||
fn get_type(&self, index: usize) -> Result<TypeIdentifier>;
|
||||
fn get_base_stat(&self, stat: Statistic) -> u16;
|
||||
fn find_ability_index(&self, ability: &dyn Ability) -> Option<AbilityIndex>;
|
||||
fn get_ability(&self, index: AbilityIndex) -> &StringKey;
|
||||
fn get_random_ability(&self, rand: &mut Random) -> &StringKey;
|
||||
fn get_random_hidden_ability(&self, rand: &mut Random) -> &StringKey;
|
||||
fn get_ability<'a>(&'a self, index: AbilityIndex) -> Result<&'a StringKey>;
|
||||
fn get_random_ability<'a>(&'a self, rand: &mut Random) -> Result<&'a StringKey>;
|
||||
fn get_random_hidden_ability<'a>(&'a self, rand: &mut Random) -> Result<&'a StringKey>;
|
||||
fn has_flag(&self, key: &StringKey) -> bool;
|
||||
fn has_flag_by_hash(&self, key_hash: u32) -> bool;
|
||||
}
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
use anyhow_ext::Result;
|
||||
use indexmap::IndexSet;
|
||||
use std::fmt::Debug;
|
||||
|
||||
use crate::defines::LevelInt;
|
||||
use crate::StringKey;
|
||||
use crate::{StringKey, VecExt};
|
||||
|
||||
/// The storage of the moves a Pokemon can learn.
|
||||
pub trait LearnableMoves: Debug {
|
||||
/// Adds a new level move the Pokemon can learn.
|
||||
fn add_level_move(&mut self, level: LevelInt, m: &StringKey);
|
||||
fn add_level_move(&mut self, level: LevelInt, m: &StringKey) -> Result<()>;
|
||||
/// Gets all moves a Pokemon can learn when leveling up to a specific level.
|
||||
fn get_learned_by_level(&self, level: LevelInt) -> Option<&Vec<StringKey>>;
|
||||
/// Gets the distinct moves a Pokemon can learn through leveling up.
|
||||
|
@ -35,9 +36,10 @@ impl LearnableMovesImpl {
|
|||
|
||||
impl LearnableMoves for LearnableMovesImpl {
|
||||
/// Adds a new level move the Pokemon can learn.
|
||||
fn add_level_move(&mut self, level: LevelInt, m: &StringKey) {
|
||||
self.learned_by_level.get_mut(level as usize).unwrap().push(m.clone());
|
||||
fn add_level_move(&mut self, level: LevelInt, m: &StringKey) -> Result<()> {
|
||||
self.learned_by_level.get_mut_res(level as usize)?.push(m.clone());
|
||||
self.distinct_level_moves.insert(m.clone());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Gets all moves a Pokemon can learn when leveling up to a specific level.
|
||||
|
@ -52,14 +54,16 @@ impl LearnableMoves for LearnableMovesImpl {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn adds_level_moves() {
|
||||
let mut moves = LearnableMovesImpl::new(100);
|
||||
moves.add_level_move(1, &"foo".into());
|
||||
moves.add_level_move(1, &"bar".into());
|
||||
moves.add_level_move(1, &"foo".into()).unwrap();
|
||||
moves.add_level_move(1, &"bar".into()).unwrap();
|
||||
|
||||
let m = moves.get_learned_by_level(1u8).unwrap();
|
||||
assert_eq!(m.len(), 2);
|
||||
|
@ -71,8 +75,8 @@ pub(crate) mod tests {
|
|||
fn adds_two_same_moves_at_different_level() {
|
||||
let mut moves = LearnableMovesImpl::new(100);
|
||||
|
||||
moves.add_level_move(1, &"foo".into());
|
||||
moves.add_level_move(5, &"foo".into());
|
||||
moves.add_level_move(1, &"foo".into()).unwrap();
|
||||
moves.add_level_move(5, &"foo".into()).unwrap();
|
||||
|
||||
let m = moves.get_learned_by_level(1u8).unwrap();
|
||||
assert_eq!(m.len(), 1);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use anyhow_ext::{anyhow, Result};
|
||||
use std::fmt::Debug;
|
||||
use std::sync::{Arc, LazyLock};
|
||||
|
||||
|
@ -34,7 +35,7 @@ pub trait Species: ValueIdentifiable + Debug {
|
|||
/// Gets a form by the hash of its name.
|
||||
fn get_form_by_hash(&self, name_hash: u32) -> Option<Arc<dyn Form>>;
|
||||
/// Gets the form the Pokemon will have by default, if no other form is specified.
|
||||
fn get_default_form(&self) -> Arc<dyn Form>;
|
||||
fn get_default_form(&self) -> Result<Arc<dyn Form>>;
|
||||
/// Gets a random gender.
|
||||
fn get_random_gender(&self, rand: &mut Random) -> Gender;
|
||||
/// Check whether the Pokemon has a specific flag set.
|
||||
|
@ -145,8 +146,13 @@ impl Species for SpeciesImpl {
|
|||
}
|
||||
|
||||
/// Gets the form the Pokemon will have by default, if no other form is specified.
|
||||
fn get_default_form(&self) -> Arc<dyn Form> {
|
||||
self.forms.read().get(&get_default_key()).unwrap().clone()
|
||||
fn get_default_form(&self) -> Result<Arc<dyn Form>> {
|
||||
Ok(self
|
||||
.forms
|
||||
.read()
|
||||
.get(&get_default_key())
|
||||
.ok_or(anyhow!("No default form for species"))?
|
||||
.clone())
|
||||
}
|
||||
|
||||
/// Gets a random gender.
|
||||
|
@ -177,6 +183,8 @@ impl ValueIdentifiable for SpeciesImpl {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
|
||||
|
@ -194,7 +202,7 @@ pub(crate) mod tests {
|
|||
fn add_form(&self, id: StringKey, form: Arc<dyn Form>);
|
||||
fn get_form(&self, id: &StringKey) -> Option<Arc<dyn Form>>;
|
||||
fn get_form_by_hash(&self, name_hash: u32) -> Option<Arc<dyn Form>>;
|
||||
fn get_default_form(&self) -> Arc<dyn Form>;
|
||||
fn get_default_form(&self) -> Result<Arc<dyn Form>>;
|
||||
fn get_random_gender(&self, rand: &mut Random) -> Gender;
|
||||
fn has_flag(&self, key: &StringKey) -> bool;
|
||||
fn has_flag_by_hash(&self, key_hash: u32) -> bool;
|
||||
|
|
|
@ -275,10 +275,12 @@ where
|
|||
T: PrimInt,
|
||||
{
|
||||
/// The lowest value a value on the set can have.
|
||||
#[allow(clippy::unwrap_used)] // Should never fail
|
||||
pub fn min() -> T {
|
||||
<T as NumCast>::from(MIN).unwrap()
|
||||
}
|
||||
/// The highest value a value on the set can have.
|
||||
#[allow(clippy::unwrap_used)] // Should never fail
|
||||
pub fn max() -> T {
|
||||
<T as NumCast>::from(MAX).unwrap()
|
||||
}
|
||||
|
|
|
@ -91,6 +91,8 @@ impl Random {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
mod tests {
|
||||
use std::hint::black_box;
|
||||
use test::Bencher;
|
||||
|
|
|
@ -29,6 +29,7 @@ static EMPTY: LazyLock<StringKey> = LazyLock::new(|| StringKey::new(""));
|
|||
|
||||
impl StringKey {
|
||||
#[inline(always)]
|
||||
#[allow(clippy::indexing_slicing)] // Should never fail. More efficient than using get.
|
||||
/// Gets the hash of a string.
|
||||
pub const fn get_hash(s: &str) -> u32 {
|
||||
let mut crc: u32 = 0xffffffff;
|
||||
|
@ -119,6 +120,7 @@ impl Display for StringKey {
|
|||
}
|
||||
|
||||
impl From<&CStr> for StringKey {
|
||||
#[allow(clippy::unwrap_used)] // Fix later.
|
||||
fn from(s: &CStr) -> Self {
|
||||
StringKey::new(s.to_str().unwrap())
|
||||
}
|
||||
|
|
|
@ -121,7 +121,9 @@ pub fn load_types(path: &String) -> Box<dyn TypeLibrary> {
|
|||
|
||||
for (i, v) in record.iter().skip(1).enumerate() {
|
||||
let effectiveness = v.parse::<f32>().unwrap();
|
||||
type_library.set_effectiveness(offensive_type_id, ((i + 1) as u8).into(), effectiveness);
|
||||
type_library
|
||||
.set_effectiveness(offensive_type_id, ((i + 1) as u8).into(), effectiveness)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
type_library
|
||||
|
@ -463,7 +465,7 @@ fn parse_moves(value: &Value, move_library: &Box<dyn MoveLibrary>) -> Box<dyn Le
|
|||
let name = level_move.get("name").unwrap().as_str().unwrap().into();
|
||||
let level = level_move.get("level").unwrap().as_u64().unwrap() as LevelInt;
|
||||
assert!(move_library.get(&name).is_some());
|
||||
moves.add_level_move(level, &name);
|
||||
moves.add_level_move(level, &name).unwrap();
|
||||
}
|
||||
|
||||
Box::new(moves)
|
||||
|
@ -500,10 +502,12 @@ fn test_type_library_loaded() {
|
|||
let types = load_types(&path.to_str().unwrap().to_string());
|
||||
|
||||
assert_eq!(
|
||||
types.get_effectiveness(
|
||||
types.get_type_id(&"fire".into()).unwrap(),
|
||||
&[types.get_type_id(&"grass".into()).unwrap()],
|
||||
),
|
||||
types
|
||||
.get_effectiveness(
|
||||
types.get_type_id(&"fire".into()).unwrap(),
|
||||
&[types.get_type_id(&"grass".into()).unwrap()],
|
||||
)
|
||||
.unwrap(),
|
||||
2.0
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue