Support for serializing and deserializing a Pokemon
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
This commit is contained in:
parent
f23fc43405
commit
12802ce542
|
@ -14,7 +14,7 @@ path = "src/lib.rs"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
ffi = []
|
ffi = []
|
||||||
serde = ["dep:serde"]
|
serde = ["dep:serde", "dep:serde-xml-rs", "atomig/serde"]
|
||||||
wasm = ["dep:wasmer"]
|
wasm = ["dep:wasmer"]
|
||||||
default = ["serde", "wasm", "ffi"]
|
default = ["serde", "wasm", "ffi"]
|
||||||
|
|
||||||
|
@ -53,6 +53,8 @@ hashbrown = "0.13.1"
|
||||||
indexmap = "1.8.2"
|
indexmap = "1.8.2"
|
||||||
parking_lot = "0.12.1"
|
parking_lot = "0.12.1"
|
||||||
serde = { version = "1.0.137", optional = true, features = ["derive"] }
|
serde = { version = "1.0.137", optional = true, features = ["derive"] }
|
||||||
|
serde_repr = "0.1.12"
|
||||||
|
serde-xml-rs = { version = "0.6.0", optional = true }
|
||||||
wasmer = { version = "3.3.0", optional = true, default-features = false, features = ["sys", "wat", "llvm"] }
|
wasmer = { version = "3.3.0", optional = true, default-features = false, features = ["sys", "wat", "llvm"] }
|
||||||
uuid = "1.2.2"
|
uuid = "1.2.2"
|
||||||
paste = { version = "1.0.8" }
|
paste = { version = "1.0.8" }
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use crate::dynamic_data::DynamicLibrary;
|
||||||
|
use crate::PkmnError;
|
||||||
use std::sync::atomic::{AtomicU8, Ordering};
|
use std::sync::atomic::{AtomicU8, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -10,7 +12,7 @@ pub struct LearnedMove {
|
||||||
/// The immutable move information of the move.
|
/// The immutable move information of the move.
|
||||||
move_data: Arc<dyn MoveData>,
|
move_data: Arc<dyn MoveData>,
|
||||||
/// The maximal power points for this move.
|
/// The maximal power points for this move.
|
||||||
max_pp: u8,
|
max_pp_modification: u8,
|
||||||
/// The amount of remaining power points. If this is 0, we can not use the move anymore.
|
/// The amount of remaining power points. If this is 0, we can not use the move anymore.
|
||||||
remaining_pp: AtomicU8,
|
remaining_pp: AtomicU8,
|
||||||
/// The way the move was learned.
|
/// The way the move was learned.
|
||||||
|
@ -20,6 +22,7 @@ pub struct LearnedMove {
|
||||||
/// The different ways a move can be learned.
|
/// The different ways a move can be learned.
|
||||||
#[derive(Copy, Clone, Debug, Default)]
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde_repr::Serialize_repr, serde_repr::Deserialize_repr))]
|
||||||
pub enum MoveLearnMethod {
|
pub enum MoveLearnMethod {
|
||||||
/// We do not know the learn method.
|
/// We do not know the learn method.
|
||||||
#[default]
|
#[default]
|
||||||
|
@ -31,11 +34,11 @@ pub enum MoveLearnMethod {
|
||||||
impl LearnedMove {
|
impl LearnedMove {
|
||||||
/// Instantiate a new learned move.
|
/// Instantiate a new learned move.
|
||||||
pub fn new(move_data: Arc<dyn MoveData>, learn_method: MoveLearnMethod) -> Self {
|
pub fn new(move_data: Arc<dyn MoveData>, learn_method: MoveLearnMethod) -> Self {
|
||||||
let max_pp = move_data.base_usages();
|
let base_usages = move_data.base_usages();
|
||||||
Self {
|
Self {
|
||||||
move_data,
|
move_data,
|
||||||
max_pp,
|
max_pp_modification: 0,
|
||||||
remaining_pp: AtomicU8::new(max_pp),
|
remaining_pp: AtomicU8::new(base_usages),
|
||||||
learn_method,
|
learn_method,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,8 +49,15 @@ impl LearnedMove {
|
||||||
}
|
}
|
||||||
/// The maximal power points for this move.
|
/// The maximal power points for this move.
|
||||||
pub fn max_pp(&self) -> u8 {
|
pub fn max_pp(&self) -> u8 {
|
||||||
self.max_pp
|
self.move_data.base_usages() + self.max_pp_modification
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The amount by which the maximal power points have been modified for this move.
|
||||||
|
/// This could for example be due to PP Ups.
|
||||||
|
pub fn max_pp_modification(&self) -> u8 {
|
||||||
|
self.max_pp_modification
|
||||||
|
}
|
||||||
|
|
||||||
/// The amount of remaining power points. If this is 0, we can not use the move anymore.
|
/// The amount of remaining power points. If this is 0, we can not use the move anymore.
|
||||||
pub fn remaining_pp(&self) -> u8 {
|
pub fn remaining_pp(&self) -> u8 {
|
||||||
self.remaining_pp.load(Ordering::Relaxed)
|
self.remaining_pp.load(Ordering::Relaxed)
|
||||||
|
@ -72,20 +82,44 @@ impl LearnedMove {
|
||||||
|
|
||||||
/// Set the remaining PP to the max amount of PP.
|
/// Set the remaining PP to the max amount of PP.
|
||||||
pub fn restore_all_uses(&self) {
|
pub fn restore_all_uses(&self) {
|
||||||
self.remaining_pp.store(self.max_pp, Ordering::SeqCst);
|
self.remaining_pp.store(self.max_pp(), Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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) {
|
||||||
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 {
|
let max = self.max_pp();
|
||||||
uses = self.max_pp - x;
|
if x + uses > max {
|
||||||
|
uses = max - x;
|
||||||
}
|
}
|
||||||
Some(x + uses)
|
Some(x + uses)
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Deserialize a learned move from a serialized learned move.
|
||||||
|
pub fn deserialize(
|
||||||
|
library: &Arc<dyn DynamicLibrary>,
|
||||||
|
value: &super::serialization::SerializedLearnedMove,
|
||||||
|
) -> anyhow_ext::Result<Self> {
|
||||||
|
Ok(Self {
|
||||||
|
move_data: {
|
||||||
|
let move_data =
|
||||||
|
library
|
||||||
|
.static_data()
|
||||||
|
.moves()
|
||||||
|
.get(&value.move_data)
|
||||||
|
.ok_or_else(|| PkmnError::InvalidMoveName {
|
||||||
|
move_name: value.move_data.clone(),
|
||||||
|
})?;
|
||||||
|
Arc::clone(&move_data)
|
||||||
|
},
|
||||||
|
max_pp_modification: value.max_pp_modification,
|
||||||
|
remaining_pp: AtomicU8::new(value.remaining_pp),
|
||||||
|
learn_method: value.learn_method,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -35,3 +35,6 @@ mod pokemon;
|
||||||
mod pokemon_builder;
|
mod pokemon_builder;
|
||||||
/// Data for a group of Pokemon belonging to a trainer.
|
/// Data for a group of Pokemon belonging to a trainer.
|
||||||
mod pokemon_party;
|
mod pokemon_party;
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
/// Data for serialization of models
|
||||||
|
pub(crate) mod serialization;
|
||||||
|
|
|
@ -188,7 +188,7 @@ impl Pokemon {
|
||||||
ability_index: ability,
|
ability_index: ability,
|
||||||
override_ability: None,
|
override_ability: None,
|
||||||
battle_data: RwLock::new(None),
|
battle_data: RwLock::new(None),
|
||||||
moves: RwLock::new([None, None, None, None]),
|
moves: RwLock::new(Default::default()),
|
||||||
allowed_experience: false,
|
allowed_experience: false,
|
||||||
types: RwLock::new(form.types().to_vec()),
|
types: RwLock::new(form.types().to_vec()),
|
||||||
is_egg: false,
|
is_egg: false,
|
||||||
|
@ -222,6 +222,10 @@ impl Pokemon {
|
||||||
pub fn form(&self) -> Arc<dyn Form> {
|
pub fn form(&self) -> Arc<dyn Form> {
|
||||||
self.data.form.read().clone()
|
self.data.form.read().clone()
|
||||||
}
|
}
|
||||||
|
/// Whether or not the Pokemon is showing as a different species than it actually is.
|
||||||
|
pub fn has_different_display_species(&self) -> bool {
|
||||||
|
self.data.display_species.is_some()
|
||||||
|
}
|
||||||
/// The species that should be displayed to the user. This handles stuff like the Illusion ability.
|
/// The species that should be displayed to the user. This handles stuff like the Illusion ability.
|
||||||
pub fn display_species(&self) -> Arc<dyn Species> {
|
pub fn display_species(&self) -> Arc<dyn Species> {
|
||||||
if let Some(v) = &self.data.display_species {
|
if let Some(v) = &self.data.display_species {
|
||||||
|
@ -230,6 +234,10 @@ impl Pokemon {
|
||||||
self.species()
|
self.species()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Whether or not the Pokemon is showing as a different form than it actually is.
|
||||||
|
pub fn has_different_display_form(&self) -> bool {
|
||||||
|
self.data.display_form.is_some()
|
||||||
|
}
|
||||||
/// The form that should be displayed to the user. This handles stuff like the Illusion ability.
|
/// The form that should be displayed to the user. This handles stuff like the Illusion ability.
|
||||||
pub fn display_form(&self) -> Arc<dyn Form> {
|
pub fn display_form(&self) -> Arc<dyn Form> {
|
||||||
if let Some(v) = &self.data.display_form {
|
if let Some(v) = &self.data.display_form {
|
||||||
|
@ -351,6 +359,11 @@ impl Pokemon {
|
||||||
&self.data.stat_boost
|
&self.data.stat_boost
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether or not this Pokemon is still an egg, and therefore cannot battle.
|
||||||
|
pub fn is_egg(&self) -> bool {
|
||||||
|
self.data.is_egg
|
||||||
|
}
|
||||||
|
|
||||||
/// The stats of the Pokemon including the stat boosts
|
/// The stats of the Pokemon including the stat boosts
|
||||||
pub fn boosted_stats(&self) -> &Arc<StatisticSet<u32>> {
|
pub fn boosted_stats(&self) -> &Arc<StatisticSet<u32>> {
|
||||||
&self.data.boosted_stats
|
&self.data.boosted_stats
|
||||||
|
@ -833,6 +846,136 @@ impl Pokemon {
|
||||||
self.recalculate_flat_stats()
|
self.recalculate_flat_stats()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Converts the Pokemon into a serializable form.
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
pub fn serialize(&self) -> Result<super::serialization::SerializedPokemon> {
|
||||||
|
self.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deserializes a Pokemon from a serializable form.
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
pub fn deserialize(
|
||||||
|
library: &Arc<dyn DynamicLibrary>,
|
||||||
|
value: &super::serialization::SerializedPokemon,
|
||||||
|
) -> Result<Self> {
|
||||||
|
let species = library
|
||||||
|
.static_data()
|
||||||
|
.species()
|
||||||
|
.get(&value.species)
|
||||||
|
.ok_or(PkmnError::InvalidSpeciesName {
|
||||||
|
species: value.species.clone(),
|
||||||
|
})?;
|
||||||
|
let form = species.get_form(&value.form).ok_or(PkmnError::InvalidFormName {
|
||||||
|
species: value.species.clone(),
|
||||||
|
form: value.form.clone(),
|
||||||
|
})?;
|
||||||
|
let display_species = if let Some(v) = &value.display_species {
|
||||||
|
Some(
|
||||||
|
library
|
||||||
|
.static_data()
|
||||||
|
.species()
|
||||||
|
.get(v)
|
||||||
|
.ok_or(PkmnError::InvalidSpeciesName { species: v.clone() })?,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let display_form = if let Some(v) = &value.display_form {
|
||||||
|
if let Some(display_species) = &display_species {
|
||||||
|
Some(display_species.get_form(v).ok_or(PkmnError::InvalidFormName {
|
||||||
|
species: display_species.name().clone(),
|
||||||
|
form: v.clone(),
|
||||||
|
})?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
data: Arc::new(PokemonData {
|
||||||
|
library: library.clone(),
|
||||||
|
species: RwLock::new(species),
|
||||||
|
form: RwLock::new(form),
|
||||||
|
display_species,
|
||||||
|
display_form,
|
||||||
|
level: Atomic::new(value.level),
|
||||||
|
experience: AtomicU32::new(value.experience),
|
||||||
|
personality_value: value.personality_value,
|
||||||
|
gender: RwLock::new(value.gender),
|
||||||
|
coloring: value.coloring,
|
||||||
|
held_item: RwLock::new({
|
||||||
|
if let Some(v) = &value.held_item {
|
||||||
|
Some(
|
||||||
|
library
|
||||||
|
.static_data()
|
||||||
|
.items()
|
||||||
|
.get(v)
|
||||||
|
.ok_or(PkmnError::InvalidItemName { item: v.clone() })?,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
current_health: AtomicU32::new(value.current_health),
|
||||||
|
weight: Atomic::new(value.weight),
|
||||||
|
height: Atomic::new(value.height),
|
||||||
|
flat_stats: Arc::new(Default::default()),
|
||||||
|
stat_boost: Arc::new(value.stat_boosts.clone()),
|
||||||
|
boosted_stats: Arc::new(Default::default()),
|
||||||
|
individual_values: Arc::new(value.individual_values.clone()),
|
||||||
|
effort_values: Arc::new(value.effort_values.clone()),
|
||||||
|
nature: {
|
||||||
|
library
|
||||||
|
.static_data()
|
||||||
|
.natures()
|
||||||
|
.get_nature(&value.nature)
|
||||||
|
.ok_or(PkmnError::InvalidNatureName {
|
||||||
|
nature: value.nature.clone(),
|
||||||
|
})?
|
||||||
|
},
|
||||||
|
nickname: value.nickname.clone(),
|
||||||
|
ability_index: value.ability_index,
|
||||||
|
override_ability: {
|
||||||
|
if let Some(v) = &value.override_ability {
|
||||||
|
Some(
|
||||||
|
library
|
||||||
|
.static_data()
|
||||||
|
.abilities()
|
||||||
|
.get(v)
|
||||||
|
.ok_or(PkmnError::InvalidAbilityName { ability: v.clone() })?,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
},
|
||||||
|
battle_data: Default::default(),
|
||||||
|
moves: {
|
||||||
|
let mut moves: [Option<Arc<LearnedMove>>; MAX_MOVES] = Default::default();
|
||||||
|
for (i, v) in value.moves.iter().enumerate() {
|
||||||
|
if i >= MAX_MOVES {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
moves
|
||||||
|
.get_mut(i)
|
||||||
|
.replace(&mut Some(Arc::new(LearnedMove::deserialize(library, v)?)));
|
||||||
|
}
|
||||||
|
RwLock::new(moves)
|
||||||
|
},
|
||||||
|
allowed_experience: value.allowed_experience,
|
||||||
|
types: RwLock::new(value.types.clone()),
|
||||||
|
is_egg: value.is_egg,
|
||||||
|
is_caught: false,
|
||||||
|
held_item_trigger_script: Default::default(),
|
||||||
|
ability_script: Default::default(),
|
||||||
|
status_script: Default::default(),
|
||||||
|
volatile: Arc::new(Default::default()),
|
||||||
|
script_source_data: Default::default(),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Take a weak reference to the Pokemon.
|
/// Take a weak reference to the Pokemon.
|
||||||
pub fn weak(&self) -> WeakPokemonReference {
|
pub fn weak(&self) -> WeakPokemonReference {
|
||||||
WeakPokemonReference {
|
WeakPokemonReference {
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
/// Serialization format for Pokemon data.
|
||||||
|
mod serialized_pokemon;
|
||||||
|
|
||||||
|
pub use serialized_pokemon::*;
|
|
@ -0,0 +1,135 @@
|
||||||
|
use crate::defines::LevelInt;
|
||||||
|
use crate::dynamic_data::{MoveLearnMethod, Pokemon, VolatileScriptsOwner};
|
||||||
|
use crate::static_data::{AbilityIndex, ClampedStatisticSet, Gender, TypeIdentifier};
|
||||||
|
use crate::StringKey;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
/// The serializable version of a [Pokemon](Pokemon).
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct SerializedPokemon {
|
||||||
|
/// The name of the species of the Pokemon.
|
||||||
|
pub species: StringKey,
|
||||||
|
/// The name of the form of the Pokemon.
|
||||||
|
pub form: StringKey,
|
||||||
|
/// The name of the species of the Pokemon that is displayed, if overridden.
|
||||||
|
pub display_species: Option<StringKey>,
|
||||||
|
/// The name of the form of the Pokemon that is displayed, if overridden.
|
||||||
|
pub display_form: Option<StringKey>,
|
||||||
|
/// The level of the Pokemon.
|
||||||
|
pub level: LevelInt,
|
||||||
|
/// The experience of the Pokemon.
|
||||||
|
pub experience: u32,
|
||||||
|
/// The personality value of the Pokemon.
|
||||||
|
pub personality_value: u32,
|
||||||
|
/// The gender of the Pokemon.
|
||||||
|
pub gender: Gender,
|
||||||
|
/// The coloring of the Pokemon.
|
||||||
|
pub coloring: u8,
|
||||||
|
/// The held item of the Pokemon.
|
||||||
|
pub held_item: Option<StringKey>,
|
||||||
|
/// The current health of the Pokemon.
|
||||||
|
pub current_health: u32,
|
||||||
|
/// The weight of the Pokemon.
|
||||||
|
pub weight: f32,
|
||||||
|
/// The height of the Pokemon.
|
||||||
|
pub height: f32,
|
||||||
|
/// The stat boosts of the Pokemon.
|
||||||
|
pub stat_boosts: ClampedStatisticSet<i8, -6, 6>,
|
||||||
|
/// The individual values of the Pokemon.
|
||||||
|
pub individual_values: ClampedStatisticSet<u8, 0, 31>,
|
||||||
|
/// The effort values of the Pokemon.
|
||||||
|
pub effort_values: ClampedStatisticSet<u8, 0, 252>,
|
||||||
|
/// The nature of the Pokemon.
|
||||||
|
pub nature: StringKey,
|
||||||
|
/// The nickname of the Pokemon.
|
||||||
|
pub nickname: Option<String>,
|
||||||
|
/// The ability index of the Pokemon.
|
||||||
|
pub ability_index: AbilityIndex,
|
||||||
|
/// The ability that is currently active on the Pokemon, if overridden.
|
||||||
|
pub override_ability: Option<StringKey>,
|
||||||
|
/// The moves of the Pokemon.
|
||||||
|
pub moves: Vec<SerializedLearnedMove>,
|
||||||
|
/// Whether the Pokemon is allowed to gain experience.
|
||||||
|
pub allowed_experience: bool,
|
||||||
|
/// The types of the Pokemon.
|
||||||
|
pub types: Vec<TypeIdentifier>,
|
||||||
|
/// Whether the Pokemon is an egg.
|
||||||
|
pub is_egg: bool,
|
||||||
|
/// The volatile scripts of the Pokemon.
|
||||||
|
pub volatile_scripts: Vec<StringKey>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The serializable version of a [LearnedMove](LearnedMove).
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct SerializedLearnedMove {
|
||||||
|
/// The name of the move.
|
||||||
|
pub move_data: StringKey,
|
||||||
|
/// A potential offset from the normal maximal power points.
|
||||||
|
pub max_pp_modification: u8,
|
||||||
|
/// The amount of remaining power points. If this is 0, we can not use the move anymore.
|
||||||
|
pub remaining_pp: u8,
|
||||||
|
/// The way the move was learned.
|
||||||
|
pub learn_method: MoveLearnMethod,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::from_over_into)] // From doesn't allow for Result.
|
||||||
|
impl Into<anyhow_ext::Result<SerializedPokemon>> for &Pokemon {
|
||||||
|
fn into(self) -> anyhow::Result<SerializedPokemon> {
|
||||||
|
Ok(SerializedPokemon {
|
||||||
|
species: self.species().name().clone(),
|
||||||
|
form: self.form().name().clone(),
|
||||||
|
display_species: if self.has_different_display_species() {
|
||||||
|
Some(self.display_species().name().clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
display_form: if self.has_different_display_form() {
|
||||||
|
Some(self.display_form().name().clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
level: self.level(),
|
||||||
|
experience: self.experience(),
|
||||||
|
personality_value: self.personality_value(),
|
||||||
|
gender: self.gender(),
|
||||||
|
coloring: self.coloring(),
|
||||||
|
held_item: {
|
||||||
|
let held_item = self.held_item().read();
|
||||||
|
held_item.as_ref().map(|held_item| held_item.name().clone())
|
||||||
|
},
|
||||||
|
current_health: self.current_health(),
|
||||||
|
weight: self.weight(),
|
||||||
|
height: self.height(),
|
||||||
|
stat_boosts: self.stat_boosts().deref().deref().clone(),
|
||||||
|
individual_values: self.individual_values().deref().deref().clone(),
|
||||||
|
effort_values: self.effort_values().deref().deref().clone(),
|
||||||
|
nature: self.library().static_data().natures().get_nature_name(self.nature())?,
|
||||||
|
nickname: self.nickname().clone(),
|
||||||
|
ability_index: *self.real_ability(),
|
||||||
|
override_ability: {
|
||||||
|
if self.is_ability_overriden() {
|
||||||
|
Some(self.active_ability()?.name().clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
},
|
||||||
|
moves: self
|
||||||
|
.learned_moves()
|
||||||
|
.read()
|
||||||
|
.iter()
|
||||||
|
.flatten()
|
||||||
|
.map(|lm| SerializedLearnedMove {
|
||||||
|
move_data: lm.move_data().name().clone(),
|
||||||
|
max_pp_modification: lm.max_pp_modification(),
|
||||||
|
remaining_pp: lm.remaining_pp(),
|
||||||
|
learn_method: lm.learn_method(),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
allowed_experience: self.allowed_experience_gain(),
|
||||||
|
types: self.types().to_vec(),
|
||||||
|
is_egg: self.is_egg(),
|
||||||
|
volatile_scripts: self.volatile_scripts().get_script_names(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -154,4 +154,14 @@ impl ScriptSet {
|
||||||
}
|
}
|
||||||
v
|
v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the names of all scripts in the set.
|
||||||
|
pub fn get_script_names(&self) -> Vec<StringKey> {
|
||||||
|
let s = self.scripts.read();
|
||||||
|
let mut v = Vec::with_capacity(s.deref().len());
|
||||||
|
for script in s.deref().keys() {
|
||||||
|
v.push(script.clone());
|
||||||
|
}
|
||||||
|
v
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -450,3 +450,51 @@ extern "C" fn pokemon_learn_move(
|
||||||
extern "C" fn pokemon_clear_status(handle: FFIHandle<Pokemon>) {
|
extern "C" fn pokemon_clear_status(handle: FFIHandle<Pokemon>) {
|
||||||
handle.from_ffi_handle().clear_status()
|
handle.from_ffi_handle().clear_status()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a serialized version of the Pokemon as xml.
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
#[no_mangle]
|
||||||
|
extern "C" fn pokemon_serialize_as_xml(handle: FFIHandle<Pokemon>) -> FFIResult<OwnedPtrString> {
|
||||||
|
let serialized = match handle.from_ffi_handle().serialize() {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(err) => return FFIResult::err(err),
|
||||||
|
};
|
||||||
|
let serialized =
|
||||||
|
match serde_xml_rs::to_string(&serialized).map_err(|err| anyhow!("Could not serialize Pokemon: {}", err)) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(err) => return FFIResult::err(err),
|
||||||
|
};
|
||||||
|
let cstring = match CString::new(serialized) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(err) => return FFIResult::err(anyhow!("Could not convert serialized Pokemon to CString: {}", err)),
|
||||||
|
};
|
||||||
|
FFIResult::ok(OwnedPtrString(cstring.into_raw()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts an XML representation of a Pokemon to a Pokemon.
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
#[no_mangle]
|
||||||
|
extern "C" fn pokemon_deserialize_from_xml(
|
||||||
|
library: FFIHandle<Arc<dyn DynamicLibrary>>,
|
||||||
|
str: OwnedPtrString,
|
||||||
|
) -> FFIResult<FFIHandle<Pokemon>> {
|
||||||
|
let str = unsafe {
|
||||||
|
match CString::from_raw(str.0).to_str() {
|
||||||
|
Ok(v) => v.to_string(),
|
||||||
|
Err(err) => return FFIResult::err(anyhow!("Could not read CString: {}", err)),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let deserialized = match serde_xml_rs::from_str::<crate::dynamic_data::serialization::SerializedPokemon>(&str)
|
||||||
|
.map_err(|err| anyhow!("Could not deserialize Pokemon: {}", err))
|
||||||
|
{
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(err) => return FFIResult::err(err),
|
||||||
|
};
|
||||||
|
|
||||||
|
let library = library.from_ffi_handle();
|
||||||
|
let pokemon = match Pokemon::deserialize(&library, &deserialized) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(err) => return FFIResult::err(err),
|
||||||
|
};
|
||||||
|
FFIResult::ok(FFIHandle::get_handle(pokemon.into()))
|
||||||
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ mod static_data;
|
||||||
pub(self) use ffi_handle::*;
|
pub(self) use ffi_handle::*;
|
||||||
|
|
||||||
/// Helper type for clearer functions.
|
/// Helper type for clearer functions.
|
||||||
#[repr(C)]
|
#[repr(transparent)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
struct OwnedPtrString(*mut c_char);
|
struct OwnedPtrString(*mut c_char);
|
||||||
|
|
||||||
|
|
14
src/lib.rs
14
src/lib.rs
|
@ -97,6 +97,12 @@ pub enum PkmnError {
|
||||||
/// The move that was requested
|
/// The move that was requested
|
||||||
move_name: StringKey,
|
move_name: StringKey,
|
||||||
},
|
},
|
||||||
|
/// Unable to get an item
|
||||||
|
#[error("Unable to get item {item}")]
|
||||||
|
InvalidItemName {
|
||||||
|
/// The item that was requested
|
||||||
|
item: StringKey,
|
||||||
|
},
|
||||||
/// Unable to get nature
|
/// Unable to get nature
|
||||||
#[error("Unable to get nature {nature}")]
|
#[error("Unable to get nature {nature}")]
|
||||||
InvalidNatureName {
|
InvalidNatureName {
|
||||||
|
@ -109,6 +115,14 @@ pub enum PkmnError {
|
||||||
/// The species that was requested
|
/// The species that was requested
|
||||||
species: StringKey,
|
species: StringKey,
|
||||||
},
|
},
|
||||||
|
/// Unable to get form
|
||||||
|
#[error("Unable to get form {form} for species {species}")]
|
||||||
|
InvalidFormName {
|
||||||
|
/// The species for which the form was requested
|
||||||
|
species: StringKey,
|
||||||
|
/// The form that was requested
|
||||||
|
form: StringKey,
|
||||||
|
},
|
||||||
/// Unable to get type
|
/// Unable to get type
|
||||||
#[error("Unable to get type {type_id}")]
|
#[error("Unable to get type {type_id}")]
|
||||||
InvalidTypeIdentifier {
|
InvalidTypeIdentifier {
|
||||||
|
|
|
@ -9,7 +9,8 @@ use crate::{PkmnError, StringKey};
|
||||||
/// A unique key that can be used to store a reference to a type. Opaque reference to a byte
|
/// A unique key that can be used to store a reference to a type. Opaque reference to a byte
|
||||||
/// internally.
|
/// internally.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash, Atom)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash, Atom)]
|
||||||
#[repr(C)]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
#[repr(transparent)]
|
||||||
pub struct TypeIdentifier {
|
pub struct TypeIdentifier {
|
||||||
/// The unique internal value.
|
/// The unique internal value.
|
||||||
val: u8,
|
val: u8,
|
||||||
|
|
|
@ -53,6 +53,7 @@ impl Ability for AbilityImpl {
|
||||||
/// An ability index allows us to find an ability on a form. It combines a bool for whether the
|
/// An ability index allows us to find an ability on a form. It combines a bool for whether the
|
||||||
/// ability is hidden or not, and then an index of the ability.
|
/// ability is hidden or not, and then an index of the ability.
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct AbilityIndex {
|
pub struct AbilityIndex {
|
||||||
/// Whether or not the ability we're referring to is a hidden ability.
|
/// Whether or not the ability we're referring to is a hidden ability.
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
/// Required for standard pokemon functions, but somewhat controversial nowadays. Consider adding a feature
|
/// Required for standard pokemon functions, but somewhat controversial nowadays. Consider adding a feature
|
||||||
/// that allows for a more progressive gender system for those that want it?
|
/// that allows for a more progressive gender system for those that want it?
|
||||||
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
|
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde_repr::Serialize_repr, serde_repr::Deserialize_repr))]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum Gender {
|
pub enum Gender {
|
||||||
/// The Pokemon has no gender.
|
/// The Pokemon has no gender.
|
||||||
|
|
|
@ -205,6 +205,7 @@ where
|
||||||
/// A clamped statistic set holds the 6 normal stats for a Pokemon, but ensures it always remains
|
/// A clamped statistic set holds the 6 normal stats for a Pokemon, but ensures it always remains
|
||||||
/// between two values (inclusive on the two values).
|
/// between two values (inclusive on the two values).
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
pub struct ClampedStatisticSet<T, const MIN: i64, const MAX: i64>
|
pub struct ClampedStatisticSet<T, const MIN: i64, const MAX: i64>
|
||||||
where
|
where
|
||||||
T: PrimitiveAtom,
|
T: PrimitiveAtom,
|
||||||
|
@ -376,6 +377,28 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T, const MIN: i64, const MAX: i64> Clone for ClampedStatisticSet<T, MIN, MAX>
|
||||||
|
where
|
||||||
|
T: PrimitiveAtom,
|
||||||
|
T: Atom,
|
||||||
|
T: PrimitiveAtomInteger,
|
||||||
|
<T as Atom>::Repr: PrimitiveAtomInteger,
|
||||||
|
T: AtomInteger,
|
||||||
|
T: NumCast,
|
||||||
|
T: PrimInt,
|
||||||
|
{
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
hp: Atomic::<T>::new(self.hp()),
|
||||||
|
attack: Atomic::<T>::new(self.attack()),
|
||||||
|
defense: Atomic::<T>::new(self.defense()),
|
||||||
|
special_attack: Atomic::<T>::new(self.special_attack()),
|
||||||
|
special_defense: Atomic::<T>::new(self.special_defense()),
|
||||||
|
speed: Atomic::<T>::new(self.speed()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use arcstr::ArcStr;
|
use arcstr::ArcStr;
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
|
use serde::{Deserializer, Serializer};
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
|
@ -170,6 +171,47 @@ const CRC_TABLE: &[u32] = &[
|
||||||
0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D,
|
0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl serde::Serialize for StringKey {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
serializer.serialize_str(self.str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A visitor for deserializing a StringKey.
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
struct StringKeyVisitor;
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl<'de> serde::de::Visitor<'de> for StringKeyVisitor {
|
||||||
|
type Value = String;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
|
||||||
|
formatter.write_str("a string")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: serde::de::Error,
|
||||||
|
{
|
||||||
|
Ok(String::from(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl<'de> serde::Deserialize<'de> for StringKey {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let str = deserializer.deserialize_string(StringKeyVisitor)?;
|
||||||
|
Ok(StringKey::new(&str))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
extern crate test;
|
extern crate test;
|
||||||
|
|
Loading…
Reference in New Issue