From d8b8559c2eb0b89379193567cea49e4783e0bc8d Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Sat, 29 Jul 2023 12:57:52 +0200 Subject: [PATCH] Basic implementation of evolutions --- .../libraries/evolution_library.rs | 57 +++++++++ src/dynamic_data/libraries/mod.rs | 2 + src/dynamic_data/models/pokemon.rs | 14 ++- .../serialization/serialized_pokemon.rs | 3 + .../script_handling/item_script.rs | 4 +- src/dynamic_data/script_handling/script.rs | 4 +- src/ffi/ffi_handle.rs | 6 +- src/ffi/static_data/ability.rs | 8 +- src/ffi/static_data/mod.rs | 44 +++---- src/ffi/static_data/move_data.rs | 7 +- src/ffi/static_data/species.rs | 2 + .../wasm/export_registry/mod.rs | 40 +++--- .../export_registry/static_data/ability.rs | 4 +- .../wasm/export_registry/wasm_object.rs | 8 +- .../wasm/item_script.rs | 6 +- src/script_implementations/wasm/script.rs | 6 +- src/static_data/libraries/species_library.rs | 2 + src/static_data/mod.rs | 28 ++--- src/static_data/moves/secondary_effect.rs | 12 +- src/static_data/species_data/ability.rs | 12 +- .../species_data/evolution_data.rs | 118 +++++++++++++++++ src/static_data/species_data/mod.rs | 4 + src/static_data/species_data/species.rs | 24 ++++ tests/common/library_loader.rs | 119 ++++++++++++++++-- tests/data/Pokemon.json | 18 ++- 25 files changed, 437 insertions(+), 115 deletions(-) create mode 100644 src/dynamic_data/libraries/evolution_library.rs create mode 100644 src/static_data/species_data/evolution_data.rs diff --git a/src/dynamic_data/libraries/evolution_library.rs b/src/dynamic_data/libraries/evolution_library.rs new file mode 100644 index 0000000..a5dc14c --- /dev/null +++ b/src/dynamic_data/libraries/evolution_library.rs @@ -0,0 +1,57 @@ +use crate::dynamic_data::Pokemon; +use crate::static_data::{EvolutionMethod, TimeOfDay}; + +/// A library for handling the checking of evolution requirements. +pub trait EvolutionLibrary { + /// Checks if the given Pokemon fulfills the given evolution conditions. + fn pokemon_fulfills_evolution_conditions(&self, pokemon: &Pokemon, method: &EvolutionMethod) -> bool; +} + +/// A standard implementation of the Evolution Library. +pub struct EvolutionLibraryImpl; + +impl EvolutionLibrary for EvolutionLibraryImpl { + fn pokemon_fulfills_evolution_conditions(&self, pokemon: &Pokemon, method: &EvolutionMethod) -> bool { + match method { + EvolutionMethod::Level { level } => pokemon.level() >= *level, + EvolutionMethod::LevelGender { level, gender } => pokemon.level() >= *level && pokemon.gender() == *gender, + EvolutionMethod::Item { .. } => false, + EvolutionMethod::ItemGender { .. } => false, + EvolutionMethod::HoldItem { item } => pokemon.has_held_item(item), + EvolutionMethod::DayHoldItem { item } => { + let time_of_day = pokemon.library().misc_library().time_of_day(); + pokemon.has_held_item(item) && time_of_day == TimeOfDay::Morning || time_of_day == TimeOfDay::Day + } + EvolutionMethod::NightHoldItem { item } => { + let time_of_day = pokemon.library().misc_library().time_of_day(); + pokemon.has_held_item(item) && time_of_day == TimeOfDay::Evening || time_of_day == TimeOfDay::Night + } + EvolutionMethod::HasMove { move_name } => pokemon + .learned_moves() + .read() + .iter() + .any(|learned_move| learned_move.as_ref().is_some_and(|v| v.move_data().name() == move_name)), + EvolutionMethod::Happiness { happiness } => pokemon.happiness() >= *happiness, + EvolutionMethod::HappinessDay { happiness } => { + let time_of_day = pokemon.library().misc_library().time_of_day(); + pokemon.happiness() >= *happiness && time_of_day == TimeOfDay::Morning || time_of_day == TimeOfDay::Day + } + EvolutionMethod::HappinessNight { happiness } => { + let time_of_day = pokemon.library().misc_library().time_of_day(); + pokemon.happiness() >= *happiness && time_of_day == TimeOfDay::Evening + || time_of_day == TimeOfDay::Night + } + EvolutionMethod::Trade => false, + EvolutionMethod::TradeSpecies { .. } => false, + EvolutionMethod::TradeItem { .. } => false, + EvolutionMethod::Location { .. } => { + // TODO: Implement this. + false + } + EvolutionMethod::Custom { .. } => { + // TODO: Implement this. + false + } + } + } +} diff --git a/src/dynamic_data/libraries/mod.rs b/src/dynamic_data/libraries/mod.rs index 398a98a..9ce6105 100755 --- a/src/dynamic_data/libraries/mod.rs +++ b/src/dynamic_data/libraries/mod.rs @@ -19,3 +19,5 @@ pub(crate) mod dynamic_library; mod misc_library; /// Data for dynamically resolving scripts during battles. mod script_resolver; +/// Handling for checking evolution requirements. +mod evolution_library; diff --git a/src/dynamic_data/models/pokemon.rs b/src/dynamic_data/models/pokemon.rs index 0dde446..1fcfa1b 100755 --- a/src/dynamic_data/models/pokemon.rs +++ b/src/dynamic_data/models/pokemon.rs @@ -68,6 +68,9 @@ struct PokemonData { /// The height of the Pokemon in meters. height: Atomic, + /// The happiness of the Pokemon. Also known as friendship. + happiness: AtomicU8, + /// The stats of the Pokemon when disregarding any stat boosts. flat_stats: Arc>, /// The statistics boosts of the Pokemon. Will prevent the value from going above 6, and below @@ -158,6 +161,7 @@ impl Pokemon { .calculate_experience(species.growth_rate(), level)?; let weight = form.weight(); let height = form.height(); + let base_happiness = species.base_happiness(); let nature = library .static_data() .natures() @@ -198,6 +202,7 @@ impl Pokemon { status_script: ScriptContainer::default(), volatile: Default::default(), script_source_data: Default::default(), + happiness: AtomicU8::new(base_happiness), }; let pokemon = Self { @@ -326,11 +331,16 @@ impl Pokemon { pub fn set_weight(&self, weight: f32) { self.data.weight.store(weight, Ordering::Relaxed) } - /// The height of the Pokemon in meters. pub fn height(&self) -> f32 { self.data.height.load(Ordering::Relaxed) } + + /// The current happiness of the Pokemon. Also known as friendship. + pub fn happiness(&self) -> u8 { + self.data.happiness.load(Ordering::Relaxed) + } + /// An optional nickname of the Pokemon. pub fn nickname(&self) -> &Option { &self.data.nickname @@ -921,6 +931,7 @@ impl Pokemon { current_health: AtomicU32::new(value.current_health), weight: Atomic::new(value.weight), height: Atomic::new(value.height), + happiness: AtomicU8::new(value.happiness), flat_stats: Arc::new(Default::default()), stat_boost: Arc::new(value.stat_boosts.clone()), boosted_stats: Arc::new(Default::default()), @@ -1163,6 +1174,7 @@ pub mod test { }); species.expect_growth_rate().return_const(StringKey::empty()); species.expect_name().return_const(StringKey::new("test_species")); + species.expect_base_happiness().return_const(100); let s: Arc = Arc::new(species); Some(s) diff --git a/src/dynamic_data/models/serialization/serialized_pokemon.rs b/src/dynamic_data/models/serialization/serialized_pokemon.rs index 6226f33..dee6ffc 100644 --- a/src/dynamic_data/models/serialization/serialized_pokemon.rs +++ b/src/dynamic_data/models/serialization/serialized_pokemon.rs @@ -34,6 +34,8 @@ pub struct SerializedPokemon { pub weight: f32, /// The height of the Pokemon. pub height: f32, + /// The happiness of the Pokemon. + pub happiness: u8, /// The stat boosts of the Pokemon. pub stat_boosts: ClampedStatisticSet, /// The individual values of the Pokemon. @@ -101,6 +103,7 @@ impl Into> for &Pokemon { current_health: self.current_health(), weight: self.weight(), height: self.height(), + happiness: self.happiness(), stat_boosts: self.stat_boosts().deref().deref().clone(), individual_values: self.individual_values().deref().deref().clone(), effort_values: self.effort_values().deref().deref().clone(), diff --git a/src/dynamic_data/script_handling/item_script.rs b/src/dynamic_data/script_handling/item_script.rs index 14459ce..41a4228 100755 --- a/src/dynamic_data/script_handling/item_script.rs +++ b/src/dynamic_data/script_handling/item_script.rs @@ -1,11 +1,11 @@ use crate::dynamic_data::Pokemon; -use crate::static_data::EffectParameter; +use crate::static_data::Parameter; use std::sync::Arc; /// The script functions that are relevant to item use. pub trait ItemScript { /// Initializes the script with the given parameters for a specific item - fn on_initialize(&self, _pars: Vec>) -> anyhow_ext::Result<()> { + fn on_initialize(&self, _pars: Vec>) -> anyhow_ext::Result<()> { Ok(()) } diff --git a/src/dynamic_data/script_handling/script.rs b/src/dynamic_data/script_handling/script.rs index da88d33..b97a45f 100755 --- a/src/dynamic_data/script_handling/script.rs +++ b/src/dynamic_data/script_handling/script.rs @@ -13,8 +13,8 @@ use crate::dynamic_data::{Battle, DynamicLibrary}; use crate::dynamic_data::{BattleSide, DamageSource}; use crate::dynamic_data::{ExecutingMove, WeakBattleReference, WeakPokemonReference}; use crate::dynamic_data::{Pokemon, WeakBattleSideReference}; -use crate::static_data::{EffectParameter, TypeIdentifier}; use crate::static_data::{Item, Statistic}; +use crate::static_data::{Parameter, TypeIdentifier}; use crate::StringKey; /// The script trait is used to make changes to how a battle executes, without requiring hardcoded @@ -69,7 +69,7 @@ pub trait Script: Send + Sync { Ok(()) } /// This function is ran when this script starts being in effect. - fn on_initialize(&self, _library: &Arc, _pars: Vec>) -> Result<()> { + fn on_initialize(&self, _library: &Arc, _pars: Vec>) -> Result<()> { Ok(()) } /// This function is ran just before the start of the turn. Everyone has made its choices here, diff --git a/src/ffi/ffi_handle.rs b/src/ffi/ffi_handle.rs index 91bf6b6..e1e8c9f 100644 --- a/src/ffi/ffi_handle.rs +++ b/src/ffi/ffi_handle.rs @@ -1,5 +1,5 @@ use crate::static_data::{ - Ability, EffectParameter, Form, GrowthRate, Item, LearnableMoves, MoveData, Nature, SecondaryEffect, Species, + Ability, Form, GrowthRate, Item, LearnableMoves, MoveData, Nature, Parameter, SecondaryEffect, Species, StaticStatisticSet, StatisticSet, }; use anyhow::anyhow; @@ -60,7 +60,7 @@ impl Default for FFIHandle { #[allow(clippy::missing_docs_in_private_items)] // I'm not documenting these items. pub(super) enum FFIObject { Ability(Arc), - EffectParameter(Arc), + EffectParameter(Arc), StatisticSetU8(Arc>), StatisticSetI8(Arc>), StatisticSetU32(Arc>), @@ -307,7 +307,7 @@ macro_rules! ffi_obj_conversions { } ffi_obj_conversions!(Arc, Ability); -ffi_obj_conversions!(Arc, EffectParameter); +ffi_obj_conversions!(Arc, EffectParameter); ffi_obj_conversions!(Arc>, StatisticSetI8); ffi_obj_conversions!(Arc>, StatisticSetU8); ffi_obj_conversions!(Arc>, StatisticSetU32); diff --git a/src/ffi/static_data/ability.rs b/src/ffi/static_data/ability.rs index 8879955..43f1f89 100644 --- a/src/ffi/static_data/ability.rs +++ b/src/ffi/static_data/ability.rs @@ -1,7 +1,7 @@ use crate::ffi::FFIHandle; use crate::ffi::FromFFIHandle; use crate::ffi::{FFIResult, OwnedPtrString}; -use crate::static_data::{Ability, AbilityImpl, EffectParameter}; +use crate::static_data::{Ability, AbilityImpl, Parameter}; use crate::StringKey; use anyhow::anyhow; use std::ffi::{c_char, CStr, CString}; @@ -12,11 +12,11 @@ use std::sync::Arc; unsafe extern "C" fn ability_new( name: *const c_char, effect: *const c_char, - parameters: *const FFIHandle>, + parameters: *const FFIHandle>, parameters_length: usize, ) -> FFIResult>> { let parameters = std::slice::from_raw_parts(parameters, parameters_length); - let mut parameters_vec: Vec> = Vec::with_capacity(parameters_length); + let mut parameters_vec: Vec> = Vec::with_capacity(parameters_length); for parameter in parameters { parameters_vec.push(parameter.from_ffi_handle()); } @@ -63,7 +63,7 @@ unsafe extern "C" fn ability_parameter_length(ptr: FFIHandle>) unsafe extern "C" fn ability_parameter_get( ptr: FFIHandle>, index: usize, -) -> FFIHandle> { +) -> FFIHandle> { if let Some(p) = ptr.from_ffi_handle().parameters().get(index) { FFIHandle::get_handle(p.clone().into()) } else { diff --git a/src/ffi/static_data/mod.rs b/src/ffi/static_data/mod.rs index 88c89c1..2d90be3 100644 --- a/src/ffi/static_data/mod.rs +++ b/src/ffi/static_data/mod.rs @@ -1,6 +1,6 @@ use crate::ffi::ffi_handle::{FFIHandle, FFIObject, FromFFIHandle}; use crate::ffi::{FFIResult, OwnedPtrString}; -use crate::static_data::EffectParameter; +use crate::static_data::Parameter; use crate::{PkmnError, StringKey}; use anyhow::anyhow; use std::ffi::{c_char, CStr, CString}; @@ -30,50 +30,50 @@ mod statistic_set; /// Instantiates an effect parameter with a boolean. #[no_mangle] -extern "C" fn effect_parameter_new_bool(value: u8) -> FFIHandle> { - FFIHandle::get_handle(FFIObject::EffectParameter(Arc::new(EffectParameter::from(value == 1)))) +extern "C" fn effect_parameter_new_bool(value: u8) -> FFIHandle> { + FFIHandle::get_handle(FFIObject::EffectParameter(Arc::new(Parameter::from(value == 1)))) } /// Instantiates an effect parameter with an integer. #[no_mangle] -extern "C" fn effect_parameter_new_int(value: i64) -> FFIHandle> { - FFIHandle::get_handle(FFIObject::EffectParameter(Arc::new(EffectParameter::from(value)))) +extern "C" fn effect_parameter_new_int(value: i64) -> FFIHandle> { + FFIHandle::get_handle(FFIObject::EffectParameter(Arc::new(Parameter::from(value)))) } /// Instantiates an effect parameter with a float. #[no_mangle] -extern "C" fn effect_parameter_new_float(value: f32) -> FFIHandle> { - FFIHandle::get_handle(FFIObject::EffectParameter(Arc::new(EffectParameter::from(value)))) +extern "C" fn effect_parameter_new_float(value: f32) -> FFIHandle> { + FFIHandle::get_handle(FFIObject::EffectParameter(Arc::new(Parameter::from(value)))) } /// Instantiates an effect parameter with a string. #[no_mangle] -unsafe extern "C" fn effect_parameter_new_string(value: *const c_char) -> FFIResult>> { +unsafe extern "C" fn effect_parameter_new_string(value: *const c_char) -> FFIResult>> { let sk: StringKey = match CStr::from_ptr(value).to_str() { Ok(sk) => sk.into(), Err(_) => return FFIResult::err(PkmnError::InvalidCString.into()), }; FFIResult::ok(FFIHandle::get_handle(FFIObject::EffectParameter(Arc::new( - EffectParameter::from(sk), + Parameter::from(sk), )))) } /// Get the type of an effect parameter. #[no_mangle] -extern "C" fn effect_parameter_get_type(ptr: FFIHandle>) -> u8 { +extern "C" fn effect_parameter_get_type(ptr: FFIHandle>) -> u8 { match ptr.from_ffi_handle().deref() { - EffectParameter::Bool(_) => 0, - EffectParameter::Int(_) => 1, - EffectParameter::Float(_) => 2, - EffectParameter::String(_) => 3, + Parameter::Bool(_) => 0, + Parameter::Int(_) => 1, + Parameter::Float(_) => 2, + Parameter::String(_) => 3, } } /// Get the boolean contained in the effect parameter, panics if the effect parameter is not a bool. #[no_mangle] -extern "C" fn effect_parameter_get_as_bool(ptr: FFIHandle>) -> FFIResult { +extern "C" fn effect_parameter_get_as_bool(ptr: FFIHandle>) -> FFIResult { let p = ptr.from_ffi_handle(); - if let EffectParameter::Bool(b) = p.deref() { + if let Parameter::Bool(b) = p.deref() { FFIResult::ok(u8::from(*b)) } else { FFIResult::err(anyhow!("Unexpected effect parameter. Expected bool, was: {}", p)) @@ -82,9 +82,9 @@ extern "C" fn effect_parameter_get_as_bool(ptr: FFIHandle>) /// Get the int contained in the effect parameter, panics if the effect parameter is not a int. #[no_mangle] -extern "C" fn effect_parameter_get_as_int(ptr: FFIHandle>) -> FFIResult { +extern "C" fn effect_parameter_get_as_int(ptr: FFIHandle>) -> FFIResult { let p = ptr.from_ffi_handle(); - if let EffectParameter::Int(b) = p.deref() { + if let Parameter::Int(b) = p.deref() { FFIResult::ok(*b) } else { FFIResult::err(anyhow!("Unexpected effect parameter. Expected int, was: {}", p)) @@ -93,9 +93,9 @@ extern "C" fn effect_parameter_get_as_int(ptr: FFIHandle>) /// Get the float contained in the effect parameter, panics if the effect parameter is not a float. #[no_mangle] -extern "C" fn effect_parameter_get_as_float(ptr: FFIHandle>) -> FFIResult { +extern "C" fn effect_parameter_get_as_float(ptr: FFIHandle>) -> FFIResult { let p = ptr.from_ffi_handle(); - if let EffectParameter::Float(b) = p.deref() { + if let Parameter::Float(b) = p.deref() { FFIResult::ok(*b) } else { FFIResult::err(anyhow!("Unexpected effect parameter. Expected float, was: {}", p)) @@ -104,9 +104,9 @@ extern "C" fn effect_parameter_get_as_float(ptr: FFIHandle> /// Get the string contained in the effect parameter, panics if the effect parameter is not a string. #[no_mangle] -extern "C" fn effect_parameter_get_as_string(ptr: FFIHandle>) -> FFIResult { +extern "C" fn effect_parameter_get_as_string(ptr: FFIHandle>) -> FFIResult { let p = ptr.from_ffi_handle(); - if let EffectParameter::String(b) = p.deref() { + if let Parameter::String(b) = p.deref() { match CString::new(b.str().to_string()) { Ok(cstr) => FFIResult::ok(OwnedPtrString(cstr.into_raw())), Err(_) => FFIResult::err(PkmnError::InvalidCString.into()), diff --git a/src/ffi/static_data/move_data.rs b/src/ffi/static_data/move_data.rs index 55e949b..4d8ad72 100644 --- a/src/ffi/static_data/move_data.rs +++ b/src/ffi/static_data/move_data.rs @@ -1,8 +1,7 @@ use crate::ffi::ffi_handle::{FFIHandle, FromFFIHandle}; use crate::ffi::{ffi_handle_arc_dyn_getter, FFIResult, NonOwnedPtrString, OwnedPtrString}; use crate::static_data::{ - EffectParameter, MoveCategory, MoveData, MoveDataImpl, MoveTarget, SecondaryEffect, SecondaryEffectImpl, - TypeIdentifier, + MoveCategory, MoveData, MoveDataImpl, MoveTarget, Parameter, SecondaryEffect, SecondaryEffectImpl, TypeIdentifier, }; use crate::StringKey; use anyhow::anyhow; @@ -100,7 +99,7 @@ unsafe extern "C" fn move_data_has_flag(ptr: FFIHandle>, flag: unsafe extern "C" fn secondary_effect_new( chance: f32, effect_name: NonOwnedPtrString, - parameters: *mut FFIHandle>, + parameters: *mut FFIHandle>, parameters_length: usize, ) -> FFIHandle> { let parameter_slice = std::slice::from_raw_parts(parameters, parameters_length); @@ -148,7 +147,7 @@ unsafe extern "C" fn secondary_effect_parameter_length(ptr: FFIHandle>, index: usize, -) -> FFIHandle> { +) -> FFIHandle> { if let Some(v) = ptr.from_ffi_handle().parameters().get(index) { FFIHandle::get_handle(v.clone().into()) } else { diff --git a/src/ffi/static_data/species.rs b/src/ffi/static_data/species.rs index b210317..e7b475c 100644 --- a/src/ffi/static_data/species.rs +++ b/src/ffi/static_data/species.rs @@ -50,8 +50,10 @@ unsafe extern "C" fn species_new( gender_rate, &growth_rate, capture_rate, + 120, default_form.clone(), flags_set, + Vec::new(), )); FFIResult::ok(FFIHandle::get_handle(a.into())) } diff --git a/src/script_implementations/wasm/export_registry/mod.rs b/src/script_implementations/wasm/export_registry/mod.rs index 116f219..895bc0c 100755 --- a/src/script_implementations/wasm/export_registry/mod.rs +++ b/src/script_implementations/wasm/export_registry/mod.rs @@ -6,7 +6,7 @@ use wasmer::{FunctionEnv, FunctionEnvMut, Imports, StoreMut}; use crate::script_implementations::wasm::extern_ref::ExternRef; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; -use crate::static_data::EffectParameter; +use crate::static_data::Parameter; use crate::StringKey; /// Dynamic data registration @@ -140,51 +140,39 @@ fn string_key_get_str(env: FunctionEnvMut, string_key: ExternRef } /// Gets the type of an EffectParameter -fn effect_parameter_get_type( - env: FunctionEnvMut, - parameter: ExternRef, -) -> WasmResult { +fn effect_parameter_get_type(env: FunctionEnvMut, parameter: ExternRef) -> WasmResult { let value = get_value_arc!(parameter, env); wasm_ok(match value.deref() { - EffectParameter::Bool(_) => 1, - EffectParameter::Int(_) => 2, - EffectParameter::Float(_) => 3, - EffectParameter::String(_) => 4, + Parameter::Bool(_) => 1, + Parameter::Int(_) => 2, + Parameter::Float(_) => 3, + Parameter::String(_) => 4, }) } /// Gets the inner bool data of an EffectParameter. Panics if it's not a bool. -fn effect_parameter_as_bool( - env: FunctionEnvMut, - parameter: ExternRef, -) -> WasmResult { +fn effect_parameter_as_bool(env: FunctionEnvMut, parameter: ExternRef) -> WasmResult { let value = get_value_arc!(parameter, env); match value.deref() { - EffectParameter::Bool(b) => wasm_ok(>::from(*b)), + Parameter::Bool(b) => wasm_ok(>::from(*b)), _ => wasm_err::(anyhow!("Unexpected parameter type. Expected bool, got {}", value), &env), } } /// Gets the inner int data of an EffectParameter. Panics if it's not an int. -fn effect_parameter_as_int( - env: FunctionEnvMut, - parameter: ExternRef, -) -> WasmResult { +fn effect_parameter_as_int(env: FunctionEnvMut, parameter: ExternRef) -> WasmResult { let value = get_value_arc!(parameter, env); match value.deref() { - EffectParameter::Int(i) => wasm_ok(*i), + Parameter::Int(i) => wasm_ok(*i), _ => wasm_err::(anyhow!("Unexpected parameter type. Expected int, got {}", value), &env), } } /// Gets the inner float data of an EffectParameter. Panics if it's not a float. -fn effect_parameter_as_float( - env: FunctionEnvMut, - parameter: ExternRef, -) -> WasmResult { +fn effect_parameter_as_float(env: FunctionEnvMut, parameter: ExternRef) -> WasmResult { let value = get_value_arc!(parameter, env); match value.deref() { - EffectParameter::Float(f) => wasm_ok(*f), + Parameter::Float(f) => wasm_ok(*f), _ => wasm_err::( anyhow!("Unexpected parameter type. Expected float, got {}", value), &env, @@ -195,11 +183,11 @@ fn effect_parameter_as_float( /// Gets the inner string data of an EffectParameter. Panics if it's not a string. fn effect_parameter_as_string( env: FunctionEnvMut, - parameter: ExternRef, + parameter: ExternRef, ) -> WasmResult> { let value = get_value_arc!(parameter, env); match value.deref() { - EffectParameter::String(s) => wasm_ok(ExternRef::::func_new(&env, s.clone().into())), + Parameter::String(s) => wasm_ok(ExternRef::::func_new(&env, s.clone().into())), _ => wasm_err::>( anyhow!("Unexpected parameter type. Expected string, got {}", value), &env, diff --git a/src/script_implementations/wasm/export_registry/static_data/ability.rs b/src/script_implementations/wasm/export_registry/static_data/ability.rs index 0571a32..621cdfb 100644 --- a/src/script_implementations/wasm/export_registry/static_data/ability.rs +++ b/src/script_implementations/wasm/export_registry/static_data/ability.rs @@ -3,7 +3,7 @@ use crate::script_implementations::wasm::export_registry::FunctionEnvMut; use crate::script_implementations::wasm::export_registry::{register, try_wasm, wasm_ok, WasmResult}; use crate::script_implementations::wasm::extern_ref::ExternRef; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; -use crate::static_data::{Ability, EffectParameter}; +use crate::static_data::{Ability, Parameter}; use crate::StringKey; register! { @@ -32,7 +32,7 @@ fn ability_get_parameters( let parameters = ability.parameters(); let mut vec : Vec = Vec::with_capacity(parameters.len()); for parameter in parameters { - vec.push(ExternRef::::func_new(&env, parameter.into()).index() as u32); + vec.push(ExternRef::::func_new(&env, parameter.into()).index() as u32); } let wasm_ptr = try_wasm!(env.data().data().copy_value_vec_to_wasm(&vec), env); let r: u64 = unsafe { std::mem::transmute((wasm_ptr, vec.len() as u32)) }; diff --git a/src/script_implementations/wasm/export_registry/wasm_object.rs b/src/script_implementations/wasm/export_registry/wasm_object.rs index dd2a9ab..9903dd0 100644 --- a/src/script_implementations/wasm/export_registry/wasm_object.rs +++ b/src/script_implementations/wasm/export_registry/wasm_object.rs @@ -9,7 +9,7 @@ use std::sync::{Arc, Weak}; pub(crate) enum WasmObject { // Static data StringKey(crate::StringKey), - EffectParameter(Weak), + EffectParameter(Weak), MoveData(Weak), Species(Weak), @@ -182,13 +182,13 @@ impl FromWasmObj for crate::StringKey { } } -impl From<&Arc> for WasmObject { - fn from(value: &Arc) -> Self { +impl From<&Arc> for WasmObject { + fn from(value: &Arc) -> Self { Self::EffectParameter(Arc::downgrade(value)) } } -impl_from_wasm_obj!(EffectParameter, Arc); +impl_from_wasm_obj!(EffectParameter, Arc); impl From<&Arc> for WasmObject { fn from(value: &Arc) -> Self { diff --git a/src/script_implementations/wasm/item_script.rs b/src/script_implementations/wasm/item_script.rs index 8bbcba4..737cd8f 100644 --- a/src/script_implementations/wasm/item_script.rs +++ b/src/script_implementations/wasm/item_script.rs @@ -2,7 +2,7 @@ use crate::dynamic_data::{ItemScript, Pokemon}; use crate::script_implementations::wasm::export_registry::WasmVoidResultExtension; use crate::script_implementations::wasm::extern_ref::ExternRef; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnvironmentData; -use crate::static_data::EffectParameter; +use crate::static_data::Parameter; use crate::StringKey; use std::sync::Arc; @@ -64,12 +64,12 @@ macro_rules! ex_ref { } impl ItemScript for WebAssemblyItemScript { - fn on_initialize(&self, pars: Vec>) -> anyhow_ext::Result<()> { + fn on_initialize(&self, pars: Vec>) -> anyhow_ext::Result<()> { let env = &self.environment; if let Some(func) = env.script_function_cache().item_on_initialize(env) { let pars = pars .into_iter() - .map(|p| ExternRef::::new(env, (&p).into()).index() as u32) + .map(|p| ExternRef::::new(env, (&p).into()).index() as u32) .collect::>(); let wasm_ptr = env.copy_value_vec_to_wasm(&pars)?; let r: u64 = unsafe { std::mem::transmute((wasm_ptr, pars.len() as u32)) }; diff --git a/src/script_implementations/wasm/script.rs b/src/script_implementations/wasm/script.rs index 4848750..04f1f96 100755 --- a/src/script_implementations/wasm/script.rs +++ b/src/script_implementations/wasm/script.rs @@ -12,7 +12,7 @@ use crate::script_implementations::wasm::export_registry::WasmVoidResultExtensio use crate::script_implementations::wasm::extern_ref::ExternRef; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnvironmentData; use crate::script_implementations::wasm::WebAssemblyScriptCapabilities; -use crate::static_data::{EffectParameter, Item, Statistic, TypeIdentifier}; +use crate::static_data::{Item, Parameter, Statistic, TypeIdentifier}; use crate::StringKey; /// A WebAssemblyScript is there to implement the Script trait within WebAssemblyScript. @@ -125,7 +125,7 @@ impl Script for WebAssemblyScript { Ok(()) } - fn on_initialize(&self, library: &Arc, pars: Vec>) -> Result<()> { + fn on_initialize(&self, library: &Arc, pars: Vec>) -> Result<()> { if !self.has_capability(&WebAssemblyScriptCapabilities::Initialize) { return Ok(()); } @@ -134,7 +134,7 @@ impl Script for WebAssemblyScript { if let Some(func) = env.script_function_cache().on_initialize(env) { let pars = pars .into_iter() - .map(|p| ExternRef::::new(env, (&p).into()).index() as u32) + .map(|p| ExternRef::::new(env, (&p).into()).index() as u32) .collect::>(); let wasm_ptr = env.copy_value_vec_to_wasm(&pars)?; let r: u64 = unsafe { std::mem::transmute((wasm_ptr, pars.len() as u32)) }; diff --git a/src/static_data/libraries/species_library.rs b/src/static_data/libraries/species_library.rs index d03bbe1..6f4f224 100755 --- a/src/static_data/libraries/species_library.rs +++ b/src/static_data/libraries/species_library.rs @@ -65,6 +65,7 @@ pub mod tests { 0.5, &"test_growthrate".into(), 0, + 120, Arc::new(FormImpl::new( &"default".into(), 0.0, @@ -78,6 +79,7 @@ pub mod tests { HashSet::new(), )), HashSet::new(), + Vec::new(), )) } diff --git a/src/static_data/mod.rs b/src/static_data/mod.rs index 777c76d..c413118 100755 --- a/src/static_data/mod.rs +++ b/src/static_data/mod.rs @@ -59,7 +59,7 @@ mod time_of_day; /// A parameter for an effect. This is basically a simple way to dynamically store multiple different /// primitives on data. #[derive(PartialEq, Debug, Clone)] -pub enum EffectParameter { +pub enum Parameter { /// A boolean value. Bool(bool), /// An integer value. Stored as a 64 bit int to deal with potentially large numbers. @@ -70,37 +70,37 @@ pub enum EffectParameter { String(StringKey), } -impl From for EffectParameter { +impl From for Parameter { fn from(b: bool) -> Self { - EffectParameter::Bool(b) + Parameter::Bool(b) } } -impl From for EffectParameter { +impl From for Parameter { fn from(i: i64) -> Self { - EffectParameter::Int(i) + Parameter::Int(i) } } -impl From for EffectParameter { +impl From for Parameter { fn from(f: f32) -> Self { - EffectParameter::Float(f) + Parameter::Float(f) } } -impl From for EffectParameter { +impl From for Parameter { fn from(s: StringKey) -> Self { - EffectParameter::String(s) + Parameter::String(s) } } -impl Display for EffectParameter { +impl Display for Parameter { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { - EffectParameter::Bool(v) => f.write_fmt(format_args!("EffectParameter::Bool({v})")), - EffectParameter::Int(v) => f.write_fmt(format_args!("EffectParameter::Int({v})")), - EffectParameter::Float(v) => f.write_fmt(format_args!("EffectParameter::Float({v})")), - EffectParameter::String(v) => f.write_fmt(format_args!("EffectParameter::String({v})")), + Parameter::Bool(v) => f.write_fmt(format_args!("EffectParameter::Bool({v})")), + Parameter::Int(v) => f.write_fmt(format_args!("EffectParameter::Int({v})")), + Parameter::Float(v) => f.write_fmt(format_args!("EffectParameter::Float({v})")), + Parameter::String(v) => f.write_fmt(format_args!("EffectParameter::String({v})")), } } } diff --git a/src/static_data/moves/secondary_effect.rs b/src/static_data/moves/secondary_effect.rs index 07fd518..42d7ed2 100755 --- a/src/static_data/moves/secondary_effect.rs +++ b/src/static_data/moves/secondary_effect.rs @@ -1,4 +1,4 @@ -use crate::static_data::EffectParameter; +use crate::static_data::Parameter; use crate::StringKey; use std::fmt::Debug; use std::sync::Arc; @@ -10,7 +10,7 @@ pub trait SecondaryEffect: Debug { /// The name of the effect. fn effect_name(&self) -> &StringKey; /// A list of parameters for the effect. - fn parameters(&self) -> &Vec>; + fn parameters(&self) -> &Vec>; } /// A secondary effect is an effect on a move that happens after it hits. @@ -21,12 +21,12 @@ pub struct SecondaryEffectImpl { /// The name of the effect. effect_name: StringKey, /// A list of parameters for the effect. - parameters: Vec>, + parameters: Vec>, } impl SecondaryEffectImpl { /// Instantiates a new Secondary Effect. - pub fn new(chance: f32, effect_name: StringKey, parameters: Vec>) -> Self { + pub fn new(chance: f32, effect_name: StringKey, parameters: Vec>) -> Self { Self { chance, effect_name, @@ -45,7 +45,7 @@ impl SecondaryEffect for SecondaryEffectImpl { &self.effect_name } /// A list of parameters for the effect. - fn parameters(&self) -> &Vec> { + fn parameters(&self) -> &Vec> { &self.parameters } } @@ -66,7 +66,7 @@ pub(crate) mod tests { impl SecondaryEffect for SecondaryEffect { fn chance(&self) -> f32; fn effect_name(&self) -> &StringKey; - fn parameters(&self) -> &Vec>; + fn parameters(&self) -> &Vec>; } } diff --git a/src/static_data/species_data/ability.rs b/src/static_data/species_data/ability.rs index 15daa1a..721ed3e 100755 --- a/src/static_data/species_data/ability.rs +++ b/src/static_data/species_data/ability.rs @@ -1,4 +1,4 @@ -use crate::static_data::EffectParameter; +use crate::static_data::Parameter; use crate::StringKey; use std::fmt::Debug; use std::sync::Arc; @@ -10,7 +10,7 @@ pub trait Ability: Debug { /// The name of the script effect of the ability. fn effect(&self) -> &StringKey; /// The parameters for the script effect of the ability. - fn parameters(&self) -> &Vec>; + fn parameters(&self) -> &Vec>; } /// An ability is a passive effect in battle that is attached to a Pokemon. @@ -21,12 +21,12 @@ pub struct AbilityImpl { /// The name of the script effect of the ability. effect: StringKey, /// The parameters for the script effect of the ability. - parameters: Vec>, + parameters: Vec>, } impl AbilityImpl { /// Instantiates a new ability. - pub fn new(name: &StringKey, effect: &StringKey, parameters: Vec>) -> Self { + pub fn new(name: &StringKey, effect: &StringKey, parameters: Vec>) -> Self { Self { name: name.clone(), effect: effect.clone(), @@ -45,7 +45,7 @@ impl Ability for AbilityImpl { &self.effect } /// The parameters for the script effect of the ability. - fn parameters(&self) -> &Vec> { + fn parameters(&self) -> &Vec> { &self.parameters } } @@ -74,7 +74,7 @@ pub(crate) mod tests { impl Ability for Ability { fn name(&self) -> &StringKey; fn effect(&self) -> &StringKey; - fn parameters(&self) -> &Vec>; + fn parameters(&self) -> &Vec>; } } } diff --git a/src/static_data/species_data/evolution_data.rs b/src/static_data/species_data/evolution_data.rs new file mode 100644 index 0000000..f9c39d7 --- /dev/null +++ b/src/static_data/species_data/evolution_data.rs @@ -0,0 +1,118 @@ +use crate::defines::LevelInt; +use crate::static_data::{Gender, Parameter}; +use crate::StringKey; +use std::sync::Arc; + +/// The different ways a Pokemon can evolve. +#[derive(Debug)] +pub enum EvolutionMethod { + /// Evolves when a certain level is reached. + Level { + /// The level at which the Pokemon evolves. + level: LevelInt, + }, + /// Evolves when a certain level is reached, and the Pokemon is a specific gender. + LevelGender { + /// The level at which the Pokemon evolves. + level: LevelInt, + /// The gender the Pokemon needs to be. + gender: Gender, + }, + /// Evolves when an item is used. + Item { + /// The item that needs to be used. + item: StringKey, + }, + /// Evolves when an item is used, and the Pokemon is a specific gender. + ItemGender { + /// The item that needs to be used. + item: StringKey, + /// The gender the Pokemon needs to be. + gender: Gender, + }, + /// Evolves if an item is held. + HoldItem { + /// The item that needs to be held. + item: StringKey, + }, + /// Evolves if a held item is held, and it's day. + DayHoldItem { + /// The item that needs to be held. + item: StringKey, + }, + /// Evolves if a held item is held, and it's night. + NightHoldItem { + /// The item that needs to be held. + item: StringKey, + }, + /// Evolves if the Pokemon knows a certain move. + HasMove { + /// The move that needs to be known. + move_name: StringKey, + }, + /// Evolves when above a certain happiness level. + Happiness { + /// The happiness level that needs to be reached. + happiness: u8, + }, + /// Evolves when above a certain happiness level, and it's day. + HappinessDay { + /// The happiness level that needs to be reached. + happiness: u8, + }, + /// Evolves when above a certain happiness level, and it's night. + HappinessNight { + /// The happiness level that needs to be reached. + happiness: u8, + }, + /// Evolves when traded. + Trade, + /// Evolves when traded with a certain species. + TradeSpecies { + /// The species that needs to be traded with. + species: StringKey, + }, + /// Evolves when traded while it's holding a certain item. + TradeItem { + /// The item that needs to be held. + item: StringKey, + }, + /// Evolves in a certain location. + Location { + /// The location the Pokemon needs to be in. + location: StringKey, + }, + /// A custom evolution method, implemented by the user. + Custom { + /// The name of the custom evolution method. + name: StringKey, + /// The parameters of the custom evolution method. + params: Vec>, + }, +} + +#[derive(Debug)] +/// Data about how and into which Pokemon a species can evolve. +pub struct EvolutionData { + /// The method of evolution. + method: EvolutionMethod, + /// The Pokemon the species evolves into. + to: StringKey, +} + +impl EvolutionData { + /// Creates a new evolution data instance. + pub fn new(method: EvolutionMethod, to: StringKey) -> Self { + Self { method, to } + } + + /// Returns the method of evolution. + pub fn method(&self) -> &EvolutionMethod { + &self.method + } + + /// Returns the Pokemon the species evolves into. + pub fn to(&self) -> &StringKey { + &self.to + } +} diff --git a/src/static_data/species_data/mod.rs b/src/static_data/species_data/mod.rs index b5254cc..53c33b9 100755 --- a/src/static_data/species_data/mod.rs +++ b/src/static_data/species_data/mod.rs @@ -1,6 +1,8 @@ #[doc(inline)] pub use ability::*; #[doc(inline)] +pub use evolution_data::*; +#[doc(inline)] pub use form::*; #[doc(inline)] pub use gender::*; @@ -19,6 +21,8 @@ pub(crate) mod tests { /// An ability is a passive effect in battle that is attached to a Pokemon. mod ability; +/// Data regarding into which Pokemon a species can evolve. +mod evolution_data; /// A form is a variant of a specific species. A species always has at least one form, but can have /// many more. mod form; diff --git a/src/static_data/species_data/species.rs b/src/static_data/species_data/species.rs index 5c0ba10..433cd24 100755 --- a/src/static_data/species_data/species.rs +++ b/src/static_data/species_data/species.rs @@ -6,6 +6,7 @@ use hashbrown::{HashMap, HashSet}; use parking_lot::lock_api::RwLockReadGuard; use parking_lot::{RawRwLock, RwLock}; +use crate::static_data::species_data::evolution_data::EvolutionData; use crate::static_data::Form; use crate::static_data::Gender; use crate::Random; @@ -24,6 +25,8 @@ pub trait Species: Debug { /// How hard it is to capture a Pokemon. 255 means this will be always caught, 0 means this is /// uncatchable. fn capture_rate(&self) -> u8; + /// The base happiness of the Pokemon. + fn base_happiness(&self) -> u8; /// The forms that belong to this Pokemon. fn forms(&self) -> RwLockReadGuard<'_, RawRwLock, HashMap>>; /// The arbitrary flags that can be set on a Pokemon for script use. @@ -42,6 +45,8 @@ pub trait Species: Debug { fn has_flag(&self, key: &StringKey) -> bool; /// Check whether the Pokemon has a specific flag set. fn has_flag_by_hash(&self, key_hash: u32) -> bool; + /// The data regarding into which Pokemons this species can evolve, and how. + fn evolution_data(&self) -> &Vec; } /// The data belonging to a Pokemon with certain characteristics. @@ -58,10 +63,14 @@ pub struct SpeciesImpl { /// How hard it is to capture a Pokemon. 255 means this will be always caught, 0 means this is /// uncatchable. capture_rate: u8, + /// The base happiness of the Pokemon. + base_happiness: u8, /// The forms that belong to this Pokemon. forms: RwLock>>, /// The arbitrary flags that can be set on a Pokemon for script use. flags: HashSet, + /// The data regarding into which Pokemons this species can evolve, and how. + evolution_data: Vec, } /// A cached String Key to get the default form. @@ -80,8 +89,10 @@ impl SpeciesImpl { gender_rate: f32, growth_rate: &StringKey, capture_rate: u8, + base_happiness: u8, default_form: Arc, flags: HashSet, + evolution_data: Vec, ) -> Self { let mut forms = HashMap::with_capacity(1); forms.insert_unique_unchecked(get_default_key(), default_form); @@ -91,8 +102,10 @@ impl SpeciesImpl { gender_rate, growth_rate: growth_rate.clone(), capture_rate, + base_happiness, forms: RwLock::new(forms), flags, + evolution_data, } } } @@ -119,6 +132,11 @@ impl Species for SpeciesImpl { fn capture_rate(&self) -> u8 { self.capture_rate } + + fn base_happiness(&self) -> u8 { + self.base_happiness + } + /// The forms that belong to this Pokemon. fn forms(&self) -> RwLockReadGuard<'_, RawRwLock, HashMap>> { self.forms.read() @@ -171,6 +189,10 @@ impl Species for SpeciesImpl { fn has_flag_by_hash(&self, key_hash: u32) -> bool { self.flags.contains::(&key_hash) } + + fn evolution_data(&self) -> &Vec { + &self.evolution_data + } } #[cfg(test)] @@ -188,6 +210,7 @@ pub(crate) mod tests { fn gender_rate(&self) -> f32; fn growth_rate(&self) -> &StringKey; fn capture_rate(&self) -> u8; + fn base_happiness(&self) -> u8; fn forms(&self) -> RwLockReadGuard<'_, RawRwLock, HashMap>>; fn flags(&self) -> &HashSet; fn add_form(&self, id: StringKey, form: Arc); @@ -197,6 +220,7 @@ pub(crate) mod tests { fn get_random_gender(&self, rand: &mut Random) -> Gender; fn has_flag(&self, key: &StringKey) -> bool; fn has_flag_by_hash(&self, key_hash: u32) -> bool; + fn evolution_data(&self) -> &Vec; } } } diff --git a/tests/common/library_loader.rs b/tests/common/library_loader.rs index 6aa0b35..881ba43 100755 --- a/tests/common/library_loader.rs +++ b/tests/common/library_loader.rs @@ -16,11 +16,12 @@ use pkmn_lib::dynamic_data::Gen7MiscLibrary; use pkmn_lib::dynamic_data::{DynamicLibrary, DynamicLibraryImpl}; use pkmn_lib::dynamic_data::{Gen7BattleStatCalculator, ScriptResolver}; use pkmn_lib::static_data::{ - AbilityImpl, AbilityLibrary, AbilityLibraryImpl, BattleItemCategory, DataLibrary, EffectParameter, Form, FormImpl, - GrowthRateLibrary, GrowthRateLibraryImpl, ItemImpl, ItemLibrary, ItemLibraryImpl, LearnableMoves, - LearnableMovesImpl, LibrarySettingsImpl, LookupGrowthRate, MoveDataImpl, MoveLibrary, MoveLibraryImpl, NatureImpl, - NatureLibrary, NatureLibraryImpl, SecondaryEffect, SecondaryEffectImpl, SpeciesImpl, SpeciesLibrary, - SpeciesLibraryImpl, StaticDataImpl, StaticStatisticSet, Statistic, TimeOfDay, TypeLibrary, TypeLibraryImpl, + AbilityImpl, AbilityLibrary, AbilityLibraryImpl, BattleItemCategory, DataLibrary, EvolutionData, EvolutionMethod, + Form, FormImpl, Gender, GrowthRateLibrary, GrowthRateLibraryImpl, ItemImpl, ItemLibrary, ItemLibraryImpl, + LearnableMoves, LearnableMovesImpl, LibrarySettingsImpl, LookupGrowthRate, MoveDataImpl, MoveLibrary, + MoveLibraryImpl, NatureImpl, NatureLibrary, NatureLibraryImpl, Parameter, SecondaryEffect, SecondaryEffectImpl, + SpeciesImpl, SpeciesLibrary, SpeciesLibraryImpl, StaticDataImpl, StaticStatisticSet, Statistic, TimeOfDay, + TypeLibrary, TypeLibraryImpl, }; use pkmn_lib::StringKey; @@ -225,7 +226,7 @@ pub fn load_abilities(path: &String) -> Arc { let mut parameters = Vec::new(); if let Some(p) = value.get("parameters") { for par in p.as_array().unwrap() { - parameters.push(parse_effect_parameter(par)); + parameters.push(parse_parameter(par)); } } @@ -261,7 +262,7 @@ pub fn load_moves(path: &String, types: &Arc) -> Arc EvolutionData { + let species = StringKey::new(value.get("species").unwrap().as_str().unwrap()); + let method = value.get("method").unwrap().as_str().unwrap(); + let method = match method { + "level" => { + let level = value.get("data").unwrap().as_i64().unwrap() as LevelInt; + EvolutionMethod::Level { level } + } + "levelfemale" => { + let level = value.get("data").unwrap().as_i64().unwrap() as LevelInt; + EvolutionMethod::LevelGender { + level, + gender: Gender::Female, + } + } + "levelmale" => { + let level = value.get("data").unwrap().as_i64().unwrap() as LevelInt; + EvolutionMethod::LevelGender { + level, + gender: Gender::Male, + } + } + "happiness" => { + let happiness = value.get("data").unwrap().as_i64().unwrap() as u8; + EvolutionMethod::Happiness { happiness } + } + "happinessday" => { + let happiness = value.get("data").unwrap().as_i64().unwrap() as u8; + EvolutionMethod::HappinessDay { happiness } + } + "happinessnight" => { + let happiness = value.get("data").unwrap().as_i64().unwrap() as u8; + EvolutionMethod::HappinessNight { happiness } + } + "item" => { + let item = StringKey::new(value.get("data").unwrap().as_str().unwrap()); + EvolutionMethod::Item { item } + } + "itemmale" => { + let item = StringKey::new(value.get("data").unwrap().as_str().unwrap()); + EvolutionMethod::ItemGender { + item, + gender: Gender::Male, + } + } + "itemfemale" => { + let item = StringKey::new(value.get("data").unwrap().as_str().unwrap()); + EvolutionMethod::ItemGender { + item, + gender: Gender::Female, + } + } + "holditem" => { + let item = StringKey::new(value.get("data").unwrap().as_str().unwrap()); + EvolutionMethod::HoldItem { item } + } + "dayholditem" => { + let item = StringKey::new(value.get("data").unwrap().as_str().unwrap()); + EvolutionMethod::DayHoldItem { item } + } + "nightholditem" => { + let item = StringKey::new(value.get("data").unwrap().as_str().unwrap()); + EvolutionMethod::NightHoldItem { item } + } + "hasmove" => { + let move_name = StringKey::new(value.get("data").unwrap().as_str().unwrap()); + EvolutionMethod::HasMove { move_name } + } + "trade" => EvolutionMethod::Trade, + "tradespecies" => { + let species = StringKey::new(value.get("data").unwrap().as_str().unwrap()); + EvolutionMethod::TradeSpecies { species } + } + "tradeitem" => { + let item = StringKey::new(value.get("data").unwrap().as_str().unwrap()); + EvolutionMethod::TradeItem { item } + } + "location" => { + let location = StringKey::new(value.get("data").unwrap().as_str().unwrap()); + EvolutionMethod::Location { location } + } + "custom" => { + let data = value.get("data").unwrap().as_array().unwrap(); + let name = StringKey::new(data[0].as_str().unwrap()); + let params = data[1..].iter().map(parse_parameter).collect(); + EvolutionMethod::Custom { name, params } + } + _ => panic!("Unknown evolution method: {}", method), + }; + EvolutionData::new(method, species) +} + #[cfg(not(feature = "wasm"))] fn load_script_resolver(path: &String) -> Arc { Arc::new(EmptyScriptResolver::default()) @@ -471,7 +570,7 @@ fn parse_moves(value: &Value, move_library: &Arc) -> Arc Arc { +fn parse_parameter(value: &Value) -> Arc { Arc::new(match value { Value::Null => { panic!("Unexpected type") diff --git a/tests/data/Pokemon.json b/tests/data/Pokemon.json index f549a2a..b072daa 100755 --- a/tests/data/Pokemon.json +++ b/tests/data/Pokemon.json @@ -8390,8 +8390,16 @@ } }, "evolutions": [ - { "species": "wormadam", "method": "levelfemale", "data": "20" }, - { "species": "mothim", "method": "levelmale", "data": "20" } + { + "species": "wormadam", + "method": "levelfemale", + "data": 20 + }, + { + "species": "mothim", + "method": "levelmale", + "data": 20 + } ] }, "butterfree": { @@ -12710,7 +12718,11 @@ } }, "evolutions": [ - { "species": "vespiquen", "method": "levelfemale", "data": "21" } + { + "species": "vespiquen", + "method": "levelfemale", + "data": 21 + } ] }, "combusken": {