FFI for Pokemon class
This commit is contained in:
@@ -3,7 +3,8 @@ use std::sync::atomic::{AtomicBool, AtomicU32, AtomicU8, Ordering};
|
||||
use std::sync::{Arc, Weak};
|
||||
|
||||
use atomig::Atomic;
|
||||
use parking_lot::RwLock;
|
||||
use parking_lot::lock_api::RwLockReadGuard;
|
||||
use parking_lot::{RawRwLock, RwLock};
|
||||
|
||||
use crate::defines::{LevelInt, MAX_MOVES};
|
||||
use crate::dynamic_data::event_hooks::Event;
|
||||
@@ -21,18 +22,20 @@ use crate::static_data::{Ability, Statistic};
|
||||
use crate::static_data::{AbilityIndex, DataLibrary};
|
||||
use crate::static_data::{ClampedStatisticSet, StatisticSet};
|
||||
use crate::utils::Random;
|
||||
use crate::{script_hook, PkmnResult, StringKey};
|
||||
use crate::{script_hook, PkmnResult, StringKey, ValueIdentifiable, ValueIdentifier};
|
||||
|
||||
/// An individual Pokemon as we know and love them.
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "wasm", derive(unique_type_id_derive::UniqueTypeId))]
|
||||
pub struct Pokemon {
|
||||
/// A unique identifier so we know what value this is.
|
||||
identifier: ValueIdentifier,
|
||||
/// The library data of the Pokemon.
|
||||
library: Arc<DynamicLibrary>,
|
||||
/// The species of the Pokemon.
|
||||
species: Arc<Species>,
|
||||
species: RwLock<Arc<Species>>,
|
||||
/// The form of the Pokemon.
|
||||
form: Arc<Form>,
|
||||
form: RwLock<Arc<Form>>,
|
||||
|
||||
/// An optional display species of the Pokemon. If this is set, the client should display this
|
||||
/// species. An example of usage for this is the Illusion ability.
|
||||
@@ -49,7 +52,7 @@ pub struct Pokemon {
|
||||
unique_identifier: u32,
|
||||
|
||||
/// The gender of the Pokemon.
|
||||
gender: Gender,
|
||||
gender: RwLock<Gender>,
|
||||
/// The coloring of the Pokemon. Value 0 is the default, value 1 means shiny. Other values are
|
||||
/// currently not used, and can be used for other implementations.
|
||||
coloring: u8,
|
||||
@@ -96,7 +99,7 @@ pub struct Pokemon {
|
||||
allowed_experience: bool,
|
||||
|
||||
/// The current types of the Pokemon.
|
||||
types: Vec<TypeIdentifier>,
|
||||
types: RwLock<Vec<TypeIdentifier>>,
|
||||
/// Whether or not this Pokemon is an egg.
|
||||
is_egg: bool,
|
||||
/// Whether or not this Pokemon was caught this battle.
|
||||
@@ -142,15 +145,16 @@ impl Pokemon {
|
||||
.unwrap_or_else(|| panic!("Unknown nature name was given: {}.", &nature))
|
||||
.clone();
|
||||
let mut pokemon = Self {
|
||||
identifier: Default::default(),
|
||||
library,
|
||||
species,
|
||||
form: form.clone(),
|
||||
species: RwLock::new(species),
|
||||
form: RwLock::new(form.clone()),
|
||||
display_species: None,
|
||||
display_form: None,
|
||||
level,
|
||||
experience: AtomicU32::new(experience),
|
||||
unique_identifier,
|
||||
gender,
|
||||
gender: RwLock::new(gender),
|
||||
coloring,
|
||||
held_item: RwLock::new(None),
|
||||
current_health: AtomicU32::new(1),
|
||||
@@ -168,7 +172,7 @@ impl Pokemon {
|
||||
battle_data: RwLock::new(None),
|
||||
moves: RwLock::new([None, None, None, None]),
|
||||
allowed_experience: false,
|
||||
types: form.types().to_vec(),
|
||||
types: RwLock::new(form.types().to_vec()),
|
||||
is_egg: false,
|
||||
is_caught: false,
|
||||
held_item_trigger_script: ScriptContainer::default(),
|
||||
@@ -189,27 +193,27 @@ impl Pokemon {
|
||||
&self.library
|
||||
}
|
||||
/// The species of the Pokemon.
|
||||
pub fn species(&self) -> &Arc<Species> {
|
||||
&self.species
|
||||
pub fn species(&self) -> Arc<Species> {
|
||||
self.species.read().clone()
|
||||
}
|
||||
/// The form of the Pokemon.
|
||||
pub fn form(&self) -> &Arc<Form> {
|
||||
&self.form
|
||||
pub fn form(&self) -> Arc<Form> {
|
||||
self.form.read().clone()
|
||||
}
|
||||
/// The species that should be displayed to the user. This handles stuff like the Illusion ability.
|
||||
pub fn display_species(&self) -> &Arc<Species> {
|
||||
pub fn display_species(&self) -> Arc<Species> {
|
||||
if let Some(v) = &self.display_species {
|
||||
v
|
||||
v.clone()
|
||||
} else {
|
||||
&self.species
|
||||
self.species()
|
||||
}
|
||||
}
|
||||
/// The form that should be displayed to the user. This handles stuff like the Illusion ability.
|
||||
pub fn display_form(&self) -> &Arc<Form> {
|
||||
pub fn display_form(&self) -> Arc<Form> {
|
||||
if let Some(v) = &self.display_form {
|
||||
v
|
||||
v.clone()
|
||||
} else {
|
||||
&self.form
|
||||
self.form()
|
||||
}
|
||||
}
|
||||
/// The current level of the Pokemon.
|
||||
@@ -226,7 +230,7 @@ impl Pokemon {
|
||||
}
|
||||
/// The gender of the Pokemon.
|
||||
pub fn gender(&self) -> Gender {
|
||||
self.gender
|
||||
*self.gender.read()
|
||||
}
|
||||
/// The coloring of the Pokemon. Value 0 is the default, value 1 means shiny. Other values are
|
||||
/// currently not used, and can be used for other implementations.
|
||||
@@ -245,11 +249,11 @@ impl Pokemon {
|
||||
}
|
||||
false
|
||||
}
|
||||
/// Changes the held item of the Pokemon/
|
||||
/// Changes the held item of the Pokemon. Returns the previously held item.
|
||||
pub fn set_held_item(&self, item: &Arc<Item>) -> Option<Arc<Item>> {
|
||||
self.held_item.write().replace(item.clone())
|
||||
}
|
||||
/// Removes the held item from the Pokemon.
|
||||
/// Removes the held item from the Pokemon. Returns the previously held item.
|
||||
pub fn remove_held_item(&self) -> Option<Arc<Item>> {
|
||||
self.held_item.write().take()
|
||||
}
|
||||
@@ -300,8 +304,8 @@ impl Pokemon {
|
||||
&self.ability_index
|
||||
}
|
||||
/// The current types of the Pokemon.
|
||||
pub fn types(&self) -> &Vec<TypeIdentifier> {
|
||||
&self.types
|
||||
pub fn types(&self) -> RwLockReadGuard<'_, RawRwLock, Vec<TypeIdentifier>> {
|
||||
self.types.read()
|
||||
}
|
||||
/// The moves the Pokemon has learned. This is of a set length of [`MAX_MOVES`]. Empty move slots
|
||||
/// are defined by None.
|
||||
@@ -314,7 +318,7 @@ impl Pokemon {
|
||||
&self.flat_stats
|
||||
}
|
||||
|
||||
/// The stats of the Pokemon including the stat boosts
|
||||
/// The amount of boosts on a specific stat.
|
||||
pub fn stat_boosts(&self) -> &ClampedStatisticSet<i8, -6, 6> {
|
||||
&self.stat_boost
|
||||
}
|
||||
@@ -420,7 +424,7 @@ impl Pokemon {
|
||||
self.library
|
||||
.static_data()
|
||||
.abilities()
|
||||
.get(self.form.get_ability(self.ability_index))
|
||||
.get(self.form().get_ability(self.ability_index))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
@@ -444,14 +448,16 @@ impl Pokemon {
|
||||
&self.nature
|
||||
}
|
||||
|
||||
/// Calculates the flat stats on the Pokemon.
|
||||
/// Calculates the flat stats on the Pokemon. This should be called when for example the base
|
||||
/// stats, level, nature, IV, or EV changes. This has a side effect of recalculating the boosted
|
||||
/// stats, as those depend on the flat stats.
|
||||
pub fn recalculate_flat_stats(&self) {
|
||||
self.library
|
||||
.stat_calculator()
|
||||
.calculate_flat_stats(self, &self.flat_stats);
|
||||
self.recalculate_boosted_stats();
|
||||
}
|
||||
/// Calculates the boosted stats on the Pokemon.
|
||||
/// Calculates the boosted stats on the Pokemon. This should be called when a stat boost changes.
|
||||
pub fn recalculate_boosted_stats(&self) {
|
||||
self.library
|
||||
.stat_calculator()
|
||||
@@ -459,25 +465,25 @@ impl Pokemon {
|
||||
}
|
||||
|
||||
/// Change the species of the Pokemon.
|
||||
pub fn change_species(&mut self, species: Arc<Species>, form: Arc<Form>) {
|
||||
self.species = species.clone();
|
||||
self.form = form.clone();
|
||||
pub fn change_species(&self, species: Arc<Species>, form: Arc<Form>) {
|
||||
*self.species.write() = species.clone();
|
||||
*self.form.write() = form.clone();
|
||||
|
||||
// If the pokemon is genderless, but it's new species is not, we want to set its gender
|
||||
if self.gender != Gender::Genderless && species.gender_rate() < 0.0 {
|
||||
if self.gender() != Gender::Genderless && species.gender_rate() < 0.0 {
|
||||
// If we're in battle, use the battle random for predictability
|
||||
let r = self.battle_data.read();
|
||||
if let Some(data) = r.deref() {
|
||||
let mut random = data.battle().unwrap().random().get_rng().lock().unwrap();
|
||||
self.gender = species.get_random_gender(random.deref_mut());
|
||||
*self.gender.write() = species.get_random_gender(random.deref_mut());
|
||||
} else {
|
||||
// If we're not in battle, just use a new random.
|
||||
self.gender = species.get_random_gender(&mut Random::default());
|
||||
*self.gender.write() = species.get_random_gender(&mut Random::default());
|
||||
}
|
||||
}
|
||||
// Else if the new species is genderless, but the pokemon has a gender, make the creature genderless.
|
||||
else if species.gender_rate() < 0.0 && self.gender != Gender::Genderless {
|
||||
self.gender = Gender::Genderless;
|
||||
else if species.gender_rate() < 0.0 && self.gender() != Gender::Genderless {
|
||||
*self.gender.write() = Gender::Genderless;
|
||||
}
|
||||
let r = self.battle_data.read();
|
||||
if let Some(battle_data) = &r.deref() {
|
||||
@@ -492,15 +498,18 @@ impl Pokemon {
|
||||
}
|
||||
|
||||
/// Change the form of the Pokemon.
|
||||
pub fn change_form(&mut self, form: &Arc<Form>) {
|
||||
if Arc::ptr_eq(&self.form, form) {
|
||||
pub fn change_form(&self, form: &Arc<Form>) {
|
||||
if self.form().value_identifier() == form.value_identifier() {
|
||||
return;
|
||||
}
|
||||
self.form = form.clone();
|
||||
*self.form.write() = form.clone();
|
||||
|
||||
self.types.clear();
|
||||
for t in form.types() {
|
||||
self.types.push(*t);
|
||||
{
|
||||
let mut type_lock = self.types.write();
|
||||
type_lock.clear();
|
||||
for t in form.types() {
|
||||
type_lock.push(*t);
|
||||
}
|
||||
}
|
||||
self.weight.store(form.weight(), Ordering::SeqCst);
|
||||
self.height.store(form.height(), Ordering::SeqCst);
|
||||
@@ -572,8 +581,8 @@ impl Pokemon {
|
||||
data.on_battle_field.store(value, Ordering::SeqCst);
|
||||
if !value {
|
||||
self.volatile.clear();
|
||||
self.weight.store(self.form.weight(), Ordering::SeqCst);
|
||||
self.height.store(self.form.height(), Ordering::SeqCst);
|
||||
self.weight.store(self.form().weight(), Ordering::SeqCst);
|
||||
self.height.store(self.form().height(), Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -782,6 +791,12 @@ impl VolatileScriptsOwner for Pokemon {
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueIdentifiable for Pokemon {
|
||||
fn value_identifier(&self) -> ValueIdentifier {
|
||||
self.identifier
|
||||
}
|
||||
}
|
||||
|
||||
/// A source of damage. This should be as unique as possible.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[repr(u8)]
|
||||
@@ -821,7 +836,7 @@ pub mod test {
|
||||
0,
|
||||
&"test_nature".into(),
|
||||
);
|
||||
assert_eq!(pokemon.species.name(), &"foo".into());
|
||||
assert_eq!(pokemon.form.name(), &"default".into());
|
||||
assert_eq!(pokemon.species().name(), &"foo".into());
|
||||
assert_eq!(pokemon.form().name(), &"default".into());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user