Loads of work to replace panics with results.
continuous-integration/drone/push Build is failing
Details
continuous-integration/drone/push Build is failing
Details
This commit is contained in:
parent
2849cad57b
commit
6f1880c768
|
@ -60,6 +60,7 @@ arcstr = { version = "1.1.4", features = ["std"] }
|
||||||
enum-display-derive = "0.1.1"
|
enum-display-derive = "0.1.1"
|
||||||
anyhow = "1.0.69"
|
anyhow = "1.0.69"
|
||||||
anyhow_ext = "0.2.1"
|
anyhow_ext = "0.2.1"
|
||||||
|
thiserror = "1.0.39"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
csv = "1.1.6"
|
csv = "1.1.6"
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use anyhow::{bail, Result};
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -110,23 +111,23 @@ impl TurnChoice {
|
||||||
|
|
||||||
/// Helper function to get the move choice data from a turn. Note that this will panic if not
|
/// Helper function to get the move choice data from a turn. Note that this will panic if not
|
||||||
/// used on a move choice.
|
/// used on a move choice.
|
||||||
pub(crate) fn get_move_turn_data(&self) -> &MoveChoice {
|
pub(crate) fn get_move_turn_data(&self) -> Result<&MoveChoice> {
|
||||||
if let TurnChoice::Move(data) = self {
|
if let TurnChoice::Move(data) = self {
|
||||||
return data;
|
return Ok(data);
|
||||||
}
|
}
|
||||||
panic!("Invalid turn choice");
|
bail!("Invalid turn choice");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScriptSource for TurnChoice {
|
impl ScriptSource for TurnChoice {
|
||||||
fn get_script_count(&self) -> usize {
|
fn get_script_count(&self) -> Result<usize> {
|
||||||
match self {
|
Ok(match self {
|
||||||
TurnChoice::Move(data) => data.get_script_count(),
|
TurnChoice::Move(data) => data.get_script_count()?,
|
||||||
TurnChoice::Item(data) => data.get_script_count(),
|
TurnChoice::Item(data) => data.get_script_count()?,
|
||||||
TurnChoice::Switch(data) => data.get_script_count(),
|
TurnChoice::Switch(data) => data.get_script_count()?,
|
||||||
TurnChoice::Flee(data) => data.get_script_count(),
|
TurnChoice::Flee(data) => data.get_script_count()?,
|
||||||
TurnChoice::Pass(data) => data.get_script_count(),
|
TurnChoice::Pass(data) => data.get_script_count()?,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
|
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
|
||||||
|
@ -149,14 +150,15 @@ impl ScriptSource for TurnChoice {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) {
|
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) -> Result<()> {
|
||||||
match self {
|
match self {
|
||||||
TurnChoice::Move(data) => data.collect_scripts(scripts),
|
TurnChoice::Move(data) => data.collect_scripts(scripts)?,
|
||||||
TurnChoice::Item(data) => data.collect_scripts(scripts),
|
TurnChoice::Item(data) => data.collect_scripts(scripts)?,
|
||||||
TurnChoice::Switch(data) => data.collect_scripts(scripts),
|
TurnChoice::Switch(data) => data.collect_scripts(scripts)?,
|
||||||
TurnChoice::Flee(data) => data.collect_scripts(scripts),
|
TurnChoice::Flee(data) => data.collect_scripts(scripts)?,
|
||||||
TurnChoice::Pass(data) => data.collect_scripts(scripts),
|
TurnChoice::Pass(data) => data.collect_scripts(scripts)?,
|
||||||
}
|
};
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,8 +232,8 @@ impl MoveChoice {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScriptSource for MoveChoice {
|
impl ScriptSource for MoveChoice {
|
||||||
fn get_script_count(&self) -> usize {
|
fn get_script_count(&self) -> Result<usize> {
|
||||||
1 + self.choice_data.user.get_script_count()
|
Ok(self.choice_data.user.get_script_count()? + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
|
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
|
||||||
|
@ -242,9 +244,9 @@ impl ScriptSource for MoveChoice {
|
||||||
scripts.push((&self.script).into());
|
scripts.push((&self.script).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) {
|
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) -> Result<()> {
|
||||||
self.get_own_scripts(scripts);
|
self.get_own_scripts(scripts);
|
||||||
self.choice_data.user.collect_scripts(scripts);
|
self.choice_data.user.collect_scripts(scripts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,8 +275,8 @@ impl ItemChoice {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScriptSource for ItemChoice {
|
impl ScriptSource for ItemChoice {
|
||||||
fn get_script_count(&self) -> usize {
|
fn get_script_count(&self) -> Result<usize> {
|
||||||
0
|
Ok(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
|
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
|
||||||
|
@ -283,8 +285,8 @@ impl ScriptSource for ItemChoice {
|
||||||
|
|
||||||
fn get_own_scripts(&self, _scripts: &mut Vec<ScriptWrapper>) {}
|
fn get_own_scripts(&self, _scripts: &mut Vec<ScriptWrapper>) {}
|
||||||
|
|
||||||
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) {
|
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) -> Result<()> {
|
||||||
self.choice_data.user.collect_scripts(scripts);
|
self.choice_data.user.collect_scripts(scripts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -313,8 +315,8 @@ impl SwitchChoice {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScriptSource for SwitchChoice {
|
impl ScriptSource for SwitchChoice {
|
||||||
fn get_script_count(&self) -> usize {
|
fn get_script_count(&self) -> Result<usize> {
|
||||||
0
|
Ok(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
|
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
|
||||||
|
@ -323,8 +325,8 @@ impl ScriptSource for SwitchChoice {
|
||||||
|
|
||||||
fn get_own_scripts(&self, _scripts: &mut Vec<ScriptWrapper>) {}
|
fn get_own_scripts(&self, _scripts: &mut Vec<ScriptWrapper>) {}
|
||||||
|
|
||||||
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) {
|
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) -> Result<()> {
|
||||||
self.choice_data.user.collect_scripts(scripts);
|
self.choice_data.user.collect_scripts(scripts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -352,8 +354,8 @@ impl FleeChoice {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScriptSource for FleeChoice {
|
impl ScriptSource for FleeChoice {
|
||||||
fn get_script_count(&self) -> usize {
|
fn get_script_count(&self) -> Result<usize> {
|
||||||
0
|
Ok(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
|
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
|
||||||
|
@ -362,8 +364,8 @@ impl ScriptSource for FleeChoice {
|
||||||
|
|
||||||
fn get_own_scripts(&self, _scripts: &mut Vec<ScriptWrapper>) {}
|
fn get_own_scripts(&self, _scripts: &mut Vec<ScriptWrapper>) {}
|
||||||
|
|
||||||
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) {
|
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) -> Result<()> {
|
||||||
self.choice_data.user.collect_scripts(scripts);
|
self.choice_data.user.collect_scripts(scripts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -392,8 +394,8 @@ impl PassChoice {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScriptSource for PassChoice {
|
impl ScriptSource for PassChoice {
|
||||||
fn get_script_count(&self) -> usize {
|
fn get_script_count(&self) -> Result<usize> {
|
||||||
0
|
Ok(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
|
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
|
||||||
|
@ -402,8 +404,8 @@ impl ScriptSource for PassChoice {
|
||||||
|
|
||||||
fn get_own_scripts(&self, _scripts: &mut Vec<ScriptWrapper>) {}
|
fn get_own_scripts(&self, _scripts: &mut Vec<ScriptWrapper>) {}
|
||||||
|
|
||||||
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) {
|
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) -> Result<()> {
|
||||||
self.choice_data.user.collect_scripts(scripts);
|
self.choice_data.user.collect_scripts(scripts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::dynamic_data::choices::TurnChoice;
|
use crate::dynamic_data::choices::TurnChoice;
|
||||||
use crate::dynamic_data::script_handling::ScriptSource;
|
use crate::dynamic_data::script_handling::ScriptSource;
|
||||||
use crate::dynamic_data::Pokemon;
|
use crate::dynamic_data::Pokemon;
|
||||||
use crate::{script_hook, ValueIdentifiable, ValueIdentifier};
|
use crate::{script_hook, PkmnError, ValueIdentifiable, ValueIdentifier};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use anyhow_ext::anyhow;
|
use anyhow_ext::anyhow;
|
||||||
use parking_lot::lock_api::MappedRwLockReadGuard;
|
use parking_lot::lock_api::MappedRwLockReadGuard;
|
||||||
|
@ -39,14 +39,17 @@ impl ChoiceQueue {
|
||||||
/// Dequeues the next turn choice to be executed. This gives ownership to the callee, and replaces
|
/// Dequeues the next turn choice to be executed. This gives ownership to the callee, and replaces
|
||||||
/// our own reference to the turn choice with an empty spot. It also increments the current position
|
/// our own reference to the turn choice with an empty spot. It also increments the current position
|
||||||
/// by one.
|
/// by one.
|
||||||
pub fn dequeue(&mut self) -> Option<TurnChoice> {
|
pub fn dequeue(&mut self) -> Result<Option<TurnChoice>> {
|
||||||
let mut write_lock = self.queue.write();
|
let mut write_lock = self.queue.write();
|
||||||
if self.current >= write_lock.len() {
|
if self.current >= write_lock.len() {
|
||||||
return None;
|
return Ok(None);
|
||||||
}
|
}
|
||||||
let c = write_lock[self.current].take();
|
let c = write_lock
|
||||||
|
.get_mut(self.current)
|
||||||
|
.ok_or(anyhow!("Unable to get current turn choice"))?
|
||||||
|
.take();
|
||||||
self.current += 1;
|
self.current += 1;
|
||||||
c
|
Ok(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This reads what the next choice to execute will be, without modifying state.
|
/// This reads what the next choice to execute will be, without modifying state.
|
||||||
|
@ -55,7 +58,10 @@ impl ChoiceQueue {
|
||||||
if self.current >= read_lock.len() {
|
if self.current >= read_lock.len() {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
} else {
|
} else {
|
||||||
let v = RwLockReadGuard::try_map(read_lock, |a| a[self.current].as_ref());
|
let v = RwLockReadGuard::try_map(read_lock, |a| match a.get(self.current) {
|
||||||
|
Some(Some(v)) => Some(v),
|
||||||
|
_ => None,
|
||||||
|
});
|
||||||
match v {
|
match v {
|
||||||
Ok(v) => Ok(Some(v)),
|
Ok(v) => Ok(Some(v)),
|
||||||
Err(_) => Err(anyhow!("Could not map choice")),
|
Err(_) => Err(anyhow!("Could not map choice")),
|
||||||
|
@ -71,11 +77,13 @@ impl ChoiceQueue {
|
||||||
/// This resorts the yet to be executed choices. This can be useful for dealing with situations
|
/// This resorts the yet to be executed choices. This can be useful for dealing with situations
|
||||||
/// such as Pokemon changing forms just after the very start of a turn, when turn order has
|
/// such as Pokemon changing forms just after the very start of a turn, when turn order has
|
||||||
/// technically already been decided.
|
/// technically already been decided.
|
||||||
pub fn resort(&mut self) {
|
pub fn resort(&mut self) -> Result<()> {
|
||||||
let len = self.queue.read().len();
|
let len = self.queue.read().len();
|
||||||
let mut write_lock = self.queue.write();
|
let mut write_lock = self.queue.write();
|
||||||
for index in self.current..len {
|
for index in self.current..len {
|
||||||
let choice = &mut write_lock[index];
|
let choice = &mut write_lock
|
||||||
|
.get_mut(index)
|
||||||
|
.ok_or(PkmnError::IndexOutOfBounds { index, len })?;
|
||||||
if let Some(choice) = choice {
|
if let Some(choice) = choice {
|
||||||
let mut speed = choice.user().boosted_stats().speed();
|
let mut speed = choice.user().boosted_stats().speed();
|
||||||
script_hook!(change_speed, (*choice), choice, &mut speed);
|
script_hook!(change_speed, (*choice), choice, &mut speed);
|
||||||
|
@ -83,7 +91,14 @@ impl ChoiceQueue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
write_lock[self.current..len].sort_unstable_by(|a, b| b.cmp(a));
|
write_lock
|
||||||
|
.get_mut(self.current..len)
|
||||||
|
.ok_or(PkmnError::IndexOutOfBounds {
|
||||||
|
index: self.current,
|
||||||
|
len,
|
||||||
|
})?
|
||||||
|
.sort_unstable_by(|a, b| b.cmp(a));
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This moves the choice of a specific Pokemon up to the next choice to be executed.
|
/// This moves the choice of a specific Pokemon up to the next choice to be executed.
|
||||||
|
@ -92,7 +107,7 @@ impl ChoiceQueue {
|
||||||
let mut desired_index = None;
|
let mut desired_index = None;
|
||||||
// Find the index for the choice we want to move up.
|
// Find the index for the choice we want to move up.
|
||||||
for index in self.current..queue_lock.len() {
|
for index in self.current..queue_lock.len() {
|
||||||
if let Some(choice) = &queue_lock[index] {
|
if let Some(Some(choice)) = &queue_lock.get(index) {
|
||||||
if pokemon.value_identifier() == choice.user().value_identifier() {
|
if pokemon.value_identifier() == choice.user().value_identifier() {
|
||||||
desired_index = Some(index);
|
desired_index = Some(index);
|
||||||
break;
|
break;
|
||||||
|
@ -106,8 +121,14 @@ impl ChoiceQueue {
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let len = queue_lock.len();
|
||||||
// Take the choice we want to move forward out of it's place.
|
// Take the choice we want to move forward out of it's place.
|
||||||
let choice = queue_lock[desired_index]
|
let choice = queue_lock
|
||||||
|
.get_mut(desired_index)
|
||||||
|
.ok_or(PkmnError::IndexOutOfBounds {
|
||||||
|
index: self.current,
|
||||||
|
len,
|
||||||
|
})?
|
||||||
.take()
|
.take()
|
||||||
.ok_or(anyhow!("Choice was already taken"))?;
|
.ok_or(anyhow!("Choice was already taken"))?;
|
||||||
// Iterate backwards from the spot before the choice we want to move up, push them all back
|
// Iterate backwards from the spot before the choice we want to move up, push them all back
|
||||||
|
@ -115,8 +136,15 @@ impl ChoiceQueue {
|
||||||
for index in (self.current..desired_index).rev() {
|
for index in (self.current..desired_index).rev() {
|
||||||
queue_lock.swap(index, index + 1);
|
queue_lock.swap(index, index + 1);
|
||||||
}
|
}
|
||||||
|
let len = queue_lock.len();
|
||||||
// Place the choice that needs to be next in the next to be executed position.
|
// Place the choice that needs to be next in the next to be executed position.
|
||||||
let _ = queue_lock[self.current].insert(choice);
|
let _ = queue_lock
|
||||||
|
.get_mut(self.current)
|
||||||
|
.ok_or(PkmnError::IndexOutOfBounds {
|
||||||
|
index: self.current,
|
||||||
|
len,
|
||||||
|
})?
|
||||||
|
.insert(choice);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
None => false,
|
None => false,
|
||||||
|
@ -125,9 +153,16 @@ impl ChoiceQueue {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Internal helper function to be easily able to iterate over the yet to be executed choices.
|
/// Internal helper function to be easily able to iterate over the yet to be executed choices.
|
||||||
pub(crate) fn get_queue(&self) -> MappedRwLockReadGuard<'_, RawRwLock, [Option<TurnChoice>]> {
|
pub(crate) fn get_queue(&self) -> Result<MappedRwLockReadGuard<'_, RawRwLock, [Option<TurnChoice>]>> {
|
||||||
let read_lock = self.queue.read();
|
let read_lock = self.queue.read();
|
||||||
RwLockReadGuard::map(read_lock, |a| &a[self.current..self.queue.read().len()])
|
match RwLockReadGuard::try_map(read_lock, |a| a.get(self.current..self.queue.read().len())) {
|
||||||
|
Ok(v) => Ok(v),
|
||||||
|
Err(_) => Err(PkmnError::IndexOutOfBounds {
|
||||||
|
index: self.current,
|
||||||
|
len: self.queue.read().len(),
|
||||||
|
}
|
||||||
|
.into()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,7 +190,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn dequeue_from_empty_queue() {
|
fn dequeue_from_empty_queue() {
|
||||||
let mut queue = ChoiceQueue::new(Vec::new());
|
let mut queue = ChoiceQueue::new(Vec::new());
|
||||||
assert!(queue.dequeue().is_none());
|
assert!(queue.dequeue().unwrap().is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_user(level: LevelInt) -> Pokemon {
|
fn get_user(level: LevelInt) -> Pokemon {
|
||||||
|
@ -196,7 +231,7 @@ mod tests {
|
||||||
|
|
||||||
let mut queue = ChoiceQueue::new(vec![Some(TurnChoice::Pass(PassChoice::new(user)))]);
|
let mut queue = ChoiceQueue::new(vec![Some(TurnChoice::Pass(PassChoice::new(user)))]);
|
||||||
assert!(queue.has_next());
|
assert!(queue.has_next());
|
||||||
assert_eq!(7, queue.dequeue().unwrap().speed());
|
assert_eq!(7, queue.dequeue().unwrap().unwrap().speed());
|
||||||
assert!(!queue.has_next());
|
assert!(!queue.has_next());
|
||||||
assert!(queue.peek().unwrap().is_none());
|
assert!(queue.peek().unwrap().is_none());
|
||||||
}
|
}
|
||||||
|
@ -224,7 +259,7 @@ mod tests {
|
||||||
Some(TurnChoice::Pass(PassChoice::new(user1.clone()))),
|
Some(TurnChoice::Pass(PassChoice::new(user1.clone()))),
|
||||||
Some(TurnChoice::Pass(PassChoice::new(user2))),
|
Some(TurnChoice::Pass(PassChoice::new(user2))),
|
||||||
]);
|
]);
|
||||||
let inner_queue = queue.get_queue();
|
let inner_queue = queue.get_queue().unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
inner_queue[0].as_ref().unwrap().user().value_identifier(),
|
inner_queue[0].as_ref().unwrap().user().value_identifier(),
|
||||||
user1.value_identifier()
|
user1.value_identifier()
|
||||||
|
@ -240,8 +275,8 @@ mod tests {
|
||||||
Some(TurnChoice::Pass(PassChoice::new(user1))),
|
Some(TurnChoice::Pass(PassChoice::new(user1))),
|
||||||
Some(TurnChoice::Pass(PassChoice::new(user2))),
|
Some(TurnChoice::Pass(PassChoice::new(user2))),
|
||||||
]);
|
]);
|
||||||
assert_eq!(25, queue.dequeue().unwrap().speed());
|
assert_eq!(25, queue.dequeue().unwrap().unwrap().speed());
|
||||||
assert_eq!(6, queue.dequeue().unwrap().speed());
|
assert_eq!(6, queue.dequeue().unwrap().unwrap().speed());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -254,12 +289,12 @@ mod tests {
|
||||||
Some(TurnChoice::Pass(PassChoice::new(user2.clone()))),
|
Some(TurnChoice::Pass(PassChoice::new(user2.clone()))),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
user2.change_level_by(60);
|
user2.change_level_by(60).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
user1.value_identifier(),
|
user1.value_identifier(),
|
||||||
queue.peek().unwrap().unwrap().user().value_identifier()
|
queue.peek().unwrap().unwrap().user().value_identifier()
|
||||||
);
|
);
|
||||||
queue.resort();
|
queue.resort().unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
user2.value_identifier(),
|
user2.value_identifier(),
|
||||||
queue.peek().unwrap().unwrap().user().value_identifier()
|
queue.peek().unwrap().unwrap().user().value_identifier()
|
||||||
|
@ -283,11 +318,11 @@ mod tests {
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
user2.value_identifier(),
|
user2.value_identifier(),
|
||||||
queue.dequeue().unwrap().user().value_identifier()
|
queue.dequeue().unwrap().unwrap().user().value_identifier()
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
user1.value_identifier(),
|
user1.value_identifier(),
|
||||||
queue.dequeue().unwrap().user().value_identifier()
|
queue.dequeue().unwrap().unwrap().user().value_identifier()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,13 +337,13 @@ mod tests {
|
||||||
]);
|
]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
user2.value_identifier(),
|
user2.value_identifier(),
|
||||||
queue.dequeue().unwrap().user().value_identifier()
|
queue.dequeue().unwrap().unwrap().user().value_identifier()
|
||||||
);
|
);
|
||||||
assert!(!queue.move_pokemon_choice_next(user2.as_ref()).unwrap());
|
assert!(!queue.move_pokemon_choice_next(user2.as_ref()).unwrap());
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
user1.value_identifier(),
|
user1.value_identifier(),
|
||||||
queue.dequeue().unwrap().user().value_identifier()
|
queue.dequeue().unwrap().unwrap().user().value_identifier()
|
||||||
);
|
);
|
||||||
assert!(queue.peek().unwrap().is_none())
|
assert!(queue.peek().unwrap().is_none())
|
||||||
}
|
}
|
||||||
|
@ -362,18 +397,18 @@ mod tests {
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
users[4].value_identifier(),
|
users[4].value_identifier(),
|
||||||
queue.dequeue().unwrap().user().value_identifier()
|
queue.dequeue().unwrap().unwrap().user().value_identifier()
|
||||||
);
|
);
|
||||||
for index in 0..4 {
|
for index in 0..4 {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
users[index].value_identifier(),
|
users[index].value_identifier(),
|
||||||
queue.dequeue().unwrap().user().value_identifier()
|
queue.dequeue().unwrap().unwrap().user().value_identifier()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
for index in 5..7 {
|
for index in 5..7 {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
users[index].value_identifier(),
|
users[index].value_identifier(),
|
||||||
queue.dequeue().unwrap().user().value_identifier()
|
queue.dequeue().unwrap().unwrap().user().value_identifier()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use anyhow::Result;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -6,6 +7,7 @@ use num_traits::abs;
|
||||||
use crate::dynamic_data::Battle;
|
use crate::dynamic_data::Battle;
|
||||||
use crate::dynamic_data::Pokemon;
|
use crate::dynamic_data::Pokemon;
|
||||||
use crate::static_data::MoveTarget;
|
use crate::static_data::MoveTarget;
|
||||||
|
use crate::PkmnError;
|
||||||
|
|
||||||
/// Helper type for the vector of targets we will return.
|
/// Helper type for the vector of targets we will return.
|
||||||
pub type TargetList = Vec<Option<Arc<Pokemon>>>;
|
pub type TargetList = Vec<Option<Arc<Pokemon>>>;
|
||||||
|
@ -102,8 +104,8 @@ fn get_all_adjacent(side: u8, index: u8, battle: &Battle) -> TargetList {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the target for a specific move target type, given the targeted position.
|
/// Gets the target for a specific move target type, given the targeted position.
|
||||||
pub fn resolve_targets(side: u8, index: u8, target: MoveTarget, battle: &Battle) -> TargetList {
|
pub fn resolve_targets(side: u8, index: u8, target: MoveTarget, battle: &Battle) -> Result<TargetList> {
|
||||||
match target {
|
Ok(match target {
|
||||||
// These all resolve to a single position. We let the client deal with where the target is,
|
// These all resolve to a single position. We let the client deal with where the target is,
|
||||||
// and just return the Pokemon at that given target here.
|
// and just return the Pokemon at that given target here.
|
||||||
MoveTarget::Adjacent
|
MoveTarget::Adjacent
|
||||||
|
@ -125,12 +127,21 @@ pub fn resolve_targets(side: u8, index: u8, target: MoveTarget, battle: &Battle)
|
||||||
// the client deal with what side is passed.
|
// the client deal with what side is passed.
|
||||||
MoveTarget::AllAlly | MoveTarget::AllOpponent => {
|
MoveTarget::AllAlly | MoveTarget::AllOpponent => {
|
||||||
let mut v = Vec::new();
|
let mut v = Vec::new();
|
||||||
for pokemon in battle.sides()[side as usize].pokemon().deref() {
|
for pokemon in battle
|
||||||
|
.sides()
|
||||||
|
.get(side as usize)
|
||||||
|
.ok_or(PkmnError::IndexOutOfBounds {
|
||||||
|
index: side as usize,
|
||||||
|
len: battle.sides().len(),
|
||||||
|
})?
|
||||||
|
.pokemon()
|
||||||
|
.deref()
|
||||||
|
{
|
||||||
v.push(pokemon.as_ref().cloned());
|
v.push(pokemon.as_ref().cloned());
|
||||||
}
|
}
|
||||||
v
|
v
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks whether a given side and index are valid for a given move target and user.
|
/// Checks whether a given side and index are valid for a given move target and user.
|
||||||
|
|
|
@ -12,15 +12,12 @@ use crate::dynamic_data::DamageSource;
|
||||||
use crate::dynamic_data::ExecutingMove;
|
use crate::dynamic_data::ExecutingMove;
|
||||||
use crate::dynamic_data::Pokemon;
|
use crate::dynamic_data::Pokemon;
|
||||||
use crate::static_data::MoveCategory;
|
use crate::static_data::MoveCategory;
|
||||||
use crate::{run_scripts, script_hook};
|
use crate::{run_scripts, script_hook, PkmnError};
|
||||||
|
|
||||||
/// Simple macro to get a read lock on the choice queue.
|
/// Simple macro to get a read lock on the choice queue.
|
||||||
macro_rules! read_choice_queue {
|
macro_rules! read_choice_queue {
|
||||||
($choice_queue:ident) => {
|
($choice_queue:ident) => {
|
||||||
$choice_queue
|
$choice_queue.read().as_ref().ok_or(PkmnError::UnableToAcquireLock)?
|
||||||
.read()
|
|
||||||
.as_ref()
|
|
||||||
.ok_or(anyhow!("Could not get a lock on choice queue"))?
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,7 +33,7 @@ impl Battle {
|
||||||
// is primarily intended to be used to reset variables on a script (for example scripts that need
|
// is primarily intended to be used to reset variables on a script (for example scripts that need
|
||||||
// to check whether a pokemon was hit this turn. By resetting here, and setting a variable to true
|
// to check whether a pokemon was hit this turn. By resetting here, and setting a variable to true
|
||||||
// they can then know this later on.)
|
// they can then know this later on.)
|
||||||
for choice in read_choice_queue!(choice_queue).get_queue().iter().flatten() {
|
for choice in read_choice_queue!(choice_queue).get_queue()?.iter().flatten() {
|
||||||
script_hook!(on_before_turn, choice, choice);
|
script_hook!(on_before_turn, choice, choice);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,8 +44,8 @@ impl Battle {
|
||||||
let choice = choice_queue
|
let choice = choice_queue
|
||||||
.write()
|
.write()
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.ok_or(anyhow!("Failed to get a write lock on choice queue"))?
|
.ok_or(PkmnError::UnableToAcquireLock)?
|
||||||
.dequeue();
|
.dequeue()?;
|
||||||
if let Some(choice) = choice {
|
if let Some(choice) = choice {
|
||||||
self.execute_choice(&choice)?;
|
self.execute_choice(&choice)?;
|
||||||
}
|
}
|
||||||
|
@ -110,7 +107,7 @@ impl Battle {
|
||||||
|
|
||||||
/// Executes a move choice.
|
/// Executes a move choice.
|
||||||
fn execute_move_choice<'func>(&'func self, choice: &'func TurnChoice) -> Result<()> {
|
fn execute_move_choice<'func>(&'func self, choice: &'func TurnChoice) -> Result<()> {
|
||||||
let move_choice = choice.get_move_turn_data();
|
let move_choice = choice.get_move_turn_data()?;
|
||||||
let used_move = move_choice.used_move();
|
let used_move = move_choice.used_move();
|
||||||
let move_data = {
|
let move_data = {
|
||||||
let move_data_lock = used_move;
|
let move_data_lock = used_move;
|
||||||
|
@ -125,7 +122,7 @@ impl Battle {
|
||||||
};
|
};
|
||||||
// FIXME: also change the script on the choice if changed;
|
// FIXME: also change the script on the choice if changed;
|
||||||
let target_type = move_data.target();
|
let target_type = move_data.target();
|
||||||
let targets = resolve_targets(move_choice.target_side(), move_choice.target_index(), target_type, self);
|
let targets = resolve_targets(move_choice.target_side(), move_choice.target_index(), target_type, self)?;
|
||||||
|
|
||||||
let mut number_of_hits: u8 = 1;
|
let mut number_of_hits: u8 = 1;
|
||||||
script_hook!(change_number_of_hits, choice, choice, &mut number_of_hits);
|
script_hook!(change_number_of_hits, choice, choice, &mut number_of_hits);
|
||||||
|
@ -210,7 +207,7 @@ impl Battle {
|
||||||
hit_index,
|
hit_index,
|
||||||
&mut hit_type
|
&mut hit_type
|
||||||
);
|
);
|
||||||
let hit_data = executing_move.get_hit_from_raw_index(target_hit_stat + hit_index as usize);
|
let hit_data = executing_move.get_hit_from_raw_index(target_hit_stat + hit_index as usize)?;
|
||||||
hit_data.set_move_type(hit_type);
|
hit_data.set_move_type(hit_type);
|
||||||
let mut effectiveness = self
|
let mut effectiveness = self
|
||||||
.library()
|
.library()
|
||||||
|
@ -256,7 +253,7 @@ impl Battle {
|
||||||
target,
|
target,
|
||||||
hit_index,
|
hit_index,
|
||||||
executing_move.get_hit_data(target, hit_index)?,
|
executing_move.get_hit_data(target, hit_index)?,
|
||||||
);
|
)?;
|
||||||
hit_data.set_base_power(base_power);
|
hit_data.set_base_power(base_power);
|
||||||
let damage = self.library().damage_calculator().get_damage(
|
let damage = self.library().damage_calculator().get_damage(
|
||||||
executing_move,
|
executing_move,
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use anyhow::{bail, Result};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use crate::dynamic_data::Pokemon;
|
use crate::dynamic_data::Pokemon;
|
||||||
|
@ -8,13 +9,13 @@ use crate::{ValueIdentifiable, ValueIdentifier};
|
||||||
/// A battle stat calculator is used to calculate stats for a Pokemon.
|
/// A battle stat calculator is used to calculate stats for a Pokemon.
|
||||||
pub trait BattleStatCalculator: Debug + ValueIdentifiable {
|
pub trait BattleStatCalculator: Debug + ValueIdentifiable {
|
||||||
/// Calculate all the flat stats of a Pokemon, disregarding stat boosts.
|
/// Calculate all the flat stats of a Pokemon, disregarding stat boosts.
|
||||||
fn calculate_flat_stats(&self, pokemon: &Pokemon, stats: &StatisticSet<u32>);
|
fn calculate_flat_stats(&self, pokemon: &Pokemon, stats: &StatisticSet<u32>) -> Result<()>;
|
||||||
/// Calculate a single flat stat of a Pokemon, disregarding stat boost
|
/// Calculate a single flat stat of a Pokemon, disregarding stat boost
|
||||||
fn calculate_flat_stat(&self, pokemon: &Pokemon, stat: Statistic) -> u32;
|
fn calculate_flat_stat(&self, pokemon: &Pokemon, stat: Statistic) -> Result<u32>;
|
||||||
/// Calculate all the boosted stats of a Pokemon, including stat boosts.
|
/// Calculate all the boosted stats of a Pokemon, including stat boosts.
|
||||||
fn calculate_boosted_stats(&self, pokemon: &Pokemon, stats: &StatisticSet<u32>);
|
fn calculate_boosted_stats(&self, pokemon: &Pokemon, stats: &StatisticSet<u32>) -> Result<()>;
|
||||||
/// Calculate a single boosted stat of a Pokemon, including stat boosts.
|
/// Calculate a single boosted stat of a Pokemon, including stat boosts.
|
||||||
fn calculate_boosted_stat(&self, pokemon: &Pokemon, stat: Statistic) -> u32;
|
fn calculate_boosted_stat(&self, pokemon: &Pokemon, stat: Statistic) -> Result<u32>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A basic implementation of the Gen 7 stat calculator.
|
/// A basic implementation of the Gen 7 stat calculator.
|
||||||
|
@ -39,28 +40,28 @@ impl Gen7BattleStatCalculator {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The calculation used for health points.
|
/// The calculation used for health points.
|
||||||
fn calculate_health_stat(&self, pokemon: &Pokemon) -> u32 {
|
fn calculate_health_stat(&self, pokemon: &Pokemon) -> Result<u32> {
|
||||||
let base = pokemon.form().get_base_stat(Statistic::HP) as u32;
|
let base = pokemon.form().get_base_stat(Statistic::HP) as u32;
|
||||||
let iv = pokemon.individual_values().hp() as u32;
|
let iv = pokemon.individual_values().hp() as u32;
|
||||||
let ev = pokemon.effort_values().hp() as u32;
|
let ev = pokemon.effort_values().hp() as u32;
|
||||||
let level = pokemon.level() as u32;
|
let level = pokemon.level() as u32;
|
||||||
(((2 * base + iv + (ev / 4)) * level) / 100) + level + 10
|
Ok((((2 * base + iv + (ev / 4)) * level) / 100) + level + 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The calculation used for all other stats
|
/// The calculation used for all other stats
|
||||||
fn calculate_other_stat(&self, pokemon: &Pokemon, stat: Statistic) -> u32 {
|
fn calculate_other_stat(&self, pokemon: &Pokemon, stat: Statistic) -> Result<u32> {
|
||||||
let base = pokemon.form().get_base_stat(stat) as u32;
|
let base = pokemon.form().get_base_stat(stat) as u32;
|
||||||
let iv = pokemon.individual_values().get_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 ev = pokemon.effort_values().get_stat(stat) as u32;
|
||||||
let level = pokemon.level() as u32;
|
let level = pokemon.level() as u32;
|
||||||
let unmodified = (((2 * base + iv + (ev / 4)) * level) / 100) + 5;
|
let unmodified = (((2 * base + iv + (ev / 4)) * level) / 100) + 5;
|
||||||
return (unmodified as f32 * pokemon.nature().get_stat_modifier(stat)) as u32;
|
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.
|
/// 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) -> f32 {
|
fn get_stat_boost_modifier(&self, pokemon: &Pokemon, stat: Statistic) -> Result<f32> {
|
||||||
let boost = pokemon.stat_boost(stat);
|
let boost = pokemon.stat_boost(stat);
|
||||||
match boost {
|
Ok(match boost {
|
||||||
-6 => 2.0 / 8.0,
|
-6 => 2.0 / 8.0,
|
||||||
-5 => 2.0 / 7.0,
|
-5 => 2.0 / 7.0,
|
||||||
-4 => 2.0 / 6.0,
|
-4 => 2.0 / 6.0,
|
||||||
|
@ -74,31 +75,35 @@ impl Gen7BattleStatCalculator {
|
||||||
4 => 6.0 / 2.0,
|
4 => 6.0 / 2.0,
|
||||||
5 => 7.0 / 2.0,
|
5 => 7.0 / 2.0,
|
||||||
6 => 8.0 / 2.0,
|
6 => 8.0 / 2.0,
|
||||||
_ => panic!("Stat boost was out of expected range of -6 to 6"),
|
_ => bail!("Stat boost was out of expected range of -6 to 6"),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BattleStatCalculator for Gen7BattleStatCalculator {
|
impl BattleStatCalculator for Gen7BattleStatCalculator {
|
||||||
fn calculate_flat_stats(&self, pokemon: &Pokemon, stats: &StatisticSet<u32>) {
|
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::HP, self.calculate_health_stat(pokemon)?);
|
||||||
stats.set_stat(Statistic::Attack, self.calculate_other_stat(pokemon, Statistic::Attack));
|
stats.set_stat(
|
||||||
|
Statistic::Attack,
|
||||||
|
self.calculate_other_stat(pokemon, Statistic::Attack)?,
|
||||||
|
);
|
||||||
stats.set_stat(
|
stats.set_stat(
|
||||||
Statistic::Defense,
|
Statistic::Defense,
|
||||||
self.calculate_other_stat(pokemon, Statistic::Defense),
|
self.calculate_other_stat(pokemon, Statistic::Defense)?,
|
||||||
);
|
);
|
||||||
stats.set_stat(
|
stats.set_stat(
|
||||||
Statistic::SpecialAttack,
|
Statistic::SpecialAttack,
|
||||||
self.calculate_other_stat(pokemon, Statistic::SpecialAttack),
|
self.calculate_other_stat(pokemon, Statistic::SpecialAttack)?,
|
||||||
);
|
);
|
||||||
stats.set_stat(
|
stats.set_stat(
|
||||||
Statistic::SpecialDefense,
|
Statistic::SpecialDefense,
|
||||||
self.calculate_other_stat(pokemon, Statistic::SpecialDefense),
|
self.calculate_other_stat(pokemon, Statistic::SpecialDefense)?,
|
||||||
);
|
);
|
||||||
stats.set_stat(Statistic::Speed, self.calculate_other_stat(pokemon, Statistic::Speed));
|
stats.set_stat(Statistic::Speed, self.calculate_other_stat(pokemon, Statistic::Speed)?);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calculate_flat_stat(&self, pokemon: &Pokemon, stat: Statistic) -> u32 {
|
fn calculate_flat_stat(&self, pokemon: &Pokemon, stat: Statistic) -> Result<u32> {
|
||||||
if stat == Statistic::HP {
|
if stat == Statistic::HP {
|
||||||
self.calculate_health_stat(pokemon)
|
self.calculate_health_stat(pokemon)
|
||||||
} else {
|
} else {
|
||||||
|
@ -106,29 +111,33 @@ impl BattleStatCalculator for Gen7BattleStatCalculator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calculate_boosted_stats(&self, pokemon: &Pokemon, stats: &StatisticSet<u32>) {
|
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::HP, self.calculate_boosted_stat(pokemon, Statistic::HP)?);
|
||||||
stats.set_stat(
|
stats.set_stat(
|
||||||
Statistic::Attack,
|
Statistic::Attack,
|
||||||
self.calculate_boosted_stat(pokemon, Statistic::Attack),
|
self.calculate_boosted_stat(pokemon, Statistic::Attack)?,
|
||||||
);
|
);
|
||||||
stats.set_stat(
|
stats.set_stat(
|
||||||
Statistic::Defense,
|
Statistic::Defense,
|
||||||
self.calculate_boosted_stat(pokemon, Statistic::Defense),
|
self.calculate_boosted_stat(pokemon, Statistic::Defense)?,
|
||||||
);
|
);
|
||||||
stats.set_stat(
|
stats.set_stat(
|
||||||
Statistic::SpecialAttack,
|
Statistic::SpecialAttack,
|
||||||
self.calculate_boosted_stat(pokemon, Statistic::SpecialAttack),
|
self.calculate_boosted_stat(pokemon, Statistic::SpecialAttack)?,
|
||||||
);
|
);
|
||||||
stats.set_stat(
|
stats.set_stat(
|
||||||
Statistic::SpecialDefense,
|
Statistic::SpecialDefense,
|
||||||
self.calculate_boosted_stat(pokemon, Statistic::SpecialDefense),
|
self.calculate_boosted_stat(pokemon, Statistic::SpecialDefense)?,
|
||||||
);
|
);
|
||||||
stats.set_stat(Statistic::Speed, self.calculate_boosted_stat(pokemon, Statistic::Speed));
|
stats.set_stat(
|
||||||
|
Statistic::Speed,
|
||||||
|
self.calculate_boosted_stat(pokemon, Statistic::Speed)?,
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calculate_boosted_stat(&self, pokemon: &Pokemon, stat: Statistic) -> u32 {
|
fn calculate_boosted_stat(&self, pokemon: &Pokemon, stat: Statistic) -> Result<u32> {
|
||||||
(self.calculate_flat_stat(pokemon, stat) as f32 * self.get_stat_boost_modifier(pokemon, stat)) as u32
|
Ok((self.calculate_flat_stat(pokemon, stat)? as f32 * self.get_stat_boost_modifier(pokemon, stat)?) as u32)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,10 +154,10 @@ pub mod tests {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub BattleStatCalculator{}
|
pub BattleStatCalculator{}
|
||||||
impl BattleStatCalculator for BattleStatCalculator {
|
impl BattleStatCalculator for BattleStatCalculator {
|
||||||
fn calculate_flat_stats(&self, pokemon: &Pokemon, stats: &StatisticSet<u32>);
|
fn calculate_flat_stats(&self, pokemon: &Pokemon, stats: &StatisticSet<u32>) -> Result<()>;
|
||||||
fn calculate_flat_stat(&self, pokemon: &Pokemon, stat: Statistic) -> u32;
|
fn calculate_flat_stat(&self, pokemon: &Pokemon, stat: Statistic) -> Result<u32>;
|
||||||
fn calculate_boosted_stats(&self, pokemon: &Pokemon, stats: &StatisticSet<u32>);
|
fn calculate_boosted_stats(&self, pokemon: &Pokemon, stats: &StatisticSet<u32>) -> Result<()>;
|
||||||
fn calculate_boosted_stat(&self, pokemon: &Pokemon, stat: Statistic) -> u32;
|
fn calculate_boosted_stat(&self, pokemon: &Pokemon, stat: Statistic) -> Result<u32>;
|
||||||
}
|
}
|
||||||
impl ValueIdentifiable for BattleStatCalculator {
|
impl ValueIdentifiable for BattleStatCalculator {
|
||||||
fn value_identifier(&self) -> ValueIdentifier{
|
fn value_identifier(&self) -> ValueIdentifier{
|
||||||
|
|
|
@ -27,7 +27,7 @@ pub trait DamageLibrary: std::fmt::Debug + ValueIdentifiable {
|
||||||
target: &Arc<Pokemon>,
|
target: &Arc<Pokemon>,
|
||||||
hit_number: u8,
|
hit_number: u8,
|
||||||
hit_data: &HitData,
|
hit_data: &HitData,
|
||||||
) -> u8;
|
) -> Result<u8>;
|
||||||
|
|
||||||
/// Returns whether a specified hit should be critical or not.
|
/// Returns whether a specified hit should be critical or not.
|
||||||
fn is_critical(
|
fn is_critical(
|
||||||
|
@ -65,7 +65,7 @@ impl Gen7DamageLibrary {
|
||||||
target: &Arc<Pokemon>,
|
target: &Arc<Pokemon>,
|
||||||
hit_number: u8,
|
hit_number: u8,
|
||||||
hit_data: &HitData,
|
hit_data: &HitData,
|
||||||
) -> f32 {
|
) -> Result<f32> {
|
||||||
let user = executing_move.user();
|
let user = executing_move.user();
|
||||||
let offensive_stat;
|
let offensive_stat;
|
||||||
let defensive_stat;
|
let defensive_stat;
|
||||||
|
@ -144,7 +144,7 @@ impl Gen7DamageLibrary {
|
||||||
&mut stat_modifier
|
&mut stat_modifier
|
||||||
);
|
);
|
||||||
|
|
||||||
stat_modifier
|
Ok(stat_modifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the damage modifier. This is a value that defaults to 1.0, but can be modified by scripts
|
/// Gets the damage modifier. This is a value that defaults to 1.0, but can be modified by scripts
|
||||||
|
@ -155,7 +155,7 @@ impl Gen7DamageLibrary {
|
||||||
target: &Arc<Pokemon>,
|
target: &Arc<Pokemon>,
|
||||||
hit_number: u8,
|
hit_number: u8,
|
||||||
_hit_data: &HitData,
|
_hit_data: &HitData,
|
||||||
) -> f32 {
|
) -> Result<f32> {
|
||||||
let mut modifier = 1.0;
|
let mut modifier = 1.0;
|
||||||
script_hook!(
|
script_hook!(
|
||||||
change_damage_modifier,
|
change_damage_modifier,
|
||||||
|
@ -165,7 +165,7 @@ impl Gen7DamageLibrary {
|
||||||
hit_number,
|
hit_number,
|
||||||
&mut modifier
|
&mut modifier
|
||||||
);
|
);
|
||||||
modifier
|
Ok(modifier)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,8 +183,8 @@ impl DamageLibrary for Gen7DamageLibrary {
|
||||||
|
|
||||||
let level_modifier = ((2.0 * executing_move.user().level() as f32) / 5.0).floor() + 2.0;
|
let level_modifier = ((2.0 * executing_move.user().level() as f32) / 5.0).floor() + 2.0;
|
||||||
let base_power = hit_data.base_power();
|
let base_power = hit_data.base_power();
|
||||||
let stat_modifier = self.get_stat_modifier(executing_move, target, hit_number, hit_data);
|
let stat_modifier = self.get_stat_modifier(executing_move, target, hit_number, hit_data)?;
|
||||||
let damage_modifier = self.get_damage_modifier(executing_move, target, hit_number, hit_data);
|
let damage_modifier = self.get_damage_modifier(executing_move, target, hit_number, hit_data)?;
|
||||||
|
|
||||||
let mut float_damage = (level_modifier * base_power as f32).floor();
|
let mut float_damage = (level_modifier * base_power as f32).floor();
|
||||||
float_damage = (float_damage * stat_modifier).floor();
|
float_damage = (float_damage * stat_modifier).floor();
|
||||||
|
@ -264,9 +264,9 @@ impl DamageLibrary for Gen7DamageLibrary {
|
||||||
target: &Arc<Pokemon>,
|
target: &Arc<Pokemon>,
|
||||||
hit_number: u8,
|
hit_number: u8,
|
||||||
_hit_data: &HitData,
|
_hit_data: &HitData,
|
||||||
) -> u8 {
|
) -> Result<u8> {
|
||||||
if executing_move.use_move().category() == MoveCategory::Status {
|
if executing_move.use_move().category() == MoveCategory::Status {
|
||||||
return 0;
|
return Ok(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut base_power = executing_move.use_move().base_power();
|
let mut base_power = executing_move.use_move().base_power();
|
||||||
|
@ -278,7 +278,7 @@ impl DamageLibrary for Gen7DamageLibrary {
|
||||||
hit_number,
|
hit_number,
|
||||||
&mut base_power
|
&mut base_power
|
||||||
);
|
);
|
||||||
base_power
|
Ok(base_power)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_critical(
|
fn is_critical(
|
||||||
|
|
|
@ -115,7 +115,8 @@ impl DynamicLibrary for DynamicLibraryImpl {
|
||||||
/// combinations, returns None. Note that ItemScripts are immutable, as their script should be
|
/// combinations, returns None. Note that ItemScripts are immutable, as their script should be
|
||||||
/// shared between all different usages.
|
/// shared between all different usages.
|
||||||
fn load_item_script(&self, _key: &Arc<dyn Item>) -> Result<Option<Arc<dyn ItemScript>>> {
|
fn load_item_script(&self, _key: &Arc<dyn Item>) -> Result<Option<Arc<dyn ItemScript>>> {
|
||||||
todo!()
|
// TODO
|
||||||
|
Err(anyhow::anyhow!("Not implemented yet"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ use crate::dynamic_data::VolatileScriptsOwner;
|
||||||
use crate::dynamic_data::{is_valid_target, ScriptWrapper};
|
use crate::dynamic_data::{is_valid_target, ScriptWrapper};
|
||||||
use crate::dynamic_data::{ChoiceQueue, ScriptContainer};
|
use crate::dynamic_data::{ChoiceQueue, ScriptContainer};
|
||||||
use crate::dynamic_data::{ScriptCategory, ScriptSource, ScriptSourceData};
|
use crate::dynamic_data::{ScriptCategory, ScriptSource, ScriptSourceData};
|
||||||
use crate::{script_hook, StringKey, ValueIdentifiable, ValueIdentifier};
|
use crate::{script_hook, PkmnError, StringKey, ValueIdentifiable, ValueIdentifier};
|
||||||
|
|
||||||
/// A pokemon battle, with any amount of sides and pokemon per side.
|
/// A pokemon battle, with any amount of sides and pokemon per side.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -271,7 +271,13 @@ impl Battle {
|
||||||
let side = choice.user().get_battle_side_index();
|
let side = choice.user().get_battle_side_index();
|
||||||
match side {
|
match side {
|
||||||
Some(side) => {
|
Some(side) => {
|
||||||
self.sides[side as usize].set_choice(choice);
|
self.sides
|
||||||
|
.get(side as usize)
|
||||||
|
.ok_or(PkmnError::IndexOutOfBounds {
|
||||||
|
index: side as usize,
|
||||||
|
len: self.sides.len(),
|
||||||
|
})?
|
||||||
|
.set_choice(choice)?;
|
||||||
self.check_choices_set_and_run()?;
|
self.check_choices_set_and_run()?;
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
@ -286,7 +292,7 @@ impl Battle {
|
||||||
if !side.all_choices_set() {
|
if !side.all_choices_set() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
if !side.all_slots_filled() {
|
if !side.all_slots_filled()? {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -361,10 +367,7 @@ impl Battle {
|
||||||
if let Some(script) = self.weather.get() {
|
if let Some(script) = self.weather.get() {
|
||||||
let lock = script.read();
|
let lock = script.read();
|
||||||
Ok(Some(
|
Ok(Some(
|
||||||
lock.as_ref()
|
lock.as_ref().ok_or(PkmnError::UnableToAcquireLock)?.name().clone(),
|
||||||
.ok_or(anyhow!("Failed to get a lock on weather"))?
|
|
||||||
.name()
|
|
||||||
.clone(),
|
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
|
@ -383,8 +386,8 @@ impl VolatileScriptsOwner for Battle {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScriptSource for Battle {
|
impl ScriptSource for Battle {
|
||||||
fn get_script_count(&self) -> usize {
|
fn get_script_count(&self) -> Result<usize> {
|
||||||
1
|
Ok(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
|
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
|
||||||
|
@ -396,8 +399,9 @@ impl ScriptSource for Battle {
|
||||||
scripts.push((&self.volatile_scripts).into());
|
scripts.push((&self.volatile_scripts).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) {
|
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) -> Result<()> {
|
||||||
self.get_own_scripts(scripts);
|
self.get_own_scripts(scripts);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use anyhow_ext::anyhow;
|
|
||||||
use std::fmt::{Debug, Formatter};
|
use std::fmt::{Debug, Formatter};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
@ -7,7 +6,7 @@ use crate::dynamic_data::models::executing_move::ExecutingMove;
|
||||||
use crate::dynamic_data::models::pokemon::Pokemon;
|
use crate::dynamic_data::models::pokemon::Pokemon;
|
||||||
use crate::dynamic_data::script_handling::ScriptSource;
|
use crate::dynamic_data::script_handling::ScriptSource;
|
||||||
use crate::utils::Random;
|
use crate::utils::Random;
|
||||||
use crate::{script_hook, ValueIdentifiable, ValueIdentifier};
|
use crate::{script_hook, PkmnError, ValueIdentifiable, ValueIdentifier};
|
||||||
|
|
||||||
/// The RNG for a battle.
|
/// The RNG for a battle.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -37,21 +36,21 @@ impl BattleRandom {
|
||||||
pub fn get(&self) -> Result<i32> {
|
pub fn get(&self) -> Result<i32> {
|
||||||
match self.get_rng().lock() {
|
match self.get_rng().lock() {
|
||||||
Ok(mut l) => Ok(l.get()),
|
Ok(mut l) => Ok(l.get()),
|
||||||
Err(_) => Err(anyhow!("Failed to get a RNG lock")),
|
Err(_) => Err(PkmnError::UnableToAcquireLock.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get a random 32 bit integer between 0 and max.
|
/// Get a random 32 bit integer between 0 and max.
|
||||||
pub fn get_max(&self, max: i32) -> Result<i32> {
|
pub fn get_max(&self, max: i32) -> Result<i32> {
|
||||||
match self.get_rng().lock() {
|
match self.get_rng().lock() {
|
||||||
Ok(mut l) => Ok(l.get_max(max)),
|
Ok(mut l) => Ok(l.get_max(max)),
|
||||||
Err(_) => Err(anyhow!("Failed to get a RNG lock")),
|
Err(_) => Err(PkmnError::UnableToAcquireLock.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get a random 32 bit integer between min and max.
|
/// Get a random 32 bit integer between min and max.
|
||||||
pub fn get_between(&self, min: i32, max: i32) -> Result<i32> {
|
pub fn get_between(&self, min: i32, max: i32) -> Result<i32> {
|
||||||
match self.get_rng().lock() {
|
match self.get_rng().lock() {
|
||||||
Ok(mut l) => Ok(l.get_between(min, max)),
|
Ok(mut l) => Ok(l.get_between(min, max)),
|
||||||
Err(_) => Err(anyhow!("Failed to get a RNG lock")),
|
Err(_) => Err(PkmnError::UnableToAcquireLock.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,7 +84,7 @@ impl BattleRandom {
|
||||||
if chance > 0.0 {
|
if chance > 0.0 {
|
||||||
match self.get_rng().lock() {
|
match self.get_rng().lock() {
|
||||||
Ok(mut l) => Ok(l.get_float() < (chance / 100.0)),
|
Ok(mut l) => Ok(l.get_float() < (chance / 100.0)),
|
||||||
Err(_) => Err(anyhow!("Failed to get a RNG lock")),
|
Err(_) => Err(PkmnError::UnableToAcquireLock.into()),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Ok(false)
|
Ok(false)
|
||||||
|
|
|
@ -2,20 +2,19 @@ use std::ops::Deref;
|
||||||
use std::sync::atomic::{AtomicBool, AtomicU8, Ordering};
|
use std::sync::atomic::{AtomicBool, AtomicU8, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::{anyhow, Result};
|
||||||
use parking_lot::lock_api::RwLockReadGuard;
|
use parking_lot::lock_api::RwLockReadGuard;
|
||||||
use parking_lot::{RawRwLock, RwLock};
|
use parking_lot::{RawRwLock, RwLock};
|
||||||
|
|
||||||
use crate::dynamic_data::choices::TurnChoice;
|
use crate::dynamic_data::choices::TurnChoice;
|
||||||
use crate::dynamic_data::event_hooks::Event;
|
use crate::dynamic_data::event_hooks::Event;
|
||||||
use crate::dynamic_data::models::battle::Battle;
|
use crate::dynamic_data::models::battle::Battle;
|
||||||
use crate::dynamic_data::models::battle_party::BattleParty;
|
|
||||||
use crate::dynamic_data::models::pokemon::Pokemon;
|
use crate::dynamic_data::models::pokemon::Pokemon;
|
||||||
use crate::dynamic_data::script_handling::{ScriptSource, ScriptSourceData, ScriptWrapper};
|
use crate::dynamic_data::script_handling::{ScriptSource, ScriptSourceData, ScriptWrapper};
|
||||||
use crate::dynamic_data::Script;
|
use crate::dynamic_data::Script;
|
||||||
use crate::dynamic_data::ScriptSet;
|
use crate::dynamic_data::ScriptSet;
|
||||||
use crate::dynamic_data::VolatileScriptsOwner;
|
use crate::dynamic_data::VolatileScriptsOwner;
|
||||||
use crate::{script_hook, StringKey, ValueIdentifiable, ValueIdentifier};
|
use crate::{script_hook, PkmnError, StringKey, ValueIdentifiable, ValueIdentifier};
|
||||||
|
|
||||||
/// A side on a battle.
|
/// A side on a battle.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -107,8 +106,8 @@ impl BattleSide {
|
||||||
self.choices_set.load(Ordering::SeqCst)
|
self.choices_set.load(Ordering::SeqCst)
|
||||||
}
|
}
|
||||||
/// A reference to the battle we're part of.
|
/// A reference to the battle we're part of.
|
||||||
pub fn battle(&self) -> &Battle {
|
pub fn battle(&self) -> Result<&Battle> {
|
||||||
unsafe { self.battle.as_ref().unwrap() }
|
unsafe { self.battle.as_ref().ok_or(anyhow!("Battle was not set, but requested")) }
|
||||||
}
|
}
|
||||||
/// Whether or not this side has fled.
|
/// Whether or not this side has fled.
|
||||||
pub fn has_fled_battle(&self) -> bool {
|
pub fn has_fled_battle(&self) -> bool {
|
||||||
|
@ -127,60 +126,93 @@ impl BattleSide {
|
||||||
/// Returns true if there are slots that need to be filled with a new pokemon, that have parties
|
/// Returns true if there are slots that need to be filled with a new pokemon, that have parties
|
||||||
/// responsible for them. Returns false if all slots are filled with usable pokemon, or slots are
|
/// responsible for them. Returns false if all slots are filled with usable pokemon, or slots are
|
||||||
/// empty, but can't be filled by any party anymore.
|
/// empty, but can't be filled by any party anymore.
|
||||||
pub fn all_slots_filled(&self) -> bool {
|
pub fn all_slots_filled(&self) -> Result<bool> {
|
||||||
for (i, pokemon) in self.pokemon.read().iter().enumerate() {
|
for (i, pokemon) in self.pokemon.read().iter().enumerate() {
|
||||||
if (!pokemon.is_none() || !pokemon.as_ref().unwrap().is_usable())
|
match pokemon {
|
||||||
&& self.battle().can_slot_be_filled(self.index, i as u8)
|
Some(pokemon) => {
|
||||||
{
|
if !pokemon.is_usable() && self.battle()?.can_slot_be_filled(self.index, i as u8) {
|
||||||
return false;
|
return Ok(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
true
|
None => {
|
||||||
}
|
if self.battle()?.can_slot_be_filled(self.index, i as u8) {
|
||||||
|
return Ok(false);
|
||||||
/// Sets a choice for a Pokemon on this side.
|
}
|
||||||
pub(crate) fn set_choice(&self, choice: TurnChoice) {
|
|
||||||
for (index, pokemon_slot) in self.pokemon.read().iter().enumerate() {
|
|
||||||
if let Some(pokemon) = pokemon_slot {
|
|
||||||
if std::ptr::eq(pokemon.deref(), choice.user().deref()) {
|
|
||||||
self.choices.write()[index] = Some(choice);
|
|
||||||
self.choices_set.fetch_add(1, Ordering::SeqCst);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets a choice for a Pokemon on this side.
|
||||||
|
pub(crate) fn set_choice(&self, choice: TurnChoice) -> Result<()> {
|
||||||
|
for (index, pokemon_slot) in self.pokemon.read().iter().enumerate() {
|
||||||
|
if let Some(pokemon) = pokemon_slot {
|
||||||
|
if std::ptr::eq(pokemon.deref(), choice.user().deref()) {
|
||||||
|
let len = self.choices.read().len();
|
||||||
|
self.choices
|
||||||
|
.write()
|
||||||
|
.get_mut(index)
|
||||||
|
.ok_or(PkmnError::IndexOutOfBounds { index, len })?
|
||||||
|
.replace(choice);
|
||||||
|
self.choices_set.fetch_add(1, Ordering::SeqCst);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resets all choices on this side.
|
/// Resets all choices on this side.
|
||||||
pub fn reset_choices(&self) {
|
pub fn reset_choices(&self) {
|
||||||
let len = self.choices.read().len();
|
let len = self.choices.read().len();
|
||||||
for i in 0..len {
|
for i in 0..len {
|
||||||
self.choices.write()[i] = None;
|
self.choices.write().get_mut(i).take();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Forcibly removes a Pokemon from the field.
|
/// Forcibly removes a Pokemon from the field.
|
||||||
pub fn force_clear_pokemon(&mut self, index: u8) {
|
pub fn force_clear_pokemon(&mut self, index: u8) {
|
||||||
self.pokemon.write()[index as usize] = None;
|
self.pokemon.write().get_mut(index as usize).take();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Switches out a spot on the field for a different Pokemon.
|
/// Switches out a spot on the field for a different Pokemon.
|
||||||
pub fn set_pokemon(&self, index: u8, pokemon: Option<Arc<Pokemon>>) {
|
pub fn set_pokemon(&self, index: u8, pokemon: Option<Arc<Pokemon>>) -> Result<()> {
|
||||||
{
|
{
|
||||||
let old = &self.pokemon.read()[index as usize];
|
let mut write_lock = self.pokemon.write();
|
||||||
if let Some(old_pokemon) = old {
|
let old = write_lock.get_mut(index as usize).ok_or(PkmnError::IndexOutOfBounds {
|
||||||
|
index: index as usize,
|
||||||
|
len: self.pokemon_per_side as usize,
|
||||||
|
})?;
|
||||||
|
let old = match pokemon {
|
||||||
|
Some(pokemon) => old.replace(pokemon),
|
||||||
|
None => old.take(),
|
||||||
|
};
|
||||||
|
if let Some(old_pokemon) = old.clone() {
|
||||||
|
// We need to drop the lock before calling the hook, as it might try to access the
|
||||||
|
// side again.
|
||||||
|
drop(write_lock);
|
||||||
script_hook!(on_remove, old_pokemon,);
|
script_hook!(on_remove, old_pokemon,);
|
||||||
old_pokemon.set_on_battlefield(false);
|
old_pokemon.set_on_battlefield(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.pokemon.write()[index as usize] = pokemon;
|
|
||||||
let pokemon = &self.pokemon.read()[index as usize];
|
let pokemon = {
|
||||||
|
let read_lock = self.pokemon.read();
|
||||||
|
&read_lock
|
||||||
|
.get(index as usize)
|
||||||
|
.ok_or(PkmnError::IndexOutOfBounds {
|
||||||
|
index: index as usize,
|
||||||
|
len: read_lock.len(),
|
||||||
|
})?
|
||||||
|
.clone()
|
||||||
|
};
|
||||||
if let Some(pokemon) = pokemon {
|
if let Some(pokemon) = pokemon {
|
||||||
pokemon.set_battle_data(self.battle, self.index);
|
pokemon.set_battle_data(self.battle, self.index);
|
||||||
pokemon.set_on_battlefield(true);
|
pokemon.set_on_battlefield(true);
|
||||||
pokemon.set_battle_index(index);
|
pokemon.set_battle_index(index);
|
||||||
|
|
||||||
let battle = self.battle();
|
let battle = self.battle()?;
|
||||||
for side in battle.sides() {
|
for side in battle.sides() {
|
||||||
if side.index() == self.index {
|
if side.index() == self.index {
|
||||||
continue;
|
continue;
|
||||||
|
@ -197,12 +229,13 @@ impl BattleSide {
|
||||||
});
|
});
|
||||||
script_hook!(on_switch_in, pokemon, pokemon);
|
script_hook!(on_switch_in, pokemon, pokemon);
|
||||||
} else {
|
} else {
|
||||||
self.battle().event_hook().trigger(Event::Switch {
|
self.battle()?.event_hook().trigger(Event::Switch {
|
||||||
side_index: self.index,
|
side_index: self.index,
|
||||||
index,
|
index,
|
||||||
pokemon: None,
|
pokemon: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks whether a Pokemon is on the field in this side.
|
/// Checks whether a Pokemon is on the field in this side.
|
||||||
|
@ -217,20 +250,34 @@ impl BattleSide {
|
||||||
|
|
||||||
/// Marks a slot as unfillable. This happens when no parties are able to fill the slot anymore.
|
/// Marks a slot as unfillable. This happens when no parties are able to fill the slot anymore.
|
||||||
/// If this happens, the slot can not be used again.
|
/// If this happens, the slot can not be used again.
|
||||||
pub(crate) fn mark_slot_as_unfillable(&self, index: u8) {
|
pub(crate) fn mark_slot_as_unfillable(&self, index: u8) -> Result<()> {
|
||||||
self.fillable_slots[index as usize].store(false, Ordering::SeqCst);
|
self.fillable_slots
|
||||||
|
.get(index as usize)
|
||||||
|
.ok_or(PkmnError::IndexOutOfBounds {
|
||||||
|
index: index as usize,
|
||||||
|
len: self.fillable_slots.len(),
|
||||||
|
})?
|
||||||
|
.store(false, Ordering::SeqCst);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks whether a slot is unfillable or not.
|
/// Checks whether a slot is unfillable or not.
|
||||||
pub fn is_slot_unfillable(&self, pokemon: Arc<Pokemon>) -> bool {
|
pub fn is_slot_unfillable(&self, pokemon: Arc<Pokemon>) -> Result<bool> {
|
||||||
for (i, slot) in self.pokemon.read().iter().enumerate() {
|
for (i, slot) in self.pokemon.read().iter().enumerate() {
|
||||||
if let Some(p) = slot {
|
if let Some(p) = slot {
|
||||||
if std::ptr::eq(p.deref().deref(), pokemon.deref()) {
|
if std::ptr::eq(p.deref().deref(), pokemon.deref()) {
|
||||||
return self.fillable_slots[i].load(Ordering::Relaxed);
|
return Ok(self
|
||||||
|
.fillable_slots
|
||||||
|
.get(i)
|
||||||
|
.ok_or(PkmnError::IndexOutOfBounds {
|
||||||
|
index: i,
|
||||||
|
len: self.fillable_slots.len(),
|
||||||
|
})?
|
||||||
|
.load(Ordering::Relaxed));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
false
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks whether the side has been defeated.
|
/// Checks whether the side has been defeated.
|
||||||
|
@ -251,24 +298,24 @@ impl BattleSide {
|
||||||
/// Gets a random Pokemon on the given side.
|
/// Gets a random Pokemon on the given side.
|
||||||
pub fn get_random_creature_index(&self) -> Result<u8> {
|
pub fn get_random_creature_index(&self) -> Result<u8> {
|
||||||
// TODO: Consider adding parameter to only get index for available creatures.
|
// TODO: Consider adding parameter to only get index for available creatures.
|
||||||
Ok(self.battle().random().get_max(self.pokemon_per_side as i32)? as u8)
|
Ok(self.battle()?.random().get_max(self.pokemon_per_side as i32)? as u8)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Swap two Pokemon on a single side around.
|
/// Swap two Pokemon on a single side around.
|
||||||
pub fn swap_positions(&mut self, a: u8, b: u8) -> bool {
|
pub fn swap_positions(&mut self, a: u8, b: u8) -> Result<bool> {
|
||||||
// If out of range, don't allow swapping.
|
// If out of range, don't allow swapping.
|
||||||
if a >= self.pokemon_per_side || b >= self.pokemon_per_side {
|
if a >= self.pokemon_per_side || b >= self.pokemon_per_side {
|
||||||
return false;
|
return Ok(false);
|
||||||
}
|
}
|
||||||
// If the two indices are the same, don't allow swapping.
|
// If the two indices are the same, don't allow swapping.
|
||||||
if a == b {
|
if a == b {
|
||||||
return false;
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch parties for the two indices.
|
// Fetch parties for the two indices.
|
||||||
let mut party_a = None;
|
let mut party_a = None;
|
||||||
let mut party_b = None;
|
let mut party_b = None;
|
||||||
for party in self.battle().parties() {
|
for party in self.battle()?.parties() {
|
||||||
if party.is_responsible_for_index(self.index, a) {
|
if party.is_responsible_for_index(self.index, a) {
|
||||||
party_a = Some(party);
|
party_a = Some(party);
|
||||||
}
|
}
|
||||||
|
@ -277,21 +324,26 @@ impl BattleSide {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If either of the parties does not exist, fail.
|
// If either of the parties does not exist, fail.
|
||||||
if party_a.is_none() || party_b.is_none() {
|
let party_a = match party_a {
|
||||||
return false;
|
Some(party) => party,
|
||||||
}
|
None => return Ok(false),
|
||||||
|
};
|
||||||
|
let party_b = match party_b {
|
||||||
|
Some(party) => party,
|
||||||
|
None => return Ok(false),
|
||||||
|
};
|
||||||
// Don't allow swapping if different parties are responsible for the indices.
|
// Don't allow swapping if different parties are responsible for the indices.
|
||||||
if party_a.unwrap() as *const BattleParty != party_b.unwrap() as *const BattleParty {
|
if party_a.value_identifier() != party_b.value_identifier() {
|
||||||
return false;
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.pokemon.write().swap(a as usize, b as usize);
|
self.pokemon.write().swap(a as usize, b as usize);
|
||||||
self.battle().event_hook().trigger(Event::Swap {
|
self.battle()?.event_hook().trigger(Event::Swap {
|
||||||
side_index: self.index,
|
side_index: self.index,
|
||||||
index_a: a,
|
index_a: a,
|
||||||
index_b: b,
|
index_b: b,
|
||||||
});
|
});
|
||||||
true
|
Ok(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -301,15 +353,15 @@ impl VolatileScriptsOwner for BattleSide {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_volatile_script(&self, key: &StringKey) -> Result<Option<Arc<dyn Script>>> {
|
fn load_volatile_script(&self, key: &StringKey) -> Result<Option<Arc<dyn Script>>> {
|
||||||
self.battle()
|
self.battle()?
|
||||||
.library()
|
.library()
|
||||||
.load_script(self.into(), crate::ScriptCategory::Side, key)
|
.load_script(self.into(), crate::ScriptCategory::Side, key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScriptSource for BattleSide {
|
impl ScriptSource for BattleSide {
|
||||||
fn get_script_count(&self) -> usize {
|
fn get_script_count(&self) -> Result<usize> {
|
||||||
self.battle().get_script_count() + 1
|
Ok(self.battle()?.get_script_count()? + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
|
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
|
||||||
|
@ -320,9 +372,9 @@ impl ScriptSource for BattleSide {
|
||||||
scripts.push((&self.volatile_scripts).into());
|
scripts.push((&self.volatile_scripts).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) {
|
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) -> Result<()> {
|
||||||
self.get_own_scripts(scripts);
|
self.get_own_scripts(scripts);
|
||||||
self.battle().collect_scripts(scripts);
|
self.battle()?.collect_scripts(scripts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ use crate::dynamic_data::script_handling::{ScriptSource, ScriptSourceData, Scrip
|
||||||
use crate::dynamic_data::ScriptContainer;
|
use crate::dynamic_data::ScriptContainer;
|
||||||
use crate::dynamic_data::TargetList;
|
use crate::dynamic_data::TargetList;
|
||||||
use crate::static_data::{MoveData, TypeIdentifier};
|
use crate::static_data::{MoveData, TypeIdentifier};
|
||||||
use crate::{ValueIdentifiable, ValueIdentifier};
|
use crate::{PkmnError, ValueIdentifiable, ValueIdentifier};
|
||||||
|
|
||||||
/// A hit data is the data for a single hit, on a single target.
|
/// A hit data is the data for a single hit, on a single target.
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
|
@ -171,7 +171,14 @@ impl ExecutingMove {
|
||||||
if let Some(target) = target {
|
if let Some(target) = target {
|
||||||
if std::ptr::eq(target.deref().deref(), for_target.deref().deref()) {
|
if std::ptr::eq(target.deref().deref(), for_target.deref().deref()) {
|
||||||
let i = index * self.number_of_hits as usize + hit as usize;
|
let i = index * self.number_of_hits as usize + hit as usize;
|
||||||
return Ok(&self.hits[i]);
|
return match self.hits.get(i) {
|
||||||
|
Some(hit) => Ok(hit),
|
||||||
|
None => Err(PkmnError::IndexOutOfBounds {
|
||||||
|
index: i,
|
||||||
|
len: self.hits.len(),
|
||||||
|
}
|
||||||
|
.into()),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -202,14 +209,21 @@ impl ExecutingMove {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a hit based on its raw index.
|
/// Gets a hit based on its raw index.
|
||||||
pub(crate) fn get_hit_from_raw_index(&self, index: usize) -> &HitData {
|
pub(crate) fn get_hit_from_raw_index(&self, index: usize) -> Result<&HitData> {
|
||||||
&self.hits[index]
|
match self.hits.get(index) {
|
||||||
|
Some(hit) => Ok(hit),
|
||||||
|
None => Err(PkmnError::IndexOutOfBounds {
|
||||||
|
index,
|
||||||
|
len: self.hits.len(),
|
||||||
|
}
|
||||||
|
.into()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScriptSource for ExecutingMove {
|
impl ScriptSource for ExecutingMove {
|
||||||
fn get_script_count(&self) -> usize {
|
fn get_script_count(&self) -> Result<usize> {
|
||||||
1
|
Ok(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
|
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
|
||||||
|
@ -220,9 +234,10 @@ impl ScriptSource for ExecutingMove {
|
||||||
scripts.push((&self.script).into());
|
scripts.push((&self.script).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) {
|
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) -> Result<()> {
|
||||||
self.get_own_scripts(scripts);
|
self.get_own_scripts(scripts);
|
||||||
self.user.get_own_scripts(scripts);
|
self.user.get_own_scripts(scripts);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use anyhow::Result;
|
||||||
use std::sync::atomic::{AtomicU8, Ordering};
|
use std::sync::atomic::{AtomicU8, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -80,7 +81,7 @@ impl LearnedMove {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Restore the remaining PP by a certain amount. Will prevent it from going above max PP.
|
/// Restore the remaining PP by a certain amount. Will prevent it from going above max PP.
|
||||||
pub fn restore_uses(&self, mut uses: u8) {
|
pub fn restore_uses(&self, mut uses: u8) -> Result<()> {
|
||||||
self.remaining_pp
|
self.remaining_pp
|
||||||
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| {
|
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| {
|
||||||
if x + uses > self.max_pp {
|
if x + uses > self.max_pp {
|
||||||
|
@ -88,7 +89,8 @@ impl LearnedMove {
|
||||||
}
|
}
|
||||||
Some(x + uses)
|
Some(x + uses)
|
||||||
})
|
})
|
||||||
.unwrap();
|
.ok();
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,7 +112,7 @@ mod tests {
|
||||||
let data: Arc<dyn MoveData> = Arc::new(mock);
|
let data: Arc<dyn MoveData> = Arc::new(mock);
|
||||||
let learned_move = LearnedMove::new(data, MoveLearnMethod::Level);
|
let learned_move = LearnedMove::new(data, MoveLearnMethod::Level);
|
||||||
assert!(learned_move.try_use(15));
|
assert!(learned_move.try_use(15));
|
||||||
learned_move.restore_uses(5);
|
learned_move.restore_uses(5).unwrap();
|
||||||
assert_eq!(20, learned_move.remaining_pp());
|
assert_eq!(20, learned_move.remaining_pp());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,8 +22,8 @@ use crate::static_data::TypeIdentifier;
|
||||||
use crate::static_data::{Ability, Statistic};
|
use crate::static_data::{Ability, Statistic};
|
||||||
use crate::static_data::{ClampedStatisticSet, StatisticSet};
|
use crate::static_data::{ClampedStatisticSet, StatisticSet};
|
||||||
use crate::utils::Random;
|
use crate::utils::Random;
|
||||||
use crate::{script_hook, StringKey, ValueIdentifiable, ValueIdentifier};
|
use crate::{script_hook, PkmnError, StringKey, ValueIdentifiable, ValueIdentifier};
|
||||||
use anyhow::Result;
|
use anyhow::{anyhow, bail, Result};
|
||||||
|
|
||||||
/// An individual Pokemon as we know and love them.
|
/// An individual Pokemon as we know and love them.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -142,7 +142,7 @@ impl Pokemon {
|
||||||
.static_data()
|
.static_data()
|
||||||
.natures()
|
.natures()
|
||||||
.get_nature(nature)
|
.get_nature(nature)
|
||||||
.unwrap_or_else(|| panic!("Unknown nature name was given: {}.", &nature));
|
.ok_or(PkmnError::InvalidNatureName { nature: nature.clone() })?;
|
||||||
let mut pokemon = Self {
|
let mut pokemon = Self {
|
||||||
identifier: Default::default(),
|
identifier: Default::default(),
|
||||||
library,
|
library,
|
||||||
|
@ -180,7 +180,7 @@ impl Pokemon {
|
||||||
volatile: Default::default(),
|
volatile: Default::default(),
|
||||||
script_source_data: Default::default(),
|
script_source_data: Default::default(),
|
||||||
};
|
};
|
||||||
pokemon.recalculate_flat_stats();
|
pokemon.recalculate_flat_stats()?;
|
||||||
let health = pokemon.flat_stats().hp();
|
let health = pokemon.flat_stats().hp();
|
||||||
pokemon.current_health = AtomicU32::new(health);
|
pokemon.current_health = AtomicU32::new(health);
|
||||||
|
|
||||||
|
@ -257,20 +257,19 @@ impl Pokemon {
|
||||||
self.held_item.write().take()
|
self.held_item.write().take()
|
||||||
}
|
}
|
||||||
/// Makes the Pokemon uses its held item.
|
/// 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() {
|
if self.held_item.read().is_none() {
|
||||||
return false;
|
return Ok(false);
|
||||||
}
|
}
|
||||||
let script = self
|
let script = self
|
||||||
.library
|
.library
|
||||||
.load_item_script(self.held_item.read().as_ref().unwrap())
|
.load_item_script(self.held_item.read().as_ref().ok_or(PkmnError::UnableToAcquireLock)?)?;
|
||||||
.unwrap();
|
|
||||||
if script.is_none() {
|
if script.is_none() {
|
||||||
return false;
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: the entire item use part.
|
// TODO: the entire item use part.
|
||||||
todo!();
|
bail!("Not implemented yet.")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The remaining health points of the Pokemon.
|
/// The remaining health points of the Pokemon.
|
||||||
|
@ -331,7 +330,7 @@ impl Pokemon {
|
||||||
self.stat_boost.get_stat(stat)
|
self.stat_boost.get_stat(stat)
|
||||||
}
|
}
|
||||||
/// Change a boosted stat by a certain amount.
|
/// 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;
|
let mut prevent = false;
|
||||||
script_hook!(
|
script_hook!(
|
||||||
prevent_stat_boost_change,
|
prevent_stat_boost_change,
|
||||||
|
@ -343,7 +342,7 @@ impl Pokemon {
|
||||||
&mut prevent
|
&mut prevent
|
||||||
);
|
);
|
||||||
if prevent {
|
if prevent {
|
||||||
return false;
|
return Ok(false);
|
||||||
}
|
}
|
||||||
script_hook!(
|
script_hook!(
|
||||||
change_stat_boost_change,
|
change_stat_boost_change,
|
||||||
|
@ -354,7 +353,7 @@ impl Pokemon {
|
||||||
&mut diff_amount
|
&mut diff_amount
|
||||||
);
|
);
|
||||||
if diff_amount == 0 {
|
if diff_amount == 0 {
|
||||||
return false;
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut changed = false;
|
let mut changed = false;
|
||||||
|
@ -378,9 +377,9 @@ impl Pokemon {
|
||||||
new_value,
|
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.
|
/// The [individual values](https://bulbapedia.bulbagarden.net/wiki/Individual_values) of the Pokemon.
|
||||||
|
@ -416,15 +415,21 @@ impl Pokemon {
|
||||||
self.override_ability.is_some()
|
self.override_ability.is_some()
|
||||||
}
|
}
|
||||||
/// Returns the currently active ability.
|
/// 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 {
|
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()
|
.static_data()
|
||||||
.abilities()
|
.abilities()
|
||||||
.get(self.form().get_ability(self.ability_index))
|
.get(ability)
|
||||||
.unwrap()
|
.ok_or(PkmnError::InvalidAbilityName {
|
||||||
|
ability: ability.clone(),
|
||||||
|
})?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The script for the status.
|
/// 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
|
/// 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, level, nature, IV, or EV changes. This has a side effect of recalculating the boosted
|
||||||
/// stats, as those depend on the flat stats.
|
/// stats, as those depend on the flat stats.
|
||||||
pub fn recalculate_flat_stats(&self) {
|
pub fn recalculate_flat_stats(&self) -> Result<()> {
|
||||||
self.library
|
self.library
|
||||||
.stat_calculator()
|
.stat_calculator()
|
||||||
.calculate_flat_stats(self, &self.flat_stats);
|
.calculate_flat_stats(self, &self.flat_stats)?;
|
||||||
self.recalculate_boosted_stats();
|
self.recalculate_boosted_stats()?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
/// Calculates the boosted stats on the Pokemon, _without_ recalculating the flat stats.
|
/// Calculates the boosted stats on the Pokemon, _without_ recalculating the flat stats.
|
||||||
/// This should be called when a stat boost changes.
|
/// This should be called when a stat boost changes.
|
||||||
pub fn recalculate_boosted_stats(&self) {
|
pub fn recalculate_boosted_stats(&self) -> Result<()> {
|
||||||
self.library
|
self.library
|
||||||
.stat_calculator()
|
.stat_calculator()
|
||||||
.calculate_boosted_stats(self, &self.boosted_stats);
|
.calculate_boosted_stats(self, &self.boosted_stats)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Change the species of the Pokemon.
|
/// 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.species.write() = species.clone();
|
||||||
*self.form.write() = form.clone();
|
*self.form.write() = form.clone();
|
||||||
|
|
||||||
|
@ -474,7 +480,16 @@ impl Pokemon {
|
||||||
// If we're in battle, use the battle random for predictability
|
// If we're in battle, use the battle random for predictability
|
||||||
let r = self.battle_data.read();
|
let r = self.battle_data.read();
|
||||||
if let Some(data) = r.deref() {
|
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());
|
*self.gender.write() = species.get_random_gender(random.deref_mut());
|
||||||
} else {
|
} else {
|
||||||
// If we're not in battle, just use a new random.
|
// If we're not in battle, just use a new random.
|
||||||
|
@ -495,12 +510,13 @@ impl Pokemon {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Change the form of the Pokemon.
|
/// 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() {
|
if self.form().value_identifier() == form.value_identifier() {
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
*self.form.write() = form.clone();
|
*self.form.write() = form.clone();
|
||||||
|
|
||||||
|
@ -514,21 +530,21 @@ impl Pokemon {
|
||||||
self.weight.store(form.weight(), Ordering::SeqCst);
|
self.weight.store(form.weight(), Ordering::SeqCst);
|
||||||
self.height.store(form.height(), Ordering::SeqCst);
|
self.height.store(form.height(), Ordering::SeqCst);
|
||||||
|
|
||||||
|
let ability = self.active_ability()?;
|
||||||
let ability_script = self
|
let ability_script = self
|
||||||
.library
|
.library
|
||||||
.load_script(self.into(), ScriptCategory::Ability, self.active_ability().name())
|
.load_script(self.into(), ScriptCategory::Ability, ability.name())?;
|
||||||
.unwrap();
|
|
||||||
if let Some(ability_script) = ability_script {
|
if let Some(ability_script) = ability_script {
|
||||||
self.ability_script
|
self.ability_script
|
||||||
.set(ability_script)
|
.set(ability_script)
|
||||||
.as_ref()
|
.as_ref()
|
||||||
// Ensure the ability script gets initialized with the parameters for the ability.
|
// 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 {
|
} else {
|
||||||
self.ability_script.clear();
|
self.ability_script.clear();
|
||||||
}
|
}
|
||||||
let old_health = self.max_health();
|
let old_health = self.max_health();
|
||||||
self.recalculate_flat_stats();
|
self.recalculate_flat_stats()?;
|
||||||
let diff_health = (self.max_health() - old_health) as i32;
|
let diff_health = (self.max_health() - old_health) as i32;
|
||||||
if self.current_health() == 0 && (self.current_health() as i32) < -diff_health {
|
if self.current_health() == 0 && (self.current_health() as i32) < -diff_health {
|
||||||
self.current_health.store(0, Ordering::SeqCst);
|
self.current_health.store(0, Ordering::SeqCst);
|
||||||
|
@ -547,7 +563,8 @@ impl Pokemon {
|
||||||
form: form.clone(),
|
form: form.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether or not the Pokemon is useable in a battle.
|
/// Whether or not the Pokemon is useable in a battle.
|
||||||
|
@ -657,8 +674,14 @@ impl Pokemon {
|
||||||
script_hook!(on_remove, self,);
|
script_hook!(on_remove, self,);
|
||||||
|
|
||||||
if !battle.can_slot_be_filled(battle_data.battle_side_index(), battle_data.index()) {
|
if !battle.can_slot_be_filled(battle_data.battle_side_index(), battle_data.index()) {
|
||||||
battle.sides()[battle_data.battle_side_index() as usize]
|
battle
|
||||||
.mark_slot_as_unfillable(battle_data.index());
|
.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()?;
|
battle.validate_battle_state()?;
|
||||||
|
@ -695,14 +718,22 @@ impl Pokemon {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Learn a move.
|
/// 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 mut learned_moves = self.learned_moves().write();
|
||||||
let move_pos = learned_moves.iter().position(|a| a.is_none());
|
let move_pos = learned_moves.iter().position(|a| a.is_none());
|
||||||
if move_pos.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)));
|
learned_moves[move_pos.unwrap()] = Some(Arc::new(LearnedMove::new(move_data, learn_method)));
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes the current non-volatile status from the Pokemon.
|
/// Removes the current non-volatile status from the Pokemon.
|
||||||
|
@ -711,7 +742,7 @@ impl Pokemon {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Increases the level by a certain amount
|
/// 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
|
self.level
|
||||||
.fetch_update(Ordering::SeqCst, Ordering::Relaxed, |x| {
|
.fetch_update(Ordering::SeqCst, Ordering::Relaxed, |x| {
|
||||||
let max_level = self.library().static_data().settings().maximum_level();
|
let max_level = self.library().static_data().settings().maximum_level();
|
||||||
|
@ -721,8 +752,8 @@ impl Pokemon {
|
||||||
Some(x + amount)
|
Some(x + amount)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.expect("Failed to change level.");
|
.ok();
|
||||||
self.recalculate_flat_stats();
|
self.recalculate_flat_stats()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -770,14 +801,14 @@ impl PokemonBattleData {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScriptSource for Pokemon {
|
impl ScriptSource for Pokemon {
|
||||||
fn get_script_count(&self) -> usize {
|
fn get_script_count(&self) -> Result<usize> {
|
||||||
let mut c = 3;
|
let mut c = 3;
|
||||||
if let Some(battle_data) = &self.battle_data.read().deref() {
|
if let Some(battle_data) = &self.battle_data.read().deref() {
|
||||||
if let Some(battle) = battle_data.battle() {
|
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> {
|
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
|
||||||
|
@ -791,13 +822,14 @@ impl ScriptSource for Pokemon {
|
||||||
scripts.push((&self.volatile).into());
|
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);
|
self.get_own_scripts(scripts);
|
||||||
if let Some(battle_data) = &self.battle_data.read().deref() {
|
if let Some(battle_data) = &self.battle_data.read().deref() {
|
||||||
if let Some(battle) = battle_data.battle() {
|
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));
|
static_lib.expect_natures().return_const(Box::new(nature_lib));
|
||||||
|
|
||||||
let mut stat_calculator = MockBattleStatCalculator::new();
|
let mut stat_calculator = MockBattleStatCalculator::new();
|
||||||
stat_calculator.expect_calculate_flat_stats().returning(|_, _| {});
|
stat_calculator.expect_calculate_flat_stats().returning(|_, _| Ok(()));
|
||||||
stat_calculator.expect_calculate_boosted_stats().returning(|_, _| {});
|
stat_calculator
|
||||||
|
.expect_calculate_boosted_stats()
|
||||||
|
.returning(|_, _| Ok(()));
|
||||||
|
|
||||||
let mut lib = MockDynamicLibrary::new();
|
let mut lib = MockDynamicLibrary::new();
|
||||||
lib.expect_static_data().return_const(Box::new(static_lib));
|
lib.expect_static_data().return_const(Box::new(static_lib));
|
||||||
|
|
|
@ -64,7 +64,7 @@ impl PokemonBuilder {
|
||||||
&"hardy".into(),
|
&"hardy".into(),
|
||||||
)?;
|
)?;
|
||||||
for learned_move in self.learned_moves {
|
for learned_move in self.learned_moves {
|
||||||
p.learn_move(&learned_move, MoveLearnMethod::Unknown);
|
p.learn_move(&learned_move, MoveLearnMethod::Unknown)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(p)
|
Ok(p)
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use anyhow::Result;
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
|
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
|
@ -25,7 +26,7 @@ mod volatile_scripts_owner;
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! script_hook {
|
macro_rules! script_hook {
|
||||||
($hook_name: ident, $source: expr, $($parameters: expr),*) => {
|
($hook_name: ident, $source: expr, $($parameters: expr),*) => {
|
||||||
let mut aggregator = $source.get_script_iterator();
|
let mut aggregator = $source.get_script_iterator()?;
|
||||||
while let Some(script_container) = aggregator.get_next() {
|
while let Some(script_container) = aggregator.get_next() {
|
||||||
let script = script_container.get();
|
let script = script_container.get();
|
||||||
if let Some(script) = script {
|
if let Some(script) = script {
|
||||||
|
@ -89,20 +90,20 @@ pub struct ScriptSourceData {
|
||||||
pub trait ScriptSource {
|
pub trait ScriptSource {
|
||||||
/// Gets an iterator over all the scripts that are relevant to this script source. If the data
|
/// Gets an iterator over all the scripts that are relevant to this script source. If the data
|
||||||
/// has not been initialised, it will do so here.
|
/// has not been initialised, it will do so here.
|
||||||
fn get_script_iterator(&self) -> ScriptIterator {
|
fn get_script_iterator(&self) -> Result<ScriptIterator> {
|
||||||
let lock = self.get_script_source_data();
|
let lock = self.get_script_source_data();
|
||||||
if !lock.read().is_initialized {
|
if !lock.read().is_initialized {
|
||||||
let mut data = lock.write();
|
let mut data = lock.write();
|
||||||
data.scripts = Vec::with_capacity(self.get_script_count());
|
data.scripts = Vec::with_capacity(self.get_script_count()?);
|
||||||
self.collect_scripts(&mut data.scripts);
|
self.collect_scripts(&mut data.scripts)?;
|
||||||
data.is_initialized = true;
|
data.is_initialized = true;
|
||||||
}
|
}
|
||||||
ScriptIterator::new(&lock.read().scripts as *const Vec<ScriptWrapper>)
|
Ok(ScriptIterator::new(&lock.read().scripts as *const Vec<ScriptWrapper>))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The number of scripts that are expected to be relevant for this source. This generally is
|
/// The number of scripts that are expected to be relevant for this source. This generally is
|
||||||
/// the number of its own scripts + the number of scripts for any parents.
|
/// the number of its own scripts + the number of scripts for any parents.
|
||||||
fn get_script_count(&self) -> usize;
|
fn get_script_count(&self) -> Result<usize>;
|
||||||
/// Returns the underlying data required for us to be a script source.
|
/// Returns the underlying data required for us to be a script source.
|
||||||
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData>;
|
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData>;
|
||||||
/// This should add all scripts belonging to this source to the scripts Vec, disregarding its
|
/// This should add all scripts belonging to this source to the scripts Vec, disregarding its
|
||||||
|
@ -110,7 +111,7 @@ pub trait ScriptSource {
|
||||||
fn get_own_scripts(&self, scripts: &mut Vec<ScriptWrapper>);
|
fn get_own_scripts(&self, scripts: &mut Vec<ScriptWrapper>);
|
||||||
/// This should add all scripts that are relevant to the source the the scripts Vec, including
|
/// This should add all scripts that are relevant to the source the the scripts Vec, including
|
||||||
/// everything that belongs to its parents.
|
/// everything that belongs to its parents.
|
||||||
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>);
|
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) -> Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enum to store both ScriptSets and sets in a single value.
|
/// Enum to store both ScriptSets and sets in a single value.
|
||||||
|
@ -475,8 +476,8 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScriptSource for TestScriptSource {
|
impl ScriptSource for TestScriptSource {
|
||||||
fn get_script_count(&self) -> usize {
|
fn get_script_count(&self) -> Result<usize> {
|
||||||
1
|
Ok(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
|
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
|
||||||
|
@ -487,8 +488,9 @@ mod tests {
|
||||||
scripts.push((&self.script).into());
|
scripts.push((&self.script).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) {
|
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) -> Result<()> {
|
||||||
self.get_own_scripts(scripts);
|
self.get_own_scripts(scripts);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -499,7 +501,7 @@ mod tests {
|
||||||
script: ScriptContainer::default(),
|
script: ScriptContainer::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut aggregator = source.get_script_iterator();
|
let mut aggregator = source.get_script_iterator().unwrap();
|
||||||
aggregator.reset();
|
aggregator.reset();
|
||||||
assert!(aggregator.get_next().is_none());
|
assert!(aggregator.get_next().is_none());
|
||||||
aggregator.reset();
|
aggregator.reset();
|
||||||
|
@ -514,7 +516,7 @@ mod tests {
|
||||||
script: ScriptContainer::default(),
|
script: ScriptContainer::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut aggregator = source.get_script_iterator();
|
let mut aggregator = source.get_script_iterator().unwrap();
|
||||||
source.script.set(Arc::new(TestScript::new()));
|
source.script.set(Arc::new(TestScript::new()));
|
||||||
assert!(aggregator.get_next().is_some());
|
assert!(aggregator.get_next().is_some());
|
||||||
aggregator.reset();
|
aggregator.reset();
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::dynamic_data::{BattleStatCalculator, Gen7BattleStatCalculator, Pokemon};
|
use crate::dynamic_data::{BattleStatCalculator, Gen7BattleStatCalculator, Pokemon};
|
||||||
use crate::ffi::{ExternPointer, IdentifiablePointer, OwnedPtr};
|
use crate::ffi::{ExternPointer, IdentifiablePointer, NativeResult, OwnedPtr};
|
||||||
use crate::static_data::{Statistic, StatisticSet};
|
use crate::static_data::{Statistic, StatisticSet};
|
||||||
use std::ptr::drop_in_place;
|
use std::ptr::drop_in_place;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -25,8 +25,10 @@ extern "C" fn battle_stat_calculator_calculate_flat_stats(
|
||||||
ptr: ExternPointer<Box<dyn BattleStatCalculator>>,
|
ptr: ExternPointer<Box<dyn BattleStatCalculator>>,
|
||||||
pokemon: ExternPointer<Arc<Pokemon>>,
|
pokemon: ExternPointer<Arc<Pokemon>>,
|
||||||
mut stats: ExternPointer<Box<StatisticSet<u32>>>,
|
mut stats: ExternPointer<Box<StatisticSet<u32>>>,
|
||||||
) {
|
) -> NativeResult<()> {
|
||||||
ptr.as_ref().calculate_flat_stats(pokemon.as_ref(), stats.as_mut());
|
ptr.as_ref()
|
||||||
|
.calculate_flat_stats(pokemon.as_ref(), stats.as_mut())
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate a single flat stat of a Pokemon, disregarding stat boost
|
/// Calculate a single flat stat of a Pokemon, disregarding stat boost
|
||||||
|
@ -35,8 +37,8 @@ extern "C" fn battle_stat_calculator_calculate_flat_stat(
|
||||||
ptr: ExternPointer<Box<dyn BattleStatCalculator>>,
|
ptr: ExternPointer<Box<dyn BattleStatCalculator>>,
|
||||||
pokemon: ExternPointer<Arc<Pokemon>>,
|
pokemon: ExternPointer<Arc<Pokemon>>,
|
||||||
stat: Statistic,
|
stat: Statistic,
|
||||||
) -> u32 {
|
) -> NativeResult<u32> {
|
||||||
ptr.as_ref().calculate_flat_stat(pokemon.as_ref(), stat)
|
ptr.as_ref().calculate_flat_stat(pokemon.as_ref(), stat).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate all the boosted stats of a Pokemon, including stat boosts.
|
/// Calculate all the boosted stats of a Pokemon, including stat boosts.
|
||||||
|
@ -45,8 +47,10 @@ extern "C" fn battle_stat_calculator_calculate_boosted_stats(
|
||||||
ptr: ExternPointer<Box<dyn BattleStatCalculator>>,
|
ptr: ExternPointer<Box<dyn BattleStatCalculator>>,
|
||||||
pokemon: ExternPointer<Arc<Pokemon>>,
|
pokemon: ExternPointer<Arc<Pokemon>>,
|
||||||
mut stats: ExternPointer<Box<StatisticSet<u32>>>,
|
mut stats: ExternPointer<Box<StatisticSet<u32>>>,
|
||||||
) {
|
) -> NativeResult<()> {
|
||||||
ptr.as_ref().calculate_boosted_stats(pokemon.as_ref(), stats.as_mut());
|
ptr.as_ref()
|
||||||
|
.calculate_boosted_stats(pokemon.as_ref(), stats.as_mut())
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate a single boosted stat of a Pokemon, including stat boosts.
|
/// Calculate a single boosted stat of a Pokemon, including stat boosts.
|
||||||
|
@ -55,6 +59,6 @@ extern "C" fn battle_stat_calculator_calculate_boosted_stat(
|
||||||
ptr: ExternPointer<Box<dyn BattleStatCalculator>>,
|
ptr: ExternPointer<Box<dyn BattleStatCalculator>>,
|
||||||
pokemon: ExternPointer<Arc<Pokemon>>,
|
pokemon: ExternPointer<Arc<Pokemon>>,
|
||||||
stat: Statistic,
|
stat: Statistic,
|
||||||
) -> u32 {
|
) -> NativeResult<u32> {
|
||||||
ptr.as_ref().calculate_boosted_stat(pokemon.as_ref(), stat)
|
ptr.as_ref().calculate_boosted_stat(pokemon.as_ref(), stat).into()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::dynamic_data::{LearnedMove, MoveLearnMethod};
|
use crate::dynamic_data::{LearnedMove, MoveLearnMethod};
|
||||||
use crate::ffi::{ExternPointer, IdentifiablePointer, OwnedPtr};
|
use crate::ffi::{ExternPointer, IdentifiablePointer, NativeResult, OwnedPtr};
|
||||||
use crate::static_data::MoveData;
|
use crate::static_data::MoveData;
|
||||||
use std::ptr::drop_in_place;
|
use std::ptr::drop_in_place;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -60,6 +60,6 @@ extern "C" fn learned_move_restore_all_uses(learned_move: ExternPointer<Arc<Lear
|
||||||
|
|
||||||
/// Restore the remaining PP by a certain amount. Will prevent it from going above max PP.
|
/// Restore the remaining PP by a certain amount. Will prevent it from going above max PP.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
extern "C" fn learned_move_restore_uses(learned_move: ExternPointer<Arc<LearnedMove>>, amount: u8) {
|
extern "C" fn learned_move_restore_uses(learned_move: ExternPointer<Arc<LearnedMove>>, amount: u8) -> NativeResult<()> {
|
||||||
learned_move.as_ref().restore_uses(amount);
|
learned_move.as_ref().restore_uses(amount).into()
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,8 +127,11 @@ extern "C" fn pokemon_remove_held_item(ptr: ExternPointer<Arc<Pokemon>>) -> Iden
|
||||||
|
|
||||||
/// Makes the Pokemon uses its held item.
|
/// Makes the Pokemon uses its held item.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
extern "C" fn pokemon_consume_held_item(ptr: ExternPointer<Arc<Pokemon>>) -> u8 {
|
extern "C" fn pokemon_consume_held_item(ptr: ExternPointer<Arc<Pokemon>>) -> NativeResult<u8> {
|
||||||
u8::from(ptr.as_ref().consume_held_item())
|
match ptr.as_ref().consume_held_item() {
|
||||||
|
Ok(v) => NativeResult::ok(u8::from(v)),
|
||||||
|
Err(err) => NativeResult::err(err),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ffi_arc_getter!(Pokemon, current_health, u32);
|
ffi_arc_getter!(Pokemon, current_health, u32);
|
||||||
|
@ -200,8 +203,11 @@ extern "C" fn pokemon_change_stat_boost(
|
||||||
stat: Statistic,
|
stat: Statistic,
|
||||||
diff_amount: i8,
|
diff_amount: i8,
|
||||||
self_inflicted: u8,
|
self_inflicted: u8,
|
||||||
) -> u8 {
|
) -> NativeResult<u8> {
|
||||||
u8::from(ptr.as_ref().change_stat_boost(stat, diff_amount, self_inflicted == 1))
|
match ptr.as_ref().change_stat_boost(stat, diff_amount, self_inflicted == 1) {
|
||||||
|
Ok(v) => NativeResult::ok(u8::from(v)),
|
||||||
|
Err(e) => NativeResult::err(e),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a [individual value](https://bulbapedia.bulbagarden.net/wiki/Individual_values) of the Pokemon.
|
/// Gets a [individual value](https://bulbapedia.bulbagarden.net/wiki/Individual_values) of the Pokemon.
|
||||||
|
@ -212,9 +218,13 @@ extern "C" fn pokemon_get_individual_value(ptr: ExternPointer<Arc<Pokemon>>, sta
|
||||||
|
|
||||||
/// Modifies a [individual value](https://bulbapedia.bulbagarden.net/wiki/Individual_values) of the Pokemon.
|
/// Modifies a [individual value](https://bulbapedia.bulbagarden.net/wiki/Individual_values) of the Pokemon.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
extern "C" fn pokemon_set_individual_value(ptr: ExternPointer<Arc<Pokemon>>, stat: Statistic, value: u8) {
|
extern "C" fn pokemon_set_individual_value(
|
||||||
|
ptr: ExternPointer<Arc<Pokemon>>,
|
||||||
|
stat: Statistic,
|
||||||
|
value: u8,
|
||||||
|
) -> NativeResult<()> {
|
||||||
ptr.as_ref().individual_values().set_stat(stat, value);
|
ptr.as_ref().individual_values().set_stat(stat, value);
|
||||||
ptr.as_ref().recalculate_flat_stats();
|
ptr.as_ref().recalculate_flat_stats().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a [effort value](https://bulbapedia.bulbagarden.net/wiki/Effort_values) of the Pokemon.
|
/// Gets a [effort value](https://bulbapedia.bulbagarden.net/wiki/Effort_values) of the Pokemon.
|
||||||
|
@ -225,9 +235,13 @@ extern "C" fn pokemon_get_effort_value(ptr: ExternPointer<Arc<Pokemon>>, stat: S
|
||||||
|
|
||||||
/// Modifies a [effort value](https://bulbapedia.bulbagarden.net/wiki/Effort_values) of the Pokemon.
|
/// Modifies a [effort value](https://bulbapedia.bulbagarden.net/wiki/Effort_values) of the Pokemon.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
extern "C" fn pokemon_set_effort_value(ptr: ExternPointer<Arc<Pokemon>>, stat: Statistic, value: u8) {
|
extern "C" fn pokemon_set_effort_value(
|
||||||
|
ptr: ExternPointer<Arc<Pokemon>>,
|
||||||
|
stat: Statistic,
|
||||||
|
value: u8,
|
||||||
|
) -> NativeResult<()> {
|
||||||
ptr.as_ref().effort_values().set_stat(stat, value);
|
ptr.as_ref().effort_values().set_stat(stat, value);
|
||||||
ptr.as_ref().recalculate_flat_stats();
|
ptr.as_ref().recalculate_flat_stats().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the data for the battle the Pokemon is currently in. If the Pokemon is not in a battle, this
|
/// Gets the data for the battle the Pokemon is currently in. If the Pokemon is not in a battle, this
|
||||||
|
@ -263,8 +277,13 @@ extern "C" fn pokemon_is_ability_overriden(ptr: ExternPointer<Arc<Pokemon>>) ->
|
||||||
|
|
||||||
/// Returns the currently active ability.
|
/// Returns the currently active ability.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
extern "C" fn pokemon_active_ability(ptr: ExternPointer<Arc<Pokemon>>) -> IdentifiablePointer<Arc<dyn Ability>> {
|
extern "C" fn pokemon_active_ability(
|
||||||
ptr.as_ref().active_ability().into()
|
ptr: ExternPointer<Arc<Pokemon>>,
|
||||||
|
) -> NativeResult<IdentifiablePointer<Arc<dyn Ability>>> {
|
||||||
|
match ptr.as_ref().active_ability() {
|
||||||
|
Ok(v) => NativeResult::ok(v.clone().into()),
|
||||||
|
Err(e) => NativeResult::err(e),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether or not the Pokemon is allowed to gain experience.
|
/// Whether or not the Pokemon is allowed to gain experience.
|
||||||
|
@ -283,14 +302,14 @@ extern "C" fn pokemon_nature(ptr: ExternPointer<Arc<Pokemon>>) -> IdentifiablePo
|
||||||
/// stats, level, nature, IV, or EV changes. This has a side effect of recalculating the boosted
|
/// stats, level, nature, IV, or EV changes. This has a side effect of recalculating the boosted
|
||||||
/// stats, as those depend on the flat stats.
|
/// stats, as those depend on the flat stats.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
extern "C" fn pokemon_recalculate_flat_stats(ptr: ExternPointer<Arc<Pokemon>>) {
|
extern "C" fn pokemon_recalculate_flat_stats(ptr: ExternPointer<Arc<Pokemon>>) -> NativeResult<()> {
|
||||||
ptr.as_ref().recalculate_flat_stats()
|
ptr.as_ref().recalculate_flat_stats().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculates the boosted stats on the Pokemon. This should be called when a stat boost changes.
|
/// Calculates the boosted stats on the Pokemon. This should be called when a stat boost changes.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
extern "C" fn pokemon_recalculate_boosted_stats(ptr: ExternPointer<Arc<Pokemon>>) {
|
extern "C" fn pokemon_recalculate_boosted_stats(ptr: ExternPointer<Arc<Pokemon>>) -> NativeResult<()> {
|
||||||
ptr.as_ref().recalculate_boosted_stats()
|
ptr.as_ref().recalculate_boosted_stats().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Change the species of the Pokemon.
|
/// Change the species of the Pokemon.
|
||||||
|
@ -299,15 +318,19 @@ extern "C" fn pokemon_change_species(
|
||||||
ptr: ExternPointer<Arc<Pokemon>>,
|
ptr: ExternPointer<Arc<Pokemon>>,
|
||||||
species: ExternPointer<Arc<dyn Species>>,
|
species: ExternPointer<Arc<dyn Species>>,
|
||||||
form: ExternPointer<Arc<dyn Form>>,
|
form: ExternPointer<Arc<dyn Form>>,
|
||||||
) {
|
) -> NativeResult<()> {
|
||||||
ptr.as_ref()
|
ptr.as_ref()
|
||||||
.change_species(species.as_ref().clone(), form.as_ref().clone())
|
.change_species(species.as_ref().clone(), form.as_ref().clone())
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Change the form of the Pokemon.
|
/// Change the form of the Pokemon.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
extern "C" fn pokemon_change_form(ptr: ExternPointer<Arc<Pokemon>>, form: ExternPointer<Arc<dyn Form>>) {
|
extern "C" fn pokemon_change_form(
|
||||||
ptr.as_ref().change_form(form.as_ref())
|
ptr: ExternPointer<Arc<Pokemon>>,
|
||||||
|
form: ExternPointer<Arc<dyn Form>>,
|
||||||
|
) -> NativeResult<()> {
|
||||||
|
ptr.as_ref().change_form(form.as_ref()).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether or not the Pokemon is useable in a battle.
|
/// Whether or not the Pokemon is useable in a battle.
|
||||||
|
@ -347,8 +370,12 @@ extern "C" fn pokemon_learn_move(
|
||||||
ptr: ExternPointer<Arc<Pokemon>>,
|
ptr: ExternPointer<Arc<Pokemon>>,
|
||||||
move_name: *const c_char,
|
move_name: *const c_char,
|
||||||
learn_method: MoveLearnMethod,
|
learn_method: MoveLearnMethod,
|
||||||
) {
|
) -> NativeResult<()> {
|
||||||
unsafe { ptr.as_ref().learn_move(&CStr::from_ptr(move_name).into(), learn_method) }
|
unsafe {
|
||||||
|
ptr.as_ref()
|
||||||
|
.learn_move(&CStr::from_ptr(move_name).into(), learn_method)
|
||||||
|
.into()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes the current non-volatile status from the Pokemon.
|
/// Removes the current non-volatile status from the Pokemon.
|
||||||
|
|
45
src/lib.rs
45
src/lib.rs
|
@ -6,8 +6,14 @@
|
||||||
#![allow(ambiguous_glob_reexports)]
|
#![allow(ambiguous_glob_reexports)]
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
#![deny(clippy::missing_docs_in_private_items)]
|
#![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::unwrap_used)]
|
||||||
// #![deny(clippy::expect_used)]
|
// #![deny(clippy::expect_used)]
|
||||||
|
// #![deny(clippy::indexing_slicing)]
|
||||||
|
// #![deny(clippy::string_slice)]
|
||||||
|
// #![deny(clippy::exit)]
|
||||||
|
// #![deny(clippy::panic)]
|
||||||
// Features
|
// Features
|
||||||
#![feature(test)]
|
#![feature(test)]
|
||||||
#![feature(const_option)]
|
#![feature(const_option)]
|
||||||
|
@ -18,7 +24,6 @@
|
||||||
#![feature(unboxed_closures)]
|
#![feature(unboxed_closures)]
|
||||||
#![feature(trait_upcasting)]
|
#![feature(trait_upcasting)]
|
||||||
#![feature(lazy_cell)]
|
#![feature(lazy_cell)]
|
||||||
#![feature(is_some_and)]
|
|
||||||
|
|
||||||
//! PkmnLib
|
//! PkmnLib
|
||||||
//! PkmnLib is a full featured implementation of Pokemon. while currently focused on implementing
|
//! PkmnLib is a full featured implementation of Pokemon. while currently focused on implementing
|
||||||
|
@ -51,3 +56,41 @@ pub mod script_implementations;
|
||||||
pub mod static_data;
|
pub mod static_data;
|
||||||
/// The utils module includes misc utils that are used within PkmnLib
|
/// The utils module includes misc utils that are used within PkmnLib
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
/// The PkmnError enum is the error type for PkmnLib. It is used to return common errors from functions
|
||||||
|
/// that can fail. For uncommon errors, anyhow is used.
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum PkmnError {
|
||||||
|
/// The given index is out of bounds for the given length of the array.
|
||||||
|
#[error("The given index {index} is out of bounds for the given len {len}")]
|
||||||
|
IndexOutOfBounds {
|
||||||
|
/// The index that was given
|
||||||
|
index: usize,
|
||||||
|
/// The length of the array
|
||||||
|
len: usize,
|
||||||
|
},
|
||||||
|
/// We tried to get a lock on a mutex, but the lock was poisoned. This means that another thread
|
||||||
|
/// panicked while holding the lock. This is a fatal error.
|
||||||
|
#[error("Unable to acquire a lock")]
|
||||||
|
UnableToAcquireLock,
|
||||||
|
/// Unable to get ability
|
||||||
|
#[error("Unable to get ability {ability}")]
|
||||||
|
InvalidAbilityName {
|
||||||
|
/// The ability that was requested
|
||||||
|
ability: StringKey,
|
||||||
|
},
|
||||||
|
/// Unable to get move
|
||||||
|
#[error("Unable to get move {move_name}")]
|
||||||
|
InvalidMoveName {
|
||||||
|
/// The move that was requested
|
||||||
|
move_name: StringKey,
|
||||||
|
},
|
||||||
|
/// Unable to get nature
|
||||||
|
#[error("Unable to get nature {nature}")]
|
||||||
|
InvalidNatureName {
|
||||||
|
/// The nature that was requested
|
||||||
|
nature: StringKey,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ register! {
|
||||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||||
side: ExternRef<BattleSide>,
|
side: ExternRef<BattleSide>,
|
||||||
) -> ExternRef<Battle> {
|
) -> ExternRef<Battle> {
|
||||||
ExternRef::func_new(&env, side.value_func(&env).unwrap().battle())
|
ExternRef::func_new(&env, side.value_func(&env).unwrap().battle().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn battleside_get_pokemon(
|
fn battleside_get_pokemon(
|
||||||
|
|
|
@ -36,7 +36,7 @@ register! {
|
||||||
turn_choice: ExternRef<LearnedMove>,
|
turn_choice: ExternRef<LearnedMove>,
|
||||||
amount: u8,
|
amount: u8,
|
||||||
) {
|
) {
|
||||||
turn_choice.value_func(&env).unwrap().restore_uses(amount);
|
turn_choice.value_func(&env).unwrap().restore_uses(amount).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -135,7 +135,7 @@ register! {
|
||||||
self_inflicted: u8
|
self_inflicted: u8
|
||||||
) -> u8 {
|
) -> u8 {
|
||||||
unsafe{
|
unsafe{
|
||||||
u8::from(pokemon.value_func(&env).unwrap().change_stat_boost(transmute(stat), amount, self_inflicted == 1))
|
u8::from(pokemon.value_func(&env).unwrap().change_stat_boost(transmute(stat), amount, self_inflicted == 1).unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,7 +277,7 @@ register! {
|
||||||
env: FunctionEnvMut<WebAssemblyEnv>,
|
env: FunctionEnvMut<WebAssemblyEnv>,
|
||||||
pokemon: ExternRef<Pokemon>,
|
pokemon: ExternRef<Pokemon>,
|
||||||
) -> u8 {
|
) -> u8 {
|
||||||
if pokemon.value_func(&env).unwrap().consume_held_item() { 1 } else { 0 }
|
if pokemon.value_func(&env).unwrap().consume_held_item().unwrap() { 1 } else { 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pokemon_get_types_length(
|
fn pokemon_get_types_length(
|
||||||
|
@ -312,7 +312,7 @@ register! {
|
||||||
pokemon.value_func(&env).unwrap().change_species(
|
pokemon.value_func(&env).unwrap().change_species(
|
||||||
species.value_func_arc(&env).unwrap(),
|
species.value_func_arc(&env).unwrap(),
|
||||||
form.value_func_arc(&env).unwrap(),
|
form.value_func_arc(&env).unwrap(),
|
||||||
);
|
).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pokemon_change_form(
|
fn pokemon_change_form(
|
||||||
|
@ -322,7 +322,7 @@ register! {
|
||||||
) {
|
) {
|
||||||
pokemon.value_func(&env).unwrap().change_form(
|
pokemon.value_func(&env).unwrap().change_form(
|
||||||
&form.value_func_arc(&env).unwrap(),
|
&form.value_func_arc(&env).unwrap(),
|
||||||
);
|
).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pokemon_get_current_health(
|
fn pokemon_get_current_health(
|
||||||
|
|
|
@ -35,7 +35,7 @@ impl TestStep {
|
||||||
match self {
|
match self {
|
||||||
TestStep::SetPokemon { place, from_party } => {
|
TestStep::SetPokemon { place, from_party } => {
|
||||||
let p = battle.parties()[from_party[0] as usize].get_pokemon(from_party[1] as usize);
|
let p = battle.parties()[from_party[0] as usize].get_pokemon(from_party[1] as usize);
|
||||||
battle.sides_mut()[place[0] as usize].set_pokemon(place[1], p);
|
battle.sides_mut()[place[0] as usize].set_pokemon(place[1], p).unwrap();
|
||||||
}
|
}
|
||||||
TestStep::SetMoveChoice {
|
TestStep::SetMoveChoice {
|
||||||
for_pokemon,
|
for_pokemon,
|
||||||
|
|
|
@ -107,8 +107,8 @@ fn validate_assurance() {
|
||||||
|
|
||||||
let battle = Battle::new(lib.clone(), vec![party1, party2], false, 2, 1, None);
|
let battle = Battle::new(lib.clone(), vec![party1, party2], false, 2, 1, None);
|
||||||
|
|
||||||
battle.sides()[0].set_pokemon(0, Some(p1.clone()));
|
battle.sides()[0].set_pokemon(0, Some(p1.clone())).unwrap();
|
||||||
battle.sides()[1].set_pokemon(0, Some(p2.clone()));
|
battle.sides()[1].set_pokemon(0, Some(p2.clone())).unwrap();
|
||||||
|
|
||||||
let script = lib
|
let script = lib
|
||||||
.load_script(ScriptOwnerData::None, ScriptCategory::Move, &"assurance".into())
|
.load_script(ScriptOwnerData::None, ScriptCategory::Move, &"assurance".into())
|
||||||
|
|
Loading…
Reference in New Issue