Loads of work to replace panics with results.
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
2023-04-15 19:33:29 +02:00
parent 2849cad57b
commit 6f1880c768
25 changed files with 547 additions and 309 deletions

View File

@@ -22,8 +22,8 @@ use crate::static_data::TypeIdentifier;
use crate::static_data::{Ability, Statistic};
use crate::static_data::{ClampedStatisticSet, StatisticSet};
use crate::utils::Random;
use crate::{script_hook, StringKey, ValueIdentifiable, ValueIdentifier};
use anyhow::Result;
use crate::{script_hook, PkmnError, StringKey, ValueIdentifiable, ValueIdentifier};
use anyhow::{anyhow, bail, Result};
/// An individual Pokemon as we know and love them.
#[derive(Debug)]
@@ -142,7 +142,7 @@ impl Pokemon {
.static_data()
.natures()
.get_nature(nature)
.unwrap_or_else(|| panic!("Unknown nature name was given: {}.", &nature));
.ok_or(PkmnError::InvalidNatureName { nature: nature.clone() })?;
let mut pokemon = Self {
identifier: Default::default(),
library,
@@ -180,7 +180,7 @@ impl Pokemon {
volatile: Default::default(),
script_source_data: Default::default(),
};
pokemon.recalculate_flat_stats();
pokemon.recalculate_flat_stats()?;
let health = pokemon.flat_stats().hp();
pokemon.current_health = AtomicU32::new(health);
@@ -257,20 +257,19 @@ impl Pokemon {
self.held_item.write().take()
}
/// Makes the Pokemon uses its held item.
pub fn consume_held_item(&self) -> bool {
pub fn consume_held_item(&self) -> Result<bool> {
if self.held_item.read().is_none() {
return false;
return Ok(false);
}
let script = self
.library
.load_item_script(self.held_item.read().as_ref().unwrap())
.unwrap();
.load_item_script(self.held_item.read().as_ref().ok_or(PkmnError::UnableToAcquireLock)?)?;
if script.is_none() {
return false;
return Ok(false);
}
// TODO: the entire item use part.
todo!();
bail!("Not implemented yet.")
}
/// The remaining health points of the Pokemon.
@@ -331,7 +330,7 @@ impl Pokemon {
self.stat_boost.get_stat(stat)
}
/// Change a boosted stat by a certain amount.
pub fn change_stat_boost(&self, stat: Statistic, mut diff_amount: i8, self_inflicted: bool) -> bool {
pub fn change_stat_boost(&self, stat: Statistic, mut diff_amount: i8, self_inflicted: bool) -> Result<bool> {
let mut prevent = false;
script_hook!(
prevent_stat_boost_change,
@@ -343,7 +342,7 @@ impl Pokemon {
&mut prevent
);
if prevent {
return false;
return Ok(false);
}
script_hook!(
change_stat_boost_change,
@@ -354,7 +353,7 @@ impl Pokemon {
&mut diff_amount
);
if diff_amount == 0 {
return false;
return Ok(false);
}
let mut changed = false;
@@ -378,9 +377,9 @@ impl Pokemon {
new_value,
})
}
self.recalculate_boosted_stats();
self.recalculate_boosted_stats()?;
}
changed
Ok(changed)
}
/// The [individual values](https://bulbapedia.bulbagarden.net/wiki/Individual_values) of the Pokemon.
@@ -416,15 +415,21 @@ impl Pokemon {
self.override_ability.is_some()
}
/// Returns the currently active ability.
pub fn active_ability(&self) -> Arc<dyn Ability> {
pub fn active_ability(&self) -> Result<Arc<dyn Ability>> {
if let Some(v) = &self.override_ability {
return v.clone();
return Ok(v.clone());
}
self.library
let form = self.form();
let ability = form.get_ability(self.ability_index);
Ok(self
.library
.static_data()
.abilities()
.get(self.form().get_ability(self.ability_index))
.unwrap()
.get(ability)
.ok_or(PkmnError::InvalidAbilityName {
ability: ability.clone(),
})?)
}
/// The script for the status.
@@ -450,22 +455,23 @@ impl Pokemon {
/// Calculates the flat stats on the Pokemon. This should be called when for example the base
/// stats, level, nature, IV, or EV changes. This has a side effect of recalculating the boosted
/// stats, as those depend on the flat stats.
pub fn recalculate_flat_stats(&self) {
pub fn recalculate_flat_stats(&self) -> Result<()> {
self.library
.stat_calculator()
.calculate_flat_stats(self, &self.flat_stats);
self.recalculate_boosted_stats();
.calculate_flat_stats(self, &self.flat_stats)?;
self.recalculate_boosted_stats()?;
Ok(())
}
/// Calculates the boosted stats on the Pokemon, _without_ recalculating the flat stats.
/// This should be called when a stat boost changes.
pub fn recalculate_boosted_stats(&self) {
pub fn recalculate_boosted_stats(&self) -> Result<()> {
self.library
.stat_calculator()
.calculate_boosted_stats(self, &self.boosted_stats);
.calculate_boosted_stats(self, &self.boosted_stats)
}
/// Change the species of the Pokemon.
pub fn change_species(&self, species: Arc<dyn Species>, form: Arc<dyn Form>) {
pub fn change_species(&self, species: Arc<dyn Species>, form: Arc<dyn Form>) -> Result<()> {
*self.species.write() = species.clone();
*self.form.write() = form.clone();
@@ -474,7 +480,16 @@ impl Pokemon {
// If we're in battle, use the battle random for predictability
let r = self.battle_data.read();
if let Some(data) = r.deref() {
let mut random = data.battle().unwrap().random().get_rng().lock().unwrap();
let mut random = match data
.battle()
.ok_or(anyhow!("Battle not set"))?
.random()
.get_rng()
.lock()
{
Ok(v) => v,
Err(_) => return Err(PkmnError::UnableToAcquireLock.into()),
};
*self.gender.write() = species.get_random_gender(random.deref_mut());
} else {
// If we're not in battle, just use a new random.
@@ -495,12 +510,13 @@ impl Pokemon {
})
}
}
Ok(())
}
/// Change the form of the Pokemon.
pub fn change_form(&self, form: &Arc<dyn Form>) {
pub fn change_form(&self, form: &Arc<dyn Form>) -> Result<()> {
if self.form().value_identifier() == form.value_identifier() {
return;
return Ok(());
}
*self.form.write() = form.clone();
@@ -514,21 +530,21 @@ impl Pokemon {
self.weight.store(form.weight(), Ordering::SeqCst);
self.height.store(form.height(), Ordering::SeqCst);
let ability = self.active_ability()?;
let ability_script = self
.library
.load_script(self.into(), ScriptCategory::Ability, self.active_ability().name())
.unwrap();
.load_script(self.into(), ScriptCategory::Ability, ability.name())?;
if let Some(ability_script) = ability_script {
self.ability_script
.set(ability_script)
.as_ref()
// Ensure the ability script gets initialized with the parameters for the ability.
.on_initialize(&self.library, self.active_ability().parameters().to_vec())
.on_initialize(&self.library, ability.parameters().to_vec())
} else {
self.ability_script.clear();
}
let old_health = self.max_health();
self.recalculate_flat_stats();
self.recalculate_flat_stats()?;
let diff_health = (self.max_health() - old_health) as i32;
if self.current_health() == 0 && (self.current_health() as i32) < -diff_health {
self.current_health.store(0, Ordering::SeqCst);
@@ -547,7 +563,8 @@ impl Pokemon {
form: form.clone(),
})
}
}
};
Ok(())
}
/// Whether or not the Pokemon is useable in a battle.
@@ -657,8 +674,14 @@ impl Pokemon {
script_hook!(on_remove, self,);
if !battle.can_slot_be_filled(battle_data.battle_side_index(), battle_data.index()) {
battle.sides()[battle_data.battle_side_index() as usize]
.mark_slot_as_unfillable(battle_data.index());
battle
.sides()
.get(battle_data.battle_side_index() as usize)
.ok_or(PkmnError::IndexOutOfBounds {
index: battle_data.battle_side_index() as usize,
len: battle.sides().len(),
})?
.mark_slot_as_unfillable(battle_data.index())?;
}
battle.validate_battle_state()?;
@@ -695,14 +718,22 @@ impl Pokemon {
}
/// Learn a move.
pub fn learn_move(&self, move_name: &StringKey, learn_method: MoveLearnMethod) {
pub fn learn_move(&self, move_name: &StringKey, learn_method: MoveLearnMethod) -> Result<()> {
let mut learned_moves = self.learned_moves().write();
let move_pos = learned_moves.iter().position(|a| a.is_none());
if move_pos.is_none() {
panic!("No more moves with an empty space found.");
bail!("No more moves with an empty space found.");
}
let move_data = self.library.static_data().moves().get(move_name).unwrap();
let move_data = self
.library
.static_data()
.moves()
.get(move_name)
.ok_or(PkmnError::InvalidMoveName {
move_name: move_name.clone(),
})?;
learned_moves[move_pos.unwrap()] = Some(Arc::new(LearnedMove::new(move_data, learn_method)));
Ok(())
}
/// Removes the current non-volatile status from the Pokemon.
@@ -711,7 +742,7 @@ impl Pokemon {
}
/// Increases the level by a certain amount
pub fn change_level_by(&self, amount: LevelInt) {
pub fn change_level_by(&self, amount: LevelInt) -> Result<()> {
self.level
.fetch_update(Ordering::SeqCst, Ordering::Relaxed, |x| {
let max_level = self.library().static_data().settings().maximum_level();
@@ -721,8 +752,8 @@ impl Pokemon {
Some(x + amount)
}
})
.expect("Failed to change level.");
self.recalculate_flat_stats();
.ok();
self.recalculate_flat_stats()
}
}
@@ -770,14 +801,14 @@ impl PokemonBattleData {
}
impl ScriptSource for Pokemon {
fn get_script_count(&self) -> usize {
fn get_script_count(&self) -> Result<usize> {
let mut c = 3;
if let Some(battle_data) = &self.battle_data.read().deref() {
if let Some(battle) = battle_data.battle() {
c += battle.sides()[battle_data.battle_side_index() as usize].get_script_count();
c += battle.sides()[battle_data.battle_side_index() as usize].get_script_count()?;
}
}
c
Ok(c)
}
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
@@ -791,13 +822,14 @@ impl ScriptSource for Pokemon {
scripts.push((&self.volatile).into());
}
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) {
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) -> Result<()> {
self.get_own_scripts(scripts);
if let Some(battle_data) = &self.battle_data.read().deref() {
if let Some(battle) = battle_data.battle() {
battle.sides()[battle_data.battle_side_index() as usize].collect_scripts(scripts);
battle.sides()[battle_data.battle_side_index() as usize].collect_scripts(scripts)?;
}
}
Ok(())
}
}
@@ -883,8 +915,10 @@ pub mod test {
static_lib.expect_natures().return_const(Box::new(nature_lib));
let mut stat_calculator = MockBattleStatCalculator::new();
stat_calculator.expect_calculate_flat_stats().returning(|_, _| {});
stat_calculator.expect_calculate_boosted_stats().returning(|_, _| {});
stat_calculator.expect_calculate_flat_stats().returning(|_, _| Ok(()));
stat_calculator
.expect_calculate_boosted_stats()
.returning(|_, _| Ok(()));
let mut lib = MockDynamicLibrary::new();
lib.expect_static_data().return_const(Box::new(static_lib));